MetaBoss
User Guides
Synopsis
Beginner's Guide
Configuration Guide
Design Studio Guide
Programming Model Guide
Testing Framework Guide
'How To ...' Guides
References
Synopsis
Design Model Reference
Design Model UML Profile
Test Schema Reference
MetaBoss design library
Run-time libraries API
Dev-time libraries API
HatMaker Example
Synopsis
Enterprise Model Description
Enterprise Model Reference
SystemTest Example
WebServices Example
Miscellaneous
About MetaBoss
Quality Assurance
Compatibility Notes
Acknowledgments
Glossary
Change Log
Version 1.4.0001
Built on 15 Dec 2005 22:31:47

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:

    /** Transfer funds between accounts */
    public void transferFunds(AccountNum pAccountFrom, AccountNum pAccountTo, Amount pAmount)
    {
        // Create initial naming context
        javax.naming.Context lContext = new javax.naming.InitialContext();

        // Obtain UserTransaction object and begin transaction
        UserTransaction lTransaction = lContext.lookup(UserTransaction.COMPONENT_URL);
        lTransaction.begin();
        try
        {
            // Call transactional service to transfer the money
            BSAccountManagement lAccountService = lContext.lookup(BSAccountManagement.COMPONENT_URL);
            lAccountService.transfer(pAccountFrom,pAcountTo,pAmount);

            // Call another transactional service to send transfer notification
            BSClientCommunication lMessengerService = lContext.lookup(BSClientCommunication.COMPONENT_URL);
            lMessengerService.sendTransferNotification(pAccountFrom,pAcountTo,pAmount);
        }
        catch(InvalidAccountException e)
        {
            // Mark transaction for rollback
            lTransaction.setRollbackOnly();
        }
        finally
        {
            // Complete the transaction we have created
            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:

    /** Find required student and update the post code */
    public void updateStudentPostCode(StudentNo pStudentNo, PostCode pNewPostcode)
    {
        // Create initial naming context
        javax.naming.Context lContext = new javax.naming.InitialContext();

        // Obtain domain object and make it editable
        BODomain lDomain = (BODomain)lContext.lookup(BODomain.COMPONENT_URL);
        lDomain.beginEdit();
        
        // Obtain student by student number and make it editable
        BOStudent lStudent = lDomain.getStudent(pStudentNo);
        lStudent.beginEdit();

        // Update the postcode, save changes and return
        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:

    ....
    ....
    // Create initial naming context
    javax.naming.Context lContext = new javax.naming.InitialContext();

    // Obtain TransactionManager object
    TransactionManager lTransactionManager = lContext.lookup(TransactionManager.COMPONENT_URL);
    
    // Suspend current transaction
    Transaction lSuspendedTransaction = lTransactionManager.suspend();
    try
    {
        // Begin new transaction
        lTransactionManager.begin();
        try
        {
            // Do some transactional work
            ....
            ....
        }
        finally
        {
            // Complete the transaction we have created
            int lStatus = lTransactionManager.getStatus();
            if (lStatus == Status.STATUS_ACTIVE)
                lTransactionManager.commit();
            else
                lTransactionManager.rollback();
        }
    }
    finally
    {
        // Restore suspended transaction if necessary
        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.