You are viewing a plain text version of this content. The canonical link for it is here.
Posted to dev@tomcat.apache.org by ma...@apache.org on 2019/05/03 18:41:47 UTC

[tomcat] 01/02: Align fork of Commons DBCP 2 with the 9.0.x copy excluding JDBC 4.2

This is an automated email from the ASF dual-hosted git repository.

markt pushed a commit to branch 8.5.x
in repository https://gitbox.apache.org/repos/asf/tomcat.git

commit f9efa75376e7f16689b62aae9b93e4d69cb9b625
Author: Mark Thomas <ma...@apache.org>
AuthorDate: Fri May 3 19:27:09 2019 +0100

    Align fork of Commons DBCP 2 with the 9.0.x copy excluding JDBC 4.2
---
 .../apache/tomcat/dbcp/dbcp2/BasicDataSource.java  | 3388 ++++++++++----------
 .../tomcat/dbcp/dbcp2/BasicDataSourceFactory.java  |  224 +-
 .../dbcp/dbcp2/DataSourceConnectionFactory.java    |   26 +-
 .../tomcat/dbcp/dbcp2/DelegatingConnection.java    |   12 +-
 .../dbcp/dbcp2/DelegatingDatabaseMetaData.java     |    6 +-
 .../dbcp/dbcp2/DelegatingPreparedStatement.java    |    2 +-
 .../tomcat/dbcp/dbcp2/DelegatingResultSet.java     |    8 +-
 .../tomcat/dbcp/dbcp2/DelegatingStatement.java     |    6 +-
 .../tomcat/dbcp/dbcp2/DriverConnectionFactory.java |   24 +
 .../dbcp/dbcp2/DriverManagerConnectionFactory.java |   62 +-
 .../org/apache/tomcat/dbcp/dbcp2/Jdbc41Bridge.java |  482 +++
 .../tomcat/dbcp/dbcp2/LocalStrings.properties      |    6 +-
 .../tomcat/dbcp/dbcp2/ObjectNameWrapper.java       |    9 +
 .../tomcat/dbcp/dbcp2/PoolableConnection.java      |   16 +
 .../dbcp/dbcp2/PoolableConnectionFactory.java      |  680 ++--
 .../tomcat/dbcp/dbcp2/PoolingConnection.java       |    2 +-
 .../tomcat/dbcp/dbcp2/PoolingDataSource.java       |    2 +-
 java/org/apache/tomcat/dbcp/dbcp2/Utils.java       |   68 +-
 .../dbcp/dbcp2/cpdsadapter/DriverAdapterCPDS.java  |   45 +
 .../dbcp2/cpdsadapter/PooledConnectionImpl.java    |    3 +-
 .../dbcp2/datasources/CPDSConnectionFactory.java   |   26 +
 .../dbcp2/datasources/InstanceKeyDataSource.java   |   75 +
 .../dbcp2/datasources/SharedPoolDataSource.java    |    7 +
 .../tomcat/dbcp/dbcp2/datasources/UserPassKey.java |    7 +-
 java/org/apache/tomcat/dbcp/dbcp2/overview.html    |   26 -
 webapps/docs/changelog.xml                         |    5 +
 26 files changed, 3055 insertions(+), 2162 deletions(-)

diff --git a/java/org/apache/tomcat/dbcp/dbcp2/BasicDataSource.java b/java/org/apache/tomcat/dbcp/dbcp2/BasicDataSource.java
index 7f07797..ba3e38a 100644
--- a/java/org/apache/tomcat/dbcp/dbcp2/BasicDataSource.java
+++ b/java/org/apache/tomcat/dbcp/dbcp2/BasicDataSource.java
@@ -63,8 +63,21 @@ import org.apache.tomcat.dbcp.pool2.impl.GenericObjectPoolConfig;
  */
 public class BasicDataSource implements DataSource, BasicDataSourceMXBean, MBeanRegistration, AutoCloseable {
 
+    /**
+     * @since 2.0
+     */
+    private class PaGetConnection implements PrivilegedExceptionAction<Connection> {
+
+        @Override
+        public Connection run() throws SQLException {
+            return createDataSource().getConnection();
+        }
+    }
+
     private static final Log log = LogFactory.getLog(BasicDataSource.class);
 
+    // ------------------------------------------------------------- Properties
+
     static {
         // Attempt to prevent deadlocks - see DBCP - 272
         DriverManager.getDrivers();
@@ -95,7 +108,22 @@ public class BasicDataSource implements DataSource, BasicDataSourceMXBean, MBean
         }
     }
 
-    // ------------------------------------------------------------- Properties
+    protected static void validateConnectionFactory(final PoolableConnectionFactory connectionFactory)
+            throws Exception {
+        PoolableConnection conn = null;
+        PooledObject<PoolableConnection> p = null;
+        try {
+            p = connectionFactory.makeObject();
+            conn = p.getObject();
+            connectionFactory.activateObject(p);
+            connectionFactory.validateConnection(conn);
+            connectionFactory.passivateObject(p);
+        } finally {
+            if (p != null) {
+                connectionFactory.destroyObject(p);
+            }
+        }
+    }
 
     /**
      * The default auto-commit state of connections created by this pool.
@@ -103,1756 +131,1503 @@ public class BasicDataSource implements DataSource, BasicDataSourceMXBean, MBean
     private volatile Boolean defaultAutoCommit;
 
     /**
-     * Returns the default auto-commit property.
-     *
-     * @return true if default auto-commit is enabled
+     * The default read-only state of connections created by this pool.
      */
-    @Override
-    public Boolean getDefaultAutoCommit() {
-        return defaultAutoCommit;
-    }
+    private transient Boolean defaultReadOnly;
 
     /**
-     * <p>
-     * Sets default auto-commit state of connections returned by this datasource.
-     * </p>
-     * <p>
-     * Note: this method currently has no effect once the pool has been initialized. The pool is initialized the first
-     * time one of the following methods is invoked: <code>getConnection, setLogwriter,
-     * setLoginTimeout, getLoginTimeout, getLogWriter.</code>
-     * </p>
-     *
-     * @param defaultAutoCommit
-     *            default auto-commit value
+     * The default TransactionIsolation state of connections created by this pool.
      */
-    public void setDefaultAutoCommit(final Boolean defaultAutoCommit) {
-        this.defaultAutoCommit = defaultAutoCommit;
-    }
+    private volatile int defaultTransactionIsolation = PoolableConnectionFactory.UNKNOWN_TRANSACTION_ISOLATION;
+
+    private Integer defaultQueryTimeoutSeconds;
 
     /**
-     * The default read-only state of connections created by this pool.
+     * The default "catalog" of connections created by this pool.
      */
-    private transient Boolean defaultReadOnly;
+    private volatile String defaultCatalog;
 
     /**
-     * Returns the default readOnly property.
-     *
-     * @return true if connections are readOnly by default
+     * The default "schema" of connections created by this pool.
      */
-    @Override
-    public Boolean getDefaultReadOnly() {
-        return defaultReadOnly;
-    }
+    private volatile String defaultSchema;
 
     /**
-     * <p>
-     * Sets defaultReadonly property.
-     * </p>
-     * <p>
-     * Note: this method currently has no effect once the pool has been initialized. The pool is initialized the first
-     * time one of the following methods is invoked: <code>getConnection, setLogwriter,
-     * setLoginTimeout, getLoginTimeout, getLogWriter.</code>
-     * </p>
-     *
-     * @param defaultReadOnly
-     *            default read-only value
+     * The property that controls if the pooled connections cache some state rather than query the database for current
+     * state to improve performance.
      */
-    public void setDefaultReadOnly(final Boolean defaultReadOnly) {
-        this.defaultReadOnly = defaultReadOnly;
-    }
+    private boolean cacheState = true;
 
     /**
-     * The default TransactionIsolation state of connections created by this pool.
+     * The instance of the JDBC Driver to use.
      */
-    private volatile int defaultTransactionIsolation = PoolableConnectionFactory.UNKNOWN_TRANSACTIONISOLATION;
+    private Driver driver;
 
     /**
-     * Returns the default transaction isolation state of returned connections.
-     *
-     * @return the default value for transaction isolation state
-     * @see Connection#getTransactionIsolation
+     * The fully qualified Java class name of the JDBC driver to be used.
      */
-    @Override
-    public int getDefaultTransactionIsolation() {
-        return this.defaultTransactionIsolation;
-    }
+    private String driverClassName;
 
     /**
-     * <p>
-     * Sets the default transaction isolation state for returned connections.
-     * </p>
-     * <p>
-     * Note: this method currently has no effect once the pool has been initialized. The pool is initialized the first
-     * time one of the following methods is invoked: <code>getConnection, setLogwriter,
-     * setLoginTimeout, getLoginTimeout, getLogWriter.</code>
-     * </p>
-     *
-     * @param defaultTransactionIsolation
-     *            the default transaction isolation state
-     * @see Connection#getTransactionIsolation
+     * The class loader instance to use to load the JDBC driver. If not specified, {@link Class#forName(String)} is used
+     * to load the JDBC driver. If specified, {@link Class#forName(String, boolean, ClassLoader)} is used.
      */
-    public void setDefaultTransactionIsolation(final int defaultTransactionIsolation) {
-        this.defaultTransactionIsolation = defaultTransactionIsolation;
-    }
+    private ClassLoader driverClassLoader;
 
-    private Integer defaultQueryTimeoutSeconds;
+    /**
+     * True means that borrowObject returns the most recently used ("last in") connection in the pool (if there are idle
+     * connections available). False means that the pool behaves as a FIFO queue - connections are taken from the idle
+     * instance pool in the order that they are returned to the pool.
+     */
+    private boolean lifo = BaseObjectPoolConfig.DEFAULT_LIFO;
 
     /**
-     * Gets the default query timeout that will be used for {@link java.sql.Statement Statement}s created from this
-     * connection. <code>null</code> means that the driver default will be used.
-     *
-     * @return The default query timeout in seconds.
+     * The maximum number of active connections that can be allocated from this pool at the same time, or negative for
+     * no limit.
      */
-    public Integer getDefaultQueryTimeout() {
-        return defaultQueryTimeoutSeconds;
-    }
+    private int maxTotal = GenericObjectPoolConfig.DEFAULT_MAX_TOTAL;
 
     /**
-     * Sets the default query timeout that will be used for {@link java.sql.Statement Statement}s created from this
-     * connection. <code>null</code> means that the driver default will be used.
-     *
-     * @param defaultQueryTimeoutSeconds
-     *            The default query timeout in seconds.
+     * The maximum number of connections that can remain idle in the pool, without extra ones being destroyed, or
+     * negative for no limit. If maxIdle is set too low on heavily loaded systems it is possible you will see
+     * connections being closed and almost immediately new connections being opened. This is a result of the active
+     * threads momentarily closing connections faster than they are opening them, causing the number of idle connections
+     * to rise above maxIdle. The best value for maxIdle for heavily loaded system will vary but the default is a good
+     * starting point.
      */
-    public void setDefaultQueryTimeout(final Integer defaultQueryTimeoutSeconds) {
-        this.defaultQueryTimeoutSeconds = defaultQueryTimeoutSeconds;
-    }
+    private int maxIdle = GenericObjectPoolConfig.DEFAULT_MAX_IDLE;
 
     /**
-     * The default "catalog" of connections created by this pool.
+     * The minimum number of active connections that can remain idle in the pool, without extra ones being created when
+     * the evictor runs, or 0 to create none. The pool attempts to ensure that minIdle connections are available when
+     * the idle object evictor runs. The value of this property has no effect unless
+     * {@link #timeBetweenEvictionRunsMillis} has a positive value.
      */
-    private volatile String defaultCatalog;
+    private int minIdle = GenericObjectPoolConfig.DEFAULT_MIN_IDLE;
 
     /**
-     * The default "schema" of connections created by this pool.
+     * The initial number of connections that are created when the pool is started.
      */
-    private volatile String defaultSchema;
+    private int initialSize = 0;
 
     /**
-     * Returns the default catalog.
-     *
-     * @return the default catalog
+     * The maximum number of milliseconds that the pool will wait (when there are no available connections) for a
+     * connection to be returned before throwing an exception, or <= 0 to wait indefinitely.
      */
-    @Override
-    public String getDefaultCatalog() {
-        return this.defaultCatalog;
-    }
+    private long maxWaitMillis = BaseObjectPoolConfig.DEFAULT_MAX_WAIT_MILLIS;
 
     /**
-     * Returns the default schema.
-     *
-     * @return the default schema.
-     * @since 2.5.0
+     * Prepared statement pooling for this pool. When this property is set to <code>true</code> both PreparedStatements
+     * and CallableStatements are pooled.
      */
-    @Override
-    public String getDefaultSchema() {
-        return this.defaultSchema;
-    }
+    private boolean poolPreparedStatements = false;
 
     /**
      * <p>
-     * Sets the default catalog.
+     * The maximum number of open statements that can be allocated from the statement pool at the same time, or negative
+     * for no limit. Since a connection usually only uses one or two statements at a time, this is mostly used to help
+     * detect resource leaks.
      * </p>
      * <p>
-     * Note: this method currently has no effect once the pool has been initialized. The pool is initialized the first
-     * time one of the following methods is invoked: <code>getConnection, setLogwriter,
-     * setLoginTimeout, getLoginTimeout, getLogWriter.</code>
+     * Note: As of version 1.3, CallableStatements (those produced by {@link Connection#prepareCall}) are pooled along
+     * with PreparedStatements (produced by {@link Connection#prepareStatement}) and
+     * <code>maxOpenPreparedStatements</code> limits the total number of prepared or callable statements that may be in
+     * use at a given time.
      * </p>
-     *
-     * @param defaultCatalog
-     *            the default catalog
      */
-    public void setDefaultCatalog(final String defaultCatalog) {
-        if (defaultCatalog != null && defaultCatalog.trim().length() > 0) {
-            this.defaultCatalog = defaultCatalog;
-        } else {
-            this.defaultCatalog = null;
-        }
-    }
+    private int maxOpenPreparedStatements = GenericKeyedObjectPoolConfig.DEFAULT_MAX_TOTAL;
 
     /**
-     * <p>
-     * Sets the default schema.
-     * </p>
-     * <p>
-     * Note: this method currently has no effect once the pool has been initialized. The pool is initialized the first
-     * time one of the following methods is invoked: <code>getConnection, setLogwriter,
-     * setLoginTimeout, getLoginTimeout, getLogWriter.</code>
-     * </p>
-     *
-     * @param defaultSchema
-     *            the default catalog
-     * @since 2.5.0
+     * The indication of whether objects will be validated as soon as they have been created by the pool. If the object
+     * fails to validate, the borrow operation that triggered the creation will fail.
      */
-    public void setDefaultSchema(final String defaultSchema) {
-        if (defaultSchema != null && defaultSchema.trim().length() > 0) {
-            this.defaultSchema = defaultSchema;
-        } else {
-            this.defaultSchema = null;
-        }
-    }
+    private boolean testOnCreate = false;
 
     /**
-     * The property that controls if the pooled connections cache some state rather than query the database for current
-     * state to improve performance.
+     * The indication of whether objects will be validated before being borrowed from the pool. If the object fails to
+     * validate, it will be dropped from the pool, and we will attempt to borrow another.
      */
-    private boolean cacheState = true;
+    private boolean testOnBorrow = true;
 
     /**
-     * Returns the state caching flag.
-     *
-     * @return the state caching flag
+     * The indication of whether objects will be validated before being returned to the pool.
      */
-    @Override
-    public boolean getCacheState() {
-        return cacheState;
-    }
+    private boolean testOnReturn = false;
 
     /**
-     * Sets the state caching flag.
-     *
-     * @param cacheState
-     *            The new value for the state caching flag
+     * The number of milliseconds to sleep between runs of the idle object evictor thread. When non-positive, no idle
+     * object evictor thread will be run.
      */
-    public void setCacheState(final boolean cacheState) {
-        this.cacheState = cacheState;
-    }
+    private long timeBetweenEvictionRunsMillis = BaseObjectPoolConfig.DEFAULT_TIME_BETWEEN_EVICTION_RUNS_MILLIS;
 
     /**
-     * The instance of the JDBC Driver to use.
+     * The number of objects to examine during each run of the idle object evictor thread (if any).
      */
-    private Driver driver;
+    private int numTestsPerEvictionRun = BaseObjectPoolConfig.DEFAULT_NUM_TESTS_PER_EVICTION_RUN;
 
     /**
-     * Returns the JDBC Driver that has been configured for use by this pool.
-     * <p>
-     * Note: This getter only returns the last value set by a call to {@link #setDriver(Driver)}. It does not return any
-     * driver instance that may have been created from the value set via {@link #setDriverClassName(String)}.
-     * </p>
-     *
-     * @return the JDBC Driver that has been configured for use by this pool
+     * The minimum amount of time an object may sit idle in the pool before it is eligible for eviction by the idle
+     * object evictor (if any).
      */
-    public synchronized Driver getDriver() {
-        return driver;
-    }
+    private long minEvictableIdleTimeMillis = BaseObjectPoolConfig.DEFAULT_MIN_EVICTABLE_IDLE_TIME_MILLIS;
 
     /**
-     * Sets the JDBC Driver instance to use for this pool.
-     * <p>
-     * Note: this method currently has no effect once the pool has been initialized. The pool is initialized the first
-     * time one of the following methods is invoked: <code>getConnection, setLogwriter,
-     * setLoginTimeout, getLoginTimeout, getLogWriter.</code>
-     * </p>
-     *
-     * @param driver
-     *            The JDBC Driver instance to use for this pool.
+     * The minimum amount of time a connection may sit idle in the pool before it is eligible for eviction by the idle
+     * object evictor, with the extra condition that at least "minIdle" connections remain in the pool. Note that
+     * {@code minEvictableIdleTimeMillis} takes precedence over this parameter. See
+     * {@link #getSoftMinEvictableIdleTimeMillis()}.
      */
-    public synchronized void setDriver(final Driver driver) {
-        this.driver = driver;
-    }
+    private long softMinEvictableIdleTimeMillis = BaseObjectPoolConfig.DEFAULT_SOFT_MIN_EVICTABLE_IDLE_TIME_MILLIS;
+
+    private String evictionPolicyClassName = BaseObjectPoolConfig.DEFAULT_EVICTION_POLICY_CLASS_NAME;
 
     /**
-     * The fully qualified Java class name of the JDBC driver to be used.
+     * The indication of whether objects will be validated by the idle object evictor (if any). If an object fails to
+     * validate, it will be dropped from the pool.
      */
-    private String driverClassName;
+    private boolean testWhileIdle = false;
 
     /**
-     * Returns the JDBC driver class name.
-     * <p>
-     * Note: This getter only returns the last value set by a call to {@link #setDriverClassName(String)}. It does not
-     * return the class name of any driver that may have been set via {@link #setDriver(Driver)}.
-     * </p>
-     *
-     * @return the JDBC driver class name
+     * The connection password to be passed to our JDBC driver to establish a connection.
      */
-    @Override
-    public synchronized String getDriverClassName() {
-        return this.driverClassName;
-    }
+    private volatile String password;
 
     /**
-     * <p>
-     * Sets the JDBC driver class name.
-     * </p>
-     * <p>
-     * Note: this method currently has no effect once the pool has been initialized. The pool is initialized the first
-     * time one of the following methods is invoked: <code>getConnection, setLogwriter,
-     * setLoginTimeout, getLoginTimeout, getLogWriter.</code>
-     * </p>
-     *
-     * @param driverClassName
-     *            the class name of the JDBC driver
+     * The connection URL to be passed to our JDBC driver to establish a connection.
      */
-    public synchronized void setDriverClassName(final String driverClassName) {
-        if (driverClassName != null && driverClassName.trim().length() > 0) {
-            this.driverClassName = driverClassName;
-        } else {
-            this.driverClassName = null;
-        }
-    }
+    private String url;
 
     /**
-     * The class loader instance to use to load the JDBC driver. If not specified, {@link Class#forName(String)} is used
-     * to load the JDBC driver. If specified, {@link Class#forName(String, boolean, ClassLoader)} is used.
+     * The connection user name to be passed to our JDBC driver to establish a connection.
      */
-    private ClassLoader driverClassLoader;
+    private String userName;
 
     /**
-     * Returns the class loader specified for loading the JDBC driver. Returns <code>null</code> if no class loader has
-     * been explicitly specified.
-     * <p>
-     * Note: This getter only returns the last value set by a call to {@link #setDriverClassLoader(ClassLoader)}. It
-     * does not return the class loader of any driver that may have been set via {@link #setDriver(Driver)}.
-     * </p>
-     *
-     * @return The class loader specified for loading the JDBC driver.
+     * The SQL query that will be used to validate connections from this pool before returning them to the caller. If
+     * specified, this query <strong>MUST</strong> be an SQL SELECT statement that returns at least one row. If not
+     * specified, {@link Connection#isValid(int)} will be used to validate connections.
      */
-    public synchronized ClassLoader getDriverClassLoader() {
-        return this.driverClassLoader;
-    }
+    private volatile String validationQuery;
 
     /**
+     * Timeout in seconds before connection validation queries fail.
+     */
+    private volatile int validationQueryTimeoutSeconds = -1;
+
+    /**
+     * These SQL statements run once after a Connection is created.
      * <p>
-     * Sets the class loader to be used to load the JDBC driver.
-     * </p>
-     * <p>
-     * Note: this method currently has no effect once the pool has been initialized. The pool is initialized the first
-     * time one of the following methods is invoked: <code>getConnection, setLogwriter,
-     * setLoginTimeout, getLoginTimeout, getLogWriter.</code>
+     * This property can be used for example to run ALTER SESSION SET NLS_SORT=XCYECH in an Oracle Database only once
+     * after connection creation.
      * </p>
-     *
-     * @param driverClassLoader
-     *            the class loader with which to load the JDBC driver
      */
-    public synchronized void setDriverClassLoader(final ClassLoader driverClassLoader) {
-        this.driverClassLoader = driverClassLoader;
-    }
+    private volatile List<String> connectionInitSqls;
 
     /**
-     * True means that borrowObject returns the most recently used ("last in") connection in the pool (if there are idle
-     * connections available). False means that the pool behaves as a FIFO queue - connections are taken from the idle
-     * instance pool in the order that they are returned to the pool.
+     * Controls access to the underlying connection.
      */
-    private boolean lifo = BaseObjectPoolConfig.DEFAULT_LIFO;
+    private boolean accessToUnderlyingConnectionAllowed = false;
+
+    private long maxConnLifetimeMillis = -1;
+
+    private boolean logExpiredConnections = true;
+
+    private String jmxName;
+
+    private boolean autoCommitOnReturn = true;
+
+    private boolean rollbackOnReturn = true;
+
+    private volatile Set<String> disconnectionSqlCodes;
+
+    private boolean fastFailValidation;
 
     /**
-     * Returns the LIFO property.
-     *
-     * @return true if connection pool behaves as a LIFO queue.
+     * The object pool that internally manages our connections.
      */
-    @Override
-    public synchronized boolean getLifo() {
-        return this.lifo;
-    }
+    private volatile GenericObjectPool<PoolableConnection> connectionPool;
 
     /**
-     * Sets the LIFO property. True means the pool behaves as a LIFO queue; false means FIFO.
-     *
-     * @param lifo
-     *            the new value for the LIFO property
+     * The connection properties that will be sent to our JDBC driver when establishing new connections.
+     * <strong>NOTE</strong> - The "user" and "password" properties will be passed explicitly, so they do not need to be
+     * included here.
      */
-    public synchronized void setLifo(final boolean lifo) {
-        this.lifo = lifo;
-        if (connectionPool != null) {
-            connectionPool.setLifo(lifo);
-        }
-    }
+    private Properties connectionProperties = new Properties();
 
     /**
-     * The maximum number of active connections that can be allocated from this pool at the same time, or negative for
-     * no limit.
+     * The data source we will use to manage connections. This object should be acquired <strong>ONLY</strong> by calls
+     * to the <code>createDataSource()</code> method.
      */
-    private int maxTotal = GenericObjectPoolConfig.DEFAULT_MAX_TOTAL;
+    private volatile DataSource dataSource;
 
     /**
-     * <p>
-     * Returns the maximum number of active connections that can be allocated at the same time.
-     * </p>
-     * <p>
-     * A negative number means that there is no limit.
-     * </p>
-     *
-     * @return the maximum number of active connections
+     * The PrintWriter to which log messages should be directed.
      */
-    @Override
-    public synchronized int getMaxTotal() {
-        return this.maxTotal;
-    }
+    private volatile PrintWriter logWriter = new PrintWriter(
+            new OutputStreamWriter(System.out, StandardCharsets.UTF_8));
+
+    private AbandonedConfig abandonedConfig;
+
+    private boolean closed;
 
     /**
-     * Sets the maximum total number of idle and borrows connections that can be active at the same time. Use a negative
-     * value for no limit.
-     *
-     * @param maxTotal
-     *            the new value for maxTotal
-     * @see #getMaxTotal()
+     * Actual name under which this component has been registered.
      */
-    public synchronized void setMaxTotal(final int maxTotal) {
-        this.maxTotal = maxTotal;
-        if (connectionPool != null) {
-            connectionPool.setMaxTotal(maxTotal);
-        }
-    }
+    private ObjectNameWrapper registeredJmxObjectName;
 
     /**
-     * The maximum number of connections that can remain idle in the pool, without extra ones being destroyed, or
-     * negative for no limit. If maxIdle is set too low on heavily loaded systems it is possible you will see
-     * connections being closed and almost immediately new connections being opened. This is a result of the active
-     * threads momentarily closing connections faster than they are opening them, causing the number of idle connections
-     * to rise above maxIdle. The best value for maxIdle for heavily loaded system will vary but the default is a good
-     * starting point.
+     * Adds a custom connection property to the set that will be passed to our JDBC driver. This <strong>MUST</strong>
+     * be called before the first connection is retrieved (along with all the other configuration property setters).
+     * Calls to this method after the connection pool has been initialized have no effect.
+     *
+     * @param name
+     *            Name of the custom connection property
+     * @param value
+     *            Value of the custom connection property
      */
-    private int maxIdle = GenericObjectPoolConfig.DEFAULT_MAX_IDLE;
+    public void addConnectionProperty(final String name, final String value) {
+        connectionProperties.put(name, value);
+    }
 
     /**
      * <p>
-     * Returns the maximum number of connections that can remain idle in the pool. Excess idle connections are destroyed
-     * on return to the pool.
+     * Closes and releases all idle connections that are currently stored in the connection pool associated with this
+     * data source.
      * </p>
      * <p>
-     * A negative value indicates that there is no limit
+     * Connections that are checked out to clients when this method is invoked are not affected. When client
+     * applications subsequently invoke {@link Connection#close()} to return these connections to the pool, the
+     * underlying JDBC connections are closed.
+     * </p>
+     * <p>
+     * Attempts to acquire connections using {@link #getConnection()} after this method has been invoked result in
+     * SQLExceptions.
+     * </p>
+     * <p>
+     * This method is idempotent - i.e., closing an already closed BasicDataSource has no effect and does not generate
+     * exceptions.
      * </p>
      *
-     * @return the maximum number of idle connections
+     * @throws SQLException
+     *             if an error occurs closing idle connections
      */
     @Override
