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.Exchange; 020 import org.apache.camel.ExchangePattern; 021 import org.apache.camel.component.netty.NettyConstants; 022 import org.apache.camel.component.netty.NettyConsumer; 023 import org.apache.camel.component.netty.NettyHelper; 024 import org.apache.camel.component.netty.NettyPayloadHelper; 025 import org.apache.camel.processor.Logger; 026 import org.apache.camel.util.ExchangeHelper; 027 import org.apache.commons.logging.Log; 028 import org.apache.commons.logging.LogFactory; 029 import org.jboss.netty.channel.ChannelHandler; 030 import org.jboss.netty.channel.ChannelHandlerContext; 031 import org.jboss.netty.channel.ChannelStateEvent; 032 import org.jboss.netty.channel.ExceptionEvent; 033 import org.jboss.netty.channel.MessageEvent; 034 import org.jboss.netty.channel.SimpleChannelUpstreamHandler; 035 036 /** 037 * Server handler which is shared 038 */ 039 @ChannelHandler.Sharable 040 public class ServerChannelHandler extends SimpleChannelUpstreamHandler { 041 private static final transient Log LOG = LogFactory.getLog(ServerChannelHandler.class); 042 private NettyConsumer consumer; 043 private Logger noReplyLogger; 044 045 public ServerChannelHandler(NettyConsumer consumer) { 046 super(); 047 this.consumer = consumer; 048 this.noReplyLogger = new Logger(LOG, consumer.getConfiguration().getNoReplyLogLevel()); 049 } 050 051 @Override 052 public void channelOpen(ChannelHandlerContext ctx, ChannelStateEvent e) throws Exception { 053 if (LOG.isTraceEnabled()) { 054 LOG.trace("Channel open: " + e.getChannel()); 055 } 056 // to keep track of open sockets 057 consumer.getAllChannels().add(e.getChannel()); 058 } 059 060 @Override 061 public void channelClosed(ChannelHandlerContext ctx, ChannelStateEvent e) throws Exception { 062 if (LOG.isTraceEnabled()) { 063 LOG.trace("Channel closed: " + e.getChannel()); 064 } 065 } 066 067 @Override 068 public void exceptionCaught(ChannelHandlerContext ctx, ExceptionEvent exceptionEvent) throws Exception { 069 // only close if we are still allowed to run 070 if (consumer.isRunAllowed()) { 071 LOG.warn("Closing channel as an exception was thrown from Netty", exceptionEvent.getCause()); 072 073 // close channel in case an exception was thrown 074 NettyHelper.close(exceptionEvent.getChannel()); 075 } 076 } 077 078 @Override 079 public void messageReceived(ChannelHandlerContext ctx, MessageEvent messageEvent) throws Exception { 080 Object in = messageEvent.getMessage(); 081 if (LOG.isDebugEnabled()) { 082 LOG.debug("Incoming message: " + in); 083 } 084 085 // create Exchange and let the consumer process it 086 Exchange exchange = consumer.getEndpoint().createExchange(ctx, messageEvent); 087 if (consumer.getConfiguration().isSync()) { 088 exchange.setPattern(ExchangePattern.InOut); 089 } 090 // set the exchange charset property for converting 091 if (consumer.getConfiguration().getCharsetName() != null) { 092 exchange.setProperty(Exchange.CHARSET_NAME, consumer.getConfiguration().getCharsetName()); 093 } 094 095 try { 096 consumer.getProcessor().process(exchange); 097 } catch (Throwable e) { 098 consumer.getExceptionHandler().handleException(e); 099 } 100 101 // send back response if the communication is synchronous 102 if (consumer.getConfiguration().isSync()) { 103 sendResponse(messageEvent, exchange); 104 } 105 } 106 107 private void sendResponse(MessageEvent messageEvent, Exchange exchange) throws Exception { 108 Object body; 109 if (ExchangeHelper.isOutCapable(exchange)) { 110 body = NettyPayloadHelper.getOut(consumer.getEndpoint(), exchange); 111 } else { 112 body = NettyPayloadHelper.getIn(consumer.getEndpoint(), exchange); 113 } 114 115 boolean failed = exchange.isFailed(); 116 if (failed && !consumer.getEndpoint().getConfiguration().isTransferExchange()) { 117 if (exchange.getException() != null) { 118 body = exchange.getException(); 119 } else { 120 // failed and no exception, must be a fault 121 body = exchange.getOut().getBody(); 122 } 123 } 124 125 if (body == null) { 126 noReplyLogger.log("No payload to send as reply for exchange: " + exchange); 127 if (consumer.getConfiguration().isDisconnectOnNoReply()) { 128 // must close session if no data to write otherwise client will never receive a response 129 // and wait forever (if not timing out) 130 if (LOG.isDebugEnabled()) { 131 LOG.debug("Closing channel as no payload to send as reply at address: " + messageEvent.getRemoteAddress()); 132 } 133 NettyHelper.close(messageEvent.getChannel()); 134 } 135 } else { 136 // if textline enabled then covert to a String which must be used for textline 137 if (consumer.getConfiguration().isTextline()) { 138 body = NettyHelper.getTextlineBody(body, exchange, consumer.getConfiguration().getDelimiter(), consumer.getConfiguration().isAutoAppendDelimiter()); 139 } 140 141 // we got a body to write 142 if (LOG.isDebugEnabled()) { 143 LOG.debug("Writing body: " + body); 144 } 145 if (consumer.getConfiguration().isTcp()) { 146 NettyHelper.writeBodySync(messageEvent.getChannel(), null, body, exchange); 147 } else { 148 NettyHelper.writeBodySync(messageEvent.getChannel(), messageEvent.getRemoteAddress(), body, exchange); 149 } 150 } 151 152 // should channel be closed after complete? 153 Boolean close; 154 if (ExchangeHelper.isOutCapable(exchange)) { 155 close = exchange.getOut().getHeader(NettyConstants.NETTY_CLOSE_CHANNEL_WHEN_COMPLETE, Boolean.class); 156 } else { 157 close = exchange.getIn().getHeader(NettyConstants.NETTY_CLOSE_CHANNEL_WHEN_COMPLETE, Boolean.class); 158 } 159 160 // should we disconnect, the header can override the configuration 161 boolean disconnect = consumer.getConfiguration().isDisconnect(); 162 if (close != null) { 163 disconnect = close; 164 } 165 if (disconnect) { 166 if (LOG.isDebugEnabled()) { 167 LOG.debug("Closing channel when complete at address: " + messageEvent.getRemoteAddress()); 168 } 169 NettyHelper.close(messageEvent.getChannel()); 170 } 171 } 172 173 }