@Override
  public void run() {
    while (true) {
      try {
        synchronized (_pendingRequest) {
          Iterator changes = _pendingRequest.iterator();
          while (changes.hasNext()) {
            ChangeRequest change = (ChangeRequest) changes.next();
            if (!processPendingRequest(change)) break;
            changes.remove();
          }
        }

        // wait events from selected channels
        _selector.select();

        Iterator selectedKeys = _selector.selectedKeys().iterator();
        while (selectedKeys.hasNext()) {
          SelectionKey key = (SelectionKey) selectedKeys.next();
          selectedKeys.remove();

          if (!key.isValid()) {
            continue;
          }

          processSelect(key);
        }
      } catch (Exception e) {
        logger.warning(Util.getErrorMessage(e));
      }
    }
  }
  @Override
  public void send(ChangeRequest request, byte[] data) {
    synchronized (_pendingRequest) {
      _pendingRequest.add(request);

      switch (request.type) {
        case ChangeRequest.CHANGE_SOCKET_OP:
          synchronized (_pendingData) {
            List queue = (List) _pendingData.get(request.socket);

            // in general case, the write queue is always existed, unless, the socket has been
            // shutdown
            if (queue != null) {
              queue.add(ByteBuffer.wrap(data));
            } else {
              logger.warning(
                  Util.getErrorMessage(new Throwable("Socket is closed! dropping this request")));
            }
          }
          break;
      }
    }

    _selector.wakeup();
  }
  public void close() {
    try {
      _selector.close();

    } catch (IOException e) {
      logger.warning(Util.getErrorMessage(e));
    }
  }
  protected void cleanUp(SocketChannel socketChannel) {

    try {
      socketChannel.close();
    } catch (IOException e) {
      logger.info(Util.getErrorMessage(e));
    }
    SelectionKey key = socketChannel.keyFor(_selector);
    if (key != null) {
      key.cancel();
    }

    if (_pendingData.containsKey(socketChannel)) {
      _pendingData.remove(socketChannel);
    }
  }