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.ExchangeTimedOutException;
023 import org.apache.camel.NoTypeConversionAvailableException;
024 import org.apache.camel.component.netty.NettyConstants;
025 import org.apache.camel.component.netty.NettyHelper;
026 import org.apache.camel.component.netty.NettyPayloadHelper;
027 import org.apache.camel.component.netty.NettyProducer;
028 import org.apache.camel.util.ExchangeHelper;
029 import org.apache.commons.logging.Log;
030 import org.apache.commons.logging.LogFactory;
031 import org.jboss.netty.channel.ChannelHandlerContext;
032 import org.jboss.netty.channel.ChannelStateEvent;
033 import org.jboss.netty.channel.ExceptionEvent;
034 import org.jboss.netty.channel.MessageEvent;
035 import org.jboss.netty.channel.SimpleChannelUpstreamHandler;
036 import org.jboss.netty.handler.timeout.TimeoutException;
037
038 /**
039 * Client handler which cannot be shared
040 */
041 public class ClientChannelHandler extends SimpleChannelUpstreamHandler {
042 private static final transient Log LOG = LogFactory.getLog(ClientChannelHandler.class);
043 private final NettyProducer producer;
044 private final Exchange exchange;
045 private final AsyncCallback callback;
046 private boolean messageReceived;
047 private boolean exceptionHandled;
048
049 public ClientChannelHandler(NettyProducer producer, Exchange exchange, AsyncCallback callback) {
050 super();
051 this.producer = producer;
052 this.exchange = exchange;
053 this.callback = callback;
054 }
055
056 @Override
057 public void channelOpen(ChannelHandlerContext ctx, ChannelStateEvent channelStateEvent) throws Exception {
058 // to keep track of open sockets
059 producer.getAllChannels().add(channelStateEvent.getChannel());
060 }
061
062 @Override
063 public void exceptionCaught(ChannelHandlerContext ctx, ExceptionEvent exceptionEvent) throws Exception {
064 if (LOG.isTraceEnabled()) {
065 LOG.trace("Exception caught at Channel: " + ctx.getChannel(), exceptionEvent.getCause());
066
067 }
068 if (exceptionHandled) {
069 // ignore subsequent exceptions being thrown
070 return;
071 }
072
073 exceptionHandled = true;
074 Throwable cause = exceptionEvent.getCause();
075
076 // was it the timeout
077 if (cause instanceof TimeoutException) {
078 // timeout occurred
079 exchange.setException(new ExchangeTimedOutException(exchange, producer.getConfiguration().getTimeout()));
080
081 // signal callback
082 callback.done(false);
083 } else {
084 if (LOG.isDebugEnabled()) {
085 LOG.debug("Closing channel as an exception was thrown from Netty", cause);
086 }
087 // set the cause on the exchange
088 exchange.setException(cause);
089
090 // close channel in case an exception was thrown
091 NettyHelper.close(exceptionEvent.getChannel());
092
093 // signal callback
094 callback.done(false);
095 }
096 }
097
098 @Override
099 public void channelClosed(ChannelHandlerContext ctx, ChannelStateEvent e) throws Exception {
100 if (LOG.isTraceEnabled()) {
101 LOG.trace("Channel closed: " + ctx.getChannel());
102 }
103
104 if (producer.getConfiguration().isSync() && !messageReceived && !exceptionHandled) {
105 // session was closed but no message received. This could be because the remote server had an internal error
106 // and could not return a response. We should count down to stop waiting for a response
107 if (LOG.isDebugEnabled()) {
108 LOG.debug("Channel closed but no message received from address: " + producer.getConfiguration().getAddress());
109 }
110 exchange.setException(new CamelExchangeException("No response received from remote server: " + producer.getConfiguration().getAddress(), exchange));
111 // signal callback
112 callback.done(false);
113 }
114 }
115
116 @Override
117 public void messageReceived(ChannelHandlerContext ctx, MessageEvent messageEvent) throws Exception {
118 messageReceived = true;
119
120 Object body = messageEvent.getMessage();
121 if (LOG.isDebugEnabled()) {
122 LOG.debug("Message received: " + body);
123 }
124
125 // if textline enabled then covert to a String which must be used for textline
126 if (producer.getConfiguration().isTextline()) {
127 try {
128 body = producer.getContext().getTypeConverter().mandatoryConvertTo(String.class, exchange, body);
129 } catch (NoTypeConversionAvailableException e) {
130 exchange.setException(e);
131 callback.done(false);
132 }
133 }
134
135
136 // set the result on either IN or OUT on the original exchange depending on its pattern
137 if (ExchangeHelper.isOutCapable(exchange)) {
138 NettyPayloadHelper.setOut(exchange, body);
139 } else {
140 NettyPayloadHelper.setIn(exchange, body);
141 }
142
143 try {
144 // should channel be closed after complete?
145 Boolean close;
146 if (ExchangeHelper.isOutCapable(exchange)) {
147 close = exchange.getOut().getHeader(NettyConstants.NETTY_CLOSE_CHANNEL_WHEN_COMPLETE, Boolean.class);
148 } else {
149 close = exchange.getIn().getHeader(NettyConstants.NETTY_CLOSE_CHANNEL_WHEN_COMPLETE, Boolean.class);
150 }
151
152 // should we disconnect, the header can override the configuration
153 boolean disconnect = producer.getConfiguration().isDisconnect();
154 if (close != null) {
155 disconnect = close;
156 }
157 if (disconnect) {
158 if (LOG.isDebugEnabled()) {
159 LOG.debug("Closing channel when complete at address: " + producer.getConfiguration().getAddress());
160 }
161 NettyHelper.close(ctx.getChannel());
162 }
163 } finally {
164 // signal callback
165 callback.done(false);
166 }
167 }
168
169 }