org.wso2.carbon.deployment.notifier.internal.JMSConnectionFactory.java Source code

Java tutorial

Introduction

Here is the source code for org.wso2.carbon.deployment.notifier.internal.JMSConnectionFactory.java

Source

/*
* 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.wso2.carbon.deployment.notifier.internal;

import org.apache.commons.pool.PoolableObjectFactory;
import org.apache.commons.pool.impl.GenericObjectPool;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.wso2.carbon.deployment.notifier.Constants;
import org.wso2.carbon.deployment.notifier.DeploymentNotifierException;

import java.util.Hashtable;
import javax.jms.Connection;
import javax.jms.ConnectionFactory;
import javax.jms.Destination;
import javax.jms.JMSException;
import javax.jms.MessageProducer;
import javax.jms.Session;
import javax.naming.Context;
import javax.naming.InitialContext;
import javax.naming.NamingException;

/**
 * Encapsulate a JMS Connection factory.
 * <p>
 * JMS Connection Factory definitions, allows JNDI properties as well as other service
 * level parameters to be defined, and re-used by each service that binds to it
 * <p>
 * When used for sending messages out, the JMSConnectionFactory'ies are able to cache
 * a Connection, Session or Producer
 * <p>
 *
 * Borrowed generously from Apache Axi2 JMS implementation.
 */
public class JMSConnectionFactory {

    private static final Logger log = LoggerFactory.getLogger(JMSConnectionFactory.class);

    /**
     * The list of parameters from the deployment.yml
     */
    private Hashtable<String, String> parameters = new Hashtable<>();
    private String name;

    /**
     * The cached InitialContext reference
     */
    private Context context = null;
    /**
     * The JMS ConnectionFactory this definition refers to
     */
    private ConnectionFactory conFactory = null;

    /**
     * The Shared Destination
     */
    private Destination sharedDestination = null;

    private int maxConnections;

    private GenericObjectPool connectionPool;

    private String destinationName;

    /**
     * Digest a JMS CF definition and construct.
     * Set max concurrent connections to be 5 if unspecified.
     */
    public JMSConnectionFactory(Hashtable<String, String> parameters, String name, String destination) {
        this(parameters, name, destination, 5);
    }

    /**
     * Digest a JMS CF definition  'Parameter' and construct
     */
    @SuppressWarnings("unchecked")
    public JMSConnectionFactory(Hashtable<String, String> parameters, String name, String destination,
            int maxConcurrentConnections) {
        this.parameters = (Hashtable<String, String>) parameters.clone();
        this.name = name;
        this.destinationName = destination;

        if (maxConcurrentConnections > 0) {
            this.maxConnections = maxConcurrentConnections;
        }

        try {
            context = new InitialContext(parameters);
            conFactory = JMSUtils.lookup(context, ConnectionFactory.class,
                    parameters.get(Constants.PARAM_CONFAC_JNDI_NAME));
            log.info("JMS ConnectionFactory : " + name + " initialized");

        } catch (NamingException e) {
            throw new DeploymentNotifierException("Cannot acquire JNDI context, JMS Connection factory : "
                    + parameters.get(Constants.PARAM_CONFAC_JNDI_NAME) + " or default destinationName : "
                    + parameters.get(Constants.PARAM_DESTINATION) + " for JMS CF : " + name + " using : "
                    + parameters, e);
        }

        createConnectionPool();
    }

    // need to initialize
    private void createConnectionPool() {
        GenericObjectPool.Config poolConfig = new GenericObjectPool.Config();
        poolConfig.minEvictableIdleTimeMillis = 3000;
        poolConfig.maxWait = 3000;
        poolConfig.maxActive = maxConnections;
        poolConfig.maxIdle = maxConnections;
        poolConfig.minIdle = 0;
        poolConfig.numTestsPerEvictionRun = Math.max(1, maxConnections / 10);
        poolConfig.timeBetweenEvictionRunsMillis = 5000;
        this.connectionPool = new GenericObjectPool(new PoolableJMSConnectionFactory(), poolConfig);

    }

    public void returnPooledConnection(JMSPooledConnectionHolder pooledConnection) {
        try {
            this.connectionPool.returnObject(pooledConnection);
        } catch (Exception e) {
            log.error(e.getMessage(), e);
        }
    }

    /**
     * Create a new MessageProducer
     *
     * @param session     Session to be used
     * @param destination Destination to be used
     * @return a new MessageProducer
     */
    private MessageProducer createProducer(Session session, Destination destination) {
        try {
            if (log.isDebugEnabled()) {
                log.debug("Creating a new JMS MessageProducer from JMS CF : " + name);
            }

            return JMSUtils.createProducer(session, destination, isQueue(), isJmsSpec11());

        } catch (JMSException e) {
            handleException("Error creating JMS producer from JMS CF : " + name, e);
        }
        return null;
    }

    /**
     * Get cached InitialContext
     *
     * @return cache InitialContext
     */
    @SuppressWarnings("unused")
    public Context getContext() {
        return context;
    }

