ofer baranes opened SPR-16270 and commented

On the following scenario, an attempt to send message to a TOPIC using JmsTemplate is not effective (the message is never sent and there is no error): 1. Open Transaction using spring framework 2. Register TransactionSynchronization to the active Transaction 3. On the TransactionSynchronization, implement the 'afterCommit' as below:

  • create JmsTemplate and set it to be 'transacted'
  • send a message to a topic

  • commit the Transaction

The expectation is that the message would be send and the subscriber would handle it. On real, the message is never handled by the subscriber. The reason is that the message wasn't truly send.

Other observations: The expectation is fulfilled and the subscriber gets the message if: a) Using 'afterCompletion' instead of 'afterCommit', b) Using none transacted JmsTemplate (on the 'afterCommit')

It appears that Spring determines the existence of an ongoing transaction by checking state

_TransactionSynchronizationManager public static boolean isSynchronizationActive() { return synchronizations.get() != null; } _

Since the 'synchronizations' thread local is not cleared when executing the 'afterCommit' callback, Spring mistakenly assumes that there is ongoing transaction and hence bounds the JMS operation to the ongoing transaction (instead of commiting it immediately) and since there is no ongoing transaction the JMS operation would never be committed. (When using 'afterCompletion' it works since the 'synchronizations' thread local was cleared)


Affects: 4.3.11

Issue Links: - #16214 TransactionSynchronizationManager - throw an Exception or log a warning if a Synchronization wants to add a Synchronization and afterCompletion is already called

Comment From: spring-projects-issues

Juergen Hoeller commented

While I agree that this behavior is unintuitive, it is actually documented in the TransactionSynchronization javadoc, explicitly stating that this callback allows for late participation in the transaction... not least of it all for post-commit cleanup steps where the original resources need to be active still. Our recommendation certainly is not to use a transactional JMS setup from that callback, or it really needs to be, to do so within a managed PROPAGATION_REQUIRES_NEW transaction.

That said, we should be doing something to make this more obvious: maybe logging a warning for newly created resources at that point, or even outright rejection of new resource creation after the managed transaction commit.

Comment From: jhoeller

We are initiating a late local JMS transaction commit in JmsResourceSynchronization.afterCompletion if necessary now, tracking whether our local afterCommit has been bypassed due to late registration and making up for it in afterCompletion then. This should hopefully lead to the expected outcome even in such a semantically questionable scenario,