All Downloads are FREE. Search and download functionalities are using the official Maven repository.

bitronix.tm.resource.jms.JmsPooledConnection Maven / Gradle / Ivy

There is a newer version: 62
Show newest version
/*
 * Copyright (C) 2006-2013 Bitronix Software (http://www.bitronix.be)
 *
 * Licensed 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 bitronix.tm.resource.jms;

import bitronix.tm.internal.BitronixSystemException;
import bitronix.tm.internal.LogDebugCheck;
import bitronix.tm.resource.common.AbstractXAStatefulHolder;
import bitronix.tm.resource.common.RecoveryXAResourceHolder;
import bitronix.tm.resource.common.StateChangeListener;
import bitronix.tm.resource.common.TransactionContextHelper;
import bitronix.tm.resource.jms.lrc.LrcXAConnectionFactory;
import bitronix.tm.utils.ManagementRegistrar;
import bitronix.tm.utils.MonotonicClock;
import bitronix.tm.utils.Scheduler;

import jakarta.jms.*;
import javax.transaction.xa.XAResource;
import java.util.*;
import java.util.logging.Level;

/**
 * Implementation of a JMS pooled connection wrapping vendor's {@link XAConnection} implementation.
 *
 * @author Ludovic Orban
 */
@SuppressWarnings("unused")
public class JmsPooledConnection
		extends AbstractXAStatefulHolder
		implements JmsPooledConnectionMBean
{

	private static final java.util.logging.Logger log = java.util.logging.Logger.getLogger(JmsPooledConnection.class.toString());
	private static final String EMULATE = "emulating XA for resource ";

	private final PoolingConnectionFactory poolingConnectionFactory;
	private final Set sessions = Collections.synchronizedSet(new HashSet<>());
	/* management */
	private final String jmxName;
	private volatile XAConnection xaConnection;
	private volatile Date acquisitionDate;
	private volatile Date lastReleaseDate;

	/**
	 * Constructor JmsPooledConnection creates a new JmsPooledConnection instance.
	 *
	 * @param poolingConnectionFactory
	 * 		of type PoolingConnectionFactory
	 * @param connection
	 * 		of type XAConnection
	 */
	protected JmsPooledConnection(PoolingConnectionFactory poolingConnectionFactory, XAConnection connection)
	{
		this.poolingConnectionFactory = poolingConnectionFactory;
		this.xaConnection = connection;
		this.lastReleaseDate = new Date(MonotonicClock.currentTimeMillis());
		addStateChangeEventListener(new JmsPooledConnectionStateChangeListener());

		if (LrcXAConnectionFactory.class.getName()
		                                .equals(poolingConnectionFactory.getClassName()))
		{
			if (LogDebugCheck.isDebugEnabled())
			{
				log.finer(EMULATE + poolingConnectionFactory.getUniqueName() + " - changing twoPcOrderingPosition to ALWAYS_LAST_POSITION");
			}
			poolingConnectionFactory.setTwoPcOrderingPosition(Scheduler.ALWAYS_LAST_POSITION);
			if (LogDebugCheck.isDebugEnabled())
			{
				log.finer(EMULATE + poolingConnectionFactory.getUniqueName() + " - changing deferConnectionRelease to true");
			}
			poolingConnectionFactory.setDeferConnectionRelease(true);
			if (LogDebugCheck.isDebugEnabled())
			{
				log.finer(EMULATE + poolingConnectionFactory.getUniqueName() + " - changing useTmJoin to true");
			}
			poolingConnectionFactory.setUseTmJoin(true);
		}

		this.jmxName = "bitronix.tm:type=JMS,UniqueName=" + ManagementRegistrar.makeValidName(poolingConnectionFactory.getUniqueName()) + ",Id=" +
		               poolingConnectionFactory.incCreatedResourcesCounter();
		ManagementRegistrar.register(jmxName, this);
	}

	/**
	 * Method getXAConnection returns the XAConnection of this JmsPooledConnection object.
	 *
	 * @return the XAConnection (type XAConnection) of this JmsPooledConnection object.
	 */
	public XAConnection getXAConnection()
	{
		return xaConnection;
	}

	/**
	 * Method getPoolingConnectionFactory returns the poolingConnectionFactory of this JmsPooledConnection object.
	 *
	 * @return the poolingConnectionFactory (type PoolingConnectionFactory) of this JmsPooledConnection object.
	 */
	public PoolingConnectionFactory getPoolingConnectionFactory()
	{
		return poolingConnectionFactory;
	}

	/**
	 * Method createRecoveryXAResourceHolder ...
	 *
	 * @return RecoveryXAResourceHolder
	 *
	 * @throws JMSException
	 * 		when
	 */
	public synchronized RecoveryXAResourceHolder createRecoveryXAResourceHolder() throws JMSException
	{
		DualSessionWrapper dualSessionWrapper = new DualSessionWrapper(this, false, 0);
		dualSessionWrapper.getSession(true); // force creation of XASession to allow access to XAResource
		return new RecoveryXAResourceHolder(dualSessionWrapper);
	}

	/**
	 * Get the list of {@link bitronix.tm.resource.common.XAResourceHolder}s created by this
	 * {@link bitronix.tm.resource.common.XAStatefulHolder} that are still open.
	 * 

This method is thread-safe.

* * @return the list of {@link bitronix.tm.resource.common.XAResourceHolder}s created by this * {@link bitronix.tm.resource.common.XAStatefulHolder} that are still open. */ @Override public List getXAResourceHolders() { synchronized (sessions) { return new ArrayList<>(sessions); } } /** * Create a disposable handler used to drive a pooled instance of * {@link bitronix.tm.resource.common.XAStatefulHolder}. *

This method is thread-safe.

* * @return a resource-specific disposable connection object. * * @throws Exception * a resource-specific exception thrown when the disposable connection cannot be created. */ @Override public Object getConnectionHandle() throws Exception { if (LogDebugCheck.isDebugEnabled()) { log.finer("getting connection handle from " + this); } State oldState = getState(); setState(State.ACCESSIBLE); if (oldState == State.IN_POOL) { if (LogDebugCheck.isDebugEnabled()) { log.finer("connection " + xaConnection + " was in state IN_POOL, testing it"); } testXAConnection(); } else { if (LogDebugCheck.isDebugEnabled()) { log.finer("connection " + xaConnection + " was in state " + oldState + ", no need to test it"); } } if (LogDebugCheck.isDebugEnabled()) { log.finer("got connection handle from " + this); } return new JmsConnectionHandle(this, xaConnection); } /** * Close the physical connection that this {@link bitronix.tm.resource.common.XAStatefulHolder} represents. * * @throws JMSException * a resource-specific exception thrown when there is an error closing the physical connection. */ @Override public synchronized void close() throws JMSException { if (xaConnection != null) { poolingConnectionFactory.unregister(this); setState(State.CLOSED); try { xaConnection.close(); } finally { xaConnection = null; } } } /** * Get the date at which this object was last released to the pool. This is required to check if it is eligible * for discard when the containing pool needs to shrink. * * @return the date at which this object was last released to the pool or null if it never left the pool. */ @Override public Date getLastReleaseDate() { return lastReleaseDate; } /** * Method testXAConnection ... * * @throws JMSException * when */ private void testXAConnection() throws JMSException { if (!poolingConnectionFactory.getTestConnections()) { if (LogDebugCheck.isDebugEnabled()) { log.finer("not testing connection of " + this); } return; } if (LogDebugCheck.isDebugEnabled()) { log.finer("testing connection of " + this); } XASession xaSession = xaConnection.createXASession(); try { TemporaryQueue tq = xaSession.createTemporaryQueue(); tq.delete(); } finally { xaSession.close(); } } /** * Method release ... * * @throws JMSException * when */ protected void release() throws JMSException { if (LogDebugCheck.isDebugEnabled()) { log.finer("releasing to pool " + this); } closePendingSessions(); // requeuing try { TransactionContextHelper.requeue(this, poolingConnectionFactory); } catch (BitronixSystemException ex) { throw (JMSException) new JMSException("error requeueing " + this).initCause(ex); } if (LogDebugCheck.isDebugEnabled()) { log.finer("released to pool " + this); } } /** * Method closePendingSessions ... */ private void closePendingSessions() { synchronized (sessions) { for (DualSessionWrapper dualSessionWrapper : sessions) { if (dualSessionWrapper.getState() != State.ACCESSIBLE) { continue; } try { if (LogDebugCheck.isDebugEnabled()) { log.finer("trying to close pending session " + dualSessionWrapper); } dualSessionWrapper.close(); } catch (JMSException ex) { log.log(Level.WARNING, "error closing pending session " + dualSessionWrapper, ex); } } } } /** * Method createSession ... * * @param transacted * of type boolean * @param acknowledgeMode * of type int * * @return Session */ protected Session createSession(boolean transacted, int acknowledgeMode) { synchronized (sessions) { DualSessionWrapper sessionHandle = getNotAccessibleSession(); if (sessionHandle == null) { if (LogDebugCheck.isDebugEnabled()) { log.finer("no session handle found in NOT_ACCESSIBLE state, creating new session"); } sessionHandle = new DualSessionWrapper(this, transacted, acknowledgeMode); sessionHandle.addStateChangeEventListener(new JmsConnectionHandleStateChangeListener()); sessions.add(sessionHandle); } else { if (LogDebugCheck.isDebugEnabled()) { log.finer("found session handle in NOT_ACCESSIBLE state, recycling it: " + sessionHandle); } sessionHandle.setState(State.ACCESSIBLE); } return sessionHandle; } } /** * Method getNotAccessibleSession returns the notAccessibleSession of this JmsPooledConnection object. * * @return the notAccessibleSession (type DualSessionWrapper) of this JmsPooledConnection object. */ private DualSessionWrapper getNotAccessibleSession() { synchronized (sessions) { if (LogDebugCheck.isDebugEnabled()) { log.finer(sessions.size() + " session(s) open from " + this); } for (DualSessionWrapper sessionHandle : sessions) { if (sessionHandle.getState() == State.NOT_ACCESSIBLE) { return sessionHandle; } } return null; } } /** * Method toString ... * * @return String */ @Override public String toString() { return "a JmsPooledConnection of pool " + poolingConnectionFactory.getUniqueName() + " in state " + getState() + " with underlying connection " + xaConnection; } /* management */ /** * Method getStateDescription returns the stateDescription of this JmsPooledConnection object. * * @return the stateDescription (type String) of this JmsPooledConnection object. */ @Override public String getStateDescription() { return getState().toString(); } /** * Method getAcquisitionDate returns the acquisitionDate of this JmsPooledConnection object. * * @return the acquisitionDate (type Date) of this JmsPooledConnection object. */ @Override public Date getAcquisitionDate() { return acquisitionDate; } /** * Method getTransactionGtridsCurrentlyHoldingThis returns the transactionGtridsCurrentlyHoldingThis of this JmsPooledConnection object. * * @return the transactionGtridsCurrentlyHoldingThis (type Collection String ) of this JmsPooledConnection object. */ @Override public Collection getTransactionGtridsCurrentlyHoldingThis() { synchronized (sessions) { Set result = new HashSet<>(); for (DualSessionWrapper dsw : sessions) { result.addAll(dsw.getXAResourceHolderStateGtrids()); } return result; } } /** * Method getXAResourceHolderForXaResource ... * * @param xaResource * of type XAResource * * @return DualSessionWrapper */ public DualSessionWrapper getXAResourceHolderForXaResource(XAResource xaResource) { synchronized (sessions) { for (DualSessionWrapper xaResourceHolder : sessions) { if (xaResourceHolder.getXAResource() == xaResource) { return xaResourceHolder; } } return null; } } /** * {@link JmsPooledConnection} {@link bitronix.tm.resource.common.StateChangeListener}. * When state changes to State.CLOSED, the connection is unregistered from * {@link bitronix.tm.utils.ManagementRegistrar}. */ private final class JmsPooledConnectionStateChangeListener implements StateChangeListener { /** * Method stateChanged ... * * @param source * of type JmsPooledConnection * @param oldState * of type State * @param newState * of type State */ @Override public void stateChanged(JmsPooledConnection source, State oldState, State newState) { if (newState == State.IN_POOL) { if (LogDebugCheck.isDebugEnabled()) { log.finer("requeued JMS connection of " + poolingConnectionFactory); } lastReleaseDate = new Date(MonotonicClock.currentTimeMillis()); } if (oldState == State.IN_POOL && newState == State.ACCESSIBLE) { acquisitionDate = new Date(MonotonicClock.currentTimeMillis()); } if (newState == State.CLOSED) { ManagementRegistrar.unregister(jmxName); } } /** * Method stateChanging ... * * @param source * of type JmsPooledConnection * @param currentState * of type State * @param futureState * of type State */ @Override public void stateChanging(JmsPooledConnection source, State currentState, State futureState) { //Nothing needed } } /** * {@link JmsConnectionHandle} {@link bitronix.tm.resource.common.StateChangeListener}. * When state changes to State.CLOSED, the session is removed from the list of opened sessions. */ private final class JmsConnectionHandleStateChangeListener implements StateChangeListener { /** * Method stateChanged ... * * @param source * of type DualSessionWrapper * @param oldState * of type State * @param newState * of type State */ @Override public void stateChanged(DualSessionWrapper source, State oldState, State newState) { if (newState == State.CLOSED) { synchronized (sessions) { sessions.remove(source); if (LogDebugCheck.isDebugEnabled()) { log.finer("DualSessionWrapper has been closed, " + sessions.size() + " session(s) left open in pooled connection"); } } } } /** * Method stateChanging ... * * @param source * of type DualSessionWrapper * @param currentState * of type State * @param futureState * of type State */ @Override public void stateChanging(DualSessionWrapper source, State currentState, State futureState) { //Nothing needed } } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy