Package org.jboss.internal.soa.esb.rosetta.pooling

Source Code of org.jboss.internal.soa.esb.rosetta.pooling.JmsConnectionPool$DaemonThreadFactory

/*
* JBoss, Home of Professional Open Source
* Copyright 2006, JBoss Inc., and individual contributors as indicated
* by the @authors tag. See the copyright.txt in the distribution for a
* full listing of individual contributors.
*
* This is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as
* published by the Free Software Foundation; either version 2.1 of
* the License, or (at your option) any later version.
*
* This software is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this software; if not, write to the Free
* Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
* 02110-1301 USA, or see the FSF site: http://www.fsf.org.
*/
package org.jboss.internal.soa.esb.rosetta.pooling;

import java.sql.SQLException;
import java.util.*;
import java.util.concurrent.Callable;
import java.util.concurrent.CompletionService;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.Executor;
import java.util.concurrent.ExecutorCompletionService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
import java.util.concurrent.ThreadFactory;

import javax.jms.Connection;
import javax.jms.ConnectionFactory;
import javax.jms.ExceptionListener;
import javax.jms.JMSException;
import javax.jms.Session;
import javax.jms.XAConnection;
import javax.jms.XAConnectionFactory;
import javax.naming.Context;
import javax.naming.NamingException;

import org.apache.log4j.Logger;
import org.jboss.soa.esb.addressing.eprs.JMSEpr;
import org.jboss.soa.esb.common.Configuration;
import org.jboss.soa.esb.common.Environment;
import org.jboss.soa.esb.common.ModulePropertyManager;
import org.jboss.soa.esb.common.TransactionStrategy;
import org.jboss.soa.esb.common.TransactionStrategyException;
import org.jboss.soa.esb.helpers.NamingContextException;
import org.jboss.soa.esb.helpers.NamingContextPool;
import org.jboss.soa.esb.util.ClassUtil;
import org.jboss.soa.esb.util.JmsUtil;

import com.arjuna.common.util.propertyservice.PropertyManager;

/**
* JmsConnectionPool.
*
* @author kstam
* @author <a href="mailto:daniel.bevenius@gmail.com">Daniel Bevenius</a>       
* Date: March 10, 2007
*/
public class JmsConnectionPool
{
  private static final int DEFAULT_POOL_SIZE = 20;
  private static final int DEFAULT_SLEEP = 30;
 
  private static int CONFIGURED_POOL_SIZE = DEFAULT_POOL_SIZE;
  private static int CONFIGURED_SLEEP = DEFAULT_SLEEP;

  static final int XA_TRANSACTED = -1 ;
  /**
   * The executor used to create sessions.
   */
  private static final Executor SESSION_EXECUTOR = Executors.newSingleThreadExecutor(new DaemonThreadFactory()) ;
  /**
   * The completion service.
   */
  private static final CompletionService<JmsSession> COMPLETION_SERVICE = new ExecutorCompletionService<JmsSession>(SESSION_EXECUTOR) ;
  /** Logger */
  private static final Logger LOGGER = Logger.getLogger(JmsConnectionPool.class);
 
  /**
   * The JMS COnnection Exception Handlers.
   */
  private static final JmsConnectionExceptionHandler[] CONNECTION_EXCEPTION_HANDLERS ;
 
    /** Maximum number of Sessions that will be created in this pool */
    private int maxSessions = DEFAULT_POOL_SIZE;    //TODO Make this manageable

    /**
     * Connection ClientId.
     */
    private String clientId;

    /**
     * The max number of sessions per connection.  Defaults to "maxSessions".
     */
    private int maxSessionsPerConnection;

    /**
     * The max number of XA sessions per connection.  Defaults to "maxSessionsPerConnection".
     */
    private int maxXASessionsPerConnection;

    /** Time to sleep when trying to get a session. */
    private int sleepTime = DEFAULT_SLEEP;

    /** The Indentifier of the pool */
    private Map<String, String> poolKey;

    /**
     * JMS Session Pools.
     */
    private List<JmsSessionPool> sessionPools = new ArrayList<JmsSessionPool>();
    /**
     * The flag representing XA aware connections.
     */
    private Boolean isXAAware ;
    /**
     * Flag signifying that the pool has been terminated.
     */
    private boolean terminated ;
    /**
     * The pool instance id.
     */
    private long id ;

    /**
     * Contructor of the pool.
     *
     */
    public JmsConnectionPool(Map<String, String> poolKey) throws ConnectionException {
      this(poolKey, JmsConnectionPool.CONFIGURED_POOL_SIZE, JmsConnectionPool.CONFIGURED_SLEEP);
    }
   
