/**
   * Connection will be closed [onlyClose()] and onDisconnect() method will be executed on another
   * thread [DisconnectionThreadPool] after getDisconnectionDelay() time in ms. This method may only
   * be called by current Dispatcher Thread.
   *
   * @param con
   */
  protected final void closeConnectionImpl(AConnection con) {
    /**
     * Test if this build should use assertion. If NetworkAssertion == false javac will remove this
     * code block
     */
    if (Assertion.NetworkAssertion) assert Thread.currentThread() == this;

    if (con.onlyClose())
      dcPool.scheduleDisconnection(new DisconnectionTask(con), con.getDisconnectionDelay());
  }
  /**
   * Parse data from buffer and prepare buffer for reading just one packet - call
   * processData(ByteBuffer b).
   *
   * @param con Connection
   * @param buf Buffer with packet data
   * @return True if packet was parsed.
   */
  private boolean parse(AConnection con, ByteBuffer buf) {
    short sz = 0;
    try {
      sz = buf.getShort();
      if (sz > 1) sz -= 2;
      ByteBuffer b = (ByteBuffer) buf.slice().limit(sz);
      b.order(ByteOrder.LITTLE_ENDIAN);
      /** read message fully */
      buf.position(buf.position() + sz);

      return con.processData(b);
    } catch (IllegalArgumentException e) {
      log.warn(
          "Error on parsing input from client - account: "
              + con
              + " packet size: "
              + sz
              + " real size:"
              + buf.remaining(),
          e);
      return false;
    }
  }
  /**
   * Write as much as possible data to socketChannel represented by SelectionKey key. If all data
   * were written key write interest will be disabled.
   *
   * @param key
   */
  final void write(SelectionKey key) {
    SocketChannel socketChannel = (SocketChannel) key.channel();
    AConnection con = (AConnection) key.attachment();

    int numWrite;
    ByteBuffer wb = con.writeBuffer;
    /** We have not writted data */
    if (wb.hasRemaining()) {
      try {
        numWrite = socketChannel.write(wb);
      } catch (IOException e) {
        closeConnectionImpl(con);
        return;
      }

      if (numWrite == 0) {
        log.info("Write " + numWrite + " ip: " + con.getIP());
        return;
      }

      /** Again not all data was send */
      if (wb.hasRemaining()) return;
    }

    while (true) {
      wb.clear();
      boolean writeFailed = !con.writeData(wb);

      if (writeFailed) {
        wb.limit(0);
        break;
      }

      /** Attempt to write to the channel */
      try {
        numWrite = socketChannel.write(wb);
      } catch (IOException e) {
        closeConnectionImpl(con);
        return;
      }

      if (numWrite == 0) {
        log.info("Write " + numWrite + " ip: " + con.getIP());
        return;
      }

      /** not all data was send */
      if (wb.hasRemaining()) return;
    }

    /**
     * Test if this build should use assertion. If NetworkAssertion == false javac will remove this
     * code block
     */
    if (Assertion.NetworkAssertion) {
      assert !wb.hasRemaining();
    }

    /** We wrote away all data, so we're no longer interested in writing on this socket. */
    key.interestOps(key.interestOps() & ~SelectionKey.OP_WRITE);

    /** We wrote all data so we can close connection that is "PandingClose" */
    if (con.isPendingClose()) closeConnectionImpl(con);
  }
 /**
  * Register new client connected to this Dispatcher and set SelectionKey (result of registration)
  * as this key of given AConnection.
  *
  * @param ch
  * @param ops
  * @param att
  * @throws IOException
  */
 public final void register(SelectableChannel ch, int ops, AConnection att) throws IOException {
   synchronized (gate) {
     selector.wakeup();
     att.setKey(ch.register(selector, ops, att));
   }
 }