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 if (LOG.isTraceEnabled()) {
090 LOG.trace("Channel closed: " + ctx.getChannel());
091 }
092
093 if (producer.getConfiguration().isSync() && !messageReceived && !exceptionHandled) {
094 // session was closed but no message received. This could be because the remote server had an internal error
095 // and could not return a response. We should count down to stop waiting for a response
096 if (LOG.isDebugEnabled()) {
097 LOG.debug("Channel closed but no message received from address: " + producer.getConfiguration().getAddress());
098 }
099 exchange.setException(new CamelExchangeException("No response received from remote server: " + producer.getConfiguration().getAddress(), exchange));
100 // signal callback
101 callback.done(false);
102 }
103 }
104
105 @Override
106 public void messageReceived(ChannelHandlerContext ctx, MessageEvent messageEvent) throws Exception {
107 messageReceived = true;
108
109 Object body = messageEvent.getMessage();
110 if (LOG.isDebugEnabled()) {
111 LOG.debug("Message received: " + body);
112 }
113
114 // if textline enabled then covert to a String which must be used for textline
115 if (producer.getConfiguration().isTextline()) {
116 try {
117 body = producer.getContext().getTypeConverter().mandatoryConvertTo(String.class, exchange, body);
118 } catch (NoTypeConversionAvailableException e) {
119 exchange.setException(e);
120 callback.done(false);
121 }
122 }
123
124
125 // set the result on either IN or OUT on the original exchange depending on its pattern
126 if (ExchangeHelper.isOutCapable(exchange)) {
127 NettyPayloadHelper.setOut(exchange, body);
128 } else {
129 NettyPayloadHelper.setIn(exchange, body);
130 }
131
132 try {
133 // should channel be closed after complete?
134 Boolean close;
135 if (ExchangeHelper.isOutCapable(exchange)) {
136 close = exchange.getOut().getHeader(NettyConstants.NETTY_CLOSE_CHANNEL_WHEN_COMPLETE, Boolean.class);
137 } else {
138 close = exchange.getIn().getHeader(NettyConstants.NETTY_CLOSE_CHANNEL_WHEN_COMPLETE, Boolean.class);
139 }
140
141 // should we disconnect, the header can override the configuration
142 boolean disconnect = producer.getConfiguration().isDisconnect();
143 if (close != null) {
144 disconnect = close;
145 }
146 if (disconnect) {
147 if (LOG.isDebugEnabled()) {
148 LOG.debug("Closing channel when complete at address: " + producer.getConfiguration().getAddress());
149 }
150 NettyHelper.close(ctx.getChannel());
151 }
152 } finally {
153 // signal callback
154 callback.done(false);
155 }
156 }
157
158 }