    public JmsConnectionPool(Map<String, String> poolKey, int poolSize, int sleepTime) throws ConnectionException {
        this.poolKey = poolKey;
       
        maxSessions = poolSize;
        this.sleepTime = sleepTime;

        maxSessionsPerConnection = getIntPoolConfig(poolKey, JMSEpr.MAX_SESSIONS_PER_CONNECTION, maxSessions);
        if(maxSessionsPerConnection < 1) {
            throw new ConnectionException("Invalid '" + JMSEpr.MAX_SESSIONS_PER_CONNECTION + "' configuration value '" + maxSessionsPerConnection + "'.  Must be greater than 0.");
        }
        maxXASessionsPerConnection = getIntPoolConfig(poolKey, JMSEpr.MAX_XA_SESSIONS_PER_CONNECTION, maxSessionsPerConnection);
        if(maxXASessionsPerConnection < 1) {
            throw new ConnectionException("Invalid '" + JMSEpr.MAX_XA_SESSIONS_PER_CONNECTION + "' configuration value '" + maxXASessionsPerConnection + "'.  Must be greater than 0.");
        } else if(maxXASessionsPerConnection > maxSessionsPerConnection) {
            throw new ConnectionException("Invalid '" + JMSEpr.MAX_XA_SESSIONS_PER_CONNECTION + "' configuration value '" + maxXASessionsPerConnection + "'.  Cannot be greater than the configured value for '" + JMSEpr.MAX_SESSIONS_PER_CONNECTION + "', which is " + maxSessionsPerConnection + ".");
        }
       
        clientId = poolKey.get(JMSEpr.CLIENT_ID);
    }

    private int getIntPoolConfig(Map<String, String> poolKey, String configKey, int defaultVal) throws ConnectionException {
        String configValueString = poolKey.get(configKey);
        int configValue = defaultVal;

        if(configValueString != null) {
            try {
                configValue = Integer.parseInt(configValueString.trim());
            } catch(NumberFormatException e) {
                throw new ConnectionException("Invalid '" + configKey + "' configuration value '" +  configValueString.trim() + "'.  Must be a valid Integer.");
            }
        }

        return configValue;
    }

    protected int getMaxSessions() {
        return maxSessions;
    }

    protected int getMaxSessionsPerConnection() {
        return maxSessionsPerConnection;
    }

    public int getMaxXASessionsPerConnection() {
        return maxXASessionsPerConnection;
    }

    protected List<JmsSessionPool> getSessionPools() {
        return sessionPools;
    }

    /**
     *  This method can be called whenever a connection is needed from the pool.
     *
     * @return Connection to be used
     * @throws ConnectionException
     */
    public synchronized JmsSession getSession(final int acknowledgeMode) throws NamingException, JMSException, ConnectionException
    {
        if (terminated)
        {
            throw new ConnectionException("Connection pool has been terminated") ;
        }

        try
        {
            return internalGetSession(acknowledgeMode) ;
        }
        catch (final JMSException jmse)
        {
            if (isConnectionFailure(jmse) != null)
            {
                return internalGetSession(acknowledgeMode) ;
            }
            throw jmse ;
        }
    }

    private synchronized JmsSession internalGetSession(final int acknowledgeMode)
        throws NamingException, JMSException, ConnectionException
    {
        if(sessionPools.isEmpty()) {
            // Create the first pool entry...
            addSessionPool();
        }

        if(isXAAware == null) {
          try
          {
              getFactoryConnection() ;
          }
          catch (final NamingContextException nce)
          {
              throw new ConnectionException("Unexpected exception accessing Naming Context", nce) ;
           }
        }

        final boolean transacted ;
        try {
            transacted = (isXAAware && TransactionStrategy.getTransactionStrategy(true).isActive()) ;
        } catch (final TransactionStrategyException tse) {
            throw new ConnectionException("Failed to determine current transaction context", tse) ;
        }

        if (transacted)
        {
            final JmsXASession currentSession = getXASession() ;
            if (currentSession != null)
            {
                currentSession.incrementReferenceCount() ;
                return currentSession ;
            }
        }

        final int mode = (transacted ? XA_TRANSACTED : acknowledgeMode) ;
        final long end = System.currentTimeMillis() + (sleepTime * 1000) ;
        boolean emitExpiry = LOGGER.isDebugEnabled() ;

        while(true) {
            // Cycle through all the existing session pools and try getting a
            // free session.  Will JmsSessionPool.getSession will add a session
            // to a pool that is not "full"...
            for (JmsSessionPool sessionPool : sessionPools) {
                try {
                    JmsSession session = sessionPool.getSession(mode, transacted);
   
                    if(session != null) {
                        return session;
                    }  
                } catch (final JMSException jmse) {
                    final Boolean failure = isConnectionFailure(jmse) ;
                    if (failure != null) {
                        sessionPool.cleanSessionPool(failure.booleanValue()) ;
                    }
                    throw jmse ;
                }
            }

            // OK... all the existing session pools are full and have no free sessions.  If we can add
            // another session pool, add it and then start this loop again...
            if(getSessionsInPool() < maxSessions) {
                addSessionPool();
                continue;
            }

            // We've reached our max permitted number of connections and the sessions
            // associated with these connections are all in use. Drop into the
            // delay below and wait for a session to be freed...
            if (emitExpiry)
            {
                LOGGER.debug("The connection pool was exhausted, waiting for a session to be released.") ;
                emitExpiry = false ;
            }

            // Wait and loop again...
            final long now = System.currentTimeMillis() ;
            final long delay = (end - now) ;
            if (delay <= 0)
            {
                throw new ConnectionException("Could not obtain a JMS connection from the pool after "+ sleepTime +"s.");
            }
            else
            {
                try
                {
                    wait(delay) ;
                }
                catch (final InterruptedException ie) {} // ignore
            }
        }
    }
   
