001/** 002 * 003 * Licensed to the Apache Software Foundation (ASF) under one or more 004 * contributor license agreements. See the NOTICE file distributed with 005 * this work for additional information regarding copyright ownership. 006 * The ASF licenses this file to You under the Apache License, Version 2.0 007 * (the "License"); you may not use this file except in compliance with 008 * the License. You may obtain a copy of the License at 009 * 010 * http://www.apache.org/licenses/LICENSE-2.0 011 * 012 * Unless required by applicable law or agreed to in writing, software 013 * distributed under the License is distributed on an "AS IS" BASIS, 014 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 015 * See the License for the specific language governing permissions and 016 * limitations under the License. 017 */ 018package org.apache.commons.dbcp2.managed; 019 020import javax.sql.ConnectionEvent; 021import javax.sql.ConnectionEventListener; 022import javax.sql.PooledConnection; 023import javax.sql.XAConnection; 024import javax.sql.XADataSource; 025import javax.transaction.TransactionManager; 026import javax.transaction.xa.XAResource; 027 028import java.sql.Connection; 029import java.sql.SQLException; 030 031/** 032 * An implementation of XAConnectionFactory which uses a real XADataSource to obtain connections and XAResources. 033 * 034 * @author Dain Sundstrom 035 * @since 2.0 036 */ 037public class DataSourceXAConnectionFactory implements XAConnectionFactory { 038 private final TransactionRegistry transactionRegistry; 039 private final XADataSource xaDataSource; 040 private String username; 041 private String password; 042 043 /** 044 * Creates an DataSourceXAConnectionFactory which uses the specified XADataSource to create database 045 * connections. The connections are enlisted into transactions using the specified transaction manager. 046 * 047 * @param transactionManager the transaction manager in which connections will be enlisted 048 * @param xaDataSource the data source from which connections will be retrieved 049 */ 050 public DataSourceXAConnectionFactory(final TransactionManager transactionManager, final XADataSource xaDataSource) { 051 this(transactionManager, xaDataSource, null, null); 052 } 053 054 /** 055 * Creates an DataSourceXAConnectionFactory which uses the specified XADataSource to create database 056 * connections. The connections are enlisted into transactions using the specified transaction manager. 057 * 058 * @param transactionManager the transaction manager in which connections will be enlisted 059 * @param xaDataSource the data source from which connections will be retrieved 060 * @param username the username used for authenticating new connections or null for unauthenticated 061 * @param password the password used for authenticating new connections 062 */ 063 public DataSourceXAConnectionFactory(final TransactionManager transactionManager, final XADataSource xaDataSource, final String username, final String password) { 064 if (transactionManager == null) { 065 throw new NullPointerException("transactionManager is null"); 066 } 067 if (xaDataSource == null) { 068 throw new NullPointerException("xaDataSource is null"); 069 } 070 071 this.transactionRegistry = new TransactionRegistry(transactionManager); 072 this.xaDataSource = xaDataSource; 073 this.username = username; 074 this.password = password; 075 } 076 077 /** 078 * Gets the username used to authenticate new connections. 079 * @return the user name or null if unauthenticated connections are used 080 */ 081 public String getUsername() { 082 return username; 083 } 084 085 /** 086 * Sets the username used to authenticate new connections. 087 * @param username the username used for authenticating the connection or null for unauthenticated 088 */ 089 public void setUsername(final String username) { 090 this.username = username; 091 } 092 093 /** 094 * Sets the password used to authenticate new connections. 095 * @param password the password used for authenticating the connection or null for unauthenticated 096 */ 097 public void setPassword(final String password) { 098 this.password = password; 099 } 100 101 @Override 102 public TransactionRegistry getTransactionRegistry() { 103 return transactionRegistry; 104 } 105 106 @Override 107 public Connection createConnection() throws SQLException { 108 // create a new XAConnection 109 XAConnection xaConnection; 110 if (username == null) { 111 xaConnection = xaDataSource.getXAConnection(); 112 } else { 113 xaConnection = xaDataSource.getXAConnection(username, password); 114 } 115 116 // get the real connection and XAResource from the connection 117 final Connection connection = xaConnection.getConnection(); 118 final XAResource xaResource = xaConnection.getXAResource(); 119 120 // register the xa resource for the connection 121 transactionRegistry.registerConnection(connection, xaResource); 122 123 // The Connection we're returning is a handle on the XAConnection. 124 // When the pool calling us closes the Connection, we need to 125 // also close the XAConnection that holds the physical connection. 126 xaConnection.addConnectionEventListener(new ConnectionEventListener() { 127 128 @Override 129 public void connectionClosed(final ConnectionEvent event) { 130 final PooledConnection pc = (PooledConnection) event.getSource(); 131 pc.removeConnectionEventListener(this); 132 try { 133 pc.close(); 134 } catch (final SQLException e) { 135 System.err.println("Failed to close XAConnection"); 136 e.printStackTrace(); 137 } 138 } 139 140 @Override 141 public void connectionErrorOccurred(final ConnectionEvent event) { 142 connectionClosed(event); 143 } 144 }); 145 146 147 return connection; 148 } 149}