| /* |
| * 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.util.NoSuchElementException; |
| 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; |
| |
| /** |
| * <p> |
| * The base class for <code>SharedPoolDataSource</code> and <code>PerUserPoolDataSource</code>. 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</code> 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</code> 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</code> 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</code>'s based on this class with JDBC driver implementations that do not |
| * supply a <code>ConnectionPoolDataSource</code>, 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 in seconds */ |
| private int loginTimeout; |
| |
| /** 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 long defaultMaxWaitMillis = BaseObjectPoolConfig.DEFAULT_MAX_WAIT_MILLIS; |
| private long defaultMinEvictableIdleTimeMillis = BaseObjectPoolConfig.DEFAULT_MIN_EVICTABLE_IDLE_TIME_MILLIS; |
| private int defaultMinIdle = GenericKeyedObjectPoolConfig.DEFAULT_MIN_IDLE_PER_KEY; |
| private int defaultNumTestsPerEvictionRun = BaseObjectPoolConfig.DEFAULT_NUM_TESTS_PER_EVICTION_RUN; |
| private long defaultSoftMinEvictableIdleTimeMillis = BaseObjectPoolConfig.DEFAULT_SOFT_MIN_EVICTABLE_IDLE_TIME_MILLIS; |
| 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 long defaultTimeBetweenEvictionRunsMillis = BaseObjectPoolConfig.DEFAULT_TIME_BETWEEN_EVICTION_RUNS_MILLIS; |
| |
| // Connection factory properties |
| private String validationQuery; |
| private int validationQueryTimeoutSeconds = -1; |
| private boolean rollbackAfterValidation; |
| private long maxConnLifetimeMillis = -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. |
| */ |
| 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; |
| |
| protected abstract PooledConnectionManager getConnectionManager(UserPassKey upkey); |
| |
| /* JDBC_4_ANT_KEY_BEGIN */ |
| @Override |
| public boolean isWrapperFor(final Class<?> iface) throws SQLException { |
| return false; |
| } |
| |
| @Override |
| public <T> T unwrap(final Class<T> iface) throws SQLException { |
| throw new SQLException("InstanceKeyDataSource is not a wrapper."); |
| } |
| /* JDBC_4_ANT_KEY_END */ |
| |
| @Override |
| public Logger getParentLogger() throws SQLFeatureNotSupportedException { |
| throw new SQLFeatureNotSupportedException(); |
| } |
| |
| // ------------------------------------------------------------------- |
| // Properties |
| |
| /** |
| * 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; |
| } |
| |
| /** |
| * 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; |
| } |
| |
| /** |
| * 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; |
| } |
| |
| /** |
| * 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; |
| } |
| |
| /** |
| * 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; |
| } |
| |
| /** |
| * 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; |
| } |
| |
| /** |
| * 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; |
| } |
| |
| /** |
| * 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; |
| } |
| |
| /** |
| * 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; |
| } |
| |
| /** |
| * 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; |
| } |
| |
| /** |
| * Gets the default value for {@link GenericKeyedObjectPoolConfig#getMaxWaitMillis()} for each per user pool. |
| * |
| * @return The default value for {@link GenericKeyedObjectPoolConfig#getMaxWaitMillis()} for each per user pool. |
| */ |
| public long getDefaultMaxWaitMillis() { |
| return this.defaultMaxWaitMillis; |
| } |
| |
| /** |
| * 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. |
| */ |
| public void setDefaultMaxWaitMillis(final long maxWaitMillis) { |
| assertInitializationAllowed(); |
| this.defaultMaxWaitMillis = maxWaitMillis; |
| } |
| |
| /** |
| * Gets the default value for {@link GenericKeyedObjectPoolConfig#getMinEvictableIdleTimeMillis()} for each per user |
| * pool. |
| * |
| * @return The default value for {@link GenericKeyedObjectPoolConfig#getMinEvictableIdleTimeMillis()} for each per |
| * user pool. |
| */ |
| public long getDefaultMinEvictableIdleTimeMillis() { |
| return this.defaultMinEvictableIdleTimeMillis; |
| } |
| |
| /** |
| * Sets the default value for {@link GenericKeyedObjectPoolConfig#getMinEvictableIdleTimeMillis()} for each per user |
| * pool. |
| * |
| * @param minEvictableIdleTimeMillis |
| * The default value for {@link GenericKeyedObjectPoolConfig#getMinEvictableIdleTimeMillis()} for each |
| * per user pool. |
| */ |
| public void setDefaultMinEvictableIdleTimeMillis(final long minEvictableIdleTimeMillis) { |
| assertInitializationAllowed(); |
| this.defaultMinEvictableIdleTimeMillis = minEvictableIdleTimeMillis; |
| } |
| |
| /** |
| * 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; |
| } |
| |
| /** |
| * 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; |
| } |
| |
| /** |
| * 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; |
| } |
| |
| /** |
| * 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; |
| } |
| |
| /** |
| * 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. |
| */ |
| public long getDefaultSoftMinEvictableIdleTimeMillis() { |
| return this.defaultSoftMinEvictableIdleTimeMillis; |
| } |
| |
| /** |
| * 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. |
| */ |
| public void setDefaultSoftMinEvictableIdleTimeMillis(final long softMinEvictableIdleTimeMillis) { |
| assertInitializationAllowed(); |
| this.defaultSoftMinEvictableIdleTimeMillis = softMinEvictableIdleTimeMillis; |
| } |
| |
| /** |
| * 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; |
| } |
| |
| /** |
| * 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; |
| } |
| |
| /** |
| * 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; |
| } |
| |
| /** |
| * 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; |
| } |
| |
| /** |
| * 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; |
| } |
| |
| /** |
| * 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; |
| } |
| |
| /** |
| * 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; |
| } |
| |
| /** |
| * 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; |
| } |
| |
| /** |
| * Gets the default value for {@link org.apache.commons.pool2.impl.GenericObjectPool |
| * GenericObjectPool#getTimeBetweenEvictionRunsMillis ()} for each per user pool. |
| * |
| * @return The default value for {@link org.apache.commons.pool2.impl.GenericObjectPool |
| * GenericObjectPool#getTimeBetweenEvictionRunsMillis ()} for each per user pool. |
| */ |
| public long getDefaultTimeBetweenEvictionRunsMillis() { |
| return this.defaultTimeBetweenEvictionRunsMillis; |
| } |
| |
| /** |
| * Sets the default value for {@link org.apache.commons.pool2.impl.GenericObjectPool |
| * GenericObjectPool#getTimeBetweenEvictionRunsMillis ()} for each per user pool. |
| * |
| * @param timeBetweenEvictionRunsMillis |
| * The default value for {@link org.apache.commons.pool2.impl.GenericObjectPool |
| * GenericObjectPool#getTimeBetweenEvictionRunsMillis ()} for each per user pool. |
| */ |
| public void setDefaultTimeBetweenEvictionRunsMillis(final long timeBetweenEvictionRunsMillis) { |
| assertInitializationAllowed(); |
| this.defaultTimeBetweenEvictionRunsMillis = timeBetweenEvictionRunsMillis; |
| } |
| |
| /** |
| * Gets the value of connectionPoolDataSource. This method will return null, if the backing datasource is being |
| * accessed via jndi. |
| * |
| * @return value of connectionPoolDataSource. |
| */ |
| public ConnectionPoolDataSource getConnectionPoolDataSource() { |
| return dataSource; |
| } |
| |
| /** |
| * Sets the backend ConnectionPoolDataSource. This property should not be set if using jndi to access the |
| * datasource. |
| * |
| * @param v |
| * Value to assign to connectionPoolDataSource. |
| */ |
| public void setConnectionPoolDataSource(final ConnectionPoolDataSource v) { |
| assertInitializationAllowed(); |
| if (dataSourceName != null) { |
| throw new IllegalStateException("Cannot set the DataSource, if JNDI is used."); |
| } |
| if (dataSource != null) { |
| throw new IllegalStateException("The CPDS has already been set. It cannot be altered."); |
| } |
| dataSource = v; |
| instanceKey = InstanceKeyDataSourceFactory.registerNewInstance(this); |
| } |
| |
| /** |
| * Gets the name of the ConnectionPoolDataSource which backs this pool. This name is used to look up the datasource |
| * from a jndi service provider. |
| * |
| * @return value of dataSourceName. |
| */ |
| public String getDataSourceName() { |
| return dataSourceName; |
| } |
| |
| /** |
| * Sets the name of the ConnectionPoolDataSource which backs this pool. This name is used to look up the datasource |
| * from a jndi service provider. |
| * |
| * @param v |
| * Value to assign to dataSourceName. |
| */ |
| public void setDataSourceName(final String v) { |
| assertInitializationAllowed(); |
| if (dataSource != null) { |
| throw new IllegalStateException("Cannot set the JNDI name for the DataSource, if already " |
| + "set using setConnectionPoolDataSource."); |
| } |
| if (dataSourceName != null) { |
| throw new IllegalStateException("The DataSourceName has already been set. " + "It cannot be altered."); |
| } |
| this.dataSourceName = v; |
| instanceKey = InstanceKeyDataSourceFactory.registerNewInstance(this); |
| } |
| |
| /** |
| * 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</code> which |
| * will use the default value for the drive. |
| * |
| * @return value of defaultAutoCommit. |
| */ |
| public Boolean isDefaultAutoCommit() { |
| return defaultAutoCommit; |
| } |
| |
| /** |
| * 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</code> which |
| * will use the default value for the drive. |
| * |
| * @param v |
| * Value to assign to defaultAutoCommit. |
| */ |
| public void setDefaultAutoCommit(final Boolean v) { |
| assertInitializationAllowed(); |
| this.defaultAutoCommit = v; |
| } |
| |
| /** |
| * 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</code> which |
| * will use the default value for the drive. |
| * |
| * @return value of defaultReadOnly. |
| */ |
| public Boolean isDefaultReadOnly() { |
| return defaultReadOnly; |
| } |
| |
| /** |
| * 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</code> which |
| * will use the default value for the drive. |
| * |
| * @param v |
| * Value to assign to defaultReadOnly. |
| */ |
| public void setDefaultReadOnly(final Boolean v) { |
| assertInitializationAllowed(); |
| this.defaultReadOnly = v; |
| } |
| |
| /** |
| * 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; |
| } |
| |
| /** |
| * 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 v |
| * Value to assign to defaultTransactionIsolation |
| */ |
| public void setDefaultTransactionIsolation(final int v) { |
| assertInitializationAllowed(); |
| switch (v) { |
| 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 = v; |
| } |
| |
| /** |
| * 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; |
| } |
| |
| /** |
| * 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 v |
| * Value to assign to description. |
| */ |
| public void setDescription(final String v) { |
| this.description = v; |
| } |
| |
| 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 backend 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; |
| } |
| |
| /** |
| * Sets the value of the given JNDI environment property to be used when instantiating a JNDI InitialContext. This |
| * InitialContext is used to locate the backend 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 JNDI environment to be used when instantiating a JNDI InitialContext. This InitialContext is used to |
| * locate the backend 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); |
| } |
| |
| /** |
| * Gets the value of loginTimeout. |
| * |
| * @return value of loginTimeout. |
| */ |
| @Override |
| public int getLoginTimeout() { |
| return loginTimeout; |
| } |
| |
| /** |
| * Sets the value of loginTimeout. |
| * |
| * @param v |
| * Value to assign to loginTimeout. |
| */ |
| @Override |
| public void setLoginTimeout(final int v) { |
| this.loginTimeout = v; |
| } |
| |
| /** |
| * 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; |
| } |
| |
| /** |
| * Sets the value of logWriter. |
| * |
| * @param v |
| * Value to assign to logWriter. |
| */ |
| @Override |
| public void setLogWriter(final PrintWriter v) { |
| this.logWriter = v; |
| } |
| |
| /** |
| * 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; |
| } |
| |
| /** |
| * 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; |
| } |
| |
| /** |
| * Returns the timeout in seconds before the validation query fails. |
| * |
| * @return The timeout in seconds before the validation query fails. |
| */ |
| public int getValidationQueryTimeout() { |
| return validationQueryTimeoutSeconds; |
| } |
| |
| /** |
| * Sets the timeout in seconds before the validation query fails. |
| * |
| * @param validationQueryTimeoutSeconds |
| * The new timeout in seconds |
| */ |
| public void setValidationQueryTimeout(final int validationQueryTimeoutSeconds) { |
| this.validationQueryTimeoutSeconds = validationQueryTimeoutSeconds; |
| } |
| |
| /** |
| * 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; |
| } |
| |
| /** |
| * 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; |
| } |
| |
| /** |
| * Returns 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. |
| */ |
| public long getMaxConnLifetimeMillis() { |
| return 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. |
| */ |
| public void setMaxConnLifetimeMillis(final long maxConnLifetimeMillis) { |
| this.maxConnLifetimeMillis = maxConnLifetimeMillis; |
| } |
| |
| // ---------------------------------------------------------------------- |
| // Instrumentation Methods |
| |
| // ---------------------------------------------------------------------- |
| // DataSource implementation |
| |
| /** |
| * 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 {@link PooledConnectionAndInfo} instance returned by |
| * <code>getPooledConnectionAndInfo</code> is compared to the <code>password</code> 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</code> instance retrieved with the old password is |
| * destroyed and the <code>getPooledConnectionAndInfo</code> is repeatedly invoked until a |
| * <code>PooledConnectionAndInfo</code> 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 NoSuchElementException e) { |
| closeDueToException(info); |
| throw new SQLException("Cannot borrow connection from pool", e); |
| } catch (final RuntimeException e) { |
| closeDueToException(info); |
| throw e; |
| } catch (final 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 NoSuchElementException e) { |
| closeDueToException(info); |
| throw new SQLException("Cannot borrow connection from pool", e); |
| } catch (final RuntimeException e) { |
| closeDueToException(info); |
| throw e; |
| } catch (final 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 con = info.getPooledConnection().getConnection(); |
| try { |
| setupDefaults(con, userName); |
| con.clearWarnings(); |
| return con; |
| } catch (final SQLException ex) { |
| try { |
| con.close(); |
| } catch (final Exception exc) { |
| getLogWriter().println("ignoring exception during close: " + exc); |
| } |
| throw ex; |
| } |
| } |
| |
| protected abstract PooledConnectionAndInfo getPooledConnectionAndInfo(String userName, String userPassword) |
| throws SQLException; |
| |
| protected abstract void setupDefaults(Connection connection, String userName) throws SQLException; |
| |
| 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()); |
| } |
| } |
| } |
| |
| 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) { |
| cpds = (ConnectionPoolDataSource) ds; |
| } else { |
| throw new SQLException("Illegal configuration: " + "DataSource " + dataSourceName + " (" |
| + ds.getClass().getName() + ")" + " doesn't implement javax.sql.ConnectionPoolDataSource"); |
| } |
| } |
| |
| // 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(", loginTimeout="); |
| builder.append(loginTimeout); |
| builder.append(", logWriter="); |
| builder.append(logWriter); |
| builder.append(", instanceKey="); |
| builder.append(instanceKey); |
| builder.append(", defaultBlockWhenExhausted="); |
| builder.append(defaultBlockWhenExhausted); |
| builder.append(", defaultEvictionPolicyClassName="); |
| builder.append(defaultEvictionPolicyClassName); |
| builder.append(", defaultLifo="); |
| builder.append(defaultLifo); |
| builder.append(", defaultMaxIdle="); |
| builder.append(defaultMaxIdle); |
| builder.append(", defaultMaxTotal="); |
| builder.append(defaultMaxTotal); |
| builder.append(", defaultMaxWaitMillis="); |
| builder.append(defaultMaxWaitMillis); |
| builder.append(", defaultMinEvictableIdleTimeMillis="); |
| builder.append(defaultMinEvictableIdleTimeMillis); |
| builder.append(", defaultMinIdle="); |
| builder.append(defaultMinIdle); |
| builder.append(", defaultNumTestsPerEvictionRun="); |
| builder.append(defaultNumTestsPerEvictionRun); |
| builder.append(", defaultSoftMinEvictableIdleTimeMillis="); |
| builder.append(defaultSoftMinEvictableIdleTimeMillis); |
| builder.append(", defaultTestOnCreate="); |
| builder.append(defaultTestOnCreate); |
| builder.append(", defaultTestOnBorrow="); |
| builder.append(defaultTestOnBorrow); |
| builder.append(", defaultTestOnReturn="); |
| builder.append(defaultTestOnReturn); |
| builder.append(", defaultTestWhileIdle="); |
| builder.append(defaultTestWhileIdle); |
| builder.append(", defaultTimeBetweenEvictionRunsMillis="); |
| builder.append(defaultTimeBetweenEvictionRunsMillis); |
| builder.append(", validationQuery="); |
| builder.append(validationQuery); |
| builder.append(", validationQueryTimeoutSeconds="); |
| builder.append(validationQueryTimeoutSeconds); |
| builder.append(", rollbackAfterValidation="); |
| builder.append(rollbackAfterValidation); |
| builder.append(", maxConnLifetimeMillis="); |
| builder.append(maxConnLifetimeMillis); |
| builder.append(", defaultAutoCommit="); |
| builder.append(defaultAutoCommit); |
| builder.append(", defaultTransactionIsolation="); |
| builder.append(defaultTransactionIsolation); |
| builder.append(", defaultReadOnly="); |
| builder.append(defaultReadOnly); |
| } |
| } |