    private Object getFactoryConnection()
        throws NamingContextException, NamingException
    {
        String connectionFactoryString = poolKey.get(JMSEpr.CONNECTION_FACTORY_TAG);
        Object factoryConnection=null;

        Properties jndiEnvironment = JmsConnectionPoolContainer.getJndiEnvironment(poolKey);
        Context jndiContext = NamingContextPool.getNamingContext(jndiEnvironment);
        try
        {
            factoryConnection = jndiContext.lookup(connectionFactoryString);
        }
        catch (NamingException ne)
        {
            LOGGER.info("Received NamingException, refreshing context.");
            jndiContext = NamingContextPool.replaceNamingContext(jndiContext, JmsConnectionPoolContainer.getJndiEnvironment(poolKey));
            factoryConnection = jndiContext.lookup(connectionFactoryString);
        }
        finally
        {
            NamingContextPool.releaseNamingContext(jndiContext) ;
        }

        isXAAware = (factoryConnection instanceof XAConnectionFactory) ;

        return factoryConnection ;
    }

    private JmsSessionPool addSessionPool() throws NamingException, JMSException, ConnectionException {
        final JmsSessionPool sessionPool = new JmsSessionPool();

        // And add it to the pool...
        sessionPools.add(sessionPool);

        return sessionPool;
    }

    /**
     *  This method can be called whenever a Session is needed from the pool.
     * @return
     * @throws NamingException
     * @throws JMSException
     * @throws ConnectionException
     */
    public JmsSession getSession() throws NamingException, JMSException, ConnectionException
    {
        return getSession(Session.AUTO_ACKNOWLEDGE);
    }
   
    /**
     * This method closes an open connection and returns the connection to the pool.
     * @param session The connection to be returned to the pool.
     * @throws SQLException
     * @deprecated
     */
    public void closeSession(Session session){
        if (session instanceof JmsSession) {
            closeSession((JmsSession)session) ;
        } else {
            LOGGER.error("Invalid JMS Session type in closeSession: " + session);
        }
    }
   
    /**
     * This method closes an open connection and returns the connection to the pool.
     * @param session The connection to be returned to the pool.
     * @throws SQLException
     */
    public void closeSession(JmsSession session){
        session.handleCloseSession(this) ;
    }
   
    /**
     * Handle the real work of closing the connection.
     * @param session The session to close.
     */
    synchronized void handleCloseSession(final JmsSession session)
    {
        JmsSessionPool sessionPool = findOwnerPool(session);

        if(sessionPool != null) {
            sessionPool.handleCloseSession(session);
        }
    }
   
    /**
     * Handle the real work of releasing the connection.
     * @param session The session to release.
     */
    synchronized void handleReleaseSession(final JmsSession session)
    {
        session.releaseResources();
        try
        {
            session.close() ;
        }
        catch (final Throwable th) {} // ignore
        releaseInUseSession(session) ;
    }
   
    /**
     * Handle exceptions that occur in the session or its child objects.
     * @param jmsSession The jmsSession associated with the call.
     * @param jmse The JMSException to check.
     * @throws JMSException if the exception is to be overridden.
     */
    void handleException(final JmsSession jmsSession, final JMSException jmse)
        throws JMSException
    {
        final Boolean failure = isConnectionFailure(jmse) ;
        if (failure != null) {
            final JmsSessionPool sessionPool = findOwnerPool(jmsSession);

            if(sessionPool != null) {
                sessionPool.cleanSessionPool(failure.booleanValue()) ;
            }
            throw new JmsConnectionFailureException("The underlying exception appears to have failed", jmse) ;
        }
    }

