Description
org.apache.activemq.artemis.ra.inflow.ActiveMQMessageHandler.onMessage calls to endpoint.beforeDelivery(ActiveMQActivation.ONMESSAGE) (i.e. Spring framework) to begin a Transaction (associating it to the Thread) and enlist the XAResource to that Transaction. These operations may fail independently:
// AbstractMessageEndpointFactory.java public void beginTransaction() throws Exception { if (transactionFactory != null && this.xaResource != null) { // This line works, associating the tx to the Thread, because that's a local operation. this.transaction = transactionFactory.createTransaction(transactionName, transactionTimeout); // This line fails, because it's a remote call to a dead MQ server this.transaction.enlistResource(this.xaResource); } }
# example trace (wildfly application server, artemis 1.5.5) ERROR [org.apache.activemq.artemis.ra] (Thread-40 (ActiveMQ-client-global-threads)) AMQ154004: Failed to deliver message: javax.resource.spi.ApplicationServerInternalException: Failed to begin transaction at org.springframework.jca.endpoint.AbstractMessageEndpointFactory$AbstractMessageEndpoint.beforeDelivery(AbstractMessageEndpointFactory.java:221) at org.apache.activemq.artemis.ra.inflow.ActiveMQMessageHandler.onMessage(ActiveMQMessageHandler.java:293) at org.apache.activemq.artemis.core.client.impl.ClientConsumerImpl.callOnMessage(ClientConsumerImpl.java:1001) at org.apache.activemq.artemis.core.client.impl.ClientConsumerImpl.access$400(ClientConsumerImpl.java:49) at org.apache.activemq.artemis.core.client.impl.ClientConsumerImpl$Runner.run(ClientConsumerImpl.java:1124) at org.apache.activemq.artemis.utils.OrderedExecutorFactory$OrderedExecutor$ExecutorTask.run(OrderedExecutorFactory.java:122) at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1142) at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:617) at java.lang.Thread.run(Thread.java:745) Caused by: javax.transaction.SystemException: WFTXN0089: Failed to configure transaction timeout of 299 at org.wildfly.transaction.client.LocalTransaction.enlistResource(LocalTransaction.java:155) at org.springframework.transaction.jta.ManagedTransactionAdapter.enlistResource(ManagedTransactionAdapter.java:83) at org.springframework.jca.endpoint.AbstractMessageEndpointFactory$TransactionDelegate.beginTransaction(AbstractMessageEndpointFactory.java:313) at org.springframework.jca.endpoint.AbstractMessageEndpointFactory$AbstractMessageEndpoint.beforeDelivery(AbstractMessageEndpointFactory.java:218) ... 8 more
In such case, it is necessary that ActiveMQMessageHandler ensure termination of the transaction before returning control. This can be accomplished by e.g.
try { endpoint.beforeDelivery(ActiveMQActivation.ONMESSAGE); } catch(ApplicationServerInternalException asie) { Throwable cause = asie.getCause(); if(cause instanceof SystemException) { if(tm != null && tm.getStatus() != Status.STATUS_NO_TRANSACTION) { ActiveMQRALogger.LOGGER.trace("HornetQMessageHandler::tx begin failed uncleanly with "+cause.getMessage()+". Trying to clean up."); tm.setRollbackOnly(); endpoint.afterDelivery(); } } throw asie; } |
Failure to include this cleanup step results in the broken Transaction remaining associated to the Thread, which thereafter cannot be reused in a pooled environment.