-    public synchronized int getMaxIdle() {
-        return this.maxIdle;
+    public synchronized void close() throws SQLException {
+        if (registeredJmxObjectName != null) {
+            registeredJmxObjectName.unregisterMBean();
+            registeredJmxObjectName = null;
+        }
+        closed = true;
+        final GenericObjectPool<?> oldPool = connectionPool;
+        connectionPool = null;
+        dataSource = null;
+        try {
+            if (oldPool != null) {
+                oldPool.close();
+            }
+        } catch (final RuntimeException e) {
+            throw e;
+        } catch (final Exception e) {
+            throw new SQLException(Utils.getMessage("pool.close.fail"), e);
+        }
     }
 
     /**
-     * Sets the maximum number of connections that can remain idle in the pool. Excess idle connections are destroyed on
-     * return to the pool.
-     *
-     * @see #getMaxIdle()
-     * @param maxIdle
-     *            the new value for maxIdle
+     * Closes the connection pool, silently swallowing any exception that occurs.
      */
-    public synchronized void setMaxIdle(final int maxIdle) {
-        this.maxIdle = maxIdle;
-        if (connectionPool != null) {
-            connectionPool.setMaxIdle(maxIdle);
-        }
-    }
-
-    /**
-     * The minimum number of active connections that can remain idle in the pool, without extra ones being created when
-     * the evictor runs, or 0 to create none. The pool attempts to ensure that minIdle connections are available when
-     * the idle object evictor runs. The value of this property has no effect unless
-     * {@link #timeBetweenEvictionRunsMillis} has a positive value.
-     */
-    private int minIdle = GenericObjectPoolConfig.DEFAULT_MIN_IDLE;
-
-    /**
-     * Returns the minimum number of idle connections in the pool. The pool attempts to ensure that minIdle connections
-     * are available when the idle object evictor runs. The value of this property has no effect unless
-     * {@link #timeBetweenEvictionRunsMillis} has a positive value.
-     *
-     * @return the minimum number of idle connections
-     * @see GenericObjectPool#getMinIdle()
-     */
-    @Override
-    public synchronized int getMinIdle() {
-        return this.minIdle;
-    }
-
-    /**
-     * Sets the minimum number of idle connections in the pool. The pool attempts to ensure that minIdle connections are
-     * available when the idle object evictor runs. The value of this property has no effect unless
-     * {@link #timeBetweenEvictionRunsMillis} has a positive value.
-     *
-     * @param minIdle
-     *            the new value for minIdle
-     * @see GenericObjectPool#setMinIdle(int)
-     */
-    public synchronized void setMinIdle(final int minIdle) {
-        this.minIdle = minIdle;
-        if (connectionPool != null) {
-            connectionPool.setMinIdle(minIdle);
+    private void closeConnectionPool() {
+        final GenericObjectPool<?> oldPool = connectionPool;
+        connectionPool = null;
+        try {
+            if (oldPool != null) {
+                oldPool.close();
+            }
+        } catch (final Exception e) {
+            /* Ignore */
         }
     }
 
     /**
-     * The initial number of connections that are created when the pool is started.
-     */
-    private int initialSize = 0;
-
-    /**
-     * Returns the initial size of the connection pool.
+     * Creates a JDBC connection factory for this datasource. The JDBC driver is loaded using the following algorithm:
+     * <ol>
+     * <li>If a Driver instance has been specified via {@link #setDriver(Driver)} use it</li>
+     * <li>If no Driver instance was specified and {@link #driverClassName} is specified that class is loaded using the
+     * {@link ClassLoader} of this class or, if {@link #driverClassLoader} is set, {@link #driverClassName} is loaded
+     * with the specified {@link ClassLoader}.</li>
+     * <li>If {@link #driverClassName} is specified and the previous attempt fails, the class is loaded using the
+     * context class loader of the current thread.</li>
+     * <li>If a driver still isn't loaded one is loaded via the {@link DriverManager} using the specified {@link #url}.
+     * </ol>
+     * This method exists so subclasses can replace the implementation class.
      *
-     * @return the number of connections created when the pool is initialized
-     */
-    @Override
-    public synchronized int getInitialSize() {
-        return this.initialSize;
-    }
-
-    /**
-     * <p>
-     * Sets the initial size of the connection pool.
-     * </p>
-     * <p>
-     * Note: this method currently has no effect once the pool has been initialized. The pool is initialized the first
-     * time one of the following methods is invoked: <code>getConnection, setLogwriter,
-     * setLoginTimeout, getLoginTimeout, getLogWriter.</code>
-     * </p>
+     * @return A new connection factory.
      *
-     * @param initialSize
-     *            the number of connections created when the pool is initialized
+     * @throws SQLException
+     *            If the connection factort cannot be created
      */
-    public synchronized void setInitialSize(final int initialSize) {
-        this.initialSize = initialSize;
-    }
+    protected ConnectionFactory createConnectionFactory() throws SQLException {
+        // Load the JDBC driver class
+        Driver driverToUse = this.driver;
 
-    /**
-     * The maximum number of milliseconds that the pool will wait (when there are no available connections) for a
-     * connection to be returned before throwing an exception, or <= 0 to wait indefinitely.
-     */
-    private long maxWaitMillis = BaseObjectPoolConfig.DEFAULT_MAX_WAIT_MILLIS;
+        if (driverToUse == null) {
+            Class<?> driverFromCCL = null;
+            if (driverClassName != null) {
+                try {
+                    try {
+                        if (driverClassLoader == null) {
+                            driverFromCCL = Class.forName(driverClassName);
+                        } else {
+                            driverFromCCL = Class.forName(driverClassName, true, driverClassLoader);
+                        }
+                    } catch (final ClassNotFoundException cnfe) {
+                        driverFromCCL = Thread.currentThread().getContextClassLoader().loadClass(driverClassName);
+                    }
+                } catch (final Exception t) {
+                    final String message = "Cannot load JDBC driver class '" + driverClassName + "'";
+                    logWriter.println(message);
+                    t.printStackTrace(logWriter);
+                    throw new SQLException(message, t);
+                }
+            }
 
-    /**
-     * Returns the maximum number of milliseconds that the pool will wait for a connection to be returned before
-     * throwing an exception. A value less than or equal to zero means the pool is set to wait indefinitely.
-     *
-     * @return the maxWaitMillis property value
-     */
-    @Override
-    public synchronized long getMaxWaitMillis() {
-        return this.maxWaitMillis;
-    }
+            try {
+                if (driverFromCCL == null) {
+                    driverToUse = DriverManager.getDriver(url);
+                } else {
+                    // Usage of DriverManager is not possible, as it does not
+                    // respect the ContextClassLoader
+                    // N.B. This cast may cause ClassCastException which is handled below
+                    driverToUse = (Driver) driverFromCCL.getConstructor().newInstance();
+                    if (!driverToUse.acceptsURL(url)) {
+                        throw new SQLException("No suitable driver", "08001");
+                    }
+                }
+            } catch (final Exception t) {
+                final String message = "Cannot create JDBC driver of class '"
+                        + (driverClassName != null ? driverClassName : "") + "' for connect URL '" + url + "'";
+                logWriter.println(message);
+                t.printStackTrace(logWriter);
+                throw new SQLException(message, t);
+            }
+        }
 
-    /**
-     * Sets the MaxWaitMillis property. Use -1 to make the pool wait indefinitely.
-     *
-     * @param maxWaitMillis
-     *            the new value for MaxWaitMillis
-     * @see #getMaxWaitMillis()
-     */
-    public synchronized void setMaxWaitMillis(final long maxWaitMillis) {
-        this.maxWaitMillis = maxWaitMillis;
-        if (connectionPool != null) {
-            connectionPool.setMaxWaitMillis(maxWaitMillis);
+        // Set up the driver connection factory we will use
+        final String user = userName;
+        if (user != null) {
+            connectionProperties.put("user", user);
+        } else {
+            log("DBCP DataSource configured without a 'username'");
         }
-    }
 
-    /**
-     * Prepared statement pooling for this pool. When this property is set to <code>true</code> both PreparedStatements
-     * and CallableStatements are pooled.
-     */
-    private boolean poolPreparedStatements = false;
+        final String pwd = password;
+        if (pwd != null) {
+            connectionProperties.put("password", pwd);
+        } else {
+            log("DBCP DataSource configured without a 'password'");
+        }
 
-    /**
-     * Returns true if we are pooling statements.
-     *
-     * @return true if prepared and callable statements are pooled
-     */
-    @Override
-    public synchronized boolean isPoolPreparedStatements() {
-        return this.poolPreparedStatements;
+        final ConnectionFactory driverConnectionFactory = new DriverConnectionFactory(driverToUse, url,
+                connectionProperties);
+        return driverConnectionFactory;
     }
 
     /**
+     * Creates a connection pool for this datasource. This method only exists so subclasses can replace the
+     * implementation class.
      * <p>
-     * Sets whether to pool statements or not.
-     * </p>
-     * <p>
-     * Note: this method currently has no effect once the pool has been initialized. The pool is initialized the first
-     * time one of the following methods is invoked: <code>getConnection, setLogwriter,
-     * setLoginTimeout, getLoginTimeout, getLogWriter.</code>
+     * This implementation configures all pool properties other than timeBetweenEvictionRunsMillis. Setting that
+     * property is deferred to {@link #startPoolMaintenance()}, since setting timeBetweenEvictionRunsMillis to a
+     * positive value causes {@link GenericObjectPool}'s eviction timer to be started.
      * </p>
      *
-     * @param poolingStatements
-     *            pooling on or off
+     * @param factory
+     *            The factory to use to create new connections for this pool.
      */
-    public synchronized void setPoolPreparedStatements(final boolean poolingStatements) {
-        this.poolPreparedStatements = poolingStatements;
+    protected void createConnectionPool(final PoolableConnectionFactory factory) {
+        // Create an object pool to contain our active connections
+        final GenericObjectPoolConfig<PoolableConnection> config = new GenericObjectPoolConfig<>();
+        updateJmxName(config);
+        // Disable JMX on the underlying pool if the DS is not registered:
+        config.setJmxEnabled(registeredJmxObjectName != null);
+        final GenericObjectPool<PoolableConnection> gop = createObjectPool(factory, config, abandonedConfig);
+        gop.setMaxTotal(maxTotal);
+        gop.setMaxIdle(maxIdle);
+        gop.setMinIdle(minIdle);
+        gop.setMaxWaitMillis(maxWaitMillis);
+        gop.setTestOnCreate(testOnCreate);
+        gop.setTestOnBorrow(testOnBorrow);
+        gop.setTestOnReturn(testOnReturn);
+        gop.setNumTestsPerEvictionRun(numTestsPerEvictionRun);
+        gop.setMinEvictableIdleTimeMillis(minEvictableIdleTimeMillis);
+        gop.setSoftMinEvictableIdleTimeMillis(softMinEvictableIdleTimeMillis);
+        gop.setTestWhileIdle(testWhileIdle);
+        gop.setLifo(lifo);
+        gop.setSwallowedExceptionListener(new SwallowedExceptionLogger(log, logExpiredConnections));
+        gop.setEvictionPolicyClassName(evictionPolicyClassName);
+        factory.setPool(gop);
+        connectionPool = gop;
     }
 
     /**
      * <p>
-     * The maximum number of open statements that can be allocated from the statement pool at the same time, or negative
-     * for no limit. Since a connection usually only uses one or two statements at a time, this is mostly used to help
-     * detect resource leaks.
-     * </p>
-     * <p>
-     * Note: As of version 1.3, CallableStatements (those produced by {@link Connection#prepareCall}) are pooled along
-     * with PreparedStatements (produced by {@link Connection#prepareStatement}) and
-     * <code>maxOpenPreparedStatements</code> limits the total number of prepared or callable statements that may be in
-     * use at a given time.
+     * Creates (if necessary) and return the internal data source we are using to manage our connections.
      * </p>
-     */
-    private int maxOpenPreparedStatements = GenericKeyedObjectPoolConfig.DEFAULT_MAX_TOTAL;
-
-    /**
-     * Gets the value of the <code>maxOpenPreparedStatements</code> property.
      *
-     * @return the maximum number of open statements
+     * @return The current internal DataSource or a newly created instance if it has not yet been created.
+     * @throws SQLException
+     *             if the object pool cannot be created.
      */
-    @Override
-    public synchronized int getMaxOpenPreparedStatements() {
-        return this.maxOpenPreparedStatements;
-    }
+    protected DataSource createDataSource() throws SQLException {
+        if (closed) {
+            throw new SQLException("Data source is closed");
+        }
 
-    /**
-     * <p>
-     * Sets the value of the <code>maxOpenPreparedStatements</code> property.
-     * </p>
-     * <p>
-     * Note: this method currently has no effect once the pool has been initialized. The pool is initialized the first
-     * time one of the following methods is invoked: <code>getConnection, setLogwriter,
-     * setLoginTimeout, getLoginTimeout, getLogWriter.</code>
-     * </p>
-     *
-     * @param maxOpenStatements
-     *            the new maximum number of prepared statements
-     */
-    public synchronized void setMaxOpenPreparedStatements(final int maxOpenStatements) {
-        this.maxOpenPreparedStatements = maxOpenStatements;
-    }
+        // Return the pool if we have already created it
+        // This is double-checked locking. This is safe since dataSource is
+        // volatile and the code is targeted at Java 5 onwards.
+        if (dataSource != null) {
+            return dataSource;
+        }
+        synchronized (this) {
+            if (dataSource != null) {
+                return dataSource;
+            }
 
-    /**
-     * The indication of whether objects will be validated as soon as they have been created by the pool. If the object
-     * fails to validate, the borrow operation that triggered the creation will fail.
-     */
-    private boolean testOnCreate = false;
+            jmxRegister();
 
-    /**
-     * Returns the {@link #testOnCreate} property.
-     *
-     * @return true if objects are validated immediately after they are created by the pool
-     * @see #testOnCreate
-     */
-    @Override
-    public synchronized boolean getTestOnCreate() {
-        return this.testOnCreate;
-    }
+            // create factory which returns raw physical connections
+            final ConnectionFactory driverConnectionFactory = createConnectionFactory();
 
-    /**
-     * Sets the {@link #testOnCreate} property. This property determines whether or not the pool will validate objects
-     * immediately after they are created by the pool
-     *
-     * @param testOnCreate
-     *            new value for testOnCreate property
-     */
-    public synchronized void setTestOnCreate(final boolean testOnCreate) {
-        this.testOnCreate = testOnCreate;
-        if (connectionPool != null) {
-            connectionPool.setTestOnCreate(testOnCreate);
+            // Set up the poolable connection factory
+            boolean success = false;
+            PoolableConnectionFactory poolableConnectionFactory;
+            try {
+                poolableConnectionFactory = createPoolableConnectionFactory(driverConnectionFactory);
+                poolableConnectionFactory.setPoolStatements(poolPreparedStatements);
+                poolableConnectionFactory.setMaxOpenPreparedStatements(maxOpenPreparedStatements);
+                success = true;
+            } catch (final SQLException se) {
+                throw se;
+            } catch (final RuntimeException rte) {
+                throw rte;
+            } catch (final Exception ex) {
+                throw new SQLException("Error creating connection factory", ex);
+            }
+
+            if (success) {
+                // create a pool for our connections
+                createConnectionPool(poolableConnectionFactory);
+            }
+
+            // Create the pooling data source to manage connections
+            DataSource newDataSource;
+            success = false;
+            try {
+                newDataSource = createDataSourceInstance();
+                newDataSource.setLogWriter(logWriter);
+                success = true;
+            } catch (final SQLException se) {
+                throw se;
+            } catch (final RuntimeException rte) {
+                throw rte;
+            } catch (final Exception ex) {
+                throw new SQLException("Error creating datasource", ex);
+            } finally {
+                if (!success) {
+                    closeConnectionPool();
+                }
+            }
+
+            // If initialSize > 0, preload the pool
+            try {
+                for (int i = 0; i < initialSize; i++) {
+                    connectionPool.addObject();
+                }
+            } catch (final Exception e) {
+                closeConnectionPool();
+                throw new SQLException("Error preloading the connection pool", e);
+            }
+
+            // If timeBetweenEvictionRunsMillis > 0, start the pool's evictor task
+            startPoolMaintenance();
+
+            dataSource = newDataSource;
+            return dataSource;
         }
     }
 
     /**
-     * The indication of whether objects will be validated before being borrowed from the pool. If the object fails to
-     * validate, it will be dropped from the pool, and we will attempt to borrow another.
-     */
-    private boolean testOnBorrow = true;
-
-    /**
-     * Returns the {@link #testOnBorrow} property.
+     * Creates the actual data source instance. This method only exists so that subclasses can replace the
+     * implementation class.
      *
-     * @return true if objects are validated before being borrowed from the pool
+     * @throws SQLException
+     *             if unable to create a datasource instance
      *
-     * @see #testOnBorrow
+     * @return A new DataSource instance
      */
-    @Override
-    public synchronized boolean getTestOnBorrow() {
-        return this.testOnBorrow;
+    protected DataSource createDataSourceInstance() throws SQLException {
+        final PoolingDataSource<PoolableConnection> pds = new PoolingDataSource<>(connectionPool);
+        pds.setAccessToUnderlyingConnectionAllowed(isAccessToUnderlyingConnectionAllowed());
+        return pds;
     }
 
     /**
-     * Sets the {@link #testOnBorrow} property. This property determines whether or not the pool will validate objects
-     * before they are borrowed from the pool.
+     * Creates an object pool used to provide pooling support for {@link Connection JDBC connections}.
      *
-     * @param testOnBorrow
-     *            new value for testOnBorrow property
+     * @param factory
+     *            the object factory
+     * @param poolConfig
+     *            the object pool configuration
+     * @param abandonedConfig
+     *            the abandoned objects configuration
+     * @return a non-null instance
      */
-    public synchronized void setTestOnBorrow(final boolean testOnBorrow) {
-        this.testOnBorrow = testOnBorrow;
-        if (connectionPool != null) {
-            connectionPool.setTestOnBorrow(testOnBorrow);
+    protected GenericObjectPool<PoolableConnection> createObjectPool(final PoolableConnectionFactory factory,
+            final GenericObjectPoolConfig<PoolableConnection> poolConfig, final AbandonedConfig abandonedConfig) {
+        GenericObjectPool<PoolableConnection> gop;
+        if (abandonedConfig != null && (abandonedConfig.getRemoveAbandonedOnBorrow()
+                || abandonedConfig.getRemoveAbandonedOnMaintenance())) {
+            gop = new GenericObjectPool<>(factory, poolConfig, abandonedConfig);
+        } else {
+            gop = new GenericObjectPool<>(factory, poolConfig);
         }
+        return gop;
     }
 
     /**
-     * The indication of whether objects will be validated before being returned to the pool.
+     * Creates the PoolableConnectionFactory and attaches it to the connection pool. This method only exists so
+     * subclasses can replace the default implementation.
+     *
+     * @param driverConnectionFactory
+     *            JDBC connection factory
+     * @throws SQLException
+     *             if an error occurs creating the PoolableConnectionFactory
+     *
+     * @return A new PoolableConnectionFactory configured with the current configuration of this BasicDataSource
      */
-    private boolean testOnReturn = false;
+    protected PoolableConnectionFactory createPoolableConnectionFactory(final ConnectionFactory driverConnectionFactory)
+            throws SQLException {
+        PoolableConnectionFactory connectionFactory = null;
+        try {
+            connectionFactory = new PoolableConnectionFactory(driverConnectionFactory,
+                    ObjectNameWrapper.unwrap(registeredJmxObjectName));
+            connectionFactory.setValidationQuery(validationQuery);
+            connectionFactory.setValidationQueryTimeout(validationQueryTimeoutSeconds);
+            connectionFactory.setConnectionInitSql(connectionInitSqls);
+            connectionFactory.setDefaultReadOnly(defaultReadOnly);
+            connectionFactory.setDefaultAutoCommit(defaultAutoCommit);
+            connectionFactory.setDefaultTransactionIsolation(defaultTransactionIsolation);
+            connectionFactory.setDefaultCatalog(defaultCatalog);
+            connectionFactory.setDefaultSchema(defaultSchema);
+            connectionFactory.setCacheState(cacheState);
+            connectionFactory.setPoolStatements(poolPreparedStatements);
+            connectionFactory.setMaxOpenPreparedStatements(maxOpenPreparedStatements);
+            connectionFactory.setMaxConnLifetimeMillis(maxConnLifetimeMillis);
+            connectionFactory.setRollbackOnReturn(getRollbackOnReturn());
+            connectionFactory.setAutoCommitOnReturn(getAutoCommitOnReturn());
+            connectionFactory.setDefaultQueryTimeout(getDefaultQueryTimeout());
+            connectionFactory.setFastFailValidation(fastFailValidation);
+            connectionFactory.setDisconnectionSqlCodes(disconnectionSqlCodes);
+            validateConnectionFactory(connectionFactory);
+        } catch (final RuntimeException e) {
+            throw e;
+        } catch (final Exception e) {
+            throw new SQLException("Cannot create PoolableConnectionFactory (" + e.getMessage() + ")", e);
+        }
+        return connectionFactory;
+    }
 
     /**
-     * Returns the value of the {@link #testOnReturn} property.
+     * Gets the print writer used by this configuration to log information on abandoned objects.
      *
-     * @return true if objects are validated before being returned to the pool
-     * @see #testOnReturn
+     * @return The print writer used by this configuration to log information on abandoned objects.
      */
-    public synchronized boolean getTestOnReturn() {
-        return this.testOnReturn;
+    public PrintWriter getAbandonedLogWriter() {
+        if (abandonedConfig != null) {
+            return abandonedConfig.getLogWriter();
+        }
+        return null;
     }
 
     /**
-     * Sets the <code>testOnReturn</code> property. This property determines whether or not the pool will validate
-     * objects before they are returned to the pool.
+     * If the connection pool implements {@link org.apache.tomcat.dbcp.pool2.UsageTracking UsageTracking}, should the
+     * connection pool record a stack trace every time a method is called on a pooled connection and retain the most
+     * recent stack trace to aid debugging of abandoned connections?
      *
-     * @param testOnReturn
-     *            new value for testOnReturn property
+     * @return <code>true</code> if usage tracking is enabled
      */
-    public synchronized void setTestOnReturn(final boolean testOnReturn) {
-        this.testOnReturn = testOnReturn;
-        if (connectionPool != null) {
-            connectionPool.setTestOnReturn(testOnReturn);
+    @Override
+    public boolean getAbandonedUsageTracking() {
+        if (abandonedConfig != null) {
+            return abandonedConfig.getUseUsageTracking();
         }
+        return false;
     }
 
     /**
-     * The number of milliseconds to sleep between runs of the idle object evictor thread. When non-positive, no idle
-     * object evictor thread will be run.
+     * Returns the value of the flag that controls whether or not connections being returned to the pool will be checked
+     * and configured with {@link Connection#setAutoCommit(boolean) Connection.setAutoCommit(true)} if the auto commit
+     * setting is {@code false} when the connection is returned. It is <code>true</code> by default.
+     *
+     * @return Whether or not connections being returned to the pool will be checked and configured with auto-commit.
      */
-    private long timeBetweenEvictionRunsMillis = BaseObjectPoolConfig.DEFAULT_TIME_BETWEEN_EVICTION_RUNS_MILLIS;
+    public boolean getAutoCommitOnReturn() {
+        return autoCommitOnReturn;
+    }
 
     /**
-     * Returns the value of the {@link #timeBetweenEvictionRunsMillis} property.
+     * Returns the state caching flag.
      *
-     * @return the time (in milliseconds) between evictor runs
-     * @see #timeBetweenEvictionRunsMillis
+     * @return the state caching flag
      */
     @Override
-    public synchronized long getTimeBetweenEvictionRunsMillis() {
-        return this.timeBetweenEvictionRunsMillis;
+    public boolean getCacheState() {
+        return cacheState;
     }
 
     /**
-     * Sets the {@link #timeBetweenEvictionRunsMillis} property.
+     * Creates (if necessary) and return a connection to the database.
      *
-     * @param timeBetweenEvictionRunsMillis
-     *            the new time between evictor runs
-     * @see #timeBetweenEvictionRunsMillis
+     * @throws SQLException
+     *             if a database access error occurs
+     * @return a database connection
      */
-    public synchronized void setTimeBetweenEvictionRunsMillis(final long timeBetweenEvictionRunsMillis) {
-        this.timeBetweenEvictionRunsMillis = timeBetweenEvictionRunsMillis;
-        if (connectionPool != null) {
-            connectionPool.setTimeBetweenEvictionRunsMillis(timeBetweenEvictionRunsMillis);
-        }
-    }
-
-    /**
-     * The number of objects to examine during each run of the idle object evictor thread (if any).
-     */
-    private int numTestsPerEvictionRun = BaseObjectPoolConfig.DEFAULT_NUM_TESTS_PER_EVICTION_RUN;
+    @Override
+    public Connection getConnection() throws SQLException {
+        if (Utils.IS_SECURITY_ENABLED) {
+            final PrivilegedExceptionAction<Connection> action = new PaGetConnection();
+            try {
+                return AccessController.doPrivileged(action);
+            } catch (final PrivilegedActionException e) {
+                final Throwable cause = e.getCause();
+                if (cause instanceof SQLException) {
+                    throw (SQLException) cause;
+                }
+                throw new SQLException(e);
+            }
+        }
+        return createDataSource().getConnection();
+    }
 
     /**
-     * Returns the value of the {@link #numTestsPerEvictionRun} property.
+     * <strong>BasicDataSource does NOT support this method.</strong>
      *
-     * @return the number of objects to examine during idle object evictor runs
-     * @see #numTestsPerEvictionRun
+     * @param user
+     *            Database user on whose behalf the Connection is being made
+     * @param pass
+     *            The database user's password
+     *
+     * @throws UnsupportedOperationException
+     *             always thrown.
+     * @throws SQLException
+     *             if a database access error occurs
+     * @return nothing - always throws UnsupportedOperationException
      */
     @Override
-    public synchronized int getNumTestsPerEvictionRun() {
-        return this.numTestsPerEvictionRun;
+    public Connection getConnection(final String user, final String pass) throws SQLException {
+        // This method isn't supported by the PoolingDataSource returned by the createDataSource
+        throw new UnsupportedOperationException("Not supported by BasicDataSource");
     }
 
     /**
-     * Sets the value of the {@link #numTestsPerEvictionRun} property.
+     * Returns the list of SQL statements executed when a physical connection is first created. Returns an empty list if
+     * there are no initialization statements configured.
      *
-     * @param numTestsPerEvictionRun
-     *            the new {@link #numTestsPerEvictionRun} value
-     * @see #numTestsPerEvictionRun
+     * @return initialization SQL statements
      */
-    public synchronized void setNumTestsPerEvictionRun(final int numTestsPerEvictionRun) {
-        this.numTestsPerEvictionRun = numTestsPerEvictionRun;
-        if (connectionPool != null) {
-            connectionPool.setNumTestsPerEvictionRun(numTestsPerEvictionRun);
+    public List<String> getConnectionInitSqls() {
+        final List<String> result = connectionInitSqls;
+        if (result == null) {
+            return Collections.emptyList();
         }
+        return result;
     }
 
     /**
-     * The minimum amount of time an object may sit idle in the pool before it is eligible for eviction by the idle
-     * object evictor (if any).
+     * Provides the same data as {@link #getConnectionInitSqls()} but in an array so it is accessible via JMX.
      */
-    private long minEvictableIdleTimeMillis = BaseObjectPoolConfig.DEFAULT_MIN_EVICTABLE_IDLE_TIME_MILLIS;
+    @Override
+    public String[] getConnectionInitSqlsAsArray() {
+        final Collection<String> result = getConnectionInitSqls();
+        return result.toArray(new String[result.size()]);
+    }
+
+    protected GenericObjectPool<PoolableConnection> getConnectionPool() {
+        return connectionPool;
+    }
+
+    // For unit testing
+    Properties getConnectionProperties() {
+        return connectionProperties;
+    }
 
     /**
-     * Returns the {@link #minEvictableIdleTimeMillis} property.
+     * Returns the default auto-commit property.
      *
-     * @return the value of the {@link #minEvictableIdleTimeMillis} property
-     * @see #minEvictableIdleTimeMillis
+     * @return true if default auto-commit is enabled
      */
     @Override
-    public synchronized long getMinEvictableIdleTimeMillis() {
-        return this.minEvictableIdleTimeMillis;
+    public Boolean getDefaultAutoCommit() {
+        return defaultAutoCommit;
     }
 
     /**
-     * Sets the {@link #minEvictableIdleTimeMillis} property.
+     * Returns the default catalog.
      *
-     * @param minEvictableIdleTimeMillis
-     *            the minimum amount of time an object may sit idle in the pool
-     * @see #minEvictableIdleTimeMillis
+     * @return the default catalog
      */
-    public synchronized void setMinEvictableIdleTimeMillis(final long minEvictableIdleTimeMillis) {
-        this.minEvictableIdleTimeMillis = minEvictableIdleTimeMillis;
-        if (connectionPool != null) {
-            connectionPool.setMinEvictableIdleTimeMillis(minEvictableIdleTimeMillis);
-        }
+    @Override
+    public String getDefaultCatalog() {
+        return this.defaultCatalog;
     }
 
     /**
-     * The minimum amount of time a connection may sit idle in the pool before it is eligible for eviction by the idle
-     * object evictor, with the extra condition that at least "minIdle" connections remain in the pool. Note that
-     * {@code minEvictableIdleTimeMillis} takes precedence over this parameter. See
-     * {@link #getSoftMinEvictableIdleTimeMillis()}.
+     * Gets the default query timeout that will be used for {@link java.sql.Statement Statement}s created from this
+     * connection. <code>null</code> means that the driver default will be used.
+     *
+     * @return The default query timeout in seconds.
      */
-    private long softMinEvictableIdleTimeMillis = BaseObjectPoolConfig.DEFAULT_SOFT_MIN_EVICTABLE_IDLE_TIME_MILLIS;
+    public Integer getDefaultQueryTimeout() {
+        return defaultQueryTimeoutSeconds;
+    }
 
     /**
-     * Sets the minimum amount of time a connection may sit idle in the pool before it is eligible for eviction by the
-     * idle object evictor, with the extra condition that at least "minIdle" connections remain in the pool.
+     * Returns the default readOnly property.
      *
-     * @param softMinEvictableIdleTimeMillis
-     *            minimum amount of time a connection may sit idle in the pool before it is eligible for eviction,
-     *            assuming there are minIdle idle connections in the pool.
-     * @see #getSoftMinEvictableIdleTimeMillis
+     * @return true if connections are readOnly by default
      */
-    public synchronized void setSoftMinEvictableIdleTimeMillis(final long softMinEvictableIdleTimeMillis) {
-        this.softMinEvictableIdleTimeMillis = softMinEvictableIdleTimeMillis;
-        if (connectionPool != null) {
-            connectionPool.setSoftMinEvictableIdleTimeMillis(softMinEvictableIdleTimeMillis);
-        }
+    @Override
+    public Boolean getDefaultReadOnly() {
+        return defaultReadOnly;
     }
 
     /**
-     * <p>
-     * Returns the minimum amount of time a connection may sit idle in the pool before it is eligible for eviction by
-     * the idle object evictor, with the extra condition that at least "minIdle" connections remain in the pool.
-     * </p>
-     *
-     * <p>
-     * When {@link #getMinEvictableIdleTimeMillis() minEvictableIdleTimeMillis} is set to a positive value,
-     * minEvictableIdleTimeMillis is examined first by the idle connection evictor - i.e. when idle connections are
-     * visited by the evictor, idle time is first compared against {@code minEvictableIdleTimeMillis} (without
-     * considering the number of idle connections in the pool) and then against {@code softMinEvictableIdleTimeMillis},
-     * including the {@code minIdle}, constraint.
-     * </p>
+     * Returns the default schema.
      *
-     * @return minimum amount of time a connection may sit idle in the pool before it is eligible for eviction, assuming
-     *         there are minIdle idle connections in the pool
+     * @return the default schema.
+     * @since 2.5.0
      */
     @Override
-    public synchronized long getSoftMinEvictableIdleTimeMillis() {
-        return softMinEvictableIdleTimeMillis;
+    public String getDefaultSchema() {
+        return this.defaultSchema;
     }
 
-    private String evictionPolicyClassName = BaseObjectPoolConfig.DEFAULT_EVICTION_POLICY_CLASS_NAME;
-
     /**
-     * Gets the EvictionPolicy implementation in use with this connection pool.
+     * Returns the default transaction isolation state of returned connections.
      *
-     * @return The EvictionPolicy implementation in use with this connection pool.
+     * @return the default value for transaction isolation state
+     * @see Connection#getTransactionIsolation
      */
-    public synchronized String getEvictionPolicyClassName() {
-        return evictionPolicyClassName;
+    @Override
+    public int getDefaultTransactionIsolation() {
+        return this.defaultTransactionIsolation;
     }
 
     /**
-     * Sets the EvictionPolicy implementation to use with this connection pool.
+     * Returns the set of SQL_STATE codes considered to signal fatal conditions.
      *
-     * @param evictionPolicyClassName
-     *            The fully qualified class name of the EvictionPolicy implementation
+     * @return fatal disconnection state codes
+     * @see #setDisconnectionSqlCodes(Collection)
+     * @since 2.1
      */
-    public synchronized void setEvictionPolicyClassName(final String evictionPolicyClassName) {
-        if (connectionPool != null) {
-            connectionPool.setEvictionPolicyClassName(evictionPolicyClassName);
+    public Set<String> getDisconnectionSqlCodes() {
+        final Set<String> result = disconnectionSqlCodes;
+        if (result == null) {
+            return Collections.emptySet();
         }
-        this.evictionPolicyClassName = evictionPolicyClassName;
+        return result;
     }
 
     /**
-     * The indication of whether objects will be validated by the idle object evictor (if any). If an object fails to
-     * validate, it will be dropped from the pool.
-     */
-    private boolean testWhileIdle = false;
-
-    /**
-     * Returns the value of the {@link #testWhileIdle} property.
+     * Provides the same data as {@link #getDisconnectionSqlCodes} but in an array so it is accessible via JMX.
      *
-     * @return true if objects examined by the idle object evictor are validated
-     * @see #testWhileIdle
+     * @since 2.1
      */
     @Override
-    public synchronized boolean getTestWhileIdle() {
-        return this.testWhileIdle;
+    public String[] getDisconnectionSqlCodesAsArray() {
+        final Collection<String> result = getDisconnectionSqlCodes();
+        return result.toArray(new String[result.size()]);
     }
 
     /**
-     * Sets the <code>testWhileIdle</code> property. This property determines whether or not the idle object evictor
-     * will validate connections.
+     * Returns the JDBC Driver that has been configured for use by this pool.
+     * <p>
+     * Note: This getter only returns the last value set by a call to {@link #setDriver(Driver)}. It does not return any
+     * driver instance that may have been created from the value set via {@link #setDriverClassName(String)}.
+     * </p>
      *
-     * @param testWhileIdle
-     *            new value for testWhileIdle property
+     * @return the JDBC Driver that has been configured for use by this pool
      */
-    public synchronized void setTestWhileIdle(final boolean testWhileIdle) {
-        this.testWhileIdle = testWhileIdle;
-        if (connectionPool != null) {
-            connectionPool.setTestWhileIdle(testWhileIdle);
-        }
+    public synchronized Driver getDriver() {
+        return driver;
     }
 
     /**
-     * [Read Only] The current number of active connections that have been allocated from this data source.
+     * Returns the class loader specified for loading the JDBC driver. Returns <code>null</code> if no class loader has
+     * been explicitly specified.
+     * <p>
+     * Note: This getter only returns the last value set by a call to {@link #setDriverClassLoader(ClassLoader)}. It
+     * does not return the class loader of any driver that may have been set via {@link #setDriver(Driver)}.
+     * </p>
      *
-     * @return the current number of active connections
+     * @return The class loader specified for loading the JDBC driver.
      */
-    @Override
-    public int getNumActive() {
-        // Copy reference to avoid NPE if close happens after null check
-        final GenericObjectPool<PoolableConnection> pool = connectionPool;
-        if (pool != null) {
-            return pool.getNumActive();
-        }
-        return 0;
+    public synchronized ClassLoader getDriverClassLoader() {
+        return this.driverClassLoader;
     }
 
     /**
-     * [Read Only] The current number of idle connections that are waiting to be allocated from this data source.
+     * Returns the JDBC driver class name.
+     * <p>
+     * Note: This getter only returns the last value set by a call to {@link #setDriverClassName(String)}. It does not
+     * return the class name of any driver that may have been set via {@link #setDriver(Driver)}.
+     * </p>
      *
-     * @return the current number of idle connections
+     * @return the JDBC driver class name
      */
     @Override
-    public int getNumIdle() {
-        // Copy reference to avoid NPE if close happens after null check
-        final GenericObjectPool<PoolableConnection> pool = connectionPool;
-        if (pool != null) {
-            return pool.getNumIdle();
-        }
-        return 0;
+    public synchronized String getDriverClassName() {
+        return this.driverClassName;
     }
 
     /**
-     * The connection password to be passed to our JDBC driver to establish a connection.
+     * Returns the value of the flag that controls whether or not connections being returned to the pool will be checked
+     * and configured with {@link Connection#setAutoCommit(boolean) Connection.setAutoCommit(true)} if the auto commit
+     * setting is {@code false} when the connection is returned. It is <code>true</code> by default.
+     *
+     * @return Whether or not connections being returned to the pool will be checked and configured with auto-commit.
+     * @deprecated Use {@link #getAutoCommitOnReturn()}.
      */
-    private volatile String password;
+    @Deprecated
+    public boolean getEnableAutoCommitOnReturn() {
+        return autoCommitOnReturn;
+    }
 
     /**
-     * Returns the password passed to the JDBC driver to establish connections.
+     * Gets the EvictionPolicy implementation in use with this connection pool.
      *
-     * @return the connection password
+     * @return The EvictionPolicy implementation in use with this connection pool.
+     */
+    public synchronized String getEvictionPolicyClassName() {
+        return evictionPolicyClassName;
+    }
+
+    /**
+     * True means that validation will fail immediately for connections that have previously thrown SQLExceptions with
+     * SQL_STATE indicating fatal disconnection errors.
+     *
+     * @return true if connections created by this datasource will fast fail validation.
+     * @see #setDisconnectionSqlCodes(Collection)
+     * @since 2.1
      */
     @Override
-    public String getPassword() {
-        return this.password;
+    public boolean getFastFailValidation() {
+        return fastFailValidation;
     }
 
     /**
-     * <p>
-     * Sets the {@link #password}.
-     * </p>
-     * <p>
-     * Note: this method currently has no effect once the pool has been initialized. The pool is initialized the first
-     * time one of the following methods is invoked: <code>getConnection, setLogwriter,
-     * setLoginTimeout, getLoginTimeout, getLogWriter.</code>
-     * </p>
+     * Returns the initial size of the connection pool.
      *
-     * @param password
-     *            new value for the password
+     * @return the number of connections created when the pool is initialized
      */
-    public void setPassword(final String password) {
-        this.password = password;
+    @Override
+    public synchronized int getInitialSize() {
+        return this.initialSize;
     }
 
     /**
-     * The connection URL to be passed to our JDBC driver to establish a connection.
+     * Returns the JMX name that has been requested for this DataSource. If the requested name is not valid, an
+     * alternative may be chosen.
+     *
+     * @return The JMX name that has been requested for this DataSource.
      */
-    private String url;
+    public String getJmxName() {
+        return jmxName;
+    }
 
     /**
-     * Returns the JDBC connection {@link #url} property.
+     * Returns the LIFO property.
      *
-     * @return the {@link #url} passed to the JDBC driver to establish connections
+     * @return true if connection pool behaves as a LIFO queue.
      */
     @Override
-    public synchronized String getUrl() {
-        return this.url;
+    public synchronized boolean getLifo() {
+        return this.lifo;
     }
 
     /**
      * <p>
-     * Sets the {@link #url}.
+     * Flag to log stack traces for application code which abandoned a Statement or Connection.
      * </p>
      * <p>
-     * Note: this method currently has no effect once the pool has been initialized. The pool is initialized the first
-     * time one of the following methods is invoked: <code>getConnection, setLogwriter,
-     * setLoginTimeout, getLoginTimeout, getLogWriter.</code>
+     * Defaults to false.
+     * </p>
+     * <p>
+     * Logging of abandoned Statements and Connections adds overhead for every Connection open or new Statement because
+     * a stack trace has to be generated.
      * </p>
-     *
-     * @param url
-     *            the new value for the JDBC connection url
      */
-    public synchronized void setUrl(final String url) {
-        this.url = url;
+    @Override
+    public boolean getLogAbandoned() {
+        if (abandonedConfig != null) {
+            return abandonedConfig.getLogAbandoned();
+        }
+        return false;
     }
 
     /**
-     * The connection user name to be passed to our JDBC driver to establish a connection.
-     */
-    private String userName;
-
-    /**
-     * Returns the JDBC connection {@link #userName} property.
+     * When {@link #getMaxConnLifetimeMillis()} is set to limit connection lifetime, this property determines whether or
+     * not log messages are generated when the pool closes connections due to maximum lifetime exceeded.
      *
-     * @return the {@link #userName} passed to the JDBC driver to establish connections
+     * @since 2.1
      */
     @Override
-    public String getUsername() {
-        return this.userName;
+    public boolean getLogExpiredConnections() {
+        return logExpiredConnections;
     }
 
     /**
+     * <strong>BasicDataSource does NOT support this method.</strong>
+     *
      * <p>
-     * Sets the {@link #userName}.
+     * Returns the login timeout (in seconds) for connecting to the database.
      * </p>
      * <p>
-     * Note: this method currently has no effect once the pool has been initialized. The pool is initialized the first
-     * time one of the following methods is invoked: <code>getConnection, setLogwriter,
-     * setLoginTimeout, getLoginTimeout, getLogWriter.</code>
+     * Calls {@link #createDataSource()}, so has the side effect of initializing the connection pool.
      * </p>
      *
-     * @param userName
-     *            the new value for the JDBC connection user name
+     * @throws SQLException
+     *             if a database access error occurs
+     * @throws UnsupportedOperationException
+     *             If the DataSource implementation does not support the login timeout feature.
+     * @return login timeout in seconds
      */
-    public void setUsername(final String userName) {
-        this.userName = userName;
+    @Override
+    public int getLoginTimeout() throws SQLException {
+        // This method isn't supported by the PoolingDataSource returned by the createDataSource
+        throw new UnsupportedOperationException("Not supported by BasicDataSource");
     }
 
     /**
-     * The SQL query that will be used to validate connections from this pool before returning them to the caller. If
-     * specified, this query <strong>MUST</strong> be an SQL SELECT statement that returns at least one row. If not
-     * specified, {@link Connection#isValid(int)} will be used to validate connections.
+     * <p>
+     * Returns the log writer being used by this data source.
+     * </p>
+     * <p>
+     * Calls {@link #createDataSource()}, so has the side effect of initializing the connection pool.
+     * </p>
+     *
+     * @throws SQLException
+     *             if a database access error occurs
+     * @return log writer in use
      */
-    private volatile String validationQuery;
+    @Override
+    public PrintWriter getLogWriter() throws SQLException {
+        return createDataSource().getLogWriter();
+    }
 
     /**
-     * Returns the validation query used to validate connections before returning them.
-     *
-     * @return the SQL validation query
-     * @see #validationQuery
+     * Returns the maximum permitted lifetime of a connection in milliseconds. A value of zero or less indicates an
+     * infinite lifetime.
      */
     @Override
-    public String getValidationQuery() {
-        return this.validationQuery;
+    public long getMaxConnLifetimeMillis() {
+        return maxConnLifetimeMillis;
     }
 
     /**
      * <p>
-     * Sets the {@link #validationQuery}.
+     * Returns the maximum number of connections that can remain idle in the pool. Excess idle connections are destroyed
+     * on return to the pool.
      * </p>
      * <p>
-     * Note: this method currently has no effect once the pool has been initialized. The pool is initialized the first
-     * time one of the following methods is invoked: <code>getConnection, setLogwriter,
-     * setLoginTimeout, getLoginTimeout, getLogWriter.</code>
+     * A negative value indicates that there is no limit
      * </p>
      *
-     * @param validationQuery
-     *            the new value for the validation query
+     * @return the maximum number of idle connections
      */
-    public void setValidationQuery(final String validationQuery) {
-        if (validationQuery != null && validationQuery.trim().length() > 0) {
-            this.validationQuery = validationQuery;
-        } else {
-            this.validationQuery = null;
-        }
+    @Override
+    public synchronized int getMaxIdle() {
+        return this.maxIdle;
     }
 
     /**
-     * Timeout in seconds before connection validation queries fail.
-     */
-    private volatile int validationQueryTimeoutSeconds = -1;
-
-    /**
-     * Returns the validation query timeout.
+     * Gets the value of the <code>maxOpenPreparedStatements</code> property.
      *
-     * @return the timeout in seconds before connection validation queries fail.
+     * @return the maximum number of open statements
      */
     @Override
-    public int getValidationQueryTimeout() {
-        return validationQueryTimeoutSeconds;
+    public synchronized int getMaxOpenPreparedStatements() {
+        return this.maxOpenPreparedStatements;
     }
 
     /**
-     * Sets the validation query timeout, the amount of time, in seconds, that connection validation will wait for a
-     * response from the database when executing a validation query. Use a value less than or equal to 0 for no timeout.
      * <p>
-     * Note: this method currently has no effect once the pool has been initialized. The pool is initialized the first
-     * time one of the following methods is invoked: <code>getConnection, setLogwriter,
-     * setLoginTimeout, getLoginTimeout, getLogWriter.</code>
+     * Returns the maximum number of active connections that can be allocated at the same time.
+     * </p>
+     * <p>
+     * A negative number means that there is no limit.
      * </p>
      *
-     * @param validationQueryTimeoutSeconds
-     *            new validation query timeout value in seconds
+     * @return the maximum number of active connections
      */
-    public void setValidationQueryTimeout(final int validationQueryTimeoutSeconds) {
-        this.validationQueryTimeoutSeconds = validationQueryTimeoutSeconds;
+    @Override
+    public synchronized int getMaxTotal() {
+        return this.maxTotal;
     }
 
     /**
-     * These SQL statements run once after a Connection is created.
-     * <p>
-     * This property can be used for example to run ALTER SESSION SET NLS_SORT=XCYECH in an Oracle Database only once
-     * after connection creation.
-     * </p>
+     * Returns the maximum number of milliseconds that the pool will wait for a connection to be returned before
+     * throwing an exception. A value less than or equal to zero means the pool is set to wait indefinitely.
+     *
+     * @return the maxWaitMillis property value
      */
-    private volatile List<String> connectionInitSqls;
+    @Override
+    public synchronized long getMaxWaitMillis() {
+        return this.maxWaitMillis;
+    }
 
     /**
-     * Returns the list of SQL statements executed when a physical connection is first created. Returns an empty list if
-     * there are no initialization statements configured.
+     * Returns the {@link #minEvictableIdleTimeMillis} property.
      *
-     * @return initialization SQL statements
+     * @return the value of the {@link #minEvictableIdleTimeMillis} property
+     * @see #minEvictableIdleTimeMillis
      */
-    public List<String> getConnectionInitSqls() {
-        final List<String> result = connectionInitSqls;
-        if (result == null) {
-            return Collections.emptyList();
-        }
-        return result;
+    @Override
+    public synchronized long getMinEvictableIdleTimeMillis() {
+        return this.minEvictableIdleTimeMillis;
     }
 
     /**
-     * Provides the same data as {@link #getConnectionInitSqls()} but in an array so it is accessible via JMX.
+     * Returns the minimum number of idle connections in the pool. The pool attempts to ensure that minIdle connections
+     * are available when the idle object evictor runs. The value of this property has no effect unless
+     * {@link #timeBetweenEvictionRunsMillis} has a positive value.
+     *
+     * @return the minimum number of idle connections
+     * @see GenericObjectPool#getMinIdle()
      */
     @Override
-    public String[] getConnectionInitSqlsAsArray() {
-        final Collection<String> result = getConnectionInitSqls();
-        return result.toArray(new String[result.size()]);
+    public synchronized int getMinIdle() {
+        return this.minIdle;
     }
 
     /**
-     * Sets the list of SQL statements to be executed when a physical connection is first created.
-     * <p>
-     * Note: this method currently has no effect once the pool has been initialized. The pool is initialized the first
-     * time one of the following methods is invoked: <code>getConnection, setLogwriter,
-     * setLoginTimeout, getLoginTimeout, getLogWriter.</code>
-     * </p>
+     * [Read Only] The current number of active connections that have been allocated from this data source.
      *
-     * @param connectionInitSqls
-     *            Collection of SQL statements to execute on connection creation
+     * @return the current number of active connections
      */
-    public void setConnectionInitSqls(final Collection<String> connectionInitSqls) {
-        if (connectionInitSqls != null && connectionInitSqls.size() > 0) {
-            ArrayList<String> newVal = null;
-            for (final String s : connectionInitSqls) {
-                if (s != null && s.trim().length() > 0) {
-                    if (newVal == null) {
-                        newVal = new ArrayList<>();
-                    }
-                    newVal.add(s);
-                }
-            }
-            this.connectionInitSqls = newVal;
-        } else {
-            this.connectionInitSqls = null;
+    @Override
+    public int getNumActive() {
+        // Copy reference to avoid NPE if close happens after null check
+        final GenericObjectPool<PoolableConnection> pool = connectionPool;
+        if (pool != null) {
+            return pool.getNumActive();
         }
+        return 0;
     }
 
     /**
-     * Controls access to the underlying connection.
-     */
-    private boolean accessToUnderlyingConnectionAllowed = false;
-
-    /**
-     * Returns the value of the accessToUnderlyingConnectionAllowed property.
+     * [Read Only] The current number of idle connections that are waiting to be allocated from this data source.
      *
-     * @return true if access to the underlying connection is allowed, false otherwise.
+     * @return the current number of idle connections
      */
     @Override
-    public synchronized boolean isAccessToUnderlyingConnectionAllowed() {
-        return this.accessToUnderlyingConnectionAllowed;
+    public int getNumIdle() {
+        // Copy reference to avoid NPE if close happens after null check
+        final GenericObjectPool<PoolableConnection> pool = connectionPool;
+        if (pool != null) {
+            return pool.getNumIdle();
+        }
+        return 0;
     }
 
     /**
-     * <p>
-     * Sets the value of the accessToUnderlyingConnectionAllowed property. It controls if the PoolGuard allows access to
-     * the underlying connection. (Default: false)
-     * </p>
-     * <p>
-     * Note: this method currently has no effect once the pool has been initialized. The pool is initialized the first
-     * time one of the following methods is invoked: <code>getConnection, setLogwriter,
-     * setLoginTimeout, getLoginTimeout, getLogWriter.</code>
-     * </p>
+     * Returns the value of the {@link #numTestsPerEvictionRun} property.
      *
-     * @param allow
-     *            Access to the underlying connection is granted when true.
+     * @return the number of objects to examine during idle object evictor runs
+     * @see #numTestsPerEvictionRun
      */
-    public synchronized void setAccessToUnderlyingConnectionAllowed(final boolean allow) {
-        this.accessToUnderlyingConnectionAllowed = allow;
+    @Override
+    public synchronized int getNumTestsPerEvictionRun() {
+        return this.numTestsPerEvictionRun;
     }
 
-    private long maxConnLifetimeMillis = -1;
+    @Override
+    public Logger getParentLogger() throws SQLFeatureNotSupportedException {
+        throw new SQLFeatureNotSupportedException();
+    }
 
     /**
-     * Returns the maximum permitted lifetime of a connection in milliseconds. A value of zero or less indicates an
-     * infinite lifetime.
+     * Returns the password passed to the JDBC driver to establish connections.
+     *
+     * @return the connection password
      */
     @Override
-    public long getMaxConnLifetimeMillis() {
-        return maxConnLifetimeMillis;
+    public String getPassword() {
+        return this.password;
     }
 
-    private boolean logExpiredConnections = true;
+    protected ObjectName getRegisteredJmxName() {
+        return ObjectNameWrapper.unwrap(registeredJmxObjectName);
+    }
 
     /**
-     * When {@link #getMaxConnLifetimeMillis()} is set to limit connection lifetime, this property determines whether or
-     * not log messages are generated when the pool closes connections due to maximum lifetime exceeded.
+     * <p>
+     * Flag to remove abandoned connections if they exceed the removeAbandonedTimeout when borrowObject is invoked.
+     * </p>
+     * <p>
+     * The default value is false.
+     * </p>
+     * <p>
+     * If set to true a connection is considered abandoned and eligible for removal if it has not been used for more
+     * than {@link #getRemoveAbandonedTimeout() removeAbandonedTimeout} seconds.
+     * </p>
+     * <p>
+     * Abandoned connections are identified and removed when {@link #getConnection()} is invoked and all of the
+     * following conditions hold:
+     * </p>
+     * <ul>
+     * <li>{@link #getRemoveAbandonedOnBorrow()}</li>
+     * <li>{@link #getNumActive()} &gt; {@link #getMaxTotal()} - 3</li>
+     * <li>{@link #getNumIdle()} &lt; 2</li>
+     * </ul>
      *
-     * @since 2.1
+     * @see #getRemoveAbandonedTimeout()
      */
     @Override
-    public boolean getLogExpiredConnections() {
-        return logExpiredConnections;
+    public boolean getRemoveAbandonedOnBorrow() {
+        if (abandonedConfig != null) {
+            return abandonedConfig.getRemoveAbandonedOnBorrow();
+        }
+        return false;
     }
 
     /**
      * <p>
-     * Sets the maximum permitted lifetime of a connection in milliseconds. A value of zero or less indicates an
-     * infinite lifetime.
+     * Flag to remove abandoned connections if they exceed the removeAbandonedTimeout during pool maintenance.
      * </p>
+     *
      * <p>
-     * Note: this method currently has no effect once the pool has been initialized. The pool is initialized the first
-     * time one of the following methods is invoked: <code>getConnection, setLogwriter,
-     * setLoginTimeout, getLoginTimeout, getLogWriter.</code>
+     * The default value is false.
      * </p>
      *
-     * @param maxConnLifetimeMillis
-     *            The maximum permitted lifetime of a connection in milliseconds.
+     * <p>
+     * If set to true a connection is considered abandoned and eligible for removal if it has not been used for more
+     * than {@link #getRemoveAbandonedTimeout() removeAbandonedTimeout} seconds.
+     * </p>
+     *
+     * @see #getRemoveAbandonedTimeout()
      */
-    public void setMaxConnLifetimeMillis(final long maxConnLifetimeMillis) {
-        this.maxConnLifetimeMillis = maxConnLifetimeMillis;
+    @Override
+    public boolean getRemoveAbandonedOnMaintenance() {
+        if (abandonedConfig != null) {
+            return abandonedConfig.getRemoveAbandonedOnMaintenance();
+        }
+        return false;
     }
 
     /**
-     * When {@link #getMaxConnLifetimeMillis()} is set to limit connection lifetime, this property determines whether or
-     * not log messages are generated when the pool closes connections due to maximum lifetime exceeded. Set this
-     * property to false to suppress log messages when connections expire.
-     *
-     * @param logExpiredConnections
-     *            Whether or not log messages are generated when the pool closes connections due to maximum lifetime
-     *            exceeded.
+     * <p>
+     * Timeout in seconds before an abandoned connection can be removed.
+     * </p>
+     * <p>
+     * Creating a Statement, PreparedStatement or CallableStatement or using one of these to execute a query (using one
+     * of the execute methods) resets the lastUsed property of the parent connection.
+     * </p>
+     * <p>
+     * Abandoned connection cleanup happens when:
+     * </p>
+     * <ul>
+     * <li>{@link #getRemoveAbandonedOnBorrow()} or {@link #getRemoveAbandonedOnMaintenance()} = true</li>
+     * <li>{@link #getNumIdle() numIdle} &lt; 2</li>
+     * <li>{@link #getNumActive() numActive} &gt; {@link #getMaxTotal() maxTotal} - 3</li>
+     * </ul>
+     * <p>
+     * The default value is 300 seconds.
+     * </p>
      */
-    public void setLogExpiredConnections(final boolean logExpiredConnections) {
-        this.logExpiredConnections = logExpiredConnections;
+    @Override
+    public int getRemoveAbandonedTimeout() {
+        if (abandonedConfig != null) {
+            return abandonedConfig.getRemoveAbandonedTimeout();
+        }
+        return 300;
     }
 
-    private String jmxName;
-
     /**
-     * Returns the JMX name that has been requested for this DataSource. If the requested name is not valid, an
-     * alternative may be chosen.
+     * Gets the current value of the flag that controls whether a connection will be rolled back when it is returned to
+     * the pool if auto commit is not enabled and the connection is not read only.
      *
-     * @return The JMX name that has been requested for this DataSource.
+     * @return whether a connection will be rolled back when it is returned to the pool.
      */
-    public String getJmxName() {
-        return jmxName;
+    public boolean getRollbackOnReturn() {
+        return rollbackOnReturn;
     }
 
     /**
-     * Sets the JMX name that has been requested for this DataSource. If the requested name is not valid, an alternative
-     * may be chosen. This DataSource will attempt to register itself using this name. If another component registers
-     * this DataSource with JMX and this name is valid this name will be used in preference to any specified by the
-     * other component.
+     * <p>
+     * Returns the minimum amount of time a connection may sit idle in the pool before it is eligible for eviction by
+     * the idle object evictor, with the extra condition that at least "minIdle" connections remain in the pool.
+     * </p>
      *
-     * @param jmxName
-     *            The JMX name that has been requested for this DataSource
+     * <p>
+     * When {@link #getMinEvictableIdleTimeMillis() minEvictableIdleTimeMillis} is set to a positive value,
+     * minEvictableIdleTimeMillis is examined first by the idle connection evictor - i.e. when idle connections are
+     * visited by the evictor, idle time is first compared against {@code minEvictableIdleTimeMillis} (without
+     * considering the number of idle connections in the pool) and then against {@code softMinEvictableIdleTimeMillis},
+     * including the {@code minIdle}, constraint.
+     * </p>
+     *
+     * @return minimum amount of time a connection may sit idle in the pool before it is eligible for eviction, assuming
+     *         there are minIdle idle connections in the pool
      */
-    public void setJmxName(final String jmxName) {
-        this.jmxName = jmxName;
+    @Override
+    public synchronized long getSoftMinEvictableIdleTimeMillis() {
+        return softMinEvictableIdleTimeMillis;
     }
 
-    private boolean enableAutoCommitOnReturn = true;
-
     /**
-     * Returns the value of the flag that controls whether or not connections being returned to the pool will be checked
-     * and configured with {@link Connection#setAutoCommit(boolean) Connection.setAutoCommit(true)} if the auto commit
-     * setting is {@code false} when the connection is returned. It is <code>true</code> by default.
+     * Returns the {@link #testOnBorrow} property.
      *
-     * @return Whether or not connections being returned to the pool will be checked and configured with auto-commit.
+     * @return true if objects are validated before being borrowed from the pool
+     *
+     * @see #testOnBorrow
      */
-    public boolean getEnableAutoCommitOnReturn() {
-        return enableAutoCommitOnReturn;
+    @Override
+    public synchronized boolean getTestOnBorrow() {
+        return this.testOnBorrow;
     }
 
     /**
-     * Sets the value of the flag that controls whether or not connections being returned to the pool will be checked
-     * and configured with {@link Connection#setAutoCommit(boolean) Connection.setAutoCommit(true)} if the auto commit
-     * setting is {@code false} when the connection is returned. It is <code>true</code> by default.
+     * Returns the {@link #testOnCreate} property.
      *
-     * @param enableAutoCommitOnReturn
-     *            Whether or not connections being returned to the pool will be checked and configured with auto-commit.
+     * @return true if objects are validated immediately after they are created by the pool
+     * @see #testOnCreate
      */
-    public void setEnableAutoCommitOnReturn(final boolean enableAutoCommitOnReturn) {
-        this.enableAutoCommitOnReturn = enableAutoCommitOnReturn;
+    @Override
+    public synchronized boolean getTestOnCreate() {
+        return this.testOnCreate;
     }
 
-    private boolean rollbackOnReturn = true;
-
     /**
-     * Gets the current value of the flag that controls whether a connection will be rolled back when it is returned to
-     * the pool if auto commit is not enabled and the connection is not read only.
+     * Returns the value of the {@link #testOnReturn} property.
      *
-     * @return whether a connection will be rolled back when it is returned to the pool.
+     * @return true if objects are validated before being returned to the pool
+     * @see #testOnReturn
      */
-    public boolean getRollbackOnReturn() {
-        return rollbackOnReturn;
+    public synchronized boolean getTestOnReturn() {
+        return this.testOnReturn;
     }
 
     /**
-     * Sets the flag that controls if a connection will be rolled back when it is returned to the pool if auto commit is
-     * not enabled and the connection is not read only.
+     * Returns the value of the {@link #testWhileIdle} property.
      *
-     * @param rollbackOnReturn
-     *            whether a connection will be rolled back when it is returned to the pool.
+     * @return true if objects examined by the idle object evictor are validated
+     * @see #testWhileIdle
      */
-    public void setRollbackOnReturn(final boolean rollbackOnReturn) {
-        this.rollbackOnReturn = rollbackOnReturn;
+    @Override
+    public synchronized boolean getTestWhileIdle() {
+        return this.testWhileIdle;
     }
 
-    private volatile Set<String> disconnectionSqlCodes;
-
     /**
-     * Returns the set of SQL_STATE codes considered to signal fatal conditions.
+     * Returns the value of the {@link #timeBetweenEvictionRunsMillis} property.
      *
-     * @return fatal disconnection state codes
-     * @see #setDisconnectionSqlCodes(Collection)
-     * @since 2.1
+     * @return the time (in milliseconds) between evictor runs
+     * @see #timeBetweenEvictionRunsMillis
      */
-    public Set<String> getDisconnectionSqlCodes() {
-        final Set<String> result = disconnectionSqlCodes;
-        if (result == null) {
-            return Collections.emptySet();
-        }
-        return result;
+    @Override
+    public synchronized long getTimeBetweenEvictionRunsMillis() {
+        return this.timeBetweenEvictionRunsMillis;
     }
 
     /**
-     * Provides the same data as {@link #getDisconnectionSqlCodes} but in an array so it is accessible via JMX.
+     * Returns the JDBC connection {@link #url} property.
      *
-     * @since 2.1
+     * @return the {@link #url} passed to the JDBC driver to establish connections
      */
     @Override
-    public String[] getDisconnectionSqlCodesAsArray() {
-        final Collection<String> result = getDisconnectionSqlCodes();
-        return result.toArray(new String[result.size()]);
+    public synchronized String getUrl() {
+        return this.url;
     }
 
     /**
-     * Sets the SQL_STATE codes considered to signal fatal conditions.
-     * <p>
-     * Overrides the defaults in {@link Utils#DISCONNECTION_SQL_CODES} (plus anything starting with
-     * {@link Utils#DISCONNECTION_SQL_CODE_PREFIX}). If this property is non-null and {@link #getFastFailValidation()}
-     * is {@code true}, whenever connections created by this datasource generate exceptions with SQL_STATE codes in this
-     * list, they will be marked as "fatally disconnected" and subsequent validations will fail fast (no attempt at
-     * isValid or validation query).
-     * </p>
-     * <p>
-     * If {@link #getFastFailValidation()} is {@code false} setting this property has no effect.
-     * </p>
-     * <p>
-     * Note: this method currently has no effect once the pool has been initialized. The pool is initialized the first
-     * time one of the following methods is invoked: {@code getConnection, setLogwriter,
-     * setLoginTimeout, getLoginTimeout, getLogWriter}.
-     * </p>
+     * Returns the JDBC connection {@link #userName} property.
      *
-     * @param disconnectionSqlCodes
-     *            SQL_STATE codes considered to signal fatal conditions
-     * @since 2.1
+     * @return the {@link #userName} passed to the JDBC driver to establish connections
      */
-    public void setDisconnectionSqlCodes(final Collection<String> disconnectionSqlCodes) {
-        if (disconnectionSqlCodes != null && disconnectionSqlCodes.size() > 0) {
-            HashSet<String> newVal = null;
-            for (final String s : disconnectionSqlCodes) {
-                if (s != null && s.trim().length() > 0) {
-                    if (newVal == null) {
-                        newVal = new HashSet<>();
-                    }
-                    newVal.add(s);
-                }
-            }
-            this.disconnectionSqlCodes = newVal;
-        } else {
-            this.disconnectionSqlCodes = null;
-        }
+    @Override
+    public String getUsername() {
+        return this.userName;
     }
 
-    private boolean fastFailValidation;
-
     /**
-     * True means that validation will fail immediately for connections that have previously thrown SQLExceptions with
-     * SQL_STATE indicating fatal disconnection errors.
+     * Returns the validation query used to validate connections before returning them.
      *
-     * @return true if connections created by this datasource will fast fail validation.
-     * @see #setDisconnectionSqlCodes(Collection)
-     * @since 2.1
+     * @return the SQL validation query
+     * @see #validationQuery
      */
     @Override
-    public boolean getFastFailValidation() {
-        return fastFailValidation;
+    public String getValidationQuery() {
+        return this.validationQuery;
     }
 
     /**
-     * @see #getFastFailValidation()
-     * @param fastFailValidation
-     *            true means connections created by this factory will fast fail validation
-     * @since 2.1
+     * Returns the validation query timeout.
+     *
+     * @return the timeout in seconds before connection validation queries fail.
      */
-    public void setFastFailValidation(final boolean fastFailValidation) {
-        this.fastFailValidation = fastFailValidation;
+    @Override
+    public int getValidationQueryTimeout() {
+        return validationQueryTimeoutSeconds;
     }
 
     /**
-     * The object pool that internally manages our connections.
+     * Manually invalidates a connection, effectively requesting the pool to try to close it, remove it from the pool
+     * and reclaim pool capacity.
+     *
+     * @param connection
+     *            The Connection to invalidate.
+     *
+     * @throws IllegalStateException
+     *             if invalidating the connection failed.
+     * @since 2.1
      */
-    private volatile GenericObjectPool<PoolableConnection> connectionPool;
-
-    protected GenericObjectPool<PoolableConnection> getConnectionPool() {
-        return connectionPool;
-    }
+    public void invalidateConnection(final Connection connection) throws IllegalStateException {
+        if (connection == null) {
+            return;
+        }
+        if (connectionPool == null) {
+            throw new IllegalStateException("Cannot invalidate connection: ConnectionPool is null.");
+        }
 
-    /**
-     * The connection properties that will be sent to our JDBC driver when establishing new connections.
-     * <strong>NOTE</strong> - The "user" and "password" properties will be passed explicitly, so they do not need to be
-     * included here.
-     */
-    private Properties connectionProperties = new Properties();
+        final PoolableConnection poolableConnection;
+        try {
+            poolableConnection = connection.unwrap(PoolableConnection.class);
+            if (poolableConnection == null) {
+                throw new IllegalStateException(
+                        "Cannot invalidate connection: Connection is not a poolable connection.");
+            }
+        } catch (final SQLException e) {
+            throw new IllegalStateException("Cannot invalidate connection: Unwrapping poolable connection failed.", e);
+        }
 
-    // For unit testing
-    Properties getConnectionProperties() {
-        return connectionProperties;
+        try {
+            connectionPool.invalidateObject(poolableConnection);
+        } catch (final Exception e) {
+            throw new IllegalStateException("Invalidating connection threw unexpected exception", e);
+        }
     }
 
     /**
-     * The data source we will use to manage connections. This object should be acquired <strong>ONLY</strong> by calls
-     * to the <code>createDataSource()</code> method.
-     */
-    private volatile DataSource dataSource;
-
-    /**
-     * The PrintWriter to which log messages should be directed.
-     */
-    private volatile PrintWriter logWriter = new PrintWriter(
-            new OutputStreamWriter(System.out, StandardCharsets.UTF_8));
-
-    // ----------------------------------------------------- DataSource Methods
-
-    /**
-     * Creates (if necessary) and return a connection to the database.
+     * Manually evicts idle connections.
      *
-     * @throws SQLException
-     *             if a database access error occurs
-     * @return a database connection
+     * @throws Exception Thrown by {@link GenericObjectPool#evict()}.
+     * @see GenericObjectPool#evict()
      */
-    @Override
-    public Connection getConnection() throws SQLException {
-        if (Utils.IS_SECURITY_ENABLED) {
-            final PrivilegedExceptionAction<Connection> action = new PaGetConnection();
-            try {
-                return AccessController.doPrivileged(action);
-            } catch (final PrivilegedActionException e) {
-                final Throwable cause = e.getCause();
-                if (cause instanceof SQLException) {
-                    throw (SQLException) cause;
-                }
-                throw new SQLException(e);
-            }
+    public void evict() throws Exception {
+        if (connectionPool != null) {
+            connectionPool.evict();
         }
-        return createDataSource().getConnection();
     }
 
     /**
-     * <strong>BasicDataSource does NOT support this method.</strong>
-     *
-     * @param user
-     *            Database user on whose behalf the Connection is being made
-     * @param pass
-     *            The database user's password
+     * Returns the value of the accessToUnderlyingConnectionAllowed property.
      *
-     * @throws UnsupportedOperationException
-     *             always thrown.
-     * @throws SQLException
-     *             if a database access error occurs
-     * @return nothing - always throws UnsupportedOperationException
+     * @return true if access to the underlying connection is allowed, false otherwise.
      */
     @Override
-    public Connection getConnection(final String user, final String pass) throws SQLException {
-        // This method isn't supported by the PoolingDataSource returned by the createDataSource
-        throw new UnsupportedOperationException("Not supported by BasicDataSource");
+    public synchronized boolean isAccessToUnderlyingConnectionAllowed() {
+        return this.accessToUnderlyingConnectionAllowed;
     }
 
     /**
-     * <strong>BasicDataSource does NOT support this method.</strong>
-     *
-     * <p>
-     * Returns the login timeout (in seconds) for connecting to the database.
-     * </p>
-     * <p>
-     * Calls {@link #createDataSource()}, so has the side effect of initializing the connection pool.
-     * </p>
+     * If true, this data source is closed and no more connections can be retrieved from this datasource.
      *
-     * @throws SQLException
-     *             if a database access error occurs
-     * @throws UnsupportedOperationException
-     *             If the DataSource implementation does not support the login timeout feature.
-     * @return login timeout in seconds
+     * @return true, if the data source is closed; false otherwise
      */
     @Override
-    public int getLoginTimeout() throws SQLException {
-        // This method isn't supported by the PoolingDataSource returned by the createDataSource
-        throw new UnsupportedOperationException("Not supported by BasicDataSource");
+    public synchronized boolean isClosed() {
+        return closed;
     }
 
     /**
-     * <p>
-     * Returns the log writer being used by this data source.
-     * </p>
-     * <p>
-     * Calls {@link #createDataSource()}, so has the side effect of initializing the connection pool.
-     * </p>
+     * Returns true if we are pooling statements.
      *
-     * @throws SQLException
-     *             if a database access error occurs
-     * @return log writer in use
+     * @return true if prepared and callable statements are pooled
      */
     @Override
-    public PrintWriter getLogWriter() throws SQLException {
-        return createDataSource().getLogWriter();
+    public synchronized boolean isPoolPreparedStatements() {
+        return this.poolPreparedStatements;
     }
 
-    /**
-     * <strong>BasicDataSource does NOT support this method. </strong>
-     *
-     * <p>
-     * Set the login timeout (in seconds) for connecting to the database.
-     * </p>
-     * <p>
-     * Calls {@link #createDataSource()}, so has the side effect of initializing the connection pool.
-     * </p>
-     *
-     * @param loginTimeout
-     *            The new login timeout, or zero for no timeout
-     * @throws UnsupportedOperationException
-     *             If the DataSource implementation does not support the login timeout feature.
-     * @throws SQLException
-     *             if a database access error occurs
-     */
     @Override
-    public void setLoginTimeout(final int loginTimeout) throws SQLException {
-        // This method isn't supported by the PoolingDataSource returned by the createDataSource
-        throw new UnsupportedOperationException("Not supported by BasicDataSource");
+    public boolean isWrapperFor(final Class<?> iface) throws SQLException {
+        return false;
     }
 
-    /**
-     * <p>
-     * Sets the log writer being used by this data source.
-     * </p>
-     * <p>
-     * Calls {@link #createDataSource()}, so has the side effect of initializing the connection pool.
-     * </p>
-     *
-     * @param logWriter
-     *            The new log writer
-     * @throws SQLException
-     *             if a database access error occurs
-     */
-    @Override
-    public void setLogWriter(final PrintWriter logWriter) throws SQLException {
-        createDataSource().setLogWriter(logWriter);
-        this.logWriter = logWriter;
+    private void jmxRegister() {
+        // Return immediately if this DataSource has already been registered
+        if (registeredJmxObjectName != null) {
+            return;
+        }
+        // Return immediately if no JMX name has been specified
+        final String requestedName = getJmxName();
+        if (requestedName == null) {
+            return;
+        }
+        try {
+            ObjectNameWrapper.wrap(requestedName).registerMBean(this);
+        } catch (final MalformedObjectNameException e) {
+            log.warn("The requested JMX name [" + requestedName + "] was not valid and will be ignored.");
+        }
     }
 
-    private AbandonedConfig abandonedConfig;
+    protected void log(final String message) {
+        if (logWriter != null) {
+            logWriter.println(message);
+        }
+    }
 
-    /**
-     * <p>
-     * Flag to remove abandoned connections if they exceed the removeAbandonedTimeout when borrowObject is invoked.
-     * </p>
-     * <p>
-     * The default value is false.
-     * </p>
-     * <p>
-     * If set to true a connection is considered abandoned and eligible for removal if it has not been used for more
-     * than {@link #getRemoveAbandonedTimeout() removeAbandonedTimeout} seconds.
-     * </p>
-     * <p>
-     * Abandoned connections are identified and removed when {@link #getConnection()} is invoked and all of the
-     * following conditions hold:
-     * </p>
-     * <ul>
-     * <li>{@link #getRemoveAbandonedOnBorrow()}</li>
-     * <li>{@link #getNumActive()} &gt; {@link #getMaxTotal()} - 3</li>
-     * <li>{@link #getNumIdle()} &lt; 2</li>
-     * </ul>
-     *
-     * @see #getRemoveAbandonedTimeout()
-     */
     @Override
-    public boolean getRemoveAbandonedOnBorrow() {
-        if (abandonedConfig != null) {
-            return abandonedConfig.getRemoveAbandonedOnBorrow();
-        }
-        return false;
-    }
-
-    /**
-     * @param removeAbandonedOnMaintenance
-     *            true means abandoned connections may be removed on pool maintenance.
-     * @see #getRemoveAbandonedOnMaintenance()
-     */
-    public void setRemoveAbandonedOnMaintenance(final boolean removeAbandonedOnMaintenance) {
-        if (abandonedConfig == null) {
-            abandonedConfig = new AbandonedConfig();
-        }
-        abandonedConfig.setRemoveAbandonedOnMaintenance(removeAbandonedOnMaintenance);
-        final GenericObjectPool<?> gop = this.connectionPool;
-        if (gop != null) {
-            gop.setAbandonedConfig(abandonedConfig);
-        }
+    public void postDeregister() {
+        // NO-OP
     }
 
-    /**
-     * <p>
-     * Flag to remove abandoned connections if they exceed the removeAbandonedTimeout during pool maintenance.
-     * </p>
-     *
-     * <p>
-     * The default value is false.
-     * </p>
-     *
-     * <p>
-     * If set to true a connection is considered abandoned and eligible for removal if it has not been used for more
-     * than {@link #getRemoveAbandonedTimeout() removeAbandonedTimeout} seconds.
-     * </p>
-     *
-     * @see #getRemoveAbandonedTimeout()
-     */
     @Override
-    public boolean getRemoveAbandonedOnMaintenance() {
-        if (abandonedConfig != null) {
-            return abandonedConfig.getRemoveAbandonedOnMaintenance();
-        }
-        return false;
-    }
-
-    /**
-     * @param removeAbandonedOnBorrow
-     *            true means abandoned connections may be removed when connections are borrowed from the pool.
-     * @see #getRemoveAbandonedOnBorrow()
-     */
-    public void setRemoveAbandonedOnBorrow(final boolean removeAbandonedOnBorrow) {
-        if (abandonedConfig == null) {
-            abandonedConfig = new AbandonedConfig();
-        }
-        abandonedConfig.setRemoveAbandonedOnBorrow(removeAbandonedOnBorrow);
-        final GenericObjectPool<?> gop = this.connectionPool;
-        if (gop != null) {
-            gop.setAbandonedConfig(abandonedConfig);
-        }
+    public void postRegister(final Boolean registrationDone) {
+        // NO-OP
     }
 
-    /**
-     * <p>
-     * Timeout in seconds before an abandoned connection can be removed.
-     * </p>
-     * <p>
-     * Creating a Statement, PreparedStatement or CallableStatement or using one of these to execute a query (using one
-     * of the execute methods) resets the lastUsed property of the parent connection.
-     * </p>
-     * <p>
-     * Abandoned connection cleanup happens when:
-     * </p>
-     * <ul>
-     * <li>{@link #getRemoveAbandonedOnBorrow()} or {@link #getRemoveAbandonedOnMaintenance()} = true</li>
-     * <li>{@link #getNumIdle() numIdle} &lt; 2</li>
-     * <li>{@link #getNumActive() numActive} &gt; {@link #getMaxTotal() maxTotal} - 3</li>
-     * </ul>
-     * <p>
-     * The default value is 300 seconds.
-     * </p>
-     */
     @Override
-    public int getRemoveAbandonedTimeout() {
-        if (abandonedConfig != null) {
-            return abandonedConfig.getRemoveAbandonedTimeout();
-        }
-        return 300;
-    }
-
-    /**
-     * <p>
-     * Sets the timeout in seconds before an abandoned connection can be removed.
-     * </p>
-     *
-     * <p>
-     * Setting this property has no effect if {@link #getRemoveAbandonedOnBorrow()} and
-     * {@link #getRemoveAbandonedOnMaintenance()} are false.
-     * </p>
-     *
-     * @param removeAbandonedTimeout
-     *            new abandoned timeout in seconds
-     * @see #getRemoveAbandonedTimeout()
-     * @see #getRemoveAbandonedOnBorrow()
-     * @see #getRemoveAbandonedOnMaintenance()
-     */
-    public void setRemoveAbandonedTimeout(final int removeAbandonedTimeout) {
-        if (abandonedConfig == null) {
-            abandonedConfig = new AbandonedConfig();
-        }
-        abandonedConfig.setRemoveAbandonedTimeout(removeAbandonedTimeout);
-        final GenericObjectPool<?> gop = this.connectionPool;
-        if (gop != null) {
-            gop.setAbandonedConfig(abandonedConfig);
-        }
+    public void preDeregister() throws Exception {
+        // NO-OP
     }
 
-    /**
-     * <p>
-     * Flag to log stack traces for application code which abandoned a Statement or Connection.
-     * </p>
-     * <p>
-     * Defaults to false.
-     * </p>
-     * <p>
-     * Logging of abandoned Statements and Connections adds overhead for every Connection open or new Statement because
-     * a stack trace has to be generated.
-     * </p>
-     */
     @Override
-    public boolean getLogAbandoned() {
-        if (abandonedConfig != null) {
-            return abandonedConfig.getLogAbandoned();
-        }
-        return false;
-    }
-
-    /**
-     * @param logAbandoned
-     *            new logAbandoned property value
-     */
-    public void setLogAbandoned(final boolean logAbandoned) {
-        if (abandonedConfig == null) {
-            abandonedConfig = new AbandonedConfig();
+    public ObjectName preRegister(final MBeanServer server, final ObjectName objectName) {
+        final String requestedName = getJmxName();
+        if (requestedName != null) {
+            try {
+                registeredJmxObjectName = ObjectNameWrapper.wrap(requestedName);
+            } catch (final MalformedObjectNameException e) {
+                log.warn("The requested JMX name [" + requestedName + "] was not valid and will be ignored.");
+            }
         }
-        abandonedConfig.setLogAbandoned(logAbandoned);
-        final GenericObjectPool<?> gop = this.connectionPool;
-        if (gop != null) {
-            gop.setAbandonedConfig(abandonedConfig);
+        if (registeredJmxObjectName == null) {
+            registeredJmxObjectName = ObjectNameWrapper.wrap(objectName);
         }
+        return ObjectNameWrapper.unwrap(registeredJmxObjectName);
     }
 
     /**
-     * Gets the print writer used by this configuration to log information on abandoned objects.
+     * Removes a custom connection property.
      *
-     * @return The print writer used by this configuration to log information on abandoned objects.
+     * @param name
+     *            Name of the custom connection property to remove
+     * @see #addConnectionProperty(String, String)
      */
-    public PrintWriter getAbandonedLogWriter() {
-        if (abandonedConfig != null) {
-            return abandonedConfig.getLogWriter();
-        }
-        return null;
+    public void removeConnectionProperty(final String name) {
+        connectionProperties.remove(name);
     }
 
     /**
@@ -1873,21 +1648,6 @@ public class BasicDataSource implements DataSource, BasicDataSourceMXBean, MBean
     }
 
     /**
-     * If the connection pool implements {@link org.apache.tomcat.dbcp.pool2.UsageTracking UsageTracking}, should the
-     * connection pool record a stack trace every time a method is called on a pooled connection and retain the most
-     * recent stack trace to aid debugging of abandoned connections?
-     *
-     * @return <code>true</code> if usage tracking is enabled
-     */
-    @Override
-    public boolean getAbandonedUsageTracking() {
-        if (abandonedConfig != null) {
-            return abandonedConfig.getUseUsageTracking();
-        }
-        return false;
-    }
-
-    /**
      * If the connection pool implements {@link org.apache.tomcat.dbcp.pool2.UsageTracking UsageTracking}, configure whether
      * the connection pool should record a stack trace every time a method is called on a pooled connection and retain
      * the most recent stack trace to aid debugging of abandoned connections.
@@ -1908,37 +1668,83 @@ public class BasicDataSource implements DataSource, BasicDataSourceMXBean, MBean
     }
 
     /**
-     * Adds a custom connection property to the set that will be passed to our JDBC driver. This <strong>MUST</strong>
-     * be called before the first connection is retrieved (along with all the other configuration property setters).
-     * Calls to this method after the connection pool has been initialized have no effect.
+     * <p>
+     * Sets the value of the accessToUnderlyingConnectionAllowed property. It controls if the PoolGuard allows access to
+     * the underlying connection. (Default: false)
+     * </p>
+     * <p>
+     * Note: this method currently has no effect once the pool has been initialized. The pool is initialized the first
+     * time one of the following methods is invoked: <code>getConnection, setLogwriter,
+     * setLoginTimeout, getLoginTimeout, getLogWriter.</code>
+     * </p>
      *
-     * @param name
-     *            Name of the custom connection property
-     * @param value
-     *            Value of the custom connection property
+     * @param allow
+     *            Access to the underlying connection is granted when true.
      */
-    public void addConnectionProperty(final String name, final String value) {
-        connectionProperties.put(name, value);
+    public synchronized void setAccessToUnderlyingConnectionAllowed(final boolean allow) {
+        this.accessToUnderlyingConnectionAllowed = allow;
     }
 
     /**
-     * Removes a custom connection property.
+     * Sets the value of the flag that controls whether or not connections being returned to the pool will be checked
+     * and configured with {@link Connection#setAutoCommit(boolean) Connection.setAutoCommit(true)} if the auto commit
+     * setting is {@code false} when the connection is returned. It is <code>true</code> by default.
      *
-     * @param name
-     *            Name of the custom connection property to remove
-     * @see #addConnectionProperty(String, String)
+     * @param autoCommitOnReturn
+     *            Whether or not connections being returned to the pool will be checked and configured with auto-commit.
+     * @since 2.6.0
      */
-    public void removeConnectionProperty(final String name) {
-        connectionProperties.remove(name);
+    public void setAutoCommitOnReturn(final boolean autoCommitOnReturn) {
+        this.autoCommitOnReturn = autoCommitOnReturn;
     }
 
     /**
-     * Sets the connection properties passed to driver.connect(...).
-     * <p>
-     * Format of the string must be [propertyName=property;]*
-     * </p>
+     * Sets the state caching flag.
+     *
+     * @param cacheState
+     *            The new value for the state caching flag
+     */
+    public void setCacheState(final boolean cacheState) {
+        this.cacheState = cacheState;
+    }
+
+    /**
+     * Sets the list of SQL statements to be executed when a physical connection is first created.
      * <p>
-     * NOTE - The "user" and "password" properties will be added explicitly, so they do not need to be included here.
+     * Note: this method currently has no effect once the pool has been initialized. The pool is initialized the first
+     * time one of the following methods is invoked: <code>getConnection, setLogwriter,
+     * setLoginTimeout, getLoginTimeout, getLogWriter.</code>
+     * </p>
+     *
+     * @param connectionInitSqls
+     *            Collection of SQL statements to execute on connection creation
+     */
+    public void setConnectionInitSqls(final Collection<String> connectionInitSqls) {
+        if (connectionInitSqls != null && connectionInitSqls.size() > 0) {
+            ArrayList<String> newVal = null;
+            for (final String s : connectionInitSqls) {
+                if (s != null && s.trim().length() > 0) {
+                    if (newVal == null) {
+                        newVal = new ArrayList<>();
+                    }
+                    newVal.add(s);
+                }
+            }
+            this.connectionInitSqls = newVal;
+        } else {
+            this.connectionInitSqls = null;
+        }
+    }
+
+    // ----------------------------------------------------- DataSource Methods
+
+    /**
+     * Sets the connection properties passed to driver.connect(...).
+     * <p>
+     * Format of the string must be [propertyName=property;]*
+     * </p>
+     * <p>
+     * NOTE - The "user" and "password" properties will be added explicitly, so they do not need to be included here.
      * </p>
      *
      * @param connectionProperties
@@ -1964,503 +1770,752 @@ public class BasicDataSource implements DataSource, BasicDataSourceMXBean, MBean
         this.connectionProperties = properties;
     }
 
-    private boolean closed;
-
     /**
      * <p>
-     * Closes and releases all idle connections that are currently stored in the connection pool associated with this
-     * data source.
+     * Sets default auto-commit state of connections returned by this datasource.
      * </p>
      * <p>
-     * Connections that are checked out to clients when this method is invoked are not affected. When client
-     * applications subsequently invoke {@link Connection#close()} to return these connections to the pool, the
-     * underlying JDBC connections are closed.
+     * Note: this method currently has no effect once the pool has been initialized. The pool is initialized the first
+     * time one of the following methods is invoked: <code>getConnection, setLogwriter,
+     * setLoginTimeout, getLoginTimeout, getLogWriter.</code>
      * </p>
+     *
+     * @param defaultAutoCommit
+     *            default auto-commit value
+     */
+    public void setDefaultAutoCommit(final Boolean defaultAutoCommit) {
+        this.defaultAutoCommit = defaultAutoCommit;
+    }
+
+    /**
      * <p>
-     * Attempts to acquire connections using {@link #getConnection()} after this method has been invoked result in
-     * SQLExceptions.
+     * Sets the default catalog.
      * </p>
      * <p>
-     * This method is idempotent - i.e., closing an already closed BasicDataSource has no effect and does not generate
-     * exceptions.
+     * Note: this method currently has no effect once the pool has been initialized. The pool is initialized the first
+     * time one of the following methods is invoked: <code>getConnection, setLogwriter,
+     * setLoginTimeout, getLoginTimeout, getLogWriter.</code>
      * </p>
      *
-     * @throws SQLException
-     *             if an error occurs closing idle connections
+     * @param defaultCatalog
+     *            the default catalog
      */
-    @Override
-    public synchronized void close() throws SQLException {
-        if (registeredJmxObjectName != null) {
-            registeredJmxObjectName.unregisterMBean();
-            registeredJmxObjectName = null;
-        }
-        closed = true;
-        final GenericObjectPool<?> oldpool = connectionPool;
-        connectionPool = null;
-        dataSource = null;
-        try {
-            if (oldpool != null) {
-                oldpool.close();
-            }
-        } catch (final RuntimeException e) {
-            throw e;
-        } catch (final Exception e) {
-            throw new SQLException(Utils.getMessage("pool.close.fail"), e);
+    public void setDefaultCatalog(final String defaultCatalog) {
+        if (defaultCatalog != null && defaultCatalog.trim().length() > 0) {
+            this.defaultCatalog = defaultCatalog;
+        } else {
+            this.defaultCatalog = null;
         }
     }
 
     /**
-     * If true, this data source is closed and no more connections can be retrieved from this datasource.
+     * Sets the default query timeout that will be used for {@link java.sql.Statement Statement}s created from this
+     * connection. <code>null</code> means that the driver default will be used.
      *
-     * @return true, if the data source is closed; false otherwise
+     * @param defaultQueryTimeoutSeconds
+     *            The default query timeout in seconds.
      */
-    @Override
-    public synchronized boolean isClosed() {
-        return closed;
+    public void setDefaultQueryTimeout(final Integer defaultQueryTimeoutSeconds) {
+        this.defaultQueryTimeoutSeconds = defaultQueryTimeoutSeconds;
     }
 
-    @Override
-    public boolean isWrapperFor(final Class<?> iface) throws SQLException {
-        return false;
+    /**
+     * <p>
+     * Sets defaultReadonly property.
+     * </p>
+     * <p>
+     * Note: this method currently has no effect once the pool has been initialized. The pool is initialized the first
+     * time one of the following methods is invoked: <code>getConnection, setLogwriter,
+     * setLoginTimeout, getLoginTimeout, getLogWriter.</code>
+     * </p>
+     *
+     * @param defaultReadOnly
+     *            default read-only value
+     */
+    public void setDefaultReadOnly(final Boolean defaultReadOnly) {
+        this.defaultReadOnly = defaultReadOnly;
     }
 
-    @Override
-    public <T> T unwrap(final Class<T> iface) throws SQLException {
-        throw new SQLException("BasicDataSource is not a wrapper.");
+    /**
+     * <p>
+     * Sets the default schema.
+     * </p>
+     * <p>
+     * Note: this method currently has no effect once the pool has been initialized. The pool is initialized the first
+     * time one of the following methods is invoked: <code>getConnection, setLogwriter,
+     * setLoginTimeout, getLoginTimeout, getLogWriter.</code>
+     * </p>
+     *
+     * @param defaultSchema
+     *            the default catalog
+     * @since 2.5.0
+     */
+    public void setDefaultSchema(final String defaultSchema) {
+        if (defaultSchema != null && defaultSchema.trim().length() > 0) {
+            this.defaultSchema = defaultSchema;
+        } else {
+            this.defaultSchema = null;
+        }
     }
 
-    @Override
-    public Logger getParentLogger() throws SQLFeatureNotSupportedException {
-        throw new SQLFeatureNotSupportedException();
+    /**
+     * <p>
+     * Sets the default transaction isolation state for returned connections.
+     * </p>
+     * <p>
+     * Note: this method currently has no effect once the pool has been initialized. The pool is initialized the first
+     * time one of the following methods is invoked: <code>getConnection, setLogwriter,
+     * setLoginTimeout, getLoginTimeout, getLogWriter.</code>
+     * </p>
+     *
+     * @param defaultTransactionIsolation
+     *            the default transaction isolation state
+     * @see Connection#getTransactionIsolation
+     */
+    public void setDefaultTransactionIsolation(final int defaultTransactionIsolation) {
+        this.defaultTransactionIsolation = defaultTransactionIsolation;
     }
 
     /**
-     * Manually invalidates a connection, effectively requesting the pool to try to close it, remove it from the pool
-     * and reclaim pool capacity.
-     *
-     * @param connection
-     *            The Connection to invalidate.
+     * Sets the SQL_STATE codes considered to signal fatal conditions.
+     * <p>
+     * Overrides the defaults in {@link Utils#DISCONNECTION_SQL_CODES} (plus anything starting with
+     * {@link Utils#DISCONNECTION_SQL_CODE_PREFIX}). If this property is non-null and {@link #getFastFailValidation()}
+     * is {@code true}, whenever connections created by this datasource generate exceptions with SQL_STATE codes in this
+     * list, they will be marked as "fatally disconnected" and subsequent validations will fail fast (no attempt at
+     * isValid or validation query).
+     * </p>
+     * <p>
+     * If {@link #getFastFailValidation()} is {@code false} setting this property has no effect.
+     * </p>
+     * <p>
+     * Note: this method currently has no effect once the pool has been initialized. The pool is initialized the first
+     * time one of the following methods is invoked: {@code getConnection, setLogwriter,
+     * setLoginTimeout, getLoginTimeout, getLogWriter}.
+     * </p>
      *
-     * @throws IllegalStateException
-     *             if invalidating the connection failed.
+     * @param disconnectionSqlCodes
+     *            SQL_STATE codes considered to signal fatal conditions
      * @since 2.1
      */
-    public void invalidateConnection(final Connection connection) throws IllegalStateException {
-        if (connection == null) {
-            return;
-        }
-        if (connectionPool == null) {
-            throw new IllegalStateException("Cannot invalidate connection: ConnectionPool is null.");
-        }
-
-        final PoolableConnection poolableConnection;
-        try {
-            poolableConnection = connection.unwrap(PoolableConnection.class);
-            if (poolableConnection == null) {
-                throw new IllegalStateException(
-                        "Cannot invalidate connection: Connection is not a poolable connection.");
+    public void setDisconnectionSqlCodes(final Collection<String> disconnectionSqlCodes) {
+        if (disconnectionSqlCodes != null && disconnectionSqlCodes.size() > 0) {
+            HashSet<String> newVal = null;
+            for (final String s : disconnectionSqlCodes) {
+                if (s != null && s.trim().length() > 0) {
+                    if (newVal == null) {
+                        newVal = new HashSet<>();
+                    }
+                    newVal.add(s);
+                }
             }
-        } catch (final SQLException e) {
-            throw new IllegalStateException("Cannot invalidate connection: Unwrapping poolable connection failed.", e);
-        }
-
-        try {
-            connectionPool.invalidateObject(poolableConnection);
-        } catch (final Exception e) {
-            throw new IllegalStateException("Invalidating connection threw unexpected exception", e);
+            this.disconnectionSqlCodes = newVal;
+        } else {
+            this.disconnectionSqlCodes = null;
         }
     }
 
-    // ------------------------------------------------------ Protected Methods
+    /**
+     * Sets the JDBC Driver instance to use for this pool.
+     * <p>
+     * Note: this method currently has no effect once the pool has been initialized. The pool is initialized the first
+     * time one of the following methods is invoked: <code>getConnection, setLogwriter,
+     * setLoginTimeout, getLoginTimeout, getLogWriter.</code>
+     * </p>
+     *
+     * @param driver
+     *            The JDBC Driver instance to use for this pool.
+     */
+    public synchronized void setDriver(final Driver driver) {
+        this.driver = driver;
+    }
 
     /**
      * <p>
-     * Creates (if necessary) and return the internal data source we are using to manage our connections.
+     * Sets the class loader to be used to load the JDBC driver.
+     * </p>
+     * <p>
+     * Note: this method currently has no effect once the pool has been initialized. The pool is initialized the first
+     * time one of the following methods is invoked: <code>getConnection, setLogwriter,
+     * setLoginTimeout, getLoginTimeout, getLogWriter.</code>
      * </p>
      *
-     * @return The current internal DataSource or a newly created instance if it has not yet been created.
-     * @throws SQLException
-     *             if the object pool cannot be created.
+     * @param driverClassLoader
+     *            the class loader with which to load the JDBC driver
      */
-    protected DataSource createDataSource() throws SQLException {
-        if (closed) {
-            throw new SQLException("Data source is closed");
-        }
+    public synchronized void setDriverClassLoader(final ClassLoader driverClassLoader) {
+        this.driverClassLoader = driverClassLoader;
+    }
 
-        // Return the pool if we have already created it
-        // This is double-checked locking. This is safe since dataSource is
-        // volatile and the code is targeted at Java 5 onwards.
-        if (dataSource != null) {
-            return dataSource;
-        }
-        synchronized (this) {
-            if (dataSource != null) {
-                return dataSource;
-            }
+    /**
+     * <p>
+     * Sets the JDBC driver class name.
+     * </p>
+     * <p>
+     * Note: this method currently has no effect once the pool has been initialized. The pool is initialized the first
+     * time one of the following methods is invoked: <code>getConnection, setLogwriter,
+     * setLoginTimeout, getLoginTimeout, getLogWriter.</code>
+     * </p>
+     *
+     * @param driverClassName
+     *            the class name of the JDBC driver
+     */
+    public synchronized void setDriverClassName(final String driverClassName) {
+        if (driverClassName != null && driverClassName.trim().length() > 0) {
+            this.driverClassName = driverClassName;
+        } else {
+            this.driverClassName = null;
+        }
+    }
 
-            jmxRegister();
+    /**
+     * Sets the value of the flag that controls whether or not connections being returned to the pool will be checked
+     * and configured with {@link Connection#setAutoCommit(boolean) Connection.setAutoCommit(true)} if the auto commit
+     * setting is {@code false} when the connection is returned. It is <code>true</code> by default.
+     *
+     * @param autoCommitOnReturn
+     *            Whether or not connections being returned to the pool will be checked and configured with auto-commit.
+     * @deprecated Use {@link #setAutoCommitOnReturn(boolean)}.
+     */
+    @Deprecated
+    public void setEnableAutoCommitOnReturn(final boolean autoCommitOnReturn) {
+        this.autoCommitOnReturn = autoCommitOnReturn;
+    }
 
-            // create factory which returns raw physical connections
-            final ConnectionFactory driverConnectionFactory = createConnectionFactory();
+    /**
+     * Sets the EvictionPolicy implementation to use with this connection pool.
+     *
+     * @param evictionPolicyClassName
+     *            The fully qualified class name of the EvictionPolicy implementation
+     */
+    public synchronized void setEvictionPolicyClassName(final String evictionPolicyClassName) {
+        if (connectionPool != null) {
+            connectionPool.setEvictionPolicyClassName(evictionPolicyClassName);
+        }
+        this.evictionPolicyClassName = evictionPolicyClassName;
+    }
 
-            // Set up the poolable connection factory
-            boolean success = false;
-            PoolableConnectionFactory poolableConnectionFactory;
-            try {
-                poolableConnectionFactory = createPoolableConnectionFactory(driverConnectionFactory);
-                poolableConnectionFactory.setPoolStatements(poolPreparedStatements);
-                poolableConnectionFactory.setMaxOpenPreparedStatements(maxOpenPreparedStatements);
-                success = true;
-            } catch (final SQLException se) {
-                throw se;
-            } catch (final RuntimeException rte) {
-                throw rte;
-            } catch (final Exception ex) {
-                throw new SQLException("Error creating connection factory", ex);
-            }
+    /**
+     * @see #getFastFailValidation()
+     * @param fastFailValidation
+     *            true means connections created by this factory will fast fail validation
+     * @since 2.1
+     */
+    public void setFastFailValidation(final boolean fastFailValidation) {
+        this.fastFailValidation = fastFailValidation;
+    }
 
-            if (success) {
-                // create a pool for our connections
-                createConnectionPool(poolableConnectionFactory);
-            }
+    /**
+     * <p>
+     * Sets the initial size of the connection pool.
+     * </p>
+     * <p>
+     * Note: this method currently has no effect once the pool has been initialized. The pool is initialized the first
+     * time one of the following methods is invoked: <code>getConnection, setLogwriter,
+     * setLoginTimeout, getLoginTimeout, getLogWriter.</code>
+     * </p>
+     *
+     * @param initialSize
+     *            the number of connections created when the pool is initialized
+     */
+    public synchronized void setInitialSize(final int initialSize) {
+        this.initialSize = initialSize;
+    }
 
-            // Create the pooling data source to manage connections
-            DataSource newDataSource;
-            success = false;
-            try {
-                newDataSource = createDataSourceInstance();
-                newDataSource.setLogWriter(logWriter);
-                success = true;
-            } catch (final SQLException se) {
-                throw se;
-            } catch (final RuntimeException rte) {
-                throw rte;
-            } catch (final Exception ex) {
-                throw new SQLException("Error creating datasource", ex);
-            } finally {
-                if (!success) {
-                    closeConnectionPool();
-                }
-            }
+    /**
+     * Sets the JMX name that has been requested for this DataSource. If the requested name is not valid, an alternative
+     * may be chosen. This DataSource will attempt to register itself using this name. If another component registers
+     * this DataSource with JMX and this name is valid this name will be used in preference to any specified by the
+     * other component.
+     *
+     * @param jmxName
+     *            The JMX name that has been requested for this DataSource
+     */
+    public void setJmxName(final String jmxName) {
+        this.jmxName = jmxName;
+    }
 
-            // If initialSize > 0, preload the pool
-            try {
-                for (int i = 0; i < initialSize; i++) {
-                    connectionPool.addObject();
-                }
-            } catch (final Exception e) {
-                closeConnectionPool();
-                throw new SQLException("Error preloading the connection pool", e);
-            }
+    /**
+     * Sets the LIFO property. True means the pool behaves as a LIFO queue; false means FIFO.
+     *
+     * @param lifo
+     *            the new value for the LIFO property
+     */
+    public synchronized void setLifo(final boolean lifo) {
+        this.lifo = lifo;
+        if (connectionPool != null) {
+            connectionPool.setLifo(lifo);
+        }
+    }
+
+    /**
+     * @param logAbandoned
+     *            new logAbandoned property value
+     */
+    public void setLogAbandoned(final boolean logAbandoned) {
+        if (abandonedConfig == null) {
+            abandonedConfig = new AbandonedConfig();
+        }
+        abandonedConfig.setLogAbandoned(logAbandoned);
+        final GenericObjectPool<?> gop = this.connectionPool;
+        if (gop != null) {
+            gop.setAbandonedConfig(abandonedConfig);
+        }
+    }
+
+    /**
+     * When {@link #getMaxConnLifetimeMillis()} is set to limit connection lifetime, this property determines whether or
+     * not log messages are generated when the pool closes connections due to maximum lifetime exceeded. Set this
+     * property to false to suppress log messages when connections expire.
+     *
+     * @param logExpiredConnections
+     *            Whether or not log messages are generated when the pool closes connections due to maximum lifetime
+     *            exceeded.
+     */
+    public void setLogExpiredConnections(final boolean logExpiredConnections) {
+        this.logExpiredConnections = logExpiredConnections;
+    }
+
+    /**
+     * <strong>BasicDataSource does NOT support this method. </strong>
+     *
+     * <p>
+     * Set the login timeout (in seconds) for connecting to the database.
+     * </p>
+     * <p>
+     * Calls {@link #createDataSource()}, so has the side effect of initializing the connection pool.
+     * </p>
+     *
+     * @param loginTimeout
+     *            The new login timeout, or zero for no timeout
+     * @throws UnsupportedOperationException
+     *             If the DataSource implementation does not support the login timeout feature.
+     * @throws SQLException
+     *             if a database access error occurs
+     */
+    @Override
+    public void setLoginTimeout(final int loginTimeout) throws SQLException {
+        // This method isn't supported by the PoolingDataSource returned by the createDataSource
+        throw new UnsupportedOperationException("Not supported by BasicDataSource");
+    }
+
+    /**
+     * <p>
+     * Sets the log writer being used by this data source.
+     * </p>
+     * <p>
+     * Calls {@link #createDataSource()}, so has the side effect of initializing the connection pool.
+     * </p>
+     *
+     * @param logWriter
+     *            The new log writer
+     * @throws SQLException
+     *             if a database access error occurs
+     */
+    @Override
+    public void setLogWriter(final PrintWriter logWriter) throws SQLException {
+        createDataSource().setLogWriter(logWriter);
+        this.logWriter = logWriter;
+    }
+
+    /**
+     * <p>
+     * Sets the maximum permitted lifetime of a connection in milliseconds. A value of zero or less indicates an
+     * infinite lifetime.
+     * </p>
+     * <p>
+     * Note: this method currently has no effect once the pool has been initialized. The pool is initialized the first
+     * time one of the following methods is invoked: <code>getConnection, setLogwriter,
+     * setLoginTimeout, getLoginTimeout, getLogWriter.</code>
+     * </p>
+     *
+     * @param maxConnLifetimeMillis
+     *            The maximum permitted lifetime of a connection in milliseconds.
+     */
+    public void setMaxConnLifetimeMillis(final long maxConnLifetimeMillis) {
+        this.maxConnLifetimeMillis = maxConnLifetimeMillis;
+    }
+
+    /**
+     * Sets the maximum number of connections that can remain idle in the pool. Excess idle connections are destroyed on
+     * return to the pool.
+     *
+     * @see #getMaxIdle()
+     * @param maxIdle
+     *            the new value for maxIdle
+     */
+    public synchronized void setMaxIdle(final int maxIdle) {
+        this.maxIdle = maxIdle;
+        if (connectionPool != null) {
+            connectionPool.setMaxIdle(maxIdle);
+        }
+    }
+
+    /**
+     * <p>
+     * Sets the value of the <code>maxOpenPreparedStatements</code> property.
+     * </p>
+     * <p>
+     * Note: this method currently has no effect once the pool has been initialized. The pool is initialized the first
+     * time one of the following methods is invoked: <code>getConnection, setLogwriter,
+     * setLoginTimeout, getLoginTimeout, getLogWriter.</code>
+     * </p>
+     *
+     * @param maxOpenStatements
+     *            the new maximum number of prepared statements
+     */
+    public synchronized void setMaxOpenPreparedStatements(final int maxOpenStatements) {
+        this.maxOpenPreparedStatements = maxOpenStatements;
+    }
+
+    /**
+     * Sets the maximum total number of idle and borrows connections that can be active at the same time. Use a negative
+     * value for no limit.
+     *
+     * @param maxTotal
+     *            the new value for maxTotal
+     * @see #getMaxTotal()
+     */
+    public synchronized void setMaxTotal(final int maxTotal) {
+        this.maxTotal = maxTotal;
+        if (connectionPool != null) {
+            connectionPool.setMaxTotal(maxTotal);
+        }
+    }
+
+    /**
+     * Sets the MaxWaitMillis property. Use -1 to make the pool wait indefinitely.
+     *
+     * @param maxWaitMillis
+     *            the new value for MaxWaitMillis
+     * @see #getMaxWaitMillis()
+     */
+    public synchronized void setMaxWaitMillis(final long maxWaitMillis) {
+        this.maxWaitMillis = maxWaitMillis;
+        if (connectionPool != null) {
+            connectionPool.setMaxWaitMillis(maxWaitMillis);
+        }
+    }
 
-            // If timeBetweenEvictionRunsMillis > 0, start the pool's evictor task
-            startPoolMaintenance();
+    /**
+     * Sets the {@link #minEvictableIdleTimeMillis} property.
+     *
+     * @param minEvictableIdleTimeMillis
+     *            the minimum amount of time an object may sit idle in the pool
+     * @see #minEvictableIdleTimeMillis
+     */
+    public synchronized void setMinEvictableIdleTimeMillis(final long minEvictableIdleTimeMillis) {
+        this.minEvictableIdleTimeMillis = minEvictableIdleTimeMillis;
+        if (connectionPool != null) {
+            connectionPool.setMinEvictableIdleTimeMillis(minEvictableIdleTimeMillis);
+        }
+    }
 
-            dataSource = newDataSource;
-            return dataSource;
+    /**
+     * Sets the minimum number of idle connections in the pool. The pool attempts to ensure that minIdle connections are
+     * available when the idle object evictor runs. The value of this property has no effect unless
+     * {@link #timeBetweenEvictionRunsMillis} has a positive value.
+     *
+     * @param minIdle
+     *            the new value for minIdle
+     * @see GenericObjectPool#setMinIdle(int)
+     */
+    public synchronized void setMinIdle(final int minIdle) {
+        this.minIdle = minIdle;
+        if (connectionPool != null) {
+            connectionPool.setMinIdle(minIdle);
         }
     }
 
     /**
-     * Creates a JDBC connection factory for this datasource. The JDBC driver is loaded using the following algorithm:
-     * <ol>
-     * <li>If a Driver instance has been specified via {@link #setDriver(Driver)} use it</li>
-     * <li>If no Driver instance was specified and {@link #driverClassName} is specified that class is loaded using the
-     * {@link ClassLoader} of this class or, if {@link #driverClassLoader} is set, {@link #driverClassName} is loaded
-     * with the specified {@link ClassLoader}.</li>
-     * <li>If {@link #driverClassName} is specified and the previous attempt fails, the class is loaded using the
-     * context class loader of the current thread.</li>
-     * <li>If a driver still isn't loaded one is loaded via the {@link DriverManager} using the specified {@link #url}.
-     * </ol>
-     * This method exists so subclasses can replace the implementation class.
+     * Sets the value of the {@link #numTestsPerEvictionRun} property.
      *
-     * @return A new connection factory.
+     * @param numTestsPerEvictionRun
+     *            the new {@link #numTestsPerEvictionRun} value
+     * @see #numTestsPerEvictionRun
+     */
+    public synchronized void setNumTestsPerEvictionRun(final int numTestsPerEvictionRun) {
+        this.numTestsPerEvictionRun = numTestsPerEvictionRun;
+        if (connectionPool != null) {
+            connectionPool.setNumTestsPerEvictionRun(numTestsPerEvictionRun);
+        }
+    }
+
+    // ------------------------------------------------------ Protected Methods
+
+    /**
+     * <p>
+     * Sets the {@link #password}.
+     * </p>
+     * <p>
+     * Note: this method currently has no effect once the pool has been initialized. The pool is initialized the first
+     * time one of the following methods is invoked: <code>getConnection, setLogwriter,
+     * setLoginTimeout, getLoginTimeout, getLogWriter.</code>
+     * </p>
      *
-     * @throws SQLException
-     *            If the connection factort cannot be created
+     * @param password
+     *            new value for the password
      */
-    protected ConnectionFactory createConnectionFactory() throws SQLException {
-        // Load the JDBC driver class
-        Driver driverToUse = this.driver;
+    public void setPassword(final String password) {
+        this.password = password;
+    }
 
-        if (driverToUse == null) {
-            Class<?> driverFromCCL = null;
-            if (driverClassName != null) {
-                try {
-                    try {
-                        if (driverClassLoader == null) {
-                            driverFromCCL = Class.forName(driverClassName);
-                        } else {
-                            driverFromCCL = Class.forName(driverClassName, true, driverClassLoader);
-                        }
-                    } catch (final ClassNotFoundException cnfe) {
-                        driverFromCCL = Thread.currentThread().getContextClassLoader().loadClass(driverClassName);
-                    }
-                } catch (final Exception t) {
-                    final String message = "Cannot load JDBC driver class '" + driverClassName + "'";
-                    logWriter.println(message);
-                    t.printStackTrace(logWriter);
-                    throw new SQLException(message, t);
-                }
-            }
+    /**
+     * <p>
+     * Sets whether to pool statements or not.
+     * </p>
+     * <p>
+     * Note: this method currently has no effect once the pool has been initialized. The pool is initialized the first
+     * time one of the following methods is invoked: <code>getConnection, setLogwriter,
+     * setLoginTimeout, getLoginTimeout, getLogWriter.</code>
+     * </p>
+     *
+     * @param poolingStatements
+     *            pooling on or off
+     */
+    public synchronized void setPoolPreparedStatements(final boolean poolingStatements) {
+        this.poolPreparedStatements = poolingStatements;
+    }
 
-            try {
-                if (driverFromCCL == null) {
-                    driverToUse = DriverManager.getDriver(url);
-                } else {
-                    // Usage of DriverManager is not possible, as it does not
-                    // respect the ContextClassLoader
-                    // N.B. This cast may cause ClassCastException which is handled below
-                    driverToUse = (Driver) driverFromCCL.getConstructor().newInstance();
-                    if (!driverToUse.acceptsURL(url)) {
-                        throw new SQLException("No suitable driver", "08001");
-                    }
-                }
-            } catch (final Exception t) {
-                final String message = "Cannot create JDBC driver of class '"
-                        + (driverClassName != null ? driverClassName : "") + "' for connect URL '" + url + "'";
-                logWriter.println(message);
-                t.printStackTrace(logWriter);
-                throw new SQLException(message, t);
-            }
+    /**
+     * @param removeAbandonedOnBorrow
+     *            true means abandoned connections may be removed when connections are borrowed from the pool.
+     * @see #getRemoveAbandonedOnBorrow()
+     */
+    public void setRemoveAbandonedOnBorrow(final boolean removeAbandonedOnBorrow) {
+        if (abandonedConfig == null) {
+            abandonedConfig = new AbandonedConfig();
         }
-
-        // Set up the driver connection factory we will use
-        final String user = userName;
-        if (user != null) {
-            connectionProperties.put("user", user);
-        } else {
-            log("DBCP DataSource configured without a 'username'");
+        abandonedConfig.setRemoveAbandonedOnBorrow(removeAbandonedOnBorrow);
+        final GenericObjectPool<?> gop = this.connectionPool;
+        if (gop != null) {
+            gop.setAbandonedConfig(abandonedConfig);
         }
+    }
 
-        final String pwd = password;
-        if (pwd != null) {
-            connectionProperties.put("password", pwd);
-        } else {
-            log("DBCP DataSource configured without a 'password'");
+    /**
+     * @param removeAbandonedOnMaintenance
+     *            true means abandoned connections may be removed on pool maintenance.
+     * @see #getRemoveAbandonedOnMaintenance()
+     */
+    public void setRemoveAbandonedOnMaintenance(final boolean removeAbandonedOnMaintenance) {
+        if (abandonedConfig == null) {
+            abandonedConfig = new AbandonedConfig();
+        }
+        abandonedConfig.setRemoveAbandonedOnMaintenance(removeAbandonedOnMaintenance);
+        final GenericObjectPool<?> gop = this.connectionPool;
+        if (gop != null) {
+            gop.setAbandonedConfig(abandonedConfig);
         }
-
-        final ConnectionFactory driverConnectionFactory = new DriverConnectionFactory(driverToUse, url,
-                connectionProperties);
-        return driverConnectionFactory;
     }
 
     /**
-     * Creates a connection pool for this datasource. This method only exists so subclasses can replace the
-     * implementation class.
      * <p>
-     * This implementation configures all pool properties other than timeBetweenEvictionRunsMillis. Setting that
-     * property is deferred to {@link #startPoolMaintenance()}, since setting timeBetweenEvictionRunsMillis to a
-     * positive value causes {@link GenericObjectPool}'s eviction timer to be started.
+     * Sets the timeout in seconds before an abandoned connection can be removed.
      * </p>
      *
-     * @param factory
-     *            The factory to use to create new connections for this pool.
+     * <p>
+     * Setting this property has no effect if {@link #getRemoveAbandonedOnBorrow()} and
+     * {@link #getRemoveAbandonedOnMaintenance()} are false.
+     * </p>
+     *
+     * @param removeAbandonedTimeout
+     *            new abandoned timeout in seconds
+     * @see #getRemoveAbandonedTimeout()
+     * @see #getRemoveAbandonedOnBorrow()
+     * @see #getRemoveAbandonedOnMaintenance()
      */
-    protected void createConnectionPool(final PoolableConnectionFactory factory) {
-        // Create an object pool to contain our active connections
-        final GenericObjectPoolConfig<PoolableConnection> config = new GenericObjectPoolConfig<>();
-        updateJmxName(config);
-        // Disable JMX on the underlying pool if the DS is not registered:
-        config.setJmxEnabled(registeredJmxObjectName != null);
-        final GenericObjectPool<PoolableConnection> gop = createObjectPool(factory, config, abandonedConfig);
-        gop.setMaxTotal(maxTotal);
-        gop.setMaxIdle(maxIdle);
-        gop.setMinIdle(minIdle);
-        gop.setMaxWaitMillis(maxWaitMillis);
-        gop.setTestOnCreate(testOnCreate);
-        gop.setTestOnBorrow(testOnBorrow);
-        gop.setTestOnReturn(testOnReturn);
-        gop.setNumTestsPerEvictionRun(numTestsPerEvictionRun);
-        gop.setMinEvictableIdleTimeMillis(minEvictableIdleTimeMillis);
-        gop.setSoftMinEvictableIdleTimeMillis(softMinEvictableIdleTimeMillis);
-        gop.setTestWhileIdle(testWhileIdle);
-        gop.setLifo(lifo);
-        gop.setSwallowedExceptionListener(new SwallowedExceptionLogger(log, logExpiredConnections));
-        gop.setEvictionPolicyClassName(evictionPolicyClassName);
-        factory.setPool(gop);
-        connectionPool = gop;
+    public void setRemoveAbandonedTimeout(final int removeAbandonedTimeout) {
+        if (abandonedConfig == null) {
+            abandonedConfig = new AbandonedConfig();
+        }
+        abandonedConfig.setRemoveAbandonedTimeout(removeAbandonedTimeout);
+        final GenericObjectPool<?> gop = this.connectionPool;
+        if (gop != null) {
+            gop.setAbandonedConfig(abandonedConfig);
+        }
     }
 
     /**
-     * Creates an object pool used to provide pooling support for {@link Connection JDBC connections}.
+     * Sets the flag that controls if a connection will be rolled back when it is returned to the pool if auto commit is
+     * not enabled and the connection is not read only.
      *
-     * @param factory
-     *            the object factory
-     * @param poolConfig
-     *            the object pool configuration
-     * @param abandonedConfig
-     *            the abandoned objects configuration
-     * @return a non-null instance
+     * @param rollbackOnReturn
+     *            whether a connection will be rolled back when it is returned to the pool.
      */
-    protected GenericObjectPool<PoolableConnection> createObjectPool(final PoolableConnectionFactory factory,
-            final GenericObjectPoolConfig<PoolableConnection> poolConfig, final AbandonedConfig abandonedConfig) {
-        GenericObjectPool<PoolableConnection> gop;
-        if (abandonedConfig != null && (abandonedConfig.getRemoveAbandonedOnBorrow()
-                || abandonedConfig.getRemoveAbandonedOnMaintenance())) {
-            gop = new GenericObjectPool<>(factory, poolConfig, abandonedConfig);
-        } else {
-            gop = new GenericObjectPool<>(factory, poolConfig);
+    public void setRollbackOnReturn(final boolean rollbackOnReturn) {
+        this.rollbackOnReturn = rollbackOnReturn;
+    }
+
+    /**
+     * Sets the minimum amount of time a connection may sit idle in the pool before it is eligible for eviction by the
+     * idle object evictor, with the extra condition that at least "minIdle" connections remain in the pool.
+     *
+     * @param softMinEvictableIdleTimeMillis
+     *            minimum amount of time a connection may sit idle in the pool before it is eligible for eviction,
+     *            assuming there are minIdle idle connections in the pool.
+     * @see #getSoftMinEvictableIdleTimeMillis
+     */
+    public synchronized void setSoftMinEvictableIdleTimeMillis(final long softMinEvictableIdleTimeMillis) {
+        this.softMinEvictableIdleTimeMillis = softMinEvictableIdleTimeMillis;
+        if (connectionPool != null) {
+            connectionPool.setSoftMinEvictableIdleTimeMillis(softMinEvictableIdleTimeMillis);
         }
-        return gop;
     }
 
     /**
-     * Closes the connection pool, silently swallowing any exception that occurs.
+     * Sets the {@link #testOnBorrow} property. This property determines whether or not the pool will validate objects
+     * before they are borrowed from the pool.
+     *
+     * @param testOnBorrow
+     *            new value for testOnBorrow property
      */
-    private void closeConnectionPool() {
-        final GenericObjectPool<?> oldPool = connectionPool;
-        connectionPool = null;
-        try {
-            if (oldPool != null) {
-                oldPool.close();
-            }
-        } catch (final Exception e) {
-            /* Ignore */
+    public synchronized void setTestOnBorrow(final boolean testOnBorrow) {
+        this.testOnBorrow = testOnBorrow;
+        if (connectionPool != null) {
+            connectionPool.setTestOnBorrow(testOnBorrow);
         }
     }
 
     /**
-     * Starts the connection pool maintenance task, if configured.
+     * Sets the {@link #testOnCreate} property. This property determines whether or not the pool will validate objects
+     * immediately after they are created by the pool
+     *
+     * @param testOnCreate
+     *            new value for testOnCreate property
      */
-    protected void startPoolMaintenance() {
-        if (connectionPool != null && timeBetweenEvictionRunsMillis > 0) {
-            connectionPool.setTimeBetweenEvictionRunsMillis(timeBetweenEvictionRunsMillis);
+    public synchronized void setTestOnCreate(final boolean testOnCreate) {
+        this.testOnCreate = testOnCreate;
+        if (connectionPool != null) {
+            connectionPool.setTestOnCreate(testOnCreate);
         }
     }
 
     /**
-     * Creates the actual data source instance. This method only exists so that subclasses can replace the
-     * implementation class.
-     *
-     * @throws SQLException
-     *             if unable to create a datasource instance
+     * Sets the <code>testOnReturn</code> property. This property determines whether or not the pool will validate
+     * objects before they are returned to the pool.
      *
-     * @return A new DataSource instance
+     * @param testOnReturn
+     *            new value for testOnReturn property
      */
-    protected DataSource createDataSourceInstance() throws SQLException {
-        final PoolingDataSource<PoolableConnection> pds = new PoolingDataSource<>(connectionPool);
-        pds.setAccessToUnderlyingConnectionAllowed(isAccessToUnderlyingConnectionAllowed());
-        return pds;
+    public synchronized void setTestOnReturn(final boolean testOnReturn) {
+        this.testOnReturn = testOnReturn;
+        if (connectionPool != null) {
+            connectionPool.setTestOnReturn(testOnReturn);
+        }
     }
 
     /**
-     * Creates the PoolableConnectionFactory and attaches it to the connection pool. This method only exists so
-     * subclasses can replace the default implementation.
-     *
-     * @param driverConnectionFactory
-     *            JDBC connection factory
-     * @throws SQLException
-     *             if an error occurs creating the PoolableConnectionFactory
+     * Sets the <code>testWhileIdle</code> property. This property determines whether or not the idle object evictor
+     * will validate connections.
      *
-     * @return A new PoolableConnectionFactory configured with the current configuration of this BasicDataSource
+     * @param testWhileIdle
+     *            new value for testWhileIdle property
      */
-    protected PoolableConnectionFactory createPoolableConnectionFactory(final ConnectionFactory driverConnectionFactory)
-            throws SQLException {
-        PoolableConnectionFactory connectionFactory = null;
-        try {
-            connectionFactory = new PoolableConnectionFactory(driverConnectionFactory,
-                    ObjectNameWrapper.unwrap(registeredJmxObjectName));
-            connectionFactory.setValidationQuery(validationQuery);
-            connectionFactory.setValidationQueryTimeout(validationQueryTimeoutSeconds);
-            connectionFactory.setConnectionInitSql(connectionInitSqls);
-            connectionFactory.setDefaultReadOnly(defaultReadOnly);
-            connectionFactory.setDefaultAutoCommit(defaultAutoCommit);
-            connectionFactory.setDefaultTransactionIsolation(defaultTransactionIsolation);
-            connectionFactory.setDefaultCatalog(defaultCatalog);
-            connectionFactory.setDefaultSchema(defaultSchema);
-            connectionFactory.setCacheState(cacheState);
-            connectionFactory.setPoolStatements(poolPreparedStatements);
-            connectionFactory.setMaxOpenPreparedStatements(maxOpenPreparedStatements);
-            connectionFactory.setMaxConnLifetimeMillis(maxConnLifetimeMillis);
-            connectionFactory.setRollbackOnReturn(getRollbackOnReturn());
-            connectionFactory.setEnableAutoCommitOnReturn(getEnableAutoCommitOnReturn());
-            connectionFactory.setDefaultQueryTimeout(getDefaultQueryTimeout());
-            connectionFactory.setFastFailValidation(fastFailValidation);
-            connectionFactory.setDisconnectionSqlCodes(disconnectionSqlCodes);
-            validateConnectionFactory(connectionFactory);
-        } catch (final RuntimeException e) {
-            throw e;
-        } catch (final Exception e) {
-            throw new SQLException("Cannot create PoolableConnectionFactory (" + e.getMessage() + ")", e);
+    public synchronized void setTestWhileIdle(final boolean testWhileIdle) {
+        this.testWhileIdle = testWhileIdle;
+        if (connectionPool != null) {
+            connectionPool.setTestWhileIdle(testWhileIdle);
         }
-        return connectionFactory;
     }
 
-    protected static void validateConnectionFactory(final PoolableConnectionFactory connectionFactory)
-            throws Exception {
-        PoolableConnection conn = null;
-        PooledObject<PoolableConnection> p = null;
-        try {
-            p = connectionFactory.makeObject();
-            conn = p.getObject();
-            connectionFactory.activateObject(p);
-            connectionFactory.validateConnection(conn);
-            connectionFactory.passivateObject(p);
-        } finally {
-            if (p != null) {
-                connectionFactory.destroyObject(p);
-            }
+    /**
+     * Sets the {@link #timeBetweenEvictionRunsMillis} property.
+     *
+     * @param timeBetweenEvictionRunsMillis
+     *            the new time between evictor runs
+     * @see #timeBetweenEvictionRunsMillis
+     */
+    public synchronized void setTimeBetweenEvictionRunsMillis(final long timeBetweenEvictionRunsMillis) {
+        this.timeBetweenEvictionRunsMillis = timeBetweenEvictionRunsMillis;
+        if (connectionPool != null) {
+            connectionPool.setTimeBetweenEvictionRunsMillis(timeBetweenEvictionRunsMillis);
         }
     }
 
-    protected void log(final String message) {
-        if (logWriter != null) {
-            logWriter.println(message);
-        }
+    /**
+     * <p>
+     * Sets the {@link #url}.
+     * </p>
+     * <p>
+     * Note: this method currently has no effect once the pool has been initialized. The pool is initialized the first
+     * time one of the following methods is invoked: <code>getConnection, setLogwriter,
+     * setLoginTimeout, getLoginTimeout, getLogWriter.</code>
+     * </p>
+     *
+     * @param url
+     *            the new value for the JDBC connection url
+     */
+    public synchronized void setUrl(final String url) {
+        this.url = url;
     }
 
     /**
-     * Actual name under which this component has been registered.
+     * <p>
+     * Sets the {@link #userName}.
+     * </p>
+     * <p>
+     * Note: this method currently has no effect once the pool has been initialized. The pool is initialized the first
+     * time one of the following methods is invoked: <code>getConnection, setLogwriter,
+     * setLoginTimeout, getLoginTimeout, getLogWriter.</code>
+     * </p>
+     *
+     * @param userName
+     *            the new value for the JDBC connection user name
      */
-    private ObjectNameWrapper registeredJmxObjectName;
-
-    private void jmxRegister() {
-        // Return immediately if this DataSource has already been registered
-        if (registeredJmxObjectName != null) {
-            return;
-        }
-        // Return immediately if no JMX name has been specified
-        final String requestedName = getJmxName();
-        if (requestedName == null) {
-            return;
-        }
-        try {
-            ObjectNameWrapper.wrap(requestedName).registerMBean(this);
-        } catch (final MalformedObjectNameException e) {
-            log.warn("The requested JMX name [" + requestedName + "] was not valid and will be ignored.");
-        }
+    public void setUsername(final String userName) {
+        this.userName = userName;
     }
 
-    @Override
-    public ObjectName preRegister(final MBeanServer server, final ObjectName objectName) {
-        final String requestedName = getJmxName();
-        if (requestedName != null) {
-            try {
-                registeredJmxObjectName = ObjectNameWrapper.wrap(requestedName);
-            } catch (final MalformedObjectNameException e) {
-                log.warn("The requested JMX name [" + requestedName + "] was not valid and will be ignored.");
-            }
-        }
-        if (registeredJmxObjectName == null) {
-            registeredJmxObjectName = ObjectNameWrapper.wrap(objectName);
+    /**
+     * <p>
+     * Sets the {@link #validationQuery}.
+     * </p>
+     * <p>
+     * Note: this method currently has no effect once the pool has been initialized. The pool is initialized the first
+     * time one of the following methods is invoked: <code>getConnection, setLogwriter,
+     * setLoginTimeout, getLoginTimeout, getLogWriter.</code>
+     * </p>
+     *
+     * @param validationQuery
+     *            the new value for the validation query
+     */
+    public void setValidationQuery(final String validationQuery) {
+        if (validationQuery != null && validationQuery.trim().length() > 0) {
+            this.validationQuery = validationQuery;
+        } else {
+            this.validationQuery = null;
         }
-        return ObjectNameWrapper.unwrap(registeredJmxObjectName);
     }
 
-    @Override
-    public void postRegister(final Boolean registrationDone) {
-        // NO-OP
+    /**
+     * Sets the validation query timeout, the amount of time, in seconds, that connection validation will wait for a
+     * response from the database when executing a validation query. Use a value less than or equal to 0 for no timeout.
+     * <p>
+     * Note: this method currently has no effect once the pool has been initialized. The pool is initialized the first
+     * time one of the following methods is invoked: <code>getConnection, setLogwriter,
+     * setLoginTimeout, getLoginTimeout, getLogWriter.</code>
+     * </p>
+     *
+     * @param validationQueryTimeoutSeconds
+     *            new validation query timeout value in seconds
+     */
+    public void setValidationQueryTimeout(final int validationQueryTimeoutSeconds) {
+        this.validationQueryTimeoutSeconds = validationQueryTimeoutSeconds;
     }
 
-    @Override
-    public void preDeregister() throws Exception {
-        // NO-OP
+    /**
+     * Starts the connection pool maintenance task, if configured.
+     */
+    protected void startPoolMaintenance() {
+        if (connectionPool != null && timeBetweenEvictionRunsMillis > 0) {
+            connectionPool.setTimeBetweenEvictionRunsMillis(timeBetweenEvictionRunsMillis);
+        }
     }
 
     @Override
-    public void postDeregister() {
-        // NO-OP
+    public <T> T unwrap(final Class<T> iface) throws SQLException {
+        throw new SQLException("BasicDataSource is not a wrapper.");
     }
 
     private void updateJmxName(final GenericObjectPoolConfig<?> config) {
@@ -2472,19 +2527,4 @@ public class BasicDataSource implements DataSource, BasicDataSourceMXBean, MBean
         config.setJmxNameBase(base.toString());
         config.setJmxNamePrefix(Constants.JMX_CONNECTION_POOL_PREFIX);
     }
-
-    protected ObjectName getRegisteredJmxName() {
-        return ObjectNameWrapper.unwrap(registeredJmxObjectName);
-    }
-
-    /**
-     * @since 2.0
-     */
-    private class PaGetConnection implements PrivilegedExceptionAction<Connection> {
-
-        @Override
-        public Connection run() throws SQLException {
-            return createDataSource().getConnection();
-        }
-    }
 }
diff --git a/java/org/apache/tomcat/dbcp/dbcp2/BasicDataSourceFactory.java b/java/org/apache/tomcat/dbcp/dbcp2/BasicDataSourceFactory.java
index 200a8fe..eff9a7c 100644
--- a/java/org/apache/tomcat/dbcp/dbcp2/BasicDataSourceFactory.java
+++ b/java/org/apache/tomcat/dbcp/dbcp2/BasicDataSourceFactory.java
@@ -59,54 +59,54 @@ public class BasicDataSourceFactory implements ObjectFactory {
 
     private static final Log log = LogFactory.getLog(BasicDataSourceFactory.class);
 
-    private static final String PROP_DEFAULTAUTOCOMMIT = "defaultAutoCommit";
-    private static final String PROP_DEFAULTREADONLY = "defaultReadOnly";
-    private static final String PROP_DEFAULTTRANSACTIONISOLATION = "defaultTransactionIsolation";
-    private static final String PROP_DEFAULTCATALOG = "defaultCatalog";
-    private static final String PROP_DEFAULTSCHEMA = "defaultSchema";
-    private static final String PROP_CACHESTATE = "cacheState";
-    private static final String PROP_DRIVERCLASSNAME = "driverClassName";
+    private static final String PROP_DEFAULT_AUTO_COMMIT = "defaultAutoCommit";
+    private static final String PROP_DEFAULT_READ_ONLY = "defaultReadOnly";
+    private static final String PROP_DEFAULT_TRANSACTION_ISOLATION = "defaultTransactionIsolation";
+    private static final String PROP_DEFAULT_CATALOG = "defaultCatalog";
+    private static final String PROP_DEFAULT_SCHEMA = "defaultSchema";
+    private static final String PROP_CACHE_STATE = "cacheState";
+    private static final String PROP_DRIVER_CLASS_NAME = "driverClassName";
     private static final String PROP_LIFO = "lifo";
-    private static final String PROP_MAXTOTAL = "maxTotal";
-    private static final String PROP_MAXIDLE = "maxIdle";
-    private static final String PROP_MINIDLE = "minIdle";
-    private static final String PROP_INITIALSIZE = "initialSize";
-    private static final String PROP_MAXWAITMILLIS = "maxWaitMillis";
-    private static final String PROP_TESTONCREATE = "testOnCreate";
-    private static final String PROP_TESTONBORROW = "testOnBorrow";
-    private static final String PROP_TESTONRETURN = "testOnReturn";
-    private static final String PROP_TIMEBETWEENEVICTIONRUNSMILLIS = "timeBetweenEvictionRunsMillis";
-    private static final String PROP_NUMTESTSPEREVICTIONRUN = "numTestsPerEvictionRun";
-    private static final String PROP_MINEVICTABLEIDLETIMEMILLIS = "minEvictableIdleTimeMillis";
-    private static final String PROP_SOFTMINEVICTABLEIDLETIMEMILLIS = "softMinEvictableIdleTimeMillis";
-    private static final String PROP_EVICTIONPOLICYCLASSNAME = "evictionPolicyClassName";
-    private static final String PROP_TESTWHILEIDLE = "testWhileIdle";
+    private static final String PROP_MAX_TOTAL = "maxTotal";
+    private static final String PROP_MAX_IDLE = "maxIdle";
+    private static final String PROP_MIN_IDLE = "minIdle";
+    private static final String PROP_INITIAL_SIZE = "initialSize";
+    private static final String PROP_MAX_WAIT_MILLIS = "maxWaitMillis";
+    private static final String PROP_TEST_ON_CREATE = "testOnCreate";
+    private static final String PROP_TEST_ON_BORROW = "testOnBorrow";
+    private static final String PROP_TEST_ON_RETURN = "testOnReturn";
+    private static final String PROP_TIME_BETWEEN_EVICTION_RUNS_MILLIS = "timeBetweenEvictionRunsMillis";
+    private static final String PROP_NUM_TESTS_PER_EVICTION_RUN = "numTestsPerEvictionRun";
+    private static final String PROP_MIN_EVICTABLE_IDLE_TIME_MILLIS = "minEvictableIdleTimeMillis";
+    private static final String PROP_SOFT_MIN_EVICTABLE_IDLE_TIME_MILLIS = "softMinEvictableIdleTimeMillis";
+    private static final String PROP_EVICTION_POLICY_CLASS_NAME = "evictionPolicyClassName";
+    private static final String PROP_TEST_WHILE_IDLE = "testWhileIdle";
     private static final String PROP_PASSWORD = "password";
     private static final String PROP_URL = "url";
-    private static final String PROP_USERNAME = "username";
-    private static final String PROP_VALIDATIONQUERY = "validationQuery";
-    private static final String PROP_VALIDATIONQUERY_TIMEOUT = "validationQueryTimeout";
+    private static final String PROP_USER_NAME = "username";
+    private static final String PROP_VALIDATION_QUERY = "validationQuery";
+    private static final String PROP_VALIDATION_QUERY_TIMEOUT = "validationQueryTimeout";
     private static final String PROP_JMX_NAME = "jmxName";
 
     /**
      * The property name for connectionInitSqls. The associated value String must be of the form [query;]*
      */
-    private static final String PROP_CONNECTIONINITSQLS = "connectionInitSqls";
-    private static final String PROP_ACCESSTOUNDERLYINGCONNECTIONALLOWED = "accessToUnderlyingConnectionAllowed";
-    private static final String PROP_REMOVEABANDONEDONBORROW = "removeAbandonedOnBorrow";
-    private static final String PROP_REMOVEABANDONEDONMAINTENANCE = "removeAbandonedOnMaintenance";
-    private static final String PROP_REMOVEABANDONEDTIMEOUT = "removeAbandonedTimeout";
-    private static final String PROP_LOGABANDONED = "logAbandoned";
-    private static final String PROP_ABANDONEDUSAGETRACKING = "abandonedUsageTracking";
-    private static final String PROP_POOLPREPAREDSTATEMENTS = "poolPreparedStatements";
-    private static final String PROP_MAXOPENPREPAREDSTATEMENTS = "maxOpenPreparedStatements";
-    private static final String PROP_CONNECTIONPROPERTIES = "connectionProperties";
-    private static final String PROP_MAXCONNLIFETIMEMILLIS = "maxConnLifetimeMillis";
-    private static final String PROP_LOGEXPIREDCONNECTIONS = "logExpiredConnections";
+    private static final String PROP_CONNECTION_INIT_SQLS = "connectionInitSqls";
+    private static final String PROP_ACCESS_TO_UNDERLYING_CONNECTION_ALLOWED = "accessToUnderlyingConnectionAllowed";
+    private static final String PROP_REMOVE_ABANDONED_ON_BORROW = "removeAbandonedOnBorrow";
+    private static final String PROP_REMOVE_ABANDONED_ON_MAINTENANCE = "removeAbandonedOnMaintenance";
+    private static final String PROP_REMOVE_ABANDONED_TIMEOUT = "removeAbandonedTimeout";
+    private static final String PROP_LOG_ABANDONED = "logAbandoned";
+    private static final String PROP_ABANDONED_USAGE_TRACKING = "abandonedUsageTracking";
+    private static final String PROP_POOL_PREPARED_STATEMENTS = "poolPreparedStatements";
+    private static final String PROP_MAX_OPEN_PREPARED_STATEMENTS = "maxOpenPreparedStatements";
+    private static final String PROP_CONNECTION_PROPERTIES = "connectionProperties";
+    private static final String PROP_MAX_CONN_LIFETIME_MILLIS = "maxConnLifetimeMillis";
+    private static final String PROP_LOG_EXPIRED_CONNECTIONS = "logExpiredConnections";
     private static final String PROP_ROLLBACK_ON_RETURN = "rollbackOnReturn";
-    private static final String PROP_ENABLE_AUTOCOMMIT_ON_RETURN = "enableAutoCommitOnReturn";
-    private static final String PROP_DEFAULT_QUERYTIMEOUT = "defaultQueryTimeout";
-    private static final String PROP_FASTFAIL_VALIDATION = "fastFailValidation";
+    private static final String PROP_ENABLE_AUTO_COMMIT_ON_RETURN = "enableAutoCommitOnReturn";
+    private static final String PROP_DEFAULT_QUERY_TIMEOUT = "defaultQueryTimeout";
+    private static final String PROP_FAST_FAIL_VALIDATION = "fastFailValidation";
 
     /**
      * Value string must be of the form [STATE_CODE,]*
@@ -117,31 +117,31 @@ public class BasicDataSourceFactory implements ObjectFactory {
      * Block with obsolete properties from DBCP 1.x. Warn users that these are ignored and they should use the 2.x
      * properties.
      */
-    private static final String NUPROP_MAXACTIVE = "maxActive";
-    private static final String NUPROP_REMOVEABANDONED = "removeAbandoned";
+    private static final String NUPROP_MAX_ACTIVE = "maxActive";
+    private static final String NUPROP_REMOVE_ABANDONED = "removeAbandoned";
     private static final String NUPROP_MAXWAIT = "maxWait";
 
     /*
      * Block with properties expected in a DataSource This props will not be listed as ignored - we know that they may
      * appear in Resource, and not listing them as ignored.
      */
-    private static final String SILENTPROP_FACTORY = "factory";
-    private static final String SILENTPROP_SCOPE = "scope";
-    private static final String SILENTPROP_SINGLETON = "singleton";
-    private static final String SILENTPROP_AUTH = "auth";
-
-    private static final String[] ALL_PROPERTIES = {PROP_DEFAULTAUTOCOMMIT, PROP_DEFAULTREADONLY,
-            PROP_DEFAULTTRANSACTIONISOLATION, PROP_DEFAULTCATALOG, PROP_DEFAULTSCHEMA, PROP_CACHESTATE,
-            PROP_DRIVERCLASSNAME, PROP_LIFO, PROP_MAXTOTAL, PROP_MAXIDLE, PROP_MINIDLE, PROP_INITIALSIZE,
-            PROP_MAXWAITMILLIS, PROP_TESTONCREATE, PROP_TESTONBORROW, PROP_TESTONRETURN,
-            PROP_TIMEBETWEENEVICTIONRUNSMILLIS, PROP_NUMTESTSPEREVICTIONRUN, PROP_MINEVICTABLEIDLETIMEMILLIS,
-            PROP_SOFTMINEVICTABLEIDLETIMEMILLIS, PROP_EVICTIONPOLICYCLASSNAME, PROP_TESTWHILEIDLE, PROP_PASSWORD,
-            PROP_URL, PROP_USERNAME, PROP_VALIDATIONQUERY, PROP_VALIDATIONQUERY_TIMEOUT, PROP_CONNECTIONINITSQLS,
-            PROP_ACCESSTOUNDERLYINGCONNECTIONALLOWED, PROP_REMOVEABANDONEDONBORROW, PROP_REMOVEABANDONEDONMAINTENANCE,
-            PROP_REMOVEABANDONEDTIMEOUT, PROP_LOGABANDONED, PROP_ABANDONEDUSAGETRACKING, PROP_POOLPREPAREDSTATEMENTS,
-            PROP_MAXOPENPREPAREDSTATEMENTS, PROP_CONNECTIONPROPERTIES, PROP_MAXCONNLIFETIMEMILLIS,
-            PROP_LOGEXPIREDCONNECTIONS, PROP_ROLLBACK_ON_RETURN, PROP_ENABLE_AUTOCOMMIT_ON_RETURN,
-            PROP_DEFAULT_QUERYTIMEOUT, PROP_FASTFAIL_VALIDATION, PROP_DISCONNECTION_SQL_CODES, PROP_JMX_NAME };
+    private static final String SILENT_PROP_FACTORY = "factory";
+    private static final String SILENT_PROP_SCOPE = "scope";
+    private static final String SILENT_PROP_SINGLETON = "singleton";
+    private static final String SILENT_PROP_AUTH = "auth";
+
+    private static final String[] ALL_PROPERTIES = {PROP_DEFAULT_AUTO_COMMIT, PROP_DEFAULT_READ_ONLY,
+            PROP_DEFAULT_TRANSACTION_ISOLATION, PROP_DEFAULT_CATALOG, PROP_DEFAULT_SCHEMA, PROP_CACHE_STATE,
+            PROP_DRIVER_CLASS_NAME, PROP_LIFO, PROP_MAX_TOTAL, PROP_MAX_IDLE, PROP_MIN_IDLE, PROP_INITIAL_SIZE,
+            PROP_MAX_WAIT_MILLIS, PROP_TEST_ON_CREATE, PROP_TEST_ON_BORROW, PROP_TEST_ON_RETURN,
+            PROP_TIME_BETWEEN_EVICTION_RUNS_MILLIS, PROP_NUM_TESTS_PER_EVICTION_RUN, PROP_MIN_EVICTABLE_IDLE_TIME_MILLIS,
+            PROP_SOFT_MIN_EVICTABLE_IDLE_TIME_MILLIS, PROP_EVICTION_POLICY_CLASS_NAME, PROP_TEST_WHILE_IDLE, PROP_PASSWORD,
+            PROP_URL, PROP_USER_NAME, PROP_VALIDATION_QUERY, PROP_VALIDATION_QUERY_TIMEOUT, PROP_CONNECTION_INIT_SQLS,
+            PROP_ACCESS_TO_UNDERLYING_CONNECTION_ALLOWED, PROP_REMOVE_ABANDONED_ON_BORROW, PROP_REMOVE_ABANDONED_ON_MAINTENANCE,
+            PROP_REMOVE_ABANDONED_TIMEOUT, PROP_LOG_ABANDONED, PROP_ABANDONED_USAGE_TRACKING, PROP_POOL_PREPARED_STATEMENTS,
+            PROP_MAX_OPEN_PREPARED_STATEMENTS, PROP_CONNECTION_PROPERTIES, PROP_MAX_CONN_LIFETIME_MILLIS,
+            PROP_LOG_EXPIRED_CONNECTIONS, PROP_ROLLBACK_ON_RETURN, PROP_ENABLE_AUTO_COMMIT_ON_RETURN,
+            PROP_DEFAULT_QUERY_TIMEOUT, PROP_FAST_FAIL_VALIDATION, PROP_DISCONNECTION_SQL_CODES, PROP_JMX_NAME };
 
     /**
      * Obsolete properties from DBCP 1.x. with warning strings suggesting new properties. LinkedHashMap will guarantee
@@ -150,16 +150,16 @@ public class BasicDataSourceFactory implements ObjectFactory {
     private static final Map<String, String> NUPROP_WARNTEXT = new LinkedHashMap<>();
 
     static {
-        NUPROP_WARNTEXT.put(NUPROP_MAXACTIVE,
-                "Property " + NUPROP_MAXACTIVE + " is not used in DBCP2, use " + PROP_MAXTOTAL + " instead. "
-                        + PROP_MAXTOTAL + " default value is " + GenericObjectPoolConfig.DEFAULT_MAX_TOTAL + ".");
-        NUPROP_WARNTEXT.put(NUPROP_REMOVEABANDONED,
-                "Property " + NUPROP_REMOVEABANDONED + " is not used in DBCP2," + " use one or both of "
-                        + PROP_REMOVEABANDONEDONBORROW + " or " + PROP_REMOVEABANDONEDONMAINTENANCE + " instead. "
+        NUPROP_WARNTEXT.put(NUPROP_MAX_ACTIVE,
+                "Property " + NUPROP_MAX_ACTIVE + " is not used in DBCP2, use " + PROP_MAX_TOTAL + " instead. "
+                        + PROP_MAX_TOTAL + " default value is " + GenericObjectPoolConfig.DEFAULT_MAX_TOTAL + ".");
+        NUPROP_WARNTEXT.put(NUPROP_REMOVE_ABANDONED,
+                "Property " + NUPROP_REMOVE_ABANDONED + " is not used in DBCP2," + " use one or both of "
+                        + PROP_REMOVE_ABANDONED_ON_BORROW + " or " + PROP_REMOVE_ABANDONED_ON_MAINTENANCE + " instead. "
                         + "Both have default value set to false.");
         NUPROP_WARNTEXT.put(NUPROP_MAXWAIT,
-                "Property " + NUPROP_MAXWAIT + " is not used in DBCP2" + " , use " + PROP_MAXWAITMILLIS + " instead. "
-                        + PROP_MAXWAITMILLIS + " default value is " + BaseObjectPoolConfig.DEFAULT_MAX_WAIT_MILLIS
+                "Property " + NUPROP_MAXWAIT + " is not used in DBCP2" + " , use " + PROP_MAX_WAIT_MILLIS + " instead. "
+                        + PROP_MAX_WAIT_MILLIS + " default value is " + BaseObjectPoolConfig.DEFAULT_MAX_WAIT_MILLIS
                         + ".");
     }
 
@@ -170,10 +170,10 @@ public class BasicDataSourceFactory implements ObjectFactory {
     private static final List<String> SILENT_PROPERTIES = new ArrayList<>();
 
     static {
-        SILENT_PROPERTIES.add(SILENTPROP_FACTORY);
-        SILENT_PROPERTIES.add(SILENTPROP_SCOPE);
-        SILENT_PROPERTIES.add(SILENTPROP_SINGLETON);
-        SILENT_PROPERTIES.add(SILENTPROP_AUTH);
+        SILENT_PROPERTIES.add(SILENT_PROP_FACTORY);
+        SILENT_PROPERTIES.add(SILENT_PROP_SCOPE);
+        SILENT_PROPERTIES.add(SILENT_PROP_SINGLETON);
+        SILENT_PROPERTIES.add(SILENT_PROP_AUTH);
 
     }
 
@@ -297,19 +297,19 @@ public class BasicDataSourceFactory implements ObjectFactory {
         final BasicDataSource dataSource = new BasicDataSource();
         String value = null;
 
-        value = properties.getProperty(PROP_DEFAULTAUTOCOMMIT);
+        value = properties.getProperty(PROP_DEFAULT_AUTO_COMMIT);
         if (value != null) {
             dataSource.setDefaultAutoCommit(Boolean.valueOf(value));
         }
 
-        value = properties.getProperty(PROP_DEFAULTREADONLY);
+        value = properties.getProperty(PROP_DEFAULT_READ_ONLY);
         if (value != null) {
             dataSource.setDefaultReadOnly(Boolean.valueOf(value));
         }
 
-        value = properties.getProperty(PROP_DEFAULTTRANSACTIONISOLATION);
+        value = properties.getProperty(PROP_DEFAULT_TRANSACTION_ISOLATION);
         if (value != null) {
-            int level = PoolableConnectionFactory.UNKNOWN_TRANSACTIONISOLATION;
+            int level = PoolableConnectionFactory.UNKNOWN_TRANSACTION_ISOLATION;
             if ("NONE".equalsIgnoreCase(value)) {
                 level = Connection.TRANSACTION_NONE;
             } else if ("READ_COMMITTED".equalsIgnoreCase(value)) {
@@ -327,28 +327,28 @@ public class BasicDataSourceFactory implements ObjectFactory {
                     System.err.println("Could not parse defaultTransactionIsolation: " + value);
                     System.err.println("WARNING: defaultTransactionIsolation not set");
                     System.err.println("using default value of database driver");
-                    level = PoolableConnectionFactory.UNKNOWN_TRANSACTIONISOLATION;
+                    level = PoolableConnectionFactory.UNKNOWN_TRANSACTION_ISOLATION;
                 }
             }
             dataSource.setDefaultTransactionIsolation(level);
         }
 
-        value = properties.getProperty(PROP_DEFAULTCATALOG);
+        value = properties.getProperty(PROP_DEFAULT_CATALOG);
         if (value != null) {
             dataSource.setDefaultCatalog(value);
         }
 
-        value = properties.getProperty(PROP_DEFAULTSCHEMA);
+        value = properties.getProperty(PROP_DEFAULT_SCHEMA);
         if (value != null) {
             dataSource.setDefaultSchema(value);
         }
 
-        value = properties.getProperty(PROP_CACHESTATE);
+        value = properties.getProperty(PROP_CACHE_STATE);
         if (value != null) {
             dataSource.setCacheState(Boolean.valueOf(value).booleanValue());
         }
 
-        value = properties.getProperty(PROP_DRIVERCLASSNAME);
+        value = properties.getProperty(PROP_DRIVER_CLASS_NAME);
         if (value != null) {
             dataSource.setDriverClassName(value);
         }
@@ -358,72 +358,72 @@ public class BasicDataSourceFactory implements ObjectFactory {
             dataSource.setLifo(Boolean.valueOf(value).booleanValue());
         }
 
-        value = properties.getProperty(PROP_MAXTOTAL);
+        value = properties.getProperty(PROP_MAX_TOTAL);
         if (value != null) {
             dataSource.setMaxTotal(Integer.parseInt(value));
         }
 
-        value = properties.getProperty(PROP_MAXIDLE);
+        value = properties.getProperty(PROP_MAX_IDLE);
         if (value != null) {
             dataSource.setMaxIdle(Integer.parseInt(value));
         }
 
-        value = properties.getProperty(PROP_MINIDLE);
+        value = properties.getProperty(PROP_MIN_IDLE);
         if (value != null) {
             dataSource.setMinIdle(Integer.parseInt(value));
         }
 
-        value = properties.getProperty(PROP_INITIALSIZE);
+        value = properties.getProperty(PROP_INITIAL_SIZE);
         if (value != null) {
             dataSource.setInitialSize(Integer.parseInt(value));
         }
 
-        value = properties.getProperty(PROP_MAXWAITMILLIS);
+        value = properties.getProperty(PROP_MAX_WAIT_MILLIS);
         if (value != null) {
             dataSource.setMaxWaitMillis(Long.parseLong(value));
         }
 
-        value = properties.getProperty(PROP_TESTONCREATE);
+        value = properties.getProperty(PROP_TEST_ON_CREATE);
         if (value != null) {
             dataSource.setTestOnCreate(Boolean.valueOf(value).booleanValue());
         }
 
-        value = properties.getProperty(PROP_TESTONBORROW);
+        value = properties.getProperty(PROP_TEST_ON_BORROW);
         if (value != null) {
             dataSource.setTestOnBorrow(Boolean.valueOf(value).booleanValue());
         }
 
-        value = properties.getProperty(PROP_TESTONRETURN);
+        value = properties.getProperty(PROP_TEST_ON_RETURN);
         if (value != null) {
             dataSource.setTestOnReturn(Boolean.valueOf(value).booleanValue());
         }
 
-        value = properties.getProperty(PROP_TIMEBETWEENEVICTIONRUNSMILLIS);
+        value = properties.getProperty(PROP_TIME_BETWEEN_EVICTION_RUNS_MILLIS);
         if (value != null) {
             dataSource.setTimeBetweenEvictionRunsMillis(Long.parseLong(value));
         }
 
-        value = properties.getProperty(PROP_NUMTESTSPEREVICTIONRUN);
+        value = properties.getProperty(PROP_NUM_TESTS_PER_EVICTION_RUN);
         if (value != null) {
             dataSource.setNumTestsPerEvictionRun(Integer.parseInt(value));
         }
 
-        value = properties.getProperty(PROP_MINEVICTABLEIDLETIMEMILLIS);
+        value = properties.getProperty(PROP_MIN_EVICTABLE_IDLE_TIME_MILLIS);
         if (value != null) {
             dataSource.setMinEvictableIdleTimeMillis(Long.parseLong(value));
         }
 
-        value = properties.getProperty(PROP_SOFTMINEVICTABLEIDLETIMEMILLIS);
+        value = properties.getProperty(PROP_SOFT_MIN_EVICTABLE_IDLE_TIME_MILLIS);
         if (value != null) {
             dataSource.setSoftMinEvictableIdleTimeMillis(Long.parseLong(value));
         }
 
-        value = properties.getProperty(PROP_EVICTIONPOLICYCLASSNAME);
+        value = properties.getProperty(PROP_EVICTION_POLICY_CLASS_NAME);
         if (value != null) {
             dataSource.setEvictionPolicyClassName(value);
         }
 
-        value = properties.getProperty(PROP_TESTWHILEIDLE);
+        value = properties.getProperty(PROP_TEST_WHILE_IDLE);
         if (value != null) {
             dataSource.setTestWhileIdle(Boolean.valueOf(value).booleanValue());
         }
@@ -438,67 +438,67 @@ public class BasicDataSourceFactory implements ObjectFactory {
             dataSource.setUrl(value);
         }
 
-        value = properties.getProperty(PROP_USERNAME);
+        value = properties.getProperty(PROP_USER_NAME);
         if (value != null) {
             dataSource.setUsername(value);
         }
 
-        value = properties.getProperty(PROP_VALIDATIONQUERY);
+        value = properties.getProperty(PROP_VALIDATION_QUERY);
         if (value != null) {
             dataSource.setValidationQuery(value);
         }
 
-        value = properties.getProperty(PROP_VALIDATIONQUERY_TIMEOUT);
+        value = properties.getProperty(PROP_VALIDATION_QUERY_TIMEOUT);
         if (value != null) {
             dataSource.setValidationQueryTimeout(Integer.parseInt(value));
         }
 
-        value = properties.getProperty(PROP_ACCESSTOUNDERLYINGCONNECTIONALLOWED);
+        value = properties.getProperty(PROP_ACCESS_TO_UNDERLYING_CONNECTION_ALLOWED);
         if (value != null) {
             dataSource.setAccessToUnderlyingConnectionAllowed(Boolean.valueOf(value).booleanValue());
         }
 
-        value = properties.getProperty(PROP_REMOVEABANDONEDONBORROW);
+        value = properties.getProperty(PROP_REMOVE_ABANDONED_ON_BORROW);
         if (value != null) {
             dataSource.setRemoveAbandonedOnBorrow(Boolean.valueOf(value).booleanValue());
         }
 
-        value = properties.getProperty(PROP_REMOVEABANDONEDONMAINTENANCE);
+        value = properties.getProperty(PROP_REMOVE_ABANDONED_ON_MAINTENANCE);
         if (value != null) {
             dataSource.setRemoveAbandonedOnMaintenance(Boolean.valueOf(value).booleanValue());
         }
 
-        value = properties.getProperty(PROP_REMOVEABANDONEDTIMEOUT);
+        value = properties.getProperty(PROP_REMOVE_ABANDONED_TIMEOUT);
         if (value != null) {
             dataSource.setRemoveAbandonedTimeout(Integer.parseInt(value));
         }
 
-        value = properties.getProperty(PROP_LOGABANDONED);
+        value = properties.getProperty(PROP_LOG_ABANDONED);
         if (value != null) {
             dataSource.setLogAbandoned(Boolean.valueOf(value).booleanValue());
         }
 
-        value = properties.getProperty(PROP_ABANDONEDUSAGETRACKING);
+        value = properties.getProperty(PROP_ABANDONED_USAGE_TRACKING);
         if (value != null) {
             dataSource.setAbandonedUsageTracking(Boolean.valueOf(value).booleanValue());
         }
 
-        value = properties.getProperty(PROP_POOLPREPAREDSTATEMENTS);
+        value = properties.getProperty(PROP_POOL_PREPARED_STATEMENTS);
         if (value != null) {
             dataSource.setPoolPreparedStatements(Boolean.valueOf(value).booleanValue());
         }
 
-        value = properties.getProperty(PROP_MAXOPENPREPAREDSTATEMENTS);
+        value = properties.getProperty(PROP_MAX_OPEN_PREPARED_STATEMENTS);
         if (value != null) {
             dataSource.setMaxOpenPreparedStatements(Integer.parseInt(value));
         }
 
-        value = properties.getProperty(PROP_CONNECTIONINITSQLS);
+        value = properties.getProperty(PROP_CONNECTION_INIT_SQLS);
         if (value != null) {
             dataSource.setConnectionInitSqls(parseList(value, ';'));
         }
 
-        value = properties.getProperty(PROP_CONNECTIONPROPERTIES);
+        value = properties.getProperty(PROP_CONNECTION_PROPERTIES);
         if (value != null) {
             final Properties p = getProperties(value);
             final Enumeration<?> e = p.propertyNames();
@@ -508,12 +508,12 @@ public class BasicDataSourceFactory implements ObjectFactory {
             }
         }
 
-        value = properties.getProperty(PROP_MAXCONNLIFETIMEMILLIS);
+        value = properties.getProperty(PROP_MAX_CONN_LIFETIME_MILLIS);
         if (value != null) {
             dataSource.setMaxConnLifetimeMillis(Long.parseLong(value));
         }
 
-        value = properties.getProperty(PROP_LOGEXPIREDCONNECTIONS);
+        value = properties.getProperty(PROP_LOG_EXPIRED_CONNECTIONS);
         if (value != null) {
             dataSource.setLogExpiredConnections(Boolean.valueOf(value).booleanValue());
         }
@@ -523,9 +523,9 @@ public class BasicDataSourceFactory implements ObjectFactory {
             dataSource.setJmxName(value);
         }
 
-        value = properties.getProperty(PROP_ENABLE_AUTOCOMMIT_ON_RETURN);
+        value = properties.getProperty(PROP_ENABLE_AUTO_COMMIT_ON_RETURN);
         if (value != null) {
-            dataSource.setEnableAutoCommitOnReturn(Boolean.valueOf(value).booleanValue());
+            dataSource.setAutoCommitOnReturn(Boolean.valueOf(value).booleanValue());
         }
 
         value = properties.getProperty(PROP_ROLLBACK_ON_RETURN);
@@ -533,12 +533,12 @@ public class BasicDataSourceFactory implements ObjectFactory {
             dataSource.setRollbackOnReturn(Boolean.valueOf(value).booleanValue());
         }
 
-        value = properties.getProperty(PROP_DEFAULT_QUERYTIMEOUT);
+        value = properties.getProperty(PROP_DEFAULT_QUERY_TIMEOUT);
         if (value != null) {
             dataSource.setDefaultQueryTimeout(Integer.valueOf(value));
         }
 
-        value = properties.getProperty(PROP_FASTFAIL_VALIDATION);
+        value = properties.getProperty(PROP_FAST_FAIL_VALIDATION);
         if (value != null) {
             dataSource.setFastFailValidation(Boolean.valueOf(value).booleanValue());
         }
diff --git a/java/org/apache/tomcat/dbcp/dbcp2/DataSourceConnectionFactory.java b/java/org/apache/tomcat/dbcp/dbcp2/DataSourceConnectionFactory.java
index c56b500..5fdceab 100644
--- a/java/org/apache/tomcat/dbcp/dbcp2/DataSourceConnectionFactory.java
+++ b/java/org/apache/tomcat/dbcp/dbcp2/DataSourceConnectionFactory.java
@@ -58,7 +58,7 @@ public class DataSourceConnectionFactory implements ConnectionFactory {
     public DataSourceConnectionFactory(final DataSource dataSource, final String userName, final char[] userPassword) {
         this.dataSource = dataSource;
         this.userName = userName;
-        this.userPassword = userPassword;
+        this.userPassword = Utils.clone(userPassword);
     }
 
     /**
@@ -84,4 +84,28 @@ public class DataSourceConnectionFactory implements ConnectionFactory {
         }
         return dataSource.getConnection(userName, Utils.toString(userPassword));
     }
+
+    /**
+     * @return The data source.
+     * @since 2.6.0
+     */
+    public DataSource getDataSource() {
+        return dataSource;
+    }
+
+    /**
+     * @return The user name.
+     * @since 2.6.0
+     */
+    public String getUserName() {
+        return userName;
+    }
+
+    /**
+     * @return The user password.
+     * @since 2.6.0
+     */
+    public char[] getUserPassword() {
+        return userPassword;
+    }
 }
diff --git a/java/org/apache/tomcat/dbcp/dbcp2/DelegatingConnection.java b/java/org/apache/tomcat/dbcp/dbcp2/DelegatingConnection.java
index 3be956c..5869a36 100644
--- a/java/org/apache/tomcat/dbcp/dbcp2/DelegatingConnection.java
+++ b/java/org/apache/tomcat/dbcp/dbcp2/DelegatingConnection.java
@@ -88,7 +88,7 @@ public class DelegatingConnection<C extends Connection> extends AbandonedTrace i
      * Returns a string representation of the metadata associated with the innermost delegate connection.
      */
     @Override
-    public String toString() {
+    public synchronized String toString() {
         String s = null;
 
         final Connection c = this.getInnermostDelegateInternal();
@@ -928,7 +928,7 @@ public class DelegatingConnection<C extends Connection> extends AbandonedTrace i
     public void setSchema(final String schema) throws SQLException {
         checkOpen();
         try {
-            connection.setSchema(schema);
+            Jdbc41Bridge.setSchema(connection, schema);
         } catch (final SQLException e) {
             handleException(e);
         }
@@ -938,7 +938,7 @@ public class DelegatingConnection<C extends Connection> extends AbandonedTrace i
     public String getSchema() throws SQLException {
         checkOpen();
         try {
-            return connection.getSchema();
+            return Jdbc41Bridge.getSchema(connection);
         } catch (final SQLException e) {
             handleException(e);
             return null;
@@ -949,7 +949,7 @@ public class DelegatingConnection<C extends Connection> extends AbandonedTrace i
     public void abort(final Executor executor) throws SQLException {
         checkOpen();
         try {
-            connection.abort(executor);
+            Jdbc41Bridge.abort(connection, executor);
         } catch (final SQLException e) {
             handleException(e);
         }
@@ -959,7 +959,7 @@ public class DelegatingConnection<C extends Connection> extends AbandonedTrace i
     public void setNetworkTimeout(final Executor executor, final int milliseconds) throws SQLException {
         checkOpen();
         try {
-            connection.setNetworkTimeout(executor, milliseconds);
+            Jdbc41Bridge.setNetworkTimeout(connection, executor, milliseconds);
         } catch (final SQLException e) {
             handleException(e);
         }
@@ -969,7 +969,7 @@ public class DelegatingConnection<C extends Connection> extends AbandonedTrace i
     public int getNetworkTimeout() throws SQLException {
         checkOpen();
         try {
-            return connection.getNetworkTimeout();
+            return Jdbc41Bridge.getNetworkTimeout(connection);
         } catch (final SQLException e) {
             handleException(e);
             return 0;
diff --git a/java/org/apache/tomcat/dbcp/dbcp2/DelegatingDatabaseMetaData.java b/java/org/apache/tomcat/dbcp/dbcp2/DelegatingDatabaseMetaData.java
index 8084b2d..a3f9bc1 100644
--- a/java/org/apache/tomcat/dbcp/dbcp2/DelegatingDatabaseMetaData.java
+++ b/java/org/apache/tomcat/dbcp/dbcp2/DelegatingDatabaseMetaData.java
@@ -130,7 +130,7 @@ public class DelegatingDatabaseMetaData implements DatabaseMetaData {
     public boolean generatedKeyAlwaysReturned() throws SQLException {
         connection.checkOpen();
         try {
-            return databaseMetaData.generatedKeyAlwaysReturned();
+            return Jdbc41Bridge.generatedKeyAlwaysReturned(databaseMetaData);
         } catch (final SQLException e) {
             handleException(e);
             return false;
@@ -731,8 +731,8 @@ public class DelegatingDatabaseMetaData implements DatabaseMetaData {
             final String columnNamePattern) throws SQLException {
         connection.checkOpen();
         try {
-            return DelegatingResultSet.wrapResultSet(connection,
-                    databaseMetaData.getPseudoColumns(catalog, schemaPattern, tableNamePattern, columnNamePattern));
+            return DelegatingResultSet.wrapResultSet(connection, Jdbc41Bridge.getPseudoColumns(databaseMetaData,
+                    catalog, schemaPattern, tableNamePattern, columnNamePattern));
         } catch (final SQLException e) {
             handleException(e);
             throw new AssertionError();
diff --git a/java/org/apache/tomcat/dbcp/dbcp2/DelegatingPreparedStatement.java b/java/org/apache/tomcat/dbcp/dbcp2/DelegatingPreparedStatement.java
index 35049d9..6719ead 100644
--- a/java/org/apache/tomcat/dbcp/dbcp2/DelegatingPreparedStatement.java
+++ b/java/org/apache/tomcat/dbcp/dbcp2/DelegatingPreparedStatement.java
@@ -647,7 +647,7 @@ public class DelegatingPreparedStatement extends DelegatingStatement implements
      */
     @SuppressWarnings("resource")
     @Override
-    public String toString() {
+    public synchronized String toString() {
         final Statement statement = getDelegate();
         return statement == null ? "NULL" : statement.toString();
     }
diff --git a/java/org/apache/tomcat/dbcp/dbcp2/DelegatingResultSet.java b/java/org/apache/tomcat/dbcp/dbcp2/DelegatingResultSet.java
index 1739c14..e26d13c 100644
--- a/java/org/apache/tomcat/dbcp/dbcp2/DelegatingResultSet.java
+++ b/java/org/apache/tomcat/dbcp/dbcp2/DelegatingResultSet.java
@@ -739,7 +739,7 @@ public final class DelegatingResultSet extends AbandonedTrace implements ResultS
     @Override
     public <T> T getObject(final int columnIndex, final Class<T> type) throws SQLException {
         try {
-            return resultSet.getObject(columnIndex, type);
+            return Jdbc41Bridge.getObject(resultSet, columnIndex, type);
         } catch (final SQLException e) {
             handleException(e);
             return null;
@@ -769,7 +769,7 @@ public final class DelegatingResultSet extends AbandonedTrace implements ResultS
     @Override
     public <T> T getObject(final String columnLabel, final Class<T> type) throws SQLException {
         try {
-            return resultSet.getObject(columnLabel, type);
+            return Jdbc41Bridge.getObject(resultSet, columnLabel, type);
         } catch (final SQLException e) {
             handleException(e);
             return null;
@@ -1241,8 +1241,8 @@ public final class DelegatingResultSet extends AbandonedTrace implements ResultS
     }
 
     @Override
-    public String toString() {
-        return super.toString() + "[_res=" + resultSet + ", _stmt=" + statement + ", _conn=" + connection + "]";
+    public synchronized String toString() {
+        return super.toString() + "[resultSet=" + resultSet + ", statement=" + statement + ", connection=" + connection + "]";
     }
 
     @Override
diff --git a/java/org/apache/tomcat/dbcp/dbcp2/DelegatingStatement.java b/java/org/apache/tomcat/dbcp/dbcp2/DelegatingStatement.java
index 3bbdcd5..966d9b8 100644
--- a/java/org/apache/tomcat/dbcp/dbcp2/DelegatingStatement.java
+++ b/java/org/apache/tomcat/dbcp/dbcp2/DelegatingStatement.java
@@ -161,7 +161,7 @@ public class DelegatingStatement extends AbandonedTrace implements Statement {
     public void closeOnCompletion() throws SQLException {
         checkOpen();
         try {
-            statement.closeOnCompletion();
+            Jdbc41Bridge.closeOnCompletion(statement);
         } catch (final SQLException e) {
             handleException(e);
         }
@@ -527,7 +527,7 @@ public class DelegatingStatement extends AbandonedTrace implements Statement {
     public boolean isCloseOnCompletion() throws SQLException {
         checkOpen();
         try {
-            return statement.isCloseOnCompletion();
+            return Jdbc41Bridge.isCloseOnCompletion(statement);
         } catch (final SQLException e) {
             handleException(e);
             return false;
@@ -674,7 +674,7 @@ public class DelegatingStatement extends AbandonedTrace implements Statement {
      * @return String
      */
     @Override
-    public String toString() {
+    public synchronized String toString() {
         return statement == null ? "NULL" : statement.toString();
     }
 
diff --git a/java/org/apache/tomcat/dbcp/dbcp2/DriverConnectionFactory.java b/java/org/apache/tomcat/dbcp/dbcp2/DriverConnectionFactory.java
index eebe56f..7e26052 100644
--- a/java/org/apache/tomcat/dbcp/dbcp2/DriverConnectionFactory.java
+++ b/java/org/apache/tomcat/dbcp/dbcp2/DriverConnectionFactory.java
@@ -53,6 +53,30 @@ public class DriverConnectionFactory implements ConnectionFactory {
         return driver.connect(connectionString, properties);
     }
 
+    /**
+     * @return The connection String.
+     * @since 2.6.0
+     */
+    public String getConnectionString() {
+        return connectionString;
+    }
+
+    /**
+     * @return The Driver.
+     * @since 2.6.0
+     */
+    public Driver getDriver() {
+        return driver;
+    }
+
+    /**
+     * @return The Properties.
+     * @since 2.6.0
+     */
+    public Properties getProperties() {
+        return properties;
+    }
+
     @Override
     public String toString() {
         return this.getClass().getName() + " [" + String.valueOf(driver) + ";" + String.valueOf(connectionString) + ";"
diff --git a/java/org/apache/tomcat/dbcp/dbcp2/DriverManagerConnectionFactory.java b/java/org/apache/tomcat/dbcp/dbcp2/DriverManagerConnectionFactory.java
index c8f462d..b9b49a1 100644
--- a/java/org/apache/tomcat/dbcp/dbcp2/DriverManagerConnectionFactory.java
+++ b/java/org/apache/tomcat/dbcp/dbcp2/DriverManagerConnectionFactory.java
@@ -38,6 +38,14 @@ public class DriverManagerConnectionFactory implements ConnectionFactory {
         DriverManager.getDrivers();
     }
 
+    private final String connectionUri;
+
+    private final String userName;
+
+    private final char[] userPassword;
+
+    private final Properties properties;
+
     /**
      * Constructor for DriverManagerConnectionFactory.
      *
@@ -48,6 +56,8 @@ public class DriverManagerConnectionFactory implements ConnectionFactory {
     public DriverManagerConnectionFactory(final String connectionUri) {
         this.connectionUri = connectionUri;
         this.properties = new Properties();
+        this.userName = null;
+        this.userPassword = null;
     }
 
     /**
@@ -62,6 +72,26 @@ public class DriverManagerConnectionFactory implements ConnectionFactory {
     public DriverManagerConnectionFactory(final String connectionUri, final Properties properties) {
         this.connectionUri = connectionUri;
         this.properties = properties;
+        this.userName = null;
+        this.userPassword = null;
+    }
+
+    /**
+     * Constructor for DriverManagerConnectionFactory.
+     *
+     * @param connectionUri
+     *            a database url of the form <code>jdbc:<em>subprotocol</em>:<em>subname</em></code>
+     * @param userName
+     *            the database user
+     * @param userPassword
+     *            the user's password
+     */
+    public DriverManagerConnectionFactory(final String connectionUri, final String userName,
+            final char[] userPassword) {
+        this.connectionUri = connectionUri;
+        this.userName = userName;
+        this.userPassword = Utils.clone(userPassword);
+        this.properties = null;
     }
 
     /**
@@ -78,7 +108,8 @@ public class DriverManagerConnectionFactory implements ConnectionFactory {
             final String userPassword) {
         this.connectionUri = connectionUri;
         this.userName = userName;
-        this.userPassword = userPassword;
+        this.userPassword =  Utils.toCharArray(userPassword);
+        this.properties = null;
     }
 
     @Override
@@ -87,13 +118,32 @@ public class DriverManagerConnectionFactory implements ConnectionFactory {
             if (userName == null && userPassword == null) {
                 return DriverManager.getConnection(connectionUri);
             }
-            return DriverManager.getConnection(connectionUri, userName, userPassword);
+            return DriverManager.getConnection(connectionUri, userName, Utils.toString(userPassword));
         }
         return DriverManager.getConnection(connectionUri, properties);
     }
 
-    private final String connectionUri;
-    private String userName;
-    private String userPassword;
-    private Properties properties;
+    /**
+     * @return The connection URI.
+     * @since 2.6.0
+     */
+    public String getConnectionUri() {
+        return connectionUri;
+    }
+
+    /**
+     * @return The Properties.
+     * @since 2.6.0
+     */
+    public Properties getProperties() {
+        return properties;
+    }
+
+    /**
+     * @return The user name.
+     * @since 2.6.0
+     */
+    public String getUserName() {
+        return userName;
+    }
 }
diff --git a/java/org/apache/tomcat/dbcp/dbcp2/Jdbc41Bridge.java b/java/org/apache/tomcat/dbcp/dbcp2/Jdbc41Bridge.java
new file mode 100644
index 0000000..b4ee5b9
--- /dev/null
+++ b/java/org/apache/tomcat/dbcp/dbcp2/Jdbc41Bridge.java
@@ -0,0 +1,482 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.tomcat.dbcp.dbcp2;
+
+import java.io.InputStream;
+import java.io.Reader;
+import java.math.BigDecimal;
+import java.net.URL;
+import java.sql.Array;
+import java.sql.Blob;
+import java.sql.Clob;
+import java.sql.Connection;
+import java.sql.DatabaseMetaData;
+import java.sql.Date;
+import java.sql.Ref;
+import java.sql.ResultSet;
+import java.sql.RowId;
+import java.sql.SQLException;
+import java.sql.SQLFeatureNotSupportedException;
+import java.sql.SQLXML;
+import java.sql.Statement;
+import java.sql.Time;
+import java.sql.Timestamp;
+import java.util.concurrent.Executor;
+import java.util.logging.Logger;
+
+import javax.sql.CommonDataSource;
+
+/**
+ * Defines bridge methods to JDBC 4.1 (Java 7) methods to allow call sites to operate safely (without
+ * {@link AbstractMethodError}) when using a JDBC driver written for JDBC 4.0 (Java 6).
+ *
+ * @since 2.6.0
+ */
+public class Jdbc41Bridge {
+
+    /**
+     * Delegates to {@link Connection#abort(Executor)} without throwing a {@link AbstractMethodError}.
+     * <p>
+     * If the JDBC driver does not implement {@link Connection#abort(Executor)}, then call {@link Connection#close()}.
+     * </p>
+     *
+     * @param connection
+     *            the receiver
+     * @param executor
+     *            See {@link Connection#abort(Executor)}.
+     * @throws SQLException
+     *             See {@link Connection#abort(Executor)}.
+     * @see Connection#abort(Executor)
+     */
+    public static void abort(final Connection connection, final Executor executor) throws SQLException {
+        try {
+            connection.abort(executor);
+        } catch (final AbstractMethodError e) {
+            connection.close();
+        }
+    }
+
+    /**
+     * Delegates to {@link DatabaseMetaData#generatedKeyAlwaysReturned()} without throwing a
+     * {@link AbstractMethodError}.
+     * <p>
+     * If the JDBC driver does not implement {@link DatabaseMetaData#generatedKeyAlwaysReturned()}, then return false.
+     * </p>
+     *
+     * @param databaseMetaData
+     *            See {@link DatabaseMetaData#generatedKeyAlwaysReturned()}
+     * @return See {@link DatabaseMetaData#generatedKeyAlwaysReturned()}
+     * @throws SQLException
+     *             See {@link DatabaseMetaData#generatedKeyAlwaysReturned()}
+     * @see DatabaseMetaData#generatedKeyAlwaysReturned()
+     */
+    public static boolean generatedKeyAlwaysReturned(final DatabaseMetaData databaseMetaData) throws SQLException {
+        try {
+            return databaseMetaData.generatedKeyAlwaysReturned();
+        } catch (final AbstractMethodError e) {
+            // do nothing
+            return false;
+        }
+    }
+
+    /**
+     * Delegates to {@link Connection#getNetworkTimeout()} without throwing a {@link AbstractMethodError}.
+     * <p>
+     * If the JDBC driver does not implement {@link Connection#getNetworkTimeout()}, then return 0.
+     * </p>
+     *
+     * @param connection
+     *            the receiver
+     * @return See {@link Connection#getNetworkTimeout()}
+     * @throws SQLException
+     *             See {@link Connection#getNetworkTimeout()}
+     * @see Connection#getNetworkTimeout()
+     */
+    public static int getNetworkTimeout(final Connection connection) throws SQLException {
+        try {
+            return connection.getNetworkTimeout();
+        } catch (final AbstractMethodError e) {
+            return 0;
+        }
+    }
+
+    /**
+     * Delegates to {@link ResultSet#getObject(int, Class)} without throwing a {@link AbstractMethodError}.
+     * <p>
+     * If the JDBC driver does not implement {@link ResultSet#getObject(int, Class)}, then return 0.
+     * </p>
+     *
+     * @param <T>
+     *            See {@link ResultSet#getObject(int, Class)}
+     * @param resultSet
+     *            See {@link ResultSet#getObject(int, Class)}
+     * @param columnIndex
+     *            See {@link ResultSet#getObject(int, Class)}
+     * @param type
+     *            See {@link ResultSet#getObject(int, Class)}
+     * @return See {@link ResultSet#getObject(int, Class)}
+     * @throws SQLException
+     *             See {@link ResultSet#getObject(int, Class)}
+     * @see ResultSet#getObject(int, Class)
+     */
+    @SuppressWarnings("unchecked")
+    public static <T> T getObject(final ResultSet resultSet, final int columnIndex, final Class<T> type)
+            throws SQLException {
+        try {
+            return resultSet.getObject(columnIndex, type);
+        } catch (final AbstractMethodError e) {
+            if (type == String.class) {
+                return (T) resultSet.getString(columnIndex);
+            }
+            // Numbers
+            if (type == Integer.class) {
+                return (T) Integer.valueOf(resultSet.getInt(columnIndex));
+            }
+            if (type == Long.class) {
+                return (T) Long.valueOf(resultSet.getLong(columnIndex));
+            }
+            if (type == Double.class) {
+                return (T) Double.valueOf(resultSet.getDouble(columnIndex));
+            }
+            if (type == Float.class) {
+                return (T) Float.valueOf(resultSet.getFloat(columnIndex));
+            }
+            if (type == Short.class) {
+                return (T) Short.valueOf(resultSet.getShort(columnIndex));
+            }
+            if (type == BigDecimal.class) {
+                return (T) resultSet.getBigDecimal(columnIndex);
+            }
+            if (type == Byte.class) {
+                return (T) Byte.valueOf(resultSet.getByte(columnIndex));
+            }
+            // Dates
+            if (type == Date.class) {
+                return (T) resultSet.getDate(columnIndex);
+            }
+            if (type == Time.class) {
+                return (T) resultSet.getTime(columnIndex);
+            }
+            if (type == Timestamp.class) {
+                return (T) resultSet.getTimestamp(columnIndex);
+            }
+            // Streams
+            if (type == InputStream.class) {
+                return (T) resultSet.getBinaryStream(columnIndex);
+            }
+            if (type == Reader.class) {
+                return (T) resultSet.getCharacterStream(columnIndex);
+            }
+            // Other
+            if (type == Object.class) {
+                return (T) resultSet.getObject(columnIndex);
+            }
+            if (type == Boolean.class) {
+                return (T) Boolean.valueOf(resultSet.getBoolean(columnIndex));
+            }
+            if (type == Array.class) {
+                return (T) resultSet.getArray(columnIndex);
+            }
+            if (type == Blob.class) {
+                return (T) resultSet.getBlob(columnIndex);
+            }
+            if (type == Clob.class) {
+                return (T) resultSet.getClob(columnIndex);
+            }
+            if (type == Ref.class) {
+                return (T) resultSet.getRef(columnIndex);
+            }
+            if (type == RowId.class) {
+                return (T) resultSet.getRowId(columnIndex);
+            }
+            if (type == SQLXML.class) {
+                return (T) resultSet.getSQLXML(columnIndex);
+            }
+            if (type == URL.class) {
+                return (T) resultSet.getURL(columnIndex);
+            }
+            throw new SQLFeatureNotSupportedException(
+                    String.format("resultSet=%s, columnIndex=%,d, type=%s", resultSet, Integer.valueOf(columnIndex), type));
+        }
+    }
+
+    /**
+     * Delegates to {@link ResultSet#getObject(String, Class)} without throwing a {@link AbstractMethodError}.
+     *
+     * @param <T>
+     *            See {@link ResultSet#getObject(String, Class)}
+     * @param resultSet
+     *            See {@link ResultSet#getObject(String, Class)}
+     * @param columnLabel
+     *            See {@link ResultSet#getObject(String, Class)}
+     * @param type
+     *            See {@link ResultSet#getObject(String, Class)}
+     * @return See {@link ResultSet#getObject(String, Class)}
+     * @throws SQLException
+     *             See {@link ResultSet#getObject(String, Class)}
+     * @see ResultSet#getObject(int, Class)
+     */
+    @SuppressWarnings("unchecked")
+    public static <T> T getObject(final ResultSet resultSet, final String columnLabel, final Class<T> type)
+            throws SQLException {
+        try {
+            return resultSet.getObject(columnLabel, type);
+        } catch (final AbstractMethodError e) {
+            // Numbers
+            if (type == Integer.class) {
+                return (T) Integer.valueOf(resultSet.getInt(columnLabel));
+            }
+            if (type == Long.class) {
+                return (T) Long.valueOf(resultSet.getLong(columnLabel));
+            }
+            if (type == Double.class) {
+                return (T) Double.valueOf(resultSet.getDouble(columnLabel));
+            }
+            if (type == Float.class) {
+                return (T) Float.valueOf(resultSet.getFloat(columnLabel));
+            }
+            if (type == Short.class) {
+                return (T) Short.valueOf(resultSet.getShort(columnLabel));
+            }
+            if (type == BigDecimal.class) {
+                return (T) resultSet.getBigDecimal(columnLabel);
+            }
+            if (type == Byte.class) {
+                return (T) Byte.valueOf(resultSet.getByte(columnLabel));
+            }
+            // Dates
+            if (type == Date.class) {
+                return (T) resultSet.getDate(columnLabel);
+            }
+            if (type == Time.class) {
+                return (T) resultSet.getTime(columnLabel);
+            }
+            if (type == Timestamp.class) {
+                return (T) resultSet.getTimestamp(columnLabel);
+            }
+            // Streams
+            if (type == InputStream.class) {
+                return (T) resultSet.getBinaryStream(columnLabel);
+            }
+            if (type == Reader.class) {
+                return (T) resultSet.getCharacterStream(columnLabel);
+            }
+            // Other
+            if (type == Object.class) {
+                return (T) resultSet.getObject(columnLabel);
+            }
+            if (type == Boolean.class) {
+                return (T) Boolean.valueOf(resultSet.getBoolean(columnLabel));
+            }
+            if (type == Array.class) {
+                return (T) resultSet.getArray(columnLabel);
+            }
+            if (type == Blob.class) {
+                return (T) resultSet.getBlob(columnLabel);
+            }
+            if (type == Clob.class) {
+                return (T) resultSet.getClob(columnLabel);
+            }
+            if (type == Ref.class) {
+                return (T) resultSet.getRef(columnLabel);
+            }
+            if (type == RowId.class) {
+                return (T) resultSet.getRowId(columnLabel);
+            }
+            if (type == SQLXML.class) {
+                return (T) resultSet.getSQLXML(columnLabel);
+            }
+            if (type == URL.class) {
+                return (T) resultSet.getURL(columnLabel);
+            }
+            throw new SQLFeatureNotSupportedException(
+                    String.format("resultSet=%s, columnLabel=%,d, type=%s", resultSet, columnLabel, type));
+        }
+    }
+
+    /**
+     * Delegates to {@link DatabaseMetaData#getPseudoColumns(String, String, String, String)} without throwing a
+     * {@link AbstractMethodError}.
+     * <p>
+     * If the JDBC driver does not implement {@link DatabaseMetaData#getPseudoColumns(String, String, String, String)},
+     * then return null.
+     * </p>
+     *
+     * @param databaseMetaData
+     *            the receiver
+     * @param catalog
+     *            See {@link DatabaseMetaData#getPseudoColumns(String, String, String, String)}
+     * @param schemaPattern
+     *            See {@link DatabaseMetaData#getPseudoColumns(String, String, String, String)}
+     * @param tableNamePattern
+     *            See {@link DatabaseMetaData#getPseudoColumns(String, String, String, String)}
+     * @param columnNamePattern
+     *            See {@link DatabaseMetaData#getPseudoColumns(String, String, String, String)}
+     * @return See {@link DatabaseMetaData#getPseudoColumns(String, String, String, String)}
+     * @throws SQLException
+     *             See {@link DatabaseMetaData#getPseudoColumns(String, String, String, String)}
+     * @see DatabaseMetaData#getPseudoColumns(String, String, String, String)
+     */
+    public static ResultSet getPseudoColumns(final DatabaseMetaData databaseMetaData, final String catalog,
+            final String schemaPattern, final String tableNamePattern, final String columnNamePattern)
+            throws SQLException {
+        try {
+            return databaseMetaData.getPseudoColumns(catalog, schemaPattern, tableNamePattern, columnNamePattern);
+        } catch (final AbstractMethodError e) {
+            // do nothing
+            return null;
+        }
+    }
+
+    /**
+     * Delegates to {@link Connection#getSchema()} without throwing a {@link AbstractMethodError}.
+     * <p>
+     * If the JDBC driver does not implement {@link Connection#getSchema()}, then return null.
+     * </p>
+     *
+     * @param connection
+     *            the receiver
+     * @return null for a JDBC 4 driver or a value per {@link Connection#getSchema()}.
+     * @throws SQLException
+     *             See {@link Connection#getSchema()}.
+     * @see Connection#getSchema()
+     */
+    public static String getSchema(final Connection connection) throws SQLException {
+        try {
+            return connection.getSchema();
+        } catch (final AbstractMethodError e) {
+            // do nothing
+            return null;
+        }
+    }
+
+    /**
+     * Delegates to {@link Connection#setNetworkTimeout(Executor, int)} without throwing a {@link AbstractMethodError}.
+     * <p>
+     * If the JDBC driver does not implement {@link Connection#setNetworkTimeout(Executor, int)}, then do nothing.
+     * </p>
+     *
+     * @param connection
+     *            the receiver
+     * @param executor
+     *            See {@link Connection#setNetworkTimeout(Executor, int)}
+     * @param milliseconds
+     *            {@link Connection#setNetworkTimeout(Executor, int)}
+     * @throws SQLException
+     *             {@link Connection#setNetworkTimeout(Executor, int)}
+     * @see Connection#setNetworkTimeout(Executor, int)
+     */
+    public static void setNetworkTimeout(final Connection connection, final Executor executor, final int milliseconds)
+            throws SQLException {
+        try {
+            connection.setNetworkTimeout(executor, milliseconds);
+        } catch (final AbstractMethodError e) {
+            // do nothing
+        }
+    }
+
+    /**
+     * Delegates to {@link Connection#setSchema(String)} without throwing a {@link AbstractMethodError}.
+     * <p>
+     * If the JDBC driver does not implement {@link Connection#setSchema(String)}, then do nothing.
+     * </p>
+     *
+     * @param connection
+     *            the receiver
+     * @param schema
+     *            See {@link Connection#setSchema(String)}.
+     * @throws SQLException
+     *             See {@link Connection#setSchema(String)}.
+     * @see Connection#setSchema(String)
+     */
+    public static void setSchema(final Connection connection, final String schema) throws SQLException {
+        try {
+            connection.setSchema(schema);
+        } catch (final AbstractMethodError e) {
+            // do nothing
+        }
+    }
+
+    /**
+     * Delegates to {@link Statement#closeOnCompletion()} without throwing a {@link AbstractMethodError}.
+     * <p>
+     * If the JDBC driver does not implement {@link Statement#closeOnCompletion()}, then just check that the connection
+     * is closed to then throw a SQLException.
+     * </p>
+     *
+     * @param statement
+     *            See {@link Statement#closeOnCompletion()}
+     * @throws SQLException
+     *             See {@link Statement#closeOnCompletion()}
+     * @see Statement#closeOnCompletion()
+     */
+    public static void closeOnCompletion(final Statement statement) throws SQLException {
+        try {
+            statement.closeOnCompletion();
+        } catch (final AbstractMethodError e) {
+            if (statement.isClosed()) {
+                throw new SQLException("Statement closed");
+            }
+        }
+    }
+
+    /**
+     * Delegates to {@link Statement#isCloseOnCompletion()} without throwing a {@link AbstractMethodError}.
+     * <p>
+     * If the JDBC driver does not implement {@link Statement#isCloseOnCompletion()}, then just check that the
+     * connection is closed to then throw a SQLException.
+     * </p>
+     *
+     * @param statement
+     *            See {@link Statement#isCloseOnCompletion()}
+     * @return See {@link Statement#isCloseOnCompletion()}
+     * @throws SQLException
+     *             See {@link Statement#isCloseOnCompletion()}
+     * @see Statement#closeOnCompletion()
+     */
+    public static boolean isCloseOnCompletion(final Statement statement) throws SQLException {
+        try {
+            return statement.isCloseOnCompletion();
+        } catch (final AbstractMethodError e) {
+            if (statement.isClosed()) {
+                throw new SQLException("Statement closed");
+            }
+            return false;
+        }
+    }
+
+    /**
+     * Delegates to {@link CommonDataSource#getParentLogger()} without throwing a {@link AbstractMethodError}.
+     * <p>
+     * If the JDBC driver does not implement {@link CommonDataSource#getParentLogger()}, then return null.
+     * </p>
+     *
+     * @param commonDataSource
+     *            See {@link CommonDataSource#getParentLogger()}
+     * @return See {@link CommonDataSource#getParentLogger()}
+     * @throws SQLFeatureNotSupportedException
+     *             See {@link CommonDataSource#getParentLogger()}
+     */
+    public static Logger getParentLogger(final CommonDataSource commonDataSource) throws SQLFeatureNotSupportedException {
+        try {
+            return commonDataSource.getParentLogger();
+        } catch (final AbstractMethodError e) {
+            throw new SQLFeatureNotSupportedException("javax.sql.CommonDataSource#getParentLogger()");
+        }
+    }
+
+}
diff --git a/java/org/apache/tomcat/dbcp/dbcp2/LocalStrings.properties b/java/org/apache/tomcat/dbcp/dbcp2/LocalStrings.properties
index 2e0ada2..3aaa50b 100644
--- a/java/org/apache/tomcat/dbcp/dbcp2/LocalStrings.properties
+++ b/java/org/apache/tomcat/dbcp/dbcp2/LocalStrings.properties
@@ -15,12 +15,12 @@
 
 connectionFactory.lifetimeExceeded=The lifetime of the connection [{0}] milliseconds exceeds the maximum permitted value of [{1}] milliseconds
 
-poolableConnectionFactory.validateObject.fail=Failed to validate a poolable connection.
+pool.close.fail=Cannot close connection pool.
 
 poolableConnection.validate.fastFail=Fatal SQLException was thrown previously on this connection.
 
-swallowedExceptionLogger.onSwallowedException=An internal object pool swallowed an Exception.
+poolableConnectionFactory.validateObject.fail=Failed to validate a poolable connection.
 
 poolingDataSource.factoryConfig=PoolableConnectionFactory not linked to pool. Calling setPool() to fix the configuration.
 
-pool.close.fail=Cannot close connection pool.
+swallowedExceptionLogger.onSwallowedException=An internal object pool swallowed an Exception.
diff --git a/java/org/apache/tomcat/dbcp/dbcp2/ObjectNameWrapper.java b/java/org/apache/tomcat/dbcp/dbcp2/ObjectNameWrapper.java
index f036989..9c78936 100644
--- a/java/org/apache/tomcat/dbcp/dbcp2/ObjectNameWrapper.java
+++ b/java/org/apache/tomcat/dbcp/dbcp2/ObjectNameWrapper.java
@@ -18,6 +18,7 @@
 package org.apache.tomcat.dbcp.dbcp2;
 
 import java.lang.management.ManagementFactory;
+import java.util.Objects;
 
 import javax.management.MBeanServer;
 import javax.management.MalformedObjectNameException;
@@ -76,6 +77,14 @@ class ObjectNameWrapper {
         }
     }
 
+    /**
+     * @since 2.7.0
+     */
+    @Override
+    public String toString() {
+        return Objects.toString(objectName);
+    }
+
     public void unregisterMBean() {
         if (MBEAN_SERVER == null || objectName == null) {
             return;
diff --git a/java/org/apache/tomcat/dbcp/dbcp2/PoolableConnection.java b/java/org/apache/tomcat/dbcp/dbcp2/PoolableConnection.java
index 8b0a0f1..9c15c7e 100644
--- a/java/org/apache/tomcat/dbcp/dbcp2/PoolableConnection.java
+++ b/java/org/apache/tomcat/dbcp/dbcp2/PoolableConnection.java
@@ -329,4 +329,20 @@ public class PoolableConnection extends DelegatingConnection<Connection> impleme
         fatalSqlExceptionThrown |= isDisconnectionSqlException(e);
         super.handleException(e);
     }
+
+    /**
+     * @return The disconnection SQL codes.
+     * @since 2.6.0
+     */
+    public Collection<String> getDisconnectionSqlCodes() {
+        return disconnectionSqlCodes;
+    }
+
+    /**
+     * @return Whether to fail-fast.
+     * @since 2.6.0
+     */
+    public boolean isFastFailValidation() {
+        return fastFailValidation;
+    }
 }
diff --git a/java/org/apache/tomcat/dbcp/dbcp2/PoolableConnectionFactory.java b/java/org/apache/tomcat/dbcp/dbcp2/PoolableConnectionFactory.java
index 2720757..4123d16 100644
--- a/java/org/apache/tomcat/dbcp/dbcp2/PoolableConnectionFactory.java
+++ b/java/org/apache/tomcat/dbcp/dbcp2/PoolableConnectionFactory.java
@@ -46,6 +46,53 @@ public class PoolableConnectionFactory implements PooledObjectFactory<PoolableCo
     private static final Log log = LogFactory.getLog(PoolableConnectionFactory.class);
 
     /**
+     * Internal constant to indicate the level is not set.
+     */
+    static final int UNKNOWN_TRANSACTION_ISOLATION = -1;
+
+    private final ConnectionFactory connectionFactory;
+
+    private final ObjectName dataSourceJmxObjectName;
+
+    private volatile String validationQuery;
+
+    private volatile int validationQueryTimeoutSeconds = -1;
+
+    private Collection<String> connectionInitSqls;
+
+    private Collection<String> disconnectionSqlCodes;
+
+    private boolean fastFailValidation = true;
+
+    private volatile ObjectPool<PoolableConnection> pool;
+
+    private Boolean defaultReadOnly;
+
+    private Boolean defaultAutoCommit;
+
+    private boolean autoCommitOnReturn = true;
+
+    private boolean rollbackOnReturn = true;
+
+    private int defaultTransactionIsolation = UNKNOWN_TRANSACTION_ISOLATION;
+
+    private String defaultCatalog;
+
+    private String defaultSchema;
+
+    private boolean cacheState;
+
+    private boolean poolStatements;
+
+    private int maxOpenPreparedStatements = GenericKeyedObjectPoolConfig.DEFAULT_MAX_TOTAL_PER_KEY;
+
+    private long maxConnLifetimeMillis = -1;
+
+    private final AtomicLong connectionIndex = new AtomicLong(0);
+
+    private Integer defaultQueryTimeoutSeconds;
+
+    /**
      * Creates a new {@code PoolableConnectionFactory}.
      *
      * @param connFactory
@@ -58,179 +105,135 @@ public class PoolableConnectionFactory implements PooledObjectFactory<PoolableCo
         this.dataSourceJmxObjectName = dataSourceJmxObjectName;
     }
 
-    /**
-     * Sets the query I use to {@link #validateObject validate} {@link Connection}s. Should return at least one row. If
-     * not specified, {@link Connection#isValid(int)} will be used to validate connections.
-     *
-     * @param validationQuery
-     *            a query to use to {@link #validateObject validate} {@link Connection}s.
-     */
-    public void setValidationQuery(final String validationQuery) {
-        this.validationQuery = validationQuery;
+    @Override
+    public void activateObject(final PooledObject<PoolableConnection> p) throws Exception {
+
+        validateLifetime(p);
+
+        final PoolableConnection conn = p.getObject();
+        conn.activate();
+
+        if (defaultAutoCommit != null && conn.getAutoCommit() != defaultAutoCommit.booleanValue()) {
+            conn.setAutoCommit(defaultAutoCommit.booleanValue());
+        }
+        if (defaultTransactionIsolation != UNKNOWN_TRANSACTION_ISOLATION
+                && conn.getTransactionIsolation() != defaultTransactionIsolation) {
+            conn.setTransactionIsolation(defaultTransactionIsolation);
+        }
+        if (defaultReadOnly != null && conn.isReadOnly() != defaultReadOnly.booleanValue()) {
+            conn.setReadOnly(defaultReadOnly.booleanValue());
+        }
+        if (defaultCatalog != null && !defaultCatalog.equals(conn.getCatalog())) {
+            conn.setCatalog(defaultCatalog);
+        }
+        if (defaultSchema != null && !defaultSchema.equals(Jdbc41Bridge.getSchema(conn))) {
+            Jdbc41Bridge.setSchema(conn, defaultSchema);
+        }
+        conn.setDefaultQueryTimeout(defaultQueryTimeoutSeconds);
     }
 
-    /**
-     * Sets the validation query timeout, the amount of time, in seconds, that connection validation will wait for a
-     * response from the database when executing a validation query. Use a value less than or equal to 0 for no timeout.
-     *
-     * @param validationQueryTimeoutSeconds
-     *            new validation query timeout value in seconds
-     */
-    public void setValidationQueryTimeout(final int validationQueryTimeoutSeconds) {
-        this.validationQueryTimeoutSeconds = validationQueryTimeoutSeconds;
+    @Override
+    public void destroyObject(final PooledObject<PoolableConnection> p) throws Exception {
+        p.getObject().reallyClose();
     }
 
     /**
-     * Sets the SQL statements I use to initialize newly created {@link Connection}s. Using {@code null} turns off
-     * connection initialization.
-     *
-     * @param connectionInitSqls
-     *            SQL statement to initialize {@link Connection}s.
+     * @return The cache state.
+     * @since Made public in 2.6.0.
      */
-    public void setConnectionInitSql(final Collection<String> connectionInitSqls) {
-        this.connectionInitSqls = connectionInitSqls;
+    public boolean getCacheState() {
+        return cacheState;
     }
 
     /**
-     * Sets the {@link ObjectPool} in which to pool {@link Connection}s.
-     *
-     * @param pool
-     *            the {@link ObjectPool} in which to pool those {@link Connection}s
+     * @return The connection factory.
+     * @since Made public in 2.6.0.
      */
-    public synchronized void setPool(final ObjectPool<PoolableConnection> pool) {
-        if (null != this.pool && pool != this.pool) {
-            try {
-                this.pool.close();
-            } catch (final Exception e) {
-                // ignored !?!
-            }
-        }
-        this.pool = pool;
+    public ConnectionFactory getConnectionFactory() {
+        return connectionFactory;
     }
 
-    /**
-     * Returns the {@link ObjectPool} in which {@link Connection}s are pooled.
-     *
-     * @return the connection pool
-     */
-    public synchronized ObjectPool<PoolableConnection> getPool() {
-        return pool;
+    protected AtomicLong getConnectionIndex() {
+        return connectionIndex;
     }
 
     /**
-     * Sets the default "read only" setting for borrowed {@link Connection}s
-     *
-     * @param defaultReadOnly
-     *            the default "read only" setting for borrowed {@link Connection}s
+     * @return The collection of initialization SQL statements.
+     * @since 2.6.0
      */
-    public void setDefaultReadOnly(final Boolean defaultReadOnly) {
-        this.defaultReadOnly = defaultReadOnly;
+    public Collection<String> getConnectionInitSqls() {
+        return connectionInitSqls;
     }
 
     /**
-     * Sets the default "auto commit" setting for borrowed {@link Connection}s
-     *
-     * @param defaultAutoCommit
-     *            the default "auto commit" setting for borrowed {@link Connection}s
+     * @return The data source JMX ObjectName
+     * @since Made public in 2.6.0.
      */
-    public void setDefaultAutoCommit(final Boolean defaultAutoCommit) {
-        this.defaultAutoCommit = defaultAutoCommit;
+    public ObjectName getDataSourceJmxName() {
+        return dataSourceJmxObjectName;
     }
 
     /**
-     * Sets the default "Transaction Isolation" setting for borrowed {@link Connection}s
-     *
-     * @param defaultTransactionIsolation
-     *            the default "Transaction Isolation" setting for returned {@link Connection}s
+     * @return The data source JMS ObjectName.
+     * @since 2.6.0
      */
-    public void setDefaultTransactionIsolation(final int defaultTransactionIsolation) {
-        this.defaultTransactionIsolation = defaultTransactionIsolation;
+    public ObjectName getDataSourceJmxObjectName() {
+        return dataSourceJmxObjectName;
     }
 
     /**
-     * Sets the default "catalog" setting for borrowed {@link Connection}s
-     *
-     * @param defaultCatalog
-     *            the default "catalog" setting for borrowed {@link Connection}s
+     * @return Default auto-commit value.
+     * @since 2.6.0
      */
-    public void setDefaultCatalog(final String defaultCatalog) {
-        this.defaultCatalog = defaultCatalog;
+    public Boolean getDefaultAutoCommit() {
+        return defaultAutoCommit;
     }
 
     /**
-     * Sets the default "schema" setting for borrowed {@link Connection}s
-     *
-     * @param defaultSchema
-     *            the default "schema" setting for borrowed {@link Connection}s
-     * @since 2.5.0
+     * @return Default catalog.
+     * @since 2.6.0
      */
-    public void setDefaultSchema(final String defaultSchema) {
-        this.defaultSchema = defaultSchema;
-    }
-
-    public void setCacheState(final boolean cacheState) {
-        this.cacheState = cacheState;
-    }
-
-    public void setPoolStatements(final boolean poolStatements) {
-        this.poolStatements = poolStatements;
+    public String getDefaultCatalog() {
+        return defaultCatalog;
     }
 
     /**
-     * Deprecated due to typo in method name.
-     *
-     * @param maxOpenPreparedStatements
-     *            The maximum number of open prepared statements.
-     * @deprecated Use {@link #setMaxOpenPreparedStatements(int)}.
+     * @return Default query timeout in seconds.
      */
-    @Deprecated // Due to typo in method name.
-    public void setMaxOpenPrepatedStatements(final int maxOpenPreparedStatements) {
-        setMaxOpenPreparedStatements(maxOpenPreparedStatements);
+    public Integer getDefaultQueryTimeout() {
+        return defaultQueryTimeoutSeconds;
     }
 
     /**
-     * Sets the maximum number of open prepared statements.
-     *
-     * @param maxOpenPreparedStatements
-     *            The maximum number of open prepared statements.
+     * @return Default query timeout in seconds.
+     * @since 2.6.0
      */
-    public void setMaxOpenPreparedStatements(final int maxOpenPreparedStatements) {
-        this.maxOpenPreparedStatements = maxOpenPreparedStatements;
+    public Integer getDefaultQueryTimeoutSeconds() {
+        return defaultQueryTimeoutSeconds;
     }
 
     /**
-     * Sets the maximum lifetime in milliseconds of a connection after which the connection will always fail activation,
-     * passivation and validation. A value of zero or less indicates an infinite lifetime. The default value is -1.
-     *
-     * @param maxConnLifetimeMillis
-     *            The maximum lifetime in milliseconds.
+     * @return Default read-only-value.
+     * @since 2.6.0
      */
-    public void setMaxConnLifetimeMillis(final long maxConnLifetimeMillis) {
-        this.maxConnLifetimeMillis = maxConnLifetimeMillis;
-    }
-
-    public boolean isEnableAutoCommitOnReturn() {
-        return enableAutoCommitOnReturn;
-    }
-
-    public void setEnableAutoCommitOnReturn(final boolean enableAutoCommitOnReturn) {
-        this.enableAutoCommitOnReturn = enableAutoCommitOnReturn;
-    }
-
-    public boolean isRollbackOnReturn() {
-        return rollbackOnReturn;
+    public Boolean getDefaultReadOnly() {
+        return defaultReadOnly;
     }
 
-    public void setRollbackOnReturn(final boolean rollbackOnReturn) {
-        this.rollbackOnReturn = rollbackOnReturn;
-    }
-
-    public Integer getDefaultQueryTimeout() {
-        return defaultQueryTimeoutSeconds;
+    /**
+     * @return Default schema.
+     * @since 2.6.0
+     */
+    public String getDefaultSchema() {
+        return defaultSchema;
     }
 
-    public void setDefaultQueryTimeout(final Integer defaultQueryTimeoutSeconds) {
-        this.defaultQueryTimeoutSeconds = defaultQueryTimeoutSeconds;
+    /**
+     * @return Default transaction isolation.
+     * @since 2.6.0
+     */
+    public int getDefaultTransactionIsolation() {
+        return defaultTransactionIsolation;
     }
 
     /**
@@ -254,51 +257,112 @@ public class PoolableConnectionFactory implements PooledObjectFactory<PoolableCo
     }
 
     /**
-     * @param disconnectionSqlCodes
-     *            The disconnection SQL codes.
-     * @see #getDisconnectionSqlCodes()
-     * @since 2.1
+     * @return Maximum connection lifetime in milliseconds.
+     * @since 2.6.0
      */
-    public void setDisconnectionSqlCodes(final Collection<String> disconnectionSqlCodes) {
-        this.disconnectionSqlCodes = disconnectionSqlCodes;
+    public long getMaxConnLifetimeMillis() {
+        return maxConnLifetimeMillis;
+    }
+
+    protected int getMaxOpenPreparedStatements() {
+        return maxOpenPreparedStatements;
     }
 
     /**
-     * True means that validation will fail immediately for connections that have previously thrown SQLExceptions with
-     * SQL_STATE indicating fatal disconnection errors.
+     * Returns the {@link ObjectPool} in which {@link Connection}s are pooled.
      *
-     * @return true if connections created by this factory will fast fail validation.
-     * @see #setDisconnectionSqlCodes(Collection)
-     * @since 2.1
-     * @since 2.5.0 Defaults to true, previous versions defaulted to false.
+     * @return the connection pool
      */
-    public boolean isFastFailValidation() {
-        return fastFailValidation;
+    public synchronized ObjectPool<PoolableConnection> getPool() {
+        return pool;
     }
 
     /**
-     * @see #isFastFailValidation()
-     * @param fastFailValidation
-     *            true means connections created by this factory will fast fail validation
-     * @since 2.1
+     * @return Whether to pool statements.
+     * @since Made public in 2.6.0.
      */
-    public void setFastFailValidation(final boolean fastFailValidation) {
-        this.fastFailValidation = fastFailValidation;
+    public boolean getPoolStatements() {
+        return poolStatements;
     }
-
-    @Override
-    public PooledObject<PoolableConnection> makeObject() throws Exception {
-        Connection conn = connectionFactory.createConnection();
-        if (conn == null) {
-            throw new IllegalStateException("Connection factory returned null from createConnection");
+    /**
+     * @return Validation query.
+     * @since 2.6.0
+     */
+    public String getValidationQuery() {
+        return validationQuery;
+    }
+    /**
+     * @return Validation query timeout in seconds.
+     * @since 2.6.0
+     */
+    public int getValidationQueryTimeoutSeconds() {
+        return validationQueryTimeoutSeconds;
+    }
+    protected void initializeConnection(final Connection conn) throws SQLException {
+        final Collection<String> sqls = connectionInitSqls;
+        if (conn.isClosed()) {
+            throw new SQLException("initializeConnection: connection closed");
         }
-        try {
-            initializeConnection(conn);
-        } catch (final SQLException sqle) {
-            // Make sure the connection is closed
-            try {
-                conn.close();
-            } catch (final SQLException ignore) {
+        if (null != sqls) {
+            try (Statement stmt = conn.createStatement()) {
+                for (final String sql : sqls) {
+                    Objects.requireNonNull(sql, "null connectionInitSqls element");
+                    stmt.execute(sql);
+                }
+            }
+        }
+    }
+
+    /**
+     * @return Whether to auto-commit on return.
+     * @since 2.6.0
+     */
+    public boolean isAutoCommitOnReturn() {
+        return autoCommitOnReturn;
+    }
+
+    /**
+     * @return Whether to auto-commit on return.
+     * @deprecated Use {@link #isAutoCommitOnReturn()}.
+     */
+    @Deprecated
+    public boolean isEnableAutoCommitOnReturn() {
+        return autoCommitOnReturn;
+    }
+
+    /**
+     * True means that validation will fail immediately for connections that have previously thrown SQLExceptions with
+     * SQL_STATE indicating fatal disconnection errors.
+     *
+     * @return true if connections created by this factory will fast fail validation.
+     * @see #setDisconnectionSqlCodes(Collection)
+     * @since 2.1
+     * @since 2.5.0 Defaults to true, previous versions defaulted to false.
+     */
+    public boolean isFastFailValidation() {
+        return fastFailValidation;
+    }
+
+    /**
+     * @return Whether to rollback on return.
+     */
+    public boolean isRollbackOnReturn() {
+        return rollbackOnReturn;
+    }
+
+    @Override
+    public PooledObject<PoolableConnection> makeObject() throws Exception {
+        Connection conn = connectionFactory.createConnection();
+        if (conn == null) {
+            throw new IllegalStateException("Connection factory returned null from createConnection");
+        }
+        try {
+            initializeConnection(conn);
+        } catch (final SQLException sqle) {
+            // Make sure the connection is closed
+            try {
+                conn.close();
+            } catch (final SQLException ignore) {
                 // ignore
             }
             // Rethrow original exception so it is visible to caller
@@ -347,48 +411,6 @@ public class PoolableConnectionFactory implements PooledObjectFactory<PoolableCo
         return new DefaultPooledObject<>(pc);
     }
 
-    protected void initializeConnection(final Connection conn) throws SQLException {
-        final Collection<String> sqls = connectionInitSqls;
-        if (conn.isClosed()) {
-            throw new SQLException("initializeConnection: connection closed");
-        }
-        if (null != sqls) {
-            try (Statement stmt = conn.createStatement()) {
-                for (final String sql : sqls) {
-                    Objects.requireNonNull(sql, "null connectionInitSqls element");
-                    stmt.execute(sql);
-                }
-            }
-        }
-    }
-
-    @Override
-    public void destroyObject(final PooledObject<PoolableConnection> p) throws Exception {
-        p.getObject().reallyClose();
-    }
-
-    @Override
-    public boolean validateObject(final PooledObject<PoolableConnection> p) {
-        try {
-            validateLifetime(p);
-
-            validateConnection(p.getObject());
-            return true;
-        } catch (final Exception e) {
-            if (log.isDebugEnabled()) {
-                log.debug(Utils.getMessage("poolableConnectionFactory.validateObject.fail"), e);
-            }
-            return false;
-        }
-    }
-
-    public void validateConnection(final PoolableConnection conn) throws SQLException {
-        if (conn.isClosed()) {
-            throw new SQLException("validateConnection: connection closed");
-        }
-        conn.validate(validationQuery, validationQueryTimeoutSeconds);
-    }
-
     @Override
     public void passivateObject(final PooledObject<PoolableConnection> p) throws Exception {
 
@@ -407,7 +429,7 @@ public class PoolableConnectionFactory implements PooledObjectFactory<PoolableCo
 
         // DBCP-97 / DBCP-399 / DBCP-351 Idle connections in the pool should
         // have autoCommit enabled
-        if (enableAutoCommitOnReturn) {
+        if (autoCommitOnReturn) {
             if (connAutoCommit == null) {
                 connAutoCommit = Boolean.valueOf(conn.getAutoCommit());
             }
@@ -419,91 +441,217 @@ public class PoolableConnectionFactory implements PooledObjectFactory<PoolableCo
         conn.passivate();
     }
 
-    @Override
-    public void activateObject(final PooledObject<PoolableConnection> p) throws Exception {
+    public void setAutoCommitOnReturn(final boolean autoCommitOnReturn) {
+        this.autoCommitOnReturn = autoCommitOnReturn;
+    }
 
-        validateLifetime(p);
+    public void setCacheState(final boolean cacheState) {
+        this.cacheState = cacheState;
+    }
 
-        final PoolableConnection conn = p.getObject();
-        conn.activate();
+    /**
+     * Sets the SQL statements I use to initialize newly created {@link Connection}s. Using {@code null} turns off
+     * connection initialization.
+     *
+     * @param connectionInitSqls
+     *            SQL statement to initialize {@link Connection}s.
+     */
+    public void setConnectionInitSql(final Collection<String> connectionInitSqls) {
+        this.connectionInitSqls = connectionInitSqls;
+    }
 
-        if (defaultAutoCommit != null && conn.getAutoCommit() != defaultAutoCommit.booleanValue()) {
-            conn.setAutoCommit(defaultAutoCommit.booleanValue());
-        }
-        if (defaultTransactionIsolation != UNKNOWN_TRANSACTIONISOLATION
-                && conn.getTransactionIsolation() != defaultTransactionIsolation) {
-            conn.setTransactionIsolation(defaultTransactionIsolation);
-        }
-        if (defaultReadOnly != null && conn.isReadOnly() != defaultReadOnly.booleanValue()) {
-            conn.setReadOnly(defaultReadOnly.booleanValue());
-        }
-        if (defaultCatalog != null && !defaultCatalog.equals(conn.getCatalog())) {
-            conn.setCatalog(defaultCatalog);
-        }
-        if (defaultSchema != null && !defaultSchema.equals(conn.getSchema())) {
-            conn.setSchema(defaultSchema);
-        }
-        conn.setDefaultQueryTimeout(defaultQueryTimeoutSeconds);
+    /**
+     * Sets the default "auto commit" setting for borrowed {@link Connection}s
+     *
+     * @param defaultAutoCommit
+     *            the default "auto commit" setting for borrowed {@link Connection}s
+     */
+    public void setDefaultAutoCommit(final Boolean defaultAutoCommit) {
+        this.defaultAutoCommit = defaultAutoCommit;
     }
 
-    private void validateLifetime(final PooledObject<PoolableConnection> p) throws Exception {
-        if (maxConnLifetimeMillis > 0) {
-            final long lifetime = System.currentTimeMillis() - p.getCreateTime();
-            if (lifetime > maxConnLifetimeMillis) {
-                throw new LifetimeExceededException(Utils.getMessage("connectionFactory.lifetimeExceeded",
-                        Long.valueOf(lifetime), Long.valueOf(maxConnLifetimeMillis)));
-            }
-        }
+    /**
+     * Sets the default "catalog" setting for borrowed {@link Connection}s
+     *
+     * @param defaultCatalog
+     *            the default "catalog" setting for borrowed {@link Connection}s
+     */
+    public void setDefaultCatalog(final String defaultCatalog) {
+        this.defaultCatalog = defaultCatalog;
     }
 
-    protected ConnectionFactory getConnectionFactory() {
-        return connectionFactory;
+    public void setDefaultQueryTimeout(final Integer defaultQueryTimeoutSeconds) {
+        this.defaultQueryTimeoutSeconds = defaultQueryTimeoutSeconds;
+    }
+    /**
+     * Sets the default "read only" setting for borrowed {@link Connection}s
+     *
+     * @param defaultReadOnly
+     *            the default "read only" setting for borrowed {@link Connection}s
+     */
+    public void setDefaultReadOnly(final Boolean defaultReadOnly) {
+        this.defaultReadOnly = defaultReadOnly;
     }
 
-    protected boolean getPoolStatements() {
-        return poolStatements;
+    /**
+     * Sets the default "schema" setting for borrowed {@link Connection}s
+     *
+     * @param defaultSchema
+     *            the default "schema" setting for borrowed {@link Connection}s
+     * @since 2.5.0
+     */
+    public void setDefaultSchema(final String defaultSchema) {
+        this.defaultSchema = defaultSchema;
     }
 
-    protected int getMaxOpenPreparedStatements() {
-        return maxOpenPreparedStatements;
+    /**
+     * Sets the default "Transaction Isolation" setting for borrowed {@link Connection}s
+     *
+     * @param defaultTransactionIsolation
+     *            the default "Transaction Isolation" setting for returned {@link Connection}s
+     */
+    public void setDefaultTransactionIsolation(final int defaultTransactionIsolation) {
+        this.defaultTransactionIsolation = defaultTransactionIsolation;
     }
 
-    protected boolean getCacheState() {
-        return cacheState;
+    /**
+     * @param disconnectionSqlCodes
+     *            The disconnection SQL codes.
+     * @see #getDisconnectionSqlCodes()
+     * @since 2.1
+     */
+    public void setDisconnectionSqlCodes(final Collection<String> disconnectionSqlCodes) {
+        this.disconnectionSqlCodes = disconnectionSqlCodes;
     }
 
-    protected ObjectName getDataSourceJmxName() {
-        return dataSourceJmxObjectName;
+    /**
+     * @param autoCommitOnReturn Whether to auto-commit on return.
+     * @deprecated Use {@link #setAutoCommitOnReturn(boolean)}.
+     */
+    @Deprecated
+    public void setEnableAutoCommitOnReturn(final boolean autoCommitOnReturn) {
+        this.autoCommitOnReturn = autoCommitOnReturn;
     }
 
-    protected AtomicLong getConnectionIndex() {
-        return connectionIndex;
+    /**
+     * @see #isFastFailValidation()
+     * @param fastFailValidation
+     *            true means connections created by this factory will fast fail validation
+     * @since 2.1
+     */
+    public void setFastFailValidation(final boolean fastFailValidation) {
+        this.fastFailValidation = fastFailValidation;
     }
 
-    private final ConnectionFactory connectionFactory;
-    private final ObjectName dataSourceJmxObjectName;
-    private volatile String validationQuery;
-    private volatile int validationQueryTimeoutSeconds = -1;
-    private Collection<String> connectionInitSqls;
-    private Collection<String> disconnectionSqlCodes;
-    private boolean fastFailValidation = true;
-    private volatile ObjectPool<PoolableConnection> pool;
-    private Boolean defaultReadOnly;
-    private Boolean defaultAutoCommit;
-    private boolean enableAutoCommitOnReturn = true;
-    private boolean rollbackOnReturn = true;
-    private int defaultTransactionIsolation = UNKNOWN_TRANSACTIONISOLATION;
-    private String defaultCatalog;
-    private String defaultSchema;
-    private boolean cacheState;
-    private boolean poolStatements;
-    private int maxOpenPreparedStatements = GenericKeyedObjectPoolConfig.DEFAULT_MAX_TOTAL_PER_KEY;
-    private long maxConnLifetimeMillis = -1;
-    private final AtomicLong connectionIndex = new AtomicLong(0);
-    private Integer defaultQueryTimeoutSeconds;
+    /**
+     * Sets the maximum lifetime in milliseconds of a connection after which the connection will always fail activation,
+     * passivation and validation. A value of zero or less indicates an infinite lifetime. The default value is -1.
+     *
+     * @param maxConnLifetimeMillis
+     *            The maximum lifetime in milliseconds.
+     */
+    public void setMaxConnLifetimeMillis(final long maxConnLifetimeMillis) {
+        this.maxConnLifetimeMillis = maxConnLifetimeMillis;
+    }
 
     /**
-     * Internal constant to indicate the level is not set.
+     * Sets the maximum number of open prepared statements.
+     *
+     * @param maxOpenPreparedStatements
+     *            The maximum number of open prepared statements.
+     */
+    public void setMaxOpenPreparedStatements(final int maxOpenPreparedStatements) {
+        this.maxOpenPreparedStatements = maxOpenPreparedStatements;
+    }
+
+    /**
+     * Deprecated due to typo in method name.
+     *
+     * @param maxOpenPreparedStatements
+     *            The maximum number of open prepared statements.
+     * @deprecated Use {@link #setMaxOpenPreparedStatements(int)}.
      */
-    static final int UNKNOWN_TRANSACTIONISOLATION = -1;
+    @Deprecated // Due to typo in method name.
+    public void setMaxOpenPrepatedStatements(final int maxOpenPreparedStatements) {
+        setMaxOpenPreparedStatements(maxOpenPreparedStatements);
+    }
+
+    /**
+     * Sets the {@link ObjectPool} in which to pool {@link Connection}s.
+     *
+     * @param pool
+     *            the {@link ObjectPool} in which to pool those {@link Connection}s
+     */
+    public synchronized void setPool(final ObjectPool<PoolableConnection> pool) {
+        if (null != this.pool && pool != this.pool) {
+            try {
+                this.pool.close();
+            } catch (final Exception e) {
+                // ignored !?!
+            }
+        }
+        this.pool = pool;
+    }
+
+    public void setPoolStatements(final boolean poolStatements) {
+        this.poolStatements = poolStatements;
+    }
+
+    public void setRollbackOnReturn(final boolean rollbackOnReturn) {
+        this.rollbackOnReturn = rollbackOnReturn;
+    }
+
+    /**
+     * Sets the query I use to {@link #validateObject validate} {@link Connection}s. Should return at least one row. If
+     * not specified, {@link Connection#isValid(int)} will be used to validate connections.
+     *
+     * @param validationQuery
+     *            a query to use to {@link #validateObject validate} {@link Connection}s.
+     */
+    public void setValidationQuery(final String validationQuery) {
+        this.validationQuery = validationQuery;
+    }
+
+    /**
+     * Sets the validation query timeout, the amount of time, in seconds, that connection validation will wait for a
+     * response from the database when executing a validation query. Use a value less than or equal to 0 for no timeout.
+     *
+     * @param validationQueryTimeoutSeconds
+     *            new validation query timeout value in seconds
+     */
+    public void setValidationQueryTimeout(final int validationQueryTimeoutSeconds) {
+        this.validationQueryTimeoutSeconds = validationQueryTimeoutSeconds;
+    }
+
+    public void validateConnection(final PoolableConnection conn) throws SQLException {
+        if (conn.isClosed()) {
+            throw new SQLException("validateConnection: connection closed");
+        }
+        conn.validate(validationQuery, validationQueryTimeoutSeconds);
+    }
+
+    private void validateLifetime(final PooledObject<PoolableConnection> p) throws Exception {
+        if (maxConnLifetimeMillis > 0) {
+            final long lifetime = System.currentTimeMillis() - p.getCreateTime();
+            if (lifetime > maxConnLifetimeMillis) {
+                throw new LifetimeExceededException(Utils.getMessage("connectionFactory.lifetimeExceeded",
+                        Long.valueOf(lifetime), Long.valueOf(maxConnLifetimeMillis)));
+            }
+        }
+    }
+
+    @Override
+    public boolean validateObject(final PooledObject<PoolableConnection> p) {
+        try {
+            validateLifetime(p);
+
+            validateConnection(p.getObject());
+            return true;
+        } catch (final Exception e) {
+            if (log.isDebugEnabled()) {
+                log.debug(Utils.getMessage("poolableConnectionFactory.validateObject.fail"), e);
+            }
+            return false;
+        }
+    }
 }
diff --git a/java/org/apache/tomcat/dbcp/dbcp2/PoolingConnection.java b/java/org/apache/tomcat/dbcp/dbcp2/PoolingConnection.java
index cb9252e..57dd54a 100644
--- a/java/org/apache/tomcat/dbcp/dbcp2/PoolingConnection.java
+++ b/java/org/apache/tomcat/dbcp/dbcp2/PoolingConnection.java
@@ -579,7 +579,7 @@ public class PoolingConnection extends DelegatingConnection<Connection>
     }
 
     @Override
-    public String toString() {
+    public synchronized String toString() {
         if (pstmtPool != null) {
             return "PoolingConnection: " + pstmtPool.toString();
         }
diff --git a/java/org/apache/tomcat/dbcp/dbcp2/PoolingDataSource.java b/java/org/apache/tomcat/dbcp/dbcp2/PoolingDataSource.java
index 70601b0..38a0472 100644
--- a/java/org/apache/tomcat/dbcp/dbcp2/PoolingDataSource.java
+++ b/java/org/apache/tomcat/dbcp/dbcp2/PoolingDataSource.java
@@ -75,7 +75,7 @@ public class PoolingDataSource<C extends Connection> implements DataSource, Auto
      * @since 2.1
      */
     @Override
-    public void close() throws Exception {
+    public void close() throws RuntimeException, SQLException {
         try {
             pool.close();
         } catch (final RuntimeException rte) {
diff --git a/java/org/apache/tomcat/dbcp/dbcp2/Utils.java b/java/org/apache/tomcat/dbcp/dbcp2/Utils.java
index c80de46..a24360f 100644
--- a/java/org/apache/tomcat/dbcp/dbcp2/Utils.java
+++ b/java/org/apache/tomcat/dbcp/dbcp2/Utils.java
@@ -18,9 +18,6 @@
 
 package org.apache.tomcat.dbcp.dbcp2;
 
-import java.sql.Connection;
-import java.sql.ResultSet;
-import java.sql.Statement;
 import java.text.MessageFormat;
 import java.util.HashSet;
 import java.util.ResourceBundle;
@@ -47,9 +44,9 @@ public final class Utils {
     /**
      * SQL codes of fatal connection errors.
      * <ul>
-     * <li>57P01 (ADMIN SHUTDOWN)</li>
-     * <li>57P02 (CRASH SHUTDOWN)</li>
-     * <li>57P03 (CANNOT CONNECT NOW)</li>
+     * <li>57P01 (Admin shutdown)</li>
+     * <li>57P02 (Crash shutdown)</li>
+     * <li>57P03 (Cannot connect now)</li>
      * <li>01002 (SQL92 disconnect error)</li>
      * <li>JZ0C0 (Sybase disconnect error)</li>
      * <li>JZ0C1 (Sybase disconnect error)</li>
@@ -59,18 +56,14 @@ public final class Utils {
 
     static {
         DISCONNECTION_SQL_CODES = new HashSet<>();
-        DISCONNECTION_SQL_CODES.add("57P01"); // ADMIN SHUTDOWN
-        DISCONNECTION_SQL_CODES.add("57P02"); // CRASH SHUTDOWN
-        DISCONNECTION_SQL_CODES.add("57P03"); // CANNOT CONNECT NOW
+        DISCONNECTION_SQL_CODES.add("57P01"); // Admin shutdown
+        DISCONNECTION_SQL_CODES.add("57P02"); // Crash shutdown
+        DISCONNECTION_SQL_CODES.add("57P03"); // Cannot connect now
         DISCONNECTION_SQL_CODES.add("01002"); // SQL92 disconnect error
         DISCONNECTION_SQL_CODES.add("JZ0C0"); // Sybase disconnect error
         DISCONNECTION_SQL_CODES.add("JZ0C1"); // Sybase disconnect error
     }
 
-    private Utils() {
-        // not instantiable
-    }
-
     /**
      * Clones the given char[] if not null.
      *
@@ -83,47 +76,16 @@ public final class Utils {
     }
 
     /**
-     * Closes the ResultSet (which may be null).
-     *
-     * @param resultSet
-     *            a ResultSet, may be {@code null}
-     */
-    public static void closeQuietly(final ResultSet resultSet) {
-        if (resultSet != null) {
-            try {
-                resultSet.close();
-            } catch (final Exception e) {
-                // ignored
-            }
-        }
-    }
-
-    /**
-     * Closes the Connection (which may be null).
+     * Closes the AutoCloseable (which may be null).
      *
-     * @param connection
-     *            a Connection, may be {@code null}
+     * @param autoCloseable
+     *            an AutoCloseable, may be {@code null}
+     * @since 2.6.0
      */
-    public static void closeQuietly(final Connection connection) {
-        if (connection != null) {
+    public static void closeQuietly(final AutoCloseable autoCloseable) {
+        if (autoCloseable != null) {
             try {
-                connection.close();
-            } catch (final Exception e) {
-                // ignored
-            }
-        }
-    }
-
-    /**
-     * Closes the Statement (which may be null).
-     *
-     * @param statement
-     *            a Statement, may be {@code null}.
-     */
-    public static void closeQuietly(final Statement statement) {
-        if (statement != null) {
-            try {
-                statement.close();
+                autoCloseable.close();
             } catch (final Exception e) {
                 // ignored
             }
@@ -181,4 +143,8 @@ public final class Utils {
         return value == null ? null : String.valueOf(value);
     }
 
+    private Utils() {
+        // not instantiable
+    }
+
 }
diff --git a/java/org/apache/tomcat/dbcp/dbcp2/cpdsadapter/DriverAdapterCPDS.java b/java/org/apache/tomcat/dbcp/dbcp2/cpdsadapter/DriverAdapterCPDS.java
index 902edfb..fe0944f 100644
--- a/java/org/apache/tomcat/dbcp/dbcp2/cpdsadapter/DriverAdapterCPDS.java
+++ b/java/org/apache/tomcat/dbcp/dbcp2/cpdsadapter/DriverAdapterCPDS.java
@@ -730,4 +730,49 @@ public class DriverAdapterCPDS implements ConnectionPoolDataSource, Referenceabl
             }
         }
     }
+
+    /**
+     * Does not print the userName and userPassword field nor the 'user' or 'password' in the connectionProperties.
+     *
+     * @since 2.6.0
+     */
+    @Override
+    public synchronized String toString() {
+        final StringBuilder builder = new StringBuilder(super.toString());
+        builder.append("[description=");
+        builder.append(description);
+        builder.append(", url=");
+        // TODO What if the connection string contains a 'user' or 'password' query parameter but that connection string is not in a legal URL format?
+        builder.append(url);
+        builder.append(", driver=");
+        builder.append(driver);
+        builder.append(", loginTimeout=");
+        builder.append(loginTimeout);
+        builder.append(", poolPreparedStatements=");
+        builder.append(poolPreparedStatements);
+        builder.append(", maxIdle=");
+        builder.append(maxIdle);
+        builder.append(", timeBetweenEvictionRunsMillis=");
+        builder.append(timeBetweenEvictionRunsMillis);
+        builder.append(", numTestsPerEvictionRun=");
+        builder.append(numTestsPerEvictionRun);
+        builder.append(", minEvictableIdleTimeMillis=");
+        builder.append(minEvictableIdleTimeMillis);
+        builder.append(", maxPreparedStatements=");
+        builder.append(maxPreparedStatements);
+        builder.append(", getConnectionCalled=");
+        builder.append(getConnectionCalled);
+        builder.append(", connectionProperties=");
+        Properties tmpProps = connectionProperties;
+        final String pwdKey = "password";
+        if (connectionProperties.contains(pwdKey)) {
+            tmpProps = (Properties) connectionProperties.clone();
+            tmpProps.remove(pwdKey);
+        }
+        builder.append(tmpProps);
+        builder.append(", accessToUnderlyingConnectionAllowed=");
+        builder.append(accessToUnderlyingConnectionAllowed);
+        builder.append("]");
+        return builder.toString();
+    }
 }
diff --git a/java/org/apache/tomcat/dbcp/dbcp2/cpdsadapter/PooledConnectionImpl.java b/java/org/apache/tomcat/dbcp/dbcp2/cpdsadapter/PooledConnectionImpl.java
index babc2ad..74b9eee 100644
--- a/java/org/apache/tomcat/dbcp/dbcp2/cpdsadapter/PooledConnectionImpl.java
+++ b/java/org/apache/tomcat/dbcp/dbcp2/cpdsadapter/PooledConnectionImpl.java
@@ -30,6 +30,7 @@ import javax.sql.StatementEventListener;
 
 import org.apache.tomcat.dbcp.dbcp2.DelegatingConnection;
 import org.apache.tomcat.dbcp.dbcp2.DelegatingPreparedStatement;
+import org.apache.tomcat.dbcp.dbcp2.Jdbc41Bridge;
 import org.apache.tomcat.dbcp.dbcp2.PStmtKey;
 import org.apache.tomcat.dbcp.dbcp2.PoolableCallableStatement;
 import org.apache.tomcat.dbcp.dbcp2.PoolablePreparedStatement;
@@ -295,7 +296,7 @@ class PooledConnectionImpl
 
     private String getSchemaOrNull() {
         try {
-            return connection == null ? null : connection.getSchema();
+            return connection == null ? null : Jdbc41Bridge.getSchema(connection);
         } catch (final SQLException e) {
             return null;
         }
diff --git a/java/org/apache/tomcat/dbcp/dbcp2/datasources/CPDSConnectionFactory.java b/java/org/apache/tomcat/dbcp/dbcp2/datasources/CPDSConnectionFactory.java
index ea609f4..02929e9 100644
--- a/java/org/apache/tomcat/dbcp/dbcp2/datasources/CPDSConnectionFactory.java
+++ b/java/org/apache/tomcat/dbcp/dbcp2/datasources/CPDSConnectionFactory.java
@@ -397,4 +397,30 @@ class CPDSConnectionFactory
             }
         }
     }
+
+    /**
+     * @since 2.6.0
+     */
+    @Override
+    public synchronized String toString() {
+        final StringBuilder builder = new StringBuilder(super.toString());
+        builder.append("[cpds=");
+        builder.append(cpds);
+        builder.append(", validationQuery=");
+        builder.append(validationQuery);
+        builder.append(", validationQueryTimeoutSeconds=");
+        builder.append(validationQueryTimeoutSeconds);
+        builder.append(", rollbackAfterValidation=");
+        builder.append(rollbackAfterValidation);
+        builder.append(", pool=");
+        builder.append(pool);
+        builder.append(", maxConnLifetimeMillis=");
+        builder.append(maxConnLifetimeMillis);
+        builder.append(", validatingSet=");
+        builder.append(validatingSet);
+        builder.append(", pcMap=");
+        builder.append(pcMap);
+        builder.append("]");
+        return builder.toString();
+    }
 }
diff --git a/java/org/apache/tomcat/dbcp/dbcp2/datasources/InstanceKeyDataSource.java b/java/org/apache/tomcat/dbcp/dbcp2/datasources/InstanceKeyDataSource.java
index 444ec1d..a0fe1b4 100644
--- a/java/org/apache/tomcat/dbcp/dbcp2/datasources/InstanceKeyDataSource.java
+++ b/java/org/apache/tomcat/dbcp/dbcp2/datasources/InstanceKeyDataSource.java
@@ -1053,4 +1053,79 @@ public abstract class InstanceKeyDataSource implements DataSource, Referenceable
         }
         return cpds;
     }
+
+    /**
+     * @since 2.6.0
+     */
+    @Override
+    public synchronized String toString() {
+        final StringBuilder builder = new StringBuilder(super.toString());
+        builder.append("[");
+        toStringFields(builder);
+        builder.append("]");
+        return builder.toString();
+    }
+
+    protected void toStringFields(final StringBuilder builder) {
+        builder.append("getConnectionCalled=");
+        builder.append(getConnectionCalled);
+        builder.append(", dataSource=");
+        builder.append(dataSource);
+        builder.append(", dataSourceName=");
+        builder.append(dataSourceName);
+        builder.append(", description=");
+        builder.append(description);
+        builder.append(", jndiEnvironment=");
+        builder.append(jndiEnvironment);
+        builder.append(", loginTimeout=");
+        builder.append(loginTimeout);
+        builder.append(", logWriter=");
+        builder.append(logWriter);
+        builder.append(", instanceKey=");
+        builder.append(instanceKey);
+        builder.append(", defaultBlockWhenExhausted=");
+        builder.append(defaultBlockWhenExhausted);
+        builder.append(", defaultEvictionPolicyClassName=");
+        builder.append(defaultEvictionPolicyClassName);
+        builder.append(", defaultLifo=");
+        builder.append(defaultLifo);
+        builder.append(", defaultMaxIdle=");
+        builder.append(defaultMaxIdle);
+        builder.append(", defaultMaxTotal=");
+        builder.append(defaultMaxTotal);
+        builder.append(", defaultMaxWaitMillis=");
+        builder.append(defaultMaxWaitMillis);
+        builder.append(", defaultMinEvictableIdleTimeMillis=");
+        builder.append(defaultMinEvictableIdleTimeMillis);
+        builder.append(", defaultMinIdle=");
+        builder.append(defaultMinIdle);
+        builder.append(", defaultNumTestsPerEvictionRun=");
+        builder.append(defaultNumTestsPerEvictionRun);
+        builder.append(", defaultSoftMinEvictableIdleTimeMillis=");
+        builder.append(defaultSoftMinEvictableIdleTimeMillis);
+        builder.append(", defaultTestOnCreate=");
+        builder.append(defaultTestOnCreate);
+        builder.append(", defaultTestOnBorrow=");
+        builder.append(defaultTestOnBorrow);
+        builder.append(", defaultTestOnReturn=");
+        builder.append(defaultTestOnReturn);
+        builder.append(", defaultTestWhileIdle=");
+        builder.append(defaultTestWhileIdle);
+        builder.append(", defaultTimeBetweenEvictionRunsMillis=");
+        builder.append(defaultTimeBetweenEvictionRunsMillis);
+        builder.append(", validationQuery=");
+        builder.append(validationQuery);
+        builder.append(", validationQueryTimeoutSeconds=");
+        builder.append(validationQueryTimeoutSeconds);
+        builder.append(", rollbackAfterValidation=");
+        builder.append(rollbackAfterValidation);
+        builder.append(", maxConnLifetimeMillis=");
+        builder.append(maxConnLifetimeMillis);
+        builder.append(", defaultAutoCommit=");
+        builder.append(defaultAutoCommit);
+        builder.append(", defaultTransactionIsolation=");
+        builder.append(defaultTransactionIsolation);
+        builder.append(", defaultReadOnly=");
+        builder.append(defaultReadOnly);
+    }
 }
diff --git a/java/org/apache/tomcat/dbcp/dbcp2/datasources/SharedPoolDataSource.java b/java/org/apache/tomcat/dbcp/dbcp2/datasources/SharedPoolDataSource.java
index 40d3851..02188c5 100644
--- a/java/org/apache/tomcat/dbcp/dbcp2/datasources/SharedPoolDataSource.java
+++ b/java/org/apache/tomcat/dbcp/dbcp2/datasources/SharedPoolDataSource.java
@@ -235,4 +235,11 @@ public class SharedPoolDataSource extends InstanceKeyDataSource {
             throw new IOException("NamingException: " + e);
         }
     }
+
+    @Override
+    protected void toStringFields(final StringBuilder builder) {
+        super.toStringFields(builder);
+        builder.append(", maxTotal=");
+        builder.append(maxTotal);
+    }
 }
diff --git a/java/org/apache/tomcat/dbcp/dbcp2/datasources/UserPassKey.java b/java/org/apache/tomcat/dbcp/dbcp2/datasources/UserPassKey.java
index 4bcf4ab..e357028 100644
--- a/java/org/apache/tomcat/dbcp/dbcp2/datasources/UserPassKey.java
+++ b/java/org/apache/tomcat/dbcp/dbcp2/datasources/UserPassKey.java
@@ -125,9 +125,10 @@ class UserPassKey implements Serializable {
 
     @Override
     public String toString() {
-        final StringBuffer sb = new StringBuffer(50);
-        sb.append("UserPassKey(");
-        sb.append(userName).append(", ").append(userPassword).append(')');
+        final StringBuffer sb = new StringBuffer(super.toString());
+        sb.append("[");
+        sb.append(userName);
+        sb.append(']');
         return sb.toString();
     }
 }
diff --git a/java/org/apache/tomcat/dbcp/dbcp2/overview.html b/java/org/apache/tomcat/dbcp/dbcp2/overview.html
deleted file mode 100644
index f15a5e1..0000000
--- a/java/org/apache/tomcat/dbcp/dbcp2/overview.html
+++ /dev/null
@@ -1,26 +0,0 @@
-<!--
- Licensed to the Apache Software Foundation (ASF) under one or more
- contributor license agreements.  See the NOTICE file distributed with
- this work for additional information regarding copyright ownership.
- The ASF licenses this file to You under the Apache License, Version 2.0
- (the "License"); you may not use this file except in compliance with
- the License.  You may obtain a copy of the License at
-
-      http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
--->
-<html>
-   <head>
-      <title>Overview of the org.apache.commons.dbcp component</title>
-   </head>
-   <body>
-      <p>
-         Commons Database Connection Pooling
-      </p>
-   </body>
-</html>
diff --git a/webapps/docs/changelog.xml b/webapps/docs/changelog.xml
index fff45a8..37addb6 100644
--- a/webapps/docs/changelog.xml
+++ b/webapps/docs/changelog.xml
@@ -126,6 +126,11 @@
         When using the <code>OneLineFormatter</code>, don't print a blank line
         in the log after printing a stack trace. (markt)
       </fix>
+      <update>
+        Update the internal fork of Apache Commons DBCP 2 to dcdbc72
+        (2019-04-24) to pick up some clean-up and enhancements less the JDBC 4.2
+        related changes that require Java 8. (markt)
+      </update>
     </changelog>
   </subsection>
 </section>


---------------------------------------------------------------------
To unsubscribe, e-mail: dev-unsubscribe@tomcat.apache.org
For additional commands, e-mail: dev-help@tomcat.apache.org