    /**
     * Check the exception to see whether it indicates a connection failure
     * @param jmse The current JMS Exception
     * @return null if no connection failure, TRUE if the connection should be closed, FALSE if just cleaned up.
     */
    private Boolean isConnectionFailure(final JMSException jmse)
    {
        for(JmsConnectionExceptionHandler connectionExceptionHandler: CONNECTION_EXCEPTION_HANDLERS)
        {
            final Boolean isConnectionFailure = connectionExceptionHandler.isConnectionFailure(jmse) ;
            if (isConnectionFailure != null)
            {
                LOGGER.debug("Connection failure detected by handler: " + connectionExceptionHandler.getClass().getName(), jmse) ;
                LOGGER.debug("Linked exception", jmse.getLinkedException()) ;
                return isConnectionFailure ;
            }
        }
        return null ;
    }

    /**
     * Release a session from the in use mapping.
     * @param session The session to release.
     */
    private void releaseInUseSession(final JmsSession session)
    {
        JmsSessionPool sessionPool = findOwnerPool(session);

        if(sessionPool != null) {
            sessionPool.releaseInUseSession(session);
        }

        notifyAll() ;
    }
   
    /**
     * This method closes an open session without returning it to the pool.
     * @param session The session to be returned to the pool.
     * @throws SQLException
     */
    public synchronized void releaseSession(final JmsSession session) {
        session.handleReleaseSession(this) ;
    }
   
    /**
     * This method closes an open session without returning it to the pool.
     * @param session The session to be returned to the pool.
     * @throws SQLException
     * @deprecated
     */
    public synchronized void releaseSession(final Session session) {
      if (session instanceof JmsSession) {
          releaseSession((JmsSession)session) ;
      } else {
          LOGGER.error("Invalid JMS Session type in releaseSession: " + session);
      }
    }

    /**
     * This method is called when the pool needs to destroyed. It closes all open sessions
     * and the connection and removes it from the container's poolMap.
     */
    public synchronized void removeSessionPool()
    {
        for(JmsSessionPool sessionPool : sessionPools) {
            try {
                sessionPool.removeSessionPool();
            } catch(Exception e) {
                LOGGER.error("Exception while removing JmsSessionPool.", e);
            }
        }

        sessionPools.clear();
        terminated = true ;
        JmsConnectionPoolContainer.removePool(poolKey);
    }
    /**
     * Gets the total number of sessions in the pool regardless of the acknowlede mode
     * used when creating the sessions.
     * @return the session pool size
     */
    public int getSessionsInPool() {
        return getSessionsInPool(Session.AUTO_ACKNOWLEDGE) +
            getSessionsInPool(Session.CLIENT_ACKNOWLEDGE) +
            getSessionsInPool(Session.DUPS_OK_ACKNOWLEDGE) +
            getSessionsInPool(Session.SESSION_TRANSACTED) +
            getSessionsInPool(XA_TRANSACTED) ;
    }
   
    /**
     * Returns the total nr of sessions for the specifed acknowledge mode
     *
     * @param acknowledgeMode the acknowledge mode of sessions
     * @return
     */
    public synchronized int getSessionsInPool(final int acknowledgeMode) {
        return getFreeSessionsInPool(acknowledgeMode) + getInUseSessionsInPool(acknowledgeMode) ;
    }
   
    /**
     * Get the number of free sessions created with the specified acknowledge mode
     * @param acknowledgeMode the acknowledge mode of sessions
     * @return int  the number of in use sessions
     */
    public synchronized int getFreeSessionsInPool(final int acknowledgeMode) {
        int count = 0;

        for(JmsSessionPool sessionPool : sessionPools) {
            count += sessionPool.getFreeSessionsInPool(acknowledgeMode);
        }

        return count;
    }
   
    /**
     * Get the number of sessions that are in use and that were
     * created with the specified acknowledge mode
     * @param acknowledgeMode the acknowledge mode of sessions
     * @return int  the number of in use sessions
     */
    public synchronized int getInUseSessionsInPool(final int acknowledgeMode) {
        int count = 0;

        for(JmsSessionPool sessionPool : sessionPools) {
            count += sessionPool.getInUseSessionsInPool(acknowledgeMode);
        }

        return count;
    }
   
    /**
     * Get the current transaction.
     * @return The transaction or null if none present.
     * @throws ConnectionException if the transaction context cannot be obtained.
     */
    private Object getTransaction()
        throws ConnectionException
    {
        try {
            return TransactionStrategy.getTransactionStrategy(true).getTransaction() ;
        } catch (final TransactionStrategyException tse) {
            throw new ConnectionException("Failed to determine current transaction context", tse) ;
        }
    }
   
