/* * 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.KeyedPooledObjectFactory; import org.apache.commons.pool2.PooledObject; import org.apache.commons.pool2.impl.DefaultPooledObject; import org.apache.commons.pool2.impl.GenericKeyedObjectPool; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.wso2.carbon.transport.jms.exception.JMSConnectorException; import org.wso2.carbon.transport.jms.utils.JMSConstants; import java.util.Properties; import java.util.concurrent.ConcurrentHashMap; import javax.jms.Connection; import javax.jms.JMSException; import javax.jms.QueueConnection; import javax.jms.Session; import javax.jms.TopicConnection; /** * JMS Connection Factory which pools connections for better performance. */ public class PooledJMSConnectionFactory extends JMSConnectionFactory implements KeyedPooledObjectFactory<PooledConnectionKey, Connection> { private static final Logger log = LoggerFactory.getLogger(PooledJMSConnectionFactory.class); private GenericKeyedObjectPool<PooledConnectionKey, Connection> keyedObjectPool = new GenericKeyedObjectPool<>(this); private ConcurrentHashMap<Connection, PooledConnectionKey> connectionKeyMap = new ConcurrentHashMap<>(); public PooledJMSConnectionFactory(Properties properties) throws JMSConnectorException { super(properties); Object maxConcurrentConnections = properties.get(JMSConstants.MAX_CONNECTIONS); if (maxConcurrentConnections != null) { keyedObjectPool.setMaxTotal(Integer.parseInt((String) maxConcurrentConnections)); } } @Override public Connection createConnection() throws JMSException { PooledConnectionKey key = new PooledConnectionKey(null, null); Connection connection; try { connection = keyedObjectPool.borrowObject(key); if (!validateObject(key, new DefaultPooledObject<>(connection))) { keyedObjectPool.invalidateObject(key, connection); throw new JMSException("Could not create a valid connection"); } return connection; } catch (Exception e) { throw new JMSException("Error occurred creating connection : " + e.getMessage()); } } @Override public Connection createConnection(String userName, String password) throws JMSException { PooledConnectionKey key = new PooledConnectionKey(userName, password); try { return keyedObjectPool.borrowObject(key); } catch (Exception e) { throw new JMSException("Error occurred creating connection : " + e.getMessage()); } } @Override public QueueConnection createQueueConnection() throws JMSException { return (QueueConnection) createConnection(); } @Override public QueueConnection createQueueConnection(String userName, String password) throws JMSException { return (QueueConnection) createConnection(userName, password); } @Override public TopicConnection createTopicConnection() throws JMSException { return (TopicConnection) createConnection(); } @Override public TopicConnection createTopicConnection(String userName, String password) throws JMSException { return (TopicConnection) createConnection(userName, password); } @Override public void closeConnection(Connection connection) throws JMSConnectorException { PooledConnectionKey key = connectionKeyMap.get(connection); if (key != null) { try { keyedObjectPool.returnObject(key, connection); } catch (Exception e) { throw new JMSConnectorException("Failed to return the connection back to the pool", e); } } } // PooledObjectFactory implementations /** * This is invoked when a new connection object has to be made. * * @param key The connection key * @return Wrapped connection object * @throws Exception Any exception thrown when creating a JMS connection will be thrown */ @Override public PooledObject<Connection> makeObject(PooledConnectionKey key) throws Exception { Connection connection; String username = key.getUsername(); String password = key.getPassword(); if (username == null && password == null) { connection = super.createConnection(); } else { connection = super.createConnection(key.getUsername(), key.getPassword()); } connectionKeyMap.put(connection, key); return new DefaultPooledObject<>(connection); } /** * This is called when an connection object is invalidated. * * @param key The connection key * @param pooledConnectionObject The invalidated wrapped connection object. * @throws Exception Any exception thrown when closing a JMS connection will be thrown */ @Override public void destroyObject(PooledConnectionKey key, PooledObject<Connection> pooledConnectionObject) throws Exception { Connection connection = pooledConnectionObject.getObject(); connectionKeyMap.remove(connection); connection.close(); } /** * Validates a connection object and verifies it can be used further. * * @param key The connection key * @param pooledConnectionObject The wrapped connection object * @return True if the object is valid */ @Override public boolean validateObject(PooledConnectionKey key, PooledObject<Connection> pooledConnectionObject) { boolean valid = false; try { Session session = pooledConnectionObject.getObject().createSession(false, Session.AUTO_ACKNOWLEDGE); session.close(); valid = true; } catch (JMSException e) { log.warn("Connection with key " + key.hashCode() + " is not valid anymore"); } return valid; } /** * Activates a suspended connection. * * @param key The connection key * @param pooledConnectionObject Wrapped suspended connection object. * @throws Exception Any exception thrown when starting a JMS connection will be thrown */ @Override public void activateObject(PooledConnectionKey key, PooledObject<Connection> pooledConnectionObject) throws Exception { pooledConnectionObject.getObject().start(); } /** * Suspend a connection. * * @param key The connection key * @param pooledConnectionObject The wrapped connection object that needs to be suspended. * @throws Exception Any exception thrown when stopping a JMS connection will be thrown */ @Override public void passivateObject(PooledConnectionKey key, PooledObject<Connection> pooledConnectionObject) throws Exception { pooledConnectionObject.getObject().stop(); } }