@Override
  public void channelClosed(ChannelHandlerContext ctx, ChannelStateEvent e) throws Exception {
    if (LOG.isTraceEnabled()) {
      LOG.trace("Channel closed: {}", ctx.getChannel());
    }

    Exchange exchange = getExchange(ctx);
    AsyncCallback callback = getAsyncCallback(ctx);

    // remove state
    producer.removeState(ctx.getChannel());

    // to keep track of open sockets
    producer.getAllChannels().remove(ctx.getChannel());

    if (producer.getConfiguration().isSync() && !messageReceived && !exceptionHandled) {
      // To avoid call the callback.done twice
      exceptionHandled = true;
      // session was closed but no message received. This could be because the remote server had an
      // internal error
      // and could not return a response. We should count down to stop waiting for a response
      if (LOG.isDebugEnabled()) {
        LOG.debug(
            "Channel closed but no message received from address: {}",
            producer.getConfiguration().getAddress());
      }
      exchange.setException(
          new CamelExchangeException(
              "No response received from remote server: "
                  + producer.getConfiguration().getAddress(),
              exchange));
      // signal callback
      callback.done(false);
    }
  }
 @Override
 public void channelOpen(ChannelHandlerContext ctx, ChannelStateEvent channelStateEvent)
     throws Exception {
   if (LOG.isTraceEnabled()) {
     LOG.trace("Channel open: {}", ctx.getChannel());
   }
   // to keep track of open sockets
   producer.getAllChannels().add(channelStateEvent.getChannel());
 }
  /**
   * Gets the Camel {@link Message} to use as the message to be set on the current {@link Exchange}
   * when we have received a reply message.
   *
   * <p>
   *
   * @param exchange the current exchange
   * @param messageEvent the incoming event which has the response message from Netty.
   * @return the Camel {@link Message} to set on the current {@link Exchange} as the response
   *     message.
   * @throws Exception is thrown if error getting the response message
   */
  protected Message getResponseMessage(Exchange exchange, MessageEvent messageEvent)
      throws Exception {
    Object body = messageEvent.getMessage();
    if (LOG.isDebugEnabled()) {
      LOG.debug("Channel: {} received body: {}", new Object[] {messageEvent.getChannel(), body});
    }

    // if textline enabled then covert to a String which must be used for textline
    if (producer.getConfiguration().isTextline()) {
      body =
          producer.getContext().getTypeConverter().mandatoryConvertTo(String.class, exchange, body);
    }

    // set the result on either IN or OUT on the original exchange depending on its pattern
    if (ExchangeHelper.isOutCapable(exchange)) {
      NettyPayloadHelper.setOut(exchange, body);
      return exchange.getOut();
    } else {
      NettyPayloadHelper.setIn(exchange, body);
      return exchange.getIn();
    }
  }
 private AsyncCallback getAsyncCallback(ChannelHandlerContext ctx) {
   NettyCamelState state = producer.getState(ctx.getChannel());
   return state != null ? state.getCallback() : null;
 }
 private Exchange getExchange(ChannelHandlerContext ctx) {
   NettyCamelState state = producer.getState(ctx.getChannel());
   return state != null ? state.getExchange() : null;
 }
  @Override
  public void messageReceived(ChannelHandlerContext ctx, MessageEvent messageEvent)
      throws Exception {
    messageReceived = true;

    if (LOG.isTraceEnabled()) {
      LOG.trace("Message received: {}", messageEvent);
    }

    if (producer.getConfiguration().getRequestTimeout() > 0) {
      ChannelHandler handler = ctx.getPipeline().get("timeout");
      if (handler != null) {
        LOG.trace("Removing timeout channel as we received message");
        ctx.getPipeline().remove(handler);
      }
    }

    Exchange exchange = getExchange(ctx);
    if (exchange == null) {
      // we just ignore the received message as the channel is closed
      return;
    }
    AsyncCallback callback = getAsyncCallback(ctx);

    Message message;
    try {
      message = getResponseMessage(exchange, messageEvent);
    } catch (Exception e) {
      exchange.setException(e);
      callback.done(false);
      return;
    }

    // set the result on either IN or OUT on the original exchange depending on its pattern
    if (ExchangeHelper.isOutCapable(exchange)) {
      exchange.setOut(message);
    } else {
      exchange.setIn(message);
    }

    try {
      // should channel be closed after complete?
      Boolean close;
      if (ExchangeHelper.isOutCapable(exchange)) {
        close =
            exchange
                .getOut()
                .getHeader(NettyConstants.NETTY_CLOSE_CHANNEL_WHEN_COMPLETE, Boolean.class);
      } else {
        close =
            exchange
                .getIn()
                .getHeader(NettyConstants.NETTY_CLOSE_CHANNEL_WHEN_COMPLETE, Boolean.class);
      }

      // check the setting on the exchange property
      if (close == null) {
        close =
            exchange.getProperty(NettyConstants.NETTY_CLOSE_CHANNEL_WHEN_COMPLETE, Boolean.class);
      }

      // should we disconnect, the header can override the configuration
      boolean disconnect = producer.getConfiguration().isDisconnect();
      if (close != null) {
        disconnect = close;
      }
      if (disconnect) {
        if (LOG.isTraceEnabled()) {
          LOG.trace(
              "Closing channel when complete at address: {}",
              producer.getConfiguration().getAddress());
        }
        NettyHelper.close(ctx.getChannel());
      }
    } finally {
      // signal callback
      callback.done(false);
    }
  }