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 13:24:38 UTC
[commons-dbcp] 02/07: Suppress PMD EmptyCatchBlock from ruleset Error Prone
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
commit d978d4635a18c45fc152b48a109dcae2606e4240
Author: Gary Gregory <ga...@gmail.com>
AuthorDate: Sat Jul 2 08:55:55 2022 -0400
Suppress PMD EmptyCatchBlock from ruleset Error Prone
---
.../apache/commons/dbcp2/DelegatingConnection.java | 2122 ++++++++--------
.../org/apache/commons/dbcp2/Jdbc41Bridge.java | 976 +++----
.../apache/commons/dbcp2/PoolableConnection.java | 808 +++---
.../apache/commons/dbcp2/PoolingConnection.java | 1246 ++++-----
.../org/apache/commons/dbcp2/PoolingDriver.java | 520 ++--
src/main/java/org/apache/commons/dbcp2/Utils.java | 412 +--
.../dbcp2/datasources/InstanceKeyDataSource.java | 2662 ++++++++++----------
.../datasources/InstanceKeyDataSourceFactory.java | 696 ++---
.../dbcp2/datasources/PerUserPoolDataSource.java | 2636 +++++++++----------
.../dbcp2/managed/LocalXAConnectionFactory.java | 758 +++---
.../commons/dbcp2/managed/ManagedConnection.java | 656 ++---
.../commons/dbcp2/managed/TransactionContext.java | 420 +--
12 files changed, 6956 insertions(+), 6956 deletions(-)
diff --git a/src/main/java/org/apache/commons/dbcp2/DelegatingConnection.java b/src/main/java/org/apache/commons/dbcp2/DelegatingConnection.java
index 1199c96d..62163a03 100644
--- a/src/main/java/org/apache/commons/dbcp2/DelegatingConnection.java
+++ b/src/main/java/org/apache/commons/dbcp2/DelegatingConnection.java
@@ -1,1061 +1,1061 @@
-/*
- * 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;
-
-import java.sql.Array;
-import java.sql.Blob;
-import java.sql.CallableStatement;
-import java.sql.ClientInfoStatus;
-import java.sql.Clob;
-import java.sql.Connection;
-import java.sql.DatabaseMetaData;
-import java.sql.NClob;
-import java.sql.PreparedStatement;
-import java.sql.ResultSet;
-import java.sql.SQLClientInfoException;
-import java.sql.SQLException;
-import java.sql.SQLWarning;
-import java.sql.SQLXML;
-import java.sql.Savepoint;
-import java.sql.Statement;
-import java.sql.Struct;
-import java.time.Duration;
-import java.time.Instant;
-import java.util.ArrayList;
-import java.util.Collections;
-import java.util.List;
-import java.util.Map;
-import java.util.Properties;
-import java.util.concurrent.Executor;
-
-/**
- * A base delegating implementation of {@link Connection}.
- * <p>
- * All of the methods from the {@link Connection} interface simply check to see that the {@link Connection} is active,
- * and call the corresponding method on the "delegate" provided in my constructor.
- * </p>
- * <p>
- * Extends AbandonedTrace to implement Connection tracking and logging of code which created the Connection. Tracking
- * the Connection ensures that the AbandonedObjectPool can close this connection and recycle it if its pool of
- * connections is nearing exhaustion and this connection's last usage is older than the removeAbandonedTimeout.
- * </p>
- *
- * @param <C>
- * the Connection type
- *
- * @since 2.0
- */
-public class DelegatingConnection<C extends Connection> extends AbandonedTrace implements Connection {
-
- private static final Map<String, ClientInfoStatus> EMPTY_FAILED_PROPERTIES = Collections
- .<String, ClientInfoStatus>emptyMap();
-
- /** My delegate {@link Connection}. */
- private volatile C connection;
-
- private volatile boolean closed;
-
- private boolean cacheState = true;
- private Boolean cachedAutoCommit;
- private Boolean cachedReadOnly;
- private String cachedCatalog;
- private String cachedSchema;
- private Duration defaultQueryTimeoutDuration;
-
- /**
- * Creates a wrapper for the Connection which traces this Connection in the AbandonedObjectPool.
- *
- * @param connection the {@link Connection} to delegate all calls to.
- */
- public DelegatingConnection(final C connection) {
- this.connection = connection;
- }
-
- @Override
- public void abort(final Executor executor) throws SQLException {
- try {
- Jdbc41Bridge.abort(connection, executor);
- } catch (final SQLException e) {
- handleException(e);
- }
- }
-
- protected void activate() {
- closed = false;
- setLastUsed();
- if (connection instanceof DelegatingConnection) {
- ((DelegatingConnection<?>) connection).activate();
- }
- }
-
- protected void checkOpen() throws SQLException {
- if (closed) {
- if (null != connection) {
- String label = "";
- try {
- label = connection.toString();
- } catch (final Exception ex) {
- // ignore, leave label empty
- }
- throw new SQLException("Connection " + label + " is closed.");
- }
- throw new SQLException("Connection is null.");
- }
- }
-
- /**
- * Clears the cached state. Call when you known that the underlying connection may have been accessed
- * directly.
- */
- public void clearCachedState() {
- cachedAutoCommit = null;
- cachedReadOnly = null;
- cachedSchema = null;
- cachedCatalog = null;
- if (connection instanceof DelegatingConnection) {
- ((DelegatingConnection<?>) connection).clearCachedState();
- }
- }
-
- @Override
- public void clearWarnings() throws SQLException {
- checkOpen();
- try {
- connection.clearWarnings();
- } catch (final SQLException e) {
- handleException(e);
- }
- }
-
- /**
- * Closes the underlying connection, and close any Statements that were not explicitly closed. Sub-classes that
- * override this method must:
- * <ol>
- * <li>Call {@link #passivate()}</li>
- * <li>Call close (or the equivalent appropriate action) on the wrapped connection</li>
- * <li>Set {@code closed} to {@code false}</li>
- * </ol>
- */
- @Override
- public void close() throws SQLException {
- if (!closed) {
- closeInternal();
- }
- }
-
- protected final void closeInternal() throws SQLException {
- try {
- passivate();
- } finally {
- if (connection != null) {
- boolean connectionIsClosed;
- try {
- connectionIsClosed = connection.isClosed();
- } catch (final SQLException e) {
- // not sure what the state is, so assume the connection is open.
- connectionIsClosed = false;
- }
- try {
- // DBCP-512: Avoid exceptions when closing a connection in mutli-threaded use case.
- // Avoid closing again, which should be a no-op, but some drivers like H2 throw an exception when
- // closing from multiple threads.
- if (!connectionIsClosed) {
- connection.close();
- }
- } finally {
- closed = true;
- }
- } else {
- closed = true;
- }
- }
- }
-
- @Override
- public void commit() throws SQLException {
- checkOpen();
- try {
- connection.commit();
- } catch (final SQLException e) {
- handleException(e);
- }
- }
-
- @Override
- public Array createArrayOf(final String typeName, final Object[] elements) throws SQLException {
- checkOpen();
- try {
- return connection.createArrayOf(typeName, elements);
- } catch (final SQLException e) {
- handleException(e);
- return null;
- }
- }
-
- @Override
- public Blob createBlob() throws SQLException {
- checkOpen();
- try {
- return connection.createBlob();
- } catch (final SQLException e) {
- handleException(e);
- return null;
- }
- }
-
- @Override
- public Clob createClob() throws SQLException {
- checkOpen();
- try {
- return connection.createClob();
- } catch (final SQLException e) {
- handleException(e);
- return null;
- }
- }
-
- @Override
- public NClob createNClob() throws SQLException {
- checkOpen();
- try {
- return connection.createNClob();
- } catch (final SQLException e) {
- handleException(e);
- return null;
- }
- }
-
- @Override
- public SQLXML createSQLXML() throws SQLException {
- checkOpen();
- try {
- return connection.createSQLXML();
- } catch (final SQLException e) {
- handleException(e);
- return null;
- }
- }
-
- @SuppressWarnings("resource") // Caller is responsible for closing the resource.
- @Override
- public Statement createStatement() throws SQLException {
- checkOpen();
- try {
- return init(new DelegatingStatement(this, connection.createStatement()));
- } catch (final SQLException e) {
- handleException(e);
- return null;
- }
- }
-
- @SuppressWarnings("resource") // Caller is responsible for closing the resource.
- @Override
- public Statement createStatement(final int resultSetType, final int resultSetConcurrency) throws SQLException {
- checkOpen();
- try {
- return init(new DelegatingStatement(this, connection.createStatement(resultSetType, resultSetConcurrency)));
- } catch (final SQLException e) {
- handleException(e);
- return null;
- }
- }
-
- @SuppressWarnings("resource") // Caller is responsible for closing the resource.
- @Override
- public Statement createStatement(final int resultSetType, final int resultSetConcurrency,
- final int resultSetHoldability) throws SQLException {
- checkOpen();
- try {
- return init(new DelegatingStatement(this,
- connection.createStatement(resultSetType, resultSetConcurrency, resultSetHoldability)));
- } catch (final SQLException e) {
- handleException(e);
- return null;
- }
- }
-
- @Override
- public Struct createStruct(final String typeName, final Object[] attributes) throws SQLException {
- checkOpen();
- try {
- return connection.createStruct(typeName, attributes);
- } catch (final SQLException e) {
- handleException(e);
- return null;
- }
- }
-
- @Override
- public boolean getAutoCommit() throws SQLException {
- checkOpen();
- if (cacheState && cachedAutoCommit != null) {
- return cachedAutoCommit;
- }
- try {
- cachedAutoCommit = connection.getAutoCommit();
- return cachedAutoCommit;
- } catch (final SQLException e) {
- handleException(e);
- return false;
- }
- }
-
- /**
- * Returns the state caching flag.
- *
- * @return the state caching flag
- */
- public boolean getCacheState() {
- return cacheState;
- }
-
- @Override
- public String getCatalog() throws SQLException {
- checkOpen();
- if (cacheState && cachedCatalog != null) {
- return cachedCatalog;
- }
- try {
- cachedCatalog = connection.getCatalog();
- return cachedCatalog;
- } catch (final SQLException e) {
- handleException(e);
- return null;
- }
- }
-
- @Override
- public Properties getClientInfo() throws SQLException {
- checkOpen();
- try {
- return connection.getClientInfo();
- } catch (final SQLException e) {
- handleException(e);
- return null;
- }
- }
-
- @Override
- public String getClientInfo(final String name) throws SQLException {
- checkOpen();
- try {
- return connection.getClientInfo(name);
- } catch (final SQLException e) {
- handleException(e);
- return null;
- }
- }
-
- /**
- * Gets the default query timeout that will be used for {@link Statement}s created from this connection.
- * {@code null} means that the driver default will be used.
- *
- * @return query timeout limit in seconds; zero means there is no limit.
- * @deprecated Use {@link #getDefaultQueryTimeoutDuration()}.
- */
- @Deprecated
- public Integer getDefaultQueryTimeout() {
- return defaultQueryTimeoutDuration == null ? null : (int) defaultQueryTimeoutDuration.getSeconds();
- }
-
- /**
- * Gets the default query timeout that will be used for {@link Statement}s created from this connection.
- * {@code null} means that the driver default will be used.
- *
- * @return query timeout limit; zero means there is no limit.
- * @since 2.10.0
- */
- public Duration getDefaultQueryTimeoutDuration() {
- return defaultQueryTimeoutDuration;
- }
-
- /**
- * Returns my underlying {@link Connection}.
- *
- * @return my underlying {@link Connection}.
- */
- public C getDelegate() {
- return getDelegateInternal();
- }
-
- /**
- * Gets the delegate connection.
- *
- * @return the delegate connection.
- */
- protected final C getDelegateInternal() {
- return connection;
- }
-
- @Override
- public int getHoldability() throws SQLException {
- checkOpen();
- try {
- return connection.getHoldability();
- } catch (final SQLException e) {
- handleException(e);
- return 0;
- }
- }
-
- /**
- * If my underlying {@link Connection} is not a {@code DelegatingConnection}, returns it, otherwise recursively
- * invokes this method on my delegate.
- * <p>
- * Hence this method will return the first delegate that is not a {@code DelegatingConnection}, or {@code null} when
- * no non-{@code DelegatingConnection} delegate can be found by traversing this chain.
- * </p>
- * <p>
- * This method is useful when you may have nested {@code DelegatingConnection}s, and you want to make sure to obtain
- * a "genuine" {@link Connection}.
- * </p>
- *
- * @return innermost delegate.
- */
- public Connection getInnermostDelegate() {
- return getInnermostDelegateInternal();
- }
-
- /**
- * Although this method is public, it is part of the internal API and should not be used by clients. The signature
- * of this method may change at any time including in ways that break backwards compatibility.
- *
- * @return innermost delegate.
- */
- @SuppressWarnings("resource")
- public final Connection getInnermostDelegateInternal() {
- Connection conn = connection;
- while (conn instanceof DelegatingConnection) {
- conn = ((DelegatingConnection<?>) conn).getDelegateInternal();
- if (this == conn) {
- return null;
- }
- }
- return conn;
- }
-
- @Override
- public DatabaseMetaData getMetaData() throws SQLException {
- checkOpen();
- try {
- return new DelegatingDatabaseMetaData(this, connection.getMetaData());
- } catch (final SQLException e) {
- handleException(e);
- return null;
- }
- }
-
- @Override
- public int getNetworkTimeout() throws SQLException {
- checkOpen();
- try {
- return Jdbc41Bridge.getNetworkTimeout(connection);
- } catch (final SQLException e) {
- handleException(e);
- return 0;
- }
- }
-
- @Override
- public String getSchema() throws SQLException {
- checkOpen();
- if (cacheState && cachedSchema != null) {
- return cachedSchema;
- }
- try {
- cachedSchema = Jdbc41Bridge.getSchema(connection);
- return cachedSchema;
- } catch (final SQLException e) {
- handleException(e);
- return null;
- }
- }
-
- @Override
- public int getTransactionIsolation() throws SQLException {
- checkOpen();
- try {
- return connection.getTransactionIsolation();
- } catch (final SQLException e) {
- handleException(e);
- return -1;
- }
- }
-
- @Override
- public Map<String, Class<?>> getTypeMap() throws SQLException {
- checkOpen();
- try {
- return connection.getTypeMap();
- } catch (final SQLException e) {
- handleException(e);
- return null;
- }
- }
-
- @Override
- public SQLWarning getWarnings() throws SQLException {
- checkOpen();
- try {
- return connection.getWarnings();
- } catch (final SQLException e) {
- handleException(e);
- return null;
- }
- }
-
- /**
- * Handles the given exception by throwing it.
- *
- * @param e the exception to throw.
- * @throws SQLException the exception to throw.
- */
- protected void handleException(final SQLException e) throws SQLException {
- throw e;
- }
-
- /**
- * Handles the given {@code SQLException}.
- *
- * @param <T> The throwable type.
- * @param e The SQLException
- * @return the given {@code SQLException}
- * @since 2.7.0
- */
- protected <T extends Throwable> T handleExceptionNoThrow(final T e) {
- return e;
- }
-
- /**
- * Initializes the given statement with this connection's settings.
- *
- * @param <T> The DelegatingStatement type.
- * @param delegatingStatement The DelegatingStatement to initialize.
- * @return The given DelegatingStatement.
- * @throws SQLException if a database access error occurs, this method is called on a closed Statement.
- */
- private <T extends DelegatingStatement> T init(final T delegatingStatement) throws SQLException {
- if (defaultQueryTimeoutDuration != null && defaultQueryTimeoutDuration.getSeconds() != delegatingStatement.getQueryTimeout()) {
- delegatingStatement.setQueryTimeout((int) defaultQueryTimeoutDuration.getSeconds());
- }
- return delegatingStatement;
- }
-
- /**
- * Compares innermost delegate to the given connection.
- *
- * @param c
- * connection to compare innermost delegate with
- * @return true if innermost delegate equals {@code c}
- */
- @SuppressWarnings("resource")
- public boolean innermostDelegateEquals(final Connection c) {
- final Connection innerCon = getInnermostDelegateInternal();
- if (innerCon == null) {
- return c == null;
- }
- return innerCon.equals(c);
- }
-
- @Override
- public boolean isClosed() throws SQLException {
- return closed || connection == null || connection.isClosed();
- }
-
- protected boolean isClosedInternal() {
- return closed;
- }
-
- @Override
- public boolean isReadOnly() throws SQLException {
- checkOpen();
- if (cacheState && cachedReadOnly != null) {
- return cachedReadOnly;
- }
- try {
- cachedReadOnly = connection.isReadOnly();
- return cachedReadOnly;
- } catch (final SQLException e) {
- handleException(e);
- return false;
- }
- }
-
- /**
- * Tests if the connection has not been closed and is still valid.
- *
- * @param timeout The duration to wait for the database operation used to validate the connection to complete.
- * @return See {@link Connection#isValid(int)}.
- * @throws SQLException See {@link Connection#isValid(int)}.
- * @see Connection#isValid(int)
- * @since 2.10.0
- */
- public boolean isValid(final Duration timeout) throws SQLException {
- if (isClosed()) {
- return false;
- }
- try {
- return connection.isValid((int) timeout.getSeconds());
- } catch (final SQLException e) {
- handleException(e);
- return false;
- }
- }
-
- /**
- * @deprecated Use {@link #isValid(Duration)}.
- */
- @Override
- @Deprecated
- public boolean isValid(final int timeoutSeconds) throws SQLException {
- return isValid(Duration.ofSeconds(timeoutSeconds));
- }
-
- @Override
- public boolean isWrapperFor(final Class<?> iface) throws SQLException {
- if (iface.isAssignableFrom(getClass())) {
- return true;
- }
- if (iface.isAssignableFrom(connection.getClass())) {
- return true;
- }
- return connection.isWrapperFor(iface);
- }
-
- @Override
- public String nativeSQL(final String sql) throws SQLException {
- checkOpen();
- try {
- return connection.nativeSQL(sql);
- } catch (final SQLException e) {
- handleException(e);
- return null;
- }
- }
-
- protected void passivate() throws SQLException {
- // The JDBC specification requires that a Connection close any open
- // Statement's when it is closed.
- // DBCP-288. Not all the traced objects will be statements
- final List<AbandonedTrace> traces = getTrace();
- if (traces != null && !traces.isEmpty()) {
- final List<Exception> thrownList = new ArrayList<>();
- for (final Object trace : traces) {
- if (trace instanceof Statement) {
- try {
- ((Statement) trace).close();
- } catch (final Exception e) {
- thrownList.add(e);
- }
- } else if (trace instanceof ResultSet) {
- // DBCP-265: Need to close the result sets that are
- // generated via DatabaseMetaData
- try {
- ((ResultSet) trace).close();
- } catch (final Exception e) {
- thrownList.add(e);
- }
- }
- }
- clearTrace();
- if (!thrownList.isEmpty()) {
- throw new SQLExceptionList(thrownList);
- }
- }
- setLastUsed(Instant.EPOCH);
- }
-
- @SuppressWarnings("resource") // Caller is responsible for closing the resource.
- @Override
- public CallableStatement prepareCall(final String sql) throws SQLException {
- checkOpen();
- try {
- return init(new DelegatingCallableStatement(this, connection.prepareCall(sql)));
- } catch (final SQLException e) {
- handleException(e);
- return null;
- }
- }
-
- @SuppressWarnings("resource") // Caller is responsible for closing the resource.
- @Override
- public CallableStatement prepareCall(final String sql, final int resultSetType, final int resultSetConcurrency)
- throws SQLException {
- checkOpen();
- try {
- return init(new DelegatingCallableStatement(this,
- connection.prepareCall(sql, resultSetType, resultSetConcurrency)));
- } catch (final SQLException e) {
- handleException(e);
- return null;
- }
- }
-
- @SuppressWarnings("resource") // Caller is responsible for closing the resource.
- @Override
- public CallableStatement prepareCall(final String sql, final int resultSetType, final int resultSetConcurrency,
- final int resultSetHoldability) throws SQLException {
- checkOpen();
- try {
- return init(new DelegatingCallableStatement(this,
- connection.prepareCall(sql, resultSetType, resultSetConcurrency, resultSetHoldability)));
- } catch (final SQLException e) {
- handleException(e);
- return null;
- }
- }
-
- @SuppressWarnings("resource") // Caller is responsible for closing the resource.
- @Override
- public PreparedStatement prepareStatement(final String sql) throws SQLException {
- checkOpen();
- try {
- return init(new DelegatingPreparedStatement(this, connection.prepareStatement(sql)));
- } catch (final SQLException e) {
- handleException(e);
- return null;
- }
- }
-
- @SuppressWarnings("resource") // Caller is responsible for closing the resource.
- @Override
- public PreparedStatement prepareStatement(final String sql, final int autoGeneratedKeys) throws SQLException {
- checkOpen();
- try {
- return init(new DelegatingPreparedStatement(this, connection.prepareStatement(sql, autoGeneratedKeys)));
- } catch (final SQLException e) {
- handleException(e);
- return null;
- }
- }
-
- @SuppressWarnings("resource") // Caller is responsible for closing the resource.
- @Override
- public PreparedStatement prepareStatement(final String sql, final int resultSetType, final int resultSetConcurrency)
- throws SQLException {
- checkOpen();
- try {
- return init(new DelegatingPreparedStatement(this,
- connection.prepareStatement(sql, resultSetType, resultSetConcurrency)));
- } catch (final SQLException e) {
- handleException(e);
- return null;
- }
- }
-
- @SuppressWarnings("resource") // Caller is responsible for closing the resource.
- @Override
- public PreparedStatement prepareStatement(final String sql, final int resultSetType, final int resultSetConcurrency,
- final int resultSetHoldability) throws SQLException {
- checkOpen();
- try {
- return init(new DelegatingPreparedStatement(this,
- connection.prepareStatement(sql, resultSetType, resultSetConcurrency, resultSetHoldability)));
- } catch (final SQLException e) {
- handleException(e);
- return null;
- }
- }
-
- @SuppressWarnings("resource") // Caller is responsible for closing the resource.
- @Override
- public PreparedStatement prepareStatement(final String sql, final int[] columnIndexes) throws SQLException {
- checkOpen();
- try {
- return init(new DelegatingPreparedStatement(this, connection.prepareStatement(sql, columnIndexes)));
- } catch (final SQLException e) {
- handleException(e);
- return null;
- }
- }
-
- @SuppressWarnings("resource") // Caller is responsible for closing the resource.
- @Override
- public PreparedStatement prepareStatement(final String sql, final String[] columnNames) throws SQLException {
- checkOpen();
- try {
- return init(new DelegatingPreparedStatement(this, connection.prepareStatement(sql, columnNames)));
- } catch (final SQLException e) {
- handleException(e);
- return null;
- }
- }
-
- @Override
- public void releaseSavepoint(final Savepoint savepoint) throws SQLException {
- checkOpen();
- try {
- connection.releaseSavepoint(savepoint);
- } catch (final SQLException e) {
- handleException(e);
- }
- }
-
- @Override
- public void rollback() throws SQLException {
- checkOpen();
- try {
- connection.rollback();
- } catch (final SQLException e) {
- handleException(e);
- }
- }
-
- @Override
- public void rollback(final Savepoint savepoint) throws SQLException {
- checkOpen();
- try {
- connection.rollback(savepoint);
- } catch (final SQLException e) {
- handleException(e);
- }
- }
-
- @Override
- public void setAutoCommit(final boolean autoCommit) throws SQLException {
- checkOpen();
- try {
- connection.setAutoCommit(autoCommit);
- if (cacheState) {
- cachedAutoCommit = connection.getAutoCommit();
- }
- } catch (final SQLException e) {
- cachedAutoCommit = null;
- handleException(e);
- }
- }
-
- /**
- * Sets the state caching flag.
- *
- * @param cacheState
- * The new value for the state caching flag
- */
- public void setCacheState(final boolean cacheState) {
- this.cacheState = cacheState;
- }
-
- @Override
- public void setCatalog(final String catalog) throws SQLException {
- checkOpen();
- try {
- connection.setCatalog(catalog);
- if (cacheState) {
- cachedCatalog = connection.getCatalog();
- }
- } catch (final SQLException e) {
- cachedCatalog = null;
- handleException(e);
- }
- }
-
- @Override
- public void setClientInfo(final Properties properties) throws SQLClientInfoException {
- try {
- checkOpen();
- connection.setClientInfo(properties);
- } catch (final SQLClientInfoException e) {
- throw e;
- } catch (final SQLException e) {
- throw new SQLClientInfoException("Connection is closed.", EMPTY_FAILED_PROPERTIES, e);
- }
- }
-
- @Override
- public void setClientInfo(final String name, final String value) throws SQLClientInfoException {
- try {
- checkOpen();
- connection.setClientInfo(name, value);
- } catch (final SQLClientInfoException e) {
- throw e;
- } catch (final SQLException e) {
- throw new SQLClientInfoException("Connection is closed.", EMPTY_FAILED_PROPERTIES, e);
- }
- }
-
- protected void setClosedInternal(final boolean closed) {
- this.closed = closed;
- }
-
- /**
- * Sets the default query timeout that will be used for {@link Statement}s created from this connection.
- * {@code null} means that the driver default will be used.
- *
- * @param defaultQueryTimeoutDuration
- * the new query timeout limit Duration; zero means there is no limit.
- * @since 2.10.0
- */
- public void setDefaultQueryTimeout(final Duration defaultQueryTimeoutDuration) {
- this.defaultQueryTimeoutDuration = defaultQueryTimeoutDuration;
- }
-
- /**
- * Sets the default query timeout that will be used for {@link Statement}s created from this connection.
- * {@code null} means that the driver default will be used.
- *
- * @param defaultQueryTimeoutSeconds
- * the new query timeout limit in seconds; zero means there is no limit.
- * @deprecated Use {@link #setDefaultQueryTimeout(Duration)}.
- */
- @Deprecated
- public void setDefaultQueryTimeout(final Integer defaultQueryTimeoutSeconds) {
- this.defaultQueryTimeoutDuration = defaultQueryTimeoutSeconds == null ? null : Duration.ofSeconds(defaultQueryTimeoutSeconds);
- }
-
- /**
- * Sets my delegate.
- *
- * @param connection
- * my delegate.
- */
- public void setDelegate(final C connection) {
- this.connection = connection;
- }
-
- @Override
- public void setHoldability(final int holdability) throws SQLException {
- checkOpen();
- try {
- connection.setHoldability(holdability);
- } catch (final SQLException e) {
- handleException(e);
- }
- }
-
- @Override
- public void setNetworkTimeout(final Executor executor, final int milliseconds) throws SQLException {
- checkOpen();
- try {
- Jdbc41Bridge.setNetworkTimeout(connection, executor, milliseconds);
- } catch (final SQLException e) {
- handleException(e);
- }
- }
-
- @Override
- public void setReadOnly(final boolean readOnly) throws SQLException {
- checkOpen();
- try {
- connection.setReadOnly(readOnly);
- if (cacheState) {
- cachedReadOnly = connection.isReadOnly();
- }
- } catch (final SQLException e) {
- cachedReadOnly = null;
- handleException(e);
- }
- }
-
- @Override
- public Savepoint setSavepoint() throws SQLException {
- checkOpen();
- try {
- return connection.setSavepoint();
- } catch (final SQLException e) {
- handleException(e);
- return null;
- }
- }
-
- @Override
- public Savepoint setSavepoint(final String name) throws SQLException {
- checkOpen();
- try {
- return connection.setSavepoint(name);
- } catch (final SQLException e) {
- handleException(e);
- return null;
- }
- }
-
- @Override
- public void setSchema(final String schema) throws SQLException {
- checkOpen();
- try {
- Jdbc41Bridge.setSchema(connection, schema);
- if (cacheState) {
- cachedSchema = connection.getSchema();
- }
- } catch (final SQLException e) {
- cachedSchema = null;
- handleException(e);
- }
- }
-
- @Override
- public void setTransactionIsolation(final int level) throws SQLException {
- checkOpen();
- try {
- connection.setTransactionIsolation(level);
- } catch (final SQLException e) {
- handleException(e);
- }
- }
-
- @Override
- public void setTypeMap(final Map<String, Class<?>> map) throws SQLException {
- checkOpen();
- try {
- connection.setTypeMap(map);
- } catch (final SQLException e) {
- handleException(e);
- }
- }
-
- /**
- * Returns a string representation of the metadata associated with the innermost delegate connection.
- */
- @SuppressWarnings("resource")
- @Override
- public synchronized String toString() {
- String str = null;
-
- final Connection conn = this.getInnermostDelegateInternal();
- if (conn != null) {
- try {
- if (conn.isClosed()) {
- str = "connection is closed";
- } else {
- final StringBuilder sb = new StringBuilder();
- sb.append(hashCode());
- final DatabaseMetaData meta = conn.getMetaData();
- if (meta != null) {
- sb.append(", URL=");
- sb.append(meta.getURL());
- sb.append(", ");
- sb.append(meta.getDriverName());
- str = sb.toString();
- }
- }
- } catch (final SQLException ex) {
- // Ignore
- }
- }
- return str != null ? str : super.toString();
- }
-
- @Override
- public <T> T unwrap(final Class<T> iface) throws SQLException {
- if (iface.isAssignableFrom(getClass())) {
- return iface.cast(this);
- }
- if (iface.isAssignableFrom(connection.getClass())) {
- return iface.cast(connection);
- }
- return connection.unwrap(iface);
- }
-}
+/*
+ * 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;
+
+import java.sql.Array;
+import java.sql.Blob;
+import java.sql.CallableStatement;
+import java.sql.ClientInfoStatus;
+import java.sql.Clob;
+import java.sql.Connection;
+import java.sql.DatabaseMetaData;
+import java.sql.NClob;
+import java.sql.PreparedStatement;
+import java.sql.ResultSet;
+import java.sql.SQLClientInfoException;
+import java.sql.SQLException;
+import java.sql.SQLWarning;
+import java.sql.SQLXML;
+import java.sql.Savepoint;
+import java.sql.Statement;
+import java.sql.Struct;
+import java.time.Duration;
+import java.time.Instant;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+import java.util.Map;
+import java.util.Properties;
+import java.util.concurrent.Executor;
+
+/**
+ * A base delegating implementation of {@link Connection}.
+ * <p>
+ * All of the methods from the {@link Connection} interface simply check to see that the {@link Connection} is active,
+ * and call the corresponding method on the "delegate" provided in my constructor.
+ * </p>
+ * <p>
+ * Extends AbandonedTrace to implement Connection tracking and logging of code which created the Connection. Tracking
+ * the Connection ensures that the AbandonedObjectPool can close this connection and recycle it if its pool of
+ * connections is nearing exhaustion and this connection's last usage is older than the removeAbandonedTimeout.
+ * </p>
+ *
+ * @param <C>
+ * the Connection type
+ *
+ * @since 2.0
+ */
+public class DelegatingConnection<C extends Connection> extends AbandonedTrace implements Connection {
+
+ private static final Map<String, ClientInfoStatus> EMPTY_FAILED_PROPERTIES = Collections
+ .<String, ClientInfoStatus>emptyMap();
+
+ /** My delegate {@link Connection}. */
+ private volatile C connection;
+
+ private volatile boolean closed;
+
+ private boolean cacheState = true;
+ private Boolean cachedAutoCommit;
+ private Boolean cachedReadOnly;
+ private String cachedCatalog;
+ private String cachedSchema;
+ private Duration defaultQueryTimeoutDuration;
+
+ /**
+ * Creates a wrapper for the Connection which traces this Connection in the AbandonedObjectPool.
+ *
+ * @param connection the {@link Connection} to delegate all calls to.
+ */
+ public DelegatingConnection(final C connection) {
+ this.connection = connection;
+ }
+
+ @Override
+ public void abort(final Executor executor) throws SQLException {
+ try {
+ Jdbc41Bridge.abort(connection, executor);
+ } catch (final SQLException e) {
+ handleException(e);
+ }
+ }
+
+ protected void activate() {
+ closed = false;
+ setLastUsed();
+ if (connection instanceof DelegatingConnection) {
+ ((DelegatingConnection<?>) connection).activate();
+ }
+ }
+
+ protected void checkOpen() throws SQLException {
+ if (closed) {
+ if (null != connection) {
+ String label = "";
+ try {
+ label = connection.toString();
+ } catch (final Exception ignored) {
+ // ignore, leave label empty
+ }
+ throw new SQLException("Connection " + label + " is closed.");
+ }
+ throw new SQLException("Connection is null.");
+ }
+ }
+
+ /**
+ * Clears the cached state. Call when you known that the underlying connection may have been accessed
+ * directly.
+ */
+ public void clearCachedState() {
+ cachedAutoCommit = null;
+ cachedReadOnly = null;
+ cachedSchema = null;
+ cachedCatalog = null;
+ if (connection instanceof DelegatingConnection) {
+ ((DelegatingConnection<?>) connection).clearCachedState();
+ }
+ }
+
+ @Override
+ public void clearWarnings() throws SQLException {
+ checkOpen();
+ try {
+ connection.clearWarnings();
+ } catch (final SQLException e) {
+ handleException(e);
+ }
+ }
+
+ /**
+ * Closes the underlying connection, and close any Statements that were not explicitly closed. Sub-classes that
+ * override this method must:
+ * <ol>
+ * <li>Call {@link #passivate()}</li>
+ * <li>Call close (or the equivalent appropriate action) on the wrapped connection</li>
+ * <li>Set {@code closed} to {@code false}</li>
+ * </ol>
+ */
+ @Override
+ public void close() throws SQLException {
+ if (!closed) {
+ closeInternal();
+ }
+ }
+
+ protected final void closeInternal() throws SQLException {
+ try {
+ passivate();
+ } finally {
+ if (connection != null) {
+ boolean connectionIsClosed;
+ try {
+ connectionIsClosed = connection.isClosed();
+ } catch (final SQLException e) {
+ // not sure what the state is, so assume the connection is open.
+ connectionIsClosed = false;
+ }
+ try {
+ // DBCP-512: Avoid exceptions when closing a connection in mutli-threaded use case.
+ // Avoid closing again, which should be a no-op, but some drivers like H2 throw an exception when
+ // closing from multiple threads.
+ if (!connectionIsClosed) {
+ connection.close();
+ }
+ } finally {
+ closed = true;
+ }
+ } else {
+ closed = true;
+ }
+ }
+ }
+
+ @Override
+ public void commit() throws SQLException {
+ checkOpen();
+ try {
+ connection.commit();
+ } catch (final SQLException e) {
+ handleException(e);
+ }
+ }
+
+ @Override
+ public Array createArrayOf(final String typeName, final Object[] elements) throws SQLException {
+ checkOpen();
+ try {
+ return connection.createArrayOf(typeName, elements);
+ } catch (final SQLException e) {
+ handleException(e);
+ return null;
+ }
+ }
+
+ @Override
+ public Blob createBlob() throws SQLException {
+ checkOpen();
+ try {
+ return connection.createBlob();
+ } catch (final SQLException e) {
+ handleException(e);
+ return null;
+ }
+ }
+
+ @Override
+ public Clob createClob() throws SQLException {
+ checkOpen();
+ try {
+ return connection.createClob();
+ } catch (final SQLException e) {
+ handleException(e);
+ return null;
+ }
+ }
+
+ @Override
+ public NClob createNClob() throws SQLException {
+ checkOpen();
+ try {
+ return connection.createNClob();
+ } catch (final SQLException e) {
+ handleException(e);
+ return null;
+ }
+ }
+
+ @Override
+ public SQLXML createSQLXML() throws SQLException {
+ checkOpen();
+ try {
+ return connection.createSQLXML();
+ } catch (final SQLException e) {
+ handleException(e);
+ return null;
+ }
+ }
+
+ @SuppressWarnings("resource") // Caller is responsible for closing the resource.
+ @Override
+ public Statement createStatement() throws SQLException {
+ checkOpen();
+ try {
+ return init(new DelegatingStatement(this, connection.createStatement()));
+ } catch (final SQLException e) {
+ handleException(e);
+ return null;
+ }
+ }
+
+ @SuppressWarnings("resource") // Caller is responsible for closing the resource.
+ @Override
+ public Statement createStatement(final int resultSetType, final int resultSetConcurrency) throws SQLException {
+ checkOpen();
+ try {
+ return init(new DelegatingStatement(this, connection.createStatement(resultSetType, resultSetConcurrency)));
+ } catch (final SQLException e) {
+ handleException(e);
+ return null;
+ }
+ }
+
+ @SuppressWarnings("resource") // Caller is responsible for closing the resource.
+ @Override
+ public Statement createStatement(final int resultSetType, final int resultSetConcurrency,
+ final int resultSetHoldability) throws SQLException {
+ checkOpen();
+ try {
+ return init(new DelegatingStatement(this,
+ connection.createStatement(resultSetType, resultSetConcurrency, resultSetHoldability)));
+ } catch (final SQLException e) {
+ handleException(e);
+ return null;
+ }
+ }
+
+ @Override
+ public Struct createStruct(final String typeName, final Object[] attributes) throws SQLException {
+ checkOpen();
+ try {
+ return connection.createStruct(typeName, attributes);
+ } catch (final SQLException e) {
+ handleException(e);
+ return null;
+ }
+ }
+
+ @Override
+ public boolean getAutoCommit() throws SQLException {
+ checkOpen();
+ if (cacheState && cachedAutoCommit != null) {
+ return cachedAutoCommit;
+ }
+ try {
+ cachedAutoCommit = connection.getAutoCommit();
+ return cachedAutoCommit;
+ } catch (final SQLException e) {
+ handleException(e);
+ return false;
+ }
+ }
+
+ /**
+ * Returns the state caching flag.
+ *
+ * @return the state caching flag
+ */
+ public boolean getCacheState() {
+ return cacheState;
+ }
+
+ @Override
+ public String getCatalog() throws SQLException {
+ checkOpen();
+ if (cacheState && cachedCatalog != null) {
+ return cachedCatalog;
+ }
+ try {
+ cachedCatalog = connection.getCatalog();
+ return cachedCatalog;
+ } catch (final SQLException e) {
+ handleException(e);
+ return null;
+ }
+ }
+
+ @Override
+ public Properties getClientInfo() throws SQLException {
+ checkOpen();
+ try {
+ return connection.getClientInfo();
+ } catch (final SQLException e) {
+ handleException(e);
+ return null;
+ }
+ }
+
+ @Override
+ public String getClientInfo(final String name) throws SQLException {
+ checkOpen();
+ try {
+ return connection.getClientInfo(name);
+ } catch (final SQLException e) {
+ handleException(e);
+ return null;
+ }
+ }
+
+ /**
+ * Gets the default query timeout that will be used for {@link Statement}s created from this connection.
+ * {@code null} means that the driver default will be used.
+ *
+ * @return query timeout limit in seconds; zero means there is no limit.
+ * @deprecated Use {@link #getDefaultQueryTimeoutDuration()}.
+ */
+ @Deprecated
+ public Integer getDefaultQueryTimeout() {
+ return defaultQueryTimeoutDuration == null ? null : (int) defaultQueryTimeoutDuration.getSeconds();
+ }
+
+ /**
+ * Gets the default query timeout that will be used for {@link Statement}s created from this connection.
+ * {@code null} means that the driver default will be used.
+ *
+ * @return query timeout limit; zero means there is no limit.
+ * @since 2.10.0
+ */
+ public Duration getDefaultQueryTimeoutDuration() {
+ return defaultQueryTimeoutDuration;
+ }
+
+ /**
+ * Returns my underlying {@link Connection}.
+ *
+ * @return my underlying {@link Connection}.
+ */
+ public C getDelegate() {
+ return getDelegateInternal();
+ }
+
+ /**
+ * Gets the delegate connection.
+ *
+ * @return the delegate connection.
+ */
+ protected final C getDelegateInternal() {
+ return connection;
+ }
+
+ @Override
+ public int getHoldability() throws SQLException {
+ checkOpen();
+ try {
+ return connection.getHoldability();
+ } catch (final SQLException e) {
+ handleException(e);
+ return 0;
+ }
+ }
+
+ /**
+ * If my underlying {@link Connection} is not a {@code DelegatingConnection}, returns it, otherwise recursively
+ * invokes this method on my delegate.
+ * <p>
+ * Hence this method will return the first delegate that is not a {@code DelegatingConnection}, or {@code null} when
+ * no non-{@code DelegatingConnection} delegate can be found by traversing this chain.
+ * </p>
+ * <p>
+ * This method is useful when you may have nested {@code DelegatingConnection}s, and you want to make sure to obtain
+ * a "genuine" {@link Connection}.
+ * </p>
+ *
+ * @return innermost delegate.
+ */
+ public Connection getInnermostDelegate() {
+ return getInnermostDelegateInternal();
+ }
+
+ /**
+ * Although this method is public, it is part of the internal API and should not be used by clients. The signature
+ * of this method may change at any time including in ways that break backwards compatibility.
+ *
+ * @return innermost delegate.
+ */
+ @SuppressWarnings("resource")
+ public final Connection getInnermostDelegateInternal() {
+ Connection conn = connection;
+ while (conn instanceof DelegatingConnection) {
+ conn = ((DelegatingConnection<?>) conn).getDelegateInternal();
+ if (this == conn) {
+ return null;
+ }
+ }
+ return conn;
+ }
+
+ @Override
+ public DatabaseMetaData getMetaData() throws SQLException {
+ checkOpen();
+ try {
+ return new DelegatingDatabaseMetaData(this, connection.getMetaData());
+ } catch (final SQLException e) {
+ handleException(e);
+ return null;
+ }
+ }
+
+ @Override
+ public int getNetworkTimeout() throws SQLException {
+ checkOpen();
+ try {
+ return Jdbc41Bridge.getNetworkTimeout(connection);
+ } catch (final SQLException e) {
+ handleException(e);
+ return 0;
+ }
+ }
+
+ @Override
+ public String getSchema() throws SQLException {
+ checkOpen();
+ if (cacheState && cachedSchema != null) {
+ return cachedSchema;
+ }
+ try {
+ cachedSchema = Jdbc41Bridge.getSchema(connection);
+ return cachedSchema;
+ } catch (final SQLException e) {
+ handleException(e);
+ return null;
+ }
+ }
+
+ @Override
+ public int getTransactionIsolation() throws SQLException {
+ checkOpen();
+ try {
+ return connection.getTransactionIsolation();
+ } catch (final SQLException e) {
+ handleException(e);
+ return -1;
+ }
+ }
+
+ @Override
+ public Map<String, Class<?>> getTypeMap() throws SQLException {
+ checkOpen();
+ try {
+ return connection.getTypeMap();
+ } catch (final SQLException e) {
+ handleException(e);
+ return null;
+ }
+ }
+
+ @Override
+ public SQLWarning getWarnings() throws SQLException {
+ checkOpen();
+ try {
+ return connection.getWarnings();
+ } catch (final SQLException e) {
+ handleException(e);
+ return null;
+ }
+ }
+
+ /**
+ * Handles the given exception by throwing it.
+ *
+ * @param e the exception to throw.
+ * @throws SQLException the exception to throw.
+ */
+ protected void handleException(final SQLException e) throws SQLException {
+ throw e;
+ }
+
+ /**
+ * Handles the given {@code SQLException}.
+ *
+ * @param <T> The throwable type.
+ * @param e The SQLException
+ * @return the given {@code SQLException}
+ * @since 2.7.0
+ */
+ protected <T extends Throwable> T handleExceptionNoThrow(final T e) {
+ return e;
+ }
+
+ /**
+ * Initializes the given statement with this connection's settings.
+ *
+ * @param <T> The DelegatingStatement type.
+ * @param delegatingStatement The DelegatingStatement to initialize.
+ * @return The given DelegatingStatement.
+ * @throws SQLException if a database access error occurs, this method is called on a closed Statement.
+ */
+ private <T extends DelegatingStatement> T init(final T delegatingStatement) throws SQLException {
+ if (defaultQueryTimeoutDuration != null && defaultQueryTimeoutDuration.getSeconds() != delegatingStatement.getQueryTimeout()) {
+ delegatingStatement.setQueryTimeout((int) defaultQueryTimeoutDuration.getSeconds());
+ }
+ return delegatingStatement;
+ }
+
+ /**
+ * Compares innermost delegate to the given connection.
+ *
+ * @param c
+ * connection to compare innermost delegate with
+ * @return true if innermost delegate equals {@code c}
+ */
+ @SuppressWarnings("resource")
+ public boolean innermostDelegateEquals(final Connection c) {
+ final Connection innerCon = getInnermostDelegateInternal();
+ if (innerCon == null) {
+ return c == null;
+ }
+ return innerCon.equals(c);
+ }
+
+ @Override
+ public boolean isClosed() throws SQLException {
+ return closed || connection == null || connection.isClosed();
+ }
+
+ protected boolean isClosedInternal() {
+ return closed;
+ }
+
+ @Override
+ public boolean isReadOnly() throws SQLException {
+ checkOpen();
+ if (cacheState && cachedReadOnly != null) {
+ return cachedReadOnly;
+ }
+ try {
+ cachedReadOnly = connection.isReadOnly();
+ return cachedReadOnly;
+ } catch (final SQLException e) {
+ handleException(e);
+ return false;
+ }
+ }
+
+ /**
+ * Tests if the connection has not been closed and is still valid.
+ *
+ * @param timeout The duration to wait for the database operation used to validate the connection to complete.
+ * @return See {@link Connection#isValid(int)}.
+ * @throws SQLException See {@link Connection#isValid(int)}.
+ * @see Connection#isValid(int)
+ * @since 2.10.0
+ */
+ public boolean isValid(final Duration timeout) throws SQLException {
+ if (isClosed()) {
+ return false;
+ }
+ try {
+ return connection.isValid((int) timeout.getSeconds());
+ } catch (final SQLException e) {
+ handleException(e);
+ return false;
+ }
+ }
+
+ /**
+ * @deprecated Use {@link #isValid(Duration)}.
+ */
+ @Override
+ @Deprecated
+ public boolean isValid(final int timeoutSeconds) throws SQLException {
+ return isValid(Duration.ofSeconds(timeoutSeconds));
+ }
+
+ @Override
+ public boolean isWrapperFor(final Class<?> iface) throws SQLException {
+ if (iface.isAssignableFrom(getClass())) {
+ return true;
+ }
+ if (iface.isAssignableFrom(connection.getClass())) {
+ return true;
+ }
+ return connection.isWrapperFor(iface);
+ }
+
+ @Override
+ public String nativeSQL(final String sql) throws SQLException {
+ checkOpen();
+ try {
+ return connection.nativeSQL(sql);
+ } catch (final SQLException e) {
+ handleException(e);
+ return null;
+ }
+ }
+
+ protected void passivate() throws SQLException {
+ // The JDBC specification requires that a Connection close any open
+ // Statement's when it is closed.
+ // DBCP-288. Not all the traced objects will be statements
+ final List<AbandonedTrace> traces = getTrace();
+ if (traces != null && !traces.isEmpty()) {
+ final List<Exception> thrownList = new ArrayList<>();
+ for (final Object trace : traces) {
+ if (trace instanceof Statement) {
+ try {
+ ((Statement) trace).close();
+ } catch (final Exception e) {
+ thrownList.add(e);
+ }
+ } else if (trace instanceof ResultSet) {
+ // DBCP-265: Need to close the result sets that are
+ // generated via DatabaseMetaData
+ try {
+ ((ResultSet) trace).close();
+ } catch (final Exception e) {
+ thrownList.add(e);
+ }
+ }
+ }
+ clearTrace();
+ if (!thrownList.isEmpty()) {
+ throw new SQLExceptionList(thrownList);
+ }
+ }
+ setLastUsed(Instant.EPOCH);
+ }
+
+ @SuppressWarnings("resource") // Caller is responsible for closing the resource.
+ @Override
+ public CallableStatement prepareCall(final String sql) throws SQLException {
+ checkOpen();
+ try {
+ return init(new DelegatingCallableStatement(this, connection.prepareCall(sql)));
+ } catch (final SQLException e) {
+ handleException(e);
+ return null;
+ }
+ }
+
+ @SuppressWarnings("resource") // Caller is responsible for closing the resource.
+ @Override
+ public CallableStatement prepareCall(final String sql, final int resultSetType, final int resultSetConcurrency)
+ throws SQLException {
+ checkOpen();
+ try {
+ return init(new DelegatingCallableStatement(this,
+ connection.prepareCall(sql, resultSetType, resultSetConcurrency)));
+ } catch (final SQLException e) {
+ handleException(e);
+ return null;
+ }
+ }
+
+ @SuppressWarnings("resource") // Caller is responsible for closing the resource.
+ @Override
+ public CallableStatement prepareCall(final String sql, final int resultSetType, final int resultSetConcurrency,
+ final int resultSetHoldability) throws SQLException {
+ checkOpen();
+ try {
+ return init(new DelegatingCallableStatement(this,
+ connection.prepareCall(sql, resultSetType, resultSetConcurrency, resultSetHoldability)));
+ } catch (final SQLException e) {
+ handleException(e);
+ return null;
+ }
+ }
+
+ @SuppressWarnings("resource") // Caller is responsible for closing the resource.
+ @Override
+ public PreparedStatement prepareStatement(final String sql) throws SQLException {
+ checkOpen();
+ try {
+ return init(new DelegatingPreparedStatement(this, connection.prepareStatement(sql)));
+ } catch (final SQLException e) {
+ handleException(e);
+ return null;
+ }
+ }
+
+ @SuppressWarnings("resource") // Caller is responsible for closing the resource.
+ @Override
+ public PreparedStatement prepareStatement(final String sql, final int autoGeneratedKeys) throws SQLException {
+ checkOpen();
+ try {
+ return init(new DelegatingPreparedStatement(this, connection.prepareStatement(sql, autoGeneratedKeys)));
+ } catch (final SQLException e) {
+ handleException(e);
+ return null;
+ }
+ }
+
+ @SuppressWarnings("resource") // Caller is responsible for closing the resource.
+ @Override
+ public PreparedStatement prepareStatement(final String sql, final int resultSetType, final int resultSetConcurrency)
+ throws SQLException {
+ checkOpen();
+ try {
+ return init(new DelegatingPreparedStatement(this,
+ connection.prepareStatement(sql, resultSetType, resultSetConcurrency)));
+ } catch (final SQLException e) {
+ handleException(e);
+ return null;
+ }
+ }
+
+ @SuppressWarnings("resource") // Caller is responsible for closing the resource.
+ @Override
+ public PreparedStatement prepareStatement(final String sql, final int resultSetType, final int resultSetConcurrency,
+ final int resultSetHoldability) throws SQLException {
+ checkOpen();
+ try {
+ return init(new DelegatingPreparedStatement(this,
+ connection.prepareStatement(sql, resultSetType, resultSetConcurrency, resultSetHoldability)));
+ } catch (final SQLException e) {
+ handleException(e);
+ return null;
+ }
+ }
+
+ @SuppressWarnings("resource") // Caller is responsible for closing the resource.
+ @Override
+ public PreparedStatement prepareStatement(final String sql, final int[] columnIndexes) throws SQLException {
+ checkOpen();
+ try {
+ return init(new DelegatingPreparedStatement(this, connection.prepareStatement(sql, columnIndexes)));
+ } catch (final SQLException e) {
+ handleException(e);
+ return null;
+ }
+ }
+
+ @SuppressWarnings("resource") // Caller is responsible for closing the resource.
+ @Override
+ public PreparedStatement prepareStatement(final String sql, final String[] columnNames) throws SQLException {
+ checkOpen();
+ try {
+ return init(new DelegatingPreparedStatement(this, connection.prepareStatement(sql, columnNames)));
+ } catch (final SQLException e) {
+ handleException(e);
+ return null;
+ }
+ }
+
+ @Override
+ public void releaseSavepoint(final Savepoint savepoint) throws SQLException {
+ checkOpen();
+ try {
+ connection.releaseSavepoint(savepoint);
+ } catch (final SQLException e) {
+ handleException(e);
+ }
+ }
+
+ @Override
+ public void rollback() throws SQLException {
+ checkOpen();
+ try {
+ connection.rollback();
+ } catch (final SQLException e) {
+ handleException(e);
+ }
+ }
+
+ @Override
+ public void rollback(final Savepoint savepoint) throws SQLException {
+ checkOpen();
+ try {
+ connection.rollback(savepoint);
+ } catch (final SQLException e) {
+ handleException(e);
+ }
+ }
+
+ @Override
+ public void setAutoCommit(final boolean autoCommit) throws SQLException {
+ checkOpen();
+ try {
+ connection.setAutoCommit(autoCommit);
+ if (cacheState) {
+ cachedAutoCommit = connection.getAutoCommit();
+ }
+ } catch (final SQLException e) {
+ cachedAutoCommit = null;
+ handleException(e);
+ }
+ }
+
+ /**
+ * Sets the state caching flag.
+ *
+ * @param cacheState
+ * The new value for the state caching flag
+ */
+ public void setCacheState(final boolean cacheState) {
+ this.cacheState = cacheState;
+ }
+
+ @Override
+ public void setCatalog(final String catalog) throws SQLException {
+ checkOpen();
+ try {
+ connection.setCatalog(catalog);
+ if (cacheState) {
+ cachedCatalog = connection.getCatalog();
+ }
+ } catch (final SQLException e) {
+ cachedCatalog = null;
+ handleException(e);
+ }
+ }
+
+ @Override
+ public void setClientInfo(final Properties properties) throws SQLClientInfoException {
+ try {
+ checkOpen();
+ connection.setClientInfo(properties);
+ } catch (final SQLClientInfoException e) {
+ throw e;
+ } catch (final SQLException e) {
+ throw new SQLClientInfoException("Connection is closed.", EMPTY_FAILED_PROPERTIES, e);
+ }
+ }
+
+ @Override
+ public void setClientInfo(final String name, final String value) throws SQLClientInfoException {
+ try {
+ checkOpen();
+ connection.setClientInfo(name, value);
+ } catch (final SQLClientInfoException e) {
+ throw e;
+ } catch (final SQLException e) {
+ throw new SQLClientInfoException("Connection is closed.", EMPTY_FAILED_PROPERTIES, e);
+ }
+ }
+
+ protected void setClosedInternal(final boolean closed) {
+ this.closed = closed;
+ }
+
+ /**
+ * Sets the default query timeout that will be used for {@link Statement}s created from this connection.
+ * {@code null} means that the driver default will be used.
+ *
+ * @param defaultQueryTimeoutDuration
+ * the new query timeout limit Duration; zero means there is no limit.
+ * @since 2.10.0
+ */
+ public void setDefaultQueryTimeout(final Duration defaultQueryTimeoutDuration) {
+ this.defaultQueryTimeoutDuration = defaultQueryTimeoutDuration;
+ }
+
+ /**
+ * Sets the default query timeout that will be used for {@link Statement}s created from this connection.
+ * {@code null} means that the driver default will be used.
+ *
+ * @param defaultQueryTimeoutSeconds
+ * the new query timeout limit in seconds; zero means there is no limit.
+ * @deprecated Use {@link #setDefaultQueryTimeout(Duration)}.
+ */
+ @Deprecated
+ public void setDefaultQueryTimeout(final Integer defaultQueryTimeoutSeconds) {
+ this.defaultQueryTimeoutDuration = defaultQueryTimeoutSeconds == null ? null : Duration.ofSeconds(defaultQueryTimeoutSeconds);
+ }
+
+ /**
+ * Sets my delegate.
+ *
+ * @param connection
+ * my delegate.
+ */
+ public void setDelegate(final C connection) {
+ this.connection = connection;
+ }
+
+ @Override
+ public void setHoldability(final int holdability) throws SQLException {
+ checkOpen();
+ try {
+ connection.setHoldability(holdability);
+ } catch (final SQLException e) {
+ handleException(e);
+ }
+ }
+
+ @Override
+ public void setNetworkTimeout(final Executor executor, final int milliseconds) throws SQLException {
+ checkOpen();
+ try {
+ Jdbc41Bridge.setNetworkTimeout(connection, executor, milliseconds);
+ } catch (final SQLException e) {
+ handleException(e);
+ }
+ }
+
+ @Override
+ public void setReadOnly(final boolean readOnly) throws SQLException {
+ checkOpen();
+ try {
+ connection.setReadOnly(readOnly);
+ if (cacheState) {
+ cachedReadOnly = connection.isReadOnly();
+ }
+ } catch (final SQLException e) {
+ cachedReadOnly = null;
+ handleException(e);
+ }
+ }
+
+ @Override
+ public Savepoint setSavepoint() throws SQLException {
+ checkOpen();
+ try {
+ return connection.setSavepoint();
+ } catch (final SQLException e) {
+ handleException(e);
+ return null;
+ }
+ }
+
+ @Override
+ public Savepoint setSavepoint(final String name) throws SQLException {
+ checkOpen();
+ try {
+ return connection.setSavepoint(name);
+ } catch (final SQLException e) {
+ handleException(e);
+ return null;
+ }
+ }
+
+ @Override
+ public void setSchema(final String schema) throws SQLException {
+ checkOpen();
+ try {
+ Jdbc41Bridge.setSchema(connection, schema);
+ if (cacheState) {
+ cachedSchema = connection.getSchema();
+ }
+ } catch (final SQLException e) {
+ cachedSchema = null;
+ handleException(e);
+ }
+ }
+
+ @Override
+ public void setTransactionIsolation(final int level) throws SQLException {
+ checkOpen();
+ try {
+ connection.setTransactionIsolation(level);
+ } catch (final SQLException e) {
+ handleException(e);
+ }
+ }
+
+ @Override
+ public void setTypeMap(final Map<String, Class<?>> map) throws SQLException {
+ checkOpen();
+ try {
+ connection.setTypeMap(map);
+ } catch (final SQLException e) {
+ handleException(e);
+ }
+ }
+
+ /**
+ * Returns a string representation of the metadata associated with the innermost delegate connection.
+ */
+ @SuppressWarnings("resource")
+ @Override
+ public synchronized String toString() {
+ String str = null;
+
+ final Connection conn = this.getInnermostDelegateInternal();
+ if (conn != null) {
+ try {
+ if (conn.isClosed()) {
+ str = "connection is closed";
+ } else {
+ final StringBuilder sb = new StringBuilder();
+ sb.append(hashCode());
+ final DatabaseMetaData meta = conn.getMetaData();
+ if (meta != null) {
+ sb.append(", URL=");
+ sb.append(meta.getURL());
+ sb.append(", ");
+ sb.append(meta.getDriverName());
+ str = sb.toString();
+ }
+ }
+ } catch (final SQLException ignored) {
+ // Ignore
+ }
+ }
+ return str != null ? str : super.toString();
+ }
+
+ @Override
+ public <T> T unwrap(final Class<T> iface) throws SQLException {
+ if (iface.isAssignableFrom(getClass())) {
+ return iface.cast(this);
+ }
+ if (iface.isAssignableFrom(connection.getClass())) {
+ return iface.cast(connection);
+ }
+ return connection.unwrap(iface);
+ }
+}
diff --git a/src/main/java/org/apache/commons/dbcp2/Jdbc41Bridge.java b/src/main/java/org/apache/commons/dbcp2/Jdbc41Bridge.java
index 1dd75476..3ad0e110 100644
--- a/src/main/java/org/apache/commons/dbcp2/Jdbc41Bridge.java
+++ b/src/main/java/org/apache/commons/dbcp2/Jdbc41Bridge.java
@@ -1,488 +1,488 @@
-/*
- * 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;
-
-import java.io.InputStream;
-import java.io.Reader;
-import java.math.BigDecimal;
-import java.net.URL;
-import java.sql.Array;
-import java.sql.Blob;
-import java.sql.Clob;
-import java.sql.Connection;
-import java.sql.DatabaseMetaData;
-import java.sql.Date;
-import java.sql.Ref;
-import java.sql.ResultSet;
-import java.sql.RowId;
-import java.sql.SQLException;
-import java.sql.SQLFeatureNotSupportedException;
-import java.sql.SQLXML;
-import java.sql.Statement;
-import java.sql.Time;
-import java.sql.Timestamp;
-import java.util.concurrent.Executor;
-import java.util.logging.Logger;
-
-import javax.sql.CommonDataSource;
-
-/**
- * Defines bridge methods to JDBC 4.1 (Java 7) methods to allow call sites to operate safely (without
- * {@link AbstractMethodError}) when using a JDBC driver written for JDBC 4.0 (Java 6).
- * <p>
- * There should be no need to this kind of code for JDBC 4.2 in Java 8 due to JDBC's use of default methods.
- * </p>
- * <p>
- * This should probably be moved or at least copied in some form to Apache Commons DbUtils.
- * </p>
- *
- * @since 2.6.0
- */
-public class Jdbc41Bridge {
-
- /**
- * Delegates to {@link Connection#abort(Executor)} without throwing an {@link AbstractMethodError}.
- * <p>
- * If the JDBC driver does not implement {@link Connection#abort(Executor)}, then call {@link Connection#close()}.
- * </p>
- *
- * @param connection
- * the receiver
- * @param executor
- * See {@link Connection#abort(Executor)}.
- * @throws SQLException
- * See {@link Connection#abort(Executor)}.
- * @see Connection#abort(Executor)
- */
- public static void abort(final Connection connection, final Executor executor) throws SQLException {
- try {
- connection.abort(executor);
- } catch (final AbstractMethodError e) {
- connection.close();
- }
- }
-
- /**
- * Delegates to {@link Statement#closeOnCompletion()} without throwing an {@link AbstractMethodError}.
- * <p>
- * If the JDBC driver does not implement {@link Statement#closeOnCompletion()}, then just check that the connection
- * is closed to then throw an SQLException.
- * </p>
- *
- * @param statement
- * See {@link Statement#closeOnCompletion()}
- * @throws SQLException
- * See {@link Statement#closeOnCompletion()}
- * @see Statement#closeOnCompletion()
- */
- public static void closeOnCompletion(final Statement statement) throws SQLException {
- try {
- statement.closeOnCompletion();
- } catch (final AbstractMethodError e) {
- if (statement.isClosed()) {
- throw new SQLException("Statement closed");
- }
- }
- }
-
- /**
- * Delegates to {@link DatabaseMetaData#generatedKeyAlwaysReturned()} without throwing a
- * {@link AbstractMethodError}.
- * <p>
- * If the JDBC driver does not implement {@link DatabaseMetaData#generatedKeyAlwaysReturned()}, then return false.
- * </p>
- *
- * @param databaseMetaData
- * See {@link DatabaseMetaData#generatedKeyAlwaysReturned()}
- * @return See {@link DatabaseMetaData#generatedKeyAlwaysReturned()}
- * @throws SQLException
- * See {@link DatabaseMetaData#generatedKeyAlwaysReturned()}
- * @see DatabaseMetaData#generatedKeyAlwaysReturned()
- */
- public static boolean generatedKeyAlwaysReturned(final DatabaseMetaData databaseMetaData) throws SQLException {
- try {
- return databaseMetaData.generatedKeyAlwaysReturned();
- } catch (final AbstractMethodError e) {
- // do nothing
- return false;
- }
- }
-
- /**
- * Delegates to {@link Connection#getNetworkTimeout()} without throwing an {@link AbstractMethodError}.
- * <p>
- * If the JDBC driver does not implement {@link Connection#getNetworkTimeout()}, then return 0.
- * </p>
- *
- * @param connection
- * the receiver
- * @return See {@link Connection#getNetworkTimeout()}
- * @throws SQLException
- * See {@link Connection#getNetworkTimeout()}
- * @see Connection#getNetworkTimeout()
- */
- public static int getNetworkTimeout(final Connection connection) throws SQLException {
- try {
- return connection.getNetworkTimeout();
- } catch (final AbstractMethodError e) {
- return 0;
- }
- }
-
- /**
- * Delegates to {@link ResultSet#getObject(int, Class)} without throwing an {@link AbstractMethodError}.
- * <p>
- * If the JDBC driver does not implement {@link ResultSet#getObject(int, Class)}, then return 0.
- * </p>
- *
- * @param <T>
- * See {@link ResultSet#getObject(int, Class)}
- * @param resultSet
- * See {@link ResultSet#getObject(int, Class)}
- * @param columnIndex
- * See {@link ResultSet#getObject(int, Class)}
- * @param type
- * See {@link ResultSet#getObject(int, Class)}
- * @return See {@link ResultSet#getObject(int, Class)}
- * @throws SQLException
- * See {@link ResultSet#getObject(int, Class)}
- * @see ResultSet#getObject(int, Class)
- */
- @SuppressWarnings("unchecked")
- public static <T> T getObject(final ResultSet resultSet, final int columnIndex, final Class<T> type)
- throws SQLException {
- try {
- return resultSet.getObject(columnIndex, type);
- } catch (final AbstractMethodError e) {
- if (type == String.class) {
- return (T) resultSet.getString(columnIndex);
- }
- // Numbers
- if (type == Integer.class) {
- return (T) Integer.valueOf(resultSet.getInt(columnIndex));
- }
- if (type == Long.class) {
- return (T) Long.valueOf(resultSet.getLong(columnIndex));
- }
- if (type == Double.class) {
- return (T) Double.valueOf(resultSet.getDouble(columnIndex));
- }
- if (type == Float.class) {
- return (T) Float.valueOf(resultSet.getFloat(columnIndex));
- }
- if (type == Short.class) {
- return (T) Short.valueOf(resultSet.getShort(columnIndex));
- }
- if (type == BigDecimal.class) {
- return (T) resultSet.getBigDecimal(columnIndex);
- }
- if (type == Byte.class) {
- return (T) Byte.valueOf(resultSet.getByte(columnIndex));
- }
- // Dates
- if (type == Date.class) {
- return (T) resultSet.getDate(columnIndex);
- }
- if (type == Time.class) {
- return (T) resultSet.getTime(columnIndex);
- }
- if (type == Timestamp.class) {
- return (T) resultSet.getTimestamp(columnIndex);
- }
- // Streams
- if (type == InputStream.class) {
- return (T) resultSet.getBinaryStream(columnIndex);
- }
- if (type == Reader.class) {
- return (T) resultSet.getCharacterStream(columnIndex);
- }
- // Other
- if (type == Object.class) {
- return (T) resultSet.getObject(columnIndex);
- }
- if (type == Boolean.class) {
- return (T) Boolean.valueOf(resultSet.getBoolean(columnIndex));
- }
- if (type == Array.class) {
- return (T) resultSet.getArray(columnIndex);
- }
- if (type == Blob.class) {
- return (T) resultSet.getBlob(columnIndex);
- }
- if (type == Clob.class) {
- return (T) resultSet.getClob(columnIndex);
- }
- if (type == Ref.class) {
- return (T) resultSet.getRef(columnIndex);
- }
- if (type == RowId.class) {
- return (T) resultSet.getRowId(columnIndex);
- }
- if (type == SQLXML.class) {
- return (T) resultSet.getSQLXML(columnIndex);
- }
- if (type == URL.class) {
- return (T) resultSet.getURL(columnIndex);
- }
- throw new SQLFeatureNotSupportedException(
- String.format("resultSet=%s, columnIndex=%,d, type=%s", resultSet, columnIndex, type));
- }
- }
-
- /**
- * Delegates to {@link ResultSet#getObject(String, Class)} without throwing an {@link AbstractMethodError}.
- *
- * @param <T>
- * See {@link ResultSet#getObject(String, Class)}
- * @param resultSet
- * See {@link ResultSet#getObject(String, Class)}
- * @param columnLabel
- * See {@link ResultSet#getObject(String, Class)}
- * @param type
- * See {@link ResultSet#getObject(String, Class)}
- * @return See {@link ResultSet#getObject(String, Class)}
- * @throws SQLException
- * See {@link ResultSet#getObject(String, Class)}
- * @see ResultSet#getObject(int, Class)
- */
- @SuppressWarnings("unchecked")
- public static <T> T getObject(final ResultSet resultSet, final String columnLabel, final Class<T> type)
- throws SQLException {
- try {
- return resultSet.getObject(columnLabel, type);
- } catch (final AbstractMethodError e) {
- // Numbers
- if (type == Integer.class) {
- return (T) Integer.valueOf(resultSet.getInt(columnLabel));
- }
- if (type == Long.class) {
- return (T) Long.valueOf(resultSet.getLong(columnLabel));
- }
- if (type == Double.class) {
- return (T) Double.valueOf(resultSet.getDouble(columnLabel));
- }
- if (type == Float.class) {
- return (T) Float.valueOf(resultSet.getFloat(columnLabel));
- }
- if (type == Short.class) {
- return (T) Short.valueOf(resultSet.getShort(columnLabel));
- }
- if (type == BigDecimal.class) {
- return (T) resultSet.getBigDecimal(columnLabel);
- }
- if (type == Byte.class) {
- return (T) Byte.valueOf(resultSet.getByte(columnLabel));
- }
- // Dates
- if (type == Date.class) {
- return (T) resultSet.getDate(columnLabel);
- }
- if (type == Time.class) {
- return (T) resultSet.getTime(columnLabel);
- }
- if (type == Timestamp.class) {
- return (T) resultSet.getTimestamp(columnLabel);
- }
- // Streams
- if (type == InputStream.class) {
- return (T) resultSet.getBinaryStream(columnLabel);
- }
- if (type == Reader.class) {
- return (T) resultSet.getCharacterStream(columnLabel);
- }
- // Other
- if (type == Object.class) {
- return (T) resultSet.getObject(columnLabel);
- }
- if (type == Boolean.class) {
- return (T) Boolean.valueOf(resultSet.getBoolean(columnLabel));
- }
- if (type == Array.class) {
- return (T) resultSet.getArray(columnLabel);
- }
- if (type == Blob.class) {
- return (T) resultSet.getBlob(columnLabel);
- }
- if (type == Clob.class) {
- return (T) resultSet.getClob(columnLabel);
- }
- if (type == Ref.class) {
- return (T) resultSet.getRef(columnLabel);
- }
- if (type == RowId.class) {
- return (T) resultSet.getRowId(columnLabel);
- }
- if (type == SQLXML.class) {
- return (T) resultSet.getSQLXML(columnLabel);
- }
- if (type == URL.class) {
- return (T) resultSet.getURL(columnLabel);
- }
- throw new SQLFeatureNotSupportedException(
- String.format("resultSet=%s, columnLabel=%s, type=%s", resultSet, columnLabel, type));
- }
- }
-
- /**
- * Delegates to {@link CommonDataSource#getParentLogger()} without throwing an {@link AbstractMethodError}.
- * <p>
- * If the JDBC driver does not implement {@link CommonDataSource#getParentLogger()}, then return null.
- * </p>
- *
- * @param commonDataSource
- * See {@link CommonDataSource#getParentLogger()}
- * @return See {@link CommonDataSource#getParentLogger()}
- * @throws SQLFeatureNotSupportedException
- * See {@link CommonDataSource#getParentLogger()}
- */
- public static Logger getParentLogger(final CommonDataSource commonDataSource) throws SQLFeatureNotSupportedException {
- try {
- return commonDataSource.getParentLogger();
- } catch (final AbstractMethodError e) {
- throw new SQLFeatureNotSupportedException("javax.sql.CommonDataSource#getParentLogger()");
- }
- }
-
- /**
- * Delegates to {@link DatabaseMetaData#getPseudoColumns(String, String, String, String)} without throwing a
- * {@link AbstractMethodError}.
- * <p>
- * If the JDBC driver does not implement {@link DatabaseMetaData#getPseudoColumns(String, String, String, String)},
- * then return null.
- * </p>
- *
- * @param databaseMetaData
- * the receiver
- * @param catalog
- * See {@link DatabaseMetaData#getPseudoColumns(String, String, String, String)}
- * @param schemaPattern
- * See {@link DatabaseMetaData#getPseudoColumns(String, String, String, String)}
- * @param tableNamePattern
- * See {@link DatabaseMetaData#getPseudoColumns(String, String, String, String)}
- * @param columnNamePattern
- * See {@link DatabaseMetaData#getPseudoColumns(String, String, String, String)}
- * @return See {@link DatabaseMetaData#getPseudoColumns(String, String, String, String)}
- * @throws SQLException
- * See {@link DatabaseMetaData#getPseudoColumns(String, String, String, String)}
- * @see DatabaseMetaData#getPseudoColumns(String, String, String, String)
- */
- public static ResultSet getPseudoColumns(final DatabaseMetaData databaseMetaData, final String catalog,
- final String schemaPattern, final String tableNamePattern, final String columnNamePattern)
- throws SQLException {
- try {
- return databaseMetaData.getPseudoColumns(catalog, schemaPattern, tableNamePattern, columnNamePattern);
- } catch (final AbstractMethodError e) {
- // do nothing
- return null;
- }
- }
-
- /**
- * Delegates to {@link Connection#getSchema()} without throwing an {@link AbstractMethodError}.
- * <p>
- * If the JDBC driver does not implement {@link Connection#getSchema()}, then return null.
- * </p>
- *
- * @param connection
- * the receiver
- * @return null for a JDBC 4 driver or a value per {@link Connection#getSchema()}.
- * @throws SQLException
- * See {@link Connection#getSchema()}.
- * @see Connection#getSchema()
- */
- public static String getSchema(final Connection connection) throws SQLException {
- try {
- return connection.getSchema();
- } catch (final AbstractMethodError e) {
- // do nothing
- return null;
- }
- }
-
- /**
- * Delegates to {@link Statement#isCloseOnCompletion()} without throwing an {@link AbstractMethodError}.
- * <p>
- * If the JDBC driver does not implement {@link Statement#isCloseOnCompletion()}, then just check that the
- * connection is closed to then throw an SQLException.
- * </p>
- *
- * @param statement
- * See {@link Statement#isCloseOnCompletion()}
- * @return See {@link Statement#isCloseOnCompletion()}
- * @throws SQLException
- * See {@link Statement#isCloseOnCompletion()}
- * @see Statement#closeOnCompletion()
- */
- public static boolean isCloseOnCompletion(final Statement statement) throws SQLException {
- try {
- return statement.isCloseOnCompletion();
- } catch (final AbstractMethodError e) {
- if (statement.isClosed()) {
- throw new SQLException("Statement closed");
- }
- return false;
- }
- }
-
- /**
- * Delegates to {@link Connection#setNetworkTimeout(Executor, int)} without throwing an {@link AbstractMethodError}.
- * <p>
- * If the JDBC driver does not implement {@link Connection#setNetworkTimeout(Executor, int)}, then do nothing.
- * </p>
- *
- * @param connection
- * the receiver
- * @param executor
- * See {@link Connection#setNetworkTimeout(Executor, int)}
- * @param milliseconds
- * {@link Connection#setNetworkTimeout(Executor, int)}
- * @throws SQLException
- * {@link Connection#setNetworkTimeout(Executor, int)}
- * @see Connection#setNetworkTimeout(Executor, int)
- */
- public static void setNetworkTimeout(final Connection connection, final Executor executor, final int milliseconds)
- throws SQLException {
- try {
- connection.setNetworkTimeout(executor, milliseconds);
- } catch (final AbstractMethodError e) {
- // do nothing
- }
- }
-
- /**
- * Delegates to {@link Connection#setSchema(String)} without throwing an {@link AbstractMethodError}.
- * <p>
- * If the JDBC driver does not implement {@link Connection#setSchema(String)}, then do nothing.
- * </p>
- *
- * @param connection
- * the receiver
- * @param schema
- * See {@link Connection#setSchema(String)}.
- * @throws SQLException
- * See {@link Connection#setSchema(String)}.
- * @see Connection#setSchema(String)
- */
- public static void setSchema(final Connection connection, final String schema) throws SQLException {
- try {
- connection.setSchema(schema);
- } catch (final AbstractMethodError e) {
- // do nothing
- }
- }
-
-}
+/*
+ * 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;
+
+import java.io.InputStream;
+import java.io.Reader;
+import java.math.BigDecimal;
+import java.net.URL;
+import java.sql.Array;
+import java.sql.Blob;
+import java.sql.Clob;
+import java.sql.Connection;
+import java.sql.DatabaseMetaData;
+import java.sql.Date;
+import java.sql.Ref;
+import java.sql.ResultSet;
+import java.sql.RowId;
+import java.sql.SQLException;
+import java.sql.SQLFeatureNotSupportedException;
+import java.sql.SQLXML;
+import java.sql.Statement;
+import java.sql.Time;
+import java.sql.Timestamp;
+import java.util.concurrent.Executor;
+import java.util.logging.Logger;
+
+import javax.sql.CommonDataSource;
+
+/**
+ * Defines bridge methods to JDBC 4.1 (Java 7) methods to allow call sites to operate safely (without
+ * {@link AbstractMethodError}) when using a JDBC driver written for JDBC 4.0 (Java 6).
+ * <p>
+ * There should be no need to this kind of code for JDBC 4.2 in Java 8 due to JDBC's use of default methods.
+ * </p>
+ * <p>
+ * This should probably be moved or at least copied in some form to Apache Commons DbUtils.
+ * </p>
+ *
+ * @since 2.6.0
+ */
+public class Jdbc41Bridge {
+
+ /**
+ * Delegates to {@link Connection#abort(Executor)} without throwing an {@link AbstractMethodError}.
+ * <p>
+ * If the JDBC driver does not implement {@link Connection#abort(Executor)}, then call {@link Connection#close()}.
+ * </p>
+ *
+ * @param connection
+ * the receiver
+ * @param executor
+ * See {@link Connection#abort(Executor)}.
+ * @throws SQLException
+ * See {@link Connection#abort(Executor)}.
+ * @see Connection#abort(Executor)
+ */
+ public static void abort(final Connection connection, final Executor executor) throws SQLException {
+ try {
+ connection.abort(executor);
+ } catch (final AbstractMethodError e) {
+ connection.close();
+ }
+ }
+
+ /**
+ * Delegates to {@link Statement#closeOnCompletion()} without throwing an {@link AbstractMethodError}.
+ * <p>
+ * If the JDBC driver does not implement {@link Statement#closeOnCompletion()}, then just check that the connection
+ * is closed to then throw an SQLException.
+ * </p>
+ *
+ * @param statement
+ * See {@link Statement#closeOnCompletion()}
+ * @throws SQLException
+ * See {@link Statement#closeOnCompletion()}
+ * @see Statement#closeOnCompletion()
+ */
+ public static void closeOnCompletion(final Statement statement) throws SQLException {
+ try {
+ statement.closeOnCompletion();
+ } catch (final AbstractMethodError e) {
+ if (statement.isClosed()) {
+ throw new SQLException("Statement closed");
+ }
+ }
+ }
+
+ /**
+ * Delegates to {@link DatabaseMetaData#generatedKeyAlwaysReturned()} without throwing a
+ * {@link AbstractMethodError}.
+ * <p>
+ * If the JDBC driver does not implement {@link DatabaseMetaData#generatedKeyAlwaysReturned()}, then return false.
+ * </p>
+ *
+ * @param databaseMetaData
+ * See {@link DatabaseMetaData#generatedKeyAlwaysReturned()}
+ * @return See {@link DatabaseMetaData#generatedKeyAlwaysReturned()}
+ * @throws SQLException
+ * See {@link DatabaseMetaData#generatedKeyAlwaysReturned()}
+ * @see DatabaseMetaData#generatedKeyAlwaysReturned()
+ */
+ public static boolean generatedKeyAlwaysReturned(final DatabaseMetaData databaseMetaData) throws SQLException {
+ try {
+ return databaseMetaData.generatedKeyAlwaysReturned();
+ } catch (final AbstractMethodError e) {
+ // do nothing
+ return false;
+ }
+ }
+
+ /**
+ * Delegates to {@link Connection#getNetworkTimeout()} without throwing an {@link AbstractMethodError}.
+ * <p>
+ * If the JDBC driver does not implement {@link Connection#getNetworkTimeout()}, then return 0.
+ * </p>
+ *
+ * @param connection
+ * the receiver
+ * @return See {@link Connection#getNetworkTimeout()}
+ * @throws SQLException
+ * See {@link Connection#getNetworkTimeout()}
+ * @see Connection#getNetworkTimeout()
+ */
+ public static int getNetworkTimeout(final Connection connection) throws SQLException {
+ try {
+ return connection.getNetworkTimeout();
+ } catch (final AbstractMethodError e) {
+ return 0;
+ }
+ }
+
+ /**
+ * Delegates to {@link ResultSet#getObject(int, Class)} without throwing an {@link AbstractMethodError}.
+ * <p>
+ * If the JDBC driver does not implement {@link ResultSet#getObject(int, Class)}, then return 0.
+ * </p>
+ *
+ * @param <T>
+ * See {@link ResultSet#getObject(int, Class)}
+ * @param resultSet
+ * See {@link ResultSet#getObject(int, Class)}
+ * @param columnIndex
+ * See {@link ResultSet#getObject(int, Class)}
+ * @param type
+ * See {@link ResultSet#getObject(int, Class)}
+ * @return See {@link ResultSet#getObject(int, Class)}
+ * @throws SQLException
+ * See {@link ResultSet#getObject(int, Class)}
+ * @see ResultSet#getObject(int, Class)
+ */
+ @SuppressWarnings("unchecked")
+ public static <T> T getObject(final ResultSet resultSet, final int columnIndex, final Class<T> type)
+ throws SQLException {
+ try {
+ return resultSet.getObject(columnIndex, type);
+ } catch (final AbstractMethodError e) {
+ if (type == String.class) {
+ return (T) resultSet.getString(columnIndex);
+ }
+ // Numbers
+ if (type == Integer.class) {
+ return (T) Integer.valueOf(resultSet.getInt(columnIndex));
+ }
+ if (type == Long.class) {
+ return (T) Long.valueOf(resultSet.getLong(columnIndex));
+ }
+ if (type == Double.class) {
+ return (T) Double.valueOf(resultSet.getDouble(columnIndex));
+ }
+ if (type == Float.class) {
+ return (T) Float.valueOf(resultSet.getFloat(columnIndex));
+ }
+ if (type == Short.class) {
+ return (T) Short.valueOf(resultSet.getShort(columnIndex));
+ }
+ if (type == BigDecimal.class) {
+ return (T) resultSet.getBigDecimal(columnIndex);
+ }
+ if (type == Byte.class) {
+ return (T) Byte.valueOf(resultSet.getByte(columnIndex));
+ }
+ // Dates
+ if (type == Date.class) {
+ return (T) resultSet.getDate(columnIndex);
+ }
+ if (type == Time.class) {
+ return (T) resultSet.getTime(columnIndex);
+ }
+ if (type == Timestamp.class) {
+ return (T) resultSet.getTimestamp(columnIndex);
+ }
+ // Streams
+ if (type == InputStream.class) {
+ return (T) resultSet.getBinaryStream(columnIndex);
+ }
+ if (type == Reader.class) {
+ return (T) resultSet.getCharacterStream(columnIndex);
+ }
+ // Other
+ if (type == Object.class) {
+ return (T) resultSet.getObject(columnIndex);
+ }
+ if (type == Boolean.class) {
+ return (T) Boolean.valueOf(resultSet.getBoolean(columnIndex));
+ }
+ if (type == Array.class) {
+ return (T) resultSet.getArray(columnIndex);
+ }
+ if (type == Blob.class) {
+ return (T) resultSet.getBlob(columnIndex);
+ }
+ if (type == Clob.class) {
+ return (T) resultSet.getClob(columnIndex);
+ }
+ if (type == Ref.class) {
+ return (T) resultSet.getRef(columnIndex);
+ }
+ if (type == RowId.class) {
+ return (T) resultSet.getRowId(columnIndex);
+ }
+ if (type == SQLXML.class) {
+ return (T) resultSet.getSQLXML(columnIndex);
+ }
+ if (type == URL.class) {
+ return (T) resultSet.getURL(columnIndex);
+ }
+ throw new SQLFeatureNotSupportedException(
+ String.format("resultSet=%s, columnIndex=%,d, type=%s", resultSet, columnIndex, type));
+ }
+ }
+
+ /**
+ * Delegates to {@link ResultSet#getObject(String, Class)} without throwing an {@link AbstractMethodError}.
+ *
+ * @param <T>
+ * See {@link ResultSet#getObject(String, Class)}
+ * @param resultSet
+ * See {@link ResultSet#getObject(String, Class)}
+ * @param columnLabel
+ * See {@link ResultSet#getObject(String, Class)}
+ * @param type
+ * See {@link ResultSet#getObject(String, Class)}
+ * @return See {@link ResultSet#getObject(String, Class)}
+ * @throws SQLException
+ * See {@link ResultSet#getObject(String, Class)}
+ * @see ResultSet#getObject(int, Class)
+ */
+ @SuppressWarnings("unchecked")
+ public static <T> T getObject(final ResultSet resultSet, final String columnLabel, final Class<T> type)
+ throws SQLException {
+ try {
+ return resultSet.getObject(columnLabel, type);
+ } catch (final AbstractMethodError e) {
+ // Numbers
+ if (type == Integer.class) {
+ return (T) Integer.valueOf(resultSet.getInt(columnLabel));
+ }
+ if (type == Long.class) {
+ return (T) Long.valueOf(resultSet.getLong(columnLabel));
+ }
+ if (type == Double.class) {
+ return (T) Double.valueOf(resultSet.getDouble(columnLabel));
+ }
+ if (type == Float.class) {
+ return (T) Float.valueOf(resultSet.getFloat(columnLabel));
+ }
+ if (type == Short.class) {
+ return (T) Short.valueOf(resultSet.getShort(columnLabel));
+ }
+ if (type == BigDecimal.class) {
+ return (T) resultSet.getBigDecimal(columnLabel);
+ }
+ if (type == Byte.class) {
+ return (T) Byte.valueOf(resultSet.getByte(columnLabel));
+ }
+ // Dates
+ if (type == Date.class) {
+ return (T) resultSet.getDate(columnLabel);
+ }
+ if (type == Time.class) {
+ return (T) resultSet.getTime(columnLabel);
+ }
+ if (type == Timestamp.class) {
+ return (T) resultSet.getTimestamp(columnLabel);
+ }
+ // Streams
+ if (type == InputStream.class) {
+ return (T) resultSet.getBinaryStream(columnLabel);
+ }
+ if (type == Reader.class) {
+ return (T) resultSet.getCharacterStream(columnLabel);
+ }
+ // Other
+ if (type == Object.class) {
+ return (T) resultSet.getObject(columnLabel);
+ }
+ if (type == Boolean.class) {
+ return (T) Boolean.valueOf(resultSet.getBoolean(columnLabel));
+ }
+ if (type == Array.class) {
+ return (T) resultSet.getArray(columnLabel);
+ }
+ if (type == Blob.class) {
+ return (T) resultSet.getBlob(columnLabel);
+ }
+ if (type == Clob.class) {
+ return (T) resultSet.getClob(columnLabel);
+ }
+ if (type == Ref.class) {
+ return (T) resultSet.getRef(columnLabel);
+ }
+ if (type == RowId.class) {
+ return (T) resultSet.getRowId(columnLabel);
+ }
+ if (type == SQLXML.class) {
+ return (T) resultSet.getSQLXML(columnLabel);
+ }
+ if (type == URL.class) {
+ return (T) resultSet.getURL(columnLabel);
+ }
+ throw new SQLFeatureNotSupportedException(
+ String.format("resultSet=%s, columnLabel=%s, type=%s", resultSet, columnLabel, type));
+ }
+ }
+
+ /**
+ * Delegates to {@link CommonDataSource#getParentLogger()} without throwing an {@link AbstractMethodError}.
+ * <p>
+ * If the JDBC driver does not implement {@link CommonDataSource#getParentLogger()}, then return null.
+ * </p>
+ *
+ * @param commonDataSource
+ * See {@link CommonDataSource#getParentLogger()}
+ * @return See {@link CommonDataSource#getParentLogger()}
+ * @throws SQLFeatureNotSupportedException
+ * See {@link CommonDataSource#getParentLogger()}
+ */
+ public static Logger getParentLogger(final CommonDataSource commonDataSource) throws SQLFeatureNotSupportedException {
+ try {
+ return commonDataSource.getParentLogger();
+ } catch (final AbstractMethodError e) {
+ throw new SQLFeatureNotSupportedException("javax.sql.CommonDataSource#getParentLogger()");
+ }
+ }
+
+ /**
+ * Delegates to {@link DatabaseMetaData#getPseudoColumns(String, String, String, String)} without throwing a
+ * {@link AbstractMethodError}.
+ * <p>
+ * If the JDBC driver does not implement {@link DatabaseMetaData#getPseudoColumns(String, String, String, String)},
+ * then return null.
+ * </p>
+ *
+ * @param databaseMetaData
+ * the receiver
+ * @param catalog
+ * See {@link DatabaseMetaData#getPseudoColumns(String, String, String, String)}
+ * @param schemaPattern
+ * See {@link DatabaseMetaData#getPseudoColumns(String, String, String, String)}
+ * @param tableNamePattern
+ * See {@link DatabaseMetaData#getPseudoColumns(String, String, String, String)}
+ * @param columnNamePattern
+ * See {@link DatabaseMetaData#getPseudoColumns(String, String, String, String)}
+ * @return See {@link DatabaseMetaData#getPseudoColumns(String, String, String, String)}
+ * @throws SQLException
+ * See {@link DatabaseMetaData#getPseudoColumns(String, String, String, String)}
+ * @see DatabaseMetaData#getPseudoColumns(String, String, String, String)
+ */
+ public static ResultSet getPseudoColumns(final DatabaseMetaData databaseMetaData, final String catalog,
+ final String schemaPattern, final String tableNamePattern, final String columnNamePattern)
+ throws SQLException {
+ try {
+ return databaseMetaData.getPseudoColumns(catalog, schemaPattern, tableNamePattern, columnNamePattern);
+ } catch (final AbstractMethodError e) {
+ // do nothing
+ return null;
+ }
+ }
+
+ /**
+ * Delegates to {@link Connection#getSchema()} without throwing an {@link AbstractMethodError}.
+ * <p>
+ * If the JDBC driver does not implement {@link Connection#getSchema()}, then return null.
+ * </p>
+ *
+ * @param connection
+ * the receiver
+ * @return null for a JDBC 4 driver or a value per {@link Connection#getSchema()}.
+ * @throws SQLException
+ * See {@link Connection#getSchema()}.
+ * @see Connection#getSchema()
+ */
+ public static String getSchema(final Connection connection) throws SQLException {
+ try {
+ return connection.getSchema();
+ } catch (final AbstractMethodError e) {
+ // do nothing
+ return null;
+ }
+ }
+
+ /**
+ * Delegates to {@link Statement#isCloseOnCompletion()} without throwing an {@link AbstractMethodError}.
+ * <p>
+ * If the JDBC driver does not implement {@link Statement#isCloseOnCompletion()}, then just check that the
+ * connection is closed to then throw an SQLException.
+ * </p>
+ *
+ * @param statement
+ * See {@link Statement#isCloseOnCompletion()}
+ * @return See {@link Statement#isCloseOnCompletion()}
+ * @throws SQLException
+ * See {@link Statement#isCloseOnCompletion()}
+ * @see Statement#closeOnCompletion()
+ */
+ public static boolean isCloseOnCompletion(final Statement statement) throws SQLException {
+ try {
+ return statement.isCloseOnCompletion();
+ } catch (final AbstractMethodError e) {
+ if (statement.isClosed()) {
+ throw new SQLException("Statement closed");
+ }
+ return false;
+ }
+ }
+
+ /**
+ * Delegates to {@link Connection#setNetworkTimeout(Executor, int)} without throwing an {@link AbstractMethodError}.
+ * <p>
+ * If the JDBC driver does not implement {@link Connection#setNetworkTimeout(Executor, int)}, then do nothing.
+ * </p>
+ *
+ * @param connection
+ * the receiver
+ * @param executor
+ * See {@link Connection#setNetworkTimeout(Executor, int)}
+ * @param milliseconds
+ * {@link Connection#setNetworkTimeout(Executor, int)}
+ * @throws SQLException
+ * {@link Connection#setNetworkTimeout(Executor, int)}
+ * @see Connection#setNetworkTimeout(Executor, int)
+ */
+ public static void setNetworkTimeout(final Connection connection, final Executor executor, final int milliseconds)
+ throws SQLException {
+ try {
+ connection.setNetworkTimeout(executor, milliseconds);
+ } catch (final AbstractMethodError ignored) {
+ // do nothing
+ }
+ }
+
+ /**
+ * Delegates to {@link Connection#setSchema(String)} without throwing an {@link AbstractMethodError}.
+ * <p>
+ * If the JDBC driver does not implement {@link Connection#setSchema(String)}, then do nothing.
+ * </p>
+ *
+ * @param connection
+ * the receiver
+ * @param schema
+ * See {@link Connection#setSchema(String)}.
+ * @throws SQLException
+ * See {@link Connection#setSchema(String)}.
+ * @see Connection#setSchema(String)
+ */
+ public static void setSchema(final Connection connection, final String schema) throws SQLException {
+ try {
+ connection.setSchema(schema);
+ } catch (final AbstractMethodError ignored) {
+ // do nothing
+ }
+ }
+
+}
diff --git a/src/main/java/org/apache/commons/dbcp2/PoolableConnection.java b/src/main/java/org/apache/commons/dbcp2/PoolableConnection.java
index d9e463b6..ed5a2a3d 100644
--- a/src/main/java/org/apache/commons/dbcp2/PoolableConnection.java
+++ b/src/main/java/org/apache/commons/dbcp2/PoolableConnection.java
@@ -1,404 +1,404 @@
-/*
- * 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;
-
-import java.lang.management.ManagementFactory;
-import java.sql.Connection;
-import java.sql.PreparedStatement;
-import java.sql.ResultSet;
-import java.sql.SQLException;
-import java.time.Duration;
-import java.util.Collection;
-import java.util.concurrent.Executor;
-
-import javax.management.InstanceAlreadyExistsException;
-import javax.management.MBeanRegistrationException;
-import javax.management.MBeanServer;
-import javax.management.NotCompliantMBeanException;
-import javax.management.ObjectName;
-
-import org.apache.commons.pool2.ObjectPool;
-
-/**
- * A delegating connection that, rather than closing the underlying connection, returns itself to an {@link ObjectPool}
- * when closed.
- *
- * @since 2.0
- */
-public class PoolableConnection extends DelegatingConnection<Connection> implements PoolableConnectionMXBean {
-
- private static MBeanServer MBEAN_SERVER;
-
- static {
- try {
- MBEAN_SERVER = ManagementFactory.getPlatformMBeanServer();
- } catch (final NoClassDefFoundError | Exception ex) {
- // ignore - JMX not available
- }
- }
-
- /** The pool to which I should return. */
- private final ObjectPool<PoolableConnection> pool;
-
- private final ObjectNameWrapper jmxObjectName;
-
- // Use a prepared statement for validation, retaining the last used SQL to
- // check if the validation query has changed.
- private PreparedStatement validationPreparedStatement;
- private String lastValidationSql;
-
- /**
- * Indicate that unrecoverable SQLException was thrown when using this connection. Such a connection should be
- * considered broken and not pass validation in the future.
- */
- private boolean fatalSqlExceptionThrown;
-
- /**
- * SQL_STATE codes considered to signal fatal conditions. Overrides the defaults in
- * {@link Utils#DISCONNECTION_SQL_CODES} (plus anything starting with {@link Utils#DISCONNECTION_SQL_CODE_PREFIX}).
- */
- private final Collection<String> disconnectionSqlCodes;
-
- /** Whether or not to fast fail validation after fatal connection errors */
- private final boolean fastFailValidation;
-
- /**
- *
- * @param conn
- * my underlying connection
- * @param pool
- * the pool to which I should return when closed
- * @param jmxName
- * JMX name
- */
- public PoolableConnection(final Connection conn, final ObjectPool<PoolableConnection> pool,
- final ObjectName jmxName) {
- this(conn, pool, jmxName, null, true);
- }
-
- /**
- *
- * @param conn
- * my underlying connection
- * @param pool
- * the pool to which I should return when closed
- * @param jmxObjectName
- * JMX name
- * @param disconnectSqlCodes
- * SQL_STATE codes considered fatal disconnection errors
- * @param fastFailValidation
- * true means fatal disconnection errors cause subsequent validations to fail immediately (no attempt to
- * run query or isValid)
- */
- public PoolableConnection(final Connection conn, final ObjectPool<PoolableConnection> pool,
- final ObjectName jmxObjectName, final Collection<String> disconnectSqlCodes,
- final boolean fastFailValidation) {
- super(conn);
- this.pool = pool;
- this.jmxObjectName = ObjectNameWrapper.wrap(jmxObjectName);
- this.disconnectionSqlCodes = disconnectSqlCodes;
- this.fastFailValidation = fastFailValidation;
-
- if (jmxObjectName != null) {
- try {
- MBEAN_SERVER.registerMBean(this, jmxObjectName);
- } catch (InstanceAlreadyExistsException | MBeanRegistrationException | NotCompliantMBeanException e) {
- // For now, simply skip registration
- }
- }
- }
-
- /**
- * Abort my underlying {@link Connection}.
- *
- * @since 2.9.0
- */
- @Override
- public void abort(final Executor executor) throws SQLException {
- if (jmxObjectName != null) {
- jmxObjectName.unregisterMBean();
- }
- super.abort(executor);
- }
-
- /**
- * Returns me to my pool.
- */
- @Override
- public synchronized void close() throws SQLException {
- if (isClosedInternal()) {
- // already closed
- return;
- }
-
- boolean isUnderlyingConnectionClosed;
- try {
- isUnderlyingConnectionClosed = getDelegateInternal().isClosed();
- } catch (final SQLException e) {
- try {
- pool.invalidateObject(this);
- } catch (final IllegalStateException ise) {
- // pool is closed, so close the connection
- passivate();
- getInnermostDelegate().close();
- } catch (final Exception ie) {
- // DO NOTHING the original exception will be rethrown
- }
- throw new SQLException("Cannot close connection (isClosed check failed)", e);
- }
-
- /*
- * Can't set close before this code block since the connection needs to be open when validation runs. Can't set
- * close after this code block since by then the connection will have been returned to the pool and may have
- * been borrowed by another thread. Therefore, the close flag is set in passivate().
- */
- if (isUnderlyingConnectionClosed) {
- // Abnormal close: underlying connection closed unexpectedly, so we
- // must destroy this proxy
- try {
- pool.invalidateObject(this);
- } catch (final IllegalStateException e) {
- // pool is closed, so close the connection
- passivate();
- getInnermostDelegate().close();
- } catch (final Exception e) {
- throw new SQLException("Cannot close connection (invalidating pooled object failed)", e);
- }
- } else {
- // Normal close: underlying connection is still open, so we
- // simply need to return this proxy to the pool
- try {
- pool.returnObject(this);
- } catch (final IllegalStateException e) {
- // pool is closed, so close the connection
- passivate();
- getInnermostDelegate().close();
- } catch (final SQLException | RuntimeException e) {
- throw e;
- } catch (final Exception e) {
- throw new SQLException("Cannot close connection (return to pool failed)", e);
- }
- }
- }
-
- /**
- * @return The disconnection SQL codes.
- * @since 2.6.0
- */
- public Collection<String> getDisconnectionSqlCodes() {
- return disconnectionSqlCodes;
- }
-
- /**
- * Expose the {@link #toString()} method via a bean getter so it can be read as a property via JMX.
- */
- @Override
- public String getToString() {
- return toString();
- }
-
- @Override
- protected void handleException(final SQLException e) throws SQLException {
- fatalSqlExceptionThrown |= isFatalException(e);
- super.handleException(e);
- }
-
- /**
- * {@inheritDoc}
- * <p>
- * This method should not be used by a client to determine whether or not a connection should be return to the
- * connection pool (by calling {@link #close()}). Clients should always attempt to return a connection to the pool
- * once it is no longer required.
- */
- @Override
- public boolean isClosed() throws SQLException {
- if (isClosedInternal()) {
- return true;
- }
-
- if (getDelegateInternal().isClosed()) {
- // Something has gone wrong. The underlying connection has been
- // closed without the connection being returned to the pool. Return
- // it now.
- close();
- return true;
- }
-
- return false;
- }
-
- /**
- * Checks the SQLState of the input exception.
- * <p>
- * If {@link #disconnectionSqlCodes} has been set, sql states are compared to those in the configured list of fatal
- * exception codes. If this property is not set, codes are compared against the default codes in
- * {@link Utils#DISCONNECTION_SQL_CODES} and in this case anything starting with #{link
- * Utils.DISCONNECTION_SQL_CODE_PREFIX} is considered a disconnection.
- * </p>
- *
- * @param e SQLException to be examined
- * @return true if the exception signals a disconnection
- */
- boolean isDisconnectionSqlException(final SQLException e) {
- boolean fatalException = false;
- final String sqlState = e.getSQLState();
- if (sqlState != null) {
- fatalException = disconnectionSqlCodes == null
- ? sqlState.startsWith(Utils.DISCONNECTION_SQL_CODE_PREFIX) || Utils.DISCONNECTION_SQL_CODES.contains(sqlState)
- : disconnectionSqlCodes.contains(sqlState);
- }
- return fatalException;
- }
-
- /**
- * Checks the SQLState of the input exception and any nested SQLExceptions it wraps.
- * <p>
- * If {@link #disconnectionSqlCodes} has been set, sql states are compared to those in the
- * configured list of fatal exception codes. If this property is not set, codes are compared against the default
- * codes in {@link Utils#DISCONNECTION_SQL_CODES} and in this case anything starting with #{link
- * Utils.DISCONNECTION_SQL_CODE_PREFIX} is considered a disconnection.
- * </p>
- *
- * @param e
- * SQLException to be examined
- * @return true if the exception signals a disconnection
- */
- boolean isFatalException(final SQLException e) {
- boolean fatalException = isDisconnectionSqlException(e);
- if (!fatalException) {
- SQLException parentException = e;
- SQLException nextException = e.getNextException();
- while (nextException != null && nextException != parentException && !fatalException) {
- fatalException = isDisconnectionSqlException(nextException);
- parentException = nextException;
- nextException = parentException.getNextException();
- }
- }
- return fatalException;
- }
-
- /**
- * @return Whether to fail-fast.
- * @since 2.6.0
- */
- public boolean isFastFailValidation() {
- return fastFailValidation;
- }
-
- @Override
- protected void passivate() throws SQLException {
- super.passivate();
- setClosedInternal(true);
- if (getDelegateInternal() instanceof PoolingConnection) {
- ((PoolingConnection) getDelegateInternal()).connectionReturnedToPool();
- }
- }
-
- /**
- * Actually close my underlying {@link Connection}.
- */
- @Override
- public void reallyClose() throws SQLException {
- if (jmxObjectName != null) {
- jmxObjectName.unregisterMBean();
- }
-
- if (validationPreparedStatement != null) {
- Utils.closeQuietly((AutoCloseable) validationPreparedStatement);
- }
-
- super.closeInternal();
- }
-
- /**
- * Validates the connection, using the following algorithm:
- * <ol>
- * <li>If {@code fastFailValidation} (constructor argument) is {@code true} and this connection has previously
- * thrown a fatal disconnection exception, a {@code SQLException} is thrown.</li>
- * <li>If {@code sql} is null, the driver's #{@link Connection#isValid(int) isValid(timeout)} is called. If it
- * returns {@code false}, {@code SQLException} is thrown; otherwise, this method returns successfully.</li>
- * <li>If {@code sql} is not null, it is executed as a query and if the resulting {@code ResultSet} contains at
- * least one row, this method returns successfully. If not, {@code SQLException} is thrown.</li>
- * </ol>
- *
- * @param sql
- * The validation SQL query.
- * @param timeoutSeconds
- * The validation timeout in seconds.
- * @throws SQLException
- * Thrown when validation fails or an SQLException occurs during validation
- * @deprecated Use {@link #validate(String, Duration)}.
- */
- @Deprecated
- public void validate(final String sql, final int timeoutSeconds) throws SQLException {
- validate(sql, Duration.ofSeconds(timeoutSeconds));
- }
-
- /**
- * Validates the connection, using the following algorithm:
- * <ol>
- * <li>If {@code fastFailValidation} (constructor argument) is {@code true} and this connection has previously
- * thrown a fatal disconnection exception, a {@code SQLException} is thrown.</li>
- * <li>If {@code sql} is null, the driver's #{@link Connection#isValid(int) isValid(timeout)} is called. If it
- * returns {@code false}, {@code SQLException} is thrown; otherwise, this method returns successfully.</li>
- * <li>If {@code sql} is not null, it is executed as a query and if the resulting {@code ResultSet} contains at
- * least one row, this method returns successfully. If not, {@code SQLException} is thrown.</li>
- * </ol>
- *
- * @param sql
- * The validation SQL query.
- * @param timeoutDuration
- * The validation timeout in seconds.
- * @throws SQLException
- * Thrown when validation fails or an SQLException occurs during validation
- * @since 2.10.0
- */
- public void validate(final String sql, Duration timeoutDuration) throws SQLException {
- if (fastFailValidation && fatalSqlExceptionThrown) {
- throw new SQLException(Utils.getMessage("poolableConnection.validate.fastFail"));
- }
-
- if (sql == null || sql.isEmpty()) {
- if (timeoutDuration.isNegative()) {
- timeoutDuration = Duration.ZERO;
- }
- if (!isValid(timeoutDuration)) {
- throw new SQLException("isValid() returned false");
- }
- return;
- }
-
- if (!sql.equals(lastValidationSql)) {
- lastValidationSql = sql;
- // Has to be the innermost delegate else the prepared statement will
- // be closed when the pooled connection is passivated.
- validationPreparedStatement = getInnermostDelegateInternal().prepareStatement(sql);
- }
-
- if (timeoutDuration.compareTo(Duration.ZERO) > 0) {
- validationPreparedStatement.setQueryTimeout((int) timeoutDuration.getSeconds());
- }
-
- try (ResultSet rs = validationPreparedStatement.executeQuery()) {
- if (!rs.next()) {
- throw new SQLException("validationQuery didn't return a row");
- }
- } catch (final SQLException sqle) {
- throw sqle;
- }
- }
-}
+/*
+ * 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;
+
+import java.lang.management.ManagementFactory;
+import java.sql.Connection;
+import java.sql.PreparedStatement;
+import java.sql.ResultSet;
+import java.sql.SQLException;
+import java.time.Duration;
+import java.util.Collection;
+import java.util.concurrent.Executor;
+
+import javax.management.InstanceAlreadyExistsException;
+import javax.management.MBeanRegistrationException;
+import javax.management.MBeanServer;
+import javax.management.NotCompliantMBeanException;
+import javax.management.ObjectName;
+
+import org.apache.commons.pool2.ObjectPool;
+
+/**
+ * A delegating connection that, rather than closing the underlying connection, returns itself to an {@link ObjectPool}
+ * when closed.
+ *
+ * @since 2.0
+ */
+public class PoolableConnection extends DelegatingConnection<Connection> implements PoolableConnectionMXBean {
+
+ private static MBeanServer MBEAN_SERVER;
+
+ static {
+ try {
+ MBEAN_SERVER = ManagementFactory.getPlatformMBeanServer();
+ } catch (final NoClassDefFoundError | Exception ignored) {
+ // ignore - JMX not available
+ }
+ }
+
+ /** The pool to which I should return. */
+ private final ObjectPool<PoolableConnection> pool;
+
+ private final ObjectNameWrapper jmxObjectName;
+
+ // Use a prepared statement for validation, retaining the last used SQL to
+ // check if the validation query has changed.
+ private PreparedStatement validationPreparedStatement;
+ private String lastValidationSql;
+
+ /**
+ * Indicate that unrecoverable SQLException was thrown when using this connection. Such a connection should be
+ * considered broken and not pass validation in the future.
+ */
+ private boolean fatalSqlExceptionThrown;
+
+ /**
+ * SQL_STATE codes considered to signal fatal conditions. Overrides the defaults in
+ * {@link Utils#DISCONNECTION_SQL_CODES} (plus anything starting with {@link Utils#DISCONNECTION_SQL_CODE_PREFIX}).
+ */
+ private final Collection<String> disconnectionSqlCodes;
+
+ /** Whether or not to fast fail validation after fatal connection errors */
+ private final boolean fastFailValidation;
+
+ /**
+ *
+ * @param conn
+ * my underlying connection
+ * @param pool
+ * the pool to which I should return when closed
+ * @param jmxName
+ * JMX name
+ */
+ public PoolableConnection(final Connection conn, final ObjectPool<PoolableConnection> pool,
+ final ObjectName jmxName) {
+ this(conn, pool, jmxName, null, true);
+ }
+
+ /**
+ *
+ * @param conn
+ * my underlying connection
+ * @param pool
+ * the pool to which I should return when closed
+ * @param jmxObjectName
+ * JMX name
+ * @param disconnectSqlCodes
+ * SQL_STATE codes considered fatal disconnection errors
+ * @param fastFailValidation
+ * true means fatal disconnection errors cause subsequent validations to fail immediately (no attempt to
+ * run query or isValid)
+ */
+ public PoolableConnection(final Connection conn, final ObjectPool<PoolableConnection> pool,
+ final ObjectName jmxObjectName, final Collection<String> disconnectSqlCodes,
+ final boolean fastFailValidation) {
+ super(conn);
+ this.pool = pool;
+ this.jmxObjectName = ObjectNameWrapper.wrap(jmxObjectName);
+ this.disconnectionSqlCodes = disconnectSqlCodes;
+ this.fastFailValidation = fastFailValidation;
+
+ if (jmxObjectName != null) {
+ try {
+ MBEAN_SERVER.registerMBean(this, jmxObjectName);
+ } catch (InstanceAlreadyExistsException | MBeanRegistrationException | NotCompliantMBeanException ignored) {
+ // For now, simply skip registration
+ }
+ }
+ }
+
+ /**
+ * Abort my underlying {@link Connection}.
+ *
+ * @since 2.9.0
+ */
+ @Override
+ public void abort(final Executor executor) throws SQLException {
+ if (jmxObjectName != null) {
+ jmxObjectName.unregisterMBean();
+ }
+ super.abort(executor);
+ }
+
+ /**
+ * Returns me to my pool.
+ */
+ @Override
+ public synchronized void close() throws SQLException {
+ if (isClosedInternal()) {
+ // already closed
+ return;
+ }
+
+ boolean isUnderlyingConnectionClosed;
+ try {
+ isUnderlyingConnectionClosed = getDelegateInternal().isClosed();
+ } catch (final SQLException e) {
+ try {
+ pool.invalidateObject(this);
+ } catch (final IllegalStateException ise) {
+ // pool is closed, so close the connection
+ passivate();
+ getInnermostDelegate().close();
+ } catch (final Exception ignored) {
+ // DO NOTHING the original exception will be rethrown
+ }
+ throw new SQLException("Cannot close connection (isClosed check failed)", e);
+ }
+
+ /*
+ * Can't set close before this code block since the connection needs to be open when validation runs. Can't set
+ * close after this code block since by then the connection will have been returned to the pool and may have
+ * been borrowed by another thread. Therefore, the close flag is set in passivate().
+ */
+ if (isUnderlyingConnectionClosed) {
+ // Abnormal close: underlying connection closed unexpectedly, so we
+ // must destroy this proxy
+ try {
+ pool.invalidateObject(this);
+ } catch (final IllegalStateException e) {
+ // pool is closed, so close the connection
+ passivate();
+ getInnermostDelegate().close();
+ } catch (final Exception e) {
+ throw new SQLException("Cannot close connection (invalidating pooled object failed)", e);
+ }
+ } else {
+ // Normal close: underlying connection is still open, so we
+ // simply need to return this proxy to the pool
+ try {
+ pool.returnObject(this);
+ } catch (final IllegalStateException e) {
+ // pool is closed, so close the connection
+ passivate();
+ getInnermostDelegate().close();
+ } catch (final SQLException | RuntimeException e) {
+ throw e;
+ } catch (final Exception e) {
+ throw new SQLException("Cannot close connection (return to pool failed)", e);
+ }
+ }
+ }
+
+ /**
+ * @return The disconnection SQL codes.
+ * @since 2.6.0
+ */
+ public Collection<String> getDisconnectionSqlCodes() {
+ return disconnectionSqlCodes;
+ }
+
+ /**
+ * Expose the {@link #toString()} method via a bean getter so it can be read as a property via JMX.
+ */
+ @Override
+ public String getToString() {
+ return toString();
+ }
+
+ @Override
+ protected void handleException(final SQLException e) throws SQLException {
+ fatalSqlExceptionThrown |= isFatalException(e);
+ super.handleException(e);
+ }
+
+ /**
+ * {@inheritDoc}
+ * <p>
+ * This method should not be used by a client to determine whether or not a connection should be return to the
+ * connection pool (by calling {@link #close()}). Clients should always attempt to return a connection to the pool
+ * once it is no longer required.
+ */
+ @Override
+ public boolean isClosed() throws SQLException {
+ if (isClosedInternal()) {
+ return true;
+ }
+
+ if (getDelegateInternal().isClosed()) {
+ // Something has gone wrong. The underlying connection has been
+ // closed without the connection being returned to the pool. Return
+ // it now.
+ close();
+ return true;
+ }
+
+ return false;
+ }
+
+ /**
+ * Checks the SQLState of the input exception.
+ * <p>
+ * If {@link #disconnectionSqlCodes} has been set, sql states are compared to those in the configured list of fatal
+ * exception codes. If this property is not set, codes are compared against the default codes in
+ * {@link Utils#DISCONNECTION_SQL_CODES} and in this case anything starting with #{link
+ * Utils.DISCONNECTION_SQL_CODE_PREFIX} is considered a disconnection.
+ * </p>
+ *
+ * @param e SQLException to be examined
+ * @return true if the exception signals a disconnection
+ */
+ boolean isDisconnectionSqlException(final SQLException e) {
+ boolean fatalException = false;
+ final String sqlState = e.getSQLState();
+ if (sqlState != null) {
+ fatalException = disconnectionSqlCodes == null
+ ? sqlState.startsWith(Utils.DISCONNECTION_SQL_CODE_PREFIX) || Utils.DISCONNECTION_SQL_CODES.contains(sqlState)
+ : disconnectionSqlCodes.contains(sqlState);
+ }
+ return fatalException;
+ }
+
+ /**
+ * Checks the SQLState of the input exception and any nested SQLExceptions it wraps.
+ * <p>
+ * If {@link #disconnectionSqlCodes} has been set, sql states are compared to those in the
+ * configured list of fatal exception codes. If this property is not set, codes are compared against the default
+ * codes in {@link Utils#DISCONNECTION_SQL_CODES} and in this case anything starting with #{link
+ * Utils.DISCONNECTION_SQL_CODE_PREFIX} is considered a disconnection.
+ * </p>
+ *
+ * @param e
+ * SQLException to be examined
+ * @return true if the exception signals a disconnection
+ */
+ boolean isFatalException(final SQLException e) {
+ boolean fatalException = isDisconnectionSqlException(e);
+ if (!fatalException) {
+ SQLException parentException = e;
+ SQLException nextException = e.getNextException();
+ while (nextException != null && nextException != parentException && !fatalException) {
+ fatalException = isDisconnectionSqlException(nextException);
+ parentException = nextException;
+ nextException = parentException.getNextException();
+ }
+ }
+ return fatalException;
+ }
+
+ /**
+ * @return Whether to fail-fast.
+ * @since 2.6.0
+ */
+ public boolean isFastFailValidation() {
+ return fastFailValidation;
+ }
+
+ @Override
+ protected void passivate() throws SQLException {
+ super.passivate();
+ setClosedInternal(true);
+ if (getDelegateInternal() instanceof PoolingConnection) {
+ ((PoolingConnection) getDelegateInternal()).connectionReturnedToPool();
+ }
+ }
+
+ /**
+ * Actually close my underlying {@link Connection}.
+ */
+ @Override
+ public void reallyClose() throws SQLException {
+ if (jmxObjectName != null) {
+ jmxObjectName.unregisterMBean();
+ }
+
+ if (validationPreparedStatement != null) {
+ Utils.closeQuietly((AutoCloseable) validationPreparedStatement);
+ }
+
+ super.closeInternal();
+ }
+
+ /**
+ * Validates the connection, using the following algorithm:
+ * <ol>
+ * <li>If {@code fastFailValidation} (constructor argument) is {@code true} and this connection has previously
+ * thrown a fatal disconnection exception, a {@code SQLException} is thrown.</li>
+ * <li>If {@code sql} is null, the driver's #{@link Connection#isValid(int) isValid(timeout)} is called. If it
+ * returns {@code false}, {@code SQLException} is thrown; otherwise, this method returns successfully.</li>
+ * <li>If {@code sql} is not null, it is executed as a query and if the resulting {@code ResultSet} contains at
+ * least one row, this method returns successfully. If not, {@code SQLException} is thrown.</li>
+ * </ol>
+ *
+ * @param sql
+ * The validation SQL query.
+ * @param timeoutSeconds
+ * The validation timeout in seconds.
+ * @throws SQLException
+ * Thrown when validation fails or an SQLException occurs during validation
+ * @deprecated Use {@link #validate(String, Duration)}.
+ */
+ @Deprecated
+ public void validate(final String sql, final int timeoutSeconds) throws SQLException {
+ validate(sql, Duration.ofSeconds(timeoutSeconds));
+ }
+
+ /**
+ * Validates the connection, using the following algorithm:
+ * <ol>
+ * <li>If {@code fastFailValidation} (constructor argument) is {@code true} and this connection has previously
+ * thrown a fatal disconnection exception, a {@code SQLException} is thrown.</li>
+ * <li>If {@code sql} is null, the driver's #{@link Connection#isValid(int) isValid(timeout)} is called. If it
+ * returns {@code false}, {@code SQLException} is thrown; otherwise, this method returns successfully.</li>
+ * <li>If {@code sql} is not null, it is executed as a query and if the resulting {@code ResultSet} contains at
+ * least one row, this method returns successfully. If not, {@code SQLException} is thrown.</li>
+ * </ol>
+ *
+ * @param sql
+ * The validation SQL query.
+ * @param timeoutDuration
+ * The validation timeout in seconds.
+ * @throws SQLException
+ * Thrown when validation fails or an SQLException occurs during validation
+ * @since 2.10.0
+ */
+ public void validate(final String sql, Duration timeoutDuration) throws SQLException {
+ if (fastFailValidation && fatalSqlExceptionThrown) {
+ throw new SQLException(Utils.getMessage("poolableConnection.validate.fastFail"));
+ }
+
+ if (sql == null || sql.isEmpty()) {
+ if (timeoutDuration.isNegative()) {
+ timeoutDuration = Duration.ZERO;
+ }
+ if (!isValid(timeoutDuration)) {
+ throw new SQLException("isValid() returned false");
+ }
+ return;
+ }
+
+ if (!sql.equals(lastValidationSql)) {
+ lastValidationSql = sql;
+ // Has to be the innermost delegate else the prepared statement will
+ // be closed when the pooled connection is passivated.
+ validationPreparedStatement = getInnermostDelegateInternal().prepareStatement(sql);
+ }
+
+ if (timeoutDuration.compareTo(Duration.ZERO) > 0) {
+ validationPreparedStatement.setQueryTimeout((int) timeoutDuration.getSeconds());
+ }
+
+ try (ResultSet rs = validationPreparedStatement.executeQuery()) {
+ if (!rs.next()) {
+ throw new SQLException("validationQuery didn't return a row");
+ }
+ } catch (final SQLException sqle) {
+ throw sqle;
+ }
+ }
+}
diff --git a/src/main/java/org/apache/commons/dbcp2/PoolingConnection.java b/src/main/java/org/apache/commons/dbcp2/PoolingConnection.java
index 3dd9650d..fb1a7915 100644
--- a/src/main/java/org/apache/commons/dbcp2/PoolingConnection.java
+++ b/src/main/java/org/apache/commons/dbcp2/PoolingConnection.java
@@ -1,623 +1,623 @@
-/*
- * 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;
-
-import java.sql.CallableStatement;
-import java.sql.Connection;
-import java.sql.PreparedStatement;
-import java.sql.SQLException;
-import java.util.NoSuchElementException;
-
-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 DelegatingConnection} that pools {@link PreparedStatement}s.
- * <p>
- * The {@link #prepareStatement} and {@link #prepareCall} methods, rather than creating a new PreparedStatement each
- * time, may actually pull the statement from a pool of unused statements. The {@link PreparedStatement#close} method of
- * the returned statement doesn't actually close the statement, but rather returns it to the pool. (See
- * {@link PoolablePreparedStatement}, {@link PoolableCallableStatement}.)
- * </p>
- *
- * @see PoolablePreparedStatement
- * @since 2.0
- */
-public class PoolingConnection extends DelegatingConnection<Connection>
- implements KeyedPooledObjectFactory<PStmtKey, DelegatingPreparedStatement> {
-
- /**
- * Statement types.
- *
- * @since 2.0 protected enum.
- * @since 2.4.0 public enum.
- */
- public enum StatementType {
-
- /**
- * Callable statement.
- */
- CALLABLE_STATEMENT,
-
- /**
- * Prepared statement.
- */
- PREPARED_STATEMENT
- }
-
- /** Pool of {@link PreparedStatement}s. and {@link CallableStatement}s */
- private KeyedObjectPool<PStmtKey, DelegatingPreparedStatement> pstmtPool;
-
- private boolean clearStatementPoolOnReturn;
-
- /**
- * Constructor.
- *
- * @param connection
- * the underlying {@link Connection}.
- */
- public PoolingConnection(final Connection connection) {
- super(connection);
- }
-
- /**
- * {@link KeyedPooledObjectFactory} method for activating pooled statements.
- *
- * @param key
- * ignored
- * @param pooledObject
- * wrapped pooled statement to be activated
- */
- @Override
- public void activateObject(final PStmtKey key, final PooledObject<DelegatingPreparedStatement> pooledObject)
- throws Exception {
- pooledObject.getObject().activate();
- }
-
- /**
- * Closes and frees all {@link PreparedStatement}s or {@link CallableStatement}s from the pool, and close the
- * underlying connection.
- */
- @Override
- public synchronized void close() throws SQLException {
- try {
- if (null != pstmtPool) {
- final KeyedObjectPool<PStmtKey, DelegatingPreparedStatement> oldpool = pstmtPool;
- pstmtPool = null;
- try {
- oldpool.close();
- } catch (final RuntimeException e) {
- throw e;
- } catch (final Exception e) {
- throw new SQLException("Cannot close connection", e);
- }
- }
- } finally {
- try {
- getDelegateInternal().close();
- } finally {
- setClosedInternal(true);
- }
- }
- }
-
- /**
- * Notification from {@link PoolableConnection} that we returned to the pool.
- *
- * @throws SQLException when {@code clearStatementPoolOnReturn} is true and the statement pool could not be
- * cleared
- * @since 2.8.0
- */
- public void connectionReturnedToPool() throws SQLException {
- if (pstmtPool != null && clearStatementPoolOnReturn) {
- try {
- pstmtPool.clear();
- } catch (final Exception e) {
- throw new SQLException("Error clearing statement pool", e);
- }
- }
- }
-
- /**
- * Creates a PStmtKey for the given arguments.
- *
- * @param sql
- * the SQL string used to define the statement
- *
- * @return the PStmtKey created for the given arguments.
- */
- protected PStmtKey createKey(final String sql) {
- return new PStmtKey(normalizeSQL(sql), getCatalogOrNull(), getSchemaOrNull());
- }
-
- /**
- * Creates a PStmtKey for the given arguments.
- *
- * @param sql
- * the SQL string used to define the 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 the PStmtKey created for the given arguments.
- */
- protected PStmtKey createKey(final String sql, final int autoGeneratedKeys) {
- return new PStmtKey(normalizeSQL(sql), getCatalogOrNull(), getSchemaOrNull(), autoGeneratedKeys);
- }
-
- /**
- * Creates a PStmtKey for the given arguments.
- *
- * @param sql
- * the SQL string used to define the statement
- * @param resultSetType
- * result set type
- * @param resultSetConcurrency
- * result set concurrency
- *
- * @return the PStmtKey created for the given arguments.
- */
- protected PStmtKey createKey(final String sql, final int resultSetType, final int resultSetConcurrency) {
- return new PStmtKey(normalizeSQL(sql), getCatalogOrNull(), getSchemaOrNull(), resultSetType, resultSetConcurrency);
- }
-
- /**
- * Creates a PStmtKey for the given arguments.
- *
- * @param sql
- * the SQL string used to define the statement
- * @param resultSetType
- * result set type
- * @param resultSetConcurrency
- * result set concurrency
- * @param resultSetHoldability
- * result set holdability
- *
- * @return the PStmtKey created for the given arguments.
- */
- 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 PStmtKey for the given arguments.
- *
- * @param sql
- * the SQL string used to define the statement
- * @param resultSetType
- * result set type
- * @param resultSetConcurrency
- * result set concurrency
- * @param resultSetHoldability
- * result set holdability
- * @param statementType
- * statement type
- *
- * @return the PStmtKey created for the given arguments.
- */
- 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 PStmtKey for the given arguments.
- *
- * @param sql
- * the SQL string used to define the statement
- * @param resultSetType
- * result set type
- * @param resultSetConcurrency
- * result set concurrency
- * @param statementType
- * statement type
- *
- * @return the PStmtKey created for the given arguments.
- */
- 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 PStmtKey for the given arguments.
- *
- * @param sql
- * the SQL string used to define the statement
- * @param columnIndexes
- * An array of column indexes indicating the columns that should be returned from the inserted row or
- * rows.
- *
- * @return the PStmtKey created for the given arguments.
- */
- protected PStmtKey createKey(final String sql, final int[] columnIndexes) {
- return new PStmtKey(normalizeSQL(sql), getCatalogOrNull(), getSchemaOrNull(), columnIndexes);
- }
-
- /**
- * Creates a PStmtKey for the given arguments.
- *
- * @param sql
- * the SQL string used to define the statement
- * @param statementType
- * statement type
- *
- * @return the PStmtKey created for the given arguments.
- */
- protected PStmtKey createKey(final String sql, final StatementType statementType) {
- return new PStmtKey(normalizeSQL(sql), getCatalogOrNull(), getSchemaOrNull(), statementType, null);
- }
-
- /**
- * Creates a PStmtKey for the given arguments.
- *
- * @param sql
- * the SQL string used to define the statement
- * @param columnNames
- * column names
- *
- * @return the PStmtKey created for the given arguments.
- */
- protected PStmtKey createKey(final String sql, final String[] columnNames) {
- return new PStmtKey(normalizeSQL(sql), getCatalogOrNull(), getSchemaOrNull(), columnNames);
- }
-
- /**
- * {@link KeyedPooledObjectFactory} method for destroying PoolablePreparedStatements and PoolableCallableStatements.
- * Closes the underlying statement.
- *
- * @param key
- * ignored
- * @param pooledObject
- * the wrapped pooled statement to be destroyed.
- */
- @Override
- public void destroyObject(final PStmtKey key, final PooledObject<DelegatingPreparedStatement> pooledObject)
- throws Exception {
- pooledObject.getObject().getInnermostDelegate().close();
- }
-
- private String getCatalogOrNull() {
- String catalog = null;
- try {
- catalog = getCatalog();
- } catch (final SQLException e) {
- // Ignored
- }
- return catalog;
- }
-
- private String getSchemaOrNull() {
- String schema = null;
- try {
- schema = getSchema();
- } catch (final SQLException e) {
- // Ignored
- }
- return schema;
- }
-
- /**
- * Returns the prepared statement pool we're using.
- *
- * @return statement pool
- * @since 2.8.0
- */
- public KeyedObjectPool<PStmtKey, DelegatingPreparedStatement> getStatementPool() {
- return pstmtPool;
- }
-
- /**
- * {@link KeyedPooledObjectFactory} method for creating {@link PoolablePreparedStatement}s or
- * {@link PoolableCallableStatement}s. The {@code stmtType} field in the key determines whether a
- * PoolablePreparedStatement or PoolableCallableStatement is created.
- *
- * @param key
- * the key for the {@link PreparedStatement} to be created
- * @see #createKey(String, int, int, StatementType)
- */
- @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(getDelegate());
- @SuppressWarnings({"rawtypes", "unchecked" }) // Unable to find way to avoid this
- final PoolablePreparedStatement pps = new PoolablePreparedStatement(statement, key, pstmtPool, this);
- return new DefaultPooledObject<>(pps);
- }
- final CallableStatement statement = (CallableStatement) key.createStatement(getDelegate());
- final PoolableCallableStatement pcs = new PoolableCallableStatement(statement, key, pstmtPool, this);
- return new DefaultPooledObject<>(pcs);
- }
-
- /**
- * Normalizes the given SQL statement, producing a canonical form that is semantically equivalent to the original.
- *
- * @param sql The statement to be normalized.
- *
- * @return The canonical form of the supplied SQL statement.
- */
- protected String normalizeSQL(final String sql) {
- return sql.trim();
- }
-
- /**
- * {@link KeyedPooledObjectFactory} method for passivating {@link PreparedStatement}s or {@link CallableStatement}s.
- * 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 the pool.
- *
- * @param key
- * a {@link PStmtKey} for the given arguments
- * @return a {@link PoolableCallableStatement}
- * @throws SQLException
- * Wraps an underlying exception.
- */
- private CallableStatement prepareCall(final PStmtKey key) throws SQLException {
- return (CallableStatement) prepareStatement(key);
- }
-
- /**
- * Creates or obtains a {@link CallableStatement} from the pool.
- *
- * @param sql
- * the SQL string used to define the CallableStatement
- * @return a {@link PoolableCallableStatement}
- * @throws SQLException
- * Wraps an underlying exception.
- */
- @Override
- public CallableStatement prepareCall(final String sql) throws SQLException {
- return prepareCall(createKey(sql, StatementType.CALLABLE_STATEMENT));
- }
-
- /**
- * Creates or obtains a {@link CallableStatement} from the pool.
- *
- * @param sql
- * the SQL string used to define the CallableStatement
- * @param resultSetType
- * result set type
- * @param resultSetConcurrency
- * result set concurrency
- * @return a {@link PoolableCallableStatement}
- * @throws SQLException
- * Wraps an underlying exception.
- */
- @Override
- public CallableStatement prepareCall(final String sql, final int resultSetType, final int resultSetConcurrency)
- throws SQLException {
- return prepareCall(createKey(sql, resultSetType, resultSetConcurrency, StatementType.CALLABLE_STATEMENT));
- }
-
- /**
- * Creates or obtains a {@link CallableStatement} from the pool.
- *
- * @param sql
- * the SQL string used to define the CallableStatement
- * @param resultSetType
- * result set type
- * @param resultSetConcurrency
- * result set concurrency
- * @param resultSetHoldability
- * result set holdability
- * @return a {@link PoolableCallableStatement}
- * @throws SQLException
- * Wraps an underlying exception.
- */
- @Override
- public CallableStatement prepareCall(final String sql, final int resultSetType, final int resultSetConcurrency,
- final int resultSetHoldability) throws SQLException {
- return prepareCall(createKey(sql, resultSetType, resultSetConcurrency,
- resultSetHoldability, StatementType.CALLABLE_STATEMENT));
- }
-
- /**
- * Creates or obtains a {@link PreparedStatement} from the pool.
- *
- * @param key
- * a {@link PStmtKey} for the given arguments
- * @return a {@link PoolablePreparedStatement}
- * @throws SQLException
- * Wraps an underlying exception.
- */
- private PreparedStatement prepareStatement(final PStmtKey key) throws SQLException {
- if (null == pstmtPool) {
- throw new SQLException("Statement pool is null - closed or invalid PoolingConnection.");
- }
- try {
- return pstmtPool.borrowObject(key);
- } catch (final NoSuchElementException e) {
- throw new SQLException("MaxOpenPreparedStatements limit reached", e);
- } 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 the pool.
- *
- * @param sql
- * the SQL string used to define the PreparedStatement
- * @return a {@link PoolablePreparedStatement}
- * @throws SQLException
- * Wraps an underlying exception.
- */
- @Override
- public PreparedStatement prepareStatement(final String sql) throws SQLException {
- return prepareStatement(createKey(sql));
- }
-
- /*
- * Creates or obtains a {@link PreparedStatement} from the pool.
- *
- * @param sql
- * the SQL string used to define the PreparedStatement
- * @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
- * Wraps an underlying exception.
- */
- @Override
- public PreparedStatement prepareStatement(final String sql, final int autoGeneratedKeys) throws SQLException {
- return prepareStatement(createKey(sql, autoGeneratedKeys));
- }
-
- /**
- * Creates or obtains a {@link PreparedStatement} from the pool.
- *
- * @param sql
- * the SQL string used to define the PreparedStatement
- * @param resultSetType
- * result set type
- * @param resultSetConcurrency
- * result set concurrency
- * @return a {@link PoolablePreparedStatement}
- * @throws SQLException
- * Wraps an underlying exception.
- */
- @Override
- public PreparedStatement prepareStatement(final String sql, final int resultSetType, final int resultSetConcurrency)
- throws SQLException {
- return prepareStatement(createKey(sql, resultSetType, resultSetConcurrency));
- }
-
- /**
- * Creates or obtains a {@link PreparedStatement} from the pool.
- *
- * @param sql
- * the SQL string used to define the PreparedStatement
- * @param resultSetType
- * result set type
- * @param resultSetConcurrency
- * result set concurrency
- * @param resultSetHoldability
- * result set holdability
- * @return a {@link PoolablePreparedStatement}
- * @throws SQLException
- * Wraps an underlying exception.
- */
- @Override
- public PreparedStatement prepareStatement(final String sql, final int resultSetType, final int resultSetConcurrency,
- final int resultSetHoldability) throws SQLException {
- return prepareStatement(createKey(sql, resultSetType, resultSetConcurrency, resultSetHoldability));
- }
-
- /**
- * Creates or obtains a {@link PreparedStatement} from the pool.
- *
- * @param sql
- * the SQL string used to define the PreparedStatement
- * @param columnIndexes
- * An array of column indexes indicating the columns that should be returned from the inserted row or
- * rows.
- * @return a {@link PoolablePreparedStatement}
- * @throws SQLException
- * Wraps an underlying exception.
- *
- */
- @Override
- public PreparedStatement prepareStatement(final String sql, final int[] columnIndexes) throws SQLException {
- return prepareStatement(createKey(sql, columnIndexes));
- }
-
- /**
- * Creates or obtains a {@link PreparedStatement} from the pool.
- *
- * @param sql
- * the SQL string used to define the PreparedStatement
- * @param columnNames
- * column names
- * @return a {@link PoolablePreparedStatement}
- * @throws SQLException
- * Wraps an underlying exception.
- */
- @Override
- public PreparedStatement prepareStatement(final String sql, final String[] columnNames) throws SQLException {
- return prepareStatement(createKey(sql, columnNames));
- }
-
- /**
- * Sets whether the pool of statements should be cleared when the connection is returned to its pool.
- * Default is false.
- *
- * @param clearStatementPoolOnReturn clear or not
- * @since 2.8.0
- */
- public void setClearStatementPoolOnReturn(final boolean clearStatementPoolOnReturn) {
- this.clearStatementPoolOnReturn = clearStatementPoolOnReturn;
- }
-
- /**
- * Sets the prepared statement pool.
- *
- * @param pool
- * the prepared statement pool.
- */
- public void setStatementPool(final KeyedObjectPool<PStmtKey, DelegatingPreparedStatement> pool) {
- pstmtPool = pool;
- }
-
- @Override
- public synchronized String toString() {
- if (pstmtPool != null) {
- return "PoolingConnection: " + pstmtPool.toString();
- }
- return "PoolingConnection: null";
- }
-
- /**
- * {@link KeyedPooledObjectFactory} method for validating pooled statements. Currently always returns true.
- *
- * @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;
+
+import java.sql.CallableStatement;
+import java.sql.Connection;
+import java.sql.PreparedStatement;
+import java.sql.SQLException;
+import java.util.NoSuchElementException;
+
+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 DelegatingConnection} that pools {@link PreparedStatement}s.
+ * <p>
+ * The {@link #prepareStatement} and {@link #prepareCall} methods, rather than creating a new PreparedStatement each
+ * time, may actually pull the statement from a pool of unused statements. The {@link PreparedStatement#close} method of
+ * the returned statement doesn't actually close the statement, but rather returns it to the pool. (See
+ * {@link PoolablePreparedStatement}, {@link PoolableCallableStatement}.)
+ * </p>
+ *
+ * @see PoolablePreparedStatement
+ * @since 2.0
+ */
+public class PoolingConnection extends DelegatingConnection<Connection>
+ implements KeyedPooledObjectFactory<PStmtKey, DelegatingPreparedStatement> {
+
+ /**
+ * Statement types.
+ *
+ * @since 2.0 protected enum.
+ * @since 2.4.0 public enum.
+ */
+ public enum StatementType {
+
+ /**
+ * Callable statement.
+ */
+ CALLABLE_STATEMENT,
+
+ /**
+ * Prepared statement.
+ */
+ PREPARED_STATEMENT
+ }
+
+ /** Pool of {@link PreparedStatement}s. and {@link CallableStatement}s */
+ private KeyedObjectPool<PStmtKey, DelegatingPreparedStatement> pstmtPool;
+
+ private boolean clearStatementPoolOnReturn;
+
+ /**
+ * Constructor.
+ *
+ * @param connection
+ * the underlying {@link Connection}.
+ */
+ public PoolingConnection(final Connection connection) {
+ super(connection);
+ }
+
+ /**
+ * {@link KeyedPooledObjectFactory} method for activating pooled statements.
+ *
+ * @param key
+ * ignored
+ * @param pooledObject
+ * wrapped pooled statement to be activated
+ */
+ @Override
+ public void activateObject(final PStmtKey key, final PooledObject<DelegatingPreparedStatement> pooledObject)
+ throws Exception {
+ pooledObject.getObject().activate();
+ }
+
+ /**
+ * Closes and frees all {@link PreparedStatement}s or {@link CallableStatement}s from the pool, and close the
+ * underlying connection.
+ */
+ @Override
+ public synchronized void close() throws SQLException {
+ try {
+ if (null != pstmtPool) {
+ final KeyedObjectPool<PStmtKey, DelegatingPreparedStatement> oldpool = pstmtPool;
+ pstmtPool = null;
+ try {
+ oldpool.close();
+ } catch (final RuntimeException e) {
+ throw e;
+ } catch (final Exception e) {
+ throw new SQLException("Cannot close connection", e);
+ }
+ }
+ } finally {
+ try {
+ getDelegateInternal().close();
+ } finally {
+ setClosedInternal(true);
+ }
+ }
+ }
+
+ /**
+ * Notification from {@link PoolableConnection} that we returned to the pool.
+ *
+ * @throws SQLException when {@code clearStatementPoolOnReturn} is true and the statement pool could not be
+ * cleared
+ * @since 2.8.0
+ */
+ public void connectionReturnedToPool() throws SQLException {
+ if (pstmtPool != null && clearStatementPoolOnReturn) {
+ try {
+ pstmtPool.clear();
+ } catch (final Exception e) {
+ throw new SQLException("Error clearing statement pool", e);
+ }
+ }
+ }
+
+ /**
+ * Creates a PStmtKey for the given arguments.
+ *
+ * @param sql
+ * the SQL string used to define the statement
+ *
+ * @return the PStmtKey created for the given arguments.
+ */
+ protected PStmtKey createKey(final String sql) {
+ return new PStmtKey(normalizeSQL(sql), getCatalogOrNull(), getSchemaOrNull());
+ }
+
+ /**
+ * Creates a PStmtKey for the given arguments.
+ *
+ * @param sql
+ * the SQL string used to define the 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 the PStmtKey created for the given arguments.
+ */
+ protected PStmtKey createKey(final String sql, final int autoGeneratedKeys) {
+ return new PStmtKey(normalizeSQL(sql), getCatalogOrNull(), getSchemaOrNull(), autoGeneratedKeys);
+ }
+
+ /**
+ * Creates a PStmtKey for the given arguments.
+ *
+ * @param sql
+ * the SQL string used to define the statement
+ * @param resultSetType
+ * result set type
+ * @param resultSetConcurrency
+ * result set concurrency
+ *
+ * @return the PStmtKey created for the given arguments.
+ */
+ protected PStmtKey createKey(final String sql, final int resultSetType, final int resultSetConcurrency) {
+ return new PStmtKey(normalizeSQL(sql), getCatalogOrNull(), getSchemaOrNull(), resultSetType, resultSetConcurrency);
+ }
+
+ /**
+ * Creates a PStmtKey for the given arguments.
+ *
+ * @param sql
+ * the SQL string used to define the statement
+ * @param resultSetType
+ * result set type
+ * @param resultSetConcurrency
+ * result set concurrency
+ * @param resultSetHoldability
+ * result set holdability
+ *
+ * @return the PStmtKey created for the given arguments.
+ */
+ 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 PStmtKey for the given arguments.
+ *
+ * @param sql
+ * the SQL string used to define the statement
+ * @param resultSetType
+ * result set type
+ * @param resultSetConcurrency
+ * result set concurrency
+ * @param resultSetHoldability
+ * result set holdability
+ * @param statementType
+ * statement type
+ *
+ * @return the PStmtKey created for the given arguments.
+ */
+ 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 PStmtKey for the given arguments.
+ *
+ * @param sql
+ * the SQL string used to define the statement
+ * @param resultSetType
+ * result set type
+ * @param resultSetConcurrency
+ * result set concurrency
+ * @param statementType
+ * statement type
+ *
+ * @return the PStmtKey created for the given arguments.
+ */
+ 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 PStmtKey for the given arguments.
+ *
+ * @param sql
+ * the SQL string used to define the statement
+ * @param columnIndexes
+ * An array of column indexes indicating the columns that should be returned from the inserted row or
+ * rows.
+ *
+ * @return the PStmtKey created for the given arguments.
+ */
+ protected PStmtKey createKey(final String sql, final int[] columnIndexes) {
+ return new PStmtKey(normalizeSQL(sql), getCatalogOrNull(), getSchemaOrNull(), columnIndexes);
+ }
+
+ /**
+ * Creates a PStmtKey for the given arguments.
+ *
+ * @param sql
+ * the SQL string used to define the statement
+ * @param statementType
+ * statement type
+ *
+ * @return the PStmtKey created for the given arguments.
+ */
+ protected PStmtKey createKey(final String sql, final StatementType statementType) {
+ return new PStmtKey(normalizeSQL(sql), getCatalogOrNull(), getSchemaOrNull(), statementType, null);
+ }
+
+ /**
+ * Creates a PStmtKey for the given arguments.
+ *
+ * @param sql
+ * the SQL string used to define the statement
+ * @param columnNames
+ * column names
+ *
+ * @return the PStmtKey created for the given arguments.
+ */
+ protected PStmtKey createKey(final String sql, final String[] columnNames) {
+ return new PStmtKey(normalizeSQL(sql), getCatalogOrNull(), getSchemaOrNull(), columnNames);
+ }
+
+ /**
+ * {@link KeyedPooledObjectFactory} method for destroying PoolablePreparedStatements and PoolableCallableStatements.
+ * Closes the underlying statement.
+ *
+ * @param key
+ * ignored
+ * @param pooledObject
+ * the wrapped pooled statement to be destroyed.
+ */
+ @Override
+ public void destroyObject(final PStmtKey key, final PooledObject<DelegatingPreparedStatement> pooledObject)
+ throws Exception {
+ pooledObject.getObject().getInnermostDelegate().close();
+ }
+
+ private String getCatalogOrNull() {
+ String catalog = null;
+ try {
+ catalog = getCatalog();
+ } catch (final SQLException ignored) {
+ // Ignored
+ }
+ return catalog;
+ }
+
+ private String getSchemaOrNull() {
+ String schema = null;
+ try {
+ schema = getSchema();
+ } catch (final SQLException ignored) {
+ // Ignored
+ }
+ return schema;
+ }
+
+ /**
+ * Returns the prepared statement pool we're using.
+ *
+ * @return statement pool
+ * @since 2.8.0
+ */
+ public KeyedObjectPool<PStmtKey, DelegatingPreparedStatement> getStatementPool() {
+ return pstmtPool;
+ }
+
+ /**
+ * {@link KeyedPooledObjectFactory} method for creating {@link PoolablePreparedStatement}s or
+ * {@link PoolableCallableStatement}s. The {@code stmtType} field in the key determines whether a
+ * PoolablePreparedStatement or PoolableCallableStatement is created.
+ *
+ * @param key
+ * the key for the {@link PreparedStatement} to be created
+ * @see #createKey(String, int, int, StatementType)
+ */
+ @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(getDelegate());
+ @SuppressWarnings({"rawtypes", "unchecked" }) // Unable to find way to avoid this
+ final PoolablePreparedStatement pps = new PoolablePreparedStatement(statement, key, pstmtPool, this);
+ return new DefaultPooledObject<>(pps);
+ }
+ final CallableStatement statement = (CallableStatement) key.createStatement(getDelegate());
+ final PoolableCallableStatement pcs = new PoolableCallableStatement(statement, key, pstmtPool, this);
+ return new DefaultPooledObject<>(pcs);
+ }
+
+ /**
+ * Normalizes the given SQL statement, producing a canonical form that is semantically equivalent to the original.
+ *
+ * @param sql The statement to be normalized.
+ *
+ * @return The canonical form of the supplied SQL statement.
+ */
+ protected String normalizeSQL(final String sql) {
+ return sql.trim();
+ }
+
+ /**
+ * {@link KeyedPooledObjectFactory} method for passivating {@link PreparedStatement}s or {@link CallableStatement}s.
+ * 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 the pool.
+ *
+ * @param key
+ * a {@link PStmtKey} for the given arguments
+ * @return a {@link PoolableCallableStatement}
+ * @throws SQLException
+ * Wraps an underlying exception.
+ */
+ private CallableStatement prepareCall(final PStmtKey key) throws SQLException {
+ return (CallableStatement) prepareStatement(key);
+ }
+
+ /**
+ * Creates or obtains a {@link CallableStatement} from the pool.
+ *
+ * @param sql
+ * the SQL string used to define the CallableStatement
+ * @return a {@link PoolableCallableStatement}
+ * @throws SQLException
+ * Wraps an underlying exception.
+ */
+ @Override
+ public CallableStatement prepareCall(final String sql) throws SQLException {
+ return prepareCall(createKey(sql, StatementType.CALLABLE_STATEMENT));
+ }
+
+ /**
+ * Creates or obtains a {@link CallableStatement} from the pool.
+ *
+ * @param sql
+ * the SQL string used to define the CallableStatement
+ * @param resultSetType
+ * result set type
+ * @param resultSetConcurrency
+ * result set concurrency
+ * @return a {@link PoolableCallableStatement}
+ * @throws SQLException
+ * Wraps an underlying exception.
+ */
+ @Override
+ public CallableStatement prepareCall(final String sql, final int resultSetType, final int resultSetConcurrency)
+ throws SQLException {
+ return prepareCall(createKey(sql, resultSetType, resultSetConcurrency, StatementType.CALLABLE_STATEMENT));
+ }
+
+ /**
+ * Creates or obtains a {@link CallableStatement} from the pool.
+ *
+ * @param sql
+ * the SQL string used to define the CallableStatement
+ * @param resultSetType
+ * result set type
+ * @param resultSetConcurrency
+ * result set concurrency
+ * @param resultSetHoldability
+ * result set holdability
+ * @return a {@link PoolableCallableStatement}
+ * @throws SQLException
+ * Wraps an underlying exception.
+ */
+ @Override
+ public CallableStatement prepareCall(final String sql, final int resultSetType, final int resultSetConcurrency,
+ final int resultSetHoldability) throws SQLException {
+ return prepareCall(createKey(sql, resultSetType, resultSetConcurrency,
+ resultSetHoldability, StatementType.CALLABLE_STATEMENT));
+ }
+
+ /**
+ * Creates or obtains a {@link PreparedStatement} from the pool.
+ *
+ * @param key
+ * a {@link PStmtKey} for the given arguments
+ * @return a {@link PoolablePreparedStatement}
+ * @throws SQLException
+ * Wraps an underlying exception.
+ */
+ private PreparedStatement prepareStatement(final PStmtKey key) throws SQLException {
+ if (null == pstmtPool) {
+ throw new SQLException("Statement pool is null - closed or invalid PoolingConnection.");
+ }
+ try {
+ return pstmtPool.borrowObject(key);
+ } catch (final NoSuchElementException e) {
+ throw new SQLException("MaxOpenPreparedStatements limit reached", e);
+ } 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 the pool.
+ *
+ * @param sql
+ * the SQL string used to define the PreparedStatement
+ * @return a {@link PoolablePreparedStatement}
+ * @throws SQLException
+ * Wraps an underlying exception.
+ */
+ @Override
+ public PreparedStatement prepareStatement(final String sql) throws SQLException {
+ return prepareStatement(createKey(sql));
+ }
+
+ /*
+ * Creates or obtains a {@link PreparedStatement} from the pool.
+ *
+ * @param sql
+ * the SQL string used to define the PreparedStatement
+ * @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
+ * Wraps an underlying exception.
+ */
+ @Override
+ public PreparedStatement prepareStatement(final String sql, final int autoGeneratedKeys) throws SQLException {
+ return prepareStatement(createKey(sql, autoGeneratedKeys));
+ }
+
+ /**
+ * Creates or obtains a {@link PreparedStatement} from the pool.
+ *
+ * @param sql
+ * the SQL string used to define the PreparedStatement
+ * @param resultSetType
+ * result set type
+ * @param resultSetConcurrency
+ * result set concurrency
+ * @return a {@link PoolablePreparedStatement}
+ * @throws SQLException
+ * Wraps an underlying exception.
+ */
+ @Override
+ public PreparedStatement prepareStatement(final String sql, final int resultSetType, final int resultSetConcurrency)
+ throws SQLException {
+ return prepareStatement(createKey(sql, resultSetType, resultSetConcurrency));
+ }
+
+ /**
+ * Creates or obtains a {@link PreparedStatement} from the pool.
+ *
+ * @param sql
+ * the SQL string used to define the PreparedStatement
+ * @param resultSetType
+ * result set type
+ * @param resultSetConcurrency
+ * result set concurrency
+ * @param resultSetHoldability
+ * result set holdability
+ * @return a {@link PoolablePreparedStatement}
+ * @throws SQLException
+ * Wraps an underlying exception.
+ */
+ @Override
+ public PreparedStatement prepareStatement(final String sql, final int resultSetType, final int resultSetConcurrency,
+ final int resultSetHoldability) throws SQLException {
+ return prepareStatement(createKey(sql, resultSetType, resultSetConcurrency, resultSetHoldability));
+ }
+
+ /**
+ * Creates or obtains a {@link PreparedStatement} from the pool.
+ *
+ * @param sql
+ * the SQL string used to define the PreparedStatement
+ * @param columnIndexes
+ * An array of column indexes indicating the columns that should be returned from the inserted row or
+ * rows.
+ * @return a {@link PoolablePreparedStatement}
+ * @throws SQLException
+ * Wraps an underlying exception.
+ *
+ */
+ @Override
+ public PreparedStatement prepareStatement(final String sql, final int[] columnIndexes) throws SQLException {
+ return prepareStatement(createKey(sql, columnIndexes));
+ }
+
+ /**
+ * Creates or obtains a {@link PreparedStatement} from the pool.
+ *
+ * @param sql
+ * the SQL string used to define the PreparedStatement
+ * @param columnNames
+ * column names
+ * @return a {@link PoolablePreparedStatement}
+ * @throws SQLException
+ * Wraps an underlying exception.
+ */
+ @Override
+ public PreparedStatement prepareStatement(final String sql, final String[] columnNames) throws SQLException {
+ return prepareStatement(createKey(sql, columnNames));
+ }
+
+ /**
+ * Sets whether the pool of statements should be cleared when the connection is returned to its pool.
+ * Default is false.
+ *
+ * @param clearStatementPoolOnReturn clear or not
+ * @since 2.8.0
+ */
+ public void setClearStatementPoolOnReturn(final boolean clearStatementPoolOnReturn) {
+ this.clearStatementPoolOnReturn = clearStatementPoolOnReturn;
+ }
+
+ /**
+ * Sets the prepared statement pool.
+ *
+ * @param pool
+ * the prepared statement pool.
+ */
+ public void setStatementPool(final KeyedObjectPool<PStmtKey, DelegatingPreparedStatement> pool) {
+ pstmtPool = pool;
+ }
+
+ @Override
+ public synchronized String toString() {
+ if (pstmtPool != null) {
+ return "PoolingConnection: " + pstmtPool.toString();
+ }
+ return "PoolingConnection: null";
+ }
+
+ /**
+ * {@link KeyedPooledObjectFactory} method for validating pooled statements. Currently always returns true.
+ *
+ * @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/PoolingDriver.java b/src/main/java/org/apache/commons/dbcp2/PoolingDriver.java
index 234490c4..7a683553 100644
--- a/src/main/java/org/apache/commons/dbcp2/PoolingDriver.java
+++ b/src/main/java/org/apache/commons/dbcp2/PoolingDriver.java
@@ -1,260 +1,260 @@
-/*
- * 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;
-
-import java.sql.Connection;
-import java.sql.Driver;
-import java.sql.DriverManager;
-import java.sql.DriverPropertyInfo;
-import java.sql.SQLException;
-import java.sql.SQLFeatureNotSupportedException;
-import java.util.HashMap;
-import java.util.NoSuchElementException;
-import java.util.Properties;
-import java.util.logging.Logger;
-
-import org.apache.commons.pool2.ObjectPool;
-
-/**
- * A {@link Driver} implementation that obtains {@link Connection}s from a registered {@link ObjectPool}.
- *
- * @since 2.0
- */
-public class PoolingDriver implements Driver {
-
- /**
- * PoolGuardConnectionWrapper is a Connection wrapper that makes sure a closed connection cannot be used anymore.
- *
- * @since 2.0
- */
- private class PoolGuardConnectionWrapper extends DelegatingConnection<Connection> {
-
- private final ObjectPool<? extends Connection> pool;
-
- PoolGuardConnectionWrapper(final ObjectPool<? extends Connection> pool, final Connection delegate) {
- super(delegate);
- this.pool = pool;
- }
-
- /**
- * @see org.apache.commons.dbcp2.DelegatingConnection#getDelegate()
- */
- @Override
- public Connection getDelegate() {
- if (isAccessToUnderlyingConnectionAllowed()) {
- return super.getDelegate();
- }
- return null;
- }
-
- /**
- * @see org.apache.commons.dbcp2.DelegatingConnection#getInnermostDelegate()
- */
- @Override
- public Connection getInnermostDelegate() {
- if (isAccessToUnderlyingConnectionAllowed()) {
- return super.getInnermostDelegate();
- }
- return null;
- }
- }
-
- private static final DriverPropertyInfo[] EMPTY_DRIVER_PROPERTY_INFO_ARRAY = {};
-
- /* Register myself with the {@link DriverManager}. */
- static {
- try {
- DriverManager.registerDriver(new PoolingDriver());
- } catch (final Exception e) {
- // ignore
- }
- }
-
- /** The map of registered pools. */
- protected static final HashMap<String, ObjectPool<? extends Connection>> pools = new HashMap<>();
-
- /** My URL prefix */
- public static final String URL_PREFIX = "jdbc:apache:commons:dbcp:";
-
- protected static final int URL_PREFIX_LEN = URL_PREFIX.length();
-
- // version numbers
- protected static final int MAJOR_VERSION = 1;
-
- protected static final int MINOR_VERSION = 0;
-
- /** Controls access to the underlying connection */
- private final boolean accessToUnderlyingConnectionAllowed;
-
- /**
- * Constructs a new driver with {@code accessToUnderlyingConnectionAllowed} enabled.
- */
- public PoolingDriver() {
- this(true);
- }
-
- /**
- * For unit testing purposes.
- *
- * @param accessToUnderlyingConnectionAllowed
- * Do {@link DelegatingConnection}s created by this driver permit access to the delegate?
- */
- protected PoolingDriver(final boolean accessToUnderlyingConnectionAllowed) {
- this.accessToUnderlyingConnectionAllowed = accessToUnderlyingConnectionAllowed;
- }
-
- @Override
- public boolean acceptsURL(final String url) throws SQLException {
- return url != null && url.startsWith(URL_PREFIX);
- }
-
- /**
- * Closes a named pool.
- *
- * @param name
- * The pool name.
- * @throws SQLException
- * Thrown when a problem is caught closing the pool.
- */
- public synchronized void closePool(final String name) throws SQLException {
- @SuppressWarnings("resource")
- final ObjectPool<? extends Connection> pool = pools.get(name);
- if (pool != null) {
- pools.remove(name);
- try {
- pool.close();
- } catch (final Exception e) {
- throw new SQLException("Error closing pool " + name, e);
- }
- }
- }
-
- @Override
- public Connection connect(final String url, final Properties info) throws SQLException {
- if (acceptsURL(url)) {
- final ObjectPool<? extends Connection> pool = getConnectionPool(url.substring(URL_PREFIX_LEN));
-
- try {
- final Connection conn = pool.borrowObject();
- if (conn == null) {
- return null;
- }
- return new PoolGuardConnectionWrapper(pool, conn);
- } catch (final NoSuchElementException e) {
- throw new SQLException("Cannot get a connection, pool error: " + e.getMessage(), e);
- } catch (final SQLException | RuntimeException e) {
- throw e;
- } catch (final Exception e) {
- throw new SQLException("Cannot get a connection, general error: " + e.getMessage(), e);
- }
- }
- return null;
- }
-
- /**
- * Gets the connection pool for the given name.
- *
- * @param name
- * The pool name
- * @return The pool
- * @throws SQLException
- * Thrown when the named pool is not registered.
- */
- public synchronized ObjectPool<? extends Connection> getConnectionPool(final String name) throws SQLException {
- final ObjectPool<? extends Connection> pool = pools.get(name);
- if (null == pool) {
- throw new SQLException("Pool not registered: " + name);
- }
- return pool;
- }
-
- @Override
- public int getMajorVersion() {
- return MAJOR_VERSION;
- }
-
- @Override
- public int getMinorVersion() {
- return MINOR_VERSION;
- }
-
- @Override
- public Logger getParentLogger() throws SQLFeatureNotSupportedException {
- throw new SQLFeatureNotSupportedException();
- }
-
- /**
- * Gets the pool names.
- *
- * @return the pool names.
- */
- public synchronized String[] getPoolNames() {
- return pools.keySet().toArray(Utils.EMPTY_STRING_ARRAY);
- }
-
- @Override
- public DriverPropertyInfo[] getPropertyInfo(final String url, final Properties info) {
- return EMPTY_DRIVER_PROPERTY_INFO_ARRAY;
- }
- /**
- * Invalidates the given connection.
- *
- * @param conn
- * connection to invalidate
- * @throws SQLException
- * if the connection is not a {@code PoolGuardConnectionWrapper} or an error occurs invalidating
- * the connection
- */
- public void invalidateConnection(final Connection conn) throws SQLException {
- if (!(conn instanceof PoolGuardConnectionWrapper)) {
- throw new SQLException("Invalid connection class");
- }
- final PoolGuardConnectionWrapper pgconn = (PoolGuardConnectionWrapper) conn;
- @SuppressWarnings("unchecked")
- final ObjectPool<Connection> pool = (ObjectPool<Connection>) pgconn.pool;
- try {
- pool.invalidateObject(pgconn.getDelegateInternal());
- } catch (final Exception e) {
- // Ignore.
- }
- }
-
- /**
- * Returns the value of the accessToUnderlyingConnectionAllowed property.
- *
- * @return true if access to the underlying is allowed, false otherwise.
- */
- protected boolean isAccessToUnderlyingConnectionAllowed() {
- return accessToUnderlyingConnectionAllowed;
- }
- @Override
- public boolean jdbcCompliant() {
- return true;
- }
-
- /**
- * Registers a named pool.
- *
- * @param name
- * The pool name.
- * @param pool
- * The pool.
- */
- public synchronized void registerPool(final String name, final ObjectPool<? extends Connection> pool) {
- pools.put(name, pool);
- }
-}
+/*
+ * 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;
+
+import java.sql.Connection;
+import java.sql.Driver;
+import java.sql.DriverManager;
+import java.sql.DriverPropertyInfo;
+import java.sql.SQLException;
+import java.sql.SQLFeatureNotSupportedException;
+import java.util.HashMap;
+import java.util.NoSuchElementException;
+import java.util.Properties;
+import java.util.logging.Logger;
+
+import org.apache.commons.pool2.ObjectPool;
+
+/**
+ * A {@link Driver} implementation that obtains {@link Connection}s from a registered {@link ObjectPool}.
+ *
+ * @since 2.0
+ */
+public class PoolingDriver implements Driver {
+
+ /**
+ * PoolGuardConnectionWrapper is a Connection wrapper that makes sure a closed connection cannot be used anymore.
+ *
+ * @since 2.0
+ */
+ private class PoolGuardConnectionWrapper extends DelegatingConnection<Connection> {
+
+ private final ObjectPool<? extends Connection> pool;
+
+ PoolGuardConnectionWrapper(final ObjectPool<? extends Connection> pool, final Connection delegate) {
+ super(delegate);
+ this.pool = pool;
+ }
+
+ /**
+ * @see org.apache.commons.dbcp2.DelegatingConnection#getDelegate()
+ */
+ @Override
+ public Connection getDelegate() {
+ if (isAccessToUnderlyingConnectionAllowed()) {
+ return super.getDelegate();
+ }
+ return null;
+ }
+
+ /**
+ * @see org.apache.commons.dbcp2.DelegatingConnection#getInnermostDelegate()
+ */
+ @Override
+ public Connection getInnermostDelegate() {
+ if (isAccessToUnderlyingConnectionAllowed()) {
+ return super.getInnermostDelegate();
+ }
+ return null;
+ }
+ }
+
+ private static final DriverPropertyInfo[] EMPTY_DRIVER_PROPERTY_INFO_ARRAY = {};
+
+ /* Register myself with the {@link DriverManager}. */
+ static {
+ try {
+ DriverManager.registerDriver(new PoolingDriver());
+ } catch (final Exception ignored) {
+ // Ignored
+ }
+ }
+
+ /** The map of registered pools. */
+ protected static final HashMap<String, ObjectPool<? extends Connection>> pools = new HashMap<>();
+
+ /** My URL prefix */
+ public static final String URL_PREFIX = "jdbc:apache:commons:dbcp:";
+
+ protected static final int URL_PREFIX_LEN = URL_PREFIX.length();
+
+ // version numbers
+ protected static final int MAJOR_VERSION = 1;
+
+ protected static final int MINOR_VERSION = 0;
+
+ /** Controls access to the underlying connection */
+ private final boolean accessToUnderlyingConnectionAllowed;
+
+ /**
+ * Constructs a new driver with {@code accessToUnderlyingConnectionAllowed} enabled.
+ */
+ public PoolingDriver() {
+ this(true);
+ }
+
+ /**
+ * For unit testing purposes.
+ *
+ * @param accessToUnderlyingConnectionAllowed
+ * Do {@link DelegatingConnection}s created by this driver permit access to the delegate?
+ */
+ protected PoolingDriver(final boolean accessToUnderlyingConnectionAllowed) {
+ this.accessToUnderlyingConnectionAllowed = accessToUnderlyingConnectionAllowed;
+ }
+
+ @Override
+ public boolean acceptsURL(final String url) throws SQLException {
+ return url != null && url.startsWith(URL_PREFIX);
+ }
+
+ /**
+ * Closes a named pool.
+ *
+ * @param name
+ * The pool name.
+ * @throws SQLException
+ * Thrown when a problem is caught closing the pool.
+ */
+ public synchronized void closePool(final String name) throws SQLException {
+ @SuppressWarnings("resource")
+ final ObjectPool<? extends Connection> pool = pools.get(name);
+ if (pool != null) {
+ pools.remove(name);
+ try {
+ pool.close();
+ } catch (final Exception e) {
+ throw new SQLException("Error closing pool " + name, e);
+ }
+ }
+ }
+
+ @Override
+ public Connection connect(final String url, final Properties info) throws SQLException {
+ if (acceptsURL(url)) {
+ final ObjectPool<? extends Connection> pool = getConnectionPool(url.substring(URL_PREFIX_LEN));
+
+ try {
+ final Connection conn = pool.borrowObject();
+ if (conn == null) {
+ return null;
+ }
+ return new PoolGuardConnectionWrapper(pool, conn);
+ } catch (final NoSuchElementException e) {
+ throw new SQLException("Cannot get a connection, pool error: " + e.getMessage(), e);
+ } catch (final SQLException | RuntimeException e) {
+ throw e;
+ } catch (final Exception e) {
+ throw new SQLException("Cannot get a connection, general error: " + e.getMessage(), e);
+ }
+ }
+ return null;
+ }
+
+ /**
+ * Gets the connection pool for the given name.
+ *
+ * @param name
+ * The pool name
+ * @return The pool
+ * @throws SQLException
+ * Thrown when the named pool is not registered.
+ */
+ public synchronized ObjectPool<? extends Connection> getConnectionPool(final String name) throws SQLException {
+ final ObjectPool<? extends Connection> pool = pools.get(name);
+ if (null == pool) {
+ throw new SQLException("Pool not registered: " + name);
+ }
+ return pool;
+ }
+
+ @Override
+ public int getMajorVersion() {
+ return MAJOR_VERSION;
+ }
+
+ @Override
+ public int getMinorVersion() {
+ return MINOR_VERSION;
+ }
+
+ @Override
+ public Logger getParentLogger() throws SQLFeatureNotSupportedException {
+ throw new SQLFeatureNotSupportedException();
+ }
+
+ /**
+ * Gets the pool names.
+ *
+ * @return the pool names.
+ */
+ public synchronized String[] getPoolNames() {
+ return pools.keySet().toArray(Utils.EMPTY_STRING_ARRAY);
+ }
+
+ @Override
+ public DriverPropertyInfo[] getPropertyInfo(final String url, final Properties info) {
+ return EMPTY_DRIVER_PROPERTY_INFO_ARRAY;
+ }
+ /**
+ * Invalidates the given connection.
+ *
+ * @param conn
+ * connection to invalidate
+ * @throws SQLException
+ * if the connection is not a {@code PoolGuardConnectionWrapper} or an error occurs invalidating
+ * the connection
+ */
+ public void invalidateConnection(final Connection conn) throws SQLException {
+ if (!(conn instanceof PoolGuardConnectionWrapper)) {
+ throw new SQLException("Invalid connection class");
+ }
+ final PoolGuardConnectionWrapper pgconn = (PoolGuardConnectionWrapper) conn;
+ @SuppressWarnings("unchecked")
+ final ObjectPool<Connection> pool = (ObjectPool<Connection>) pgconn.pool;
+ try {
+ pool.invalidateObject(pgconn.getDelegateInternal());
+ } catch (final Exception ignored) {
+ // Ignored.
+ }
+ }
+
+ /**
+ * Returns the value of the accessToUnderlyingConnectionAllowed property.
+ *
+ * @return true if access to the underlying is allowed, false otherwise.
+ */
+ protected boolean isAccessToUnderlyingConnectionAllowed() {
+ return accessToUnderlyingConnectionAllowed;
+ }
+ @Override
+ public boolean jdbcCompliant() {
+ return true;
+ }
+
+ /**
+ * Registers a named pool.
+ *
+ * @param name
+ * The pool name.
+ * @param pool
+ * The pool.
+ */
+ public synchronized void registerPool(final String name, final ObjectPool<? extends Connection> pool) {
+ pools.put(name, pool);
+ }
+}
diff --git a/src/main/java/org/apache/commons/dbcp2/Utils.java b/src/main/java/org/apache/commons/dbcp2/Utils.java
index 34f17151..85ce3b90 100644
--- a/src/main/java/org/apache/commons/dbcp2/Utils.java
+++ b/src/main/java/org/apache/commons/dbcp2/Utils.java
@@ -1,206 +1,206 @@
-/*
- * 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;
-
-import java.sql.Connection;
-import java.sql.ResultSet;
-import java.sql.Statement;
-import java.text.MessageFormat;
-import java.util.HashSet;
-import java.util.Properties;
-import java.util.ResourceBundle;
-import java.util.Set;
-
-/**
- * Utility methods.
- *
- * @since 2.0
- */
-public final class Utils {
-
- private static final ResourceBundle messages = ResourceBundle
- .getBundle(Utils.class.getPackage().getName() + ".LocalStrings");
-
- /**
- * Whether the security manager is enabled.
- *
- * @deprecated No replacement.
- */
- @Deprecated
- public static final boolean IS_SECURITY_ENABLED = isSecurityEnabled();
-
- /** Any SQL_STATE starting with this value is considered a fatal disconnect */
- public static final String DISCONNECTION_SQL_CODE_PREFIX = "08";
-
- /**
- * SQL codes of fatal connection errors.
- * <ul>
- * <li>57P01 (Admin shutdown)</li>
- * <li>57P02 (Crash shutdown)</li>
- * <li>57P03 (Cannot connect now)</li>
- * <li>01002 (SQL92 disconnect error)</li>
- * <li>JZ0C0 (Sybase disconnect error)</li>
- * <li>JZ0C1 (Sybase disconnect error)</li>
- * </ul>
- */
- public static final Set<String> DISCONNECTION_SQL_CODES;
-
- static final ResultSet[] EMPTY_RESULT_SET_ARRAY = {};
-
- static final String[] EMPTY_STRING_ARRAY = {};
- static {
- DISCONNECTION_SQL_CODES = new HashSet<>();
- DISCONNECTION_SQL_CODES.add("57P01"); // Admin shutdown
- DISCONNECTION_SQL_CODES.add("57P02"); // Crash shutdown
- DISCONNECTION_SQL_CODES.add("57P03"); // Cannot connect now
- DISCONNECTION_SQL_CODES.add("01002"); // SQL92 disconnect error
- DISCONNECTION_SQL_CODES.add("JZ0C0"); // Sybase disconnect error
- DISCONNECTION_SQL_CODES.add("JZ0C1"); // Sybase disconnect error
- }
-
- /**
- * Clones the given char[] if not null.
- *
- * @param value may be null.
- * @return a cloned char[] or null.
- */
- public static char[] clone(final char[] value) {
- return value == null ? null : value.clone();
- }
-
- /**
- * Clones the given {@link Properties} without the standard "user" or "password" entries.
- *
- * @param properties may be null
- * @return a clone of the input without the standard "user" or "password" entries.
- * @since 2.8.0
- */
- public static Properties cloneWithoutCredentials(final Properties properties) {
- if (properties != null) {
- final Properties temp = (Properties) properties.clone();
- temp.remove(Constants.KEY_USER);
- temp.remove(Constants.KEY_PASSWORD);
- return temp;
- }
- return properties;
- }
-
- /**
- * Closes the AutoCloseable (which may be null).
- *
- * @param autoCloseable an AutoCloseable, may be {@code null}
- * @since 2.6.0
- */
- public static void closeQuietly(final AutoCloseable autoCloseable) {
- if (autoCloseable != null) {
- try {
- autoCloseable.close();
- } catch (final Exception e) {
- // ignored
- }
- }
- }
-
- /**
- * Closes the Connection (which may be null).
- *
- * @param connection a Connection, may be {@code null}
- * @deprecated Use {@link #closeQuietly(AutoCloseable)}.
- */
- @Deprecated
- public static void closeQuietly(final Connection connection) {
- closeQuietly((AutoCloseable) connection);
- }
-
- /**
- * Closes the ResultSet (which may be null).
- *
- * @param resultSet a ResultSet, may be {@code null}
- * @deprecated Use {@link #closeQuietly(AutoCloseable)}.
- */
- @Deprecated
- public static void closeQuietly(final ResultSet resultSet) {
- closeQuietly((AutoCloseable) resultSet);
- }
-
- /**
- * Closes the Statement (which may be null).
- *
- * @param statement a Statement, may be {@code null}.
- * @deprecated Use {@link #closeQuietly(AutoCloseable)}.
- */
- @Deprecated
- public static void closeQuietly(final Statement statement) {
- closeQuietly((AutoCloseable) statement);
- }
-
- /**
- * Gets the correct i18n message for the given key.
- *
- * @param key The key to look up an i18n message.
- * @return The i18n message.
- */
- public static String getMessage(final String key) {
- return getMessage(key, (Object[]) null);
- }
-
- /**
- * Gets the correct i18n message for the given key with placeholders replaced by the supplied arguments.
- *
- * @param key A message key.
- * @param args The message arguments.
- * @return An i18n message.
- */
- public static String getMessage(final String key, final Object... args) {
- final String msg = messages.getString(key);
- if (args == null || args.length == 0) {
- return msg;
- }
- final MessageFormat mf = new MessageFormat(msg);
- return mf.format(args, new StringBuffer(), null).toString();
- }
-
- static boolean isSecurityEnabled() {
- return System.getSecurityManager() != null;
- }
-
- /**
- * Converts the given String to a char[].
- *
- * @param value may be null.
- * @return a char[] or null.
- */
- public static char[] toCharArray(final String value) {
- return value != null ? value.toCharArray() : null;
- }
-
- /**
- * Converts the given char[] to a String.
- *
- * @param value may be null.
- * @return a String or null.
- */
- public static String toString(final char[] value) {
- return value == null ? null : String.valueOf(value);
- }
-
- private Utils() {
- // not instantiable
- }
-
-}
+/*
+ * 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;
+
+import java.sql.Connection;
+import java.sql.ResultSet;
+import java.sql.Statement;
+import java.text.MessageFormat;
+import java.util.HashSet;
+import java.util.Properties;
+import java.util.ResourceBundle;
+import java.util.Set;
+
+/**
+ * Utility methods.
+ *
+ * @since 2.0
+ */
+public final class Utils {
+
+ private static final ResourceBundle messages = ResourceBundle
+ .getBundle(Utils.class.getPackage().getName() + ".LocalStrings");
+
+ /**
+ * Whether the security manager is enabled.
+ *
+ * @deprecated No replacement.
+ */
+ @Deprecated
+ public static final boolean IS_SECURITY_ENABLED = isSecurityEnabled();
+
+ /** Any SQL_STATE starting with this value is considered a fatal disconnect */
+ public static final String DISCONNECTION_SQL_CODE_PREFIX = "08";
+
+ /**
+ * SQL codes of fatal connection errors.
+ * <ul>
+ * <li>57P01 (Admin shutdown)</li>
+ * <li>57P02 (Crash shutdown)</li>
+ * <li>57P03 (Cannot connect now)</li>
+ * <li>01002 (SQL92 disconnect error)</li>
+ * <li>JZ0C0 (Sybase disconnect error)</li>
+ * <li>JZ0C1 (Sybase disconnect error)</li>
+ * </ul>
+ */
+ public static final Set<String> DISCONNECTION_SQL_CODES;
+
+ static final ResultSet[] EMPTY_RESULT_SET_ARRAY = {};
+
+ static final String[] EMPTY_STRING_ARRAY = {};
+ static {
+ DISCONNECTION_SQL_CODES = new HashSet<>();
+ DISCONNECTION_SQL_CODES.add("57P01"); // Admin shutdown
+ DISCONNECTION_SQL_CODES.add("57P02"); // Crash shutdown
+ DISCONNECTION_SQL_CODES.add("57P03"); // Cannot connect now
+ DISCONNECTION_SQL_CODES.add("01002"); // SQL92 disconnect error
+ DISCONNECTION_SQL_CODES.add("JZ0C0"); // Sybase disconnect error
+ DISCONNECTION_SQL_CODES.add("JZ0C1"); // Sybase disconnect error
+ }
+
+ /**
+ * Clones the given char[] if not null.
+ *
+ * @param value may be null.
+ * @return a cloned char[] or null.
+ */
+ public static char[] clone(final char[] value) {
+ return value == null ? null : value.clone();
+ }
+
+ /**
+ * Clones the given {@link Properties} without the standard "user" or "password" entries.
+ *
+ * @param properties may be null
+ * @return a clone of the input without the standard "user" or "password" entries.
+ * @since 2.8.0
+ */
+ public static Properties cloneWithoutCredentials(final Properties properties) {
+ if (properties != null) {
+ final Properties temp = (Properties) properties.clone();
+ temp.remove(Constants.KEY_USER);
+ temp.remove(Constants.KEY_PASSWORD);
+ return temp;
+ }
+ return properties;
+ }
+
+ /**
+ * Closes the AutoCloseable (which may be null).
+ *
+ * @param autoCloseable an AutoCloseable, may be {@code null}
+ * @since 2.6.0
+ */
+ public static void closeQuietly(final AutoCloseable autoCloseable) {
+ if (autoCloseable != null) {
+ try {
+ autoCloseable.close();
+ } catch (final Exception ignored) {
+ // ignored
+ }
+ }
+ }
+
+ /**
+ * Closes the Connection (which may be null).
+ *
+ * @param connection a Connection, may be {@code null}
+ * @deprecated Use {@link #closeQuietly(AutoCloseable)}.
+ */
+ @Deprecated
+ public static void closeQuietly(final Connection connection) {
+ closeQuietly((AutoCloseable) connection);
+ }
+
+ /**
+ * Closes the ResultSet (which may be null).
+ *
+ * @param resultSet a ResultSet, may be {@code null}
+ * @deprecated Use {@link #closeQuietly(AutoCloseable)}.
+ */
+ @Deprecated
+ public static void closeQuietly(final ResultSet resultSet) {
+ closeQuietly((AutoCloseable) resultSet);
+ }
+
+ /**
+ * Closes the Statement (which may be null).
+ *
+ * @param statement a Statement, may be {@code null}.
+ * @deprecated Use {@link #closeQuietly(AutoCloseable)}.
+ */
+ @Deprecated
+ public static void closeQuietly(final Statement statement) {
+ closeQuietly((AutoCloseable) statement);
+ }
+
+ /**
+ * Gets the correct i18n message for the given key.
+ *
+ * @param key The key to look up an i18n message.
+ * @return The i18n message.
+ */
+ public static String getMessage(final String key) {
+ return getMessage(key, (Object[]) null);
+ }
+
+ /**
+ * Gets the correct i18n message for the given key with placeholders replaced by the supplied arguments.
+ *
+ * @param key A message key.
+ * @param args The message arguments.
+ * @return An i18n message.
+ */
+ public static String getMessage(final String key, final Object... args) {
+ final String msg = messages.getString(key);
+ if (args == null || args.length == 0) {
+ return msg;
+ }
+ final MessageFormat mf = new MessageFormat(msg);
+ return mf.format(args, new StringBuffer(), null).toString();
+ }
+
+ static boolean isSecurityEnabled() {
+ return System.getSecurityManager() != null;
+ }
+
+ /**
+ * Converts the given String to a char[].
+ *
+ * @param value may be null.
+ * @return a char[] or null.
+ */
+ public static char[] toCharArray(final String value) {
+ return value != null ? value.toCharArray() : null;
+ }
+
+ /**
+ * Converts the given char[] to a String.
+ *
+ * @param value may be null.
+ * @return a String or null.
+ */
+ public static String toString(final char[] value) {
+ return value == null ? null : String.valueOf(value);
+ }
+
+ private Utils() {
+ // not instantiable
+ }
+
+}
diff --git a/src/main/java/org/apache/commons/dbcp2/datasources/InstanceKeyDataSource.java b/src/main/java/org/apache/commons/dbcp2/datasources/InstanceKeyDataSource.java
index 64ad0a0e..6b8fb227 100644
--- a/src/main/java/org/apache/commons/dbcp2/datasources/InstanceKeyDataSource.java
+++ b/src/main/java/org/apache/commons/dbcp2/datasources/InstanceKeyDataSource.java
@@ -1,1331 +1,1331 @@
-/*
- * 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.OutputStreamWriter;
-import java.io.PrintWriter;
-import java.io.Serializable;
-import java.nio.charset.StandardCharsets;
-import java.sql.Connection;
-import java.sql.SQLException;
-import java.sql.SQLFeatureNotSupportedException;
-import java.time.Duration;
-import java.util.Properties;
-import java.util.logging.Logger;
-
-import javax.naming.Context;
-import javax.naming.InitialContext;
-import javax.naming.Referenceable;
-import javax.sql.ConnectionPoolDataSource;
-import javax.sql.DataSource;
-import javax.sql.PooledConnection;
-
-import org.apache.commons.pool2.impl.BaseObjectPoolConfig;
-import org.apache.commons.pool2.impl.GenericKeyedObjectPoolConfig;
-import org.apache.commons.pool2.impl.GenericObjectPool;
-
-/**
- * <p>
- * The base class for {@code SharedPoolDataSource} and {@code PerUserPoolDataSource}. Many of the
- * configuration properties are shared and defined here. This class is declared public in order to allow particular
- * usage with commons-beanutils; do not make direct use of it outside of <em>commons-dbcp2</em>.
- * </p>
- *
- * <p>
- * A J2EE container will normally provide some method of initializing the {@code DataSource} whose attributes are
- * presented as bean getters/setters and then deploying it via JNDI. It is then available to an application as a source
- * of pooled logical connections to the database. The pool needs a source of physical connections. This source is in the
- * form of a {@code ConnectionPoolDataSource} that can be specified via the {@link #setDataSourceName(String)} used
- * to lookup the source via JNDI.
- * </p>
- *
- * <p>
- * Although normally used within a JNDI environment, A DataSource can be instantiated and initialized as any bean. In
- * this case the {@code ConnectionPoolDataSource} will likely be instantiated in a similar manner. This class
- * allows the physical source of connections to be attached directly to this pool using the
- * {@link #setConnectionPoolDataSource(ConnectionPoolDataSource)} method.
- * </p>
- *
- * <p>
- * The dbcp package contains an adapter, {@link org.apache.commons.dbcp2.cpdsadapter.DriverAdapterCPDS}, that can be
- * used to allow the use of {@code DataSource}'s based on this class with JDBC driver implementations that do not
- * supply a {@code ConnectionPoolDataSource}, but still provide a {@link java.sql.Driver} implementation.
- * </p>
- *
- * <p>
- * The <a href="package-summary.html">package documentation</a> contains an example using Apache Tomcat and JNDI and it
- * also contains a non-JNDI example.
- * </p>
- *
- * @since 2.0
- */
-public abstract class InstanceKeyDataSource implements DataSource, Referenceable, Serializable, AutoCloseable {
-
- private static final long serialVersionUID = -6819270431752240878L;
-
- private static final String GET_CONNECTION_CALLED = "A Connection was already requested from this source, "
- + "further initialization is not allowed.";
- private static final String BAD_TRANSACTION_ISOLATION = "The requested TransactionIsolation level is invalid.";
-
- /**
- * Internal constant to indicate the level is not set.
- */
- protected static final int UNKNOWN_TRANSACTIONISOLATION = -1;
-
- /** Guards property setters - once true, setters throw IllegalStateException */
- private volatile boolean getConnectionCalled;
-
- /** Underlying source of PooledConnections */
- private ConnectionPoolDataSource dataSource;
-
- /** DataSource Name used to find the ConnectionPoolDataSource */
- private String dataSourceName;
-
- /** Description */
- private String description;
-
- /** Environment that may be used to set up a JNDI initial context. */
- private Properties jndiEnvironment;
-
- /** Login Timeout */
- private Duration loginTimeoutDuration = Duration.ZERO;
-
- /** Log stream */
- private PrintWriter logWriter;
-
- /** Instance key */
- private String instanceKey;
-
- // Pool properties
- private boolean defaultBlockWhenExhausted = BaseObjectPoolConfig.DEFAULT_BLOCK_WHEN_EXHAUSTED;
- private String defaultEvictionPolicyClassName = BaseObjectPoolConfig.DEFAULT_EVICTION_POLICY_CLASS_NAME;
- private boolean defaultLifo = BaseObjectPoolConfig.DEFAULT_LIFO;
- private int defaultMaxIdle = GenericKeyedObjectPoolConfig.DEFAULT_MAX_IDLE_PER_KEY;
- private int defaultMaxTotal = GenericKeyedObjectPoolConfig.DEFAULT_MAX_TOTAL;
- private Duration defaultMaxWaitDuration = BaseObjectPoolConfig.DEFAULT_MAX_WAIT;
- private Duration defaultMinEvictableIdleDuration = BaseObjectPoolConfig.DEFAULT_MIN_EVICTABLE_IDLE_DURATION;
- private int defaultMinIdle = GenericKeyedObjectPoolConfig.DEFAULT_MIN_IDLE_PER_KEY;
- private int defaultNumTestsPerEvictionRun = BaseObjectPoolConfig.DEFAULT_NUM_TESTS_PER_EVICTION_RUN;
- private Duration defaultSoftMinEvictableIdleDuration = BaseObjectPoolConfig.DEFAULT_SOFT_MIN_EVICTABLE_IDLE_DURATION;
- private boolean defaultTestOnCreate = BaseObjectPoolConfig.DEFAULT_TEST_ON_CREATE;
- private boolean defaultTestOnBorrow = BaseObjectPoolConfig.DEFAULT_TEST_ON_BORROW;
- private boolean defaultTestOnReturn = BaseObjectPoolConfig.DEFAULT_TEST_ON_RETURN;
- private boolean defaultTestWhileIdle = BaseObjectPoolConfig.DEFAULT_TEST_WHILE_IDLE;
- private Duration defaultDurationBetweenEvictionRuns = BaseObjectPoolConfig.DEFAULT_TIME_BETWEEN_EVICTION_RUNS;
-
- // Connection factory properties
- private String validationQuery;
- private Duration validationQueryTimeoutDuration = Duration.ofSeconds(-1);
- private boolean rollbackAfterValidation;
- private Duration maxConnDuration = Duration.ofMillis(-1);
-
- // Connection properties
- private Boolean defaultAutoCommit;
- private int defaultTransactionIsolation = UNKNOWN_TRANSACTIONISOLATION;
- private Boolean defaultReadOnly;
-
- /**
- * Default no-arg constructor for Serialization.
- */
- public InstanceKeyDataSource() {
- }
-
- /**
- * Throws an IllegalStateException, if a PooledConnection has already been requested.
- *
- * @throws IllegalStateException Thrown if a PooledConnection has already been requested.
- */
- protected void assertInitializationAllowed() throws IllegalStateException {
- if (getConnectionCalled) {
- throw new IllegalStateException(GET_CONNECTION_CALLED);
- }
- }
-
- /**
- * Closes the connection pool being maintained by this datasource.
- */
- @Override
- public abstract void close() throws Exception;
-
- private void closeDueToException(final PooledConnectionAndInfo info) {
- if (info != null) {
- try {
- info.getPooledConnection().getConnection().close();
- } catch (final Exception e) {
- // do not throw this exception because we are in the middle
- // of handling another exception. But record it because
- // it potentially leaks connections from the pool.
- getLogWriter().println("[ERROR] Could not return connection to pool during exception handling. " + e.getMessage());
- }
- }
- }
-
- /**
- * Attempts to establish a database connection.
- */
- @Override
- public Connection getConnection() throws SQLException {
- return getConnection(null, null);
- }
-
- /**
- * Attempts to retrieve a database connection using {@link #getPooledConnectionAndInfo(String, String)} with the
- * provided user name and password. The password on the {@code PooledConnectionAndInfo} instance returned by
- * {@code getPooledConnectionAndInfo} is compared to the {@code password} parameter. If the comparison
- * fails, a database connection using the supplied user name and password is attempted. If the connection attempt
- * fails, an SQLException is thrown, indicating that the given password did not match the password used to create
- * the pooled connection. If the connection attempt succeeds, this means that the database password has been
- * changed. In this case, the {@code PooledConnectionAndInfo} instance retrieved with the old password is
- * destroyed and the {@code getPooledConnectionAndInfo} is repeatedly invoked until a
- * {@code PooledConnectionAndInfo} instance with the new password is returned.
- */
- @Override
- public Connection getConnection(final String userName, final String userPassword) throws SQLException {
- if (instanceKey == null) {
- throw new SQLException("Must set the ConnectionPoolDataSource "
- + "through setDataSourceName or setConnectionPoolDataSource" + " before calling getConnection.");
- }
- getConnectionCalled = true;
- PooledConnectionAndInfo info = null;
- try {
- info = getPooledConnectionAndInfo(userName, userPassword);
- } catch (final RuntimeException | SQLException e) {
- closeDueToException(info);
- throw e;
- } catch (final Exception e) {
- closeDueToException(info);
- throw new SQLException("Cannot borrow connection from pool", e);
- }
-
- // Password on PooledConnectionAndInfo does not match
- if (!(null == userPassword ? null == info.getPassword() : userPassword.equals(info.getPassword()))) {
- try { // See if password has changed by attempting connection
- testCPDS(userName, userPassword);
- } catch (final SQLException ex) {
- // Password has not changed, so refuse client, but return connection to the pool
- closeDueToException(info);
- throw new SQLException(
- "Given password did not match password used" + " to create the PooledConnection.", ex);
- } catch (final javax.naming.NamingException ne) {
- throw new SQLException("NamingException encountered connecting to database", ne);
- }
- /*
- * Password must have changed -> destroy connection and keep retrying until we get a new, good one,
- * destroying any idle connections with the old password as we pull them from the pool.
- */
- final UserPassKey upkey = info.getUserPassKey();
- final PooledConnectionManager manager = getConnectionManager(upkey);
- // Destroy and remove from pool
- manager.invalidate(info.getPooledConnection());
- // Reset the password on the factory if using CPDSConnectionFactory
- manager.setPassword(upkey.getPassword());
- info = null;
- for (int i = 0; i < 10; i++) { // Bound the number of retries - only needed if bad instances return
- try {
- info = getPooledConnectionAndInfo(userName, userPassword);
- } catch (final RuntimeException | SQLException e) {
- closeDueToException(info);
- throw e;
- } catch (final Exception e) {
- closeDueToException(info);
- throw new SQLException("Cannot borrow connection from pool", e);
- }
- if (info != null && userPassword != null && userPassword.equals(info.getPassword())) {
- break;
- }
- if (info != null) {
- manager.invalidate(info.getPooledConnection());
- }
- info = null;
- }
- if (info == null) {
- throw new SQLException("Cannot borrow connection from pool - password change failure.");
- }
- }
-
- final Connection connection = info.getPooledConnection().getConnection();
- try {
- setupDefaults(connection, userName);
- connection.clearWarnings();
- return connection;
- } catch (final SQLException ex) {
- try {
- connection.close();
- } catch (final Exception exc) {
- getLogWriter().println("ignoring exception during close: " + exc);
- }
- throw ex;
- }
- }
-
- protected abstract PooledConnectionManager getConnectionManager(UserPassKey upkey);
-
- /**
- * Gets the value of connectionPoolDataSource. This method will return null, if the backing data source is being
- * accessed via JNDI.
- *
- * @return value of connectionPoolDataSource.
- */
- public ConnectionPoolDataSource getConnectionPoolDataSource() {
- return dataSource;
- }
-
- /**
- * Gets the name of the ConnectionPoolDataSource which backs this pool. This name is used to look up the data source
- * from a JNDI service provider.
- *
- * @return value of dataSourceName.
- */
- public String getDataSourceName() {
- return dataSourceName;
- }
-
- /**
- * Gets the default value for {@link GenericKeyedObjectPoolConfig#getBlockWhenExhausted()} for each per user pool.
- *
- * @return The default value for {@link GenericKeyedObjectPoolConfig#getBlockWhenExhausted()} for each per user
- * pool.
- */
- public boolean getDefaultBlockWhenExhausted() {
- return this.defaultBlockWhenExhausted;
- }
-
- /**
- * Gets the default value for {@link GenericObjectPool#getDurationBetweenEvictionRuns()} for each per user pool.
- *
- * @return The default value for {@link GenericObjectPool#getDurationBetweenEvictionRuns()} for each per user pool.
- * @since 2.10.0
- */
- public Duration getDefaultDurationBetweenEvictionRuns() {
- return this.defaultDurationBetweenEvictionRuns;
- }
-
- /**
- * Gets the default value for {@link GenericKeyedObjectPoolConfig#getEvictionPolicyClassName()} for each per user
- * pool.
- *
- * @return The default value for {@link GenericKeyedObjectPoolConfig#getEvictionPolicyClassName()} for each per user
- * pool.
- */
- public String getDefaultEvictionPolicyClassName() {
- return this.defaultEvictionPolicyClassName;
- }
-
- /**
- * Gets the default value for {@link GenericKeyedObjectPoolConfig#getLifo()} for each per user pool.
- *
- * @return The default value for {@link GenericKeyedObjectPoolConfig#getLifo()} for each per user pool.
- */
- public boolean getDefaultLifo() {
- return this.defaultLifo;
- }
-
- /**
- * Gets the default value for {@link GenericKeyedObjectPoolConfig#getMaxIdlePerKey()} for each per user pool.
- *
- * @return The default value for {@link GenericKeyedObjectPoolConfig#getMaxIdlePerKey()} for each per user pool.
- */
- public int getDefaultMaxIdle() {
- return this.defaultMaxIdle;
- }
-
- /**
- * Gets the default value for {@link GenericKeyedObjectPoolConfig#getMaxTotalPerKey()} for each per user pool.
- *
- * @return The default value for {@link GenericKeyedObjectPoolConfig#getMaxTotalPerKey()} for each per user pool.
- */
- public int getDefaultMaxTotal() {
- return this.defaultMaxTotal;
- }
-
- /**
- * Gets the default value for {@link GenericKeyedObjectPoolConfig#getMaxWaitDuration()} for each per user pool.
- *
- * @return The default value for {@link GenericKeyedObjectPoolConfig#getMaxWaitDuration()} for each per user pool.
- * @since 2.9.0
- */
- public Duration getDefaultMaxWait() {
- return this.defaultMaxWaitDuration;
- }
-
- /**
- * Gets the default value for {@link GenericKeyedObjectPoolConfig#getMaxWaitDuration()} for each per user pool.
- *
- * @return The default value for {@link GenericKeyedObjectPoolConfig#getMaxWaitDuration()} for each per user pool.
- * @deprecated Use {@link #getDefaultMaxWait()}.
- */
- @Deprecated
- public long getDefaultMaxWaitMillis() {
- return getDefaultMaxWait().toMillis();
- }
-
- /**
- * Gets the default value for {@link GenericKeyedObjectPoolConfig#getMinEvictableIdleDuration()} for each per user
- * pool.
- *
- * @return The default value for {@link GenericKeyedObjectPoolConfig#getMinEvictableIdleDuration()} for each per
- * user pool.
- * @since 2.10.0
- */
- public Duration getDefaultMinEvictableIdleDuration() {
- return this.defaultMinEvictableIdleDuration;
- }
-
- /**
- * Gets the default value for {@link GenericKeyedObjectPoolConfig#getMinEvictableIdleDuration()} for each per user
- * pool.
- *
- * @return The default value for {@link GenericKeyedObjectPoolConfig#getMinEvictableIdleDuration()} for each per
- * user pool.
- * @deprecated Use {@link #getDefaultMinEvictableIdleDuration()}.
- */
- @Deprecated
- public long getDefaultMinEvictableIdleTimeMillis() {
- return this.defaultMinEvictableIdleDuration.toMillis();
- }
-
- /**
- * Gets the default value for {@link GenericKeyedObjectPoolConfig#getMinIdlePerKey()} for each per user pool.
- *
- * @return The default value for {@link GenericKeyedObjectPoolConfig#getMinIdlePerKey()} for each per user pool.
- */
- public int getDefaultMinIdle() {
- return this.defaultMinIdle;
- }
-
- /**
- * Gets the default value for {@link GenericKeyedObjectPoolConfig#getNumTestsPerEvictionRun()} for each per user
- * pool.
- *
- * @return The default value for {@link GenericKeyedObjectPoolConfig#getNumTestsPerEvictionRun()} for each per user
- * pool.
- */
- public int getDefaultNumTestsPerEvictionRun() {
- return this.defaultNumTestsPerEvictionRun;
- }
-
- /**
- * Gets the default value for {@link org.apache.commons.pool2.impl.GenericObjectPool
- * GenericObjectPool#getSoftMinEvictableIdleTimeMillis()} for each per user pool.
- *
- * @return The default value for {@link org.apache.commons.pool2.impl.GenericObjectPool
- * GenericObjectPool#getSoftMinEvictableIdleTimeMillis()} for each per user pool.
- * @since 2.10.0
- */
- public Duration getDefaultSoftMinEvictableIdleDuration() {
- return this.defaultSoftMinEvictableIdleDuration;
- }
-
- /**
- * Gets the default value for {@link org.apache.commons.pool2.impl.GenericObjectPool
- * GenericObjectPool#getSoftMinEvictableIdleTimeMillis()} for each per user pool.
- *
- * @return The default value for {@link org.apache.commons.pool2.impl.GenericObjectPool
- * GenericObjectPool#getSoftMinEvictableIdleTimeMillis()} for each per user pool.
- * @deprecated Use {@link #getDefaultSoftMinEvictableIdleDuration()}.
- */
- @Deprecated
- public long getDefaultSoftMinEvictableIdleTimeMillis() {
- return this.defaultSoftMinEvictableIdleDuration.toMillis();
- }
-
- /**
- * Gets the default value for {@link org.apache.commons.pool2.impl.GenericObjectPool
- * GenericObjectPool#getTestOnBorrow()} for each per user pool.
- *
- * @return The default value for {@link org.apache.commons.pool2.impl.GenericObjectPool
- * GenericObjectPool#getTestOnBorrow()} for each per user pool.
- */
- public boolean getDefaultTestOnBorrow() {
- return this.defaultTestOnBorrow;
- }
-
- /**
- * Gets the default value for {@link org.apache.commons.pool2.impl.GenericObjectPool
- * GenericObjectPool#getTestOnCreate()} for each per user pool.
- *
- * @return The default value for {@link org.apache.commons.pool2.impl.GenericObjectPool
- * GenericObjectPool#getTestOnCreate()} for each per user pool.
- */
- public boolean getDefaultTestOnCreate() {
- return this.defaultTestOnCreate;
- }
-
- /**
- * Gets the default value for {@link org.apache.commons.pool2.impl.GenericObjectPool
- * GenericObjectPool#getTestOnReturn()} for each per user pool.
- *
- * @return The default value for {@link org.apache.commons.pool2.impl.GenericObjectPool
- * GenericObjectPool#getTestOnReturn()} for each per user pool.
- */
- public boolean getDefaultTestOnReturn() {
- return this.defaultTestOnReturn;
- }
-
- /**
- * Gets the default value for {@link org.apache.commons.pool2.impl.GenericObjectPool
- * GenericObjectPool#getTestWhileIdle()} for each per user pool.
- *
- * @return The default value for {@link org.apache.commons.pool2.impl.GenericObjectPool
- * GenericObjectPool#getTestWhileIdle()} for each per user pool.
- */
- public boolean getDefaultTestWhileIdle() {
- return this.defaultTestWhileIdle;
- }
-
- /**
- * Gets the default value for {@link GenericObjectPool#getDurationBetweenEvictionRuns ()} for each per user pool.
- *
- * @return The default value for {@link GenericObjectPool#getDurationBetweenEvictionRuns ()} for each per user pool.
- * @deprecated Use {@link #getDefaultDurationBetweenEvictionRuns()}.
- */
- @Deprecated
- public long getDefaultTimeBetweenEvictionRunsMillis() {
- return this.defaultDurationBetweenEvictionRuns.toMillis();
- }
-
- /**
- * Gets the value of defaultTransactionIsolation, which defines the state of connections handed out from this pool.
- * The value can be changed on the Connection using Connection.setTransactionIsolation(int). If this method returns
- * -1, the default is JDBC driver dependent.
- *
- * @return value of defaultTransactionIsolation.
- */
- public int getDefaultTransactionIsolation() {
- return defaultTransactionIsolation;
- }
-
- /**
- * Gets the description. This property is defined by JDBC as for use with GUI (or other) tools that might deploy the
- * datasource. It serves no internal purpose.
- *
- * @return value of description.
- */
- public String getDescription() {
- return description;
- }
-
- /**
- * Gets the instance key.
- *
- * @return the instance key.
- */
- protected String getInstanceKey() {
- return instanceKey;
- }
-
- /**
- * Gets the value of jndiEnvironment which is used when instantiating a JNDI InitialContext. This InitialContext is
- * used to locate the back end ConnectionPoolDataSource.
- *
- * @param key
- * JNDI environment key.
- * @return value of jndiEnvironment.
- */
- public String getJndiEnvironment(final String key) {
- String value = null;
- if (jndiEnvironment != null) {
- value = jndiEnvironment.getProperty(key);
- }
- return value;
- }
-
- /**
- * Gets the value of loginTimeout.
- *
- * @return value of loginTimeout.
- * @deprecated Use {@link #getLoginTimeoutDuration()}.
- */
- @Deprecated
- @Override
- public int getLoginTimeout() {
- return (int) loginTimeoutDuration.getSeconds();
- }
-
- /**
- * Gets the value of loginTimeout.
- *
- * @return value of loginTimeout.
- * @since 2.10.0
- */
- public Duration getLoginTimeoutDuration() {
- return loginTimeoutDuration;
- }
-
- /**
- * Gets the value of logWriter.
- *
- * @return value of logWriter.
- */
- @Override
- public PrintWriter getLogWriter() {
- if (logWriter == null) {
- logWriter = new PrintWriter(new OutputStreamWriter(System.out, StandardCharsets.UTF_8));
- }
- return logWriter;
- }
-
- /**
- * Gets the maximum permitted lifetime of a connection. A value of zero or less indicates an
- * infinite lifetime.
- *
- * @return The maximum permitted lifetime of a connection. A value of zero or less indicates an
- * infinite lifetime.
- * @since 2.10.0
- */
- public Duration getMaxConnDuration() {
- return maxConnDuration;
- }
-
- /**
- * Gets the maximum permitted lifetime of a connection. A value of zero or less indicates an
- * infinite lifetime.
- *
- * @return The maximum permitted lifetime of a connection. A value of zero or less indicates an
- * infinite lifetime.
- * @deprecated Use {@link #getMaxConnDuration()}.
- */
- @Deprecated
- public Duration getMaxConnLifetime() {
- return maxConnDuration;
- }
-
- /**
- * Gets the maximum permitted lifetime of a connection in milliseconds. A value of zero or less indicates an
- * infinite lifetime.
- *
- * @return The maximum permitted lifetime of a connection in milliseconds. A value of zero or less indicates an
- * infinite lifetime.
- * @deprecated Use {@link #getMaxConnLifetime()}.
- */
- @Deprecated
- public long getMaxConnLifetimeMillis() {
- return maxConnDuration.toMillis();
- }
-
- @Override
- public Logger getParentLogger() throws SQLFeatureNotSupportedException {
- throw new SQLFeatureNotSupportedException();
- }
-
- /**
- * This method is protected but can only be implemented in this package because PooledConnectionAndInfo is a package
- * private type.
- *
- * @param userName The user name.
- * @param userPassword The user password.
- * @return Matching PooledConnectionAndInfo.
- * @throws SQLException Connection or registration failure.
- */
- protected abstract PooledConnectionAndInfo getPooledConnectionAndInfo(String userName, String userPassword)
- throws SQLException;
-
- /**
- * Gets the SQL query that will be used to validate connections from this pool before returning them to the caller.
- * If specified, this query <strong>MUST</strong> be an SQL SELECT statement that returns at least one row. If not
- * specified, {@link Connection#isValid(int)} will be used to validate connections.
- *
- * @return The SQL query that will be used to validate connections from this pool before returning them to the
- * caller.
- */
- public String getValidationQuery() {
- return this.validationQuery;
- }
-
- /**
- * Returns the timeout in seconds before the validation query fails.
- *
- * @return The timeout in seconds before the validation query fails.
- * @deprecated Use {@link #getValidationQueryTimeoutDuration()}.
- */
- @Deprecated
- public int getValidationQueryTimeout() {
- return (int) validationQueryTimeoutDuration.getSeconds();
- }
-
- /**
- * Returns the timeout Duration before the validation query fails.
- *
- * @return The timeout Duration before the validation query fails.
- */
- public Duration getValidationQueryTimeoutDuration() {
- return validationQueryTimeoutDuration;
- }
-
- /**
- * Gets the value of defaultAutoCommit, which defines the state of connections handed out from this pool. The value
- * can be changed on the Connection using Connection.setAutoCommit(boolean). The default is {@code null} which
- * will use the default value for the drive.
- *
- * @return value of defaultAutoCommit.
- */
- public Boolean isDefaultAutoCommit() {
- return defaultAutoCommit;
- }
-
- /**
- * Gets the value of defaultReadOnly, which defines the state of connections handed out from this pool. The value
- * can be changed on the Connection using Connection.setReadOnly(boolean). The default is {@code null} which
- * will use the default value for the drive.
- *
- * @return value of defaultReadOnly.
- */
- public Boolean isDefaultReadOnly() {
- return defaultReadOnly;
- }
-
- /**
- * Whether a rollback will be issued after executing the SQL query that will be used to validate connections from
- * this pool before returning them to the caller.
- *
- * @return true if a rollback will be issued after executing the validation query
- */
- public boolean isRollbackAfterValidation() {
- return this.rollbackAfterValidation;
- }
-
- @Override
- public boolean isWrapperFor(final Class<?> iface) throws SQLException {
- return iface.isInstance(this);
- }
-
- /**
- * Sets the back end ConnectionPoolDataSource. This property should not be set if using JNDI to access the
- * data source.
- *
- * @param dataSource
- * Value to assign to connectionPoolDataSource.
- */
- public void setConnectionPoolDataSource(final ConnectionPoolDataSource dataSource) {
- assertInitializationAllowed();
- if (dataSourceName != null) {
- throw new IllegalStateException("Cannot set the DataSource, if JNDI is used.");
- }
- if (this.dataSource != null) {
- throw new IllegalStateException("The CPDS has already been set. It cannot be altered.");
- }
- this.dataSource = dataSource;
- instanceKey = InstanceKeyDataSourceFactory.registerNewInstance(this);
- }
-
- /**
- * Sets the name of the ConnectionPoolDataSource which backs this pool. This name is used to look up the data source
- * from a JNDI service provider.
- *
- * @param dataSourceName
- * Value to assign to dataSourceName.
- */
- public void setDataSourceName(final String dataSourceName) {
- assertInitializationAllowed();
- if (dataSource != null) {
- throw new IllegalStateException("Cannot set the JNDI name for the DataSource, if already "
- + "set using setConnectionPoolDataSource.");
- }
- if (this.dataSourceName != null) {
- throw new IllegalStateException("The DataSourceName has already been set. " + "It cannot be altered.");
- }
- this.dataSourceName = dataSourceName;
- instanceKey = InstanceKeyDataSourceFactory.registerNewInstance(this);
- }
-
- /**
- * Sets the value of defaultAutoCommit, which defines the state of connections handed out from this pool. The value
- * can be changed on the Connection using Connection.setAutoCommit(boolean). The default is {@code null} which
- * will use the default value for the drive.
- *
- * @param defaultAutoCommit
- * Value to assign to defaultAutoCommit.
- */
- public void setDefaultAutoCommit(final Boolean defaultAutoCommit) {
- assertInitializationAllowed();
- this.defaultAutoCommit = defaultAutoCommit;
- }
-
- /**
- * Sets the default value for {@link GenericKeyedObjectPoolConfig#getBlockWhenExhausted()} for each per user pool.
- *
- * @param blockWhenExhausted
- * The default value for {@link GenericKeyedObjectPoolConfig#getBlockWhenExhausted()} for each per user
- * pool.
- */
- public void setDefaultBlockWhenExhausted(final boolean blockWhenExhausted) {
- assertInitializationAllowed();
- this.defaultBlockWhenExhausted = blockWhenExhausted;
- }
-
- /**
- * Sets the default value for {@link GenericObjectPool#getDurationBetweenEvictionRuns ()} for each per user pool.
- *
- * @param defaultDurationBetweenEvictionRuns The default value for
- * {@link GenericObjectPool#getDurationBetweenEvictionRuns ()} for each per user pool.
- * @since 2.10.0
- */
- public void setDefaultDurationBetweenEvictionRuns(final Duration defaultDurationBetweenEvictionRuns) {
- assertInitializationAllowed();
- this.defaultDurationBetweenEvictionRuns = defaultDurationBetweenEvictionRuns;
- }
-
- /**
- * Sets the default value for {@link GenericKeyedObjectPoolConfig#getEvictionPolicyClassName()} for each per user
- * pool.
- *
- * @param evictionPolicyClassName
- * The default value for {@link GenericKeyedObjectPoolConfig#getEvictionPolicyClassName()} for each per
- * user pool.
- */
- public void setDefaultEvictionPolicyClassName(final String evictionPolicyClassName) {
- assertInitializationAllowed();
- this.defaultEvictionPolicyClassName = evictionPolicyClassName;
- }
-
- /**
- * Sets the default value for {@link GenericKeyedObjectPoolConfig#getLifo()} for each per user pool.
- *
- * @param lifo
- * The default value for {@link GenericKeyedObjectPoolConfig#getLifo()} for each per user pool.
- */
- public void setDefaultLifo(final boolean lifo) {
- assertInitializationAllowed();
- this.defaultLifo = lifo;
- }
-
- /**
- * Sets the default value for {@link GenericKeyedObjectPoolConfig#getMaxIdlePerKey()} for each per user pool.
- *
- * @param maxIdle
- * The default value for {@link GenericKeyedObjectPoolConfig#getMaxIdlePerKey()} for each per user pool.
- */
- public void setDefaultMaxIdle(final int maxIdle) {
- assertInitializationAllowed();
- this.defaultMaxIdle = maxIdle;
- }
-
- /**
- * Sets the default value for {@link GenericKeyedObjectPoolConfig#getMaxTotalPerKey()} for each per user pool.
- *
- * @param maxTotal
- * The default value for {@link GenericKeyedObjectPoolConfig#getMaxTotalPerKey()} for each per user pool.
- */
- public void setDefaultMaxTotal(final int maxTotal) {
- assertInitializationAllowed();
- this.defaultMaxTotal = maxTotal;
- }
-
- /**
- * Sets the default value for {@link GenericKeyedObjectPoolConfig#getMaxWaitDuration()} for each per user pool.
- *
- * @param maxWaitMillis
- * The default value for {@link GenericKeyedObjectPoolConfig#getMaxWaitDuration()} for each per user pool.
- * @since 2.9.0
- */
- public void setDefaultMaxWait(final Duration maxWaitMillis) {
- assertInitializationAllowed();
- this.defaultMaxWaitDuration = maxWaitMillis;
- }
-
- /**
- * Sets the default value for {@link GenericKeyedObjectPoolConfig#getMaxWaitMillis()} for each per user pool.
- *
- * @param maxWaitMillis
- * The default value for {@link GenericKeyedObjectPoolConfig#getMaxWaitMillis()} for each per user pool.
- * @deprecated Use {@link #setDefaultMaxWait(Duration)}.
- */
- @Deprecated
- public void setDefaultMaxWaitMillis(final long maxWaitMillis) {
- setDefaultMaxWait(Duration.ofMillis(maxWaitMillis));
- }
-
- /**
- * Sets the default value for {@link GenericKeyedObjectPoolConfig#getMinEvictableIdleDuration()} for each per user
- * pool.
- *
- * @param defaultMinEvictableIdleDuration
- * The default value for {@link GenericKeyedObjectPoolConfig#getMinEvictableIdleDuration()} for each
- * per user pool.
- * @since 2.10.0
- */
- public void setDefaultMinEvictableIdle(final Duration defaultMinEvictableIdleDuration) {
- assertInitializationAllowed();
- this.defaultMinEvictableIdleDuration = defaultMinEvictableIdleDuration;
- }
-
- /**
- * Sets the default value for {@link GenericKeyedObjectPoolConfig#getMinEvictableIdleDuration()} for each per user
- * pool.
- *
- * @param minEvictableIdleTimeMillis
- * The default value for {@link GenericKeyedObjectPoolConfig#getMinEvictableIdleDuration()} for each
- * per user pool.
- * @deprecated Use {@link #setDefaultMinEvictableIdle(Duration)}.
- */
- @Deprecated
- public void setDefaultMinEvictableIdleTimeMillis(final long minEvictableIdleTimeMillis) {
- assertInitializationAllowed();
- this.defaultMinEvictableIdleDuration = Duration.ofMillis(minEvictableIdleTimeMillis);
- }
-
- /**
- * Sets the default value for {@link GenericKeyedObjectPoolConfig#getMinIdlePerKey()} for each per user pool.
- *
- * @param minIdle
- * The default value for {@link GenericKeyedObjectPoolConfig#getMinIdlePerKey()} for each per user pool.
- */
- public void setDefaultMinIdle(final int minIdle) {
- assertInitializationAllowed();
- this.defaultMinIdle = minIdle;
- }
-
- /**
- * Sets the default value for {@link GenericKeyedObjectPoolConfig#getNumTestsPerEvictionRun()} for each per user
- * pool.
- *
- * @param numTestsPerEvictionRun
- * The default value for {@link GenericKeyedObjectPoolConfig#getNumTestsPerEvictionRun()} for each per
- * user pool.
- */
- public void setDefaultNumTestsPerEvictionRun(final int numTestsPerEvictionRun) {
- assertInitializationAllowed();
- this.defaultNumTestsPerEvictionRun = numTestsPerEvictionRun;
- }
-
- /**
- * Sets the value of defaultReadOnly, which defines the state of connections handed out from this pool. The value
- * can be changed on the Connection using Connection.setReadOnly(boolean). The default is {@code null} which
- * will use the default value for the drive.
- *
- * @param defaultReadOnly
- * Value to assign to defaultReadOnly.
- */
- public void setDefaultReadOnly(final Boolean defaultReadOnly) {
- assertInitializationAllowed();
- this.defaultReadOnly = defaultReadOnly;
- }
-
- /**
- * Sets the default value for {@link org.apache.commons.pool2.impl.GenericObjectPool
- * GenericObjectPool#getSoftMinEvictableIdleTimeMillis()} for each per user pool.
- *
- * @param defaultSoftMinEvictableIdleDuration
- * The default value for {@link org.apache.commons.pool2.impl.GenericObjectPool
- * GenericObjectPool#getSoftMinEvictableIdleTimeMillis()} for each per user pool.
- * @since 2.10.0
- */
- public void setDefaultSoftMinEvictableIdle(final Duration defaultSoftMinEvictableIdleDuration) {
- assertInitializationAllowed();
- this.defaultSoftMinEvictableIdleDuration = defaultSoftMinEvictableIdleDuration;
- }
-
- /**
- * Sets the default value for {@link org.apache.commons.pool2.impl.GenericObjectPool
- * GenericObjectPool#getSoftMinEvictableIdleTimeMillis()} for each per user pool.
- *
- * @param softMinEvictableIdleTimeMillis
- * The default value for {@link org.apache.commons.pool2.impl.GenericObjectPool
- * GenericObjectPool#getSoftMinEvictableIdleTimeMillis()} for each per user pool.
- * @deprecated Use {@link #setDefaultSoftMinEvictableIdle(Duration)}.
- */
- @Deprecated
- public void setDefaultSoftMinEvictableIdleTimeMillis(final long softMinEvictableIdleTimeMillis) {
- assertInitializationAllowed();
- this.defaultSoftMinEvictableIdleDuration = Duration.ofMillis(softMinEvictableIdleTimeMillis);
- }
-
- /**
- * Sets the default value for {@link org.apache.commons.pool2.impl.GenericObjectPool
- * GenericObjectPool#getTestOnBorrow()} for each per user pool.
- *
- * @param testOnBorrow
- * The default value for {@link org.apache.commons.pool2.impl.GenericObjectPool
- * GenericObjectPool#getTestOnBorrow()} for each per user pool.
- */
- public void setDefaultTestOnBorrow(final boolean testOnBorrow) {
- assertInitializationAllowed();
- this.defaultTestOnBorrow = testOnBorrow;
- }
-
- /**
- * Sets the default value for {@link org.apache.commons.pool2.impl.GenericObjectPool
- * GenericObjectPool#getTestOnCreate()} for each per user pool.
- *
- * @param testOnCreate
- * The default value for {@link org.apache.commons.pool2.impl.GenericObjectPool
- * GenericObjectPool#getTestOnCreate()} for each per user pool.
- */
- public void setDefaultTestOnCreate(final boolean testOnCreate) {
- assertInitializationAllowed();
- this.defaultTestOnCreate = testOnCreate;
- }
-
- /**
- * Sets the default value for {@link org.apache.commons.pool2.impl.GenericObjectPool
- * GenericObjectPool#getTestOnReturn()} for each per user pool.
- *
- * @param testOnReturn
- * The default value for {@link org.apache.commons.pool2.impl.GenericObjectPool
- * GenericObjectPool#getTestOnReturn()} for each per user pool.
- */
- public void setDefaultTestOnReturn(final boolean testOnReturn) {
- assertInitializationAllowed();
- this.defaultTestOnReturn = testOnReturn;
- }
-
- /**
- * Sets the default value for {@link org.apache.commons.pool2.impl.GenericObjectPool
- * GenericObjectPool#getTestWhileIdle()} for each per user pool.
- *
- * @param testWhileIdle
- * The default value for {@link org.apache.commons.pool2.impl.GenericObjectPool
- * GenericObjectPool#getTestWhileIdle()} for each per user pool.
- */
- public void setDefaultTestWhileIdle(final boolean testWhileIdle) {
- assertInitializationAllowed();
- this.defaultTestWhileIdle = testWhileIdle;
- }
-
- /**
- * Sets the default value for {@link GenericObjectPool#getDurationBetweenEvictionRuns()} for each per user pool.
- *
- * @param timeBetweenEvictionRunsMillis The default value for
- * {@link GenericObjectPool#getDurationBetweenEvictionRuns()} for each per user pool.
- * @deprecated Use {@link #setDefaultDurationBetweenEvictionRuns(Duration)}.
- */
- @Deprecated
- public void setDefaultTimeBetweenEvictionRunsMillis(final long timeBetweenEvictionRunsMillis) {
- assertInitializationAllowed();
- this.defaultDurationBetweenEvictionRuns = Duration.ofMillis(timeBetweenEvictionRunsMillis);
- }
-
- /**
- * Sets the value of defaultTransactionIsolation, which defines the state of connections handed out from this pool.
- * The value can be changed on the Connection using Connection.setTransactionIsolation(int). The default is JDBC
- * driver dependent.
- *
- * @param defaultTransactionIsolation
- * Value to assign to defaultTransactionIsolation
- */
- public void setDefaultTransactionIsolation(final int defaultTransactionIsolation) {
- assertInitializationAllowed();
- switch (defaultTransactionIsolation) {
- case Connection.TRANSACTION_NONE:
- case Connection.TRANSACTION_READ_COMMITTED:
- case Connection.TRANSACTION_READ_UNCOMMITTED:
- case Connection.TRANSACTION_REPEATABLE_READ:
- case Connection.TRANSACTION_SERIALIZABLE:
- break;
- default:
- throw new IllegalArgumentException(BAD_TRANSACTION_ISOLATION);
- }
- this.defaultTransactionIsolation = defaultTransactionIsolation;
- }
-
- /**
- * Sets the description. This property is defined by JDBC as for use with GUI (or other) tools that might deploy the
- * datasource. It serves no internal purpose.
- *
- * @param description
- * Value to assign to description.
- */
- public void setDescription(final String description) {
- this.description = description;
- }
-
- /**
- * Sets the JNDI environment to be used when instantiating a JNDI InitialContext. This InitialContext is used to
- * locate the back end ConnectionPoolDataSource.
- *
- * @param properties
- * the JNDI environment property to set which will overwrite any current settings
- */
- void setJndiEnvironment(final Properties properties) {
- if (jndiEnvironment == null) {
- jndiEnvironment = new Properties();
- } else {
- jndiEnvironment.clear();
- }
- jndiEnvironment.putAll(properties);
- }
-
- /**
- * Sets the value of the given JNDI environment property to be used when instantiating a JNDI InitialContext. This
- * InitialContext is used to locate the back end ConnectionPoolDataSource.
- *
- * @param key
- * the JNDI environment property to set.
- * @param value
- * the value assigned to specified JNDI environment property.
- */
- public void setJndiEnvironment(final String key, final String value) {
- if (jndiEnvironment == null) {
- jndiEnvironment = new Properties();
- }
- jndiEnvironment.setProperty(key, value);
- }
-
- /**
- * Sets the value of loginTimeout.
- *
- * @param loginTimeout
- * Value to assign to loginTimeout.
- * @since 2.10.0
- */
- public void setLoginTimeout(final Duration loginTimeout) {
- this.loginTimeoutDuration = loginTimeout;
- }
-
- /**
- * Sets the value of loginTimeout.
- *
- * @param loginTimeout
- * Value to assign to loginTimeout.
- * @deprecated Use {@link #setLoginTimeout(Duration)}.
- */
- @Deprecated
- @Override
- public void setLoginTimeout(final int loginTimeout) {
- this.loginTimeoutDuration = Duration.ofSeconds(loginTimeout);
- }
-
- /**
- * Sets the value of logWriter.
- *
- * @param logWriter
- * Value to assign to logWriter.
- */
- @Override
- public void setLogWriter(final PrintWriter logWriter) {
- this.logWriter = logWriter;
- }
-
- /**
- * <p>
- * Sets the maximum permitted lifetime of a connection. A value of zero or less indicates an
- * infinite lifetime.
- * </p>
- * <p>
- * Note: this method currently has no effect once the pool has been initialized. The pool is initialized the first
- * time one of the following methods is invoked: <code>getConnection, setLogwriter,
- * setLoginTimeout, getLoginTimeout, getLogWriter.</code>
- * </p>
- *
- * @param maxConnLifetimeMillis
- * The maximum permitted lifetime of a connection. A value of zero or less indicates an
- * infinite lifetime.
- * @since 2.9.0
- */
- public void setMaxConnLifetime(final Duration maxConnLifetimeMillis) {
- this.maxConnDuration = maxConnLifetimeMillis;
- }
-
- /**
- * <p>
- * Sets the maximum permitted lifetime of a connection in milliseconds. A value of zero or less indicates an
- * infinite lifetime.
- * </p>
- * <p>
- * Note: this method currently has no effect once the pool has been initialized. The pool is initialized the first
- * time one of the following methods is invoked: <code>getConnection, setLogwriter,
- * setLoginTimeout, getLoginTimeout, getLogWriter.</code>
- * </p>
- *
- * @param maxConnLifetimeMillis
- * The maximum permitted lifetime of a connection in milliseconds. A value of zero or less indicates an
- * infinite lifetime.
- * @deprecated Use {@link #setMaxConnLifetime(Duration)}.
- */
- @Deprecated
- public void setMaxConnLifetimeMillis(final long maxConnLifetimeMillis) {
- setMaxConnLifetime(Duration.ofMillis(maxConnLifetimeMillis));
- }
-
- /**
- * Whether a rollback will be issued after executing the SQL query that will be used to validate connections from
- * this pool before returning them to the caller. Default behavior is NOT to issue a rollback. The setting will only
- * have an effect if a validation query is set
- *
- * @param rollbackAfterValidation
- * new property value
- */
- public void setRollbackAfterValidation(final boolean rollbackAfterValidation) {
- assertInitializationAllowed();
- this.rollbackAfterValidation = rollbackAfterValidation;
- }
-
- protected abstract void setupDefaults(Connection connection, String userName) throws SQLException;
-
- /**
- * Sets the SQL query that will be used to validate connections from this pool before returning them to the caller.
- * If specified, this query <strong>MUST</strong> be an SQL SELECT statement that returns at least one row. If not
- * specified, connections will be validated using {@link Connection#isValid(int)}.
- *
- * @param validationQuery
- * The SQL query that will be used to validate connections from this pool before returning them to the
- * caller.
- */
- public void setValidationQuery(final String validationQuery) {
- assertInitializationAllowed();
- this.validationQuery = validationQuery;
- }
-
- /**
- * Sets the timeout duration before the validation query fails.
- *
- * @param validationQueryTimeoutDuration
- * The new timeout duration.
- */
- public void setValidationQueryTimeout(final Duration validationQueryTimeoutDuration) {
- this.validationQueryTimeoutDuration = validationQueryTimeoutDuration;
- }
-
- /**
- * Sets the timeout in seconds before the validation query fails.
- *
- * @param validationQueryTimeoutSeconds
- * The new timeout in seconds
- * @deprecated Use {@link #setValidationQueryTimeout(Duration)}.
- */
- @Deprecated
- public void setValidationQueryTimeout(final int validationQueryTimeoutSeconds) {
- this.validationQueryTimeoutDuration = Duration.ofSeconds(validationQueryTimeoutSeconds);
- }
-
- protected ConnectionPoolDataSource testCPDS(final String userName, final String userPassword)
- throws javax.naming.NamingException, SQLException {
- // The source of physical db connections
- ConnectionPoolDataSource cpds = this.dataSource;
- if (cpds == null) {
- Context ctx = null;
- if (jndiEnvironment == null) {
- ctx = new InitialContext();
- } else {
- ctx = new InitialContext(jndiEnvironment);
- }
- final Object ds = ctx.lookup(dataSourceName);
- if (!(ds instanceof ConnectionPoolDataSource)) {
- throw new SQLException("Illegal configuration: " + "DataSource " + dataSourceName + " ("
- + ds.getClass().getName() + ")" + " doesn't implement javax.sql.ConnectionPoolDataSource");
- }
- cpds = (ConnectionPoolDataSource) ds;
- }
-
- // try to get a connection with the supplied userName/password
- PooledConnection conn = null;
- try {
- if (userName != null) {
- conn = cpds.getPooledConnection(userName, userPassword);
- } else {
- conn = cpds.getPooledConnection();
- }
- if (conn == null) {
- throw new SQLException("Cannot connect using the supplied userName/password");
- }
- } finally {
- if (conn != null) {
- try {
- conn.close();
- } catch (final SQLException e) {
- // at least we could connect
- }
- }
- }
- return cpds;
- }
-
- /**
- * @since 2.6.0
- */
- @Override
- public synchronized String toString() {
- final StringBuilder builder = new StringBuilder(super.toString());
- builder.append('[');
- toStringFields(builder);
- builder.append(']');
- return builder.toString();
- }
-
- protected void toStringFields(final StringBuilder builder) {
- builder.append("getConnectionCalled=");
- builder.append(getConnectionCalled);
- builder.append(", dataSource=");
- builder.append(dataSource);
- builder.append(", dataSourceName=");
- builder.append(dataSourceName);
- builder.append(", description=");
- builder.append(description);
- builder.append(", jndiEnvironment=");
- builder.append(jndiEnvironment);
- builder.append(", loginTimeoutDuration=");
- builder.append(loginTimeoutDuration);
- builder.append(", logWriter=");
- builder.append(logWriter);
- builder.append(", instanceKey=");
- builder.append(instanceKey);
- builder.append(", defaultBlockWhenExhausted=");
- builder.append(defaultBlockWhenExhausted);
- builder.append(", defaultEvictionPolicyClassName=");
- builder.append(defaultEvictionPolicyClassName);
- builder.append(", defaultLifo=");
- builder.append(defaultLifo);
- builder.append(", defaultMaxIdle=");
- builder.append(defaultMaxIdle);
- builder.append(", defaultMaxTotal=");
- builder.append(defaultMaxTotal);
- builder.append(", defaultMaxWaitDuration=");
- builder.append(defaultMaxWaitDuration);
- builder.append(", defaultMinEvictableIdleDuration=");
- builder.append(defaultMinEvictableIdleDuration);
- builder.append(", defaultMinIdle=");
- builder.append(defaultMinIdle);
- builder.append(", defaultNumTestsPerEvictionRun=");
- builder.append(defaultNumTestsPerEvictionRun);
- builder.append(", defaultSoftMinEvictableIdleDuration=");
- builder.append(defaultSoftMinEvictableIdleDuration);
- builder.append(", defaultTestOnCreate=");
- builder.append(defaultTestOnCreate);
- builder.append(", defaultTestOnBorrow=");
- builder.append(defaultTestOnBorrow);
- builder.append(", defaultTestOnReturn=");
- builder.append(defaultTestOnReturn);
- builder.append(", defaultTestWhileIdle=");
- builder.append(defaultTestWhileIdle);
- builder.append(", defaultDurationBetweenEvictionRuns=");
- builder.append(defaultDurationBetweenEvictionRuns);
- builder.append(", validationQuery=");
- builder.append(validationQuery);
- builder.append(", validationQueryTimeoutDuration=");
- builder.append(validationQueryTimeoutDuration);
- builder.append(", rollbackAfterValidation=");
- builder.append(rollbackAfterValidation);
- builder.append(", maxConnDuration=");
- builder.append(maxConnDuration);
- builder.append(", defaultAutoCommit=");
- builder.append(defaultAutoCommit);
- builder.append(", defaultTransactionIsolation=");
- builder.append(defaultTransactionIsolation);
- builder.append(", defaultReadOnly=");
- builder.append(defaultReadOnly);
- }
-
- @Override
- @SuppressWarnings("unchecked")
- public <T> T unwrap(final Class<T> iface) throws SQLException {
- if (isWrapperFor(iface)) {
- return (T) this;
- }
- throw new SQLException(this + " is not a wrapper for " + iface);
- }
- /* JDBC_4_ANT_KEY_END */
-}
+/*
+ * 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.OutputStreamWriter;
+import java.io.PrintWriter;
+import java.io.Serializable;
+import java.nio.charset.StandardCharsets;
+import java.sql.Connection;
+import java.sql.SQLException;
+import java.sql.SQLFeatureNotSupportedException;
+import java.time.Duration;
+import java.util.Properties;
+import java.util.logging.Logger;
+
+import javax.naming.Context;
+import javax.naming.InitialContext;
+import javax.naming.Referenceable;
+import javax.sql.ConnectionPoolDataSource;
+import javax.sql.DataSource;
+import javax.sql.PooledConnection;
+
+import org.apache.commons.pool2.impl.BaseObjectPoolConfig;
+import org.apache.commons.pool2.impl.GenericKeyedObjectPoolConfig;
+import org.apache.commons.pool2.impl.GenericObjectPool;
+
+/**
+ * <p>
+ * The base class for {@code SharedPoolDataSource} and {@code PerUserPoolDataSource}. Many of the
+ * configuration properties are shared and defined here. This class is declared public in order to allow particular
+ * usage with commons-beanutils; do not make direct use of it outside of <em>commons-dbcp2</em>.
+ * </p>
+ *
+ * <p>
+ * A J2EE container will normally provide some method of initializing the {@code DataSource} whose attributes are
+ * presented as bean getters/setters and then deploying it via JNDI. It is then available to an application as a source
+ * of pooled logical connections to the database. The pool needs a source of physical connections. This source is in the
+ * form of a {@code ConnectionPoolDataSource} that can be specified via the {@link #setDataSourceName(String)} used
+ * to lookup the source via JNDI.
+ * </p>
+ *
+ * <p>
+ * Although normally used within a JNDI environment, A DataSource can be instantiated and initialized as any bean. In
+ * this case the {@code ConnectionPoolDataSource} will likely be instantiated in a similar manner. This class
+ * allows the physical source of connections to be attached directly to this pool using the
+ * {@link #setConnectionPoolDataSource(ConnectionPoolDataSource)} method.
+ * </p>
+ *
+ * <p>
+ * The dbcp package contains an adapter, {@link org.apache.commons.dbcp2.cpdsadapter.DriverAdapterCPDS}, that can be
+ * used to allow the use of {@code DataSource}'s based on this class with JDBC driver implementations that do not
+ * supply a {@code ConnectionPoolDataSource}, but still provide a {@link java.sql.Driver} implementation.
+ * </p>
+ *
+ * <p>
+ * The <a href="package-summary.html">package documentation</a> contains an example using Apache Tomcat and JNDI and it
+ * also contains a non-JNDI example.
+ * </p>
+ *
+ * @since 2.0
+ */
+public abstract class InstanceKeyDataSource implements DataSource, Referenceable, Serializable, AutoCloseable {
+
+ private static final long serialVersionUID = -6819270431752240878L;
+
+ private static final String GET_CONNECTION_CALLED = "A Connection was already requested from this source, "
+ + "further initialization is not allowed.";
+ private static final String BAD_TRANSACTION_ISOLATION = "The requested TransactionIsolation level is invalid.";
+
+ /**
+ * Internal constant to indicate the level is not set.
+ */
+ protected static final int UNKNOWN_TRANSACTIONISOLATION = -1;
+
+ /** Guards property setters - once true, setters throw IllegalStateException */
+ private volatile boolean getConnectionCalled;
+
+ /** Underlying source of PooledConnections */
+ private ConnectionPoolDataSource dataSource;
+
+ /** DataSource Name used to find the ConnectionPoolDataSource */
+ private String dataSourceName;
+
+ /** Description */
+ private String description;
+
+ /** Environment that may be used to set up a JNDI initial context. */
+ private Properties jndiEnvironment;
+
+ /** Login Timeout */
+ private Duration loginTimeoutDuration = Duration.ZERO;
+
+ /** Log stream */
+ private PrintWriter logWriter;
+
+ /** Instance key */
+ private String instanceKey;
+
+ // Pool properties
+ private boolean defaultBlockWhenExhausted = BaseObjectPoolConfig.DEFAULT_BLOCK_WHEN_EXHAUSTED;
+ private String defaultEvictionPolicyClassName = BaseObjectPoolConfig.DEFAULT_EVICTION_POLICY_CLASS_NAME;
+ private boolean defaultLifo = BaseObjectPoolConfig.DEFAULT_LIFO;
+ private int defaultMaxIdle = GenericKeyedObjectPoolConfig.DEFAULT_MAX_IDLE_PER_KEY;
+ private int defaultMaxTotal = GenericKeyedObjectPoolConfig.DEFAULT_MAX_TOTAL;
+ private Duration defaultMaxWaitDuration = BaseObjectPoolConfig.DEFAULT_MAX_WAIT;
+ private Duration defaultMinEvictableIdleDuration = BaseObjectPoolConfig.DEFAULT_MIN_EVICTABLE_IDLE_DURATION;
+ private int defaultMinIdle = GenericKeyedObjectPoolConfig.DEFAULT_MIN_IDLE_PER_KEY;
+ private int defaultNumTestsPerEvictionRun = BaseObjectPoolConfig.DEFAULT_NUM_TESTS_PER_EVICTION_RUN;
+ private Duration defaultSoftMinEvictableIdleDuration = BaseObjectPoolConfig.DEFAULT_SOFT_MIN_EVICTABLE_IDLE_DURATION;
+ private boolean defaultTestOnCreate = BaseObjectPoolConfig.DEFAULT_TEST_ON_CREATE;
+ private boolean defaultTestOnBorrow = BaseObjectPoolConfig.DEFAULT_TEST_ON_BORROW;
+ private boolean defaultTestOnReturn = BaseObjectPoolConfig.DEFAULT_TEST_ON_RETURN;
+ private boolean defaultTestWhileIdle = BaseObjectPoolConfig.DEFAULT_TEST_WHILE_IDLE;
+ private Duration defaultDurationBetweenEvictionRuns = BaseObjectPoolConfig.DEFAULT_TIME_BETWEEN_EVICTION_RUNS;
+
+ // Connection factory properties
+ private String validationQuery;
+ private Duration validationQueryTimeoutDuration = Duration.ofSeconds(-1);
+ private boolean rollbackAfterValidation;
+ private Duration maxConnDuration = Duration.ofMillis(-1);
+
+ // Connection properties
+ private Boolean defaultAutoCommit;
+ private int defaultTransactionIsolation = UNKNOWN_TRANSACTIONISOLATION;
+ private Boolean defaultReadOnly;
+
+ /**
+ * Default no-arg constructor for Serialization.
+ */
+ public InstanceKeyDataSource() {
+ }
+
+ /**
+ * Throws an IllegalStateException, if a PooledConnection has already been requested.
+ *
+ * @throws IllegalStateException Thrown if a PooledConnection has already been requested.
+ */
+ protected void assertInitializationAllowed() throws IllegalStateException {
+ if (getConnectionCalled) {
+ throw new IllegalStateException(GET_CONNECTION_CALLED);
+ }
+ }
+
+ /**
+ * Closes the connection pool being maintained by this datasource.
+ */
+ @Override
+ public abstract void close() throws Exception;
+
+ private void closeDueToException(final PooledConnectionAndInfo info) {
+ if (info != null) {
+ try {
+ info.getPooledConnection().getConnection().close();
+ } catch (final Exception e) {
+ // do not throw this exception because we are in the middle
+ // of handling another exception. But record it because
+ // it potentially leaks connections from the pool.
+ getLogWriter().println("[ERROR] Could not return connection to pool during exception handling. " + e.getMessage());
+ }
+ }
+ }
+
+ /**
+ * Attempts to establish a database connection.
+ */
+ @Override
+ public Connection getConnection() throws SQLException {
+ return getConnection(null, null);
+ }
+
+ /**
+ * Attempts to retrieve a database connection using {@link #getPooledConnectionAndInfo(String, String)} with the
+ * provided user name and password. The password on the {@code PooledConnectionAndInfo} instance returned by
+ * {@code getPooledConnectionAndInfo} is compared to the {@code password} parameter. If the comparison
+ * fails, a database connection using the supplied user name and password is attempted. If the connection attempt
+ * fails, an SQLException is thrown, indicating that the given password did not match the password used to create
+ * the pooled connection. If the connection attempt succeeds, this means that the database password has been
+ * changed. In this case, the {@code PooledConnectionAndInfo} instance retrieved with the old password is
+ * destroyed and the {@code getPooledConnectionAndInfo} is repeatedly invoked until a
+ * {@code PooledConnectionAndInfo} instance with the new password is returned.
+ */
+ @Override
+ public Connection getConnection(final String userName, final String userPassword) throws SQLException {
+ if (instanceKey == null) {
+ throw new SQLException("Must set the ConnectionPoolDataSource "
+ + "through setDataSourceName or setConnectionPoolDataSource" + " before calling getConnection.");
+ }
+ getConnectionCalled = true;
+ PooledConnectionAndInfo info = null;
+ try {
+ info = getPooledConnectionAndInfo(userName, userPassword);
+ } catch (final RuntimeException | SQLException e) {
+ closeDueToException(info);
+ throw e;
+ } catch (final Exception e) {
+ closeDueToException(info);
+ throw new SQLException("Cannot borrow connection from pool", e);
+ }
+
+ // Password on PooledConnectionAndInfo does not match
+ if (!(null == userPassword ? null == info.getPassword() : userPassword.equals(info.getPassword()))) {
+ try { // See if password has changed by attempting connection
+ testCPDS(userName, userPassword);
+ } catch (final SQLException ex) {
+ // Password has not changed, so refuse client, but return connection to the pool
+ closeDueToException(info);
+ throw new SQLException(
+ "Given password did not match password used" + " to create the PooledConnection.", ex);
+ } catch (final javax.naming.NamingException ne) {
+ throw new SQLException("NamingException encountered connecting to database", ne);
+ }
+ /*
+ * Password must have changed -> destroy connection and keep retrying until we get a new, good one,
+ * destroying any idle connections with the old password as we pull them from the pool.
+ */
+ final UserPassKey upkey = info.getUserPassKey();
+ final PooledConnectionManager manager = getConnectionManager(upkey);
+ // Destroy and remove from pool
+ manager.invalidate(info.getPooledConnection());
+ // Reset the password on the factory if using CPDSConnectionFactory
+ manager.setPassword(upkey.getPassword());
+ info = null;
+ for (int i = 0; i < 10; i++) { // Bound the number of retries - only needed if bad instances return
+ try {
+ info = getPooledConnectionAndInfo(userName, userPassword);
+ } catch (final RuntimeException | SQLException e) {
+ closeDueToException(info);
+ throw e;
+ } catch (final Exception e) {
+ closeDueToException(info);
+ throw new SQLException("Cannot borrow connection from pool", e);
+ }
+ if (info != null && userPassword != null && userPassword.equals(info.getPassword())) {
+ break;
+ }
+ if (info != null) {
+ manager.invalidate(info.getPooledConnection());
+ }
+ info = null;
+ }
+ if (info == null) {
+ throw new SQLException("Cannot borrow connection from pool - password change failure.");
+ }
+ }
+
+ final Connection connection = info.getPooledConnection().getConnection();
+ try {
+ setupDefaults(connection, userName);
+ connection.clearWarnings();
+ return connection;
+ } catch (final SQLException ex) {
+ try {
+ connection.close();
+ } catch (final Exception exc) {
+ getLogWriter().println("ignoring exception during close: " + exc);
+ }
+ throw ex;
+ }
+ }
+
+ protected abstract PooledConnectionManager getConnectionManager(UserPassKey upkey);
+
+ /**
+ * Gets the value of connectionPoolDataSource. This method will return null, if the backing data source is being
+ * accessed via JNDI.
+ *
+ * @return value of connectionPoolDataSource.
+ */
+ public ConnectionPoolDataSource getConnectionPoolDataSource() {
+ return dataSource;
+ }
+
+ /**
+ * Gets the name of the ConnectionPoolDataSource which backs this pool. This name is used to look up the data source
+ * from a JNDI service provider.
+ *
+ * @return value of dataSourceName.
+ */
+ public String getDataSourceName() {
+ return dataSourceName;
+ }
+
+ /**
+ * Gets the default value for {@link GenericKeyedObjectPoolConfig#getBlockWhenExhausted()} for each per user pool.
+ *
+ * @return The default value for {@link GenericKeyedObjectPoolConfig#getBlockWhenExhausted()} for each per user
+ * pool.
+ */
+ public boolean getDefaultBlockWhenExhausted() {
+ return this.defaultBlockWhenExhausted;
+ }
+
+ /**
+ * Gets the default value for {@link GenericObjectPool#getDurationBetweenEvictionRuns()} for each per user pool.
+ *
+ * @return The default value for {@link GenericObjectPool#getDurationBetweenEvictionRuns()} for each per user pool.
+ * @since 2.10.0
+ */
+ public Duration getDefaultDurationBetweenEvictionRuns() {
+ return this.defaultDurationBetweenEvictionRuns;
+ }
+
+ /**
+ * Gets the default value for {@link GenericKeyedObjectPoolConfig#getEvictionPolicyClassName()} for each per user
+ * pool.
+ *
+ * @return The default value for {@link GenericKeyedObjectPoolConfig#getEvictionPolicyClassName()} for each per user
+ * pool.
+ */
+ public String getDefaultEvictionPolicyClassName() {
+ return this.defaultEvictionPolicyClassName;
+ }
+
+ /**
+ * Gets the default value for {@link GenericKeyedObjectPoolConfig#getLifo()} for each per user pool.
+ *
+ * @return The default value for {@link GenericKeyedObjectPoolConfig#getLifo()} for each per user pool.
+ */
+ public boolean getDefaultLifo() {
+ return this.defaultLifo;
+ }
+
+ /**
+ * Gets the default value for {@link GenericKeyedObjectPoolConfig#getMaxIdlePerKey()} for each per user pool.
+ *
+ * @return The default value for {@link GenericKeyedObjectPoolConfig#getMaxIdlePerKey()} for each per user pool.
+ */
+ public int getDefaultMaxIdle() {
+ return this.defaultMaxIdle;
+ }
+
+ /**
+ * Gets the default value for {@link GenericKeyedObjectPoolConfig#getMaxTotalPerKey()} for each per user pool.
+ *
+ * @return The default value for {@link GenericKeyedObjectPoolConfig#getMaxTotalPerKey()} for each per user pool.
+ */
+ public int getDefaultMaxTotal() {
+ return this.defaultMaxTotal;
+ }
+
+ /**
+ * Gets the default value for {@link GenericKeyedObjectPoolConfig#getMaxWaitDuration()} for each per user pool.
+ *
+ * @return The default value for {@link GenericKeyedObjectPoolConfig#getMaxWaitDuration()} for each per user pool.
+ * @since 2.9.0
+ */
+ public Duration getDefaultMaxWait() {
+ return this.defaultMaxWaitDuration;
+ }
+
+ /**
+ * Gets the default value for {@link GenericKeyedObjectPoolConfig#getMaxWaitDuration()} for each per user pool.
+ *
+ * @return The default value for {@link GenericKeyedObjectPoolConfig#getMaxWaitDuration()} for each per user pool.
+ * @deprecated Use {@link #getDefaultMaxWait()}.
+ */
+ @Deprecated
+ public long getDefaultMaxWaitMillis() {
+ return getDefaultMaxWait().toMillis();
+ }
+
+ /**
+ * Gets the default value for {@link GenericKeyedObjectPoolConfig#getMinEvictableIdleDuration()} for each per user
+ * pool.
+ *
+ * @return The default value for {@link GenericKeyedObjectPoolConfig#getMinEvictableIdleDuration()} for each per
+ * user pool.
+ * @since 2.10.0
+ */
+ public Duration getDefaultMinEvictableIdleDuration() {
+ return this.defaultMinEvictableIdleDuration;
+ }
+
+ /**
+ * Gets the default value for {@link GenericKeyedObjectPoolConfig#getMinEvictableIdleDuration()} for each per user
+ * pool.
+ *
+ * @return The default value for {@link GenericKeyedObjectPoolConfig#getMinEvictableIdleDuration()} for each per
+ * user pool.
+ * @deprecated Use {@link #getDefaultMinEvictableIdleDuration()}.
+ */
+ @Deprecated
+ public long getDefaultMinEvictableIdleTimeMillis() {
+ return this.defaultMinEvictableIdleDuration.toMillis();
+ }
+
+ /**
+ * Gets the default value for {@link GenericKeyedObjectPoolConfig#getMinIdlePerKey()} for each per user pool.
+ *
+ * @return The default value for {@link GenericKeyedObjectPoolConfig#getMinIdlePerKey()} for each per user pool.
+ */
+ public int getDefaultMinIdle() {
+ return this.defaultMinIdle;
+ }
+
+ /**
+ * Gets the default value for {@link GenericKeyedObjectPoolConfig#getNumTestsPerEvictionRun()} for each per user
+ * pool.
+ *
+ * @return The default value for {@link GenericKeyedObjectPoolConfig#getNumTestsPerEvictionRun()} for each per user
+ * pool.
+ */
+ public int getDefaultNumTestsPerEvictionRun() {
+ return this.defaultNumTestsPerEvictionRun;
+ }
+
+ /**
+ * Gets the default value for {@link org.apache.commons.pool2.impl.GenericObjectPool
+ * GenericObjectPool#getSoftMinEvictableIdleTimeMillis()} for each per user pool.
+ *
+ * @return The default value for {@link org.apache.commons.pool2.impl.GenericObjectPool
+ * GenericObjectPool#getSoftMinEvictableIdleTimeMillis()} for each per user pool.
+ * @since 2.10.0
+ */
+ public Duration getDefaultSoftMinEvictableIdleDuration() {
+ return this.defaultSoftMinEvictableIdleDuration;
+ }
+
+ /**
+ * Gets the default value for {@link org.apache.commons.pool2.impl.GenericObjectPool
+ * GenericObjectPool#getSoftMinEvictableIdleTimeMillis()} for each per user pool.
+ *
+ * @return The default value for {@link org.apache.commons.pool2.impl.GenericObjectPool
+ * GenericObjectPool#getSoftMinEvictableIdleTimeMillis()} for each per user pool.
+ * @deprecated Use {@link #getDefaultSoftMinEvictableIdleDuration()}.
+ */
+ @Deprecated
+ public long getDefaultSoftMinEvictableIdleTimeMillis() {
+ return this.defaultSoftMinEvictableIdleDuration.toMillis();
+ }
+
+ /**
+ * Gets the default value for {@link org.apache.commons.pool2.impl.GenericObjectPool
+ * GenericObjectPool#getTestOnBorrow()} for each per user pool.
+ *
+ * @return The default value for {@link org.apache.commons.pool2.impl.GenericObjectPool
+ * GenericObjectPool#getTestOnBorrow()} for each per user pool.
+ */
+ public boolean getDefaultTestOnBorrow() {
+ return this.defaultTestOnBorrow;
+ }
+
+ /**
+ * Gets the default value for {@link org.apache.commons.pool2.impl.GenericObjectPool
+ * GenericObjectPool#getTestOnCreate()} for each per user pool.
+ *
+ * @return The default value for {@link org.apache.commons.pool2.impl.GenericObjectPool
+ * GenericObjectPool#getTestOnCreate()} for each per user pool.
+ */
+ public boolean getDefaultTestOnCreate() {
+ return this.defaultTestOnCreate;
+ }
+
+ /**
+ * Gets the default value for {@link org.apache.commons.pool2.impl.GenericObjectPool
+ * GenericObjectPool#getTestOnReturn()} for each per user pool.
+ *
+ * @return The default value for {@link org.apache.commons.pool2.impl.GenericObjectPool
+ * GenericObjectPool#getTestOnReturn()} for each per user pool.
+ */
+ public boolean getDefaultTestOnReturn() {
+ return this.defaultTestOnReturn;
+ }
+
+ /**
+ * Gets the default value for {@link org.apache.commons.pool2.impl.GenericObjectPool
+ * GenericObjectPool#getTestWhileIdle()} for each per user pool.
+ *
+ * @return The default value for {@link org.apache.commons.pool2.impl.GenericObjectPool
+ * GenericObjectPool#getTestWhileIdle()} for each per user pool.
+ */
+ public boolean getDefaultTestWhileIdle() {
+ return this.defaultTestWhileIdle;
+ }
+
+ /**
+ * Gets the default value for {@link GenericObjectPool#getDurationBetweenEvictionRuns ()} for each per user pool.
+ *
+ * @return The default value for {@link GenericObjectPool#getDurationBetweenEvictionRuns ()} for each per user pool.
+ * @deprecated Use {@link #getDefaultDurationBetweenEvictionRuns()}.
+ */
+ @Deprecated
+ public long getDefaultTimeBetweenEvictionRunsMillis() {
+ return this.defaultDurationBetweenEvictionRuns.toMillis();
+ }
+
+ /**
+ * Gets the value of defaultTransactionIsolation, which defines the state of connections handed out from this pool.
+ * The value can be changed on the Connection using Connection.setTransactionIsolation(int). If this method returns
+ * -1, the default is JDBC driver dependent.
+ *
+ * @return value of defaultTransactionIsolation.
+ */
+ public int getDefaultTransactionIsolation() {
+ return defaultTransactionIsolation;
+ }
+
+ /**
+ * Gets the description. This property is defined by JDBC as for use with GUI (or other) tools that might deploy the
+ * datasource. It serves no internal purpose.
+ *
+ * @return value of description.
+ */
+ public String getDescription() {
+ return description;
+ }
+
+ /**
+ * Gets the instance key.
+ *
+ * @return the instance key.
+ */
+ protected String getInstanceKey() {
+ return instanceKey;
+ }
+
+ /**
+ * Gets the value of jndiEnvironment which is used when instantiating a JNDI InitialContext. This InitialContext is
+ * used to locate the back end ConnectionPoolDataSource.
+ *
+ * @param key
+ * JNDI environment key.
+ * @return value of jndiEnvironment.
+ */
+ public String getJndiEnvironment(final String key) {
+ String value = null;
+ if (jndiEnvironment != null) {
+ value = jndiEnvironment.getProperty(key);
+ }
+ return value;
+ }
+
+ /**
+ * Gets the value of loginTimeout.
+ *
+ * @return value of loginTimeout.
+ * @deprecated Use {@link #getLoginTimeoutDuration()}.
+ */
+ @Deprecated
+ @Override
+ public int getLoginTimeout() {
+ return (int) loginTimeoutDuration.getSeconds();
+ }
+
+ /**
+ * Gets the value of loginTimeout.
+ *
+ * @return value of loginTimeout.
+ * @since 2.10.0
+ */
+ public Duration getLoginTimeoutDuration() {
+ return loginTimeoutDuration;
+ }
+
+ /**
+ * Gets the value of logWriter.
+ *
+ * @return value of logWriter.
+ */
+ @Override
+ public PrintWriter getLogWriter() {
+ if (logWriter == null) {
+ logWriter = new PrintWriter(new OutputStreamWriter(System.out, StandardCharsets.UTF_8));
+ }
+ return logWriter;
+ }
+
+ /**
+ * Gets the maximum permitted lifetime of a connection. A value of zero or less indicates an
+ * infinite lifetime.
+ *
+ * @return The maximum permitted lifetime of a connection. A value of zero or less indicates an
+ * infinite lifetime.
+ * @since 2.10.0
+ */
+ public Duration getMaxConnDuration() {
+ return maxConnDuration;
+ }
+
+ /**
+ * Gets the maximum permitted lifetime of a connection. A value of zero or less indicates an
+ * infinite lifetime.
+ *
+ * @return The maximum permitted lifetime of a connection. A value of zero or less indicates an
+ * infinite lifetime.
+ * @deprecated Use {@link #getMaxConnDuration()}.
+ */
+ @Deprecated
+ public Duration getMaxConnLifetime() {
+ return maxConnDuration;
+ }
+
+ /**
+ * Gets the maximum permitted lifetime of a connection in milliseconds. A value of zero or less indicates an
+ * infinite lifetime.
+ *
+ * @return The maximum permitted lifetime of a connection in milliseconds. A value of zero or less indicates an
+ * infinite lifetime.
+ * @deprecated Use {@link #getMaxConnLifetime()}.
+ */
+ @Deprecated
+ public long getMaxConnLifetimeMillis() {
+ return maxConnDuration.toMillis();
+ }
+
+ @Override
+ public Logger getParentLogger() throws SQLFeatureNotSupportedException {
+ throw new SQLFeatureNotSupportedException();
+ }
+
+ /**
+ * This method is protected but can only be implemented in this package because PooledConnectionAndInfo is a package
+ * private type.
+ *
+ * @param userName The user name.
+ * @param userPassword The user password.
+ * @return Matching PooledConnectionAndInfo.
+ * @throws SQLException Connection or registration failure.
+ */
+ protected abstract PooledConnectionAndInfo getPooledConnectionAndInfo(String userName, String userPassword)
+ throws SQLException;
+
+ /**
+ * Gets the SQL query that will be used to validate connections from this pool before returning them to the caller.
+ * If specified, this query <strong>MUST</strong> be an SQL SELECT statement that returns at least one row. If not
+ * specified, {@link Connection#isValid(int)} will be used to validate connections.
+ *
+ * @return The SQL query that will be used to validate connections from this pool before returning them to the
+ * caller.
+ */
+ public String getValidationQuery() {
+ return this.validationQuery;
+ }
+
+ /**
+ * Returns the timeout in seconds before the validation query fails.
+ *
+ * @return The timeout in seconds before the validation query fails.
+ * @deprecated Use {@link #getValidationQueryTimeoutDuration()}.
+ */
+ @Deprecated
+ public int getValidationQueryTimeout() {
+ return (int) validationQueryTimeoutDuration.getSeconds();
+ }
+
+ /**
+ * Returns the timeout Duration before the validation query fails.
+ *
+ * @return The timeout Duration before the validation query fails.
+ */
+ public Duration getValidationQueryTimeoutDuration() {
+ return validationQueryTimeoutDuration;
+ }
+
+ /**
+ * Gets the value of defaultAutoCommit, which defines the state of connections handed out from this pool. The value
+ * can be changed on the Connection using Connection.setAutoCommit(boolean). The default is {@code null} which
+ * will use the default value for the drive.
+ *
+ * @return value of defaultAutoCommit.
+ */
+ public Boolean isDefaultAutoCommit() {
+ return defaultAutoCommit;
+ }
+
+ /**
+ * Gets the value of defaultReadOnly, which defines the state of connections handed out from this pool. The value
+ * can be changed on the Connection using Connection.setReadOnly(boolean). The default is {@code null} which
+ * will use the default value for the drive.
+ *
+ * @return value of defaultReadOnly.
+ */
+ public Boolean isDefaultReadOnly() {
+ return defaultReadOnly;
+ }
+
+ /**
+ * Whether a rollback will be issued after executing the SQL query that will be used to validate connections from
+ * this pool before returning them to the caller.
+ *
+ * @return true if a rollback will be issued after executing the validation query
+ */
+ public boolean isRollbackAfterValidation() {
+ return this.rollbackAfterValidation;
+ }
+
+ @Override
+ public boolean isWrapperFor(final Class<?> iface) throws SQLException {
+ return iface.isInstance(this);
+ }
+
+ /**
+ * Sets the back end ConnectionPoolDataSource. This property should not be set if using JNDI to access the
+ * data source.
+ *
+ * @param dataSource
+ * Value to assign to connectionPoolDataSource.
+ */
+ public void setConnectionPoolDataSource(final ConnectionPoolDataSource dataSource) {
+ assertInitializationAllowed();
+ if (dataSourceName != null) {
+ throw new IllegalStateException("Cannot set the DataSource, if JNDI is used.");
+ }
+ if (this.dataSource != null) {
+ throw new IllegalStateException("The CPDS has already been set. It cannot be altered.");
+ }
+ this.dataSource = dataSource;
+ instanceKey = InstanceKeyDataSourceFactory.registerNewInstance(this);
+ }
+
+ /**
+ * Sets the name of the ConnectionPoolDataSource which backs this pool. This name is used to look up the data source
+ * from a JNDI service provider.
+ *
+ * @param dataSourceName
+ * Value to assign to dataSourceName.
+ */
+ public void setDataSourceName(final String dataSourceName) {
+ assertInitializationAllowed();
+ if (dataSource != null) {
+ throw new IllegalStateException("Cannot set the JNDI name for the DataSource, if already "
+ + "set using setConnectionPoolDataSource.");
+ }
+ if (this.dataSourceName != null) {
+ throw new IllegalStateException("The DataSourceName has already been set. " + "It cannot be altered.");
+ }
+ this.dataSourceName = dataSourceName;
+ instanceKey = InstanceKeyDataSourceFactory.registerNewInstance(this);
+ }
+
+ /**
+ * Sets the value of defaultAutoCommit, which defines the state of connections handed out from this pool. The value
+ * can be changed on the Connection using Connection.setAutoCommit(boolean). The default is {@code null} which
+ * will use the default value for the drive.
+ *
+ * @param defaultAutoCommit
+ * Value to assign to defaultAutoCommit.
+ */
+ public void setDefaultAutoCommit(final Boolean defaultAutoCommit) {
+ assertInitializationAllowed();
+ this.defaultAutoCommit = defaultAutoCommit;
+ }
+
+ /**
+ * Sets the default value for {@link GenericKeyedObjectPoolConfig#getBlockWhenExhausted()} for each per user pool.
+ *
+ * @param blockWhenExhausted
+ * The default value for {@link GenericKeyedObjectPoolConfig#getBlockWhenExhausted()} for each per user
+ * pool.
+ */
+ public void setDefaultBlockWhenExhausted(final boolean blockWhenExhausted) {
+ assertInitializationAllowed();
+ this.defaultBlockWhenExhausted = blockWhenExhausted;
+ }
+
+ /**
+ * Sets the default value for {@link GenericObjectPool#getDurationBetweenEvictionRuns ()} for each per user pool.
+ *
+ * @param defaultDurationBetweenEvictionRuns The default value for
+ * {@link GenericObjectPool#getDurationBetweenEvictionRuns ()} for each per user pool.
+ * @since 2.10.0
+ */
+ public void setDefaultDurationBetweenEvictionRuns(final Duration defaultDurationBetweenEvictionRuns) {
+ assertInitializationAllowed();
+ this.defaultDurationBetweenEvictionRuns = defaultDurationBetweenEvictionRuns;
+ }
+
+ /**
+ * Sets the default value for {@link GenericKeyedObjectPoolConfig#getEvictionPolicyClassName()} for each per user
+ * pool.
+ *
+ * @param evictionPolicyClassName
+ * The default value for {@link GenericKeyedObjectPoolConfig#getEvictionPolicyClassName()} for each per
+ * user pool.
+ */
+ public void setDefaultEvictionPolicyClassName(final String evictionPolicyClassName) {
+ assertInitializationAllowed();
+ this.defaultEvictionPolicyClassName = evictionPolicyClassName;
+ }
+
+ /**
+ * Sets the default value for {@link GenericKeyedObjectPoolConfig#getLifo()} for each per user pool.
+ *
+ * @param lifo
+ * The default value for {@link GenericKeyedObjectPoolConfig#getLifo()} for each per user pool.
+ */
+ public void setDefaultLifo(final boolean lifo) {
+ assertInitializationAllowed();
+ this.defaultLifo = lifo;
+ }
+
+ /**
+ * Sets the default value for {@link GenericKeyedObjectPoolConfig#getMaxIdlePerKey()} for each per user pool.
+ *
+ * @param maxIdle
+ * The default value for {@link GenericKeyedObjectPoolConfig#getMaxIdlePerKey()} for each per user pool.
+ */
+ public void setDefaultMaxIdle(final int maxIdle) {
+ assertInitializationAllowed();
+ this.defaultMaxIdle = maxIdle;
+ }
+
+ /**
+ * Sets the default value for {@link GenericKeyedObjectPoolConfig#getMaxTotalPerKey()} for each per user pool.
+ *
+ * @param maxTotal
+ * The default value for {@link GenericKeyedObjectPoolConfig#getMaxTotalPerKey()} for each per user pool.
+ */
+ public void setDefaultMaxTotal(final int maxTotal) {
+ assertInitializationAllowed();
+ this.defaultMaxTotal = maxTotal;
+ }
+
+ /**
+ * Sets the default value for {@link GenericKeyedObjectPoolConfig#getMaxWaitDuration()} for each per user pool.
+ *
+ * @param maxWaitMillis
+ * The default value for {@link GenericKeyedObjectPoolConfig#getMaxWaitDuration()} for each per user pool.
+ * @since 2.9.0
+ */
+ public void setDefaultMaxWait(final Duration maxWaitMillis) {
+ assertInitializationAllowed();
+ this.defaultMaxWaitDuration = maxWaitMillis;
+ }
+
+ /**
+ * Sets the default value for {@link GenericKeyedObjectPoolConfig#getMaxWaitMillis()} for each per user pool.
+ *
+ * @param maxWaitMillis
+ * The default value for {@link GenericKeyedObjectPoolConfig#getMaxWaitMillis()} for each per user pool.
+ * @deprecated Use {@link #setDefaultMaxWait(Duration)}.
+ */
+ @Deprecated
+ public void setDefaultMaxWaitMillis(final long maxWaitMillis) {
+ setDefaultMaxWait(Duration.ofMillis(maxWaitMillis));
+ }
+
+ /**
+ * Sets the default value for {@link GenericKeyedObjectPoolConfig#getMinEvictableIdleDuration()} for each per user
+ * pool.
+ *
+ * @param defaultMinEvictableIdleDuration
+ * The default value for {@link GenericKeyedObjectPoolConfig#getMinEvictableIdleDuration()} for each
+ * per user pool.
+ * @since 2.10.0
+ */
+ public void setDefaultMinEvictableIdle(final Duration defaultMinEvictableIdleDuration) {
+ assertInitializationAllowed();
+ this.defaultMinEvictableIdleDuration = defaultMinEvictableIdleDuration;
+ }
+
+ /**
+ * Sets the default value for {@link GenericKeyedObjectPoolConfig#getMinEvictableIdleDuration()} for each per user
+ * pool.
+ *
+ * @param minEvictableIdleTimeMillis
+ * The default value for {@link GenericKeyedObjectPoolConfig#getMinEvictableIdleDuration()} for each
+ * per user pool.
+ * @deprecated Use {@link #setDefaultMinEvictableIdle(Duration)}.
+ */
+ @Deprecated
+ public void setDefaultMinEvictableIdleTimeMillis(final long minEvictableIdleTimeMillis) {
+ assertInitializationAllowed();
+ this.defaultMinEvictableIdleDuration = Duration.ofMillis(minEvictableIdleTimeMillis);
+ }
+
+ /**
+ * Sets the default value for {@link GenericKeyedObjectPoolConfig#getMinIdlePerKey()} for each per user pool.
+ *
+ * @param minIdle
+ * The default value for {@link GenericKeyedObjectPoolConfig#getMinIdlePerKey()} for each per user pool.
+ */
+ public void setDefaultMinIdle(final int minIdle) {
+ assertInitializationAllowed();
+ this.defaultMinIdle = minIdle;
+ }
+
+ /**
+ * Sets the default value for {@link GenericKeyedObjectPoolConfig#getNumTestsPerEvictionRun()} for each per user
+ * pool.
+ *
+ * @param numTestsPerEvictionRun
+ * The default value for {@link GenericKeyedObjectPoolConfig#getNumTestsPerEvictionRun()} for each per
+ * user pool.
+ */
+ public void setDefaultNumTestsPerEvictionRun(final int numTestsPerEvictionRun) {
+ assertInitializationAllowed();
+ this.defaultNumTestsPerEvictionRun = numTestsPerEvictionRun;
+ }
+
+ /**
+ * Sets the value of defaultReadOnly, which defines the state of connections handed out from this pool. The value
+ * can be changed on the Connection using Connection.setReadOnly(boolean). The default is {@code null} which
+ * will use the default value for the drive.
+ *
+ * @param defaultReadOnly
+ * Value to assign to defaultReadOnly.
+ */
+ public void setDefaultReadOnly(final Boolean defaultReadOnly) {
+ assertInitializationAllowed();
+ this.defaultReadOnly = defaultReadOnly;
+ }
+
+ /**
+ * Sets the default value for {@link org.apache.commons.pool2.impl.GenericObjectPool
+ * GenericObjectPool#getSoftMinEvictableIdleTimeMillis()} for each per user pool.
+ *
+ * @param defaultSoftMinEvictableIdleDuration
+ * The default value for {@link org.apache.commons.pool2.impl.GenericObjectPool
+ * GenericObjectPool#getSoftMinEvictableIdleTimeMillis()} for each per user pool.
+ * @since 2.10.0
+ */
+ public void setDefaultSoftMinEvictableIdle(final Duration defaultSoftMinEvictableIdleDuration) {
+ assertInitializationAllowed();
+ this.defaultSoftMinEvictableIdleDuration = defaultSoftMinEvictableIdleDuration;
+ }
+
+ /**
+ * Sets the default value for {@link org.apache.commons.pool2.impl.GenericObjectPool
+ * GenericObjectPool#getSoftMinEvictableIdleTimeMillis()} for each per user pool.
+ *
+ * @param softMinEvictableIdleTimeMillis
+ * The default value for {@link org.apache.commons.pool2.impl.GenericObjectPool
+ * GenericObjectPool#getSoftMinEvictableIdleTimeMillis()} for each per user pool.
+ * @deprecated Use {@link #setDefaultSoftMinEvictableIdle(Duration)}.
+ */
+ @Deprecated
+ public void setDefaultSoftMinEvictableIdleTimeMillis(final long softMinEvictableIdleTimeMillis) {
+ assertInitializationAllowed();
+ this.defaultSoftMinEvictableIdleDuration = Duration.ofMillis(softMinEvictableIdleTimeMillis);
+ }
+
+ /**
+ * Sets the default value for {@link org.apache.commons.pool2.impl.GenericObjectPool
+ * GenericObjectPool#getTestOnBorrow()} for each per user pool.
+ *
+ * @param testOnBorrow
+ * The default value for {@link org.apache.commons.pool2.impl.GenericObjectPool
+ * GenericObjectPool#getTestOnBorrow()} for each per user pool.
+ */
+ public void setDefaultTestOnBorrow(final boolean testOnBorrow) {
+ assertInitializationAllowed();
+ this.defaultTestOnBorrow = testOnBorrow;
+ }
+
+ /**
+ * Sets the default value for {@link org.apache.commons.pool2.impl.GenericObjectPool
+ * GenericObjectPool#getTestOnCreate()} for each per user pool.
+ *
+ * @param testOnCreate
+ * The default value for {@link org.apache.commons.pool2.impl.GenericObjectPool
+ * GenericObjectPool#getTestOnCreate()} for each per user pool.
+ */
+ public void setDefaultTestOnCreate(final boolean testOnCreate) {
+ assertInitializationAllowed();
+ this.defaultTestOnCreate = testOnCreate;
+ }
+
+ /**
+ * Sets the default value for {@link org.apache.commons.pool2.impl.GenericObjectPool
+ * GenericObjectPool#getTestOnReturn()} for each per user pool.
+ *
+ * @param testOnReturn
+ * The default value for {@link org.apache.commons.pool2.impl.GenericObjectPool
+ * GenericObjectPool#getTestOnReturn()} for each per user pool.
+ */
+ public void setDefaultTestOnReturn(final boolean testOnReturn) {
+ assertInitializationAllowed();
+ this.defaultTestOnReturn = testOnReturn;
+ }
+
+ /**
+ * Sets the default value for {@link org.apache.commons.pool2.impl.GenericObjectPool
+ * GenericObjectPool#getTestWhileIdle()} for each per user pool.
+ *
+ * @param testWhileIdle
+ * The default value for {@link org.apache.commons.pool2.impl.GenericObjectPool
+ * GenericObjectPool#getTestWhileIdle()} for each per user pool.
+ */
+ public void setDefaultTestWhileIdle(final boolean testWhileIdle) {
+ assertInitializationAllowed();
+ this.defaultTestWhileIdle = testWhileIdle;
+ }
+
+ /**
+ * Sets the default value for {@link GenericObjectPool#getDurationBetweenEvictionRuns()} for each per user pool.
+ *
+ * @param timeBetweenEvictionRunsMillis The default value for
+ * {@link GenericObjectPool#getDurationBetweenEvictionRuns()} for each per user pool.
+ * @deprecated Use {@link #setDefaultDurationBetweenEvictionRuns(Duration)}.
+ */
+ @Deprecated
+ public void setDefaultTimeBetweenEvictionRunsMillis(final long timeBetweenEvictionRunsMillis) {
+ assertInitializationAllowed();
+ this.defaultDurationBetweenEvictionRuns = Duration.ofMillis(timeBetweenEvictionRunsMillis);
+ }
+
+ /**
+ * Sets the value of defaultTransactionIsolation, which defines the state of connections handed out from this pool.
+ * The value can be changed on the Connection using Connection.setTransactionIsolation(int). The default is JDBC
+ * driver dependent.
+ *
+ * @param defaultTransactionIsolation
+ * Value to assign to defaultTransactionIsolation
+ */
+ public void setDefaultTransactionIsolation(final int defaultTransactionIsolation) {
+ assertInitializationAllowed();
+ switch (defaultTransactionIsolation) {
+ case Connection.TRANSACTION_NONE:
+ case Connection.TRANSACTION_READ_COMMITTED:
+ case Connection.TRANSACTION_READ_UNCOMMITTED:
+ case Connection.TRANSACTION_REPEATABLE_READ:
+ case Connection.TRANSACTION_SERIALIZABLE:
+ break;
+ default:
+ throw new IllegalArgumentException(BAD_TRANSACTION_ISOLATION);
+ }
+ this.defaultTransactionIsolation = defaultTransactionIsolation;
+ }
+
+ /**
+ * Sets the description. This property is defined by JDBC as for use with GUI (or other) tools that might deploy the
+ * datasource. It serves no internal purpose.
+ *
+ * @param description
+ * Value to assign to description.
+ */
+ public void setDescription(final String description) {
+ this.description = description;
+ }
+
+ /**
+ * Sets the JNDI environment to be used when instantiating a JNDI InitialContext. This InitialContext is used to
+ * locate the back end ConnectionPoolDataSource.
+ *
+ * @param properties
+ * the JNDI environment property to set which will overwrite any current settings
+ */
+ void setJndiEnvironment(final Properties properties) {
+ if (jndiEnvironment == null) {
+ jndiEnvironment = new Properties();
+ } else {
+ jndiEnvironment.clear();
+ }
+ jndiEnvironment.putAll(properties);
+ }
+
+ /**
+ * Sets the value of the given JNDI environment property to be used when instantiating a JNDI InitialContext. This
+ * InitialContext is used to locate the back end ConnectionPoolDataSource.
+ *
+ * @param key
+ * the JNDI environment property to set.
+ * @param value
+ * the value assigned to specified JNDI environment property.
+ */
+ public void setJndiEnvironment(final String key, final String value) {
+ if (jndiEnvironment == null) {
+ jndiEnvironment = new Properties();
+ }
+ jndiEnvironment.setProperty(key, value);
+ }
+
+ /**
+ * Sets the value of loginTimeout.
+ *
+ * @param loginTimeout
+ * Value to assign to loginTimeout.
+ * @since 2.10.0
+ */
+ public void setLoginTimeout(final Duration loginTimeout) {
+ this.loginTimeoutDuration = loginTimeout;
+ }
+
+ /**
+ * Sets the value of loginTimeout.
+ *
+ * @param loginTimeout
+ * Value to assign to loginTimeout.
+ * @deprecated Use {@link #setLoginTimeout(Duration)}.
+ */
+ @Deprecated
+ @Override
+ public void setLoginTimeout(final int loginTimeout) {
+ this.loginTimeoutDuration = Duration.ofSeconds(loginTimeout);
+ }
+
+ /**
+ * Sets the value of logWriter.
+ *
+ * @param logWriter
+ * Value to assign to logWriter.
+ */
+ @Override
+ public void setLogWriter(final PrintWriter logWriter) {
+ this.logWriter = logWriter;
+ }
+
+ /**
+ * <p>
+ * Sets the maximum permitted lifetime of a connection. A value of zero or less indicates an
+ * infinite lifetime.
+ * </p>
+ * <p>
+ * Note: this method currently has no effect once the pool has been initialized. The pool is initialized the first
+ * time one of the following methods is invoked: <code>getConnection, setLogwriter,
+ * setLoginTimeout, getLoginTimeout, getLogWriter.</code>
+ * </p>
+ *
+ * @param maxConnLifetimeMillis
+ * The maximum permitted lifetime of a connection. A value of zero or less indicates an
+ * infinite lifetime.
+ * @since 2.9.0
+ */
+ public void setMaxConnLifetime(final Duration maxConnLifetimeMillis) {
+ this.maxConnDuration = maxConnLifetimeMillis;
+ }
+
+ /**
+ * <p>
+ * Sets the maximum permitted lifetime of a connection in milliseconds. A value of zero or less indicates an
+ * infinite lifetime.
+ * </p>
+ * <p>
+ * Note: this method currently has no effect once the pool has been initialized. The pool is initialized the first
+ * time one of the following methods is invoked: <code>getConnection, setLogwriter,
+ * setLoginTimeout, getLoginTimeout, getLogWriter.</code>
+ * </p>
+ *
+ * @param maxConnLifetimeMillis
+ * The maximum permitted lifetime of a connection in milliseconds. A value of zero or less indicates an
+ * infinite lifetime.
+ * @deprecated Use {@link #setMaxConnLifetime(Duration)}.
+ */
+ @Deprecated
+ public void setMaxConnLifetimeMillis(final long maxConnLifetimeMillis) {
+ setMaxConnLifetime(Duration.ofMillis(maxConnLifetimeMillis));
+ }
+
+ /**
+ * Whether a rollback will be issued after executing the SQL query that will be used to validate connections from
+ * this pool before returning them to the caller. Default behavior is NOT to issue a rollback. The setting will only
+ * have an effect if a validation query is set
+ *
+ * @param rollbackAfterValidation
+ * new property value
+ */
+ public void setRollbackAfterValidation(final boolean rollbackAfterValidation) {
+ assertInitializationAllowed();
+ this.rollbackAfterValidation = rollbackAfterValidation;
+ }
+
+ protected abstract void setupDefaults(Connection connection, String userName) throws SQLException;
+
+ /**
+ * Sets the SQL query that will be used to validate connections from this pool before returning them to the caller.
+ * If specified, this query <strong>MUST</strong> be an SQL SELECT statement that returns at least one row. If not
+ * specified, connections will be validated using {@link Connection#isValid(int)}.
+ *
+ * @param validationQuery
+ * The SQL query that will be used to validate connections from this pool before returning them to the
+ * caller.
+ */
+ public void setValidationQuery(final String validationQuery) {
+ assertInitializationAllowed();
+ this.validationQuery = validationQuery;
+ }
+
+ /**
+ * Sets the timeout duration before the validation query fails.
+ *
+ * @param validationQueryTimeoutDuration
+ * The new timeout duration.
+ */
+ public void setValidationQueryTimeout(final Duration validationQueryTimeoutDuration) {
+ this.validationQueryTimeoutDuration = validationQueryTimeoutDuration;
+ }
+
+ /**
+ * Sets the timeout in seconds before the validation query fails.
+ *
+ * @param validationQueryTimeoutSeconds
+ * The new timeout in seconds
+ * @deprecated Use {@link #setValidationQueryTimeout(Duration)}.
+ */
+ @Deprecated
+ public void setValidationQueryTimeout(final int validationQueryTimeoutSeconds) {
+ this.validationQueryTimeoutDuration = Duration.ofSeconds(validationQueryTimeoutSeconds);
+ }
+
+ protected ConnectionPoolDataSource testCPDS(final String userName, final String userPassword)
+ throws javax.naming.NamingException, SQLException {
+ // The source of physical db connections
+ ConnectionPoolDataSource cpds = this.dataSource;
+ if (cpds == null) {
+ Context ctx = null;
+ if (jndiEnvironment == null) {
+ ctx = new InitialContext();
+ } else {
+ ctx = new InitialContext(jndiEnvironment);
+ }
+ final Object ds = ctx.lookup(dataSourceName);
+ if (!(ds instanceof ConnectionPoolDataSource)) {
+ throw new SQLException("Illegal configuration: " + "DataSource " + dataSourceName + " ("
+ + ds.getClass().getName() + ")" + " doesn't implement javax.sql.ConnectionPoolDataSource");
+ }
+ cpds = (ConnectionPoolDataSource) ds;
+ }
+
+ // try to get a connection with the supplied userName/password
+ PooledConnection conn = null;
+ try {
+ if (userName != null) {
+ conn = cpds.getPooledConnection(userName, userPassword);
+ } else {
+ conn = cpds.getPooledConnection();
+ }
+ if (conn == null) {
+ throw new SQLException("Cannot connect using the supplied userName/password");
+ }
+ } finally {
+ if (conn != null) {
+ try {
+ conn.close();
+ } catch (final SQLException ignored) {
+ // at least we could connect
+ }
+ }
+ }
+ return cpds;
+ }
+
+ /**
+ * @since 2.6.0
+ */
+ @Override
+ public synchronized String toString() {
+ final StringBuilder builder = new StringBuilder(super.toString());
+ builder.append('[');
+ toStringFields(builder);
+ builder.append(']');
+ return builder.toString();
+ }
+
+ protected void toStringFields(final StringBuilder builder) {
+ builder.append("getConnectionCalled=");
+ builder.append(getConnectionCalled);
+ builder.append(", dataSource=");
+ builder.append(dataSource);
+ builder.append(", dataSourceName=");
+ builder.append(dataSourceName);
+ builder.append(", description=");
+ builder.append(description);
+ builder.append(", jndiEnvironment=");
+ builder.append(jndiEnvironment);
+ builder.append(", loginTimeoutDuration=");
+ builder.append(loginTimeoutDuration);
+ builder.append(", logWriter=");
+ builder.append(logWriter);
+ builder.append(", instanceKey=");
+ builder.append(instanceKey);
+ builder.append(", defaultBlockWhenExhausted=");
+ builder.append(defaultBlockWhenExhausted);
+ builder.append(", defaultEvictionPolicyClassName=");
+ builder.append(defaultEvictionPolicyClassName);
+ builder.append(", defaultLifo=");
+ builder.append(defaultLifo);
+ builder.append(", defaultMaxIdle=");
+ builder.append(defaultMaxIdle);
+ builder.append(", defaultMaxTotal=");
+ builder.append(defaultMaxTotal);
+ builder.append(", defaultMaxWaitDuration=");
+ builder.append(defaultMaxWaitDuration);
+ builder.append(", defaultMinEvictableIdleDuration=");
+ builder.append(defaultMinEvictableIdleDuration);
+ builder.append(", defaultMinIdle=");
+ builder.append(defaultMinIdle);
+ builder.append(", defaultNumTestsPerEvictionRun=");
+ builder.append(defaultNumTestsPerEvictionRun);
+ builder.append(", defaultSoftMinEvictableIdleDuration=");
+ builder.append(defaultSoftMinEvictableIdleDuration);
+ builder.append(", defaultTestOnCreate=");
+ builder.append(defaultTestOnCreate);
+ builder.append(", defaultTestOnBorrow=");
+ builder.append(defaultTestOnBorrow);
+ builder.append(", defaultTestOnReturn=");
+ builder.append(defaultTestOnReturn);
+ builder.append(", defaultTestWhileIdle=");
+ builder.append(defaultTestWhileIdle);
+ builder.append(", defaultDurationBetweenEvictionRuns=");
+ builder.append(defaultDurationBetweenEvictionRuns);
+ builder.append(", validationQuery=");
+ builder.append(validationQuery);
+ builder.append(", validationQueryTimeoutDuration=");
+ builder.append(validationQueryTimeoutDuration);
+ builder.append(", rollbackAfterValidation=");
+ builder.append(rollbackAfterValidation);
+ builder.append(", maxConnDuration=");
+ builder.append(maxConnDuration);
+ builder.append(", defaultAutoCommit=");
+ builder.append(defaultAutoCommit);
+ builder.append(", defaultTransactionIsolation=");
+ builder.append(defaultTransactionIsolation);
+ builder.append(", defaultReadOnly=");
+ builder.append(defaultReadOnly);
+ }
+
+ @Override
+ @SuppressWarnings("unchecked")
+ public <T> T unwrap(final Class<T> iface) throws SQLException {
+ if (isWrapperFor(iface)) {
+ return (T) this;
+ }
+ throw new SQLException(this + " is not a wrapper for " + iface);
+ }
+ /* JDBC_4_ANT_KEY_END */
+}
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 5f2cbbcf..23832cb4 100644
--- a/src/main/java/org/apache/commons/dbcp2/datasources/InstanceKeyDataSourceFactory.java
+++ b/src/main/java/org/apache/commons/dbcp2/datasources/InstanceKeyDataSourceFactory.java
@@ -1,348 +1,348 @@
-/*
- * 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.ByteArrayInputStream;
-import java.io.IOException;
-import java.io.ObjectInputStream;
-import java.time.Duration;
-import java.util.ArrayList;
-import java.util.Hashtable;
-import java.util.List;
-import java.util.Map;
-import java.util.Map.Entry;
-import java.util.Properties;
-import java.util.concurrent.ConcurrentHashMap;
-
-import javax.naming.Context;
-import javax.naming.Name;
-import javax.naming.RefAddr;
-import javax.naming.Reference;
-import javax.naming.spi.ObjectFactory;
-
-import org.apache.commons.dbcp2.ListException;
-import org.apache.commons.dbcp2.Utils;
-
-/**
- * A JNDI ObjectFactory which creates {@code SharedPoolDataSource}s or {@code PerUserPoolDataSource}s
- *
- * @since 2.0
- */
-abstract class InstanceKeyDataSourceFactory implements ObjectFactory {
-
- private static final Map<String, InstanceKeyDataSource> INSTANCE_MAP = new ConcurrentHashMap<>();
-
- /**
- * Closes all pools associated with this class.
- *
- * @throws Exception
- * 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 {
- // 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()) {
- // Bullet-proof to avoid anything else but problems from InstanceKeyDataSource#close().
- if (next != null) {
- @SuppressWarnings("resource") final InstanceKeyDataSource value = next.getValue();
- if (value != null) {
- try {
- value.close();
- } catch (final Exception e) {
- exceptionList.add(e);
- }
- }
- }
- }
- INSTANCE_MAP.clear();
- if (!exceptionList.isEmpty()) {
- throw new ListException("Could not close all InstanceKeyDataSource instances.", exceptionList);
- }
- }
-
- /**
- * Deserializes the provided byte array to create an object.
- *
- * @param data
- * Data to deserialize to create the configuration parameter.
- *
- * @return The Object created by deserializing the data.
- *
- * @throws ClassNotFoundException
- * If a class cannot be found during the deserialization of a configuration parameter.
- * @throws IOException
- * If an I/O error occurs during the deserialization of a configuration parameter.
- */
- protected static final Object deserialize(final byte[] data) throws IOException, ClassNotFoundException {
- ObjectInputStream in = null;
- try {
- in = new ObjectInputStream(new ByteArrayInputStream(data));
- return in.readObject();
- } finally {
- Utils.closeQuietly(in);
- }
- }
-
- static synchronized String registerNewInstance(final InstanceKeyDataSource ds) {
- int max = 0;
- for (final String s : INSTANCE_MAP.keySet()) {
- if (s != null) {
- try {
- max = Math.max(max, Integer.parseInt(s));
- } catch (final NumberFormatException e) {
- // no sweat, ignore those keys
- }
- }
- }
- final String instanceKey = String.valueOf(max + 1);
- // Put a placeholder here for now, so other instances will not
- // take our key. We will replace with a pool when ready.
- INSTANCE_MAP.put(instanceKey, ds);
- return instanceKey;
- }
-
- static void removeInstance(final String key) {
- if (key != null) {
- INSTANCE_MAP.remove(key);
- }
- }
-
- /**
- * Creates an instance of the subclass and sets any properties contained in the Reference.
- *
- * @param ref
- * The properties to be set on the created DataSource
- *
- * @return A configured DataSource of the appropriate type.
- *
- * @throws ClassNotFoundException
- * If a class cannot be found during the deserialization of a configuration parameter.
- * @throws IOException
- * If an I/O error occurs during the deserialization of a configuration parameter.
- */
- protected abstract InstanceKeyDataSource getNewInstance(Reference ref) throws IOException, ClassNotFoundException;
-
- /**
- * Implements ObjectFactory to create an instance of SharedPoolDataSource or PerUserPoolDataSource
- */
- @Override
- public Object getObjectInstance(final Object refObj, final Name name, final Context context,
- final Hashtable<?, ?> env) throws IOException, ClassNotFoundException {
- // The spec says to return null if we can't create an instance
- // of the reference
- Object obj = null;
- if (refObj instanceof Reference) {
- final Reference ref = (Reference) refObj;
- if (isCorrectClass(ref.getClassName())) {
- final RefAddr refAddr = ref.get("instanceKey");
- if (refAddr != null && refAddr.getContent() != null) {
- // object was bound to JNDI via Referenceable API.
- obj = INSTANCE_MAP.get(refAddr.getContent());
- } else {
- // Tomcat JNDI creates a Reference out of server.xml
- // <ResourceParam> configuration and passes it to an
- // instance of the factory given in server.xml.
- String key = null;
- if (name != null) {
- key = name.toString();
- obj = INSTANCE_MAP.get(key);
- }
- if (obj == null) {
- final InstanceKeyDataSource ds = getNewInstance(ref);
- setCommonProperties(ref, ds);
- obj = ds;
- if (key != null) {
- INSTANCE_MAP.put(key, ds);
- }
- }
- }
- }
- }
- return obj;
- }
-
- /**
- * Tests if className is the value returned from getClass().getName().toString().
- *
- * @param className
- * The class name to test.
- *
- * @return true if and only if className is the value returned from getClass().getName().toString()
- */
- protected abstract boolean isCorrectClass(String className);
-
- boolean parseBoolean(final RefAddr refAddr) {
- return Boolean.parseBoolean(toString(refAddr));
- }
-
- int parseInt(final RefAddr refAddr) {
- return Integer.parseInt(toString(refAddr));
- }
-
- long parseLong(final RefAddr refAddr) {
- return Long.parseLong(toString(refAddr));
- }
-
- private void setCommonProperties(final Reference ref, final InstanceKeyDataSource ikds)
- throws IOException, ClassNotFoundException {
-
- RefAddr refAddr = ref.get("dataSourceName");
- if (refAddr != null && refAddr.getContent() != null) {
- ikds.setDataSourceName(toString(refAddr));
- }
-
- refAddr = ref.get("description");
- if (refAddr != null && refAddr.getContent() != null) {
- ikds.setDescription(toString(refAddr));
- }
-
- refAddr = ref.get("jndiEnvironment");
- if (refAddr != null && refAddr.getContent() != null) {
- final byte[] serialized = (byte[]) refAddr.getContent();
- ikds.setJndiEnvironment((Properties) deserialize(serialized));
- }
-
- refAddr = ref.get("loginTimeout");
- if (refAddr != null && refAddr.getContent() != null) {
- ikds.setLoginTimeout(Duration.ofSeconds(parseInt(refAddr)));
- }
-
- // Pool properties
- refAddr = ref.get("blockWhenExhausted");
- if (refAddr != null && refAddr.getContent() != null) {
- ikds.setDefaultBlockWhenExhausted(parseBoolean(refAddr));
- }
-
- refAddr = ref.get("evictionPolicyClassName");
- if (refAddr != null && refAddr.getContent() != null) {
- ikds.setDefaultEvictionPolicyClassName(toString(refAddr));
- }
-
- // Pool properties
- refAddr = ref.get("lifo");
- if (refAddr != null && refAddr.getContent() != null) {
- ikds.setDefaultLifo(parseBoolean(refAddr));
- }
-
- refAddr = ref.get("maxIdlePerKey");
- if (refAddr != null && refAddr.getContent() != null) {
- ikds.setDefaultMaxIdle(parseInt(refAddr));
- }
-
- refAddr = ref.get("maxTotalPerKey");
- if (refAddr != null && refAddr.getContent() != null) {
- ikds.setDefaultMaxTotal(parseInt(refAddr));
- }
-
- refAddr = ref.get("maxWaitMillis");
- if (refAddr != null && refAddr.getContent() != null) {
- ikds.setDefaultMaxWait(Duration.ofMillis(parseLong(refAddr)));
- }
-
- refAddr = ref.get("minEvictableIdleTimeMillis");
- if (refAddr != null && refAddr.getContent() != null) {
- ikds.setDefaultMinEvictableIdle(Duration.ofMillis(parseLong(refAddr)));
- }
-
- refAddr = ref.get("minIdlePerKey");
- if (refAddr != null && refAddr.getContent() != null) {
- ikds.setDefaultMinIdle(parseInt(refAddr));
- }
-
- refAddr = ref.get("numTestsPerEvictionRun");
- if (refAddr != null && refAddr.getContent() != null) {
- ikds.setDefaultNumTestsPerEvictionRun(parseInt(refAddr));
- }
-
- refAddr = ref.get("softMinEvictableIdleTimeMillis");
- if (refAddr != null && refAddr.getContent() != null) {
- ikds.setDefaultSoftMinEvictableIdle(Duration.ofMillis(parseLong(refAddr)));
- }
-
- refAddr = ref.get("testOnCreate");
- if (refAddr != null && refAddr.getContent() != null) {
- ikds.setDefaultTestOnCreate(parseBoolean(refAddr));
- }
-
- refAddr = ref.get("testOnBorrow");
- if (refAddr != null && refAddr.getContent() != null) {
- ikds.setDefaultTestOnBorrow(parseBoolean(refAddr));
- }
-
- refAddr = ref.get("testOnReturn");
- if (refAddr != null && refAddr.getContent() != null) {
- ikds.setDefaultTestOnReturn(parseBoolean(refAddr));
- }
-
- refAddr = ref.get("testWhileIdle");
- if (refAddr != null && refAddr.getContent() != null) {
- ikds.setDefaultTestWhileIdle(parseBoolean(refAddr));
- }
-
- refAddr = ref.get("timeBetweenEvictionRunsMillis");
- if (refAddr != null && refAddr.getContent() != null) {
- ikds.setDefaultDurationBetweenEvictionRuns(Duration.ofMillis(parseLong(refAddr)));
- }
-
- // Connection factory properties
-
- refAddr = ref.get("validationQuery");
- if (refAddr != null && refAddr.getContent() != null) {
- ikds.setValidationQuery(toString(refAddr));
- }
-
- refAddr = ref.get("validationQueryTimeout");
- if (refAddr != null && refAddr.getContent() != null) {
- ikds.setValidationQueryTimeout(Duration.ofSeconds(parseInt(refAddr)));
- }
-
- refAddr = ref.get("rollbackAfterValidation");
- if (refAddr != null && refAddr.getContent() != null) {
- ikds.setRollbackAfterValidation(parseBoolean(refAddr));
- }
-
- refAddr = ref.get("maxConnLifetimeMillis");
- if (refAddr != null && refAddr.getContent() != null) {
- ikds.setMaxConnLifetime(Duration.ofMillis(parseLong(refAddr)));
- }
-
- // Connection properties
-
- refAddr = ref.get("defaultAutoCommit");
- if (refAddr != null && refAddr.getContent() != null) {
- ikds.setDefaultAutoCommit(Boolean.valueOf(toString(refAddr)));
- }
-
- refAddr = ref.get("defaultTransactionIsolation");
- if (refAddr != null && refAddr.getContent() != null) {
- ikds.setDefaultTransactionIsolation(parseInt(refAddr));
- }
-
- refAddr = ref.get("defaultReadOnly");
- if (refAddr != null && refAddr.getContent() != null) {
- ikds.setDefaultReadOnly(Boolean.valueOf(toString(refAddr)));
- }
- }
-
- String toString(final RefAddr refAddr) {
- return refAddr.getContent().toString();
- }
-}
+/*
+ * 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.ByteArrayInputStream;
+import java.io.IOException;
+import java.io.ObjectInputStream;
+import java.time.Duration;
+import java.util.ArrayList;
+import java.util.Hashtable;
+import java.util.List;
+import java.util.Map;
+import java.util.Map.Entry;
+import java.util.Properties;
+import java.util.concurrent.ConcurrentHashMap;
+
+import javax.naming.Context;
+import javax.naming.Name;
+import javax.naming.RefAddr;
+import javax.naming.Reference;
+import javax.naming.spi.ObjectFactory;
+
+import org.apache.commons.dbcp2.ListException;
+import org.apache.commons.dbcp2.Utils;
+
+/**
+ * A JNDI ObjectFactory which creates {@code SharedPoolDataSource}s or {@code PerUserPoolDataSource}s
+ *
+ * @since 2.0
+ */
+abstract class InstanceKeyDataSourceFactory implements ObjectFactory {
+
+ private static final Map<String, InstanceKeyDataSource> INSTANCE_MAP = new ConcurrentHashMap<>();
+
+ /**
+ * Closes all pools associated with this class.
+ *
+ * @throws Exception
+ * 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 {
+ // 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()) {
+ // Bullet-proof to avoid anything else but problems from InstanceKeyDataSource#close().
+ if (next != null) {
+ @SuppressWarnings("resource") final InstanceKeyDataSource value = next.getValue();
+ if (value != null) {
+ try {
+ value.close();
+ } catch (final Exception e) {
+ exceptionList.add(e);
+ }
+ }
+ }
+ }
+ INSTANCE_MAP.clear();
+ if (!exceptionList.isEmpty()) {
+ throw new ListException("Could not close all InstanceKeyDataSource instances.", exceptionList);
+ }
+ }
+
+ /**
+ * Deserializes the provided byte array to create an object.
+ *
+ * @param data
+ * Data to deserialize to create the configuration parameter.
+ *
+ * @return The Object created by deserializing the data.
+ *
+ * @throws ClassNotFoundException
+ * If a class cannot be found during the deserialization of a configuration parameter.
+ * @throws IOException
+ * If an I/O error occurs during the deserialization of a configuration parameter.
+ */
+ protected static final Object deserialize(final byte[] data) throws IOException, ClassNotFoundException {
+ ObjectInputStream in = null;
+ try {
+ in = new ObjectInputStream(new ByteArrayInputStream(data));
+ return in.readObject();
+ } finally {
+ Utils.closeQuietly(in);
+ }
+ }
+
+ static synchronized String registerNewInstance(final InstanceKeyDataSource ds) {
+ int max = 0;
+ for (final String s : INSTANCE_MAP.keySet()) {
+ if (s != null) {
+ try {
+ max = Math.max(max, Integer.parseInt(s));
+ } catch (final NumberFormatException ignored) {
+ // no sweat, ignore those keys
+ }
+ }
+ }
+ final String instanceKey = String.valueOf(max + 1);
+ // Put a placeholder here for now, so other instances will not
+ // take our key. We will replace with a pool when ready.
+ INSTANCE_MAP.put(instanceKey, ds);
+ return instanceKey;
+ }
+
+ static void removeInstance(final String key) {
+ if (key != null) {
+ INSTANCE_MAP.remove(key);
+ }
+ }
+
+ /**
+ * Creates an instance of the subclass and sets any properties contained in the Reference.
+ *
+ * @param ref
+ * The properties to be set on the created DataSource
+ *
+ * @return A configured DataSource of the appropriate type.
+ *
+ * @throws ClassNotFoundException
+ * If a class cannot be found during the deserialization of a configuration parameter.
+ * @throws IOException
+ * If an I/O error occurs during the deserialization of a configuration parameter.
+ */
+ protected abstract InstanceKeyDataSource getNewInstance(Reference ref) throws IOException, ClassNotFoundException;
+
+ /**
+ * Implements ObjectFactory to create an instance of SharedPoolDataSource or PerUserPoolDataSource
+ */
+ @Override
+ public Object getObjectInstance(final Object refObj, final Name name, final Context context,
+ final Hashtable<?, ?> env) throws IOException, ClassNotFoundException {
+ // The spec says to return null if we can't create an instance
+ // of the reference
+ Object obj = null;
+ if (refObj instanceof Reference) {
+ final Reference ref = (Reference) refObj;
+ if (isCorrectClass(ref.getClassName())) {
+ final RefAddr refAddr = ref.get("instanceKey");
+ if (refAddr != null && refAddr.getContent() != null) {
+ // object was bound to JNDI via Referenceable API.
+ obj = INSTANCE_MAP.get(refAddr.getContent());
+ } else {
+ // Tomcat JNDI creates a Reference out of server.xml
+ // <ResourceParam> configuration and passes it to an
+ // instance of the factory given in server.xml.
+ String key = null;
+ if (name != null) {
+ key = name.toString();
+ obj = INSTANCE_MAP.get(key);
+ }
+ if (obj == null) {
+ final InstanceKeyDataSource ds = getNewInstance(ref);
+ setCommonProperties(ref, ds);
+ obj = ds;
+ if (key != null) {
+ INSTANCE_MAP.put(key, ds);
+ }
+ }
+ }
+ }
+ }
+ return obj;
+ }
+
+ /**
+ * Tests if className is the value returned from getClass().getName().toString().
+ *
+ * @param className
+ * The class name to test.
+ *
+ * @return true if and only if className is the value returned from getClass().getName().toString()
+ */
+ protected abstract boolean isCorrectClass(String className);
+
+ boolean parseBoolean(final RefAddr refAddr) {
+ return Boolean.parseBoolean(toString(refAddr));
+ }
+
+ int parseInt(final RefAddr refAddr) {
+ return Integer.parseInt(toString(refAddr));
+ }
+
+ long parseLong(final RefAddr refAddr) {
+ return Long.parseLong(toString(refAddr));
+ }
+
+ private void setCommonProperties(final Reference ref, final InstanceKeyDataSource ikds)
+ throws IOException, ClassNotFoundException {
+
+ RefAddr refAddr = ref.get("dataSourceName");
+ if (refAddr != null && refAddr.getContent() != null) {
+ ikds.setDataSourceName(toString(refAddr));
+ }
+
+ refAddr = ref.get("description");
+ if (refAddr != null && refAddr.getContent() != null) {
+ ikds.setDescription(toString(refAddr));
+ }
+
+ refAddr = ref.get("jndiEnvironment");
+ if (refAddr != null && refAddr.getContent() != null) {
+ final byte[] serialized = (byte[]) refAddr.getContent();
+ ikds.setJndiEnvironment((Properties) deserialize(serialized));
+ }
+
+ refAddr = ref.get("loginTimeout");
+ if (refAddr != null && refAddr.getContent() != null) {
+ ikds.setLoginTimeout(Duration.ofSeconds(parseInt(refAddr)));
+ }
+
+ // Pool properties
+ refAddr = ref.get("blockWhenExhausted");
+ if (refAddr != null && refAddr.getContent() != null) {
+ ikds.setDefaultBlockWhenExhausted(parseBoolean(refAddr));
+ }
+
+ refAddr = ref.get("evictionPolicyClassName");
+ if (refAddr != null && refAddr.getContent() != null) {
+ ikds.setDefaultEvictionPolicyClassName(toString(refAddr));
+ }
+
+ // Pool properties
+ refAddr = ref.get("lifo");
+ if (refAddr != null && refAddr.getContent() != null) {
+ ikds.setDefaultLifo(parseBoolean(refAddr));
+ }
+
+ refAddr = ref.get("maxIdlePerKey");
+ if (refAddr != null && refAddr.getContent() != null) {
+ ikds.setDefaultMaxIdle(parseInt(refAddr));
+ }
+
+ refAddr = ref.get("maxTotalPerKey");
+ if (refAddr != null && refAddr.getContent() != null) {
+ ikds.setDefaultMaxTotal(parseInt(refAddr));
+ }
+
+ refAddr = ref.get("maxWaitMillis");
+ if (refAddr != null && refAddr.getContent() != null) {
+ ikds.setDefaultMaxWait(Duration.ofMillis(parseLong(refAddr)));
+ }
+
+ refAddr = ref.get("minEvictableIdleTimeMillis");
+ if (refAddr != null && refAddr.getContent() != null) {
+ ikds.setDefaultMinEvictableIdle(Duration.ofMillis(parseLong(refAddr)));
+ }
+
+ refAddr = ref.get("minIdlePerKey");
+ if (refAddr != null && refAddr.getContent() != null) {
+ ikds.setDefaultMinIdle(parseInt(refAddr));
+ }
+
+ refAddr = ref.get("numTestsPerEvictionRun");
+ if (refAddr != null && refAddr.getContent() != null) {
+ ikds.setDefaultNumTestsPerEvictionRun(parseInt(refAddr));
+ }
+
+ refAddr = ref.get("softMinEvictableIdleTimeMillis");
+ if (refAddr != null && refAddr.getContent() != null) {
+ ikds.setDefaultSoftMinEvictableIdle(Duration.ofMillis(parseLong(refAddr)));
+ }
+
+ refAddr = ref.get("testOnCreate");
+ if (refAddr != null && refAddr.getContent() != null) {
+ ikds.setDefaultTestOnCreate(parseBoolean(refAddr));
+ }
+
+ refAddr = ref.get("testOnBorrow");
+ if (refAddr != null && refAddr.getContent() != null) {
+ ikds.setDefaultTestOnBorrow(parseBoolean(refAddr));
+ }
+
+ refAddr = ref.get("testOnReturn");
+ if (refAddr != null && refAddr.getContent() != null) {
+ ikds.setDefaultTestOnReturn(parseBoolean(refAddr));
+ }
+
+ refAddr = ref.get("testWhileIdle");
+ if (refAddr != null && refAddr.getContent() != null) {
+ ikds.setDefaultTestWhileIdle(parseBoolean(refAddr));
+ }
+
+ refAddr = ref.get("timeBetweenEvictionRunsMillis");
+ if (refAddr != null && refAddr.getContent() != null) {
+ ikds.setDefaultDurationBetweenEvictionRuns(Duration.ofMillis(parseLong(refAddr)));
+ }
+
+ // Connection factory properties
+
+ refAddr = ref.get("validationQuery");
+ if (refAddr != null && refAddr.getContent() != null) {
+ ikds.setValidationQuery(toString(refAddr));
+ }
+
+ refAddr = ref.get("validationQueryTimeout");
+ if (refAddr != null && refAddr.getContent() != null) {
+ ikds.setValidationQueryTimeout(Duration.ofSeconds(parseInt(refAddr)));
+ }
+
+ refAddr = ref.get("rollbackAfterValidation");
+ if (refAddr != null && refAddr.getContent() != null) {
+ ikds.setRollbackAfterValidation(parseBoolean(refAddr));
+ }
+
+ refAddr = ref.get("maxConnLifetimeMillis");
+ if (refAddr != null && refAddr.getContent() != null) {
+ ikds.setMaxConnLifetime(Duration.ofMillis(parseLong(refAddr)));
+ }
+
+ // Connection properties
+
+ refAddr = ref.get("defaultAutoCommit");
+ if (refAddr != null && refAddr.getContent() != null) {
+ ikds.setDefaultAutoCommit(Boolean.valueOf(toString(refAddr)));
+ }
+
+ refAddr = ref.get("defaultTransactionIsolation");
+ if (refAddr != null && refAddr.getContent() != null) {
+ ikds.setDefaultTransactionIsolation(parseInt(refAddr));
+ }
+
+ refAddr = ref.get("defaultReadOnly");
+ if (refAddr != null && refAddr.getContent() != null) {
+ ikds.setDefaultReadOnly(Boolean.valueOf(toString(refAddr)));
+ }
+ }
+
+ String toString(final RefAddr refAddr) {
+ return refAddr.getContent().toString();
+ }
+}
diff --git a/src/main/java/org/apache/commons/dbcp2/datasources/PerUserPoolDataSource.java b/src/main/java/org/apache/commons/dbcp2/datasources/PerUserPoolDataSource.java
index fe5891f2..7056a46f 100644
--- a/src/main/java/org/apache/commons/dbcp2/datasources/PerUserPoolDataSource.java
+++ b/src/main/java/org/apache/commons/dbcp2/datasources/PerUserPoolDataSource.java
@@ -1,1318 +1,1318 @@
-/*
- * 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 java.time.Duration;
-import java.util.HashMap;
-import java.util.Map;
-import java.util.NoSuchElementException;
-
-import javax.naming.NamingException;
-import javax.naming.Reference;
-import javax.naming.StringRefAddr;
-import javax.sql.ConnectionPoolDataSource;
-
-import org.apache.commons.dbcp2.SwallowedExceptionLogger;
-import org.apache.commons.dbcp2.Utils;
-import org.apache.commons.logging.Log;
-import org.apache.commons.logging.LogFactory;
-import org.apache.commons.pool2.ObjectPool;
-import org.apache.commons.pool2.impl.GenericObjectPool;
-
-/**
- * <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. This datasource uses individual pools per user, and some
- * properties can be set specifically for a given user, if the deployment environment can support initialization of
- * mapped properties. So for example, a pool of admin or write-access Connections can be guaranteed a certain number of
- * connections, separate from a maximum set for users with read-only connections.
- * </p>
- *
- * <p>
- * User passwords can be changed without re-initializing the datasource. When a
- * {@code getConnection(userName, password)} request is processed with a password that is different from those used
- * to create connections in the pool associated with {@code userName}, an attempt is made to create a new
- * connection using the supplied password and if this succeeds, the existing pool is cleared and a new pool is created
- * for connections using the new password.
- * </p>
- *
- * @since 2.0
- */
-public class PerUserPoolDataSource extends InstanceKeyDataSource {
-
- private static final long serialVersionUID = 7872747993848065028L;
-
- private static final Log log = LogFactory.getLog(PerUserPoolDataSource.class);
-
- // Per user pool properties
- private Map<String, Boolean> perUserBlockWhenExhausted;
- private Map<String, String> perUserEvictionPolicyClassName;
- private Map<String, Boolean> perUserLifo;
- private Map<String, Integer> perUserMaxIdle;
- private Map<String, Integer> perUserMaxTotal;
- private Map<String, Duration> perUserMaxWaitDuration;
- private Map<String, Duration> perUserMinEvictableIdleDuration;
- private Map<String, Integer> perUserMinIdle;
- private Map<String, Integer> perUserNumTestsPerEvictionRun;
- private Map<String, Duration> perUserSoftMinEvictableIdleDuration;
- private Map<String, Boolean> perUserTestOnCreate;
- private Map<String, Boolean> perUserTestOnBorrow;
- private Map<String, Boolean> perUserTestOnReturn;
- private Map<String, Boolean> perUserTestWhileIdle;
- private Map<String, Duration> perUserDurationBetweenEvictionRuns;
-
- // Per user connection properties
- private Map<String, Boolean> perUserDefaultAutoCommit;
- private Map<String, Integer> perUserDefaultTransactionIsolation;
- private Map<String, Boolean> perUserDefaultReadOnly;
-
- /**
- * Map to keep track of Pools for a given user.
- */
- private transient Map<PoolKey, PooledConnectionManager> managers = new HashMap<>();
-
- /**
- * Default no-arg constructor for Serialization.
- */
- public PerUserPoolDataSource() {
- }
-
- /**
- * Clears pool(s) maintained by this data source.
- *
- * @see org.apache.commons.pool2.ObjectPool#clear()
- * @since 2.3.0
- */
- public void clear() {
- for (final PooledConnectionManager manager : managers.values()) {
- try {
- getCPDSConnectionFactoryPool(manager).clear();
- } catch (final Exception closePoolException) {
- // ignore and try to close others.
- }
- }
- InstanceKeyDataSourceFactory.removeInstance(getInstanceKey());
- }
-
- /**
- * Closes pool(s) maintained by this data source.
- *
- * @see org.apache.commons.pool2.ObjectPool#close()
- */
- @SuppressWarnings("resource")
- @Override
- public void close() {
- for (final PooledConnectionManager manager : managers.values()) {
- Utils.closeQuietly(getCPDSConnectionFactoryPool(manager));
- }
- InstanceKeyDataSourceFactory.removeInstance(getInstanceKey());
- }
-
- /**
- * Converts a map with Long milliseconds values to another map with Duration values.
- */
- private Map<String, Duration> convertMap(final Map<String, Duration> currentMap, final Map<String, Long> longMap) {
- final Map<String, Duration> durationMap = new HashMap<>();
- longMap.forEach((k, v) -> durationMap.put(k, toDurationOrNull(v)));
- if (currentMap == null) {
- return durationMap;
- }
- currentMap.clear();
- currentMap.putAll(durationMap);
- return currentMap;
-
- }
-
- private HashMap<String, Boolean> createMap() {
- // Should there be a default size different than what this ctor provides?
- return new HashMap<>();
- }
-
- @Override
- protected PooledConnectionManager getConnectionManager(final UserPassKey upKey) {
- return managers.get(getPoolKey(upKey.getUserName()));
- }
-
- /**
- * Gets the underlying pool but does NOT allocate it.
- *
- * @param manager A CPDSConnectionFactory.
- * @return the underlying pool.
- */
- private ObjectPool<PooledConnectionAndInfo> getCPDSConnectionFactoryPool(final PooledConnectionManager manager) {
- return ((CPDSConnectionFactory) manager).getPool();
- }
-
- /**
- * Gets the number of active connections in the default pool.
- *
- * @return The number of active connections in the default pool.
- */
- public int getNumActive() {
- return getNumActive(null);
- }
-
- /**
- * Gets the number of active connections in the pool for a given user.
- *
- * @param userName
- * The user name key.
- * @return The user specific value.
- */
- @SuppressWarnings("resource")
- public int getNumActive(final String userName) {
- final ObjectPool<PooledConnectionAndInfo> pool = getPool(getPoolKey(userName));
- return pool == null ? 0 : pool.getNumActive();
- }
-
- /**
- * Gets the number of idle connections in the default pool.
- *
- * @return The number of idle connections in the default pool.
- */
- public int getNumIdle() {
- return getNumIdle(null);
- }
-
- /**
- * Gets the number of idle connections in the pool for a given user.
- *
- * @param userName
- * The user name key.
- * @return The user specific value.
- */
- @SuppressWarnings("resource")
- public int getNumIdle(final String userName) {
- final ObjectPool<PooledConnectionAndInfo> pool = getPool(getPoolKey(userName));
- return pool == null ? 0 : pool.getNumIdle();
- }
-
- /**
- * Gets the user specific value for {@link GenericObjectPool#getBlockWhenExhausted()} for the specified user's pool
- * or the default if no user specific value is defined.
- *
- * @param userName
- * The user name key.
- * @return The user specific value.
- */
- public boolean getPerUserBlockWhenExhausted(final String userName) {
- Boolean value = null;
- if (perUserBlockWhenExhausted != null) {
- value = perUserBlockWhenExhausted.get(userName);
- }
- if (value == null) {
- return getDefaultBlockWhenExhausted();
- }
- return value;
- }
-
- /**
- * Gets the user specific default value for {@link Connection#setAutoCommit(boolean)} for the specified user's pool.
- *
- * @param userName
- * The user name key.
- * @return The user specific value.
- */
- public Boolean getPerUserDefaultAutoCommit(final String userName) {
- Boolean value = null;
- if (perUserDefaultAutoCommit != null) {
- value = perUserDefaultAutoCommit.get(userName);
- }
- return value;
- }
-
- /**
- * Gets the user specific default value for {@link Connection#setReadOnly(boolean)} for the specified user's pool.
- *
- * @param userName
- * The user name key.
- * @return The user specific value.
- */
- public Boolean getPerUserDefaultReadOnly(final String userName) {
- Boolean value = null;
- if (perUserDefaultReadOnly != null) {
- value = perUserDefaultReadOnly.get(userName);
- }
- return value;
- }
-
- /**
- * Gets the user specific default value for {@link Connection#setTransactionIsolation(int)} for the specified user's
- * pool.
- *
- * @param userName
- * The user name key.
- * @return The user specific value.
- */
- public Integer getPerUserDefaultTransactionIsolation(final String userName) {
- Integer value = null;
- if (perUserDefaultTransactionIsolation != null) {
- value = perUserDefaultTransactionIsolation.get(userName);
- }
- return value;
- }
-
- /**
- * Gets the user specific value for {@link GenericObjectPool#getDurationBetweenEvictionRuns()} for the specified
- * user's pool or the default if no user specific value is defined.
- *
- * @param userName
- * The user name key.
- * @return The user specific value.
- * @since 2.10.0
- */
- public Duration getPerUserDurationBetweenEvictionRuns(final String userName) {
- Duration value = null;
- if (perUserDurationBetweenEvictionRuns != null) {
- value = perUserDurationBetweenEvictionRuns.get(userName);
- }
- if (value == null) {
- return getDefaultDurationBetweenEvictionRuns();
- }
- return value;
- }
-
- /**
- * Gets the user specific value for {@link GenericObjectPool#getEvictionPolicyClassName()} for the specified user's
- * pool or the default if no user specific value is defined.
- *
- * @param userName
- * The user name key.
- * @return The user specific value.
- */
- public String getPerUserEvictionPolicyClassName(final String userName) {
- String value = null;
- if (perUserEvictionPolicyClassName != null) {
- value = perUserEvictionPolicyClassName.get(userName);
- }
- if (value == null) {
- return getDefaultEvictionPolicyClassName();
- }
- return value;
- }
-
- /**
- * Gets the user specific value for {@link GenericObjectPool#getLifo()} for the specified user's pool or the default
- * if no user specific value is defined.
- *
- * @param userName
- * The user name key.
- * @return The user specific value.
- */
- public boolean getPerUserLifo(final String userName) {
- Boolean value = null;
- if (perUserLifo != null) {
- value = perUserLifo.get(userName);
- }
- if (value == null) {
- return getDefaultLifo();
- }
- return value;
- }
-
- /**
- * Gets the user specific value for {@link GenericObjectPool#getMaxIdle()} for the specified user's pool or the
- * default if no user specific value is defined.
- *
- * @param userName
- * The user name key.
- * @return The user specific value.
- */
- public int getPerUserMaxIdle(final String userName) {
- Integer value = null;
- if (perUserMaxIdle != null) {
- value = perUserMaxIdle.get(userName);
- }
- if (value == null) {
- return getDefaultMaxIdle();
- }
- return value;
- }
-
- /**
- * Gets the user specific value for {@link GenericObjectPool#getMaxTotal()} for the specified user's pool or the
- * default if no user specific value is defined.
- *
- * @param userName
- * The user name key.
- * @return The user specific value.
- */
- public int getPerUserMaxTotal(final String userName) {
- Integer value = null;
- if (perUserMaxTotal != null) {
- value = perUserMaxTotal.get(userName);
- }
- if (value == null) {
- return getDefaultMaxTotal();
- }
- return value;
- }
-
- /**
- * Gets the user specific value for {@link GenericObjectPool#getMaxWaitDuration()} for the specified user's pool or
- * the default if no user specific value is defined.
- *
- * @param userName
- * The user name key.
- * @return The user specific value.
- * @since 2.10.0
- */
- public Duration getPerUserMaxWaitDuration(final String userName) {
- Duration value = null;
- if (perUserMaxWaitDuration != null) {
- value = perUserMaxWaitDuration.get(userName);
- }
- if (value == null) {
- return getDefaultMaxWait();
- }
- return value;
- }
-
- /**
- * Gets the user specific value for {@link GenericObjectPool#getMaxWaitDuration()} for the specified user's pool or
- * the default if no user specific value is defined.
- *
- * @param userName
- * The user name key.
- * @return The user specific value.
- * @deprecated Use {@link #getPerUserMaxWaitDuration}.
- */
- @Deprecated
- public long getPerUserMaxWaitMillis(final String userName) {
- return getPerUserMaxWaitDuration(userName).toMillis();
- }
-
- /**
- * Gets the user specific value for {@link GenericObjectPool#getMinEvictableIdleDuration()} for the specified
- * user's pool or the default if no user specific value is defined.
- *
- * @param userName
- * The user name key.
- * @return The user specific value, never null.
- * @since 2.10.0
- */
- public Duration getPerUserMinEvictableIdleDuration(final String userName) {
- Duration value = null;
- if (perUserMinEvictableIdleDuration != null) {
- value = perUserMinEvictableIdleDuration.get(userName);
- }
- if (value == null) {
- return getDefaultMinEvictableIdleDuration();
- }
- return value;
- }
-
- /**
- * Gets the user specific value for {@link GenericObjectPool#getMinEvictableIdleDuration()} for the specified
- * user's pool or the default if no user specific value is defined.
- *
- * @param userName
- * The user name key.
- * @return The user specific value.
- * @deprecated Use {@link #getPerUserMinEvictableIdleDuration(String)}.
- */
- @Deprecated
- public long getPerUserMinEvictableIdleTimeMillis(final String userName) {
- return getPerUserMinEvictableIdleDuration(userName).toMillis();
- }
-
- /**
- * Gets the user specific value for {@link GenericObjectPool#getMinIdle()} for the specified user's pool or the
- * default if no user specific value is defined.
- *
- * @param userName
- * The user name key.
- * @return The user specific value.
- */
- public int getPerUserMinIdle(final String userName) {
- Integer value = null;
- if (perUserMinIdle != null) {
- value = perUserMinIdle.get(userName);
- }
- if (value == null) {
- return getDefaultMinIdle();
- }
- return value;
- }
-
- /**
- * Gets the user specific value for {@link GenericObjectPool#getNumTestsPerEvictionRun()} for the specified user's
- * pool or the default if no user specific value is defined.
- *
- * @param userName
- * The user name key.
- * @return The user specific value.
- */
- public int getPerUserNumTestsPerEvictionRun(final String userName) {
- Integer value = null;
- if (perUserNumTestsPerEvictionRun != null) {
- value = perUserNumTestsPerEvictionRun.get(userName);
- }
- if (value == null) {
- return getDefaultNumTestsPerEvictionRun();
- }
- return value;
- }
-
- /**
- * Gets the user specific value for {@link GenericObjectPool#getSoftMinEvictableIdleDuration()} for the specified
- * user's pool or the default if no user specific value is defined.
- *
- * @param userName
- * The user name key.
- * @return The user specific value.
- * @since 2.10.0
- */
- public Duration getPerUserSoftMinEvictableIdleDuration(final String userName) {
- Duration value = null;
- if (perUserSoftMinEvictableIdleDuration != null) {
- value = perUserSoftMinEvictableIdleDuration.get(userName);
- }
- if (value == null) {
- return getDefaultSoftMinEvictableIdleDuration();
- }
- return value;
- }
... 3992 lines suppressed ...