    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>
    034 * BasicManagedDataSource is an extension of BasicDataSource which creates ManagedConnections. This data source can
    035 * create either full two-phase-commit XA connections or one-phase-commit local connections. Both types of connections
    036 * are committed or rolled back as part of the global transaction (a.k.a. XA transaction or JTA Transaction), but only
    037 * XA connections can be recovered in the case of a system crash.
    038 * </p>
    039 * <p>
    040 * BasicManagedDataSource adds the TransactionManager and XADataSource properties. The TransactionManager property is
    041 * required and is used to enlist connections in global transactions. The XADataSource is optional and if set is the
    042 * class name of the XADataSource class for a two-phase-commit JDBC driver. If the XADataSource property is set, the
    043 * driverClassName is ignored and a DataSourceXAConnectionFactory is created. Otherwise, a standard
    044 * DriverConnectionFactory is created and wrapped with a LocalXAConnectionFactory.
    045 * </p>
    046 *
    047 * @see BasicDataSource
    048 * @see ManagedConnection
    049 * @since 2.0
    050 */
    051public class BasicManagedDataSource extends BasicDataSource {
    052
    053    /** Transaction Registry */
    054    private TransactionRegistry transactionRegistry;
    055
    056    /** Transaction Manager */
    057    private transient TransactionManager transactionManager;
    058
    059    /** XA data source class name */
    060    private String xaDataSource;
    061
    062    /** XA data source 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>
    076     * Sets the XADataSource instance used by the XAConnectionFactory.
    077     * </p>
    078     * <p>
    079     * Note: this method currently has no effect once the pool has been initialized. The pool is initialized the first
    080     * time one of the following methods is invoked: <code>getConnection, setLogwriter,
    081     * setLoginTimeout, getLoginTimeout, getLogWriter.</code>
    082     * </p>
    083     *
    084     * @param xaDataSourceInstance
    085     *            XADataSource instance
    086     */
    087    public synchronized void setXaDataSourceInstance(final XADataSource xaDataSourceInstance) {
    088        this.xaDataSourceInstance = xaDataSourceInstance;
    089        xaDataSource = xaDataSourceInstance == null ? null : xaDataSourceInstance.getClass().getName();
    090    }
    091
    092    /**
    093     * Gets the required transaction manager property.
    094     *
    095     * @return the transaction manager used to enlist connections
    096     */
    097    public TransactionManager getTransactionManager() {
    098        return transactionManager;
    099    }
    100
    101    /**
    102     * Gets the transaction registry.
    103     *
    104     * @return the transaction registry associating XAResources with managed connections
    105     */
    106    protected synchronized TransactionRegistry getTransactionRegistry() {
    107        return transactionRegistry;
    108    }
    109
    110    /**
    111     * Sets the required transaction manager property.
    112     *
    113     * @param transactionManager
    114     *            the transaction manager used to enlist connections
    115     */
    116    public void setTransactionManager(final TransactionManager transactionManager) {
    117        this.transactionManager = transactionManager;
    118    }
    119
    120    /**
    121     * Gets the optional XADataSource class name.
    122     *
    123     * @return the optional XADataSource class name
    124     */
    125    public synchronized String getXADataSource() {
    126        return xaDataSource;
    127    }
    128
    129    /**
    130     * Sets the optional XADataSource class name.
    131     *
    132     * @param xaDataSource
    133     *            the optional XADataSource class name
    134     */
    135    public synchronized void setXADataSource(final String xaDataSource) {
    136        this.xaDataSource = xaDataSource;
    137    }
    138
    139    @Override
    140    protected ConnectionFactory createConnectionFactory() throws SQLException {
    141        if (transactionManager == null) {
    142            throw new SQLException("Transaction manager must be set before a connection can be created");
    143        }
    144
    145        // If xa data source is not specified a DriverConnectionFactory is created and wrapped with a
    146        // LocalXAConnectionFactory
    147        if (xaDataSource == null) {
    148            final ConnectionFactory connectionFactory = super.createConnectionFactory();
    149            final XAConnectionFactory xaConnectionFactory = new LocalXAConnectionFactory(getTransactionManager(),
    150                    connectionFactory);
    151            transactionRegistry = xaConnectionFactory.getTransactionRegistry();
    152            return xaConnectionFactory;
    153        }
    154
    155        // Create the XADataSource instance using the configured class name if it has not been set
    156        if (xaDataSourceInstance == null) {
    157            Class<?> xaDataSourceClass = null;
    158            try {
    159                xaDataSourceClass = Class.forName(xaDataSource);
    160            } catch (final Exception t) {
    161                final String message = "Cannot load XA data source class '" + xaDataSource + "'";
    162                throw new SQLException(message, t);
    163            }
    164
    165            try {
    166                xaDataSourceInstance = (XADataSource) xaDataSourceClass.newInstance();
    167            } catch (final Exception t) {
    168                final String message = "Cannot create XA data source of class '" + xaDataSource + "'";
    169                throw new SQLException(message, t);
    170            }
    171        }
    172
    173        // finally, create the XAConnectionFactory using the XA data source
    174        final XAConnectionFactory xaConnectionFactory = new DataSourceXAConnectionFactory(getTransactionManager(),
    175                xaDataSourceInstance, getUsername(), getPassword());
    176        transactionRegistry = xaConnectionFactory.getTransactionRegistry();
    177        return xaConnectionFactory;
    178    }
    179
    180    @Override
    181    protected DataSource createDataSourceInstance() throws SQLException {
    182        final PoolingDataSource<PoolableConnection> pds = new ManagedDataSource<>(getConnectionPool(),
    183                transactionRegistry);
    184        pds.setAccessToUnderlyingConnectionAllowed(isAccessToUnderlyingConnectionAllowed());
    185        return pds;
    186    }
    187
    188    /**
    189     * Creates the PoolableConnectionFactory and attaches it to the connection pool.
    190     *
    191     * @param driverConnectionFactory
    192     *            JDBC connection factory created by {@link #createConnectionFactory()}
    193     * @throws SQLException
    194     *             if an error occurs creating the PoolableConnectionFactory
    195     */
    196    @Override
    197    protected PoolableConnectionFactory createPoolableConnectionFactory(final ConnectionFactory driverConnectionFactory)
    198            throws SQLException {
    199        PoolableConnectionFactory connectionFactory = null;
    200        try {
    201            connectionFactory = new PoolableManagedConnectionFactory((XAConnectionFactory) driverConnectionFactory,
    202                    getRegisteredJmxName());
    203            connectionFactory.setValidationQuery(getValidationQuery());
    204            connectionFactory.setValidationQueryTimeout(getValidationQueryTimeout());
    205            connectionFactory.setConnectionInitSql(getConnectionInitSqls());
    206            connectionFactory.setDefaultReadOnly(getDefaultReadOnly());
    207            connectionFactory.setDefaultAutoCommit(getDefaultAutoCommit());
    208            connectionFactory.setDefaultTransactionIsolation(getDefaultTransactionIsolation());
    209            connectionFactory.setDefaultCatalog(getDefaultCatalog());
    210            connectionFactory.setCacheState(getCacheState());
    211            connectionFactory.setPoolStatements(isPoolPreparedStatements());
    212            connectionFactory.setMaxOpenPreparedStatements(getMaxOpenPreparedStatements());
    213            connectionFactory.setMaxConnLifetimeMillis(getMaxConnLifetimeMillis());
    214            connectionFactory.setRollbackOnReturn(getRollbackOnReturn());
    215            connectionFactory.setEnableAutoCommitOnReturn(getEnableAutoCommitOnReturn());
    216            connectionFactory.setDefaultQueryTimeout(getDefaultQueryTimeout());
    217            connectionFactory.setFastFailValidation(getFastFailValidation());
    218            connectionFactory.setDisconnectionSqlCodes(getDisconnectionSqlCodes());
    219            validateConnectionFactory(connectionFactory);
    220        } catch (final RuntimeException e) {
    221            throw e;
    222        } catch (final Exception e) {
    223            throw new SQLException("Cannot create PoolableConnectionFactory (" + e.getMessage() + ")", e);
    224        }
    225        return connectionFactory;
    226    }
    227}