getConnection in UserTransaction returned closed connection after XAResource.commit() failed on same thread
Environment
- Red Hat JBoss Enterprise Application Platform (EAP)
- 7.x
Issue
- When retrying the Usertransaction after
javax.transaction.Usertransaction#commit()failed,javax.sql.DataSource#getConnection()returns closed connection in new Usertransaction.
First, Usertransaction failed with the following Error.
WARN [com.arjuna.ats.jta] (default task-1) ARJUNA016039: onePhaseCommit on < formatId=131077, gtrid_length=29, bqual_length=36, tx_uid=0:ffff7f000001:21fea082:5dc8ed99:f, node_name=1, branch_uid=0:ffff7f000001:21fea082:5dc8ed99:14, subordinatenodename=null, eis_name=java:jboss/datasources/ExampleDS > (LocalXAResourceImpl@6cb4daa9[connectionListener=6a0eecdd connectionManager=36f87501 warned=false currentXid=null productName=H2 productVersion=@PROJECT_VERSION@ (2016-10-31) jndiName=java:jboss/datasources/ExampleDS]) failed with exception XAException.XA_RBROLLBACK: org.jboss.jca.core.spi.transaction.local.LocalXAException: IJ001156: Could not commit local transaction
at org.jboss.jca.core.tx.jbossts.LocalXAResourceImpl.commit(LocalXAResourceImpl.java:182)
at com.arjuna.ats.internal.jta.resources.arjunacore.XAOnePhaseResource.commit(XAOnePhaseResource.java:120)
at com.arjuna.ats.internal.arjuna.abstractrecords.LastResourceRecord.topLevelOnePhaseCommit(LastResourceRecord.java:172)
at com.arjuna.ats.arjuna.coordinator.BasicAction.onePhaseCommit(BasicAction.java:2386)
at com.arjuna.ats.arjuna.coordinator.BasicAction.End(BasicAction.java:1497)
at com.arjuna.ats.arjuna.coordinator.TwoPhaseCoordinator.end(TwoPhaseCoordinator.java:96)
at com.arjuna.ats.arjuna.AtomicAction.commit(AtomicAction.java:162)
at com.arjuna.ats.internal.jta.transaction.arjunacore.TransactionImple.commitAndDisassociate(TransactionImple.java:1287)
at com.arjuna.ats.internal.jta.transaction.arjunacore.BaseTransaction.commit(BaseTransaction.java:126)
at com.arjuna.ats.jbossatx.BaseTransactionManagerDelegate.commit(BaseTransactionManagerDelegate.java:94)
at org.wildfly.transaction.client.LocalTransaction.commitAndDissociate(LocalTransaction.java:75)
at org.wildfly.transaction.client.ContextTransactionManager.commit(ContextTransactionManager.java:71)
at org.wildfly.transaction.client.LocalUserTransaction.commit(LocalUserTransaction.java:53)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:498)
at org.jboss.weld.util.reflection.Reflections.invokeAndUnwrap(Reflections.java:410)
at org.jboss.weld.bean.builtin.CallableMethodHandler.invoke(CallableMethodHandler.java:42)
at org.jboss.weld.bean.proxy.EnterpriseTargetBeanInstance.invoke(EnterpriseTargetBeanInstance.java:56)
at org.jboss.weld.bean.proxy.ProxyMethodHandler.invoke(ProxyMethodHandler.java:106)
at org.jboss.weldx.transaction.UserTransaction$$Proxy$_$$_Weld$Proxy$.commit(Unknown Source)
at com.example.HelloServlet.doGet(HelloServlet.java:65)
After that, when retrying Usertransaction and javax.sql.DataSource#getConnection() on same thread, application got the closed connection.
e.g. (the detail of SQLException depends on the JDBC vendor.)
org.h2.jdbc.JdbcSQLException: The object is already closed [90007-193]
at org.h2.message.DbException.getJdbcSQLException(DbException.java:345)
at org.h2.message.DbException.get(DbException.java:179)
at org.h2.message.DbException.get(DbException.java:155)
at org.h2.message.DbException.get(DbException.java:144)
at org.h2.jdbc.JdbcConnection.checkClosed(JdbcConnection.java:1480)
at org.h2.jdbc.JdbcConnection.checkClosed(JdbcConnection.java:1458)
at org.h2.jdbc.JdbcConnection.createStatement(JdbcConnection.java:202)
at org.jboss.jca.adapters.jdbc.WrappedConnection$1.produce(WrappedConnection.java:357)
at org.jboss.jca.adapters.jdbc.WrappedConnection$1.produce(WrappedConnection.java:355)
at org.jboss.jca.adapters.jdbc.SecurityActions.executeInTccl(SecurityActions.java:97)
at org.jboss.jca.adapters.jdbc.WrappedConnection.createStatement(WrappedConnection.java:355)
at com.example.HelloServlet.doGet(HelloServlet.java:59)
at javax.servlet.http.HttpServlet.service(HttpServlet.java:686)
Resolution
Close the connection properly before javax.transaction.Usertransaction.commit() as in the following example.
e.g.
javax.transaction.UserTransaction userTransaction = ... // get user transaction here
userTransaction.begin();
java.sql.Connection conn = dataSource.getConnection();
try {
java.sql.Statement stmt = conn.createStatement();
try {
stmt.executeUpdate("...");
} finally {
stmt.close();
}
} finally {
conn.close(); // close connection before commit
}
userTransaction.commit();
This issue is resolved as part of:
- This content is not included.EAP 7.2 cumulative patch (CP) 71
- This content is not included.EAP 7.3 cumulative patch (CP) 12
Root Cause
- Content from docs.oracle.com is not included.javax.transaction.xa.XAResource.commit(Xid xid, boolean onePhase) will release the resource after commit fail.
- If the
java.sql.Connection.close()is not called beforejavax.transaction.UserTransaction.commit(), the closed connection would be returned by the datasource. - Vulnerability to failure of the application code to properly close the connection is a known defect (This content is not included.JBJCA-1396).
Components
Category
Tags
This solution is part of Red Hat’s fast-track publication program, providing a huge library of solutions that Red Hat engineers have created while supporting our customers. To give you the knowledge you need the instant it becomes available, these articles may be presented in a raw and unedited form.