/* * Copyright (c) 2016, 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.axis2.transport.rabbitmq; import com.rabbitmq.client.AMQP; import com.rabbitmq.client.AlreadyClosedException; import com.rabbitmq.client.Channel; import com.rabbitmq.client.Connection; import com.rabbitmq.client.ConsumerCancelledException; import com.rabbitmq.client.QueueingConsumer; import com.rabbitmq.client.ShutdownSignalException; import org.apache.axiom.om.OMException; import org.apache.axis2.transport.base.threads.WorkerPool; import org.apache.axis2.transport.rabbitmq.utils.AxisRabbitMQException; import org.apache.axis2.transport.rabbitmq.utils.RabbitMQConstants; import org.apache.axis2.transport.rabbitmq.utils.RabbitMQUtils; import org.apache.commons.lang.StringUtils; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import java.io.IOException; import java.net.SocketException; import java.util.ArrayList; import java.util.Collections; import java.util.Hashtable; import java.util.List; import java.util.Map; import java.util.concurrent.TimeoutException; /** * Each service will have one ServiceTaskManager instance that will send, manage and also destroy * idle tasks created for it, for message receipt. It uses the MessageListenerTask to poll for the * RabbitMQ AMQP Listening destination and consume messages. The consumed messages is build and sent to * axis2 engine for processing */ public class ServiceTaskManager { private static final Log log = LogFactory.getLog(ServiceTaskManager.class); private static final int STATE_STOPPED = 0; private static final int STATE_STARTING = 1; private static final int STATE_STARTED = 2; private static final int STATE_PAUSED = 3; private static final int STATE_SHUTTING_DOWN = 4; private static final int STATE_FAULTY = 5; private static final int STATE_DUMMY = -1; /** * Number of concurrent consumers - for PubSub, this should be 1 to prevent multiple receipt */ private int concurrentConsumers = RabbitMQConstants.CONCURRENT_CONSUMER_COUNT_DEFAULT; /** * The JMS Connection shared between multiple polling tasks - when enabled (reccomended) */ private volatile Connection sharedConnection = null; /** * JMS Resource cache level - Connection, Session, Consumer. Auto will select safe default */ private volatile int cacheLevel = RabbitMQConstants.CACHE_NONE; private WorkerPool workerPool = null; private volatile String serviceName; private volatile Hashtable<String, String> rabbitMQProperties = new Hashtable<>(); private final RabbitMQConnectionFactory rabbitMQConnectionFactory; private final List<MessageListenerTask> pollingTasks = Collections.synchronizedList(new ArrayList<MessageListenerTask>()); private volatile RabbitMQMessageReceiver rabbitMQMessageReceiver; private State serviceTaskManagerState; /** * Class to keep States of the ServiceTaskManager and MessageListner */ private class State { private volatile int state = STATE_STOPPED; public State(boolean update, int state) { getUpdateState(update, state); } /** * This method will get or update the states in thread safe manner * * @param update * @param state * @return state */ public int getUpdateState(boolean update, int state) { synchronized (this) { if (update) { this.state = state; } return this.state; } } /** * This method is to get state * * @return state */ public int getState() { return this.state; } /** * This method is to check whether the state is active or not * * @return true if active and false otherwise */ public boolean isActive() { if (this.state == STATE_STARTED) { return true; } return false; } } /** * Constructor of service task manager. * * @param rabbitMQConnectionFactory */ public ServiceTaskManager( RabbitMQConnectionFactory rabbitMQConnectionFactory) { this.rabbitMQConnectionFactory = rabbitMQConnectionFactory; this.serviceTaskManagerState = new State(true, STATE_STOPPED); } /** * Start the Task Manager by adding a new MessageListenerTasks to the worker pool. */ public void start() { serviceTaskManagerState.getUpdateState(true, STATE_STARTING); //set the concurrentConsumerCount value so that, serviceTask manager will start that number of messagelistners String concurrentConsumerCountString = rabbitMQProperties.get(RabbitMQConstants.CONCURRENT_CONSUMER_COUNT); if (concurrentConsumerCountString != null && !"".equals(concurrentConsumerCountString)) { try { concurrentConsumers = Integer.parseInt(concurrentConsumerCountString); } catch (NumberFormatException e) { concurrentConsumers = RabbitMQConstants.CONCURRENT_CONSUMER_COUNT_DEFAULT; log.warn("Can't parse given RabbitMQ concurrentConsumerCount value as a integer, hence using " + "channel with default (one MessageListner), provided concurrentConsumerCount value - " + concurrentConsumerCountString); } } /** * set the cacheLevel value so that, message listners may or may not share same connection depending on this * value, 0 means no cache (connection per listner) and 1 means shared connection (connection shared between * listners) */ String cacheLevelString = rabbitMQProperties.get(RabbitMQConstants.CACHE_LEVEL); if (cacheLevelString != null && !"".equals(cacheLevelString)) { try { cacheLevel = Integer.parseInt(cacheLevelString); } catch (NumberFormatException e) { cacheLevel = RabbitMQConstants.CACHE_NONE; log.warn("Can't parse given RabbitMQ cacheLevel value as a integer, hence using " + "default (CACHE_NONE - no cache), provided cacheLevel value - " + cacheLevelString); } } for (int i = 0; i < concurrentConsumers; i++) { workerPool.execute(new MessageListenerTask()); } serviceTaskManagerState.getUpdateState(true, STATE_STARTED); } /** * Stop the consumer by changing the state, later closing the connections. */ public void stop() { serviceTaskManagerState.getUpdateState(true, STATE_SHUTTING_DOWN); synchronized (pollingTasks) { for (MessageListenerTask lstTask : pollingTasks) { lstTask.requestShutdown(); } try { //Waiting DEFAULT_WAIT_TIME_BEFORE_CLOSING before starting to close connections Thread.sleep(RabbitMQConstants.DEFAULT_WAIT_TIME_BEFORE_CLOSING); } catch (InterruptedException e) { log.warn("Closing connections before waiting for them to be closed automatically, may throw " + "exceptions " + serviceName, e); } if (cacheLevel >= RabbitMQConstants.CACHE_CONNECTION) { closeSharedConnection(); } else { for (MessageListenerTask lstTask : pollingTasks) { lstTask.closeConnection(); } } } serviceTaskManagerState.getUpdateState(true, STATE_STOPPED); } /** * Helper method to close the shared connection(if connection caching is enabled) */ private void closeSharedConnection() { if (sharedConnection != null) { try { if (sharedConnection.isOpen()) { sharedConnection.close(); log.info("RabbitMQ sharedConnection closed for service " + serviceName); } } catch (AlreadyClosedException e) { log.warn("Error while closing sharedConnection, AlreadyClosedException, service - " + serviceName + ", Listner id - " + Thread.currentThread().getId()); if (log.isDebugEnabled()) { log.debug("Error while closing sharedConnection, AlreadyClosedException, service - " + serviceName + ", Listner id - " + Thread.currentThread().getId(), e); } } catch (ShutdownSignalException ex) { log.warn("Error while closing sharedConnection, ShutdownSignalException, service - " + serviceName + ", Listner id - " + Thread.currentThread().getId()); if (log.isDebugEnabled()) { log.debug("Error while closing sharedConnection, ShutdownSignalException, service - " + serviceName + ", Listner id - " + Thread.currentThread().getId(), ex); } } catch (SocketException exx) { log.warn("Error while closing sharedConnection, SocketException, service - " + serviceName + ", Listner id - " + Thread.currentThread().getId()); if (log.isDebugEnabled()) { log.debug("Error while closing sharedConnection, SocketException, service - " + serviceName + ", Listner id - " + Thread.currentThread().getId(), exx); } } catch (IOException e) { log.warn("Error while closing sharedConnection, IOException, service - " + serviceName + ", Listner id - " + Thread.currentThread().getId()); if (log.isDebugEnabled()) { log.debug("Error while closing sharedConnection, IOException, service - " + serviceName + ", Listner id - " + Thread.currentThread().getId(), e); } } finally { sharedConnection = null; } } } @Deprecated public synchronized void pause() { //TODO implement me .. } @Deprecated public synchronized void resume() { //TODO implement me .. } public void setWorkerPool(WorkerPool workerPool) { this.workerPool = workerPool; } public void setRabbitMQMessageReceiver(RabbitMQMessageReceiver rabbitMQMessageReceiver) { this.rabbitMQMessageReceiver = rabbitMQMessageReceiver; } public Hashtable<String, String> getRabbitMQProperties() { return rabbitMQProperties; } public void addRabbitMQProperties(Map<String, String> rabbitMQProperties) { this.rabbitMQProperties.putAll(rabbitMQProperties); } public void removeAMQPProperties(String key) { this.rabbitMQProperties.remove(key); } public String getServiceName() { return serviceName; } public void setServiceName(String serviceName) { this.serviceName = serviceName; } /** * The actual threads/tasks that perform message polling */ private class MessageListenerTask implements Runnable { private volatile int listnerState; private String queueName, routeKey, exchangeName; private boolean autoAck = false; private Connection localConnection = null; private int qos = -1; //-1 means qos is not specified, in that case only basic qos will be applied to channel private QueueingConsumer queueingConsumer; private volatile boolean connected = false; MessageListenerTask() { synchronized (pollingTasks) { getUpdateListnerState(true, STATE_STOPPED); pollingTasks.add(this); } } @Deprecated public void pause() { //TODO implement me } @Deprecated public void resume() { //TODO implement me } /** * Execute the polling worker task * Actual reconnection is not happening in here, re-connection is handled by the rabbitmq connection * itself. The only use of recovery interval in here is that to output the info message */ public void run() { try { getUpdateListnerState(true, STATE_STARTED); while (isListnerActive()) { try { initConsumer(); startConsumer(); } catch (AlreadyClosedException e) { if (isServiceTaskManagerActive()) { log.error("Error, Connection already closed " + serviceName + ", Listner id - " + Thread.currentThread().getId(), e); if (!retryIfNotStopped()) { break; } } else { log.warn("Error, Connection already closed " + serviceName + ", Listner id - " + Thread.currentThread().getId()); if (log.isDebugEnabled()) { log.debug("Error, Connection already closed " + serviceName + ", Listner id - " + Thread.currentThread().getId(), e); } } } catch (ShutdownSignalException sse) { if (sse.isInitiatedByApplication()) { log.warn("RabbitMQ Listener of the service " + serviceName + " was disconnected, " + "Shutdown signal issued, Listner id - " + Thread.currentThread().getId()); if (log.isDebugEnabled()) { log.debug("RabbitMQ Listener of the service " + serviceName + " was disconnected, " + "Shutdown signal issued, Listner id - " + Thread.currentThread().getId(), sse); } getUpdateListnerState(true, STATE_STOPPED); break; } else if (!isServiceTaskManagerActive()) { log.warn("RabbitMQ Listener of the service " + serviceName + " was disconnected, Listner id - " + Thread.currentThread().getId()); if (log.isDebugEnabled()) { log.debug("RabbitMQ Listener of the service " + serviceName + " was disconnected, Listner id - " + Thread.currentThread().getId(), sse); } } else { log.error("RabbitMQ Listener of the service " + serviceName + " was disconnected, " + "task manager still active, Listner id - " + Thread.currentThread().getId(), sse); if (!retryIfNotStopped()) { break; } } } catch (OMException e) { log.error("Invalid Message Format while Consuming the message, Listner id - " + Thread.currentThread().getId(), e); } catch (SocketException exx) { if (!isServiceTaskManagerActive()) { log.warn("RabbitMQ listner disconnected, service - " + serviceName + ", Listner id - " + Thread.currentThread().getId()); if (log.isDebugEnabled()) { log.debug("RabbitMQ listner disconnected, service - " + serviceName + ", Listner id - " + Thread.currentThread().getId(), exx); } } else { log.error("RabbitMQ listner disconnected, service - " + serviceName + ", Listner id - " + Thread.currentThread().getId(), exx); } } catch (IOException e) { if (e.getCause() instanceof ShutdownSignalException && ((ShutdownSignalException) e.getCause()).isInitiatedByApplication()) { log.warn("RabbitMQ Listener of the service " + serviceName + " was disconnected, " + "Shutdown signal issued, Listner id - " + Thread.currentThread().getId()); if (log.isDebugEnabled()) { log.debug("RabbitMQ Listener of the service " + serviceName + " was disconnected, " + "Shutdown signal issued, Listner id - " + Thread.currentThread().getId(), e); } getUpdateListnerState(true, STATE_STOPPED); break; } else if (!isServiceTaskManagerActive()) { log.warn("RabbitMQ Listener of the service " + serviceName + " was disconnected, " + "IOException occurred, Listner id - " + Thread.currentThread().getId()); if (log.isDebugEnabled()) { log.debug("RabbitMQ Listener of the service " + serviceName + " was disconnected, " + "IOException occurred, Listner id - " + Thread.currentThread().getId(), e); } } else { log.error("RabbitMQ Listener of the service " + serviceName + " was disconnected, " + "IOException occurred, but task manager still active, Listner id " + Thread.currentThread().getId(), e); if (!retryIfNotStopped()) { break; } } } catch (AxisRabbitMQException e) { if (isServiceTaskManagerActive()) { log.error("Error, Connection closed, AxisRabbitMQException occurred, service " + serviceName + ", Listner id - " + Thread.currentThread().getId(), e); if (!retryIfNotStopped()) { break; } } else { log.warn("Error, Connection closed, AxisRabbitMQException occurred, service " + serviceName + ", Listner id - " + Thread.currentThread().getId()); if (log.isDebugEnabled()) { log.debug("Error, Connection closed, AxisRabbitMQException occurred, service " + serviceName + ", Listner id - " + Thread.currentThread().getId(), e); } } } catch (Exception e) { if (isServiceTaskManagerActive()) { log.error("Error, Connection closed, Exception occurred, service " + serviceName + ", Listner id - " + Thread.currentThread().getId(), e); if (!retryIfNotStopped()) { break; } } else { log.warn("Error, Connection closed, Exception occurred, service " + serviceName + ", Listner id - " + Thread.currentThread().getId()); if (log.isDebugEnabled()) { log.debug("Error, Connection closed, Exception occurred, service " + serviceName + ", Listner id - " + Thread.currentThread().getId(), e); } } } } } finally { closeConnection(); getUpdateListnerState(true, STATE_STOPPED); synchronized (pollingTasks) { pollingTasks.remove(this); } } } /** * Create a queue consumer using the properties form transport listener configuration. * When connection is re-connected. This method will request for the connection and create * channel, queues, exchanges and bind queues to exchanges before consuming messages * * @throws IOException on error */ private void initConsumer() throws IOException { if (log.isDebugEnabled()) { log.debug("Initializing consumer for service " + serviceName); } //set the qos value for the RMQ channel so it will be applied when getting a channel back String qosString = rabbitMQProperties.get(RabbitMQConstants.CONSUMER_QOS); if (qosString != null && !"".equals(qosString)) { try { qos = Integer.parseInt(qosString); } catch (NumberFormatException e) { qos = -1; log.warn("Can't parse given RabbitMQ qos value as a integer, hence using " + "channel without qos, provided qos value - " + qosString); } } queueingConsumer = new QueueingConsumer(getChannel()); queueName = rabbitMQProperties.get(RabbitMQConstants.QUEUE_NAME); routeKey = rabbitMQProperties.get(RabbitMQConstants.QUEUE_ROUTING_KEY); exchangeName = rabbitMQProperties.get(RabbitMQConstants.EXCHANGE_NAME); if (log.isDebugEnabled()) { log.debug("Starting MessageListner Thread - " + Thread.currentThread().getId() + ", with channel(hashcode) - " + queueingConsumer.getChannel().hashCode()); } String autoAckStringValue = rabbitMQProperties.get(RabbitMQConstants.QUEUE_AUTO_ACK); if (autoAckStringValue != null) { try { autoAck = Boolean.parseBoolean(autoAckStringValue); } catch (Exception e) { log.debug("Format error in rabbitmq.queue.auto.ack parameter"); } } //If no queue name is specified then service name will be used as queue name if (StringUtils.isEmpty(queueName)) { queueName = serviceName; log.info("No queue name is specified for " + serviceName + ". " + "Service name will be used as queue name"); } if (routeKey == null) { log.info( "No routing key specified. Using queue name as the " + "routing key."); routeKey = queueName; } String queueAutoDeclareStr = rabbitMQProperties.get(RabbitMQConstants.QUEUE_AUTODECLARE); String exchangeAutoDeclareStr = rabbitMQProperties.get(RabbitMQConstants.EXCHANGE_AUTODECLARE); boolean queueAutoDeclare = true; boolean exchangeAutoDeclare = true; if (!StringUtils.isEmpty(queueAutoDeclareStr)) { queueAutoDeclare = Boolean.parseBoolean(queueAutoDeclareStr); } if (!StringUtils.isEmpty(exchangeAutoDeclareStr)) { exchangeAutoDeclare = Boolean.parseBoolean(exchangeAutoDeclareStr); } if (queueAutoDeclare && !StringUtils.isEmpty(queueName)) { //declaring queue RabbitMQUtils.declareQueue(queueingConsumer.getChannel(), queueName, rabbitMQProperties); } if (exchangeAutoDeclare && !StringUtils.isEmpty(exchangeName)) { //declaring exchange RabbitMQUtils.declareExchange(queueingConsumer.getChannel(), exchangeName, rabbitMQProperties); queueingConsumer.getChannel().queueBind(queueName, exchangeName, routeKey); if (log.isDebugEnabled()) { log.debug("Bind queue '" + queueName + "' to exchange '" + exchangeName + "' with route key '" + routeKey + "'"); } } String consumerTagString = rabbitMQProperties.get(RabbitMQConstants.CONSUMER_TAG); if (consumerTagString != null) { queueingConsumer.getChannel().basicConsume(queueName, autoAck, consumerTagString, queueingConsumer); if (log.isDebugEnabled()) { log.debug("Start consuming queue '" + queueName + "' with consumer tag '" + consumerTagString + "' for service " + serviceName); } } else { consumerTagString = queueingConsumer.getChannel().basicConsume(queueName, autoAck, queueingConsumer); if (log.isDebugEnabled()) { log.debug("Start consuming queue '" + queueName + "' with consumer tag '" + consumerTagString + "' for service " + serviceName); } } } /** * Used to start consuming messages. This method is called in startup when reconnection happens * * @throws ShutdownSignalException * @throws IOException */ private void startConsumer() throws ShutdownSignalException, IOException { //unable to connect to the queue if (queueingConsumer == null) { getUpdateListnerState(true, STATE_STOPPED); return; } Channel channel = queueingConsumer.getChannel(); while (isListnerActive()) { try { channel.txSelect(); } catch (SocketException exx) { if (!isServiceTaskManagerActive()) { throw exx; } else { log.error("Error while starting transaction, SocketException, service - " + serviceName + ", Listner id - " + Thread.currentThread().getId(), exx); continue; } } catch (IOException e) { if (!isServiceTaskManagerActive()) { throw e; } else { log.error("Error while starting transaction, IOException, service - " + serviceName + ", Listner id - " + Thread.currentThread().getId(), e); continue; } } boolean successful = false; RabbitMQMessage message = null; try { message = getConsumerDelivery(queueingConsumer); } catch (InterruptedException e) { log.error("Error while consuming message", e); continue; } if (log.isDebugEnabled()) { log.debug("Processing message by Message Listner Thread - " + Thread.currentThread().getId() + ", time - " + System.nanoTime() + ", channel(hashcode) - " + channel.hashCode()); } if (message != null) { try { successful = rabbitMQMessageReceiver.onMessage(message); } finally { if (successful) { try { if (!autoAck) { channel.basicAck(message.getDeliveryTag(), false); } channel.txCommit(); } catch (SocketException exx) { if (!isServiceTaskManagerActive()) { throw exx; } else { log.error("Error while committing transaction, SocketException, service - " + serviceName + ", Listner id - " + Thread.currentThread().getId(), exx); continue; } } catch (IOException e) { if (!isServiceTaskManagerActive()) { throw e; } else { log.error("Error while committing transaction, IOException, service - " + serviceName + ", Listner id - " + Thread.currentThread().getId(), e); continue; } } } else { try { channel.txRollback(); } catch (SocketException exx) { if (!isServiceTaskManagerActive()) { throw exx; } else { log.error("Error while trying to roll back transaction, SocketException, service - " + serviceName + ", Listner id - " + Thread.currentThread().getId(), exx); continue; } } catch (IOException e) { if (!isServiceTaskManagerActive()) { throw e; } else { log.error("Error while trying to roll back transaction, IOException, service - " + serviceName + ", Listner id - " + Thread.currentThread().getId(), e); continue; } } } } } } } /** * Returns the delivery from the consumer * * @param consumer the consumer to get the delivery * @return RabbitMQMessage consumed by the consumer * @throws InterruptedException on error * @throws com.rabbitmq.client.ShutdownSignalException */ private RabbitMQMessage getConsumerDelivery(QueueingConsumer consumer) throws InterruptedException, ShutdownSignalException { RabbitMQMessage message = new RabbitMQMessage(); QueueingConsumer.Delivery delivery = null; try { if (log.isDebugEnabled()) { log.debug("Waiting for next delivery from queue for service " + serviceName + ", Listner id - " + Thread.currentThread().getId()); } delivery = consumer.nextDelivery(); } catch (InterruptedException e) { return null; } catch (ConsumerCancelledException e) { return null; } if (delivery != null) { AMQP.BasicProperties properties = delivery.getProperties(); Map<String, Object> headers = properties.getHeaders(); message.setBody(delivery.getBody()); message.setDeliveryTag(delivery.getEnvelope().getDeliveryTag()); message.setReplyTo(properties.getReplyTo()); message.setMessageId(properties.getMessageId()); // Content type is as set in delivered message. If not, from service parameters. String contentType = properties.getContentType(); if (contentType == null) { contentType = rabbitMQProperties.get(RabbitMQConstants.CONTENT_TYPE); } message.setContentType(contentType); message.setContentEncoding(properties.getContentEncoding()); message.setCorrelationId(properties.getCorrelationId()); if (headers != null) { message.setHeaders(headers); if (headers.get(RabbitMQConstants.SOAP_ACTION) != null) { message.setSoapAction(headers.get( RabbitMQConstants.SOAP_ACTION).toString()); } } } else { if (log.isDebugEnabled()) { log.debug("Queue delivery item is null for service " + serviceName); } return null; } return message; } /** * Retrying the connection if service not deactivated * * @return true if reconnection successful false otherwise */ private boolean retryIfNotStopped() { int retryInterval = rabbitMQConnectionFactory.getRetryInterval(); int retryCountMax = rabbitMQConnectionFactory.getRetryCount(); int retryCount = 0; while (isListnerActive() && !localConnection.isOpen() && ((retryCountMax == -1) || (retryCount < retryCountMax))) { retryCount++; log.info("Attempting to reconnect to RabbitMQ Broker for the service " + serviceName + " in " + retryInterval + " ms, Listner id - " + Thread.currentThread().getId()); try { Thread.sleep(retryInterval); } catch (InterruptedException e) { log.error("Error while trying to reconnect to RabbitMQ Broker for the service - " + serviceName + ", Listner id - " + Thread.currentThread().getId(), e); } } if (!isListnerActive() || !serviceTaskManagerState.isActive()) { if (log.isDebugEnabled()) { log.debug("Service deactivated during waiting period, service - " + serviceName + ", Listner id - " + Thread.currentThread().getId()); } getUpdateListnerState(true, STATE_SHUTTING_DOWN); return false; } else if (localConnection.isOpen()) { log.info("Successfully reconnected to RabbitMQ Broker for the service " + serviceName + ", Listner id - " + Thread.currentThread().getId()); return true; } log.error("Could not reconnect to the RabbitMQ Broker for the service " + serviceName + ". Connection is closed, Listner id - " + Thread.currentThread().getId()); getUpdateListnerState(true, STATE_FAULTY); return false; } /** * This will be true if there is a rabbitMQ connection * * @return true if connected, false otherwise */ @Deprecated public boolean isConnected() { return connected; } /** * Setter method for connected * * @param connected */ @Deprecated public void setConnected(boolean connected) { this.connected = connected; } /** * Helper method to get create and get the channel * * @return channel * @throws IOException */ private Channel getChannel() throws IOException { Channel channel = getConnection().createChannel(); //If qos is applicable, then apply qos before returning the channel if (this.qos > 0) { channel.basicQos(this.qos); } return channel; } /** * Helper method to get connection. If connection caching is enabled, return shared connection, else create a * local connection * * @return localConnection * @throws IOException */ private Connection getConnection() throws IOException { if (cacheLevel < RabbitMQConstants.CACHE_CONNECTION) { // Connection is not shared if (localConnection == null) { if (isServiceTaskManagerActive()) { localConnection = createConnection(); setConnected(true); } else { throw new AxisRabbitMQException("Error, Shutdown signal issued, hence won't create " + "the localConnection, service - " + serviceName + ", Listner id - " + Thread.currentThread().getId()); } } } else if (localConnection == null) { // Connection is shared, but may not have been created synchronized (ServiceTaskManager.this) { if (sharedConnection == null) { if (isServiceTaskManagerActive()) { sharedConnection = createConnection(); } else { throw new AxisRabbitMQException("Error, Shutdown signal issued, hence won't create " + "the sharedConnection, service - " + serviceName + ", Listner id - " + Thread.currentThread().getId()); } } } localConnection = sharedConnection; setConnected(true); } // else: Connection is shared and is already referenced by this.connection return localConnection; } /** * Helper method to create connection using connection factory. * * @return connection * @throws IOException */ private Connection createConnection() throws IOException { Connection connection = null; try { connection = rabbitMQConnectionFactory.createConnection(); log.info("RabbitMQ connection created for service " + serviceName); } catch (Exception e) { log.error("Error while creating RabbitMQ connection for service " + serviceName, e); throw new AxisRabbitMQException("Error while creating RabbitMQ connection for service " + serviceName, e); } return connection; } /** * Method used to change states to STATE_SHUTTING_DOWN, so in next cycle, message listner will shut down */ protected void requestShutdown() { getUpdateListnerState(true, STATE_SHUTTING_DOWN); } /** * Method used to close connections. This will close local connection and connection resides in messageConsumer */ protected void closeConnection() { getUpdateListnerState(true, STATE_SHUTTING_DOWN); try { //closing the class local connection if (localConnection != null) { try { if (localConnection.isOpen()) { localConnection.close(); log.info("RabbitMQ localConnection closed for service " + serviceName + ", Listner id - " + Thread.currentThread().getId()); } } catch (AlreadyClosedException e) { log.warn("Error while closing Connection, AlreadyClosedException, service - " + serviceName + ", Listner id - " + Thread.currentThread().getId()); if (log.isDebugEnabled()) { log.debug("Error while closing Connection, AlreadyClosedException, service - " + serviceName + ", Listner id - " + Thread.currentThread().getId(), e); } } catch (ShutdownSignalException ex) { log.warn("Error while closing Connection, ShutdownSignalException, service - " + serviceName + ", Listner id - " + Thread.currentThread().getId()); if (log.isDebugEnabled()) { log.debug("Error while closing Connection, ShutdownSignalException, service - " + serviceName + ", Listner id - " + Thread.currentThread().getId(), ex); } } catch (SocketException exx) { log.warn("Error while closing Connection, SocketException, service - " + serviceName + ", Listner id - " + Thread.currentThread().getId()); if (log.isDebugEnabled()) { log.debug("Error while closing Connection, SocketException, service - " + serviceName + ", Listner id - " + Thread.currentThread().getId(), exx); } } catch (IOException e) { log.warn("Error while closing Connection, IOException, service - " + serviceName + ", Listner id - " + Thread.currentThread().getId()); if (log.isDebugEnabled()) { log.debug("Error while closing Connection, IOException, service - " + serviceName + ", Listner id - " + Thread.currentThread().getId(), e); } } } } finally { localConnection = null; } Channel channel = null; if (queueingConsumer != null) { channel = queueingConsumer.getChannel(); } try { if (channel != null) { try { //closing the channel and connection in queing consumer if (channel.isOpen()) { channel.close(); log.info("RabbitMQ consumer channel closed for service " + serviceName + ", Listner id - " + Thread.currentThread().getId()); } if (channel.getConnection() != null && channel.getConnection().isOpen()) { channel.getConnection().close(); log.info("RabbitMQ consumer connection closed for service " + serviceName + ", Listner id - " + Thread.currentThread().getId()); } } catch (AlreadyClosedException e) { log.warn("Error while closing consumer Connection, AlreadyClosedException, service - " + serviceName + ", Listner id - " + Thread.currentThread().getId()); if (log.isDebugEnabled()) { log.debug("Error while closing consumer Connection, AlreadyClosedException, service - " + serviceName + ", Listner id - " + Thread.currentThread().getId(), e); } } catch (ShutdownSignalException ex) { log.warn("Error while closing consumer Connection, ShutdownSignalException, service - " + serviceName + ", Listner id - " + Thread.currentThread().getId()); if (log.isDebugEnabled()) { log.debug("Error while closing consumer Connection, ShutdownSignalException, service - " + serviceName + ", Listner id - " + Thread.currentThread().getId(), ex); } } catch (SocketException exx) { log.warn("Error while closing consumer Connection, SocketException, service - " + serviceName + ", Listner id - " + Thread.currentThread().getId()); if (log.isDebugEnabled()) { log.debug("Error while closing consumer Connection, SocketException, service - " + serviceName + ", Listner id - " + Thread.currentThread().getId(), exx); } } catch (TimeoutException e) { log.warn("Error while closing consumer Connection, TimeoutException, service - " + serviceName + ", Listner id - " + Thread.currentThread().getId()); if (log.isDebugEnabled()) { log.debug("Error while closing consumer Connection, TimeoutException, service - " + serviceName + ", Listner id - " + Thread.currentThread().getId(), e); } } catch (IOException e) { log.warn("Error while closing consumer Connection, IOException, service - " + serviceName + ", Listner id - " + Thread.currentThread().getId()); if (log.isDebugEnabled()) { log.debug("Error while closing consumer Connection, IOException, service - " + serviceName + ", Listner id - " + Thread.currentThread().getId(), e); } } } } finally { channel = null; } getUpdateListnerState(true, STATE_STOPPED); } /** * Helper method to get or update listner states * * @param update * @param state * @return listnerState */ private int getUpdateListnerState(boolean update, int state) { synchronized (ServiceTaskManager.this) { if (update) { this.listnerState = state; } return this.listnerState; } } /** * Helper method to check whether listner is active or not * * @return true if listner is active, false otherwise */ private boolean isListnerActive() { if (this.listnerState == STATE_STARTED) { return true; } return false; } /** * Helper method which will check whether both listner and service task manager in active state. If not * update listner state to STATE_STOPPED as well * @return true if both are in STATE_STARTED, false otherwise */ private boolean isServiceTaskManagerActive() { if (getUpdateListnerState(false, STATE_DUMMY) != STATE_STARTED || serviceTaskManagerState.getUpdateState(false, STATE_DUMMY) != STATE_STARTED) { getUpdateListnerState(true, STATE_STOPPED); return false; } return true; } } }