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