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.Logger;
027    import org.apache.camel.util.ExchangeHelper;
028    import org.apache.commons.logging.Log;
029    import org.apache.commons.logging.LogFactory;
030    import org.jboss.netty.channel.ChannelHandler;
031    import org.jboss.netty.channel.ChannelHandlerContext;
032    import org.jboss.netty.channel.ChannelStateEvent;
033    import org.jboss.netty.channel.ExceptionEvent;
034    import org.jboss.netty.channel.MessageEvent;
035    import org.jboss.netty.channel.SimpleChannelUpstreamHandler;
036    
037    /**
038     * Server handler which is shared
039     */
040    @ChannelHandler.Sharable
041    public class ServerChannelHandler extends SimpleChannelUpstreamHandler {
042        private static final transient Log LOG = LogFactory.getLog(ServerChannelHandler.class);
043        private NettyConsumer consumer;
044        private Logger noReplyLogger;
045    
046        public ServerChannelHandler(NettyConsumer consumer) {
047            super();
048            this.consumer = consumer;    
049            this.noReplyLogger = new Logger(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    }