/** * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.camel.component.mina; import java.net.SocketAddress; import org.apache.camel.CamelException; import org.apache.camel.Exchange; import org.apache.camel.Processor; import org.apache.camel.impl.DefaultConsumer; import org.apache.camel.util.CamelLogger; import org.apache.camel.util.ExchangeHelper; import org.apache.camel.util.IOHelper; import org.apache.mina.common.ConnectFuture; import org.apache.mina.common.IoAcceptor; import org.apache.mina.common.IoConnector; import org.apache.mina.common.IoHandler; import org.apache.mina.common.IoHandlerAdapter; import org.apache.mina.common.IoSession; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** * A {@link org.apache.camel.Consumer Consumer} implementation for Apache MINA. * * @version */ public class MinaConsumer extends DefaultConsumer { private static final Logger LOG = LoggerFactory.getLogger(MinaConsumer.class); private final SocketAddress address; private final IoAcceptor acceptor; private final IoConnector connector; private final boolean sync; private final String protocol; private final boolean clientMode; private IoSession session; private CamelLogger noReplyLogger; public MinaConsumer(final MinaEndpoint endpoint, Processor processor) { super(endpoint, processor); this.address = endpoint.getAddress(); this.acceptor = endpoint.getAcceptor(); this.connector = endpoint.getConnector(); this.protocol = endpoint.getConfiguration().getProtocol(); this.clientMode = endpoint.getConfiguration().isClientMode(); this.sync = endpoint.getConfiguration().isSync(); this.noReplyLogger = new CamelLogger(LOG, endpoint.getConfiguration().getNoReplyLogLevel()); } @Override protected void doStart() throws Exception { super.doStart(); LOG.info("Binding to server address: {} using acceptor: {}", address, acceptor); IoHandler handler = new ReceiveHandler(); if (protocol.equals("tcp") && clientMode) { ConnectFuture future = connector.connect(address, handler, getEndpoint().getConnectorConfig()); future.join(); session = future.getSession(); } else { acceptor.bind(address, handler, getEndpoint().getAcceptorConfig()); } } @Override protected void doStop() throws Exception { LOG.info("Unbinding from server address: {} using acceptor: {}", address, acceptor); if (protocol.equals("tcp") && clientMode) { if (session != null) { session.close(); session = null; } } else { acceptor.unbind(address); } super.doStop(); } @Override public MinaEndpoint getEndpoint() { return (MinaEndpoint) super.getEndpoint(); } /** * Handles consuming messages and replying if the exchange is out capable. */ private final class ReceiveHandler extends IoHandlerAdapter { @Override public void exceptionCaught(IoSession session, Throwable cause) throws Exception { // close invalid session if (session != null) { LOG.debug("Closing session as an exception was thrown from MINA"); session.close(); } // must wrap and rethrow since cause can be of Throwable and we must only throw Exception throw new CamelException(cause); } @Override public void messageReceived(IoSession session, Object object) throws Exception { // log what we received if (LOG.isDebugEnabled()) { Object in = object; if (in instanceof byte[]) { // byte arrays is not readable so convert to string in = getEndpoint().getCamelContext().getTypeConverter().convertTo(String.class, in); } LOG.debug("Received body: {}", in); } Exchange exchange = getEndpoint().createExchange(session, object); //Set the exchange charset property for converting if (getEndpoint().getConfiguration().getCharsetName() != null) { exchange.setProperty(Exchange.CHARSET_NAME, IOHelper.normalizeCharset(getEndpoint().getConfiguration().getCharsetName())); } try { getProcessor().process(exchange); } catch (Throwable e) { getExceptionHandler().handleException(e); } // if sync then we should return a response if (sync) { Object body; if (exchange.hasOut()) { body = MinaPayloadHelper.getOut(getEndpoint(), exchange); } else { body = MinaPayloadHelper.getIn(getEndpoint(), exchange); } boolean failed = exchange.isFailed(); if (failed && !getEndpoint().getConfiguration().isTransferExchange()) { if (exchange.getException() != null) { body = exchange.getException(); } else { // failed and no exception, must be a fault body = exchange.getOut().getBody(); } } if (body == null) { noReplyLogger.log("No payload to send as reply for exchange: " + exchange); if (getEndpoint().getConfiguration().isDisconnectOnNoReply()) { // must close session if no data to write otherwise client will never receive a response // and wait forever (if not timing out) LOG.debug("Closing session as no payload to send as reply at address: {}", address); session.close(); } } else { // we got a response to write LOG.debug("Writing body: {}", body); MinaHelper.writeBody(session, body, exchange); } } // should session be closed after complete? Boolean close; if (ExchangeHelper.isOutCapable(exchange)) { close = exchange.getOut().getHeader(MinaConstants.MINA_CLOSE_SESSION_WHEN_COMPLETE, Boolean.class); } else { close = exchange.getIn().getHeader(MinaConstants.MINA_CLOSE_SESSION_WHEN_COMPLETE, Boolean.class); } // should we disconnect, the header can override the configuration boolean disconnect = getEndpoint().getConfiguration().isDisconnect(); if (close != null) { disconnect = close; } if (disconnect) { LOG.debug("Closing session when complete at address: {}", address); session.close(); } } } }