org.opensubsystems.core.persist.db.transaction
Class SimpleLocalTransactionFactoryImpl

java.lang.Object
  extended by org.opensubsystems.core.persist.db.DatabaseTransactionFactoryImpl
      extended by org.opensubsystems.core.persist.db.transaction.SimpleLocalTransactionFactoryImpl
All Implemented Interfaces:
javax.transaction.UserTransaction, DatabaseTransactionFactory, TransactionFactory

public class SimpleLocalTransactionFactoryImpl
extends DatabaseTransactionFactoryImpl
implements javax.transaction.UserTransaction

Simplified transaction management implementation based on database connection using following assumptions: Assumptions: ------------ 1. Most applications use/access only single database, therefore there is no need for distributed transaction implementation. 2. Applications consists of threads and each thread is using/needing only single database connection at a time. Most threads will never need two connections at the same time to the same database. 3. At most one transaction is in progress at any time in one thread. Therefore if an application is accessing only a single database and threads of the application use only single connection at a time and start at most one transaction at a time the application can use this implementation of transaction manager. How it works: ------------- 1. Thread is using JDBC database connection to access the database. 2. If thread starts an transaction using UserTransaction.begin then the connection used to access the database in this transaction has to have autocommit set to false and the thread cannot issue commit or rollback on the transaction. Then when thread ends transaction using UserTransaction.commit or UserTransaction.rollback then the transaction manager commits or rollbacks connections which was used to access the database within this transaction. 3. If thread is accessing the database outside of transaction, it can do whatever it wants with the connection including setting autocommit to true and calling commit and rollback on the connection. There are 4 basic and 4 combined scenarios possible: ---------------------------------------------------- 1. Connection is requested before the transaction and returned after the transaction DatabaseConnectionFactory.requestConnection UserTransaction.begin UserTransaction.commit/rollback DatabaseConnectionFactory.returnConnection 2. Connection is requested in the transaction and returned after the transaction UserTransaction.begin DatabaseConnectionFactory.requestConnection UserTransaction.commit/rollback DatabaseConnectionFactory.returnConnection 3. Connection is requested before the transaction and returned in the transaction DatabaseConnectionFactory.requestConnection UserTransaction.begin DatabaseConnectionFactory.returnConnection UserTransaction.commit/rollback 4. Connection is requested in the transaction and returned in the transaction UserTransaction.begin DatabaseConnectionFactory.requestConnection DatabaseConnectionFactory.returnConnection UserTransaction.commit/rollback 5. Connection is requested before the transaction and returned in the transaction and then connection is requested again ithe transaction and returned in the transaction DatabaseConnectionFactory.requestConnection UserTransaction.begin DatabaseConnectionFactory.returnConnection DatabaseConnectionFactory.requestConnection DatabaseConnectionFactory.returnConnection UserTransaction.commit/rollback 6. Connection is requested before the transaction and returned in the transaction and then connection is requested again ithe transaction and returned after the transaction DatabaseConnectionFactory.requestConnection UserTransaction.begin DatabaseConnectionFactory.returnConnection DatabaseConnectionFactory.requestConnection UserTransaction.commit/rollback DatabaseConnectionFactory.returnConnection 7. Connection is requested in the transaction and returned in the transaction and then connection is requested again ithe transaction and returned after the transaction UserTransaction.begin DatabaseConnectionFactory.requestConnection DatabaseConnectionFactory.returnConnection DatabaseConnectionFactory.requestConnection UserTransaction.commit/rollback DatabaseConnectionFactory.returnConnection 8. Connection is requested in the transaction and returned in the transaction and then connection is requested again ithe transaction and returned in the transaction UserTransaction.begin DatabaseConnectionFactory.requestConnection DatabaseConnectionFactory.returnConnection DatabaseConnectionFactory.requestConnection DatabaseConnectionFactory.returnConnection UserTransaction.commit/rollback What are the issues: -------------------- A. If thread acquires connection before the transaction is started then at the time when the transaction is started we have to associate the connection with the transaction. Affects: 1,3,5,6 A.1 If the connection wasn't used within the transaction the the commit or rollback on transaction should be a no op. B. If the thread acquired connection during the transaction then this connection should be automatically associated with the transaction Affects: 2,4,5,6,7,8 C. If thread returns the connection before the transaction is finished then the transaction manager has to keep the connection open until the thread ends the transaction. Affects: 3,4,5,8 D: If the thread keeps the connection after the transaction is finished then after the transaction is finished the connection should be fully usable as any other JDBC connection including autocommit and commit/rollback but in the transaction the autocommit/commit/rollback should be disabled. Affects: 1,2,6,7 E: If the thread requests and returns connection multiple times during the same transaction then the same connection has to be given to it since the transaction is responsible for doing commit/rollback on the connection and our assumption is that the thread is using only single connection. Affects: 5,6,7,8 What are the solutions: ----------------------- A: The DatabaseConnectionFactory has to keep track if there is a connection issued to the calling thread so that when the transaction is started then this connection can be associated to the transaction. This can be done using ThreadLocal storing the requested connection. Then when the transaction is started, the UserTransaction has to check if there is an already requested connection and associate it with the transaction. This can be done using ThreadLocal storing the transactional connection. A.1 This can be done be creating wrapper around Connection. The DatabaseConnectionFactory will then on request and return create this wrapper which delegates all calls into underlying connection. When any method is called, it marks the connection as used. The transaction manager can then reset the used flag when the connection is first time associated with transaction or check it once commit or rollback were issued. B: The DatabaseConnectionFactory has to be aware of the started transaction and when an connection is requested then the connection is associated with the transaction. This can be done using ThreadLocal storing a flag if the transaction is in progress or not. If the transaction is in progress then the returned connection will be stored in the transactional connection. C: The UserTransaction has to keep track of the connection which was used in the transaction and don't allow to return it until the transaction is finished. Then, once the transaction is finished it has to return the connection. This can be done using ThreadLocal storing a flag for the transactional connection if the connection was returned in the transaction or not. D: The connection should be aware if there is a transaction in progress or not and based on that allow autocommit and commit or rollback or not. This can be done be creating wrapper around Connection. The DatabaseConnectionFactory will then on request and return create this wrapper which delegates all calls into underlying connection and intercepts and checks autocommit and commit and rollback. E. The DatabaseConnectionFactory has to be aware of the started transaction and when a connection is requested then the connection already associated with the transaction should be returned. What are the implications: -------------------------- 1. If the connection is associated with transaction it is not returned to the factory and not available for others. This is natural since there are some operations pending on that connection and even though the thread said it no longer needs it, it cannot be reused until the operations are flushed with commit/rollback. Implementation: --------------- 1. D. requires to create wrapper around connection. We will implement TransactionalConnection class as wrapper around JDBC Connection class, which will delegate all operations directly except setautocommit/commit/rollback, which will be delegated only after check for pending transaction. 2. The previous step required us to create integration between DatabaseTransactionFactory and DatabaseConnectionFactory, so that the transaction factory can act as a proxy for connection factory and get the real connection and wrap it with a wrapper. This will be implemented as a delegator pattern when DatabaseConnectionFactoryImpl will be delegating calls to DatabaseTransactionFactoryImpl and vice versa. The requestConnection call will - check if there is an transaction in progress, if there is and it has already associated connection then the connection associated with the transaction will be returned. - if there is no transaction in progress and there is already connection issued then return the same connection since this would represent the situation (such as subprocedure call) DatabaseConnectionFactory.requestConnection DatabaseConnectionFactory.requestConnection DatabaseConnectionFactory.returnConnection DatabaseConnectionFactory.returnConnection - if there is no transaction in progress and no already issued connection then get connection from underlying connection factory, create wrapper and remember the connection as issued using ThreadLocal. This will solve A, B and E The returnConnection call will - check if there is an transaction in progress, if there is then the connection will not be returned to the factory and will be returned when the transaction is done - if there is no transaction in progress then the underlying connection will be returned to the factory This will solve C 3. We will provide implementation of UserTransaction. The begin call will - check if there was connection issued and if it was, it will associate it with this transaction. It will also tell the connection that it is part of the transaction to ignore the setautocommit/commit/rollback calls. The commit call will - if there is connection associated with this transaction, it will commit the connection. If the connection was returned to the factory in the transaction then this time it will be really returned to the factory otherwise it will be just disassociated from this transaction so that the setautocommit/commit/rollback calls are no longer ignored. The rollback call will - if there is connection associated with this transaction, it will rollback the connection. If the connection was returned to the factory in the transaction then this time it will be really returned to the factory otherwise it will be just disassociated from this transaction so that the setautocommit/commit/rollback calls are no longer ignored. TransactionalConnection To distinguish if the TransactionalConnection was associated in transaction it will have inTransaction flag. To distinguish if the TransactionalConnection was used during the transaction it will have used flag which will be false initially and set to true if any method on connection is called. To distinguish if the TransactionalConnection was returned to factory or it is still used by application it will have active counter which will be incremented when the connection is requested from factory and decremented when it is returned.

