Using Spring-JMS it is possible to receive messages within an external transaction context via the DefaultMessageListenerContainer.
However the only documented way to write a message is via JmsTemplate.send(…) and I can’t see how this can be coerced to use a given TransactionManager.
Can anyone point me in the right direction?
More info: Ensuring a transaction manager is available (WebSphereUowTransactionManager), using JmsTemplate.write against an Oracle AQjmsFactory.getQueueConnectionFactory(dataSource) results in:
org.springframework.jms.UncategorizedJmsException: Uncategorized exception occured during JMS processing; nested exception is oracle.jms.AQjmsException: could not use local transaction commit in a global transaction
at org.springframework.jms.support.JmsUtils.convertJmsAccessException(JmsUtils.java:316)
at org.springframework.jms.support.JmsAccessor.convertJmsAccessException(JmsAccessor.java:168)
at org.springframework.jms.core.JmsTemplate.execute(JmsTemplate.java:469)
at org.springframework.jms.core.JmsTemplate.send(JmsTemplate.java:534)
Caused by: oracle.jms.AQjmsException: could not use local transaction commit in a global transaction
at oracle.jms.AQjmsSession.commitNoCheck(AQjmsSession.java:1053)
at oracle.jms.AQjmsSession.commit(AQjmsSession.java:1021)
at org.springframework.jms.support.JmsUtils.commitIfNecessary(JmsUtils.java:217)
at org.springframework.jms.core.JmsTemplate.doSend(JmsTemplate.java:573)
at org.springframework.jms.core.JmsTemplate$3.doInJms(JmsTemplate.java:536)
at org.springframework.jms.core.JmsTemplate.execute(JmsTemplate.java:466)
... 24 more
Caused by: java.sql.SQLException: could not use local transaction commit in a global transaction
at oracle.jdbc.driver.PhysicalConnection.disallowGlobalTxnMode(PhysicalConnection.java:6647)
at oracle.jdbc.driver.PhysicalConnection.commit(PhysicalConnection.java:3635)
at oracle.jdbc.driver.PhysicalConnection.commit(PhysicalConnection.java:3680)
at oracle.jdbc.OracleConnectionWrapper.commit(OracleConnectionWrapper.java:133)
at oracle.jms.AQjmsSession.commitNoCheck(AQjmsSession.java:1049)
... 29 more
So whilst I have no reason to doubt the advise below, I am not able to test it as I can’t figure out how to get AQ JMS to not attempt a commit. Will update as I learn more.
My understanding is that JMS producers are inherently transacted via JTA. By sending a message via JMS
MessageProducer, the thread-local JTA transaction is used (if one is present).This is hinting at by the Spring manual (section 21.2.5):
This is also suggested by
JmsAccessor.setSessionTransacted(the superclass ofJmsTemplate) (javadoc):So by start a JTA transaction (i.e. by using Spring’s transaction API with
JtaTransactionManager) and callingJmsTemplate.send(...), you will be sending the message bound to that transaction.