/**
   * Excute the pattern matching algorithm to determine if a the current connection must be
   * suspended or not, and when.
   *
   * @param ctx The current {@link Context}
   * @return true if the ProtocolChain should continue its execution, false if the connection has
   *     been suspended.
   * @throws java.io.IOException
   */
  public boolean execute(Context ctx) throws IOException {
    WorkerThread wt = (WorkerThread) Thread.currentThread();
    ByteBuffer bb = wt.getByteBuffer();
    controller = ctx.getController();

    if (ctx.getProtocol() == Controller.Protocol.TCP) {
      ctx.getSelectionKey().attach(null);
    } else {
      wt.getAttachment().setTimeout(Long.MIN_VALUE);
    }

    if (protocolChain == null) {
      if (ctx.getProtocolChain() instanceof DefaultProtocolChain) {
        protocolChain = (DefaultProtocolChain) ctx.getProtocolChain();
      } else {
        throw new IllegalStateException(
            "SuspendableFilter cannot be " + "used without the DefaultProtocolChain");
      }
    }

    if (logger.isLoggable(Level.FINE)) {
      logger.fine("Trying to match " + ctx.getSelectionKey());
    }

    // This will be quite slow if a lot of registration.
    // TODO: Need a better algorithm.
    SuspendableHandlerWrapper<? extends T> sh = null;
    Iterator<byte[]> iterator = suspendCandidates.keySet().iterator();
    byte[] matchBytes = null;
    while (iterator.hasNext()) {
      matchBytes = iterator.next();
      if (Utils.findBytes(bb, matchBytes) > -1) {
        if (logger.isLoggable(Level.FINE)) {
          logger.fine(
              "Find match: " + (new String(matchBytes)) + " Suspending: " + ctx.getSelectionKey());
        }
        sh = suspendCandidates.get(matchBytes);
        break;
      }
    }

    if (sh != null) {
      KeyHandler kh = new KeyHandler();
      kh.setSuspendableHandler(sh);
      suspendedKeys.put(ctx.getSelectionKey(), kh);
      if (sh.getSuspendWhen() == Suspend.BEFORE) {
        suspend(ctx, true);
        if (logger.isLoggable(Level.FINE)) {
          logger.fine("-----> " + ctx.getKeyRegistrationState());
        }
        return false;
      }
    }

    return true;
  }
  /**
   * Suspend the request by creating the appropriate structure so the state of the current
   * connection is not lost.
   *
   * @param ctx The current {@link Context}
   * @param incomingRequest suspend now of after.
   */
  private void suspend(Context ctx, boolean incomingRequest) {
    try {
      SelectionKey key = ctx.getSelectionKey();
      KeyHandler kh = suspendedKeys.get(key);

      SuspendableHandlerWrapper<? extends T> sh = null;
      if (kh != null) {
        sh = kh.getSuspendableHandler();
      } else {
        kh = new KeyHandler();
      }

      if (kh != null && !incomingRequest) {
        if (sh.getSuspendWhen() == Suspend.BEFORE) {
          return;
        }
      }

      // If the users didn't want to be notified.
      if (sh == null) {
        // TODO: Configurable.
        sh =
            new SuspendableHandlerWrapper(
                new SuspendableHandler() {

                  public void interupted(Object attachment) {}

                  public void expired(Object attachment) {}

                  public void resumed(Object attachment) {}
                },
                null,
                30000,
                new Suspendable(this),
                Suspend.AFTER);
      }
      sh.setSuspendableFilter(this);
      sh.suspendable.setKey(key);
      sh.setSelectorHandler(ctx.getSelectorHandler());

      kh.setSuspendableHandler(sh);
      kh.setKey(key);
      WorkerThread workerThread = (WorkerThread) Thread.currentThread();
      ThreadAttachment attachment = workerThread.getAttachment();
      attachment.setMode(Mode.STORE_ALL);
      kh.setThreadAttachment(workerThread.detach());
      ctx.setKeyRegistrationState(KeyRegistrationState.NONE);

      suspendableMonitor.suspend(kh);
    } catch (Throwable ex) {
      if (logger.isLoggable(Level.FINE)) {
        logger.log(Level.FINE, "suspend", ex);
      }
    }
  }