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