A previous article described some new sample programs showing the use of the MQ Spring Boot starter with transactions. There is a further similar sample now available, using a different transaction manager: Narayana.
If you want to have resources updated reliably, to give consistent results even if there is a system failure, then you need to use transactions. And if you want to have multiple resources be part of the same transaction (say, a messaging system and a database, or multiple messaging systems) then you need a transaction manager to coordinate the updates. In the Java world, this is likely to be something that implements the JTA standard.
IBM MQ can be a particpant in these transactions. With JEE, the MQ Resource Adapter hooks into the right places in the application server and drives the JTA or XA flows. With Spring, you need something else to be the coordinator. And the application has to have the calls that start and complete transactions.
The sample programs
The first Spring samples for MQ that dealt with global transactions (called s4
and s4a
in the repository) used Atomikos as the transaction coordinator. But Spring Boot removed some deprecated APIs and Atomikos has not (yet?) dealt with those changes. It stopped working with Boot version 3.4.0. And despite raising an issue, there doesn’t appear to be any urgency to release a fixed version – at least in the “public” levels available.
What I decided to do was to
- Keep the Atomikos samples but not move the underlying Spring Boot version forward
- Add a similar sample using the Narayana transaction manager. As this has Red Hat backing, it’s also perhaps more appropriate for an IBM solution to be using!
The new sample, s4n
is functionally the same demonstration as the s4
sample. Messages are moved between two queue managers, with the transactions getting either a commit or rollback resolution.
Differences between the TMs
Functionally, both TMs behave the same. As they should. But there were programming differences that I had to deal with. In particular, with Narayana, the application code needed to explicitly enrol in each transaction. Atomikos’s proxy objects that wrap the JMS connection factory seemed to handle this automatically. The Narayana equivalent proxies did not – at least, not without there being a huge performance overhead.
Whan I was trying (and failing) to use the recommended approach for Narayana, while also keeping acceptable performance, someone suggested using the messaginghub.pooled
interfaces. That did not work at all. At first, because the necessary constructors were not visible outside the MQ Spring Boot Starter itself (only the non-XA classes could be accessed). So I have made them public in the latest version of the module.
It still didn’t help with the Narayana performance issue though. Basically, it did not seem to preserve an existing JMSContext and enrol it; the only way I could get the correct transactional behaviour with the proxies was to create a new JMSContext along with the tm.begin()
call. Which turns into an MQCONN
and all that implies for creating sockets, negotiating TLS etc. It was important that the Context (or Session if you prefer the older style of JMS programming) could be created once and reused throughout the application.
What I ended up with was code that bypasses the Narayana proxy classes and directly enrols in the transaction. So the application code for Narayana looks something like:
XAJMSContext ctx1; XAJMSContext ctx2; XAResource xares1; XAResource xares2; TransactionManager tm = com.arjuna.ats.jta.TransactionManager.transactionManager(); xacf1 = context.getBean("qm1p", JmsPoolXAConnectionFactory.class); xacf2 = context.getBean("qm2p", JmsPoolXAConnectionFactory.class); ctx1 = xacf1.createXAContext(); ctx2 = xacf2.createXAContext(); xares1 = ctx1.getXAResource(); xares2 = ctx2.getXAResource(); while (work to do) { tm.begin(); tm.getTransaction().enlistResource(xares1); tm.getTransaction().enlistResource(xares2); ... do some work tm.commit() or tm.rollback(); }
Tracing/Logging
One other thing I found with Narayana was that it seems over-enthusiastic about logging Java exceptions. When I turned on logging for the com.arjuna
classes, exceptions were reported during the rollback operation. I looked into those, and found that the return values coming from the MQ JMS layer were reasonable, and should be expected. But the Narayana package logged them anyway, including at the WARN loglevel. Only when I set the level to ERROR did they disappear.
Conclusion
Making use of public components always has a risk, if that component becomes out-of-date. If Atomikos is ever updated to work with later levels of Spring, I’ll look to refresh those samples. But at least now I’ve shown how to use two different ways of managing global transactions using MQ.
This post was last updated on March 9th, 2025 at 12:20 pm