    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}