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

com.mchange.v2.c3p0.impl.C3P0PooledConnectionPool Maven / Gradle / Ivy

There is a newer version: 0.9.5.5_1
Show newest version
package com.mchange.v2.c3p0.impl;

import com.mchange.v2.c3p0.stmt.*;
import com.mchange.v2.c3p0.ConnectionCustomizer;
import com.mchange.v2.c3p0.SQLWarnings;
import com.mchange.v2.c3p0.UnifiedConnectionTester;
import com.mchange.v2.c3p0.WrapperConnectionPoolDataSource;

import java.lang.reflect.Method;
import java.sql.Connection;
import java.sql.SQLException;
import java.sql.SQLWarning;
import java.util.LinkedList;
import java.util.WeakHashMap;
import java.util.concurrent.locks.ReentrantLock;

import javax.sql.ConnectionEvent;
import javax.sql.ConnectionEventListener;
import javax.sql.ConnectionPoolDataSource;
import javax.sql.PooledConnection;

import com.mchange.v1.db.sql.ConnectionUtils;
import com.mchange.v2.async.AsynchronousRunner;
import com.mchange.v2.async.ThreadPoolReportingAsynchronousRunner;
import com.mchange.v2.log.MLevel;
import com.mchange.v2.log.MLog;
import com.mchange.v2.log.MLogger;
import com.mchange.v2.c3p0.C3P0Registry;
import com.mchange.v2.c3p0.ConnectionTester;
import com.mchange.v2.c3p0.QueryConnectionTester;
import com.mchange.v2.resourcepool.CannotAcquireResourceException;
import com.mchange.v2.resourcepool.ResourcePool;
import com.mchange.v2.resourcepool.ResourcePoolException;
import com.mchange.v2.resourcepool.ResourcePoolFactory;
import com.mchange.v2.resourcepool.TimeoutException;
import com.mchange.v2.sql.SqlUtils;

public final class C3P0PooledConnectionPool
{
    private final static boolean ASYNCHRONOUS_CONNECTION_EVENT_LISTENER = false;

    final static MLogger logger = MLog.getLogger( C3P0PooledConnectionPool.class );

    final ResourcePool rp;
    final ConnectionEventListener cl = new ConnectionEventListenerImpl();

    final ConnectionTester     connectionTester;
    final GooGooStatementCache scache;

    final Resurrectables resurrectables;

    final boolean c3p0PooledConnections;

    final int checkoutTimeout;
    final int connectionIsValidTimeout;

    final boolean disableSessionBoundaries;

    final AsynchronousRunner sharedTaskRunner;
    final AsynchronousRunner deferredStatementDestroyer;

    final AbstractInternalUseLockManager internalUseLockManager;

    //MT: protected by this' lock
    private RequestBoundaryMarker requestBoundaryMarker;

    public int getStatementDestroyerNumConnectionsInUse()                           { return scache == null ? -1 : scache.getStatementDestroyerNumConnectionsInUse(); }
    public int getStatementDestroyerNumConnectionsWithDeferredDestroyStatements()   { return scache == null ? -1 : scache.getStatementDestroyerNumConnectionsWithDeferredDestroyStatements(); }
    public int getStatementDestroyerNumDeferredDestroyStatements()                  { return scache == null ? -1 : scache.getStatementDestroyerNumDeferredDestroyStatements(); }

    private static class Resurrectables
    {
        //MT: protected by this' lock
        WeakHashMap candidates = new WeakHashMap();

        public synchronized void markResurrectable( Object resc )
        {
            candidates.put( resc, this );
            if (Debug.DEBUG && logger.isLoggable(MLevel.FINER))
                logger.log(MLevel.FINER, "Marked broken resource resurrectable: " + resc);
        }

        // both checks and removes/clears pconn
        public synchronized boolean checkResurrectable( Object resc )
        {
            boolean out = (candidates.remove( resc ) != null);
            if (Debug.DEBUG && logger.isLoggable(MLevel.FINER) && out)
                logger.log(MLevel.FINER, "Found and cleared resurrectable resource: " + resc);
            return out;
        }
    }

    /**
     *  This "internal use lock" crap is a lot of ado about very little.
     *  In theory, there is a hazard that pool maintenance tasks could
     *  get sufficiently backed up in the Thread pool that say, multple
     *  Connection tests might run simultaneously. That would be okay,
     *  but the Statement cache's "mark-in-use" functionality doesn't
     *  track nested use of resources. So we enforce exclusive, sequential
     *  execution of internal tests by requiring the tests hold a lock.
     */
    private static abstract class AbstractInternalUseLockManager
    {
	abstract ReentrantLock getInternalUseLock(Object resc);

	public void lockInternalUse(Object resc)   { getInternalUseLock(resc).lock(); }
	public void unlockInternalUse(Object resc) { getInternalUseLock(resc).unlock(); }
    }

    private static class WeakHashMapInternalUseLockManager extends AbstractInternalUseLockManager
    {
        WeakHashMap inUseLocks = new WeakHashMap();

	synchronized ReentrantLock getInternalUseLock(Object resc)
        {
            ReentrantLock out = (ReentrantLock) inUseLocks.get(resc);
            if (out == null)
            {
                out = new ReentrantLock();
                inUseLocks.put( resc, out );
            }
            return out;
        }

	public synchronized void lockInternalUse(Object resc)   { super.lockInternalUse(resc); }
	public synchronized void unlockInternalUse(Object resc) { super.unlockInternalUse(resc); }
    }

    private static class C3P0PooledConnectionNestedLockInternalUseLockManager extends AbstractInternalUseLockManager
    {
	ReentrantLock getInternalUseLock(Object resc)
	{ return ((AbstractC3P0PooledConnection) resc).inInternalUseLock; }
    }

    private interface RequestBoundaryMarker
    {
	public void attemptNotifyBeginRequest(PooledConnection pc);
	public void attemptNotifyEndRequest(PooledConnection pc);
    }

    private static RequestBoundaryMarker NO_OP_REQUEST_BOUNDARY_MARKER = new RequestBoundaryMarker()
    {
	public void attemptNotifyBeginRequest(PooledConnection pc) {}
	public void attemptNotifyEndRequest(PooledConnection pc) {}
    };

