001 /**
002 * Licensed to the Apache Software Foundation (ASF) under one or more
003 * contributor license agreements. See the NOTICE file distributed with
004 * this work for additional information regarding copyright ownership.
005 * The ASF licenses this file to You under the Apache License, Version 2.0
006 * (the "License"); you may not use this file except in compliance with
007 * the License. You may obtain a copy of the License at
008 *
009 * http://www.apache.org/licenses/LICENSE-2.0
010 *
011 * Unless required by applicable law or agreed to in writing, software
012 * distributed under the License is distributed on an "AS IS" BASIS,
013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014 * See the License for the specific language governing permissions and
015 * limitations under the License.
016 */
017 package org.apache.camel.component.netty.handlers;
018
019 import org.apache.camel.AsyncCallback;
020 import org.apache.camel.CamelExchangeException;
021 import org.apache.camel.Exchange;
022 import org.apache.camel.NoTypeConversionAvailableException;
023 import org.apache.camel.component.netty.NettyConstants;
024 import org.apache.camel.component.netty.NettyHelper;
025 import org.apache.camel.component.netty.NettyPayloadHelper;
026 import org.apache.camel.component.netty.NettyProducer;
027 import org.apache.camel.util.ExchangeHelper;
028 import org.jboss.netty.channel.ChannelHandlerContext;
029 import org.jboss.netty.channel.ChannelStateEvent;
030 import org.jboss.netty.channel.ExceptionEvent;
031 import org.jboss.netty.channel.MessageEvent;
032 import org.jboss.netty.channel.SimpleChannelUpstreamHandler;
033 import org.slf4j.Logger;
034 import org.slf4j.LoggerFactory;
035
036 /**
037 * Client handler which cannot be shared
038 */
039 public class ClientChannelHandler extends SimpleChannelUpstreamHandler {
040 private static final transient Logger LOG = LoggerFactory.getLogger(ClientChannelHandler.class);
041 private final NettyProducer producer;
042 private final Exchange exchange;
043 private final AsyncCallback callback;
044 private boolean messageReceived;
045 private boolean exceptionHandled;
046
047 public ClientChannelHandler(NettyProducer producer, Exchange exchange, AsyncCallback callback) {
048 super();
049 this.producer = producer;
050 this.exchange = exchange;
051 this.callback = callback;
052 }
053
054 @Override
055 public void channelOpen(ChannelHandlerContext ctx, ChannelStateEvent channelStateEvent) throws Exception {
056 // to keep track of open sockets
057 producer.getAllChannels().add(channelStateEvent.getChannel());
058 }
059
060 @Override
061 public void exceptionCaught(ChannelHandlerContext ctx, ExceptionEvent exceptionEvent) throws Exception {
062 if (LOG.isTraceEnabled()) {
063 LOG.trace("Exception caught at Channel: " + ctx.getChannel(), exceptionEvent.getCause());
064
065 }
066 if (exceptionHandled) {
067 // ignore subsequent exceptions being thrown
068 return;
069 }
070
071 exceptionHandled = true;
072 Throwable cause = exceptionEvent.getCause();
073
074 if (LOG.isDebugEnabled()) {
075 LOG.debug("Closing channel as an exception was thrown from Netty", cause);
076 }
077 // set the cause on the exchange
078 exchange.setException(cause);
079
080 // close channel in case an exception was thrown
081 NettyHelper.close(exceptionEvent.getChannel());
082
083 // signal callback
084 callback.done(false);
085 }
086
087 @Override
088 public void channelClosed(ChannelHandlerContext ctx, ChannelStateEvent e) throws Exception {
089 LOG.trace("Channel closed: {}", ctx.getChannel());
090
091 if (producer.getConfiguration().isSync() && !messageReceived && !exceptionHandled) {
092 // session was closed but no message received. This could be because the remote server had an internal error
093 // and could not return a response. We should count down to stop waiting for a response
094 if (LOG.isDebugEnabled()) {
095 LOG.debug("Channel closed but no message received from address: {}", producer.getConfiguration().getAddress());
096 }
097 exchange.setException(new CamelExchangeException("No response received from remote server: " + producer.getConfiguration().getAddress(), exchange));
098 // signal callback
099 callback.done(false);
100 }
101 }
102
103 @Override
104 public void messageReceived(ChannelHandlerContext ctx, MessageEvent messageEvent) throws Exception {
105 messageReceived = true;
106
107 Object body = messageEvent.getMessage();
108 LOG.debug("Message received: {}", body);
109
110 // if textline enabled then covert to a String which must be used for textline
111 if (producer.getConfiguration().isTextline()) {
112 try {
113 body = producer.getContext().getTypeConverter().mandatoryConvertTo(String.class, exchange, body);
114 } catch (NoTypeConversionAvailableException e) {
115 exchange.setException(e);
116 callback.done(false);
117 }
118 }
119
120
121 // set the result on either IN or OUT on the original exchange depending on its pattern
122 if (ExchangeHelper.isOutCapable(exchange)) {
123 NettyPayloadHelper.setOut(exchange, body);
124 } else {
125 NettyPayloadHelper.setIn(exchange, body);
126 }
127
128 try {
129 // should channel be closed after complete?
130 Boolean close;
131 if (ExchangeHelper.isOutCapable(exchange)) {
132 close = exchange.getOut().getHeader(NettyConstants.NETTY_CLOSE_CHANNEL_WHEN_COMPLETE, Boolean.class);
133 } else {
134 close = exchange.getIn().getHeader(NettyConstants.NETTY_CLOSE_CHANNEL_WHEN_COMPLETE, Boolean.class);
135 }
136
137 // should we disconnect, the header can override the configuration
138 boolean disconnect = producer.getConfiguration().isDisconnect();
139 if (close != null) {
140 disconnect = close;
141 }
142 if (disconnect) {
143 if (LOG.isDebugEnabled()) {
144 LOG.debug("Closing channel when complete at address: {}", producer.getConfiguration().getAddress());
145 }
146 NettyHelper.close(ctx.getChannel());
147 }
148 } finally {
149 // signal callback
150 callback.done(false);
151 }
152 }
153
154 }