去掉还没实现的xa部分代码

git-svn-id: http://code.alibabatech.com/svn/druid/trunk@349 b9813039-fb51-4c41-a8b9-e21c2acb5095
This commit is contained in:
wenshao
2011-07-03 06:44:31 +00:00
parent 4ef6aa267e
commit 11f3232aaa
11 changed files with 2 additions and 1281 deletions
-6
View File
@@ -147,12 +147,6 @@
<version>2.5</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>jta</groupId>
<artifactId>jta</artifactId>
<version>1.0.1b</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>commons-logging</groupId>
<artifactId>commons-logging</artifactId>
@@ -1,117 +0,0 @@
/*
* Copyright 1999-2011 Alibaba Group Holding Ltd.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.alibaba.druid.pool.xa;
import javax.sql.XAConnection;
import javax.sql.XADataSource;
import javax.transaction.TransactionManager;
import javax.transaction.xa.XAResource;
import java.sql.Connection;
import java.sql.SQLException;
/**
* An implementation of XAConnectionFactory which uses a real XADataSource to obtain connections and XAResources.
*
* @author Dain Sundstrom
* @version $Revision$
*/
public class DataSourceXAConnectionFactory implements XAConnectionFactory {
protected TransactionRegistry transactionRegistry;
protected XADataSource xaDataSource;
protected String username;
protected String password;
/**
* Creates an DataSourceXAConnectionFactory which uses the specified XADataSource to create database connections.
* The connections are enlisted into transactions using the specified transaction manager.
*
* @param transactionManager the transaction manager in which connections will be enlisted
* @param xaDataSource the data source from which connections will be retrieved
*/
public DataSourceXAConnectionFactory(TransactionManager transactionManager, XADataSource xaDataSource){
this(transactionManager, xaDataSource, null, null);
}
/**
* Creates an DataSourceXAConnectionFactory which uses the specified XADataSource to create database connections.
* The connections are enlisted into transactions using the specified transaction manager.
*
* @param transactionManager the transaction manager in which connections will be enlisted
* @param xaDataSource the data source from which connections will be retrieved
* @param username the username used for authenticating new connections or null for unauthenticated
* @param password the password used for authenticating new connections
*/
public DataSourceXAConnectionFactory(TransactionManager transactionManager, XADataSource xaDataSource, String username, String password){
if (transactionManager == null) throw new NullPointerException("transactionManager is null");
if (xaDataSource == null) throw new NullPointerException("xaDataSource is null");
this.transactionRegistry = new TransactionRegistry(transactionManager);
this.xaDataSource = xaDataSource;
this.username = username;
this.password = password;
}
/**
* Gets the username used to authenticate new connections.
*
* @return the user name or null if unauthenticated connections are used
*/
public String getUsername() {
return username;
}
/**
* Sets the username used to authenticate new connections.
*
* @param username the username used for authenticating the connection or null for unauthenticated
*/
public void setUsername(String username) {
this.username = username;
}
/**
* Sets the password used to authenticate new connections.
*
* @param password the password used for authenticating the connection or null for unauthenticated
*/
public void setPassword(String password) {
this.password = password;
}
public TransactionRegistry getTransactionRegistry() {
return transactionRegistry;
}
public Connection createConnection() throws SQLException {
// create a new XAConection
XAConnection xaConnection;
if (username == null) {
xaConnection = xaDataSource.getXAConnection();
} else {
xaConnection = xaDataSource.getXAConnection(username, password);
}
// get the real connection and XAResource from the connection
Connection connection = xaConnection.getConnection();
XAResource xaResource = xaConnection.getXAResource();
// register the xa resource for the connection
transactionRegistry.registerConnection(connection, xaResource);
return connection;
}
}
@@ -1,175 +0,0 @@
/*
* Copyright 1999-2011 Alibaba Group Holding Ltd.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.alibaba.druid.pool.xa;
import java.sql.SQLException;
import javax.sql.XADataSource;
import javax.transaction.TransactionManager;
import com.alibaba.druid.pool.ConnectionFactory;
import com.alibaba.druid.pool.DruidDataSource;
public class DruidXADataSource extends DruidDataSource {
private static final long serialVersionUID = 1L;
/** Transaction Registry */
private TransactionRegistry transactionRegistry;
/** Transaction Manager */
private transient TransactionManager transactionManager;
/** XA datasource class name */
private String xaDataSource;
/** XA datasource instance */
private XADataSource xaDataSourceInstance;
/**
* Gets the XADataSource instance used by the XAConnectionFactory.
*
* @return the XADataSource
*/
public synchronized XADataSource getXaDataSourceInstance() {
return xaDataSourceInstance;
}
/**
* <p>
* Sets the XADataSource instance used by the XAConnectionFactory.
* </p>
* <p>
* Note: this method currently has no effect once the pool has been initialized. The pool is initialized the first
* time one of the following methods is invoked: <code>getConnection, setLogwriter,
* setLoginTimeout, getLoginTimeout, getLogWriter.</code>
* </p>
*
* @param xaDataSourceInstance XADataSource instance
*/
public synchronized void setXaDataSourceInstance(XADataSource xaDataSourceInstance) {
this.xaDataSourceInstance = xaDataSourceInstance;
xaDataSource = xaDataSourceInstance == null ? null : xaDataSourceInstance.getClass().getName();
}
/**
* Gets the required transaction manager property.
*
* @return the transaction manager used to enlist connections
*/
public TransactionManager getTransactionManager() {
return transactionManager;
}
/**
* Gets the transaction registry.
*
* @return the transaction registry associating XAResources with managed connections
*/
protected synchronized TransactionRegistry getTransactionRegistry() {
return transactionRegistry;
}
/**
* Sets the required transaction manager property.
*
* @param transactionManager the transaction manager used to enlist connections
*/
public void setTransactionManager(TransactionManager transactionManager) {
this.transactionManager = transactionManager;
}
/**
* Gets the optional XADataSource class name.
*
* @return the optional XADataSource class name
*/
public synchronized String getXADataSource() {
return xaDataSource;
}
/**
* Sets the optional XADataSource class name.
*
* @param xaDataSource the optional XADataSource class name
*/
public synchronized void setXADataSource(String xaDataSource) {
this.xaDataSource = xaDataSource;
}
protected ConnectionFactory createConnectionFactory() throws SQLException {
if (transactionManager == null) {
throw new SQLException("Transaction manager must be set before a connection can be created");
}
// If xa data source is not specified a DriverConnectionFactory is created and wrapped with a
// LocalXAConnectionFactory
if (xaDataSource == null) {
ConnectionFactory connectionFactory = super.createConnectionFactory();
XAConnectionFactory xaConnectionFactory = new LocalXAConnectionFactory(getTransactionManager(), connectionFactory);
transactionRegistry = xaConnectionFactory.getTransactionRegistry();
return xaConnectionFactory;
}
// Create the XADataSource instance using the configured class name if it has not been set
if (xaDataSourceInstance == null) {
Class<?> xaDataSourceClass = null;
try {
xaDataSourceClass = Class.forName(xaDataSource);
} catch (Throwable t) {
String message = "Cannot load XA data source class '" + xaDataSource + "'";
throw (SQLException) new SQLException(message).initCause(t);
}
try {
xaDataSourceInstance = (XADataSource) xaDataSourceClass.newInstance();
} catch (Throwable t) {
String message = "Cannot create XA data source of class '" + xaDataSource + "'";
throw (SQLException) new SQLException(message).initCause(t);
}
}
// finally, create the XAConectionFactory using the XA data source
XAConnectionFactory xaConnectionFactory = new DataSourceXAConnectionFactory(getTransactionManager(), xaDataSourceInstance, username, password);
transactionRegistry = xaConnectionFactory.getTransactionRegistry();
return xaConnectionFactory;
}
/**
* Creates the PoolableConnectionFactory and attaches it to the connection pool.
*
* @param driverConnectionFactory JDBC connection factory created by {@link #createConnectionFactory()}
* @param statementPoolFactory statement pool factory (null if statement pooling is turned off)
* @param abandonedConfig abandoned connection tracking configuration (null if no tracking)
* @throws SQLException if an error occurs creating the PoolableConnectionFactory
*/
protected void createPoolableConnectionFactory(ConnectionFactory driverConnectionFactory) throws SQLException {
// PoolableConnectionFactory connectionFactory = null;
// try {
// connectionFactory = new PoolableManagedConnectionFactory((XAConnectionFactory) driverConnectionFactory,
// connectionPool, statementPoolFactory,
// validationQuery, validationQueryTimeout,
// connectionInitSqls, defaultReadOnly,
// defaultAutoCommit, defaultTransactionIsolation,
// defaultCatalog, abandonedConfig);
// validateConnectionFactory(connectionFactory);
// } catch (RuntimeException e) {
// throw e;
// } catch (Exception e) {
// throw (SQLException) new SQLException("Cannot create PoolableConnectionFactory (" + e.getMessage() +
// ")").initCause(e);
// }
throw new SQLException("TODO");
}
}
@@ -1,301 +0,0 @@
/*
* Copyright 1999-2011 Alibaba Group Holding Ltd.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.alibaba.druid.pool.xa;
import java.sql.Connection;
import java.sql.SQLException;
import javax.transaction.TransactionManager;
import javax.transaction.xa.XAException;
import javax.transaction.xa.XAResource;
import javax.transaction.xa.Xid;
import com.alibaba.druid.pool.ConnectionFactory;
/**
* An implementation of XAConnectionFactory which manages non-XA connections in XA transactions. A non-XA connection
* commits and rolls back as part of the XA transaction, but is not recoverable since the connection does not implement
* the 2-phase protocol.
*
* @author Dain Sundstrom
* @version $Revision$
*/
public class LocalXAConnectionFactory implements XAConnectionFactory {
protected TransactionRegistry transactionRegistry;
protected ConnectionFactory connectionFactory;
/**
* Creates an LocalXAConnectionFactory which uses the specified connection factory to create database connections.
* The connections are enlisted into transactions using the specified transaction manager.
*
* @param transactionManager the transaction manager in which connections will be enlisted
* @param connectionFactory the connection factory from which connections will be retrieved
*/
public LocalXAConnectionFactory(TransactionManager transactionManager, ConnectionFactory connectionFactory){
if (transactionManager == null) {
throw new NullPointerException("transactionManager is null");
}
if (connectionFactory == null) {
throw new NullPointerException("connectionFactory is null");
}
this.transactionRegistry = new TransactionRegistry(transactionManager);
this.connectionFactory = connectionFactory;
}
public TransactionRegistry getTransactionRegistry() {
return transactionRegistry;
}
public Connection createConnection() throws SQLException {
// create a new connection
Connection connection = connectionFactory.createConnection();
// create a XAResource to manage the connection during XA transactions
XAResource xaResource = new LocalXAResource(connection);
// register the xa resource for the connection
transactionRegistry.registerConnection(connection, xaResource);
return connection;
}
/**
* LocalXAResource is a fake XAResource for non-XA connections. When a transaction is started the connection
* auto-commit is turned off. When the connection is committed or rolled back, the commit or rollback method is
* called on the connection and then the original auto-commit value is restored. </p> The LocalXAResource also
* respects the connection read-only setting. If the connection is read-only the commit method will not be called,
* and the prepare method returns the XA_RDONLY. </p> It is assumed that the wrapper around a managed connection
* disables the setAutoCommit(), commit(), rollback() and setReadOnly() methods while a transaction is in progress.
*/
protected static class LocalXAResource implements XAResource {
private final Connection connection;
private Xid currentXid;
private boolean originalAutoCommit;
public LocalXAResource(Connection localTransaction){
this.connection = localTransaction;
}
/**
* Gets the current xid of the transaction branch associated with this XAResource.
*
* @return the current xid of the transaction branch associated with this XAResource.
*/
public synchronized Xid getXid() {
return currentXid;
}
/**
* Signals that a the connection has been enrolled in a transaction. This method saves off the current auto
* commit flag, and then disables auto commit. The original auto commit setting is restored when the transaction
* completes.
*
* @param xid the id of the transaction branch for this connection
* @param flag either XAResource.TMNOFLAGS or XAResource.TMRESUME
* @throws XAException if the connection is already enlisted in another transaction, or if auto-commit could not
* be disabled
*/
public synchronized void start(Xid xid, int flag) throws XAException {
if (flag == XAResource.TMNOFLAGS) {
// first time in this transaction
// make sure we aren't already in another tx
if (this.currentXid != null) {
throw new XAException("Already enlisted in another transaction with xid " + xid);
}
// save off the current auto commit flag so it can be restored after the transaction completes
try {
originalAutoCommit = connection.getAutoCommit();
} catch (SQLException ignored) {
// no big deal, just assume it was off
originalAutoCommit = true;
}
// update the auto commit flag
try {
connection.setAutoCommit(false);
} catch (SQLException e) {
throw (XAException) new XAException("Count not turn off auto commit for a XA transaction").initCause(e);
}
this.currentXid = xid;
} else if (flag == XAResource.TMRESUME) {
if (xid != this.currentXid) {
throw new XAException("Attempting to resume in different transaction: expected " + this.currentXid + ", but was " + xid);
}
} else {
throw new XAException("Unknown start flag " + flag);
}
}
/**
* This method does nothing.
*
* @param xid the id of the transaction branch for this connection
* @param flag ignored
* @throws XAException if the connection is already enlisted in another transaction
*/
public synchronized void end(Xid xid, int flag) throws XAException {
if (xid == null) throw new NullPointerException("xid is null");
if (!this.currentXid.equals(xid)) throw new XAException("Invalid Xid: expected " + this.currentXid + ", but was " + xid);
// This notification tells us that the application server is done using this
// connection for the time being. The connection is still associated with an
// open transaction, so we must still wait for the commit or rollback method
}
/**
* This method does nothing since the LocalXAConnection does not support two-phase-commit. This method will
* return XAResource.XA_RDONLY if the connection isReadOnly(). This assumes that the physical connection is
* wrapped with a proxy that prevents an application from changing the read-only flag while enrolled in a
* transaction.
*
* @param xid the id of the transaction branch for this connection
* @return XAResource.XA_RDONLY if the connection.isReadOnly(); XAResource.XA_OK otherwise
*/
public synchronized int prepare(Xid xid) {
// if the connection is read-only, then the resource is read-only
// NOTE: this assumes that the outer proxy throws an exception when application code
// attempts to set this in a transaction
try {
if (connection.isReadOnly()) {
// update the auto commit flag
connection.setAutoCommit(originalAutoCommit);
// tell the transaction manager we are read only
return XAResource.XA_RDONLY;
}
} catch (SQLException ignored) {
// no big deal
}
// this is a local (one phase) only connection, so we can't prepare
return XAResource.XA_OK;
}
/**
* Commits the transaction and restores the original auto commit setting.
*
* @param xid the id of the transaction branch for this connection
* @param flag ignored
* @throws XAException if connection.commit() throws a SQLException
*/
public synchronized void commit(Xid xid, boolean flag) throws XAException {
if (xid == null) throw new NullPointerException("xid is null");
if (!this.currentXid.equals(xid)) throw new XAException("Invalid Xid: expected " + this.currentXid + ", but was " + xid);
try {
// make sure the connection isn't already closed
if (connection.isClosed()) {
throw new XAException("Conection is closed");
}
// A read only connection should not be committed
if (!connection.isReadOnly()) {
connection.commit();
}
} catch (SQLException e) {
throw (XAException) new XAException().initCause(e);
} finally {
try {
connection.setAutoCommit(originalAutoCommit);
} catch (SQLException e) {
}
this.currentXid = null;
}
}
/**
* Rolls back the transaction and restores the original auto commit setting.
*
* @param xid the id of the transaction branch for this connection
* @throws XAException if connection.rollback() throws a SQLException
*/
public synchronized void rollback(Xid xid) throws XAException {
if (xid == null) throw new NullPointerException("xid is null");
if (!this.currentXid.equals(xid)) throw new XAException("Invalid Xid: expected " + this.currentXid + ", but was " + xid);
try {
connection.rollback();
} catch (SQLException e) {
throw (XAException) new XAException().initCause(e);
} finally {
try {
connection.setAutoCommit(originalAutoCommit);
} catch (SQLException e) {
}
this.currentXid = null;
}
}
/**
* Returns true if the specified XAResource == this XAResource.
*
* @param xaResource the XAResource to test
* @return true if the specified XAResource == this XAResource; false otherwise
*/
public boolean isSameRM(XAResource xaResource) {
return this == xaResource;
}
/**
* Clears the currently associated transaction if it is the specified xid.
*
* @param xid the id of the transaction to forget
*/
public synchronized void forget(Xid xid) {
if (xid != null && this.currentXid.equals(xid)) {
this.currentXid = null;
}
}
/**
* Always returns a zero length Xid array. The LocalXAConnectionFactory can not support recovery, so no xids
* will ever be found.
*
* @param flag ignored since recovery is not supported
* @return always a zero length Xid array.
*/
public Xid[] recover(int flag) {
return new Xid[0];
}
/**
* Always returns 0 since we have no way to set a transaction timeout on a JDBC connection.
*
* @return always 0
*/
public int getTransactionTimeout() {
return 0;
}
/**
* Always returns false since we have no way to set a transaction timeout on a JDBC connection.
*
* @param transactionTimeout ignored since we have no way to set a transaction timeout on a JDBC connection
* @return always false
*/
public boolean setTransactionTimeout(int transactionTimeout) {
return false;
}
}
}
@@ -1,251 +0,0 @@
/*
* Copyright 1999-2011 Alibaba Group Holding Ltd.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.alibaba.druid.pool.xa;
import java.sql.Connection;
import java.sql.SQLException;
import com.alibaba.druid.pool.ConnectionHolder;
import com.alibaba.druid.pool.PoolableConnection;
/**
* ManagedConnection is responsible for managing a database connection in a transactional environment (typically called
* "Container Managed"). A managed connection operates like any other connection when no global transaction (a.k.a. XA
* transaction or JTA Transaction) is in progress. When a global transaction is active a single physical connection to
* the database is used by all ManagedConnections accessed in the scope of the transaction. Connection sharing means
* that all data access during a transaction has a consistent view of the database. When the global transaction is
* committed or rolled back the enlisted connections are committed or rolled back. Typically upon transaction
* completion, a connection returns to the auto commit setting in effect before being enlisted in the transaction, but
* some vendors do not properly implement this. When enlisted in a transaction the setAutoCommit(), commit(),
* rollback(), and setReadOnly() methods throw a SQLException. This is necessary to assure that the transaction
* completes as a single unit.
*
* @author Dain Sundstrom
* @version $Revision$
*/
@SuppressWarnings("unused")
public class ManagedConnection extends PoolableConnection {
private final TransactionRegistry transactionRegistry;
private final boolean accessToUnderlyingConnectionAllowed;
private TransactionContext transactionContext;
private boolean isSharedConnection;
public ManagedConnection(ConnectionHolder holder, TransactionRegistry transactionRegistry, boolean accessToUnderlyingConnectionAllowed) throws SQLException{
super(holder);
this.transactionRegistry = transactionRegistry;
this.accessToUnderlyingConnectionAllowed = accessToUnderlyingConnectionAllowed;
updateTransactionStatus();
}
protected void checkOpen() throws SQLException {
super.checkOpen();
updateTransactionStatus();
}
private void updateTransactionStatus() throws SQLException {
// // if there is a is an active transaction context, assure the transaction context hasn't changed
// if (transactionContext != null) {
// if (transactionContext.isActive()) {
// if (transactionContext != transactionRegistry.getActiveTransactionContext()) {
// throw new SQLException("Connection can not be used while enlisted in another transaction");
// }
// return;
// } else {
// // transaction should have been cleared up by TransactionContextListener, but in
// // rare cases another lister could have registered which uses the connection before
// // our listener is called. In that rare case, trigger the transaction complete call now
// transactionComplete();
// }
// }
//
// // the existing transaction context ended (or we didn't have one), get the active transaction context
// transactionContext = transactionRegistry.getActiveTransactionContext();
//
// // if there is an active transaction context and it already has a shared connection, use it
// if (transactionContext != null && transactionContext.getSharedConnection() != null) {
// // A connection for the connection factory has already been enrolled
// // in the transaction, replace our delegate with the enrolled connection
//
// // return current connection to the pool
// Connection connection = getDelegateInternal();
// setDelegate(null);
// if (connection != null) {
// try {
// pool.returnObject(connection);
// } catch (Exception ignored) {
// // whatever... try to invalidate the connection
// try {
// pool.invalidateObject(connection);
// } catch (Exception ignore) {
// // no big deal
// }
// }
// }
//
// // add a listener to the transaction context
// transactionContext.addTransactionContextListener(new CompletionListener());
//
// // set our delegate to the shared connection
// setDelegate(transactionContext.getSharedConnection());
//
// // remember that we are using a shared connection so it can be cleared after the
// // transaction completes
// isSharedConnection = true;
// } else {
// // if our delegate is null, create one
// if (getDelegateInternal() == null) {
// try {
// // borrow a new connection from the pool
// Connection connection = (Connection) pool.borrowObject();
// setDelegate(connection);
// } catch (Exception e) {
// throw (SQLException) new SQLException("Unable to acquire a new connection from the pool").initCause(e);
// }
// }
//
// // if we have a transaction, out delegate becomes the shared delegate
// if (transactionContext != null) {
// // add a listener to the transaction context
// transactionContext.addTransactionContextListener(new CompletionListener());
//
// // register our connection as the shared connection
// try {
// transactionContext.setSharedConnection(getDelegateInternal());
// } catch (SQLException e) {
// // transaction is hosed
// transactionContext = null;
// throw e;
// }
// }
// }
throw new SQLException("TODO");
}
public void close() throws SQLException {
if (transactionContext == null) {
super.close();
}
}
/**
* Delegates to {@link ManagedConnection#transactionComplete()} for transaction completion events.
*/
protected class CompletionListener implements TransactionContextListener {
public void afterCompletion(TransactionContext completedContext, boolean commited) {
if (completedContext == transactionContext) {
transactionComplete();
}
}
}
protected void transactionComplete() {
// transactionContext = null;
//
// // if we were using a shared connection, clear the reference now that the transaction has completed
// if (isSharedConnection) {
// // for now, just set the delegate to null, it will be created later if needed
// setDelegate(null);
// isSharedConnection = false;
// }
//
// // if this connection was closed during the transaction and there is still a delegate present close it
// Connection delegate = getDelegateInternal();
// if (isClosed() && delegate != null) {
// try {
// setDelegate(null);
//
// // don't actually close the connection if in a transaction
// if (!delegate.isClosed()) {
// // don't use super.close() because it calls passivate() which marks the
// // the connection as closed without returning it to the pool
// delegate.close();
// }
// } catch (SQLException ignored) {
// // not a whole lot we can do here as connection is closed
// // and this is a transaction callback so there is no
// // way to report the error
// } finally {
// _closed = true;
// }
// }
throw new RuntimeException("TODO");
}
//
// The following methods can't be used while enlisted in a transaction
//
public void setAutoCommit(boolean autoCommit) throws SQLException {
if (transactionContext != null) {
throw new SQLException("Auto-commit can not be set while enrolled in a transaction");
}
super.setAutoCommit(autoCommit);
}
public void commit() throws SQLException {
if (transactionContext != null) {
throw new SQLException("Commit can not be set while enrolled in a transaction");
}
super.commit();
}
public void rollback() throws SQLException {
if (transactionContext != null) {
throw new SQLException("Commit can not be set while enrolled in a transaction");
}
super.rollback();
}
public void setReadOnly(boolean readOnly) throws SQLException {
if (transactionContext != null) {
throw new SQLException("Read-only can not be set while enrolled in a transaction");
}
super.setReadOnly(readOnly);
}
//
// Methods for accessing the delegate connection
//
/**
* If false, getDelegate() and getInnermostDelegate() will return null.
*
* @return if false, getDelegate() and getInnermostDelegate() will return null
*/
public boolean isAccessToUnderlyingConnectionAllowed() {
return accessToUnderlyingConnectionAllowed;
}
@Deprecated
public Connection getDelegate() {
if (isAccessToUnderlyingConnectionAllowed()) {
return getRawConnection();
} else {
return null;
}
}
@Deprecated
public Connection getInnermostDelegate() {
if (isAccessToUnderlyingConnectionAllowed()) {
return getRawConnection();
} else {
return null;
}
}
}
@@ -1,75 +0,0 @@
/*
* Copyright 1999-2011 Alibaba Group Holding Ltd.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.alibaba.druid.pool.xa;
import java.sql.Connection;
import java.sql.SQLException;
/**
* The ManagedDataSource is a PoolingDataSource that creates ManagedConnections.
*
* @author Dain Sundstrom
* @version $Revision$
*/
public class ManagedDataSource {
private TransactionRegistry transactionRegistry;
/**
* Creates an uninitialized datasource. Before this data source can be used a pool and transaction registry must be
* set.
*/
public ManagedDataSource(){
}
/**
* Creates a ManagedDataSource which obtains connections from the specified pool and manages them using the
* specified transaction registry. The TransactionRegistry must be the transaction registry obtained from the
* XAConnectionFactory used to create the connection pool. If not an error will occure when attempting to use the
* connection in a global transaction because the XAResource object associated with the connection will be
* unavailable.
*
* @param pool the connection pool
* @param transactionRegistry the transaction registry obtained from the XAConnectionFactory used to create the
* connection pool object factory
*/
public ManagedDataSource(TransactionRegistry transactionRegistry){
this.transactionRegistry = transactionRegistry;
}
/**
* Sets the transaction registry from the XAConnectionFactory used to create the pool. The transaction registry can
* only be set once using either a connector or this setter method.
*
* @param transactionRegistry the transaction registry acquired from the XAConnectionFactory used to create the pool
*/
public void setTransactionRegistry(TransactionRegistry transactionRegistry) {
if (this.transactionRegistry != null) throw new IllegalStateException("TransactionRegistry already set");
if (transactionRegistry == null) throw new NullPointerException("TransactionRegistry is null");
this.transactionRegistry = transactionRegistry;
}
public Connection getConnection() throws SQLException {
// if (_pool == null) throw new IllegalStateException("Pool has not been set");
// if (transactionRegistry == null) throw new IllegalStateException("TransactionRegistry has not been set");
//
// Connection connection = new ManagedConnection(_pool, transactionRegistry,
// isAccessToUnderlyingConnectionAllowed());
// return connection;
throw new SQLException("TODO"); // TODO
}
}
@@ -1,144 +0,0 @@
/*
* Copyright 1999-2011 Alibaba Group Holding Ltd.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.alibaba.druid.pool.xa;
import javax.transaction.RollbackException;
import javax.transaction.Status;
import javax.transaction.Synchronization;
import javax.transaction.SystemException;
import javax.transaction.Transaction;
import javax.transaction.xa.XAResource;
import java.sql.Connection;
import java.sql.SQLException;
import java.lang.ref.WeakReference;
/**
* TransactionContext represents the association between a single XAConnectionFactory and a Transaction. This context
* contains a single shared connection which should be used by all ManagedConnections for the XAConnectionFactory, the
* ability to listen for the transaction completion event, and a method to check the status of the transaction.
*
* @author Dain Sundstrom
* @version $Revision$
*/
public class TransactionContext {
private final TransactionRegistry transactionRegistry;
private final WeakReference<Transaction> transactionRef;
private Connection sharedConnection;
/**
* Creates a TransactionContext for the specified Transaction and TransactionRegistry. The TransactionRegistry is
* used to obtain the XAResource for the shared connection when it is enlisted in the transaction.
*
* @param transactionRegistry the TransactionRegistry used to obtain the XAResource for the shared connection
* @param transaction the transaction
*/
public TransactionContext(TransactionRegistry transactionRegistry, Transaction transaction){
if (transactionRegistry == null) throw new NullPointerException("transactionRegistry is null");
if (transaction == null) throw new NullPointerException("transaction is null");
this.transactionRegistry = transactionRegistry;
this.transactionRef = new WeakReference<Transaction>(transaction);
}
/**
* Gets the connection shared by all ManagedConnections in the transaction. Specifically, connection using the same
* XAConnectionFactory from which the TransactionRegistry was obtained.
*
* @return the shared connection for this transaction
*/
public Connection getSharedConnection() {
return sharedConnection;
}
/**
* Sets the shared connection for this transaction. The shared connection is enlisted in the transaction.
*
* @param sharedConnection the shared connection
* @throws SQLException if a shared connection is already set, if XAResource for the connection could not be found
* in the transaction registry, or if there was a problem enlisting the connection in the transaction
*/
public void setSharedConnection(Connection sharedConnection) throws SQLException {
if (this.sharedConnection != null) {
throw new IllegalStateException("A shared connection is alredy set");
}
// This is the first use of the connection in this transaction, so we must
// enlist it in the transaction
Transaction transaction = getTransaction();
try {
XAResource xaResource = transactionRegistry.getXAResource(sharedConnection);
transaction.enlistResource(xaResource);
} catch (RollbackException e) {
// transaction was rolled back... proceed as if there never was a transaction
} catch (SystemException e) {
throw (SQLException) new SQLException("Unable to enlist connection the transaction").initCause(e);
}
this.sharedConnection = sharedConnection;
}
/**
* Adds a listener for transaction completion events.
*
* @param listener the listener to add
* @throws SQLException if a problem occurs adding the listener to the transaction
*/
public void addTransactionContextListener(final TransactionContextListener listener) throws SQLException {
try {
getTransaction().registerSynchronization(new Synchronization() {
public void beforeCompletion() {
}
public void afterCompletion(int status) {
listener.afterCompletion(TransactionContext.this, status == Status.STATUS_COMMITTED);
}
});
} catch (RollbackException e) {
// JTA spec doesn't let us register with a transaction marked rollback only
// just ignore this and the tx state will be cleared another way.
} catch (Exception e) {
throw (SQLException) new SQLException("Unable to register transaction context listener").initCause(e);
}
}
/**
* True if the transaction is active or marked for rollback only.
*
* @return true if the transaction is active or marked for rollback only; false otherwise
* @throws SQLException if a problem occurs obtaining the transaction status
*/
public boolean isActive() throws SQLException {
try {
Transaction transaction = (Transaction) this.transactionRef.get();
if (transaction == null) {
return false;
}
int status = transaction.getStatus();
return status == Status.STATUS_ACTIVE || status == Status.STATUS_MARKED_ROLLBACK;
} catch (SystemException e) {
throw (SQLException) new SQLException("Unable to get transaction status").initCause(e);
}
}
private Transaction getTransaction() throws SQLException {
Transaction transaction = (Transaction) this.transactionRef.get();
if (transaction == null) {
throw new SQLException("Unable to enlist connection because the transaction has been garbage collected");
}
return transaction;
}
}
@@ -1,33 +0,0 @@
/*
* Copyright 1999-2011 Alibaba Group Holding Ltd.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.alibaba.druid.pool.xa;
/**
* A listener for transaction completion events.
*
* @author Dain Sundstrom
* @version $Revision$
*/
public interface TransactionContextListener {
/**
* Occurs after the transaction commits or rolls back.
*
* @param transactionContext the transaction context that completed
* @param commited true if the transaction committed; false otherwise
*/
void afterCompletion(TransactionContext transactionContext, boolean commited);
}
@@ -1,125 +0,0 @@
/*
* Copyright 1999-2011 Alibaba Group Holding Ltd.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.alibaba.druid.pool.xa;
import java.sql.Connection;
import java.sql.SQLException;
import java.util.Map;
import java.util.WeakHashMap;
import javax.transaction.Status;
import javax.transaction.SystemException;
import javax.transaction.Transaction;
import javax.transaction.TransactionManager;
import javax.transaction.xa.XAResource;
/**
* TransactionRegistry tracks Connections and XAResources in a transacted environment for a single XAConnectionFactory.
* </p> The TransactionRegistry hides the details of transaction processing from the existing DBCP pooling code, and
* gives the ManagedConnection a way to enlist connections in a transaction, allowing for the maximal rescue of DBCP.
*
* @author Dain Sundstrom
* @version $Revision$
*/
public class TransactionRegistry {
private final TransactionManager transactionManager;
private final Map<Transaction, TransactionContext> caches = new WeakHashMap<Transaction, TransactionContext>();
private final Map<Connection, XAResource> xaResources = new WeakHashMap<Connection, XAResource>();
/**
* Creates a TransactionRegistry for the specified transaction manager.
*
* @param transactionManager the transaction manager used to enlist connections
*/
public TransactionRegistry(TransactionManager transactionManager){
this.transactionManager = transactionManager;
}
/**
* Registers the association between a Connection and a XAResource. When a connection is enlisted in a transaction,
* it is actually the XAResource that is given to the transaction manager.
*
* @param connection the JDBC connection
* @param xaResource the XAResource which managed the connection within a transaction
*/
public synchronized void registerConnection(Connection connection, XAResource xaResource) {
if (connection == null) throw new NullPointerException("connection is null");
if (xaResource == null) throw new NullPointerException("xaResource is null");
xaResources.put(connection, xaResource);
}
/**
* Gets the XAResource registered for the connection.
*
* @param connection the connection
* @return the XAResource registered for the connection; never null
* @throws SQLException if the connection does not have a registered XAResource
*/
public synchronized XAResource getXAResource(Connection connection) throws SQLException {
if (connection == null) throw new NullPointerException("connection is null");
XAResource xaResource = (XAResource) xaResources.get(connection);
if (xaResource == null) {
throw new SQLException("Connection does not have a registered XAResource " + connection);
}
return xaResource;
}
/**
* Gets the active TransactionContext or null if not Transaction is active.
*
* @return the active TransactionContext or null if not Transaction is active
* @throws SQLException if an error occurs while fetching the transaction
*/
public TransactionContext getActiveTransactionContext() throws SQLException {
Transaction transaction = null;
try {
transaction = transactionManager.getTransaction();
// was there a transaction?
if (transaction == null) {
return null;
}
// is it active
int status = transaction.getStatus();
if (status != Status.STATUS_ACTIVE && status != Status.STATUS_MARKED_ROLLBACK) {
return null;
}
} catch (SystemException e) {
throw (SQLException) new SQLException("Unable to determine current transaction ").initCause(e);
}
// register the the context (or create a new one)
synchronized (this) {
TransactionContext cache = (TransactionContext) caches.get(transaction);
if (cache == null) {
cache = new TransactionContext(this, transaction);
caches.put(transaction, cache);
}
return cache;
}
}
/**
* Unregisters a destroyed connection from {@link TransactionRegistry}
*
* @param connection
*/
public synchronized void unregisterConnection(Connection connection) {
xaResources.remove(connection);
}
}
@@ -1,52 +0,0 @@
/*
* Copyright 1999-2011 Alibaba Group Holding Ltd.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.alibaba.druid.pool.xa;
import java.sql.Connection;
import java.sql.SQLException;
import com.alibaba.druid.pool.ConnectionFactory;
/**
* XAConnectionFactory is an extension of ConnectionFactory used to create connections in a transaction managed
* environment. The XAConnectionFactory opperates like a normal ConnectionFactory except an TransactionRegistry is
* provided from which the XAResource for a connection can be obtained. This allows the existing DBCP pool code to work
* with XAConnections and gives a the ManagedConnection a way to enlist a connection in the the transaction.
*
* @author Dain Sundstrom
* @author Rodney Waldhoff
* @version $Revision$
*/
public interface XAConnectionFactory extends ConnectionFactory {
/**
* Gets the TransactionRegistry for this connection factory which contains a the XAResource for every connection
* created by this factory.
*
* @return the transaction registry for this connection factory
*/
TransactionRegistry getTransactionRegistry();
/**
* Create a new {@link java.sql.Connection} in an implementation specific fashion. </p> An implementation can assume
* that the caller of this will wrap the connection in a proxy that protects access to the setAutoCommit, commit and
* rollback when enrolled in a XA transaction.
*
* @return a new {@link java.sql.Connection}
* @throws java.sql.SQLException if a database error occurs creating the connection
*/
Connection createConnection() throws SQLException;
}
@@ -35,8 +35,8 @@ public class TestOracle_DruidDataSource extends TestCase {
dataSource.setFilters("stat");
dataSource.setExceptionSoter(OracleExceptionSorter.class.getName());
final int COUNT = 100;
final int THREAD_COUNT = 1000;
final int COUNT = 100 * 100;
final int THREAD_COUNT = 10;
final CountDownLatch startLatch = new CountDownLatch(1);
final CountDownLatch endLatch = new CountDownLatch(THREAD_COUNT);