    /**
     * Get a JMS session associated with the current transaction.
     * @return The JMS session or null if not associated.
     * @throws ConnectionException For accessint the current transaction context
     */
    private synchronized JmsXASession getXASession()
        throws ConnectionException
    {
        final Object tx = getTransaction() ;

        for(JmsSessionPool sessionPool : sessionPools) {
            JmsXASession session = sessionPool.transactionsToSessions.get(tx);
            if(session != null) {
                return session;
            }
        }

        return null;
    }
   
    /**
     * Associate the JMS XA Session with the current transaction.
     * @param session The XA session.
     * @throws ConnectionException if there is no transaction active.
     */
    void associateTransaction(final JmsXASession session)
        throws ConnectionException
    {
        JmsSessionPool sessionPool = findOwnerPool(session);

        if(sessionPool != null) {
            sessionPool.associateTransaction(session);
        }
    }

    /**
     * Disassociate the JMS XA Session from a transaction.
     * @param session The XA session.
     */
    void disassociateTransaction(final JmsXASession session)
    {
        JmsSessionPool sessionPool = findOwnerPool(session);

        if(sessionPool != null) {
            sessionPool.disassociateTransaction(session);
        }
    }
   
    /**
     * Check that the session is associated with the current transaction.
     * @param session The XA session.
     */
    boolean isAssociated(final JmsXASession session)
    {
        JmsSessionPool sessionPool = findOwnerPool(session);

        if(sessionPool != null) {
            return sessionPool.isAssociated(session);
        } else {
            return false ;
        }
    }

    JmsSessionPool findOwnerPool(final JmsSession session) {
        return session.getSessionPool() ;
    }

    static
    {
      PropertyManager prop = ModulePropertyManager.getPropertyManager(ModulePropertyManager.TRANSPORTS_MODULE);
      String value = prop.getProperty(Environment.JMS_CONNECTION_POOL_SIZE);
     
      if (value != null)
      {
        try
        {
          CONFIGURED_POOL_SIZE = Integer.parseInt(value);
        }
        catch (NumberFormatException ex)
        {
          ex.printStackTrace();
        }
      }
     
      value = prop.getProperty(Environment.JMS_SESSION_SLEEP);
     
      if (value != null)
      {
        try
        {
          CONFIGURED_SLEEP = Integer.parseInt(value);
        }
        catch (NumberFormatException ex)
        {
          ex.printStackTrace();
        }
      }
     
      value = Configuration.getJMSConnectionExceptionHandlers() ;
        final String[] handlerNames = (value == null ? null : value.split(",")) ;
       
        final ArrayList<JmsConnectionExceptionHandler> handlers = new ArrayList<JmsConnectionExceptionHandler>() ;
        for(String handler: handlerNames)
        {
            final Class<?> handlerClass ;
            try
            {
                handlerClass = ClassUtil.forName(handler.trim(), JmsConnectionPool.class) ;
            }
            catch (final ClassNotFoundException cnfe)
            {
                LOGGER.error("Failed to instantiate connection exception handler, ignoring", cnfe) ;
                continue ;
            }
            if (!JmsConnectionExceptionHandler.class.isAssignableFrom(handlerClass))
            {
                LOGGER.error("Handler does not implement JmsConnectionExceptionHandler interface, ignoring: " + handlerClass.getName()) ;
                continue ;
            }
            final Object instance ;
            try
            {
                instance = handlerClass.newInstance() ;
            }
            catch (final Throwable th)
            {
                LOGGER.error("Failed to instantiate handler, ignoring", th) ;
                continue ;
            }
            handlers.add((JmsConnectionExceptionHandler)instance) ;
        }
        CONNECTION_EXCEPTION_HANDLERS = handlers.toArray(new JmsConnectionExceptionHandler[handlers.size()]) ;
    }

    class JmsSessionPool {

        /** Reference to a Queue or Topic Connection, we only need one per pool */
        protected Connection jmsConnection ;

        /** Number of free sessions in the pool that can be given out. Indexed by session key */
        private Map<Integer,ArrayList<JmsSession>> freeSessionsMap = new HashMap<Integer,ArrayList<JmsSession>>();

        /** Number of session that are currently in use. Indexed by session key mode */
        private Map<Integer,ArrayList<JmsSession>> inUseSessionsMap = new HashMap<Integer,ArrayList<JmsSession>>();

        /**
         * Mapping from transactions to sessions.
         */
        private Map<Object, JmsXASession> transactionsToSessions = new HashMap<Object, JmsXASession>() ;

        /**
         * Mapping from sessions to transactions.
         */
        private Map<JmsXASession, Object> sessionsToTransactions = new HashMap<JmsXASession, Object>() ;

