View Javadoc

1   /*
2    *   @(#) $Id: SocketAcceptor.java 165586 2005-05-02 06:27:27Z trustin $
3    *
4    *   Copyright 2004 The Apache Software Foundation
5    *
6    *   Licensed under the Apache License, Version 2.0 (the "License");
7    *   you may not use this file except in compliance with the License.
8    *   You may obtain a copy of the License at
9    *
10   *       http://www.apache.org/licenses/LICENSE-2.0
11   *
12   *   Unless required by applicable law or agreed to in writing, software
13   *   distributed under the License is distributed on an "AS IS" BASIS,
14   *   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15   *   See the License for the specific language governing permissions and
16   *   limitations under the License.
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             // close the channel
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(); // wake up again to trigger thread death
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 }