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 }