    private static RequestBoundaryMarker INTERFACE_REQUEST_BOUNDARY_MARKER = new RequestBoundaryMarker()
    {
	public void attemptNotifyBeginRequest(PooledConnection pc)
	{
	    if (pc instanceof AbstractC3P0PooledConnection)
	    {
		AbstractC3P0PooledConnection acpc = (AbstractC3P0PooledConnection) pc;
		Connection conn = acpc.getPhysicalConnection();
		try
		{
                    conn.beginRequest();

                    if (Debug.DEBUG && logger.isLoggable(MLevel.FINEST))
                        logger.log(MLevel.FINEST, "beginRequest method called");
		}
                catch (AbstractMethodError ame)
                {
                    if (logger.isLoggable(MLevel.WARNING))
                        logger.log(MLevel.WARNING, "AbstractMethodError invoking beginRequest method for Connction, even though Connections were tested for the presence of this method previously.", ame);
                }
		catch (Exception ex)
		{
                    if (logger.isLoggable(MLevel.WARNING))
                        logger.log(MLevel.WARNING, "Error invoking beginRequest method for connection", ex);
                }
	    }
	}
	public void attemptNotifyEndRequest(PooledConnection pc)
	{
	    if (pc instanceof AbstractC3P0PooledConnection)
	    {
		AbstractC3P0PooledConnection acpc = (AbstractC3P0PooledConnection) pc;
		Connection conn = acpc.getPhysicalConnection();
		try
		{
                    conn.endRequest();

                    if (Debug.DEBUG && logger.isLoggable(MLevel.FINEST))
                        logger.log(MLevel.FINEST, "endRequest method called");
		}
                catch (AbstractMethodError ame)
                {
                    if (logger.isLoggable(MLevel.WARNING))
                        logger.log(MLevel.WARNING, "AbstractMethodError invoking endRequest method for Connction, even though Connections were tested for the presence of this method previously.", ame);
                }
		catch (Exception ex)
		{
                    if (logger.isLoggable(MLevel.WARNING))
                        logger.log(MLevel.WARNING, "Error invoking endRequest method for connection", ex);
                }
	    }
	}
    };

    private static class ReflectiveRequestBoundaryMarker implements RequestBoundaryMarker
    {
	Method beginRequest;
	Method endRequest;

	ReflectiveRequestBoundaryMarker(Method beginRequest, Method endRequest)
	{
	    this.beginRequest = beginRequest;
	    this.endRequest   = endRequest;
            if (!beginRequest.isAccessible()) beginRequest.setAccessible(true);
            if (!endRequest.isAccessible()) endRequest.setAccessible(true);
	}
	public void attemptNotifyBeginRequest(PooledConnection pc)
	{
	    if (pc instanceof AbstractC3P0PooledConnection)
	    {
		AbstractC3P0PooledConnection acpc = (AbstractC3P0PooledConnection) pc;
		Connection conn = acpc.getPhysicalConnection();
		try
		{
		    beginRequest.invoke(conn);

                    if (Debug.DEBUG && logger.isLoggable(MLevel.FINEST))
                        logger.log(MLevel.FINEST, "beginRequest method called");
		}
		catch (Exception ex)
		{
                    if (logger.isLoggable(MLevel.WARNING))
                        logger.log(MLevel.WARNING, "Error invoking beginRequest method for connection", ex);
                }
	    }
	}
	public void attemptNotifyEndRequest(PooledConnection pc)
	{
	    if (pc instanceof AbstractC3P0PooledConnection)
	    {
		AbstractC3P0PooledConnection acpc = (AbstractC3P0PooledConnection) pc;
		Connection conn = acpc.getPhysicalConnection();
		try
		{
		    endRequest.invoke(conn);

                    if (Debug.DEBUG && logger.isLoggable(MLevel.FINEST))
                        logger.log(MLevel.FINEST, "endRequest method called");
		}
		catch (Exception ex)
		{
                    if (logger.isLoggable(MLevel.WARNING))
                        logger.log(MLevel.WARNING, "Error invoking endRequest method for connection", ex);
                }
	    }
	}
    }

    // we assume (pretty safely I think) that all PooledConnections we see will have the same type
    // and physical connection type
    private synchronized RequestBoundaryMarker findRequestBoundaryMarker(PooledConnection pc)
    {
	if (this.requestBoundaryMarker != null)
	    return this.requestBoundaryMarker;
	else
	{
	    if (this.disableSessionBoundaries)
	    {
		this.requestBoundaryMarker = NO_OP_REQUEST_BOUNDARY_MARKER;

                if (Debug.DEBUG && logger.isLoggable(MLevel.FINE))
		    logger.log(MLevel.FINE, "Installed no-op request boundary marker due to markSessionBoundaries setting.");
	    }
	    else if (pc instanceof AbstractC3P0PooledConnection)
	    {
		AbstractC3P0PooledConnection acpc = (AbstractC3P0PooledConnection) pc;
		Connection conn = acpc.getPhysicalConnection();
		try
		{
		    Method beginRequest = conn.getClass().getMethod("beginRequest");
		    Method endRequest = conn.getClass().getMethod("endRequest");

                    if (Debug.DEBUG && logger.isLoggable(MLevel.FINEST))
                        logger.log(MLevel.FINEST, "Request boundary methods found.");

                    boolean intfc_has_methods;
                    try
                    {
                        Method intfcBeginRequest = Connection.class.getMethod("beginRequest");
                        Method intfcEndRequest = Connection.class.getMethod("endRequest");

                        if (Debug.DEBUG && logger.isLoggable(MLevel.FINEST))
                            logger.log(MLevel.FINEST, "Interface request boundary methods found.");

                        intfc_has_methods = true;
                    }
                    catch (NoSuchMethodException e)
                    { intfc_has_methods = false; }

                    if (intfc_has_methods)
                    {
                        this.requestBoundaryMarker = INTERFACE_REQUEST_BOUNDARY_MARKER;

                        if (Debug.DEBUG && logger.isLoggable(MLevel.FINE))
                            logger.log(MLevel.FINE, "Installed interface-based request boundary marker.");
                    }
                    else
                    {
                        this.requestBoundaryMarker = new ReflectiveRequestBoundaryMarker(beginRequest, endRequest);

                        if (Debug.DEBUG && logger.isLoggable(MLevel.FINE))
                            logger.log(MLevel.FINE, "Installed reflective request boundary marker.");
                    }
		}
		catch (NoSuchMethodException nsme)
		{
		    // let methods be null, driver does not implement them
                    if (logger.isLoggable(MLevel.WARNING))
                        logger.log(MLevel.WARNING, "Request boundary methods not found.");

		    this.requestBoundaryMarker = NO_OP_REQUEST_BOUNDARY_MARKER;

                    if (Debug.DEBUG && logger.isLoggable(MLevel.FINE))
                        logger.log(MLevel.FINE, "Installed no-op request boundary marker, because request boundary methods not found.");
		}
		catch (SecurityException se)
		{
                    if (logger.isLoggable(MLevel.WARNING))
                        logger.log(MLevel.WARNING, "Could not make boundary methods accessible.");
                    if (Debug.DEBUG && logger.isLoggable(MLevel.FINE))
                        logger.log(MLevel.FINE, "SecurityException:", se);

		    this.requestBoundaryMarker = NO_OP_REQUEST_BOUNDARY_MARKER;

                    if (Debug.DEBUG && logger.isLoggable(MLevel.FINE))
                        logger.log(MLevel.FINE, "Installed no-op request boundary marker, because request boundary methods could not be made accessible.");
		}
                catch (Exception e)
                {
                    if (logger.isLoggable(MLevel.WARNING))
                        logger.log(MLevel.WARNING, "An unexpected Exception occurred while querying request boundary methods.", e);

		    this.requestBoundaryMarker = NO_OP_REQUEST_BOUNDARY_MARKER;

                    if (Debug.DEBUG && logger.isLoggable(MLevel.FINE))
                        logger.log(MLevel.FINE, "Installed no-op request boundary marker, because an unexpected exception occurred while querying request boundary methods.");
                }
	    }
	    else
	    {
		this.requestBoundaryMarker = NO_OP_REQUEST_BOUNDARY_MARKER;

		if (logger.isLoggable(MLevel.WARNING))
		    logger.log(MLevel.WARNING, "Could not mark request boundaries when pooling non-c3p0 PooledConnections.");
		if (Debug.DEBUG && logger.isLoggable(MLevel.FINE))
		    logger.log(MLevel.FINE, "Installed no-op request boundary marker, because we are working with non-c3p0 pooled connections, and do not support request boundary marking in this case.");
	    }

	    return this.requestBoundaryMarker;
	}
    }

