    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.TransactionSynchronizationRegistry;
    027import javax.transaction.xa.XAResource;
    028
    029import org.apache.commons.dbcp2.Utils;
    030
    031import java.sql.Connection;
    032import java.sql.SQLException;
    033import java.util.Objects;
    034
    035/**
    036 * An implementation of XAConnectionFactory which uses a real XADataSource to obtain connections and XAResources.
    037 *
    038 * @since 2.0
    039 */
    040public class DataSourceXAConnectionFactory implements XAConnectionFactory {
    041    private final TransactionRegistry transactionRegistry;
    042    private final XADataSource xaDataSource;
    043    private String userName;
    044    private char[] userPassword;
    045
    046    /**
    047     * Creates an DataSourceXAConnectionFactory which uses the specified XADataSource to create database connections.
    048     * The connections are enlisted into transactions using the specified transaction manager.
    049     *
    050     * @param transactionManager
    051     *            the transaction manager in which connections will be enlisted
    052     * @param xaDataSource
    053     *            the data source from which connections will be retrieved
    054     * @since 2.6.0
    055     */
    056    public DataSourceXAConnectionFactory(final TransactionManager transactionManager, final XADataSource xaDataSource) {
    057        this(transactionManager, xaDataSource, null, (char[]) null, null);
    058    }
    059
    060    /**
    061     * Creates an DataSourceXAConnectionFactory which uses the specified XADataSource to create database connections.
    062     * The connections are enlisted into transactions using the specified transaction manager.
    063     *
    064     * @param transactionManager
    065     *            the transaction manager in which connections will be enlisted
    066     * @param xaDataSource
    067     *            the data source from which connections will be retrieved
    068     * @param userName
    069     *            the user name used for authenticating new connections or null for unauthenticated
    070     * @param userPassword
    071     *            the password used for authenticating new connections
    072     */
    073    public DataSourceXAConnectionFactory(final TransactionManager transactionManager, final XADataSource xaDataSource,
    074            final String userName, final char[] userPassword) {
    075        this(transactionManager, xaDataSource, userName, userPassword, null);
    076    }
    077
    078    /**
    079     * Creates an DataSourceXAConnectionFactory which uses the specified XADataSource to create database connections.
    080     * The connections are enlisted into transactions using the specified transaction manager.
    081     *
    082     * @param transactionManager
    083     *            the transaction manager in which connections will be enlisted
    084     * @param xaDataSource
    085     *            the data source from which connections will be retrieved
    086     * @param userName
    087     *            the user name used for authenticating new connections or null for unauthenticated
    088     * @param userPassword
    089     *            the password used for authenticating new connections
    090     * @param transactionSynchronizationRegistry
    091     *            register with this TransactionSynchronizationRegistry
    092     * @since 2.6.0
    093     */
    094    public DataSourceXAConnectionFactory(final TransactionManager transactionManager, final XADataSource xaDataSource,
    095            final String userName, final char[] userPassword, final TransactionSynchronizationRegistry transactionSynchronizationRegistry) {
    096        Objects.requireNonNull(transactionManager, "transactionManager is null");
    097        Objects.requireNonNull(xaDataSource, "xaDataSource is null");
    098
    099        // We do allow the transactionSynchronizationRegistry to be null for non-app server environments
    100
    101        this.transactionRegistry = new TransactionRegistry(transactionManager, transactionSynchronizationRegistry);
    102        this.xaDataSource = xaDataSource;
    103        this.userName = userName;
    104        this.userPassword = userPassword;
    105    }
    106
    107    /**
    108     * Creates an DataSourceXAConnectionFactory which uses the specified XADataSource to create database connections.
    109     * The connections are enlisted into transactions using the specified transaction manager.
    110     *
    111     * @param transactionManager
    112     *            the transaction manager in which connections will be enlisted
    113     * @param xaDataSource
    114     *            the data source from which connections will be retrieved
    115     * @param userName
    116     *            the user name used for authenticating new connections or null for unauthenticated
    117     * @param userPassword
    118     *            the password used for authenticating new connections
    119     */
    120    public DataSourceXAConnectionFactory(final TransactionManager transactionManager, final XADataSource xaDataSource,
    121            final String userName, final String userPassword) {
    122        this(transactionManager, xaDataSource, userName, Utils.toCharArray(userPassword), null);
    123    }
    124
    125    /**
    126     * Creates an DataSourceXAConnectionFactory which uses the specified XADataSource to create database connections.
    127     * The connections are enlisted into transactions using the specified transaction manager.
    128     *
    129     * @param transactionManager
    130     *            the transaction manager in which connections will be enlisted
    131     * @param xaDataSource
    132     *            the data source from which connections will be retrieved
    133     * @param transactionSynchronizationRegistry
    134     *            register with this TransactionSynchronizationRegistry
    135     */
    136    public DataSourceXAConnectionFactory(final TransactionManager transactionManager, final XADataSource xaDataSource, final TransactionSynchronizationRegistry transactionSynchronizationRegistry) {
    137        this(transactionManager, xaDataSource, null, (char[]) null, transactionSynchronizationRegistry);
    138    }
    139
    140    @Override
    141    public Connection createConnection() throws SQLException {
    142        // create a new XAConnection
    143        XAConnection xaConnection;
    144        if (userName == null) {
    145            xaConnection = xaDataSource.getXAConnection();
    146        } else {
    147            xaConnection = xaDataSource.getXAConnection(userName, Utils.toString(userPassword));
    148        }
    149
    150        // get the real connection and XAResource from the connection
    151        final Connection connection = xaConnection.getConnection();
    152        final XAResource xaResource = xaConnection.getXAResource();
    153
    154        // register the xa resource for the connection
    155        transactionRegistry.registerConnection(connection, xaResource);
    156
    157        // The Connection we're returning is a handle on the XAConnection.
    158        // When the pool calling us closes the Connection, we need to
    159        // also close the XAConnection that holds the physical connection.
    160        xaConnection.addConnectionEventListener(new ConnectionEventListener() {
    161
    162            @Override
    163            public void connectionClosed(final ConnectionEvent event) {
    164                final PooledConnection pc = (PooledConnection) event.getSource();
    165                pc.removeConnectionEventListener(this);
    166                try {
    167                    pc.close();
    168                } catch (final SQLException e) {
    169                    System.err.println("Failed to close XAConnection");
    170                    e.printStackTrace();
    171                }
    172            }
    173
    174            @Override
    175            public void connectionErrorOccurred(final ConnectionEvent event) {
    176                connectionClosed(event);
    177            }
    178        });
    179
    180        return connection;
    181    }
    182
    183    @Override
    184    public TransactionRegistry getTransactionRegistry() {
    185        return transactionRegistry;
    186    }
    187
    188    /**
    189     * Gets the user name used to authenticate new connections.
    190     *
    191     * @return the user name or null if unauthenticated connections are used
    192     * @deprecated Use {@link #getUserName()}.
    193     */
    194    @Deprecated
    195    public String getUsername() {
    196        return userName;
    197    }
    198
    199    /**
    200     * Gets the user name used to authenticate new connections.
    201     *
    202     * @return the user name or null if unauthenticated connections are used
    203     * @since 2.6.0
    204     */
    205    public String getUserName() {
    206        return userName;
    207    }
    208
    209    public char[] getUserPassword() {
    210        return userPassword;
    211    }
    212
    213    public XADataSource getXaDataSource() {
    214        return xaDataSource;
    215    }
    216
    217    /**
    218     * Sets the password used to authenticate new connections.
    219     *
    220     * @param userPassword
    221     *            the password used for authenticating the connection or null for unauthenticated.
    222     * @since 2.4.0
    223     */
    224    public void setPassword(final char[] userPassword) {
    225        this.userPassword = userPassword;
    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     */
    234    public void setPassword(final String userPassword) {
    235        this.userPassword = Utils.toCharArray(userPassword);
    236    }
    237
    238    /**
    239     * Sets the user name used to authenticate new connections.
    240     *
    241     * @param userName
    242     *            the user name used for authenticating the connection or null for unauthenticated
    243     */
    244    public void setUsername(final String userName) {
    245        this.userName = userName;
    246    }
    247}