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