package org.jboss.resource.connectionmanager;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.security.AccessController;
import java.security.PrivilegedAction;
import javax.management.Notification;
import javax.management.NotificationFilter;
import javax.management.NotificationListener;
import javax.management.ObjectName;
import javax.resource.ResourceException;
import javax.resource.spi.ConnectionRequestInfo;
import javax.resource.spi.ManagedConnectionFactory;
import javax.security.auth.Subject;
import javax.transaction.Transaction;
import javax.transaction.TransactionManager;
import org.jboss.deployment.DeploymentException;
import org.jboss.logging.Logger;
import org.jboss.mx.util.JMXExceptionDecoder;
import org.jboss.system.ServiceMBeanSupport;
import org.jboss.tm.TransactionLocal;
public class JBossManagedConnectionPool
extends ServiceMBeanSupport
implements JBossManagedConnectionPoolMBean,
NotificationListener
{
private ObjectName managedConnectionFactoryName;
private String criteria;
private ManagedConnectionPool poolingStrategy;
private final InternalManagedConnectionPool.PoolParams poolParams = new InternalManagedConnectionPool.PoolParams();
private boolean noTxSeparatePools;
public JBossManagedConnectionPool()
{
}
public ManagedConnectionPool getManagedConnectionPool()
{
return poolingStrategy;
}
public ObjectName getManagedConnectionFactoryName()
{
return managedConnectionFactoryName;
}
public void setManagedConnectionFactoryName(ObjectName newManagedConnectionFactoryName)
{
this.managedConnectionFactoryName = newManagedConnectionFactoryName;
}
public long getAvailableConnectionCount()
{
return (poolingStrategy == null) ? 0 : poolingStrategy.getAvailableConnectionCount();
}
public long getMaxConnectionsInUseCount()
{
return (poolingStrategy == null) ? 0 : poolingStrategy.getMaxConnectionsInUseCount();
}
public long getInUseConnectionCount ()
{
return (poolingStrategy == null) ? 0 : poolingStrategy.getInUseConnectionCount();
}
public int getMinSize()
{
return poolParams.minSize;
}
public void setMinSize(int newMinSize)
{
poolParams.minSize = newMinSize;
}
public int getMaxSize()
{
return poolParams.maxSize;
}
public void setMaxSize(int newMaxSize)
{
poolParams.maxSize = newMaxSize;
}
public int getBlockingTimeoutMillis()
{
return poolParams.blockingTimeout;
}
public void setBlockingTimeoutMillis(int newBlockingTimeout)
{
poolParams.blockingTimeout = newBlockingTimeout;
}
public long getIdleTimeoutMinutes()
{
return poolParams.idleTimeout / (1000 * 60);
}
public void setIdleTimeoutMinutes(long newIdleTimeoutMinutes)
{
poolParams.idleTimeout = newIdleTimeoutMinutes * 1000 * 60;
}
public long getIdleTimeout()
{
return poolParams.idleTimeout;
}
public void setIdleTimeout(long newIdleTimeout)
{
poolParams.idleTimeout = newIdleTimeout;
}
public String getCriteria()
{
return criteria;
}
public void setCriteria(String newCriteria)
{
this.criteria = newCriteria;
}
public boolean getNoTxSeparatePools()
{
return noTxSeparatePools;
}
public void setNoTxSeparatePools(boolean value)
{
this.noTxSeparatePools = value;
}
public void flush()
{
if (poolingStrategy == null)
throw new IllegalStateException("The connection pool is not started");
poolingStrategy.flush();
}
public int getConnectionCount()
{
return (poolingStrategy == null)? 0: poolingStrategy.getConnectionCount();
}
public int getConnectionCreatedCount()
{
return (poolingStrategy == null)? 0: poolingStrategy.getConnectionCreatedCount();
}
public int getConnectionDestroyedCount()
{
return (poolingStrategy == null)? 0: poolingStrategy.getConnectionDestroyedCount();
}
public String getName()
{
return "JBossManagedConnectionPool";
}
protected void startService() throws Exception
{
ManagedConnectionFactory mcf = null;
if (managedConnectionFactoryName == null)
throw new DeploymentException("ManagedConnectionFactory not set!");
try
{
mcf = (ManagedConnectionFactory)server.getAttribute(managedConnectionFactoryName, "McfInstance");
}
catch (Exception e)
{
JMXExceptionDecoder.rethrow(e);
} getServer().addNotificationListener
(
managedConnectionFactoryName,
this,
new NotificationFilter()
{
public boolean isNotificationEnabled(Notification n)
{
return RARDeployment.MCF_ATTRIBUTE_CHANGED_NOTIFICATION.equals(n.getType())
&& managedConnectionFactoryName.equals(n.getSource());
}
},
null
);
if ("ByContainerAndApplication".equals(criteria))
poolingStrategy = new PoolBySubjectAndCri(mcf, poolParams, noTxSeparatePools, log);
else if ("ByContainer".equals(criteria))
poolingStrategy = new PoolBySubject(mcf, poolParams, noTxSeparatePools, log);
else if ("ByApplication".equals(criteria))
poolingStrategy = new PoolByCri(mcf, poolParams, noTxSeparatePools, log);
else if ("ByNothing".equals(criteria))
poolingStrategy = new OnePool(mcf, poolParams, noTxSeparatePools, log);
else
throw new DeploymentException("Unknown pooling criteria: " + criteria);
}
protected void stopService() throws Exception
{
if (poolingStrategy != null)
poolingStrategy.shutdown();
getServer().removeNotificationListener(managedConnectionFactoryName, this);
poolingStrategy = null;
}
public void handleNotification(Notification notification,
Object handback)
{
flush();
}
public abstract static class BasePool implements ManagedConnectionPool
{
private final Map pools = new HashMap();
private final ManagedConnectionFactory mcf;
private ConnectionListenerFactory clf;
private final InternalManagedConnectionPool.PoolParams poolParams;
private boolean noTxSeparatePools;
private final Map trackByTxPools = new HashMap();
private final Logger log;
private boolean traceEnabled = false;
public BasePool(final ManagedConnectionFactory mcf, final InternalManagedConnectionPool.PoolParams poolParams,
final boolean noTxSeparatePools, final Logger log)
{
this.mcf = mcf;
this.poolParams = poolParams;
this.noTxSeparatePools = noTxSeparatePools;
this.log = log;
this.traceEnabled = log.isTraceEnabled();
}
protected abstract Object getKey(Subject subject, ConnectionRequestInfo cri, boolean separateNoTx) throws ResourceException;
public ManagedConnectionFactory getManagedConnectionFactory()
{
return mcf;
}
public void setConnectionListenerFactory(ConnectionListenerFactory clf)
{
this.clf = clf;
}
public int getInUseConnectionCount()
{
int count = 0;
synchronized (pools)
{
for (Iterator i = pools.values().iterator(); i.hasNext(); )
count += ((InternalManagedConnectionPool)i.next()).getConnectionInUseCount();
}
return count;
}
public ConnectionListener getConnection(Transaction transaction, Subject subject, ConnectionRequestInfo cri)
throws ResourceException
{
boolean separateNoTx = false;
if (noTxSeparatePools)
separateNoTx = clf.isTransactional();
Object key = getKey(subject, cri, separateNoTx);
InternalManagedConnectionPool mcp = getPool(key, subject, cri);
TransactionLocal trackByTx = null;
if (transaction != null)
trackByTx = (TransactionLocal) trackByTxPools.get(key);
if (trackByTx != null)
{
ConnectionListener cl = (ConnectionListener) trackByTx.get(transaction);
if (cl != null)
{
if (traceEnabled)
dump("Getting connection tracked by transaction " + cl);
return cl;
}
}
ConnectionListener cl = mcp.getConnection(subject, cri);
if (transaction != null && trackByTx != null)
{
cl.setTrackByTx(true);
trackByTx.set(cl);
}
if (traceEnabled)
dump("Getting connection from pool " + cl);
return cl;
}
public void returnConnection(ConnectionListener cl, boolean kill) throws ResourceException
{
cl.setTrackByTx(false);
InternalManagedConnectionPool mcp = (InternalManagedConnectionPool) cl.getContext();
mcp.returnConnection(cl, kill);
if (traceEnabled)
dump("Returning connection to pool " + cl);
}
public int getConnectionCount()
{
int count = 0;
synchronized (pools)
{
for (Iterator i = pools.values().iterator(); i.hasNext(); )
count += ((InternalManagedConnectionPool)i.next()).getConnectionCount();
}
return count;
}
public int getConnectionCreatedCount()
{
int count = 0;
synchronized (pools)
{
for (Iterator i = pools.values().iterator(); i.hasNext(); )
count += ((InternalManagedConnectionPool)i.next()).getConnectionCreatedCount();
}
return count;
}
public int getConnectionDestroyedCount()
{
int count = 0;
synchronized (pools)
{
for (Iterator i = pools.values().iterator(); i.hasNext(); )
count += ((InternalManagedConnectionPool)i.next()).getConnectionDestroyedCount();
}
return count;
}
public long getAvailableConnectionCount()
{
long count = 0;
synchronized (pools)
{
if (pools.size() == 0)
return poolParams.maxSize;
for (Iterator i = pools.values().iterator(); i.hasNext(); )
count += ((InternalManagedConnectionPool)i.next()).getAvailableConnections();
}
return count;
}
public int getMaxConnectionsInUseCount()
{
int count = 0;
synchronized (pools)
{
for (Iterator i = pools.values().iterator(); i.hasNext(); )
count += ((InternalManagedConnectionPool)i.next()).getMaxConnectionsInUseCount();
}
return count;
}
public void shutdown()
{
synchronized (pools)
{
for (Iterator i = pools.values().iterator(); i.hasNext(); )
((InternalManagedConnectionPool)i.next()).shutdown();
pools.clear();
}
}
public void flush()
{
synchronized (pools)
{
for (Iterator i = pools.values().iterator(); i.hasNext(); )
((InternalManagedConnectionPool)i.next()).shutdown();
pools.clear();
}
}
protected TransactionManager getTransactionManager()
{
if (clf != null)
return clf.getTransactionManagerInstance();
else
return null;
}
private InternalManagedConnectionPool getPool(Object key, Subject subject, ConnectionRequestInfo cri)
throws ResourceException
{
InternalManagedConnectionPool mcp = null;
synchronized (pools)
{
mcp = (InternalManagedConnectionPool) pools.get(key);
if (mcp == null)
{
mcp = new InternalManagedConnectionPool(mcf, clf, subject, cri, poolParams, log);
pools.put(key, mcp);
TransactionManager tm = getTransactionManager();
if (tm != null)
trackByTxPools.put(key, new TransactionLocal(tm));
}
}
return mcp;
}
private void dump(String info)
{
if (traceEnabled)
{
StringBuffer toLog = new StringBuffer(100);
toLog.append(info).append(" [InUse/Available/Max]: [");
toLog.append(this.getInUseConnectionCount()).append("/");
toLog.append(this.getAvailableConnectionCount()).append("/");
toLog.append(this.poolParams.maxSize);
toLog.append("]");;
log.trace(toLog);
}
}
}
public static class PoolBySubjectAndCri
extends BasePool
{
public PoolBySubjectAndCri(final ManagedConnectionFactory mcf,
final InternalManagedConnectionPool.PoolParams poolParams,
final boolean noTxSeparatePools,
final Logger log)
{
super(mcf, poolParams, noTxSeparatePools, log);
}
protected Object getKey(final Subject subject, final ConnectionRequestInfo cri, final boolean separateNoTx) throws ResourceException
{
return new SubjectCriKey(subject, cri, separateNoTx);
}
}
private static class SubjectCriKey
{
private static final Subject NOSUBJECT = new Subject();
private static final Object NOCRI = new Object();
private final Subject subject;
private final Object cri;
private boolean separateNoTx;
SubjectCriKey(Subject subject, ConnectionRequestInfo cri, boolean separateNoTx)
{
this.subject = (subject == null)? NOSUBJECT:subject;
this.cri = (cri == null)? NOCRI:cri;
this.separateNoTx = separateNoTx;
}
public int hashCode()
{
return SubjectActions.hashCode(subject) ^ cri.hashCode();
}
public boolean equals(Object obj)
{
if (this == obj)
return true;
if (obj == null || (obj instanceof SubjectCriKey) == false)
return false;
SubjectCriKey other = (SubjectCriKey) obj;
return SubjectActions.equals(subject, other.subject)
&& cri.equals(other.cri)
&& separateNoTx == other.separateNoTx;
}
}
public static class PoolBySubject
extends BasePool
{
public PoolBySubject(final ManagedConnectionFactory mcf,
final InternalManagedConnectionPool.PoolParams poolParams,
final boolean noTxSeparatePools,
final Logger log)
{
super(mcf, poolParams, noTxSeparatePools, log);
}
protected Object getKey(final Subject subject, final ConnectionRequestInfo cri, boolean separateNoTx)
{
return new SubjectKey(subject, separateNoTx);
}
}
private static class SubjectKey
{
private static final Subject NOSUBJECT = new Subject();
private final Subject subject;
private boolean separateNoTx;
SubjectKey(Subject subject, boolean separateNoTx)
{
this.subject = (subject == null)? NOSUBJECT:subject;
this.separateNoTx = separateNoTx;
}
public int hashCode()
{
return SubjectActions.hashCode(subject);
}
public boolean equals(Object obj)
{
if (this == obj)
return true;
if (obj == null || (obj instanceof SubjectKey) == false)
return false;
SubjectKey other = (SubjectKey) obj;
return SubjectActions.equals(subject, other.subject)
&& separateNoTx == other.separateNoTx;
}
}
public static class PoolByCri
extends BasePool
{
public PoolByCri(final ManagedConnectionFactory mcf,
final InternalManagedConnectionPool.PoolParams poolParams,
final boolean noTxSeparatePools,
final Logger log)
{
super(mcf, poolParams, noTxSeparatePools, log);
}
protected Object getKey(final Subject subject, final ConnectionRequestInfo cri, boolean separateNoTx)
{
return new CriKey(cri, separateNoTx);
}
}
private static class CriKey
{
private static final Object NOCRI = new Object();
private final Object cri;
private boolean separateNoTx;
CriKey(ConnectionRequestInfo cri, boolean separateNoTx)
{
this.cri = (cri == null)? NOCRI:cri;
this.separateNoTx = separateNoTx;
}
public int hashCode()
{
return cri.hashCode();
}
public boolean equals(Object obj)
{
if (this == obj)
return true;
if (obj == null || (obj instanceof CriKey) == false)
return false;
CriKey other = (CriKey) obj;
return cri.equals(other.cri) && separateNoTx == other.separateNoTx;
}
}
public static class OnePool
extends BasePool
{
public OnePool(final ManagedConnectionFactory mcf,
final InternalManagedConnectionPool.PoolParams poolParams,
final boolean noTxSeparatePools,
final Logger log)
{
super(mcf, poolParams, noTxSeparatePools, log);
}
protected Object getKey(final Subject subject, final ConnectionRequestInfo cri, boolean separateNoTx)
{
if (separateNoTx)
return Boolean.TRUE;
else
return Boolean.FALSE;
}
}
private static class SubjectActions implements PrivilegedAction
{
Subject subject;
Subject other;
SubjectActions(Subject subject, Subject other)
{
this.subject = subject;
this.other = other;
}
public Object run()
{
Object value = null;
if( other == null )
value = new Integer(subject.hashCode());
else
value = new Boolean(subject.equals(other));
return value;
}
static int hashCode(Subject subject)
{
SubjectActions action = new SubjectActions(subject, null);
Integer hash = (Integer) AccessController.doPrivileged(action);
return hash.intValue();
}
static boolean equals(Subject subject, Subject other)
{
SubjectActions action = new SubjectActions(subject, other);
Boolean equals = (Boolean) AccessController.doPrivileged(action);
return equals.booleanValue();
}
}
}