Version:
$Id: SimpleLocalTransactionFactoryImpl.java,v 1.7 2007/01/07 06:14:21 bastafidli Exp $
Author:
Miro Halas
Code reviewer:
Miro Halas
Code reviewed:
1.4 2006/05/24 15:18:32 bastafidli

Field Summary
static java.lang.Integer STATUS_ACTIVE_OBJ
          A transaction is associated with the target object and it is in the active state.
static java.lang.Integer STATUS_COMMITTED_OBJ
          A transaction is associated with the target object and it has been committed.
static java.lang.Integer STATUS_COMMITTING_OBJ
          A transaction is associated with the target object and it is in the process of committing.
static java.lang.Integer STATUS_MARKED_ROLLBACK_OBJ
          A transaction is associated with the target object and it has been marked for rollback, perhaps as a result of a setRollbackOnly operation.
static java.lang.Integer STATUS_NO_TRANSACTION_OBJ
          No transaction is currently associated with the target object.
static java.lang.Integer STATUS_PREPARED_OBJ
          A transaction is associated with the target object and it has been prepared, i.e.
static java.lang.Integer STATUS_PREPARING_OBJ
          A transaction is associated with the target object and it is in the process of preparing.
static java.lang.Integer STATUS_ROLLEDBACK_OBJ
          A transaction is associated with the target object and the outcome has been determined as rollback.
