org.wso2.carbon.transport.jms.factory.JMSClientConnectionFactory.java Source code

Java tutorial

Introduction

Here is the source code for org.wso2.carbon.transport.jms.factory.JMSClientConnectionFactory.java

Source

/*
 *  Copyright (c) 2017, 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.transport.jms.factory;

import org.apache.commons.pool2.impl.GenericObjectPool;
import org.apache.commons.pool2.impl.GenericObjectPoolConfig;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.wso2.carbon.transport.jms.exception.JMSConnectorException;
import org.wso2.carbon.transport.jms.sender.sessionpool.SessionPoolFactory;
import org.wso2.carbon.transport.jms.sender.wrappers.ConnectionWrapper;
import org.wso2.carbon.transport.jms.sender.wrappers.SessionWrapper;
import org.wso2.carbon.transport.jms.utils.JMSConstants;

import java.util.ArrayList;
import java.util.List;
import java.util.Properties;
import javax.jms.Connection;
import javax.jms.ExceptionListener;
import javax.jms.JMSException;
import javax.jms.XAConnection;

/**
 * Extended class to handle Client side Connection Factory requirements.
 */
public class JMSClientConnectionFactory extends JMSConnectionResourceFactory {

    private static final Logger logger = LoggerFactory.getLogger(JMSClientConnectionFactory.class);
    /**
     * Default Wait timeout (in milliseconds) for the Session pool.
     */
    private static final int poolWaitTimeout = 30 * 1000;
    /**
     * Default Size of the Connection list.
     */
    private int maxNumberOfConnections = 5;
    /**
     * Default Number of session per Connection.
     */
    private int maxSessionsPerConnection = 10;
    /**
     * Indicates cache level given by the user.
     */
    private boolean clientCaching = true;

    /**
     * List of Connection wrapper objects.
     */
    private List<ConnectionWrapper> connections = null;

    /**
     * Pool of Session wrapper objects.
     */
    private GenericObjectPool<SessionWrapper> sessionPool = null;

    /**
     * Constructor.
     *
     * @param properties JMS properties.
     * @throws JMSConnectorException if an error thrown from parents constructor.
     */
    public JMSClientConnectionFactory(Properties properties, boolean isCached) throws JMSConnectorException {
        super(properties);
        this.clientCaching = isCached;

        // session pool configurations
        if (properties.getProperty(JMSConstants.PARAM_MAX_CONNECTIONS) != null) {
            try {
                this.maxNumberOfConnections = Integer
                        .parseInt(properties.getProperty(JMSConstants.PARAM_MAX_CONNECTIONS));
            } catch (NumberFormatException ex) {
                logger.error("Non-integer value configured for JMS Client Connection count", ex);
            }
        }
        if (properties.getProperty(JMSConstants.PARAM_MAX_SESSIONS_ON_CONNECTION) != null) {
            try {
                this.maxSessionsPerConnection = Integer
                        .parseInt(properties.getProperty(JMSConstants.PARAM_MAX_SESSIONS_ON_CONNECTION));
            } catch (NumberFormatException ex) {
                logger.error("Non-integer value configured for JMS Client Sessions count", ex);
            }
        }

        if (clientCaching) {
            connections = new ArrayList<>();
            initSessionPool();
        }
    }

    public int getMaxNumberOfConnections() {
        return maxNumberOfConnections;
    }

    public int getMaxSessionsPerConnection() {
        return maxSessionsPerConnection;
    }

    /**
     * Initialize the session pool with provided configuration.
     */
    private void initSessionPool() {
        SessionPoolFactory sessionPoolFactory = new SessionPoolFactory(this);

        //create pool configurations
        GenericObjectPoolConfig config = new GenericObjectPoolConfig();
        config.setMaxTotal(maxNumberOfConnections * maxSessionsPerConnection);
        //todo: set the ideal limit and make the idle sessions timedout
        config.setMaxIdle(maxNumberOfConnections * maxSessionsPerConnection);
        config.setBlockWhenExhausted(true);
        config.setMaxWaitMillis(poolWaitTimeout);

        //initialize the pool
        sessionPool = new GenericObjectPool<SessionWrapper>(sessionPoolFactory, config);
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public synchronized void notifyError(JMSException ex) {
        logger.error("Error occurred in JMS Client Connections. Re-initializing the resources. ", ex);

        try {
            closeJMSResources();
        } catch (JMSConnectorException e) {
            // If an exception was triggered due to a connection issue, closing connection will also fail with a
            // connection exception
            logger.error("Error closing connection after exception", e);
        } finally {
            connections.clear();
        }

        initSessionPool();
    }

    /**
     * @return Connections list.
     */
    public List<ConnectionWrapper> getConnections() {
        return connections;
    }

    /**
     * Borrow an object from the session pool.
     *
     * @return SessionWrapper instance.
     * @throws Exception Exception when borrowing an object from the pool.
     */
    public SessionWrapper getSessionWrapper() throws Exception {
        return sessionPool.borrowObject();
    }

    /**
     * Return an object to the Session pool.
     *
     * @param sessionWrapper SessionWrapper instance.
     */
    public void returnSessionWrapper(SessionWrapper sessionWrapper) throws JMSConnectorException {
        try {
            sessionPool.returnObject(sessionWrapper);
        } catch (IllegalStateException e) {
            /* Catching the runtime exception here because it can be ignored in some situations where
             * the pool is already closed and re-initialized due to a Connection level exception.
             */
            throw new JMSConnectorException("Unable to return session instance to the pool. ", e);
        }
    }

    /**
     * Close cached JMS resources allocated for this Connection Factory.
     */
    public void closeJMSResources() throws JMSConnectorException {
        if (clientCaching) {
            sessionPool.clear();
            sessionPool.close();
            for (int i = 0; i < connections.size(); i++) {
                try {
                    closeConnection(connections.get(i).getConnection());
                } catch (JMSException e) {
                    throw new JMSConnectorException("Error closing the connection.", e);
                }
            }
        }
    }

    /**
     * Is this Client Connection factory is configured to use caching/pooling.
     *
     * @return isClientCaching set.
     */
    public boolean isClientCaching() {
        return clientCaching;
    }

    @Override
    public Connection createConnection() throws JMSException {
        Connection connection = super.createConnection();
        connection.setExceptionListener(new JMSErrorListener());
        return connection;
    }

    @Override
    public XAConnection createXAConnection() throws JMSException {
        XAConnection xaConnection = super.createXAConnection();
        xaConnection.setExceptionListener(new JMSErrorListener());
        return xaConnection;
    }

    /**
     * JMS Client Connection Error Listener class that implements {@link ExceptionListener} from JMS API.
     */
    private class JMSErrorListener implements ExceptionListener {
        @Override
        public void onException(JMSException e) {
            notifyError(e);
        }
    }
}