/**
   * Dispose the encoder, removing its instance from the session's attributes, and calling the
   * associated dispose method.
   */
  private void disposeEncoder(IoSession session) {
    ProtocolEncoder encoder = (ProtocolEncoder) session.removeAttribute(ENCODER);
    if (encoder == null) {
      return;
    }

    try {
      encoder.dispose(session);
    } catch (Throwable t) {
      LOGGER.warn("Failed to dispose: " + encoder.getClass().getName() + " (" + encoder + ')');
    }
  }
  @Override
  public void filterWrite(NextFilter nextFilter, IoSession session, WriteRequest writeRequest)
      throws Exception {
    Object message = writeRequest.getMessage();

    // Bypass the encoding if the message is contained in a IoBuffer,
    // as it has already been encoded before
    if (message instanceof IoBuffer || message instanceof FileRegion) {
      nextFilter.filterWrite(session, writeRequest);
      return;
    }

    // Get the encoder in the session
    ProtocolEncoder encoder = getEncoder(session);

    ProtocolEncoderOutput encoderOut = getEncoderOut(session, nextFilter, writeRequest);

    try {
      // Now we can try to encode the response
      encoder.encode(session, message, encoderOut);

      // Send it directly
      ((ProtocolEncoderOutputImpl) encoderOut).flushWithoutFuture();

      // Call the next filter
      nextFilter.filterWrite(session, new MessageWriteRequest(writeRequest));
    } catch (Throwable t) {
      ProtocolEncoderException pee;

      // Generate the correct exception
      if (t instanceof ProtocolEncoderException) {
        pee = (ProtocolEncoderException) t;
      } else {
        pee = new ProtocolEncoderException(t);
      }

      throw pee;
    }
  }