    private void markBeginRequest(PooledConnection pc) { findRequestBoundaryMarker(pc).attemptNotifyBeginRequest(pc); }
    private void markEndRequest(PooledConnection pc) { findRequestBoundaryMarker(pc).attemptNotifyEndRequest(pc); }

    C3P0PooledConnectionPool( final ConnectionPoolDataSource cpds,
			      final DbAuth auth,
			      int min,
			      int max,
			      int start,
			      int inc,
			      int acq_retry_attempts,
			      int acq_retry_delay,
			      boolean break_after_acq_failure,
			      int checkoutTimeout, //milliseconds
			      final int connectionIsValidTimeout, // seconds
			      int idleConnectionTestPeriod, //seconds
			      int maxIdleTime, //seconds
			      int maxIdleTimeExcessConnections, //seconds
			      int maxConnectionAge, //seconds
			      int propertyCycle, //seconds
			      int unreturnedConnectionTimeout, //seconds
			      boolean debugUnreturnedConnectionStackTraces,
			      boolean forceSynchronousCheckins,
			      final boolean testConnectionOnCheckout,
			      final boolean testConnectionOnCheckin,
                              boolean attemptResurrectOnCheckin,
			      int maxStatements,
			      int maxStatementsPerConnection,
			      String markSessionBoundaries,
			      /* boolean statementCacheDeferredClose,      */
			      final ConnectionTester connectionTester,
			      final ConnectionCustomizer connectionCustomizer,
			      final String testQuery,
			      final ResourcePoolFactory fact,
			      ThreadPoolReportingAsynchronousRunner taskRunner,
			      ThreadPoolReportingAsynchronousRunner deferredStatementDestroyer,
			      final String parentDataSourceIdentityToken) throws SQLException
    {
        try
        {
            this.c3p0PooledConnections = (cpds instanceof WrapperConnectionPoolDataSource);

            if (!c3p0PooledConnections)
            {
                if (logger.isLoggable(MLevel.WARNING) && (maxStatements > 0 || maxStatementsPerConnection > 0))
                    logger.log(MLevel.WARNING, "Statement caching is configured, but cannot be supported, because the provided ConnectionPoolDataSource is not a c3p0 implementation. Initializing with no statement cache.");
                this.scache = null;
            }
            else if (maxStatements > 0 && maxStatementsPerConnection > 0)
                this.scache = new DoubleMaxStatementCache( taskRunner, deferredStatementDestroyer, maxStatements, maxStatementsPerConnection );
            else if (maxStatementsPerConnection > 0)
                this.scache = new PerConnectionMaxOnlyStatementCache( taskRunner, deferredStatementDestroyer, maxStatementsPerConnection );
            else if (maxStatements > 0)
                this.scache = new GlobalMaxOnlyStatementCache( taskRunner, deferredStatementDestroyer, maxStatements );
            else
                this.scache = null;

            if (attemptResurrectOnCheckin)
                this.resurrectables = new Resurrectables();
            else
                this.resurrectables = null;

            this.connectionTester = connectionTester;

            this.checkoutTimeout = checkoutTimeout;

	    this.connectionIsValidTimeout = connectionIsValidTimeout;

            this.sharedTaskRunner = taskRunner;
	    this.deferredStatementDestroyer = deferredStatementDestroyer;

	    if ("always".equalsIgnoreCase(markSessionBoundaries))
		this.disableSessionBoundaries = false;
	    else if ("never".equalsIgnoreCase(markSessionBoundaries))
		this.disableSessionBoundaries = true;
	    else if ("if-no-statement-cache".equalsIgnoreCase(markSessionBoundaries))
		this.disableSessionBoundaries = this.scache != null;
	    else
		{
		    if (logger.isLoggable(MLevel.WARNING))
			logger.log(MLevel.WARNING, "markSessionBoundaries should be one of 'always','never', or 'if-no-statement-cache'. Found illegal value '" + markSessionBoundaries  + "'. Defaulting to 'always'.");
		    this.disableSessionBoundaries = false;
		}

	    this.internalUseLockManager = (c3p0PooledConnections ? new C3P0PooledConnectionNestedLockInternalUseLockManager() : new WeakHashMapInternalUseLockManager());

            class PooledConnectionResourcePoolManager implements ResourcePool.Manager
            {
                ConnectionTestPath connectionTestPath;

                void initAfterResourcePoolConstructed()
                {
		    if (connectionTester == null)
		    {
			if (testQuery != null)
			{
			    if(connectionIsValidTimeout == C3P0Defaults.connectionIsValidTimeout())
			    {
				if (logger.isLoggable(MLevel.WARNING))
				    logger.log( MLevel.WARNING,
						"Although no ConnectionTester is set, preferredTestQuery (or automaticTestTable) is also set, which can only be supported by a ConnectionTester. " +
						"Reverting to use of ConnectionTester com.mchange.v2.c3p0.impl.DefaultConnectionTester." );
				this.connectionTestPath = new ConnectionTesterConnectionTestPath( rp, C3P0Registry.getConnectionTester(DefaultConnectionTester.class.getName()), scache, testQuery, c3p0PooledConnections );
			    }
			    else
			    {
				if (logger.isLoggable(MLevel.WARNING))
				    logger.log( MLevel.WARNING,
						"Both a preferredTestQuery (or automaticTestTable) and a non-default value of connectionIsValidTimeout are set, but only " +
						"one can be simultaneously supported. Will use Connection.isValid( connectionIsValidTimeout ), and " +
						"ignore test query '" + testQuery + "'." );
				this.connectionTestPath = new IsValidSimplifiedConnectionTestPath( rp, connectionIsValidTimeout );
			    }
			}
			else
			    this.connectionTestPath = new IsValidSimplifiedConnectionTestPath( rp, connectionIsValidTimeout );
		    }
		    else
		    {
			if (logger.isLoggable(MLevel.WARNING) && connectionIsValidTimeout != C3P0Defaults.connectionIsValidTimeout())
			{
				logger.log( MLevel.WARNING,
					    "A ConnectionTester '" + connectionTester +
					    "' is explicitly set, but also a nondefault connectionIsValidTimeout (" + connectionIsValidTimeout +
					    "). Unfortunately connectionIsValidTimeout is not supported by ConnectionTesters, and will be ignored. " +
					    "If you are using com.mchange.v2.c3p0.impl.DefaultConnectionTester, you may " +
					    "set config or system property com.mchange.v2.c3p0.impl.DefaultConnectionTester.isValidTimeout instead. " +
					    "Alternatively, just switch to simple isValid(...) testing by setting connectionTesterClassName to null" );
			}
			this.connectionTestPath = new ConnectionTesterConnectionTestPath( rp, connectionTester, scache, testQuery, c3p0PooledConnections );
		    }
                }

                public Object acquireResource() throws Exception
                {
                    PooledConnection out;

                    if ( connectionCustomizer == null)
                    {
                        out = (auth.equals( C3P0ImplUtils.NULL_AUTH ) ?
                               cpds.getPooledConnection() :
                               cpds.getPooledConnection( auth.getUser(),
                                                         auth.getPassword() ) );
                    }
                    else
                    {
                        try
                        {
                            WrapperConnectionPoolDataSourceBase wcpds = (WrapperConnectionPoolDataSourceBase) cpds;

                            out = (auth.equals( C3P0ImplUtils.NULL_AUTH ) ?
                                   wcpds.getPooledConnection( connectionCustomizer, parentDataSourceIdentityToken ) :
                                   wcpds.getPooledConnection( auth.getUser(),
                                                              auth.getPassword(),
                                                              connectionCustomizer, parentDataSourceIdentityToken ) );
                        }
                        catch (ClassCastException e)
                        {
                            String msg =
                                "Cannot use a ConnectionCustomizer with a non-c3p0 ConnectionPoolDataSource." +
                                " ConnectionPoolDataSource: " + cpds.getClass().getName();
                            throw SqlUtils.toSQLException(msg, e);
                        }
                    }

                    //connectionCounter.increment();
                    //totalOpenedCounter.increment();

                    try
                    {
                        if (scache != null)
                        {
                            if (c3p0PooledConnections)
                                ((AbstractC3P0PooledConnection) out).initStatementCache(scache);
                            else
                            {
                                // System.err.print("Warning! StatementPooling not ");
                                // System.err.print("implemented for external (non-c3p0) ");
                                // System.err.println("ConnectionPoolDataSources.");

                                logger.warning("StatementPooling not implemented for external (non-c3p0) ConnectionPoolDataSources.");
                            }
                        }

                        // log and clear any SQLWarnings present upon acquisition
                        Connection con = null;
                        try
                        {
                            scacheWaitMarkPooledConnectionInUse(out);
                            con = out.getConnection();
                            SQLWarnings.logAndClearWarnings( con );
                        }
                        finally
                        {
                            //invalidate the proxy Connection
                            ConnectionUtils.attemptClose( con );

                            scacheUnmarkPooledConnectionInUse(out);
                        }

                        return out;
                    }
                    catch (Exception e)
                    {
                        if (logger.isLoggable( MLevel.WARNING ))
                            logger.log(MLevel.WARNING,
				       "A PooledConnection was acquired, but an Exception occurred while preparing it for use. Attempting to destroy.",
				       e);
                        try { destroyResource( out, false ); }
                        catch (Exception e2)
                        {
                            if (logger.isLoggable( MLevel.WARNING ))
                                logger.log( MLevel.WARNING,
                                                "An Exception occurred while trying to close partially acquired PooledConnection.",
                                                e2 );
                        }

                        throw e;
                    }
                    finally
                    {
                        if (logger.isLoggable( MLevel.FINEST ))
                            logger.finest(this + ".acquireResource() returning. " );
                        //"Currently open Connections: " + connectionCounter.getValue() +
                        //"; Failed close count: " + failedCloseCounter.getValue() +
                        //"; Total processed by this pool: " + totalOpenedCounter.getValue());
                    }
                }

                // REFURBISHMENT:
                // the PooledConnection refurbishes itself when
                // its Connection view is closed, prior to being
                // checked back in to the pool. But we still may want to
                // test to make sure it is still good.

                public void refurbishResourceOnCheckout( Object resc ) throws Exception
                {
                    internalUseLockManager.lockInternalUse(resc);
                    try
		    {
			if ( connectionCustomizer != null )
			{
			    Connection physicalConnection = null;
			    try
			    {
				physicalConnection =  ((AbstractC3P0PooledConnection) resc).getPhysicalConnection();
				scacheWaitMarkPhysicalConnectionInUse( physicalConnection );
				if ( testConnectionOnCheckout )
				{
				    if ( Debug.DEBUG && logger.isLoggable( MLevel.FINER ) )
					finerLoggingTestPooledConnection( resc, "CHECKOUT" );
				    else
					testPooledConnection( resc );
				}
				connectionCustomizer.onCheckOut( physicalConnection, parentDataSourceIdentityToken );
			    }
			    catch (ClassCastException e)
			    {
				throw SqlUtils.toSQLException("Cannot use a ConnectionCustomizer with a non-c3p0 PooledConnection." +
							      " PooledConnection: " + resc +
							      "; ConnectionPoolDataSource: " + cpds.getClass().getName(), e);
			    }
			    finally
			    { scacheUnmarkPhysicalConnectionInUse(physicalConnection); }
			}
			else
			{
			    if ( testConnectionOnCheckout )
			    {
				PooledConnection pc = (PooledConnection) resc;
				try
				{
				    scacheWaitMarkPooledConnectionInUse( pc );

				    if ( Debug.DEBUG && logger.isLoggable( MLevel.FINER ) )
					finerLoggingTestPooledConnection( pc, "CHECKOUT" );
				    else
					testPooledConnection( pc );
				}
				finally
				{
				    scacheUnmarkPooledConnectionInUse(pc);
				}
			    }
			}
		    }
                    finally
                    { internalUseLockManager.unlockInternalUse(resc); }
                }

		// TODO: refactor this by putting the connectionCustomizer if logic inside the (currently repeated) logic
                public void refurbishResourceOnCheckin( Object resc ) throws Exception
                {
		    Connection proxyToClose = null; // can't close a proxy while we own parent PooledConnection's lock.
                    boolean attemptResurrect = (resurrectables != null && resurrectables.checkResurrectable(resc));
		    try
		    {
                      internalUseLockManager.lockInternalUse(resc);
                      try
		      {
			if ( connectionCustomizer != null )
			{
			    Connection physicalConnection = null;
			    try
			    {
				physicalConnection =  ((AbstractC3P0PooledConnection) resc).getPhysicalConnection();

				// so by the time we are checked in, all marked-for-destruction statements should be closed.
				scacheWaitMarkPhysicalConnectionInUse( physicalConnection );
				connectionCustomizer.onCheckIn( physicalConnection, parentDataSourceIdentityToken );
				SQLWarnings.logAndClearWarnings( physicalConnection );

				if ( testConnectionOnCheckin || attemptResurrect)
				{
				    if ( Debug.DEBUG && logger.isLoggable( MLevel.FINER ) )
					finerLoggingTestPooledConnection( resc, "CHECKIN" );
				    else
					testPooledConnection( resc );
				}

			    }
			    catch (ClassCastException e)
			    {
				throw SqlUtils.toSQLException("Cannot use a ConnectionCustomizer with a non-c3p0 PooledConnection." +
							      " PooledConnection: " + resc +
							      "; ConnectionPoolDataSource: " + cpds.getClass().getName(), e);
			    }
			    finally
			    { scacheUnmarkPhysicalConnectionInUse(physicalConnection); }
			}
			else
			{
			    PooledConnection pc = (PooledConnection) resc;
			    Connection con = null;

			    try
			    {

				// so by the time we are checked in, all marked-for-destruction statements should be closed.
				scacheWaitMarkPooledConnectionInUse( pc );
				con = pc.getConnection();
				SQLWarnings.logAndClearWarnings(con);

				if ( testConnectionOnCheckin || attemptResurrect )
				{
				    if ( Debug.DEBUG && logger.isLoggable( MLevel.FINER ) )
					finerLoggingTestPooledConnection( resc, con, "CHECKIN" );
				    else
					testPooledConnection( resc, con );
				}

			    }
			    finally
			    {
				proxyToClose = con;
				scacheUnmarkPooledConnectionInUse( pc );
			    }
			}
		      }
                      finally
                      { internalUseLockManager.unlockInternalUse(resc); }

                      // if we haven't failed the test by throwing then...
                      if (Debug.DEBUG && logger.isLoggable(MLevel.FINE) && attemptResurrect)
                          logger.log(MLevel.FINE, "A resource that had previously experienced a Connection error has been successfully resurrected on checkin: " + resc);
		    }
		    finally
		    {
			// close any opened proxy Connection
			ConnectionUtils.attemptClose(proxyToClose);
		    }
                }

                public void refurbishIdleResource( Object resc ) throws Exception
                {
                    internalUseLockManager.lockInternalUse(resc);
                    try
		    {
			PooledConnection pc = (PooledConnection) resc;
			try
			{
			    scacheWaitMarkPooledConnectionInUse( pc );
			    if ( Debug.DEBUG && logger.isLoggable( MLevel.FINER ) )
				finerLoggingTestPooledConnection( resc, "IDLE CHECK" );
			    else
				testPooledConnection( resc );
			}
			finally
			{ scacheUnmarkPooledConnectionInUse( pc ); }
		    }
                    finally
                    { internalUseLockManager.unlockInternalUse(resc); }
                }

                private void finerLoggingTestPooledConnection(Object resc, String testImpetus) throws Exception
		{ finerLoggingTestPooledConnection( resc, null, testImpetus); }


                private void finerLoggingTestPooledConnection(Object resc, Connection proxyConn, String testImpetus) throws Exception
                {
                    logger.finer("Testing PooledConnection [" + resc + "] on " + testImpetus + ".");
                    try
                    {
                        testPooledConnection( resc, proxyConn );
                        logger.finer("Test of PooledConnection [" + resc + "] on " + testImpetus + " has SUCCEEDED.");
                    }
                    catch (Exception e)
                    {
                        logger.log(MLevel.FINER, "Test of PooledConnection [" + resc + "] on "+testImpetus+" has FAILED.", e);
                        e.fillInStackTrace();
                        throw e;
                    }
                }

                // connections should be marked in use prior to any test
                // and unmarked in some finally after the test and other operations complete
                private void testPooledConnection(Object resc) throws Exception
		{ testPooledConnection( resc, null ); }

                // connections should be marked in use prior to any test
                // and unmarked in some finally after the test and other operations complete
                private void testPooledConnection(Object resc, Connection proxyConn) throws Exception
                {
                    PooledConnection pc = (PooledConnection) resc;
		    assert !Boolean.FALSE.equals(scachePooledConnectionInUse( pc )); //null or true are okay

                    connectionTestPath.testPooledConnection( pc, proxyConn );
                }

                public void destroyResource(Object resc, boolean checked_out) throws Exception
                {
                    try
		    {
                        internalUseLockManager.lockInternalUse(resc);
			try
			    {
				scacheWaitMarkPooledConnectionInUse((PooledConnection) resc);

				if ( connectionCustomizer != null )
				    {
					Connection physicalConnection = null;
					try
					    {
						physicalConnection =  ((AbstractC3P0PooledConnection) resc).getPhysicalConnection();

						connectionCustomizer.onDestroy( physicalConnection, parentDataSourceIdentityToken );
					    }
					catch (ClassCastException e)
					    {
						throw SqlUtils.toSQLException("Cannot use a ConnectionCustomizer with a non-c3p0 PooledConnection." +
									      " PooledConnection: " + resc +
									      "; ConnectionPoolDataSource: " + cpds.getClass().getName(), e);
					    }
					catch (Exception e)
					    {
						if (logger.isLoggable( MLevel.WARNING ))
						    logger.log( MLevel.WARNING,
								"An exception occurred while executing the onDestroy() method of " + connectionCustomizer +
								". c3p0 will attempt to destroy the target Connection regardless, but this issue " +
								" should be investigated and fixed.",
								e );
					    }
				    }

				if (Debug.DEBUG && Debug.TRACE == Debug.TRACE_MAX && logger.isLoggable( MLevel.FINER ))
				    logger.log( MLevel.FINER, "Preparing to destroy PooledConnection: " + resc);

				if (c3p0PooledConnections)
				    ((AbstractC3P0PooledConnection) resc).closeMaybeCheckedOut( checked_out );
				else
				    ((PooledConnection) resc).close();

				// inaccurate, as Connections can be removed more than once
				//connectionCounter.decrement();

				if (Debug.DEBUG && Debug.TRACE == Debug.TRACE_MAX && logger.isLoggable( MLevel.FINER ))
				    logger.log( MLevel.FINER,
						"Successfully destroyed PooledConnection: " + resc );
				//". Currently open Connections: " + connectionCounter.getValue() +
				//"; Failed close count: " + failedCloseCounter.getValue() +
				//"; Total processed by this pool: " + totalOpenedCounter.getValue());
			    }
			catch (Exception e)
			    {
				//failedCloseCounter.increment();

				if (Debug.DEBUG && Debug.TRACE == Debug.TRACE_MAX && logger.isLoggable( MLevel.FINER ))
				    logger.log( MLevel.FINER, "Failed to destroy PooledConnection: " + resc );
				//". Currently open Connections: " + connectionCounter.getValue() +
				//"; Failed close count: " + failedCloseCounter.getValue() +
				//"; Total processed by this pool: " + totalOpenedCounter.getValue());

				throw e;
			    }
			finally
			    { scacheUnmarkPooledConnectionInUse((PooledConnection) resc); }
		    }
                    finally
                    { internalUseLockManager.unlockInternalUse(resc); }
		}
            }

            PooledConnectionResourcePoolManager manager = new PooledConnectionResourcePoolManager();

            synchronized (fact)
            {
                fact.setMin( min );
                fact.setMax( max );
                fact.setStart( start );
                fact.setIncrement( inc );
                fact.setIdleResourceTestPeriod( idleConnectionTestPeriod * 1000);
                fact.setResourceMaxIdleTime( maxIdleTime * 1000 );
                fact.setExcessResourceMaxIdleTime( maxIdleTimeExcessConnections * 1000 );
                fact.setResourceMaxAge( maxConnectionAge * 1000 );
                fact.setExpirationEnforcementDelay( propertyCycle * 1000 );
                fact.setDestroyOverdueResourceTime( unreturnedConnectionTimeout * 1000 );
                fact.setDebugStoreCheckoutStackTrace( debugUnreturnedConnectionStackTraces );
                fact.setForceSynchronousCheckins( forceSynchronousCheckins );
                fact.setAcquisitionRetryAttempts( acq_retry_attempts );
                fact.setAcquisitionRetryDelay( acq_retry_delay );
                fact.setBreakOnAcquisitionFailure( break_after_acq_failure );
                this.rp = fact.createPool( manager );
            }

            manager.initAfterResourcePoolConstructed();
        }
        catch (ResourcePoolException e)
        { throw SqlUtils.toSQLException(e); }
    }

