org.wso2.carbon.inbound.endpoint.protocol.jms.JMSPollingConsumer.java Source code

Java tutorial

Introduction

Here is the source code for org.wso2.carbon.inbound.endpoint.protocol.jms.JMSPollingConsumer.java

Source

/*
 *  Copyright (c) 2005-2014, 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.wso2.carbon.inbound.endpoint.protocol.jms;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.wso2.carbon.inbound.endpoint.protocol.jms.factory.CachedJMSConnectionFactory;

import java.util.Date;
import java.util.Properties;
import javax.jms.Connection;
import javax.jms.Destination;
import javax.jms.JMSException;
import javax.jms.Message;
import javax.jms.MessageConsumer;
import javax.jms.Session;
import javax.jms.TextMessage;

public class JMSPollingConsumer {

    private static final Log logger = LogFactory.getLog(JMSPollingConsumer.class.getName());

    private CachedJMSConnectionFactory jmsConnectionFactory;
    private JMSInjectHandler injectHandler;
    private long scanInterval;
    private Long lastRanTime;
    private String strUserName;
    private String strPassword;
    private Integer iReceiveTimeout;
    private String replyDestinationName;
    private String name;
    private Properties jmsProperties;
    private boolean isConnected;
    private Long reconnectDuration;
    private long retryDuration;
    private int retryIteration;
    private double reconnectionProgressionFactor;
    private long maxReconnectDuration;

    private Connection connection = null;
    private Session session = null;
    private Destination destination = null;
    private MessageConsumer messageConsumer = null;
    private Destination replyDestination = null;

    public JMSPollingConsumer(Properties jmsProperties, long scanInterval, String name) {
        this.jmsConnectionFactory = new CachedJMSConnectionFactory(jmsProperties);
        strUserName = jmsProperties.getProperty(JMSConstants.PARAM_JMS_USERNAME);
        strPassword = jmsProperties.getProperty(JMSConstants.PARAM_JMS_PASSWORD);
        this.name = name;
        this.retryIteration = 0;
        this.reconnectionProgressionFactor = 2.0;
        this.maxReconnectDuration = 60000;
        this.retryDuration = 1000;

        String strReceiveTimeout = jmsProperties.getProperty(JMSConstants.RECEIVER_TIMEOUT);
        if (strReceiveTimeout != null) {
            try {
                iReceiveTimeout = Integer.parseInt(strReceiveTimeout.trim());
            } catch (NumberFormatException e) {
                logger.warn("Invalid value for transport.jms.ReceiveTimeout : " + strReceiveTimeout);
                iReceiveTimeout = null;
            }
        }

        String strReconnectDuration = jmsProperties.getProperty(JMSConstants.JMS_RETRY_DURATION);
        if (strReconnectDuration != null) {
            try {
                this.reconnectDuration = Long.parseLong(strReconnectDuration.trim());
            } catch (NumberFormatException e) {
                logger.warn("Invalid value for transport.jms.retry.duration : " + strReconnectDuration);
                this.reconnectDuration = null;
            }
        }
        this.replyDestinationName = jmsProperties.getProperty(JMSConstants.PARAM_REPLY_DESTINATION);
        this.scanInterval = scanInterval;
        this.lastRanTime = null;
        this.jmsProperties = jmsProperties;
    }

    /**
     * 
     * Register a handler to implement injection of the retrieved message
     * 
     * @param injectHandler
     */
    public void registerHandler(JMSInjectHandler injectHandler) {
        this.injectHandler = injectHandler;
    }

    /**
     * This will be called by the task scheduler. If a cycle execution takes
     * more than the schedule interval, tasks will call this method ignoring the
     * interval. Timestamp based check is done to avoid that.
     */
    public void execute() {
        try {
            logger.debug("Executing : JMS Inbound EP : ");
            // Check if the cycles are running in correct interval and start
            // scan
            long currentTime = (new Date()).getTime();
            if (lastRanTime == null || ((lastRanTime + (scanInterval)) <= currentTime)) {
                lastRanTime = currentTime;
                poll();
            } else if (logger.isDebugEnabled()) {
                logger.debug("Skip cycle since concurrent rate is higher than the scan interval : JMS Inbound EP ");
            }
            if (logger.isDebugEnabled()) {
                logger.debug("End : JMS Inbound EP : ");
            }
        } catch (Exception e) {
            logger.error("Error while retrieving or injecting JMS message. " + e.getMessage(), e);
        }
    }

    /**
     * Create connection with broker and retrieve the messages. Then inject
     * according to the registered handler
     */
    public Message poll() {
        logger.debug("Polling JMS messages.");

        try {
            connection = jmsConnectionFactory.getConnection(strUserName, strPassword);
            if (connection == null) {
                logger.warn("Inbound JMS endpoint unable to get a connection.");
                isConnected = false;
                return null;
            }
            if (retryIteration != 0) {
                logger.info("Reconnection attempt: " + retryIteration + " for the JMS Inbound: " + name
                        + " was successful!");
                this.retryIteration = 0;
                this.retryDuration = 1;
            }
            isConnected = true;
            session = jmsConnectionFactory.getSession(connection);
            //Fixing ESBJAVA-4446
            //Closing the connection if we cannot get a session.
            //Then in the next poll iteration it will create a new connection
            //instead of using cached connection
            if (session == null) {
                logger.warn("Inbound JMS endpoint unable to get a session.");
                jmsConnectionFactory.closeConnection();
                return null;
            }
            destination = jmsConnectionFactory.getDestination(session);
            if (replyDestinationName != null && !replyDestinationName.trim().equals("")) {
                if (logger.isDebugEnabled()) {
                    logger.debug(
                            "Using the reply destination as " + replyDestinationName + " in inbound endpoint.");
                }
                replyDestination = jmsConnectionFactory.createDestination(session, replyDestinationName);
            }
            messageConsumer = jmsConnectionFactory.getMessageConsumer(session, destination);
            Message msg = receiveMessage(messageConsumer);
            if (msg == null) {
                logger.debug("Inbound JMS Endpoint. No JMS message received.");
                return null;
            }
            while (msg != null) {
                if (!JMSUtils.inferJMSMessageType(msg).equals(TextMessage.class.getName())) {
                    logger.error("JMS " + "Inbound transport support JMS TextMessage type only. Found message type "
                            + JMSUtils.inferJMSMessageType(msg));
                    return null;
                }

                if (injectHandler != null) {

                    boolean commitOrAck = true;
                    // Set the reply destination and connection
                    if (replyDestination != null) {
                        injectHandler.setReplyDestination(replyDestination);
                    }
                    injectHandler.setConnection(connection);
                    commitOrAck = injectHandler.invoke(msg, name);
                    // if client acknowledgement is selected, and processing
                    // requested ACK
                    if (jmsConnectionFactory.getSessionAckMode() == Session.CLIENT_ACKNOWLEDGE) {
                        if (commitOrAck) {
                            try {
                                msg.acknowledge();
                                if (logger.isDebugEnabled()) {
                                    logger.debug("Message : " + msg.getJMSMessageID() + " acknowledged");
                                }
                            } catch (JMSException e) {
                                logger.error("Error acknowledging message : " + msg.getJMSMessageID(), e);
                            }
                        } else {
                            // Need to create a new consumer and session since
                            // we need to rollback the message
                            if (messageConsumer != null) {
                                jmsConnectionFactory.closeConsumer(messageConsumer);
                            }
                            if (session != null) {
                                jmsConnectionFactory.closeSession(session);
                            }
                            session = jmsConnectionFactory.getSession(connection);
                            messageConsumer = jmsConnectionFactory.getMessageConsumer(session, destination);
                        }
                    }
                    // if session was transacted, commit it or rollback
                    if (jmsConnectionFactory.isTransactedSession()) {
                        try {
                            if (session.getTransacted()) {
                                if (commitOrAck) {
                                    session.commit();
                                    if (logger.isDebugEnabled()) {
                                        logger.debug(
                                                "Session for message : " + msg.getJMSMessageID() + " committed");
                                    }
                                } else {
                                    session.rollback();
                                    if (logger.isDebugEnabled()) {
                                        logger.debug(
                                                "Session for message : " + msg.getJMSMessageID() + " rolled back");
                                    }
                                }
                            }
                        } catch (JMSException e) {
                            logger.error("Error " + (commitOrAck ? "committing" : "rolling back")
                                    + " local session txn for message : " + msg.getJMSMessageID(), e);
                        }
                    }
                } else {
                    return msg;
                }
                msg = receiveMessage(messageConsumer);
            }

        } catch (JMSException e) {
            logger.error("Error while receiving JMS message. " + e.getMessage(), e);
        } catch (Exception e) {
            logger.error("Error while receiving JMS message. " + e.getMessage(), e);
        } finally {
            if (!isConnected) {
                if (reconnectDuration != null) {
                    retryDuration = reconnectDuration;
                    logger.error("Reconnection attempt : " + (retryIteration++) + " for JMS Inbound : " + name
                            + " failed. Next retry in " + (retryDuration / 1000) + " seconds. (Fixed Interval)");
                } else {
                    retryDuration = (long) (retryDuration * reconnectionProgressionFactor);
                    if (retryDuration > maxReconnectDuration) {
                        retryDuration = maxReconnectDuration;
                        logger.info("InitialReconnectDuration reached to MaxReconnectDuration.");
                    }
                    logger.error("Reconnection attempt : " + (retryIteration++) + " for JMS Inbound : " + name
                            + " failed. Next retry in " + (retryDuration / 1000) + " seconds");
                }
                try {
                    Thread.sleep(retryDuration);
                } catch (InterruptedException ignore) {
                }
            }
            if (messageConsumer != null) {
                jmsConnectionFactory.closeConsumer(messageConsumer);
            }
            if (session != null) {
                jmsConnectionFactory.closeSession(session);
            }
            if (connection != null) {
                jmsConnectionFactory.closeConnection(connection);
            }
        }
        return null;
    }

    public void destroy() {
        if (messageConsumer != null) {
            jmsConnectionFactory.closeConsumer(messageConsumer, true);
        }
        if (session != null) {
            jmsConnectionFactory.closeSession(session, true);
        }
        if (connection != null) {
            jmsConnectionFactory.closeConnection(connection, true);
        }
    }

    private Message receiveMessage(MessageConsumer messageConsumer) throws JMSException {
        Message msg = null;
        if (iReceiveTimeout == null) {
            msg = messageConsumer.receive(1);
        } else if (iReceiveTimeout > 0) {
            msg = messageConsumer.receive(iReceiveTimeout);
        } else {
            msg = messageConsumer.receive();
        }
        return msg;
    }

    protected Properties getInboundProperites() {
        return jmsProperties;
    }
}