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.session;
    21
    22  import java.io.File;
    23  import java.io.FileInputStream;
    24  import java.io.IOException;
    25  import java.net.SocketAddress;
    26  import java.nio.channels.FileChannel;
    27  import java.util.Iterator;
    28  import java.util.Queue;
    29  import java.util.Set;
    30  import java.util.concurrent.ConcurrentLinkedQueue;
    31  import java.util.concurrent.atomic.AtomicBoolean;
    32  import java.util.concurrent.atomic.AtomicInteger;
    33  import java.util.concurrent.atomic.AtomicLong;
    34
    35  import org.apache.mina.core.buffer.IoBuffer;
    36  import org.apache.mina.core.file.DefaultFileRegion;
    37  import org.apache.mina.core.file.FilenameFileRegion;
    38  import org.apache.mina.core.filterchain.IoFilterChain;
    39  import org.apache.mina.core.future.CloseFuture;
    40  import org.apache.mina.core.future.DefaultCloseFuture;
    41  import org.apache.mina.core.future.DefaultReadFuture;
    42  import org.apache.mina.core.future.DefaultWriteFuture;
    43  import org.apache.mina.core.future.IoFutureListener;
    44  import org.apache.mina.core.future.ReadFuture;
    45  import org.apache.mina.core.future.WriteFuture;
    46  import org.apache.mina.core.service.AbstractIoService;
    47  import org.apache.mina.core.service.IoAcceptor;
    48  import org.apache.mina.core.service.IoHandler;
    49  import org.apache.mina.core.service.IoProcessor;
    50  import org.apache.mina.core.service.IoService;
    51  import org.apache.mina.core.service.TransportMetadata;
    52  import org.apache.mina.core.write.DefaultWriteRequest;
    53  import org.apache.mina.core.write.WriteException;
    54  import org.apache.mina.core.write.WriteRequest;
    55  import org.apache.mina.core.write.WriteRequestQueue;
    56  import org.apache.mina.core.write.WriteTimeoutException;
    57  import org.apache.mina.core.write.WriteToClosedSessionException;
    58  import org.apache.mina.util.ExceptionMonitor;
    59
    60  /**
    61   * Base implementation of {@link IoSession}.
    62   *
    63   * @author <a href="http://mina.apache.org">Apache MINA Project</a>
    64   */
    65  public abstract class AbstractIoSession implements IoSession {
    66      /** The associated handler */
    67      private final IoHandler handler;
    68
    69      /** The session config */
    70      protected IoSessionConfig config;
    71
    72      /** The service which will manage this session */
    73      private final IoService service;
    74
    75      private static final AttributeKey READY_READ_FUTURES_KEY = new AttributeKey(AbstractIoSession.class,
    76              "readyReadFutures");
    77
    78      private static final AttributeKey WAITING_READ_FUTURES_KEY = new AttributeKey(AbstractIoSession.class,
    79              "waitingReadFutures");
    80
    81      private static final IoFutureListener<CloseFuture> SCHEDULED_COUNTER_RESETTER = new IoFutureListener<CloseFuture>() {
    82          public void operationComplete(CloseFuture future) {
    83              AbstractIoSession session = (AbstractIoSession) future.getSession();
    84              session.scheduledWriteBytes.set(0);
    85              session.scheduledWriteMessages.set(0);
    86              session.readBytesThroughput = 0;
    87              session.readMessagesThroughput = 0;
    88              session.writtenBytesThroughput = 0;
    89              session.writtenMessagesThroughput = 0;
    90          }
    91      };
    92
    93      /**
    94       * An internal write request object that triggers session close.
    95       *
    96       * @see #writeRequestQueue
    97       */
    98      private static final WriteRequest CLOSE_REQUEST = new DefaultWriteRequest(new Object());
    99
    100     private final Object lock = new Object();
    101
    102     private IoSessionAttributeMap attributes;
    103
    104     private WriteRequestQueue writeRequestQueue;
    105
    106     private WriteRequest currentWriteRequest;
    107
    108     /** The Session creation's time */
    109     private final long creationTime;
    110
    111     /** An id generator guaranteed to generate unique IDs for the session */
    112     private static AtomicLong idGenerator = new AtomicLong(0);
    113
    114     /** The session ID */
    115     private long sessionId;
    116
    117     /**
    118      * A future that will be set 'closed' when the connection is closed.
    119      */
    120     private final CloseFuture closeFuture = new DefaultCloseFuture(this);
    121
    122     private volatile boolean closing;
    123
    124     // traffic control
    125     private boolean readSuspended = false;
    126
    127     private boolean writeSuspended = false;
    128
    129     // Status variables
    130     private final AtomicBoolean scheduledForFlush = new AtomicBoolean();
    131
    132     private final AtomicInteger scheduledWriteBytes = new AtomicInteger();
    133
    134     private final AtomicInteger scheduledWriteMessages = new AtomicInteger();
    135
    136     private long readBytes;
    137
    138     private long writtenBytes;
    139
    140     private long readMessages;
    141
    142     private long writtenMessages;
    143
    144     private long lastReadTime;
    145
    146     private long lastWriteTime;
    147
    148     private long lastThroughputCalculationTime;
    149
    150     private long lastReadBytes;
    151
    152     private long lastWrittenBytes;
    153
    154     private long lastReadMessages;
    155
    156     private long lastWrittenMessages;
    157
    158     private double readBytesThroughput;
    159
    160     private double writtenBytesThroughput;
    161
    162     private double readMessagesThroughput;
    163
    164     private double writtenMessagesThroughput;
    165
    166     private AtomicInteger idleCountForBoth = new AtomicInteger();
    167
    168     private AtomicInteger idleCountForRead = new AtomicInteger();
    169
    170     private AtomicInteger idleCountForWrite = new AtomicInteger();
    171
    172     private long lastIdleTimeForBoth;
    173
    174     private long lastIdleTimeForRead;
    175
    176     private long lastIdleTimeForWrite;
    177
    178     private boolean deferDecreaseReadBuffer = true;
    179
    180     /**
    181      * TODO Add method documentation
    182      */
    183     protected AbstractIoSession(IoService service) {
    184         this.service = service;
    185         this.handler = service.getHandler();
    186
    187         // Initialize all the Session counters to the current time
    188         long currentTime = System.currentTimeMillis();
    189         creationTime = currentTime;
    190         lastThroughputCalculationTime = currentTime;
    191         lastReadTime = currentTime;
    192         lastWriteTime = currentTime;
    193         lastIdleTimeForBoth = currentTime;
    194         lastIdleTimeForRead = currentTime;
    195         lastIdleTimeForWrite = currentTime;
    196
    197         // TODO add documentation
    198         closeFuture.addListener(SCHEDULED_COUNTER_RESETTER);
    199
    200         // Set a new ID for this session
    201         sessionId = idGenerator.incrementAndGet();
    202     }
    203
    204     /**
    205      * {@inheritDoc}
    206      *
    207      * We use an AtomicLong to guarantee that the session ID are unique.
    208      */
    209     public final long getId() {
    210         return sessionId;
    211     }
    212
    213     /**
    214      * @return The associated IoProcessor for this session
    215      */
    216     public abstract IoProcessor getProcessor();
    217
    218     /**
    219      * {@inheritDoc}
    220      */
    221     public final boolean isConnected() {
    222         return !closeFuture.isClosed();
    223     }
    224
    225     /**
    226      * {@inheritDoc}
    227      */
    228     public final boolean isClosing() {
    229         return closing || closeFuture.isClosed();
    230     }
    231
    232     /**
    233      * {@inheritDoc}
    234      */
    235     public final CloseFuture getCloseFuture() {
    236         return closeFuture;
    237     }
    238
    239     /**
    240      * Tells if the session is scheduled for flushed
    241      *
    242      * @param true if the session is scheduled for flush
    243      */
    244     public final boolean isScheduledForFlush() {
    245         return scheduledForFlush.get();
    246     }
    247
    248     /**
    249      * Schedule the session for flushed
    250      */
    251     public final void scheduledForFlush() {
    252         scheduledForFlush.set(true);
    253     }
    254
    255     /**
    256      * Change the session's status : it's not anymore scheduled for flush
    257      */
    258     public final void unscheduledForFlush() {
    259         scheduledForFlush.set(false);
    260     }
    261
    262     /**
    263      * Set the scheduledForFLush flag. As we may have concurrent access to this
    264      * flag, we compare and set it in one call.
    265      *
    266      * @param schedule
    267      *            the new value to set if not already set.
    268      * @return true if the session flag has been set, and if it wasn't set
    269      *         already.
    270      */
    271     public final boolean setScheduledForFlush(boolean schedule) {
    272         if (schedule) {
    273             // If the current tag is set to false, switch it to true,
    274             // otherwise, we do nothing but return false : the session
    275             // is already scheduled for flush
    276             return scheduledForFlush.compareAndSet(false, schedule);
    277         }
    278
    279         scheduledForFlush.set(schedule);
    280         return true;
    281     }
    282
    283     /**
    284      * {@inheritDoc}
    285      */
    286     public final CloseFuture close(boolean rightNow) {
    287         if (!isClosing()) {
    288             if (rightNow) {
    289                 return close();
    290             }
    291
    292             return closeOnFlush();
    293         } else {
    294             return closeFuture;
    295         }
    296     }
    297
    298     /**
    299      * {@inheritDoc}
    300      */
    301     public final CloseFuture close() {
    302         synchronized (lock) {
    303             if (isClosing()) {
    304                 return closeFuture;
    305             }
    306
    307             closing = true;
    308         }
    309
    310         getFilterChain().fireFilterClose();
    311         return closeFuture;
    312     }
    313
    314     private final CloseFuture closeOnFlush() {
    315         getWriteRequestQueue().offer(this, CLOSE_REQUEST);
    316         getProcessor().flush(this);
    317         return closeFuture;
    318     }
    319
    320     /**
    321      * {@inheritDoc}
    322      */
    323     public IoHandler getHandler() {
    324         return handler;
    325     }
    326
    327     /**
    328      * {@inheritDoc}
    329      */
    330     public IoSessionConfig getConfig() {
    331         return config;
    332     }
    333
    334     /**
    335      * {@inheritDoc}
    336      */
    337     public final ReadFuture read() {
    338         if (!getConfig().isUseReadOperation()) {
    339             throw new IllegalStateException("useReadOperation is not enabled.");
    340         }
    341
    342         Queue<ReadFuture> readyReadFutures = getReadyReadFutures();
    343         ReadFuture future;
    344         synchronized (readyReadFutures) {
    345             future = readyReadFutures.poll();
    346             if (future != null) {
    347                 if (future.isClosed()) {
    348                     // Let other readers get notified.
    349                     readyReadFutures.offer(future);
    350                 }
    351             } else {
    352                 future = new DefaultReadFuture(this);
    353                 getWaitingReadFutures().offer(future);
    354             }
    355         }
    356
    357         return future;
    358     }
    359
    360     /**
    361      * TODO Add method documentation
    362      */
    363     public final void offerReadFuture(Object message) {
    364         newReadFuture().setRead(message);
    365     }
    366
    367     /**
    368      * TODO Add method documentation
    369      */
    370     public final void offerFailedReadFuture(Throwable exception) {
    371         newReadFuture().setException(exception);
    372     }
    373
    374     /**
    375      * TODO Add method documentation
    376      */
    377     public final void offerClosedReadFuture() {
    378         Queue<ReadFuture> readyReadFutures = getReadyReadFutures();
    379         synchronized (readyReadFutures) {
    380             newReadFuture().setClosed();
    381         }
    382     }
    383
    384     /**
    385      * TODO Add method documentation
    386      */
    387     private ReadFuture newReadFuture() {
    388         Queue<ReadFuture> readyReadFutures = getReadyReadFutures();
    389         Queue<ReadFuture> waitingReadFutures = getWaitingReadFutures();
    390         ReadFuture future;
    391         synchronized (readyReadFutures) {
    392             future = waitingReadFutures.poll();
    393             if (future == null) {
    394                 future = new DefaultReadFuture(this);
    395                 readyReadFutures.offer(future);
    396             }
    397         }
    398         return future;
    399     }
    400
    401     /**
    402      * TODO Add method documentation
    403      */
    404     private Queue<ReadFuture> getReadyReadFutures() {
    405         Queue<ReadFuture> readyReadFutures = (Queue<ReadFuture>) getAttribute(READY_READ_FUTURES_KEY);
    406         if (readyReadFutures == null) {
    407             readyReadFutures = new ConcurrentLinkedQueue<ReadFuture>();
    408
    409             Queue<ReadFuture> oldReadyReadFutures = (Queue<ReadFuture>) setAttributeIfAbsent(READY_READ_FUTURES_KEY,
    410                     readyReadFutures);
    411             if (oldReadyReadFutures != null) {
    412                 readyReadFutures = oldReadyReadFutures;
    413             }
    414         }
    415         return readyReadFutures;
    416     }
    417
    418     /**
    419      * TODO Add method documentation
    420      */
    421     private Queue<ReadFuture> getWaitingReadFutures() {
    422         Queue<ReadFuture> waitingReadyReadFutures = (Queue<ReadFuture>) getAttribute(WAITING_READ_FUTURES_KEY);
    423         if (waitingReadyReadFutures == null) {
    424             waitingReadyReadFutures = new ConcurrentLinkedQueue<ReadFuture>();
    425
    426             Queue<ReadFuture> oldWaitingReadyReadFutures = (Queue<ReadFuture>) setAttributeIfAbsent(
    427                     WAITING_READ_FUTURES_KEY, waitingReadyReadFutures);
    428             if (oldWaitingReadyReadFutures != null) {
    429                 waitingReadyReadFutures = oldWaitingReadyReadFutures;
    430             }
    431         }
    432         return waitingReadyReadFutures;
    433     }
    434
    435     /**
    436      * {@inheritDoc}
    437      */
    438     public WriteFuture write(Object message) {
    439         return write(message, null);
    440     }
    441
    442     /**
    443      * {@inheritDoc}
    444      */
    445     public WriteFuture write(Object message, SocketAddress remoteAddress) {
    446         if (message == null) {
    447             throw new IllegalArgumentException("Trying to write a null message : not allowed");
    448         }
    449
    450         // We can't send a message to a connected session if we don't have
    451         // the remote address
    452         if (!getTransportMetadata().isConnectionless() && (remoteAddress != null)) {
    453             throw new UnsupportedOperationException();
    454         }
    455
    456         // If the session has been closed or is closing, we can't either
    457         // send a message to the remote side. We generate a future
    458         // containing an exception.
    459         if (isClosing() || !isConnected()) {
    460             WriteFuture future = new DefaultWriteFuture(this);
    461             WriteRequest request = new DefaultWriteRequest(message, future, remoteAddress);
    462             WriteException writeException = new WriteToClosedSessionException(request);
    463             future.setException(writeException);
    464             return future;
    465         }
    466
    467         FileChannel openedFileChannel = null;
    468
    469         // TODO: remove this code as soon as we use InputStream
    470         // instead of Object for the message.
    471         try {
    472             if ((message instanceof IoBuffer) && !((IoBuffer) message).hasRemaining()) {
    473                 // Nothing to write : probably an error in the user code
    474                 throw new IllegalArgumentException("message is empty. Forgot to call flip()?");
    475             } else if (message instanceof FileChannel) {
    476                 FileChannel fileChannel = (FileChannel) message;
    477                 message = new DefaultFileRegion(fileChannel, 0, fileChannel.size());
    478             } else if (message instanceof File) {
    479                 File file = (File) message;
    480                 openedFileChannel = new FileInputStream(file).getChannel();
    481                 message = new FilenameFileRegion(file, openedFileChannel, 0, openedFileChannel.size());
    482             }
    483         } catch (IOException e) {
    484             ExceptionMonitor.getInstance().exceptionCaught(e);
    485             return DefaultWriteFuture.newNotWrittenFuture(this, e);
    486         }
    487
    488         // Now, we can write the message. First, create a future
    489         WriteFuture writeFuture = new DefaultWriteFuture(this);
    490         WriteRequest writeRequest = new DefaultWriteRequest(message, writeFuture, remoteAddress);
    491
    492         // Then, get the chain and inject the WriteRequest into it
    493         IoFilterChain filterChain = getFilterChain();
    494         filterChain.fireFilterWrite(writeRequest);
    495
    496         // TODO : This is not our business ! The caller has created a
    497         // FileChannel,
    498         // he has to close it !
    499         if (openedFileChannel != null) {
    500             // If we opened a FileChannel, it needs to be closed when the write
    501             // has completed
    502             final FileChannel finalChannel = openedFileChannel;
    503             writeFuture.addListener(new IoFutureListener<WriteFuture>() {
    504                 public void operationComplete(WriteFuture future) {
    505                     try {
    506                         finalChannel.close();
    507                     } catch (IOException e) {
    508                         ExceptionMonitor.getInstance().exceptionCaught(e);
    509                     }
    510                 }
    511             });
    512         }
    513
    514         // Return the WriteFuture.
    515         return writeFuture;
    516     }
    517
    518     /**
    519      * {@inheritDoc}
    520      */
    521     public final Object getAttachment() {
    522         return getAttribute("");
    523     }
    524
    525     /**
    526      * {@inheritDoc}
    527      */
    528     public final Object setAttachment(Object attachment) {
    529         return setAttribute("", attachment);
    530     }
    531
    532     /**
    533      * {@inheritDoc}
    534      */
    535     public final Object getAttribute(Object key) {
    536         return getAttribute(key, null);
    537     }
    538
    539     /**
    540      * {@inheritDoc}
    541      */
    542     public final Object getAttribute(Object key, Object defaultValue) {
    543         return attributes.getAttribute(this, key, defaultValue);
    544     }
    545
    546     /**
    547      * {@inheritDoc}
    548      */
    549     public final Object setAttribute(Object key, Object value) {
    550         return attributes.setAttribute(this, key, value);
    551     }
    552
    553     /**
    554      * {@inheritDoc}
    555      */
    556     public final Object setAttribute(Object key) {
    557         return setAttribute(key, Boolean.TRUE);
    558     }
    559
    560     /**
    561      * {@inheritDoc}
    562      */
    563     public final Object setAttributeIfAbsent(Object key, Object value) {
    564         return attributes.setAttributeIfAbsent(this, key, value);
    565     }
    566
    567     /**
    568      * {@inheritDoc}
    569      */
    570     public final Object setAttributeIfAbsent(Object key) {
    571         return setAttributeIfAbsent(key, Boolean.TRUE);
    572     }
    573
    574     /**
    575      * {@inheritDoc}
    576      */
    577     public final Object removeAttribute(Object key) {
    578         return attributes.removeAttribute(this, key);
    579     }
    580
    581     /**
    582      * {@inheritDoc}
    583      */
    584     public final boolean removeAttribute(Object key, Object value) {
    585         return attributes.removeAttribute(this, key, value);
    586     }
    587
    588     /**
    589      * {@inheritDoc}
    590      */
    591     public final boolean replaceAttribute(Object key, Object oldValue, Object newValue) {
    592         return attributes.replaceAttribute(this, key, oldValue, newValue);
    593     }
    594
    595     /**
    596      * {@inheritDoc}
    597      */
    598     public final boolean containsAttribute(Object key) {
    599         return attributes.containsAttribute(this, key);
    600     }
    601
    602     /**
    603      * {@inheritDoc}
    604      */
    605     public final Set<Object> getAttributeKeys() {
    606         return attributes.getAttributeKeys(this);
    607     }
    608
    609     /**
    610      * TODO Add method documentation
    611      */
    612     public final IoSessionAttributeMap getAttributeMap() {
    613         return attributes;
    614     }
    615
    616     /**
    617      * TODO Add method documentation
    618      */
    619     public final void setAttributeMap(IoSessionAttributeMap attributes) {
    620         this.attributes = attributes;
    621     }
    622
    623     /**
    624      * Create a new close aware write queue, based on the given write queue.
    625      *
    626      * @param writeRequestQueue
    627      *            The write request queue
    628      */
    629     public final void setWriteRequestQueue(WriteRequestQueue writeRequestQueue) {
    630         this.writeRequestQueue = new CloseAwareWriteQueue(writeRequestQueue);
    631     }
    632
    633     /**
    634      * {@inheritDoc}
    635      */
    636     public final void suspendRead() {
    637         readSuspended = true;
    638         if (isClosing() || !isConnected()) {
    639             return;
    640         }
    641         getProcessor().updateTrafficControl(this);
    642     }
    643
    644     /**
    645      * {@inheritDoc}
    646      */
    647     public final void suspendWrite() {
    648         writeSuspended = true;
    649         if (isClosing() || !isConnected()) {
    650             return;
    651         }
    652         getProcessor().updateTrafficControl(this);
    653     }
    654
    655     /**
    656      * {@inheritDoc}
    657      */
    658     @SuppressWarnings("unchecked")
    659     public final void resumeRead() {
    660         readSuspended = false;
    661         if (isClosing() || !isConnected()) {
    662             return;
    663         }
    664         getProcessor().updateTrafficControl(this);
    665     }
    666
    667     /**
    668      * {@inheritDoc}
    669      */
    670     @SuppressWarnings("unchecked")
    671     public final void resumeWrite() {
    672         writeSuspended = false;
    673         if (isClosing() || !isConnected()) {
    674             return;
    675         }
    676         getProcessor().updateTrafficControl(this);
    677     }
    678
    679     /**
    680      * {@inheritDoc}
    681      */
    682     public boolean isReadSuspended() {
    683         return readSuspended;
    684     }
    685
    686     /**
    687      * {@inheritDoc}
    688      */
    689     public boolean isWriteSuspended() {
    690         return writeSuspended;
    691     }
    692
    693     /**
    694      * {@inheritDoc}
    695      */
    696     public final long getReadBytes() {
    697         return readBytes;
    698     }
    699
    700     /**
    701      * {@inheritDoc}
    702      */
    703     public final long getWrittenBytes() {
    704         return writtenBytes;
    705     }
    706
    707     /**
    708      * {@inheritDoc}
    709      */
    710     public final long getReadMessages() {
    711         return readMessages;
    712     }
    713
    714     /**
    715      * {@inheritDoc}
    716      */
    717     public final long getWrittenMessages() {
    718         return writtenMessages;
    719     }
    720
    721     /**
    722      * {@inheritDoc}
    723      */
    724     public final double getReadBytesThroughput() {
    725         return readBytesThroughput;
    726     }
    727
    728     /**
    729      * {@inheritDoc}
    730      */
    731     public final double getWrittenBytesThroughput() {
    732         return writtenBytesThroughput;
    733     }
    734
    735     /**
    736      * {@inheritDoc}
    737      */
    738     public final double getReadMessagesThroughput() {
    739         return readMessagesThroughput;
    740     }
    741
    742     /**
    743      * {@inheritDoc}
    744      */
    745     public final double getWrittenMessagesThroughput() {
    746         return writtenMessagesThroughput;
    747     }
    748
    749     /**
    750      * {@inheritDoc}
    751      */
    752     public final void updateThroughput(long currentTime, boolean force) {
    753         int interval = (int) (currentTime - lastThroughputCalculationTime);
    754
    755         long minInterval = getConfig().getThroughputCalculationIntervalInMillis();
    756         if ((minInterval == 0) || (interval < minInterval)) {
    757             if (!force) {
    758                 return;
    759             }
    760         }
    761
    762         readBytesThroughput = (readBytes - lastReadBytes) * 1000.0 / interval;
    763         writtenBytesThroughput = (writtenBytes - lastWrittenBytes) * 1000.0 / interval;
    764         readMessagesThroughput = (readMessages - lastReadMessages) * 1000.0 / interval;
    765         writtenMessagesThroughput = (writtenMessages - lastWrittenMessages) * 1000.0 / interval;
    766
    767         lastReadBytes = readBytes;
    768         lastWrittenBytes = writtenBytes;
    769         lastReadMessages = readMessages;
    770         lastWrittenMessages = writtenMessages;
    771
    772         lastThroughputCalculationTime = currentTime;
    773     }
    774
    775     /**
    776      * {@inheritDoc}
    777      */
    778     public final long getScheduledWriteBytes() {
    779         return scheduledWriteBytes.get();
    780     }
    781
    782     /**
    783      * {@inheritDoc}
    784      */
    785     public final int getScheduledWriteMessages() {
    786         return scheduledWriteMessages.get();
    787     }
    788
    789     /**
    790      * TODO Add method documentation
    791      */
    792     protected void setScheduledWriteBytes(int byteCount) {
    793         scheduledWriteBytes.set(byteCount);
    794     }
    795
    796     /**
    797      * TODO Add method documentation
    798      */
    799     protected void setScheduledWriteMessages(int messages) {
    800         scheduledWriteMessages.set(messages);
    801     }
    802
    803     /**
    804      * TODO Add method documentation
    805      */
    806     public final void increaseReadBytes(long increment, long currentTime) {
    807         if (increment <= 0) {
    808             return;
    809         }
    810
    811         readBytes += increment;
    812         lastReadTime = currentTime;
    813         idleCountForBoth.set(0);
    814         idleCountForRead.set(0);
    815
    816         if (getService() instanceof AbstractIoService) {
    817             ((AbstractIoService) getService()).getStatistics().increaseReadBytes(increment, currentTime);
    818         }
    819     }
    820
    821     /**
    822      * TODO Add method documentation
    823      */
    824     public final void increaseReadMessages(long currentTime) {
    825         readMessages++;
    826         lastReadTime = currentTime;
    827         idleCountForBoth.set(0);
    828         idleCountForRead.set(0);
    829
    830         if (getService() instanceof AbstractIoService) {
    831             ((AbstractIoService) getService()).getStatistics().increaseReadMessages(currentTime);
    832         }
    833     }
    834
    835     /**
    836      * TODO Add method documentation
    837      */
    838     public final void increaseWrittenBytes(int increment, long currentTime) {
    839         if (increment <= 0) {
    840             return;
    841         }
    842
    843         writtenBytes += increment;
    844         lastWriteTime = currentTime;
    845         idleCountForBoth.set(0);
    846         idleCountForWrite.set(0);
    847
    848         if (getService() instanceof AbstractIoService) {
    849             ((AbstractIoService) getService()).getStatistics().increaseWrittenBytes(increment, currentTime);
    850         }
    851
    852         increaseScheduledWriteBytes(-increment);
    853     }
    854
    855     /**
    856      * TODO Add method documentation
    857      */
    858     public final void increaseWrittenMessages(WriteRequest request, long currentTime) {
    859         Object message = request.getMessage();
    860         if (message instanceof IoBuffer) {
    861             IoBuffer b = (IoBuffer) message;
    862             if (b.hasRemaining()) {
    863                 return;
    864             }
    865         }
    866
    867         writtenMessages++;
    868         lastWriteTime = currentTime;
    869         if (getService() instanceof AbstractIoService) {
    870             ((AbstractIoService) getService()).getStatistics().increaseWrittenMessages(currentTime);
    871         }
    872
    873         decreaseScheduledWriteMessages();
    874     }
    875
    876     /**
    877      * TODO Add method documentation
    878      */
    879     public final void increaseScheduledWriteBytes(int increment) {
    880         scheduledWriteBytes.addAndGet(increment);
    881         if (getService() instanceof AbstractIoService) {
    882             ((AbstractIoService) getService()).getStatistics().increaseScheduledWriteBytes(increment);
    883         }
    884     }
    885
    886     /**
    887      * TODO Add method documentation
    888      */
    889     public final void increaseScheduledWriteMessages() {
    890         scheduledWriteMessages.incrementAndGet();
    891         if (getService() instanceof AbstractIoService) {
    892             ((AbstractIoService) getService()).getStatistics().increaseScheduledWriteMessages();
    893         }
    894     }
    895
    896     /**
    897      * TODO Add method documentation
    898      */
    899     private void decreaseScheduledWriteMessages() {
    900         scheduledWriteMessages.decrementAndGet();
    901         if (getService() instanceof AbstractIoService) {
    902             ((AbstractIoService) getService()).getStatistics().decreaseScheduledWriteMessages();
    903         }
    904     }
    905
    906     /**
    907      * TODO Add method documentation
    908      */
    909     public final void decreaseScheduledBytesAndMessages(WriteRequest request) {
    910         Object message = request.getMessage();
    911         if (message instanceof IoBuffer) {
    912             IoBuffer b = (IoBuffer) message;
    913             if (b.hasRemaining()) {
    914                 increaseScheduledWriteBytes(-((IoBuffer) message).remaining());
    915             } else {
    916                 decreaseScheduledWriteMessages();
    917             }
    918         } else {
    919             decreaseScheduledWriteMessages();
    920         }
    921     }
    922
    923     /**
    924      * {@inheritDoc}
    925      */
    926     public final WriteRequestQueue getWriteRequestQueue() {
    927         if (writeRequestQueue == null) {
    928             throw new IllegalStateException();
    929         }
    930         return writeRequestQueue;
    931     }
    932
    933     /**
    934      * {@inheritDoc}
    935      */
    936     public final WriteRequest getCurrentWriteRequest() {
    937         return currentWriteRequest;
    938     }
    939
    940     /**
    941      * {@inheritDoc}
    942      */
    943     public final Object getCurrentWriteMessage() {
    944         WriteRequest req = getCurrentWriteRequest();
    945         if (req == null) {
    946             return null;
    947         }
    948         return req.getMessage();
    949     }
    950
    951     /**
    952      * {@inheritDoc}
    953      */
    954     public final void setCurrentWriteRequest(WriteRequest currentWriteRequest) {
    955         this.currentWriteRequest = currentWriteRequest;
    956     }
    957
    958     /**
    959      * TODO Add method documentation
    960      */
    961     public final void increaseReadBufferSize() {
    962         int newReadBufferSize = getConfig().getReadBufferSize() << 1;
    963         if (newReadBufferSize <= getConfig().getMaxReadBufferSize()) {
    964             getConfig().setReadBufferSize(newReadBufferSize);
    965         } else {
    966             getConfig().setReadBufferSize(getConfig().getMaxReadBufferSize());
    967         }
    968
    969         deferDecreaseReadBuffer = true;
    970     }
    971
    972     /**
    973      * TODO Add method documentation
    974      */
    975     public final void decreaseReadBufferSize() {
    976         if (deferDecreaseReadBuffer) {
    977             deferDecreaseReadBuffer = false;
    978             return;
    979         }
    980
    981         if (getConfig().getReadBufferSize() > getConfig().getMinReadBufferSize()) {
    982             getConfig().setReadBufferSize(getConfig().getReadBufferSize() >>> 1);
    983         }
    984
    985         deferDecreaseReadBuffer = true;
    986     }
    987
    988     /**
    989      * {@inheritDoc}
    990      */
    991     public final long getCreationTime() {
    992         return creationTime;
    993     }
    994
    995     /**
    996      * {@inheritDoc}
    997      */
    998     public final long getLastIoTime() {
    999         return Math.max(lastReadTime, lastWriteTime);
    1000     }
    1001
    1002     /**
    1003      * {@inheritDoc}
    1004      */
    1005     public final long getLastReadTime() {
    1006         return lastReadTime;
    1007     }
    1008
    1009     /**
    1010      * {@inheritDoc}
    1011      */
    1012     public final long getLastWriteTime() {
    1013         return lastWriteTime;
    1014     }
    1015
    1016     /**
    1017      * {@inheritDoc}
    1018      */
    1019     public final boolean isIdle(IdleStatus status) {
    1020         if (status == IdleStatus.BOTH_IDLE) {
    1021             return idleCountForBoth.get() > 0;
    1022         }
    1023
    1024         if (status == IdleStatus.READER_IDLE) {
    1025             return idleCountForRead.get() > 0;
    1026         }
    1027
    1028         if (status == IdleStatus.WRITER_IDLE) {
    1029             return idleCountForWrite.get() > 0;
    1030         }
    1031
    1032         throw new IllegalArgumentException("Unknown idle status: " + status);
    1033     }
    1034
    1035     /**
    1036      * {@inheritDoc}
    1037      */
    1038     public final boolean isBothIdle() {
    1039         return isIdle(IdleStatus.BOTH_IDLE);
    1040     }
    1041
    1042     /**
    1043      * {@inheritDoc}
    1044      */
    1045     public final boolean isReaderIdle() {
    1046         return isIdle(IdleStatus.READER_IDLE);
    1047     }
    1048
    1049     /**
    1050      * {@inheritDoc}
    1051      */
    1052     public final boolean isWriterIdle() {
    1053         return isIdle(IdleStatus.WRITER_IDLE);
    1054     }
    1055
    1056     /**
    1057      * {@inheritDoc}
    1058      */
    1059     public final int getIdleCount(IdleStatus status) {
    1060         if (getConfig().getIdleTime(status) == 0) {
    1061             if (status == IdleStatus.BOTH_IDLE) {
    1062                 idleCountForBoth.set(0);
    1063             }
    1064
    1065             if (status == IdleStatus.READER_IDLE) {
    1066                 idleCountForRead.set(0);
    1067             }
    1068
    1069             if (status == IdleStatus.WRITER_IDLE) {
    1070                 idleCountForWrite.set(0);
    1071             }
    1072         }
    1073
    1074         if (status == IdleStatus.BOTH_IDLE) {
    1075             return idleCountForBoth.get();
    1076         }
    1077
    1078         if (status == IdleStatus.READER_IDLE) {
    1079             return idleCountForRead.get();
    1080         }
    1081
    1082         if (status == IdleStatus.WRITER_IDLE) {
    1083             return idleCountForWrite.get();
    1084         }
    1085
    1086         throw new IllegalArgumentException("Unknown idle status: " + status);
    1087     }
    1088
    1089     /**
    1090      * {@inheritDoc}
    1091      */
    1092     public final long getLastIdleTime(IdleStatus status) {
    1093         if (status == IdleStatus.BOTH_IDLE) {
    1094             return lastIdleTimeForBoth;
    1095         }
    1096
    1097         if (status == IdleStatus.READER_IDLE) {
    1098             return lastIdleTimeForRead;
    1099         }
    1100
    1101         if (status == IdleStatus.WRITER_IDLE) {
    1102             return lastIdleTimeForWrite;
    1103         }
    1104
    1105         throw new IllegalArgumentException("Unknown idle status: " + status);
    1106     }
    1107
    1108     /**
    1109      * TODO Add method documentation
    1110      */
    1111     public final void increaseIdleCount(IdleStatus status, long currentTime) {
    1112         if (status == IdleStatus.BOTH_IDLE) {
    1113             idleCountForBoth.incrementAndGet();
    1114             lastIdleTimeForBoth = currentTime;
    1115         } else if (status == IdleStatus.READER_IDLE) {
    1116             idleCountForRead.incrementAndGet();
    1117             lastIdleTimeForRead = currentTime;
    1118         } else if (status == IdleStatus.WRITER_IDLE) {
    1119             idleCountForWrite.incrementAndGet();
    1120             lastIdleTimeForWrite = currentTime;
    1121         } else {
    1122             throw new IllegalArgumentException("Unknown idle status: " + status);
    1123         }
    1124     }
    1125
    1126     /**
    1127      * {@inheritDoc}
    1128      */
    1129     public final int getBothIdleCount() {
    1130         return getIdleCount(IdleStatus.BOTH_IDLE);
    1131     }
    1132
    1133     /**
    1134      * {@inheritDoc}
    1135      */
    1136     public final long getLastBothIdleTime() {
    1137         return getLastIdleTime(IdleStatus.BOTH_IDLE);
    1138     }
    1139
    1140     /**
    1141      * {@inheritDoc}
    1142      */
    1143     public final long getLastReaderIdleTime() {
    1144         return getLastIdleTime(IdleStatus.READER_IDLE);
    1145     }
    1146
    1147     /**
    1148      * {@inheritDoc}
    1149      */
    1150     public final long getLastWriterIdleTime() {
    1151         return getLastIdleTime(IdleStatus.WRITER_IDLE);
    1152     }
    1153
    1154     /**
    1155      * {@inheritDoc}
    1156      */
    1157     public final int getReaderIdleCount() {
    1158         return getIdleCount(IdleStatus.READER_IDLE);
    1159     }
    1160
    1161     /**
    1162      * {@inheritDoc}
    1163      */
    1164     public final int getWriterIdleCount() {
    1165         return getIdleCount(IdleStatus.WRITER_IDLE);
    1166     }
    1167
    1168     /**
    1169      * {@inheritDoc}
    1170      */
    1171     public SocketAddress getServiceAddress() {
    1172         IoService service = getService();
    1173         if (service instanceof IoAcceptor) {
    1174             return ((IoAcceptor) service).getLocalAddress();
    1175         }
    1176
    1177         return getRemoteAddress();
    1178     }
    1179
    1180     /**
    1181      * {@inheritDoc}
    1182      */
    1183     @Override
    1184     public final int hashCode() {
    1185         return super.hashCode();
    1186     }
    1187
    1188     /**
    1189      * {@inheritDoc} TODO This is a ridiculous implementation. Need to be
    1190      * replaced.
    1191      */
    1192     @Override
    1193     public final boolean equals(Object o) {
    1194         return super.equals(o);
    1195     }
    1196
    1197     /**
    1198      * {@inheritDoc}
    1199      */
    1200     @Override
    1201     public String toString() {
    1202         if (isConnected() || isClosing()) {
    1203             String remote = null;
    1204             String local = null;
    1205
    1206             try {
    1207                 remote = String.valueOf(getRemoteAddress());
    1208             } catch (Throwable t) {
    1209                 remote = "Cannot get the remote address informations: " + t.getMessage();
    1210             }
    1211
    1212             try {
    1213                 local = String.valueOf(getLocalAddress());
    1214             } catch (Throwable t) {
    1215                 local = "Cannot get the local address informations: " + t.getMessage();
    1216             }
    1217
    1218             if (getService() instanceof IoAcceptor) {
    1219                 return "(" + getIdAsString() + ": " + getServiceName() + ", server, " + remote + " => " + local + ')';
    1220             }
    1221
    1222             return "(" + getIdAsString() + ": " + getServiceName() + ", client, " + local + " => " + remote + ')';
    1223         }
    1224
    1225         return "(" + getIdAsString() + ") Session disconnected ...";
    1226     }
    1227
    1228     /**
    1229      * TODO Add method documentation
    1230      */
    1231     private String getIdAsString() {
    1232         String id = Long.toHexString(getId()).toUpperCase();
    1233
    1234         // Somewhat inefficient, but it won't happen that often
    1235         // because an ID is often a big integer.
    1236         while (id.length() < 8) {
    1237             id = '0' + id; // padding
    1238         }
    1239         id = "0x" + id;
    1240
    1241         return id;
    1242     }
    1243
    1244     /**
    1245      * TODO Add method documentation
    1246      */
    1247     private String getServiceName() {
    1248         TransportMetadata tm = getTransportMetadata();
    1249         if (tm == null) {
    1250             return "null";
    1251         }
    1252
    1253         return tm.getProviderName() + ' ' + tm.getName();
    1254     }
    1255
    1256     /**
    1257      * {@inheritDoc}
    1258      */
    1259     public IoService getService() {
    1260         return service;
    1261     }
    1262
    1263     /**
    1264      * Fires a {@link IoEventType#SESSION_IDLE} event to any applicable sessions
    1265      * in the specified collection.
    1266      *
    1267      * @param currentTime
    1268      *            the current time (i.e. {@link System#currentTimeMillis()})
    1269      */
    1270     public static void notifyIdleness(Iterator<? extends IoSession> sessions, long currentTime) {
    1271         IoSession s = null;
    1272         while (sessions.hasNext()) {
    1273             s = sessions.next();
    1274             notifyIdleSession(s, currentTime);
    1275         }
    1276     }
    1277
    1278     /**
    1279      * Fires a {@link IoEventType#SESSION_IDLE} event if applicable for the
    1280      * specified {@code session}.
    1281      *
    1282      * @param currentTime
    1283      *            the current time (i.e. {@link System#currentTimeMillis()})
    1284      */
    1285     public static void notifyIdleSession(IoSession session, long currentTime) {
    1286         notifyIdleSession0(session, currentTime, session.getConfig().getIdleTimeInMillis(IdleStatus.BOTH_IDLE),
    1287                 IdleStatus.BOTH_IDLE, Math.max(session.getLastIoTime(), session.getLastIdleTime(IdleStatus.BOTH_IDLE)));
    1288
    1289         notifyIdleSession0(session, currentTime, session.getConfig().getIdleTimeInMillis(IdleStatus.READER_IDLE),
    1290                 IdleStatus.READER_IDLE,
    1291                 Math.max(session.getLastReadTime(), session.getLastIdleTime(IdleStatus.READER_IDLE)));
    1292
    1293         notifyIdleSession0(session, currentTime, session.getConfig().getIdleTimeInMillis(IdleStatus.WRITER_IDLE),
    1294                 IdleStatus.WRITER_IDLE,
    1295                 Math.max(session.getLastWriteTime(), session.getLastIdleTime(IdleStatus.WRITER_IDLE)));
    1296
    1297         notifyWriteTimeout(session, currentTime);
    1298     }
    1299
    1300     private static void notifyIdleSession0(IoSession session, long currentTime, long idleTime, IdleStatus status,
    1301             long lastIoTime) {
    1302         if ((idleTime > 0) && (lastIoTime != 0) && (currentTime - lastIoTime >= idleTime)) {
    1303             session.getFilterChain().fireSessionIdle(status);
    1304         }
    1305     }
    1306
    1307     private static void notifyWriteTimeout(IoSession session, long currentTime) {
    1308
    1309         long writeTimeout = session.getConfig().getWriteTimeoutInMillis();
    1310         if ((writeTimeout > 0) && (currentTime - session.getLastWriteTime() >= writeTimeout)
    1311                 && !session.getWriteRequestQueue().isEmpty(session)) {
    1312             WriteRequest request = session.getCurrentWriteRequest();
    1313             if (request != null) {
    1314                 session.setCurrentWriteRequest(null);
    1315                 WriteTimeoutException cause = new WriteTimeoutException(request);
    1316                 request.getFuture().setException(cause);
    1317                 session.getFilterChain().fireExceptionCaught(cause);
    1318                 // WriteException is an IOException, so we close the session.
    1319                 session.close(true);
    1320             }
    1321         }
    1322     }
    1323
    1324     /**
    1325      * A queue which handles the CLOSE request.
    1326      *
    1327      * TODO : Check that when closing a session, all the pending requests are
    1328      * correctly sent.
    1329      */
    1330     private class CloseAwareWriteQueue implements WriteRequestQueue {
    1331
    1332         private final WriteRequestQueue queue;
    1333
    1334         /**
    1335          * {@inheritDoc}
    1336          */
    1337         public CloseAwareWriteQueue(WriteRequestQueue queue) {
    1338             this.queue = queue;
    1339         }
    1340
    1341         /**
    1342          * {@inheritDoc}
    1343          */
    1344         public synchronized WriteRequest poll(IoSession session) {
    1345             WriteRequest answer = queue.poll(session);
    1346
    1347             if (answer == CLOSE_REQUEST) {
    1348                 AbstractIoSession.this.close();
    1349                 dispose(session);
    1350                 answer = null;
    1351             }
    1352
    1353             return answer;
    1354         }
    1355
    1356         /**
    1357          * {@inheritDoc}
    1358          */
    1359         public void offer(IoSession session, WriteRequest e) {
    1360             queue.offer(session, e);
    1361         }
    1362
    1363         /**
    1364          * {@inheritDoc}
    1365          */
    1366         public boolean isEmpty(IoSession session) {
    1367             return queue.isEmpty(session);
    1368         }
    1369
    1370         /**
    1371          * {@inheritDoc}
    1372          */
    1373         public void clear(IoSession session) {
    1374             queue.clear(session);
    1375         }
    1376
    1377         /**
    1378          * {@inheritDoc}
    1379          */
    1380         public void dispose(IoSession session) {
    1381             queue.dispose(session);
    1382         }
    1383
    1384         /**
    1385          * {@inheritDoc}
    1386          */
    1387         public int size() {
    1388             return queue.size();
    1389         }
    1390     }
    1391 }