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}