    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 (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}