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