BasicManagedDataSource.java

  1. /*
  2.  * Licensed to the Apache Software Foundation (ASF) under one or more
  3.  * contributor license agreements.  See the NOTICE file distributed with
  4.  * this work for additional information regarding copyright ownership.
  5.  * The ASF licenses this file to You under the Apache License, Version 2.0
  6.  * (the "License"); you may not use this file except in compliance with
  7.  * the License.  You may obtain a copy of the License at
  8.  *
  9.  *      http://www.apache.org/licenses/LICENSE-2.0
  10.  *
  11.  * Unless required by applicable law or agreed to in writing, software
  12.  * distributed under the License is distributed on an "AS IS" BASIS,
  13.  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  14.  * See the License for the specific language governing permissions and
  15.  * limitations under the License.
  16.  */
  17. package org.apache.commons.dbcp2.managed;

  18. import java.sql.SQLException;

  19. import javax.sql.DataSource;
  20. import javax.sql.XADataSource;
  21. import javax.transaction.TransactionManager;
  22. import javax.transaction.TransactionSynchronizationRegistry;

  23. import org.apache.commons.dbcp2.BasicDataSource;
  24. import org.apache.commons.dbcp2.ConnectionFactory;
  25. import org.apache.commons.dbcp2.PoolableConnection;
  26. import org.apache.commons.dbcp2.PoolableConnectionFactory;
  27. import org.apache.commons.dbcp2.PoolingDataSource;
  28. import org.apache.commons.dbcp2.Utils;

  29. /**
  30.  * <p>
  31.  * BasicManagedDataSource is an extension of BasicDataSource which creates ManagedConnections. This data source can
  32.  * create either full two-phase-commit XA connections or one-phase-commit local connections. Both types of connections
  33.  * are committed or rolled back as part of the global transaction (a.k.a. XA transaction or JTA Transaction), but only
  34.  * XA connections can be recovered in the case of a system crash.
  35.  * </p>
  36.  * <p>
  37.  * BasicManagedDataSource adds the TransactionManager and XADataSource properties. The TransactionManager property is
  38.  * required and is used to enlist connections in global transactions. The XADataSource is optional and if set is the
  39.  * class name of the XADataSource class for a two-phase-commit JDBC driver. If the XADataSource property is set, the
  40.  * driverClassName is ignored and a DataSourceXAConnectionFactory is created. Otherwise, a standard
  41.  * DriverConnectionFactory is created and wrapped with a LocalXAConnectionFactory.
  42.  * </p>
  43.  *
  44.  * @see BasicDataSource
  45.  * @see ManagedConnection
  46.  * @since 2.0
  47.  */
  48. public class BasicManagedDataSource extends BasicDataSource {

  49.     /** Transaction Registry */
  50.     private TransactionRegistry transactionRegistry;

  51.     /** Transaction Manager */
  52.     private transient TransactionManager transactionManager;

  53.     /** XA data source class name */
  54.     private String xaDataSource;

  55.     /** XA data source instance */
  56.     private XADataSource xaDataSourceInstance;

  57.     /** Transaction Synchronization Registry */
  58.     private transient TransactionSynchronizationRegistry transactionSynchronizationRegistry;

  59.     @Override
  60.     protected ConnectionFactory createConnectionFactory() throws SQLException {
  61.         if (transactionManager == null) {
  62.             throw new SQLException("Transaction manager must be set before a connection can be created");
  63.         }

  64.         // If XA data source is not specified a DriverConnectionFactory is created and wrapped with a
  65.         // LocalXAConnectionFactory
  66.         if (xaDataSource == null) {
  67.             final ConnectionFactory connectionFactory = super.createConnectionFactory();
  68.             final XAConnectionFactory xaConnectionFactory = new LocalXAConnectionFactory(getTransactionManager(),
  69.                     getTransactionSynchronizationRegistry(), connectionFactory);
  70.             transactionRegistry = xaConnectionFactory.getTransactionRegistry();
  71.             return xaConnectionFactory;
  72.         }

  73.         // Create the XADataSource instance using the configured class name if it has not been set
  74.         if (xaDataSourceInstance == null) {
  75.             Class<?> xaDataSourceClass = null;
  76.             try {
  77.                 xaDataSourceClass = Class.forName(xaDataSource);
  78.             } catch (final Exception e) {
  79.                 throw new SQLException("Cannot load XA data source class '" + xaDataSource + "'", e);
  80.             }

  81.             try {
  82.                 xaDataSourceInstance = (XADataSource) xaDataSourceClass.getConstructor().newInstance();
  83.             } catch (final Exception e) {
  84.                 throw new SQLException("Cannot create XA data source of class '" + xaDataSource + "'", e);
  85.             }
  86.         }

  87.         // finally, create the XAConnectionFactory using the XA data source
  88.         final XAConnectionFactory xaConnectionFactory = new DataSourceXAConnectionFactory(getTransactionManager(),
  89.                 xaDataSourceInstance, getUserName(), Utils.toCharArray(getPassword()), getTransactionSynchronizationRegistry());
  90.         transactionRegistry = xaConnectionFactory.getTransactionRegistry();
  91.         return xaConnectionFactory;
  92.     }

  93.     @Override
  94.     protected DataSource createDataSourceInstance() throws SQLException {
  95.         final PoolingDataSource<PoolableConnection> pds = new ManagedDataSource<>(getConnectionPool(),
  96.                 transactionRegistry);
  97.         pds.setAccessToUnderlyingConnectionAllowed(isAccessToUnderlyingConnectionAllowed());
  98.         return pds;
  99.     }

  100.     /**
  101.      * Creates the PoolableConnectionFactory and attaches it to the connection pool.
  102.      *
  103.      * @param driverConnectionFactory
  104.      *            JDBC connection factory created by {@link #createConnectionFactory()}
  105.      * @throws SQLException
  106.      *             if an error occurs creating the PoolableConnectionFactory
  107.      */
  108.     @Override
  109.     protected PoolableConnectionFactory createPoolableConnectionFactory(final ConnectionFactory driverConnectionFactory)
  110.             throws SQLException {
  111.         PoolableConnectionFactory connectionFactory = null;
  112.         try {
  113.             connectionFactory = new PoolableManagedConnectionFactory((XAConnectionFactory) driverConnectionFactory, getRegisteredJmxName());
  114.             connectionFactory.setValidationQuery(getValidationQuery());
  115.             connectionFactory.setValidationQueryTimeout(getValidationQueryTimeoutDuration());
  116.             connectionFactory.setConnectionInitSql(getConnectionInitSqls());
  117.             connectionFactory.setDefaultReadOnly(getDefaultReadOnly());
  118.             connectionFactory.setDefaultAutoCommit(getDefaultAutoCommit());
  119.             connectionFactory.setDefaultTransactionIsolation(getDefaultTransactionIsolation());
  120.             connectionFactory.setDefaultCatalog(getDefaultCatalog());
  121.             connectionFactory.setDefaultSchema(getDefaultSchema());
  122.             connectionFactory.setCacheState(getCacheState());
  123.             connectionFactory.setPoolStatements(isPoolPreparedStatements());
  124.             connectionFactory.setClearStatementPoolOnReturn(isClearStatementPoolOnReturn());
  125.             connectionFactory.setMaxOpenPreparedStatements(getMaxOpenPreparedStatements());
  126.             connectionFactory.setMaxConn(getMaxConnDuration());
  127.             connectionFactory.setRollbackOnReturn(getRollbackOnReturn());
  128.             connectionFactory.setAutoCommitOnReturn(getAutoCommitOnReturn());
  129.             connectionFactory.setDefaultQueryTimeout(getDefaultQueryTimeoutDuration());
  130.             connectionFactory.setFastFailValidation(getFastFailValidation());
  131.             connectionFactory.setDisconnectionSqlCodes(getDisconnectionSqlCodes());
  132.             connectionFactory.setDisconnectionIgnoreSqlCodes(getDisconnectionIgnoreSqlCodes());
  133.             validateConnectionFactory(connectionFactory);
  134.         } catch (final RuntimeException e) {
  135.             throw e;
  136.         } catch (final Exception e) {
  137.             throw new SQLException("Cannot create PoolableConnectionFactory (" + e.getMessage() + ")", e);
  138.         }
  139.         return connectionFactory;
  140.     }

  141.     /**
  142.      * Gets the required transaction manager property.
  143.      *
  144.      * @return the transaction manager used to enlist connections
  145.      */
  146.     public TransactionManager getTransactionManager() {
  147.         return transactionManager;
  148.     }

  149.     /**
  150.      * Gets the transaction registry.
  151.      *
  152.      * @return the transaction registry associating XAResources with managed connections
  153.      */
  154.     protected synchronized TransactionRegistry getTransactionRegistry() {
  155.         return transactionRegistry;
  156.     }

  157.     /**
  158.      * Gets the optional TransactionSynchronizationRegistry.
  159.      *
  160.      * @return the TSR that can be used to register synchronizations.
  161.      * @since 2.6.0
  162.      */
  163.     public TransactionSynchronizationRegistry getTransactionSynchronizationRegistry() {
  164.         return transactionSynchronizationRegistry;
  165.     }

  166.     /**
  167.      * Gets the optional XADataSource class name.
  168.      *
  169.      * @return the optional XADataSource class name
  170.      */
  171.     public synchronized String getXADataSource() {
  172.         return xaDataSource;
  173.     }

  174.     /**
  175.      * Gets the XADataSource instance used by the XAConnectionFactory.
  176.      *
  177.      * @return the XADataSource
  178.      */
  179.     public synchronized XADataSource getXaDataSourceInstance() {
  180.         return xaDataSourceInstance;
  181.     }

  182.     /**
  183.      * Sets the required transaction manager property.
  184.      *
  185.      * @param transactionManager
  186.      *            the transaction manager used to enlist connections
  187.      */
  188.     public void setTransactionManager(final TransactionManager transactionManager) {
  189.         this.transactionManager = transactionManager;
  190.     }

  191.     /**
  192.      * Sets the optional TransactionSynchronizationRegistry property.
  193.      *
  194.      * @param transactionSynchronizationRegistry
  195.      *            the TSR used to register synchronizations
  196.      * @since 2.6.0
  197.      */
  198.     public void setTransactionSynchronizationRegistry(
  199.             final TransactionSynchronizationRegistry transactionSynchronizationRegistry) {
  200.         this.transactionSynchronizationRegistry = transactionSynchronizationRegistry;
  201.     }

  202.     /**
  203.      * Sets the optional XADataSource class name.
  204.      *
  205.      * @param xaDataSource
  206.      *            the optional XADataSource class name
  207.      */
  208.     public synchronized void setXADataSource(final String xaDataSource) {
  209.         this.xaDataSource = xaDataSource;
  210.     }

  211.     /**
  212.      * Sets the XADataSource instance used by the XAConnectionFactory.
  213.      * <p>
  214.      * 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
  215.      * invoked: {@link #getConnection()}, {@link #setLogWriter(java.io.PrintWriter)}, {@link #setLoginTimeout(int)}, {@link #getLoginTimeout()},
  216.      * {@link #getLogWriter()}.
  217.      * </p>
  218.      *
  219.      * @param xaDataSourceInstance XADataSource instance
  220.      */
  221.     public synchronized void setXaDataSourceInstance(final XADataSource xaDataSourceInstance) {
  222.         this.xaDataSourceInstance = xaDataSourceInstance;
  223.         xaDataSource = xaDataSourceInstance == null ? null : xaDataSourceInstance.getClass().getName();
  224.     }
  225. }