Contact Us 1-800-596-4880

Handling Transactions in JMS

Transactional connections in JMS allow you to execute a series of operations that are actually performed only when the transaction is committed. This has two main scenarios:

  • Publishing a Message as part of a transaction: When performing a publish, the Message will be effectively sent to the destination once the transaction is committed. If for some reason the transaction fails and a rollback occurs, then the Message will never be sent.

  • Consuming a Message as part of a transaction: In case of consuming a Message, either with the listener or consume, the Message will be acknowledged only after the transacion is committed, and instead it will be returned to the destination for redelivery if the transaction is rolledback.

The JMS Connector provides support for executing its operations in a transactional way OOTB using Mule’s transactionalAction configuration.

In order to execute an operation as part of the transaction, we have the following two options.

Processing New Messages With Transactional Session

If you want a JMS Listener to use a transactional session when dispatching the messages, you should set the transactionalAction to 'ALWAYS_BEGIN':

<jms:listener config-ref="JMS_Config" destination="${originQueue}" transactionalAction="ALWAYS_BEGIN"/>

With this configuration, each new message will be processed in a transaction that is propagate to all the flow’s components and is committed once the flow execution is completed successfully. The transaction will be rolledback if the flow execution is completed with an error.

By default, other components in the flow will not join the transaction created by the listener. In order to execute other operations using the listener’s transaction, you must declare them either with 'ALWAYS_JOIN', or 'JOIN_IF_POSSIBLE'.

Execute Operations As Part Of A Transaction

To execute an operation like publish or consume as part of a transaction, we have to set the transactionalAction to 'ALWAYS_JOIN', or 'JOIN_IF_POSSIBLE':

This operations can join a transaction initiated by the listener:

<flow name="joiningToListenerTransaction">
    <jms:listener config-ref="JMS_Config" destination="${originQueue}" transactionalAction="ALWAYS_BEGIN"/>
    <jms:publish config-ref="JMS_Config" destination="#[attributes.properties.userProperties.redirectDestination]" transactionalAction="JOIN_IF_POSSIBLE"/>
    <jms:consume config-ref="JMS_Config" destination="#[attributes.properties.userProperties.callbackDestination]" transactionalAction="JOIN_IF_POSSIBLE"/>
</flow>

Or they can join an scoped transaction:

<flow name="nonTxPublishMustNotJoinCurrentTx">
    <http:listener config-ref="HTTP_Config" path="/orders"/>
    <try transactionalAction="ALWAYS_BEGIN">
        <jms:publish config-ref="config" destination="${billingService}" transactionalAction="ALWAYS_JOIN"/>
        <jms:publish config-ref="config" destination="${shipmentService}" transactionalAction="ALWAYS_JOIN"/>
        <jms:publish-consume config-ref="JMS_Config" destination="${invoicesVerificationService}"/>
        <validation:is-true expression="#[payload]"/>
    </try>
</flow>
View on GitHub