static java.lang.Integer STATUS_ROLLING_BACK_OBJ
          A transaction is associated with the target object and it is in the process of rolling back.
static java.lang.Integer STATUS_UNKNOWN_OBJ
          A transaction is associated with the target object but its current status cannot be determined.
 
Fields inherited from class org.opensubsystems.core.persist.db.DatabaseTransactionFactoryImpl
s_bTransactionMonitor, s_iTransactionTimeout, TRANSACTION_MONITOR, TRANSACTION_MONITOR_DEFAULT, TRANSACTION_TIMEOUT, TRANSACTION_TIMEOUT_DEFAULT
 
Constructor Summary
SimpleLocalTransactionFactoryImpl()
          Default constructor using default database connection factory.
 
Method Summary
 void begin()
          
 void commit()
          
protected  void endTransaction(boolean bCommit)
          End active transaction by commit or rollback.
 int getStatus()
          
 javax.transaction.TransactionManager getTransactionManager()
          Get transaction manager for this factory.
 javax.transaction.UserTransaction requestTransaction()
          Get transaction object which we can use to begin/commit/rollback transactions.
protected  java.sql.Connection requestTransactionalConnection(boolean bAutoCommit, java.lang.String strDataSourceName, java.lang.String strUser, java.lang.String strPassword, DatabaseConnectionFactoryImpl connectionFactory)
          Check if there is an transaction in progress, if there is and it has already associated connection then the connection associated with the transaction will be returned.
 void reset()
          This method is here mainly for testing and it should reset the transaction manager to initial status to that tests can start from known environment instead of being influenced by other tests.
