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    }