MetaBoss Programming Model Guide.
Transaction Management.
Index
Introduction
Java Transaction Architecture
Client side transaction management
Server side transaction management
Business Service transaction policies
Platform independent Transaction lookup
Conclusion
Introduction
A transaction is a set of related actions that either succeed or fail as a unit.
In MetaBoss terms, transaction either commits or rolls back all changes to all affected entities.
Afected entities may reside in one or more domains (databases) and for a transaction to commit,
all participating databases must guarantee that any change to data will be permanent.
Changes must persist despite system crashes or other unforeseen events. If even a single participant fails to make this guarantee,
the entire transaction rolls back, which means that all changes to entities done within the scope of the transaction are rolled back to the original state).
From the transaction mechanism point of view, programming model must define interfaces to
manage transaction bracket (begin, commit, rollback) and manage association between particular
actions and the transaction. This chapter describes MetaBoss's approach to the transaction management.
Java Transaction Architecture
The MetaBoss programming model uses Java Transaction Architecture (JTA), which is one of the
standards in the J2EE stack. JTA defines standard transaction management interfaces between application code and underlying transaction manager.
It allows to disassociate application code from the transaction manager implementation particulars. You can read more about JTA on Sun's site
or in the J2EE documentation pack.
In MetaBoss programming model, the generated and handcoded programs
simply not aware of who and how is managing transactions, all they use is JTA interfaces.
As we have already mentioned, JTA implementation comes as a standard with all J2EE containers.
But what about cases when MetaBoss system is deployed without any J2EE container (eg. as a set of CORBA components
or even as a single 'fat' web application)? How do we provide JTA facilities in this case? Well, we use
standalone JTA compliant transaction manager. While at the moment we use Tyrex transaction manager,
thanks to the plug-in architecture (based on JNDI), any other JTA compliant transaction manager can be used instead.
Client side transaction management
In JTA the client is supposed to control transactions via
javax.transaction.UserTransaction object. This usually involves obtaining
an instance of UserTransaction, beginning the new transaction, doing some transactional work
(eg. calling some services) and commiting or rolling back all changes at the end. The simplified example below
shows the typical client code:
public void transferFunds(AccountNum pAccountFrom, AccountNum pAccountTo, Amount pAmount)
{
javax.naming.Context lContext = new javax.naming.InitialContext();
UserTransaction lTransaction = lContext.lookup(UserTransaction.COMPONENT_URL);
lTransaction.begin();
try
{
BSAccountManagement lAccountService = lContext.lookup(BSAccountManagement.COMPONENT_URL);
lAccountService.transfer(pAccountFrom,pAcountTo,pAmount);
BSClientCommunication lMessengerService = lContext.lookup(BSClientCommunication.COMPONENT_URL);
lMessengerService.sendTransferNotification(pAccountFrom,pAcountTo,pAmount);
}
catch(InvalidAccountException e)
{
lTransaction.setRollbackOnly();
}
finally
{
int lStatus = lTransaction.getStatus();
if (lStatus == Status.STATUS_ACTIVE)
lTransaction.commit();
else
lTransaction.rollback();
}
}
Notice how in the above example the try-finally blocks are used
to ensure that the transaction is managed correctly regardless of the operation outcome.
Server side transaction management
The good thing about JTA transaction management is that it keeps the transaction
and transaction management invisible to the server code. Consider the following simplified server code fragment:
public void updateStudentPostCode(StudentNo pStudentNo, PostCode pNewPostcode)
{
javax.naming.Context lContext = new javax.naming.InitialContext();
BODomain lDomain = (BODomain)lContext.lookup(BODomain.COMPONENT_URL);
lDomain.beginEdit();
BOStudent lStudent = lDomain.getStudent(pStudentNo);
lStudent.beginEdit();
lStudent.setPostCode(pPostCode);
lDomain.saveChanges();
return;
}
In this example the service code has obtained Domain object and the Student object,
made these objects editable (this is akin to locking the object for editing), updated the data and saved changes.
While this operation requires that the current execution thread is associated with the active transaction (and
the beginEdit() operation will fail if this is not the case), the management of the transaction bracket is left to the caller.
This means that the client will decide when to create transaction, which operations to invoke, how many times and in which order
to invoke them and when to commit all accumulated changes.
Some times, however, server code may need to manage transactions explicitly.
Typically this may be required if operation needs to perform an action as a standalone unit of work (eg. update some counter value in database).
In this case server might want to access current transaction, suspend it for a time being and
execute some actions within brand new transaction. All facilities for this requirement are
available in JTA and the code would look something like in the simplified example below:
....
....
javax.naming.Context lContext = new javax.naming.InitialContext();
TransactionManager lTransactionManager = lContext.lookup(TransactionManager.COMPONENT_URL);
Transaction lSuspendedTransaction = lTransactionManager.suspend();
try
{
lTransactionManager.begin();
try
{
....
....
}
finally
{
int lStatus = lTransactionManager.getStatus();
if (lStatus == Status.STATUS_ACTIVE)
lTransactionManager.commit();
else
lTransactionManager.rollback();
}
}
finally
{
if (lSuspendedTransaction != null)
lTransactionManager.resume(lSuspendedTransaction);
}
....
....
Notice how in the above example the try-finally blocks are used
to ensure that the transaction is managed correctly regardless of the operation outcome.
Business Service transaction policies
As it was mentioned above, the server code normally
does not manage the transactions. However, the nature of the operation itself
(ie. what does it do and how does it do it) may require very particular
transaction status at the time the operation is invoked. For example the
updateStudentPostCode() operation above is written with an assumption that
calling thread has active transaction at the time of call to this operation.
This shows a number of interesting issues:
- Does this mean that server code has to check that transaction is in
certain state and if it is not satisfied throw back some meaningful exception?
- Does this mean that the client code, which simply wants to execute a single
atomic update, still has to manage transactions explicitly?
The J2EE has the answers for these types of issues.
Basically EJB has to declare the type of the transaction policy per each operation
and the container will ensure that the client has conformed to the policy at the time of
each operation invocation. In some cases, if transaction policy allows atomic updates,
container will 'help' the client by managing transaction on it's behalf.
The transaction policy mechanism is very good and useful, but
it is only available in J2EE. MetaBoss has an inbuilt mechanism, which makes this feature
technology independent. In this mechanism the TransactionPolicy is an attribute of the
Operation definition in the MetaBoss Enterprise Model. For J2EE depoyments, this information
is used to generate J2EE specific deployment descriptors. For non J2EE deployments,
MetaBoss generates the Transaction Management proxy class which wraps around actual
service implementations (using MetaBoss componentisation framework) and provides exactly
the same transaction policy management as J2EE container does. Again, the net result is that
the generated and handcoded programs remain independent of the deployment strategy.
Platform independent Transaction lookup
One important feature in the above examples is the way how
instances of JTA interfaces are obtained in MetaBoss programming model.
This pattern is dealing with one problem with JTA specification - it does specify the
interfaces, but does not standartise the way the instances of these interfaces can be obtained.
For example, the EJB1.0 containers use JNDI and lookup the 'javax.transaction.UserTransaction', the EJB1.1 containers
use JNDI and lookup the 'java:comp/env/UserTransaction'. In his Enterprise JavaBeans book Richard Monson-Haefel says:
'EJB 1.0 doesn't specify how client applications should obtain a reference to a UserTransaction. Many vendors make
the UserTransaction available to the client application through JNDI. EJB servers may use other mechanisms, such as a
proprietary API or casting the home interface into a UserTransaction'. Situation is even more clouded if you
include non-J2EE environments.
This, of course, is unacceptable for true platform independent
code. This is why MetaBoss provides extremely thin JTA abstraction layer in the
com.metaboss.enterprise.transaction package. Interfaces in this layer are simple extensions
to the JTA interfaces, which only define COMPONENT_URL string constants used to lookup
real JTA interfaces via standard MetaBoss componentisation framework.
Two alternative jars MetaBossEnterpriseJ2EESPI.jar and
MetaBossEnterpriseInprocessSPI.jar are provided 'out of the box' with MetaBoss. They resolve
"abstract" lookup of JTA components to something concrete. The MetaBossEnterpriseJ2EESPI.jar
will access J2EE JTA implementation and the MetaBossEnterpriseInprocessSPI.jar will
access Tyrex JTA implementation. All of this is totally transparent to the
application code. All is required is to include particular jar file in the classpath.
Conclusion
Transaction management mechanism in MetaBoss programming model is based on
Java Transaction Architecture (JTA), which is part of J2EE. Few small MetaBoss specific
additions make the transaction management code fully independent of the deployment techology.
In particular it makes JTA mechanism available in non-J2EE environments.
|