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