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.Connection;
20  import java.sql.SQLException;
21  import java.util.Objects;
22  
23  import javax.sql.ConnectionEvent;
24  import javax.sql.ConnectionEventListener;
25  import javax.sql.PooledConnection;
26  import javax.sql.XAConnection;
27  import javax.sql.XADataSource;
28  import javax.transaction.TransactionManager;
29  import javax.transaction.TransactionSynchronizationRegistry;
30  import javax.transaction.xa.XAResource;
31  
32  import org.apache.commons.dbcp2.Utils;
33  
34  /**
35   * An implementation of XAConnectionFactory which uses a real XADataSource to obtain connections and XAResources.
36   *
37   * @since 2.0
38   */
39  public class DataSourceXAConnectionFactory implements XAConnectionFactory {
40  
41      private static final class XAConnectionEventListener implements ConnectionEventListener {
42          @Override
43          public void connectionClosed(final ConnectionEvent event) {
44              final PooledConnection pc = (PooledConnection) event.getSource();
45              pc.removeConnectionEventListener(this);
46              try {
47                  pc.close();
48              } catch (final SQLException e) {
49                  System.err.println("Failed to close XAConnection");
50                  e.printStackTrace();
51              }
52          }
53  
54          @Override
55          public void connectionErrorOccurred(final ConnectionEvent event) {
56              connectionClosed(event);
57          }
58      }
59  
60      private final TransactionRegistry transactionRegistry;
61      private final XADataSource xaDataSource;
62      private String userName;
63      private char[] userPassword;
64  
65      /**
66       * Creates an DataSourceXAConnectionFactory which uses the specified XADataSource to create database connections.
67       * The connections are enlisted into transactions using the specified transaction manager.
68       *
69       * @param transactionManager
70       *            the transaction manager in which connections will be enlisted
71       * @param xaDataSource
72       *            the data source from which connections will be retrieved
73       * @since 2.6.0
74       */
75      public DataSourceXAConnectionFactory(final TransactionManager transactionManager, final XADataSource xaDataSource) {
76          this(transactionManager, xaDataSource, null, (char[]) null, null);
77      }
78  
79      /**
80       * Creates an DataSourceXAConnectionFactory which uses the specified XADataSource to create database connections.
81       * The connections are enlisted into transactions using the specified transaction manager.
82       *
83       * @param transactionManager
84       *            the transaction manager in which connections will be enlisted
85       * @param xaDataSource
86       *            the data source from which connections will be retrieved
87       * @param userName
88       *            the user name used for authenticating new connections or null for unauthenticated
89       * @param userPassword
90       *            the password used for authenticating new connections
91       */
92      public DataSourceXAConnectionFactory(final TransactionManager transactionManager, final XADataSource xaDataSource,
93              final String userName, final char[] userPassword) {
94          this(transactionManager, xaDataSource, userName, userPassword, null);
95      }
96  
97      /**
98       * Creates an DataSourceXAConnectionFactory which uses the specified XADataSource to create database connections.
99       * The connections are enlisted into transactions using the specified transaction manager.
100      *
101      * @param transactionManager
102      *            the transaction manager in which connections will be enlisted
103      * @param xaDataSource
104      *            the data source from which connections will be retrieved
105      * @param userName
106      *            the user name used for authenticating new connections or null for unauthenticated
107      * @param userPassword
108      *            the password used for authenticating new connections
109      * @param transactionSynchronizationRegistry
110      *            register with this TransactionSynchronizationRegistry
111      * @since 2.6.0
112      */
113     public DataSourceXAConnectionFactory(final TransactionManager transactionManager, final XADataSource xaDataSource,
114             final String userName, final char[] userPassword, final TransactionSynchronizationRegistry transactionSynchronizationRegistry) {
115         Objects.requireNonNull(transactionManager, "transactionManager");
116         Objects.requireNonNull(xaDataSource, "xaDataSource");
117         // We do allow the transactionSynchronizationRegistry to be null for non-app server environments
118         this.transactionRegistry = new TransactionRegistry(transactionManager, transactionSynchronizationRegistry);
119         this.xaDataSource = xaDataSource;
120         this.userName = userName;
121         this.userPassword = Utils.clone(userPassword);
122     }
123 
124     /**
125      * Creates an DataSourceXAConnectionFactory which uses the specified XADataSource to create database connections.
126      * The connections are enlisted into transactions using the specified transaction manager.
127      *
128      * @param transactionManager
129      *            the transaction manager in which connections will be enlisted
130      * @param xaDataSource
131      *            the data source from which connections will be retrieved
132      * @param userName
133      *            the user name used for authenticating new connections or null for unauthenticated
134      * @param userPassword
135      *            the password used for authenticating new connections
136      */
137     public DataSourceXAConnectionFactory(final TransactionManager transactionManager, final XADataSource xaDataSource,
138             final String userName, final String userPassword) {
139         this(transactionManager, xaDataSource, userName, Utils.toCharArray(userPassword), null);
140     }
141 
142     /**
143      * Creates an DataSourceXAConnectionFactory which uses the specified XADataSource to create database connections.
144      * The connections are enlisted into transactions using the specified transaction manager.
145      *
146      * @param transactionManager
147      *            the transaction manager in which connections will be enlisted
148      * @param xaDataSource
149      *            the data source from which connections will be retrieved
150      * @param transactionSynchronizationRegistry
151      *            register with this TransactionSynchronizationRegistry
152      */
153     public DataSourceXAConnectionFactory(final TransactionManager transactionManager, final XADataSource xaDataSource, final TransactionSynchronizationRegistry transactionSynchronizationRegistry) {
154         this(transactionManager, xaDataSource, null, (char[]) null, transactionSynchronizationRegistry);
155     }
156 
157     @Override
158     public Connection createConnection() throws SQLException {
159         // create a new XAConnection
160         final XAConnection xaConnection;
161         if (userName == null) {
162             xaConnection = xaDataSource.getXAConnection();
163         } else {
164             xaConnection = xaDataSource.getXAConnection(userName, Utils.toString(userPassword));
165         }
166         // get the real connection and XAResource from the connection
167         final Connection connection = xaConnection.getConnection();
168         final XAResource xaResource = xaConnection.getXAResource();
169         // register the XA resource for the connection
170         transactionRegistry.registerConnection(connection, xaResource);
171         // The Connection we're returning is a handle on the XAConnection.
172         // When the pool calling us closes the Connection, we need to
173         // also close the XAConnection that holds the physical connection.
174         xaConnection.addConnectionEventListener(new XAConnectionEventListener());
175 
176         return connection;
177     }
178 
179     @Override
180     public TransactionRegistry getTransactionRegistry() {
181         return transactionRegistry;
182     }
183 
184     /**
185      * Gets the user name used to authenticate new connections.
186      *
187      * @return the user name or null if unauthenticated connections are used
188      * @deprecated Use {@link #getUserName()}.
189      */
190     @Deprecated
191     public String getUsername() {
192         return userName;
193     }
194 
195     /**
196      * Gets the user name used to authenticate new connections.
197      *
198      * @return the user name or null if unauthenticated connections are used
199      * @since 2.6.0
200      */
201     public String getUserName() {
202         return userName;
203     }
204 
205     /**
206      * Gets the user password.
207      *
208      * @return the user password.
209      */
210     public char[] getUserPassword() {
211         return Utils.clone(userPassword);
212     }
213 
214     /**
215      * Gets the XA data source.
216      *
217      * @return the XA data source.
218      */
219     public XADataSource getXaDataSource() {
220         return xaDataSource;
221     }
222 
223     /**
224      * Sets the password used to authenticate new connections.
225      *
226      * @param userPassword
227      *            the password used for authenticating the connection or null for unauthenticated.
228      * @since 2.4.0
229      */
230     public void setPassword(final char[] userPassword) {
231         this.userPassword = Utils.clone(userPassword);
232     }
233 
234     /**
235      * Sets the password used to authenticate new connections.
236      *
237      * @param userPassword
238      *            the password used for authenticating the connection or null for unauthenticated
239      */
240     public void setPassword(final String userPassword) {
241         this.userPassword = Utils.toCharArray(userPassword);
242     }
243 
244     /**
245      * Sets the user name used to authenticate new connections.
246      *
247      * @param userName
248      *            the user name used for authenticating the connection or null for unauthenticated
249      */
250     public void setUsername(final String userName) {
251         this.userName = userName;
252     }
253 }