IllegalStateException After Calling the getSessionContext().setRollbackOnly() Bean Method

Technote (FAQ)
Problem
When creating EJB™s in a transaction that you have already marked for rollback, you get the following unexpected exception in WebSphere® Application Server V3.5 (all releases):

java.rmi.ServerException: RemoteException occurred in server thread;
nested exception is:
com.ibm.websphere.csi.CSITransactionRolledbackException:
Transaction timed out
Cause
Unlike V4.0 and V5.0, the method setRollbackOnly() rolls back the transaction immediately in all releases of V3.5. The transaction ends immediately instead of being deferred until the completion of the method that started the transaction, which is what happens in V4.0 and V5.0. Consequently, in V3.5, the method getRollbackOnly issues IllegalStateException, because there is no transaction.
Solution

Scenario

You enter a stateless session bean (with TX_REQUIRED) and do some business logic. You find out that you are unable to perform the desired task so you set the transaction to rollback by calling getSessionContext().setRollbackOnly() in the bean method. Before you return from the bean method you would like to log this error in the database by using another session bean with transaction attribute TX_REQUIRES_NEW. You want to rollback the business logic but commit the error log. However, when creating the home interface, you get this exception:

java.rmi.ServerException: RemoteException occurred in server thread;
nested exception is:
com.ibm.websphere.csi.CSITransactionRolledbackException:
Transaction timed out


Tested environments

Platforms that were tested and found to contain the problem:

  • VisualAge for Java™ 3.5.3 on Windows 2000
  • WebSphere Application Server V3.5.4 on Windows® NT 4 SP6
  • WebSphere Application Server V3.5.4 on AIX 4.3.3

Platforms that were tested and found to work correctly:
  • WebSphere Application Developer 4 (EJB 1.1)
  • WebSphere Application Server V4
  • WebSphere Application Developer 5 (EJB 2.0)

Simple test case

This is a simple test case to show the problem. It uses two session beans as in the scenario above. The first stateless session bean with transaction attribute TX_REQUIRED (DoLogicBean) does setRollbackOnly() and then tries to create a second stateless session
bean (DoLogBean) that has got transaction attribute TX_REQUIRES_NEW.


This is all the code (most of it is debug information):


public void doIt() throws Exception
System.out.println("DoLogic.doIt(): Enter");
System.out.println("DoLogic.doIt(): ------ getRollbackOnly = " +
getSessionContext().getRollbackOnly());
try
System.out.println("DoLogic.doIt(): Getting intial context
...");
javax.naming.InitialContext vContext = new InitialContext();
System.out.println("DoLogic.doIt(): setting rollback only ...");
getSessionContext().setRollbackOnly();
System.out.println("DoLogic.doIt(): ------ getRollbackOnly = " +
getSessionContext().getRollbackOnly());
System.out.println("DoLogic.doIt(): lookup bean named
anna/logic/DoLog...");
DoLogHome vDoLogHome =
(DoLogHome) javax.rmi.PortableRemoteObject.narrow(
vContext.lookup("anna/logic/DoLog"),
DoLogHome.class);
System.out.println("DoLogic.doIt(): create bean...");
DoLog vDoLog = vDoLogHome.create();
System.out.println("DoLogic.doIt(): call bean method (with
REQUIRES_NEW)...");
vDoLog.log();
System.out.println("DoLogic.doIt(): call bean method done, we
have made it|");
System.out.println("DoLogic.doIt(): ------ getRollbackOnly = " +
getSessionContext().getRollbackOnly());
catch (Exception e)
System.out.println("DoLogic.doIt(): Execption: " +
e.getMessage());
System.out.println("");
e.printStackTrace();
throw e;

System.out.println("DoLogic.doIt(): Exit");

Attached to this document are the JAR files with the two stateless session beans, and a simple Java client that calls them. Deploy the beans and then run the program DoIt.

DescriptionJAR file
Exported deployed JAR from VAJ with session beans DoLogic (TX_REQUIRED)
and DoLog (TX_REQUIRES-NEW)
annatransejb13.jar
Exported Java and class files for the same two beans without deployed
code
annatransejb14nodepl.jar
Java code and class file for simple Java application DoIt for testing
the beans
annatranstester11.jar

The output from the test case looks like this when it is run on V3.5.4:


DoLogic.doIt(): Enter
DoLogic.doIt(): ------ getRollbackOnly = false
DoLogic.doIt(): Getting intial context ...
DoLogic.doIt(): setting rollback only ...
DoLogic.doIt(): ------ getRollbackOnly = true
DoLogic.doIt(): lookup bean named anna/logic/DoLog...
DoLogic.doIt(): create bean...
DoLogic.doIt(): Execption: RemoteException occurred in server thread;
nested exception is:
com.ibm.websphere.csi.CSITransactionRolledbackException: Transaction
timed out


Updated to V3.5.6 and found other problems

When running the same test cases on V3.5.6 on Windows NT, a different problem occurs. The container issues an IllegalStateException on the client side.

com.ibm.ejs.container.UncheckedException: ; nested exception is:

java.lang.IllegalStateException

There are some transaction differences between V3.5 and later releases causing the difference in behavior. The EJB implementation in V3.5 is different than the implementation in V4.0 and V5.0 releases, which are fully compliant to EJB 1.1 and EJB 2.0 specifications respectively. V3.5 was designed and released prior to the final EJB draft specifications, and addressed many aspects of the early EJB specification level, but is not fully compliant.

In some cases like these, an enhanced design is required to implement the required functionality. Following is an explanation of what occurs in V3.5 and a workaround that enables your application code to work unmodified from V3.5 through V5.0.

Unlike V4.0 and V5.0, the method setRollbackOnly() rollbacks the transaction immediately in V3.5. Therefore, the transaction ends immediately instead of being deferred until the completion of the method that started the transaction, as is done in V4.0 and V5.0. Consequently, in V3.5 the method getRollbackOnly() issues IllegalStateException, because there is no transaction.

This is the correct way to handle this as documented in the EJB 1.1 specification, 6.8.2 under the restrictions section. This function is consistent with the specification when the transaction is rolled back immediately as done in V3.5

To enhance V3.5 to rollback the transaction at the completion of the method starting the transaction is difficult due to the basic design. Therefore, the getRollbackOnly() returns false if the transaction has not been rolled back. Or it returns the IllegalStateException exception, which means the transaction did not exist or has been rolled back previous to the getRollbackOnly() call. It does not return true in V3.5.

Instead of not being able to address this as in V4.0 and V5.0, and considering this is the result of the base design of V3.5, we suggest a workaround that works in a straight forward manner and that is compatible with new versions of WebSphere Application Server.

Our suggestion is to modify the code to catch the exception and continue, such as:
boolean rollbackonlyStatus=false;
try{
rollbackonlyStatus=sessionContext.getRollbackOnly();// for 4.0 or 5.0 this will not throw exception.
}catch(IllegalStateException e){
//Catch InvalidStateException for 3.5
rollbackonlyStatus=true;
}
if(rollbackonlyStatus==true){
// continue with
// logging bean and abort....
}
This provides a solution for minimal effort, and ensures that the application code works across releases.












Document Information

Product categories: Software, Application Servers, Distributed Application & Web Servers, WebSphere Application Server, EJB Container
Operating system(s): Multi-Platform
Software version: 3.5
Software edition: Advanced
Reference #: 1105546
IBM Group: Software Group
Modified date: 2004-12-09