        private JmsSessionPool() {
            freeSessionsMap.put(Session.AUTO_ACKNOWLEDGE, new ArrayList<JmsSession>() );
            freeSessionsMap.put(Session.CLIENT_ACKNOWLEDGE, new ArrayList<JmsSession>() );
            freeSessionsMap.put(Session.DUPS_OK_ACKNOWLEDGE, new ArrayList<JmsSession>() );
            freeSessionsMap.put(Session.SESSION_TRANSACTED, new ArrayList<JmsSession>() );

            inUseSessionsMap.put(Session.AUTO_ACKNOWLEDGE, new ArrayList<JmsSession>() );
            inUseSessionsMap.put(Session.CLIENT_ACKNOWLEDGE, new ArrayList<JmsSession>() );
            inUseSessionsMap.put(Session.DUPS_OK_ACKNOWLEDGE, new ArrayList<JmsSession>() );
            inUseSessionsMap.put(Session.SESSION_TRANSACTED, new ArrayList<JmsSession>() );
        }

        public synchronized void removeSessionPool() {
            freeSessionsMap = null ;
            inUseSessionsMap = null ;
            transactionsToSessions = null ;
            sessionsToTransactions = null ;

            LOGGER.debug("Emptied the session pool now closing the connection to the factory.");
            if (jmsConnection!=null) {
                try {
                    jmsConnection.close();
                } catch (final Exception ex) {} // ignore
                jmsConnection=null;
            }
        }

        public synchronized JmsSession getSession(int acknowledgeMode, boolean transacted) throws ConnectionException, NamingException, JMSException {

            initConnection() ;
           
            ArrayList<JmsSession> freeSessions = freeSessionsMap.get(acknowledgeMode );
            ArrayList<JmsSession> inUseSessions = inUseSessionsMap.get(acknowledgeMode);

            if (freeSessions.size() > 0)
            {
                final JmsSession session = freeSessions.remove(freeSessions.size()-1);
                if (transacted)
                {
                    final JmsXASession xaSession = (JmsXASession)session ;
                    xaSession.incrementReferenceCount() ;
                }
                inUseSessions.add(session);
                return session ;
            } else if (getSessionsInPool() < maxSessionsPerConnection) {
                JmsSession session = null;
               
                if(transacted) {
                    if(getXASessionsInPool() < maxXASessionsPerConnection) {
                        session = addAnotherSession(poolKey, transacted, acknowledgeMode);
                    }
                } else {
                    session = addAnotherSession(poolKey, transacted, acknowledgeMode);
                }

                if(session != null) {
                    inUseSessions.add(session);
                    return session ;
                }
            }

            return null;
        }

        private synchronized void initConnection()
            throws ConnectionException, NamingException, JMSException
        {
            if (terminated)
            {
                throw new ConnectionException("Connection pool has been terminated") ;
            }

            if (jmsConnection == null)
            {
                try {
                    LOGGER.debug("Creating a JMS Connection for poolKey : " + poolKey);
                    final Object factoryConnection = getFactoryConnection() ;
                    final String username = poolKey.get( JMSEpr.JMS_SECURITY_PRINCIPAL_TAG );
                    String password = poolKey.get( JMSEpr.JMS_SECURITY_CREDENTIAL_TAG );
                    boolean useJMSSecurity = JmsUtil.isSecurityConfigured(username, password);
                    LOGGER.debug( "JMS Security principal [" + username + "] using JMS Security : " + useJMSSecurity );
                   
                    if (useJMSSecurity)
                    {
                        password = JmsUtil.getPasswordFromFile(password);
                    }
                   
                    if (isXAAware)
                    {
                        final XAConnectionFactory factory = (XAConnectionFactory)factoryConnection ;
                        jmsConnection = useJMSSecurity ? factory.createXAConnection(username,password): factory.createXAConnection();
                        freeSessionsMap.put(XA_TRANSACTED, new ArrayList<JmsSession>() );
                        inUseSessionsMap.put(XA_TRANSACTED, new ArrayList<JmsSession>() );
                    }
                    else if (factoryConnection instanceof ConnectionFactory)
                    {
                        final ConnectionFactory factory = (ConnectionFactory)factoryConnection ;
                        jmsConnection = useJMSSecurity ? factory.createConnection(username,password): factory.createConnection();
                    }
                    else
                    {
                        throw new ConnectionException("Unknown factory connection type: " + factoryConnection.getClass().getCanonicalName());
                    }
                   
                    if(clientId != null) {
                      jmsConnection.setClientID(clientId);
                    }
       
                    jmsConnection.setExceptionListener(new ExceptionListener() {
                        public void onException(JMSException arg0)
                        {
                            // This will result in all connections (and their sessions) on this pool
                            // being closed...
                            cleanSessionPool() ;
                        }
                    }) ;
                    jmsConnection.start();
                } catch (final NamingContextException nce) {
                    throw new ConnectionException("Unexpected exception accessing Naming Context", nce) ;
                }
            }
        }

