    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}