protected  void returnTransactionalConnection(java.sql.Connection cntDBConnection, DatabaseConnectionFactoryImpl connectionFactory)
          This method should be exclusively used by DatabaseConnectionFactoryImpl to get a transaction aware version of a connection.
 void rollback()
          
 void setRollbackOnly()
          
 void setTransactionTimeout(int arg0)
          
 void stop()
          Stop the transaction factory.
 
Methods inherited from class org.opensubsystems.core.persist.db.DatabaseTransactionFactoryImpl
commitTransaction, getInstance, isTransactionInProgress, isTransactionMonitored, rollbackTransaction, setInstance
 
Methods inherited from class java.lang.Object
clone, equals, finalize, getClass, hashCode, notify, notifyAll, toString, wait, wait, wait
 

Field Detail

STATUS_ACTIVE_OBJ

public static final java.lang.Integer STATUS_ACTIVE_OBJ
A transaction is associated with the target object and it is in the active state.


STATUS_COMMITTED_OBJ

public static final java.lang.Integer STATUS_COMMITTED_OBJ
A transaction is associated with the target object and it has been committed.


STATUS_COMMITTING_OBJ

public static final java.lang.Integer STATUS_COMMITTING_OBJ
A transaction is associated with the target object and it is in the process of committing.


STATUS_MARKED_ROLLBACK_OBJ

public static final java.lang.Integer STATUS_MARKED_ROLLBACK_OBJ
A transaction is associated with the target object and it has been marked for rollback, perhaps as a result of a setRollbackOnly operation.


STATUS_NO_TRANSACTION_OBJ

public static final java.lang.Integer STATUS_NO_TRANSACTION_OBJ
No transaction is currently associated with the target object.


STATUS_PREPARED_OBJ

public static final java.lang.Integer STATUS_PREPARED_OBJ
A transaction is associated with the target object and it has been prepared, i.e.


STATUS_PREPARING_OBJ

public static final java.lang.Integer STATUS_PREPARING_OBJ
A transaction is associated with the target object and it is in the process of preparing.


STATUS_ROLLEDBACK_OBJ

public static final java.lang.Integer STATUS_ROLLEDBACK_OBJ
A transaction is associated with the target object and the outcome has been determined as rollback.


STATUS_ROLLING_BACK_OBJ

public static final java.lang.Integer STATUS_ROLLING_BACK_OBJ
A transaction is associated with the target object and it is in the process of rolling back.


STATUS_UNKNOWN_OBJ

public static final java.lang.Integer STATUS_UNKNOWN_OBJ
A transaction is associated with the target object but its current status cannot be determined.

Constructor Detail

SimpleLocalTransactionFactoryImpl

public SimpleLocalTransactionFactoryImpl()
                                  throws OSSException
Default constructor using default database connection factory.

Throws:
OSSException - - an error has occured
Method Detail

requestTransaction

public javax.transaction.UserTransaction requestTransaction()
Get transaction object which we can use to begin/commit/rollback transactions. This operation is valid only if the transaction factory support explicit transaction across multiple connections.

Specified by:
requestTransaction in interface TransactionFactory
Returns:
UserTransaction - null if explicit transactions are not supported.

reset

public void reset()
           throws OSSException
This method is here mainly for testing and it should reset the transaction manager to initial status to that tests can start from known environment instead of being influenced by other tests.

Specified by:
reset in interface TransactionFactory
Throws:
OSSException - - an error has occured during reset

stop

public void stop()
          throws OSSException
Stop the transaction factory.

Specified by:
stop in interface TransactionFactory
Throws:
OSSException - - problem stoping transaction factory.

getTransactionManager

public javax.transaction.TransactionManager getTransactionManager()
Get transaction manager for this factory.

Specified by:
getTransactionManager in interface TransactionFactory
Returns:
TransactionManager

begin

public void begin()
           throws javax.transaction.NotSupportedException,
                  javax.transaction.SystemException

Specified by:
begin in interface javax.transaction.UserTransaction
Throws:
javax.transaction.NotSupportedException
javax.transaction.SystemException

commit