        /**
         * This is where we create the sessions.
         *
         * @param poolKey
         * @param transacted
         * @throws JMSException
         */
        private  synchronized JmsSession addAnotherSession(Map<String, String> poolKey, final boolean transacted, final int acknowledgeMode)
            throws JMSException
        {
            // Sessions need to be created in this way because of an issue with JBM.
            // See https://jira.jboss.org/jira/browse/JBESB-1799
            final long currentID = id ;
            final Connection currentConnection = jmsConnection ;
            final Future<JmsSession> future = COMPLETION_SERVICE.submit(new Callable<JmsSession>() {
                public JmsSession call()
                    throws JMSException
                {
                    final JmsSession session ;

                    if (transacted) {
                        final JmsXASession xaSession = new JmsXASession(JmsConnectionPool.this, JmsSessionPool.this, ((XAConnection)currentConnection).createXASession(), currentID, acknowledgeMode);
                        xaSession.incrementReferenceCount() ;
                        session = xaSession ;
                    } else {
                        session = new JmsSession(JmsConnectionPool.this, JmsSessionPool.this, currentConnection.createSession(transacted, acknowledgeMode), currentID, acknowledgeMode);
                    }

                    return session ;
                }
            }) ;

            // For now we only support JTA transacted sessions
            try
            {
                JmsSession session = future.get();
                LOGGER.debug("Number of Sessions in the pool with acknowledgeMode: " + acknowledgeMode + " is now " + getSessionsInPool(acknowledgeMode));
                return session;
            }
            catch (final InterruptedException ie) {} // ignore
            catch (final ExecutionException ee)
            {
                final Throwable th = ee.getCause() ;
                if (th instanceof JMSException)
                {
                    throw (JMSException)th ;
                }
                if (th instanceof Error)
                {
                    throw (Error)th ;
                }
                if (th instanceof RuntimeException)
                {
                    throw (RuntimeException)th ;
                }
            }

            return null;
        }

        public void cleanSessionPool() {
            cleanSessionPool(true) ;
        }
       
        public void cleanSessionPool(final boolean closeConnection) {
            final Connection connection ;
            synchronized(this)
           {
                if (terminated)
                {
                    return ;
                }
                id++ ;
                for (List<JmsSession> list : freeSessionsMap.values())
                {
                    list.clear() ;
                }
                for (List<JmsSession> list : inUseSessionsMap.values())
                {
                    list.clear() ;
                }
                transactionsToSessions.clear() ;
                sessionsToTransactions.clear() ;

                LOGGER.debug("Cleared the session pool now closing the connection to the factory.");
                connection = jmsConnection ;
                jmsConnection = null ;
            }
            if (closeConnection && (connection!=null)) {
                try {
                    connection.close();
               } catch (final Exception ex) {} // ignore
            }
        }
       
        /**
         * Returns the total number of sessions in the pool.
         * @return The total number of sessions in the pool.
         */
        private synchronized int getSessionsInPool() {
            // Get a count of all sessions (of any type) in the pool...
            return getSessionsInPool(JmsSession.class);
        }

        /**
         * Returns the total number of XA sessions in the pool.
         * @return The total number of XA sessions in the pool.
         */
        private synchronized int getXASessionsInPool() {
            // Get a count of XA sessions in the pool...
            return getSessionsInPool(JmsXASession.class);
        }

        /**
         * Returns the total number of XA sessions in the pool.
         * @return The total number of XA sessions in the pool.
         */
        private synchronized int getSessionsInPool(Class<? extends JmsSession> jmsSessionType) {
            int total = 0;

            total += getSessionsInMap(freeSessionsMap, jmsSessionType);
            total += getSessionsInMap(inUseSessionsMap, jmsSessionType);

            return total;
        }

        private synchronized int getSessionsInMap(Map<Integer, ArrayList<JmsSession>> sessionsMap, Class<? extends JmsSession> jmsSessionType) {
            Collection<ArrayList<JmsSession>> sessionLists = sessionsMap.values();
            int total = 0;

            for(ArrayList<JmsSession> sessionList : sessionLists) {
                for(JmsSession session : sessionList) {
                    if(jmsSessionType.isAssignableFrom(session.getClass())) {
                        total++;
                    }
                }
            }

            return total;
        }

