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 }