    public PooledConnection checkoutPooledConnection() throws SQLException
    {
        //System.err.println(this + " -- CHECKOUT");
        try
	    {
		PooledConnection pc = (PooledConnection) this.checkoutAndScacheMarkConnectionInUse();
		pc.addConnectionEventListener( cl );
		markBeginRequest(pc);
		return pc;
	    }
        catch (TimeoutException e)
        { throw SqlUtils.toSQLException("An attempt by a client to checkout a Connection has timed out.", e); }
        catch (CannotAcquireResourceException e)
        { throw SqlUtils.toSQLException("Connections could not be acquired from the underlying database!", "08001", e); }
        catch (Exception e)
        { throw SqlUtils.toSQLException(e); }
    }

    private void scacheWaitMarkPhysicalConnectionInUse(Connection physicalConnection) throws InterruptedException
    {
        if (scache != null)
            scache.waitMarkConnectionInUse(physicalConnection);
    }

    private boolean scacheTryMarkPhysicalConnectionInUse(Connection physicalConnection)
    { return (scache != null ? scache.tryMarkConnectionInUse(physicalConnection) : true); }

    private void scacheUnmarkPhysicalConnectionInUse(Connection physicalConnection)
    {
        if (scache != null)
            scache.unmarkConnectionInUse(physicalConnection);
    }

