Join GitHub today
GitHub is home to over 40 million developers working together to host and review code, manage projects, and build software together.Sign up
spring-jms 3.0.4 introduces a change that breaks using JMSTemplate (and for that matter any JMS resource) with Oracle AQ when JMS Session are used in SESSION_TRANSACTED mode. [SPR-10829] #15455
Actually the problem just surfaced after the IllegalStateException from JMSResourceHolder.commitAll was thrown out instead of being swallowed. (See the linked ticket for details)
In my use case we are using OracleAQ with JMS libraries, (high level operations done using spring-jms).
Now, it is a requirement for us to ensure that the DML and the message production are atomic, hence they are made to use the same underlying oracle connection (read, using spring-data-oracle convenience xml namespace).
To achieve this we have to use a transacted connection factory, and the JMS template that is used to push messages has to be made "sessionTransacted", this is done to avoid the JMSTemplate.send to commit the messages before the enclosing spring transaction commits. (In summary, JMSTemplate.send eventually reaches, session.commit or something alike that commits the underlying jdbc connection, thus, committing the normal DML operation as well).
This is a typical use case. And everything works fine with spring-jms 3.0.2 ( and also with 3.0.3), and it works "by accident".
In spring-jms 3.0.4 as the linked ticket provides details, this behavior was fixed, which broke things for Oracle AQ.
I agree that swallowing that exception is not the proper fix here, but there is no way to instruct the JMS transaction framework to "not synchronize" JMS resources in case of Oracle AQ. The decision to synchronize JMS resources is done based on the sessionTransacted flag (this is for JMSTemplate, but the logic is more or less similar everywhere else), which unfortunately has to be set true to avoid committing by JMS framework.
I believe sessionTransacted and transaction synchronization should be dealt separately. The reason I believe this logic exists is because unlike JDBC resources, there could be more than one JMS resource bound to a single transaction, so at the end all of these resources have to be synchronize with the transaction. But there may be cases where we don't want this behavior, as in my case.
Juergen Hoeller commented
After considering the various options, I went with a built-in approach. In case of an IllegalStateException on commit, we're checking for the existence of a "getDataSource()" method on the ConnectionFactory now: If found, we'll obtain the associated DataSource and check for its participation in the current Spring transaction - in which case we'll silently swallow the JMS commit exception. This should work fine on Oracle AQ, since AQjmsConnectionFactory does have a corresponding "getDataSource()" method, and is an easy enough pattern to comply with for other JDBC-based ConnectionFactory references as well.