    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.managed;
    018
    019import java.sql.SQLException;
    020
    021import javax.sql.DataSource;
    022import javax.sql.XADataSource;
    023import javax.transaction.TransactionManager;
    024import javax.transaction.TransactionSynchronizationRegistry;
    025
    026import org.apache.commons.dbcp2.BasicDataSource;
    027import org.apache.commons.dbcp2.ConnectionFactory;
    028import org.apache.commons.dbcp2.PoolableConnection;
    029import org.apache.commons.dbcp2.PoolableConnectionFactory;
    030import org.apache.commons.dbcp2.PoolingDataSource;
    031import org.apache.commons.dbcp2.Utils;
    032
    033/**
    034 * <p>
    035 * BasicManagedDataSource is an extension of BasicDataSource which creates ManagedConnections. This data source can
    036 * create either full two-phase-commit XA connections or one-phase-commit local connections. Both types of connections
    037 * are committed or rolled back as part of the global transaction (a.k.a. XA transaction or JTA Transaction), but only
    038 * XA connections can be recovered in the case of a system crash.
    039 * </p>
    040 * <p>
    041 * BasicManagedDataSource adds the TransactionManager and XADataSource properties. The TransactionManager property is
    042 * required and is used to enlist connections in global transactions. The XADataSource is optional and if set is the
    043 * class name of the XADataSource class for a two-phase-commit JDBC driver. If the XADataSource property is set, the
    044 * driverClassName is ignored and a DataSourceXAConnectionFactory is created. Otherwise, a standard
    045 * DriverConnectionFactory is created and wrapped with a LocalXAConnectionFactory.
    046 * </p>
    047 *
    048 * @see BasicDataSource
    049 * @see ManagedConnection
    050 * @since 2.0
    051 */
    052public class BasicManagedDataSource extends BasicDataSource {
    053
    054    /** Transaction Registry */
    055    private TransactionRegistry transactionRegistry;
    056
    057    /** Transaction Manager */
    058    private transient TransactionManager transactionManager;
    059
    060    /** XA data source class name */
    061    private String xaDataSource;
    062
    063    /** XA data source instance */
    064    private XADataSource xaDataSourceInstance;
    065
    066    /** Transaction Synchronization Registry */
    067    private transient TransactionSynchronizationRegistry transactionSynchronizationRegistry;
    068
    069    /**
    070     * Constructs a new instance.
    071     */
    072    public BasicManagedDataSource() {
    073        // empty
    074    }
    075
    076    @Override
    077    protected synchronized ConnectionFactory createConnectionFactory() throws SQLException {
    078        if (transactionManager == null) {
    079            throw new SQLException("Transaction manager must be set before a connection can be created");
    080        }
    081
    082        // If XA data source is not specified a DriverConnectionFactory is created and wrapped with a
    083        // LocalXAConnectionFactory
    084        if (xaDataSource == null) {
    085            final ConnectionFactory connectionFactory = super.createConnectionFactory();
    086            final XAConnectionFactory xaConnectionFactory = new LocalXAConnectionFactory(getTransactionManager(),
    087                    getTransactionSynchronizationRegistry(), connectionFactory);
    088            transactionRegistry = xaConnectionFactory.getTransactionRegistry();
    089            return xaConnectionFactory;
    090        }
    091
    092        // Create the XADataSource instance using the configured class name if it has not been set
    093        if (xaDataSourceInstance == null) {
    094            Class<?> xaDataSourceClass = null;
    095            try {
    096                xaDataSourceClass = Class.forName(xaDataSource);
    097            } catch (final Exception e) {
    098                throw new SQLException("Cannot load XA data source class '" + xaDataSource + "'", e);
    099            }
    100
    101            try {
    102                xaDataSourceInstance = (XADataSource) xaDataSourceClass.getConstructor().newInstance();
    103            } catch (final Exception e) {
    104                throw new SQLException("Cannot create XA data source of class '" + xaDataSource + "'", e);
    105            }
    106        }
    107
    108        // finally, create the XAConnectionFactory using the XA data source
    109        final XAConnectionFactory xaConnectionFactory = new DataSourceXAConnectionFactory(getTransactionManager(),
    110                xaDataSourceInstance, getUserName(), Utils.toCharArray(getPassword()), getTransactionSynchronizationRegistry());
    111        transactionRegistry = xaConnectionFactory.getTransactionRegistry();
    112        return xaConnectionFactory;
    113    }
    114
    115    @Override
    116    protected DataSource createDataSourceInstance() throws SQLException {
    117        final PoolingDataSource<PoolableConnection> pds = new ManagedDataSource<>(getConnectionPool(),
    118                transactionRegistry);
    119        pds.setAccessToUnderlyingConnectionAllowed(isAccessToUnderlyingConnectionAllowed());
    120        return pds;
    121    }
    122
    123    /**
    124     * Creates the PoolableConnectionFactory and attaches it to the connection pool.
    125     *
    126     * @param driverConnectionFactory
    127     *            JDBC connection factory created by {@link #createConnectionFactory()}
    128     * @throws SQLException
    129     *             if an error occurs creating the PoolableConnectionFactory
    130     */
    131    @Override
    132    protected PoolableConnectionFactory createPoolableConnectionFactory(final ConnectionFactory driverConnectionFactory)
    133            throws SQLException {
    134        PoolableConnectionFactory connectionFactory = null;
    135        try {
    136            connectionFactory = new PoolableManagedConnectionFactory((XAConnectionFactory) driverConnectionFactory, getRegisteredJmxName());
    137            connectionFactory.setValidationQuery(getValidationQuery());
    138            connectionFactory.setValidationQueryTimeout(getValidationQueryTimeoutDuration());
    139            connectionFactory.setConnectionInitSql(getConnectionInitSqls());
    140            connectionFactory.setDefaultReadOnly(getDefaultReadOnly());
    141            connectionFactory.setDefaultAutoCommit(getDefaultAutoCommit());
    142            connectionFactory.setDefaultTransactionIsolation(getDefaultTransactionIsolation());
    143            connectionFactory.setDefaultCatalog(getDefaultCatalog());
    144            connectionFactory.setDefaultSchema(getDefaultSchema());
    145            connectionFactory.setCacheState(getCacheState());
    146            connectionFactory.setPoolStatements(isPoolPreparedStatements());
    147            connectionFactory.setClearStatementPoolOnReturn(isClearStatementPoolOnReturn());
    148            connectionFactory.setMaxOpenPreparedStatements(getMaxOpenPreparedStatements());
    149            connectionFactory.setMaxConn(getMaxConnDuration());
    150            connectionFactory.setRollbackOnReturn(getRollbackOnReturn());
    151            connectionFactory.setAutoCommitOnReturn(getAutoCommitOnReturn());
    152            connectionFactory.setDefaultQueryTimeout(getDefaultQueryTimeoutDuration());
    153            connectionFactory.setFastFailValidation(getFastFailValidation());
    154            connectionFactory.setDisconnectionSqlCodes(getDisconnectionSqlCodes());
    155            connectionFactory.setDisconnectionIgnoreSqlCodes(getDisconnectionIgnoreSqlCodes());
    156            validateConnectionFactory(connectionFactory);
    157        } catch (final RuntimeException e) {
    158            throw e;
    159        } catch (final Exception e) {
    160            throw new SQLException("Cannot create PoolableConnectionFactory (" + e.getMessage() + ")", e);
    161        }
    162        return connectionFactory;
    163    }
    164
    165    /**
    166     * Gets the required transaction manager property.
    167     *
    168     * @return the transaction manager used to enlist connections
    169     */
    170    public TransactionManager getTransactionManager() {
    171        return transactionManager;
    172    }
    173
    174    /**
    175     * Gets the transaction registry.
    176     *
    177     * @return the transaction registry associating XAResources with managed connections
    178     */
    179    protected synchronized TransactionRegistry getTransactionRegistry() {
    180        return transactionRegistry;
    181    }
    182
    183    /**
    184     * Gets the optional TransactionSynchronizationRegistry.
    185     *
    186     * @return the TSR that can be used to register synchronizations.
    187     * @since 2.6.0
    188     */
    189    public TransactionSynchronizationRegistry getTransactionSynchronizationRegistry() {
    190        return transactionSynchronizationRegistry;
    191    }
    192
    193    /**
    194     * Gets the optional XADataSource class name.
    195     *
    196     * @return the optional XADataSource class name
    197     */
    198    public synchronized String getXADataSource() {
    199        return xaDataSource;
    200    }
    201
    202    /**
    203     * Gets the XADataSource instance used by the XAConnectionFactory.
    204     *
    205     * @return the XADataSource
    206     */
    207    public synchronized XADataSource getXaDataSourceInstance() {
    208        return xaDataSourceInstance;
    209    }
    210
    211    /**
    212     * Sets the required transaction manager property.
    213     *
    214     * @param transactionManager
    215     *            the transaction manager used to enlist connections
    216     */
    217    public void setTransactionManager(final TransactionManager transactionManager) {
    218        this.transactionManager = transactionManager;
    219    }
    220
    221    /**
    222     * Sets the optional TransactionSynchronizationRegistry property.
    223     *
    224     * @param transactionSynchronizationRegistry
    225     *            the TSR used to register synchronizations
    226     * @since 2.6.0
    227     */
    228    public void setTransactionSynchronizationRegistry(
    229            final TransactionSynchronizationRegistry transactionSynchronizationRegistry) {
    230        this.transactionSynchronizationRegistry = transactionSynchronizationRegistry;
    231    }
    232
    233    /**
    234     * Sets the optional XADataSource class name.
    235     *
    236     * @param xaDataSource
    237     *            the optional XADataSource class name
    238     */
    239    public synchronized void setXADataSource(final String xaDataSource) {
    240        this.xaDataSource = xaDataSource;
    241    }
    242
    243    /**
    244     * Sets the XADataSource instance used by the XAConnectionFactory.
    245     * <p>
    246     * Note: this method currently has no effect once the pool has been initialized. The pool is initialized the first time one of the following methods is
    247     * invoked: {@link #getConnection()}, {@link #setLogWriter(java.io.PrintWriter)}, {@link #setLoginTimeout(int)}, {@link #getLoginTimeout()},
    248     * {@link #getLogWriter()}.
    249     * </p>
    250     *
    251     * @param xaDataSourceInstance XADataSource instance
    252     */
    253    public synchronized void setXaDataSourceInstance(final XADataSource xaDataSourceInstance) {
    254        this.xaDataSourceInstance = xaDataSourceInstance;
    255        xaDataSource = xaDataSourceInstance == null ? null : xaDataSourceInstance.getClass().getName();
    256    }
    257}