1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19 package org.apache.mina.io.socket;
20
21 import java.io.IOException;
22 import java.net.InetSocketAddress;
23 import java.net.SocketAddress;
24 import java.nio.channels.SelectionKey;
25 import java.nio.channels.Selector;
26 import java.nio.channels.ServerSocketChannel;
27 import java.nio.channels.SocketChannel;
28 import java.util.HashMap;
29 import java.util.Iterator;
30 import java.util.Map;
31 import java.util.Set;
32
33 import org.apache.mina.common.BaseSessionManager;
34 import org.apache.mina.io.IoAcceptor;
35 import org.apache.mina.io.IoFilterChain;
36 import org.apache.mina.io.IoHandler;
37 import org.apache.mina.io.IoSessionManagerFilterChain;
38 import org.apache.mina.util.Queue;
39
40 /***
41 * {@link IoAcceptor} for socket transport (TCP/IP).
42 *
43 * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
44 * @version $Rev: 165586 $, $Date: 2005-05-02 15:27:27 +0900 (?, 02 5? 2005) $
45 */
46 public class SocketAcceptor extends BaseSessionManager implements IoAcceptor
47 {
48 private static volatile int nextId = 0;
49
50 private final IoSessionManagerFilterChain filters = new SocketSessionManagerFilterChain( this );
51
52 private final int id = nextId ++ ;
53
54 private final Selector selector;
55
56 private final Map channels = new HashMap();
57
58 private final Queue registerQueue = new Queue();
59
60 private final Queue cancelQueue = new Queue();
61
62 private int backlog = 50;
63
64 private Worker worker;
65
66
67 /***
68 * Creates a new instance.
69 *
70 * @throws IOException
71 */
72 public SocketAcceptor() throws IOException
73 {
74 selector = Selector.open();
75 }
76
77 /***
78 * Binds to the specified <code>address</code> and handles incoming
79 * connections with the specified <code>handler</code>. Backlog value
80 * is configured to the value of <code>backlog</code> property.
81 *
82 * @throws IOException if failed to bind
83 */
84 public void bind( SocketAddress address, IoHandler handler ) throws IOException
85 {
86 if( address == null )
87 {
88 throw new NullPointerException( "address" );
89 }
90
91 if( handler == null )
92 {
93 throw new NullPointerException( "handler" );
94 }
95
96 if( !( address instanceof InetSocketAddress ) )
97 {
98 throw new IllegalArgumentException( "Unexpected address type: " + address.getClass() );
99 }
100
101 if( ( ( InetSocketAddress ) address ).getPort() == 0 )
102 {
103 throw new IllegalArgumentException( "Unsupported port number: 0" );
104 }
105
106 RegistrationRequest request = new RegistrationRequest( address, backlog, handler );
107
108 synchronized( this )
109 {
110 synchronized( registerQueue )
111 {
112 registerQueue.push( request );
113 }
114 startupWorker();
115 }
116
117 selector.wakeup();
118
119 synchronized( request )
120 {
121 while( !request.done )
122 {
123 try
124 {
125 request.wait();
126 }
127 catch( InterruptedException e )
128 {
129 }
130 }
131 }
132
133 if( request.exception != null )
134 {
135 throw request.exception;
136 }
137 }
138
139
140 private synchronized void startupWorker()
141 {
142 if( worker == null )
143 {
144 worker = new Worker();
145
146 worker.start();
147 }
148 }
149
150
151 public void unbind( SocketAddress address )
152 {
153 if( address == null )
154 {
155 throw new NullPointerException( "address" );
156 }
157
158 CancellationRequest request = new CancellationRequest( address );
159 synchronized( this )
160 {
161 synchronized( cancelQueue )
162 {
163 cancelQueue.push( request );
164 }
165 startupWorker();
166 }
167
168 selector.wakeup();
169
170 synchronized( request )
171 {
172 while( !request.done )
173 {
174 try
175 {
176 request.wait();
177 }
178 catch( InterruptedException e )
179 {
180 }
181 }
182 }
183
184 if( request.exception != null )
185 {
186 request.exception.fillInStackTrace();
187
188 throw request.exception;
189 }
190 }
191
192 /***
193 * Returns the default backlog value which is used when user binds.
194 */
195 public int getBacklog()
196 {
197 return backlog;
198 }
199
200 /***
201 * Sets the default backlog value which is used when user binds.
202 */
203 public void setBacklog( int defaultBacklog )
204 {
205 if( defaultBacklog <= 0 )
206 {
207 throw new IllegalArgumentException( "defaultBacklog: " + defaultBacklog );
208 }
209 this.backlog = defaultBacklog;
210 }
211
212
213 private class Worker extends Thread
214 {
215 public Worker()
216 {
217 super( "SocketAcceptor-" + id );
218 }
219
220 public void run()
221 {
222 for( ;; )
223 {
224 try
225 {
226 int nKeys = selector.select();
227
228 registerNew();
229 cancelKeys();
230
231 if( nKeys > 0 )
232 {
233 processSessions( selector.selectedKeys() );
234 }
235
236 if( selector.keys().isEmpty() )
237 {
238 synchronized( SocketAcceptor.this )
239 {
240 if( selector.keys().isEmpty() &&
241 registerQueue.isEmpty() &&
242 cancelQueue.isEmpty() )
243 {
244 worker = null;
245
246 break;
247 }
248 }
249 }
250 }
251 catch( IOException e )
252 {
253 exceptionMonitor.exceptionCaught( SocketAcceptor.this, e );
254
255 try
256 {
257 Thread.sleep( 1000 );
258 }
259 catch( InterruptedException e1 )
260 {
261 }
262 }
263 }
264 }
265
266 private void processSessions( Set keys ) throws IOException
267 {
268 Iterator it = keys.iterator();
269 while( it.hasNext() )
270 {
271 SelectionKey key = ( SelectionKey ) it.next();
272
273 it.remove();
274
275 if( !key.isAcceptable() )
276 {
277 continue;
278 }
279
280 ServerSocketChannel ssc = ( ServerSocketChannel ) key.channel();
281
282 SocketChannel ch = ssc.accept();
283
284 if( ch == null )
285 {
286 continue;
287 }
288
289 boolean success = false;
290 try
291 {
292 RegistrationRequest req = ( RegistrationRequest ) key.attachment();
293 SocketSession session = new SocketSession( filters, ch, req.handler );
294 req.handler.sessionCreated( session );
295 SocketIoProcessor.getInstance().addSession( session );
296 success = true;
297 }
298 catch( Throwable t )
299 {
300 exceptionMonitor.exceptionCaught( SocketAcceptor.this, t );
301 }
302 finally
303 {
304 if( !success )
305 {
306 ch.close();
307 }
308 }
309 }
310 }
311 }
312
313
314 private void registerNew()
315 {
316 if( registerQueue.isEmpty() )
317 {
318 return;
319 }
320
321 for( ;; )
322 {
323 RegistrationRequest req;
324
325 synchronized( registerQueue )
326 {
327 req = ( RegistrationRequest ) registerQueue.pop();
328 }
329
330 if( req == null )
331 {
332 break;
333 }
334
335 ServerSocketChannel ssc = null;
336
337 try
338 {
339 ssc = ServerSocketChannel.open();
340 ssc.configureBlocking( false );
341 ssc.socket().bind( req.address, req.backlog );
342 ssc.register( selector, SelectionKey.OP_ACCEPT, req );
343
344 channels.put( req.address, ssc );
345 }
346 catch( IOException e )
347 {
348 req.exception = e;
349 }
350 finally
351 {
352 synchronized( req )
353 {
354 req.done = true;
355
356 req.notify();
357 }
358
359 if( ssc != null && req.exception != null )
360 {
361 try
362 {
363 ssc.close();
364 }
365 catch( IOException e )
366 {
367 exceptionMonitor.exceptionCaught( this, e );
368 }
369 }
370 }
371 }
372 }
373
374
375 private void cancelKeys()
376 {
377 if( cancelQueue.isEmpty() )
378 {
379 return;
380 }
381
382 for( ;; )
383 {
384 CancellationRequest request;
385
386 synchronized( cancelQueue )
387 {
388 request = ( CancellationRequest ) cancelQueue.pop();
389 }
390
391 if( request == null )
392 {
393 break;
394 }
395
396 ServerSocketChannel ssc = ( ServerSocketChannel ) channels.remove( request.address );
397
398
399 try
400 {
401 if( ssc == null )
402 {
403 request.exception = new IllegalArgumentException( "Address not bound: " + request.address );
404 }
405 else
406 {
407 SelectionKey key = ssc.keyFor( selector );
408
409 key.cancel();
410
411 selector.wakeup();
412
413 ssc.close();
414 }
415 }
416 catch( IOException e )
417 {
418 exceptionMonitor.exceptionCaught( this, e );
419 }
420 finally
421 {
422 synchronized( request )
423 {
424 request.done = true;
425
426 request.notify();
427 }
428 }
429 }
430 }
431
432 public IoFilterChain getFilterChain()
433 {
434 return filters;
435 }
436
437 private static class RegistrationRequest
438 {
439 private final SocketAddress address;
440
441 private final int backlog;
442
443 private final IoHandler handler;
444
445 private IOException exception;
446
447 private boolean done;
448
449 private RegistrationRequest( SocketAddress address, int backlog, IoHandler handler )
450 {
451 this.address = address;
452 this.backlog = backlog;
453 this.handler = handler;
454 }
455 }
456
457
458 private static class CancellationRequest
459 {
460 private final SocketAddress address;
461
462 private boolean done;
463
464 private RuntimeException exception;
465
466 private CancellationRequest( SocketAddress address )
467 {
468 this.address = address;
469 }
470 }
471 }