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