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    *      https://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      /**
70       * Constructs a new instance.
71       */
72      public BasicManagedDataSource() {
73          // empty
74      }
75  
76      @Override
77      protected synchronized ConnectionFactory createConnectionFactory() throws SQLException {
78          if (transactionManager == null) {
79              throw new SQLException("Transaction manager must be set before a connection can be created");
80          }
81  
82          // If XA data source is not specified a DriverConnectionFactory is created and wrapped with a
83          // LocalXAConnectionFactory
84          if (xaDataSource == null) {
85              final ConnectionFactory connectionFactory = super.createConnectionFactory();
86              final XAConnectionFactory xaConnectionFactory = new LocalXAConnectionFactory(getTransactionManager(),
87                      getTransactionSynchronizationRegistry(), connectionFactory);
88              transactionRegistry = xaConnectionFactory.getTransactionRegistry();
89              return xaConnectionFactory;
90          }
91  
92          // Create the XADataSource instance using the configured class name if it has not been set
93          if (xaDataSourceInstance == null) {
94              Class<?> xaDataSourceClass = null;
95              try {
96                  xaDataSourceClass = Class.forName(xaDataSource);
97              } catch (final Exception e) {
98                  throw new SQLException("Cannot load XA data source class '" + xaDataSource + "'", e);
99              }
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 }