-
Bug
-
Resolution: Done
-
Major
-
5.12.7.Final, 5.13.1.Final, 6.0.3.Final, 7.1.0.Final, 7.2.2.Final
-
-
User Experience
-
Fixes a connection and session leak in the Narayana JMS integration component when the transaction is marked rollback only.
Note when running in combination with Spring and or Spring Boot and ArtemisMQ. This behaviour has occurs with ActiveMQ and TibcoMQ. Does not occur with BTM and WebSphere TXM. We've tested versions from '5.12.7.Final' onwards, with JDK 11/17 and 21.
When the PlatformTransactionManager#rollback(txStatus) is called, and the session has already been marked for rollback, the `SessionClosingSynchronization` cannot be registered with the transactionHelper. We see the following stacktrace:
2025-08-22 16:49:25,527 TRACE [JmsFFListener[1]] ats.jta - TransactionImple.getStatus: jakarta.transaction.Status.STATUS_MARKED_ROLLBACK
2025-08-22 16:49:25,532 TRACE [JmsFFListener[1]] ats.jta - TransactionImple.registerSynchronization - Class: class org.jboss.narayana.jta.jms.SessionClosingSynchronization HashCode: 188964741 toString: org.jboss.narayana.jta.jms.SessionClosingSynchronization@b435f85
2025-08-22 15:40:06,882 WARN [JmsFFListener[1]] ats.jta - ARJUNA016124: Failed to register synchronization
jakarta.transaction.RollbackException: ARJUNA016083: Cannot register synchronization because the transaction is in aborted state
at com.arjuna.ats.internal.jta.transaction.arjunacore.TransactionImple.registerSynchronizationImple(TransactionImple.java:381) ~[narayana-jta-7.2.2.Final.jar:7.2.2.Final (revision: 405c52)]
at com.arjuna.ats.internal.jta.transaction.arjunacore.TransactionImple.registerSynchronization(TransactionImple.java:362) ~[narayana-jta-7.2.2.Final.jar:7.2.2.Final (revision: 405c52)]
at org.jboss.narayana.jta.jms.TransactionHelperImpl.registerSynchronization(TransactionHelperImpl.java:43) ~[narayana-jta-7.2.2.Final.jar:7.2.2.Final (revision: 405c52)]
at org.jboss.narayana.jta.jms.SessionProxy.close(SessionProxy.java:74) ~[narayana-jta-7.2.2.Final.jar:7.2.2.Final (revision: 405c52)]
at org.springframework.jms.connection.JmsResourceHolder.closeAll(JmsResourceHolder.java:286) ~[spring-jms-6.2.8.jar:6.2.8]
at org.springframework.jms.connection.ConnectionFactoryUtils$JmsResourceSynchronization.releaseResource(ConnectionFactoryUtils.java:458) ~[spring-jms-6.2.8.jar:6.2.8]
at org.springframework.jms.connection.ConnectionFactoryUtils$JmsResourceSynchronization.releaseResource(ConnectionFactoryUtils.java:419) ~[spring-jms-6.2.8.jar:6.2.8]
at org.springframework.transaction.support.ResourceHolderSynchronization.beforeCompletion(ResourceHolderSynchronization.java:79) ~[spring-tx-6.2.8.jar:6.2.8]
at org.springframework.transaction.support.TransactionSynchronizationUtils.triggerBeforeCompletion(TransactionSynchronizationUtils.java:138) ~[spring-tx-6.2.8.jar:6.2.8]
at org.springframework.transaction.support.AbstractPlatformTransactionManager.triggerBeforeCompletion(AbstractPlatformTransactionManager.java:996) ~[spring-tx-6.2.8.jar:6.2.8]
at org.springframework.transaction.support.AbstractPlatformTransactionManager.processRollback(AbstractPlatformTransactionManager.java:881) ~[spring-tx-6.2.8.jar:6.2.8]
at org.springframework.transaction.support.AbstractPlatformTransactionManager.rollback(AbstractPlatformTransactionManager.java:866) ~[spring-tx-6.2.8.jar:6.2.8]
at org.frankframework.jta.SpringTxManagerProxy.rollback(SpringTxManagerProxy.java:80) ~[frankframework-core-9.2.0-SNAPSHOT.jar:9.2.0-SNAPSHOT]
at org.springframework.jms.listener.AbstractPollingMessageListenerContainer.rollbackOnException(AbstractPollingMessageListenerContainer.java:421) ~[spring-jms-6.2.8.jar:6.2.8]
at org.springframework.jms.listener.AbstractPollingMessageListenerContainer.receiveAndExecute(AbstractPollingMessageListenerContainer.java:249) ~[spring-jms-6.2.8.jar:6.2.8]
at org.frankframework.jms.IbisMessageListenerContainer.receiveAndExecute(IbisMessageListenerContainer.java:112) ~[frankframework-messaging-9.2.0-SNAPSHOT.jar:9.2.0-SNAPSHOT]
at org.springframework.jms.listener.DefaultMessageListenerContainer$AsyncMessageListenerInvoker.invokeListener(DefaultMessageListenerContainer.java:1420) ~[spring-jms-6.2.8.jar:6.2.8]
at org.springframework.jms.listener.DefaultMessageListenerContainer$AsyncMessageListenerInvoker.run(DefaultMessageListenerContainer.java:1294) ~[spring-jms-6.2.8.jar:6.2.8]
at org.frankframework.threading.ThreadNamingTaskDecorator.lambda$0(ThreadNamingTaskDecorator.java:36) ~[frankframework-core-9.2.0-SNAPSHOT.jar:9.2.0-SNAPSHOT]
at java.base/java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1144) ~[?:?]
at java.base/java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:642) ~[?:?]
at java.base/java.lang.Thread.run(Thread.java:1583) [?:?]
Moments later the next stacktrace occurs:
2025-08-22 16:49:35,252 TRACE [JmsFFListener[1]] ats.jta - TransactionImple.getStatus: jakarta.transaction.Status.STATUS_MARKED_ROLLBACK
2025-08-22 16:49:35,252 TRACE [JmsFFListener[1]] ats.jta - TransactionImple.registerSynchronization - Class: class org.jboss.narayana.jta.jms.ConnectionClosingSynchronization HashCode: 165787659 toString: org.jboss.narayana.jta.jms.ConnectionClosingSynchronization@9e1b80b
2025-08-22 16:49:35,252 WARN [JmsFFListener[1]] ats.jta - ARJUNA016124: Failed to register synchronization
jakarta.transaction.RollbackException: ARJUNA016083: Cannot register synchronization because the transaction is in aborted state
at com.arjuna.ats.internal.jta.transaction.arjunacore.TransactionImple.registerSynchronizationImple(TransactionImple.java:381) ~[narayana-jta-7.2.2.Final.jar:7.2.2.Final (revision: 405c52)]
at com.arjuna.ats.internal.jta.transaction.arjunacore.TransactionImple.registerSynchronization(TransactionImple.java:362) ~[narayana-jta-7.2.2.Final.jar:7.2.2.Final (revision: 405c52)]
at org.jboss.narayana.jta.jms.TransactionHelperImpl.registerSynchronization(TransactionHelperImpl.java:43) ~[narayana-jta-7.2.2.Final.jar:7.2.2.Final (revision: 405c52)]
at org.jboss.narayana.jta.jms.ConnectionProxy.close(ConnectionProxy.java:102) ~[narayana-jta-7.2.2.Final.jar:7.2.2.Final (revision: 405c52)]
at java.base/jdk.internal.reflect.DirectMethodHandleAccessor.invoke(DirectMethodHandleAccessor.java:103) ~[?:?]
at java.base/java.lang.reflect.Method.invoke(Method.java:580) ~[?:?]
at org.springframework.jms.connection.TransactionAwareConnectionFactoryProxy$TransactionAwareConnectionInvocationHandler.invoke(TransactionAwareConnectionFactoryProxy.java:296) ~[spring-jms-6.2.8.jar:6.2.8]
at jdk.proxy4/jdk.proxy4.$Proxy187.close(Unknown Source) ~[?:?]
at org.springframework.jms.connection.ConnectionFactoryUtils.releaseConnection(ConnectionFactoryUtils.java:80) ~[spring-jms-6.2.8.jar:6.2.8]
at org.springframework.jms.connection.JmsResourceHolder.closeAll(JmsResourceHolder.java:293) ~[spring-jms-6.2.8.jar:6.2.8]
at org.springframework.jms.connection.ConnectionFactoryUtils$JmsResourceSynchronization.releaseResource(ConnectionFactoryUtils.java:458) ~[spring-jms-6.2.8.jar:6.2.8]
at org.springframework.jms.connection.ConnectionFactoryUtils$JmsResourceSynchronization.releaseResource(ConnectionFactoryUtils.java:419) ~[spring-jms-6.2.8.jar:6.2.8]
at org.springframework.transaction.support.ResourceHolderSynchronization.beforeCompletion(ResourceHolderSynchronization.java:79) ~[spring-tx-6.2.8.jar:6.2.8]
at org.springframework.transaction.support.TransactionSynchronizationUtils.triggerBeforeCompletion(TransactionSynchronizationUtils.java:138) ~[spring-tx-6.2.8.jar:6.2.8]
at org.springframework.transaction.support.AbstractPlatformTransactionManager.triggerBeforeCompletion(AbstractPlatformTransactionManager.java:996) ~[spring-tx-6.2.8.jar:6.2.8]
at org.springframework.transaction.support.AbstractPlatformTransactionManager.processRollback(AbstractPlatformTransactionManager.java:881) ~[spring-tx-6.2.8.jar:6.2.8]
at org.springframework.transaction.support.AbstractPlatformTransactionManager.rollback(AbstractPlatformTransactionManager.java:866) ~[spring-tx-6.2.8.jar:6.2.8]
at org.frankframework.jta.SpringTxManagerProxy.rollback(SpringTxManagerProxy.java:80) ~[frankframework-core-9.2.0-SNAPSHOT.jar:9.2.0-SNAPSHOT]
at org.springframework.jms.listener.AbstractPollingMessageListenerContainer.rollbackOnException(AbstractPollingMessageListenerContainer.java:421) ~[spring-jms-6.2.8.jar:6.2.8]
at org.springframework.jms.listener.AbstractPollingMessageListenerContainer.receiveAndExecute(AbstractPollingMessageListenerContainer.java:249) ~[spring-jms-6.2.8.jar:6.2.8]
at org.frankframework.jms.IbisMessageListenerContainer.receiveAndExecute(IbisMessageListenerContainer.java:112) ~[frankframework-messaging-9.2.0-SNAPSHOT.jar:9.2.0-SNAPSHOT]
at org.springframework.jms.listener.DefaultMessageListenerContainer$AsyncMessageListenerInvoker.invokeListener(DefaultMessageListenerContainer.java:1420) ~[spring-jms-6.2.8.jar:6.2.8]
at org.springframework.jms.listener.DefaultMessageListenerContainer$AsyncMessageListenerInvoker.run(DefaultMessageListenerContainer.java:1294) ~[spring-jms-6.2.8.jar:6.2.8]
at org.frankframework.threading.ThreadNamingTaskDecorator.lambda$0(ThreadNamingTaskDecorator.java:36) ~[frankframework-core-9.2.0-SNAPSHOT.jar:9.2.0-SNAPSHOT]
at java.base/java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1144) ~[?:?]
at java.base/java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:642) ~[?:?]
at java.base/java.lang.Thread.run(Thread.java:1583) [?:?]
This results in 2 sessions being leaked.
I've experimented with a simple try/catch around the `transactionHelper.registerSynchronization` methods, which closes the session directly (in case there is an exception) to mitigate this issue. Not sure how you wish to tackle this issue but one way or another the session and connection needs to be closed.