/**
   * Process a new session : - initialize it - create its chain - fire the CREATED listeners if any
   *
   * @param session The session to create
   * @return true if the session has been registered
   */
  private boolean addNow(S session) {
    boolean registered = false;

    try {
      init(session);
      registered = true;

      // Build the filter chain of this session.
      IoFilterChainBuilder chainBuilder = session.getService().getFilterChainBuilder();
      chainBuilder.buildFilterChain(session.getFilterChain());

      // DefaultIoFilterChain.CONNECT_FUTURE is cleared inside here
      // in AbstractIoFilterChain.fireSessionOpened().
      // Propagate the SESSION_CREATED event up to the chain
      IoServiceListenerSupport listeners =
          ((AbstractIoService) session.getService()).getListeners();
      listeners.fireSessionCreated(session);
    } catch (Throwable e) {
      ExceptionMonitor.getInstance().exceptionCaught(e);

      try {
        destroy(session);
      } catch (Exception e1) {
        ExceptionMonitor.getInstance().exceptionCaught(e1);
      } finally {
        registered = false;
      }
    }

    return registered;
  }
  /** {@inheritDoc} */
  public void updateTrafficControl(S session) {
    //
    try {
      setInterestedInRead(session, !session.isReadSuspended());
    } catch (Exception e) {
      IoFilterChain filterChain = session.getFilterChain();
      filterChain.fireExceptionCaught(e);
    }

    try {
      setInterestedInWrite(
          session, !session.getWriteRequestQueue().isEmpty(session) && !session.isWriteSuspended());
    } catch (Exception e) {
      IoFilterChain filterChain = session.getFilterChain();
      filterChain.fireExceptionCaught(e);
    }
  }
  private void clearWriteRequestQueue(S session) {
    WriteRequestQueue writeRequestQueue = session.getWriteRequestQueue();
    WriteRequest req;

    List<WriteRequest> failedRequests = new ArrayList<WriteRequest>();

    if ((req = writeRequestQueue.poll(session)) != null) {
      Object message = req.getMessage();

      if (message instanceof IoBuffer) {
        IoBuffer buf = (IoBuffer) message;

        // The first unwritten empty buffer must be
        // forwarded to the filter chain.
        if (buf.hasRemaining()) {
          buf.reset();
          failedRequests.add(req);
        } else {
          IoFilterChain filterChain = session.getFilterChain();
          filterChain.fireMessageSent(req);
        }
      } else {
        failedRequests.add(req);
      }

      // Discard others.
      while ((req = writeRequestQueue.poll(session)) != null) {
        failedRequests.add(req);
      }
    }

    // Create an exception and notify.
    if (!failedRequests.isEmpty()) {
      WriteToClosedSessionException cause = new WriteToClosedSessionException(failedRequests);

      for (WriteRequest r : failedRequests) {
        session.decreaseScheduledBytesAndMessages(r);
        r.getFuture().setException(cause);
      }

      IoFilterChain filterChain = session.getFilterChain();
      filterChain.fireExceptionCaught(cause);
    }
  }
  /** Write all the pending messages */
  private void flush(long currentTime) {
    if (flushingSessions.isEmpty()) {
      return;
    }

    do {
      S session = flushingSessions.poll(); // the same one with firstSession

      if (session == null) {
        // Just in case ... It should not happen.
        break;
      }

      // Reset the Schedule for flush flag for this session,
      // as we are flushing it now
      session.unscheduledForFlush();

      SessionState state = getState(session);

      switch (state) {
        case OPENED:
          try {
            boolean flushedAll = flushNow(session, currentTime);

            if (flushedAll
                && !session.getWriteRequestQueue().isEmpty(session)
                && !session.isScheduledForFlush()) {
              scheduleFlush(session);
            }
          } catch (Exception e) {
            scheduleRemove(session);
            IoFilterChain filterChain = session.getFilterChain();
            filterChain.fireExceptionCaught(e);
          }

          break;

        case CLOSING:
          // Skip if the channel is already closed.
          break;

        case OPENING:
          // Retry later if session is not yet fully initialized.
          // (In case that Session.write() is called before addSession()
          // is processed)
          scheduleFlush(session);
          return;

        default:
          throw new IllegalStateException(String.valueOf(state));
      }

    } while (!flushingSessions.isEmpty());
  }
  private boolean removeNow(S session) {
    clearWriteRequestQueue(session);

    try {
      destroy(session);
      return true;
    } catch (Exception e) {
      IoFilterChain filterChain = session.getFilterChain();
      filterChain.fireExceptionCaught(e);
    } finally {
      clearWriteRequestQueue(session);
      ((AbstractIoService) session.getService()).getListeners().fireSessionDestroyed(session);
    }
    return false;
  }
 private void fireMessageSent(S session, WriteRequest req) {
   session.setCurrentWriteRequest(null);
   IoFilterChain filterChain = session.getFilterChain();
   filterChain.fireMessageSent(req);
 }
  private boolean flushNow(S session, long currentTime) {
    if (!session.isConnected()) {
      scheduleRemove(session);
      return false;
    }

    final boolean hasFragmentation = session.getTransportMetadata().hasFragmentation();

    final WriteRequestQueue writeRequestQueue = session.getWriteRequestQueue();

    // Set limitation for the number of written bytes for read-write
    // fairness. I used maxReadBufferSize * 3 / 2, which yields best
    // performance in my experience while not breaking fairness much.
    final int maxWrittenBytes =
        session.getConfig().getMaxReadBufferSize()
            + (session.getConfig().getMaxReadBufferSize() >>> 1);
    int writtenBytes = 0;
    WriteRequest req = null;

    try {
      // Clear OP_WRITE
      setInterestedInWrite(session, false);

      do {
        // Check for pending writes.
        req = session.getCurrentWriteRequest();

        if (req == null) {
          req = writeRequestQueue.poll(session);

          if (req == null) {
            break;
          }

          session.setCurrentWriteRequest(req);
        }

        int localWrittenBytes = 0;
        Object message = req.getMessage();

        if (message instanceof IoBuffer) {
          localWrittenBytes =
              writeBuffer(
                  session, req, hasFragmentation, maxWrittenBytes - writtenBytes, currentTime);

          if ((localWrittenBytes > 0) && ((IoBuffer) message).hasRemaining()) {
            // the buffer isn't empty, we re-interest it in writing
            writtenBytes += localWrittenBytes;
            setInterestedInWrite(session, true);
            return false;
          }
        } else if (message instanceof FileRegion) {
          localWrittenBytes =
              writeFile(
                  session, req, hasFragmentation, maxWrittenBytes - writtenBytes, currentTime);

          // Fix for Java bug on Linux
          // http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=5103988
          // If there's still data to be written in the FileRegion,
          // return 0 indicating that we need
          // to pause until writing may resume.
          if ((localWrittenBytes > 0) && (((FileRegion) message).getRemainingBytes() > 0)) {
            writtenBytes += localWrittenBytes;
            setInterestedInWrite(session, true);
            return false;
          }
        } else {
          throw new IllegalStateException(
              "Don't know how to handle message of type '"
                  + message.getClass().getName()
                  + "'.  Are you missing a protocol encoder?");
        }

        if (localWrittenBytes == 0) {
          // Kernel buffer is full.
          setInterestedInWrite(session, true);
          return false;
        }

        writtenBytes += localWrittenBytes;

        if (writtenBytes >= maxWrittenBytes) {
          // Wrote too much
          scheduleFlush(session);
          return false;
        }
      } while (writtenBytes < maxWrittenBytes);
    } catch (Exception e) {
      if (req != null) {
        req.getFuture().setException(e);
      }

      IoFilterChain filterChain = session.getFilterChain();
      filterChain.fireExceptionCaught(e);
      return false;
    }

    return true;
  }
  private void read(S session) {
    IoSessionConfig config = session.getConfig();
    int bufferSize = config.getReadBufferSize();
    IoBuffer buf = IoBuffer.allocate(bufferSize);

    final boolean hasFragmentation = session.getTransportMetadata().hasFragmentation();

    try {
      int readBytes = 0;
      int ret;

      try {
        if (hasFragmentation) {

          while ((ret = read(session, buf)) > 0) {
            readBytes += ret;

            if (!buf.hasRemaining()) {
              break;
            }
          }
        } else {
          ret = read(session, buf);

          if (ret > 0) {
            readBytes = ret;
          }
        }
      } finally {
        buf.flip();
      }

      if (readBytes > 0) {
        IoFilterChain filterChain = session.getFilterChain();
        filterChain.fireMessageReceived(buf);
        buf = null;

        if (hasFragmentation) {
          if (readBytes << 1 < config.getReadBufferSize()) {
            session.decreaseReadBufferSize();
          } else if (readBytes == config.getReadBufferSize()) {
            session.increaseReadBufferSize();
          }
        }
      }

      if (ret < 0) {
        scheduleRemove(session);
      }
    } catch (Throwable e) {
      if (e instanceof IOException) {
        if (!(e instanceof PortUnreachableException)
            || !AbstractDatagramSessionConfig.class.isAssignableFrom(config.getClass())
            || ((AbstractDatagramSessionConfig) config).isCloseOnPortUnreachable()) {
          scheduleRemove(session);
        }
      }

      IoFilterChain filterChain = session.getFilterChain();
      filterChain.fireExceptionCaught(e);
    }
  }