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 }