001/* 002 * Licensed to the Apache Software Foundation (ASF) under one or more 003 * contributor license agreements. See the NOTICE file distributed with 004 * this work for additional information regarding copyright ownership. 005 * The ASF licenses this file to You under the Apache License, Version 2.0 006 * (the "License"); you may not use this file except in compliance with 007 * the License. You may obtain a copy of the License at 008 * 009 * http://www.apache.org/licenses/LICENSE-2.0 010 * 011 * Unless required by applicable law or agreed to in writing, software 012 * distributed under the License is distributed on an "AS IS" BASIS, 013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 014 * See the License for the specific language governing permissions and 015 * limitations under the License. 016 */ 017package org.apache.commons.dbcp2.managed; 018 019import java.sql.Connection; 020import java.sql.SQLException; 021import java.util.Objects; 022 023import javax.sql.ConnectionEvent; 024import javax.sql.ConnectionEventListener; 025import javax.sql.PooledConnection; 026import javax.sql.XAConnection; 027import javax.sql.XADataSource; 028import javax.transaction.TransactionManager; 029import javax.transaction.TransactionSynchronizationRegistry; 030import javax.transaction.xa.XAResource; 031 032import org.apache.commons.dbcp2.Utils; 033 034/** 035 * An implementation of XAConnectionFactory which uses a real XADataSource to obtain connections and XAResources. 036 * 037 * @since 2.0 038 */ 039public class DataSourceXAConnectionFactory implements XAConnectionFactory { 040 041 private static final class XAConnectionEventListener implements ConnectionEventListener { 042 @Override 043 public void connectionClosed(final ConnectionEvent event) { 044 final PooledConnection pc = (PooledConnection) event.getSource(); 045 pc.removeConnectionEventListener(this); 046 try { 047 pc.close(); 048 } catch (final SQLException e) { 049 System.err.println("Failed to close XAConnection"); 050 e.printStackTrace(); 051 } 052 } 053 054 @Override 055 public void connectionErrorOccurred(final ConnectionEvent event) { 056 connectionClosed(event); 057 } 058 } 059 060 private final TransactionRegistry transactionRegistry; 061 private final XADataSource xaDataSource; 062 private String userName; 063 private char[] userPassword; 064 065 /** 066 * Creates an DataSourceXAConnectionFactory which uses the specified XADataSource to create database connections. 067 * The connections are enlisted into transactions using the specified transaction manager. 068 * 069 * @param transactionManager 070 * the transaction manager in which connections will be enlisted 071 * @param xaDataSource 072 * the data source from which connections will be retrieved 073 * @since 2.6.0 074 */ 075 public DataSourceXAConnectionFactory(final TransactionManager transactionManager, final XADataSource xaDataSource) { 076 this(transactionManager, xaDataSource, null, (char[]) null, null); 077 } 078 079 /** 080 * Creates an DataSourceXAConnectionFactory which uses the specified XADataSource to create database connections. 081 * The connections are enlisted into transactions using the specified transaction manager. 082 * 083 * @param transactionManager 084 * the transaction manager in which connections will be enlisted 085 * @param xaDataSource 086 * the data source from which connections will be retrieved 087 * @param userName 088 * the user name used for authenticating new connections or null for unauthenticated 089 * @param userPassword 090 * the password used for authenticating new connections 091 */ 092 public DataSourceXAConnectionFactory(final TransactionManager transactionManager, final XADataSource xaDataSource, 093 final String userName, final char[] userPassword) { 094 this(transactionManager, xaDataSource, userName, userPassword, null); 095 } 096 097 /** 098 * Creates an DataSourceXAConnectionFactory which uses the specified XADataSource to create database connections. 099 * 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 118 // We do allow the transactionSynchronizationRegistry to be null for non-app server environments 119 120 this.transactionRegistry = new TransactionRegistry(transactionManager, transactionSynchronizationRegistry); 121 this.xaDataSource = xaDataSource; 122 this.userName = userName; 123 this.userPassword = Utils.clone(userPassword); 124 } 125 126 /** 127 * Creates an DataSourceXAConnectionFactory which uses the specified XADataSource to create database connections. 128 * The connections are enlisted into transactions using the specified transaction manager. 129 * 130 * @param transactionManager 131 * the transaction manager in which connections will be enlisted 132 * @param xaDataSource 133 * the data source from which connections will be retrieved 134 * @param userName 135 * the user name used for authenticating new connections or null for unauthenticated 136 * @param userPassword 137 * the password used for authenticating new connections 138 */ 139 public DataSourceXAConnectionFactory(final TransactionManager transactionManager, final XADataSource xaDataSource, 140 final String userName, final String userPassword) { 141 this(transactionManager, xaDataSource, userName, Utils.toCharArray(userPassword), null); 142 } 143 144 /** 145 * Creates an DataSourceXAConnectionFactory which uses the specified XADataSource to create database connections. 146 * The connections are enlisted into transactions using the specified transaction manager. 147 * 148 * @param transactionManager 149 * the transaction manager in which connections will be enlisted 150 * @param xaDataSource 151 * the data source from which connections will be retrieved 152 * @param transactionSynchronizationRegistry 153 * register with this TransactionSynchronizationRegistry 154 */ 155 public DataSourceXAConnectionFactory(final TransactionManager transactionManager, final XADataSource xaDataSource, final TransactionSynchronizationRegistry transactionSynchronizationRegistry) { 156 this(transactionManager, xaDataSource, null, (char[]) null, transactionSynchronizationRegistry); 157 } 158 159 @Override 160 public Connection createConnection() throws SQLException { 161 // create a new XAConnection 162 final XAConnection xaConnection; 163 if (userName == null) { 164 xaConnection = xaDataSource.getXAConnection(); 165 } else { 166 xaConnection = xaDataSource.getXAConnection(userName, Utils.toString(userPassword)); 167 } 168 169 // get the real connection and XAResource from the connection 170 final Connection connection = xaConnection.getConnection(); 171 final XAResource xaResource = xaConnection.getXAResource(); 172 173 // register the xa resource for the connection 174 transactionRegistry.registerConnection(connection, xaResource); 175 176 // The Connection we're returning is a handle on the XAConnection. 177 // When the pool calling us closes the Connection, we need to 178 // also close the XAConnection that holds the physical connection. 179 xaConnection.addConnectionEventListener(new XAConnectionEventListener()); 180 181 return connection; 182 } 183 184 @Override 185 public TransactionRegistry getTransactionRegistry() { 186 return transactionRegistry; 187 } 188 189 /** 190 * Gets the user name used to authenticate new connections. 191 * 192 * @return the user name or null if unauthenticated connections are used 193 * @deprecated Use {@link #getUserName()}. 194 */ 195 @Deprecated 196 public String getUsername() { 197 return userName; 198 } 199 200 /** 201 * Gets the user name used to authenticate new connections. 202 * 203 * @return the user name or null if unauthenticated connections are used 204 * @since 2.6.0 205 */ 206 public String getUserName() { 207 return userName; 208 } 209 210 /** 211 * Gets the user password. 212 * 213 * @return the user password. 214 */ 215 public char[] getUserPassword() { 216 return Utils.clone(userPassword); 217 } 218 219 /** 220 * Gets the XA data source. 221 * 222 * @return the XA data source. 223 */ 224 public XADataSource getXaDataSource() { 225 return xaDataSource; 226 } 227 228 /** 229 * Sets the password used to authenticate new connections. 230 * 231 * @param userPassword 232 * the password used for authenticating the connection or null for unauthenticated. 233 * @since 2.4.0 234 */ 235 public void setPassword(final char[] userPassword) { 236 this.userPassword = Utils.clone(userPassword); 237 } 238 239 /** 240 * Sets the password used to authenticate new connections. 241 * 242 * @param userPassword 243 * the password used for authenticating the connection or null for unauthenticated 244 */ 245 public void setPassword(final String userPassword) { 246 this.userPassword = Utils.toCharArray(userPassword); 247 } 248 249 /** 250 * Sets the user name used to authenticate new connections. 251 * 252 * @param userName 253 * the user name used for authenticating the connection or null for unauthenticated 254 */ 255 public void setUsername(final String userName) { 256 this.userName = userName; 257 } 258}