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}