    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}