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