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 org.apache.commons.dbcp2.Utils;
029
030import java.sql.Connection;
031import java.sql.SQLException;
032import java.util.Objects;
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    private final TransactionRegistry transactionRegistry;
041    private final XADataSource xaDataSource;
042    private String userName;
043    private char[] userPassword;
044
045    /**
046     * Creates an DataSourceXAConnectionFactory which uses the specified XADataSource to create database connections.
047     * The connections are enlisted into transactions using the specified transaction manager.
048     *
049     * @param transactionManager
050     *            the transaction manager in which connections will be enlisted
051     * @param xaDataSource
052     *            the data source from which connections will be retrieved
053     */
054    public DataSourceXAConnectionFactory(final TransactionManager transactionManager, final XADataSource xaDataSource) {
055        this(transactionManager, xaDataSource, null, (char[]) null);
056    }
057
058    /**
059     * Creates an DataSourceXAConnectionFactory which uses the specified XADataSource to create database connections.
060     * The connections are enlisted into transactions using the specified transaction manager.
061     *
062     * @param transactionManager
063     *            the transaction manager in which connections will be enlisted
064     * @param xaDataSource
065     *            the data source from which connections will be retrieved
066     * @param userName
067     *            the user name used for authenticating new connections or null for unauthenticated
068     * @param userPassword
069     *            the password used for authenticating new connections
070     */
071    public DataSourceXAConnectionFactory(final TransactionManager transactionManager, final XADataSource xaDataSource,
072            final String userName, final char[] userPassword) {
073        Objects.requireNonNull(transactionManager, "transactionManager is null");
074        Objects.requireNonNull(xaDataSource, "xaDataSource is null");
075        this.transactionRegistry = new TransactionRegistry(transactionManager);
076        this.xaDataSource = xaDataSource;
077        this.userName = userName;
078        this.userPassword = userPassword;
079    }
080
081    /**
082     * Creates an DataSourceXAConnectionFactory which uses the specified XADataSource to create database connections.
083     * The connections are enlisted into transactions using the specified transaction manager.
084     *
085     * @param transactionManager
086     *            the transaction manager in which connections will be enlisted
087     * @param xaDataSource
088     *            the data source from which connections will be retrieved
089     * @param userName
090     *            the user name used for authenticating new connections or null for unauthenticated
091     * @param userPassword
092     *            the password used for authenticating new connections
093     */
094    public DataSourceXAConnectionFactory(final TransactionManager transactionManager, final XADataSource xaDataSource,
095            final String userName, final String userPassword) {
096        this(transactionManager, xaDataSource, userName, Utils.toCharArray(userPassword));
097    }
098
099    /**
100     * Gets the user name used to authenticate new connections.
101     *
102     * @return the user name or null if unauthenticated connections are used
103     */
104    public String getUsername() {
105        return userName;
106    }
107
108    /**
109     * Sets the user name used to authenticate new connections.
110     *
111     * @param userName
112     *            the user name used for authenticating the connection or null for unauthenticated
113     */
114    public void setUsername(final String userName) {
115        this.userName = userName;
116    }
117
118    /**
119     * Sets the password used to authenticate new connections.
120     *
121     * @param userPassword
122     *            the password used for authenticating the connection or null for unauthenticated.
123     * @since 2.4.0
124     */
125    public void setPassword(final char[] userPassword) {
126        this.userPassword = userPassword;
127    }
128
129    /**
130     * Sets the password used to authenticate new connections.
131     *
132     * @param userPassword
133     *            the password used for authenticating the connection or null for unauthenticated
134     */
135    public void setPassword(final String userPassword) {
136        this.userPassword = Utils.toCharArray(userPassword);
137    }
138
139    @Override
140    public TransactionRegistry getTransactionRegistry() {
141        return transactionRegistry;
142    }
143
144    @Override
145    public Connection createConnection() throws SQLException {
146        // create a new XAConnection
147        XAConnection xaConnection;
148        if (userName == null) {
149            xaConnection = xaDataSource.getXAConnection();
150        } else {
151            xaConnection = xaDataSource.getXAConnection(userName, Utils.toString(userPassword));
152        }
153
154        // get the real connection and XAResource from the connection
155        final Connection connection = xaConnection.getConnection();
156        final XAResource xaResource = xaConnection.getXAResource();
157
158        // register the xa resource for the connection
159        transactionRegistry.registerConnection(connection, xaResource);
160
161        // The Connection we're returning is a handle on the XAConnection.
162        // When the pool calling us closes the Connection, we need to
163        // also close the XAConnection that holds the physical connection.
164        xaConnection.addConnectionEventListener(new ConnectionEventListener() {
165
166            @Override
167            public void connectionClosed(final ConnectionEvent event) {
168                final PooledConnection pc = (PooledConnection) event.getSource();
169                pc.removeConnectionEventListener(this);
170                try {
171                    pc.close();
172                } catch (final SQLException e) {
173                    System.err.println("Failed to close XAConnection");
174                    e.printStackTrace();
175                }
176            }
177
178            @Override
179            public void connectionErrorOccurred(final ConnectionEvent event) {
180                connectionClosed(event);
181            }
182        });
183
184        return connection;
185    }
186}