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 *      https://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 */
017package org.apache.commons.dbcp2.datasources;
018
019import java.io.OutputStreamWriter;
020import java.io.PrintWriter;
021import java.io.Serializable;
022import java.nio.charset.StandardCharsets;
023import java.sql.Connection;
024import java.sql.SQLException;
025import java.sql.SQLFeatureNotSupportedException;
026import java.time.Duration;
027import java.util.Properties;
028import java.util.logging.Logger;
029
030import javax.naming.Context;
031import javax.naming.InitialContext;
032import javax.naming.Referenceable;
033import javax.sql.ConnectionPoolDataSource;
034import javax.sql.DataSource;
035import javax.sql.PooledConnection;
036
037import org.apache.commons.dbcp2.Utils;
038import org.apache.commons.pool2.impl.BaseObjectPoolConfig;
039import org.apache.commons.pool2.impl.GenericKeyedObjectPoolConfig;
040import org.apache.commons.pool2.impl.GenericObjectPool;
041
042/**
043 * <p>
044 * The base class for {@link SharedPoolDataSource} and {@link PerUserPoolDataSource}. Many of the
045 * configuration properties are shared and defined here. This class is declared public in order to allow particular
046 * usage with commons-beanutils; do not make direct 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 {@link DataSource} whose attributes are
051 * presented as bean getters/setters and then deploying it via JNDI. It is then available to an application as a source
052 * of pooled logical connections to the database. The pool needs a source of physical connections. This source is in the
053 * form of a {@link ConnectionPoolDataSource} that can be specified via the {@link #setDataSourceName(String)} used
054 * to lookup the source via JNDI.
055 * </p>
056 *
057 * <p>
058 * Although normally used within a JNDI environment, A DataSource can be instantiated and initialized as any bean. In
059 * this case the {@link ConnectionPoolDataSource} will likely be instantiated in a similar manner. This class
060 * allows the physical source of connections to be attached directly to this pool using the
061 * {@link #setConnectionPoolDataSource(ConnectionPoolDataSource)} method.
062 * </p>
063 *
064 * <p>
065 * The dbcp package contains an adapter, {@link org.apache.commons.dbcp2.cpdsadapter.DriverAdapterCPDS}, that can be
066 * used to allow the use of {@link DataSource}'s based on this class with JDBC driver implementations that do not
067 * supply a {@link ConnectionPoolDataSource}, but still provide a {@link java.sql.Driver} implementation.
068 * </p>
069 *
070 * <p>
071 * The <a href="package-summary.html">package documentation</a> contains an example using Apache Tomcat and JNDI and it
072 * also contains a non-JNDI example.
073 * </p>
074 *
075 * @since 2.0
076 */
077public abstract class InstanceKeyDataSource implements DataSource, Referenceable, Serializable, AutoCloseable {
078
079    private static final long serialVersionUID = -6819270431752240878L;
080
081    private static final String GET_CONNECTION_CALLED = "A Connection was already requested from this source, "
082            + "further initialization is not allowed.";
083    private static final String BAD_TRANSACTION_ISOLATION = "The requested TransactionIsolation level is invalid.";
084
085    /**
086     * Internal constant to indicate the level is not set.
087     */
088    protected static final int UNKNOWN_TRANSACTIONISOLATION = -1;
089
090    /** Guards property setters - once true, setters throw IllegalStateException */
091    private volatile boolean getConnectionCalled;
092
093    /** Underlying source of PooledConnections */
094    private ConnectionPoolDataSource dataSource;
095
096    /** DataSource Name used to find the ConnectionPoolDataSource */
097    private String dataSourceName;
098
099    /** Description */
100    private String description;
101
102    /** Environment that may be used to set up a JNDI initial context. */
103    private Properties jndiEnvironment;
104
105    /** Login Timeout */
106    private Duration loginTimeoutDuration = Duration.ZERO;
107
108    /** Log stream */
109    private PrintWriter logWriter;
110
111    /** Instance key */
112    private String instanceKey;
113
114    /** Pool property defaults to {@link BaseObjectPoolConfig#DEFAULT_BLOCK_WHEN_EXHAUSTED}. */
115    private volatile boolean defaultBlockWhenExhausted = BaseObjectPoolConfig.DEFAULT_BLOCK_WHEN_EXHAUSTED;
116
117    /** Pool property defaults to {@link BaseObjectPoolConfig#DEFAULT_EVICTION_POLICY_CLASS_NAME}. */
118    private String defaultEvictionPolicyClassName = BaseObjectPoolConfig.DEFAULT_EVICTION_POLICY_CLASS_NAME;
119
120    /** Pool property defaults to {@link BaseObjectPoolConfig#DEFAULT_LIFO}. */
121    private volatile boolean defaultLifo = BaseObjectPoolConfig.DEFAULT_LIFO;
122
123    /** Pool property defaults to {@link GenericKeyedObjectPoolConfig#DEFAULT_MAX_IDLE_PER_KEY}. */
124    private volatile int defaultMaxIdle = GenericKeyedObjectPoolConfig.DEFAULT_MAX_IDLE_PER_KEY;
125
126    /** Pool property defaults to {@link GenericKeyedObjectPoolConfig#DEFAULT_MAX_TOTAL}. */
127    private volatile int defaultMaxTotal = GenericKeyedObjectPoolConfig.DEFAULT_MAX_TOTAL;
128
129    /** Pool property defaults to {@link BaseObjectPoolConfig#DEFAULT_MAX_WAIT}. */
130    private Duration defaultMaxWaitDuration = BaseObjectPoolConfig.DEFAULT_MAX_WAIT;
131
132    /** Pool property defaults to {@link BaseObjectPoolConfig#DEFAULT_MIN_EVICTABLE_IDLE_DURATION}. */
133    private Duration defaultMinEvictableIdleDuration = BaseObjectPoolConfig.DEFAULT_MIN_EVICTABLE_IDLE_DURATION;
134
135    /** Pool property defaults to {@link GenericKeyedObjectPoolConfig#DEFAULT_MIN_IDLE_PER_KEY}. */
136    private volatile int defaultMinIdle = GenericKeyedObjectPoolConfig.DEFAULT_MIN_IDLE_PER_KEY;
137
138    /** Pool property defaults to {@link BaseObjectPoolConfig#DEFAULT_NUM_TESTS_PER_EVICTION_RUN}. */
139    private volatile int defaultNumTestsPerEvictionRun = BaseObjectPoolConfig.DEFAULT_NUM_TESTS_PER_EVICTION_RUN;
140
141    /** Pool property defaults to {@link BaseObjectPoolConfig#DEFAULT_SOFT_MIN_EVICTABLE_IDLE_DURATION}. */
142    private Duration defaultSoftMinEvictableIdleDuration = BaseObjectPoolConfig.DEFAULT_SOFT_MIN_EVICTABLE_IDLE_DURATION;
143
144    /** Pool property defaults to {@link BaseObjectPoolConfig#DEFAULT_TEST_ON_CREATE}. */
145    private volatile boolean defaultTestOnCreate = BaseObjectPoolConfig.DEFAULT_TEST_ON_CREATE;
146
147    /** Pool property defaults to {@link BaseObjectPoolConfig#DEFAULT_TEST_ON_BORROW}. */
148    private volatile boolean defaultTestOnBorrow = BaseObjectPoolConfig.DEFAULT_TEST_ON_BORROW;
149
150    /** Pool property defaults to {@link BaseObjectPoolConfig#DEFAULT_TEST_ON_RETURN}. */
151    private volatile boolean defaultTestOnReturn = BaseObjectPoolConfig.DEFAULT_TEST_ON_RETURN;
152
153    /** Pool property defaults to {@link BaseObjectPoolConfig#DEFAULT_TEST_WHILE_IDLE}. */
154    private volatile boolean defaultTestWhileIdle = BaseObjectPoolConfig.DEFAULT_TEST_WHILE_IDLE;
155
156    /** Pool property defaults to {@link BaseObjectPoolConfig#DEFAULT_DURATION_BETWEEN_EVICTION_RUNS}. */
157    private Duration defaultDurationBetweenEvictionRuns = BaseObjectPoolConfig.DEFAULT_DURATION_BETWEEN_EVICTION_RUNS;
158
159    /** Connection factory property defaults to null. */
160    private String validationQuery;
161
162    /** Connection factory property defaults to -1 seconds. */
163    private Duration validationQueryTimeoutDuration = Duration.ofSeconds(-1);
164
165    /** Connection factory property defaults to false. */
166    private volatile boolean rollbackAfterValidation;
167
168    /** Connection factory property defaults to -1 milliseconds. */
169    private Duration maxConnDuration = Duration.ofMillis(-1);
170
171    /** Connection property defaults to false. */
172    private Boolean defaultAutoCommit;
173
174    /** Connection property defaults to {@link #UNKNOWN_TRANSACTIONISOLATION}. */
175    private volatile int defaultTransactionIsolation = UNKNOWN_TRANSACTIONISOLATION;
176
177    /** Connection property defaults to false. */
178    private Boolean defaultReadOnly;
179
180    /**
181     * Default no-arg constructor for Serialization.
182     */
183    public InstanceKeyDataSource() {
184    }
185
186    /**
187     * Throws an IllegalStateException, if a PooledConnection has already been requested.
188     *
189     * @throws IllegalStateException Thrown if a PooledConnection has already been requested.
190     */
191    protected void assertInitializationAllowed() throws IllegalStateException {
192        if (getConnectionCalled) {
193            throw new IllegalStateException(GET_CONNECTION_CALLED);
194        }
195    }
196
197    /**
198     * Closes the connection pool being maintained by this datasource.
199     */
200    @Override
201    public abstract void close() throws SQLException;
202
203    private void closeDueToException(final PooledConnectionAndInfo info) {
204        if (info != null) {
205            try {
206                info.getPooledConnection().getConnection().close();
207            } catch (final Exception e) {
208                // do not throw this exception because we are in the middle
209                // of handling another exception. But record it because
210                // it potentially leaks connections from the pool.
211                getLogWriter().println("[ERROR] Could not return connection to pool during exception handling. " + e.getMessage());
212            }
213        }
214    }
215
216    /**
217     * Attempts to establish a database connection.
218     */
219    @Override
220    public Connection getConnection() throws SQLException {
221        return getConnection(null, null);
222    }
223
224    /**
225     * Attempts to retrieve a database connection using {@link #getPooledConnectionAndInfo(String, String)} with the
226     * provided user name and password. The password on the {@code PooledConnectionAndInfo} instance returned by
227     * {@code getPooledConnectionAndInfo} is compared to the {@code password} parameter. If the comparison
228     * fails, a database connection using the supplied user name and password is attempted. If the connection attempt
229     * fails, an SQLException is thrown, indicating that the given password did not match the password used to create
230     * the pooled connection. If the connection attempt succeeds, this means that the database password has been
231     * changed. In this case, the {@code PooledConnectionAndInfo} instance retrieved with the old password is
232     * destroyed and the {@code getPooledConnectionAndInfo} is repeatedly invoked until a
233     * {@code PooledConnectionAndInfo} instance with the new password is returned.
234     */
235    @Override
236    public Connection getConnection(final String userName, final String userPassword) throws SQLException {
237        if (instanceKey == null) {
238            throw new SQLException("Must set the ConnectionPoolDataSource "
239                    + "through setDataSourceName or setConnectionPoolDataSource before calling getConnection.");
240        }
241        getConnectionCalled = true;
242        PooledConnectionAndInfo info = null;
243        try {
244            info = getPooledConnectionAndInfo(userName, userPassword);
245        } catch (final RuntimeException | SQLException e) {
246            closeDueToException(info);
247            throw e;
248        } catch (final Exception e) {
249            closeDueToException(info);
250            throw new SQLException("Cannot borrow connection from pool", e);
251        }
252
253        // Password on PooledConnectionAndInfo does not match
254        if (!(null == userPassword ? null == info.getPassword() : userPassword.equals(info.getPassword()))) {
255            try { // See if password has changed by attempting connection
256                testCPDS(userName, userPassword);
257            } catch (final SQLException ex) {
258                // Password has not changed, so refuse client, but return connection to the pool
259                closeDueToException(info);
260                throw new SQLException(
261                        "Given password did not match password used to create the PooledConnection.", ex);
262            } catch (final javax.naming.NamingException ne) {
263                throw new SQLException("NamingException encountered connecting to database", ne);
264            }
265            /*
266             * Password must have changed -> destroy connection and keep retrying until we get a new, good one,
267             * destroying any idle connections with the old password as we pull them from the pool.
268             */
269            final UserPassKey upkey = info.getUserPassKey();
270            final PooledConnectionManager manager = getConnectionManager(upkey);
271            // Destroy and remove from pool
272            manager.invalidate(info.getPooledConnection());
273            // Reset the password on the factory if using CPDSConnectionFactory
274            manager.setPassword(upkey.getPasswordCharArray());
275            info = null;
276            for (int i = 0; i < 10; i++) { // Bound the number of retries - only needed if bad instances return
277                try {
278                    info = getPooledConnectionAndInfo(userName, userPassword);
279                } catch (final RuntimeException | SQLException e) {
280                    closeDueToException(info);
281                    throw e;
282                } catch (final Exception e) {
283                    closeDueToException(info);
284                    throw new SQLException("Cannot borrow connection from pool", e);
285                }
286                if (info != null && userPassword != null && userPassword.equals(info.getPassword())) {
287                    break;
288                }
289                if (info != null) {
290                    manager.invalidate(info.getPooledConnection());
291                }
292                info = null;
293            }
294            if (info == null) {
295                throw new SQLException("Cannot borrow connection from pool - password change failure.");
296            }
297        }
298
299        final Connection connection = info.getPooledConnection().getConnection();
300        try {
301            setupDefaults(connection, userName);
302            connection.clearWarnings();
303            return connection;
304        } catch (final SQLException ex) {
305            Utils.close(connection, e -> getLogWriter().println("ignoring exception during close: " + e));
306            throw ex;
307        }
308    }
309
310    /**
311     * Gets the pooled connection manager for the given key.
312     *
313     * @param upkey the key.
314     * @return the pooled connection manager for the given key.
315     */
316    protected abstract PooledConnectionManager getConnectionManager(UserPassKey upkey);
317
318    /**
319     * Gets the value of connectionPoolDataSource. This method will return null, if the backing data source is being
320     * accessed via JNDI.
321     *
322     * @return value of connectionPoolDataSource.
323     */
324    public ConnectionPoolDataSource getConnectionPoolDataSource() {
325        return dataSource;
326    }
327
328    /**
329     * Gets the name of the ConnectionPoolDataSource which backs this pool. This name is used to look up the data source
330     * from a JNDI service provider.
331     *
332     * @return value of dataSourceName.
333     */
334    public String getDataSourceName() {
335        return dataSourceName;
336    }
337
338    /**
339     * Gets the default value for {@link GenericKeyedObjectPoolConfig#getBlockWhenExhausted()} for each per user pool.
340     *
341     * @return The default value for {@link GenericKeyedObjectPoolConfig#getBlockWhenExhausted()} for each per user
342     *         pool.
343     */
344    public boolean getDefaultBlockWhenExhausted() {
345        return this.defaultBlockWhenExhausted;
346    }
347
348    /**
349     * Gets the default value for {@link GenericObjectPool#getDurationBetweenEvictionRuns()} for each per user pool.
350     *
351     * @return The default value for {@link GenericObjectPool#getDurationBetweenEvictionRuns()} for each per user pool.
352     * @since 2.10.0
353     */
354    public Duration getDefaultDurationBetweenEvictionRuns() {
355        return this.defaultDurationBetweenEvictionRuns;
356    }
357
358    /**
359     * Gets the default value for {@link GenericKeyedObjectPoolConfig#getEvictionPolicyClassName()} for each per user
360     * pool.
361     *
362     * @return The default value for {@link GenericKeyedObjectPoolConfig#getEvictionPolicyClassName()} for each per user
363     *         pool.
364     */
365    public String getDefaultEvictionPolicyClassName() {
366        return this.defaultEvictionPolicyClassName;
367    }
368
369    /**
370     * Gets the default value for {@link GenericKeyedObjectPoolConfig#getLifo()} for each per user pool.
371     *
372     * @return The default value for {@link GenericKeyedObjectPoolConfig#getLifo()} for each per user pool.
373     */
374    public boolean getDefaultLifo() {
375        return this.defaultLifo;
376    }
377
378    /**
379     * Gets the default value for {@link GenericKeyedObjectPoolConfig#getMaxIdlePerKey()} for each per user pool.
380     *
381     * @return The default value for {@link GenericKeyedObjectPoolConfig#getMaxIdlePerKey()} for each per user pool.
382     */
383    public int getDefaultMaxIdle() {
384        return this.defaultMaxIdle;
385    }
386
387    /**
388     * Gets the default value for {@link GenericKeyedObjectPoolConfig#getMaxTotalPerKey()} for each per user pool.
389     *
390     * @return The default value for {@link GenericKeyedObjectPoolConfig#getMaxTotalPerKey()} for each per user pool.
391     */
392    public int getDefaultMaxTotal() {
393        return this.defaultMaxTotal;
394    }
395
396    /**
397     * Gets the default value for {@link GenericKeyedObjectPoolConfig#getMaxWaitDuration()} for each per user pool.
398     *
399     * @return The default value for {@link GenericKeyedObjectPoolConfig#getMaxWaitDuration()} for each per user pool.
400     * @since 2.9.0
401     */
402    public Duration getDefaultMaxWait() {
403        return this.defaultMaxWaitDuration;
404    }
405
406    /**
407     * Gets the default value for {@link GenericKeyedObjectPoolConfig#getMaxWaitDuration()} for each per user pool.
408     *
409     * @return The default value for {@link GenericKeyedObjectPoolConfig#getMaxWaitDuration()} for each per user pool.
410     * @deprecated Use {@link #getDefaultMaxWait()}.
411     */
412    @Deprecated
413    public long getDefaultMaxWaitMillis() {
414        return getDefaultMaxWait().toMillis();
415    }
416
417    /**
418     * Gets the default value for {@link GenericKeyedObjectPoolConfig#getMinEvictableIdleDuration()} for each per user
419     * pool.
420     *
421     * @return The default value for {@link GenericKeyedObjectPoolConfig#getMinEvictableIdleDuration()} for each per
422     *         user pool.
423     * @since 2.10.0
424     */
425    public Duration getDefaultMinEvictableIdleDuration() {
426        return this.defaultMinEvictableIdleDuration;
427    }
428
429    /**
430     * Gets the default value for {@link GenericKeyedObjectPoolConfig#getMinEvictableIdleDuration()} for each per user
431     * pool.
432     *
433     * @return The default value for {@link GenericKeyedObjectPoolConfig#getMinEvictableIdleDuration()} for each per
434     *         user pool.
435     * @deprecated Use {@link #getDefaultMinEvictableIdleDuration()}.
436     */
437    @Deprecated
438    public long getDefaultMinEvictableIdleTimeMillis() {
439        return this.defaultMinEvictableIdleDuration.toMillis();
440    }
441
442    /**
443     * Gets the default value for {@link GenericKeyedObjectPoolConfig#getMinIdlePerKey()} for each per user pool.
444     *
445     * @return The default value for {@link GenericKeyedObjectPoolConfig#getMinIdlePerKey()} for each per user pool.
446     */
447    public int getDefaultMinIdle() {
448        return this.defaultMinIdle;
449    }
450
451    /**
452     * Gets the default value for {@link GenericKeyedObjectPoolConfig#getNumTestsPerEvictionRun()} for each per user
453     * pool.
454     *
455     * @return The default value for {@link GenericKeyedObjectPoolConfig#getNumTestsPerEvictionRun()} for each per user
456     *         pool.
457     */
458    public int getDefaultNumTestsPerEvictionRun() {
459        return this.defaultNumTestsPerEvictionRun;
460    }
461
462    /**
463     * Gets the default value for {@link
464     * GenericObjectPool#getSoftMinEvictableIdleTimeMillis()} for each per user pool.
465     *
466     * @return The default value for {@link
467     *         GenericObjectPool#getSoftMinEvictableIdleTimeMillis()} for each per user pool.
468     * @since 2.10.0
469     */
470    public Duration getDefaultSoftMinEvictableIdleDuration() {
471        return this.defaultSoftMinEvictableIdleDuration;
472    }
473
474    /**
475     * Gets the default value for {@link
476     * GenericObjectPool#getSoftMinEvictableIdleTimeMillis()} for each per user pool.
477     *
478     * @return The default value for {@link
479     *         GenericObjectPool#getSoftMinEvictableIdleTimeMillis()} for each per user pool.
480     * @deprecated Use {@link #getDefaultSoftMinEvictableIdleDuration()}.
481     */
482    @Deprecated
483    public long getDefaultSoftMinEvictableIdleTimeMillis() {
484        return this.defaultSoftMinEvictableIdleDuration.toMillis();
485    }
486
487    /**
488     * Gets the default value for {@link
489     * GenericObjectPool#getTestOnBorrow()} for each per user pool.
490     *
491     * @return The default value for {@link
492     *         GenericObjectPool#getTestOnBorrow()} for each per user pool.
493     */
494    public boolean getDefaultTestOnBorrow() {
495        return this.defaultTestOnBorrow;
496    }
497
498    /**
499     * Gets the default value for {@link
500     * GenericObjectPool#getTestOnCreate()} for each per user pool.
501     *
502     * @return The default value for {@link
503     *         GenericObjectPool#getTestOnCreate()} for each per user pool.
504     */
505    public boolean getDefaultTestOnCreate() {
506        return this.defaultTestOnCreate;
507    }
508
509    /**
510     * Gets the default value for {@link
511     * GenericObjectPool#getTestOnReturn()} for each per user pool.
512     *
513     * @return The default value for {@link
514     *         GenericObjectPool#getTestOnReturn()} for each per user pool.
515     */
516    public boolean getDefaultTestOnReturn() {
517        return this.defaultTestOnReturn;
518    }
519
520    /**
521     * Gets the default value for {@link
522     * GenericObjectPool#getTestWhileIdle()} for each per user pool.
523     *
524     * @return The default value for {@link
525     *         GenericObjectPool#getTestWhileIdle()} for each per user pool.
526     */
527    public boolean getDefaultTestWhileIdle() {
528        return this.defaultTestWhileIdle;
529    }
530
531    /**
532     * Gets the default value for {@link GenericObjectPool#getDurationBetweenEvictionRuns ()} for each per user pool.
533     *
534     * @return The default value for {@link GenericObjectPool#getDurationBetweenEvictionRuns ()} for each per user pool.
535     * @deprecated Use {@link #getDefaultDurationBetweenEvictionRuns()}.
536     */
537    @Deprecated
538    public long getDefaultTimeBetweenEvictionRunsMillis() {
539        return this.defaultDurationBetweenEvictionRuns.toMillis();
540    }
541
542    /**
543     * Gets the value of defaultTransactionIsolation, which defines the state of connections handed out from this pool.
544     * The value can be changed on the Connection using Connection.setTransactionIsolation(int). If this method returns
545     * -1, the default is JDBC driver dependent.
546     *
547     * @return value of defaultTransactionIsolation.
548     */
549    public int getDefaultTransactionIsolation() {
550        return defaultTransactionIsolation;
551    }
552
553    /**
554     * Gets the description. This property is defined by JDBC as for use with GUI (or other) tools that might deploy the
555     * datasource. It serves no internal purpose.
556     *
557     * @return value of description.
558     */
559    public String getDescription() {
560        return description;
561    }
562
563    /**
564     * Gets the instance key.
565     *
566     * @return the instance key.
567     */
568    protected String getInstanceKey() {
569        return instanceKey;
570    }
571
572    /**
573     * Gets the value of jndiEnvironment which is used when instantiating a JNDI InitialContext. This InitialContext is
574     * used to locate the back end ConnectionPoolDataSource.
575     *
576     * @param key
577     *            JNDI environment key.
578     * @return value of jndiEnvironment.
579     */
580    public String getJndiEnvironment(final String key) {
581        String value = null;
582        if (jndiEnvironment != null) {
583            value = jndiEnvironment.getProperty(key);
584        }
585        return value;
586    }
587
588    /**
589     * Gets the value of loginTimeout.
590     *
591     * @return value of loginTimeout.
592     * @deprecated Use {@link #getLoginTimeoutDuration()}.
593     */
594    @Deprecated
595    @Override
596    public int getLoginTimeout() {
597        return (int) loginTimeoutDuration.getSeconds();
598    }
599
600    /**
601     * Gets the value of loginTimeout.
602     *
603     * @return value of loginTimeout.
604     * @since 2.10.0
605     */
606    public Duration getLoginTimeoutDuration() {
607        return loginTimeoutDuration;
608    }
609
610    /**
611     * Gets the value of logWriter.
612     *
613     * @return value of logWriter.
614     */
615    @Override
616    public PrintWriter getLogWriter() {
617        if (logWriter == null) {
618            logWriter = new PrintWriter(new OutputStreamWriter(System.out, StandardCharsets.UTF_8));
619        }
620        return logWriter;
621    }
622
623    /**
624     * Gets the maximum permitted lifetime of a connection. A value of zero or less indicates an
625     * infinite lifetime.
626     *
627     * @return The maximum permitted lifetime of a connection. A value of zero or less indicates an
628     *         infinite lifetime.
629     * @since 2.10.0
630     */
631    public Duration getMaxConnDuration() {
632        return maxConnDuration;
633    }
634
635    /**
636     * Gets the maximum permitted lifetime of a connection. A value of zero or less indicates an
637     * infinite lifetime.
638     *
639     * @return The maximum permitted lifetime of a connection. A value of zero or less indicates an
640     *         infinite lifetime.
641     * @deprecated Use {@link #getMaxConnDuration()}.
642     */
643    @Deprecated
644    public Duration getMaxConnLifetime() {
645        return maxConnDuration;
646    }
647
648    /**
649     * Gets the maximum permitted lifetime of a connection in milliseconds. A value of zero or less indicates an
650     * infinite lifetime.
651     *
652     * @return The maximum permitted lifetime of a connection in milliseconds. A value of zero or less indicates an
653     *         infinite lifetime.
654     * @deprecated Use {@link #getMaxConnLifetime()}.
655     */
656    @Deprecated
657    public long getMaxConnLifetimeMillis() {
658        return maxConnDuration.toMillis();
659    }
660
661    @Override
662    public Logger getParentLogger() throws SQLFeatureNotSupportedException {
663        throw new SQLFeatureNotSupportedException();
664    }
665
666    /**
667     * This method is protected but can only be implemented in this package because PooledConnectionAndInfo is a package
668     * private type.
669     *
670     * @param userName The user name.
671     * @param userPassword The user password.
672     * @return Matching PooledConnectionAndInfo.
673     * @throws SQLException Connection or registration failure.
674     */
675    protected abstract PooledConnectionAndInfo getPooledConnectionAndInfo(String userName, String userPassword)
676            throws SQLException;
677
678    /**
679     * Gets the SQL query that will be used to validate connections from this pool before returning them to the caller.
680     * If specified, this query <strong>MUST</strong> be an SQL SELECT statement that returns at least one row. If not
681     * specified, {@link Connection#isValid(int)} will be used to validate connections.
682     *
683     * @return The SQL query that will be used to validate connections from this pool before returning them to the
684     *         caller.
685     */
686    public String getValidationQuery() {
687        return this.validationQuery;
688    }
689
690    /**
691     * Returns the timeout in seconds before the validation query fails.
692     *
693     * @return The timeout in seconds before the validation query fails.
694     * @deprecated Use {@link #getValidationQueryTimeoutDuration()}.
695     */
696    @Deprecated
697    public int getValidationQueryTimeout() {
698        return (int) validationQueryTimeoutDuration.getSeconds();
699    }
700
701    /**
702     * Returns the timeout Duration before the validation query fails.
703     *
704     * @return The timeout Duration before the validation query fails.
705     */
706    public Duration getValidationQueryTimeoutDuration() {
707        return validationQueryTimeoutDuration;
708    }
709
710    /**
711     * Gets the value of defaultAutoCommit, which defines the state of connections handed out from this pool. The value
712     * can be changed on the Connection using Connection.setAutoCommit(boolean). The default is {@code null} which
713     * will use the default value for the driver.
714     *
715     * @return value of defaultAutoCommit.
716     */
717    public Boolean isDefaultAutoCommit() {
718        return defaultAutoCommit;
719    }
720
721    /**
722     * Gets the value of defaultReadOnly, which defines the state of connections handed out from this pool. The value
723     * can be changed on the Connection using Connection.setReadOnly(boolean). The default is {@code null} which
724     * will use the default value for the driver.
725     *
726     * @return value of defaultReadOnly.
727     */
728    public Boolean isDefaultReadOnly() {
729        return defaultReadOnly;
730    }
731
732    /**
733     * Tests whether a rollback will be issued after executing the SQL query that will be used to validate connections from
734     * this pool before returning them to the caller.
735     *
736     * @return true if a rollback will be issued after executing the validation query
737     */
738    public boolean isRollbackAfterValidation() {
739        return this.rollbackAfterValidation;
740    }
741
742    @Override
743    public boolean isWrapperFor(final Class<?> iface) throws SQLException {
744        return iface.isInstance(this);
745    }
746
747    /**
748     * Sets the back end ConnectionPoolDataSource. This property should not be set if using JNDI to access the
749     * data source.
750     *
751     * @param dataSource
752     *            Value to assign to connectionPoolDataSource.
753     */
754    public void setConnectionPoolDataSource(final ConnectionPoolDataSource dataSource) {
755        assertInitializationAllowed();
756        if (dataSourceName != null) {
757            throw new IllegalStateException("Cannot set the DataSource, if JNDI is used.");
758        }
759        if (this.dataSource != null) {
760            throw new IllegalStateException("The CPDS has already been set. It cannot be altered.");
761        }
762        this.dataSource = dataSource;
763        instanceKey = InstanceKeyDataSourceFactory.registerNewInstance(this);
764    }
765
766    /**
767     * Sets the name of the ConnectionPoolDataSource which backs this pool. This name is used to look up the data source
768     * from a JNDI service provider.
769     *
770     * @param dataSourceName
771     *            Value to assign to dataSourceName.
772     */
773    public void setDataSourceName(final String dataSourceName) {
774        assertInitializationAllowed();
775        if (dataSource != null) {
776            throw new IllegalStateException("Cannot set the JNDI name for the DataSource, if already "
777                    + "set using setConnectionPoolDataSource.");
778        }
779        if (this.dataSourceName != null) {
780            throw new IllegalStateException("The DataSourceName has already been set. It cannot be altered.");
781        }
782        this.dataSourceName = dataSourceName;
783        instanceKey = InstanceKeyDataSourceFactory.registerNewInstance(this);
784    }
785
786    /**
787     * Sets the value of defaultAutoCommit, which defines the state of connections handed out from this pool. The value
788     * can be changed on the Connection using Connection.setAutoCommit(boolean). The default is {@code null} which
789     * will use the default value for the driver.
790     *
791     * @param defaultAutoCommit
792     *            Value to assign to defaultAutoCommit.
793     */
794    public void setDefaultAutoCommit(final Boolean defaultAutoCommit) {
795        assertInitializationAllowed();
796        this.defaultAutoCommit = defaultAutoCommit;
797    }
798
799    /**
800     * Sets the default value for {@link GenericKeyedObjectPoolConfig#getBlockWhenExhausted()} for each per user pool.
801     *
802     * @param blockWhenExhausted
803     *            The default value for {@link GenericKeyedObjectPoolConfig#getBlockWhenExhausted()} for each per user
804     *            pool.
805     */
806    public void setDefaultBlockWhenExhausted(final boolean blockWhenExhausted) {
807        assertInitializationAllowed();
808        this.defaultBlockWhenExhausted = blockWhenExhausted;
809    }
810
811    /**
812     * Sets the default value for {@link GenericObjectPool#getDurationBetweenEvictionRuns ()} for each per user pool.
813     *
814     * @param defaultDurationBetweenEvictionRuns The default value for
815     *        {@link GenericObjectPool#getDurationBetweenEvictionRuns ()} for each per user pool.
816     * @since 2.10.0
817     */
818    public void setDefaultDurationBetweenEvictionRuns(final Duration defaultDurationBetweenEvictionRuns) {
819        assertInitializationAllowed();
820        this.defaultDurationBetweenEvictionRuns = defaultDurationBetweenEvictionRuns;
821    }
822
823    /**
824     * Sets the default value for {@link GenericKeyedObjectPoolConfig#getEvictionPolicyClassName()} for each per user
825     * pool.
826     *
827     * @param evictionPolicyClassName
828     *            The default value for {@link GenericKeyedObjectPoolConfig#getEvictionPolicyClassName()} for each per
829     *            user pool.
830     */
831    public void setDefaultEvictionPolicyClassName(final String evictionPolicyClassName) {
832        assertInitializationAllowed();
833        this.defaultEvictionPolicyClassName = evictionPolicyClassName;
834    }
835
836    /**
837     * Sets the default value for {@link GenericKeyedObjectPoolConfig#getLifo()} for each per user pool.
838     *
839     * @param lifo
840     *            The default value for {@link GenericKeyedObjectPoolConfig#getLifo()} for each per user pool.
841     */
842    public void setDefaultLifo(final boolean lifo) {
843        assertInitializationAllowed();
844        this.defaultLifo = lifo;
845    }
846
847    /**
848     * Sets the default value for {@link GenericKeyedObjectPoolConfig#getMaxIdlePerKey()} for each per user pool.
849     *
850     * @param maxIdle
851     *            The default value for {@link GenericKeyedObjectPoolConfig#getMaxIdlePerKey()} for each per user pool.
852     */
853    public void setDefaultMaxIdle(final int maxIdle) {
854        assertInitializationAllowed();
855        this.defaultMaxIdle = maxIdle;
856    }
857
858    /**
859     * Sets the default value for {@link GenericKeyedObjectPoolConfig#getMaxTotalPerKey()} for each per user pool.
860     *
861     * @param maxTotal
862     *            The default value for {@link GenericKeyedObjectPoolConfig#getMaxTotalPerKey()} for each per user pool.
863     */
864    public void setDefaultMaxTotal(final int maxTotal) {
865        assertInitializationAllowed();
866        this.defaultMaxTotal = maxTotal;
867    }
868
869    /**
870     * Sets the default value for {@link GenericKeyedObjectPoolConfig#getMaxWaitDuration()} for each per user pool.
871     *
872     * @param maxWaitMillis
873     *            The default value for {@link GenericKeyedObjectPoolConfig#getMaxWaitDuration()} for each per user pool.
874     * @since 2.9.0
875     */
876    public void setDefaultMaxWait(final Duration maxWaitMillis) {
877        assertInitializationAllowed();
878        this.defaultMaxWaitDuration = maxWaitMillis;
879    }
880
881    /**
882     * Sets the default value for {@link GenericKeyedObjectPoolConfig#getMaxWaitMillis()} for each per user pool.
883     *
884     * @param maxWaitMillis
885     *            The default value for {@link GenericKeyedObjectPoolConfig#getMaxWaitMillis()} for each per user pool.
886     * @deprecated Use {@link #setDefaultMaxWait(Duration)}.
887     */
888    @Deprecated
889    public void setDefaultMaxWaitMillis(final long maxWaitMillis) {
890        setDefaultMaxWait(Duration.ofMillis(maxWaitMillis));
891    }
892
893    /**
894     * Sets the default value for {@link GenericKeyedObjectPoolConfig#getMinEvictableIdleDuration()} for each per user
895     * pool.
896     *
897     * @param defaultMinEvictableIdleDuration
898     *            The default value for {@link GenericKeyedObjectPoolConfig#getMinEvictableIdleDuration()} for each
899     *            per user pool.
900     * @since 2.10.0
901     */
902    public void setDefaultMinEvictableIdle(final Duration defaultMinEvictableIdleDuration) {
903        assertInitializationAllowed();
904        this.defaultMinEvictableIdleDuration = defaultMinEvictableIdleDuration;
905    }
906
907    /**
908     * Sets the default value for {@link GenericKeyedObjectPoolConfig#getMinEvictableIdleDuration()} for each per user
909     * pool.
910     *
911     * @param minEvictableIdleTimeMillis
912     *            The default value for {@link GenericKeyedObjectPoolConfig#getMinEvictableIdleDuration()} for each
913     *            per user pool.
914     * @deprecated Use {@link #setDefaultMinEvictableIdle(Duration)}.
915     */
916    @Deprecated
917    public void setDefaultMinEvictableIdleTimeMillis(final long minEvictableIdleTimeMillis) {
918        assertInitializationAllowed();
919        this.defaultMinEvictableIdleDuration = Duration.ofMillis(minEvictableIdleTimeMillis);
920    }
921
922    /**
923     * Sets the default value for {@link GenericKeyedObjectPoolConfig#getMinIdlePerKey()} for each per user pool.
924     *
925     * @param minIdle
926     *            The default value for {@link GenericKeyedObjectPoolConfig#getMinIdlePerKey()} for each per user pool.
927     */
928    public void setDefaultMinIdle(final int minIdle) {
929        assertInitializationAllowed();
930        this.defaultMinIdle = minIdle;
931    }
932
933    /**
934     * Sets the default value for {@link GenericKeyedObjectPoolConfig#getNumTestsPerEvictionRun()} for each per user
935     * pool.
936     *
937     * @param numTestsPerEvictionRun
938     *            The default value for {@link GenericKeyedObjectPoolConfig#getNumTestsPerEvictionRun()} for each per
939     *            user pool.
940     */
941    public void setDefaultNumTestsPerEvictionRun(final int numTestsPerEvictionRun) {
942        assertInitializationAllowed();
943        this.defaultNumTestsPerEvictionRun = numTestsPerEvictionRun;
944    }
945
946    /**
947     * Sets the value of defaultReadOnly, which defines the state of connections handed out from this pool. The value
948     * can be changed on the Connection using Connection.setReadOnly(boolean). The default is {@code null} which
949     * will use the default value for the driver.
950     *
951     * @param defaultReadOnly
952     *            Value to assign to defaultReadOnly.
953     */
954    public void setDefaultReadOnly(final Boolean defaultReadOnly) {
955        assertInitializationAllowed();
956        this.defaultReadOnly = defaultReadOnly;
957    }
958
959    /**
960     * Sets the default value for {@link
961     * GenericObjectPool#getSoftMinEvictableIdleTimeMillis()} for each per user pool.
962     *
963     * @param defaultSoftMinEvictableIdleDuration
964     *            The default value for {@link
965     *            GenericObjectPool#getSoftMinEvictableIdleTimeMillis()} for each per user pool.
966     * @since 2.10.0
967     */
968    public void setDefaultSoftMinEvictableIdle(final Duration defaultSoftMinEvictableIdleDuration) {
969        assertInitializationAllowed();
970        this.defaultSoftMinEvictableIdleDuration = defaultSoftMinEvictableIdleDuration;
971    }
972
973    /**
974     * Sets the default value for {@link
975     * GenericObjectPool#getSoftMinEvictableIdleTimeMillis()} for each per user pool.
976     *
977     * @param softMinEvictableIdleTimeMillis
978     *            The default value for {@link
979     *            GenericObjectPool#getSoftMinEvictableIdleTimeMillis()} for each per user pool.
980     * @deprecated Use {@link #setDefaultSoftMinEvictableIdle(Duration)}.
981     */
982    @Deprecated
983    public void setDefaultSoftMinEvictableIdleTimeMillis(final long softMinEvictableIdleTimeMillis) {
984        assertInitializationAllowed();
985        this.defaultSoftMinEvictableIdleDuration = Duration.ofMillis(softMinEvictableIdleTimeMillis);
986    }
987
988    /**
989     * Sets the default value for {@link
990     * GenericObjectPool#getTestOnBorrow()} for each per user pool.
991     *
992     * @param testOnBorrow
993     *            The default value for {@link
994     *            GenericObjectPool#getTestOnBorrow()} for each per user pool.
995     */
996    public void setDefaultTestOnBorrow(final boolean testOnBorrow) {
997        assertInitializationAllowed();
998        this.defaultTestOnBorrow = testOnBorrow;
999    }
1000
1001    /**
1002     * Sets the default value for {@link
1003     * GenericObjectPool#getTestOnCreate()} for each per user pool.
1004     *
1005     * @param testOnCreate
1006     *            The default value for {@link
1007     *            GenericObjectPool#getTestOnCreate()} for each per user pool.
1008     */
1009    public void setDefaultTestOnCreate(final boolean testOnCreate) {
1010        assertInitializationAllowed();
1011        this.defaultTestOnCreate = testOnCreate;
1012    }
1013
1014    /**
1015     * Sets the default value for {@link
1016     * GenericObjectPool#getTestOnReturn()} for each per user pool.
1017     *
1018     * @param testOnReturn
1019     *            The default value for {@link
1020     *            GenericObjectPool#getTestOnReturn()} for each per user pool.
1021     */
1022    public void setDefaultTestOnReturn(final boolean testOnReturn) {
1023        assertInitializationAllowed();
1024        this.defaultTestOnReturn = testOnReturn;
1025    }
1026
1027    /**
1028     * Sets the default value for {@link
1029     * GenericObjectPool#getTestWhileIdle()} for each per user pool.
1030     *
1031     * @param testWhileIdle
1032     *            The default value for {@link
1033     *            GenericObjectPool#getTestWhileIdle()} for each per user pool.
1034     */
1035    public void setDefaultTestWhileIdle(final boolean testWhileIdle) {
1036        assertInitializationAllowed();
1037        this.defaultTestWhileIdle = testWhileIdle;
1038    }
1039
1040    /**
1041     * Sets the default value for {@link GenericObjectPool#getDurationBetweenEvictionRuns()} for each per user pool.
1042     *
1043     * @param timeBetweenEvictionRunsMillis The default value for
1044     *        {@link GenericObjectPool#getDurationBetweenEvictionRuns()} for each per user pool.
1045     * @deprecated Use {@link #setDefaultDurationBetweenEvictionRuns(Duration)}.
1046     */
1047    @Deprecated
1048    public void setDefaultTimeBetweenEvictionRunsMillis(final long timeBetweenEvictionRunsMillis) {
1049        assertInitializationAllowed();
1050        this.defaultDurationBetweenEvictionRuns = Duration.ofMillis(timeBetweenEvictionRunsMillis);
1051    }
1052
1053    /**
1054     * Sets the value of defaultTransactionIsolation, which defines the state of connections handed out from this pool.
1055     * The value can be changed on the Connection using Connection.setTransactionIsolation(int). The default is JDBC
1056     * driver dependent.
1057     *
1058     * @param defaultTransactionIsolation
1059     *            Value to assign to defaultTransactionIsolation
1060     */
1061    public void setDefaultTransactionIsolation(final int defaultTransactionIsolation) {
1062        assertInitializationAllowed();
1063        switch (defaultTransactionIsolation) {
1064        case Connection.TRANSACTION_NONE:
1065        case Connection.TRANSACTION_READ_COMMITTED:
1066        case Connection.TRANSACTION_READ_UNCOMMITTED:
1067        case Connection.TRANSACTION_REPEATABLE_READ:
1068        case Connection.TRANSACTION_SERIALIZABLE:
1069            break;
1070        default:
1071            throw new IllegalArgumentException(BAD_TRANSACTION_ISOLATION);
1072        }
1073        this.defaultTransactionIsolation = defaultTransactionIsolation;
1074    }
1075
1076    /**
1077     * Sets the description. This property is defined by JDBC as for use with GUI (or other) tools that might deploy the
1078     * datasource. It serves no internal purpose.
1079     *
1080     * @param description
1081     *            Value to assign to description.
1082     */
1083    public void setDescription(final String description) {
1084        this.description = description;
1085    }
1086
1087    /**
1088     * Sets the JNDI environment to be used when instantiating a JNDI InitialContext. This InitialContext is used to
1089     * locate the back end ConnectionPoolDataSource.
1090     *
1091     * @param properties
1092     *            the JNDI environment property to set which will overwrite any current settings
1093     */
1094    void setJndiEnvironment(final Properties properties) {
1095        if (jndiEnvironment == null) {
1096            jndiEnvironment = new Properties();
1097        } else {
1098            jndiEnvironment.clear();
1099        }
1100        jndiEnvironment.putAll(properties);
1101    }
1102
1103    /**
1104     * Sets the value of the given JNDI environment property to be used when instantiating a JNDI InitialContext. This
1105     * InitialContext is used to locate the back end ConnectionPoolDataSource.
1106     *
1107     * @param key
1108     *            the JNDI environment property to set.
1109     * @param value
1110     *            the value assigned to specified JNDI environment property.
1111     */
1112    public void setJndiEnvironment(final String key, final String value) {
1113        if (jndiEnvironment == null) {
1114            jndiEnvironment = new Properties();
1115        }
1116        jndiEnvironment.setProperty(key, value);
1117    }
1118
1119    /**
1120     * Sets the value of loginTimeout.
1121     *
1122     * @param loginTimeout
1123     *            Value to assign to loginTimeout.
1124     * @since 2.10.0
1125     */
1126    public void setLoginTimeout(final Duration loginTimeout) {
1127        this.loginTimeoutDuration = loginTimeout;
1128    }
1129
1130    /**
1131     * Sets the value of loginTimeout.
1132     *
1133     * @param loginTimeout
1134     *            Value to assign to loginTimeout.
1135     * @deprecated Use {@link #setLoginTimeout(Duration)}.
1136     */
1137    @Deprecated
1138    @Override
1139    public void setLoginTimeout(final int loginTimeout) {
1140        this.loginTimeoutDuration = Duration.ofSeconds(loginTimeout);
1141    }
1142
1143    /**
1144     * Sets the value of logWriter.
1145     *
1146     * @param logWriter
1147     *            Value to assign to logWriter.
1148     */
1149    @Override
1150    public void setLogWriter(final PrintWriter logWriter) {
1151        this.logWriter = logWriter;
1152    }
1153
1154    /**
1155     * <p>
1156     * Sets the maximum permitted lifetime of a connection. A value of zero or less indicates an infinite lifetime.
1157     * </p>
1158     * <p>
1159     * Note: this method currently has no effect once the pool has been initialized. The pool is initialized the first time one of the following methods is
1160     * invoked: {@link #getConnection()}, {@link #setLogWriter(PrintWriter)}, {@link #setLoginTimeout(Duration)}, {@link #getLoginTimeoutDuration()},
1161     * {@link #getLogWriter()}.
1162     * </p>
1163     *
1164     * @param maxConnLifetimeMillis The maximum permitted lifetime of a connection. A value of zero or less indicates an infinite lifetime.
1165     * @since 2.9.0
1166     */
1167    public void setMaxConnLifetime(final Duration maxConnLifetimeMillis) {
1168        this.maxConnDuration = maxConnLifetimeMillis;
1169    }
1170
1171    /**
1172     * <p>
1173     * Sets the maximum permitted lifetime of a connection in milliseconds. A value of zero or less indicates an infinite lifetime.
1174     * </p>
1175     * <p>
1176     * Note: this method currently has no effect once the pool has been initialized. The pool is initialized the first time one of the following methods is
1177     * invoked: {@link #getConnection()}, {@link #setLogWriter(PrintWriter)}, {@link #setLoginTimeout(Duration)}, {@link #getLoginTimeoutDuration()},
1178     * {@link #getLogWriter()}.
1179     * </p>
1180     *
1181     * @param maxConnLifetimeMillis The maximum permitted lifetime of a connection in milliseconds. A value of zero or less indicates an infinite lifetime.
1182     * @deprecated Use {@link #setMaxConnLifetime(Duration)}.
1183     */
1184    @Deprecated
1185    public void setMaxConnLifetimeMillis(final long maxConnLifetimeMillis) {
1186        setMaxConnLifetime(Duration.ofMillis(maxConnLifetimeMillis));
1187    }
1188
1189    /**
1190     * Sets whether a rollback will be issued after executing the SQL query that will be used to validate connections from
1191     * this pool before returning them to the caller. Default behavior is NOT to issue a rollback. The setting will only
1192     * have an effect if a validation query is set
1193     *
1194     * @param rollbackAfterValidation
1195     *            new property value
1196     */
1197    public void setRollbackAfterValidation(final boolean rollbackAfterValidation) {
1198        assertInitializationAllowed();
1199        this.rollbackAfterValidation = rollbackAfterValidation;
1200    }
1201
1202    /**
1203     * Sets up the defaults for a given connection.
1204     *
1205     * @param connection The target connection.
1206     * @param userName   The user name for the connection.
1207     * @throws SQLException if a database access error occurs or this method is called on a closed connection
1208     */
1209    protected abstract void setupDefaults(Connection connection, String userName) throws SQLException;
1210
1211    /**
1212     * Sets the SQL query that will be used to validate connections from this pool before returning them to the caller.
1213     * If specified, this query <strong>MUST</strong> be an SQL SELECT statement that returns at least one row. If not
1214     * specified, connections will be validated using {@link Connection#isValid(int)}.
1215     *
1216     * @param validationQuery
1217     *            The SQL query that will be used to validate connections from this pool before returning them to the
1218     *            caller.
1219     */
1220    public void setValidationQuery(final String validationQuery) {
1221        assertInitializationAllowed();
1222        this.validationQuery = validationQuery;
1223    }
1224
1225    /**
1226     * Sets the timeout duration before the validation query fails.
1227     *
1228     * @param validationQueryTimeoutDuration
1229     *            The new timeout duration.
1230     */
1231    public void setValidationQueryTimeout(final Duration validationQueryTimeoutDuration) {
1232        this.validationQueryTimeoutDuration = validationQueryTimeoutDuration;
1233    }
1234
1235    /**
1236     * Sets the timeout in seconds before the validation query fails.
1237     *
1238     * @param validationQueryTimeoutSeconds
1239     *            The new timeout in seconds
1240     * @deprecated Use {@link #setValidationQueryTimeout(Duration)}.
1241     */
1242    @Deprecated
1243    public void setValidationQueryTimeout(final int validationQueryTimeoutSeconds) {
1244        this.validationQueryTimeoutDuration = Duration.ofSeconds(validationQueryTimeoutSeconds);
1245    }
1246
1247    /**
1248     * Tests and returns whether a JNDI context can be created to lookup a ConnectionPoolDataSource to then access a PooledConnection connection.
1249     *
1250     * @param userName     An optional user name, may be null.
1251     * @param userPassword An optional user user password, may be null.
1252     * @return A ConnectionPoolDataSource from a JNDI context.
1253     * @throws javax.naming.NamingException if a naming exception is encountered.
1254     * @throws SQLException                 if a ConnectionPoolDataSource or PooledConnection is not available.
1255     */
1256    protected ConnectionPoolDataSource testCPDS(final String userName, final String userPassword)
1257            throws javax.naming.NamingException, SQLException {
1258        // The source of physical database connections
1259        ConnectionPoolDataSource cpds = this.dataSource;
1260        if (cpds == null) {
1261            Context ctx = null;
1262            if (jndiEnvironment == null) {
1263                ctx = new InitialContext();
1264            } else {
1265                ctx = new InitialContext(jndiEnvironment);
1266            }
1267            final Object ds = ctx.lookup(dataSourceName);
1268            if (!(ds instanceof ConnectionPoolDataSource)) {
1269                throw new SQLException("Illegal configuration: DataSource " + dataSourceName + " ("
1270                        + ds.getClass().getName() + ") doesn't implement javax.sql.ConnectionPoolDataSource");
1271            }
1272            cpds = (ConnectionPoolDataSource) ds;
1273        }
1274        // try to get a connection with the supplied userName/password
1275        PooledConnection conn = null;
1276        try {
1277            if (userName != null) {
1278                conn = cpds.getPooledConnection(userName, userPassword);
1279            } else {
1280                conn = cpds.getPooledConnection();
1281            }
1282            if (conn == null) {
1283                throw new SQLException("Cannot connect using the supplied userName/password");
1284            }
1285        } finally {
1286            if (conn != null) {
1287                try {
1288                    conn.close();
1289                } catch (final SQLException ignored) {
1290                    // at least we could connect
1291                }
1292            }
1293        }
1294        return cpds;
1295    }
1296
1297    /**
1298     * @since 2.6.0
1299     */
1300    @Override
1301    public synchronized String toString() {
1302        final StringBuilder builder = new StringBuilder(super.toString());
1303        builder.append('[');
1304        toStringFields(builder);
1305        builder.append(']');
1306        return builder.toString();
1307    }
1308
1309    /**
1310     * Appends this instance's fields to a string builder.
1311     *
1312     * @param builder the target string builder.
1313     */
1314    protected void toStringFields(final StringBuilder builder) {
1315        builder.append("getConnectionCalled=");
1316        builder.append(getConnectionCalled);
1317        builder.append(", dataSource=");
1318        builder.append(dataSource);
1319        builder.append(", dataSourceName=");
1320        builder.append(dataSourceName);
1321        builder.append(", description=");
1322        builder.append(description);
1323        builder.append(", jndiEnvironment=");
1324        builder.append(jndiEnvironment);
1325        builder.append(", loginTimeoutDuration=");
1326        builder.append(loginTimeoutDuration);
1327        builder.append(", logWriter=");
1328        builder.append(logWriter);
1329        builder.append(", instanceKey=");
1330        builder.append(instanceKey);
1331        builder.append(", defaultBlockWhenExhausted=");
1332        builder.append(defaultBlockWhenExhausted);
1333        builder.append(", defaultEvictionPolicyClassName=");
1334        builder.append(defaultEvictionPolicyClassName);
1335        builder.append(", defaultLifo=");
1336        builder.append(defaultLifo);
1337        builder.append(", defaultMaxIdle=");
1338        builder.append(defaultMaxIdle);
1339        builder.append(", defaultMaxTotal=");
1340        builder.append(defaultMaxTotal);
1341        builder.append(", defaultMaxWaitDuration=");
1342        builder.append(defaultMaxWaitDuration);
1343        builder.append(", defaultMinEvictableIdleDuration=");
1344        builder.append(defaultMinEvictableIdleDuration);
1345        builder.append(", defaultMinIdle=");
1346        builder.append(defaultMinIdle);
1347        builder.append(", defaultNumTestsPerEvictionRun=");
1348        builder.append(defaultNumTestsPerEvictionRun);
1349        builder.append(", defaultSoftMinEvictableIdleDuration=");
1350        builder.append(defaultSoftMinEvictableIdleDuration);
1351        builder.append(", defaultTestOnCreate=");
1352        builder.append(defaultTestOnCreate);
1353        builder.append(", defaultTestOnBorrow=");
1354        builder.append(defaultTestOnBorrow);
1355        builder.append(", defaultTestOnReturn=");
1356        builder.append(defaultTestOnReturn);
1357        builder.append(", defaultTestWhileIdle=");
1358        builder.append(defaultTestWhileIdle);
1359        builder.append(", defaultDurationBetweenEvictionRuns=");
1360        builder.append(defaultDurationBetweenEvictionRuns);
1361        builder.append(", validationQuery=");
1362        builder.append(validationQuery);
1363        builder.append(", validationQueryTimeoutDuration=");
1364        builder.append(validationQueryTimeoutDuration);
1365        builder.append(", rollbackAfterValidation=");
1366        builder.append(rollbackAfterValidation);
1367        builder.append(", maxConnDuration=");
1368        builder.append(maxConnDuration);
1369        builder.append(", defaultAutoCommit=");
1370        builder.append(defaultAutoCommit);
1371        builder.append(", defaultTransactionIsolation=");
1372        builder.append(defaultTransactionIsolation);
1373        builder.append(", defaultReadOnly=");
1374        builder.append(defaultReadOnly);
1375    }
1376
1377    @Override
1378    @SuppressWarnings("unchecked")
1379    public <T> T unwrap(final Class<T> iface) throws SQLException {
1380        if (isWrapperFor(iface)) {
1381            return (T) this;
1382        }
1383        throw new SQLException(this + " is not a wrapper for " + iface);
1384    }
1385}