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.SocketAddress;
    24  import java.nio.ByteBuffer;
    25  import java.nio.channels.DatagramChannel;
    26
    27  import org.apache.mina.api.IoService;
    28  import org.apache.mina.service.idlechecker.IdleChecker;
    29  import org.apache.mina.session.WriteRequest;
    30  import org.apache.mina.transport.udp.UdpSessionConfig;
    31  import org.slf4j.Logger;
    32  import org.slf4j.LoggerFactory;
    33
    34  /**
    35   * A UDP session based on NIO
    36   *
    37   * @author <a href="http://mina.apache.org">Apache MINA Project</a>
    38   */
    39  public class NioUdpSession extends AbstractNioSession implements SelectorListener {
    40
    41      private static final Logger LOG = LoggerFactory.getLogger(NioUdpSession.class);
    42
    43      private static final boolean IS_DEBUG = LOG.isDebugEnabled();
    44
    45      private final SocketAddress localAddress;
    46
    47      private final SocketAddress remoteAddress;
    48
    49      /**
    50       * The selector loop in charge of generating read/write events for this session Used only for UDP client session.
    51       */
    52      private SelectorLoop selectorLoop = null;
    53
    54      /** the socket configuration */
    55      private final UdpSessionConfig configuration;
    56
    57      /**
    58       * For server handled UDP sessions
    59       */
    60      /* No qualifier */NioUdpSession(IoService service, IdleChecker idleChecker, DatagramChannel datagramChannel,
    61              SocketAddress localAddress, SocketAddress remoteAddress) {
    62          super(service, datagramChannel, idleChecker);
    63          this.localAddress = localAddress;
    64          this.remoteAddress = remoteAddress;
    65          this.config = service.getSessionConfig();
    66          this.configuration = (UdpSessionConfig) this.config;
    67      }
    68
    69      /**
    70       * For client handled UDP sessions
    71       */
    72      /* No qualifier */NioUdpSession(IoService service, IdleChecker idleChecker, DatagramChannel datagramChannel,
    73              SocketAddress localAddress, SocketAddress remoteAddress, NioSelectorLoop selectorLoop) {
    74          super(service, datagramChannel, idleChecker);
    75          this.selectorLoop = selectorLoop;
    76          this.localAddress = localAddress;
    77          this.remoteAddress = remoteAddress;
    78          this.config = service.getSessionConfig();
    79          this.configuration = (UdpSessionConfig) this.config;
    80      }
    81
    82      /**
    83       * {@inheritDoc}
    84       */
    85      @Override
    86      protected void channelClose() {
    87          LOG.debug("channelClose");
    88          // No inner socket to close for UDP server, but some for UDP client
    89          if (channel != null) {
    90              try {
    91                  selectorLoop.unregister(this, channel);
    92                  channel.close();
    93              } catch (final IOException e) {
    94                  LOG.error("Exception while closing the channel : ", e);
    95                  processException(e);
    96              }
    97          }
    98          processSessionClosed();
    99      }
    100
    101     /**
    102      * {@inheritDoc}
    103      */
    104     @Override
    105     public void flushWriteQueue() {
    106         // register for write
    107         if (selectorLoop != null) {
    108             selectorLoop.modifyRegistration(false, !isReadSuspended(), true, this, channel, true);
    109         }
    110     }
    111
    112     /**
    113      * {@inheritDoc}
    114      */
    115     @Override
    116     public SocketAddress getRemoteAddress() {
    117         return remoteAddress;
    118     }
    119
    120     /**
    121      * {@inheritDoc}
    122      */
    123     @Override
    124     public SocketAddress getLocalAddress() {
    125         return localAddress;
    126     }
    127
    128     /**
    129      * {@inheritDoc}
    130      */
    131     /*
    132      * @Override public IoFuture<Void> close(boolean immediately) { switch (state) { case CREATED:
    133      * LOG.error("Session {} not opened", this); throw new IllegalStateException("cannot close an not opened session");
    134      * case CONNECTED: case CLOSING: if (immediately) { state = SessionState.CLOSED; } else { // we wait for the write
    135      * queue to be depleted state = SessionState.CLOSING; } break; case CLOSED: LOG.warn("Already closed session {}",
    136      * this); break; default: throw new IllegalStateException("not implemented session state : " + state); } return
    137      * closeFuture; }
    138      */
    139
    140     /**
    141      * {@inheritDoc}
    142      */
    143     @Override
    144     public void suspendRead() {
    145         // TODO
    146         throw new IllegalStateException("not implemented");
    147     }
    148
    149     /**
    150      * {@inheritDoc}
    151      */
    152     @Override
    153     public void suspendWrite() {
    154         // TODO
    155         throw new IllegalStateException("not implemented");
    156     }
    157
    158     /**
    159      * {@inheritDoc}
    160      */
    161     @Override
    162     public void resumeRead() {
    163         // TODO
    164         throw new IllegalStateException("not implemented");
    165     }
    166
    167     /**
    168      * {@inheritDoc}
    169      */
    170     @Override
    171     public void resumeWrite() {
    172         // TODO
    173         throw new IllegalStateException("not implemented");
    174     }
    175
    176     /**
    177      * {@inheritDoc}
    178      */
    179     @Override
    180     public boolean isReadSuspended() {
    181         // TODO
    182         return false;
    183     }
    184
    185     /**
    186      * {@inheritDoc}
    187      */
    188     @Override
    189     public boolean isWriteSuspended() {
    190         // TODO
    191         return false;
    192     }
    193
    194     /**
    195      * {@inheritDoc}
    196      */
    197     @Override
    198     public UdpSessionConfig getConfig() {
    199         return configuration;
    200     }
    201
    202     /**
    203      * Called when the session received a datagram.
    204      *
    205      * @param readBuffer the received datagram
    206      */
    207     void receivedDatagram(ByteBuffer readBuffer) {
    208         processMessageReceived(readBuffer);
    209         idleChecker.sessionRead(this, System.currentTimeMillis());
    210     }
    211
    212     /**
    213      * {@inheritDoc}
    214      */
    215     @Override
    216     protected int writeDirect(Object message) {
    217         try {
    218             // Check that we can write into the channel
    219             if (!isRegisteredForWrite()) {
    220                 // We don't have pending writes
    221                 // First, connect if we aren't already connected
    222                 if (!((DatagramChannel) channel).isConnected()) {
    223                     ((DatagramChannel) channel).connect(remoteAddress);
    224                 }
    225
    226                 // And try to write the data. We will either write them all,
    227                 // or none
    228                 return ((DatagramChannel) channel).write((ByteBuffer) message);
    229             } else {
    230                 LOG.debug("Cannot write");
    231                 return -1;
    232             }
    233         } catch (final IOException e) {
    234             LOG.error("Exception while reading : ", e);
    235             processException(e);
    236
    237             return -1;
    238         }
    239     }
    240
    241     /**
    242      * {@inheritDoc}
    243      */
    244     @Override
    245     protected ByteBuffer convertToDirectBuffer(WriteRequest writeRequest, boolean createNew) {
    246         // Here, we don't create a new DirectBuffer. We let the underlying layer do the job for us
    247         return (ByteBuffer) writeRequest.getMessage();
    248     }
    249
    250     /**
    251      * Set this session status as connected. To be called by the processor selecting/polling this session.
    252      */
    253     void setConnected() {
    254         if (!isCreated()) {
    255             throw new IllegalStateException("Trying to open a non created session");
    256         }
    257
    258         state = SessionState.CONNECTED;
    259         processSessionOpen();
    260     }
    261
    262     @Override
    263     public void ready(boolean accept, boolean connect, boolean read, ByteBuffer readBuffer, boolean write) {
    264         if (IS_DEBUG) {
    265             LOG.debug("session {} ready for accept={}, connect={}, read={}, write={}", new Object[] { this, accept,
    266                                     connect, read, write });
    267         }
    268
    269         if (read) {
    270             if (IS_DEBUG) {
    271                 LOG.debug("readable datagram for UDP service : {}", this);
    272             }
    273
    274             // Read everything we can up to the buffer size
    275             try {
    276                 readBuffer.clear();
    277                 ((DatagramChannel) channel).receive(readBuffer);
    278                 readBuffer.flip();
    279
    280                 int readbytes = readBuffer.remaining();
    281
    282                 if (IS_DEBUG) {
    283                     LOG.debug("read {} bytes", readbytes);
    284                 }
    285
    286                 if (readbytes <= 0) {
    287                     // session closed by the remote peer
    288                     if (IS_DEBUG) {
    289                         LOG.debug("session closed by the remote peer");
    290                     }
    291
    292                     close(true);
    293                 } else {
    294                     receivedDatagram(readBuffer);
    295                 }
    296             } catch (IOException e) {
    297                 processException(e);
    298             }
    299
    300         }
    301
    302         if (write) {
    303             processWrite(selectorLoop);
    304         }
    305         if (accept) {
    306             throw new IllegalStateException("accept event should never occur on NioUdpSession");
    307         }
    308
    309     }
    310
    311 }