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.CamelException;
020    import org.apache.camel.Exchange;
021    import org.apache.camel.ExchangePattern;
022    import org.apache.camel.component.netty.NettyConstants;
023    import org.apache.camel.component.netty.NettyConsumer;
024    import org.apache.camel.component.netty.NettyHelper;
025    import org.apache.camel.component.netty.NettyPayloadHelper;
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.ChannelHandlerContext;
031    import org.jboss.netty.channel.ChannelPipelineCoverage;
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    @ChannelPipelineCoverage("all")
038    public class ServerChannelHandler extends SimpleChannelUpstreamHandler {
039        private static final transient Log LOG = LogFactory.getLog(ServerChannelHandler.class);
040        private NettyConsumer consumer;
041        private Logger noReplyLogger;
042    
043        public ServerChannelHandler(NettyConsumer consumer) {
044            super();
045            this.consumer = consumer;    
046            this.noReplyLogger = new Logger(LOG, consumer.getConfiguration().getNoReplyLogLevel());
047        }
048    
049        @Override
050        public void channelOpen(ChannelHandlerContext ctx, ChannelStateEvent channelStateEvent) throws Exception {
051            // to keep track of open sockets
052            consumer.getAllChannels().add(channelStateEvent.getChannel());
053        }
054    
055        @Override
056        public void channelClosed(ChannelHandlerContext ctx, ChannelStateEvent e) throws Exception {
057            LOG.debug("Channel closed: " + e.getChannel());
058        }
059    
060        @Override
061        public void exceptionCaught(ChannelHandlerContext ctx, ExceptionEvent exceptionEvent) throws Exception {
062            LOG.warn("Closing channel as an exception was thrown from Netty", exceptionEvent.getCause());
063    
064            // close channel in case an exception was thrown
065            NettyHelper.close(exceptionEvent.getChannel());
066        }
067        
068        @Override
069        public void messageReceived(ChannelHandlerContext ctx, MessageEvent messageEvent) throws Exception {
070            Object in = messageEvent.getMessage();
071            if (LOG.isDebugEnabled()) {
072                if (in instanceof byte[]) {
073                    // byte arrays is not readable so convert to string
074                    in = consumer.getEndpoint().getCamelContext().getTypeConverter().convertTo(String.class, in);
075                }
076                LOG.debug("Incoming message: " + in);
077            }
078            
079            // create Exchange and let the consumer process it
080            Exchange exchange = consumer.getEndpoint().createExchange(ctx, messageEvent);
081            if (consumer.getConfiguration().isSync()) {
082                exchange.setPattern(ExchangePattern.InOut);
083            }
084    
085            try {
086                consumer.getProcessor().process(exchange);
087            } catch (Throwable e) {
088                consumer.getExceptionHandler().handleException(e);
089            }
090    
091            // send back response if the communication is synchronous
092            if (consumer.getConfiguration().isSync()) {
093                sendResponse(messageEvent, exchange);
094            }
095        }
096    
097        private void sendResponse(MessageEvent messageEvent, Exchange exchange) throws Exception {
098            Object body;
099            if (ExchangeHelper.isOutCapable(exchange)) {
100                body = NettyPayloadHelper.getOut(consumer.getEndpoint(), exchange);
101            } else {
102                body = NettyPayloadHelper.getIn(consumer.getEndpoint(), exchange);
103            }
104    
105            boolean failed = exchange.isFailed();
106            if (failed && !consumer.getEndpoint().getConfiguration().isTransferExchange()) {
107                if (exchange.getException() != null) {
108                    body = exchange.getException();
109                } else {
110                    // failed and no exception, must be a fault
111                    body = exchange.getOut().getBody();
112                }
113            }
114    
115            if (body == null) {
116                noReplyLogger.log("No payload to send as reply for exchange: " + exchange);
117                if (consumer.getConfiguration().isDisconnectOnNoReply()) {
118                    // must close session if no data to write otherwise client will never receive a response
119                    // and wait forever (if not timing out)
120                    if (LOG.isDebugEnabled()) {
121                        LOG.debug("Closing channel as no payload to send as reply at address: " + messageEvent.getRemoteAddress());
122                    }
123                    NettyHelper.close(messageEvent.getChannel());
124                }
125            } else {
126                // we got a body to write
127                if (LOG.isDebugEnabled()) {
128                    LOG.debug("Writing body: " + body);
129                }
130                if (consumer.getConfiguration().getProtocol().equalsIgnoreCase("udp")) {
131                    NettyHelper.writeBody(messageEvent.getChannel(), messageEvent.getRemoteAddress(), body, exchange);
132                } else {
133                    NettyHelper.writeBody(messageEvent.getChannel(), null, body, exchange);
134                }
135            }
136    
137            // should channel be closed after complete?
138            Boolean close;
139            if (ExchangeHelper.isOutCapable(exchange)) {
140                close = exchange.getOut().getHeader(NettyConstants.NETTY_CLOSE_CHANNEL_WHEN_COMPLETE, Boolean.class);
141            } else {
142                close = exchange.getIn().getHeader(NettyConstants.NETTY_CLOSE_CHANNEL_WHEN_COMPLETE, Boolean.class);
143            }
144    
145            // should we disconnect, the header can override the configuration
146            boolean disconnect = consumer.getConfiguration().isDisconnect();
147            if (close != null) {
148                disconnect = close;
149            }
150            if (disconnect) {
151                if (LOG.isDebugEnabled()) {
152                    LOG.debug("Closing channel when complete at address: " + messageEvent.getRemoteAddress());
153                }
154                NettyHelper.close(messageEvent.getChannel());
155            }
156        }
157    
158    }