    private void scacheWaitMarkPooledConnectionInUse(PooledConnection pooledCon) throws InterruptedException
    {
	if (c3p0PooledConnections)
	    scacheWaitMarkPhysicalConnectionInUse(((AbstractC3P0PooledConnection) pooledCon).getPhysicalConnection());
    }

    private boolean scacheTryMarkPooledConnectionInUse(PooledConnection pooledCon)
    {
	if (c3p0PooledConnections)
	    return scacheTryMarkPhysicalConnectionInUse(((AbstractC3P0PooledConnection) pooledCon).getPhysicalConnection());
	else
	    return true;
    }

    private void scacheUnmarkPooledConnectionInUse(PooledConnection pooledCon)
    {
	if (c3p0PooledConnections)
	    scacheUnmarkPhysicalConnectionInUse(((AbstractC3P0PooledConnection) pooledCon).getPhysicalConnection());
    }

    private Boolean scachePhysicalConnectionInUse(Connection physicalConnection) throws InterruptedException
    {
        if (physicalConnection != null && scache != null)
            return scache.inUse(physicalConnection);
	else
	    return null;
    }

    private Boolean scachePooledConnectionInUse(PooledConnection pc) throws InterruptedException
    {
        if (pc != null && scache != null)
            return scache.inUse(((AbstractC3P0PooledConnection) pc).getPhysicalConnection());
	else
	    return null;
    }

