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