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}