    001/**
    002 *
    003 * Licensed to the Apache Software Foundation (ASF) under one or more
    004 * contributor license agreements.  See the NOTICE file distributed with
    005 * this work for additional information regarding copyright ownership.
    006 * The ASF licenses this file to You under the Apache License, Version 2.0
    007 * (the "License"); you may not use this file except in compliance with
    008 * the License.  You may obtain a copy of the License at
    009 *
    010 *     http://www.apache.org/licenses/LICENSE-2.0
    011 *
    012 *  Unless required by applicable law or agreed to in writing, software
    013 *  distributed under the License is distributed on an "AS IS" BASIS,
    014 *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    015 *  See the License for the specific language governing permissions and
    016 *  limitations under the License.
    017 */
    018package org.apache.commons.dbcp2.managed;
    019
    020import org.apache.commons.dbcp2.BasicDataSource;
    021import org.apache.commons.dbcp2.ConnectionFactory;
    022import org.apache.commons.dbcp2.PoolableConnection;
    023import org.apache.commons.dbcp2.PoolableConnectionFactory;
    024import org.apache.commons.dbcp2.PoolingDataSource;
    025
    026import javax.sql.DataSource;
    027import javax.sql.XADataSource;
    028import javax.transaction.TransactionManager;
    029
    030import java.sql.SQLException;
    031
    032/**
    033 * <p>BasicManagedDataSource is an extension of BasicDataSource which
    034 * creates ManagedConnections.  This data source can create either
    035 * full two-phase-commit XA connections or one-phase-commit
    036 * local connections.  Both types of connections are committed or
    037 * rolled back as part of the global transaction (a.k.a. XA
    038 * transaction or JTA Transaction), but only XA connections can be
    039 * recovered in the case of a system crash.
    040 * </p>
    041 * <p>BasicManagedDataSource adds the TransactionManager and XADataSource
    042 * properties.  The TransactionManager property is required and is
    043 * used to enlist connections in global transactions.  The XADataSource
    044 * is optional and if set is the class name of the XADataSource class
    045 * for a two-phase-commit JDBC driver.  If the XADataSource property
    046 * is set, the driverClassName is ignored and a DataSourceXAConnectionFactory
    047 * is created. Otherwise, a standard DriverConnectionFactory is created
    048 * and wrapped with a LocalXAConnectionFactory.
    049 * </p>
    050 *
    051 * @see BasicDataSource
    052 * @see ManagedConnection
    053 * @since 2.0
    054 */
    055public class BasicManagedDataSource extends BasicDataSource {
    056    /** Transaction Registry */
    057    private TransactionRegistry transactionRegistry;
    058    /** Transaction Manager */
    059    private transient TransactionManager transactionManager;
    060    /** XA datasource class name */
    061    private String xaDataSource;
    062    /** XA datasource instance */
    063    private XADataSource xaDataSourceInstance;
    064
    065    /**
    066     * Gets the XADataSource instance used by the XAConnectionFactory.
    067     *
    068     * @return the XADataSource
    069     */
    070    public synchronized XADataSource getXaDataSourceInstance() {
    071        return xaDataSourceInstance;
    072    }
    073
    074    /**
    075     * <p>Sets the XADataSource instance used by the XAConnectionFactory.</p>
    076     * <p>
    077     * Note: this method currently has no effect once the pool has been
    078     * initialized.  The pool is initialized the first time one of the
    079     * following methods is invoked: <code>getConnection, setLogwriter,
    080     * setLoginTimeout, getLoginTimeout, getLogWriter.</code></p>
    081     *
    082     * @param xaDataSourceInstance XADataSource instance
    083     */
    084    public synchronized void setXaDataSourceInstance(final XADataSource xaDataSourceInstance) {
    085        this.xaDataSourceInstance = xaDataSourceInstance;
    086        xaDataSource = xaDataSourceInstance == null ? null : xaDataSourceInstance.getClass().getName();
    087    }
    088
    089    /**
    090     * Gets the required transaction manager property.
    091     * @return the transaction manager used to enlist connections
    092     */
    093    public TransactionManager getTransactionManager() {
    094        return transactionManager;
    095    }
    096
    097    /**
    098     * Gets the transaction registry.
    099     * @return the transaction registry associating XAResources with managed connections
    100     */
    101    protected synchronized TransactionRegistry getTransactionRegistry() {
    102        return transactionRegistry;
    103    }
    104
    105    /**
    106     * Sets the required transaction manager property.
    107     * @param transactionManager the transaction manager used to enlist connections
    108     */
    109    public void setTransactionManager(final TransactionManager transactionManager) {
    110        this.transactionManager = transactionManager;
    111    }
    112
    113    /**
    114     * Gets the optional XADataSource class name.
    115     * @return the optional XADataSource class name
    116     */
    117    public synchronized String getXADataSource() {
    118        return xaDataSource;
    119    }
    120
    121    /**
    122     * Sets the optional XADataSource class name.
    123     * @param xaDataSource the optional XADataSource class name
    124     */
    125    public synchronized void setXADataSource(final String xaDataSource) {
    126        this.xaDataSource = xaDataSource;
    127    }
    128
    129    @Override
    130    protected ConnectionFactory createConnectionFactory() throws SQLException {
    131        if (transactionManager == null) {
    132            throw new SQLException("Transaction manager must be set before a connection can be created");
    133        }
    134
    135        // If xa data source is not specified a DriverConnectionFactory is created and wrapped with a LocalXAConnectionFactory
    136        if (xaDataSource == null) {
    137            final ConnectionFactory connectionFactory = super.createConnectionFactory();
    138            final XAConnectionFactory xaConnectionFactory = new LocalXAConnectionFactory(getTransactionManager(), connectionFactory);
    139            transactionRegistry = xaConnectionFactory.getTransactionRegistry();
    140            return xaConnectionFactory;
    141        }
    142
    143        // Create the XADataSource instance using the configured class name if it has not been set
    144        if (xaDataSourceInstance == null) {
    145            Class<?> xaDataSourceClass = null;
    146            try {
    147                xaDataSourceClass = Class.forName(xaDataSource);
    148            } catch (final Exception t) {
    149                final String message = "Cannot load XA data source class '" + xaDataSource + "'";
    150                throw new SQLException(message, t);
    151            }
    152
    153            try {
    154                xaDataSourceInstance = (XADataSource) xaDataSourceClass.newInstance();
    155            } catch (final Exception t) {
    156                final String message = "Cannot create XA data source of class '" + xaDataSource + "'";
    157                throw new SQLException(message, t);
    158            }
    159        }
    160
    161        // finally, create the XAConnectionFactory using the XA data source
    162        final XAConnectionFactory xaConnectionFactory = new DataSourceXAConnectionFactory(getTransactionManager(), xaDataSourceInstance, getUsername(), getPassword());
    163        transactionRegistry = xaConnectionFactory.getTransactionRegistry();
    164        return xaConnectionFactory;
    165    }
    166
    167    @Override
    168    protected DataSource createDataSourceInstance() throws SQLException {
    169        final PoolingDataSource<PoolableConnection> pds =
    170                new ManagedDataSource<>(getConnectionPool(), transactionRegistry);
    171        pds.setAccessToUnderlyingConnectionAllowed(isAccessToUnderlyingConnectionAllowed());
    172        return pds;
    173    }
    174
    175    /**
    176     * Creates the PoolableConnectionFactory and attaches it to the connection pool.
    177     *
    178     * @param driverConnectionFactory JDBC connection factory created by {@link #createConnectionFactory()}
    179     * @throws SQLException if an error occurs creating the PoolableConnectionFactory
    180     */
    181    @Override
    182    protected PoolableConnectionFactory createPoolableConnectionFactory(
    183            final ConnectionFactory driverConnectionFactory) throws SQLException {
    184        PoolableConnectionFactory connectionFactory = null;
    185        try {
    186            connectionFactory = new PoolableManagedConnectionFactory(
    187                    (XAConnectionFactory) driverConnectionFactory, getRegisteredJmxName());
    188            connectionFactory.setValidationQuery(getValidationQuery());
    189            connectionFactory.setValidationQueryTimeout(getValidationQueryTimeout());
    190            connectionFactory.setConnectionInitSql(getConnectionInitSqls());
    191            connectionFactory.setDefaultReadOnly(getDefaultReadOnly());
    192            connectionFactory.setDefaultAutoCommit(getDefaultAutoCommit());
    193            connectionFactory.setDefaultTransactionIsolation(getDefaultTransactionIsolation());
    194            connectionFactory.setDefaultCatalog(getDefaultCatalog());
    195            connectionFactory.setCacheState(getCacheState());
    196            connectionFactory.setPoolStatements(isPoolPreparedStatements());
    197            connectionFactory.setMaxOpenPreparedStatements(
    198                    getMaxOpenPreparedStatements());
    199            connectionFactory.setMaxConnLifetimeMillis(getMaxConnLifetimeMillis());
    200            connectionFactory.setRollbackOnReturn(getRollbackOnReturn());
    201            connectionFactory.setEnableAutoCommitOnReturn(getEnableAutoCommitOnReturn());
    202            connectionFactory.setDefaultQueryTimeout(getDefaultQueryTimeout());
    203            connectionFactory.setFastFailValidation(getFastFailValidation());
    204            connectionFactory.setDisconnectionSqlCodes(getDisconnectionSqlCodes());
    205            validateConnectionFactory(connectionFactory);
    206        } catch (final RuntimeException e) {
    207            throw e;
    208        } catch (final Exception e) {
    209            throw new SQLException("Cannot create PoolableConnectionFactory (" + e.getMessage() + ")", e);
    210        }
    211        return connectionFactory;
    212    }
    213}