    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 *      https://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        XAConnection xaConnection = null;
    161        Connection connection = null;
    162        final XAResource xaResource;
    163        try {
    164            if (userName == null) {
    165                xaConnection = xaDataSource.getXAConnection();
    166            } else {
    167                xaConnection = xaDataSource.getXAConnection(userName, Utils.toString(userPassword));
    168            }
    169            // get the real connection and XAResource from the connection
    170            connection = xaConnection.getConnection();
    171            xaResource = xaConnection.getXAResource();
    172        } catch (final SQLException sqle) {
    173            if (connection != null) {
    174                try {
    175                    connection.close();
    176                } catch (final SQLException ignored) {
    177                    // Ignore
    178                }
    179            }
    180            if (xaConnection != null) {
    181                try {
    182                    xaConnection.close();
    183                } catch (final SQLException ignored) {
    184                    // Ignore
    185                }
    186            }
    187            throw sqle;
    188        }
    189        // register the XA resource for the connection
    190        transactionRegistry.registerConnection(connection, xaResource);
    191        // The Connection we're returning is a handle on the XAConnection.
    192        // When the pool calling us closes the Connection, we need to
    193        // also close the XAConnection that holds the physical connection.
    194        xaConnection.addConnectionEventListener(new XAConnectionEventListener());
    195
    196        return connection;
    197    }
    198
    199    @Override
    200    public TransactionRegistry getTransactionRegistry() {
    201        return transactionRegistry;
    202    }
    203
    204    /**
    205     * Gets the user name used to authenticate new connections.
    206     *
    207     * @return the user name or null if unauthenticated connections are used
    208     * @deprecated Use {@link #getUserName()}.
    209     */
    210    @Deprecated
    211    public String getUsername() {
    212        return userName;
    213    }
    214
    215    /**
    216     * Gets the user name used to authenticate new connections.
    217     *
    218     * @return the user name or null if unauthenticated connections are used
    219     * @since 2.6.0
    220     */
    221    public String getUserName() {
    222        return userName;
    223    }
    224
    225    /**
    226     * Gets the user password.
    227     *
    228     * @return the user password.
    229     */
    230    public char[] getUserPassword() {
    231        return Utils.clone(userPassword);
    232    }
    233
    234    /**
    235     * Gets the XA data source.
    236     *
    237     * @return the XA data source.
    238     */
    239    public XADataSource getXaDataSource() {
    240        return xaDataSource;
    241    }
    242
    243    /**
    244     * Sets the password used to authenticate new connections.
    245     *
    246     * @param userPassword
    247     *            the password used for authenticating the connection or null for unauthenticated.
    248     * @since 2.4.0
    249     */
    250    public void setPassword(final char[] userPassword) {
    251        this.userPassword = Utils.clone(userPassword);
    252    }
    253
    254    /**
    255     * Sets the password used to authenticate new connections.
    256     *
    257     * @param userPassword
    258     *            the password used for authenticating the connection or null for unauthenticated
    259     */
    260    public void setPassword(final String userPassword) {
    261        this.userPassword = Utils.toCharArray(userPassword);
    262    }
    263
    264    /**
    265     * Sets the user name used to authenticate new connections.
    266     *
    267     * @param userName
    268     *            the user name used for authenticating the connection or null for unauthenticated
    269     */
    270    public void setUsername(final String userName) {
    271        this.userName = userName;
    272    }
    273}