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