    001/*
    002 * Licensed to the Apache Software Foundation (ASF) under one or more
    003 * contributor license agreements.  See the NOTICE file distributed with
    004 * this work for additional information regarding copyright ownership.
    005 * The ASF licenses this file to You under the Apache License, Version 2.0
    006 * (the "License"); you may not use this file except in compliance with
    007 * the License.  You may obtain a copy of the License at
    008 *
    009 *      http://www.apache.org/licenses/LICENSE-2.0
    010 *
    011 * Unless required by applicable law or agreed to in writing, software
    012 * distributed under the License is distributed on an "AS IS" BASIS,
    013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    014 * See the License for the specific language governing permissions and
    015 * limitations under the License.
    016 */
    017
    018package org.apache.commons.dbcp2.datasources;
    019
    020import java.io.OutputStreamWriter;
    021import java.io.PrintWriter;
    022import java.io.Serializable;
    023import java.nio.charset.StandardCharsets;
    024import java.sql.Connection;
    025import java.sql.SQLException;
    026import java.sql.SQLFeatureNotSupportedException;
    027import java.util.NoSuchElementException;
    028import java.util.Properties;
    029import java.util.logging.Logger;
    030
    031import javax.naming.Context;
    032import javax.naming.InitialContext;
    033import javax.naming.Referenceable;
    034import javax.sql.ConnectionPoolDataSource;
    035import javax.sql.DataSource;
    036import javax.sql.PooledConnection;
    037
    038import org.apache.commons.pool2.impl.BaseObjectPoolConfig;
    039import org.apache.commons.pool2.impl.GenericKeyedObjectPoolConfig;
    040
    041/**
    042 * <p>
    043 * The base class for <code>SharedPoolDataSource</code> and <code>PerUserPoolDataSource</code>. Many of the
    044 * configuration properties are shared and defined here. This class is declared public in order to allow particular
    045 * usage with commons-beanutils; do not make direct use of it outside of <em>commons-dbcp2</em>.
    046 * </p>
    047 *
    048 * <p>
    049 * A J2EE container will normally provide some method of initializing the <code>DataSource</code> whose attributes are
    050 * presented as bean getters/setters and then deploying it via JNDI. It is then available to an application as a source
    051 * of pooled logical connections to the database. The pool needs a source of physical connections. This source is in the
    052 * form of a <code>ConnectionPoolDataSource</code> that can be specified via the {@link #setDataSourceName(String)} used
    053 * to lookup the source via JNDI.
    054 * </p>
    055 *
    056 * <p>
    057 * Although normally used within a JNDI environment, A DataSource can be instantiated and initialized as any bean. In
    058 * this case the <code>ConnectionPoolDataSource</code> will likely be instantiated in a similar manner. This class
    059 * allows the physical source of connections to be attached directly to this pool using the
    060 * {@link #setConnectionPoolDataSource(ConnectionPoolDataSource)} method.
    061 * </p>
    062 *
    063 * <p>
    064 * The dbcp package contains an adapter, {@link org.apache.commons.dbcp2.cpdsadapter.DriverAdapterCPDS}, that can be
    065 * used to allow the use of <code>DataSource</code>'s based on this class with JDBC driver implementations that do not
    066 * supply a <code>ConnectionPoolDataSource</code>, but still provide a {@link java.sql.Driver} implementation.
    067 * </p>
    068 *
    069 * <p>
    070 * The <a href="package-summary.html">package documentation</a> contains an example using Apache Tomcat and JNDI and it
    071 * also contains a non-JNDI example.
    072 * </p>
    073 *
    074 * @since 2.0
    075 */
    076public abstract class InstanceKeyDataSource implements DataSource, Referenceable, Serializable, AutoCloseable {
    077
    078    private static final long serialVersionUID = -6819270431752240878L;
    079
    080    private static final String GET_CONNECTION_CALLED = "A Connection was already requested from this source, "
    081            + "further initialization is not allowed.";
    082    private static final String BAD_TRANSACTION_ISOLATION = "The requested TransactionIsolation level is invalid.";
    083
    084    /**
    085     * Internal constant to indicate the level is not set.
    086     */
    087    protected static final int UNKNOWN_TRANSACTIONISOLATION = -1;
    088
    089    /** Guards property setters - once true, setters throw IllegalStateException */
    090    private volatile boolean getConnectionCalled;
    091
    092    /** Underlying source of PooledConnections */
    093    private ConnectionPoolDataSource dataSource;
    094
    095    /** DataSource Name used to find the ConnectionPoolDataSource */
    096    private String dataSourceName;
    097
    098    /** Description */
    099    private String description;
    100
    101    /** Environment that may be used to set up a jndi initial context. */
    102    private Properties jndiEnvironment;
    103
    104    /** Login TimeOut in seconds */
    105    private int loginTimeout;
    106
    107    /** Log stream */
    108    private PrintWriter logWriter;
    109
    110    /** Instance key */
    111    private String instanceKey;
    112
    113    // Pool properties
    114    private boolean defaultBlockWhenExhausted = BaseObjectPoolConfig.DEFAULT_BLOCK_WHEN_EXHAUSTED;
    115    private String defaultEvictionPolicyClassName = BaseObjectPoolConfig.DEFAULT_EVICTION_POLICY_CLASS_NAME;
    116    private boolean defaultLifo = BaseObjectPoolConfig.DEFAULT_LIFO;
    117    private int defaultMaxIdle = GenericKeyedObjectPoolConfig.DEFAULT_MAX_IDLE_PER_KEY;
    118    private int defaultMaxTotal = GenericKeyedObjectPoolConfig.DEFAULT_MAX_TOTAL;
    119    private long defaultMaxWaitMillis = BaseObjectPoolConfig.DEFAULT_MAX_WAIT_MILLIS;
    120    private long defaultMinEvictableIdleTimeMillis = BaseObjectPoolConfig.DEFAULT_MIN_EVICTABLE_IDLE_TIME_MILLIS;
    121    private int defaultMinIdle = GenericKeyedObjectPoolConfig.DEFAULT_MIN_IDLE_PER_KEY;
    122    private int defaultNumTestsPerEvictionRun = BaseObjectPoolConfig.DEFAULT_NUM_TESTS_PER_EVICTION_RUN;
    123    private long defaultSoftMinEvictableIdleTimeMillis = BaseObjectPoolConfig.DEFAULT_SOFT_MIN_EVICTABLE_IDLE_TIME_MILLIS;
    124    private boolean defaultTestOnCreate = BaseObjectPoolConfig.DEFAULT_TEST_ON_CREATE;
    125    private boolean defaultTestOnBorrow = BaseObjectPoolConfig.DEFAULT_TEST_ON_BORROW;
    126    private boolean defaultTestOnReturn = BaseObjectPoolConfig.DEFAULT_TEST_ON_RETURN;
    127    private boolean defaultTestWhileIdle = BaseObjectPoolConfig.DEFAULT_TEST_WHILE_IDLE;
    128    private long defaultTimeBetweenEvictionRunsMillis = BaseObjectPoolConfig.DEFAULT_TIME_BETWEEN_EVICTION_RUNS_MILLIS;
    129
    130    // Connection factory properties
    131    private String validationQuery;
    132    private int validationQueryTimeoutSeconds = -1;
    133    private boolean rollbackAfterValidation;
    134    private long maxConnLifetimeMillis = -1;
    135
    136    // Connection properties
    137    private Boolean defaultAutoCommit;
    138    private int defaultTransactionIsolation = UNKNOWN_TRANSACTIONISOLATION;
    139    private Boolean defaultReadOnly;
    140
    141    /**
    142     * Default no-arg constructor for Serialization
    143     */
    144    public InstanceKeyDataSource() {
    145    }
    146
    147    /**
    148     * Throws an IllegalStateException, if a PooledConnection has already been requested.
    149     */
    150    protected void assertInitializationAllowed() throws IllegalStateException {
    151        if (getConnectionCalled) {
    152            throw new IllegalStateException(GET_CONNECTION_CALLED);
    153        }
    154    }
    155
    156    /**
    157     * Closes the connection pool being maintained by this datasource.
    158     */
    159    @Override
    160    public abstract void close() throws Exception;
    161
    162    protected abstract PooledConnectionManager getConnectionManager(UserPassKey upkey);
    163
    164    /* JDBC_4_ANT_KEY_BEGIN */
    165    @Override
    166    public boolean isWrapperFor(final Class<?> iface) throws SQLException {
    167        return false;
    168    }
    169
    170    @Override
    171    public <T> T unwrap(final Class<T> iface) throws SQLException {
    172        throw new SQLException("InstanceKeyDataSource is not a wrapper.");
    173    }
    174    /* JDBC_4_ANT_KEY_END */
    175
    176    @Override
    177    public Logger getParentLogger() throws SQLFeatureNotSupportedException {
    178        throw new SQLFeatureNotSupportedException();
    179    }
    180
    181    // -------------------------------------------------------------------
    182    // Properties
    183
    184    /**
    185     * Gets the default value for {@link GenericKeyedObjectPoolConfig#getBlockWhenExhausted()} for each per user pool.
    186     *
    187     * @return The default value for {@link GenericKeyedObjectPoolConfig#getBlockWhenExhausted()} for each per user
    188     *         pool.
    189     */
    190    public boolean getDefaultBlockWhenExhausted() {
    191        return this.defaultBlockWhenExhausted;
    192    }
    193
    194    /**
    195     * Sets the default value for {@link GenericKeyedObjectPoolConfig#getBlockWhenExhausted()} for each per user pool.
    196     *
    197     * @param blockWhenExhausted
    198     *            The default value for {@link GenericKeyedObjectPoolConfig#getBlockWhenExhausted()} for each per user
    199     *            pool.
    200     */
    201    public void setDefaultBlockWhenExhausted(final boolean blockWhenExhausted) {
    202        assertInitializationAllowed();
    203        this.defaultBlockWhenExhausted = blockWhenExhausted;
    204    }
    205
    206    /**
    207     * Gets the default value for {@link GenericKeyedObjectPoolConfig#getEvictionPolicyClassName()} for each per user
    208     * pool.
    209     *
    210     * @return The default value for {@link GenericKeyedObjectPoolConfig#getEvictionPolicyClassName()} for each per user
    211     *         pool.
    212     */
    213    public String getDefaultEvictionPolicyClassName() {
    214        return this.defaultEvictionPolicyClassName;
    215    }
    216
    217    /**
    218     * Sets the default value for {@link GenericKeyedObjectPoolConfig#getEvictionPolicyClassName()} for each per user
    219     * pool.
    220     *
    221     * @param evictionPolicyClassName
    222     *            The default value for {@link GenericKeyedObjectPoolConfig#getEvictionPolicyClassName()} for each per
    223     *            user pool.
    224     */
    225    public void setDefaultEvictionPolicyClassName(final String evictionPolicyClassName) {
    226        assertInitializationAllowed();
    227        this.defaultEvictionPolicyClassName = evictionPolicyClassName;
    228    }
    229
    230    /**
    231     * Gets the default value for {@link GenericKeyedObjectPoolConfig#getLifo()} for each per user pool.
    232     *
    233     * @return The default value for {@link GenericKeyedObjectPoolConfig#getLifo()} for each per user pool.
    234     */
    235    public boolean getDefaultLifo() {
    236        return this.defaultLifo;
    237    }
    238
    239    /**
    240     * Sets the default value for {@link GenericKeyedObjectPoolConfig#getLifo()} for each per user pool.
    241     *
    242     * @param lifo
    243     *            The default value for {@link GenericKeyedObjectPoolConfig#getLifo()} for each per user pool.
    244     */
    245    public void setDefaultLifo(final boolean lifo) {
    246        assertInitializationAllowed();
    247        this.defaultLifo = lifo;
    248    }
    249
    250    /**
    251     * Gets the default value for {@link GenericKeyedObjectPoolConfig#getMaxIdlePerKey()} for each per user pool.
    252     *
    253     * @return The default value for {@link GenericKeyedObjectPoolConfig#getMaxIdlePerKey()} for each per user pool.
    254     */
    255    public int getDefaultMaxIdle() {
    256        return this.defaultMaxIdle;
    257    }
    258
    259    /**
    260     * Sets the default value for {@link GenericKeyedObjectPoolConfig#getMaxIdlePerKey()} for each per user pool.
    261     *
    262     * @param maxIdle
    263     *            The default value for {@link GenericKeyedObjectPoolConfig#getMaxIdlePerKey()} for each per user pool.
    264     */
    265    public void setDefaultMaxIdle(final int maxIdle) {
    266        assertInitializationAllowed();
    267        this.defaultMaxIdle = maxIdle;
    268    }
    269
    270    /**
    271     * Gets the default value for {@link GenericKeyedObjectPoolConfig#getMaxTotalPerKey()} for each per user pool.
    272     *
    273     * @return The default value for {@link GenericKeyedObjectPoolConfig#getMaxTotalPerKey()} for each per user pool.
    274     */
    275    public int getDefaultMaxTotal() {
    276        return this.defaultMaxTotal;
    277    }
    278
    279    /**
    280     * Sets the default value for {@link GenericKeyedObjectPoolConfig#getMaxTotalPerKey()} for each per user pool.
    281     *
    282     * @param maxTotal
    283     *            The default value for {@link GenericKeyedObjectPoolConfig#getMaxTotalPerKey()} for each per user pool.
    284     */
    285    public void setDefaultMaxTotal(final int maxTotal) {
    286        assertInitializationAllowed();
    287        this.defaultMaxTotal = maxTotal;
    288    }
    289
    290    /**
    291     * Gets the default value for {@link GenericKeyedObjectPoolConfig#getMaxWaitMillis()} for each per user pool.
    292     *
    293     * @return The default value for {@link GenericKeyedObjectPoolConfig#getMaxWaitMillis()} for each per user pool.
    294     */
    295    public long getDefaultMaxWaitMillis() {
    296        return this.defaultMaxWaitMillis;
    297    }
    298
    299    /**
    300     * Sets the default value for {@link GenericKeyedObjectPoolConfig#getMaxWaitMillis()} for each per user pool.
    301     *
    302     * @param maxWaitMillis
    303     *            The default value for {@link GenericKeyedObjectPoolConfig#getMaxWaitMillis()} for each per user pool.
    304     */
    305    public void setDefaultMaxWaitMillis(final long maxWaitMillis) {
    306        assertInitializationAllowed();
    307        this.defaultMaxWaitMillis = maxWaitMillis;
    308    }
    309
    310    /**
    311     * Gets the default value for {@link GenericKeyedObjectPoolConfig#getMinEvictableIdleTimeMillis()} for each per user
    312     * pool.
    313     *
    314     * @return The default value for {@link GenericKeyedObjectPoolConfig#getMinEvictableIdleTimeMillis()} for each per
    315     *         user pool.
    316     */
    317    public long getDefaultMinEvictableIdleTimeMillis() {
    318        return this.defaultMinEvictableIdleTimeMillis;
    319    }
    320
    321    /**
    322     * Sets the default value for {@link GenericKeyedObjectPoolConfig#getMinEvictableIdleTimeMillis()} for each per user
    323     * pool.
    324     *
    325     * @param minEvictableIdleTimeMillis
    326     *            The default value for {@link GenericKeyedObjectPoolConfig#getMinEvictableIdleTimeMillis()} for each
    327     *            per user pool.
    328     */
    329    public void setDefaultMinEvictableIdleTimeMillis(final long minEvictableIdleTimeMillis) {
    330        assertInitializationAllowed();
    331        this.defaultMinEvictableIdleTimeMillis = minEvictableIdleTimeMillis;
    332    }
    333
    334    /**
    335     * Gets the default value for {@link GenericKeyedObjectPoolConfig#getMinIdlePerKey()} for each per user pool.
    336     *
    337     * @return The default value for {@link GenericKeyedObjectPoolConfig#getMinIdlePerKey()} for each per user pool.
    338     */
    339    public int getDefaultMinIdle() {
    340        return this.defaultMinIdle;
    341    }
    342
    343    /**
    344     * Sets the default value for {@link GenericKeyedObjectPoolConfig#getMinIdlePerKey()} for each per user pool.
    345     *
    346     * @param minIdle
    347     *            The default value for {@link GenericKeyedObjectPoolConfig#getMinIdlePerKey()} for each per user pool.
    348     */
    349    public void setDefaultMinIdle(final int minIdle) {
    350        assertInitializationAllowed();
    351        this.defaultMinIdle = minIdle;
    352    }
    353
    354    /**
    355     * Gets the default value for {@link GenericKeyedObjectPoolConfig#getNumTestsPerEvictionRun()} for each per user
    356     * pool.
    357     *
    358     * @return The default value for {@link GenericKeyedObjectPoolConfig#getNumTestsPerEvictionRun()} for each per user
    359     *         pool.
    360     */
    361    public int getDefaultNumTestsPerEvictionRun() {
    362        return this.defaultNumTestsPerEvictionRun;
    363    }
    364
    365    /**
    366     * Sets the default value for {@link GenericKeyedObjectPoolConfig#getNumTestsPerEvictionRun()} for each per user
    367     * pool.
    368     *
    369     * @param numTestsPerEvictionRun
    370     *            The default value for {@link GenericKeyedObjectPoolConfig#getNumTestsPerEvictionRun()} for each per
    371     *            user pool.
    372     */
    373    public void setDefaultNumTestsPerEvictionRun(final int numTestsPerEvictionRun) {
    374        assertInitializationAllowed();
    375        this.defaultNumTestsPerEvictionRun = numTestsPerEvictionRun;
    376    }
    377
    378    /**
    379     * Gets the default value for {@link org.apache.commons.pool2.impl.GenericObjectPool
    380     * GenericObjectPool#getSoftMinEvictableIdleTimeMillis()} for each per user pool.
    381     *
    382     * @return The default value for {@link org.apache.commons.pool2.impl.GenericObjectPool
    383     *         GenericObjectPool#getSoftMinEvictableIdleTimeMillis()} for each per user pool.
    384     */
    385    public long getDefaultSoftMinEvictableIdleTimeMillis() {
    386        return this.defaultSoftMinEvictableIdleTimeMillis;
    387    }
    388
    389    /**
    390     * Sets the default value for {@link org.apache.commons.pool2.impl.GenericObjectPool
    391     * GenericObjectPool#getSoftMinEvictableIdleTimeMillis()} for each per user pool.
    392     *
    393     * @param softMinEvictableIdleTimeMillis
    394     *            The default value for {@link org.apache.commons.pool2.impl.GenericObjectPool
    395     *            GenericObjectPool#getSoftMinEvictableIdleTimeMillis()} for each per user pool.
    396     */
    397    public void setDefaultSoftMinEvictableIdleTimeMillis(final long softMinEvictableIdleTimeMillis) {
    398        assertInitializationAllowed();
    399        this.defaultSoftMinEvictableIdleTimeMillis = softMinEvictableIdleTimeMillis;
    400    }
    401
    402    /**
    403     * Gets the default value for {@link org.apache.commons.pool2.impl.GenericObjectPool
    404     * GenericObjectPool#getTestOnCreate()} for each per user pool.
    405     *
    406     * @return The default value for {@link org.apache.commons.pool2.impl.GenericObjectPool
    407     *         GenericObjectPool#getTestOnCreate()} for each per user pool.
    408     */
    409    public boolean getDefaultTestOnCreate() {
    410        return this.defaultTestOnCreate;
    411    }
    412
    413    /**
    414     * Sets the default value for {@link org.apache.commons.pool2.impl.GenericObjectPool
    415     * GenericObjectPool#getTestOnCreate()} for each per user pool.
    416     *
    417     * @param testOnCreate
    418     *            The default value for {@link org.apache.commons.pool2.impl.GenericObjectPool
    419     *            GenericObjectPool#getTestOnCreate()} for each per user pool.
    420     */
    421    public void setDefaultTestOnCreate(final boolean testOnCreate) {
    422        assertInitializationAllowed();
    423        this.defaultTestOnCreate = testOnCreate;
    424    }
    425
    426    /**
    427     * Gets the default value for {@link org.apache.commons.pool2.impl.GenericObjectPool
    428     * GenericObjectPool#getTestOnBorrow()} for each per user pool.
    429     *
    430     * @return The default value for {@link org.apache.commons.pool2.impl.GenericObjectPool
    431     *         GenericObjectPool#getTestOnBorrow()} for each per user pool.
    432     */
    433    public boolean getDefaultTestOnBorrow() {
    434        return this.defaultTestOnBorrow;
    435    }
    436
    437    /**
    438     * Sets the default value for {@link org.apache.commons.pool2.impl.GenericObjectPool
    439     * GenericObjectPool#getTestOnBorrow()} for each per user pool.
    440     *
    441     * @param testOnBorrow
    442     *            The default value for {@link org.apache.commons.pool2.impl.GenericObjectPool
    443     *            GenericObjectPool#getTestOnBorrow()} for each per user pool.
    444     */
    445    public void setDefaultTestOnBorrow(final boolean testOnBorrow) {
    446        assertInitializationAllowed();
    447        this.defaultTestOnBorrow = testOnBorrow;
    448    }
    449
    450    /**
    451     * Gets the default value for {@link org.apache.commons.pool2.impl.GenericObjectPool
    452     * GenericObjectPool#getTestOnReturn()} for each per user pool.
    453     *
    454     * @return The default value for {@link org.apache.commons.pool2.impl.GenericObjectPool
    455     *         GenericObjectPool#getTestOnReturn()} for each per user pool.
    456     */
    457    public boolean getDefaultTestOnReturn() {
    458        return this.defaultTestOnReturn;
    459    }
    460
    461    /**
    462     * Sets the default value for {@link org.apache.commons.pool2.impl.GenericObjectPool
    463     * GenericObjectPool#getTestOnReturn()} for each per user pool.
    464     *
    465     * @param testOnReturn
    466     *            The default value for {@link org.apache.commons.pool2.impl.GenericObjectPool
    467     *            GenericObjectPool#getTestOnReturn()} for each per user pool.
    468     */
    469    public void setDefaultTestOnReturn(final boolean testOnReturn) {
    470        assertInitializationAllowed();
    471        this.defaultTestOnReturn = testOnReturn;
    472    }
    473
    474    /**
    475     * Gets the default value for {@link org.apache.commons.pool2.impl.GenericObjectPool
    476     * GenericObjectPool#getTestWhileIdle()} for each per user pool.
    477     *
    478     * @return The default value for {@link org.apache.commons.pool2.impl.GenericObjectPool
    479     *         GenericObjectPool#getTestWhileIdle()} for each per user pool.
    480     */
    481    public boolean getDefaultTestWhileIdle() {
    482        return this.defaultTestWhileIdle;
    483    }
    484
    485    /**
    486     * Sets the default value for {@link org.apache.commons.pool2.impl.GenericObjectPool
    487     * GenericObjectPool#getTestWhileIdle()} for each per user pool.
    488     *
    489     * @param testWhileIdle
    490     *            The default value for {@link org.apache.commons.pool2.impl.GenericObjectPool
    491     *            GenericObjectPool#getTestWhileIdle()} for each per user pool.
    492     */
    493    public void setDefaultTestWhileIdle(final boolean testWhileIdle) {
    494        assertInitializationAllowed();
    495        this.defaultTestWhileIdle = testWhileIdle;
    496    }
    497
    498    /**
    499     * Gets the default value for {@link org.apache.commons.pool2.impl.GenericObjectPool
    500     * GenericObjectPool#getTimeBetweenEvictionRunsMillis ()} for each per user pool.
    501     *
    502     * @return The default value for {@link org.apache.commons.pool2.impl.GenericObjectPool
    503     *         GenericObjectPool#getTimeBetweenEvictionRunsMillis ()} for each per user pool.
    504     */
    505    public long getDefaultTimeBetweenEvictionRunsMillis() {
    506        return this.defaultTimeBetweenEvictionRunsMillis;
    507    }
    508
    509    /**
    510     * Sets the default value for {@link org.apache.commons.pool2.impl.GenericObjectPool
    511     * GenericObjectPool#getTimeBetweenEvictionRunsMillis ()} for each per user pool.
    512     *
    513     * @param timeBetweenEvictionRunsMillis
    514     *            The default value for {@link org.apache.commons.pool2.impl.GenericObjectPool
    515     *            GenericObjectPool#getTimeBetweenEvictionRunsMillis ()} for each per user pool.
    516     */
    517    public void setDefaultTimeBetweenEvictionRunsMillis(final long timeBetweenEvictionRunsMillis) {
    518        assertInitializationAllowed();
    519        this.defaultTimeBetweenEvictionRunsMillis = timeBetweenEvictionRunsMillis;
    520    }
    521
    522    /**
    523     * Gets the value of connectionPoolDataSource. This method will return null, if the backing datasource is being
    524     * accessed via jndi.
    525     *
    526     * @return value of connectionPoolDataSource.
    527     */
    528    public ConnectionPoolDataSource getConnectionPoolDataSource() {
    529        return dataSource;
    530    }
    531
    532    /**
    533     * Sets the backend ConnectionPoolDataSource. This property should not be set if using jndi to access the
    534     * datasource.
    535     *
    536     * @param v
    537     *            Value to assign to connectionPoolDataSource.
    538     */
    539    public void setConnectionPoolDataSource(final ConnectionPoolDataSource v) {
    540        assertInitializationAllowed();
    541        if (dataSourceName != null) {
    542            throw new IllegalStateException("Cannot set the DataSource, if JNDI is used.");
    543        }
    544        if (dataSource != null) {
    545            throw new IllegalStateException("The CPDS has already been set. It cannot be altered.");
    546        }
    547        dataSource = v;
    548        instanceKey = InstanceKeyDataSourceFactory.registerNewInstance(this);
    549    }
    550
    551    /**
    552     * Gets the name of the ConnectionPoolDataSource which backs this pool. This name is used to look up the datasource
    553     * from a jndi service provider.
    554     *
    555     * @return value of dataSourceName.
    556     */
    557    public String getDataSourceName() {
    558        return dataSourceName;
    559    }
    560
    561    /**
    562     * Sets the name of the ConnectionPoolDataSource which backs this pool. This name is used to look up the datasource
    563     * from a jndi service provider.
    564     *
    565     * @param v
    566     *            Value to assign to dataSourceName.
    567     */
    568    public void setDataSourceName(final String v) {
    569        assertInitializationAllowed();
    570        if (dataSource != null) {
    571            throw new IllegalStateException("Cannot set the JNDI name for the DataSource, if already "
    572                    + "set using setConnectionPoolDataSource.");
    573        }
    574        if (dataSourceName != null) {
    575            throw new IllegalStateException("The DataSourceName has already been set. " + "It cannot be altered.");
    576        }
    577        this.dataSourceName = v;
    578        instanceKey = InstanceKeyDataSourceFactory.registerNewInstance(this);
    579    }
    580
    581    /**
    582     * Gets the value of defaultAutoCommit, which defines the state of connections handed out from this pool. The value
    583     * can be changed on the Connection using Connection.setAutoCommit(boolean). The default is <code>null</code> which
    584     * will use the default value for the drive.
    585     *
    586     * @return value of defaultAutoCommit.
    587     */
    588    public Boolean isDefaultAutoCommit() {
    589        return defaultAutoCommit;
    590    }
    591
    592    /**
    593     * Sets the value of defaultAutoCommit, which defines the state of connections handed out from this pool. The value
    594     * can be changed on the Connection using Connection.setAutoCommit(boolean). The default is <code>null</code> which
    595     * will use the default value for the drive.
    596     *
    597     * @param v
    598     *            Value to assign to defaultAutoCommit.
    599     */
    600    public void setDefaultAutoCommit(final Boolean v) {
    601        assertInitializationAllowed();
    602        this.defaultAutoCommit = v;
    603    }
    604
    605    /**
    606     * Gets the value of defaultReadOnly, which defines the state of connections handed out from this pool. The value
    607     * can be changed on the Connection using Connection.setReadOnly(boolean). The default is <code>null</code> which
    608     * will use the default value for the drive.
    609     *
    610     * @return value of defaultReadOnly.
    611     */
    612    public Boolean isDefaultReadOnly() {
    613        return defaultReadOnly;
    614    }
    615
    616    /**
    617     * Sets the value of defaultReadOnly, which defines the state of connections handed out from this pool. The value
    618     * can be changed on the Connection using Connection.setReadOnly(boolean). The default is <code>null</code> which
    619     * will use the default value for the drive.
    620     *
    621     * @param v
    622     *            Value to assign to defaultReadOnly.
    623     */
    624    public void setDefaultReadOnly(final Boolean v) {
    625        assertInitializationAllowed();
    626        this.defaultReadOnly = v;
    627    }
    628
    629    /**
    630     * Gets the value of defaultTransactionIsolation, which defines the state of connections handed out from this pool.
    631     * The value can be changed on the Connection using Connection.setTransactionIsolation(int). If this method returns
    632     * -1, the default is JDBC driver dependent.
    633     *
    634     * @return value of defaultTransactionIsolation.
    635     */
    636    public int getDefaultTransactionIsolation() {
    637        return defaultTransactionIsolation;
    638    }
    639
    640    /**
    641     * Sets the value of defaultTransactionIsolation, which defines the state of connections handed out from this pool.
    642     * The value can be changed on the Connection using Connection.setTransactionIsolation(int). The default is JDBC
    643     * driver dependent.
    644     *
    645     * @param v
    646     *            Value to assign to defaultTransactionIsolation
    647     */
    648    public void setDefaultTransactionIsolation(final int v) {
    649        assertInitializationAllowed();
    650        switch (v) {
    651        case Connection.TRANSACTION_NONE:
    652        case Connection.TRANSACTION_READ_COMMITTED:
    653        case Connection.TRANSACTION_READ_UNCOMMITTED:
    654        case Connection.TRANSACTION_REPEATABLE_READ:
    655        case Connection.TRANSACTION_SERIALIZABLE:
    656            break;
    657        default:
    658            throw new IllegalArgumentException(BAD_TRANSACTION_ISOLATION);
    659        }
    660        this.defaultTransactionIsolation = v;
    661    }
    662
    663    /**
    664     * Gets the description. This property is defined by JDBC as for use with GUI (or other) tools that might deploy the
    665     * datasource. It serves no internal purpose.
    666     *
    667     * @return value of description.
    668     */
    669    public String getDescription() {
    670        return description;
    671    }
    672
    673    /**
    674     * Sets the description. This property is defined by JDBC as for use with GUI (or other) tools that might deploy the
    675     * datasource. It serves no internal purpose.
    676     *
    677     * @param v
    678     *            Value to assign to description.
    679     */
    680    public void setDescription(final String v) {
    681        this.description = v;
    682    }
    683
    684    protected String getInstanceKey() {
    685        return instanceKey;
    686    }
    687
    688    /**
    689     * Gets the value of jndiEnvironment which is used when instantiating a JNDI InitialContext. This InitialContext is
    690     * used to locate the backend ConnectionPoolDataSource.
    691     *
    692     * @param key
    693     *            JNDI environment key.
    694     * @return value of jndiEnvironment.
    695     */
    696    public String getJndiEnvironment(final String key) {
    697        String value = null;
    698        if (jndiEnvironment != null) {
    699            value = jndiEnvironment.getProperty(key);
    700        }
    701        return value;
    702    }
    703
    704    /**
    705     * Sets the value of the given JNDI environment property to be used when instantiating a JNDI InitialContext. This
    706     * InitialContext is used to locate the backend ConnectionPoolDataSource.
    707     *
    708     * @param key
    709     *            the JNDI environment property to set.
    710     * @param value
    711     *            the value assigned to specified JNDI environment property.
    712     */
    713    public void setJndiEnvironment(final String key, final String value) {
    714        if (jndiEnvironment == null) {
    715            jndiEnvironment = new Properties();
    716        }
    717        jndiEnvironment.setProperty(key, value);
    718    }
    719
    720    /**
    721     * Sets the JNDI environment to be used when instantiating a JNDI InitialContext. This InitialContext is used to
    722     * locate the backend ConnectionPoolDataSource.
    723     *
    724     * @param properties
    725     *            the JNDI environment property to set which will overwrite any current settings
    726     */
    727    void setJndiEnvironment(final Properties properties) {
    728        if (jndiEnvironment == null) {
    729            jndiEnvironment = new Properties();
    730        } else {
    731            jndiEnvironment.clear();
    732        }
    733        jndiEnvironment.putAll(properties);
    734    }
    735
    736    /**
    737     * Gets the value of loginTimeout.
    738     *
    739     * @return value of loginTimeout.
    740     */
    741    @Override
    742    public int getLoginTimeout() {
    743        return loginTimeout;
    744    }
    745
    746    /**
    747     * Sets the value of loginTimeout.
    748     *
    749     * @param v
    750     *            Value to assign to loginTimeout.
    751     */
    752    @Override
    753    public void setLoginTimeout(final int v) {
    754        this.loginTimeout = v;
    755    }
    756
    757    /**
    758     * Gets the value of logWriter.
    759     *
    760     * @return value of logWriter.
    761     */
    762    @Override
    763    public PrintWriter getLogWriter() {
    764        if (logWriter == null) {
    765            logWriter = new PrintWriter(new OutputStreamWriter(System.out, StandardCharsets.UTF_8));
    766        }
    767        return logWriter;
    768    }
    769
    770    /**
    771     * Sets the value of logWriter.
    772     *
    773     * @param v
    774     *            Value to assign to logWriter.
    775     */
    776    @Override
    777    public void setLogWriter(final PrintWriter v) {
    778        this.logWriter = v;
    779    }
    780
    781    /**
    782     * Gets the SQL query that will be used to validate connections from this pool before returning them to the caller.
    783     * If specified, this query <strong>MUST</strong> be an SQL SELECT statement that returns at least one row. If not
    784     * specified, {@link Connection#isValid(int)} will be used to validate connections.
    785     *
    786     * @return The SQL query that will be used to validate connections from this pool before returning them to the
    787     *         caller.
    788     */
    789    public String getValidationQuery() {
    790        return this.validationQuery;
    791    }
    792
    793    /**
    794     * Sets the SQL query that will be used to validate connections from this pool before returning them to the caller.
    795     * If specified, this query <strong>MUST</strong> be an SQL SELECT statement that returns at least one row. If not
    796     * specified, connections will be validated using {@link Connection#isValid(int)}.
    797     *
    798     * @param validationQuery
    799     *            The SQL query that will be used to validate connections from this pool before returning them to the
    800     *            caller.
    801     */
    802    public void setValidationQuery(final String validationQuery) {
    803        assertInitializationAllowed();
    804        this.validationQuery = validationQuery;
    805    }
    806
    807    /**
    808     * Returns the timeout in seconds before the validation query fails.
    809     *
    810     * @return The timeout in seconds before the validation query fails.
    811     */
    812    public int getValidationQueryTimeout() {
    813        return validationQueryTimeoutSeconds;
    814    }
    815
    816    /**
    817     * Sets the timeout in seconds before the validation query fails.
    818     *
    819     * @param validationQueryTimeoutSeconds
    820     *            The new timeout in seconds
    821     */
    822    public void setValidationQueryTimeout(final int validationQueryTimeoutSeconds) {
    823        this.validationQueryTimeoutSeconds = validationQueryTimeoutSeconds;
    824    }
    825
    826    /**
    827     * Whether a rollback will be issued after executing the SQL query that will be used to validate connections from
    828     * this pool before returning them to the caller.
    829     *
    830     * @return true if a rollback will be issued after executing the validation query
    831     */
    832    public boolean isRollbackAfterValidation() {
    833        return this.rollbackAfterValidation;
    834    }
    835
    836    /**
    837     * Whether a rollback will be issued after executing the SQL query that will be used to validate connections from
    838     * this pool before returning them to the caller. Default behavior is NOT to issue a rollback. The setting will only
    839     * have an effect if a validation query is set
    840     *
    841     * @param rollbackAfterValidation
    842     *            new property value
    843     */
    844    public void setRollbackAfterValidation(final boolean rollbackAfterValidation) {
    845        assertInitializationAllowed();
    846        this.rollbackAfterValidation = rollbackAfterValidation;
    847    }
    848
    849    /**
    850     * Returns the maximum permitted lifetime of a connection in milliseconds. A value of zero or less indicates an
    851     * infinite lifetime.
    852     *
    853     * @return The maximum permitted lifetime of a connection in milliseconds. A value of zero or less indicates an
    854     *         infinite lifetime.
    855     */
    856    public long getMaxConnLifetimeMillis() {
    857        return maxConnLifetimeMillis;
    858    }
    859
    860    /**
    861     * <p>
    862     * Sets the maximum permitted lifetime of a connection in milliseconds. A value of zero or less indicates an
    863     * infinite lifetime.
    864     * </p>
    865     * <p>
    866     * Note: this method currently has no effect once the pool has been initialized. The pool is initialized the first
    867     * time one of the following methods is invoked: <code>getConnection, setLogwriter,
    868     * setLoginTimeout, getLoginTimeout, getLogWriter.</code>
    869     * </p>
    870     *
    871     * @param maxConnLifetimeMillis
    872     *            The maximum permitted lifetime of a connection in milliseconds. A value of zero or less indicates an
    873     *            infinite lifetime.
    874     */
    875    public void setMaxConnLifetimeMillis(final long maxConnLifetimeMillis) {
    876        this.maxConnLifetimeMillis = maxConnLifetimeMillis;
    877    }
    878
    879    // ----------------------------------------------------------------------
    880    // Instrumentation Methods
    881
    882    // ----------------------------------------------------------------------
    883    // DataSource implementation
    884
    885    /**
    886     * Attempts to establish a database connection.
    887     */
    888    @Override
    889    public Connection getConnection() throws SQLException {
    890        return getConnection(null, null);
    891    }
    892
    893    /**
    894     * Attempts to retrieve a database connection using {@link #getPooledConnectionAndInfo(String, String)} with the
    895     * provided user name and password. The password on the {@link PooledConnectionAndInfo} instance returned by
    896     * <code>getPooledConnectionAndInfo</code> is compared to the <code>password</code> parameter. If the comparison
    897     * fails, a database connection using the supplied user name and password is attempted. If the connection attempt
    898     * fails, an SQLException is thrown, indicating that the given password did not match the password used to create
    899     * the pooled connection. If the connection attempt succeeds, this means that the database password has been
    900     * changed. In this case, the <code>PooledConnectionAndInfo</code> instance retrieved with the old password is
    901     * destroyed and the <code>getPooledConnectionAndInfo</code> is repeatedly invoked until a
    902     * <code>PooledConnectionAndInfo</code> instance with the new password is returned.
    903     */
    904    @Override
    905    public Connection getConnection(final String userName, final String userPassword) throws SQLException {
    906        if (instanceKey == null) {
    907            throw new SQLException("Must set the ConnectionPoolDataSource "
    908                    + "through setDataSourceName or setConnectionPoolDataSource" + " before calling getConnection.");
    909        }
    910        getConnectionCalled = true;
    911        PooledConnectionAndInfo info = null;
    912        try {
    913            info = getPooledConnectionAndInfo(userName, userPassword);
    914        } catch (final NoSuchElementException e) {
    915            closeDueToException(info);
    916            throw new SQLException("Cannot borrow connection from pool", e);
    917        } catch (final RuntimeException e) {
    918            closeDueToException(info);
    919            throw e;
    920        } catch (final SQLException e) {
    921            closeDueToException(info);
    922            throw e;
    923        } catch (final Exception e) {
    924            closeDueToException(info);
    925            throw new SQLException("Cannot borrow connection from pool", e);
    926        }
    927
    928        // Password on PooledConnectionAndInfo does not match
    929        if (!(null == userPassword ? null == info.getPassword() : userPassword.equals(info.getPassword()))) {
    930            try { // See if password has changed by attempting connection
    931                testCPDS(userName, userPassword);
    932            } catch (final SQLException ex) {
    933                // Password has not changed, so refuse client, but return connection to the pool
    934                closeDueToException(info);
    935                throw new SQLException(
    936                        "Given password did not match password used" + " to create the PooledConnection.", ex);
    937            } catch (final javax.naming.NamingException ne) {
    938                throw new SQLException("NamingException encountered connecting to database", ne);
    939            }
    940            /*
    941             * Password must have changed -> destroy connection and keep retrying until we get a new, good one,
    942             * destroying any idle connections with the old password as we pull them from the pool.
    943             */
    944            final UserPassKey upkey = info.getUserPassKey();
    945            final PooledConnectionManager manager = getConnectionManager(upkey);
    946            // Destroy and remove from pool
    947            manager.invalidate(info.getPooledConnection());
    948            // Reset the password on the factory if using CPDSConnectionFactory
    949            manager.setPassword(upkey.getPassword());
    950            info = null;
    951            for (int i = 0; i < 10; i++) { // Bound the number of retries - only needed if bad instances return
    952                try {
    953                    info = getPooledConnectionAndInfo(userName, userPassword);
    954                } catch (final NoSuchElementException e) {
    955                    closeDueToException(info);
    956                    throw new SQLException("Cannot borrow connection from pool", e);
    957                } catch (final RuntimeException e) {
    958                    closeDueToException(info);
    959                    throw e;
    960                } catch (final SQLException e) {
    961                    closeDueToException(info);
    962                    throw e;
    963                } catch (final Exception e) {
    964                    closeDueToException(info);
    965                    throw new SQLException("Cannot borrow connection from pool", e);
    966                }
    967                if (info != null && userPassword != null && userPassword.equals(info.getPassword())) {
    968                    break;
    969                }
    970                if (info != null) {
    971                    manager.invalidate(info.getPooledConnection());
    972                }
    973                info = null;
    974            }
    975            if (info == null) {
    976                throw new SQLException("Cannot borrow connection from pool - password change failure.");
    977            }
    978        }
    979
    980        final Connection con = info.getPooledConnection().getConnection();
    981        try {
    982            setupDefaults(con, userName);
    983            con.clearWarnings();
    984            return con;
    985        } catch (final SQLException ex) {
    986            try {
    987                con.close();
    988            } catch (final Exception exc) {
    989                getLogWriter().println("ignoring exception during close: " + exc);
    990            }
    991            throw ex;
    992        }
    993    }
    994
    995    protected abstract PooledConnectionAndInfo getPooledConnectionAndInfo(String userName, String userPassword)
    996            throws SQLException;
    997
    998    protected abstract void setupDefaults(Connection connection, String userName) throws SQLException;
    999
    1000    private void closeDueToException(final PooledConnectionAndInfo info) {
    1001        if (info != null) {
    1002            try {
    1003                info.getPooledConnection().getConnection().close();
    1004            } catch (final Exception e) {
    1005                // do not throw this exception because we are in the middle
    1006                // of handling another exception. But record it because
    1007                // it potentially leaks connections from the pool.
    1008                getLogWriter().println("[ERROR] Could not return connection to " + "pool during exception handling. "
    1009                        + e.getMessage());
    1010            }
    1011        }
    1012    }
    1013
    1014    protected ConnectionPoolDataSource testCPDS(final String userName, final String userPassword)
    1015            throws javax.naming.NamingException, SQLException {
    1016        // The source of physical db connections
    1017        ConnectionPoolDataSource cpds = this.dataSource;
    1018        if (cpds == null) {
    1019            Context ctx = null;
    1020            if (jndiEnvironment == null) {
    1021                ctx = new InitialContext();
    1022            } else {
    1023                ctx = new InitialContext(jndiEnvironment);
    1024            }
    1025            final Object ds = ctx.lookup(dataSourceName);
    1026            if (ds instanceof ConnectionPoolDataSource) {
    1027                cpds = (ConnectionPoolDataSource) ds;
    1028            } else {
    1029                throw new SQLException("Illegal configuration: " + "DataSource " + dataSourceName + " ("
    1030                        + ds.getClass().getName() + ")" + " doesn't implement javax.sql.ConnectionPoolDataSource");
    1031            }
    1032        }
    1033
    1034        // try to get a connection with the supplied userName/password
    1035        PooledConnection conn = null;
    1036        try {
    1037            if (userName != null) {
    1038                conn = cpds.getPooledConnection(userName, userPassword);
    1039            } else {
    1040                conn = cpds.getPooledConnection();
    1041            }
    1042            if (conn == null) {
    1043                throw new SQLException("Cannot connect using the supplied userName/password");
    1044            }
    1045        } finally {
    1046            if (conn != null) {
    1047                try {
    1048                    conn.close();
    1049                } catch (final SQLException e) {
    1050                    // at least we could connect
    1051                }
    1052            }
    1053        }
    1054        return cpds;
    1055    }
    1056}