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 * @version $Id: DataSourceXAConnectionFactory.java 1649430 2015-01-04 21:29:32Z tn $
036 * @since 2.0
037 */
038public class DataSourceXAConnectionFactory implements XAConnectionFactory {
039    private final TransactionRegistry transactionRegistry;
040    private final XADataSource xaDataSource;
041    private String username;
042    private String password;
043
044    /**
045     * Creates an DataSourceXAConnectionFactory which uses the specified XADataSource to create database
046     * connections.  The connections are enlisted into transactions using the specified transaction manager.
047     *
048     * @param transactionManager the transaction manager in which connections will be enlisted
049     * @param xaDataSource the data source from which connections will be retrieved
050     */
051    public DataSourceXAConnectionFactory(TransactionManager transactionManager, XADataSource xaDataSource) {
052        this(transactionManager, xaDataSource, null, null);
053    }
054
055    /**
056     * Creates an DataSourceXAConnectionFactory which uses the specified XADataSource to create database
057     * connections.  The connections are enlisted into transactions using the specified transaction manager.
058     *
059     * @param transactionManager the transaction manager in which connections will be enlisted
060     * @param xaDataSource the data source from which connections will be retrieved
061     * @param username the username used for authenticating new connections or null for unauthenticated
062     * @param password the password used for authenticating new connections
063     */
064    public DataSourceXAConnectionFactory(TransactionManager transactionManager, XADataSource xaDataSource, String username, String password) {
065        if (transactionManager == null) {
066            throw new NullPointerException("transactionManager is null");
067        }
068        if (xaDataSource == null) {
069            throw new NullPointerException("xaDataSource is null");
070        }
071
072        this.transactionRegistry = new TransactionRegistry(transactionManager);
073        this.xaDataSource = xaDataSource;
074        this.username = username;
075        this.password = password;
076    }
077
078    /**
079     * Gets the username used to authenticate new connections.
080     * @return the user name or null if unauthenticated connections are used
081     */
082    public String getUsername() {
083        return username;
084    }
085
086    /**
087     * Sets the username used to authenticate new connections.
088     * @param username the username used for authenticating the connection or null for unauthenticated
089     */
090    public void setUsername(String username) {
091        this.username = username;
092    }
093
094    /**
095     * Sets the password used to authenticate new connections.
096     * @param password the password used for authenticating the connection or null for unauthenticated
097     */
098    public void setPassword(String password) {
099        this.password = password;
100    }
101
102    @Override
103    public TransactionRegistry getTransactionRegistry() {
104        return transactionRegistry;
105    }
106
107    @Override
108    public Connection createConnection() throws SQLException {
109        // create a new XAConection
110        XAConnection xaConnection;
111        if (username == null) {
112            xaConnection = xaDataSource.getXAConnection();
113        } else {
114            xaConnection = xaDataSource.getXAConnection(username, password);
115        }
116
117        // get the real connection and XAResource from the connection
118        Connection connection = xaConnection.getConnection();
119        XAResource xaResource = xaConnection.getXAResource();
120
121        // register the xa resource for the connection
122        transactionRegistry.registerConnection(connection, xaResource);
123
124        // The Connection we're returning is a handle on the XAConnection.
125        // When the pool calling us closes the Connection, we need to
126        // also close the XAConnection that holds the physical connection.
127        xaConnection.addConnectionEventListener(new ConnectionEventListener() {
128
129            @Override
130            public void connectionClosed(ConnectionEvent event) {
131                PooledConnection pc = (PooledConnection) event.getSource();
132                pc.removeConnectionEventListener(this);
133                try {
134                    pc.close();
135                } catch (SQLException e) {
136                    System.err.println("Failed to close XAConnection");
137                    e.printStackTrace();
138                }
139            }
140
141            @Override
142            public void connectionErrorOccurred(ConnectionEvent event) {
143                connectionClosed(event);
144            }
145        });
146
147
148        return connection;
149    }
150}