/** * Copyright (c) 2015, WSO2 Inc. (http://www.wso2.org) All Rights Reserved. * * WSO2 Inc. 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.synapse.message.store.impl.rabbitmq; import com.rabbitmq.client.Channel; import com.rabbitmq.client.Connection; import com.rabbitmq.client.GetResponse; import com.rabbitmq.client.ShutdownSignalException; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.apache.synapse.MessageContext; import org.apache.synapse.message.MessageConsumer; import org.apache.synapse.message.store.impl.commons.MessageConverter; import org.apache.synapse.message.store.impl.commons.StorableMessage; import java.io.ByteArrayInputStream; import java.io.IOException; import java.io.ObjectInput; import java.io.ObjectInputStream; public class RabbitMQConsumer implements MessageConsumer { private static final Log logger = LogFactory.getLog(RabbitMQConsumer.class.getName()); private Connection connection; private Channel channel; private RabbitMQStore store; private String queueName; private String idString; private boolean isInitialized; /** * Did last receive() call cause an error? */ private boolean isReceiveError; /** * Holds the last message read from the message store. */ private CachedMessage cachedMessage; public RabbitMQConsumer(RabbitMQStore store) { if (store == null) { logger.error("Cannot initialize."); return; } this.store = store; cachedMessage = new CachedMessage(); isReceiveError = false; isInitialized = true; } public MessageContext receive() { if (!checkConnection()) { if (!reconnect()) { if (logger.isDebugEnabled()) { logger.debug(getId() + " cannot receive message from store. Can not reconnect."); } return null; } else { logger.info(getId() + " reconnected to store."); isReceiveError = false; } } //setting channel if (channel != null) { if (!channel.isOpen()) { if (!setChannel()) { logger.info(getId() + " unable to create the channel."); return null; } } } else { if (!setChannel()) { logger.info(getId() + " unable to create the channel."); return null; } } //receive messages try { GetResponse delivery = null; delivery = channel.basicGet(queueName, false); if (delivery != null) { //deserilizing message StorableMessage storableMessage = null; ByteArrayInputStream bis = new ByteArrayInputStream(delivery.getBody()); ObjectInput in = new ObjectInputStream(bis); try { storableMessage = (StorableMessage) in.readObject(); } catch (ClassNotFoundException e) { logger.error(getId() + "unable to read the stored message" + e); channel.basicAck(delivery.getEnvelope().getDeliveryTag(), false); } bis.close(); in.close(); org.apache.axis2.context.MessageContext axis2Mc = store.newAxis2Mc(); MessageContext synapseMc = store.newSynapseMc(axis2Mc); synapseMc = MessageConverter.toMessageContext(storableMessage, axis2Mc, synapseMc); updateCache(delivery, synapseMc, null, false); if (logger.isDebugEnabled()) { logger.debug(getId() + " Received MessageId:" + delivery.getProps().getMessageId()); } return synapseMc; } } catch (ShutdownSignalException sse) { logger.error(getId() + " connection error when receiving messages" + sse); } catch (IOException ioe) { logger.error(getId() + " connection error when receiving messages" + ioe); } return null; } public boolean ack() { boolean result = cachedMessage.ack(); if (result) { store.dequeued(); } return result; } public boolean cleanup() { if (logger.isDebugEnabled()) { logger.debug(getId() + " cleaning up..."); } boolean result = store.cleanup(connection, true); if (result) { connection = null; return true; } return false; } @Override public boolean isAlive() { return true; } public RabbitMQConsumer setConnection(Connection connection) { this.connection = connection; return this; } public void setQueueName(String queueName) { this.queueName = queueName; } public boolean setChannel() { if (connection != null && connection.isOpen()) { try { this.channel = connection.createChannel(); return true; } catch (IOException e) { return false; } } return false; } public void setId(int id) { idString = "[" + store.getName() + "-C-" + id + "]"; } public String getId() { return idString; } public Connection getConnection() { return connection; } private boolean checkConnection() { if (connection == null) { if (logger.isDebugEnabled()) { logger.debug(getId() + " cannot proceed. RabbitMQ Connection is null."); } return false; } if (!connection.isOpen()) { if (logger.isDebugEnabled()) { logger.debug(getId() + " cannot proceed. RabbitMQ Connection is closed."); } return false; } return true; } private boolean reconnect() { RabbitMQConsumer consumer = (RabbitMQConsumer) store.getConsumer(); if (consumer.getConnection() == null) { if (logger.isDebugEnabled()) { logger.debug(getId() + " could not reconnect to the broker."); } return false; } setConnection(consumer.getConnection()); if (logger.isDebugEnabled()) { logger.debug(getId() + " ===> " + consumer.getId()); } idString = consumer.getId(); return true; } private void updateCache(GetResponse delivery, MessageContext synCtx, String messageId, boolean receiveError) throws IOException { isReceiveError = receiveError; cachedMessage.setMessage(delivery); cachedMessage.setMc(synCtx); cachedMessage.setId(messageId); } /** * This is used to store the last received message * if the message is successfully sent to the endpoint, ack will sent to the store * in order to delete message from the queue * <p/> * In RabbitMQ message ack should be using the same channel which was consumed the message * There for the consumed channel will also stored without closing until the message is ackd */ private final class CachedMessage { private GetResponse message = null; private MessageContext mc = null; private String id = ""; public CachedMessage setMessage(GetResponse message) { this.message = message; return this; } public boolean ack() { if (message != null && channel != null && channel.isOpen()) { try { channel.basicAck(message.getEnvelope().getDeliveryTag(), false); return true; } catch (IOException e) { logger.error(getId() + " cannot ack last read message. Error:" + e.getLocalizedMessage(), e); return false; } } return false; } public GetResponse getMessage() { return message; } public CachedMessage setMc(MessageContext mc) { this.mc = mc; return this; } public CachedMessage setId(String id) { this.id = id; return this; } public String getId() { return id; } } }