AbstractPool.java
/*
* IronJacamar, a Java EE Connector Architecture implementation
* Copyright 2015, Red Hat Inc, and individual contributors
* as indicated by the @author tags. See the copyright.txt file 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 Eclipse Public License 1.0 as
* published by the Free Software Foundation.
*
* 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 Eclipse
* Public License for more details.
*
* You should have received a copy of the Eclipse 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.ironjacamar.core.connectionmanager.pool;
import org.ironjacamar.common.api.metadata.common.FlushStrategy;
import org.ironjacamar.core.api.connectionmanager.pool.FlushMode;
import org.ironjacamar.core.api.connectionmanager.pool.PoolConfiguration;
import org.ironjacamar.core.api.connectionmanager.pool.PoolStatistics;
import org.ironjacamar.core.connectionmanager.ConnectionManager;
import org.ironjacamar.core.connectionmanager.Credential;
import org.ironjacamar.core.connectionmanager.TransactionalConnectionManager;
import org.ironjacamar.core.connectionmanager.listener.ConnectionListener;
import org.ironjacamar.core.connectionmanager.pool.capacity.DefaultCapacity;
import org.ironjacamar.core.connectionmanager.pool.capacity.TimedOutDecrementer;
import org.ironjacamar.core.spi.transaction.ConnectableResource;
import org.ironjacamar.core.spi.transaction.TxUtils;
import org.ironjacamar.core.spi.transaction.local.LocalXAResource;
import org.ironjacamar.core.tracer.Tracer;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import javax.resource.ResourceException;
import javax.resource.spi.ConnectionRequestInfo;
import javax.resource.spi.ManagedConnection;
import javax.resource.spi.TransactionSupport.TransactionSupportLevel;
import javax.security.auth.Subject;
import javax.transaction.Transaction;
import javax.transaction.xa.XAResource;
import org.jboss.logging.Logger;
import static org.ironjacamar.core.connectionmanager.listener.ConnectionListener.DESTROY;
/**
* The base class for all pool implementations
*
* @author <a href="jesper.pedersen@ironjacamar.org">Jesper Pedersen</a>
*/
public abstract class AbstractPool implements Pool
{
/** The logger */
private static Logger log = Logger.getLogger(AbstractPool.class);
/**
* The connection manager
*/
protected ConnectionManager cm;
/**
* The pool configuration
*/
protected PoolConfiguration poolConfiguration;
/**
* The pools
*/
protected ConcurrentHashMap<Credential, ManagedConnectionPool> pools;
/**
* The transaction map
*/
protected ConcurrentHashMap<Object, Map<ManagedConnectionPool, ConnectionListener>> transactionMap;
/**
* The semaphore
*/
protected Semaphore semaphore;
private Credential prefillCredential;
private FlushStrategy flushStrategy;
/** The capacity */
private Capacity capacity;
/**
* The statistics
*/
protected PoolStatisticsImpl statistics;
/**
* The janitor
*/
protected Janitor janitor;
/**
* Constructor
*
* @param cm The connection manager
* @param pc The pool configuration
*/
public AbstractPool(ConnectionManager cm, PoolConfiguration pc)
{
this.cm = cm;
this.poolConfiguration = pc;
this.pools = new ConcurrentHashMap<Credential, ManagedConnectionPool>();
this.transactionMap = new ConcurrentHashMap<Object, Map<ManagedConnectionPool, ConnectionListener>>();
this.statistics = new PoolStatisticsImpl(poolConfiguration.getMaxSize());
this.semaphore = new Semaphore(poolConfiguration.getMaxSize(), statistics);
this.flushStrategy = poolConfiguration.getFlushStrategy();
this.capacity = null;
this.janitor = null;
}
/**
* {@inheritDoc}
*/
public ConnectionManager getConnectionManager()
{
return cm;
}
/**
* {@inheritDoc}
*/
public PoolConfiguration getConfiguration()
{
return poolConfiguration;
}
/**
* {@inheritDoc}
*/
public Semaphore getPermits()
{
return semaphore;
}
/**
* {@inheritDoc}
*/
public PoolStatistics getStatistics()
{
return statistics;
}
/**
* {@inheritDoc}
*/
public PoolStatisticsImpl getInternalStatistics()
{
return statistics;
}
/**
* {@inheritDoc}
*/
public Janitor getJanitor()
{
return janitor;
}
/**
* {@inheritDoc}
*/
public void setJanitor(Janitor v)
{
janitor = v;
}
/**
* {@inheritDoc}
*/
public ConnectionListener getConnectionListener(Credential credential) throws ResourceException
{
log.tracef("getConnectionListener(%s)", credential);
ConnectionListener cl = null;
ManagedConnectionPool mcp = getManagedConnectionPool(credential);
if (isShutdown())
throw new ResourceException();
if (cm.getTransactionSupport() == TransactionSupportLevel.LocalTransaction
|| cm.getTransactionSupport() == TransactionSupportLevel.XATransaction)
{
try
{
TransactionalConnectionManager txCM = (TransactionalConnectionManager) cm;
Transaction tx = txCM.getTransactionIntegration().getTransactionManager().getTransaction();
if (TxUtils.isUncommitted(tx))
{
Object id = txCM.getTransactionIntegration().getTransactionSynchronizationRegistry().getTransactionKey();
Map<ManagedConnectionPool, ConnectionListener> currentMap = transactionMap.get(id);
if (currentMap == null)
{
Map<ManagedConnectionPool, ConnectionListener> map = new HashMap<>();
currentMap = transactionMap.putIfAbsent(id, map);
if (currentMap == null)
{
currentMap = map;
}
}
cl = currentMap.get(mcp);
if (cl == null)
{
if (TxUtils.isActive(tx))
{
cl = mcp.getConnectionListener();
currentMap.put(mcp, cl);
txCM.getTransactionIntegration().getTransactionSynchronizationRegistry().
registerInterposedSynchronization(new TransactionMapCleanup(id, transactionMap));
}
else
{
throw new ResourceException();
}
}
}
}
catch (ResourceException re)
{
throw re;
}
catch (Exception e)
{
throw new ResourceException(e);
}
}
if (cl == null)
cl = mcp.getConnectionListener();
return cl;
}
/**
* Get from existing pools or create mcp w/ specified credential
* It's used during prefill operation
*
* @param credential credential used to match
* @return
*/
private ManagedConnectionPool getManagedConnectionPool(Credential credential)
{
ManagedConnectionPool mcp = pools.get(credential);
if (mcp == null)
{
synchronized (this)
{
mcp = pools.get(credential);
if (mcp == null)
{
ManagedConnectionPool newMcp = createManagedConnectionPool(credential);
mcp = pools.putIfAbsent(credential, newMcp);
if (mcp == null)
{
mcp = newMcp;
if (Tracer.isEnabled())
Tracer.createManagedConnectionPool(poolConfiguration.getId(), mcp);
}
else
{
newMcp.shutdown();
}
}
}
}
return mcp;
}
/**
* {@inheritDoc}
*/
public void returnConnectionListener(ConnectionListener cl, boolean kill) throws ResourceException
{
log.tracef("returnConnectionListener(%s, %s)", cl, kill);
ManagedConnectionPool mcp = pools.get(cl.getCredential());
if (!kill)
kill = cl.getState() == DESTROY;
if (Tracer.isEnabled())
Tracer.returnConnectionListener(poolConfiguration.getId(), cl.getManagedConnectionPool(),
cl, kill, false,
Tracer.isRecordCallstacks() ? new Throwable("CALLSTACK") : null);
mcp.returnConnectionListener(cl, kill);
}
/**
* {@inheritDoc}
*/
public boolean isFull()
{
return semaphore.availablePermits() == 0;
}
/**
* {@inheritDoc}
*/
public synchronized void shutdown()
{
for (ManagedConnectionPool mcp : pools.values())
{
mcp.shutdown();
if (Tracer.isEnabled())
Tracer.destroyManagedConnectionPool(poolConfiguration.getId(), mcp);
}
pools.clear();
}
/**
* {@inheritDoc}
*/
@Override
public boolean isShutdown()
{
return cm.isShutdown();
}
/**
* Get a LocalXAResource instance
*
* @param mc The ManagedConnection
* @return The instance
* @throws ResourceException Thrown if an error occurs
*/
protected LocalXAResource getLocalXAResource(ManagedConnection mc) throws ResourceException
{
TransactionalConnectionManager txCM = (TransactionalConnectionManager) cm;
LocalXAResource xaResource = null;
String eisProductName = null;
String eisProductVersion = null;
String jndiName = cm.getConnectionManagerConfiguration().getJndiName();
try
{
if (mc.getMetaData() != null)
{
eisProductName = mc.getMetaData().getEISProductName();
eisProductVersion = mc.getMetaData().getEISProductVersion();
}
}
catch (ResourceException re)
{
// Ignore
}
if (eisProductName == null)
eisProductName = jndiName;
if (eisProductVersion == null)
eisProductVersion = jndiName;
if (cm.getConnectionManagerConfiguration().isConnectable())
{
if (mc instanceof org.ironjacamar.core.spi.transaction.ConnectableResource)
{
ConnectableResource cr = (ConnectableResource) mc;
xaResource = txCM.getTransactionIntegration()
.createConnectableLocalXAResource(cm, eisProductName, eisProductVersion, jndiName, cr, statistics);
}
else if (txCM.getTransactionIntegration().isConnectableResource(mc))
{
xaResource = txCM.getTransactionIntegration()
.createConnectableLocalXAResource(cm, eisProductName, eisProductVersion, jndiName, mc, statistics);
}
}
if (xaResource == null)
xaResource = txCM.getTransactionIntegration()
.createLocalXAResource(cm, eisProductName, eisProductVersion, jndiName, statistics);
return xaResource;
}
/**
* Get a XAResource instance
*
* @param mc The ManagedConnection
* @return The instance
* @throws ResourceException Thrown if an error occurs
*/
protected XAResource getXAResource(ManagedConnection mc) throws ResourceException
{
TransactionalConnectionManager txCM = (TransactionalConnectionManager) cm;
XAResource xaResource = null;
if (cm.getConnectionManagerConfiguration().isWrapXAResource())
{
String eisProductName = null;
String eisProductVersion = null;
String jndiName = cm.getConnectionManagerConfiguration().getJndiName();
boolean padXid = cm.getConnectionManagerConfiguration().isPadXid();
Boolean isSameRMOverride = cm.getConnectionManagerConfiguration().isIsSameRMOverride();
try
{
if (mc.getMetaData() != null)
{
eisProductName = mc.getMetaData().getEISProductName();
eisProductVersion = mc.getMetaData().getEISProductVersion();
}
}
catch (ResourceException re)
{
// Ignore
}
if (eisProductName == null)
eisProductName = jndiName;
if (eisProductVersion == null)
eisProductVersion = jndiName;
if (cm.getConnectionManagerConfiguration().isConnectable())
{
if (mc instanceof org.ironjacamar.core.spi.transaction.ConnectableResource)
{
ConnectableResource cr = (ConnectableResource) mc;
xaResource = txCM.getTransactionIntegration()
.createConnectableXAResourceWrapper(mc.getXAResource(), padXid, isSameRMOverride, eisProductName,
eisProductVersion, jndiName, cr, statistics);
}
else if (txCM.getTransactionIntegration().isConnectableResource(mc))
{
xaResource = txCM.getTransactionIntegration()
.createConnectableXAResourceWrapper(mc.getXAResource(), padXid, isSameRMOverride, eisProductName,
eisProductVersion, jndiName, mc, statistics);
}
}
if (xaResource == null)
{
XAResource xar = mc.getXAResource();
if (!(xar instanceof org.ironjacamar.core.spi.transaction.xa.XAResourceWrapper))
{
boolean firstResource = txCM.getTransactionIntegration().isFirstResource(mc);
xaResource = txCM.getTransactionIntegration()
.createXAResourceWrapper(xar, padXid, isSameRMOverride, eisProductName, eisProductVersion,
jndiName, firstResource, statistics);
}
else
{
xaResource = xar;
}
}
}
else
{
xaResource = mc.getXAResource();
}
return xaResource;
}
/**
* Prefill the connection pool
*/
@Override
public void prefill()
{
if (isShutdown())
return;
if (poolConfiguration.isPrefill())
{
ManagedConnectionPool mcp = pools.get(getPrefillCredential());
if (mcp == null)
{
// Trigger the initial-pool-size prefill by creating the ManagedConnectionPool
getManagedConnectionPool(getPrefillCredential());
}
else
{
// Standard prefill request
mcp.prefill();
}
}
}
/**
* Get prefill credential
*
* @return credential used to prefill
*/
@Override
public Credential getPrefillCredential()
{
if (this.prefillCredential == null)
{
if (cm.getSubjectFactory() == null || cm.getConnectionManagerConfiguration().getSecurityDomain() == null)
{
prefillCredential = new Credential(null, null);
}
else
{
prefillCredential =
new Credential(SecurityActions.createSubject(cm.getSubjectFactory(),
cm.getConnectionManagerConfiguration().getSecurityDomain(),
cm.getManagedConnectionFactory()),
null);
}
}
return this.prefillCredential;
}
/**
* {@inheritDoc}
*/
public void emptyManagedConnectionPool(ManagedConnectionPool mcp)
{
if (pools.values().remove(mcp))
{
mcp.shutdown();
if (Tracer.isEnabled())
Tracer.destroyManagedConnectionPool(poolConfiguration.getId(), mcp);
}
}
/**
* Get the flush strategy
* @return The value
*/
@Override
public FlushStrategy getFlushStrategy()
{
return flushStrategy;
}
/**
* {@inheritDoc}
*/
@Override
public void flush()
{
flush(FlushMode.IDLE);
}
/**
* {@inheritDoc}
*/
@Override
public synchronized void flush(FlushMode mode)
{
if (isShutdown())
return;
for (Credential credential : pools.keySet())
{
ManagedConnectionPool mcp = pools.get(credential);
if (mcp != null)
{
mcp.flush(mode);
if (mcp.isEmpty() && !poolConfiguration.isPrefill())
{
mcp.shutdown();
pools.remove(credential);
if (Tracer.isEnabled())
Tracer.destroyManagedConnectionPool(poolConfiguration.getId(), mcp);
}
}
}
}
/**
* {@inheritDoc}
*/
public boolean testConnection()
{
return internalTestConnection(getPrefillCredential());
}
/**
* {@inheritDoc}
*/
public boolean testConnection(ConnectionRequestInfo cri, Subject subject)
{
return internalTestConnection(new Credential(subject, cri));
}
/**
* Test if a connection can be obtained
* @param credential The credential
* @return True if possible; otherwise false
*/
protected boolean internalTestConnection(Credential credential)
{
boolean result = false;
boolean kill = false;
ConnectionListener cl = null;
if (isShutdown())
return false;
if (isFull())
return false;
try
{
ManagedConnectionPool mcp = getManagedConnectionPool(credential);
cl = mcp.getConnectionListener();
result = true;
}
catch (Throwable t)
{
kill = true;
}
finally
{
if (cl != null)
{
try
{
returnConnectionListener(cl, kill);
}
catch (ResourceException ire)
{
// Ignore
}
}
}
return result;
}
/**
* {@inheritDoc}
*/
public void enlist(ManagedConnection mc) throws ResourceException
{
if (cm.getTransactionSupport() == TransactionSupportLevel.NoTransaction)
return;
ConnectionListener cl = findConnectionListener(mc, null);
if (cl != null)
{
if (cl.isEnlisted())
throw new ResourceException();
try
{
TransactionalConnectionManager txCM = (TransactionalConnectionManager) cm;
Transaction tx = txCM.getTransactionIntegration().getTransactionManager().getTransaction();
if (TxUtils.isUncommitted(tx))
{
Object id = txCM.getTransactionIntegration().getTransactionSynchronizationRegistry().getTransactionKey();
Map<ManagedConnectionPool, ConnectionListener> currentMap = transactionMap.get(id);
if (currentMap == null)
{
Map<ManagedConnectionPool, ConnectionListener> map = new HashMap<>();
currentMap = transactionMap.putIfAbsent(id, map);
if (currentMap == null)
{
currentMap = map;
}
}
ConnectionListener existing = currentMap.get(cl.getManagedConnectionPool());
if (existing == null)
{
if (TxUtils.isActive(tx))
{
cl.enlist();
currentMap.put(cl.getManagedConnectionPool(), cl);
txCM.getTransactionIntegration().getTransactionSynchronizationRegistry().
registerInterposedSynchronization(new TransactionMapCleanup(id, transactionMap));
}
else
{
throw new ResourceException();
}
}
else
{
log.tracef("Already a connection listener in the pool tracked by transaction=%s (existing=%s)",
id, existing);
if (existing.equals(cl))
{
if (TxUtils.isActive(tx))
{
cl.enlist();
}
else
{
throw new ResourceException();
}
}
else
{
throw new ResourceException();
}
}
}
else
{
throw new ResourceException();
}
}
catch (ResourceException re)
{
throw re;
}
catch (Throwable t)
{
throw new ResourceException(t);
}
}
else
{
throw new ResourceException();
}
}
/**
* {@inheritDoc}
*/
public void delist(ConnectionListener cl) throws ResourceException
{
if (cm.getTransactionSupport() == TransactionSupportLevel.NoTransaction)
return;
if (cl != null)
{
try
{
TransactionalConnectionManager txCM = (TransactionalConnectionManager) cm;
Transaction tx = txCM.getTransactionIntegration().getTransactionManager().getTransaction();
if (TxUtils.isUncommitted(tx))
{
try
{
cl.delist();
}
finally
{
Object id = txCM.getTransactionIntegration()
.getTransactionSynchronizationRegistry().getTransactionKey();
Map<ManagedConnectionPool, ConnectionListener> currentMap = transactionMap.get(id);
if (currentMap != null)
{
ConnectionListener registered = currentMap.remove(cl.getManagedConnectionPool());
transactionMap.put(id, currentMap);
}
}
}
}
catch (ResourceException re)
{
throw re;
}
catch (Exception e)
{
throw new ResourceException(e);
}
}
else
{
throw new ResourceException();
}
}
/**
* {@inheritDoc}
*/
public ConnectionListener findConnectionListener(ManagedConnection mc, Object c)
{
for (ManagedConnectionPool mcp : pools.values())
{
ConnectionListener cl = mcp.findConnectionListener(mc, c);
if (cl != null)
return cl;
}
return null;
}
/**
* {@inheritDoc}
*/
public ConnectionListener removeConnectionListener(Credential credential)
{
if (credential == null)
{
// Any free
for (ManagedConnectionPool mcp : pools.values())
{
ConnectionListener cl = mcp.removeConnectionListener(true);
if (cl != null)
return cl;
}
}
else
{
ManagedConnectionPool mcp = pools.get(credential);
if (mcp != null)
return mcp.removeConnectionListener(false);
}
return null;
}
/**
* {@inheritDoc}
*/
public ConnectionListener getActiveConnectionListener(Credential credential)
{
if (cm.getTransactionSupport() == TransactionSupportLevel.NoTransaction)
return null;
try
{
TransactionalConnectionManager txCM = (TransactionalConnectionManager) cm;
Transaction tx = txCM.getTransactionIntegration().getTransactionManager().getTransaction();
if (TxUtils.isUncommitted(tx))
{
Object id = txCM.getTransactionIntegration().getTransactionSynchronizationRegistry().getTransactionKey();
Map<ManagedConnectionPool, ConnectionListener> currentMap = transactionMap.get(id);
ManagedConnectionPool key = pools.get(credential);
return currentMap.get(key);
}
}
catch (Exception e)
{
log.tracef(e, "getActiveConnectionListener(%s)", credential);
}
return null;
}
/**
* {@inheritDoc}
*/
public Capacity getCapacity()
{
if (capacity == null)
return DefaultCapacity.INSTANCE;
return capacity;
}
/**
* {@inheritDoc}
*/
public void setCapacity(Capacity c)
{
capacity = c;
}
/**
* {@inheritDoc}
*/
public boolean isFIFO()
{
if (capacity == null || capacity.getDecrementer() == null ||
TimedOutDecrementer.class.getName().equals(capacity.getDecrementer().getClass().getName()))
return false;
return true;
}
}