package com.icentris.sql;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import java.sql.*;
import com.icentris.util.CodeTimer;
import com.icentris.util.CodeTimerSegment;
import com.icentris.util.Caller;
import java.util.Timer;
import java.util.TimerTask;
import java.util.*;
import com.icentris.jmx.JMX;
import javax.management.MBeanNotificationInfo;
import javax.management.NotificationBroadcasterSupport;
import javax.management.Notification;
public class ConnectionPool
extends NotificationBroadcasterSupport
implements ConnectionPoolMBean
{
/**
* Logger for this class
*/
private static final Log logger = LogFactory.getLog(ConnectionPool.class);
//--------------------------------------------------------------
// JMX constants
//--------------------------------------------------------------
public static final String NOTIF_CREATECONNECTION = "ConnectionPool.createConnection";
public static final String NOTIF_DESTROYCONNECTION = "ConnectionPool.destroyConnection";
public static final String NOTIF_OPENCONNECTION = "ConnectionPool.openConnection";
public static final String NOTIF_PRUNEBEGIN = "ConnectionPool.pruneBegin";
public static final String NOTIF_PRUNECONNECTION = "ConnectionPool.pruneConnection";
public static final String NOTIF_PRUNEFINISH = "ConnectionPool.pruneFinish";
long notificationSequence = 1;
//--------------------------------------------------------------
// JMX method
//--------------------------------------------------------------
public MBeanNotificationInfo[] getNotificationInfo() {
return new MBeanNotificationInfo[] {
new MBeanNotificationInfo( new String[] {NOTIF_CREATECONNECTION}, "createConnection",
"Triggered when a new connection is created and added to the pool"
),
new MBeanNotificationInfo( new String[] {NOTIF_DESTROYCONNECTION}, "destroyConnection",
"Triggered when a connection is automatically removed from the pool because of strictTimeout, closeOnError, or pool smart-sizing"
),
new MBeanNotificationInfo( new String[] {NOTIF_OPENCONNECTION}, "openConnection",
"Triggered when getConnection() method is called"
),
new MBeanNotificationInfo( new String[] {NOTIF_PRUNEBEGIN}, "pruneBegin",
"Triggered when pruner begins a run"
),
new MBeanNotificationInfo( new String[] {NOTIF_PRUNECONNECTION}, "pruneConnection",
"Triggered when pruner closes a connection because it's been checked out too long"
),
new MBeanNotificationInfo( new String[] {NOTIF_PRUNEFINISH}, "pruneFinish",
"Triggered when pruner finishes a run"
)
};
}
private static Hashtable poolsByDb = new Hashtable();
private static boolean foundCodeTimerClasses = false;
static {
try {
Class.forName("com.icentris.util.CodeTimer");
Class.forName("com.icentris.util.CodeTimerSegment");
foundCodeTimerClasses = true;
} catch (LinkageError e) {
} catch (ClassNotFoundException e) {}
}
private String dbDriver;
private String dbUrl;
private String dbUser;
private String dbPass;
private String poolName;
private int maxConnections = 50;
private int minConnections = 1;
private int timeoutInMinutes = 30;
private boolean strictTimeout = true;
private boolean closeOnError = true;
private boolean showDebugging = true;
private boolean useCodeTimer = foundCodeTimerClasses;
private long timerLimitInMillis = 100;
private int callerDepth = 1;
private String[] callersToIgnore;
private double pruneRunInMinutes;
private PruneConnectionPool pruner;
private List connections = (List) Collections.synchronizedList( new LinkedList() );
private Set connectionsInUse = (Set) Collections.synchronizedSet( new HashSet() );
private int connectionCount = 0;
private java.util.Date prunerLastRun;
public ConnectionPool(String dbDriver, String dbUrl, String dbUser, String dbPass)
throws ClassNotFoundException
{
if ( dbDriver != null ) Class.forName(dbDriver);
this.dbDriver = dbDriver;
this.dbUrl = dbUrl;
this.dbUser = dbUser;
this.dbPass = dbPass;
}
public String getDbDriver() {
return dbDriver;
}
public String getDbUrl() {
return dbUrl;
}
public String getDbUser() {
return dbUser;
}
public String getDbPass() {
return dbPass;
}
public String getPoolName() {
return poolName;
}
public void setPoolName(String poolName) {
this.poolName = poolName;
}
public int getMaxConnections() {
return maxConnections;
}
public void setMaxConnections(int maxConnections) {
this.maxConnections = maxConnections;
}
public int getMinConnections() {
return minConnections;
}
public void setMinConnections(int minConnections) {
this.minConnections = minConnections;
}
public int getTimeoutInMinutes() {
return timeoutInMinutes;
}
public void setTimeoutInMinutes(int timeoutInMinutes) {
this.timeoutInMinutes = timeoutInMinutes;
}
public boolean strictTimeout() {
return strictTimeout;
}
public void setStrictTimeout(boolean strictTimeout) {
this.strictTimeout = strictTimeout;
}
/** Wrapper of {@link #strictTimeout()} with "is" name to comply with MBean standard. */
public boolean isStrictTimeout() { return strictTimeout(); }
public boolean closeOnError() {
return closeOnError;
}
public void setCloseOnError(boolean closeOnError) {
this.closeOnError = closeOnError;
}
/** Wrapper of {@link #closeOnError()} with "is" name to comply with MBean standard. */
public boolean isCloseOnError() { return closeOnError(); }
public boolean showDebugging() {
return showDebugging;
}
public void setShowDebugging(boolean showDebugging) {
this.showDebugging = showDebugging;
}
/** Wrapper of {@link #showDebugging()} with "is" name to comply with MBean standard. */
public boolean isShowDebugging() { return showDebugging(); }
public boolean useCodeTimer() {
return useCodeTimer;
}
public void setUseCodeTimer(boolean useCodeTimer) {
// make sure if useCodeTimer is true,
// we're deployed with CodeTimer and CodeTimerSegment
if ( useCodeTimer == true &&
foundCodeTimerClasses == false )
{
throw new IllegalStateException("useCodeTimer set to true, but com.icentris.util.CodeTimer and com.icentris.util.CodeTimerSegment not available!");
}
this.useCodeTimer = useCodeTimer;
}
public long getTimerLimitInMillis() {
return timerLimitInMillis;
}
public void setTimerLimitInMillis(long timerLimitInMillis) {
this.timerLimitInMillis = timerLimitInMillis;
}
public int getCallerDepth() {
return callerDepth;
}
public void setCallerDepth(int callerDepth) {
this.callerDepth = callerDepth;
}
public String[] getCallersToIgnore() {
if ( callersToIgnore == null || callersToIgnore.length == 0 ) {
callersToIgnore = new String[] { "com.icentris.sql.ConnectionPool.getConnection" };
}
return callersToIgnore;
}
public void setCallersToIgnore(String[] callersToIgnore) {
if ( callersToIgnore != null && callersToIgnore.length > 0 ) {
ArrayList tmpArray = new ArrayList(Arrays.asList(callersToIgnore));
if ( ! tmpArray.contains("com.icentris.sql.ConnectionPool.getConnection") ) {
tmpArray.add("com.icentris.sql.ConnectionPool.getConnection");
callersToIgnore = (String[]) tmpArray.toArray(new String[0]);
}
}
this.callersToIgnore = callersToIgnore;
}
public double getPruneRunInMinutes() {
return pruneRunInMinutes;
}
public synchronized void startPruner(double pruneRunInMinutes) {
if ( pruneRunInMinutes <= 0 ) {
throw new IllegalArgumentException("Illegal attempt to start pruner to run every " + pruneRunInMinutes + " minutes, pruneRunInMinutes must be greater than 0");
}
Timer timer = new Timer();
long pruneRunInMillis = (long) (pruneRunInMinutes * 60 * 1000);
pruner = new PruneConnectionPool(this);
timer.scheduleAtFixedRate( pruner, pruneRunInMillis, pruneRunInMillis );
this.pruneRunInMinutes = pruneRunInMinutes;
}
public void pruneOnce() {
(new PruneConnectionPool(this)).run();
}
public synchronized void stopPruner() {
if ( pruner == null ) return;
pruner.cancel();
pruneRunInMinutes = -1;
}
public synchronized void finalize() {
while ( connections.size() > 0 ) {
ConnectionWrapper connection = (ConnectionWrapper) connections.get(0);
removeConnection(connection);
}
if ( connectionsInUse.size() > 0 ) {
// run the pruner once to clear any connection older than 1 minute
pruneRunInMinutes = 1;
(new PruneConnectionPool(this)).run();
stopPruner();
// if there's still leftover connections, oh well!
if ( connectionsInUse.size() > 0 ) {
logger
.error(
"finalize() - ERROR: [ConnectionPool] Failed to gracefully finalize. I'm going to have to shut off the following in-use connections:", null); //$NON-NLS-1$
Iterator inUseSetIterator = connectionsInUse.iterator();
while ( inUseSetIterator.hasNext() ) {
ConnectionWrapper connection = (ConnectionWrapper) inUseSetIterator.next();
logger.error("finalize() - " + connection.getStatusString(), null); //$NON-NLS-1$
try {
connection.realConnection.close();
} catch (SQLException e) {
logger.error("finalize() - ERROR: [ConnectionPool.finalize] error closing connection [" + connection + "]:", e); //$NON-NLS-1$ //$NON-NLS-2$
logger.error("finalize()", e); //$NON-NLS-1$
}
}
}
}
connectionsInUse.clear();
connectionCount = 0;
JMX.attemptUnregisterMBean( "icentris.sql:name=ConnectionPool(" + poolName + ")" );
}
public synchronized int getConnectionCount() {
return connections.size() + connectionsInUse.size();
}
public int getAvailableCount() {
return connections.size();
}
public int getInUseCount() {
return connectionsInUse.size();
}
public void releaseConnection(ConnectionWrapper connection) {
try {
if ( closeOnError() == true && connection.getMostRecentError() != null ) {
if ( showDebugging() ) {
logger
.error(
"releaseConnection(ConnectionWrapper) - INFO: [ConnectionPool] closing connection [" + connection + "] because it had the following error:", null); //$NON-NLS-1$ //$NON-NLS-2$
logger.error("releaseConnection(ConnectionWrapper)", connection.getMostRecentError()); //$NON-NLS-1$
}
connectionCount--;
connection.realConnection.close();
//sendNotification( new Notification(NOTIF_DESTROYCONNECTION, NOTIF_DESTROYCONNECTION,
// notificationSequence++, connection.toString()) );
//JMX.attemptUnregisterMBean( "icentris.sql:name=" + connection );
}
connection.setCaller("");
connection.setInUse( false );
synchronized(this) {
connections.add(connection);
connectionsInUse.remove(connection);
notify();
}
} catch (Throwable t) {
logger.error("releaseConnection(ConnectionWrapper) - ERROR: [ConnectionPool] error closing connection [" + connection + "]:", t); //$NON-NLS-1$ //$NON-NLS-2$
logger.error("releaseConnection(ConnectionWrapper)", t); //$NON-NLS-1$
}
}
/**
* Careful! Only use this in desparate situations!
*/
public synchronized void removeConnection(String connectionString) {
if ( connectionString == null ) return;
for ( int i=0; i < connections.size(); i++ ) {
ConnectionWrapper connection = (ConnectionWrapper) connections.get(i);
if ( connectionString.equals(connection.toString()) ) {
removeConnection(connection);
return;
}
}
Iterator inUseSetIterator = connectionsInUse.iterator();
while ( inUseSetIterator.hasNext() ) {
ConnectionWrapper connection = (ConnectionWrapper) inUseSetIterator.next();
if ( connectionString.equals(connection.toString()) ) {
removeConnection(connection);
return;
}
}
if (logger.isDebugEnabled()) {
logger.debug("removeConnection(String) - removeConnection(connectionString) called but Connection=[" + connectionString + "] not found in pool"); //$NON-NLS-1$ //$NON-NLS-2$
}
}
public synchronized void removeConnection(ConnectionWrapper connection) {
try {
connection.realConnection.close();
} catch (SQLException e) {
logger.error("removeConnection(ConnectionWrapper) - ERROR: [ConnectionPool.removeConnection] error closing connection [" + connection + "]:", e); //$NON-NLS-1$ //$NON-NLS-2$
logger.error("removeConnection(ConnectionWrapper)", e); //$NON-NLS-1$
}
connections.remove(connection);
connectionsInUse.remove(connection);
connectionCount--;
//JMX.attemptUnregisterMBean( "icentris.sql:name=" + connection );
}
public static void initializePool(String poolName, ConnectionPool pool) {
if ( poolName == null ) poolName = "default";
if ( pool == null ) {
throw new IllegalArgumentException("Cannot initialize a null pool");
}
pool.setPoolName(poolName);
synchronized (poolsByDb) {
// I'm not waiting for the garbage collection to free
// my db connections, let's do it now!
if ( poolsByDb.get(poolName) != null ) {
ConnectionPool oldPool = (ConnectionPool) poolsByDb.get(poolName);
oldPool.finalize();
}
JMX.attemptRegisterMBean( pool, "icentris.sql:name=ConnectionPool(" + poolName + ")" );
poolsByDb.put(poolName, pool);
}
}
public static String[] getPoolNames() {
ArrayList names = new ArrayList();
synchronized (poolsByDb) {
Enumeration enumerator = poolsByDb.keys();
while ( enumerator.hasMoreElements() ) {
names.add(enumerator.nextElement());
}
}
return (String[]) names.toArray(new String[0]);
}
public static ConnectionPool getPool() {
return getPool("default");
}
public static ConnectionPool getPool(String poolName) {
if ( poolName == null ) poolName = "default";
ConnectionPool pool = (ConnectionPool) poolsByDb.get(poolName);
if ( pool == null ) {
throw new IllegalStateException("The ConnectionPool (" + poolName + ") has not been initialized yet");
} else {
return pool;
}
}
public static Connection getConnection()
throws SQLException
{
return getConnection("default");
}
public static java.sql.Connection getConnection(String poolName)
throws SQLException
{
CodeTimer timer = new CodeTimer();
CodeTimerSegment segment = timer.start("ConnectionPool: getConnection");
segment.setCanBeSubSegmentOnlyIfUnacceptable( true );
ConnectionPool pool = (ConnectionPool) poolsByDb.get(poolName);
if ( pool == null ) {
throw new IllegalStateException("The ConnectionPool (" + poolName + ") has not been initialized yet");
}
//sendNotification( new Notification(NOTIF_OPENCONNECTION, NOTIF_OPENCONNECTION,
// notificationSequence++, connection.toString()) );
try {
ConnectionWrapper connection = pool.prepareConnection();
timer.stop("ConnectionPool: getConnection");
// hopefully the common case, we're done!
return connection;
} catch (IndexOutOfBoundsException e) {}
synchronized (pool) {
boolean timeToCreate = false;
do {
// try again now that we're synchronized
if ( pool.connections.size() > 0 ) {
try {
ConnectionWrapper connection = pool.prepareConnection();
timer.stop("ConnectionPool: getConnection");
return connection;
} catch (IndexOutOfBoundsException e) {}
} else if ( pool.connectionCount < pool.getMaxConnections() ) {
timeToCreate = true;
} else {
// Too many connections? Let's wait for some to free up.
if (logger.isDebugEnabled()) {
logger
.debug("getConnection(String) - WARNING: [ConnectionPool] too many connections in use [" + pool.connectionsInUse.size() + "], waiting [" + Thread.currentThread() + "]"); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
}
try { pool.wait(); } catch (InterruptedException e) {}
if ( pool.showDebugging() ) {
if (logger.isDebugEnabled()) {
logger
.debug("getConnection(String) - INFO: [ConnectionPool] freed up! [" + Thread.currentThread() + "] connections in use= [" + pool.connectionsInUse.size() + "]"); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
}
}
}
} while ( timeToCreate == false );
// if we made it here, the pool is too small or there are no more
// connections in the pool, time to initialize another
pool.connectionCount++;
}
// Now we're unsynchronzied so others can get a lock on the pool to do their thing.
// We should be safe to open a new connection because we already reserved it by
// doing pool.connectionCount++. But if we fail in getting the new connection, let's
// make sure to pool.connectionCount-- so others can try.
try {
ConnectionWrapper connection = pool.openNewConnection();
timer.stop("ConnectionPool: getConnection");
return connection;
} catch (SQLException e) {
pool.connectionCount--;
timer.stop("ConnectionPool: getConnection");
throw e;
}
}
/** Should only be called once inside getConnection() method. */
private ConnectionWrapper openNewConnection() throws SQLException {
Connection newConnection =
DriverManager.getConnection(getDbUrl(), getDbUser(), getDbPass());
ConnectionWrapper connection = new ConnectionWrapper(newConnection, this);
//sendNotification( new Notification(NOTIF_CREATECONNECTION, NOTIF_CREATECONNECTION,
// notificationSequence++, connection.toString()) );
connection.setInUse( true );
connection.updateCheckedOutTime();
addToInUseSet( connection );
connection.setUseCodeTimer( useCodeTimer() );
connection.setTimerLimitInMillis( getTimerLimitInMillis() );
connection.setCallerDepth( getCallerDepth() );
connection.setCallersToIgnore( getCallersToIgnore() );
if ( showDebugging() ) {
if (logger.isDebugEnabled()) {
logger.debug("openNewConnection() - INFO: [ConnectionPool] creating new connection=[" + connection + "]"); //$NON-NLS-1$ //$NON-NLS-2$
}
}
return connection;
}
/** Should only be called once inside prepareConnection() method. */
private synchronized ConnectionWrapper tryToGetConnection() throws IndexOutOfBoundsException {
ConnectionWrapper connection = (ConnectionWrapper) connections.remove(0);
return connection;
}
/** Should only be called once inside prepareConnection() method,
* and once inside openNewConnection() method. */
private synchronized void addToInUseSet(ConnectionWrapper connection) {
connectionsInUse.add(connection);
}
/** The contract of prepareConnection() is no synchronization block. It does, however,
* synchronize twice when calling tryToGetConnection() and addToInUseSet(). */
private ConnectionWrapper prepareConnection()
throws SQLException, IndexOutOfBoundsException
{
ConnectionWrapper connection;
boolean connectionIsGood;
do {
connection = tryToGetConnection();
boolean itsTimeToClose = false;
long nowInMinutes = System.currentTimeMillis() / 60000;
long minutesSinceLastUse = nowInMinutes - connection.getLastUsedTime();
long minutesSinceCreation = nowInMinutes - connection.getCreatedTime();
if ( strictTimeout() == true && minutesSinceCreation >= getTimeoutInMinutes() ) {
itsTimeToClose = true;
if ( showDebugging() ) {
if (logger.isDebugEnabled()) {
logger
.debug("prepareConnection() - INFO: [ConnectionPool] closing connection=[" + connection + "] which has been open for [" + minutesSinceCreation + "] minutes. The strict timeout is [" + getTimeoutInMinutes() + "]"); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$
}
}
} else if ( minutesSinceLastUse >= getTimeoutInMinutes() ) {
itsTimeToClose = true;
if ( showDebugging() ) {
if (logger.isDebugEnabled()) {
logger
.debug("prepareConnection() - INFO: [ConnectionPool] closing connection=[" + connection + "] which has been unused for [" + minutesSinceLastUse + "] minutes."); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
}
}
}
if ( itsTimeToClose == true ) {
connectionIsGood = false;
try {
connectionCount--;
connection.realConnection.close();
//sendNotification( new Notification(NOTIF_DESTROYCONNECTION, NOTIF_DESTROYCONNECTION,
// notificationSequence++, connection.toString()) );
//JMX.attemptUnregisterMBean( "icentris.sql:name=" + connection );
} catch (Throwable t) {
logger.error("prepareConnection() - ERROR: [ConnectionPool] error closing connection [" + connection + "]:", t); //$NON-NLS-1$ //$NON-NLS-2$
logger.error("prepareConnection()", t); //$NON-NLS-1$
}
} else {
if ( connection.isClosed() == true ) {
if ( showDebugging() ) {
logger.error("prepareConnection() - INFO: [ConnectionPool] trashing connection [" + connection + "] because isClosed() returned true", null); //$NON-NLS-1$ //$NON-NLS-2$
if ( connection.getMostRecentError() != null ) {
logger.error("prepareConnection()", connection.getMostRecentError()); //$NON-NLS-1$
}
}
connectionIsGood = false;
} else {
connectionIsGood = true;
}
}
// if this connection is no good let's try the next one
} while ( connectionIsGood == false );
connection.setInUse( true );
connection.updateCheckedOutTime();
connection.setLastCloser( null );
addToInUseSet( connection );
String caller = Caller.getCaller( connection.getCallersToIgnore() );
connection.setCaller(caller);
//connection.sendNotification( new Notification(connection.NOTIF_OPEN, connection.NOTIF_OPEN,
// connection.notificationSequence++, connection.getCaller()) );
return connection;
}
private static class PruneConnectionPool extends TimerTask {
private ConnectionPool pool;
public PruneConnectionPool(ConnectionPool pool) {
this.pool = pool;
}
public void run() {
//pool.sendNotification( new Notification(NOTIF_PRUNEBEGIN, NOTIF_PRUNEBEGIN,
// pool.notificationSequence++, null) );
pool.prunerLastRun = new java.util.Date();
ArrayList connectionsToClose = new ArrayList();
synchronized (pool) {
Iterator inUseSetIterator = pool.connectionsInUse.iterator();
while ( inUseSetIterator.hasNext() ) {
ConnectionWrapper connection = (ConnectionWrapper) inUseSetIterator.next();
long lastUsedTime = connection.getLastUsedTime();
long nowInMinutes = System.currentTimeMillis() / 60000;
long minutesSinceLastUse = nowInMinutes - lastUsedTime;
double minutesAllowed = pool.getPruneRunInMinutes();
// let's not close down connections that have been open for less than 1 minute
if ( minutesAllowed < 1 ) minutesAllowed = 1;
if ( minutesSinceLastUse >= minutesAllowed ) {
connectionsToClose.add( connection );
String statusString = connection.getStatusString();
if ( pool.showDebugging() ) {
if (logger.isDebugEnabled()) {
logger.debug("run() - INFO: [ConnectionPool] Closing connection not used in " + minutesAllowed + " minutes: " + connection); //$NON-NLS-1$ //$NON-NLS-2$
}
if (logger.isDebugEnabled()) {
logger.debug("run() - statuString=[" + statusString + "]"); //$NON-NLS-1$ //$NON-NLS-2$
}
}
inUseSetIterator.remove();
pool.connectionCount--;
//JMX.attemptUnregisterMBean( "icentris.sql:name=" + connection );
pool.sendNotification( new Notification(NOTIF_PRUNECONNECTION, NOTIF_PRUNECONNECTION,
pool.notificationSequence++, connection + ": " + statusString) );
}
}
}
for (int i=0; i < connectionsToClose.size(); i++ ) {
ConnectionWrapper connection = (ConnectionWrapper) connectionsToClose.get(i);
try {
connection.realConnection.close();
} catch (SQLException e) {
logger.error("run() - ERROR: [ConnectionPool] error closing inUse connection [" + connection + "]:", e); //$NON-NLS-1$ //$NON-NLS-2$
if (logger.isDebugEnabled()) {
logger.debug("run() - statuString=[" + connection.getStatusString() + "]"); //$NON-NLS-1$ //$NON-NLS-2$
}
logger.error("run()", e); //$NON-NLS-1$
}
}
//pool.sendNotification( new Notification(NOTIF_PRUNEFINISH, NOTIF_PRUNEFINISH,
// pool.notificationSequence++, null) );
}
}
public static String getFullStatusReport() {
StringBuffer sb = new StringBuffer();
synchronized (poolsByDb) {
Enumeration enumerator = poolsByDb.keys();
while ( enumerator.hasMoreElements() ) {
String poolName = (String) enumerator.nextElement();
ConnectionPool pool = (ConnectionPool) poolsByDb.get(poolName);
sb.append(pool.getStatusReport());
}
}
return sb.toString();
}
public static String getStatusReport(String poolName) {
ConnectionPool pool = (ConnectionPool) poolsByDb.get(poolName);
if ( pool == null ) return "";
return pool.getStatusReport();
}
public synchronized String getStatusReport() {
StringBuffer sb = new StringBuffer();
sb.append("Pool info for poolname=[" + poolName + "]\n");
sb.append(" dbDriver=[" + dbDriver + "]\n");
sb.append(" dbUrl=[" + dbUrl + "]\n");
sb.append(" dbUser=[" + dbUser + "]\n");
sb.append(" maxConnections=[" + maxConnections + "]\n");
sb.append(" minConnections=[" + minConnections + "]\n");
sb.append(" timeoutInMinutes=[" + timeoutInMinutes + "]\n");
sb.append(" strictTimeout=[" + strictTimeout + "]\n");
sb.append(" closeOnError=[" + closeOnError + "]\n");
sb.append(" showDebugging=[" + showDebugging + "]\n");
sb.append(" pruneRunInMinutes=[" + pruneRunInMinutes + "]\n");
sb.append(" useCodeTimer=[" + useCodeTimer + "]\n");
sb.append(" timerLimitInMillis=[" + timerLimitInMillis + "]\n");
sb.append(" callerDepth=[" + callerDepth + "]\n");
sb.append(" callersToIgnore=[" + (callersToIgnore != null ? Arrays.asList(callersToIgnore) : null) + "]\n");
sb.append(" connectionCount=[" + connectionCount + "]\n");
sb.append(connections.size() + " Waiting in pool:\n");
for ( int i=0; i < connections.size(); i++ ) {
ConnectionWrapper connection = (ConnectionWrapper) connections.get(i);
sb.append(" " + connection);
// This getOracleSessionId() isn't working, so I'm commenting it for now
///String oracleSessionId = connection.getOracleSessionId();
///if ( oracleSessionId != null ) {
/// sb.append(" OracleSessionId[" + oracleSessionId + "]");
///}
sb.append( connection.getStatusString() + "\n" );
}
sb.append(connectionsInUse.size() + " In Use:\n");
Iterator inUseSetIterator = connectionsInUse.iterator();
while ( inUseSetIterator.hasNext() ) {
ConnectionWrapper connection = (ConnectionWrapper) inUseSetIterator.next();
sb.append(" " + connection);
// This getOracleSessionId() isn't working, so I'm commenting it for now
//String oracleSessionId = connection.getOracleSessionId();
//if ( oracleSessionId != null ) {
// sb.append(" OracleSessionId[" + oracleSessionId + "]");
//}
sb.append( connection.getStatusString() + "\n" );
}
sb.append("Pruner Last Ran: " + prunerLastRun + "\n");
return sb.toString();
}
}