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.mina;
018    
019    import java.net.SocketAddress;
020    
021    import org.apache.camel.CamelException;
022    import org.apache.camel.Exchange;
023    import org.apache.camel.Processor;
024    import org.apache.camel.impl.DefaultConsumer;
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.apache.mina.common.IoAcceptor;
030    import org.apache.mina.common.IoHandlerAdapter;
031    import org.apache.mina.common.IoSession;
032    
033    /**
034     * A {@link org.apache.camel.Consumer Consumer} implementation for Apache MINA.
035     *
036     * @version $Revision: 19337 $
037     */
038    public class MinaConsumer extends DefaultConsumer {
039        private static final transient Log LOG = LogFactory.getLog(MinaConsumer.class);
040    
041        private final MinaEndpoint endpoint;
042        private final SocketAddress address;
043        private final IoAcceptor acceptor;
044        private boolean sync;
045        private Logger noReplyLogger;
046    
047        public MinaConsumer(final MinaEndpoint endpoint, Processor processor) {
048            super(endpoint, processor);
049            this.endpoint = endpoint;
050            this.address = endpoint.getAddress();
051            this.acceptor = endpoint.getAcceptor();
052            this.sync = endpoint.getConfiguration().isSync();
053            this.noReplyLogger = new Logger(LOG, endpoint.getConfiguration().getNoReplyLogLevel());
054        }
055    
056        @Override
057        protected void doStart() throws Exception {
058            super.doStart();
059            if (LOG.isInfoEnabled()) {
060                LOG.info("Binding to server address: " + address + " using acceptor: " + acceptor);
061            }
062    
063            acceptor.bind(address, new ReceiveHandler(), endpoint.getAcceptorConfig());
064        }
065    
066        @Override
067        protected void doStop() throws Exception {
068            if (LOG.isInfoEnabled()) {
069                LOG.info("Unbinding from server address: " + address + " using acceptor: " + acceptor);
070            }
071            acceptor.unbind(address);
072            super.doStop();
073        }
074    
075        /**
076         * Handles consuming messages and replying if the exchange is out capable.
077         */
078        private final class ReceiveHandler extends IoHandlerAdapter {
079    
080            @Override
081            public void exceptionCaught(IoSession session, Throwable cause) throws Exception {
082                // close invalid session
083                if (session != null) {
084                    LOG.debug("Closing session as an exception was thrown from MINA");
085                    session.close();
086                }
087    
088                // must wrap and rethrow since cause can be of Throwable and we must only throw Exception
089                throw new CamelException(cause);
090            }
091    
092            @Override
093            public void messageReceived(IoSession session, Object object) throws Exception {
094                // log what we received
095                if (LOG.isDebugEnabled()) {
096                    Object in = object;
097                    if (in instanceof byte[]) {
098                        // byte arrays is not readable so convert to string
099                        in = endpoint.getCamelContext().getTypeConverter().convertTo(String.class, in);
100                    }
101                    LOG.debug("Received body: " + in);
102                }
103                Exchange exchange = endpoint.createExchange(session, object);
104                //Set the exchange charset property for converting
105                if (endpoint.getConfiguration().getCharsetName() != null) {
106                    exchange.setProperty(Exchange.CHARSET_NAME, endpoint.getConfiguration().getCharsetName());
107                }
108    
109                try {
110                    getProcessor().process(exchange);
111                } catch (Throwable e) {
112                    getExceptionHandler().handleException(e);
113                }
114    
115                // if sync then we should return a response
116                if (sync) {
117                    Object body;
118                    if (ExchangeHelper.isOutCapable(exchange)) {
119                        body = MinaPayloadHelper.getOut(endpoint, exchange);
120                    } else {
121                        body = MinaPayloadHelper.getIn(endpoint, exchange);
122                    }
123    
124                    boolean failed = exchange.isFailed();
125                    if (failed && !endpoint.getConfiguration().isTransferExchange()) {
126                        if (exchange.getException() != null) {
127                            body = exchange.getException();
128                        } else {
129                            // failed and no exception, must be a fault
130                            body = exchange.getOut().getBody();
131                        }
132                    }
133    
134                    if (body == null) {
135                        noReplyLogger.log("No payload to send as reply for exchange: " + exchange);
136                        if (endpoint.getConfiguration().isDisconnectOnNoReply()) {
137                            // must close session if no data to write otherwise client will never receive a response
138                            // and wait forever (if not timing out)
139                            if (LOG.isDebugEnabled()) {
140                                LOG.debug("Closing session as no payload to send as reply at address: " + address);
141                            }
142                            session.close();
143                        }
144                    } else {
145                        // we got a response to write
146                        if (LOG.isDebugEnabled()) {
147                            LOG.debug("Writing body: " + body);
148                        }
149                        MinaHelper.writeBody(session, body, exchange);
150                    }
151                }
152    
153                // should session be closed after complete?
154                Boolean close;
155                if (ExchangeHelper.isOutCapable(exchange)) {
156                    close = exchange.getOut().getHeader(MinaConstants.MINA_CLOSE_SESSION_WHEN_COMPLETE, Boolean.class);
157                } else {
158                    close = exchange.getIn().getHeader(MinaConstants.MINA_CLOSE_SESSION_WHEN_COMPLETE, Boolean.class);
159                }
160    
161                // should we disconnect, the header can override the configuration
162                boolean disconnect = endpoint.getConfiguration().isDisconnect();
163                if (close != null) {
164                    disconnect = close;
165                }
166                if (disconnect) {
167                    if (LOG.isDebugEnabled()) {
168                        LOG.debug("Closing session when complete at address: " + address);
169                    }
170                    session.close();
171                }
172            }
173        }
174    
175    }
176