        /**
         * Returns the total nr of sessions for the specifed acknowledge mode
         *
         * @param acknowledgeMode the acknowledge mode of sessions
         * @return
         */
        public synchronized int getSessionsInPool(final int acknowledgeMode) {
            return getFreeSessionsInPool(acknowledgeMode) + getInUseSessionsInPool(acknowledgeMode) ;
        }

        /**
         * Get the number of free sessions created with the specified acknowledge mode
         * @param acknowledgeMode the acknowledge mode of sessions
         * @return int  the number of in use sessions
         */
        public synchronized int getFreeSessionsInPool(final int acknowledgeMode) {
            final ArrayList<JmsSession> freeSessionMap = (freeSessionsMap == null ? null : freeSessionsMap.get(acknowledgeMode)) ;
            final int numFreeSessions = (freeSessionMap == null ? 0 : freeSessionMap.size()) ;
            return numFreeSessions;
        }

        /**
         * Get the number of sessions that are in use and that were
         * created with the specified acknowledge mode
         * @param acknowledgeMode the acknowledge mode of sessions
         * @return int  the number of in use sessions
         */
        public synchronized int getInUseSessionsInPool(final int acknowledgeMode) {
            final ArrayList<JmsSession> inUseSessionMap = (inUseSessionsMap == null ? null : inUseSessionsMap.get(acknowledgeMode)) ;
            final int numInUseSessions = (inUseSessionMap == null ? 0 : inUseSessionMap.size()) ;
            return numInUseSessions;
        }

        /**
         * Associate the JMS XA Session with the current transaction.
         * @param session The XA session.
         * @throws ConnectionException if there is no transaction active.
         */
        void associateTransaction(final JmsXASession session)
            throws ConnectionException
        {
            final Object tx = getTransaction() ;
            if (tx == null)
            {
                throw new ConnectionException("No active transaction") ;
            }
            synchronized (this)
            {
                transactionsToSessions.put(tx, session) ;
                sessionsToTransactions.put(session, tx) ;
            }
        }

        /**
         * Disassociate the JMS XA Session from a transaction.
         * @param session The XA session.
         */
        void disassociateTransaction(final JmsXASession session)
        {
            final Object tx ;
            synchronized(this)
            {
                tx = sessionsToTransactions.remove(session) ;
                transactionsToSessions.remove(tx) ;
            }
        }

        /**
         * Check that the session is associated with the current transaction.
         * @param session The XA session.
         */
        boolean isAssociated(final JmsXASession session)
        {
            try
            {
                final Object current = TransactionStrategy.getTransactionStrategy(true).getTransaction() ;
                final Object tx ;
                synchronized(this)
                {
                    tx = sessionsToTransactions.get(session) ;
                }
                if (tx != null)
                {
                    return tx.equals(current) ;
                }
            }
            catch (final TransactionStrategyException tse) {} // ignore
           
            return false ;
        }

        public void releaseInUseSession(JmsSession session) {
            final int mode = session.getRequestedAcknowledgeMode() ;

            final ArrayList<JmsSession> sessions = (inUseSessionsMap == null ? null : inUseSessionsMap.get(mode));
            if (sessions != null) {
                sessions.remove(session) ;
            }
        }

        public void handleCloseSession(JmsSession session) {
            if (session.isSuspect())
            {
                LOGGER.debug("Session is suspect, dropping") ;
                handleReleaseSession(session) ;
            }
            else
            {
                if (session.getId() != id)
                {
                    LOGGER.debug("Session is from a previous incarnation, dropping") ;
                }
                else
                {
                    final int mode = session.getRequestedAcknowledgeMode() ;

                    final ArrayList<JmsSession> sessions = (freeSessionsMap == null ? null : freeSessionsMap.get(mode));
                    if (sessions != null) {
                        sessions.add(session.duplicateSession()) ;
                    }
                }
                session.releaseResources() ;
                releaseInUseSession(session) ;
            }
        }
    }

    /**
     * Thread factory returning daemon threads.
     * <p/>
     * Required as part of the fix for https://jira.jboss.org/jira/browse/JBESB-1799
     *
     * @author kevin
     */
    private static final class DaemonThreadFactory implements ThreadFactory
    {
        /**
         * The default executor factory.
         */
        private final ThreadFactory defaultFactory = Executors.defaultThreadFactory() ;

        /**
         * Return a new daemon thread.
         * @param runnable The runnable associated with the thread.
         */
        public Thread newThread(final Runnable runnable)
        {
            final Thread thread = defaultFactory.newThread(runnable) ;
            thread.setDaemon(true) ;
            return thread ;
        }
    }
}
TOP

Related Classes of org.jboss.internal.soa.esb.rosetta.pooling.JmsConnectionPool$DaemonThreadFactory

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.