You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@commons.apache.org by gg...@apache.org on 2022/07/02 22:47:25 UTC
[commons-dbcp] branch master updated: Replace Exception with SQLException in some method signatures (preserves binary compatibility, not source).
This is an automated email from the ASF dual-hosted git repository.
ggregory pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/commons-dbcp.git
The following commit(s) were added to refs/heads/master by this push:
new 67d2c666 Replace Exception with SQLException in some method signatures (preserves binary compatibility, not source).
67d2c666 is described below
commit 67d2c666d8e9824c25c083f6341ff00d76591ae7
Author: Gary Gregory <ga...@gmail.com>
AuthorDate: Sat Jul 2 18:47:16 2022 -0400
Replace Exception with SQLException in some method signatures (preserves
binary compatibility, not source).
---
src/changes/changes.xml | 3 +
.../commons/dbcp2/PoolableConnectionFactory.java | 6 +-
.../apache/commons/dbcp2/PoolingConnection.java | 8 +-
.../dbcp2/cpdsadapter/DriverAdapterCPDS.java | 1628 ++++++++++----------
.../dbcp2/cpdsadapter/PooledConnectionImpl.java | 1516 +++++++++---------
.../dbcp2/datasources/CPDSConnectionFactory.java | 996 ++++++------
.../datasources/InstanceKeyDataSourceFactory.java | 5 +-
.../datasources/KeyedCPDSConnectionFactory.java | 780 +++++-----
.../dbcp2/datasources/SharedPoolDataSource.java | 460 +++---
.../managed/PoolableManagedConnectionFactory.java | 221 +--
10 files changed, 2813 insertions(+), 2810 deletions(-)
diff --git a/src/changes/changes.xml b/src/changes/changes.xml
index 0233572f..f7c7627f 100644
--- a/src/changes/changes.xml
+++ b/src/changes/changes.xml
@@ -104,6 +104,9 @@ The <action> type attribute can be add,update,fix,remove.
<action dev="ggregory" type="add" due-to="Gary Gregory">
Add Utils.getDisconnectionSqlCodes() and Utils.DISCONNECTION_SQL_CODES.
</action>
+ <action dev="ggregory" type="add" due-to="Gary Gregory">
+ Replace Exception with SQLException in some method signatures (preserves binary compatibility, not source).
+ </action>
<!-- UPDATE -->
<action dev="ggregory" type="update" due-to="Dependabot, Gary Gregory">
Bump actions/cache from 2.1.6 to 3.0.4 #147, #176.
diff --git a/src/main/java/org/apache/commons/dbcp2/PoolableConnectionFactory.java b/src/main/java/org/apache/commons/dbcp2/PoolableConnectionFactory.java
index dd2234ca..80aa601d 100644
--- a/src/main/java/org/apache/commons/dbcp2/PoolableConnectionFactory.java
+++ b/src/main/java/org/apache/commons/dbcp2/PoolableConnectionFactory.java
@@ -137,7 +137,7 @@ public class PoolableConnectionFactory implements PooledObjectFactory<PoolableCo
}
@Override
- public void destroyObject(final PooledObject<PoolableConnection> p) throws Exception {
+ public void destroyObject(final PooledObject<PoolableConnection> p) throws SQLException {
p.getObject().reallyClose();
}
@@ -145,7 +145,7 @@ public class PoolableConnectionFactory implements PooledObjectFactory<PoolableCo
* @since 2.9.0
*/
@Override
- public void destroyObject(final PooledObject<PoolableConnection> p, final DestroyMode mode) throws Exception {
+ public void destroyObject(final PooledObject<PoolableConnection> p, final DestroyMode mode) throws SQLException {
if (mode == DestroyMode.ABANDONED) {
p.getObject().getInnermostDelegate().abort(Runnable::run);
} else {
@@ -744,7 +744,7 @@ public class PoolableConnectionFactory implements PooledObjectFactory<PoolableCo
conn.validate(validationQuery, validationQueryTimeoutDuration);
}
- private void validateLifetime(final PooledObject<PoolableConnection> p) throws Exception {
+ private void validateLifetime(final PooledObject<PoolableConnection> p) throws LifetimeExceededException {
if (maxConnDuration.compareTo(Duration.ZERO) > 0) {
final Duration lifetimeDuration = Duration.between(p.getCreateInstant(), Instant.now());
if (lifetimeDuration.compareTo(maxConnDuration) > 0) {
diff --git a/src/main/java/org/apache/commons/dbcp2/PoolingConnection.java b/src/main/java/org/apache/commons/dbcp2/PoolingConnection.java
index fb1a7915..18f68aa5 100644
--- a/src/main/java/org/apache/commons/dbcp2/PoolingConnection.java
+++ b/src/main/java/org/apache/commons/dbcp2/PoolingConnection.java
@@ -86,7 +86,7 @@ public class PoolingConnection extends DelegatingConnection<Connection>
*/
@Override
public void activateObject(final PStmtKey key, final PooledObject<DelegatingPreparedStatement> pooledObject)
- throws Exception {
+ throws SQLException {
pooledObject.getObject().activate();
}
@@ -292,7 +292,7 @@ public class PoolingConnection extends DelegatingConnection<Connection>
*/
@Override
public void destroyObject(final PStmtKey key, final PooledObject<DelegatingPreparedStatement> pooledObject)
- throws Exception {
+ throws SQLException {
pooledObject.getObject().getInnermostDelegate().close();
}
@@ -337,7 +337,7 @@ public class PoolingConnection extends DelegatingConnection<Connection>
*/
@SuppressWarnings("resource")
@Override
- public PooledObject<DelegatingPreparedStatement> makeObject(final PStmtKey key) throws Exception {
+ public PooledObject<DelegatingPreparedStatement> makeObject(final PStmtKey key) throws SQLException {
if (null == key) {
throw new IllegalArgumentException("Prepared statement key is null or invalid.");
}
@@ -374,7 +374,7 @@ public class PoolingConnection extends DelegatingConnection<Connection>
*/
@Override
public void passivateObject(final PStmtKey key, final PooledObject<DelegatingPreparedStatement> pooledObject)
- throws Exception {
+ throws SQLException {
@SuppressWarnings("resource")
final DelegatingPreparedStatement dps = pooledObject.getObject();
dps.clearParameters();
diff --git a/src/main/java/org/apache/commons/dbcp2/cpdsadapter/DriverAdapterCPDS.java b/src/main/java/org/apache/commons/dbcp2/cpdsadapter/DriverAdapterCPDS.java
index 17df35e5..881181bf 100644
--- a/src/main/java/org/apache/commons/dbcp2/cpdsadapter/DriverAdapterCPDS.java
+++ b/src/main/java/org/apache/commons/dbcp2/cpdsadapter/DriverAdapterCPDS.java
@@ -1,814 +1,814 @@
-/*
- * 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.commons.dbcp2.cpdsadapter;
-
-import java.io.PrintWriter;
-import java.io.Serializable;
-import java.sql.DriverManager;
-import java.sql.SQLException;
-import java.sql.SQLFeatureNotSupportedException;
-import java.time.Duration;
-import java.util.Hashtable;
-import java.util.Properties;
-import java.util.logging.Logger;
-
-import javax.naming.Context;
-import javax.naming.Name;
-import javax.naming.NamingException;
-import javax.naming.RefAddr;
-import javax.naming.Reference;
-import javax.naming.Referenceable;
-import javax.naming.StringRefAddr;
-import javax.naming.spi.ObjectFactory;
-import javax.sql.ConnectionPoolDataSource;
-import javax.sql.PooledConnection;
-
-import org.apache.commons.dbcp2.Constants;
-import org.apache.commons.dbcp2.DelegatingPreparedStatement;
-import org.apache.commons.dbcp2.PStmtKey;
-import org.apache.commons.dbcp2.Utils;
-import org.apache.commons.pool2.KeyedObjectPool;
-import org.apache.commons.pool2.impl.BaseObjectPoolConfig;
-import org.apache.commons.pool2.impl.GenericKeyedObjectPool;
-import org.apache.commons.pool2.impl.GenericKeyedObjectPoolConfig;
-
-/**
- * <p>
- * An adapter for JDBC drivers that do not include an implementation of {@link javax.sql.ConnectionPoolDataSource}, but
- * still include a {@link java.sql.DriverManager} implementation. {@code ConnectionPoolDataSource}s are not used
- * within general applications. They are used by {@code DataSource} implementations that pool
- * {@code Connection}s, such as {@link org.apache.commons.dbcp2.datasources.SharedPoolDataSource}. A J2EE container
- * will normally provide some method of initializing the {@code ConnectionPoolDataSource} whose attributes are
- * presented as bean getters/setters and then deploying it via JNDI. It is then available as a source of physical
- * connections to the database, when the pooling {@code DataSource} needs to create a new physical connection.
- * </p>
- * <p>
- * Although normally used within a JNDI environment, the DriverAdapterCPDS can be instantiated and initialized as any
- * bean and then attached directly to a pooling {@code DataSource}. {@code Jdbc2PoolDataSource} can use the
- * {@code ConnectionPoolDataSource} with or without the use of JNDI.
- * </p>
- * <p>
- * The DriverAdapterCPDS also provides {@code PreparedStatement} pooling which is not generally available in jdbc2
- * {@code ConnectionPoolDataSource} implementation, but is addressed within the jdbc3 specification. The
- * {@code PreparedStatement} pool in DriverAdapterCPDS has been in the dbcp package for some time, but it has not
- * undergone extensive testing in the configuration used here. It should be considered experimental and can be toggled
- * with the poolPreparedStatements attribute.
- * </p>
- * <p>
- * The <a href="package-summary.html">package documentation</a> contains an example using catalina and JNDI. The
- * <a href="../datasources/package-summary.html">datasources package documentation</a> shows how to use
- * {@code DriverAdapterCPDS} as a source for {@code Jdbc2PoolDataSource} without the use of JNDI.
- * </p>
- *
- * @since 2.0
- */
-public class DriverAdapterCPDS implements ConnectionPoolDataSource, Referenceable, Serializable, ObjectFactory {
-
- private static final long serialVersionUID = -4820523787212147844L;
-
- private static final String GET_CONNECTION_CALLED = "A PooledConnection was already requested from this source, "
- + "further initialization is not allowed.";
-
- static {
- // Attempt to prevent deadlocks - see DBCP - 272
- DriverManager.getDrivers();
- }
-
- /** Description */
- private String description;
-
- /** Url name */
- private String url;
-
- /** User name */
- private String userName;
-
- /** User password */
- private char[] userPassword;
-
- /** Driver class name */
- private String driver;
-
- /** Login TimeOut in seconds */
- private int loginTimeout;
-
- /** Log stream. NOT USED */
- private transient PrintWriter logWriter;
-
- // PreparedStatement pool properties
- private boolean poolPreparedStatements;
- private int maxIdle = 10;
- private Duration durationBetweenEvictionRuns = BaseObjectPoolConfig.DEFAULT_TIME_BETWEEN_EVICTION_RUNS;
- private int numTestsPerEvictionRun = -1;
- private Duration minEvictableIdleDuration = BaseObjectPoolConfig.DEFAULT_MIN_EVICTABLE_IDLE_DURATION;
-
- private int maxPreparedStatements = -1;
-
- /** Whether or not getConnection has been called */
- private volatile boolean getConnectionCalled;
-
- /** Connection properties passed to JDBC Driver */
- private Properties connectionProperties;
-
- /**
- * Controls access to the underlying connection
- */
- private boolean accessToUnderlyingConnectionAllowed;
-
- /**
- * Default no-argument constructor for Serialization
- */
- public DriverAdapterCPDS() {
- }
-
- /**
- * Throws an IllegalStateException, if a PooledConnection has already been requested.
- */
- private void assertInitializationAllowed() throws IllegalStateException {
- if (getConnectionCalled) {
- throw new IllegalStateException(GET_CONNECTION_CALLED);
- }
- }
-
- private boolean getBooleanContentString(final RefAddr ra) {
- return Boolean.parseBoolean(getStringContent(ra));
- }
-
- /**
- * Gets the connection properties passed to the JDBC driver.
- *
- * @return the JDBC connection properties used when creating connections.
- */
- public Properties getConnectionProperties() {
- return connectionProperties;
- }
-
- /**
- * Gets the value of description. This property is here for use by the code which will deploy this data source. It
- * is not used internally.
- *
- * @return value of description, may be null.
- * @see #setDescription(String)
- */
- public String getDescription() {
- return description;
- }
-
- /**
- * Gets the driver class name.
- *
- * @return value of driver.
- */
- public String getDriver() {
- return driver;
- }
-
- /**
- * Gets the duration to sleep between runs of the idle object evictor thread. When non-positive, no
- * idle object evictor thread will be run.
- *
- * @return the value of the evictor thread timer
- * @see #setDurationBetweenEvictionRuns(Duration)
- * @since 2.9.0
- */
- public Duration getDurationBetweenEvictionRuns() {
- return durationBetweenEvictionRuns;
- }
-
- private int getIntegerStringContent(final RefAddr ra) {
- return Integer.parseInt(getStringContent(ra));
- }
-
- /**
- * Gets the maximum time in seconds that this data source can wait while attempting to connect to a database. NOT
- * USED.
- */
- @Override
- public int getLoginTimeout() {
- return loginTimeout;
- }
-
- /**
- * Gets the log writer for this data source. NOT USED.
- */
- @Override
- public PrintWriter getLogWriter() {
- return logWriter;
- }
-
- /**
- * Gets the maximum number of statements that can remain idle in the pool, without extra ones being released, or
- * negative for no limit.
- *
- * @return the value of maxIdle
- */
- public int getMaxIdle() {
- return maxIdle;
- }
-
- /**
- * Gets the maximum number of prepared statements.
- *
- * @return maxPrepartedStatements value
- */
- public int getMaxPreparedStatements() {
- return maxPreparedStatements;
- }
-
- /**
- * Gets the minimum amount of time a statement may sit idle in the pool before it is eligible for eviction by the
- * idle object evictor (if any).
- *
- * @see #setMinEvictableIdleDuration
- * @see #setDurationBetweenEvictionRuns
- * @return the minimum amount of time a statement may sit idle in the pool.
- * @since 2.9.0
- */
- public Duration getMinEvictableIdleDuration() {
- return minEvictableIdleDuration;
- }
-
- /**
- * Gets the minimum amount of time a statement may sit idle in the pool before it is eligible for eviction by the
- * idle object evictor (if any).
- *
- * @see #setMinEvictableIdleTimeMillis
- * @see #setTimeBetweenEvictionRunsMillis
- * @return the minimum amount of time a statement may sit idle in the pool.
- * @deprecated USe {@link #getMinEvictableIdleDuration()}.
- */
- @Deprecated
- public int getMinEvictableIdleTimeMillis() {
- return (int) minEvictableIdleDuration.toMillis();
- }
-
- /**
- * Gets the number of statements to examine during each run of the idle object evictor thread (if any.)
- *
- * @see #setNumTestsPerEvictionRun
- * @see #setTimeBetweenEvictionRunsMillis
- * @return the number of statements to examine during each run of the idle object evictor thread (if any.)
- */
- public int getNumTestsPerEvictionRun() {
- return numTestsPerEvictionRun;
- }
-
- /**
- * Implements {@link ObjectFactory} to create an instance of this class
- */
- @Override
- public Object getObjectInstance(final Object refObj, final Name name, final Context context,
- final Hashtable<?, ?> env) throws Exception {
- // The spec says to return null if we can't create an instance
- // of the reference
- DriverAdapterCPDS cpds = null;
- if (refObj instanceof Reference) {
- final Reference ref = (Reference) refObj;
- if (ref.getClassName().equals(getClass().getName())) {
- RefAddr ra = ref.get("description");
- if (isNotEmpty(ra)) {
- setDescription(getStringContent(ra));
- }
-
- ra = ref.get("driver");
- if (isNotEmpty(ra)) {
- setDriver(getStringContent(ra));
- }
- ra = ref.get("url");
- if (isNotEmpty(ra)) {
- setUrl(getStringContent(ra));
- }
- ra = ref.get(Constants.KEY_USER);
- if (isNotEmpty(ra)) {
- setUser(getStringContent(ra));
- }
- ra = ref.get(Constants.KEY_PASSWORD);
- if (isNotEmpty(ra)) {
- setPassword(getStringContent(ra));
- }
-
- ra = ref.get("poolPreparedStatements");
- if (isNotEmpty(ra)) {
- setPoolPreparedStatements(getBooleanContentString(ra));
- }
- ra = ref.get("maxIdle");
- if (isNotEmpty(ra)) {
- setMaxIdle(getIntegerStringContent(ra));
- }
-
- ra = ref.get("timeBetweenEvictionRunsMillis");
- if (isNotEmpty(ra)) {
- setTimeBetweenEvictionRunsMillis(getIntegerStringContent(ra));
- }
-
- ra = ref.get("numTestsPerEvictionRun");
- if (isNotEmpty(ra)) {
- setNumTestsPerEvictionRun(getIntegerStringContent(ra));
- }
-
- ra = ref.get("minEvictableIdleTimeMillis");
- if (isNotEmpty(ra)) {
- setMinEvictableIdleTimeMillis(getIntegerStringContent(ra));
- }
-
- ra = ref.get("maxPreparedStatements");
- if (isNotEmpty(ra)) {
- setMaxPreparedStatements(getIntegerStringContent(ra));
- }
-
- ra = ref.get("accessToUnderlyingConnectionAllowed");
- if (isNotEmpty(ra)) {
- setAccessToUnderlyingConnectionAllowed(getBooleanContentString(ra));
- }
-
- cpds = this;
- }
- }
- return cpds;
- }
-
- @Override
- public Logger getParentLogger() throws SQLFeatureNotSupportedException {
- throw new SQLFeatureNotSupportedException();
- }
-
- /**
- * Gets the value of password for the default user.
- *
- * @return value of password.
- */
- public String getPassword() {
- return Utils.toString(userPassword);
- }
-
- /**
- * Gets the value of password for the default user.
- *
- * @return value of password.
- * @since 2.4.0
- */
- public char[] getPasswordCharArray() {
- return userPassword == null ? null : userPassword.clone();
- }
-
- /**
- * Attempts to establish a database connection using the default user and password.
- */
- @Override
- public PooledConnection getPooledConnection() throws SQLException {
- return getPooledConnection(getUser(), getPassword());
- }
-
- /**
- * Attempts to establish a database connection.
- *
- * @param pooledUserName name to be used for the connection
- * @param pooledUserPassword password to be used fur the connection
- */
- @Override
- public PooledConnection getPooledConnection(final String pooledUserName, final String pooledUserPassword)
- throws SQLException {
- getConnectionCalled = true;
- PooledConnectionImpl pooledConnection = null;
- // Workaround for buggy WebLogic 5.1 class loader - ignore the exception upon first invocation.
- try {
- if (connectionProperties != null) {
- update(connectionProperties, Constants.KEY_USER, pooledUserName);
- update(connectionProperties, Constants.KEY_PASSWORD, pooledUserPassword);
- pooledConnection = new PooledConnectionImpl(
- DriverManager.getConnection(getUrl(), connectionProperties));
- } else {
- pooledConnection = new PooledConnectionImpl(
- DriverManager.getConnection(getUrl(), pooledUserName, pooledUserPassword));
- }
- pooledConnection.setAccessToUnderlyingConnectionAllowed(isAccessToUnderlyingConnectionAllowed());
- } catch (final ClassCircularityError e) {
- if (connectionProperties != null) {
- pooledConnection = new PooledConnectionImpl(
- DriverManager.getConnection(getUrl(), connectionProperties));
- } else {
- pooledConnection = new PooledConnectionImpl(
- DriverManager.getConnection(getUrl(), pooledUserName, pooledUserPassword));
- }
- pooledConnection.setAccessToUnderlyingConnectionAllowed(isAccessToUnderlyingConnectionAllowed());
- }
- KeyedObjectPool<PStmtKey, DelegatingPreparedStatement> stmtPool = null;
- if (isPoolPreparedStatements()) {
- final GenericKeyedObjectPoolConfig<DelegatingPreparedStatement> config = new GenericKeyedObjectPoolConfig<>();
- config.setMaxTotalPerKey(Integer.MAX_VALUE);
- config.setBlockWhenExhausted(false);
- config.setMaxWait(Duration.ZERO);
- config.setMaxIdlePerKey(getMaxIdle());
- if (getMaxPreparedStatements() <= 0) {
- // Since there is no limit, create a prepared statement pool with an eviction thread;
- // evictor settings are the same as the connection pool settings.
- config.setTimeBetweenEvictionRuns(getDurationBetweenEvictionRuns());
- config.setNumTestsPerEvictionRun(getNumTestsPerEvictionRun());
- config.setMinEvictableIdleTime(getMinEvictableIdleDuration());
- } else {
- // Since there is a limit, create a prepared statement pool without an eviction thread;
- // pool has LRU functionality so when the limit is reached, 15% of the pool is cleared.
- // see org.apache.commons.pool2.impl.GenericKeyedObjectPool.clearOldest method
- config.setMaxTotal(getMaxPreparedStatements());
- config.setTimeBetweenEvictionRuns(Duration.ofMillis(-1));
- config.setNumTestsPerEvictionRun(0);
- config.setMinEvictableIdleTime(Duration.ZERO);
- }
- stmtPool = new GenericKeyedObjectPool<>(pooledConnection, config);
- pooledConnection.setStatementPool(stmtPool);
- }
- return pooledConnection;
- }
-
- /**
- * Implements {@link Referenceable}.
- */
- @Override
- public Reference getReference() throws NamingException {
- // this class implements its own factory
- final String factory = getClass().getName();
-
- final Reference ref = new Reference(getClass().getName(), factory, null);
-
- ref.add(new StringRefAddr("description", getDescription()));
- ref.add(new StringRefAddr("driver", getDriver()));
- ref.add(new StringRefAddr("loginTimeout", String.valueOf(getLoginTimeout())));
- ref.add(new StringRefAddr(Constants.KEY_PASSWORD, getPassword()));
- ref.add(new StringRefAddr(Constants.KEY_USER, getUser()));
- ref.add(new StringRefAddr("url", getUrl()));
-
- ref.add(new StringRefAddr("poolPreparedStatements", String.valueOf(isPoolPreparedStatements())));
- ref.add(new StringRefAddr("maxIdle", String.valueOf(getMaxIdle())));
- ref.add(new StringRefAddr("numTestsPerEvictionRun", String.valueOf(getNumTestsPerEvictionRun())));
- ref.add(new StringRefAddr("maxPreparedStatements", String.valueOf(getMaxPreparedStatements())));
- //
- // Pair of current and deprecated.
- ref.add(new StringRefAddr("durationBetweenEvictionRuns", String.valueOf(getDurationBetweenEvictionRuns())));
- ref.add(new StringRefAddr("timeBetweenEvictionRunsMillis", String.valueOf(getTimeBetweenEvictionRunsMillis())));
- //
- // Pair of current and deprecated.
- ref.add(new StringRefAddr("minEvictableIdleDuration", String.valueOf(getMinEvictableIdleDuration())));
- ref.add(new StringRefAddr("minEvictableIdleTimeMillis", String.valueOf(getMinEvictableIdleTimeMillis())));
-
- return ref;
- }
-
- private String getStringContent(final RefAddr ra) {
- return ra.getContent().toString();
- }
-
- /**
- * Gets 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.
- *
- * @return the value of the evictor thread timer
- * @see #setDurationBetweenEvictionRuns(Duration)
- * @deprecated Use {@link #getDurationBetweenEvictionRuns()}.
- */
- @Deprecated
- public long getTimeBetweenEvictionRunsMillis() {
- return durationBetweenEvictionRuns.toMillis();
- }
-
- /**
- * Gets the value of url used to locate the database for this datasource.
- *
- * @return value of url.
- */
- public String getUrl() {
- return url;
- }
-
- /**
- * Gets the value of default user (login or user name).
- *
- * @return value of user.
- */
- public String getUser() {
- return userName;
- }
-
- /**
- * Returns the value of the accessToUnderlyingConnectionAllowed property.
- *
- * @return true if access to the underlying is allowed, false otherwise.
- */
- public synchronized boolean isAccessToUnderlyingConnectionAllowed() {
- return this.accessToUnderlyingConnectionAllowed;
- }
-
- private boolean isNotEmpty(final RefAddr ra) {
- return ra != null && ra.getContent() != null;
- }
-
- /**
- * Whether to toggle the pooling of {@code PreparedStatement}s
- *
- * @return value of poolPreparedStatements.
- */
- public boolean isPoolPreparedStatements() {
- return poolPreparedStatements;
- }
-
- /**
- * Sets the value of the accessToUnderlyingConnectionAllowed property. It controls if the PoolGuard allows access to
- * the underlying connection. (Default: false)
- *
- * @param allow Access to the underlying connection is granted when true.
- */
- public synchronized void setAccessToUnderlyingConnectionAllowed(final boolean allow) {
- this.accessToUnderlyingConnectionAllowed = allow;
- }
-
- /**
- * Sets the connection properties passed to the JDBC driver.
- * <p>
- * If {@code props} contains "user" and/or "password" properties, the corresponding instance properties are
- * set. If these properties are not present, they are filled in using {@link #getUser()}, {@link #getPassword()}
- * when {@link #getPooledConnection()} is called, or using the actual parameters to the method call when
- * {@link #getPooledConnection(String, String)} is called. Calls to {@link #setUser(String)} or
- * {@link #setPassword(String)} overwrite the values of these properties if {@code connectionProperties} is not
- * null.
- * </p>
- *
- * @param props Connection properties to use when creating new connections.
- * @throws IllegalStateException if {@link #getPooledConnection()} has been called
- */
- public void setConnectionProperties(final Properties props) {
- assertInitializationAllowed();
- connectionProperties = props;
- if (connectionProperties != null) {
- if (connectionProperties.containsKey(Constants.KEY_USER)) {
- setUser(connectionProperties.getProperty(Constants.KEY_USER));
- }
- if (connectionProperties.containsKey(Constants.KEY_PASSWORD)) {
- setPassword(connectionProperties.getProperty(Constants.KEY_PASSWORD));
- }
- }
- }
-
- /**
- * Sets the value of description. This property is here for use by the code which will deploy this datasource. It is
- * not used internally.
- *
- * @param description Value to assign to description.
- */
- public void setDescription(final String description) {
- this.description = description;
- }
-
- /**
- * Sets the driver class name. Setting the driver class name cause the driver to be registered with the
- * DriverManager.
- *
- * @param driver Value to assign to driver.
- * @throws IllegalStateException if {@link #getPooledConnection()} has been called
- * @throws ClassNotFoundException if the class cannot be located
- */
- public void setDriver(final String driver) throws ClassNotFoundException {
- assertInitializationAllowed();
- this.driver = driver;
- // make sure driver is registered
- Class.forName(driver);
- }
-
- /**
- * Sets the duration to sleep between runs of the idle object evictor thread. When non-positive, no
- * idle object evictor thread will be run.
- *
- * @param durationBetweenEvictionRuns The duration to sleep between runs of the idle object evictor
- * thread. When non-positive, no idle object evictor thread will be run.
- * @see #getDurationBetweenEvictionRuns()
- * @throws IllegalStateException if {@link #getPooledConnection()} has been called
- * @since 2.9.0
- */
- public void setDurationBetweenEvictionRuns(final Duration durationBetweenEvictionRuns) {
- assertInitializationAllowed();
- this.durationBetweenEvictionRuns = durationBetweenEvictionRuns;
- }
-
- /**
- * Sets the maximum time in seconds that this data source will wait while attempting to connect to a database. NOT
- * USED.
- */
- @Override
- public void setLoginTimeout(final int seconds) {
- this.loginTimeout = seconds;
- }
-
- /**
- * Sets the log writer for this data source. NOT USED.
- */
- @Override
- public void setLogWriter(final PrintWriter logWriter) {
- this.logWriter = logWriter;
- }
-
- /**
- * Gets the maximum number of statements that can remain idle in the pool, without extra ones being released, or
- * negative for no limit.
- *
- * @param maxIdle The maximum number of statements that can remain idle
- * @throws IllegalStateException if {@link #getPooledConnection()} has been called
- */
- public void setMaxIdle(final int maxIdle) {
- assertInitializationAllowed();
- this.maxIdle = maxIdle;
- }
-
- /**
- * Sets the maximum number of prepared statements.
- *
- * @param maxPreparedStatements the new maximum number of prepared statements
- */
- public void setMaxPreparedStatements(final int maxPreparedStatements) {
- this.maxPreparedStatements = maxPreparedStatements;
- }
-
- /**
- * Sets the minimum amount of time a statement may sit idle in the pool before it is eligible for eviction by the
- * idle object evictor (if any). When non-positive, no objects will be evicted from the pool due to idle time alone.
- *
- * @param minEvictableIdleDuration minimum time to set in milliseconds.
- * @see #getMinEvictableIdleDuration()
- * @see #setDurationBetweenEvictionRuns(Duration)
- * @throws IllegalStateException if {@link #getPooledConnection()} has been called.
- * @since 2.9.0
- */
- public void setMinEvictableIdleDuration(final Duration minEvictableIdleDuration) {
- assertInitializationAllowed();
- this.minEvictableIdleDuration = minEvictableIdleDuration;
- }
-
- /**
- * Sets the minimum amount of time a statement may sit idle in the pool before it is eligible for eviction by the
- * idle object evictor (if any). When non-positive, no objects will be evicted from the pool due to idle time alone.
- *
- * @param minEvictableIdleTimeMillis minimum time to set in milliseconds.
- * @see #getMinEvictableIdleDuration()
- * @see #setDurationBetweenEvictionRuns(Duration)
- * @throws IllegalStateException if {@link #getPooledConnection()} has been called
- * @deprecated Use {@link #setMinEvictableIdleDuration(Duration)}.
- */
- @Deprecated
- public void setMinEvictableIdleTimeMillis(final int minEvictableIdleTimeMillis) {
- assertInitializationAllowed();
- this.minEvictableIdleDuration = Duration.ofMillis(minEvictableIdleTimeMillis);
- }
-
- /**
- * Sets the number of statements to examine during each run of the idle object evictor thread (if any).
- * <p>
- * When a negative value is supplied,
- * {@code ceil({@link BasicDataSource#getNumIdle})/abs({@link #getNumTestsPerEvictionRun})} tests will be run.
- * I.e., when the value is <i>-n</i>, roughly one <i>n</i>th of the idle objects will be tested per run.
- * </p>
- *
- * @param numTestsPerEvictionRun number of statements to examine per run
- * @see #getNumTestsPerEvictionRun()
- * @see #setDurationBetweenEvictionRuns(Duration)
- * @throws IllegalStateException if {@link #getPooledConnection()} has been called
- */
- public void setNumTestsPerEvictionRun(final int numTestsPerEvictionRun) {
- assertInitializationAllowed();
- this.numTestsPerEvictionRun = numTestsPerEvictionRun;
- }
-
- /**
- * Sets the value of password for the default user.
- *
- * @param userPassword Value to assign to password.
- * @throws IllegalStateException if {@link #getPooledConnection()} has been called
- */
- public void setPassword(final char[] userPassword) {
- assertInitializationAllowed();
- this.userPassword = Utils.clone(userPassword);
- update(connectionProperties, Constants.KEY_PASSWORD, Utils.toString(this.userPassword));
- }
-
- /**
- * Sets the value of password for the default user.
- *
- * @param userPassword Value to assign to password.
- * @throws IllegalStateException if {@link #getPooledConnection()} has been called
- */
- public void setPassword(final String userPassword) {
- assertInitializationAllowed();
- this.userPassword = Utils.toCharArray(userPassword);
- update(connectionProperties, Constants.KEY_PASSWORD, userPassword);
- }
-
- /**
- * Whether to toggle the pooling of {@code PreparedStatement}s
- *
- * @param poolPreparedStatements true to pool statements.
- * @throws IllegalStateException if {@link #getPooledConnection()} has been called
- */
- public void setPoolPreparedStatements(final boolean poolPreparedStatements) {
- assertInitializationAllowed();
- this.poolPreparedStatements = poolPreparedStatements;
- }
-
- /**
- * Sets 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.
- *
- * @param timeBetweenEvictionRunsMillis 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.
- * @see #getDurationBetweenEvictionRuns()
- * @throws IllegalStateException if {@link #getPooledConnection()} has been called
- * @deprecated Use {@link #setDurationBetweenEvictionRuns(Duration)}.
- */
- @Deprecated
- public void setTimeBetweenEvictionRunsMillis(final long timeBetweenEvictionRunsMillis) {
- assertInitializationAllowed();
- this.durationBetweenEvictionRuns = Duration.ofMillis(timeBetweenEvictionRunsMillis);
- }
-
- /**
- * Sets the value of URL string used to locate the database for this datasource.
- *
- * @param url Value to assign to url.
- * @throws IllegalStateException if {@link #getPooledConnection()} has been called
- */
- public void setUrl(final String url) {
- assertInitializationAllowed();
- this.url = url;
- }
-
- /**
- * Sets the value of default user (login or user name).
- *
- * @param userName Value to assign to user.
- * @throws IllegalStateException if {@link #getPooledConnection()} has been called
- */
- public void setUser(final String userName) {
- assertInitializationAllowed();
- this.userName = userName;
- update(connectionProperties, Constants.KEY_USER, userName);
- }
-
- /**
- * 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(durationBetweenEvictionRuns);
- builder.append(", numTestsPerEvictionRun=");
- builder.append(numTestsPerEvictionRun);
- builder.append(", minEvictableIdleTimeMillis=");
- builder.append(minEvictableIdleDuration);
- builder.append(", maxPreparedStatements=");
- builder.append(maxPreparedStatements);
- builder.append(", getConnectionCalled=");
- builder.append(getConnectionCalled);
- builder.append(", connectionProperties=");
- builder.append(Utils.cloneWithoutCredentials(connectionProperties));
- builder.append(", accessToUnderlyingConnectionAllowed=");
- builder.append(accessToUnderlyingConnectionAllowed);
- builder.append("]");
- return builder.toString();
- }
-
- private void update(final Properties properties, final String key, final String value) {
- if (properties != null && key != null) {
- if (value == null) {
- properties.remove(key);
- } else {
- properties.setProperty(key, value);
- }
- }
- }
-}
+/*
+ * 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.commons.dbcp2.cpdsadapter;
+
+import java.io.PrintWriter;
+import java.io.Serializable;
+import java.sql.DriverManager;
+import java.sql.SQLException;
+import java.sql.SQLFeatureNotSupportedException;
+import java.time.Duration;
+import java.util.Hashtable;
+import java.util.Properties;
+import java.util.logging.Logger;
+
+import javax.naming.Context;
+import javax.naming.Name;
+import javax.naming.NamingException;
+import javax.naming.RefAddr;
+import javax.naming.Reference;
+import javax.naming.Referenceable;
+import javax.naming.StringRefAddr;
+import javax.naming.spi.ObjectFactory;
+import javax.sql.ConnectionPoolDataSource;
+import javax.sql.PooledConnection;
+
+import org.apache.commons.dbcp2.Constants;
+import org.apache.commons.dbcp2.DelegatingPreparedStatement;
+import org.apache.commons.dbcp2.PStmtKey;
+import org.apache.commons.dbcp2.Utils;
+import org.apache.commons.pool2.KeyedObjectPool;
+import org.apache.commons.pool2.impl.BaseObjectPoolConfig;
+import org.apache.commons.pool2.impl.GenericKeyedObjectPool;
+import org.apache.commons.pool2.impl.GenericKeyedObjectPoolConfig;
+
+/**
+ * <p>
+ * An adapter for JDBC drivers that do not include an implementation of {@link javax.sql.ConnectionPoolDataSource}, but
+ * still include a {@link java.sql.DriverManager} implementation. {@code ConnectionPoolDataSource}s are not used
+ * within general applications. They are used by {@code DataSource} implementations that pool
+ * {@code Connection}s, such as {@link org.apache.commons.dbcp2.datasources.SharedPoolDataSource}. A J2EE container
+ * will normally provide some method of initializing the {@code ConnectionPoolDataSource} whose attributes are
+ * presented as bean getters/setters and then deploying it via JNDI. It is then available as a source of physical
+ * connections to the database, when the pooling {@code DataSource} needs to create a new physical connection.
+ * </p>
+ * <p>
+ * Although normally used within a JNDI environment, the DriverAdapterCPDS can be instantiated and initialized as any
+ * bean and then attached directly to a pooling {@code DataSource}. {@code Jdbc2PoolDataSource} can use the
+ * {@code ConnectionPoolDataSource} with or without the use of JNDI.
+ * </p>
+ * <p>
+ * The DriverAdapterCPDS also provides {@code PreparedStatement} pooling which is not generally available in jdbc2
+ * {@code ConnectionPoolDataSource} implementation, but is addressed within the jdbc3 specification. The
+ * {@code PreparedStatement} pool in DriverAdapterCPDS has been in the dbcp package for some time, but it has not
+ * undergone extensive testing in the configuration used here. It should be considered experimental and can be toggled
+ * with the poolPreparedStatements attribute.
+ * </p>
+ * <p>
+ * The <a href="package-summary.html">package documentation</a> contains an example using catalina and JNDI. The
+ * <a href="../datasources/package-summary.html">datasources package documentation</a> shows how to use
+ * {@code DriverAdapterCPDS} as a source for {@code Jdbc2PoolDataSource} without the use of JNDI.
+ * </p>
+ *
+ * @since 2.0
+ */
+public class DriverAdapterCPDS implements ConnectionPoolDataSource, Referenceable, Serializable, ObjectFactory {
+
+ private static final long serialVersionUID = -4820523787212147844L;
+
+ private static final String GET_CONNECTION_CALLED = "A PooledConnection was already requested from this source, "
+ + "further initialization is not allowed.";
+
+ static {
+ // Attempt to prevent deadlocks - see DBCP - 272
+ DriverManager.getDrivers();
+ }
+
+ /** Description */
+ private String description;
+
+ /** Url name */
+ private String url;
+
+ /** User name */
+ private String userName;
+
+ /** User password */
+ private char[] userPassword;
+
+ /** Driver class name */
+ private String driver;
+
+ /** Login TimeOut in seconds */
+ private int loginTimeout;
+
+ /** Log stream. NOT USED */
+ private transient PrintWriter logWriter;
+
+ // PreparedStatement pool properties
+ private boolean poolPreparedStatements;
+ private int maxIdle = 10;
+ private Duration durationBetweenEvictionRuns = BaseObjectPoolConfig.DEFAULT_TIME_BETWEEN_EVICTION_RUNS;
+ private int numTestsPerEvictionRun = -1;
+ private Duration minEvictableIdleDuration = BaseObjectPoolConfig.DEFAULT_MIN_EVICTABLE_IDLE_DURATION;
+
+ private int maxPreparedStatements = -1;
+
+ /** Whether or not getConnection has been called */
+ private volatile boolean getConnectionCalled;
+
+ /** Connection properties passed to JDBC Driver */
+ private Properties connectionProperties;
+
+ /**
+ * Controls access to the underlying connection
+ */
+ private boolean accessToUnderlyingConnectionAllowed;
+
+ /**
+ * Default no-argument constructor for Serialization
+ */
+ public DriverAdapterCPDS() {
+ }
+
+ /**
+ * Throws an IllegalStateException, if a PooledConnection has already been requested.
+ */
+ private void assertInitializationAllowed() throws IllegalStateException {
+ if (getConnectionCalled) {
+ throw new IllegalStateException(GET_CONNECTION_CALLED);
+ }
+ }
+
+ private boolean getBooleanContentString(final RefAddr ra) {
+ return Boolean.parseBoolean(getStringContent(ra));
+ }
+
+ /**
+ * Gets the connection properties passed to the JDBC driver.
+ *
+ * @return the JDBC connection properties used when creating connections.
+ */
+ public Properties getConnectionProperties() {
+ return connectionProperties;
+ }
+
+ /**
+ * Gets the value of description. This property is here for use by the code which will deploy this data source. It
+ * is not used internally.
+ *
+ * @return value of description, may be null.
+ * @see #setDescription(String)
+ */
+ public String getDescription() {
+ return description;
+ }
+
+ /**
+ * Gets the driver class name.
+ *
+ * @return value of driver.
+ */
+ public String getDriver() {
+ return driver;
+ }
+
+ /**
+ * Gets the duration to sleep between runs of the idle object evictor thread. When non-positive, no
+ * idle object evictor thread will be run.
+ *
+ * @return the value of the evictor thread timer
+ * @see #setDurationBetweenEvictionRuns(Duration)
+ * @since 2.9.0
+ */
+ public Duration getDurationBetweenEvictionRuns() {
+ return durationBetweenEvictionRuns;
+ }
+
+ private int getIntegerStringContent(final RefAddr ra) {
+ return Integer.parseInt(getStringContent(ra));
+ }
+
+ /**
+ * Gets the maximum time in seconds that this data source can wait while attempting to connect to a database. NOT
+ * USED.
+ */
+ @Override
+ public int getLoginTimeout() {
+ return loginTimeout;
+ }
+
+ /**
+ * Gets the log writer for this data source. NOT USED.
+ */
+ @Override
+ public PrintWriter getLogWriter() {
+ return logWriter;
+ }
+
+ /**
+ * Gets the maximum number of statements that can remain idle in the pool, without extra ones being released, or
+ * negative for no limit.
+ *
+ * @return the value of maxIdle
+ */
+ public int getMaxIdle() {
+ return maxIdle;
+ }
+
+ /**
+ * Gets the maximum number of prepared statements.
+ *
+ * @return maxPrepartedStatements value
+ */
+ public int getMaxPreparedStatements() {
+ return maxPreparedStatements;
+ }
+
+ /**
+ * Gets the minimum amount of time a statement may sit idle in the pool before it is eligible for eviction by the
+ * idle object evictor (if any).
+ *
+ * @see #setMinEvictableIdleDuration
+ * @see #setDurationBetweenEvictionRuns
+ * @return the minimum amount of time a statement may sit idle in the pool.
+ * @since 2.9.0
+ */
+ public Duration getMinEvictableIdleDuration() {
+ return minEvictableIdleDuration;
+ }
+
+ /**
+ * Gets the minimum amount of time a statement may sit idle in the pool before it is eligible for eviction by the
+ * idle object evictor (if any).
+ *
+ * @see #setMinEvictableIdleTimeMillis
+ * @see #setTimeBetweenEvictionRunsMillis
+ * @return the minimum amount of time a statement may sit idle in the pool.
+ * @deprecated USe {@link #getMinEvictableIdleDuration()}.
+ */
+ @Deprecated
+ public int getMinEvictableIdleTimeMillis() {
+ return (int) minEvictableIdleDuration.toMillis();
+ }
+
+ /**
+ * Gets the number of statements to examine during each run of the idle object evictor thread (if any.)
+ *
+ * @see #setNumTestsPerEvictionRun
+ * @see #setTimeBetweenEvictionRunsMillis
+ * @return the number of statements to examine during each run of the idle object evictor thread (if any.)
+ */
+ public int getNumTestsPerEvictionRun() {
+ return numTestsPerEvictionRun;
+ }
+
+ /**
+ * Implements {@link ObjectFactory} to create an instance of this class
+ */
+ @Override
+ public Object getObjectInstance(final Object refObj, final Name name, final Context context,
+ final Hashtable<?, ?> env) throws ClassNotFoundException {
+ // The spec says to return null if we can't create an instance
+ // of the reference
+ DriverAdapterCPDS cpds = null;
+ if (refObj instanceof Reference) {
+ final Reference ref = (Reference) refObj;
+ if (ref.getClassName().equals(getClass().getName())) {
+ RefAddr ra = ref.get("description");
+ if (isNotEmpty(ra)) {
+ setDescription(getStringContent(ra));
+ }
+
+ ra = ref.get("driver");
+ if (isNotEmpty(ra)) {
+ setDriver(getStringContent(ra));
+ }
+ ra = ref.get("url");
+ if (isNotEmpty(ra)) {
+ setUrl(getStringContent(ra));
+ }
+ ra = ref.get(Constants.KEY_USER);
+ if (isNotEmpty(ra)) {
+ setUser(getStringContent(ra));
+ }
+ ra = ref.get(Constants.KEY_PASSWORD);
+ if (isNotEmpty(ra)) {
+ setPassword(getStringContent(ra));
+ }
+
+ ra = ref.get("poolPreparedStatements");
+ if (isNotEmpty(ra)) {
+ setPoolPreparedStatements(getBooleanContentString(ra));
+ }
+ ra = ref.get("maxIdle");
+ if (isNotEmpty(ra)) {
+ setMaxIdle(getIntegerStringContent(ra));
+ }
+
+ ra = ref.get("timeBetweenEvictionRunsMillis");
+ if (isNotEmpty(ra)) {
+ setTimeBetweenEvictionRunsMillis(getIntegerStringContent(ra));
+ }
+
+ ra = ref.get("numTestsPerEvictionRun");
+ if (isNotEmpty(ra)) {
+ setNumTestsPerEvictionRun(getIntegerStringContent(ra));
+ }
+
+ ra = ref.get("minEvictableIdleTimeMillis");
+ if (isNotEmpty(ra)) {
+ setMinEvictableIdleTimeMillis(getIntegerStringContent(ra));
+ }
+
+ ra = ref.get("maxPreparedStatements");
+ if (isNotEmpty(ra)) {
+ setMaxPreparedStatements(getIntegerStringContent(ra));
+ }
+
+ ra = ref.get("accessToUnderlyingConnectionAllowed");
+ if (isNotEmpty(ra)) {
+ setAccessToUnderlyingConnectionAllowed(getBooleanContentString(ra));
+ }
+
+ cpds = this;
+ }
+ }
+ return cpds;
+ }
+
+ @Override
+ public Logger getParentLogger() throws SQLFeatureNotSupportedException {
+ throw new SQLFeatureNotSupportedException();
+ }
+
+ /**
+ * Gets the value of password for the default user.
+ *
+ * @return value of password.
+ */
+ public String getPassword() {
+ return Utils.toString(userPassword);
+ }
+
+ /**
+ * Gets the value of password for the default user.
+ *
+ * @return value of password.
+ * @since 2.4.0
+ */
+ public char[] getPasswordCharArray() {
+ return userPassword == null ? null : userPassword.clone();
+ }
+
+ /**
+ * Attempts to establish a database connection using the default user and password.
+ */
+ @Override
+ public PooledConnection getPooledConnection() throws SQLException {
+ return getPooledConnection(getUser(), getPassword());
+ }
+
+ /**
+ * Attempts to establish a database connection.
+ *
+ * @param pooledUserName name to be used for the connection
+ * @param pooledUserPassword password to be used fur the connection
+ */
+ @Override
+ public PooledConnection getPooledConnection(final String pooledUserName, final String pooledUserPassword)
+ throws SQLException {
+ getConnectionCalled = true;
+ PooledConnectionImpl pooledConnection = null;
+ // Workaround for buggy WebLogic 5.1 class loader - ignore the exception upon first invocation.
+ try {
+ if (connectionProperties != null) {
+ update(connectionProperties, Constants.KEY_USER, pooledUserName);
+ update(connectionProperties, Constants.KEY_PASSWORD, pooledUserPassword);
+ pooledConnection = new PooledConnectionImpl(
+ DriverManager.getConnection(getUrl(), connectionProperties));
+ } else {
+ pooledConnection = new PooledConnectionImpl(
+ DriverManager.getConnection(getUrl(), pooledUserName, pooledUserPassword));
+ }
+ pooledConnection.setAccessToUnderlyingConnectionAllowed(isAccessToUnderlyingConnectionAllowed());
+ } catch (final ClassCircularityError e) {
+ if (connectionProperties != null) {
+ pooledConnection = new PooledConnectionImpl(
+ DriverManager.getConnection(getUrl(), connectionProperties));
+ } else {
+ pooledConnection = new PooledConnectionImpl(
+ DriverManager.getConnection(getUrl(), pooledUserName, pooledUserPassword));
+ }
+ pooledConnection.setAccessToUnderlyingConnectionAllowed(isAccessToUnderlyingConnectionAllowed());
+ }
+ KeyedObjectPool<PStmtKey, DelegatingPreparedStatement> stmtPool = null;
+ if (isPoolPreparedStatements()) {
+ final GenericKeyedObjectPoolConfig<DelegatingPreparedStatement> config = new GenericKeyedObjectPoolConfig<>();
+ config.setMaxTotalPerKey(Integer.MAX_VALUE);
+ config.setBlockWhenExhausted(false);
+ config.setMaxWait(Duration.ZERO);
+ config.setMaxIdlePerKey(getMaxIdle());
+ if (getMaxPreparedStatements() <= 0) {
+ // Since there is no limit, create a prepared statement pool with an eviction thread;
+ // evictor settings are the same as the connection pool settings.
+ config.setTimeBetweenEvictionRuns(getDurationBetweenEvictionRuns());
+ config.setNumTestsPerEvictionRun(getNumTestsPerEvictionRun());
+ config.setMinEvictableIdleTime(getMinEvictableIdleDuration());
+ } else {
+ // Since there is a limit, create a prepared statement pool without an eviction thread;
+ // pool has LRU functionality so when the limit is reached, 15% of the pool is cleared.
+ // see org.apache.commons.pool2.impl.GenericKeyedObjectPool.clearOldest method
+ config.setMaxTotal(getMaxPreparedStatements());
+ config.setTimeBetweenEvictionRuns(Duration.ofMillis(-1));
+ config.setNumTestsPerEvictionRun(0);
+ config.setMinEvictableIdleTime(Duration.ZERO);
+ }
+ stmtPool = new GenericKeyedObjectPool<>(pooledConnection, config);
+ pooledConnection.setStatementPool(stmtPool);
+ }
+ return pooledConnection;
+ }
+
+ /**
+ * Implements {@link Referenceable}.
+ */
+ @Override
+ public Reference getReference() throws NamingException {
+ // this class implements its own factory
+ final String factory = getClass().getName();
+
+ final Reference ref = new Reference(getClass().getName(), factory, null);
+
+ ref.add(new StringRefAddr("description", getDescription()));
+ ref.add(new StringRefAddr("driver", getDriver()));
+ ref.add(new StringRefAddr("loginTimeout", String.valueOf(getLoginTimeout())));
+ ref.add(new StringRefAddr(Constants.KEY_PASSWORD, getPassword()));
+ ref.add(new StringRefAddr(Constants.KEY_USER, getUser()));
+ ref.add(new StringRefAddr("url", getUrl()));
+
+ ref.add(new StringRefAddr("poolPreparedStatements", String.valueOf(isPoolPreparedStatements())));
+ ref.add(new StringRefAddr("maxIdle", String.valueOf(getMaxIdle())));
+ ref.add(new StringRefAddr("numTestsPerEvictionRun", String.valueOf(getNumTestsPerEvictionRun())));
+ ref.add(new StringRefAddr("maxPreparedStatements", String.valueOf(getMaxPreparedStatements())));
+ //
+ // Pair of current and deprecated.
+ ref.add(new StringRefAddr("durationBetweenEvictionRuns", String.valueOf(getDurationBetweenEvictionRuns())));
+ ref.add(new StringRefAddr("timeBetweenEvictionRunsMillis", String.valueOf(getTimeBetweenEvictionRunsMillis())));
+ //
+ // Pair of current and deprecated.
+ ref.add(new StringRefAddr("minEvictableIdleDuration", String.valueOf(getMinEvictableIdleDuration())));
+ ref.add(new StringRefAddr("minEvictableIdleTimeMillis", String.valueOf(getMinEvictableIdleTimeMillis())));
+
+ return ref;
+ }
+
+ private String getStringContent(final RefAddr ra) {
+ return ra.getContent().toString();
+ }
+
+ /**
+ * Gets 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.
+ *
+ * @return the value of the evictor thread timer
+ * @see #setDurationBetweenEvictionRuns(Duration)
+ * @deprecated Use {@link #getDurationBetweenEvictionRuns()}.
+ */
+ @Deprecated
+ public long getTimeBetweenEvictionRunsMillis() {
+ return durationBetweenEvictionRuns.toMillis();
+ }
+
+ /**
+ * Gets the value of url used to locate the database for this datasource.
+ *
+ * @return value of url.
+ */
+ public String getUrl() {
+ return url;
+ }
+
+ /**
+ * Gets the value of default user (login or user name).
+ *
+ * @return value of user.
+ */
+ public String getUser() {
+ return userName;
+ }
+
+ /**
+ * Returns the value of the accessToUnderlyingConnectionAllowed property.
+ *
+ * @return true if access to the underlying is allowed, false otherwise.
+ */
+ public synchronized boolean isAccessToUnderlyingConnectionAllowed() {
+ return this.accessToUnderlyingConnectionAllowed;
+ }
+
+ private boolean isNotEmpty(final RefAddr ra) {
+ return ra != null && ra.getContent() != null;
+ }
+
+ /**
+ * Whether to toggle the pooling of {@code PreparedStatement}s
+ *
+ * @return value of poolPreparedStatements.
+ */
+ public boolean isPoolPreparedStatements() {
+ return poolPreparedStatements;
+ }
+
+ /**
+ * Sets the value of the accessToUnderlyingConnectionAllowed property. It controls if the PoolGuard allows access to
+ * the underlying connection. (Default: false)
+ *
+ * @param allow Access to the underlying connection is granted when true.
+ */
+ public synchronized void setAccessToUnderlyingConnectionAllowed(final boolean allow) {
+ this.accessToUnderlyingConnectionAllowed = allow;
+ }
+
+ /**
+ * Sets the connection properties passed to the JDBC driver.
+ * <p>
+ * If {@code props} contains "user" and/or "password" properties, the corresponding instance properties are
+ * set. If these properties are not present, they are filled in using {@link #getUser()}, {@link #getPassword()}
+ * when {@link #getPooledConnection()} is called, or using the actual parameters to the method call when
+ * {@link #getPooledConnection(String, String)} is called. Calls to {@link #setUser(String)} or
+ * {@link #setPassword(String)} overwrite the values of these properties if {@code connectionProperties} is not
+ * null.
+ * </p>
+ *
+ * @param props Connection properties to use when creating new connections.
+ * @throws IllegalStateException if {@link #getPooledConnection()} has been called
+ */
+ public void setConnectionProperties(final Properties props) {
+ assertInitializationAllowed();
+ connectionProperties = props;
+ if (connectionProperties != null) {
+ if (connectionProperties.containsKey(Constants.KEY_USER)) {
+ setUser(connectionProperties.getProperty(Constants.KEY_USER));
+ }
+ if (connectionProperties.containsKey(Constants.KEY_PASSWORD)) {
+ setPassword(connectionProperties.getProperty(Constants.KEY_PASSWORD));
+ }
+ }
+ }
+
+ /**
+ * Sets the value of description. This property is here for use by the code which will deploy this datasource. It is
+ * not used internally.
+ *
+ * @param description Value to assign to description.
+ */
+ public void setDescription(final String description) {
+ this.description = description;
+ }
+
+ /**
+ * Sets the driver class name. Setting the driver class name cause the driver to be registered with the
+ * DriverManager.
+ *
+ * @param driver Value to assign to driver.
+ * @throws IllegalStateException if {@link #getPooledConnection()} has been called
+ * @throws ClassNotFoundException if the class cannot be located
+ */
+ public void setDriver(final String driver) throws ClassNotFoundException {
+ assertInitializationAllowed();
+ this.driver = driver;
+ // make sure driver is registered
+ Class.forName(driver);
+ }
+
+ /**
+ * Sets the duration to sleep between runs of the idle object evictor thread. When non-positive, no
+ * idle object evictor thread will be run.
+ *
+ * @param durationBetweenEvictionRuns The duration to sleep between runs of the idle object evictor
+ * thread. When non-positive, no idle object evictor thread will be run.
+ * @see #getDurationBetweenEvictionRuns()
+ * @throws IllegalStateException if {@link #getPooledConnection()} has been called
+ * @since 2.9.0
+ */
+ public void setDurationBetweenEvictionRuns(final Duration durationBetweenEvictionRuns) {
+ assertInitializationAllowed();
+ this.durationBetweenEvictionRuns = durationBetweenEvictionRuns;
+ }
+
+ /**
+ * Sets the maximum time in seconds that this data source will wait while attempting to connect to a database. NOT
+ * USED.
+ */
+ @Override
+ public void setLoginTimeout(final int seconds) {
+ this.loginTimeout = seconds;
+ }
+
+ /**
+ * Sets the log writer for this data source. NOT USED.
+ */
+ @Override
+ public void setLogWriter(final PrintWriter logWriter) {
+ this.logWriter = logWriter;
+ }
+
+ /**
+ * Gets the maximum number of statements that can remain idle in the pool, without extra ones being released, or
+ * negative for no limit.
+ *
+ * @param maxIdle The maximum number of statements that can remain idle
+ * @throws IllegalStateException if {@link #getPooledConnection()} has been called
+ */
+ public void setMaxIdle(final int maxIdle) {
+ assertInitializationAllowed();
+ this.maxIdle = maxIdle;
+ }
+
+ /**
+ * Sets the maximum number of prepared statements.
+ *
+ * @param maxPreparedStatements the new maximum number of prepared statements
+ */
+ public void setMaxPreparedStatements(final int maxPreparedStatements) {
+ this.maxPreparedStatements = maxPreparedStatements;
+ }
+
+ /**
+ * Sets the minimum amount of time a statement may sit idle in the pool before it is eligible for eviction by the
+ * idle object evictor (if any). When non-positive, no objects will be evicted from the pool due to idle time alone.
+ *
+ * @param minEvictableIdleDuration minimum time to set in milliseconds.
+ * @see #getMinEvictableIdleDuration()
+ * @see #setDurationBetweenEvictionRuns(Duration)
+ * @throws IllegalStateException if {@link #getPooledConnection()} has been called.
+ * @since 2.9.0
+ */
+ public void setMinEvictableIdleDuration(final Duration minEvictableIdleDuration) {
+ assertInitializationAllowed();
+ this.minEvictableIdleDuration = minEvictableIdleDuration;
+ }
+
+ /**
+ * Sets the minimum amount of time a statement may sit idle in the pool before it is eligible for eviction by the
+ * idle object evictor (if any). When non-positive, no objects will be evicted from the pool due to idle time alone.
+ *
+ * @param minEvictableIdleTimeMillis minimum time to set in milliseconds.
+ * @see #getMinEvictableIdleDuration()
+ * @see #setDurationBetweenEvictionRuns(Duration)
+ * @throws IllegalStateException if {@link #getPooledConnection()} has been called
+ * @deprecated Use {@link #setMinEvictableIdleDuration(Duration)}.
+ */
+ @Deprecated
+ public void setMinEvictableIdleTimeMillis(final int minEvictableIdleTimeMillis) {
+ assertInitializationAllowed();
+ this.minEvictableIdleDuration = Duration.ofMillis(minEvictableIdleTimeMillis);
+ }
+
+ /**
+ * Sets the number of statements to examine during each run of the idle object evictor thread (if any).
+ * <p>
+ * When a negative value is supplied,
+ * {@code ceil({@link BasicDataSource#getNumIdle})/abs({@link #getNumTestsPerEvictionRun})} tests will be run.
+ * I.e., when the value is <i>-n</i>, roughly one <i>n</i>th of the idle objects will be tested per run.
+ * </p>
+ *
+ * @param numTestsPerEvictionRun number of statements to examine per run
+ * @see #getNumTestsPerEvictionRun()
+ * @see #setDurationBetweenEvictionRuns(Duration)
+ * @throws IllegalStateException if {@link #getPooledConnection()} has been called
+ */
+ public void setNumTestsPerEvictionRun(final int numTestsPerEvictionRun) {
+ assertInitializationAllowed();
+ this.numTestsPerEvictionRun = numTestsPerEvictionRun;
+ }
+
+ /**
+ * Sets the value of password for the default user.
+ *
+ * @param userPassword Value to assign to password.
+ * @throws IllegalStateException if {@link #getPooledConnection()} has been called
+ */
+ public void setPassword(final char[] userPassword) {
+ assertInitializationAllowed();
+ this.userPassword = Utils.clone(userPassword);
+ update(connectionProperties, Constants.KEY_PASSWORD, Utils.toString(this.userPassword));
+ }
+
+ /**
+ * Sets the value of password for the default user.
+ *
+ * @param userPassword Value to assign to password.
+ * @throws IllegalStateException if {@link #getPooledConnection()} has been called
+ */
+ public void setPassword(final String userPassword) {
+ assertInitializationAllowed();
+ this.userPassword = Utils.toCharArray(userPassword);
+ update(connectionProperties, Constants.KEY_PASSWORD, userPassword);
+ }
+
+ /**
+ * Whether to toggle the pooling of {@code PreparedStatement}s
+ *
+ * @param poolPreparedStatements true to pool statements.
+ * @throws IllegalStateException if {@link #getPooledConnection()} has been called
+ */
+ public void setPoolPreparedStatements(final boolean poolPreparedStatements) {
+ assertInitializationAllowed();
+ this.poolPreparedStatements = poolPreparedStatements;
+ }
+
+ /**
+ * Sets 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.
+ *
+ * @param timeBetweenEvictionRunsMillis 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.
+ * @see #getDurationBetweenEvictionRuns()
+ * @throws IllegalStateException if {@link #getPooledConnection()} has been called
+ * @deprecated Use {@link #setDurationBetweenEvictionRuns(Duration)}.
+ */
+ @Deprecated
+ public void setTimeBetweenEvictionRunsMillis(final long timeBetweenEvictionRunsMillis) {
+ assertInitializationAllowed();
+ this.durationBetweenEvictionRuns = Duration.ofMillis(timeBetweenEvictionRunsMillis);
+ }
+
+ /**
+ * Sets the value of URL string used to locate the database for this datasource.
+ *
+ * @param url Value to assign to url.
+ * @throws IllegalStateException if {@link #getPooledConnection()} has been called
+ */
+ public void setUrl(final String url) {
+ assertInitializationAllowed();
+ this.url = url;
+ }
+
+ /**
+ * Sets the value of default user (login or user name).
+ *
+ * @param userName Value to assign to user.
+ * @throws IllegalStateException if {@link #getPooledConnection()} has been called
+ */
+ public void setUser(final String userName) {
+ assertInitializationAllowed();
+ this.userName = userName;
+ update(connectionProperties, Constants.KEY_USER, userName);
+ }
+
+ /**
+ * 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(durationBetweenEvictionRuns);
+ builder.append(", numTestsPerEvictionRun=");
+ builder.append(numTestsPerEvictionRun);
+ builder.append(", minEvictableIdleTimeMillis=");
+ builder.append(minEvictableIdleDuration);
+ builder.append(", maxPreparedStatements=");
+ builder.append(maxPreparedStatements);
+ builder.append(", getConnectionCalled=");
+ builder.append(getConnectionCalled);
+ builder.append(", connectionProperties=");
+ builder.append(Utils.cloneWithoutCredentials(connectionProperties));
+ builder.append(", accessToUnderlyingConnectionAllowed=");
+ builder.append(accessToUnderlyingConnectionAllowed);
+ builder.append("]");
+ return builder.toString();
+ }
+
+ private void update(final Properties properties, final String key, final String value) {
+ if (properties != null && key != null) {
+ if (value == null) {
+ properties.remove(key);
+ } else {
+ properties.setProperty(key, value);
+ }
+ }
+ }
+}
diff --git a/src/main/java/org/apache/commons/dbcp2/cpdsadapter/PooledConnectionImpl.java b/src/main/java/org/apache/commons/dbcp2/cpdsadapter/PooledConnectionImpl.java
index 75100909..d50329f1 100644
--- a/src/main/java/org/apache/commons/dbcp2/cpdsadapter/PooledConnectionImpl.java
+++ b/src/main/java/org/apache/commons/dbcp2/cpdsadapter/PooledConnectionImpl.java
@@ -1,758 +1,758 @@
-/*
- * 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.commons.dbcp2.cpdsadapter;
-
-import java.sql.CallableStatement;
-import java.sql.Connection;
-import java.sql.PreparedStatement;
-import java.sql.SQLException;
-import java.util.ArrayList;
-import java.util.Collections;
-import java.util.List;
-
-import javax.sql.ConnectionEvent;
-import javax.sql.ConnectionEventListener;
-import javax.sql.PooledConnection;
-import javax.sql.StatementEventListener;
-
-import org.apache.commons.dbcp2.DelegatingConnection;
-import org.apache.commons.dbcp2.DelegatingPreparedStatement;
-import org.apache.commons.dbcp2.Jdbc41Bridge;
-import org.apache.commons.dbcp2.PStmtKey;
-import org.apache.commons.dbcp2.PoolableCallableStatement;
-import org.apache.commons.dbcp2.PoolablePreparedStatement;
-import org.apache.commons.dbcp2.PoolingConnection.StatementType;
-import org.apache.commons.pool2.KeyedObjectPool;
-import org.apache.commons.pool2.KeyedPooledObjectFactory;
-import org.apache.commons.pool2.PooledObject;
-import org.apache.commons.pool2.impl.DefaultPooledObject;
-
-/**
- * Implementation of PooledConnection that is returned by PooledConnectionDataSource.
- *
- * @since 2.0
- */
-class PooledConnectionImpl
- implements PooledConnection, KeyedPooledObjectFactory<PStmtKey, DelegatingPreparedStatement> {
-
- private static final String CLOSED = "Attempted to use PooledConnection after closed() was called.";
-
- /**
- * The JDBC database connection that represents the physical db connection.
- */
- private Connection connection;
-
- /**
- * A DelegatingConnection used to create a PoolablePreparedStatementStub.
- */
- private final DelegatingConnection<?> delegatingConnection;
-
- /**
- * The JDBC database logical connection.
- */
- private Connection logicalConnection;
-
- /**
- * ConnectionEventListeners.
- */
- private final List<ConnectionEventListener> eventListeners;
-
- /**
- * StatementEventListeners.
- */
- private final List<StatementEventListener> statementEventListeners = Collections.synchronizedList(new ArrayList<>());
-
- /**
- * Flag set to true, once {@link #close()} is called.
- */
- private boolean closed;
-
- /** My pool of {@link PreparedStatement}s. */
- private KeyedObjectPool<PStmtKey, DelegatingPreparedStatement> pStmtPool;
-
- /**
- * Controls access to the underlying connection.
- */
- private boolean accessToUnderlyingConnectionAllowed;
-
- /**
- * Wraps the real connection.
- *
- * @param connection
- * the connection to be wrapped.
- */
- PooledConnectionImpl(final Connection connection) {
- this.connection = connection;
- if (connection instanceof DelegatingConnection) {
- this.delegatingConnection = (DelegatingConnection<?>) connection;
- } else {
- this.delegatingConnection = new DelegatingConnection<>(connection);
- }
- eventListeners = Collections.synchronizedList(new ArrayList<>());
- closed = false;
- }
-
- /**
- * My {@link KeyedPooledObjectFactory} method for activating {@link PreparedStatement}s.
- *
- * @param pooledObject Activates the underlying object.
- */
- @Override
- public void activateObject(final PStmtKey key, final PooledObject<DelegatingPreparedStatement> pooledObject)
- throws Exception {
- pooledObject.getObject().activate();
- }
-
- /**
- * {@inheritDoc}
- */
- @Override
- public void addConnectionEventListener(final ConnectionEventListener listener) {
- if (!eventListeners.contains(listener)) {
- eventListeners.add(listener);
- }
- }
-
- @Override
- public void addStatementEventListener(final StatementEventListener listener) {
- if (!statementEventListeners.contains(listener)) {
- statementEventListeners.add(listener);
- }
- }
- /* JDBC_4_ANT_KEY_END */
-
- /**
- * Throws an SQLException, if isClosed is true
- */
- private void assertOpen() throws SQLException {
- if (closed) {
- throw new SQLException(CLOSED);
- }
- }
-
- /**
- * Closes the physical connection and marks this {@code PooledConnection} so that it may not be used to
- * generate any more logical {@code Connection}s.
- *
- * @throws SQLException
- * Thrown when an error occurs or the connection is already closed.
- */
- @Override
- public void close() throws SQLException {
- assertOpen();
- closed = true;
- try {
- if (pStmtPool != null) {
- try {
- pStmtPool.close();
- } finally {
- pStmtPool = null;
- }
- }
- } catch (final RuntimeException e) {
- throw e;
- } catch (final Exception e) {
- throw new SQLException("Cannot close connection (return to pool failed)", e);
- } finally {
- try {
- connection.close();
- } finally {
- connection = null;
- }
- }
- }
-
- /**
- * Creates a {@link PStmtKey} for the given arguments.
- *
- * @param sql
- * The SQL statement.
- * @return a {@link PStmtKey} for the given arguments.
- */
- protected PStmtKey createKey(final String sql) {
- return new PStmtKey(normalizeSQL(sql), getCatalogOrNull(), getSchemaOrNull());
- }
-
- /**
- * Creates a {@link PStmtKey} for the given arguments.
- *
- * @param sql
- * The SQL statement.
- * @param autoGeneratedKeys
- * A flag indicating whether auto-generated keys should be returned; one of
- * {@code Statement.RETURN_GENERATED_KEYS} or {@code Statement.NO_GENERATED_KEYS}.
- * @return a key to uniquely identify a prepared statement.
- */
- protected PStmtKey createKey(final String sql, final int autoGeneratedKeys) {
- return new PStmtKey(normalizeSQL(sql), getCatalogOrNull(), getSchemaOrNull(), autoGeneratedKeys);
- }
-
- /**
- * Creates a {@link PStmtKey} for the given arguments.
- *
- * @param sql
- * The SQL statement.
- * @param resultSetType
- * A result set type; one of {@code ResultSet.TYPE_FORWARD_ONLY},
- * {@code ResultSet.TYPE_SCROLL_INSENSITIVE}, or {@code ResultSet.TYPE_SCROLL_SENSITIVE}.
- * @param resultSetConcurrency
- * A concurrency type; one of {@code ResultSet.CONCUR_READ_ONLY} or
- * {@code ResultSet.CONCUR_UPDATABLE}.
- * @return a key to uniquely identify a prepared statement.
- */
- protected PStmtKey createKey(final String sql, final int resultSetType, final int resultSetConcurrency) {
- return new PStmtKey(normalizeSQL(sql), getCatalogOrNull(), getSchemaOrNull(), resultSetType,
- resultSetConcurrency);
- }
-
- /**
- * Creates a {@link PStmtKey} for the given arguments.
- *
- * @param sql
- * The SQL statement.
- * @param resultSetType
- * a result set type; one of {@code ResultSet.TYPE_FORWARD_ONLY},
- * {@code ResultSet.TYPE_SCROLL_INSENSITIVE}, or {@code ResultSet.TYPE_SCROLL_SENSITIVE}.
- * @param resultSetConcurrency
- * A concurrency type; one of {@code ResultSet.CONCUR_READ_ONLY} or
- * {@code ResultSet.CONCUR_UPDATABLE}
- * @param resultSetHoldability
- * One of the following {@code ResultSet} constants: {@code ResultSet.HOLD_CURSORS_OVER_COMMIT}
- * or {@code ResultSet.CLOSE_CURSORS_AT_COMMIT}.
- * @return a key to uniquely identify a prepared statement.
- */
- protected PStmtKey createKey(final String sql, final int resultSetType, final int resultSetConcurrency,
- final int resultSetHoldability) {
- return new PStmtKey(normalizeSQL(sql), getCatalogOrNull(), getSchemaOrNull(), resultSetType,
- resultSetConcurrency, resultSetHoldability);
- }
-
- /**
- * Creates a {@link PStmtKey} for the given arguments.
- *
- * @param sql
- * The SQL statement.
- * @param resultSetType
- * a result set type; one of {@code ResultSet.TYPE_FORWARD_ONLY},
- * {@code ResultSet.TYPE_SCROLL_INSENSITIVE}, or {@code ResultSet.TYPE_SCROLL_SENSITIVE}
- * @param resultSetConcurrency
- * A concurrency type; one of {@code ResultSet.CONCUR_READ_ONLY} or
- * {@code ResultSet.CONCUR_UPDATABLE}.
- * @param resultSetHoldability
- * One of the following {@code ResultSet} constants: {@code ResultSet.HOLD_CURSORS_OVER_COMMIT}
- * or {@code ResultSet.CLOSE_CURSORS_AT_COMMIT}.
- * @param statementType
- * The SQL statement type, prepared or callable.
- * @return a key to uniquely identify a prepared statement.
- * @since 2.4.0
- */
- protected PStmtKey createKey(final String sql, final int resultSetType, final int resultSetConcurrency,
- final int resultSetHoldability, final StatementType statementType) {
- return new PStmtKey(normalizeSQL(sql), getCatalogOrNull(), getSchemaOrNull(), resultSetType,
- resultSetConcurrency, resultSetHoldability, statementType);
- }
-
- /**
- * Creates a {@link PStmtKey} for the given arguments.
- *
- * @param sql
- * The SQL statement.
- * @param resultSetType
- * A result set type; one of {@code ResultSet.TYPE_FORWARD_ONLY},
- * {@code ResultSet.TYPE_SCROLL_INSENSITIVE}, or {@code ResultSet.TYPE_SCROLL_SENSITIVE}.
- * @param resultSetConcurrency
- * A concurrency type; one of {@code ResultSet.CONCUR_READ_ONLY} or
- * {@code ResultSet.CONCUR_UPDATABLE}.
- * @param statementType
- * The SQL statement type, prepared or callable.
- * @return a key to uniquely identify a prepared statement.
- * @since 2.4.0
- */
- protected PStmtKey createKey(final String sql, final int resultSetType, final int resultSetConcurrency,
- final StatementType statementType) {
- return new PStmtKey(normalizeSQL(sql), getCatalogOrNull(), getSchemaOrNull(), resultSetType,
- resultSetConcurrency, statementType);
- }
-
- /**
- * Creates a {@link PStmtKey} for the given arguments.
- *
- * @param sql
- * The SQL statement.
- * @param columnIndexes
- * An array of column indexes indicating the columns that should be returned from the inserted row or
- * rows.
- * @return a key to uniquely identify a prepared statement.
- */
- protected PStmtKey createKey(final String sql, final int[] columnIndexes) {
- return new PStmtKey(normalizeSQL(sql), getCatalogOrNull(), getSchemaOrNull(), columnIndexes);
- }
-
- /**
- * Creates a {@link PStmtKey} for the given arguments.
- *
- * @param sql
- * The SQL statement.
- * @param statementType
- * The SQL statement type, prepared or callable.
- * @return a key to uniquely identify a prepared statement.
- */
- protected PStmtKey createKey(final String sql, final StatementType statementType) {
- return new PStmtKey(normalizeSQL(sql), getCatalogOrNull(), getSchemaOrNull(), statementType);
- }
-
- /**
- * Creates a {@link PStmtKey} for the given arguments.
- *
- * @param sql
- * The SQL statement.
- * @param columnNames
- * An array of column names indicating the columns that should be returned from the inserted row or rows.
- * @return a key to uniquely identify a prepared statement.
- */
- protected PStmtKey createKey(final String sql, final String[] columnNames) {
- return new PStmtKey(normalizeSQL(sql), getCatalogOrNull(), getSchemaOrNull(), columnNames);
- }
-
- /**
- * My {@link KeyedPooledObjectFactory} method for destroying {@link PreparedStatement}s.
- *
- * @param key
- * ignored
- * @param pooledObject
- * the wrapped {@link PreparedStatement} to be destroyed.
- */
- @Override
- public void destroyObject(final PStmtKey key, final PooledObject<DelegatingPreparedStatement> pooledObject)
- throws Exception {
- pooledObject.getObject().getInnermostDelegate().close();
- }
-
- /**
- * Closes the physical connection and checks that the logical connection was closed as well.
- */
- @Override
- protected void finalize() throws Throwable {
- // Closing the Connection ensures that if anyone tries to use it,
- // an error will occur.
- try {
- connection.close();
- } catch (final Exception ignored) {
- // ignore
- }
-
- // make sure the last connection is marked as closed
- if (logicalConnection != null && !logicalConnection.isClosed()) {
- throw new SQLException("PooledConnection was gc'ed, without its last Connection being closed.");
- }
- }
-
- private String getCatalogOrNull() {
- try {
- return connection == null ? null : connection.getCatalog();
- } catch (final SQLException e) {
- return null;
- }
- }
-
- /**
- * Returns a JDBC connection.
- *
- * @return The database connection.
- * @throws SQLException
- * if the connection is not open or the previous logical connection is still open
- */
- @Override
- public Connection getConnection() throws SQLException {
- assertOpen();
- // make sure the last connection is marked as closed
- if (logicalConnection != null && !logicalConnection.isClosed()) {
- // should notify pool of error so the pooled connection can
- // be removed !FIXME!
- throw new SQLException("PooledConnection was reused, without its previous Connection being closed.");
- }
-
- // the spec requires that this return a new Connection instance.
- logicalConnection = new ConnectionImpl(this, connection, isAccessToUnderlyingConnectionAllowed());
- return logicalConnection;
- }
-
- private String getSchemaOrNull() {
- try {
- return connection == null ? null : Jdbc41Bridge.getSchema(connection);
- } catch (final SQLException e) {
- return null;
- }
- }
-
- /**
- * Returns the value of the accessToUnderlyingConnectionAllowed property.
- *
- * @return true if access to the underlying is allowed, false otherwise.
- */
- public synchronized boolean isAccessToUnderlyingConnectionAllowed() {
- return this.accessToUnderlyingConnectionAllowed;
- }
-
- /**
- * My {@link KeyedPooledObjectFactory} method for creating {@link PreparedStatement}s.
- *
- * @param key
- * The key for the {@link PreparedStatement} to be created.
- */
- @SuppressWarnings("resource")
- @Override
- public PooledObject<DelegatingPreparedStatement> makeObject(final PStmtKey key) throws Exception {
- if (null == key) {
- throw new IllegalArgumentException("Prepared statement key is null or invalid.");
- }
- if (key.getStmtType() == StatementType.PREPARED_STATEMENT) {
- final PreparedStatement statement = (PreparedStatement) key.createStatement(connection);
- @SuppressWarnings({"rawtypes", "unchecked" }) // Unable to find way to avoid this
- final PoolablePreparedStatement pps = new PoolablePreparedStatement(statement, key, pStmtPool,
- delegatingConnection);
- return new DefaultPooledObject<>(pps);
- }
- final CallableStatement statement = (CallableStatement) key.createStatement(connection);
- @SuppressWarnings("unchecked")
- final PoolableCallableStatement pcs = new PoolableCallableStatement(statement, key, pStmtPool,
- (DelegatingConnection<Connection>) delegatingConnection);
- return new DefaultPooledObject<>(pcs);
- }
-
- /**
- * Normalizes the given SQL statement, producing a canonical form that is semantically equivalent to the original.
- * @param sql
- * The SQL statement.
- * @return the normalized SQL statement.
- */
- protected String normalizeSQL(final String sql) {
- return sql.trim();
- }
-
- /**
- * Sends a connectionClosed event.
- */
- void notifyListeners() {
- final ConnectionEvent event = new ConnectionEvent(this);
- final Object[] listeners = eventListeners.toArray();
- for (final Object listener : listeners) {
- ((ConnectionEventListener) listener).connectionClosed(event);
- }
- }
-
- /**
- * My {@link KeyedPooledObjectFactory} method for passivating {@link PreparedStatement}s. Currently invokes
- * {@link PreparedStatement#clearParameters}.
- *
- * @param key
- * ignored
- * @param pooledObject
- * a wrapped {@link PreparedStatement}
- */
- @Override
- public void passivateObject(final PStmtKey key, final PooledObject<DelegatingPreparedStatement> pooledObject)
- throws Exception {
- @SuppressWarnings("resource")
- final DelegatingPreparedStatement dps = pooledObject.getObject();
- dps.clearParameters();
- dps.passivate();
- }
-
- /**
- * Creates or obtains a {@link CallableStatement} from my pool.
- *
- * @param sql
- * an SQL statement that may contain one or more '?' parameter placeholders. Typically this statement is
- * specified using JDBC call escape syntax.
- * @return a default {@code CallableStatement} object containing the pre-compiled SQL statement.
- * @throws SQLException
- * Thrown if a database access error occurs or this method is called on a closed connection.
- * @since 2.4.0
- */
- CallableStatement prepareCall(final String sql) throws SQLException {
- if (pStmtPool == null) {
- return connection.prepareCall(sql);
- }
- try {
- return (CallableStatement) pStmtPool.borrowObject(createKey(sql, StatementType.CALLABLE_STATEMENT));
- } catch (final RuntimeException e) {
- throw e;
- } catch (final Exception e) {
- throw new SQLException("Borrow prepareCall from pool failed", e);
- }
- }
-
- /**
- * Creates or obtains a {@link CallableStatement} from my pool.
- *
- * @param sql
- * a {@code String} object that is the SQL statement to be sent to the database; may contain on or
- * more '?' parameters.
- * @param resultSetType
- * a result set type; one of {@code ResultSet.TYPE_FORWARD_ONLY},
- * {@code ResultSet.TYPE_SCROLL_INSENSITIVE}, or {@code ResultSet.TYPE_SCROLL_SENSITIVE}.
- * @param resultSetConcurrency
- * a concurrency type; one of {@code ResultSet.CONCUR_READ_ONLY} or
- * {@code ResultSet.CONCUR_UPDATABLE}.
- * @return a {@code CallableStatement} object containing the pre-compiled SQL statement that will produce
- * {@code ResultSet} objects with the given type and concurrency.
- * @throws SQLException
- * Thrown if a database access error occurs, this method is called on a closed connection or the given
- * parameters are not {@code ResultSet} constants indicating type and concurrency.
- * @since 2.4.0
- */
- CallableStatement prepareCall(final String sql, final int resultSetType, final int resultSetConcurrency)
- throws SQLException {
- if (pStmtPool == null) {
- return connection.prepareCall(sql, resultSetType, resultSetConcurrency);
- }
- try {
- return (CallableStatement) pStmtPool.borrowObject(
- createKey(sql, resultSetType, resultSetConcurrency, StatementType.CALLABLE_STATEMENT));
- } catch (final RuntimeException e) {
- throw e;
- } catch (final Exception e) {
- throw new SQLException("Borrow prepareCall from pool failed", e);
- }
- }
-
- /**
- * Creates or obtains a {@link CallableStatement} from my pool.
- *
- * @param sql
- * a {@code String} object that is the SQL statement to be sent to the database; may contain on or
- * more '?' parameters.
- * @param resultSetType
- * one of the following {@code ResultSet} constants: {@code ResultSet.TYPE_FORWARD_ONLY},
- * {@code ResultSet.TYPE_SCROLL_INSENSITIVE}, or {@code ResultSet.TYPE_SCROLL_SENSITIVE}.
- * @param resultSetConcurrency
- * one of the following {@code ResultSet} constants: {@code ResultSet.CONCUR_READ_ONLY} or
- * {@code ResultSet.CONCUR_UPDATABLE}.
- * @param resultSetHoldability
- * one of the following {@code ResultSet} constants: {@code ResultSet.HOLD_CURSORS_OVER_COMMIT}
- * or {@code ResultSet.CLOSE_CURSORS_AT_COMMIT}.
- * @return a new {@code CallableStatement} object, containing the pre-compiled SQL statement, that will
- * generate {@code ResultSet} objects with the given type, concurrency, and holdability.
- * @throws SQLException
- * Thrown if a database access error occurs, this method is called on a closed connection or the given
- * parameters are not {@code ResultSet} constants indicating type, concurrency, and holdability.
- * @since 2.4.0
- */
- CallableStatement prepareCall(final String sql, final int resultSetType, final int resultSetConcurrency,
- final int resultSetHoldability) throws SQLException {
- if (pStmtPool == null) {
- return connection.prepareCall(sql, resultSetType, resultSetConcurrency, resultSetHoldability);
- }
- try {
- return (CallableStatement) pStmtPool.borrowObject(createKey(sql, resultSetType, resultSetConcurrency,
- resultSetHoldability, StatementType.CALLABLE_STATEMENT));
- } catch (final RuntimeException e) {
- throw e;
- } catch (final Exception e) {
- throw new SQLException("Borrow prepareCall from pool failed", e);
- }
- }
-
- /**
- * Creates or obtains a {@link PreparedStatement} from my pool.
- *
- * @param sql the SQL statement.
- * @return a {@link PoolablePreparedStatement}
- * @throws SQLException Thrown if a database access error occurs, this method is called on a closed connection, or
- * the borrow failed.
- */
- PreparedStatement prepareStatement(final String sql) throws SQLException {
- if (pStmtPool == null) {
- return connection.prepareStatement(sql);
- }
- try {
- return pStmtPool.borrowObject(createKey(sql));
- } catch (final RuntimeException e) {
- throw e;
- } catch (final Exception e) {
- throw new SQLException("Borrow prepareStatement from pool failed", e);
- }
- }
-
- /**
- * Creates or obtains a {@link PreparedStatement} from my pool.
- *
- * @param sql
- * an SQL statement that may contain one or more '?' IN parameter placeholders.
- * @param autoGeneratedKeys
- * a flag indicating whether auto-generated keys should be returned; one of
- * {@code Statement.RETURN_GENERATED_KEYS} or {@code Statement.NO_GENERATED_KEYS}.
- * @return a {@link PoolablePreparedStatement}
- * @throws SQLException Thrown if a database access error occurs, this method is called on a closed connection, or
- * the borrow failed.
- * @see Connection#prepareStatement(String, int)
- */
- PreparedStatement prepareStatement(final String sql, final int autoGeneratedKeys) throws SQLException {
- if (pStmtPool == null) {
- return connection.prepareStatement(sql, autoGeneratedKeys);
- }
- try {
- return pStmtPool.borrowObject(createKey(sql, autoGeneratedKeys));
- } catch (final RuntimeException e) {
- throw e;
- } catch (final Exception e) {
- throw new SQLException("Borrow prepareStatement from pool failed", e);
- }
- }
-
- /**
- * Creates or obtains a {@link PreparedStatement} from my pool.
- *
- * @param sql
- * a {@code String} object that is the SQL statement to be sent to the database; may contain one or
- * more '?' IN parameters.
- * @param resultSetType
- * a result set type; one of {@code ResultSet.TYPE_FORWARD_ONLY},
- * {@code ResultSet.TYPE_SCROLL_INSENSITIVE}, or {@code ResultSet.TYPE_SCROLL_SENSITIVE}.
- * @param resultSetConcurrency
- * a concurrency type; one of {@code ResultSet.CONCUR_READ_ONLY} or
- * {@code ResultSet.CONCUR_UPDATABLE}.
- *
- * @return a {@link PoolablePreparedStatement}.
- * @throws SQLException Thrown if a database access error occurs, this method is called on a closed connection, or
- * the borrow failed.
- * @see Connection#prepareStatement(String, int, int)
- */
- PreparedStatement prepareStatement(final String sql, final int resultSetType, final int resultSetConcurrency)
- throws SQLException {
- if (pStmtPool == null) {
- return connection.prepareStatement(sql, resultSetType, resultSetConcurrency);
- }
- try {
- return pStmtPool.borrowObject(createKey(sql, resultSetType, resultSetConcurrency));
- } catch (final RuntimeException e) {
- throw e;
- } catch (final Exception e) {
- throw new SQLException("Borrow prepareStatement from pool failed", e);
- }
- }
-
- PreparedStatement prepareStatement(final String sql, final int resultSetType, final int resultSetConcurrency,
- final int resultSetHoldability) throws SQLException {
- if (pStmtPool == null) {
- return connection.prepareStatement(sql, resultSetType, resultSetConcurrency, resultSetHoldability);
- }
- try {
- return pStmtPool.borrowObject(createKey(sql, resultSetType, resultSetConcurrency, resultSetHoldability));
- } catch (final RuntimeException e) {
- throw e;
- } catch (final Exception e) {
- throw new SQLException("Borrow prepareStatement from pool failed", e);
- }
- }
-
- PreparedStatement prepareStatement(final String sql, final int[] columnIndexes) throws SQLException {
- if (pStmtPool == null) {
- return connection.prepareStatement(sql, columnIndexes);
- }
- try {
- return pStmtPool.borrowObject(createKey(sql, columnIndexes));
- } catch (final RuntimeException e) {
- throw e;
- } catch (final Exception e) {
- throw new SQLException("Borrow prepareStatement from pool failed", e);
- }
- }
-
- PreparedStatement prepareStatement(final String sql, final String[] columnNames) throws SQLException {
- if (pStmtPool == null) {
- return connection.prepareStatement(sql, columnNames);
- }
- try {
- return pStmtPool.borrowObject(createKey(sql, columnNames));
- } catch (final RuntimeException e) {
- throw e;
- } catch (final Exception e) {
- throw new SQLException("Borrow prepareStatement from pool failed", e);
- }
- }
-
- /**
- * {@inheritDoc}
- */
- @Override
- public void removeConnectionEventListener(final ConnectionEventListener listener) {
- eventListeners.remove(listener);
- }
-
- @Override
- public void removeStatementEventListener(final StatementEventListener listener) {
- statementEventListeners.remove(listener);
- }
- /* JDBC_4_ANT_KEY_END */
-
- /**
- * Sets the value of the accessToUnderlyingConnectionAllowed property. It controls if the PoolGuard allows access to
- * the underlying connection. (Default: false.)
- *
- * @param allow
- * Access to the underlying connection is granted when true.
- */
- public synchronized void setAccessToUnderlyingConnectionAllowed(final boolean allow) {
- this.accessToUnderlyingConnectionAllowed = allow;
- }
-
- public void setStatementPool(final KeyedObjectPool<PStmtKey, DelegatingPreparedStatement> statementPool) {
- pStmtPool = statementPool;
- }
-
- /**
- * @since 2.6.0
- */
- @Override
- public synchronized String toString() {
- final StringBuilder builder = new StringBuilder(super.toString());
- builder.append("[connection=");
- builder.append(connection);
- builder.append(", delegatingConnection=");
- builder.append(delegatingConnection);
- builder.append(", logicalConnection=");
- builder.append(logicalConnection);
- builder.append(", eventListeners=");
- builder.append(eventListeners);
- builder.append(", statementEventListeners=");
- builder.append(statementEventListeners);
- builder.append(", closed=");
- builder.append(closed);
- builder.append(", pStmtPool=");
- builder.append(pStmtPool);
- builder.append(", accessToUnderlyingConnectionAllowed=");
- builder.append(accessToUnderlyingConnectionAllowed);
- builder.append("]");
- return builder.toString();
- }
-
- /**
- * My {@link KeyedPooledObjectFactory} method for validating {@link PreparedStatement}s.
- *
- * @param key
- * Ignored.
- * @param pooledObject
- * Ignored.
- * @return {@code true}
- */
- @Override
- public boolean validateObject(final PStmtKey key, final PooledObject<DelegatingPreparedStatement> pooledObject) {
- return true;
- }
-}
+/*
+ * 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.commons.dbcp2.cpdsadapter;
+
+import java.sql.CallableStatement;
+import java.sql.Connection;
+import java.sql.PreparedStatement;
+import java.sql.SQLException;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+
+import javax.sql.ConnectionEvent;
+import javax.sql.ConnectionEventListener;
+import javax.sql.PooledConnection;
+import javax.sql.StatementEventListener;
+
+import org.apache.commons.dbcp2.DelegatingConnection;
+import org.apache.commons.dbcp2.DelegatingPreparedStatement;
+import org.apache.commons.dbcp2.Jdbc41Bridge;
+import org.apache.commons.dbcp2.PStmtKey;
+import org.apache.commons.dbcp2.PoolableCallableStatement;
+import org.apache.commons.dbcp2.PoolablePreparedStatement;
+import org.apache.commons.dbcp2.PoolingConnection.StatementType;
+import org.apache.commons.pool2.KeyedObjectPool;
+import org.apache.commons.pool2.KeyedPooledObjectFactory;
+import org.apache.commons.pool2.PooledObject;
+import org.apache.commons.pool2.impl.DefaultPooledObject;
+
+/**
+ * Implementation of PooledConnection that is returned by PooledConnectionDataSource.
+ *
+ * @since 2.0
+ */
+class PooledConnectionImpl
+ implements PooledConnection, KeyedPooledObjectFactory<PStmtKey, DelegatingPreparedStatement> {
+
+ private static final String CLOSED = "Attempted to use PooledConnection after closed() was called.";
+
+ /**
+ * The JDBC database connection that represents the physical db connection.
+ */
+ private Connection connection;
+
+ /**
+ * A DelegatingConnection used to create a PoolablePreparedStatementStub.
+ */
+ private final DelegatingConnection<?> delegatingConnection;
+
+ /**
+ * The JDBC database logical connection.
+ */
+ private Connection logicalConnection;
+
+ /**
+ * ConnectionEventListeners.
+ */
+ private final List<ConnectionEventListener> eventListeners;
+
+ /**
+ * StatementEventListeners.
+ */
+ private final List<StatementEventListener> statementEventListeners = Collections.synchronizedList(new ArrayList<>());
+
+ /**
+ * Flag set to true, once {@link #close()} is called.
+ */
+ private boolean closed;
+
+ /** My pool of {@link PreparedStatement}s. */
+ private KeyedObjectPool<PStmtKey, DelegatingPreparedStatement> pStmtPool;
+
+ /**
+ * Controls access to the underlying connection.
+ */
+ private boolean accessToUnderlyingConnectionAllowed;
+
+ /**
+ * Wraps the real connection.
+ *
+ * @param connection
+ * the connection to be wrapped.
+ */
+ PooledConnectionImpl(final Connection connection) {
+ this.connection = connection;
+ if (connection instanceof DelegatingConnection) {
+ this.delegatingConnection = (DelegatingConnection<?>) connection;
+ } else {
+ this.delegatingConnection = new DelegatingConnection<>(connection);
+ }
+ eventListeners = Collections.synchronizedList(new ArrayList<>());
+ closed = false;
+ }
+
+ /**
+ * My {@link KeyedPooledObjectFactory} method for activating {@link PreparedStatement}s.
+ *
+ * @param pooledObject Activates the underlying object.
+ */
+ @Override
+ public void activateObject(final PStmtKey key, final PooledObject<DelegatingPreparedStatement> pooledObject)
+ throws SQLException {
+ pooledObject.getObject().activate();
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public void addConnectionEventListener(final ConnectionEventListener listener) {
+ if (!eventListeners.contains(listener)) {
+ eventListeners.add(listener);
+ }
+ }
+
+ @Override
+ public void addStatementEventListener(final StatementEventListener listener) {
+ if (!statementEventListeners.contains(listener)) {
+ statementEventListeners.add(listener);
+ }
+ }
+ /* JDBC_4_ANT_KEY_END */
+
+ /**
+ * Throws an SQLException, if isClosed is true
+ */
+ private void assertOpen() throws SQLException {
+ if (closed) {
+ throw new SQLException(CLOSED);
+ }
+ }
+
+ /**
+ * Closes the physical connection and marks this {@code PooledConnection} so that it may not be used to
+ * generate any more logical {@code Connection}s.
+ *
+ * @throws SQLException
+ * Thrown when an error occurs or the connection is already closed.
+ */
+ @Override
+ public void close() throws SQLException {
+ assertOpen();
+ closed = true;
+ try {
+ if (pStmtPool != null) {
+ try {
+ pStmtPool.close();
+ } finally {
+ pStmtPool = null;
+ }
+ }
+ } catch (final RuntimeException e) {
+ throw e;
+ } catch (final Exception e) {
+ throw new SQLException("Cannot close connection (return to pool failed)", e);
+ } finally {
+ try {
+ connection.close();
+ } finally {
+ connection = null;
+ }
+ }
+ }
+
+ /**
+ * Creates a {@link PStmtKey} for the given arguments.
+ *
+ * @param sql
+ * The SQL statement.
+ * @return a {@link PStmtKey} for the given arguments.
+ */
+ protected PStmtKey createKey(final String sql) {
+ return new PStmtKey(normalizeSQL(sql), getCatalogOrNull(), getSchemaOrNull());
+ }
+
+ /**
+ * Creates a {@link PStmtKey} for the given arguments.
+ *
+ * @param sql
+ * The SQL statement.
+ * @param autoGeneratedKeys
+ * A flag indicating whether auto-generated keys should be returned; one of
+ * {@code Statement.RETURN_GENERATED_KEYS} or {@code Statement.NO_GENERATED_KEYS}.
+ * @return a key to uniquely identify a prepared statement.
+ */
+ protected PStmtKey createKey(final String sql, final int autoGeneratedKeys) {
+ return new PStmtKey(normalizeSQL(sql), getCatalogOrNull(), getSchemaOrNull(), autoGeneratedKeys);
+ }
+
+ /**
+ * Creates a {@link PStmtKey} for the given arguments.
+ *
+ * @param sql
+ * The SQL statement.
+ * @param resultSetType
+ * A result set type; one of {@code ResultSet.TYPE_FORWARD_ONLY},
+ * {@code ResultSet.TYPE_SCROLL_INSENSITIVE}, or {@code ResultSet.TYPE_SCROLL_SENSITIVE}.
+ * @param resultSetConcurrency
+ * A concurrency type; one of {@code ResultSet.CONCUR_READ_ONLY} or
+ * {@code ResultSet.CONCUR_UPDATABLE}.
+ * @return a key to uniquely identify a prepared statement.
+ */
+ protected PStmtKey createKey(final String sql, final int resultSetType, final int resultSetConcurrency) {
+ return new PStmtKey(normalizeSQL(sql), getCatalogOrNull(), getSchemaOrNull(), resultSetType,
+ resultSetConcurrency);
+ }
+
+ /**
+ * Creates a {@link PStmtKey} for the given arguments.
+ *
+ * @param sql
+ * The SQL statement.
+ * @param resultSetType
+ * a result set type; one of {@code ResultSet.TYPE_FORWARD_ONLY},
+ * {@code ResultSet.TYPE_SCROLL_INSENSITIVE}, or {@code ResultSet.TYPE_SCROLL_SENSITIVE}.
+ * @param resultSetConcurrency
+ * A concurrency type; one of {@code ResultSet.CONCUR_READ_ONLY} or
+ * {@code ResultSet.CONCUR_UPDATABLE}
+ * @param resultSetHoldability
+ * One of the following {@code ResultSet} constants: {@code ResultSet.HOLD_CURSORS_OVER_COMMIT}
+ * or {@code ResultSet.CLOSE_CURSORS_AT_COMMIT}.
+ * @return a key to uniquely identify a prepared statement.
+ */
+ protected PStmtKey createKey(final String sql, final int resultSetType, final int resultSetConcurrency,
+ final int resultSetHoldability) {
+ return new PStmtKey(normalizeSQL(sql), getCatalogOrNull(), getSchemaOrNull(), resultSetType,
+ resultSetConcurrency, resultSetHoldability);
+ }
+
+ /**
+ * Creates a {@link PStmtKey} for the given arguments.
+ *
+ * @param sql
+ * The SQL statement.
+ * @param resultSetType
+ * a result set type; one of {@code ResultSet.TYPE_FORWARD_ONLY},
+ * {@code ResultSet.TYPE_SCROLL_INSENSITIVE}, or {@code ResultSet.TYPE_SCROLL_SENSITIVE}
+ * @param resultSetConcurrency
+ * A concurrency type; one of {@code ResultSet.CONCUR_READ_ONLY} or
+ * {@code ResultSet.CONCUR_UPDATABLE}.
+ * @param resultSetHoldability
+ * One of the following {@code ResultSet} constants: {@code ResultSet.HOLD_CURSORS_OVER_COMMIT}
+ * or {@code ResultSet.CLOSE_CURSORS_AT_COMMIT}.
+ * @param statementType
+ * The SQL statement type, prepared or callable.
+ * @return a key to uniquely identify a prepared statement.
+ * @since 2.4.0
+ */
+ protected PStmtKey createKey(final String sql, final int resultSetType, final int resultSetConcurrency,
+ final int resultSetHoldability, final StatementType statementType) {
+ return new PStmtKey(normalizeSQL(sql), getCatalogOrNull(), getSchemaOrNull(), resultSetType,
+ resultSetConcurrency, resultSetHoldability, statementType);
+ }
+
+ /**
+ * Creates a {@link PStmtKey} for the given arguments.
+ *
+ * @param sql
+ * The SQL statement.
+ * @param resultSetType
+ * A result set type; one of {@code ResultSet.TYPE_FORWARD_ONLY},
+ * {@code ResultSet.TYPE_SCROLL_INSENSITIVE}, or {@code ResultSet.TYPE_SCROLL_SENSITIVE}.
+ * @param resultSetConcurrency
+ * A concurrency type; one of {@code ResultSet.CONCUR_READ_ONLY} or
+ * {@code ResultSet.CONCUR_UPDATABLE}.
+ * @param statementType
+ * The SQL statement type, prepared or callable.
+ * @return a key to uniquely identify a prepared statement.
+ * @since 2.4.0
+ */
+ protected PStmtKey createKey(final String sql, final int resultSetType, final int resultSetConcurrency,
+ final StatementType statementType) {
+ return new PStmtKey(normalizeSQL(sql), getCatalogOrNull(), getSchemaOrNull(), resultSetType,
+ resultSetConcurrency, statementType);
+ }
+
+ /**
+ * Creates a {@link PStmtKey} for the given arguments.
+ *
+ * @param sql
+ * The SQL statement.
+ * @param columnIndexes
+ * An array of column indexes indicating the columns that should be returned from the inserted row or
+ * rows.
+ * @return a key to uniquely identify a prepared statement.
+ */
+ protected PStmtKey createKey(final String sql, final int[] columnIndexes) {
+ return new PStmtKey(normalizeSQL(sql), getCatalogOrNull(), getSchemaOrNull(), columnIndexes);
+ }
+
+ /**
+ * Creates a {@link PStmtKey} for the given arguments.
+ *
+ * @param sql
+ * The SQL statement.
+ * @param statementType
+ * The SQL statement type, prepared or callable.
+ * @return a key to uniquely identify a prepared statement.
+ */
+ protected PStmtKey createKey(final String sql, final StatementType statementType) {
+ return new PStmtKey(normalizeSQL(sql), getCatalogOrNull(), getSchemaOrNull(), statementType);
+ }
+
+ /**
+ * Creates a {@link PStmtKey} for the given arguments.
+ *
+ * @param sql
+ * The SQL statement.
+ * @param columnNames
+ * An array of column names indicating the columns that should be returned from the inserted row or rows.
+ * @return a key to uniquely identify a prepared statement.
+ */
+ protected PStmtKey createKey(final String sql, final String[] columnNames) {
+ return new PStmtKey(normalizeSQL(sql), getCatalogOrNull(), getSchemaOrNull(), columnNames);
+ }
+
+ /**
+ * My {@link KeyedPooledObjectFactory} method for destroying {@link PreparedStatement}s.
+ *
+ * @param key
+ * ignored
+ * @param pooledObject
+ * the wrapped {@link PreparedStatement} to be destroyed.
+ */
+ @Override
+ public void destroyObject(final PStmtKey key, final PooledObject<DelegatingPreparedStatement> pooledObject)
+ throws SQLException {
+ pooledObject.getObject().getInnermostDelegate().close();
+ }
+
+ /**
+ * Closes the physical connection and checks that the logical connection was closed as well.
+ */
+ @Override
+ protected void finalize() throws Throwable {
+ // Closing the Connection ensures that if anyone tries to use it,
+ // an error will occur.
+ try {
+ connection.close();
+ } catch (final Exception ignored) {
+ // ignore
+ }
+
+ // make sure the last connection is marked as closed
+ if (logicalConnection != null && !logicalConnection.isClosed()) {
+ throw new SQLException("PooledConnection was gc'ed, without its last Connection being closed.");
+ }
+ }
+
+ private String getCatalogOrNull() {
+ try {
+ return connection == null ? null : connection.getCatalog();
+ } catch (final SQLException e) {
+ return null;
+ }
+ }
+
+ /**
+ * Returns a JDBC connection.
+ *
+ * @return The database connection.
+ * @throws SQLException
+ * if the connection is not open or the previous logical connection is still open
+ */
+ @Override
+ public Connection getConnection() throws SQLException {
+ assertOpen();
+ // make sure the last connection is marked as closed
+ if (logicalConnection != null && !logicalConnection.isClosed()) {
+ // should notify pool of error so the pooled connection can
+ // be removed !FIXME!
+ throw new SQLException("PooledConnection was reused, without its previous Connection being closed.");
+ }
+
+ // the spec requires that this return a new Connection instance.
+ logicalConnection = new ConnectionImpl(this, connection, isAccessToUnderlyingConnectionAllowed());
+ return logicalConnection;
+ }
+
+ private String getSchemaOrNull() {
+ try {
+ return connection == null ? null : Jdbc41Bridge.getSchema(connection);
+ } catch (final SQLException e) {
+ return null;
+ }
+ }
+
+ /**
+ * Returns the value of the accessToUnderlyingConnectionAllowed property.
+ *
+ * @return true if access to the underlying is allowed, false otherwise.
+ */
+ public synchronized boolean isAccessToUnderlyingConnectionAllowed() {
+ return this.accessToUnderlyingConnectionAllowed;
+ }
+
+ /**
+ * My {@link KeyedPooledObjectFactory} method for creating {@link PreparedStatement}s.
+ *
+ * @param key
+ * The key for the {@link PreparedStatement} to be created.
+ */
+ @SuppressWarnings("resource")
+ @Override
+ public PooledObject<DelegatingPreparedStatement> makeObject(final PStmtKey key) throws SQLException {
+ if (null == key) {
+ throw new IllegalArgumentException("Prepared statement key is null or invalid.");
+ }
+ if (key.getStmtType() == StatementType.PREPARED_STATEMENT) {
+ final PreparedStatement statement = (PreparedStatement) key.createStatement(connection);
+ @SuppressWarnings({"rawtypes", "unchecked" }) // Unable to find way to avoid this
+ final PoolablePreparedStatement pps = new PoolablePreparedStatement(statement, key, pStmtPool,
+ delegatingConnection);
+ return new DefaultPooledObject<>(pps);
+ }
+ final CallableStatement statement = (CallableStatement) key.createStatement(connection);
+ @SuppressWarnings("unchecked")
+ final PoolableCallableStatement pcs = new PoolableCallableStatement(statement, key, pStmtPool,
+ (DelegatingConnection<Connection>) delegatingConnection);
+ return new DefaultPooledObject<>(pcs);
+ }
+
+ /**
+ * Normalizes the given SQL statement, producing a canonical form that is semantically equivalent to the original.
+ * @param sql
+ * The SQL statement.
+ * @return the normalized SQL statement.
+ */
+ protected String normalizeSQL(final String sql) {
+ return sql.trim();
+ }
+
+ /**
+ * Sends a connectionClosed event.
+ */
+ void notifyListeners() {
+ final ConnectionEvent event = new ConnectionEvent(this);
+ final Object[] listeners = eventListeners.toArray();
+ for (final Object listener : listeners) {
+ ((ConnectionEventListener) listener).connectionClosed(event);
+ }
+ }
+
+ /**
+ * My {@link KeyedPooledObjectFactory} method for passivating {@link PreparedStatement}s. Currently invokes
+ * {@link PreparedStatement#clearParameters}.
+ *
+ * @param key
+ * ignored
+ * @param pooledObject
+ * a wrapped {@link PreparedStatement}
+ */
+ @Override
+ public void passivateObject(final PStmtKey key, final PooledObject<DelegatingPreparedStatement> pooledObject)
+ throws SQLException {
+ @SuppressWarnings("resource")
+ final DelegatingPreparedStatement dps = pooledObject.getObject();
+ dps.clearParameters();
+ dps.passivate();
+ }
+
+ /**
+ * Creates or obtains a {@link CallableStatement} from my pool.
+ *
+ * @param sql
+ * an SQL statement that may contain one or more '?' parameter placeholders. Typically this statement is
+ * specified using JDBC call escape syntax.
+ * @return a default {@code CallableStatement} object containing the pre-compiled SQL statement.
+ * @throws SQLException
+ * Thrown if a database access error occurs or this method is called on a closed connection.
+ * @since 2.4.0
+ */
+ CallableStatement prepareCall(final String sql) throws SQLException {
+ if (pStmtPool == null) {
+ return connection.prepareCall(sql);
+ }
+ try {
+ return (CallableStatement) pStmtPool.borrowObject(createKey(sql, StatementType.CALLABLE_STATEMENT));
+ } catch (final RuntimeException e) {
+ throw e;
+ } catch (final Exception e) {
+ throw new SQLException("Borrow prepareCall from pool failed", e);
+ }
+ }
+
+ /**
+ * Creates or obtains a {@link CallableStatement} from my pool.
+ *
+ * @param sql
+ * a {@code String} object that is the SQL statement to be sent to the database; may contain on or
+ * more '?' parameters.
+ * @param resultSetType
+ * a result set type; one of {@code ResultSet.TYPE_FORWARD_ONLY},
+ * {@code ResultSet.TYPE_SCROLL_INSENSITIVE}, or {@code ResultSet.TYPE_SCROLL_SENSITIVE}.
+ * @param resultSetConcurrency
+ * a concurrency type; one of {@code ResultSet.CONCUR_READ_ONLY} or
+ * {@code ResultSet.CONCUR_UPDATABLE}.
+ * @return a {@code CallableStatement} object containing the pre-compiled SQL statement that will produce
+ * {@code ResultSet} objects with the given type and concurrency.
+ * @throws SQLException
+ * Thrown if a database access error occurs, this method is called on a closed connection or the given
+ * parameters are not {@code ResultSet} constants indicating type and concurrency.
+ * @since 2.4.0
+ */
+ CallableStatement prepareCall(final String sql, final int resultSetType, final int resultSetConcurrency)
+ throws SQLException {
+ if (pStmtPool == null) {
+ return connection.prepareCall(sql, resultSetType, resultSetConcurrency);
+ }
+ try {
+ return (CallableStatement) pStmtPool.borrowObject(
+ createKey(sql, resultSetType, resultSetConcurrency, StatementType.CALLABLE_STATEMENT));
+ } catch (final RuntimeException e) {
+ throw e;
+ } catch (final Exception e) {
+ throw new SQLException("Borrow prepareCall from pool failed", e);
+ }
+ }
+
+ /**
+ * Creates or obtains a {@link CallableStatement} from my pool.
+ *
+ * @param sql
+ * a {@code String} object that is the SQL statement to be sent to the database; may contain on or
+ * more '?' parameters.
+ * @param resultSetType
+ * one of the following {@code ResultSet} constants: {@code ResultSet.TYPE_FORWARD_ONLY},
+ * {@code ResultSet.TYPE_SCROLL_INSENSITIVE}, or {@code ResultSet.TYPE_SCROLL_SENSITIVE}.
+ * @param resultSetConcurrency
+ * one of the following {@code ResultSet} constants: {@code ResultSet.CONCUR_READ_ONLY} or
+ * {@code ResultSet.CONCUR_UPDATABLE}.
+ * @param resultSetHoldability
+ * one of the following {@code ResultSet} constants: {@code ResultSet.HOLD_CURSORS_OVER_COMMIT}
+ * or {@code ResultSet.CLOSE_CURSORS_AT_COMMIT}.
+ * @return a new {@code CallableStatement} object, containing the pre-compiled SQL statement, that will
+ * generate {@code ResultSet} objects with the given type, concurrency, and holdability.
+ * @throws SQLException
+ * Thrown if a database access error occurs, this method is called on a closed connection or the given
+ * parameters are not {@code ResultSet} constants indicating type, concurrency, and holdability.
+ * @since 2.4.0
+ */
+ CallableStatement prepareCall(final String sql, final int resultSetType, final int resultSetConcurrency,
+ final int resultSetHoldability) throws SQLException {
+ if (pStmtPool == null) {
+ return connection.prepareCall(sql, resultSetType, resultSetConcurrency, resultSetHoldability);
+ }
+ try {
+ return (CallableStatement) pStmtPool.borrowObject(createKey(sql, resultSetType, resultSetConcurrency,
+ resultSetHoldability, StatementType.CALLABLE_STATEMENT));
+ } catch (final RuntimeException e) {
+ throw e;
+ } catch (final Exception e) {
+ throw new SQLException("Borrow prepareCall from pool failed", e);
+ }
+ }
+
+ /**
+ * Creates or obtains a {@link PreparedStatement} from my pool.
+ *
+ * @param sql the SQL statement.
+ * @return a {@link PoolablePreparedStatement}
+ * @throws SQLException Thrown if a database access error occurs, this method is called on a closed connection, or
+ * the borrow failed.
+ */
+ PreparedStatement prepareStatement(final String sql) throws SQLException {
+ if (pStmtPool == null) {
+ return connection.prepareStatement(sql);
+ }
+ try {
+ return pStmtPool.borrowObject(createKey(sql));
+ } catch (final RuntimeException e) {
+ throw e;
+ } catch (final Exception e) {
+ throw new SQLException("Borrow prepareStatement from pool failed", e);
+ }
+ }
+
+ /**
+ * Creates or obtains a {@link PreparedStatement} from my pool.
+ *
+ * @param sql
+ * an SQL statement that may contain one or more '?' IN parameter placeholders.
+ * @param autoGeneratedKeys
+ * a flag indicating whether auto-generated keys should be returned; one of
+ * {@code Statement.RETURN_GENERATED_KEYS} or {@code Statement.NO_GENERATED_KEYS}.
+ * @return a {@link PoolablePreparedStatement}
+ * @throws SQLException Thrown if a database access error occurs, this method is called on a closed connection, or
+ * the borrow failed.
+ * @see Connection#prepareStatement(String, int)
+ */
+ PreparedStatement prepareStatement(final String sql, final int autoGeneratedKeys) throws SQLException {
+ if (pStmtPool == null) {
+ return connection.prepareStatement(sql, autoGeneratedKeys);
+ }
+ try {
+ return pStmtPool.borrowObject(createKey(sql, autoGeneratedKeys));
+ } catch (final RuntimeException e) {
+ throw e;
+ } catch (final Exception e) {
+ throw new SQLException("Borrow prepareStatement from pool failed", e);
+ }
+ }
+
+ /**
+ * Creates or obtains a {@link PreparedStatement} from my pool.
+ *
+ * @param sql
+ * a {@code String} object that is the SQL statement to be sent to the database; may contain one or
+ * more '?' IN parameters.
+ * @param resultSetType
+ * a result set type; one of {@code ResultSet.TYPE_FORWARD_ONLY},
+ * {@code ResultSet.TYPE_SCROLL_INSENSITIVE}, or {@code ResultSet.TYPE_SCROLL_SENSITIVE}.
+ * @param resultSetConcurrency
+ * a concurrency type; one of {@code ResultSet.CONCUR_READ_ONLY} or
+ * {@code ResultSet.CONCUR_UPDATABLE}.
+ *
+ * @return a {@link PoolablePreparedStatement}.
+ * @throws SQLException Thrown if a database access error occurs, this method is called on a closed connection, or
+ * the borrow failed.
+ * @see Connection#prepareStatement(String, int, int)
+ */
+ PreparedStatement prepareStatement(final String sql, final int resultSetType, final int resultSetConcurrency)
+ throws SQLException {
+ if (pStmtPool == null) {
+ return connection.prepareStatement(sql, resultSetType, resultSetConcurrency);
+ }
+ try {
+ return pStmtPool.borrowObject(createKey(sql, resultSetType, resultSetConcurrency));
+ } catch (final RuntimeException e) {
+ throw e;
+ } catch (final Exception e) {
+ throw new SQLException("Borrow prepareStatement from pool failed", e);
+ }
+ }
+
+ PreparedStatement prepareStatement(final String sql, final int resultSetType, final int resultSetConcurrency,
+ final int resultSetHoldability) throws SQLException {
+ if (pStmtPool == null) {
+ return connection.prepareStatement(sql, resultSetType, resultSetConcurrency, resultSetHoldability);
+ }
+ try {
+ return pStmtPool.borrowObject(createKey(sql, resultSetType, resultSetConcurrency, resultSetHoldability));
+ } catch (final RuntimeException e) {
+ throw e;
+ } catch (final Exception e) {
+ throw new SQLException("Borrow prepareStatement from pool failed", e);
+ }
+ }
+
+ PreparedStatement prepareStatement(final String sql, final int[] columnIndexes) throws SQLException {
+ if (pStmtPool == null) {
+ return connection.prepareStatement(sql, columnIndexes);
+ }
+ try {
+ return pStmtPool.borrowObject(createKey(sql, columnIndexes));
+ } catch (final RuntimeException e) {
+ throw e;
+ } catch (final Exception e) {
+ throw new SQLException("Borrow prepareStatement from pool failed", e);
+ }
+ }
+
+ PreparedStatement prepareStatement(final String sql, final String[] columnNames) throws SQLException {
+ if (pStmtPool == null) {
+ return connection.prepareStatement(sql, columnNames);
+ }
+ try {
+ return pStmtPool.borrowObject(createKey(sql, columnNames));
+ } catch (final RuntimeException e) {
+ throw e;
+ } catch (final Exception e) {
+ throw new SQLException("Borrow prepareStatement from pool failed", e);
+ }
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public void removeConnectionEventListener(final ConnectionEventListener listener) {
+ eventListeners.remove(listener);
+ }
+
+ @Override
+ public void removeStatementEventListener(final StatementEventListener listener) {
+ statementEventListeners.remove(listener);
+ }
+ /* JDBC_4_ANT_KEY_END */
+
+ /**
+ * Sets the value of the accessToUnderlyingConnectionAllowed property. It controls if the PoolGuard allows access to
+ * the underlying connection. (Default: false.)
+ *
+ * @param allow
+ * Access to the underlying connection is granted when true.
+ */
+ public synchronized void setAccessToUnderlyingConnectionAllowed(final boolean allow) {
+ this.accessToUnderlyingConnectionAllowed = allow;
+ }
+
+ public void setStatementPool(final KeyedObjectPool<PStmtKey, DelegatingPreparedStatement> statementPool) {
+ pStmtPool = statementPool;
+ }
+
+ /**
+ * @since 2.6.0
+ */
+ @Override
+ public synchronized String toString() {
+ final StringBuilder builder = new StringBuilder(super.toString());
+ builder.append("[connection=");
+ builder.append(connection);
+ builder.append(", delegatingConnection=");
+ builder.append(delegatingConnection);
+ builder.append(", logicalConnection=");
+ builder.append(logicalConnection);
+ builder.append(", eventListeners=");
+ builder.append(eventListeners);
+ builder.append(", statementEventListeners=");
+ builder.append(statementEventListeners);
+ builder.append(", closed=");
+ builder.append(closed);
+ builder.append(", pStmtPool=");
+ builder.append(pStmtPool);
+ builder.append(", accessToUnderlyingConnectionAllowed=");
+ builder.append(accessToUnderlyingConnectionAllowed);
+ builder.append("]");
+ return builder.toString();
+ }
+
+ /**
+ * My {@link KeyedPooledObjectFactory} method for validating {@link PreparedStatement}s.
+ *
+ * @param key
+ * Ignored.
+ * @param pooledObject
+ * Ignored.
+ * @return {@code true}
+ */
+ @Override
+ public boolean validateObject(final PStmtKey key, final PooledObject<DelegatingPreparedStatement> pooledObject) {
+ return true;
+ }
+}
diff --git a/src/main/java/org/apache/commons/dbcp2/datasources/CPDSConnectionFactory.java b/src/main/java/org/apache/commons/dbcp2/datasources/CPDSConnectionFactory.java
index d7f3b37d..9962fe98 100644
--- a/src/main/java/org/apache/commons/dbcp2/datasources/CPDSConnectionFactory.java
+++ b/src/main/java/org/apache/commons/dbcp2/datasources/CPDSConnectionFactory.java
@@ -1,498 +1,498 @@
-/*
- * 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.commons.dbcp2.datasources;
-
-import java.sql.Connection;
-import java.sql.ResultSet;
-import java.sql.SQLException;
-import java.sql.Statement;
-import java.time.Duration;
-import java.time.Instant;
-import java.util.Collections;
-import java.util.Map;
-import java.util.Set;
-import java.util.concurrent.ConcurrentHashMap;
-
-import javax.sql.ConnectionEvent;
-import javax.sql.ConnectionEventListener;
-import javax.sql.ConnectionPoolDataSource;
-import javax.sql.PooledConnection;
-
-import org.apache.commons.dbcp2.Utils;
-import org.apache.commons.pool2.ObjectPool;
-import org.apache.commons.pool2.PooledObject;
-import org.apache.commons.pool2.PooledObjectFactory;
-import org.apache.commons.pool2.impl.DefaultPooledObject;
-
-/**
- * A {@link PooledObjectFactory} that creates {@link org.apache.commons.dbcp2.PoolableConnection PoolableConnection}s.
- *
- * @since 2.0
- */
-class CPDSConnectionFactory
- implements PooledObjectFactory<PooledConnectionAndInfo>, ConnectionEventListener, PooledConnectionManager {
-
- private static final String NO_KEY_MESSAGE = "close() was called on a Connection, but I have no record of the underlying PooledConnection.";
-
- private final ConnectionPoolDataSource cpds;
- private final String validationQuery;
- private final Duration validationQueryTimeoutDuration;
- private final boolean rollbackAfterValidation;
- private ObjectPool<PooledConnectionAndInfo> pool;
- private UserPassKey userPassKey;
- private Duration maxConnDuration = Duration.ofMillis(-1);
-
- /**
- * Map of PooledConnections for which close events are ignored. Connections are muted when they are being validated.
- */
- private final Set<PooledConnection> validatingSet = Collections.newSetFromMap(new ConcurrentHashMap<>());
-
- /**
- * Map of PooledConnectionAndInfo instances
- */
- private final Map<PooledConnection, PooledConnectionAndInfo> pcMap = new ConcurrentHashMap<>();
-
- /**
- * Creates a new {@code PoolableConnectionFactory}.
- *
- * @param cpds
- * the ConnectionPoolDataSource from which to obtain PooledConnection's
- * @param validationQuery
- * a query to use to {@link #validateObject validate} {@link Connection}s. Should return at least one
- * row. May be {@code null} in which case {@link Connection#isValid(int)} will be used to validate
- * connections.
- * @param validationQueryTimeoutDuration
- * Timeout Duration before validation fails
- * @param rollbackAfterValidation
- * whether a rollback should be issued after {@link #validateObject validating} {@link Connection}s.
- * @param userName
- * The user name to use to create connections
- * @param userPassword
- * The password to use to create connections
- * @since 2.10.0
- */
- public CPDSConnectionFactory(final ConnectionPoolDataSource cpds, final String validationQuery,
- final Duration validationQueryTimeoutDuration, final boolean rollbackAfterValidation, final String userName,
- final char[] userPassword) {
- this.cpds = cpds;
- this.validationQuery = validationQuery;
- this.validationQueryTimeoutDuration = validationQueryTimeoutDuration;
- this.userPassKey = new UserPassKey(userName, userPassword);
- this.rollbackAfterValidation = rollbackAfterValidation;
- }
-
- /**
- * Creates a new {@code PoolableConnectionFactory}.
- *
- * @param cpds
- * the ConnectionPoolDataSource from which to obtain PooledConnection's
- * @param validationQuery
- * a query to use to {@link #validateObject validate} {@link Connection}s. Should return at least one
- * row. May be {@code null} in which case {@link Connection#isValid(int)} will be used to validate
- * connections.
- * @param validationQueryTimeoutDuration
- * Timeout in seconds before validation fails
- * @param rollbackAfterValidation
- * whether a rollback should be issued after {@link #validateObject validating} {@link Connection}s.
- * @param userName
- * The user name to use to create connections
- * @param userPassword
- * The password to use to create connections
- * @since 2.10.0
- */
- public CPDSConnectionFactory(final ConnectionPoolDataSource cpds, final String validationQuery, final Duration validationQueryTimeoutDuration,
- final boolean rollbackAfterValidation, final String userName, final String userPassword) {
- this(cpds, validationQuery, validationQueryTimeoutDuration, rollbackAfterValidation, userName, Utils.toCharArray(userPassword));
- }
-
- /**
- * Creates a new {@code PoolableConnectionFactory}.
- *
- * @param cpds
- * the ConnectionPoolDataSource from which to obtain PooledConnection's
- * @param validationQuery
- * a query to use to {@link #validateObject validate} {@link Connection}s. Should return at least one
- * row. May be {@code null} in which case {@link Connection#isValid(int)} will be used to validate
- * connections.
- * @param validationQueryTimeoutSeconds
- * Timeout in seconds before validation fails
- * @param rollbackAfterValidation
- * whether a rollback should be issued after {@link #validateObject validating} {@link Connection}s.
- * @param userName
- * The user name to use to create connections
- * @param userPassword
- * The password to use to create connections
- * @since 2.4.0
- * @deprecated Use {@link #CPDSConnectionFactory(ConnectionPoolDataSource, String, Duration, boolean, String, char[])}.
- */
- @Deprecated
- public CPDSConnectionFactory(final ConnectionPoolDataSource cpds, final String validationQuery,
- final int validationQueryTimeoutSeconds, final boolean rollbackAfterValidation, final String userName,
- final char[] userPassword) {
- this.cpds = cpds;
- this.validationQuery = validationQuery;
- this.validationQueryTimeoutDuration = Duration.ofSeconds(validationQueryTimeoutSeconds);
- this.userPassKey = new UserPassKey(userName, userPassword);
- this.rollbackAfterValidation = rollbackAfterValidation;
- }
-
- /**
- * Creates a new {@code PoolableConnectionFactory}.
- *
- * @param cpds
- * the ConnectionPoolDataSource from which to obtain PooledConnection's
- * @param validationQuery
- * a query to use to {@link #validateObject validate} {@link Connection}s. Should return at least one
- * row. May be {@code null} in which case {@link Connection#isValid(int)} will be used to validate
- * connections.
- * @param validationQueryTimeoutSeconds
- * Timeout in seconds before validation fails
- * @param rollbackAfterValidation
- * whether a rollback should be issued after {@link #validateObject validating} {@link Connection}s.
- * @param userName
- * The user name to use to create connections
- * @param userPassword
- * The password to use to create connections
- * @deprecated Use {@link #CPDSConnectionFactory(ConnectionPoolDataSource, String, Duration, boolean, String, String)}.
- */
- @Deprecated
- public CPDSConnectionFactory(final ConnectionPoolDataSource cpds, final String validationQuery, final int validationQueryTimeoutSeconds,
- final boolean rollbackAfterValidation, final String userName, final String userPassword) {
- this(cpds, validationQuery, validationQueryTimeoutSeconds, rollbackAfterValidation, userName, Utils.toCharArray(userPassword));
- }
-
- @Override
- public void activateObject(final PooledObject<PooledConnectionAndInfo> p) throws Exception {
- validateLifetime(p);
- }
-
- /**
- * Verifies that the user name matches the user whose connections are being managed by this factory and closes the
- * pool if this is the case; otherwise does nothing.
- */
- @Override
- public void closePool(final String userName) throws SQLException {
- synchronized (this) {
- if (userName == null || !userName.equals(this.userPassKey.getUserName())) {
- return;
- }
- }
- try {
- pool.close();
- } catch (final Exception ex) {
- throw new SQLException("Error closing connection pool", ex);
- }
- }
-
- /**
- * This will be called if the Connection returned by the getConnection method came from a PooledConnection, and the
- * user calls the close() method of this connection object. What we need to do here is to release this
- * PooledConnection from our pool...
- */
- @Override
- public void connectionClosed(final ConnectionEvent event) {
- final PooledConnection pc = (PooledConnection) event.getSource();
- // if this event occurred because we were validating, ignore it
- // otherwise return the connection to the pool.
- if (!validatingSet.contains(pc)) {
- final PooledConnectionAndInfo pci = pcMap.get(pc);
- if (pci == null) {
- throw new IllegalStateException(NO_KEY_MESSAGE);
- }
-
- try {
- pool.returnObject(pci);
- } catch (final Exception e) {
- System.err.println("CLOSING DOWN CONNECTION AS IT COULD " + "NOT BE RETURNED TO THE POOL");
- pc.removeConnectionEventListener(this);
- try {
- doDestroyObject(pci);
- } catch (final Exception e2) {
- System.err.println("EXCEPTION WHILE DESTROYING OBJECT " + pci);
- e2.printStackTrace();
- }
- }
- }
- }
-
- /**
- * If a fatal error occurs, close the underlying physical connection so as not to be returned in the future
- */
- @Override
- public void connectionErrorOccurred(final ConnectionEvent event) {
- final PooledConnection pc = (PooledConnection) event.getSource();
- if (null != event.getSQLException()) {
- System.err.println("CLOSING DOWN CONNECTION DUE TO INTERNAL ERROR (" + event.getSQLException() + ")");
- }
- pc.removeConnectionEventListener(this);
-
- final PooledConnectionAndInfo pci = pcMap.get(pc);
- if (pci == null) {
- throw new IllegalStateException(NO_KEY_MESSAGE);
- }
- try {
- pool.invalidateObject(pci);
- } catch (final Exception e) {
- System.err.println("EXCEPTION WHILE DESTROYING OBJECT " + pci);
- e.printStackTrace();
- }
- }
-
- /**
- * Closes the PooledConnection and stops listening for events from it.
- */
- @Override
- public void destroyObject(final PooledObject<PooledConnectionAndInfo> p) throws Exception {
- doDestroyObject(p.getObject());
- }
-
- private void doDestroyObject(final PooledConnectionAndInfo pci) throws Exception {
- final PooledConnection pc = pci.getPooledConnection();
- pc.removeConnectionEventListener(this);
- pcMap.remove(pc);
- pc.close();
- }
-
- /**
- * (Testing API) Gets the value of password for the default user.
- *
- * @return value of password.
- */
- char[] getPasswordCharArray() {
- return userPassKey.getPasswordCharArray();
- }
-
- /**
- * Returns the object pool used to pool connections created by this factory.
- *
- * @return ObjectPool managing pooled connections
- */
- public ObjectPool<PooledConnectionAndInfo> getPool() {
- return pool;
- }
-
- /**
- * Invalidates the PooledConnection in the pool. The CPDSConnectionFactory closes the connection and pool counters
- * are updated appropriately. Also closes the pool. This ensures that all idle connections are closed and
- * connections that are checked out are closed on return.
- */
- @Override
- public void invalidate(final PooledConnection pc) throws SQLException {
- final PooledConnectionAndInfo pci = pcMap.get(pc);
- if (pci == null) {
- throw new IllegalStateException(NO_KEY_MESSAGE);
- }
- try {
- pool.invalidateObject(pci); // Destroy instance and update pool counters
- pool.close(); // Clear any other instances in this pool and kill others as they come back
- } catch (final Exception ex) {
- throw new SQLException("Error invalidating connection", ex);
- }
- }
-
- // ***********************************************************************
- // java.sql.ConnectionEventListener implementation
- // ***********************************************************************
-
- @Override
- public synchronized PooledObject<PooledConnectionAndInfo> makeObject() {
- final PooledConnectionAndInfo pci;
- try {
- PooledConnection pc = null;
- if (userPassKey.getUserName() == null) {
- pc = cpds.getPooledConnection();
- } else {
- pc = cpds.getPooledConnection(userPassKey.getUserName(), userPassKey.getPassword());
- }
-
- if (pc == null) {
- throw new IllegalStateException("Connection pool data source returned null from getPooledConnection");
- }
-
- // should we add this object as a listener or the pool.
- // consider the validateObject method in decision
- pc.addConnectionEventListener(this);
- pci = new PooledConnectionAndInfo(pc, userPassKey);
- pcMap.put(pc, pci);
- } catch (final SQLException e) {
- throw new RuntimeException(e.getMessage());
- }
- return new DefaultPooledObject<>(pci);
- }
-
- @Override
- public void passivateObject(final PooledObject<PooledConnectionAndInfo> p) throws Exception {
- validateLifetime(p);
- }
-
- /**
- * Sets the maximum Duration of a connection after which the connection will always fail activation,
- * passivation and validation.
- *
- * @param maxConnDuration
- * A value of zero or less indicates an infinite lifetime. The default value is -1 milliseconds.
- * @since 2.10.0
- */
- public void setMaxConn(final Duration maxConnDuration) {
- this.maxConnDuration = maxConnDuration;
- }
-
- /**
- * Sets the maximum lifetime in milliseconds of a connection after which the connection will always fail activation,
- * passivation and validation.
- *
- * @param maxConnDuration
- * A value of zero or less indicates an infinite lifetime. The default value is -1 milliseconds.
- * @since 2.9.0
- * @deprecated Use {@link #setMaxConn(Duration)}.
- */
- @Deprecated
- public void setMaxConnLifetime(final Duration maxConnDuration) {
- this.maxConnDuration = maxConnDuration;
- }
-
- /**
- * Sets the maximum lifetime in milliseconds of a connection after which the connection will always fail activation,
- * passivation and validation.
- *
- * @param maxConnLifetimeMillis
- * A value of zero or less indicates an infinite lifetime. The default value is -1.
- * @deprecated Use {@link #setMaxConn(Duration)}.
- */
- @Deprecated
- public void setMaxConnLifetimeMillis(final long maxConnLifetimeMillis) {
- setMaxConnLifetime(Duration.ofMillis(maxConnLifetimeMillis));
- }
-
- /**
- * Sets the database password used when creating new connections.
- *
- * @param userPassword
- * new password
- */
- public synchronized void setPassword(final char[] userPassword) {
- this.userPassKey = new UserPassKey(userPassKey.getUserName(), userPassword);
- }
-
- /**
- * Sets the database password used when creating new connections.
- *
- * @param userPassword
- * new password
- */
- @Override
- public synchronized void setPassword(final String userPassword) {
- this.userPassKey = new UserPassKey(userPassKey.getUserName(), userPassword);
- }
-
- /**
- *
- * @param pool
- * the {@link ObjectPool} in which to pool those {@link Connection}s
- */
- public void setPool(final ObjectPool<PooledConnectionAndInfo> pool) {
- this.pool = pool;
- }
-
- /**
- * @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(", validationQueryTimeoutDuration=");
- builder.append(validationQueryTimeoutDuration);
- builder.append(", rollbackAfterValidation=");
- builder.append(rollbackAfterValidation);
- builder.append(", pool=");
- builder.append(pool);
- builder.append(", maxConnDuration=");
- builder.append(maxConnDuration);
- builder.append(", validatingSet=");
- builder.append(validatingSet);
- builder.append(", pcMap=");
- builder.append(pcMap);
- builder.append("]");
- return builder.toString();
- }
-
- private void validateLifetime(final PooledObject<PooledConnectionAndInfo> pooledObject) throws Exception {
- if (maxConnDuration.compareTo(Duration.ZERO) > 0) {
- final Duration lifetimeDuration = Duration.between(pooledObject.getCreateInstant(), Instant.now());
- if (lifetimeDuration.compareTo(maxConnDuration) > 0) {
- throw new Exception(Utils.getMessage("connectionFactory.lifetimeExceeded", lifetimeDuration, maxConnDuration));
- }
- }
- }
-
- @Override
- public boolean validateObject(final PooledObject<PooledConnectionAndInfo> p) {
- try {
- validateLifetime(p);
- } catch (final Exception e) {
- return false;
- }
- boolean valid = false;
- final PooledConnection pconn = p.getObject().getPooledConnection();
- Connection conn = null;
- validatingSet.add(pconn);
- if (null == validationQuery) {
- Duration timeoutDuration = validationQueryTimeoutDuration;
- if (timeoutDuration.isNegative()) {
- timeoutDuration = Duration.ZERO;
- }
- try {
- conn = pconn.getConnection();
- valid = conn.isValid((int) timeoutDuration.getSeconds());
- } catch (final SQLException e) {
- valid = false;
- } finally {
- Utils.closeQuietly((AutoCloseable) conn);
- validatingSet.remove(pconn);
- }
- } else {
- Statement stmt = null;
- ResultSet rset = null;
- // logical Connection from the PooledConnection must be closed
- // before another one can be requested and closing it will
- // generate an event. Keep track so we know not to return
- // the PooledConnection
- validatingSet.add(pconn);
- try {
- conn = pconn.getConnection();
- stmt = conn.createStatement();
- rset = stmt.executeQuery(validationQuery);
- valid = rset.next();
- if (rollbackAfterValidation) {
- conn.rollback();
- }
- } catch (final Exception e) {
- valid = false;
- } finally {
- Utils.closeQuietly((AutoCloseable) rset);
- Utils.closeQuietly((AutoCloseable) stmt);
- Utils.closeQuietly((AutoCloseable) conn);
- validatingSet.remove(pconn);
- }
- }
- return valid;
- }
-}
+/*
+ * 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.commons.dbcp2.datasources;
+
+import java.sql.Connection;
+import java.sql.ResultSet;
+import java.sql.SQLException;
+import java.sql.Statement;
+import java.time.Duration;
+import java.time.Instant;
+import java.util.Collections;
+import java.util.Map;
+import java.util.Set;
+import java.util.concurrent.ConcurrentHashMap;
+
+import javax.sql.ConnectionEvent;
+import javax.sql.ConnectionEventListener;
+import javax.sql.ConnectionPoolDataSource;
+import javax.sql.PooledConnection;
+
+import org.apache.commons.dbcp2.Utils;
+import org.apache.commons.pool2.ObjectPool;
+import org.apache.commons.pool2.PooledObject;
+import org.apache.commons.pool2.PooledObjectFactory;
+import org.apache.commons.pool2.impl.DefaultPooledObject;
+
+/**
+ * A {@link PooledObjectFactory} that creates {@link org.apache.commons.dbcp2.PoolableConnection PoolableConnection}s.
+ *
+ * @since 2.0
+ */
+class CPDSConnectionFactory
+ implements PooledObjectFactory<PooledConnectionAndInfo>, ConnectionEventListener, PooledConnectionManager {
+
+ private static final String NO_KEY_MESSAGE = "close() was called on a Connection, but I have no record of the underlying PooledConnection.";
+
+ private final ConnectionPoolDataSource cpds;
+ private final String validationQuery;
+ private final Duration validationQueryTimeoutDuration;
+ private final boolean rollbackAfterValidation;
+ private ObjectPool<PooledConnectionAndInfo> pool;
+ private UserPassKey userPassKey;
+ private Duration maxConnDuration = Duration.ofMillis(-1);
+
+ /**
+ * Map of PooledConnections for which close events are ignored. Connections are muted when they are being validated.
+ */
+ private final Set<PooledConnection> validatingSet = Collections.newSetFromMap(new ConcurrentHashMap<>());
+
+ /**
+ * Map of PooledConnectionAndInfo instances
+ */
+ private final Map<PooledConnection, PooledConnectionAndInfo> pcMap = new ConcurrentHashMap<>();
+
+ /**
+ * Creates a new {@code PoolableConnectionFactory}.
+ *
+ * @param cpds
+ * the ConnectionPoolDataSource from which to obtain PooledConnection's
+ * @param validationQuery
+ * a query to use to {@link #validateObject validate} {@link Connection}s. Should return at least one
+ * row. May be {@code null} in which case {@link Connection#isValid(int)} will be used to validate
+ * connections.
+ * @param validationQueryTimeoutDuration
+ * Timeout Duration before validation fails
+ * @param rollbackAfterValidation
+ * whether a rollback should be issued after {@link #validateObject validating} {@link Connection}s.
+ * @param userName
+ * The user name to use to create connections
+ * @param userPassword
+ * The password to use to create connections
+ * @since 2.10.0
+ */
+ public CPDSConnectionFactory(final ConnectionPoolDataSource cpds, final String validationQuery,
+ final Duration validationQueryTimeoutDuration, final boolean rollbackAfterValidation, final String userName,
+ final char[] userPassword) {
+ this.cpds = cpds;
+ this.validationQuery = validationQuery;
+ this.validationQueryTimeoutDuration = validationQueryTimeoutDuration;
+ this.userPassKey = new UserPassKey(userName, userPassword);
+ this.rollbackAfterValidation = rollbackAfterValidation;
+ }
+
+ /**
+ * Creates a new {@code PoolableConnectionFactory}.
+ *
+ * @param cpds
+ * the ConnectionPoolDataSource from which to obtain PooledConnection's
+ * @param validationQuery
+ * a query to use to {@link #validateObject validate} {@link Connection}s. Should return at least one
+ * row. May be {@code null} in which case {@link Connection#isValid(int)} will be used to validate
+ * connections.
+ * @param validationQueryTimeoutDuration
+ * Timeout in seconds before validation fails
+ * @param rollbackAfterValidation
+ * whether a rollback should be issued after {@link #validateObject validating} {@link Connection}s.
+ * @param userName
+ * The user name to use to create connections
+ * @param userPassword
+ * The password to use to create connections
+ * @since 2.10.0
+ */
+ public CPDSConnectionFactory(final ConnectionPoolDataSource cpds, final String validationQuery, final Duration validationQueryTimeoutDuration,
+ final boolean rollbackAfterValidation, final String userName, final String userPassword) {
+ this(cpds, validationQuery, validationQueryTimeoutDuration, rollbackAfterValidation, userName, Utils.toCharArray(userPassword));
+ }
+
+ /**
+ * Creates a new {@code PoolableConnectionFactory}.
+ *
+ * @param cpds
+ * the ConnectionPoolDataSource from which to obtain PooledConnection's
+ * @param validationQuery
+ * a query to use to {@link #validateObject validate} {@link Connection}s. Should return at least one
+ * row. May be {@code null} in which case {@link Connection#isValid(int)} will be used to validate
+ * connections.
+ * @param validationQueryTimeoutSeconds
+ * Timeout in seconds before validation fails
+ * @param rollbackAfterValidation
+ * whether a rollback should be issued after {@link #validateObject validating} {@link Connection}s.
+ * @param userName
+ * The user name to use to create connections
+ * @param userPassword
+ * The password to use to create connections
+ * @since 2.4.0
+ * @deprecated Use {@link #CPDSConnectionFactory(ConnectionPoolDataSource, String, Duration, boolean, String, char[])}.
+ */
+ @Deprecated
+ public CPDSConnectionFactory(final ConnectionPoolDataSource cpds, final String validationQuery,
+ final int validationQueryTimeoutSeconds, final boolean rollbackAfterValidation, final String userName,
+ final char[] userPassword) {
+ this.cpds = cpds;
+ this.validationQuery = validationQuery;
+ this.validationQueryTimeoutDuration = Duration.ofSeconds(validationQueryTimeoutSeconds);
+ this.userPassKey = new UserPassKey(userName, userPassword);
+ this.rollbackAfterValidation = rollbackAfterValidation;
+ }
+
+ /**
+ * Creates a new {@code PoolableConnectionFactory}.
+ *
+ * @param cpds
+ * the ConnectionPoolDataSource from which to obtain PooledConnection's
+ * @param validationQuery
+ * a query to use to {@link #validateObject validate} {@link Connection}s. Should return at least one
+ * row. May be {@code null} in which case {@link Connection#isValid(int)} will be used to validate
+ * connections.
+ * @param validationQueryTimeoutSeconds
+ * Timeout in seconds before validation fails
+ * @param rollbackAfterValidation
+ * whether a rollback should be issued after {@link #validateObject validating} {@link Connection}s.
+ * @param userName
+ * The user name to use to create connections
+ * @param userPassword
+ * The password to use to create connections
+ * @deprecated Use {@link #CPDSConnectionFactory(ConnectionPoolDataSource, String, Duration, boolean, String, String)}.
+ */
+ @Deprecated
+ public CPDSConnectionFactory(final ConnectionPoolDataSource cpds, final String validationQuery, final int validationQueryTimeoutSeconds,
+ final boolean rollbackAfterValidation, final String userName, final String userPassword) {
+ this(cpds, validationQuery, validationQueryTimeoutSeconds, rollbackAfterValidation, userName, Utils.toCharArray(userPassword));
+ }
+
+ @Override
+ public void activateObject(final PooledObject<PooledConnectionAndInfo> p) throws Exception {
+ validateLifetime(p);
+ }
+
+ /**
+ * Verifies that the user name matches the user whose connections are being managed by this factory and closes the
+ * pool if this is the case; otherwise does nothing.
+ */
+ @Override
+ public void closePool(final String userName) throws SQLException {
+ synchronized (this) {
+ if (userName == null || !userName.equals(this.userPassKey.getUserName())) {
+ return;
+ }
+ }
+ try {
+ pool.close();
+ } catch (final Exception ex) {
+ throw new SQLException("Error closing connection pool", ex);
+ }
+ }
+
+ /**
+ * This will be called if the Connection returned by the getConnection method came from a PooledConnection, and the
+ * user calls the close() method of this connection object. What we need to do here is to release this
+ * PooledConnection from our pool...
+ */
+ @Override
+ public void connectionClosed(final ConnectionEvent event) {
+ final PooledConnection pc = (PooledConnection) event.getSource();
+ // if this event occurred because we were validating, ignore it
+ // otherwise return the connection to the pool.
+ if (!validatingSet.contains(pc)) {
+ final PooledConnectionAndInfo pci = pcMap.get(pc);
+ if (pci == null) {
+ throw new IllegalStateException(NO_KEY_MESSAGE);
+ }
+
+ try {
+ pool.returnObject(pci);
+ } catch (final Exception e) {
+ System.err.println("CLOSING DOWN CONNECTION AS IT COULD " + "NOT BE RETURNED TO THE POOL");
+ pc.removeConnectionEventListener(this);
+ try {
+ doDestroyObject(pci);
+ } catch (final Exception e2) {
+ System.err.println("EXCEPTION WHILE DESTROYING OBJECT " + pci);
+ e2.printStackTrace();
+ }
+ }
+ }
+ }
+
+ /**
+ * If a fatal error occurs, close the underlying physical connection so as not to be returned in the future
+ */
+ @Override
+ public void connectionErrorOccurred(final ConnectionEvent event) {
+ final PooledConnection pc = (PooledConnection) event.getSource();
+ if (null != event.getSQLException()) {
+ System.err.println("CLOSING DOWN CONNECTION DUE TO INTERNAL ERROR (" + event.getSQLException() + ")");
+ }
+ pc.removeConnectionEventListener(this);
+
+ final PooledConnectionAndInfo pci = pcMap.get(pc);
+ if (pci == null) {
+ throw new IllegalStateException(NO_KEY_MESSAGE);
+ }
+ try {
+ pool.invalidateObject(pci);
+ } catch (final Exception e) {
+ System.err.println("EXCEPTION WHILE DESTROYING OBJECT " + pci);
+ e.printStackTrace();
+ }
+ }
+
+ /**
+ * Closes the PooledConnection and stops listening for events from it.
+ */
+ @Override
+ public void destroyObject(final PooledObject<PooledConnectionAndInfo> p) throws SQLException {
+ doDestroyObject(p.getObject());
+ }
+
+ private void doDestroyObject(final PooledConnectionAndInfo pci) throws SQLException {
+ final PooledConnection pc = pci.getPooledConnection();
+ pc.removeConnectionEventListener(this);
+ pcMap.remove(pc);
+ pc.close();
+ }
+
+ /**
+ * (Testing API) Gets the value of password for the default user.
+ *
+ * @return value of password.
+ */
+ char[] getPasswordCharArray() {
+ return userPassKey.getPasswordCharArray();
+ }
+
+ /**
+ * Returns the object pool used to pool connections created by this factory.
+ *
+ * @return ObjectPool managing pooled connections
+ */
+ public ObjectPool<PooledConnectionAndInfo> getPool() {
+ return pool;
+ }
+
+ /**
+ * Invalidates the PooledConnection in the pool. The CPDSConnectionFactory closes the connection and pool counters
+ * are updated appropriately. Also closes the pool. This ensures that all idle connections are closed and
+ * connections that are checked out are closed on return.
+ */
+ @Override
+ public void invalidate(final PooledConnection pc) throws SQLException {
+ final PooledConnectionAndInfo pci = pcMap.get(pc);
+ if (pci == null) {
+ throw new IllegalStateException(NO_KEY_MESSAGE);
+ }
+ try {
+ pool.invalidateObject(pci); // Destroy instance and update pool counters
+ pool.close(); // Clear any other instances in this pool and kill others as they come back
+ } catch (final Exception ex) {
+ throw new SQLException("Error invalidating connection", ex);
+ }
+ }
+
+ // ***********************************************************************
+ // java.sql.ConnectionEventListener implementation
+ // ***********************************************************************
+
+ @Override
+ public synchronized PooledObject<PooledConnectionAndInfo> makeObject() {
+ final PooledConnectionAndInfo pci;
+ try {
+ PooledConnection pc = null;
+ if (userPassKey.getUserName() == null) {
+ pc = cpds.getPooledConnection();
+ } else {
+ pc = cpds.getPooledConnection(userPassKey.getUserName(), userPassKey.getPassword());
+ }
+
+ if (pc == null) {
+ throw new IllegalStateException("Connection pool data source returned null from getPooledConnection");
+ }
+
+ // should we add this object as a listener or the pool.
+ // consider the validateObject method in decision
+ pc.addConnectionEventListener(this);
+ pci = new PooledConnectionAndInfo(pc, userPassKey);
+ pcMap.put(pc, pci);
+ } catch (final SQLException e) {
+ throw new RuntimeException(e.getMessage());
+ }
+ return new DefaultPooledObject<>(pci);
+ }
+
+ @Override
+ public void passivateObject(final PooledObject<PooledConnectionAndInfo> p) throws Exception {
+ validateLifetime(p);
+ }
+
+ /**
+ * Sets the maximum Duration of a connection after which the connection will always fail activation,
+ * passivation and validation.
+ *
+ * @param maxConnDuration
+ * A value of zero or less indicates an infinite lifetime. The default value is -1 milliseconds.
+ * @since 2.10.0
+ */
+ public void setMaxConn(final Duration maxConnDuration) {
+ this.maxConnDuration = maxConnDuration;
+ }
+
+ /**
+ * Sets the maximum lifetime in milliseconds of a connection after which the connection will always fail activation,
+ * passivation and validation.
+ *
+ * @param maxConnDuration
+ * A value of zero or less indicates an infinite lifetime. The default value is -1 milliseconds.
+ * @since 2.9.0
+ * @deprecated Use {@link #setMaxConn(Duration)}.
+ */
+ @Deprecated
+ public void setMaxConnLifetime(final Duration maxConnDuration) {
+ this.maxConnDuration = maxConnDuration;
+ }
+
+ /**
+ * Sets the maximum lifetime in milliseconds of a connection after which the connection will always fail activation,
+ * passivation and validation.
+ *
+ * @param maxConnLifetimeMillis
+ * A value of zero or less indicates an infinite lifetime. The default value is -1.
+ * @deprecated Use {@link #setMaxConn(Duration)}.
+ */
+ @Deprecated
+ public void setMaxConnLifetimeMillis(final long maxConnLifetimeMillis) {
+ setMaxConnLifetime(Duration.ofMillis(maxConnLifetimeMillis));
+ }
+
+ /**
+ * Sets the database password used when creating new connections.
+ *
+ * @param userPassword
+ * new password
+ */
+ public synchronized void setPassword(final char[] userPassword) {
+ this.userPassKey = new UserPassKey(userPassKey.getUserName(), userPassword);
+ }
+
+ /**
+ * Sets the database password used when creating new connections.
+ *
+ * @param userPassword
+ * new password
+ */
+ @Override
+ public synchronized void setPassword(final String userPassword) {
+ this.userPassKey = new UserPassKey(userPassKey.getUserName(), userPassword);
+ }
+
+ /**
+ *
+ * @param pool
+ * the {@link ObjectPool} in which to pool those {@link Connection}s
+ */
+ public void setPool(final ObjectPool<PooledConnectionAndInfo> pool) {
+ this.pool = pool;
+ }
+
+ /**
+ * @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(", validationQueryTimeoutDuration=");
+ builder.append(validationQueryTimeoutDuration);
+ builder.append(", rollbackAfterValidation=");
+ builder.append(rollbackAfterValidation);
+ builder.append(", pool=");
+ builder.append(pool);
+ builder.append(", maxConnDuration=");
+ builder.append(maxConnDuration);
+ builder.append(", validatingSet=");
+ builder.append(validatingSet);
+ builder.append(", pcMap=");
+ builder.append(pcMap);
+ builder.append("]");
+ return builder.toString();
+ }
+
+ private void validateLifetime(final PooledObject<PooledConnectionAndInfo> pooledObject) throws Exception {
+ if (maxConnDuration.compareTo(Duration.ZERO) > 0) {
+ final Duration lifetimeDuration = Duration.between(pooledObject.getCreateInstant(), Instant.now());
+ if (lifetimeDuration.compareTo(maxConnDuration) > 0) {
+ throw new Exception(Utils.getMessage("connectionFactory.lifetimeExceeded", lifetimeDuration, maxConnDuration));
+ }
+ }
+ }
+
+ @Override
+ public boolean validateObject(final PooledObject<PooledConnectionAndInfo> p) {
+ try {
+ validateLifetime(p);
+ } catch (final Exception e) {
+ return false;
+ }
+ boolean valid = false;
+ final PooledConnection pconn = p.getObject().getPooledConnection();
+ Connection conn = null;
+ validatingSet.add(pconn);
+ if (null == validationQuery) {
+ Duration timeoutDuration = validationQueryTimeoutDuration;
+ if (timeoutDuration.isNegative()) {
+ timeoutDuration = Duration.ZERO;
+ }
+ try {
+ conn = pconn.getConnection();
+ valid = conn.isValid((int) timeoutDuration.getSeconds());
+ } catch (final SQLException e) {
+ valid = false;
+ } finally {
+ Utils.closeQuietly((AutoCloseable) conn);
+ validatingSet.remove(pconn);
+ }
+ } else {
+ Statement stmt = null;
+ ResultSet rset = null;
+ // logical Connection from the PooledConnection must be closed
+ // before another one can be requested and closing it will
+ // generate an event. Keep track so we know not to return
+ // the PooledConnection
+ validatingSet.add(pconn);
+ try {
+ conn = pconn.getConnection();
+ stmt = conn.createStatement();
+ rset = stmt.executeQuery(validationQuery);
+ valid = rset.next();
+ if (rollbackAfterValidation) {
+ conn.rollback();
+ }
+ } catch (final Exception e) {
+ valid = false;
+ } finally {
+ Utils.closeQuietly((AutoCloseable) rset);
+ Utils.closeQuietly((AutoCloseable) stmt);
+ Utils.closeQuietly((AutoCloseable) conn);
+ validatingSet.remove(pconn);
+ }
+ }
+ return valid;
+ }
+}
diff --git a/src/main/java/org/apache/commons/dbcp2/datasources/InstanceKeyDataSourceFactory.java b/src/main/java/org/apache/commons/dbcp2/datasources/InstanceKeyDataSourceFactory.java
index 23832cb4..f7d9f58a 100644
--- a/src/main/java/org/apache/commons/dbcp2/datasources/InstanceKeyDataSourceFactory.java
+++ b/src/main/java/org/apache/commons/dbcp2/datasources/InstanceKeyDataSourceFactory.java
@@ -49,14 +49,13 @@ abstract class InstanceKeyDataSourceFactory implements ObjectFactory {
/**
* Closes all pools associated with this class.
*
- * @throws Exception
+ * @throws ListException
* a {@link ListException} containing all exceptions thrown by {@link InstanceKeyDataSource#close()}
* @see InstanceKeyDataSource#close()
- * @see ListException
* @since 2.4.0 throws a {@link ListException} instead of, in 2.3.0 and before, the first exception thrown by
* {@link InstanceKeyDataSource#close()}.
*/
- public static void closeAll() throws Exception {
+ public static void closeAll() throws ListException {
// Get iterator to loop over all instances of this data source.
final List<Throwable> exceptionList = new ArrayList<>(INSTANCE_MAP.size());
for (final Entry<String, InstanceKeyDataSource> next : INSTANCE_MAP.entrySet()) {
diff --git a/src/main/java/org/apache/commons/dbcp2/datasources/KeyedCPDSConnectionFactory.java b/src/main/java/org/apache/commons/dbcp2/datasources/KeyedCPDSConnectionFactory.java
index 511b36e8..e2c3f991 100644
--- a/src/main/java/org/apache/commons/dbcp2/datasources/KeyedCPDSConnectionFactory.java
+++ b/src/main/java/org/apache/commons/dbcp2/datasources/KeyedCPDSConnectionFactory.java
@@ -1,390 +1,390 @@
-/*
- * 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.commons.dbcp2.datasources;
-
-import java.sql.Connection;
-import java.sql.ResultSet;
-import java.sql.SQLException;
-import java.sql.Statement;
-import java.time.Duration;
-import java.time.Instant;
-import java.util.Collections;
-import java.util.Map;
-import java.util.Set;
-import java.util.concurrent.ConcurrentHashMap;
-
-import javax.sql.ConnectionEvent;
-import javax.sql.ConnectionEventListener;
-import javax.sql.ConnectionPoolDataSource;
-import javax.sql.PooledConnection;
-
-import org.apache.commons.dbcp2.Utils;
-import org.apache.commons.pool2.KeyedObjectPool;
-import org.apache.commons.pool2.KeyedPooledObjectFactory;
-import org.apache.commons.pool2.PooledObject;
-import org.apache.commons.pool2.impl.DefaultPooledObject;
-
-/**
- * A {@link KeyedPooledObjectFactory} that creates {@link org.apache.commons.dbcp2.PoolableConnection
- * PoolableConnection}s.
- *
- * @since 2.0
- */
-class KeyedCPDSConnectionFactory implements KeyedPooledObjectFactory<UserPassKey, PooledConnectionAndInfo>,
- ConnectionEventListener, PooledConnectionManager {
-
- private static final String NO_KEY_MESSAGE = "close() was called on a Connection, but "
- + "I have no record of the underlying PooledConnection.";
-
- private final ConnectionPoolDataSource cpds;
- private final String validationQuery;
- private final Duration validationQueryTimeoutDuration;
- private final boolean rollbackAfterValidation;
- private KeyedObjectPool<UserPassKey, PooledConnectionAndInfo> pool;
- private Duration maxConnLifetime = Duration.ofMillis(-1);
-
- /**
- * Map of PooledConnections for which close events are ignored. Connections are muted when they are being validated.
- */
- private final Set<PooledConnection> validatingSet = Collections.newSetFromMap(new ConcurrentHashMap<>());
-
- /**
- * Map of PooledConnectionAndInfo instances
- */
- private final Map<PooledConnection, PooledConnectionAndInfo> pcMap = new ConcurrentHashMap<>();
-
- /**
- * Creates a new {@code KeyedPoolableConnectionFactory}.
- *
- * @param cpds
- * the ConnectionPoolDataSource from which to obtain PooledConnections
- * @param validationQuery
- * a query to use to {@link #validateObject validate} {@link Connection}s. Should return at least one
- * row. May be {@code null} in which case3 {@link Connection#isValid(int)} will be used to validate
- * connections.
- * @param validationQueryTimeoutSeconds
- * The Duration to allow for the validation query to complete
- * @param rollbackAfterValidation
- * whether a rollback should be issued after {@link #validateObject validating} {@link Connection}s.
- * @since 2.10.0
- */
- public KeyedCPDSConnectionFactory(final ConnectionPoolDataSource cpds, final String validationQuery,
- final Duration validationQueryTimeoutSeconds, final boolean rollbackAfterValidation) {
- this.cpds = cpds;
- this.validationQuery = validationQuery;
- this.validationQueryTimeoutDuration = validationQueryTimeoutSeconds;
- this.rollbackAfterValidation = rollbackAfterValidation;
- }
-
- /**
- * Creates a new {@code KeyedPoolableConnectionFactory}.
- *
- * @param cpds
- * the ConnectionPoolDataSource from which to obtain PooledConnections
- * @param validationQuery
- * a query to use to {@link #validateObject validate} {@link Connection}s. Should return at least one
- * row. May be {@code null} in which case3 {@link Connection#isValid(int)} will be used to validate
- * connections.
- * @param validationQueryTimeoutSeconds
- * The time, in seconds, to allow for the validation query to complete
- * @param rollbackAfterValidation
- * whether a rollback should be issued after {@link #validateObject validating} {@link Connection}s.
- * @deprecated Use {@link #KeyedCPDSConnectionFactory(ConnectionPoolDataSource, String, Duration, boolean)}.
- */
- @Deprecated
- public KeyedCPDSConnectionFactory(final ConnectionPoolDataSource cpds, final String validationQuery,
- final int validationQueryTimeoutSeconds, final boolean rollbackAfterValidation) {
- this(cpds, validationQuery, Duration.ofSeconds(validationQueryTimeoutSeconds), rollbackAfterValidation);
- }
-
- @Override
- public void activateObject(final UserPassKey key, final PooledObject<PooledConnectionAndInfo> p) throws Exception {
- validateLifetime(p);
- }
-
- /**
- * This implementation does not fully close the KeyedObjectPool, as this would affect all users. Instead, it clears
- * the pool associated with the given user. This method is not currently used.
- */
- @Override
- public void closePool(final String userName) throws SQLException {
- try {
- pool.clear(new UserPassKey(userName));
- } catch (final Exception ex) {
- throw new SQLException("Error closing connection pool", ex);
- }
- }
-
- /**
- * This will be called if the Connection returned by the getConnection method came from a PooledConnection, and the
- * user calls the close() method of this connection object. What we need to do here is to release this
- * PooledConnection from our pool...
- */
- @Override
- public void connectionClosed(final ConnectionEvent event) {
- final PooledConnection pc = (PooledConnection) event.getSource();
- // if this event occurred because we were validating, or if this
- // connection has been marked for removal, ignore it
- // otherwise return the connection to the pool.
- if (!validatingSet.contains(pc)) {
- final PooledConnectionAndInfo pci = pcMap.get(pc);
- if (pci == null) {
- throw new IllegalStateException(NO_KEY_MESSAGE);
- }
- try {
- pool.returnObject(pci.getUserPassKey(), pci);
- } catch (final Exception e) {
- System.err.println("CLOSING DOWN CONNECTION AS IT COULD " + "NOT BE RETURNED TO THE POOL");
- pc.removeConnectionEventListener(this);
- try {
- pool.invalidateObject(pci.getUserPassKey(), pci);
- } catch (final Exception e3) {
- System.err.println("EXCEPTION WHILE DESTROYING OBJECT " + pci);
- e3.printStackTrace();
- }
- }
- }
- }
-
- /**
- * If a fatal error occurs, close the underlying physical connection so as not to be returned in the future
- */
- @Override
- public void connectionErrorOccurred(final ConnectionEvent event) {
- final PooledConnection pc = (PooledConnection) event.getSource();
- if (null != event.getSQLException()) {
- System.err.println("CLOSING DOWN CONNECTION DUE TO INTERNAL ERROR (" + event.getSQLException() + ")");
- }
- pc.removeConnectionEventListener(this);
-
- final PooledConnectionAndInfo info = pcMap.get(pc);
- if (info == null) {
- throw new IllegalStateException(NO_KEY_MESSAGE);
- }
- try {
- pool.invalidateObject(info.getUserPassKey(), info);
- } catch (final Exception e) {
- System.err.println("EXCEPTION WHILE DESTROYING OBJECT " + info);
- e.printStackTrace();
- }
- }
-
- /**
- * Closes the PooledConnection and stops listening for events from it.
- */
- @Override
- public void destroyObject(final UserPassKey key, final PooledObject<PooledConnectionAndInfo> p) throws Exception {
- final PooledConnection pooledConnection = p.getObject().getPooledConnection();
- pooledConnection.removeConnectionEventListener(this);
- pcMap.remove(pooledConnection);
- pooledConnection.close();
- }
-
- /**
- * Returns the keyed object pool used to pool connections created by this factory.
- *
- * @return KeyedObjectPool managing pooled connections
- */
- public KeyedObjectPool<UserPassKey, PooledConnectionAndInfo> getPool() {
- return pool;
- }
-
- /**
- * Invalidates the PooledConnection in the pool. The KeyedCPDSConnectionFactory closes the connection and pool
- * counters are updated appropriately. Also clears any idle instances associated with the user name that was used to
- * create the PooledConnection. Connections associated with this user are not affected and they will not be
- * automatically closed on return to the pool.
- */
- @Override
- public void invalidate(final PooledConnection pc) throws SQLException {
- final PooledConnectionAndInfo info = pcMap.get(pc);
- if (info == null) {
- throw new IllegalStateException(NO_KEY_MESSAGE);
- }
- final UserPassKey key = info.getUserPassKey();
- try {
- pool.invalidateObject(key, info); // Destroy and update pool counters
- pool.clear(key); // Remove any idle instances with this key
- } catch (final Exception ex) {
- throw new SQLException("Error invalidating connection", ex);
- }
- }
-
- // ***********************************************************************
- // java.sql.ConnectionEventListener implementation
- // ***********************************************************************
-
- /**
- * Creates a new {@code PooledConnectionAndInfo} from the given {@code UserPassKey}.
- *
- * @param userPassKey
- * {@code UserPassKey} containing user credentials
- * @throws SQLException
- * if the connection could not be created.
- * @see org.apache.commons.pool2.KeyedPooledObjectFactory#makeObject(java.lang.Object)
- */
- @Override
- public synchronized PooledObject<PooledConnectionAndInfo> makeObject(final UserPassKey userPassKey) throws Exception {
- PooledConnection pooledConnection = null;
- final String userName = userPassKey.getUserName();
- final String password = userPassKey.getPassword();
- if (userName == null) {
- pooledConnection = cpds.getPooledConnection();
- } else {
- pooledConnection = cpds.getPooledConnection(userName, password);
- }
-
- if (pooledConnection == null) {
- throw new IllegalStateException("Connection pool data source returned null from getPooledConnection");
- }
-
- // should we add this object as a listener or the pool.
- // consider the validateObject method in decision
- pooledConnection.addConnectionEventListener(this);
- final PooledConnectionAndInfo pci = new PooledConnectionAndInfo(pooledConnection, userPassKey);
- pcMap.put(pooledConnection, pci);
-
- return new DefaultPooledObject<>(pci);
- }
-
- @Override
- public void passivateObject(final UserPassKey key, final PooledObject<PooledConnectionAndInfo> p) throws Exception {
- validateLifetime(p);
- }
-
- /**
- * Sets the maximum lifetime of a connection after which the connection will always fail activation,
- * passivation and validation.
- *
- * @param maxConnLifetimeMillis
- * A value of zero or less indicates an infinite lifetime. The default value is -1 milliseconds.
- * @since 2.10.0
- */
- public void setMaxConn(final Duration maxConnLifetimeMillis) {
- this.maxConnLifetime = maxConnLifetimeMillis;
- }
-
- /**
- * Sets the maximum lifetime of a connection after which the connection will always fail activation,
- * passivation and validation.
- *
- * @param maxConnLifetimeMillis
- * A value of zero or less indicates an infinite lifetime. The default value is -1 milliseconds.
- * @since 2.9.0
- * @deprecated Use {@link #setMaxConn(Duration)}.
- */
- @Deprecated
- public void setMaxConnLifetime(final Duration maxConnLifetimeMillis) {
- this.maxConnLifetime = maxConnLifetimeMillis;
- }
-
- /**
- * Sets the maximum lifetime in milliseconds of a connection after which the connection will always fail activation,
- * passivation and validation.
- *
- * @param maxConnLifetimeMillis
- * A value of zero or less indicates an infinite lifetime. The default value is -1.
- * @deprecated Use {@link #setMaxConnLifetime(Duration)}.
- */
- @Deprecated
- public void setMaxConnLifetimeMillis(final long maxConnLifetimeMillis) {
- setMaxConn(Duration.ofMillis(maxConnLifetimeMillis));
- }
-
- /**
- * Does nothing. This factory does not cache user credentials.
- */
- @Override
- public void setPassword(final String password) {
- // Does nothing. This factory does not cache user credentials.
- }
-
- public void setPool(final KeyedObjectPool<UserPassKey, PooledConnectionAndInfo> pool) {
- this.pool = pool;
- }
-
- private void validateLifetime(final PooledObject<PooledConnectionAndInfo> p) throws Exception {
- if (maxConnLifetime.compareTo(Duration.ZERO) > 0) {
- final Duration lifetimeDuration = Duration.between(p.getCreateInstant(), Instant.now());
- if (lifetimeDuration.compareTo(maxConnLifetime) > 0) {
- throw new Exception(Utils.getMessage("connectionFactory.lifetimeExceeded", lifetimeDuration, maxConnLifetime));
- }
- }
- }
-
- /**
- * Validates a pooled connection.
- *
- * @param key
- * ignored
- * @param pooledObject
- * wrapped {@code PooledConnectionAndInfo} containing the connection to validate
- * @return true if validation succeeds
- */
- @Override
- public boolean validateObject(final UserPassKey key, final PooledObject<PooledConnectionAndInfo> pooledObject) {
- try {
- validateLifetime(pooledObject);
- } catch (final Exception e) {
- return false;
- }
- boolean valid = false;
- final PooledConnection pooledConn = pooledObject.getObject().getPooledConnection();
- Connection conn = null;
- validatingSet.add(pooledConn);
- if (null == validationQuery) {
- Duration timeoutDuration = validationQueryTimeoutDuration;
- if (timeoutDuration.isNegative()) {
- timeoutDuration = Duration.ZERO;
- }
- try {
- conn = pooledConn.getConnection();
- valid = conn.isValid((int) timeoutDuration.getSeconds());
- } catch (final SQLException e) {
- valid = false;
- } finally {
- Utils.closeQuietly((AutoCloseable) conn);
- validatingSet.remove(pooledConn);
- }
- } else {
- Statement stmt = null;
- ResultSet rset = null;
- // logical Connection from the PooledConnection must be closed
- // before another one can be requested and closing it will
- // generate an event. Keep track so we know not to return
- // the PooledConnection
- validatingSet.add(pooledConn);
- try {
- conn = pooledConn.getConnection();
- stmt = conn.createStatement();
- rset = stmt.executeQuery(validationQuery);
- valid = rset.next();
- if (rollbackAfterValidation) {
- conn.rollback();
- }
- } catch (final Exception e) {
- valid = false;
- } finally {
- Utils.closeQuietly((AutoCloseable) rset);
- Utils.closeQuietly((AutoCloseable) stmt);
- Utils.closeQuietly((AutoCloseable) conn);
- validatingSet.remove(pooledConn);
- }
- }
- return valid;
- }
-}
+/*
+ * 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.commons.dbcp2.datasources;
+
+import java.sql.Connection;
+import java.sql.ResultSet;
+import java.sql.SQLException;
+import java.sql.Statement;
+import java.time.Duration;
+import java.time.Instant;
+import java.util.Collections;
+import java.util.Map;
+import java.util.Set;
+import java.util.concurrent.ConcurrentHashMap;
+
+import javax.sql.ConnectionEvent;
+import javax.sql.ConnectionEventListener;
+import javax.sql.ConnectionPoolDataSource;
+import javax.sql.PooledConnection;
+
+import org.apache.commons.dbcp2.Utils;
+import org.apache.commons.pool2.KeyedObjectPool;
+import org.apache.commons.pool2.KeyedPooledObjectFactory;
+import org.apache.commons.pool2.PooledObject;
+import org.apache.commons.pool2.impl.DefaultPooledObject;
+
+/**
+ * A {@link KeyedPooledObjectFactory} that creates {@link org.apache.commons.dbcp2.PoolableConnection
+ * PoolableConnection}s.
+ *
+ * @since 2.0
+ */
+class KeyedCPDSConnectionFactory implements KeyedPooledObjectFactory<UserPassKey, PooledConnectionAndInfo>,
+ ConnectionEventListener, PooledConnectionManager {
+
+ private static final String NO_KEY_MESSAGE = "close() was called on a Connection, but "
+ + "I have no record of the underlying PooledConnection.";
+
+ private final ConnectionPoolDataSource cpds;
+ private final String validationQuery;
+ private final Duration validationQueryTimeoutDuration;
+ private final boolean rollbackAfterValidation;
+ private KeyedObjectPool<UserPassKey, PooledConnectionAndInfo> pool;
+ private Duration maxConnLifetime = Duration.ofMillis(-1);
+
+ /**
+ * Map of PooledConnections for which close events are ignored. Connections are muted when they are being validated.
+ */
+ private final Set<PooledConnection> validatingSet = Collections.newSetFromMap(new ConcurrentHashMap<>());
+
+ /**
+ * Map of PooledConnectionAndInfo instances
+ */
+ private final Map<PooledConnection, PooledConnectionAndInfo> pcMap = new ConcurrentHashMap<>();
+
+ /**
+ * Creates a new {@code KeyedPoolableConnectionFactory}.
+ *
+ * @param cpds
+ * the ConnectionPoolDataSource from which to obtain PooledConnections
+ * @param validationQuery
+ * a query to use to {@link #validateObject validate} {@link Connection}s. Should return at least one
+ * row. May be {@code null} in which case3 {@link Connection#isValid(int)} will be used to validate
+ * connections.
+ * @param validationQueryTimeoutSeconds
+ * The Duration to allow for the validation query to complete
+ * @param rollbackAfterValidation
+ * whether a rollback should be issued after {@link #validateObject validating} {@link Connection}s.
+ * @since 2.10.0
+ */
+ public KeyedCPDSConnectionFactory(final ConnectionPoolDataSource cpds, final String validationQuery,
+ final Duration validationQueryTimeoutSeconds, final boolean rollbackAfterValidation) {
+ this.cpds = cpds;
+ this.validationQuery = validationQuery;
+ this.validationQueryTimeoutDuration = validationQueryTimeoutSeconds;
+ this.rollbackAfterValidation = rollbackAfterValidation;
+ }
+
+ /**
+ * Creates a new {@code KeyedPoolableConnectionFactory}.
+ *
+ * @param cpds
+ * the ConnectionPoolDataSource from which to obtain PooledConnections
+ * @param validationQuery
+ * a query to use to {@link #validateObject validate} {@link Connection}s. Should return at least one
+ * row. May be {@code null} in which case3 {@link Connection#isValid(int)} will be used to validate
+ * connections.
+ * @param validationQueryTimeoutSeconds
+ * The time, in seconds, to allow for the validation query to complete
+ * @param rollbackAfterValidation
+ * whether a rollback should be issued after {@link #validateObject validating} {@link Connection}s.
+ * @deprecated Use {@link #KeyedCPDSConnectionFactory(ConnectionPoolDataSource, String, Duration, boolean)}.
+ */
+ @Deprecated
+ public KeyedCPDSConnectionFactory(final ConnectionPoolDataSource cpds, final String validationQuery,
+ final int validationQueryTimeoutSeconds, final boolean rollbackAfterValidation) {
+ this(cpds, validationQuery, Duration.ofSeconds(validationQueryTimeoutSeconds), rollbackAfterValidation);
+ }
+
+ @Override
+ public void activateObject(final UserPassKey key, final PooledObject<PooledConnectionAndInfo> p) throws Exception {
+ validateLifetime(p);
+ }
+
+ /**
+ * This implementation does not fully close the KeyedObjectPool, as this would affect all users. Instead, it clears
+ * the pool associated with the given user. This method is not currently used.
+ */
+ @Override
+ public void closePool(final String userName) throws SQLException {
+ try {
+ pool.clear(new UserPassKey(userName));
+ } catch (final Exception ex) {
+ throw new SQLException("Error closing connection pool", ex);
+ }
+ }
+
+ /**
+ * This will be called if the Connection returned by the getConnection method came from a PooledConnection, and the
+ * user calls the close() method of this connection object. What we need to do here is to release this
+ * PooledConnection from our pool...
+ */
+ @Override
+ public void connectionClosed(final ConnectionEvent event) {
+ final PooledConnection pc = (PooledConnection) event.getSource();
+ // if this event occurred because we were validating, or if this
+ // connection has been marked for removal, ignore it
+ // otherwise return the connection to the pool.
+ if (!validatingSet.contains(pc)) {
+ final PooledConnectionAndInfo pci = pcMap.get(pc);
+ if (pci == null) {
+ throw new IllegalStateException(NO_KEY_MESSAGE);
+ }
+ try {
+ pool.returnObject(pci.getUserPassKey(), pci);
+ } catch (final Exception e) {
+ System.err.println("CLOSING DOWN CONNECTION AS IT COULD " + "NOT BE RETURNED TO THE POOL");
+ pc.removeConnectionEventListener(this);
+ try {
+ pool.invalidateObject(pci.getUserPassKey(), pci);
+ } catch (final Exception e3) {
+ System.err.println("EXCEPTION WHILE DESTROYING OBJECT " + pci);
+ e3.printStackTrace();
+ }
+ }
+ }
+ }
+
+ /**
+ * If a fatal error occurs, close the underlying physical connection so as not to be returned in the future
+ */
+ @Override
+ public void connectionErrorOccurred(final ConnectionEvent event) {
+ final PooledConnection pc = (PooledConnection) event.getSource();
+ if (null != event.getSQLException()) {
+ System.err.println("CLOSING DOWN CONNECTION DUE TO INTERNAL ERROR (" + event.getSQLException() + ")");
+ }
+ pc.removeConnectionEventListener(this);
+
+ final PooledConnectionAndInfo info = pcMap.get(pc);
+ if (info == null) {
+ throw new IllegalStateException(NO_KEY_MESSAGE);
+ }
+ try {
+ pool.invalidateObject(info.getUserPassKey(), info);
+ } catch (final Exception e) {
+ System.err.println("EXCEPTION WHILE DESTROYING OBJECT " + info);
+ e.printStackTrace();
+ }
+ }
+
+ /**
+ * Closes the PooledConnection and stops listening for events from it.
+ */
+ @Override
+ public void destroyObject(final UserPassKey key, final PooledObject<PooledConnectionAndInfo> p) throws SQLException {
+ final PooledConnection pooledConnection = p.getObject().getPooledConnection();
+ pooledConnection.removeConnectionEventListener(this);
+ pcMap.remove(pooledConnection);
+ pooledConnection.close();
+ }
+
+ /**
+ * Returns the keyed object pool used to pool connections created by this factory.
+ *
+ * @return KeyedObjectPool managing pooled connections
+ */
+ public KeyedObjectPool<UserPassKey, PooledConnectionAndInfo> getPool() {
+ return pool;
+ }
+
+ /**
+ * Invalidates the PooledConnection in the pool. The KeyedCPDSConnectionFactory closes the connection and pool
+ * counters are updated appropriately. Also clears any idle instances associated with the user name that was used to
+ * create the PooledConnection. Connections associated with this user are not affected and they will not be
+ * automatically closed on return to the pool.
+ */
+ @Override
+ public void invalidate(final PooledConnection pc) throws SQLException {
+ final PooledConnectionAndInfo info = pcMap.get(pc);
+ if (info == null) {
+ throw new IllegalStateException(NO_KEY_MESSAGE);
+ }
+ final UserPassKey key = info.getUserPassKey();
+ try {
+ pool.invalidateObject(key, info); // Destroy and update pool counters
+ pool.clear(key); // Remove any idle instances with this key
+ } catch (final Exception ex) {
+ throw new SQLException("Error invalidating connection", ex);
+ }
+ }
+
+ // ***********************************************************************
+ // java.sql.ConnectionEventListener implementation
+ // ***********************************************************************
+
+ /**
+ * Creates a new {@code PooledConnectionAndInfo} from the given {@code UserPassKey}.
+ *
+ * @param userPassKey
+ * {@code UserPassKey} containing user credentials
+ * @throws SQLException
+ * if the connection could not be created.
+ * @see org.apache.commons.pool2.KeyedPooledObjectFactory#makeObject(java.lang.Object)
+ */
+ @Override
+ public synchronized PooledObject<PooledConnectionAndInfo> makeObject(final UserPassKey userPassKey) throws SQLException {
+ PooledConnection pooledConnection = null;
+ final String userName = userPassKey.getUserName();
+ final String password = userPassKey.getPassword();
+ if (userName == null) {
+ pooledConnection = cpds.getPooledConnection();
+ } else {
+ pooledConnection = cpds.getPooledConnection(userName, password);
+ }
+
+ if (pooledConnection == null) {
+ throw new IllegalStateException("Connection pool data source returned null from getPooledConnection");
+ }
+
+ // should we add this object as a listener or the pool.
+ // consider the validateObject method in decision
+ pooledConnection.addConnectionEventListener(this);
+ final PooledConnectionAndInfo pci = new PooledConnectionAndInfo(pooledConnection, userPassKey);
+ pcMap.put(pooledConnection, pci);
+
+ return new DefaultPooledObject<>(pci);
+ }
+
+ @Override
+ public void passivateObject(final UserPassKey key, final PooledObject<PooledConnectionAndInfo> p) throws Exception {
+ validateLifetime(p);
+ }
+
+ /**
+ * Sets the maximum lifetime of a connection after which the connection will always fail activation,
+ * passivation and validation.
+ *
+ * @param maxConnLifetimeMillis
+ * A value of zero or less indicates an infinite lifetime. The default value is -1 milliseconds.
+ * @since 2.10.0
+ */
+ public void setMaxConn(final Duration maxConnLifetimeMillis) {
+ this.maxConnLifetime = maxConnLifetimeMillis;
+ }
+
+ /**
+ * Sets the maximum lifetime of a connection after which the connection will always fail activation,
+ * passivation and validation.
+ *
+ * @param maxConnLifetimeMillis
+ * A value of zero or less indicates an infinite lifetime. The default value is -1 milliseconds.
+ * @since 2.9.0
+ * @deprecated Use {@link #setMaxConn(Duration)}.
+ */
+ @Deprecated
+ public void setMaxConnLifetime(final Duration maxConnLifetimeMillis) {
+ this.maxConnLifetime = maxConnLifetimeMillis;
+ }
+
+ /**
+ * Sets the maximum lifetime in milliseconds of a connection after which the connection will always fail activation,
+ * passivation and validation.
+ *
+ * @param maxConnLifetimeMillis
+ * A value of zero or less indicates an infinite lifetime. The default value is -1.
+ * @deprecated Use {@link #setMaxConnLifetime(Duration)}.
+ */
+ @Deprecated
+ public void setMaxConnLifetimeMillis(final long maxConnLifetimeMillis) {
+ setMaxConn(Duration.ofMillis(maxConnLifetimeMillis));
+ }
+
+ /**
+ * Does nothing. This factory does not cache user credentials.
+ */
+ @Override
+ public void setPassword(final String password) {
+ // Does nothing. This factory does not cache user credentials.
+ }
+
+ public void setPool(final KeyedObjectPool<UserPassKey, PooledConnectionAndInfo> pool) {
+ this.pool = pool;
+ }
+
+ private void validateLifetime(final PooledObject<PooledConnectionAndInfo> p) throws Exception {
+ if (maxConnLifetime.compareTo(Duration.ZERO) > 0) {
+ final Duration lifetimeDuration = Duration.between(p.getCreateInstant(), Instant.now());
+ if (lifetimeDuration.compareTo(maxConnLifetime) > 0) {
+ throw new Exception(Utils.getMessage("connectionFactory.lifetimeExceeded", lifetimeDuration, maxConnLifetime));
+ }
+ }
+ }
+
+ /**
+ * Validates a pooled connection.
+ *
+ * @param key
+ * ignored
+ * @param pooledObject
+ * wrapped {@code PooledConnectionAndInfo} containing the connection to validate
+ * @return true if validation succeeds
+ */
+ @Override
+ public boolean validateObject(final UserPassKey key, final PooledObject<PooledConnectionAndInfo> pooledObject) {
+ try {
+ validateLifetime(pooledObject);
+ } catch (final Exception e) {
+ return false;
+ }
+ boolean valid = false;
+ final PooledConnection pooledConn = pooledObject.getObject().getPooledConnection();
+ Connection conn = null;
+ validatingSet.add(pooledConn);
+ if (null == validationQuery) {
+ Duration timeoutDuration = validationQueryTimeoutDuration;
+ if (timeoutDuration.isNegative()) {
+ timeoutDuration = Duration.ZERO;
+ }
+ try {
+ conn = pooledConn.getConnection();
+ valid = conn.isValid((int) timeoutDuration.getSeconds());
+ } catch (final SQLException e) {
+ valid = false;
+ } finally {
+ Utils.closeQuietly((AutoCloseable) conn);
+ validatingSet.remove(pooledConn);
+ }
+ } else {
+ Statement stmt = null;
+ ResultSet rset = null;
+ // logical Connection from the PooledConnection must be closed
+ // before another one can be requested and closing it will
+ // generate an event. Keep track so we know not to return
+ // the PooledConnection
+ validatingSet.add(pooledConn);
+ try {
+ conn = pooledConn.getConnection();
+ stmt = conn.createStatement();
+ rset = stmt.executeQuery(validationQuery);
+ valid = rset.next();
+ if (rollbackAfterValidation) {
+ conn.rollback();
+ }
+ } catch (final Exception e) {
+ valid = false;
+ } finally {
+ Utils.closeQuietly((AutoCloseable) rset);
+ Utils.closeQuietly((AutoCloseable) stmt);
+ Utils.closeQuietly((AutoCloseable) conn);
+ validatingSet.remove(pooledConn);
+ }
+ }
+ return valid;
+ }
+}
diff --git a/src/main/java/org/apache/commons/dbcp2/datasources/SharedPoolDataSource.java b/src/main/java/org/apache/commons/dbcp2/datasources/SharedPoolDataSource.java
index cbee6cda..d8c70b15 100644
--- a/src/main/java/org/apache/commons/dbcp2/datasources/SharedPoolDataSource.java
+++ b/src/main/java/org/apache/commons/dbcp2/datasources/SharedPoolDataSource.java
@@ -1,230 +1,230 @@
-/*
- * 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.commons.dbcp2.datasources;
-
-import java.io.IOException;
-import java.io.ObjectInputStream;
-import java.sql.Connection;
-import java.sql.SQLException;
-
-import javax.naming.NamingException;
-import javax.naming.Reference;
-import javax.naming.StringRefAddr;
-import javax.sql.ConnectionPoolDataSource;
-
-import org.apache.commons.pool2.KeyedObjectPool;
-import org.apache.commons.pool2.impl.GenericKeyedObjectPool;
-import org.apache.commons.pool2.impl.GenericKeyedObjectPoolConfig;
-
-/**
- * <p>
- * A pooling {@code DataSource} appropriate for deployment within J2EE environment. There are many configuration
- * options, most of which are defined in the parent class. All users (based on user name) share a single maximum number
- * of Connections in this data source.
- * </p>
- *
- * <p>
- * User passwords can be changed without re-initializing the data source. When a
- * {@code getConnection(user name, password)} request is processed with a password that is different from those
- * used to create connections in the pool associated with {@code user name}, an attempt is made to create a new
- * connection using the supplied password and if this succeeds, idle connections created using the old password are
- * destroyed and new connections are created using the new password.
- * </p>
- *
- * @since 2.0
- */
-public class SharedPoolDataSource extends InstanceKeyDataSource {
-
- private static final long serialVersionUID = -1458539734480586454L;
-
- // Pool properties
- private int maxTotal = GenericKeyedObjectPoolConfig.DEFAULT_MAX_TOTAL;
-
- private transient KeyedObjectPool<UserPassKey, PooledConnectionAndInfo> pool;
- private transient KeyedCPDSConnectionFactory factory;
-
- /**
- * Default no-argument constructor for Serialization
- */
- public SharedPoolDataSource() {
- // empty.
- }
-
- /**
- * Closes pool being maintained by this data source.
- */
- @Override
- public void close() throws Exception {
- if (pool != null) {
- pool.close();
- }
- InstanceKeyDataSourceFactory.removeInstance(getInstanceKey());
- }
-
- @Override
- protected PooledConnectionManager getConnectionManager(final UserPassKey userPassKey) {
- return factory;
- }
-
- /**
- * Gets {@link GenericKeyedObjectPool#getMaxTotal()} for this pool.
- *
- * @return {@link GenericKeyedObjectPool#getMaxTotal()} for this pool.
- */
- public int getMaxTotal() {
- return this.maxTotal;
- }
-
- // ----------------------------------------------------------------------
- // Instrumentation Methods
-
- /**
- * Gets the number of active connections in the pool.
- *
- * @return The number of active connections in the pool.
- */
- public int getNumActive() {
- return pool == null ? 0 : pool.getNumActive();
- }
-
- /**
- * Gets the number of idle connections in the pool.
- *
- * @return The number of idle connections in the pool.
- */
- public int getNumIdle() {
- return pool == null ? 0 : pool.getNumIdle();
- }
-
- @Override
- protected PooledConnectionAndInfo getPooledConnectionAndInfo(final String userName, final String userPassword)
- throws SQLException {
-
- synchronized (this) {
- if (pool == null) {
- try {
- registerPool(userName, userPassword);
- } catch (final NamingException e) {
- throw new SQLException("registerPool failed", e);
- }
- }
- }
-
- try {
- return pool.borrowObject(new UserPassKey(userName, userPassword));
- } catch (final Exception e) {
- throw new SQLException("Could not retrieve connection info from pool", e);
- }
- }
-
- /**
- * Creates a new {@link Reference} to a {@link SharedPoolDataSource}.
- */
- @Override
- public Reference getReference() throws NamingException {
- final Reference ref = new Reference(getClass().getName(), SharedPoolDataSourceFactory.class.getName(), null);
- ref.add(new StringRefAddr("instanceKey", getInstanceKey()));
- return ref;
- }
-
- /**
- * Supports Serialization interface.
- *
- * @param in
- * a {@code java.io.ObjectInputStream} value
- * @throws IOException
- * if an error occurs
- * @throws ClassNotFoundException
- * if an error occurs
- */
- private void readObject(final ObjectInputStream in) throws IOException, ClassNotFoundException {
- try {
- in.defaultReadObject();
- final SharedPoolDataSource oldDS = (SharedPoolDataSource) new SharedPoolDataSourceFactory().getObjectInstance(getReference(), null, null, null);
- this.pool = oldDS.pool;
- } catch (final NamingException e) {
- throw new IOException("NamingException: " + e);
- }
- }
-
- private void registerPool(final String userName, final String password) throws NamingException, SQLException {
-
- final ConnectionPoolDataSource cpds = testCPDS(userName, password);
-
- // Create an object pool to contain our PooledConnections
- factory = new KeyedCPDSConnectionFactory(cpds, getValidationQuery(), getValidationQueryTimeoutDuration(), isRollbackAfterValidation());
- factory.setMaxConn(getMaxConnDuration());
-
- final GenericKeyedObjectPoolConfig<PooledConnectionAndInfo> config = new GenericKeyedObjectPoolConfig<>();
- config.setBlockWhenExhausted(getDefaultBlockWhenExhausted());
- config.setEvictionPolicyClassName(getDefaultEvictionPolicyClassName());
- config.setLifo(getDefaultLifo());
- config.setMaxIdlePerKey(getDefaultMaxIdle());
- config.setMaxTotal(getMaxTotal());
- config.setMaxTotalPerKey(getDefaultMaxTotal());
- config.setMaxWait(getDefaultMaxWait());
- config.setMinEvictableIdleTime(getDefaultMinEvictableIdleDuration());
- config.setMinIdlePerKey(getDefaultMinIdle());
- config.setNumTestsPerEvictionRun(getDefaultNumTestsPerEvictionRun());
- config.setSoftMinEvictableIdleTime(getDefaultSoftMinEvictableIdleDuration());
- config.setTestOnCreate(getDefaultTestOnCreate());
- config.setTestOnBorrow(getDefaultTestOnBorrow());
- config.setTestOnReturn(getDefaultTestOnReturn());
- config.setTestWhileIdle(getDefaultTestWhileIdle());
- config.setTimeBetweenEvictionRuns(getDefaultDurationBetweenEvictionRuns());
-
- final KeyedObjectPool<UserPassKey, PooledConnectionAndInfo> tmpPool = new GenericKeyedObjectPool<>(factory, config);
- factory.setPool(tmpPool);
- pool = tmpPool;
- }
-
- /**
- * Sets {@link GenericKeyedObjectPool#getMaxTotal()} for this pool.
- *
- * @param maxTotal
- * {@link GenericKeyedObjectPool#getMaxTotal()} for this pool.
- */
- public void setMaxTotal(final int maxTotal) {
- assertInitializationAllowed();
- this.maxTotal = maxTotal;
- }
-
- @Override
- protected void setupDefaults(final Connection connection, final String userName) throws SQLException {
- final Boolean defaultAutoCommit = isDefaultAutoCommit();
- if (defaultAutoCommit != null && connection.getAutoCommit() != defaultAutoCommit) {
- connection.setAutoCommit(defaultAutoCommit);
- }
-
- final int defaultTransactionIsolation = getDefaultTransactionIsolation();
- if (defaultTransactionIsolation != UNKNOWN_TRANSACTIONISOLATION) {
- connection.setTransactionIsolation(defaultTransactionIsolation);
- }
-
- final Boolean defaultReadOnly = isDefaultReadOnly();
- if (defaultReadOnly != null && connection.isReadOnly() != defaultReadOnly) {
- connection.setReadOnly(defaultReadOnly);
- }
- }
-
- @Override
- protected void toStringFields(final StringBuilder builder) {
- super.toStringFields(builder);
- builder.append(", maxTotal=");
- builder.append(maxTotal);
- }
-}
+/*
+ * 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.commons.dbcp2.datasources;
+
+import java.io.IOException;
+import java.io.ObjectInputStream;
+import java.sql.Connection;
+import java.sql.SQLException;
+
+import javax.naming.NamingException;
+import javax.naming.Reference;
+import javax.naming.StringRefAddr;
+import javax.sql.ConnectionPoolDataSource;
+
+import org.apache.commons.pool2.KeyedObjectPool;
+import org.apache.commons.pool2.impl.GenericKeyedObjectPool;
+import org.apache.commons.pool2.impl.GenericKeyedObjectPoolConfig;
+
+/**
+ * <p>
+ * A pooling {@code DataSource} appropriate for deployment within J2EE environment. There are many configuration
+ * options, most of which are defined in the parent class. All users (based on user name) share a single maximum number
+ * of Connections in this data source.
+ * </p>
+ *
+ * <p>
+ * User passwords can be changed without re-initializing the data source. When a
+ * {@code getConnection(user name, password)} request is processed with a password that is different from those
+ * used to create connections in the pool associated with {@code user name}, an attempt is made to create a new
+ * connection using the supplied password and if this succeeds, idle connections created using the old password are
+ * destroyed and new connections are created using the new password.
+ * </p>
+ *
+ * @since 2.0
+ */
+public class SharedPoolDataSource extends InstanceKeyDataSource {
+
+ private static final long serialVersionUID = -1458539734480586454L;
+
+ // Pool properties
+ private int maxTotal = GenericKeyedObjectPoolConfig.DEFAULT_MAX_TOTAL;
+
+ private transient KeyedObjectPool<UserPassKey, PooledConnectionAndInfo> pool;
+ private transient KeyedCPDSConnectionFactory factory;
+
+ /**
+ * Default no-argument constructor for Serialization
+ */
+ public SharedPoolDataSource() {
+ // empty.
+ }
+
+ /**
+ * Closes pool being maintained by this data source.
+ */
+ @Override
+ public void close() throws Exception {
+ if (pool != null) {
+ pool.close();
+ }
+ InstanceKeyDataSourceFactory.removeInstance(getInstanceKey());
+ }
+
+ @Override
+ protected PooledConnectionManager getConnectionManager(final UserPassKey userPassKey) {
+ return factory;
+ }
+
+ /**
+ * Gets {@link GenericKeyedObjectPool#getMaxTotal()} for this pool.
+ *
+ * @return {@link GenericKeyedObjectPool#getMaxTotal()} for this pool.
+ */
+ public int getMaxTotal() {
+ return this.maxTotal;
+ }
+
+ // ----------------------------------------------------------------------
+ // Instrumentation Methods
+
+ /**
+ * Gets the number of active connections in the pool.
+ *
+ * @return The number of active connections in the pool.
+ */
+ public int getNumActive() {
+ return pool == null ? 0 : pool.getNumActive();
+ }
+
+ /**
+ * Gets the number of idle connections in the pool.
+ *
+ * @return The number of idle connections in the pool.
+ */
+ public int getNumIdle() {
+ return pool == null ? 0 : pool.getNumIdle();
+ }
+
+ @Override
+ protected PooledConnectionAndInfo getPooledConnectionAndInfo(final String userName, final String userPassword)
+ throws SQLException {
+
+ synchronized (this) {
+ if (pool == null) {
+ try {
+ registerPool(userName, userPassword);
+ } catch (final NamingException e) {
+ throw new SQLException("registerPool failed", e);
+ }
+ }
+ }
+
+ try {
+ return pool.borrowObject(new UserPassKey(userName, userPassword));
+ } catch (final Exception e) {
+ throw new SQLException("Could not retrieve connection info from pool", e);
+ }
+ }
+
+ /**
+ * Creates a new {@link Reference} to a {@link SharedPoolDataSource}.
+ */
+ @Override
+ public Reference getReference() throws NamingException {
+ final Reference ref = new Reference(getClass().getName(), SharedPoolDataSourceFactory.class.getName(), null);
+ ref.add(new StringRefAddr("instanceKey", getInstanceKey()));
+ return ref;
+ }
+
+ /**
+ * Supports Serialization interface.
+ *
+ * @param in
+ * a {@code java.io.ObjectInputStream} value
+ * @throws IOException
+ * if an error occurs
+ * @throws ClassNotFoundException
+ * if an error occurs
+ */
+ private void readObject(final ObjectInputStream in) throws IOException, ClassNotFoundException {
+ try {
+ in.defaultReadObject();
+ final SharedPoolDataSource oldDS = (SharedPoolDataSource) new SharedPoolDataSourceFactory().getObjectInstance(getReference(), null, null, null);
+ this.pool = oldDS.pool;
+ } catch (final NamingException e) {
+ throw new IOException("NamingException: " + e);
+ }
+ }
+
+ private void registerPool(final String userName, final String password) throws NamingException, SQLException {
+
+ final ConnectionPoolDataSource cpds = testCPDS(userName, password);
+
+ // Create an object pool to contain our PooledConnections
+ factory = new KeyedCPDSConnectionFactory(cpds, getValidationQuery(), getValidationQueryTimeoutDuration(), isRollbackAfterValidation());
+ factory.setMaxConn(getMaxConnDuration());
+
+ final GenericKeyedObjectPoolConfig<PooledConnectionAndInfo> config = new GenericKeyedObjectPoolConfig<>();
+ config.setBlockWhenExhausted(getDefaultBlockWhenExhausted());
+ config.setEvictionPolicyClassName(getDefaultEvictionPolicyClassName());
+ config.setLifo(getDefaultLifo());
+ config.setMaxIdlePerKey(getDefaultMaxIdle());
+ config.setMaxTotal(getMaxTotal());
+ config.setMaxTotalPerKey(getDefaultMaxTotal());
+ config.setMaxWait(getDefaultMaxWait());
+ config.setMinEvictableIdleTime(getDefaultMinEvictableIdleDuration());
+ config.setMinIdlePerKey(getDefaultMinIdle());
+ config.setNumTestsPerEvictionRun(getDefaultNumTestsPerEvictionRun());
+ config.setSoftMinEvictableIdleTime(getDefaultSoftMinEvictableIdleDuration());
+ config.setTestOnCreate(getDefaultTestOnCreate());
+ config.setTestOnBorrow(getDefaultTestOnBorrow());
+ config.setTestOnReturn(getDefaultTestOnReturn());
+ config.setTestWhileIdle(getDefaultTestWhileIdle());
+ config.setTimeBetweenEvictionRuns(getDefaultDurationBetweenEvictionRuns());
+
+ final KeyedObjectPool<UserPassKey, PooledConnectionAndInfo> tmpPool = new GenericKeyedObjectPool<>(factory, config);
+ factory.setPool(tmpPool);
+ pool = tmpPool;
+ }
+
+ /**
+ * Sets {@link GenericKeyedObjectPool#getMaxTotal()} for this pool.
+ *
+ * @param maxTotal
+ * {@link GenericKeyedObjectPool#getMaxTotal()} for this pool.
+ */
+ public void setMaxTotal(final int maxTotal) {
+ assertInitializationAllowed();
+ this.maxTotal = maxTotal;
+ }
+
+ @Override
+ protected void setupDefaults(final Connection connection, final String userName) throws SQLException {
+ final Boolean defaultAutoCommit = isDefaultAutoCommit();
+ if (defaultAutoCommit != null && connection.getAutoCommit() != defaultAutoCommit) {
+ connection.setAutoCommit(defaultAutoCommit);
+ }
+
+ final int defaultTransactionIsolation = getDefaultTransactionIsolation();
+ if (defaultTransactionIsolation != UNKNOWN_TRANSACTIONISOLATION) {
+ connection.setTransactionIsolation(defaultTransactionIsolation);
+ }
+
+ final Boolean defaultReadOnly = isDefaultReadOnly();
+ if (defaultReadOnly != null && connection.isReadOnly() != defaultReadOnly) {
+ connection.setReadOnly(defaultReadOnly);
+ }
+ }
+
+ @Override
+ protected void toStringFields(final StringBuilder builder) {
+ super.toStringFields(builder);
+ builder.append(", maxTotal=");
+ builder.append(maxTotal);
+ }
+}
diff --git a/src/main/java/org/apache/commons/dbcp2/managed/PoolableManagedConnectionFactory.java b/src/main/java/org/apache/commons/dbcp2/managed/PoolableManagedConnectionFactory.java
index e8791c0a..7eb81015 100644
--- a/src/main/java/org/apache/commons/dbcp2/managed/PoolableManagedConnectionFactory.java
+++ b/src/main/java/org/apache/commons/dbcp2/managed/PoolableManagedConnectionFactory.java
@@ -1,110 +1,111 @@
-/*
- * 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.commons.dbcp2.managed;
-
-import java.sql.Connection;
-import java.time.Duration;
-
-import javax.management.ObjectName;
-
-import org.apache.commons.dbcp2.Constants;
-import org.apache.commons.dbcp2.DelegatingPreparedStatement;
-import org.apache.commons.dbcp2.PStmtKey;
-import org.apache.commons.dbcp2.PoolableConnection;
-import org.apache.commons.dbcp2.PoolableConnectionFactory;
-import org.apache.commons.dbcp2.PoolingConnection;
-import org.apache.commons.pool2.KeyedObjectPool;
-import org.apache.commons.pool2.PooledObject;
-import org.apache.commons.pool2.impl.DefaultPooledObject;
-import org.apache.commons.pool2.impl.GenericKeyedObjectPool;
-import org.apache.commons.pool2.impl.GenericKeyedObjectPoolConfig;
-
-/**
- * A {@link PoolableConnectionFactory} that creates {@link PoolableManagedConnection}s.
- *
- * @since 2.0
- */
-public class PoolableManagedConnectionFactory extends PoolableConnectionFactory {
-
- /** Transaction registry associated with connections created by this factory */
- private final TransactionRegistry transactionRegistry;
-
- /**
- * Creates a PoolableManagedConnectionFactory and attach it to a connection pool.
- *
- * @param connFactory
- * XAConnectionFactory
- * @param dataSourceJmxName
- * The data source name.
- */
- public PoolableManagedConnectionFactory(final XAConnectionFactory connFactory, final ObjectName dataSourceJmxName) {
- super(connFactory, dataSourceJmxName);
- this.transactionRegistry = connFactory.getTransactionRegistry();
- }
-
- /**
- * @return The transaction registry.
- * @since 2.6.0
- */
- public TransactionRegistry getTransactionRegistry() {
- return transactionRegistry;
- }
-
- /**
- * Uses the configured XAConnectionFactory to create a {@link PoolableManagedConnection}. Throws
- * {@code IllegalStateException} if the connection factory returns null. Also initializes the connection using
- * configured initialization SQL (if provided) and sets up a prepared statement pool associated with the
- * PoolableManagedConnection if statement pooling is enabled.
- */
- @SuppressWarnings("resource") // Connection is released elsewhere.
- @Override
- public synchronized PooledObject<PoolableConnection> makeObject() throws Exception {
- Connection conn = getConnectionFactory().createConnection();
- if (conn == null) {
- throw new IllegalStateException("Connection factory returned null from createConnection");
- }
- initializeConnection(conn);
- if (getPoolStatements()) {
- conn = new PoolingConnection(conn);
- final GenericKeyedObjectPoolConfig<DelegatingPreparedStatement> config = new GenericKeyedObjectPoolConfig<>();
- config.setMaxTotalPerKey(-1);
- config.setBlockWhenExhausted(false);
- config.setMaxWait(Duration.ZERO);
- config.setMaxIdlePerKey(1);
- config.setMaxTotal(getMaxOpenPreparedStatements());
- final ObjectName dataSourceJmxName = getDataSourceJmxName();
- final long connIndex = getConnectionIndex().getAndIncrement();
- if (dataSourceJmxName != null) {
- final StringBuilder base = new StringBuilder(dataSourceJmxName.toString());
- base.append(Constants.JMX_CONNECTION_BASE_EXT);
- base.append(connIndex);
- config.setJmxNameBase(base.toString());
- config.setJmxNamePrefix(Constants.JMX_STATEMENT_POOL_PREFIX);
- } else {
- config.setJmxEnabled(false);
- }
- final KeyedObjectPool<PStmtKey, DelegatingPreparedStatement> stmtPool = new GenericKeyedObjectPool<>(
- (PoolingConnection) conn, config);
- ((PoolingConnection) conn).setStatementPool(stmtPool);
- ((PoolingConnection) conn).setCacheState(getCacheState());
- }
- final PoolableManagedConnection pmc = new PoolableManagedConnection(transactionRegistry, conn, getPool(),
- getDisconnectionSqlCodes(), isFastFailValidation());
- pmc.setCacheState(getCacheState());
- return new DefaultPooledObject<>(pmc);
- }
-}
+/*
+ * 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.commons.dbcp2.managed;
+
+import java.sql.Connection;
+import java.sql.SQLException;
+import java.time.Duration;
+
+import javax.management.ObjectName;
+
+import org.apache.commons.dbcp2.Constants;
+import org.apache.commons.dbcp2.DelegatingPreparedStatement;
+import org.apache.commons.dbcp2.PStmtKey;
+import org.apache.commons.dbcp2.PoolableConnection;
+import org.apache.commons.dbcp2.PoolableConnectionFactory;
+import org.apache.commons.dbcp2.PoolingConnection;
+import org.apache.commons.pool2.KeyedObjectPool;
+import org.apache.commons.pool2.PooledObject;
+import org.apache.commons.pool2.impl.DefaultPooledObject;
+import org.apache.commons.pool2.impl.GenericKeyedObjectPool;
+import org.apache.commons.pool2.impl.GenericKeyedObjectPoolConfig;
+
+/**
+ * A {@link PoolableConnectionFactory} that creates {@link PoolableManagedConnection}s.
+ *
+ * @since 2.0
+ */
+public class PoolableManagedConnectionFactory extends PoolableConnectionFactory {
+
+ /** Transaction registry associated with connections created by this factory */
+ private final TransactionRegistry transactionRegistry;
+
+ /**
+ * Creates a PoolableManagedConnectionFactory and attach it to a connection pool.
+ *
+ * @param connFactory
+ * XAConnectionFactory
+ * @param dataSourceJmxName
+ * The data source name.
+ */
+ public PoolableManagedConnectionFactory(final XAConnectionFactory connFactory, final ObjectName dataSourceJmxName) {
+ super(connFactory, dataSourceJmxName);
+ this.transactionRegistry = connFactory.getTransactionRegistry();
+ }
+
+ /**
+ * @return The transaction registry.
+ * @since 2.6.0
+ */
+ public TransactionRegistry getTransactionRegistry() {
+ return transactionRegistry;
+ }
+
+ /**
+ * Uses the configured XAConnectionFactory to create a {@link PoolableManagedConnection}. Throws
+ * {@code IllegalStateException} if the connection factory returns null. Also initializes the connection using
+ * configured initialization SQL (if provided) and sets up a prepared statement pool associated with the
+ * PoolableManagedConnection if statement pooling is enabled.
+ */
+ @SuppressWarnings("resource") // Connection is released elsewhere.
+ @Override
+ public synchronized PooledObject<PoolableConnection> makeObject() throws SQLException {
+ Connection conn = getConnectionFactory().createConnection();
+ if (conn == null) {
+ throw new IllegalStateException("Connection factory returned null from createConnection");
+ }
+ initializeConnection(conn);
+ if (getPoolStatements()) {
+ conn = new PoolingConnection(conn);
+ final GenericKeyedObjectPoolConfig<DelegatingPreparedStatement> config = new GenericKeyedObjectPoolConfig<>();
+ config.setMaxTotalPerKey(-1);
+ config.setBlockWhenExhausted(false);
+ config.setMaxWait(Duration.ZERO);
+ config.setMaxIdlePerKey(1);
+ config.setMaxTotal(getMaxOpenPreparedStatements());
+ final ObjectName dataSourceJmxName = getDataSourceJmxName();
+ final long connIndex = getConnectionIndex().getAndIncrement();
+ if (dataSourceJmxName != null) {
+ final StringBuilder base = new StringBuilder(dataSourceJmxName.toString());
+ base.append(Constants.JMX_CONNECTION_BASE_EXT);
+ base.append(connIndex);
+ config.setJmxNameBase(base.toString());
+ config.setJmxNamePrefix(Constants.JMX_STATEMENT_POOL_PREFIX);
+ } else {
+ config.setJmxEnabled(false);
+ }
+ final KeyedObjectPool<PStmtKey, DelegatingPreparedStatement> stmtPool = new GenericKeyedObjectPool<>(
+ (PoolingConnection) conn, config);
+ ((PoolingConnection) conn).setStatementPool(stmtPool);
+ ((PoolingConnection) conn).setCacheState(getCacheState());
+ }
+ final PoolableManagedConnection pmc = new PoolableManagedConnection(transactionRegistry, conn, getPool(),
+ getDisconnectionSqlCodes(), isFastFailValidation());
+ pmc.setCacheState(getCacheState());
+ return new DefaultPooledObject<>(pmc);
+ }
+}