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 }