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 }