Transaction suspend and resume: Spring 2.0 and Jboss 4.2.1
vijay2002 Jun 17, 2008 2:07 AMHi, We are using Spring and hibernate in our application in a jboss 4.2.1 container. JbossTM is our Transaction Manager. Recently we encountered a problem when using Requires_New transaction and we got the following exception:
[STDERR] java.lang.IllegalStateException: Trying to change transaction TransactionImple < ac, BasicAction: a0359b1:4e3:4734f5c7:fc status: ActionStatus.RUNNING > in enlist! [STDERR] at org.jboss.resource.connectionmanager.TxConnectionManager$TxConnectionEventListener.enlist(TxConnectionManager.java:525) [STDERR] at org.jboss.resource.connectionmanager.TxConnectionManager.transactionStarted(TxConnectionManager.java:328)Our scenario was: Service-1 (Started with "Requires" transaction scope, hence a new transaction is started) -------> Calls Service-2 (Started with "Requires_New" transaction scope, hence the earlier outer transaction created in service-1 is suspended and a new transaction is started). We were getting the above exception when Service-1 called Service-2. Spring folks are saying that this issue is happening because connection used by outer transaction in service-1 was not released to the connection pool when outer transaction was suspended. They also said that this is only happening with Jboss and not with Websphere and Weblogic. Anyways, they resolved this issue in their recent release in Spring 2.5. I am just curious and trying to simulate the above scenario by using naked Jboss transaction manager from jboss 4.2.1 and stripping out Spring/hibernate to better understand the problem. Following is the piece of code I wrote in a servlet to simulate the above behaviour, which is :
Start a transaction and check out a connection from datasource. Do some "work" on db using the connection Suspend the transaction and don't release the checked out connection Start a new transaction. I should get the IllegalStateException mentioned above as the connection used in outer transaction is not released upon suspension. Check out a new connection in inner transaction. Do some work and then rollback. Resume the outer transaction and then rollback.
JtaTransactionManager jtaTm = (JtaTransactionManager) ourAppContext.getBean("transactionManager"); jndiDS = (DataSource) ourAppContext.getBean("dataSource"); System.out.println("TM Class: " + jtaTm.getTransactionManager().getClass()); System.out.println("JNDI Datasource Class: " + jndiDS.getClass()); // start transaction TransactionManager tm = jtaTm.getTransactionManager(); System.out.println("Starting first transaction"); tm.begin(); // Retrieve a connection and insert into testJbossTrans Connection con1 = jndiDS.getConnection(); Statement stmt1 = con1.createStatement(); System.out.println("Connection's autocommit property: " + con1.getAutoCommit()); stmt1.execute("insert into testJbossTrans values(1)"); System.out.println("Suspending first transaction"); Transaction trans1 = tm.suspend(); // start a new transaction System.out.println("Starting second transaction; Connection from previous transaction not released"); tm.begin(); // Retrieve a connection and insert into testJbossTrans Connection con2 = jndiDS.getConnection(); Statement stmt2 = con2.createStatement(); stmt2.execute("insert into testJbossTrans values(2)"); System.out.println("Marking for rollback second transaction"); // mark for rollback and then, rollback tm.setRollbackOnly(); tm.rollback(); stmt2.close(); stmt2 = null; System.out.println("Resuming first transaction"); tm.resume(trans1); // mark for rollback again System.out.println("Marking for rollback first transaction"); tm.setRollbackOnly(); ResultSet rset = stmt1.executeQuery("select count(*) from testJbossTrans"); while (rset.next()) { System.out.println("Trans-1 after suspension and using con1: id = " + rset.getInt(1)); } // rollback and release connection tm.rollback(); con1.close(); con1 = null; stmt1.close(); stmt1 = null;Note that I am not releasing connection back to the connection pool when I suspend the first transaction. When I begin the second inner transaction, I was expecting to see java.lang.IllegalStateException: Trying to change transaction TransactionImple < ac, BasicAction: a0359b1:4e3:4734f5c7:fc status: ActionStatus.RUNNING > in enlist! but nothing happened. Instead, the above code ran fine and gave following output: [STDOUT] TM Class: class com.arjuna.ats.jbossatx.jta.TransactionManagerDelegate
[STDOUT] JNDI Datasource Class: class org.jboss.resource.adapter.jdbc.WrapperDataSource
[STDOUT] Starting first transaction
[STDOUT] Connection's autocommit property: false
[STDOUT] Suspending first transaction
[STDOUT] Starting second transaction; Connection from previous transaction not released
[STDOUT] Marking for rollback second transaction
[STDOUT] Resuming first transaction
[STDOUT] Marking for rollback first transaction
[STDOUT] Trans-1 after suspension and using con1: id = 1
Why am I not getting the IllegalStateException? What am I missing here? Once again, I am using Jboss 4.2.1 with Oracle Data source.