    private Object checkoutAndScacheMarkConnectionInUse() throws TimeoutException, CannotAcquireResourceException, ResourcePoolException, InterruptedException
    {
        Object out = null;
	boolean success = false;
	while (! success)
	    {
		try
		    {
			out = rp.checkoutResource( checkoutTimeout );
			if (out instanceof AbstractC3P0PooledConnection)
			    {
				// cast should succeed, because scache != null implies c3p0 pooled Connections
				AbstractC3P0PooledConnection acpc = (AbstractC3P0PooledConnection) out;
				Connection physicalConnection = acpc.getPhysicalConnection();
				success = scacheTryMarkPhysicalConnectionInUse(physicalConnection);
			    }
			else
			    success = true; //we don't pool statements from non-c3p0 PooledConnections
		    }
		finally
		    {
			try { if (!success && out != null) rp.checkinResource( out );}
			catch (Exception e) { logger.log(MLevel.WARNING, "Failed to check in a Connection that was unusable due to pending Statement closes.", e); }
		    }
            }
        return out;
    }

    private void scacheUnmarkConnectionInUseAndCheckin(PooledConnection pcon) throws ResourcePoolException
    {
        if (scache != null)
        {
            try
            {
                // cast should generally succeed, because scache != null implies c3p0 pooled Connections
                // but clients can try to check-in whatever they want, so there are potential failures here
                AbstractC3P0PooledConnection acpc = (AbstractC3P0PooledConnection) pcon;
                Connection physicalConnection = acpc.getPhysicalConnection();
                scacheUnmarkPhysicalConnectionInUse(physicalConnection);
            }
            catch (ClassCastException e)
            {
                if (logger.isLoggable(MLevel.SEVERE))
                    logger.log(MLevel.SEVERE,
                               "You are checking a non-c3p0 PooledConnection implementation into" +
                               "a c3p0 PooledConnectionPool instance that expects only c3p0-generated PooledConnections." +
                               "This isn't good, and may indicate a c3p0 bug, or an unusual (and unspported) use " +
                               "of the c3p0 library.", e);
            }
       }
       rp.checkinResource(pcon);
    }

