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     * @throws IllegalStateException Thrown if a PooledConnection has already been requested.
151     */
152    protected void assertInitializationAllowed() throws IllegalStateException {
153        if (getConnectionCalled) {
154            throw new IllegalStateException(GET_CONNECTION_CALLED);
155        }
156    }
157
158    /**
159     * Closes the connection pool being maintained by this datasource.
160     */
161    @Override
162    public abstract void close() throws Exception;
163
164    protected abstract PooledConnectionManager getConnectionManager(UserPassKey upkey);
165
166    /* JDBC_4_ANT_KEY_BEGIN */
167    @Override
168    public boolean isWrapperFor(final Class<?> iface) throws SQLException {
169        return false;
170    }
171
172    @Override
173    public <T> T unwrap(final Class<T> iface) throws SQLException {
174        throw new SQLException("InstanceKeyDataSource is not a wrapper.");
175    }
176    /* JDBC_4_ANT_KEY_END */
177
178    @Override
179    public Logger getParentLogger() throws SQLFeatureNotSupportedException {
180        throw new SQLFeatureNotSupportedException();
181    }
182
183    // -------------------------------------------------------------------
184    // Properties
185
186    /**
187     * Gets the default value for {@link GenericKeyedObjectPoolConfig#getBlockWhenExhausted()} for each per user pool.
188     *
189     * @return The default value for {@link GenericKeyedObjectPoolConfig#getBlockWhenExhausted()} for each per user
190     *         pool.
191     */
192    public boolean getDefaultBlockWhenExhausted() {
193        return this.defaultBlockWhenExhausted;
194    }
195
196    /**
197     * Sets the default value for {@link GenericKeyedObjectPoolConfig#getBlockWhenExhausted()} for each per user pool.
198     *
199     * @param blockWhenExhausted
200     *            The default value for {@link GenericKeyedObjectPoolConfig#getBlockWhenExhausted()} for each per user
201     *            pool.
202     */
203    public void setDefaultBlockWhenExhausted(final boolean blockWhenExhausted) {
204        assertInitializationAllowed();
205        this.defaultBlockWhenExhausted = blockWhenExhausted;
206    }
207
208    /**
209     * Gets the default value for {@link GenericKeyedObjectPoolConfig#getEvictionPolicyClassName()} for each per user
210     * pool.
211     *
212     * @return The default value for {@link GenericKeyedObjectPoolConfig#getEvictionPolicyClassName()} for each per user
213     *         pool.
214     */
215    public String getDefaultEvictionPolicyClassName() {
216        return this.defaultEvictionPolicyClassName;
217    }
218
219    /**
220     * Sets the default value for {@link GenericKeyedObjectPoolConfig#getEvictionPolicyClassName()} for each per user
221     * pool.
222     *
223     * @param evictionPolicyClassName
224     *            The default value for {@link GenericKeyedObjectPoolConfig#getEvictionPolicyClassName()} for each per
225     *            user pool.
226     */
227    public void setDefaultEvictionPolicyClassName(final String evictionPolicyClassName) {
228        assertInitializationAllowed();
229        this.defaultEvictionPolicyClassName = evictionPolicyClassName;
230    }
231
232    /**
233     * Gets the default value for {@link GenericKeyedObjectPoolConfig#getLifo()} for each per user pool.
234     *
235     * @return The default value for {@link GenericKeyedObjectPoolConfig#getLifo()} for each per user pool.
236     */
237    public boolean getDefaultLifo() {
238        return this.defaultLifo;
239    }
240
241    /**
242     * Sets the default value for {@link GenericKeyedObjectPoolConfig#getLifo()} for each per user pool.
243     *
244     * @param lifo
245     *            The default value for {@link GenericKeyedObjectPoolConfig#getLifo()} for each per user pool.
246     */
247    public void setDefaultLifo(final boolean lifo) {
248        assertInitializationAllowed();
249        this.defaultLifo = lifo;
250    }
251
252    /**
253     * Gets the default value for {@link GenericKeyedObjectPoolConfig#getMaxIdlePerKey()} for each per user pool.
254     *
255     * @return The default value for {@link GenericKeyedObjectPoolConfig#getMaxIdlePerKey()} for each per user pool.
256     */
257    public int getDefaultMaxIdle() {
258        return this.defaultMaxIdle;
259    }
260
261    /**
262     * Sets the default value for {@link GenericKeyedObjectPoolConfig#getMaxIdlePerKey()} for each per user pool.
263     *
264     * @param maxIdle
265     *            The default value for {@link GenericKeyedObjectPoolConfig#getMaxIdlePerKey()} for each per user pool.
266     */
267    public void setDefaultMaxIdle(final int maxIdle) {
268        assertInitializationAllowed();
269        this.defaultMaxIdle = maxIdle;
270    }
271
272    /**
273     * Gets the default value for {@link GenericKeyedObjectPoolConfig#getMaxTotalPerKey()} for each per user pool.
274     *
275     * @return The default value for {@link GenericKeyedObjectPoolConfig#getMaxTotalPerKey()} for each per user pool.
276     */
277    public int getDefaultMaxTotal() {
278        return this.defaultMaxTotal;
279    }
280
281    /**
282     * Sets the default value for {@link GenericKeyedObjectPoolConfig#getMaxTotalPerKey()} for each per user pool.
283     *
284     * @param maxTotal
285     *            The default value for {@link GenericKeyedObjectPoolConfig#getMaxTotalPerKey()} for each per user pool.
286     */
287    public void setDefaultMaxTotal(final int maxTotal) {
288        assertInitializationAllowed();
289        this.defaultMaxTotal = maxTotal;
290    }
291
292    /**
293     * Gets the default value for {@link GenericKeyedObjectPoolConfig#getMaxWaitMillis()} for each per user pool.
294     *
295     * @return The default value for {@link GenericKeyedObjectPoolConfig#getMaxWaitMillis()} for each per user pool.
296     */
297    public long getDefaultMaxWaitMillis() {
298        return this.defaultMaxWaitMillis;
299    }
300
301    /**
302     * Sets the default value for {@link GenericKeyedObjectPoolConfig#getMaxWaitMillis()} for each per user pool.
303     *
304     * @param maxWaitMillis
305     *            The default value for {@link GenericKeyedObjectPoolConfig#getMaxWaitMillis()} for each per user pool.
306     */
307    public void setDefaultMaxWaitMillis(final long maxWaitMillis) {
308        assertInitializationAllowed();
309        this.defaultMaxWaitMillis = maxWaitMillis;
310    }
311
312    /**
313     * Gets the default value for {@link GenericKeyedObjectPoolConfig#getMinEvictableIdleTimeMillis()} for each per user
314     * pool.
315     *
316     * @return The default value for {@link GenericKeyedObjectPoolConfig#getMinEvictableIdleTimeMillis()} for each per
317     *         user pool.
318     */
319    public long getDefaultMinEvictableIdleTimeMillis() {
320        return this.defaultMinEvictableIdleTimeMillis;
321    }
322
323    /**
324     * Sets the default value for {@link GenericKeyedObjectPoolConfig#getMinEvictableIdleTimeMillis()} for each per user
325     * pool.
326     *
327     * @param minEvictableIdleTimeMillis
328     *            The default value for {@link GenericKeyedObjectPoolConfig#getMinEvictableIdleTimeMillis()} for each
329     *            per user pool.
330     */
331    public void setDefaultMinEvictableIdleTimeMillis(final long minEvictableIdleTimeMillis) {
332        assertInitializationAllowed();
333        this.defaultMinEvictableIdleTimeMillis = minEvictableIdleTimeMillis;
334    }
335
336    /**
337     * Gets the default value for {@link GenericKeyedObjectPoolConfig#getMinIdlePerKey()} for each per user pool.
338     *
339     * @return The default value for {@link GenericKeyedObjectPoolConfig#getMinIdlePerKey()} for each per user pool.
340     */
341    public int getDefaultMinIdle() {
342        return this.defaultMinIdle;
343    }
344
345    /**
346     * Sets the default value for {@link GenericKeyedObjectPoolConfig#getMinIdlePerKey()} for each per user pool.
347     *
348     * @param minIdle
349     *            The default value for {@link GenericKeyedObjectPoolConfig#getMinIdlePerKey()} for each per user pool.
350     */
351    public void setDefaultMinIdle(final int minIdle) {
352        assertInitializationAllowed();
353        this.defaultMinIdle = minIdle;
354    }
355
356    /**
357     * Gets the default value for {@link GenericKeyedObjectPoolConfig#getNumTestsPerEvictionRun()} for each per user
358     * pool.
359     *
360     * @return The default value for {@link GenericKeyedObjectPoolConfig#getNumTestsPerEvictionRun()} for each per user
361     *         pool.
362     */
363    public int getDefaultNumTestsPerEvictionRun() {
364        return this.defaultNumTestsPerEvictionRun;
365    }
366
367    /**
368     * Sets the default value for {@link GenericKeyedObjectPoolConfig#getNumTestsPerEvictionRun()} for each per user
369     * pool.
370     *
371     * @param numTestsPerEvictionRun
372     *            The default value for {@link GenericKeyedObjectPoolConfig#getNumTestsPerEvictionRun()} for each per
373     *            user pool.
374     */
375    public void setDefaultNumTestsPerEvictionRun(final int numTestsPerEvictionRun) {
376        assertInitializationAllowed();
377        this.defaultNumTestsPerEvictionRun = numTestsPerEvictionRun;
378    }
379
380    /**
381     * Gets the default value for {@link org.apache.commons.pool2.impl.GenericObjectPool
382     * GenericObjectPool#getSoftMinEvictableIdleTimeMillis()} for each per user pool.
383     *
384     * @return The default value for {@link org.apache.commons.pool2.impl.GenericObjectPool
385     *         GenericObjectPool#getSoftMinEvictableIdleTimeMillis()} for each per user pool.
386     */
387    public long getDefaultSoftMinEvictableIdleTimeMillis() {
388        return this.defaultSoftMinEvictableIdleTimeMillis;
389    }
390
391    /**
392     * Sets the default value for {@link org.apache.commons.pool2.impl.GenericObjectPool
393     * GenericObjectPool#getSoftMinEvictableIdleTimeMillis()} for each per user pool.
394     *
395     * @param softMinEvictableIdleTimeMillis
396     *            The default value for {@link org.apache.commons.pool2.impl.GenericObjectPool
397     *            GenericObjectPool#getSoftMinEvictableIdleTimeMillis()} for each per user pool.
398     */
399    public void setDefaultSoftMinEvictableIdleTimeMillis(final long softMinEvictableIdleTimeMillis) {
400        assertInitializationAllowed();
401        this.defaultSoftMinEvictableIdleTimeMillis = softMinEvictableIdleTimeMillis;
402    }
403
404    /**
405     * Gets the default value for {@link org.apache.commons.pool2.impl.GenericObjectPool
406     * GenericObjectPool#getTestOnCreate()} for each per user pool.
407     *
408     * @return The default value for {@link org.apache.commons.pool2.impl.GenericObjectPool
409     *         GenericObjectPool#getTestOnCreate()} for each per user pool.
410     */
411    public boolean getDefaultTestOnCreate() {
412        return this.defaultTestOnCreate;
413    }
414
415    /**
416     * Sets the default value for {@link org.apache.commons.pool2.impl.GenericObjectPool
417     * GenericObjectPool#getTestOnCreate()} for each per user pool.
418     *
419     * @param testOnCreate
420     *            The default value for {@link org.apache.commons.pool2.impl.GenericObjectPool
421     *            GenericObjectPool#getTestOnCreate()} for each per user pool.
422     */
423    public void setDefaultTestOnCreate(final boolean testOnCreate) {
424        assertInitializationAllowed();
425        this.defaultTestOnCreate = testOnCreate;
426    }
427
428    /**
429     * Gets the default value for {@link org.apache.commons.pool2.impl.GenericObjectPool
430     * GenericObjectPool#getTestOnBorrow()} for each per user pool.
431     *
432     * @return The default value for {@link org.apache.commons.pool2.impl.GenericObjectPool
433     *         GenericObjectPool#getTestOnBorrow()} for each per user pool.
434     */
435    public boolean getDefaultTestOnBorrow() {
436        return this.defaultTestOnBorrow;
437    }
438
439    /**
440     * Sets the default value for {@link org.apache.commons.pool2.impl.GenericObjectPool
441     * GenericObjectPool#getTestOnBorrow()} for each per user pool.
442     *
443     * @param testOnBorrow
444     *            The default value for {@link org.apache.commons.pool2.impl.GenericObjectPool
445     *            GenericObjectPool#getTestOnBorrow()} for each per user pool.
446     */
447    public void setDefaultTestOnBorrow(final boolean testOnBorrow) {
448        assertInitializationAllowed();
449        this.defaultTestOnBorrow = testOnBorrow;
450    }
451
452    /**
453     * Gets the default value for {@link org.apache.commons.pool2.impl.GenericObjectPool
454     * GenericObjectPool#getTestOnReturn()} for each per user pool.
455     *
456     * @return The default value for {@link org.apache.commons.pool2.impl.GenericObjectPool
457     *         GenericObjectPool#getTestOnReturn()} for each per user pool.
458     */
459    public boolean getDefaultTestOnReturn() {
460        return this.defaultTestOnReturn;
461    }
462
463    /**
464     * Sets the default value for {@link org.apache.commons.pool2.impl.GenericObjectPool
465     * GenericObjectPool#getTestOnReturn()} for each per user pool.
466     *
467     * @param testOnReturn
468     *            The default value for {@link org.apache.commons.pool2.impl.GenericObjectPool
469     *            GenericObjectPool#getTestOnReturn()} for each per user pool.
470     */
471    public void setDefaultTestOnReturn(final boolean testOnReturn) {
472        assertInitializationAllowed();
473        this.defaultTestOnReturn = testOnReturn;
474    }
475
476    /**
477     * Gets the default value for {@link org.apache.commons.pool2.impl.GenericObjectPool
478     * GenericObjectPool#getTestWhileIdle()} for each per user pool.
479     *
480     * @return The default value for {@link org.apache.commons.pool2.impl.GenericObjectPool
481     *         GenericObjectPool#getTestWhileIdle()} for each per user pool.
482     */
483    public boolean getDefaultTestWhileIdle() {
484        return this.defaultTestWhileIdle;
485    }
486
487    /**
488     * Sets the default value for {@link org.apache.commons.pool2.impl.GenericObjectPool
489     * GenericObjectPool#getTestWhileIdle()} for each per user pool.
490     *
491     * @param testWhileIdle
492     *            The default value for {@link org.apache.commons.pool2.impl.GenericObjectPool
493     *            GenericObjectPool#getTestWhileIdle()} for each per user pool.
494     */
495    public void setDefaultTestWhileIdle(final boolean testWhileIdle) {
496        assertInitializationAllowed();
497        this.defaultTestWhileIdle = testWhileIdle;
498    }
499
500    /**
501     * Gets the default value for {@link org.apache.commons.pool2.impl.GenericObjectPool
502     * GenericObjectPool#getTimeBetweenEvictionRunsMillis ()} for each per user pool.
503     *
504     * @return The default value for {@link org.apache.commons.pool2.impl.GenericObjectPool
505     *         GenericObjectPool#getTimeBetweenEvictionRunsMillis ()} for each per user pool.
506     */
507    public long getDefaultTimeBetweenEvictionRunsMillis() {
508        return this.defaultTimeBetweenEvictionRunsMillis;
509    }
510
511    /**
512     * Sets the default value for {@link org.apache.commons.pool2.impl.GenericObjectPool
513     * GenericObjectPool#getTimeBetweenEvictionRunsMillis ()} for each per user pool.
514     *
515     * @param timeBetweenEvictionRunsMillis
516     *            The default value for {@link org.apache.commons.pool2.impl.GenericObjectPool
517     *            GenericObjectPool#getTimeBetweenEvictionRunsMillis ()} for each per user pool.
518     */
519    public void setDefaultTimeBetweenEvictionRunsMillis(final long timeBetweenEvictionRunsMillis) {
520        assertInitializationAllowed();
521        this.defaultTimeBetweenEvictionRunsMillis = timeBetweenEvictionRunsMillis;
522    }
523
524    /**
525     * Gets the value of connectionPoolDataSource. This method will return null, if the backing data source is being
526     * accessed via JNDI.
527     *
528     * @return value of connectionPoolDataSource.
529     */
530    public ConnectionPoolDataSource getConnectionPoolDataSource() {
531        return dataSource;
532    }
533
534    /**
535     * Sets the backend ConnectionPoolDataSource. This property should not be set if using JNDI to access the
536     * data source.
537     *
538     * @param v
539     *            Value to assign to connectionPoolDataSource.
540     */
541    public void setConnectionPoolDataSource(final ConnectionPoolDataSource v) {
542        assertInitializationAllowed();
543        if (dataSourceName != null) {
544            throw new IllegalStateException("Cannot set the DataSource, if JNDI is used.");
545        }
546        if (dataSource != null) {
547            throw new IllegalStateException("The CPDS has already been set. It cannot be altered.");
548        }
549        dataSource = v;
550        instanceKey = InstanceKeyDataSourceFactory.registerNewInstance(this);
551    }
552
553    /**
554     * Gets the name of the ConnectionPoolDataSource which backs this pool. This name is used to look up the data source
555     * from a JNDI service provider.
556     *
557     * @return value of dataSourceName.
558     */
559    public String getDataSourceName() {
560        return dataSourceName;
561    }
562
563    /**
564     * Sets the name of the ConnectionPoolDataSource which backs this pool. This name is used to look up the data source
565     * from a JNDI service provider.
566     *
567     * @param v
568     *            Value to assign to dataSourceName.
569     */
570    public void setDataSourceName(final String v) {
571        assertInitializationAllowed();
572        if (dataSource != null) {
573            throw new IllegalStateException("Cannot set the JNDI name for the DataSource, if already "
574                    + "set using setConnectionPoolDataSource.");
575        }
576        if (dataSourceName != null) {
577            throw new IllegalStateException("The DataSourceName has already been set. " + "It cannot be altered.");
578        }
579        this.dataSourceName = v;
580        instanceKey = InstanceKeyDataSourceFactory.registerNewInstance(this);
581    }
582
583    /**
584     * Gets the value of defaultAutoCommit, which defines the state of connections handed out from this pool. The value
585     * can be changed on the Connection using Connection.setAutoCommit(boolean). The default is <code>null</code> which
586     * will use the default value for the drive.
587     *
588     * @return value of defaultAutoCommit.
589     */
590    public Boolean isDefaultAutoCommit() {
591        return defaultAutoCommit;
592    }
593
594    /**
595     * Sets the value of defaultAutoCommit, which defines the state of connections handed out from this pool. The value
596     * can be changed on the Connection using Connection.setAutoCommit(boolean). The default is <code>null</code> which
597     * will use the default value for the drive.
598     *
599     * @param v
600     *            Value to assign to defaultAutoCommit.
601     */
602    public void setDefaultAutoCommit(final Boolean v) {
603        assertInitializationAllowed();
604        this.defaultAutoCommit = v;
605    }
606
607    /**
608     * Gets the value of defaultReadOnly, which defines the state of connections handed out from this pool. The value
609     * can be changed on the Connection using Connection.setReadOnly(boolean). The default is <code>null</code> which
610     * will use the default value for the drive.
611     *
612     * @return value of defaultReadOnly.
613     */
614    public Boolean isDefaultReadOnly() {
615        return defaultReadOnly;
616    }
617
618    /**
619     * Sets the value of defaultReadOnly, which defines the state of connections handed out from this pool. The value
620     * can be changed on the Connection using Connection.setReadOnly(boolean). The default is <code>null</code> which
621     * will use the default value for the drive.
622     *
623     * @param v
624     *            Value to assign to defaultReadOnly.
625     */
626    public void setDefaultReadOnly(final Boolean v) {
627        assertInitializationAllowed();
628        this.defaultReadOnly = v;
629    }
630
631    /**
632     * Gets the value of defaultTransactionIsolation, which defines the state of connections handed out from this pool.
633     * The value can be changed on the Connection using Connection.setTransactionIsolation(int). If this method returns
634     * -1, the default is JDBC driver dependent.
635     *
636     * @return value of defaultTransactionIsolation.
637     */
638    public int getDefaultTransactionIsolation() {
639        return defaultTransactionIsolation;
640    }
641
642    /**
643     * Sets the value of defaultTransactionIsolation, which defines the state of connections handed out from this pool.
644     * The value can be changed on the Connection using Connection.setTransactionIsolation(int). The default is JDBC
645     * driver dependent.
646     *
647     * @param v
648     *            Value to assign to defaultTransactionIsolation
649     */
650    public void setDefaultTransactionIsolation(final int v) {
651        assertInitializationAllowed();
652        switch (v) {
653        case Connection.TRANSACTION_NONE:
654        case Connection.TRANSACTION_READ_COMMITTED:
655        case Connection.TRANSACTION_READ_UNCOMMITTED:
656        case Connection.TRANSACTION_REPEATABLE_READ:
657        case Connection.TRANSACTION_SERIALIZABLE:
658            break;
659        default:
660            throw new IllegalArgumentException(BAD_TRANSACTION_ISOLATION);
661        }
662        this.defaultTransactionIsolation = v;
663    }
664
665    /**
666     * Gets the description. This property is defined by JDBC as for use with GUI (or other) tools that might deploy the
667     * datasource. It serves no internal purpose.
668     *
669     * @return value of description.
670     */
671    public String getDescription() {
672        return description;
673    }
674
675    /**
676     * Sets the description. This property is defined by JDBC as for use with GUI (or other) tools that might deploy the
677     * datasource. It serves no internal purpose.
678     *
679     * @param v
680     *            Value to assign to description.
681     */
682    public void setDescription(final String v) {
683        this.description = v;
684    }
685
686    protected String getInstanceKey() {
687        return instanceKey;
688    }
689
690    /**
691     * Gets the value of jndiEnvironment which is used when instantiating a JNDI InitialContext. This InitialContext is
692     * used to locate the backend ConnectionPoolDataSource.
693     *
694     * @param key
695     *            JNDI environment key.
696     * @return value of jndiEnvironment.
697     */
698    public String getJndiEnvironment(final String key) {
699        String value = null;
700        if (jndiEnvironment != null) {
701            value = jndiEnvironment.getProperty(key);
702        }
703        return value;
704    }
705
706    /**
707     * Sets the value of the given JNDI environment property to be used when instantiating a JNDI InitialContext. This
708     * InitialContext is used to locate the backend ConnectionPoolDataSource.
709     *
710     * @param key
711     *            the JNDI environment property to set.
712     * @param value
713     *            the value assigned to specified JNDI environment property.
714     */
715    public void setJndiEnvironment(final String key, final String value) {
716        if (jndiEnvironment == null) {
717            jndiEnvironment = new Properties();
718        }
719        jndiEnvironment.setProperty(key, value);
720    }
721
722    /**
723     * Sets the JNDI environment to be used when instantiating a JNDI InitialContext. This InitialContext is used to
724     * locate the backend ConnectionPoolDataSource.
725     *
726     * @param properties
727     *            the JNDI environment property to set which will overwrite any current settings
728     */
729    void setJndiEnvironment(final Properties properties) {
730        if (jndiEnvironment == null) {
731            jndiEnvironment = new Properties();
732        } else {
733            jndiEnvironment.clear();
734        }
735        jndiEnvironment.putAll(properties);
736    }
737
738    /**
739     * Gets the value of loginTimeout.
740     *
741     * @return value of loginTimeout.
742     */
743    @Override
744    public int getLoginTimeout() {
745        return loginTimeout;
746    }
747
748    /**
749     * Sets the value of loginTimeout.
750     *
751     * @param v
752     *            Value to assign to loginTimeout.
753     */
754    @Override
755    public void setLoginTimeout(final int v) {
756        this.loginTimeout = v;
757    }
758
759    /**
760     * Gets the value of logWriter.
761     *
762     * @return value of logWriter.
763     */
764    @Override
765    public PrintWriter getLogWriter() {
766        if (logWriter == null) {
767            logWriter = new PrintWriter(new OutputStreamWriter(System.out, StandardCharsets.UTF_8));
768        }
769        return logWriter;
770    }
771
772    /**
773     * Sets the value of logWriter.
774     *
775     * @param v
776     *            Value to assign to logWriter.
777     */
778    @Override
779    public void setLogWriter(final PrintWriter v) {
780        this.logWriter = v;
781    }
782
783    /**
784     * Gets the SQL query that will be used to validate connections from this pool before returning them to the caller.
785     * If specified, this query <strong>MUST</strong> be an SQL SELECT statement that returns at least one row. If not
786     * specified, {@link Connection#isValid(int)} will be used to validate connections.
787     *
788     * @return The SQL query that will be used to validate connections from this pool before returning them to the
789     *         caller.
790     */
791    public String getValidationQuery() {
792        return this.validationQuery;
793    }
794
795    /**
796     * Sets the SQL query that will be used to validate connections from this pool before returning them to the caller.
797     * If specified, this query <strong>MUST</strong> be an SQL SELECT statement that returns at least one row. If not
798     * specified, connections will be validated using {@link Connection#isValid(int)}.
799     *
800     * @param validationQuery
801     *            The SQL query that will be used to validate connections from this pool before returning them to the
802     *            caller.
803     */
804    public void setValidationQuery(final String validationQuery) {
805        assertInitializationAllowed();
806        this.validationQuery = validationQuery;
807    }
808
809    /**
810     * Returns the timeout in seconds before the validation query fails.
811     *
812     * @return The timeout in seconds before the validation query fails.
813     */
814    public int getValidationQueryTimeout() {
815        return validationQueryTimeoutSeconds;
816    }
817
818    /**
819     * Sets the timeout in seconds before the validation query fails.
820     *
821     * @param validationQueryTimeoutSeconds
822     *            The new timeout in seconds
823     */
824    public void setValidationQueryTimeout(final int validationQueryTimeoutSeconds) {
825        this.validationQueryTimeoutSeconds = validationQueryTimeoutSeconds;
826    }
827
828    /**
829     * Whether a rollback will be issued after executing the SQL query that will be used to validate connections from
830     * this pool before returning them to the caller.
831     *
832     * @return true if a rollback will be issued after executing the validation query
833     */
834    public boolean isRollbackAfterValidation() {
835        return this.rollbackAfterValidation;
836    }
837
838    /**
839     * Whether a rollback will be issued after executing the SQL query that will be used to validate connections from
840     * this pool before returning them to the caller. Default behavior is NOT to issue a rollback. The setting will only
841     * have an effect if a validation query is set
842     *
843     * @param rollbackAfterValidation
844     *            new property value
845     */
846    public void setRollbackAfterValidation(final boolean rollbackAfterValidation) {
847        assertInitializationAllowed();
848        this.rollbackAfterValidation = rollbackAfterValidation;
849    }
850
851    /**
852     * Returns the maximum permitted lifetime of a connection in milliseconds. A value of zero or less indicates an
853     * infinite lifetime.
854     *
855     * @return The maximum permitted lifetime of a connection in milliseconds. A value of zero or less indicates an
856     *         infinite lifetime.
857     */
858    public long getMaxConnLifetimeMillis() {
859        return maxConnLifetimeMillis;
860    }
861
862    /**
863     * <p>
864     * Sets the maximum permitted lifetime of a connection in milliseconds. A value of zero or less indicates an
865     * infinite lifetime.
866     * </p>
867     * <p>
868     * Note: this method currently has no effect once the pool has been initialized. The pool is initialized the first
869     * time one of the following methods is invoked: <code>getConnection, setLogwriter,
870     * setLoginTimeout, getLoginTimeout, getLogWriter.</code>
871     * </p>
872     *
873     * @param maxConnLifetimeMillis
874     *            The maximum permitted lifetime of a connection in milliseconds. A value of zero or less indicates an
875     *            infinite lifetime.
876     */
877    public void setMaxConnLifetimeMillis(final long maxConnLifetimeMillis) {
878        this.maxConnLifetimeMillis = maxConnLifetimeMillis;
879    }
880
881    // ----------------------------------------------------------------------
882    // Instrumentation Methods
883
884    // ----------------------------------------------------------------------
885    // DataSource implementation
886
887    /**
888     * Attempts to establish a database connection.
889     */
890    @Override
891    public Connection getConnection() throws SQLException {
892        return getConnection(null, null);
893    }
894
895    /**
896     * Attempts to retrieve a database connection using {@link #getPooledConnectionAndInfo(String, String)} with the
897     * provided user name and password. The password on the {@link PooledConnectionAndInfo} instance returned by
898     * <code>getPooledConnectionAndInfo</code> is compared to the <code>password</code> parameter. If the comparison
899     * fails, a database connection using the supplied user name and password is attempted. If the connection attempt
900     * fails, an SQLException is thrown, indicating that the given password did not match the password used to create
901     * the pooled connection. If the connection attempt succeeds, this means that the database password has been
902     * changed. In this case, the <code>PooledConnectionAndInfo</code> instance retrieved with the old password is
903     * destroyed and the <code>getPooledConnectionAndInfo</code> is repeatedly invoked until a
904     * <code>PooledConnectionAndInfo</code> instance with the new password is returned.
905     */
906    @Override
907    public Connection getConnection(final String userName, final String userPassword) throws SQLException {
908        if (instanceKey == null) {
909            throw new SQLException("Must set the ConnectionPoolDataSource "
910                    + "through setDataSourceName or setConnectionPoolDataSource" + " before calling getConnection.");
911        }
912        getConnectionCalled = true;
913        PooledConnectionAndInfo info = null;
914        try {
915            info = getPooledConnectionAndInfo(userName, userPassword);
916        } catch (final NoSuchElementException e) {
917            closeDueToException(info);
918            throw new SQLException("Cannot borrow connection from pool", e);
919        } catch (final RuntimeException e) {
920            closeDueToException(info);
921            throw e;
922        } catch (final SQLException e) {
923            closeDueToException(info);
924            throw e;
925        } catch (final Exception e) {
926            closeDueToException(info);
927            throw new SQLException("Cannot borrow connection from pool", e);
928        }
929
930        // Password on PooledConnectionAndInfo does not match
931        if (!(null == userPassword ? null == info.getPassword() : userPassword.equals(info.getPassword()))) {
932            try { // See if password has changed by attempting connection
933                testCPDS(userName, userPassword);
934            } catch (final SQLException ex) {
935                // Password has not changed, so refuse client, but return connection to the pool
936                closeDueToException(info);
937                throw new SQLException(
938                        "Given password did not match password used" + " to create the PooledConnection.", ex);
939            } catch (final javax.naming.NamingException ne) {
940                throw new SQLException("NamingException encountered connecting to database", ne);
941            }
942            /*
943             * Password must have changed -> destroy connection and keep retrying until we get a new, good one,
944             * destroying any idle connections with the old password as we pull them from the pool.
945             */
946            final UserPassKey upkey = info.getUserPassKey();
947            final PooledConnectionManager manager = getConnectionManager(upkey);
948            // Destroy and remove from pool
949            manager.invalidate(info.getPooledConnection());
950            // Reset the password on the factory if using CPDSConnectionFactory
951            manager.setPassword(upkey.getPassword());
952            info = null;
953            for (int i = 0; i < 10; i++) { // Bound the number of retries - only needed if bad instances return
954                try {
955                    info = getPooledConnectionAndInfo(userName, userPassword);
956                } catch (final NoSuchElementException e) {
957                    closeDueToException(info);
958                    throw new SQLException("Cannot borrow connection from pool", e);
959                } catch (final RuntimeException e) {
960                    closeDueToException(info);
961                    throw e;
962                } catch (final SQLException e) {
963                    closeDueToException(info);
964                    throw e;
965                } catch (final Exception e) {
966                    closeDueToException(info);
967                    throw new SQLException("Cannot borrow connection from pool", e);
968                }
969                if (info != null && userPassword != null && userPassword.equals(info.getPassword())) {
970                    break;
971                }
972                if (info != null) {
973                    manager.invalidate(info.getPooledConnection());
974                }
975                info = null;
976            }
977            if (info == null) {
978                throw new SQLException("Cannot borrow connection from pool - password change failure.");
979            }
980        }
981
982        final Connection con = info.getPooledConnection().getConnection();
983        try {
984            setupDefaults(con, userName);
985            con.clearWarnings();
986            return con;
987        } catch (final SQLException ex) {
988            try {
989                con.close();
990            } catch (final Exception exc) {
991                getLogWriter().println("ignoring exception during close: " + exc);
992            }
993            throw ex;
994        }
995    }
996
997    protected abstract PooledConnectionAndInfo getPooledConnectionAndInfo(String userName, String userPassword)
998            throws SQLException;
999
1000    protected abstract void setupDefaults(Connection connection, String userName) throws SQLException;
1001
1002    private void closeDueToException(final PooledConnectionAndInfo info) {
1003        if (info != null) {
1004            try {
1005                info.getPooledConnection().getConnection().close();
1006            } catch (final Exception e) {
1007                // do not throw this exception because we are in the middle
1008                // of handling another exception. But record it because
1009                // it potentially leaks connections from the pool.
1010                getLogWriter().println("[ERROR] Could not return connection to " + "pool during exception handling. "
1011                        + e.getMessage());
1012            }
1013        }
1014    }
1015
1016    protected ConnectionPoolDataSource testCPDS(final String userName, final String userPassword)
1017            throws javax.naming.NamingException, SQLException {
1018        // The source of physical db connections
1019        ConnectionPoolDataSource cpds = this.dataSource;
1020        if (cpds == null) {
1021            Context ctx = null;
1022            if (jndiEnvironment == null) {
1023                ctx = new InitialContext();
1024            } else {
1025                ctx = new InitialContext(jndiEnvironment);
1026            }
1027            final Object ds = ctx.lookup(dataSourceName);
1028            if (ds instanceof ConnectionPoolDataSource) {
1029                cpds = (ConnectionPoolDataSource) ds;
1030            } else {
1031                throw new SQLException("Illegal configuration: " + "DataSource " + dataSourceName + " ("
1032                        + ds.getClass().getName() + ")" + " doesn't implement javax.sql.ConnectionPoolDataSource");
1033            }
1034        }
1035
1036        // try to get a connection with the supplied userName/password
1037        PooledConnection conn = null;
1038        try {
1039            if (userName != null) {
1040                conn = cpds.getPooledConnection(userName, userPassword);
1041            } else {
1042                conn = cpds.getPooledConnection();
1043            }
1044            if (conn == null) {
1045                throw new SQLException("Cannot connect using the supplied userName/password");
1046            }
1047        } finally {
1048            if (conn != null) {
1049                try {
1050                    conn.close();
1051                } catch (final SQLException e) {
1052                    // at least we could connect
1053                }
1054            }
1055        }
1056        return cpds;
1057    }
1058
1059    /**
1060     * @since 2.6.0
1061     */
1062    @Override
1063    public synchronized String toString() {
1064        final StringBuilder builder = new StringBuilder(super.toString());
1065        builder.append("[");
1066        toStringFields(builder);
1067        builder.append("]");
1068        return builder.toString();
1069    }
1070
1071    protected void toStringFields(final StringBuilder builder) {
1072        builder.append("getConnectionCalled=");
1073        builder.append(getConnectionCalled);
1074        builder.append(", dataSource=");
1075        builder.append(dataSource);
1076        builder.append(", dataSourceName=");
1077        builder.append(dataSourceName);
1078        builder.append(", description=");
1079        builder.append(description);
1080        builder.append(", jndiEnvironment=");
1081        builder.append(jndiEnvironment);
1082        builder.append(", loginTimeout=");
1083        builder.append(loginTimeout);
1084        builder.append(", logWriter=");
1085        builder.append(logWriter);
1086        builder.append(", instanceKey=");
1087        builder.append(instanceKey);
1088        builder.append(", defaultBlockWhenExhausted=");
1089        builder.append(defaultBlockWhenExhausted);
1090        builder.append(", defaultEvictionPolicyClassName=");
1091        builder.append(defaultEvictionPolicyClassName);
1092        builder.append(", defaultLifo=");
1093        builder.append(defaultLifo);
1094        builder.append(", defaultMaxIdle=");
1095        builder.append(defaultMaxIdle);
1096        builder.append(", defaultMaxTotal=");
1097        builder.append(defaultMaxTotal);
1098        builder.append(", defaultMaxWaitMillis=");
1099        builder.append(defaultMaxWaitMillis);
1100        builder.append(", defaultMinEvictableIdleTimeMillis=");
1101        builder.append(defaultMinEvictableIdleTimeMillis);
1102        builder.append(", defaultMinIdle=");
1103        builder.append(defaultMinIdle);
1104        builder.append(", defaultNumTestsPerEvictionRun=");
1105        builder.append(defaultNumTestsPerEvictionRun);
1106        builder.append(", defaultSoftMinEvictableIdleTimeMillis=");
1107        builder.append(defaultSoftMinEvictableIdleTimeMillis);
1108        builder.append(", defaultTestOnCreate=");
1109        builder.append(defaultTestOnCreate);
1110        builder.append(", defaultTestOnBorrow=");
1111        builder.append(defaultTestOnBorrow);
1112        builder.append(", defaultTestOnReturn=");
1113        builder.append(defaultTestOnReturn);
1114        builder.append(", defaultTestWhileIdle=");
1115        builder.append(defaultTestWhileIdle);
1116        builder.append(", defaultTimeBetweenEvictionRunsMillis=");
1117        builder.append(defaultTimeBetweenEvictionRunsMillis);
1118        builder.append(", validationQuery=");
1119        builder.append(validationQuery);
1120        builder.append(", validationQueryTimeoutSeconds=");
1121        builder.append(validationQueryTimeoutSeconds);
1122        builder.append(", rollbackAfterValidation=");
1123        builder.append(rollbackAfterValidation);
1124        builder.append(", maxConnLifetimeMillis=");
1125        builder.append(maxConnLifetimeMillis);
1126        builder.append(", defaultAutoCommit=");
1127        builder.append(defaultAutoCommit);
1128        builder.append(", defaultTransactionIsolation=");
1129        builder.append(defaultTransactionIsolation);
1130        builder.append(", defaultReadOnly=");
1131        builder.append(defaultReadOnly);
1132    }
1133}