    /**
     * Lookup a Destination using this JMS CF definitions and JNDI name
     *
     * @return JMS Destination for the given JNDI name or null
     */
    public synchronized Destination getDestination() {
        try {
            if (sharedDestination == null) {
                sharedDestination = JMSUtils.lookupDestination(context, destinationName,
                        parameters.get(Constants.PARAM_DEST_TYPE));
            }
            return sharedDestination;
        } catch (NamingException e) {
            handleException("Error looking up the JMS destinationName with name " + destinationName + " of type "
                    + parameters.get(Constants.PARAM_DEST_TYPE), e);
        }

        // never executes but keeps the compiler happy
        return null;
    }

    private void handleException(String msg, Exception e) {
        log.error(msg, e);
        throw new DeploymentNotifierException(msg, e);
    }

    /**
     * Should the JMS 1.1 API be used? - defaults to yes
     *
     * @return true, if JMS 1.1 api should  be used
     */
    public boolean isJmsSpec11() {
        return parameters.get(Constants.PARAM_JMS_SPEC_VER) == null
                || "1.1".equals(parameters.get(Constants.PARAM_JMS_SPEC_VER));
    }

    /**
     * Return the type of the JMS CF Destination
     *
     * @return TRUE if a Queue, FALSE for a Topic and NULL for a JMS 1.1 Generic Destination
     */
    public Boolean isQueue() {
        if ("queue".equalsIgnoreCase(parameters.get(Constants.PARAM_DEST_TYPE))) {
            return true;
        } else if ("topic".equalsIgnoreCase(parameters.get(Constants.PARAM_DEST_TYPE))) {
            return false;
        } else {
            throw new DeploymentNotifierException("Invalid " + Constants.PARAM_DEST_TYPE + " : "
                    + parameters.get(Constants.PARAM_DEST_TYPE) + " for JMS CF : " + name);
        }
    }

    public Connection createConnection() {

        Connection connection = null;
        try {
            connection = JMSUtils.createConnection(conFactory, parameters.get(Constants.PARAM_JMS_USERNAME),
                    parameters.get(Constants.PARAM_JMS_PASSWORD), isJmsSpec11(), isQueue(), false, null);

            if (log.isDebugEnabled()) {
                log.debug("New JMS Connection from JMS CF : " + name + " created");
            }
        } catch (JMSException e) {
            handleException(
                    "Error acquiring a Connection from the JMS CF : " + name + " using properties : " + parameters,
                    e);
        }
        return connection;
    }

    public JMSPooledConnectionHolder getConnectionFromPool() {
        try {
            return (JMSPooledConnectionHolder) this.connectionPool.borrowObject();
        } catch (Exception e) {
            log.error(e.getMessage(), e);
        }
        return null;
    }

    @SuppressWarnings("unused")
    public synchronized void close() {

        try {
            connectionPool.close();
        } catch (Exception e) {
            log.error(e.getMessage(), e);
        }

        if (context != null) {
            try {
                context.close();
            } catch (NamingException e) {
                log.warn("Error while closing the InitialContext of factory : " + name, e);
            }
        }
    }

    /**
     * Holder class for connections and its session/producer.
     */
    public static class JMSPooledConnectionHolder {
        private Connection connection;
        private Session session;
        private MessageProducer producer;

        public Connection getConnection() {
            return connection;
        }

        public void setConnection(Connection connection) {
            this.connection = connection;
        }

        public Session getSession() {
            return session;
        }

        public void setSession(Session session) {
            this.session = session;
        }

        public MessageProducer getProducer() {
            return producer;
        }

        public void setProducer(MessageProducer producer) {
            this.producer = producer;
        }

    }

    /**
     * JMSConnectionFactory used by the connection pool.
     */
    private class PoolableJMSConnectionFactory implements PoolableObjectFactory {

        @Override
        public Object makeObject() throws Exception {
            Connection con = createConnection();
            try {
                Session session = JMSUtils.createSession(con, false, Session.AUTO_ACKNOWLEDGE, isJmsSpec11(),
                        isQueue());

                MessageProducer producer = createProducer(session, getDestination());

                JMSPooledConnectionHolder entry = new JMSPooledConnectionHolder();
                entry.setConnection(con);
                entry.setSession(session);
                entry.setProducer(producer);
                return entry;
            } catch (JMSException e) {
                log.error(e.getMessage(), e);
                return null;
            }

        }

        @Override
        public void destroyObject(Object o) throws Exception {
            JMSPooledConnectionHolder entry = (JMSPooledConnectionHolder) o;
            entry.getProducer().close();
            entry.getSession().close();
            entry.getConnection().close();

        }

        @Override
        public boolean validateObject(Object o) {
            return false;
        }

        @Override
        public void activateObject(Object o) throws Exception {

        }

        @Override
        public void passivateObject(Object o) throws Exception {

        }

    }

}