    private void checkinPooledConnection(PooledConnection pcon) throws SQLException
    {
        //System.err.println(this + " -- CHECKIN");
        try
	    {
		pcon.removeConnectionEventListener( cl );
		scacheUnmarkConnectionInUseAndCheckin( pcon );
		markEndRequest( pcon );
	    }
        catch (ResourcePoolException e)
        { throw SqlUtils.toSQLException(e); }
    }

    public float getEffectivePropertyCycle() throws SQLException
    {
        try
        { return rp.getEffectiveExpirationEnforcementDelay() / 1000f; }
        catch (ResourcePoolException e)
        { throw SqlUtils.toSQLException(e); }
    }

    public int getNumThreadsAwaitingCheckout() throws SQLException
    {
        try
        { return rp.getNumCheckoutWaiters(); }
        catch (ResourcePoolException e)
        { throw SqlUtils.toSQLException(e); }
    }

    public int getStatementCacheNumStatements()
    { return scache == null ? 0 : scache.getNumStatements(); }

    public int getStatementCacheNumCheckedOut()
    { return scache == null ? 0 : scache.getNumStatementsCheckedOut(); }

    public int getStatementCacheNumConnectionsWithCachedStatements()
    { return scache == null ? 0 : scache.getNumConnectionsWithCachedStatements(); }

    public String dumpStatementCacheStatus()
    { return scache == null ? "Statement caching disabled." : scache.dumpStatementCacheStatus(); }

    public void close() throws SQLException
    { close( true ); }

    public void close( boolean close_outstanding_connections ) throws SQLException
    {
        // System.err.println(this + " closing.");
        Exception throwMe = null;

        try { if (scache != null) scache.close(); }
        catch (SQLException e)
        { throwMe = e; }

        try
        { rp.close( close_outstanding_connections ); }
        catch (ResourcePoolException e)
        {
            if ( throwMe != null && logger.isLoggable( MLevel.WARNING ) )
                logger.log( MLevel.WARNING, "An Exception occurred while closing the StatementCache.", throwMe);
            throwMe = e;
        }

        if (throwMe != null)
            throw SqlUtils.toSQLException( throwMe );
    }

    class ConnectionEventListenerImpl implements ConnectionEventListener
    {

        //
        // We might want to check Connections in asynchronously,
        // because this is called
        // (indirectly) from a sync'ed method of NewPooledConnection, but
        // NewPooledConnection may be closed synchronously from a sync'ed
        // method of the resource pool, leading to a deadlock. Checking
        // Connections in asynchronously breaks the cycle.
        //
        // But then we want checkins to happen quickly and reliably,
        // whereas pool shutdowns are rare, so perhaps it's best to
        // leave this synchronous, and let the closing of pooled
        // resources on pool closes happen asynchronously to break
        // the deadlock.
        //
        // For now we're leaving both versions around, but with faster
        // and more reliable synchronous checkin enabled, and async closing
        // of resources in BasicResourcePool.close().
        //
        public void connectionClosed(final ConnectionEvent evt)
        {
            //System.err.println("Checking in: " + evt.getSource());

            if (ASYNCHRONOUS_CONNECTION_EVENT_LISTENER)
            {
                Runnable r = new Runnable()
                {
                    public void run()
                    { doCheckinResource( evt ); }
                };
                sharedTaskRunner.postRunnable( r );
            }
            else
                doCheckinResource( evt );
        }

        private void doCheckinResource(ConnectionEvent evt)
        {
            try
            {
		//rp.checkinResource( evt.getSource() );
		checkinPooledConnection( (PooledConnection) evt.getSource() );
	    }
            catch (Exception e)
            {
                //e.printStackTrace();
                logger.log( MLevel.WARNING,
                                "An Exception occurred while trying to check a PooledConection into a ResourcePool.",
                                e );
            }
        }

        //
        // We might want to update the pool asynchronously, because this is called
        // (indirectly) from a sync'ed method of NewPooledConnection, but
        // NewPooledConnection may be closed synchronously from a sync'ed
        // method of the resource pool, leading to a deadlock. Updating
        // pool status asynchronously breaks the cycle.
        //
        // But then we want checkins to happen quickly and reliably,
        // whereas pool shutdowns are rare, so perhaps it's best to
        // leave all ConnectionEvent handling synchronous, and let the closing of pooled
        // resources on pool closes happen asynchronously to break
        // the deadlock.
        //
        // For now we're leaving both versions around, but with faster
        // and more reliable synchrounous ConnectionEventHandling enabled, and async closing
        // of resources in BasicResourcePool.close().
        //
        public void connectionErrorOccurred(final ConnectionEvent evt)
        {
//          System.err.println("CONNECTION ERROR OCCURRED!");
//          System.err.println();
            if ( logger.isLoggable( MLevel.FINE ) )
                logger.fine("CONNECTION ERROR OCCURRED!");

            final PooledConnection pc = (PooledConnection) evt.getSource();
            int status;
            if (pc instanceof NewPooledConnection)
                status = ((NewPooledConnection) pc).getConnectionStatus();
            else //default to invalid connection, but not invalid database
                status = ConnectionTester.CONNECTION_IS_INVALID;

            final int final_status = status;

            if (ASYNCHRONOUS_CONNECTION_EVENT_LISTENER)
            {
                Runnable r = new Runnable()
                {
                    public void run()
                    { doMarkPoolStatus( pc, final_status ); }
                };
                sharedTaskRunner.postRunnable( r );
            }
            else
                doMarkPoolStatus( pc, final_status );
        }

