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}