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 }