View Javadoc
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  
19  import java.sql.SQLException;
20  
21  import javax.sql.DataSource;
22  import javax.sql.XADataSource;
23  import javax.transaction.TransactionManager;
24  import javax.transaction.TransactionSynchronizationRegistry;
25  
26  import org.apache.commons.dbcp2.BasicDataSource;
27  import org.apache.commons.dbcp2.ConnectionFactory;
28  import org.apache.commons.dbcp2.PoolableConnection;
29  import org.apache.commons.dbcp2.PoolableConnectionFactory;
30  import org.apache.commons.dbcp2.PoolingDataSource;
31  import org.apache.commons.dbcp2.Utils;
32  
33  /**
34   * <p>
35   * BasicManagedDataSource is an extension of BasicDataSource which creates ManagedConnections. This data source can
36   * create either full two-phase-commit XA connections or one-phase-commit local connections. Both types of connections
37   * are committed or rolled back as part of the global transaction (a.k.a. XA transaction or JTA Transaction), but only
38   * XA connections can be recovered in the case of a system crash.
39   * </p>
40   * <p>
41   * BasicManagedDataSource adds the TransactionManager and XADataSource properties. The TransactionManager property is
42   * required and is used to enlist connections in global transactions. The XADataSource is optional and if set is the
43   * class name of the XADataSource class for a two-phase-commit JDBC driver. If the XADataSource property is set, the
44   * driverClassName is ignored and a DataSourceXAConnectionFactory is created. Otherwise, a standard
45   * DriverConnectionFactory is created and wrapped with a LocalXAConnectionFactory.
46   * </p>
47   *
48   * @see BasicDataSource
49   * @see ManagedConnection
50   * @since 2.0
51   */
52  public class BasicManagedDataSource extends BasicDataSource {
53  
54      /** Transaction Registry */
55      private TransactionRegistry transactionRegistry;
56  
57      /** Transaction Manager */
58      private transient TransactionManager transactionManager;
59  
60      /** XA data source class name */
61      private String xaDataSource;
62  
63      /** XA data source instance */
64      private XADataSource xaDataSourceInstance;
65  
66      /** Transaction Synchronization Registry */
67      private transient TransactionSynchronizationRegistry transactionSynchronizationRegistry;
68  
69      @Override
70      protected ConnectionFactory createConnectionFactory() throws SQLException {
71          if (transactionManager == null) {
72              throw new SQLException("Transaction manager must be set before a connection can be created");
73          }
74  
75          // If XA data source is not specified a DriverConnectionFactory is created and wrapped with a
76          // LocalXAConnectionFactory
77          if (xaDataSource == null) {
78              final ConnectionFactory connectionFactory = super.createConnectionFactory();
79              final XAConnectionFactory xaConnectionFactory = new LocalXAConnectionFactory(getTransactionManager(),
80                      getTransactionSynchronizationRegistry(), connectionFactory);
81              transactionRegistry = xaConnectionFactory.getTransactionRegistry();
82              return xaConnectionFactory;
83          }
84  
85          // Create the XADataSource instance using the configured class name if it has not been set
86          if (xaDataSourceInstance == null) {
87              Class<?> xaDataSourceClass = null;
88              try {
89                  xaDataSourceClass = Class.forName(xaDataSource);
90              } catch (final Exception e) {
91                  throw new SQLException("Cannot load XA data source class '" + xaDataSource + "'", e);
92              }
93  
94              try {
95                  xaDataSourceInstance = (XADataSource) xaDataSourceClass.getConstructor().newInstance();
96              } catch (final Exception e) {
97                  throw new SQLException("Cannot create XA data source of class '" + xaDataSource + "'", e);
98              }
99          }
100 
101         // finally, create the XAConnectionFactory using the XA data source
102         final XAConnectionFactory xaConnectionFactory = new DataSourceXAConnectionFactory(getTransactionManager(),
103                 xaDataSourceInstance, getUserName(), Utils.toCharArray(getPassword()), getTransactionSynchronizationRegistry());
104         transactionRegistry = xaConnectionFactory.getTransactionRegistry();
105         return xaConnectionFactory;
106     }
107 
108     @Override
109     protected DataSource createDataSourceInstance() throws SQLException {
110         final PoolingDataSource<PoolableConnection> pds = new ManagedDataSource<>(getConnectionPool(),
111                 transactionRegistry);
112         pds.setAccessToUnderlyingConnectionAllowed(isAccessToUnderlyingConnectionAllowed());
113         return pds;
114     }
115 
116     /**
117      * Creates the PoolableConnectionFactory and attaches it to the connection pool.
118      *
119      * @param driverConnectionFactory
120      *            JDBC connection factory created by {@link #createConnectionFactory()}
121      * @throws SQLException
122      *             if an error occurs creating the PoolableConnectionFactory
123      */
124     @Override
125     protected PoolableConnectionFactory createPoolableConnectionFactory(final ConnectionFactory driverConnectionFactory)
126             throws SQLException {
127         PoolableConnectionFactory connectionFactory = null;
128         try {
129             connectionFactory = new PoolableManagedConnectionFactory((XAConnectionFactory) driverConnectionFactory,
130                     getRegisteredJmxName());
131             connectionFactory.setValidationQuery(getValidationQuery());
132             connectionFactory.setValidationQueryTimeout(getValidationQueryTimeoutDuration());
133             connectionFactory.setConnectionInitSql(getConnectionInitSqls());
134             connectionFactory.setDefaultReadOnly(getDefaultReadOnly());
135             connectionFactory.setDefaultAutoCommit(getDefaultAutoCommit());
136             connectionFactory.setDefaultTransactionIsolation(getDefaultTransactionIsolation());
137             connectionFactory.setDefaultCatalog(getDefaultCatalog());
138             connectionFactory.setDefaultSchema(getDefaultSchema());
139             connectionFactory.setCacheState(getCacheState());
140             connectionFactory.setPoolStatements(isPoolPreparedStatements());
141             connectionFactory.setClearStatementPoolOnReturn(isClearStatementPoolOnReturn());
142             connectionFactory.setMaxOpenPreparedStatements(getMaxOpenPreparedStatements());
143             connectionFactory.setMaxConn(getMaxConnDuration());
144             connectionFactory.setRollbackOnReturn(getRollbackOnReturn());
145             connectionFactory.setAutoCommitOnReturn(getAutoCommitOnReturn());
146             connectionFactory.setDefaultQueryTimeout(getDefaultQueryTimeoutDuration());
147             connectionFactory.setFastFailValidation(getFastFailValidation());
148             connectionFactory.setDisconnectionSqlCodes(getDisconnectionSqlCodes());
149             validateConnectionFactory(connectionFactory);
150         } catch (final RuntimeException e) {
151             throw e;
152         } catch (final Exception e) {
153             throw new SQLException("Cannot create PoolableConnectionFactory (" + e.getMessage() + ")", e);
154         }
155         return connectionFactory;
156     }
157 
158     /**
159      * Gets the required transaction manager property.
160      *
161      * @return the transaction manager used to enlist connections
162      */
163     public TransactionManager getTransactionManager() {
164         return transactionManager;
165     }
166 
167     /**
168      * Gets the transaction registry.
169      *
170      * @return the transaction registry associating XAResources with managed connections
171      */
172     protected synchronized TransactionRegistry getTransactionRegistry() {
173         return transactionRegistry;
174     }
175 
176     /**
177      * Gets the optional TransactionSynchronizationRegistry.
178      *
179      * @return the TSR that can be used to register synchronizations.
180      * @since 2.6.0
181      */
182     public TransactionSynchronizationRegistry getTransactionSynchronizationRegistry() {
183         return transactionSynchronizationRegistry;
184     }
185 
186     /**
187      * Gets the optional XADataSource class name.
188      *
189      * @return the optional XADataSource class name
190      */
191     public synchronized String getXADataSource() {
192         return xaDataSource;
193     }
194 
195     /**
196      * Gets the XADataSource instance used by the XAConnectionFactory.
197      *
198      * @return the XADataSource
199      */
200     public synchronized XADataSource getXaDataSourceInstance() {
201         return xaDataSourceInstance;
202     }
203 
204     /**
205      * Sets the required transaction manager property.
206      *
207      * @param transactionManager
208      *            the transaction manager used to enlist connections
209      */
210     public void setTransactionManager(final TransactionManager transactionManager) {
211         this.transactionManager = transactionManager;
212     }
213 
214     /**
215      * Sets the optional TransactionSynchronizationRegistry property.
216      *
217      * @param transactionSynchronizationRegistry
218      *            the TSR used to register synchronizations
219      * @since 2.6.0
220      */
221     public void setTransactionSynchronizationRegistry(
222             final TransactionSynchronizationRegistry transactionSynchronizationRegistry) {
223         this.transactionSynchronizationRegistry = transactionSynchronizationRegistry;
224     }
225 
226     /**
227      * Sets the optional XADataSource class name.
228      *
229      * @param xaDataSource
230      *            the optional XADataSource class name
231      */
232     public synchronized void setXADataSource(final String xaDataSource) {
233         this.xaDataSource = xaDataSource;
234     }
235 
236     /**
237      * <p>
238      * Sets the XADataSource instance used by the XAConnectionFactory.
239      * </p>
240      * <p>
241      * Note: this method currently has no effect once the pool has been initialized. The pool is initialized the first
242      * time one of the following methods is invoked: <code>getConnection, setLogwriter,
243      * setLoginTimeout, getLoginTimeout, getLogWriter.</code>
244      * </p>
245      *
246      * @param xaDataSourceInstance
247      *            XADataSource instance
248      */
249     public synchronized void setXaDataSourceInstance(final XADataSource xaDataSourceInstance) {
250         this.xaDataSourceInstance = xaDataSourceInstance;
251         xaDataSource = xaDataSourceInstance == null ? null : xaDataSourceInstance.getClass().getName();
252     }
253 }