        private void doMarkPoolStatus(PooledConnection pc, int status)
        {
            try
            {
                switch (status)
                {
                case ConnectionTester.CONNECTION_IS_OKAY:
                    throw new RuntimeException("connectionErrorOcccurred() should only be " +
                    "called for errors fatal to the Connection.");
                case ConnectionTester.CONNECTION_IS_INVALID:
                    if (resurrectables == null)
                        rp.markBroken( pc );
                    else
                        resurrectables.markResurrectable( pc );
                    break;
                case ConnectionTester.DATABASE_IS_INVALID:
                    if (logger.isLoggable(MLevel.WARNING))
                        logger.warning("A ConnectionTest has failed, reporting that all previously acquired Connections are likely invalid. " +
                        "The pool will be reset.");
                    rp.resetPool();
                    break;
                default:
                    throw new RuntimeException("Bad Connection Tester (" + connectionTester + ") " +
                                    "returned invalid status (" + status + ").");
                }
            }
            catch ( ResourcePoolException e )
            {
                //System.err.println("Uh oh... our resource pool is probably broken!");
                //e.printStackTrace();
                logger.log(MLevel.WARNING, "Uh oh... our resource pool is probably broken!", e);
            }
        }
    }

    public int getNumConnections() throws SQLException
    {
        try { return rp.getPoolSize(); }
        catch ( Exception e )
        {
            //e.printStackTrace();
            logger.log( MLevel.WARNING, null, e );
            throw SqlUtils.toSQLException( e );
        }
    }

    public int getNumIdleConnections() throws SQLException
    {
        try { return rp.getAvailableCount(); }
        catch ( Exception e )
        {
            //e.printStackTrace();
            logger.log( MLevel.WARNING, null, e );
            throw SqlUtils.toSQLException( e );
        }
    }

    public int getNumBusyConnections() throws SQLException
    {
        try
        { return rp.getAwaitingCheckinNotExcludedCount(); }
        catch ( Exception e )
        {
            //e.printStackTrace();
            logger.log( MLevel.WARNING, null, e );
            throw SqlUtils.toSQLException( e );
        }
    }

    public int getNumUnclosedOrphanedConnections() throws SQLException
    {
        try { return rp.getExcludedCount(); }
        catch ( Exception e )
        {
            //e.printStackTrace();
            logger.log( MLevel.WARNING, null, e );
            throw SqlUtils.toSQLException( e );
        }
    }

    public long getStartTime() throws SQLException
    {
        try { return rp.getStartTime(); }
        catch ( Exception e )
        {
            //e.printStackTrace();
            logger.log( MLevel.WARNING, null, e );
            throw SqlUtils.toSQLException( e );
        }
    }

    public long getUpTime() throws SQLException
    {
        try { return rp.getUpTime(); }
        catch ( Exception e )
        {
            //e.printStackTrace();
            logger.log( MLevel.WARNING, null, e );
            throw SqlUtils.toSQLException( e );
        }
    }

    public long getNumFailedCheckins() throws SQLException
    {
        try { return rp.getNumFailedCheckins(); }
        catch ( Exception e )
        {
            //e.printStackTrace();
            logger.log( MLevel.WARNING, null, e );
            throw SqlUtils.toSQLException( e );
        }
    }

    public long getNumFailedCheckouts() throws SQLException
    {
        try { return rp.getNumFailedCheckouts(); }
        catch ( Exception e )
        {
            //e.printStackTrace();
            logger.log( MLevel.WARNING, null, e );
            throw SqlUtils.toSQLException( e );
        }
    }

    public long getNumFailedIdleTests() throws SQLException
    {
        try { return rp.getNumFailedIdleTests(); }
        catch ( Exception e )
        {
            //e.printStackTrace();
            logger.log( MLevel.WARNING, null, e );
            throw SqlUtils.toSQLException( e );
        }
    }

    public Throwable getLastCheckinFailure() throws SQLException
    {
        try { return rp.getLastCheckinFailure(); }
        catch ( Exception e )
        {
            //e.printStackTrace();
            logger.log( MLevel.WARNING, null, e );
            throw SqlUtils.toSQLException( e );
        }
    }

    public Throwable getLastCheckoutFailure() throws SQLException
    {
        try { return rp.getLastCheckoutFailure(); }
        catch ( Exception e )
        {
            //e.printStackTrace();
            logger.log( MLevel.WARNING, null, e );
            throw SqlUtils.toSQLException( e );
        }
    }

    public Throwable getLastIdleTestFailure() throws SQLException
    {
        try { return rp.getLastIdleCheckFailure(); }
        catch ( Exception e )
        {
            //e.printStackTrace();
            logger.log( MLevel.WARNING, null, e );
            throw SqlUtils.toSQLException( e );
        }
    }

    public Throwable getLastConnectionTestFailure() throws SQLException
    {
        try { return rp.getLastResourceTestFailure(); }
        catch ( Exception e )
        {
            //e.printStackTrace();
            logger.log( MLevel.WARNING, null, e );
            throw SqlUtils.toSQLException( e );
        }
    }

    public Throwable getLastAcquisitionFailure() throws SQLException
    {
        try { return rp.getLastAcquisitionFailure(); }
        catch ( Exception e )
        {
            //e.printStackTrace();
            logger.log( MLevel.WARNING, null, e );
            throw SqlUtils.toSQLException( e );
        }
    }

    /**
     * Discards all Connections managed by the pool
     * and reacquires new Connections to populate.
     * Current checked out Connections will still
     * be valid, and should still be checked into the
     * pool (so the pool can destroy them).
     */
    public void reset() throws SQLException
    {
        try { rp.resetPool(); }
        catch ( Exception e )
        {
            //e.printStackTrace();
            logger.log( MLevel.WARNING, null, e );
            throw SqlUtils.toSQLException( e );
        }
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy