Back to Blog Archive

Manually binding a resource to a transaction in Mule

Posted on: May 19, 2020
Author:
Jeffrey

Introduction

Recently I ran into a use case where a multi-resource distributed transaction was necessary to make sure a database call and a WMQ call were grouped and executed as one single virtual transaction. As we know, Mule is capable of handling such scenarios easily. Simply drop a Database Connector and a WMQ outbound endpoint into a transactional scope and problem solved!

However, there was a catch to this particular business case where the use of the Database Connector was not allowed and instead, all database queries needed to be achieved via an external library called jOOQ. Nothing against jOOQ (or similar libraries) here, in fact, jOOQ is brilliant and comes with its own advantages, read more about it here. Still, this raised a red flag as we noticed that it would not play favourably with Mule’s pluggability when it comes to managing a transaction over different resources and providing rollback functionality in case of errors.

Scenario

Let us assume a simplistic payload and flow execution as follows:

  1. Receive a request with a JSON body containing a person’s information.
  2. Initiate a transaction that unifies the following:
    • Insert person data into a database.
    • Put the JSON payload onto the WMQ queue.
  3. Success message returned back.

Any issues occurring during the second step, i.e. during the transaction, should cause a rollback and result in no message being placed on the queue or any tables modified. Basically the Mule flow will look as follows:

But for our particular use case, the “Insert Person into DB” Database Connector needs to be substituted by a Custom Java Processor that will invoke the DB query using our friend jOOQ.

For the purpose of this blog, the use of jOOQ will be kept very simple. So, fast-forward through some jOOQ tutorials and our Mule flow now looks… well… exactly the same except there is a <custom-processor> instead of our beloved <db:insert>.

Let us quickly analyse the custom processor:

  1. We @Autowire the data source defined in the Mule configuration.
  2. We retrieve the “PersonInfo” flow variable from the Mule event.
  3. We acquire a connection from the DataSource connection pool.
  4. We connect and perform an insert on the database via jOOQ.

Excuse my crude use of jOOQ, but it has been kept simple enough for demonstration purposes. The flow works as expected with the database table being updated and the payload sent on the queue as well.

Perfect! All is good? No, all is not good, and here is why. The transactional scope is now unaware of the data source being used. Previously the Database Connector was generous enough to recognise its Mule relative, the transactional scope, and let it know about the data source, thus binding connections from a said data source to the transaction. With our custom code, we are bypassing this relationship. In other words, the transaction only covers WMQ and if the need for a rollback arises, the database will not be considered as part of the transaction.

You can quickly test this out and see for yourself, just add a scripting component at the end of the transactional scope that purposefully throws an exception.

This would cause a rollback of the transaction but you would notice that while the payload is not on the queue, the DB insert was still committed resulting in a new entry in our database table, which is not what we want.

Solution

At this point, we could consider ditching Mule’s transactional scope and implement our own transaction manager but this comes with a fair amount of complexity which can be avoided when Mule already provides the module capable of handling multi-resource transactions.

Thus the alternative is to somehow get hold of the transaction instance created by the Mule scope and bind the data source ourselves. We know that the “MuleEventContext” holds the transaction we are after. So if we change our custom message processor to implement Mule’s org.mule.api.lifecycle.callable interface we could get hold of the “MuleEventContext” and the current transaction instance via the onCall() method implementation:

However, if we investigate further and look at Mule’s “DefaultMuleEventContext” implementation of getTransaction() we simply find the following:

Great, we do not need to change our custom processor to implement the Callable interface. We can simply use the return code snippet above to access the current transaction.

Let us add this to the message processor, temporarily place it on a flow variable and add a breakpoint. Let us also add a logger with a breakpoint after the WMQ endpoint and run the flow in debug mode. A quick look with the debugger will further show what is currently happening.

 

We can see that at the beginning of the transactional scope the current transaction is a “SingleResourceCollectionTransaction” (since we have specified a multi-resource transaction type) that has an empty list as its txCollection. Now let us observe how this changes at the logger breakpoint after WMQ.

You can see now the txCollection has an element, specifically a “JmsTransaction” object that is bound to the current transaction. This is courtesy of out of the box Mule components working in harmony. We need to get to a point where “txCollection” above has another element representing our database data source, effectively binding it to the current transaction along with the WMQ resource. This can be achieved by invoking the method call bindResource() on the transaction object.

For this, we need the data source instance and a connection from the data source connection pool to bind to the current transaction. Let us introduce this line of code and debug again.

This has now bound our database connection to the current transaction.

Debugging further clearly shows this as illustrated above. The “SingleResourceCollectionTransaction” now contains two elements in its “txCollection”, a “JmsTransaction” as seen previously and a “JdbcTransacation” resulting from the resource binding that we have just performed.

Mule’s transaction will then be responsible for committing each resource bound to the transaction and perform rollback in case of errors. In fact, if we try to force an error now you will see that both the WMQ and Database call will be rolled back, confirming that we have successfully bound the data source manually to an existing transaction.

Conclusion

This brings us to the end of this article. Above we have described how to manually bind a resource (in our case a DB data source) to an existing transaction in Mule. I will now go a step further to clean up the code, make it reusable, safe to use across multiple DB queries and also consider the fact that a data source connection that we can reuse might already be bound within the transactional scope. For this I will split the message processor into two classes as follows:

Do not forget to include a “Spring bean” for the new class above to allow Spring’s @Autowire to work. 

The full XML config is shown below for reference. Thank you for reading!

Author:
Jeffrey

Comments

Contact Us

Ricston Ltd.
Triq G.F. Agius De Soldanis,
Birkirkara, BKR 4850,
Malta
MT: +356 2133 4457
UK: +44 (0)2071935107

Send our experts a message

Need Help?
Ask our Experts!