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.transport.nio;
    21
    22  import java.io.IOException;
    23  import java.net.InetSocketAddress;
    24  import java.net.SocketAddress;
    25  import java.nio.ByteBuffer;
    26  import java.nio.channels.SelectionKey;
    27  import java.nio.channels.ServerSocketChannel;
    28  import java.nio.channels.SocketChannel;
    29
    30  import org.apache.mina.api.IdleStatus;
    31  import org.apache.mina.api.MinaRuntimeException;
    32  import org.apache.mina.service.executor.IoHandlerExecutor;
    33  import org.apache.mina.service.executor.OrderedHandlerExecutor;
    34  import org.apache.mina.service.idlechecker.IdleChecker;
    35  import org.apache.mina.service.idlechecker.IndexedIdleChecker;
    36  import org.apache.mina.transport.tcp.AbstractTcpServer;
    37  import org.apache.mina.transport.tcp.TcpSessionConfig;
    38  import org.apache.mina.util.Assert;
    39  import org.slf4j.Logger;
    40  import org.slf4j.LoggerFactory;
    41
    42  /**
    43   * This class implements a TCP NIO based server.
    44   *
    45   * @author <a href="http://mina.apache.org">Apache MINA Project</a>
    46   */
    47  public class NioTcpServer extends AbstractTcpServer implements SelectorListener {
    48      /** A logger for this class */
    49      static final Logger LOG = LoggerFactory.getLogger(NioTcpServer.class);
    50
    51      /** the bound local address */
    52      private SocketAddress address = null;
    53
    54      private final SelectorLoop acceptSelectorLoop;
    55
    56      private final SelectorLoopPool readWriteSelectorPool;
    57
    58      // the key used for selecting accept event
    59      private SelectionKey acceptKey = null;
    60
    61      // the server socket for accepting clients
    62      private ServerSocketChannel serverChannel = null;
    63
    64      private IdleChecker idleChecker;
    65
    66      /**
    67       * Create a TCP server with new selector pool of default size and a {@link IoHandlerExecutor} of default type (
    68       * {@link OrderedHandlerExecutor})
    69       */
    70      public NioTcpServer() {
    71          this(new NioSelectorLoop("accept", 0), new FixedSelectorLoopPool("Server", Runtime.getRuntime()
    72                  .availableProcessors() + 1), null);
    73      }
    74
    75      /**
    76       * Create a TCP server with new selector pool of default size and a {@link IoHandlerExecutor} of default type (
    77       * {@link OrderedHandlerExecutor})
    78       *
    79       * @param config The specific configuration to use
    80       */
    81      public NioTcpServer(TcpSessionConfig config) {
    82          this(config, new NioSelectorLoop("accept", 0), new FixedSelectorLoopPool("Server", Runtime.getRuntime()
    83                  .availableProcessors() + 1), null);
    84      }
    85
    86      /**
    87       * Create a TCP server with provided selector loops pool. We will use one SelectorLoop get from the pool to manage
    88       * the OP_ACCEPT events. If the pool contains only one SelectorLoop, then all the events will be managed by the same
    89       * Selector.
    90       *
    91       * @param selectorLoopPool the selector loop pool for handling all I/O events (accept, read, write)
    92       * @param ioHandlerExecutor used for executing IoHandler event in another pool of thread (not in the low level I/O
    93       *        one). Use <code>null</code> if you don't want one. Be careful, the IoHandler processing will block the I/O
    94       *        operations.
    95       */
    96      public NioTcpServer(SelectorLoopPool selectorLoopPool, IoHandlerExecutor handlerExecutor) {
    97          this(selectorLoopPool.getSelectorLoop(), selectorLoopPool, handlerExecutor);
    98      }
    99
    100     /**
    101      * Create a TCP server with provided selector loops pool. We will use one SelectorLoop get from the pool to manage
    102      * the OP_ACCEPT events. If the pool contains only one SelectorLoop, then all the events will be managed by the same
    103      * Selector.
    104      *
    105      * @param config The specific configuration to use
    106      * @param selectorLoopPool the selector loop pool for handling all I/O events (accept, read, write)
    107      * @param ioHandlerExecutor used for executing IoHandler event in another pool of thread (not in the low level I/O
    108      *        one). Use <code>null</code> if you don't want one. Be careful, the IoHandler processing will block the I/O
    109      *        operations.
    110      */
    111     public NioTcpServer(TcpSessionConfig config, SelectorLoopPool selectorLoopPool, IoHandlerExecutor handlerExecutor) {
    112         this(config, selectorLoopPool.getSelectorLoop(), selectorLoopPool, handlerExecutor);
    113     }
    114
    115     /**
    116      * Create a TCP server with provided selector loops pool
    117      *
    118      * @param acceptSelectorLoop the selector loop for handling accept events (connection of new session)
    119      * @param readWriteSelectorLoop the pool of selector loop for handling read/write events of connected sessions
    120      * @param ioHandlerExecutor used for executing IoHandler event in another pool of thread (not in the low level I/O
    121      *        one). Use <code>null</code> if you don't want one. Be careful, the IoHandler processing will block the I/O
    122      *        operations.
    123      */
    124     public NioTcpServer(SelectorLoop acceptSelectorLoop, SelectorLoopPool readWriteSelectorLoop,
    125             IoHandlerExecutor handlerExecutor) {
    126         super(handlerExecutor);
    127         this.acceptSelectorLoop = acceptSelectorLoop;
    128         this.readWriteSelectorPool = readWriteSelectorLoop;
    129     }
    130
    131     /**
    132      * Create a TCP server with provided selector loops pool
    133      *
    134      * @param config The specific configuration to use
    135      * @param acceptSelectorLoop the selector loop for handling accept events (connection of new session)
    136      * @param readWriteSelectorLoop the pool of selector loop for handling read/write events of connected sessions
    137      * @param ioHandlerExecutor used for executing IoHandler event in another pool of thread (not in the low level I/O
    138      *        one). Use <code>null</code> if you don't want one. Be careful, the IoHandler processing will block the I/O
    139      *        operations.
    140      */
    141     public NioTcpServer(TcpSessionConfig config, SelectorLoop acceptSelectorLoop,
    142             SelectorLoopPool readWriteSelectorLoop, IoHandlerExecutor handlerExecutor) {
    143         super(config, handlerExecutor);
    144         this.acceptSelectorLoop = acceptSelectorLoop;
    145         this.readWriteSelectorPool = readWriteSelectorLoop;
    146     }
    147
    148     /**
    149      * Get the inner Server socket for accepting new client connections
    150      *
    151      * @return
    152      */
    153     public synchronized ServerSocketChannel getServerSocketChannel() {
    154         return serverChannel;
    155     }
    156
    157     public synchronized void setServerSocketChannel(final ServerSocketChannel serverChannel) {
    158         this.serverChannel = serverChannel;
    159     }
    160
    161     /**
    162      * {@inheritDoc}
    163      */
    164     @Override
    165     public void bind(final int port) {
    166         bind(new InetSocketAddress(port));
    167     }
    168
    169     /**
    170      * {@inheritDoc}
    171      */
    172     @Override
    173     public synchronized void bind(SocketAddress localAddress) {
    174         Assert.assertNotNull(localAddress, "localAddress");
    175
    176         // check if the address is already bound
    177         if (address != null) {
    178             throw new IllegalStateException("address " + address + " already bound");
    179         }
    180
    181         LOG.info("binding address {}", localAddress);
    182         address = localAddress;
    183
    184         try {
    185             serverChannel = ServerSocketChannel.open();
    186             serverChannel.socket().setReuseAddress(isReuseAddress());
    187             serverChannel.socket().bind(address);
    188             serverChannel.configureBlocking(false);
    189         } catch (IOException e) {
    190             throw new MinaRuntimeException("can't bind address" + address, e);
    191         }
    192
    193         acceptSelectorLoop.register(true, false, false, false, this, serverChannel, null);
    194
    195         idleChecker = new IndexedIdleChecker();
    196         idleChecker.start();
    197
    198         // it's the first address bound, let's fire the event
    199         fireServiceActivated();
    200     }
    201
    202     /**
    203      * {@inheritDoc}
    204      */
    205     @Override
    206     public SocketAddress getBoundAddress() {
    207         return address;
    208     }
    209
    210     /**
    211      * {@inheritDoc}
    212      */
    213     @Override
    214     public synchronized void unbind() {
    215         LOG.info("unbinding {}", address);
    216         if (this.address == null) {
    217             throw new IllegalStateException("server not bound");
    218         }
    219         try {
    220             serverChannel.socket().close();
    221             serverChannel.close();
    222         } catch (IOException e) {
    223             throw new MinaRuntimeException("can't unbind server", e);
    224         }
    225
    226         acceptSelectorLoop.unregister(this, serverChannel);
    227
    228         this.address = null;
    229         this.fireServiceInactivated();
    230
    231         // will stop the acceptor processor if we are the last service
    232         idleChecker.destroy();
    233     }
    234
    235     /**
    236      * @return the acceptKey
    237      */
    238     public SelectionKey getAcceptKey() {
    239         return acceptKey;
    240     }
    241
    242     /**
    243      * @param acceptKey the acceptKey to set
    244      */
    245     public void setAcceptKey(final SelectionKey acceptKey) {
    246         this.acceptKey = acceptKey;
    247     }
    248
    249     /**
    250      * {@inheritDoc}
    251      */
    252     @Override
    253     public void ready(final boolean accept, boolean connect, final boolean read, final ByteBuffer readBuffer,
    254             final boolean write) {
    255         if (accept) {
    256             LOG.debug("acceptable new client");
    257
    258             // accepted connection
    259             try {
    260                 LOG.debug("new client accepted");
    261                 createSession(getServerSocketChannel().accept());
    262
    263             } catch (final IOException e) {
    264                 LOG.error("error while accepting new client", e);
    265             }
    266         }
    267
    268         if (read || write) {
    269             throw new IllegalStateException("should not receive read or write events");
    270         }
    271     }
    272
    273     private synchronized void createSession(SocketChannel clientSocket) throws IOException {
    274         LOG.debug("create session");
    275         SocketChannel socketChannel = clientSocket;
    276         TcpSessionConfig config = getSessionConfig();
    277         SelectorLoop readWriteSelectorLoop = readWriteSelectorPool.getSelectorLoop();
    278         final NioTcpSession session = new NioTcpSession(this, socketChannel, readWriteSelectorLoop, idleChecker);
    279
    280         socketChannel.configureBlocking(false);
    281
    282         // apply idle configuration
    283         session.getConfig().setIdleTimeInMillis(IdleStatus.READ_IDLE, config.getIdleTimeInMillis(IdleStatus.READ_IDLE));
    284         session.getConfig().setIdleTimeInMillis(IdleStatus.WRITE_IDLE,
    285                 config.getIdleTimeInMillis(IdleStatus.WRITE_IDLE));
    286
    287         // apply the default service socket configuration
    288         Boolean keepAlive = config.isKeepAlive();
    289
    290         if (keepAlive != null) {
    291             session.getConfig().setKeepAlive(keepAlive);
    292         }
    293
    294         Boolean oobInline = config.isOobInline();
    295
    296         if (oobInline != null) {
    297             session.getConfig().setOobInline(oobInline);
    298         }
    299
    300         Boolean reuseAddress = config.isReuseAddress();
    301
    302         if (reuseAddress != null) {
    303             session.getConfig().setReuseAddress(reuseAddress);
    304         }
    305
    306         Boolean tcpNoDelay = config.isTcpNoDelay();
    307
    308         if (tcpNoDelay != null) {
    309             session.getConfig().setTcpNoDelay(tcpNoDelay);
    310         }
    311
    312         Integer receiveBufferSize = config.getReadBufferSize();
    313
    314         if (receiveBufferSize != null) {
    315             session.getConfig().setReadBufferSize(receiveBufferSize);
    316         }
    317
    318         Integer sendBufferSize = config.getSendBufferSize();
    319
    320         if (sendBufferSize != null) {
    321             session.getConfig().setSendBufferSize(sendBufferSize);
    322         }
    323
    324         Integer trafficClass = config.getTrafficClass();
    325
    326         if (trafficClass != null) {
    327             session.getConfig().setTrafficClass(trafficClass);
    328         }
    329
    330         Integer soLinger = config.getSoLinger();
    331
    332         if (soLinger != null) {
    333             session.getConfig().setSoLinger(soLinger);
    334         }
    335
    336         // Set the secured flag if the service is to be used over SSL/TLS
    337         if (config.isSecured()) {
    338             session.initSecure(config.getSslContext());
    339         }
    340
    341         // add the session to the queue for being added to the selector
    342         readWriteSelectorLoop.register(false, false, true, false, session, socketChannel, new RegistrationCallback() {
    343
    344             @Override
    345             public void done(SelectionKey selectionKey) {
    346                 session.setSelectionKey(selectionKey);
    347                 session.setConnected();
    348             }
    349         });
    350
    351         idleChecker.sessionRead(session, System.currentTimeMillis());
    352         idleChecker.sessionWritten(session, System.currentTimeMillis());
    353     }
    354
    355 }