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 java.sql.Connection;
021import java.sql.SQLException;
022import java.util.Objects;
023
024import javax.sql.ConnectionEvent;
025import javax.sql.ConnectionEventListener;
026import javax.sql.PooledConnection;
027import javax.sql.XAConnection;
028import javax.sql.XADataSource;
029import javax.transaction.TransactionManager;
030import javax.transaction.TransactionSynchronizationRegistry;
031import javax.transaction.xa.XAResource;
032
033import org.apache.commons.dbcp2.Utils;
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 == null ? null : userPassword.clone();
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        final 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    /**
210     * Gets the user password.
211     *
212     * @return the user password.
213     */
214    public char[] getUserPassword() {
215        return userPassword == null ? null : userPassword.clone();
216    }
217
218    /**
219     * Gets the XA data source.
220     *
221     * @return the XA data source.
222     */
223    public XADataSource getXaDataSource() {
224        return xaDataSource;
225    }
226
227    /**
228     * Sets the password used to authenticate new connections.
229     *
230     * @param userPassword
231     *            the password used for authenticating the connection or null for unauthenticated.
232     * @since 2.4.0
233     */
234    public void setPassword(final char[] userPassword) {
235        this.userPassword = userPassword == null ? null : userPassword.clone();
236    }
237
238    /**
239     * Sets the password used to authenticate new connections.
240     *
241     * @param userPassword
242     *            the password used for authenticating the connection or null for unauthenticated
243     */
244    public void setPassword(final String userPassword) {
245        this.userPassword = Utils.toCharArray(userPassword);
246    }
247
248    /**
249     * Sets the user name used to authenticate new connections.
250     *
251     * @param userName
252     *            the user name used for authenticating the connection or null for unauthenticated
253     */
254    public void setUsername(final String userName) {
255        this.userName = userName;
256    }
257}