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.Socket;
    25  import java.nio.ByteBuffer;
    26  import java.nio.channels.SelectionKey;
    27  import java.nio.channels.SocketChannel;
    28
    29  import org.apache.mina.api.IoService;
    30  import org.apache.mina.service.idlechecker.IdleChecker;
    31  import org.apache.mina.session.WriteRequest;
    32  import org.apache.mina.transport.ConnectFuture;
    33  import org.apache.mina.transport.tcp.ProxyTcpSessionConfig;
    34  import org.apache.mina.transport.tcp.TcpSessionConfig;
    35  import org.slf4j.Logger;
    36  import org.slf4j.LoggerFactory;
    37
    38  /**
    39   * A NIO based TCP session, should be used by {@link NioTcpServer} and {@link NioTcpClient}. A TCP session is a
    40   * connection between a our server/client and the remote end-point.
    41   *
    42   * @author <a href="http://mina.apache.org">Apache MINA Project</a>
    43   *
    44   */
    45  public class NioTcpSession extends AbstractNioSession implements SelectorListener {
    46
    47      private static final Logger LOG = LoggerFactory.getLogger(NioTcpSession.class);
    48
    49      /** the selector loop in charge of generating read/write events for this session */
    50      private final SelectorLoop selectorLoop;
    51
    52      /** the socket configuration */
    53      private final TcpSessionConfig configuration;
    54
    55      /** the future representing this session connection operation (client only) */
    56      private ConnectFuture connectFuture;
    57
    58      /** The associated selectionKey */
    59      private SelectionKey selectionKey;
    60
    61      /** The Direct Buffer used to send data */
    62      private ByteBuffer sendBuffer;
    63
    64      /** The size of the buffer configured in the socket to send data */
    65      private int sendBufferSize;
    66
    67      /* No qualifier */NioTcpSession(final IoService service, final SocketChannel channel,
    68              final SelectorLoop selectorLoop, final IdleChecker idleChecker) {
    69          super(service, channel, idleChecker);
    70          this.selectorLoop = selectorLoop;
    71          this.configuration = new ProxyTcpSessionConfig(channel.socket());
    72          sendBufferSize = configuration.getSendBufferSize();
    73          sendBuffer = ByteBuffer.allocateDirect(sendBufferSize);
    74      }
    75
    76      void setConnectFuture(ConnectFuture connectFuture) {
    77          this.connectFuture = connectFuture;
    78      }
    79
    80      /**
    81       * Get the underlying {@link SocketChannel} of this session
    82       *
    83       * @return the socket channel used by this session
    84       */
    85      SocketChannel getSocketChannel() {
    86          return (SocketChannel) channel;
    87      }
    88
    89      /**
    90       * {@inheritDoc}
    91       */
    92      @Override
    93      public InetSocketAddress getRemoteAddress() {
    94          if (channel == null) {
    95              return null;
    96          }
    97          final Socket socket = ((SocketChannel) channel).socket();
    98
    99          if (socket == null) {
    100             return null;
    101         }
    102
    103         return (InetSocketAddress) socket.getRemoteSocketAddress();
    104     }
    105
    106     /**
    107      * {@inheritDoc}
    108      */
    109     @Override
    110     public InetSocketAddress getLocalAddress() {
    111         if (channel == null) {
    112             return null;
    113         }
    114
    115         final Socket socket = ((SocketChannel) channel).socket();
    116
    117         if (socket == null) {
    118             return null;
    119         }
    120
    121         return (InetSocketAddress) socket.getLocalSocketAddress();
    122     }
    123
    124     /**
    125      * {@inheritDoc}
    126      */
    127     @Override
    128     public void suspendRead() {
    129         // TODO
    130         throw new RuntimeException("Not implemented");
    131     }
    132
    133     /**
    134      * {@inheritDoc}
    135      */
    136     @Override
    137     public void suspendWrite() {
    138         // TODO
    139         throw new RuntimeException("Not implemented");
    140     }
    141
    142     /**
    143      * {@inheritDoc}
    144      */
    145     @Override
    146     protected int writeDirect(Object message) {
    147         try {
    148             // Check that we can write into the channel
    149             if (!isRegisteredForWrite()) {
    150                 // We don't have pending writes
    151                 return ((SocketChannel) channel).write((ByteBuffer) message);
    152             } else {
    153                 return -1;
    154             }
    155         } catch (final IOException e) {
    156             LOG.error("Exception while reading : ", e);
    157             processException(e);
    158
    159             return -1;
    160         }
    161     }
    162
    163     /**
    164      * {@inheritDoc}
    165      */
    166     @Override
    167     protected ByteBuffer convertToDirectBuffer(WriteRequest writeRequest, boolean createNew) {
    168         ByteBuffer message = (ByteBuffer) writeRequest.getMessage();
    169
    170         if (!message.isDirect()) {
    171             int remaining = message.remaining();
    172
    173             if ((remaining > sendBufferSize) || createNew) {
    174                 ByteBuffer directBuffer = ByteBuffer.allocateDirect(remaining);
    175                 directBuffer.put(message);
    176                 directBuffer.flip();
    177                 writeRequest.setMessage(directBuffer);
    178
    179                 return directBuffer;
    180             } else {
    181                 sendBuffer.clear();
    182                 sendBuffer.put(message);
    183                 sendBuffer.flip();
    184                 writeRequest.setMessage(sendBuffer);
    185
    186                 return sendBuffer;
    187             }
    188         }
    189
    190         return message;
    191     }
    192
    193     /**
    194      * {@inheritDoc}
    195      */
    196     @Override
    197     public void resumeRead() {
    198         // TODO
    199         throw new RuntimeException("Not implemented");
    200     }
    201
    202     /**
    203      * {@inheritDoc}
    204      */
    205     @Override
    206     public void resumeWrite() {
    207         // TODO
    208         throw new RuntimeException("Not implemented");
    209     }
    210
    211     /**
    212      * {@inheritDoc}
    213      */
    214     @Override
    215     public boolean isReadSuspended() {
    216         // TODO
    217         return false;
    218     }
    219
    220     /**
    221      * {@inheritDoc}
    222      */
    223     @Override
    224     public boolean isWriteSuspended() {
    225         // TODO
    226         throw new RuntimeException("Not implemented");
    227     }
    228
    229     /**
    230      * {@inheritDoc}
    231      */
    232     @Override
    233     public TcpSessionConfig getConfig() {
    234         return configuration;
    235     }
    236
    237     /**
    238      * Set this session status as connected. To be called by the processor selecting/polling this session.
    239      */
    240     void setConnected() {
    241         if (!isCreated()) {
    242             throw new RuntimeException("Trying to open a non created session");
    243         }
    244
    245         state = SessionState.CONNECTED;
    246
    247         if (connectFuture != null) {
    248             connectFuture.complete(this);
    249             // free some memory
    250             connectFuture = null;
    251         }
    252
    253         processSessionOpen();
    254     }
    255
    256     /**
    257      * {@inheritDoc}
    258      */
    259     @Override
    260     protected void channelClose() {
    261         try {
    262             selectorLoop.unregister(this, channel);
    263             channel.close();
    264         } catch (final IOException e) {
    265             LOG.error("Exception while closing the channel : ", e);
    266             processException(e);
    267         }
    268     }
    269
    270     /**
    271      * {@inheritDoc}
    272      */
    273     @Override
    274     public void flushWriteQueue() {
    275         // register for write
    276         selectorLoop.modifyRegistration(false, !isReadSuspended(), true, this, channel, true);
    277     }
    278
    279     /**
    280      * Process a read operation : read the data from the channel and push them to the chain.
    281      *
    282      * @param readBuffer The buffer that will contain the read data
    283      */
    284     private void processRead(final ByteBuffer readBuffer) {
    285         try {
    286             LOG.debug("readable session : {}", this);
    287
    288             // Read everything we can up to the buffer size
    289             final int readCount = ((SocketChannel) channel).read(readBuffer);
    290
    291             LOG.debug("read {} bytes", readCount);
    292
    293             if (readCount < 0) {
    294                 // session closed by the remote peer
    295                 LOG.debug("session closed by the remote peer");
    296                 close(true);
    297             } else if (readCount > 0) {
    298                 // we have read some data
    299                 // limit at the current position & rewind buffer back to start &
    300                 // push to the chain
    301                 readBuffer.flip();
    302
    303                 if (isSecured()) {
    304                     // We are reading data over a SSL/TLS encrypted connection.
    305                     // Redirect the processing to the SslHelper class.
    306                     final SslHelper sslHelper = getAttribute(SSL_HELPER, null);
    307
    308                     if (sslHelper == null) {
    309                         throw new IllegalStateException();
    310                     }
    311
    312                     sslHelper.processRead(this, readBuffer);
    313
    314                     // We don't clear the buffer. It has been done by the sslHelper
    315                 } else {
    316                     // Plain message, not encrypted : go directly to the chain
    317                     processMessageReceived(readBuffer);
    318
    319                     // And now, clear the buffer
    320                     readBuffer.clear();
    321                 }
    322
    323                 // Update the session idle status
    324                 idleChecker.sessionRead(this, System.currentTimeMillis());
    325             }
    326         } catch (final IOException e) {
    327             LOG.error("Exception while reading : ", e);
    328             processException(e);
    329         }
    330     }
    331
    332     /**
    333      * {@inheritDoc}
    334      */
    335     @Override
    336     public void ready(final boolean accept, boolean connect, final boolean read, final ByteBuffer readBuffer,
    337             final boolean write) {
    338         if (LOG.isDebugEnabled()) {
    339             LOG.debug("session {} ready for accept={}, connect={}, read={}, write={}", new Object[] { this, accept,
    340                                     connect, read, write });
    341         }
    342         if (connect) {
    343             try {
    344
    345                 boolean isConnected = ((SocketChannel) channel).finishConnect();
    346
    347                 if (!isConnected) {
    348                     LOG.error("unable to connect session {}", this);
    349                 } else {
    350                     // cancel current registration for connection
    351                     selectionKey.cancel();
    352                     selectionKey = null;
    353
    354                     // Register for reading
    355                     selectorLoop.register(false, false, true, false, this, channel, new RegistrationCallback() {
    356
    357                         @Override
    358                         public void done(SelectionKey selectionKey) {
    359                             setConnected();
    360                         }
    361                     });
    362                 }
    363             } catch (IOException e) {
    364                 LOG.debug("Connection error, we cancel the future", e);
    365                 if (connectFuture != null) {
    366                     connectFuture.error(e);
    367                 }
    368             }
    369         }
    370
    371         if (read) {
    372             processRead(readBuffer);
    373         }
    374
    375         if (write) {
    376             processWrite(selectorLoop);
    377         }
    378         if (accept) {
    379             throw new IllegalStateException("accept event should never occur on NioTcpSession");
    380         }
    381     }
    382
    383     void setSelectionKey(SelectionKey key) {
    384         this.selectionKey = key;
    385     }
    386 }