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