View Javadoc

1   /*
2    *  Licensed to the Apache Software Foundation (ASF) under one
3    *  or more contributor license agreements.  See the NOTICE file
4    *  distributed with this work for additional information
5    *  regarding copyright ownership.  The ASF licenses this file
6    *  to you under the Apache License, Version 2.0 (the
7    *  "License"); you may not use this file except in compliance
8    *  with the License.  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,
13   *  software distributed under the License is distributed on an
14   *  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15   *  KIND, either express or implied.  See the License for the
16   *  specific language governing permissions and limitations
17   *  under the License.
18   *
19   */
20  package org.apache.mina.core.polling;
21  
22  import java.net.SocketAddress;
23  import java.util.Collections;
24  import java.util.HashMap;
25  import java.util.HashSet;
26  import java.util.Iterator;
27  import java.util.List;
28  import java.util.Map;
29  import java.util.Queue;
30  import java.util.Set;
31  import java.util.concurrent.ConcurrentLinkedQueue;
32  import java.util.concurrent.Executor;
33  import java.util.concurrent.Executors;
34  
35  import org.apache.mina.core.RuntimeIoException;
36  import org.apache.mina.core.filterchain.IoFilter;
37  import org.apache.mina.core.future.IoFuture;
38  import org.apache.mina.core.service.AbstractIoAcceptor;
39  import org.apache.mina.core.service.IoAcceptor;
40  import org.apache.mina.core.service.IoHandler;
41  import org.apache.mina.core.service.IoProcessor;
42  import org.apache.mina.core.service.SimpleIoProcessorPool;
43  import org.apache.mina.core.session.AbstractIoSession;
44  import org.apache.mina.core.session.IoSession;
45  import org.apache.mina.core.session.IoSessionConfig;
46  import org.apache.mina.transport.socket.nio.NioSocketAcceptor;
47  import org.apache.mina.util.ExceptionMonitor;
48  
49  /**
50   * A base class for implementing transport using a polling strategy. The
51   * underlying sockets will be checked in an active loop and woke up when an
52   * socket needed to be processed. This class handle the logic behind binding,
53   * accepting and disposing the server sockets. An {@link Executor} will be used
54   * for running client accepting and an {@link AbstractPollingIoProcessor} will
55   * be used for processing client I/O operations like reading, writing and
56   * closing.
57   * 
58   * All the low level methods for binding, accepting, closing need to be provided
59   * by the subclassing implementation.
60   * 
61   * @see NioSocketAcceptor for a example of implementation
62   * 
63   * @author The Apache MINA Project (dev@mina.apache.org)
64   * @version $Rev: 718251 $, $Date: 2008-06-26 17:58:30 +0200 (jeu, 26 jun 2008)
65   *          $
66   */
67  public abstract class AbstractPollingIoAcceptor<T extends AbstractIoSession, H>
68          extends AbstractIoAcceptor {
69  
70      private final IoProcessor<T> processor;
71  
72      private final boolean createdProcessor;
73  
74      private final Object lock = new Object();
75  
76      private final Queue<AcceptorOperationFuture> registerQueue = new ConcurrentLinkedQueue<AcceptorOperationFuture>();
77  
78      private final Queue<AcceptorOperationFuture> cancelQueue = new ConcurrentLinkedQueue<AcceptorOperationFuture>();
79  
80      private final Map<SocketAddress, H> boundHandles = Collections
81              .synchronizedMap(new HashMap<SocketAddress, H>());
82  
83      private final ServiceOperationFuture disposalFuture = new ServiceOperationFuture();
84  
85      /** A flag set when the acceptor has been created and initialized */
86      private volatile boolean selectable;
87  
88      /** The thread responsible of accepting incoming requests */ 
89      private Acceptor acceptor;
90  
91      /**
92       * Constructor for {@link AbstractPollingIoAcceptor}. You need to provide a default
93       * session configuration, a class of {@link IoProcessor} which will be instantiated in a
94       * {@link SimpleIoProcessorPool} for better scaling in multiprocessor systems. The default
95       * pool size will be used.
96       * 
97       * @see SimpleIoProcessorPool
98       * 
99       * @param sessionConfig
100      *            the default configuration for the managed {@link IoSession}
101      * @param processorClass a {@link Class} of {@link IoProcessor} for the associated {@link IoSession}
102      *            type.
103      */
104     protected AbstractPollingIoAcceptor(IoSessionConfig sessionConfig,
105             Class<? extends IoProcessor<T>> processorClass) {
106         this(sessionConfig, null, new SimpleIoProcessorPool<T>(processorClass),
107                 true);
108     }
109 
110     /**
111      * Constructor for {@link AbstractPollingIoAcceptor}. You need to provide a default
112      * session configuration, a class of {@link IoProcessor} which will be instantiated in a
113      * {@link SimpleIoProcessorPool} for using multiple thread for better scaling in multiprocessor
114      * systems.
115      * 
116      * @see SimpleIoProcessorPool
117      * 
118      * @param sessionConfig
119      *            the default configuration for the managed {@link IoSession}
120      * @param processorClass a {@link Class} of {@link IoProcessor} for the associated {@link IoSession}
121      *            type.
122      * @param processorCount the amount of processor to instantiate for the pool
123      */
124     protected AbstractPollingIoAcceptor(IoSessionConfig sessionConfig,
125             Class<? extends IoProcessor<T>> processorClass, int processorCount) {
126         this(sessionConfig, null, new SimpleIoProcessorPool<T>(processorClass,
127                 processorCount), true);
128     }
129 
130     /**
131      * Constructor for {@link AbstractPollingIoAcceptor}. You need to provide a default
132      * session configuration, a default {@link Executor} will be created using
133      * {@link Executors#newCachedThreadPool()}.
134      * 
135      * {@see AbstractIoService#AbstractIoService(IoSessionConfig, Executor)}
136      * 
137      * @param sessionConfig
138      *            the default configuration for the managed {@link IoSession}
139      * @param processor the {@link IoProcessor} for processing the {@link IoSession} of this transport, triggering 
140      *            events to the bound {@link IoHandler} and processing the chains of {@link IoFilter} 
141      */
142     protected AbstractPollingIoAcceptor(IoSessionConfig sessionConfig,
143             IoProcessor<T> processor) {
144         this(sessionConfig, null, processor, false);
145     }
146 
147     /**
148      * Constructor for {@link AbstractPollingIoAcceptor}. You need to provide a default
149      * session configuration and an {@link Executor} for handling I/O events. If a
150      * null {@link Executor} is provided, a default one will be created using
151      * {@link Executors#newCachedThreadPool()}.
152      * 
153      * {@see AbstractIoService#AbstractIoService(IoSessionConfig, Executor)}
154      * 
155      * @param sessionConfig
156      *            the default configuration for the managed {@link IoSession}
157      * @param executor
158      *            the {@link Executor} used for handling asynchronous execution of I/O
159      *            events. Can be <code>null</code>.
160      * @param processor the {@link IoProcessor} for processing the {@link IoSession} of this transport, triggering 
161      *            events to the bound {@link IoHandler} and processing the chains of {@link IoFilter} 
162      */
163     protected AbstractPollingIoAcceptor(IoSessionConfig sessionConfig,
164             Executor executor, IoProcessor<T> processor) {
165         this(sessionConfig, executor, processor, false);
166     }
167 
168     /**
169      * Constructor for {@link AbstractPollingIoAcceptor}. You need to provide a default
170      * session configuration and an {@link Executor} for handling I/O events. If a
171      * null {@link Executor} is provided, a default one will be created using
172      * {@link Executors#newCachedThreadPool()}.
173      * 
174      * {@see AbstractIoService#AbstractIoService(IoSessionConfig, Executor)}
175      * 
176      * @param sessionConfig
177      *            the default configuration for the managed {@link IoSession}
178      * @param executor
179      *            the {@link Executor} used for handling asynchronous execution of I/O
180      *            events. Can be <code>null</code>.
181      * @param processor the {@link IoProcessor} for processing the {@link IoSession} of 
182      * this transport, triggering events to the bound {@link IoHandler} and processing 
183      * the chains of {@link IoFilter}
184      * @param createdProcessor tagging the processor as automatically created, so it 
185      * will be automatically disposed 
186      */
187     private AbstractPollingIoAcceptor(IoSessionConfig sessionConfig,
188             Executor executor, IoProcessor<T> processor,
189             boolean createdProcessor) {
190         super(sessionConfig, executor);
191 
192         if (processor == null) {
193             throw new NullPointerException("processor");
194         }
195 
196         this.processor = processor;
197         this.createdProcessor = createdProcessor;
198 
199         try {
200             // Initialize the selector
201             init();
202             
203             // The selector is now ready, we can switch the
204             // flag to true so that incoming connection can be accepted
205             selectable = true;
206         } catch (RuntimeException e) {
207             throw e;
208         } catch (Exception e) {
209             throw new RuntimeIoException("Failed to initialize.", e);
210         } finally {
211             if (!selectable) {
212                 try {
213                     destroy();
214                 } catch (Exception e) {
215                     ExceptionMonitor.getInstance().exceptionCaught(e);
216                 }
217             }
218         }
219     }
220 
221     /**
222      * Initialize the polling system, will be called at construction time.
223      * @throws Exception any exception thrown by the underlying system calls  
224      */
225     protected abstract void init() throws Exception;
226 
227     /**
228      * Destroy the polling system, will be called when this {@link IoAcceptor}
229      * implementation will be disposed.  
230      * @throws Exception any exception thrown by the underlying systems calls
231      */
232     protected abstract void destroy() throws Exception;
233 
234     /**
235      * Check for acceptable connections, interrupt when at least a server is ready for accepting.
236      * All the ready server socket descriptors need to be returned by {@link #selectedHandles()}
237      * @return The number of sockets having got incoming client
238      * @throws Exception any exception thrown by the underlying systems calls
239      */
240     protected abstract int select() throws Exception;
241 
242     /**
243      * Interrupt the {@link #select()} method. Used when the poll set need to be modified.
244      */
245     protected abstract void wakeup();
246 
247     /**
248      * {@link Iterator} for the set of server sockets found with acceptable incoming connections
249      *  during the last {@link #select()} call.
250      * @return the list of server handles ready
251      */
252     protected abstract Iterator<H> selectedHandles();
253 
254     /**
255      * Open a server socket for a given local address.
256      * @param localAddress the associated local address
257      * @return the opened server socket
258      * @throws Exception any exception thrown by the underlying systems calls
259      */
260     protected abstract H open(SocketAddress localAddress) throws Exception;
261 
262     /**
263      * Get the local address associated with a given server socket
264      * @param handle the server socket
265      * @return the local {@link SocketAddress} associated with this handle
266      * @throws Exception any exception thrown by the underlying systems calls
267      */
268     protected abstract SocketAddress localAddress(H handle) throws Exception;
269 
270     /**
271      * Accept a client connection for a server socket and return a new {@link IoSession}
272      * associated with the given {@link IoProcessor}
273      * @param processor the {@link IoProcessor} to associate with the {@link IoSession}  
274      * @param handle the server handle
275      * @return the created {@link IoSession}
276      * @throws Exception any exception thrown by the underlying systems calls
277      */
278     protected abstract T accept(IoProcessor<T> processor, H handle)
279             throws Exception;
280 
281     /**
282      * Close a server socket.
283      * @param handle the server socket
284      * @throws Exception any exception thrown by the underlying systems calls
285      */
286     protected abstract void close(H handle) throws Exception;
287 
288     /**
289      * {@inheritDoc}
290      */
291     @Override
292     protected IoFuture dispose0() throws Exception {
293         unbind();
294         if (!disposalFuture.isDone()) {
295             startupAcceptor();
296             wakeup();
297         }
298         return disposalFuture;
299     }
300 
301     /**
302      * {@inheritDoc}
303      */
304     @Override
305     protected final Set<SocketAddress> bind0(
306             List<? extends SocketAddress> localAddresses) throws Exception {
307         AcceptorOperationFuture request = new AcceptorOperationFuture(
308                 localAddresses);
309 
310         // adds the Registration request to the queue for the Workers
311         // to handle
312         registerQueue.add(request);
313 
314         // creates the Acceptor instance and has the local
315         // executor kick it off.
316         startupAcceptor();
317         wakeup();
318         request.awaitUninterruptibly();
319 
320         if (request.getException() != null) {
321             throw request.getException();
322         }
323 
324         // Update the local addresses.
325         // setLocalAddresses() shouldn't be called from the worker thread
326         // because of deadlock.
327         Set<SocketAddress> newLocalAddresses = new HashSet<SocketAddress>();
328         for (H handle : boundHandles.values()) {
329             newLocalAddresses.add(localAddress(handle));
330         }
331 
332         return newLocalAddresses;
333     }
334 
335     /**
336      * This method is called by the doBind() and doUnbind()
337      * methods.  If the acceptor is null, the acceptor object will
338      * be created and kicked off by the executor.  If the acceptor
339      * object is null, probably already created and this class
340      * is now working, then nothing will happen and the method
341      * will just return.
342      */
343     private void startupAcceptor() {
344         // If the acceptor is not ready, clear the queues
345         // TODO : they should already be clean : do we have to do that ?
346         if (!selectable) {
347             registerQueue.clear();
348             cancelQueue.clear();
349         }
350 
351         // start the acceptor if not already started
352         synchronized (lock) {
353             if (acceptor == null) {
354                 acceptor = new Acceptor();
355                 executeWorker(acceptor);
356             }
357         }
358     }
359 
360     /**
361      * {@inheritDoc}
362      */
363     @Override
364     protected final void unbind0(List<? extends SocketAddress> localAddresses)
365             throws Exception {
366         AcceptorOperationFuture future = new AcceptorOperationFuture(
367                 localAddresses);
368 
369         cancelQueue.add(future);
370         startupAcceptor();
371         wakeup();
372 
373         future.awaitUninterruptibly();
374         if (future.getException() != null) {
375             throw future.getException();
376         }
377     }
378 
379     /**
380      * This class is called by the startupAcceptor() method and is
381      * placed into a NamePreservingRunnable class.
382      * It's a thread accepting incoming connections from clients.
383      * The loop is stopped when all the bound handlers are unbound.
384      */
385     private class Acceptor implements Runnable {
386         public void run() {
387             int nHandles = 0;
388 
389             while (selectable) {
390                 try {
391                     // Detect if we have some keys ready to be processed
392                     // The select() will be woke up if some new connection
393                     // have occurred, or if the selector has been explicitly
394                     // woke up
395                     int selected = select();
396 
397                     // this actually sets the selector to OP_ACCEPT,
398                     // and binds to the port in which this class will
399                     // listen on
400                     nHandles += registerHandles();
401 
402                     if (selected > 0) {
403                         // We have some connection request, let's process 
404                         // them here. 
405                         processHandles(selectedHandles());
406                     }
407 
408                     // check to see if any cancellation request has been made.
409                     nHandles -= unregisterHandles();
410 
411                     // Now, if the number of registred handles is 0, we can
412                     // quit the loop: we don't have any socket listening
413                     // for incoming connection.
414                     if (nHandles == 0) {
415                         synchronized (lock) {
416                             if (registerQueue.isEmpty()
417                                     && cancelQueue.isEmpty()) {
418                                 acceptor = null;
419                                 break;
420                             }
421                         }
422                     }
423                 } catch (Throwable e) {
424                     ExceptionMonitor.getInstance().exceptionCaught(e);
425 
426                     try {
427                         Thread.sleep(1000);
428                     } catch (InterruptedException e1) {
429                         ExceptionMonitor.getInstance().exceptionCaught(e1);
430                     }
431                 }
432             }
433 
434             // Cleanup all the processors, and shutdown the acceptor.
435             if (selectable && isDisposing()) {
436                 selectable = false;
437                 try {
438                     if (createdProcessor) {
439                         processor.dispose();
440                     }
441                 } finally {
442                     try {
443                         synchronized (disposalLock) {
444                             if (isDisposing()) {
445                                 destroy();
446                             }
447                         }
448                     } catch (Exception e) {
449                         ExceptionMonitor.getInstance().exceptionCaught(e);
450                     } finally {
451                         disposalFuture.setDone();
452                     }
453                 }
454             }
455         }
456 
457         /**
458          * This method will process new sessions for the Worker class.  All
459          * keys that have had their status updates as per the Selector.selectedKeys()
460          * method will be processed here.  Only keys that are ready to accept
461          * connections are handled here.
462          * <p/>
463          * Session objects are created by making new instances of SocketSessionImpl
464          * and passing the session object to the SocketIoProcessor class.
465          */
466         @SuppressWarnings("unchecked")
467         private void processHandles(Iterator<H> handles) throws Exception {
468             while (handles.hasNext()) {
469                 H handle = handles.next();
470                 handles.remove();
471 
472                 T session = accept(processor, handle);
473                 if (session == null) {
474                     break;
475                 }
476 
477                 finishSessionInitialization(session, null, null);
478 
479                 // add the session to the SocketIoProcessor
480                 session.getProcessor().add(session);
481             }
482         }
483     }
484 
485     /**
486      * Sets up the socket communications.  Sets items such as:
487      * <p/>
488      * Blocking
489      * Reuse address
490      * Receive buffer size
491      * Bind to listen port
492      * Registers OP_ACCEPT for selector
493      */
494     private int registerHandles() {
495         for (;;) {
496             // The register queue contains the list of services to manage
497             // in this acceptor.
498             AcceptorOperationFuture future = registerQueue.poll();
499             
500             if (future == null) {
501                 return 0;
502             }
503 
504             // We create a temporary map to store the bound handles,
505             // as we may have to remove them all if there is an exception
506             // during the sockets opening.
507             Map<SocketAddress, H> newHandles = new HashMap<SocketAddress, H>();
508             List<SocketAddress> localAddresses = future.getLocalAddresses();
509 
510             try {
511                 // Process all the addresses
512                 for (SocketAddress a : localAddresses) {
513                     H handle = open(a);
514                     newHandles.put(localAddress(handle), handle);
515                 }
516 
517                 // Everything went ok, we can now update the map storing
518                 // all the bound sockets.
519                 boundHandles.putAll(newHandles);
520 
521                 // and notify.
522                 future.setDone();
523                 return newHandles.size();
524             } catch (Exception e) {
525                 // We store the exception in the future
526                 future.setException(e);
527             } finally {
528                 // Roll back if failed to bind all addresses.
529                 if (future.getException() != null) {
530                     for (H handle : newHandles.values()) {
531                         try {
532                             close(handle);
533                         } catch (Exception e) {
534                             ExceptionMonitor.getInstance().exceptionCaught(e);
535                         }
536                     }
537                     
538                     // TODO : add some comment : what is the wakeup() waking up ?
539                     wakeup();
540                 }
541             }
542         }
543     }
544 
545     /**
546      * This method just checks to see if anything has been placed into the
547      * cancellation queue.  The only thing that should be in the cancelQueue
548      * is CancellationRequest objects and the only place this happens is in
549      * the doUnbind() method.
550      */
551     private int unregisterHandles() {
552         int cancelledHandles = 0;
553         for (;;) {
554             AcceptorOperationFuture future = cancelQueue.poll();
555             if (future == null) {
556                 break;
557             }
558 
559             // close the channels
560             for (SocketAddress a : future.getLocalAddresses()) {
561                 H handle = boundHandles.remove(a);
562                 if (handle == null) {
563                     continue;
564                 }
565 
566                 try {
567                     close(handle);
568                     wakeup(); // wake up again to trigger thread death
569                 } catch (Throwable e) {
570                     ExceptionMonitor.getInstance().exceptionCaught(e);
571                 } finally {
572                     cancelledHandles++;
573                 }
574             }
575 
576             future.setDone();
577         }
578 
579         return cancelledHandles;
580     }
581 
582     /**
583      * {@inheritDoc}
584      */
585     public final IoSession newSession(SocketAddress remoteAddress,
586             SocketAddress localAddress) {
587         throw new UnsupportedOperationException();
588     }
589 }