public void commit()
            throws javax.transaction.RollbackException,
                   javax.transaction.HeuristicMixedException,
                   javax.transaction.HeuristicRollbackException,
                   java.lang.SecurityException,
                   java.lang.IllegalStateException,
                   javax.transaction.SystemException

Specified by:
commit in interface javax.transaction.UserTransaction
Throws:
javax.transaction.RollbackException
javax.transaction.HeuristicMixedException
javax.transaction.HeuristicRollbackException
java.lang.SecurityException
java.lang.IllegalStateException
javax.transaction.SystemException

getStatus

public int getStatus()
              throws javax.transaction.SystemException

Specified by:
getStatus in interface javax.transaction.UserTransaction
Throws:
javax.transaction.SystemException

rollback

public void rollback()
              throws java.lang.IllegalStateException,
                     java.lang.SecurityException,
                     javax.transaction.SystemException

Specified by:
rollback in interface javax.transaction.UserTransaction
Throws:
java.lang.IllegalStateException
java.lang.SecurityException
javax.transaction.SystemException

setRollbackOnly

public void setRollbackOnly()
                     throws java.lang.IllegalStateException,
                            javax.transaction.SystemException

Specified by:
setRollbackOnly in interface javax.transaction.UserTransaction
Throws:
java.lang.IllegalStateException
javax.transaction.SystemException

setTransactionTimeout

public void setTransactionTimeout(int arg0)
                           throws javax.transaction.SystemException

Specified by:
setTransactionTimeout in interface javax.transaction.UserTransaction
Throws:
javax.transaction.SystemException

requestTransactionalConnection

protected java.sql.Connection requestTransactionalConnection(boolean bAutoCommit,
                                                             java.lang.String strDataSourceName,
                                                             java.lang.String strUser,
                                                             java.lang.String strPassword,
                                                             DatabaseConnectionFactoryImpl connectionFactory)
                                                      throws OSSDatabaseAccessException
Check if there is an transaction in progress, if there is and it has already associated connection then the connection associated with the transaction will be returned. If there is no transaction in progress and there is already connection issued then return the same connection since this would represent the situation (such as subprocedure call) DatabaseConnectionFactory.requestConnection DatabaseConnectionFactory.requestConnection DatabaseConnectionFactory.returnConnection DatabaseConnectionFactory.returnConnection If there is no transaction in progress and no already issued connection then get connection from the calling connection factory, create wrapper and remember the connection as issued using ThreadLocal.

Overrides:
requestTransactionalConnection in class DatabaseTransactionFactoryImpl
Parameters:
bAutoCommit - - The desired autocommit state of the connection. If this connection is invoked in global (JTA) transaction then the autocommit is false regardless of what value is specified here. Use true here if the client only reads the data and false if the client also modifies the data.
strDataSourceName - - datasource for which the connection was requested, if null then it is requested for default data source
strUser - - user for which the connection was requested, if null then it is requested for default user
strPassword - - password for which the connection was requested, if null then it is requested for default passwoed
connectionFactory - - connection factory which is requesting the connection
Returns:
Connection - transaction aware version of connection
Throws:
OSSDatabaseAccessException - - an error has occured

returnTransactionalConnection

protected void returnTransactionalConnection(java.sql.Connection cntDBConnection,
                                             DatabaseConnectionFactoryImpl connectionFactory)
This method should be exclusively used by DatabaseConnectionFactoryImpl to get a transaction aware version of a connection. If the connection is transaction aware then it will be realy returned if it is not involved in a pending transaction. This method is protected so that only classes from this package can access it.

Overrides:
returnTransactionalConnection in class DatabaseTransactionFactoryImpl
Parameters:
cntDBConnection - - connection to return, can be null
connectionFactory - - connection factory to which the connection should be returned

endTransaction

protected void endTransaction(boolean bCommit)
                       throws javax.transaction.SystemException
End active transaction by commit or rollback.

Parameters:
bCommit - - if true then transaction will be commited otherwise it will be rollbacked.
Throws:
javax.transaction.SystemException - - and error has occured during commit/rollback


Copyright © 2003 - 2006 OpenSubsystems s.r.o.