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.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         XAConnection xaConnection = null;
161         Connection connection = null;
162         final XAResource xaResource;
163         try {
164             if (userName == null) {
165                 xaConnection = xaDataSource.getXAConnection();
166             } else {
167                 xaConnection = xaDataSource.getXAConnection(userName, Utils.toString(userPassword));
168             }
169             // get the real connection and XAResource from the connection
170             connection = xaConnection.getConnection();
171             xaResource = xaConnection.getXAResource();
172         } catch (final SQLException sqle) {
173             if (connection != null) {
174                 try {
175                     connection.close();
176                 } catch (final SQLException ignored) {
177                     // Ignore
178                 }
179             }
180             if (xaConnection != null) {
181                 try {
182                     xaConnection.close();
183                 } catch (final SQLException ignored) {
184                     // Ignore
185                 }
186             }
187             throw sqle;
188         }
189         // register the XA resource for the connection
190         transactionRegistry.registerConnection(connection, xaResource);
191         // The Connection we're returning is a handle on the XAConnection.
192         // When the pool calling us closes the Connection, we need to
193         // also close the XAConnection that holds the physical connection.
194         xaConnection.addConnectionEventListener(new XAConnectionEventListener());
195 
196         return connection;
197     }
198 
199     @Override
200     public TransactionRegistry getTransactionRegistry() {
201         return transactionRegistry;
202     }
203 
204     /**
205      * Gets the user name used to authenticate new connections.
206      *
207      * @return the user name or null if unauthenticated connections are used
208      * @deprecated Use {@link #getUserName()}.
209      */
210     @Deprecated
211     public String getUsername() {
212         return userName;
213     }
214 
215     /**
216      * Gets the user name used to authenticate new connections.
217      *
218      * @return the user name or null if unauthenticated connections are used
219      * @since 2.6.0
220      */
221     public String getUserName() {
222         return userName;
223     }
224 
225     /**
226      * Gets the user password.
227      *
228      * @return the user password.
229      */
230     public char[] getUserPassword() {
231         return Utils.clone(userPassword);
232     }
233 
234     /**
235      * Gets the XA data source.
236      *
237      * @return the XA data source.
238      */
239     public XADataSource getXaDataSource() {
240         return xaDataSource;
241     }
242 
243     /**
244      * Sets the password used to authenticate new connections.
245      *
246      * @param userPassword
247      *            the password used for authenticating the connection or null for unauthenticated.
248      * @since 2.4.0
249      */
250     public void setPassword(final char[] userPassword) {
251         this.userPassword = Utils.clone(userPassword);
252     }
253 
254     /**
255      * Sets the password used to authenticate new connections.
256      *
257      * @param userPassword
258      *            the password used for authenticating the connection or null for unauthenticated
259      */
260     public void setPassword(final String userPassword) {
261         this.userPassword = Utils.toCharArray(userPassword);
262     }
263 
264     /**
265      * Sets the user name used to authenticate new connections.
266      *
267      * @param userName
268      *            the user name used for authenticating the connection or null for unauthenticated
269      */
270     public void setUsername(final String userName) {
271         this.userName = userName;
272     }
273 }