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