    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        // 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}