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 */
017package org.apache.commons.dbcp2;
018
019import java.sql.Connection;
020import java.sql.SQLException;
021import java.sql.Statement;
022import java.time.Duration;
023import java.util.Collection;
024import java.util.Objects;
025import java.util.concurrent.atomic.AtomicLong;
026
027import javax.management.MalformedObjectNameException;
028import javax.management.ObjectName;
029
030import org.apache.commons.logging.Log;
031import org.apache.commons.logging.LogFactory;
032import org.apache.commons.pool2.DestroyMode;
033import org.apache.commons.pool2.KeyedObjectPool;
034import org.apache.commons.pool2.ObjectPool;
035import org.apache.commons.pool2.PooledObject;
036import org.apache.commons.pool2.PooledObjectFactory;
037import org.apache.commons.pool2.impl.DefaultPooledObject;
038import org.apache.commons.pool2.impl.GenericKeyedObjectPool;
039import org.apache.commons.pool2.impl.GenericKeyedObjectPoolConfig;
040
041/**
042 * A {@link PooledObjectFactory} that creates {@link PoolableConnection}s.
043 *
044 * @since 2.0
045 */
046public class PoolableConnectionFactory implements PooledObjectFactory<PoolableConnection> {
047
048    private static final Log log = LogFactory.getLog(PoolableConnectionFactory.class);
049
050    /**
051     * Internal constant to indicate the level is not set.
052     */
053    static final int UNKNOWN_TRANSACTION_ISOLATION = -1;
054
055    private final ConnectionFactory connectionFactory;
056
057    private final ObjectName dataSourceJmxObjectName;
058
059    private volatile String validationQuery;
060
061    private volatile Duration validationQueryTimeoutDuration = Duration.ofSeconds(-1);
062
063    private Collection<String> connectionInitSqls;
064
065    private Collection<String> disconnectionSqlCodes;
066
067    private Collection<String> disconnectionIgnoreSqlCodes;
068
069    private boolean fastFailValidation = true;
070
071    private volatile ObjectPool<PoolableConnection> pool;
072
073    private Boolean defaultReadOnly;
074
075    private Boolean defaultAutoCommit;
076
077    private boolean autoCommitOnReturn = true;
078
079    private boolean rollbackOnReturn = true;
080
081    private int defaultTransactionIsolation = UNKNOWN_TRANSACTION_ISOLATION;
082
083    private String defaultCatalog;
084
085    private String defaultSchema;
086
087    private boolean cacheState;
088
089    private boolean poolStatements;
090
091    private boolean clearStatementPoolOnReturn;
092
093    private int maxOpenPreparedStatements = GenericKeyedObjectPoolConfig.DEFAULT_MAX_TOTAL_PER_KEY;
094
095    private Duration maxConnDuration = Duration.ofMillis(-1);
096
097    private final AtomicLong connectionIndex = new AtomicLong();
098
099    private Duration defaultQueryTimeoutDuration;
100
101    /**
102     * Creates a new {@code PoolableConnectionFactory}.
103     *
104     * @param connFactory
105     *            the {@link ConnectionFactory} from which to obtain base {@link Connection}s
106     * @param dataSourceJmxObjectName
107     *            The JMX object name, may be null.
108     */
109    public PoolableConnectionFactory(final ConnectionFactory connFactory, final ObjectName dataSourceJmxObjectName) {
110        this.connectionFactory = connFactory;
111        this.dataSourceJmxObjectName = dataSourceJmxObjectName;
112    }
113
114    @Override
115    public void activateObject(final PooledObject<PoolableConnection> p) throws SQLException {
116
117        validateLifetime(p);
118
119        final PoolableConnection pConnection = p.getObject();
120        pConnection.activate();
121
122        if (defaultAutoCommit != null && pConnection.getAutoCommit() != defaultAutoCommit) {
123            pConnection.setAutoCommit(defaultAutoCommit);
124        }
125        if (defaultTransactionIsolation != UNKNOWN_TRANSACTION_ISOLATION
126                && pConnection.getTransactionIsolation() != defaultTransactionIsolation) {
127            pConnection.setTransactionIsolation(defaultTransactionIsolation);
128        }
129        if (defaultReadOnly != null && pConnection.isReadOnly() != defaultReadOnly) {
130            pConnection.setReadOnly(defaultReadOnly);
131        }
132        if (defaultCatalog != null && !defaultCatalog.equals(pConnection.getCatalog())) {
133            pConnection.setCatalog(defaultCatalog);
134        }
135        if (defaultSchema != null && !defaultSchema.equals(Jdbc41Bridge.getSchema(pConnection))) {
136            Jdbc41Bridge.setSchema(pConnection, defaultSchema);
137        }
138        pConnection.setDefaultQueryTimeout(defaultQueryTimeoutDuration);
139    }
140
141    @Override
142    public void destroyObject(final PooledObject<PoolableConnection> p) throws SQLException {
143        p.getObject().reallyClose();
144    }
145
146    /**
147     * @since 2.9.0
148     */
149    @Override
150    public void destroyObject(final PooledObject<PoolableConnection> p, final DestroyMode mode) throws SQLException {
151        if (mode == DestroyMode.ABANDONED) {
152            Jdbc41Bridge.abort(p.getObject().getInnermostDelegate(), Runnable::run);
153        } else {
154            p.getObject().reallyClose();
155        }
156    }
157
158    /**
159     * Gets the cache state to propagate in {@link #makeObject()}.
160     *
161     * @return The cache state.
162     * @since 2.6.0.
163     */
164    public boolean getCacheState() {
165        return cacheState;
166    }
167
168    /**
169     * Gets the connection factory.
170     *
171     * @return The connection factory.
172     * @since 2.6.0.
173     */
174    public ConnectionFactory getConnectionFactory() {
175        return connectionFactory;
176    }
177
178    /**
179     * Gets how many connections were created in {@link #makeObject()}.
180     *
181     * @return the connection count.
182     */
183    protected AtomicLong getConnectionIndex() {
184        return connectionIndex;
185    }
186
187    /**
188     * Gets the collection of initialization SQL statements.
189     *
190     * @return The collection of initialization SQL statements.
191     * @since 2.6.0
192     */
193    public Collection<String> getConnectionInitSqls() {
194        return connectionInitSqls;
195    }
196
197    /**
198     * Gets data source JMX ObjectName.
199     *
200     * @return The data source JMX ObjectName.
201     * @since 2.6.0.
202     */
203    public ObjectName getDataSourceJmxName() {
204        return dataSourceJmxObjectName;
205    }
206
207    /**
208     * Gets the data source JMX ObjectName.
209     *
210     * @return The data source JMX ObjectName.
211     * @since 2.6.0
212     */
213    public ObjectName getDataSourceJmxObjectName() {
214        return dataSourceJmxObjectName;
215    }
216
217    /**
218     * Gets the Default auto-commit value.
219     *
220     * @return The default auto-commit value.
221     * @since 2.6.0
222     */
223    public Boolean getDefaultAutoCommit() {
224        return defaultAutoCommit;
225    }
226
227    /**
228     * Gets the default catalog.
229     *
230     * @return The default catalog.
231     * @since 2.6.0
232     */
233    public String getDefaultCatalog() {
234        return defaultCatalog;
235    }
236
237    /**
238     * Gets the default query timeout in seconds.
239     *
240     * @return The default query timeout in seconds.
241     * @deprecated Use {@link #getDefaultQueryTimeoutDuration()}.
242     */
243    @Deprecated
244    public Integer getDefaultQueryTimeout() {
245        return getDefaultQueryTimeoutSeconds();
246    }
247
248    /**
249     * Gets the default query timeout Duration.
250     *
251     * @return The default query timeout Duration.
252     * @since 2.10.0
253     */
254    public Duration getDefaultQueryTimeoutDuration() {
255        return defaultQueryTimeoutDuration;
256    }
257
258    /**
259     * Gets the default query timeout in seconds.
260     *
261     * @return The default query timeout in seconds.
262     * @since 2.6.0
263     * @deprecated Use {@link #getDefaultQueryTimeoutDuration()}.
264     */
265    @Deprecated
266    public Integer getDefaultQueryTimeoutSeconds() {
267        return defaultQueryTimeoutDuration == null ? null : (int) defaultQueryTimeoutDuration.getSeconds();
268    }
269
270    /**
271     * Gets the default read-only-value.
272     *
273     * @return The default read-only-value.
274     * @since 2.6.0
275     */
276    public Boolean getDefaultReadOnly() {
277        return defaultReadOnly;
278    }
279
280    /**
281     * Gets the default schema.
282     *
283     * @return The default schema.
284     * @since 2.6.0
285     */
286    public String getDefaultSchema() {
287        return defaultSchema;
288    }
289
290    /**
291     * Gets the default transaction isolation.
292     *
293     * @return The default transaction isolation.
294     * @since 2.6.0
295     */
296    public int getDefaultTransactionIsolation() {
297        return defaultTransactionIsolation;
298    }
299
300    /**
301     * Gets the collection of SQL State codes that are not considered fatal disconnection codes.
302     * <p>
303     * This method returns the collection of SQL State codes that have been set to be ignored when
304     * determining if a {@link SQLException} signals a disconnection. These codes are excluded from
305     * being treated as fatal even if they match the typical disconnection criteria.
306     * </p>
307     *
308     * @return a {@link Collection} of SQL State codes that should be ignored for disconnection checks.
309     * @since 2.13.0
310     */
311    public Collection<String> getDisconnectionIgnoreSqlCodes() {
312        return disconnectionIgnoreSqlCodes;
313    }
314
315    /**
316     * Gets SQL State codes considered to signal fatal conditions.
317     * <p>
318     * Overrides the defaults in {@link Utils#getDisconnectionSqlCodes()} (plus anything starting with
319     * {@link Utils#DISCONNECTION_SQL_CODE_PREFIX}). If this property is non-null and {@link #isFastFailValidation()} is
320     * {@code true}, whenever connections created by this factory generate exceptions with SQL State codes in this list,
321     * they will be marked as "fatally disconnected" and subsequent validations will fail fast (no attempt at isValid or
322     * validation query).
323     * </p>
324     * <p>
325     * If {@link #isFastFailValidation()} is {@code false} setting this property has no effect.
326     * </p>
327     *
328     * @return SQL State codes overriding defaults
329     * @since 2.1
330     */
331    public Collection<String> getDisconnectionSqlCodes() {
332        return disconnectionSqlCodes;
333    }
334
335    /**
336     * Gets the Maximum connection duration.
337     *
338     * @return Maximum connection duration.
339     * @since 2.10.0
340     */
341    public Duration getMaxConnDuration() {
342        return maxConnDuration;
343    }
344
345    /**
346     * Gets the Maximum connection lifetime in milliseconds.
347     *
348     * @return Maximum connection lifetime in milliseconds.
349     * @since 2.6.0
350     */
351    public long getMaxConnLifetimeMillis() {
352        return maxConnDuration.toMillis();
353    }
354
355    /**
356     * Gets the maximum number of open prepared statements.
357     *
358     * @return The maximum number of open prepared statements.
359     */
360    protected int getMaxOpenPreparedStatements() {
361        return maxOpenPreparedStatements;
362    }
363
364    /**
365     * Returns the {@link ObjectPool} in which {@link Connection}s are pooled.
366     *
367     * @return the connection pool
368     */
369    public synchronized ObjectPool<PoolableConnection> getPool() {
370        return pool;
371    }
372
373    /**
374     * Tests whether to pool statements.
375     *
376     * @return Whether to pool statements.
377     * @since 2.6.0.
378     */
379    public boolean getPoolStatements() {
380        return poolStatements;
381    }
382
383    /**
384     * Gets the validation query.
385     *
386     * @return Validation query.
387     * @since 2.6.0
388     */
389    public String getValidationQuery() {
390        return validationQuery;
391    }
392
393    /**
394     * Gets the query timeout in seconds.
395     *
396     * @return Validation query timeout in seconds.
397     * @since 2.10.0
398     */
399    public Duration getValidationQueryTimeoutDuration() {
400        return validationQueryTimeoutDuration;
401    }
402
403    /**
404     * Gets the query timeout in seconds.
405     *
406     * @return Validation query timeout in seconds.
407     * @since 2.6.0
408     * @deprecated Use {@link #getValidationQueryTimeoutDuration()}.
409     */
410    @Deprecated
411    public int getValidationQueryTimeoutSeconds() {
412        return (int) validationQueryTimeoutDuration.getSeconds();
413    }
414
415    /**
416     * Initializes the given connection with the collection of SQL statements set in {@link #setConnectionInitSql(Collection)}.
417     *
418     * @param conn the connection to initialize.
419     * @throws SQLException if a database access error occurs or this method is called on a closed connection.
420     * @see #setConnectionInitSql(Collection)
421     */
422    protected void initializeConnection(final Connection conn) throws SQLException {
423        final Collection<String> sqls = connectionInitSqls;
424        if (conn.isClosed()) {
425            throw new SQLException("initializeConnection: connection closed");
426        }
427        if (!Utils.isEmpty(sqls)) {
428            try (Statement statement = conn.createStatement()) {
429                for (final String sql : sqls) {
430                    statement.execute(Objects.requireNonNull(sql, "null connectionInitSqls element"));
431                }
432            }
433        }
434    }
435
436    /**
437     * Tests whether to set auto-commit on {@link #passivateObject(PooledObject)}.
438     *
439     * @return Whether to set auto-commit on {@link #passivateObject(PooledObject)}.
440     * @since 2.6.0
441     */
442    public boolean isAutoCommitOnReturn() {
443        return autoCommitOnReturn;
444    }
445
446    /**
447     * Tests whether to set auto-commit on {@link #passivateObject(PooledObject)}.
448     *
449     * @return Whether to set auto-commit on {@link #passivateObject(PooledObject)}.
450     * @deprecated Use {@link #isAutoCommitOnReturn()}.
451     */
452    @Deprecated
453    public boolean isEnableAutoCommitOnReturn() {
454        return autoCommitOnReturn;
455    }
456
457    /**
458     * True means that validation will fail immediately for connections that have previously thrown SQLExceptions with
459     * SQL State indicating fatal disconnection errors.
460     *
461     * @return true if connections created by this factory will fast fail validation.
462     * @see #setDisconnectionSqlCodes(Collection)
463     * @since 2.1
464     * @since 2.5.0 Defaults to true, previous versions defaulted to false.
465     */
466    public boolean isFastFailValidation() {
467        return fastFailValidation;
468    }
469
470    /**
471     * Tests whether to rollback on return.
472     *
473     * @return Whether to rollback on return.
474     */
475    public boolean isRollbackOnReturn() {
476        return rollbackOnReturn;
477    }
478
479    @Override
480    public PooledObject<PoolableConnection> makeObject() throws SQLException {
481        Connection conn = connectionFactory.createConnection();
482        if (conn == null) {
483            throw new IllegalStateException("Connection factory returned null from createConnection");
484        }
485        try {
486            initializeConnection(conn);
487        } catch (final SQLException e) {
488            // Make sure the connection is closed
489            Utils.closeQuietly((AutoCloseable) conn);
490            // Rethrow original exception so it is visible to caller
491            throw e;
492        }
493
494        final long connIndex = connectionIndex.getAndIncrement();
495
496        if (poolStatements) {
497            conn = new PoolingConnection(conn);
498            final GenericKeyedObjectPoolConfig<DelegatingPreparedStatement> config = new GenericKeyedObjectPoolConfig<>();
499            config.setMaxTotalPerKey(-1);
500            config.setBlockWhenExhausted(false);
501            config.setMaxWait(Duration.ZERO);
502            config.setMaxIdlePerKey(1);
503            config.setMaxTotal(maxOpenPreparedStatements);
504            if (dataSourceJmxObjectName != null) {
505                final StringBuilder base = new StringBuilder(dataSourceJmxObjectName.toString());
506                base.append(Constants.JMX_CONNECTION_BASE_EXT);
507                base.append(connIndex);
508                config.setJmxNameBase(base.toString());
509                config.setJmxNamePrefix(Constants.JMX_STATEMENT_POOL_PREFIX);
510            } else {
511                config.setJmxEnabled(false);
512            }
513            final PoolingConnection poolingConn = (PoolingConnection) conn;
514            final KeyedObjectPool<PStmtKey, DelegatingPreparedStatement> stmtPool = new GenericKeyedObjectPool<>(poolingConn, config);
515            poolingConn.setStatementPool(stmtPool);
516            poolingConn.setClearStatementPoolOnReturn(clearStatementPoolOnReturn);
517            poolingConn.setCacheState(cacheState);
518        }
519
520        // Register this connection with JMX
521        final ObjectName connJmxName;
522        if (dataSourceJmxObjectName == null) {
523            connJmxName = null;
524        } else {
525            final String name = dataSourceJmxObjectName.toString() + Constants.JMX_CONNECTION_BASE_EXT + connIndex;
526            try {
527                connJmxName = new ObjectName(name);
528            } catch (final MalformedObjectNameException e) {
529                Utils.closeQuietly((AutoCloseable) conn);
530                throw new SQLException(name, e);
531            }
532        }
533
534        final PoolableConnection pc = new PoolableConnection(conn, pool, connJmxName,
535                disconnectionSqlCodes, disconnectionIgnoreSqlCodes, fastFailValidation);
536        pc.setCacheState(cacheState);
537
538        return new DefaultPooledObject<>(pc);
539    }
540
541    @Override
542    public void passivateObject(final PooledObject<PoolableConnection> p) throws SQLException {
543
544        validateLifetime(p);
545
546        final PoolableConnection conn = p.getObject();
547        Boolean connAutoCommit = null;
548        if (rollbackOnReturn) {
549            connAutoCommit = conn.getAutoCommit();
550            if (!connAutoCommit && !conn.isReadOnly()) {
551                conn.rollback();
552            }
553        }
554
555        conn.clearWarnings();
556
557        // DBCP-97 / DBCP-399 / DBCP-351 Idle connections in the pool should
558        // have autoCommit enabled
559        if (autoCommitOnReturn) {
560            if (connAutoCommit == null) {
561                connAutoCommit = conn.getAutoCommit();
562            }
563            if (!connAutoCommit) {
564                conn.setAutoCommit(true);
565            }
566        }
567
568        conn.passivate();
569    }
570
571    /**
572     * Sets whether to set auto-commit on {@link #passivateObject(PooledObject)}.
573     *
574     * @param autoCommitOnReturn whether to set auto-commit.
575     */
576    public void setAutoCommitOnReturn(final boolean autoCommitOnReturn) {
577        this.autoCommitOnReturn = autoCommitOnReturn;
578    }
579
580    /**
581     * Sets the cache state to propagate in {@link #makeObject()}.
582     *
583     * @param cacheState the cache state to propagate.
584     */
585    public void setCacheState(final boolean cacheState) {
586        this.cacheState = cacheState;
587    }
588
589    /**
590     * Sets whether the pool of statements (which was enabled with {@link #setPoolStatements(boolean)}) should
591     * be cleared when the connection is returned to its pool. Default is false.
592     *
593     * @param clearStatementPoolOnReturn clear or not
594     * @since 2.8.0
595     */
596    public void setClearStatementPoolOnReturn(final boolean clearStatementPoolOnReturn) {
597        this.clearStatementPoolOnReturn = clearStatementPoolOnReturn;
598    }
599
600    /**
601     * Sets the SQL statements I use to initialize newly created {@link Connection}s. Using {@code null} turns off
602     * connection initialization.
603     *
604     * @param connectionInitSqls
605     *            SQL statement to initialize {@link Connection}s.
606     */
607    public void setConnectionInitSql(final Collection<String> connectionInitSqls) {
608        this.connectionInitSqls = connectionInitSqls;
609    }
610    /**
611     * Sets the default "auto commit" setting for borrowed {@link Connection}s
612     *
613     * @param defaultAutoCommit
614     *            the default "auto commit" setting for borrowed {@link Connection}s
615     */
616    public void setDefaultAutoCommit(final Boolean defaultAutoCommit) {
617        this.defaultAutoCommit = defaultAutoCommit;
618    }
619
620    /**
621     * Sets the default "catalog" setting for borrowed {@link Connection}s
622     *
623     * @param defaultCatalog
624     *            the default "catalog" setting for borrowed {@link Connection}s
625     */
626    public void setDefaultCatalog(final String defaultCatalog) {
627        this.defaultCatalog = defaultCatalog;
628    }
629
630    /**
631     * Sets the query timeout Duration.
632     *
633     * @param defaultQueryTimeoutDuration the query timeout Duration.
634     * @since 2.10.0
635     */
636    public void setDefaultQueryTimeout(final Duration defaultQueryTimeoutDuration) {
637        this.defaultQueryTimeoutDuration = defaultQueryTimeoutDuration;
638    }
639
640    /**
641     * Sets the query timeout in seconds.
642     *
643     * @param defaultQueryTimeoutSeconds the query timeout in seconds.
644     * @deprecated Use {@link #setDefaultQueryTimeout(Duration)}.
645     */
646    @Deprecated
647    public void setDefaultQueryTimeout(final Integer defaultQueryTimeoutSeconds) {
648        this.defaultQueryTimeoutDuration = defaultQueryTimeoutSeconds == null ? null : Duration.ofSeconds(defaultQueryTimeoutSeconds);
649    }
650
651    /**
652     * Sets the default "read only" setting for borrowed {@link Connection}s
653     *
654     * @param defaultReadOnly
655     *            the default "read only" setting for borrowed {@link Connection}s
656     */
657    public void setDefaultReadOnly(final Boolean defaultReadOnly) {
658        this.defaultReadOnly = defaultReadOnly;
659    }
660
661    /**
662     * Sets the default "schema" setting for borrowed {@link Connection}s
663     *
664     * @param defaultSchema
665     *            the default "schema" setting for borrowed {@link Connection}s
666     * @since 2.5.0
667     */
668    public void setDefaultSchema(final String defaultSchema) {
669        this.defaultSchema = defaultSchema;
670    }
671
672    /**
673     * Sets the default "Transaction Isolation" setting for borrowed {@link Connection}s
674     *
675     * @param defaultTransactionIsolation
676     *            the default "Transaction Isolation" setting for returned {@link Connection}s
677     */
678    public void setDefaultTransactionIsolation(final int defaultTransactionIsolation) {
679        this.defaultTransactionIsolation = defaultTransactionIsolation;
680    }
681
682    /**
683     * Sets the disconnection SQL codes to ignore.
684     *
685     * @param disconnectionIgnoreSqlCodes
686     *            The collection of SQL State codes to be ignored.
687     * @see #getDisconnectionIgnoreSqlCodes()
688     * @throws IllegalArgumentException if any SQL state codes overlap with those in {@link #disconnectionSqlCodes}.
689     * @since 2.13.0
690     */
691    public void setDisconnectionIgnoreSqlCodes(final Collection<String> disconnectionIgnoreSqlCodes) {
692        Utils.checkSqlCodes(disconnectionIgnoreSqlCodes, this.disconnectionSqlCodes);
693        this.disconnectionIgnoreSqlCodes = disconnectionIgnoreSqlCodes;
694    }
695
696    /**
697     * Sets the disconnection SQL codes.
698     *
699     * @param disconnectionSqlCodes
700     *            The disconnection SQL codes.
701     * @see #getDisconnectionSqlCodes()
702     * @since 2.1
703     * @throws IllegalArgumentException if any SQL state codes overlap with those in {@link #disconnectionIgnoreSqlCodes}.
704     */
705    public void setDisconnectionSqlCodes(final Collection<String> disconnectionSqlCodes) {
706        Utils.checkSqlCodes(disconnectionSqlCodes, this.disconnectionIgnoreSqlCodes);
707        this.disconnectionSqlCodes = disconnectionSqlCodes;
708    }
709
710    /**
711     * Sets whether to set auto-commit on {@link #passivateObject(PooledObject)}.
712     *
713     * @param autoCommitOnReturn whether to set auto-commit.
714     */
715    @Deprecated
716    public void setEnableAutoCommitOnReturn(final boolean autoCommitOnReturn) {
717        this.autoCommitOnReturn = autoCommitOnReturn;
718    }
719
720    /**
721     * @see #isFastFailValidation()
722     * @param fastFailValidation
723     *            true means connections created by this factory will fast fail validation
724     * @since 2.1
725     */
726    public void setFastFailValidation(final boolean fastFailValidation) {
727        this.fastFailValidation = fastFailValidation;
728    }
729
730    /**
731     * Sets the maximum lifetime in milliseconds of a connection after which the connection will always fail activation,
732     * passivation and validation. A value of zero or less indicates an infinite lifetime. The default value is -1.
733     *
734     * @param maxConnDuration
735     *            The maximum lifetime in milliseconds.
736     * @since 2.10.0
737     */
738    public void setMaxConn(final Duration maxConnDuration) {
739        this.maxConnDuration = maxConnDuration;
740    }
741
742    /**
743     * Sets the maximum lifetime in milliseconds of a connection after which the connection will always fail activation,
744     * passivation and validation. A value of zero or less indicates an infinite lifetime. The default value is -1.
745     *
746     * @param maxConnLifetimeMillis
747     *            The maximum lifetime in milliseconds.
748     * @deprecated Use {@link #setMaxConn(Duration)}.
749     */
750    @Deprecated
751    public void setMaxConnLifetimeMillis(final long maxConnLifetimeMillis) {
752        this.maxConnDuration = Duration.ofMillis(maxConnLifetimeMillis);
753    }
754
755    /**
756     * Sets the maximum number of open prepared statements.
757     *
758     * @param maxOpenPreparedStatements
759     *            The maximum number of open prepared statements.
760     */
761    public void setMaxOpenPreparedStatements(final int maxOpenPreparedStatements) {
762        this.maxOpenPreparedStatements = maxOpenPreparedStatements;
763    }
764
765    /**
766     * Deprecated due to typo in method name.
767     *
768     * @param maxOpenPreparedStatements
769     *            The maximum number of open prepared statements.
770     * @deprecated Use {@link #setMaxOpenPreparedStatements(int)}.
771     */
772    @Deprecated // Due to typo in method name.
773    public void setMaxOpenPrepatedStatements(final int maxOpenPreparedStatements) {
774        setMaxOpenPreparedStatements(maxOpenPreparedStatements);
775    }
776
777    /**
778     * Sets the {@link ObjectPool} in which to pool {@link Connection}s.
779     *
780     * @param pool
781     *            the {@link ObjectPool} in which to pool those {@link Connection}s
782     */
783    public synchronized void setPool(final ObjectPool<PoolableConnection> pool) {
784        if (null != this.pool && pool != this.pool) {
785            Utils.closeQuietly(this.pool);
786        }
787        this.pool = pool;
788    }
789
790    /**
791     * Sets whether to pool statements.
792     *
793     * @param poolStatements whether to pool statements.
794     */
795    public void setPoolStatements(final boolean poolStatements) {
796        this.poolStatements = poolStatements;
797    }
798
799    /**
800     * Sets whether to rollback on return.
801     *
802     * @param rollbackOnReturn whether to rollback on return.
803     */
804    public void setRollbackOnReturn(final boolean rollbackOnReturn) {
805        this.rollbackOnReturn = rollbackOnReturn;
806    }
807
808    /**
809     * Sets the query I use to {@link #validateObject validate} {@link Connection}s. Should return at least one row. If
810     * not specified, {@link Connection#isValid(int)} will be used to validate connections.
811     *
812     * @param validationQuery
813     *            a query to use to {@link #validateObject validate} {@link Connection}s.
814     */
815    public void setValidationQuery(final String validationQuery) {
816        this.validationQuery = validationQuery;
817    }
818
819    /**
820     * Sets the validation query timeout, the amount of time, that connection validation will wait for a response from the
821     * database when executing a validation query. Use a value less than or equal to 0 for no timeout.
822     *
823     * @param validationQueryTimeoutDuration new validation query timeout duration.
824     * @since 2.10.0
825     */
826    public void setValidationQueryTimeout(final Duration validationQueryTimeoutDuration) {
827        this.validationQueryTimeoutDuration = validationQueryTimeoutDuration;
828    }
829
830    /**
831     * Sets the validation query timeout, the amount of time, in seconds, that connection validation will wait for a
832     * response from the database when executing a validation query. Use a value less than or equal to 0 for no timeout.
833     *
834     * @param validationQueryTimeoutSeconds
835     *            new validation query timeout value in seconds
836     * @deprecated {@link #setValidationQueryTimeout(Duration)}.
837     */
838    @Deprecated
839    public void setValidationQueryTimeout(final int validationQueryTimeoutSeconds) {
840        this.validationQueryTimeoutDuration = Duration.ofSeconds(validationQueryTimeoutSeconds);
841    }
842
843    /**
844     * Validates the given connection if it is open.
845     *
846     * @param conn the connection to validate.
847     * @throws SQLException if the connection is closed or validate fails.
848     */
849    public void validateConnection(final PoolableConnection conn) throws SQLException {
850        if (conn.isClosed()) {
851            throw new SQLException("validateConnection: connection closed");
852        }
853        conn.validate(validationQuery, validationQueryTimeoutDuration);
854    }
855
856    private void validateLifetime(final PooledObject<PoolableConnection> p) throws LifetimeExceededException {
857        Utils.validateLifetime(p, maxConnDuration);
858    }
859
860    @Override
861    public boolean validateObject(final PooledObject<PoolableConnection> p) {
862        try {
863            validateLifetime(p);
864            validateConnection(p.getObject());
865            return true;
866        } catch (final Exception e) {
867            if (log.isDebugEnabled()) {
868                log.debug(Utils.getMessage("poolableConnectionFactory.validateObject.fail"), e);
869            }
870            return false;
871        }
872    }
873}