ONJava.com    
 Published on ONJava.com (http://www.onjava.com/)
 See this if you're having trouble printing code examples


Bean-Managed Transaction Suspension in J2EE

by Dmitri Maximovich
07/20/2005

Have you ever wondered why there are six types of Transaction demarcation attributes (NotSupported, Required, Supports, RequiresNew, Mandatory, and Never) supported in beans using container-managed transactions (CMT), but if you're using bean-managed transactions (BMT), the only functionality the EJB spec provides is to begin and commit/roll back transactions via the UserTransaction interface? Obviously the CMT model seems more capable--BMT lacks, for example, the ability to suspend and resume the current transaction, which means that you can't emulate the RequiresNew and NotSupported demarcations in BMT beans, at least not when you're using just the UserTransaction interface.

While the EJB specification doesn't explain why above-mentioned asymmetry exists, there is still a legitimate way to suspend and resume transactions in the BMT model. If you ever studied the contents of the javax.transaction package, you've probably noticed that along with the UserTransaction interface there is a TransactionManager interface that basically looks like an "extended" UserTransaction: the same methods--begin(), commit(), and rollback()--plus suspend() and resume().

If we can get a TransactionManager implementation from within our EJB, we will be able to achieve our goal of suspending and resuming transactions programmatically. Both the J2EE 1.3 and EJB 2.0 specifications are quiet about the availability of TransactionManager, but neither one of them explicitly prohibits using it. And since Container uses the Java Transaction API (JTA) internally for CMT transaction demarcation, we can be almost 100 percent sure that TransactionManager is present, and that it's just a matter of obtaining a reference to it in your code.

Related Reading

Head First EJB
Passing the Sun Certified Business Component Developer Exam
By Kathy Sierra, Bert Bates

In this article, we will see how to get a TransactionManager in several popular containers and show how to use it to extend functionality of bean-managed transactions, making them as powerful as container-managed transactions. We'll also outline some risks involved in using this advanced functionality, and at the end, explore how TransactionManager is used in the popular Spring framework.

Obtaining a Reference to TransactionManager in Various J2EE Servers

The J2EE and EJB specifications don't describe any standard means to obtain a reference to TransactionManager. Every J2EE container vendor is free to place it anywhere, or even to not provide any mechanism to access it from application code. In practice, though, all modern containers have mechanisms to obtain it. Below are examples how to get TransactionManagers from most popular J2EE containers.

Cast a UserTransaction (WebLogic, Orion, OC4J)

Any J2EE-compatible container must make the UserTransaction object available in JNDI under java:comp/UserTransaction. Since the UserTransaction interface is a subset of TransactionManager, some J2EE container vendors choose to provide a common implementation of both of them. WebLogic 8, Orion 2, and Oracle's OC4J EJB3 preview are examples of such an approach. In these containers, a reference to TransactionManager can be obtained just by getting a UserTransaction object from JNDI and casting it to TransactionManager. This is probably the simplest case.


private TransactionManager getFromUserTransaction() 
        throws Exception {
    InitialContext ctx = new InitialContext();
    UserTransaction ut = (UserTransaction)
        ctx.lookup("java:comp/UserTransaction");
    if (ut instanceof TransactionManager) {
        log("UserTransaction also TransactionManager");
        return (TransactionManager)ut;
    }
    return null;
}

Get TransactionManager Directly from JNDI (JBoss, WebLogic)

In JBoss 3 and WebLogic 8, TransactionManager is available in JNDI, albeit under different names, and therefore can be obtained by simple lookup:

private TransactionManager getFromJNDI() 
        throws Exception {
    InitialContext ctx = new InitialContext();
    try {
        // WebLogic
        return (TransactionManager)
            ctx.lookup("javax.transaction.TransactionManager");
     }
     catch (Exception e) {  }

    try {
        // JBoss
        return (TransactionManager)
            ctx.lookup("java:/TransactionManager");
    }
    catch (Exception e) { }
    return null;
}

Get TransactionManager from a Custom Factory (WebSphere)

In WebSphere 4/5/6, a reference to TransactionManager should be obtained from a factory class. It is annoying, though, that the factory class name has changed between different WebSphere versions.


public TransactionManager getFromWebsphereFactory() 
        throws Exception {
     try {
        // WebSphere 5.1 or 6.0
        return 
            com.ibm.ws.Transaction.TransactionManagerFactory
                .getTransactionManager();
    }
    catch (ClassNotFoundException ex) {}
        
    try {
        // WebSphere 5.0
        return 
            com.ibm.ejs.jts.jta.TransactionManagerFactory
                .getTransactionManager();
    }
    catch (ClassNotFoundException ex) {}
        
    try {
        // WebSphere 4.0
        com.ibm.ejs.jts.jta.JTSXA..getTransactionManager();
    }
    catch (ClassNotFoundException ex) { }

    return null;
}

In WebLogic 7/8/9, a reference to TransactionManager can be obtained from the static method getTransactionManager(), defined in TxHelper in WebLogic 7. This class was deprecated in WebLogic 8 and TransactionHelper should be used instead.


public TransactionManager getFromWebLogicFactory() 
    throws Exception {
     try {
        // WebLogic 8/9
        return 
            weblogic.transaction.TransactionHelper
                .getTransactionManager();
    }
    catch (ClassNotFoundException ex) {}
        
    try {
        // WebLogic 7
        return 
            weblogic.transaction.TxHelper
                .getTransactionManager();
    }
    catch (ClassNotFoundException ex) {}
        
    return null;
}

Using TransactionManager

Once you've successfully obtained a reference to TransactionManager, you can use it for transaction suspension and resumption, as shown in the sample code below:


...
// obtain UserTransaction object and start transaction
InitialContext ctx = new InitialContext();
UserTransaction userTransaction = (UserTransaction)
    ctx.lookup("java:comp/UserTransaction");

// start first transaction
userTransaction.begin();
            
// obtain TransactionManager 
// using one of the methods described above
TransactionManager tm = getTransactionManager();

// suspend transaction
// suspend() returns reference to suspended
// Transaction object which later should be passed
// to resume()
Transaction transaction = tm.suspend();
                
// here you can do something outside of transaction
// or start new transaction, 
// do something and then commit or rollback
userTransaction.begin();
    
// commit subtransaction
userTransaction.commit();

// resume suspended transaction
tm.resume(transaction);

// commit first transaction
userTransaction.commit();
...

As you can see, with the help of the TransactionManager interface, you can extend the standard functionality provided by UserTransaction and achieve in BMT code the same level of flexibility available for CMT beans.

One non-obvious thing to remember is that when a transaction is suspended, it doesn't mean that the transactional timer is stopped. In other words, if the transaction timeout was set to 30 seconds and then transaction was suspended for 20 seconds and resumed, the transaction would have only ten seconds left to run before timeout. Transaction suspension simply disassociates a transaction with the running thread, and the resume() call associates it back without affecting the transactional timeout timer.

Known Issues

Because the J2EE specification doesn't require the availability and functionality of TransactionManager in J2EE containers (although we know that it should be there because of underlying JTA infrastructure), there are a few issues that exist in some application servers. For example, one particularly strange anomaly exists in WebLogic versions 7, 8, and 9 (beta): if a transaction was marked for rollback (by calling UserTransaction.setRollbackOnly()) and then suspended, attempting to resume the suspended transaction will fail with:

javax.transaction.InvalidTransactionException: Attempt to resume an inactive transaction

The following code illustrates this behavior.


...
// obtain UserTransaction object and start transaction
InitialContext ctx = new InitialContext();
UserTransaction userTransaction = (UserTransaction)
    ctx.lookup("java:comp/UserTransaction");
userTransaction.begin();

// mark for rollback
userTransaction.setRollbackOnly();
            
TransactionManager tm = getTransactionManager();

// suspend transaction
Transaction transaction = tm.suspend();
                
// resume suspended transaction
// this call will fail with InvalidTransactionException
//  in WebLogic
tm.resume(transaction);
...

Fortunately, there is a workaround for this particular problem. WebLogic's implementation of TransactionManager, along with the standard resume(Transaction transaction) method, has a forceResume(Transaction transaction) method that can be used instead. The code below demonstrates the pattern that should be used when your code is running in WebLogic. Note that in this case, the reference to TransactionManager should be cast to WebLogic's custom implementation interfaces ( weblogic.transaction.TransactionManager in WebLogic 7 or weblogic.transaction.ClientTransactionManager in WebLogic 8 and above).


...
// obtain UserTransaction object and start transaction
InitialContext ctx = new InitialContext();
UserTransaction userTransaction = (UserTransaction)
    ctx.lookup("java:comp/UserTransaction");
userTransaction.begin();

// mark for rollback
userTransaction.setRollbackOnly();
            
TransactionManager tm = getTransactionManager();

// suspend transaction
Transaction transaction = tm.suspend();
                
// resume suspended transaction
try {
    // first try standard JTA call
    tm.resume(transaction);
}
catch (InvalidTransactionException e) {
    // standard method failed, try forceResume()
    if (tm instanceof 
        weblogic.transaction.ClientTransactionManager) {
        // WebLogic 8 and above
        ((weblogic.transaction.ClientTransactionManager)tm)
            .forceResume(transaction);
    }    
    else if (tm instanceof 
        weblogic.transaction.TransactionManager) {
        // WebLogic 7
        ((weblogic.transaction.TransactionManager)tm)
            .forceResume(transaction)
    }
    else {
        // cannot resume
        throw e;
    }
}
...

TransactionManager Usage in the Spring Framework

The techniques described above are used extensively in the popular Spring framework for transactional proxies. You should be aware that every time you configure Spring's TransactionProxyFactoryBean with PROPAGATION_REQUIRES_NEW or PROPAGATION_NOT_SUPPORTED, transaction attributes, and JtaTransactionManager, Spring uses JTA's TransactionManager for transaction suspension and resumption. Spring is smart enough--sometimes too smart for my taste--to discover TransactionManager in your container, even if you don't specify it. For example, you can supply a JNDI name for UserTransaction when defining JtaTransactionManager in a Spring application context and it will "autodetect" TransactionManager if UserTransaction also implements it; as shown above, this is true for WebLogic, Orion, and Oracle OC4J.

Sometimes, this could be undesirable, especially if you want to stay strictly within a J2EE/EJB specification and ensure complete portability across all J2EE containers. Sometimes, as we've seen above, there could be issues with programmatic transaction suspend and resume, although Spring knows how to work around some of them, at least the issue described above with marking transaction for rollback before suspending in WebLogic. In this case, you can set the autodetectTransactionManager property to false when configuring JtaTransactionManager. If you do so, any attempt to use PROPAGATION_REQUIRES_NEW or PROPAGATION_NOT_SUPPORTED transactional attributes should fail with TransactionSuspensionNotSupportedException, but PROPAGATION_REQUIRED, PROPAGATION_SUPPORTS, PROPAGATION_MANDATORY, and PROPAGATION_NEVER should work normally. This corresponds to the functionality provided by the JTA UserTransaction and is guaranteed to work in any J2EE-compatible container.

Below is the transaction manager definition in a Spring application context with TransactionManager autodetect disabled.


<bean id="transactionManager" class=
"org.springframework.transaction.jta.JtaTransactionManager">
  <property name="userTransactionName">
    <value>javax.transaction.UserTransaction</value>
  </property>
  <property name="autodetectTransactionManager">
    <value>false</value>
  </property>
</bean>

Conclusion

Support for the JTA TransactionManager interface is not required by the J2EE specification, but since J2EE uses JTA as its underlying transaction infrastructure, almost all J2EE servers expose it as an extension to J2EE. There are some known issues with compatibility, and under certain circumstances, container-specific code can be used to work around it. Nevertheless, using TransactionManager in J2EE is a powerful feature if you need to implement programmatic transaction suspension. Just always test it first in your chosen J2EE server.

Resources

Dmitri Maximovich is an independent consultant specializing in software design and development.


Return to ONJava.com.

Copyright © 2009 O'Reilly Media, Inc.