Status: Open (View Workflow)
Affects Version/s: 2.3.5.Final
Fix Version/s: None
Steps to Reproduce:
Create a transactional observer for event A Fire event A from a JPA post persist lifecycle listener for entity B Register an instance of B within the persistence context within a transactional method Invoke the method, so that the persistence context is flushed and B is persisted with committing the transaction Observe that the AFTER_SUCCESS observer is not invoked, but the AFTER_FAILURE observer is, but that the transaction did COMMIT and not ROLLBACK.
- Create a transactional observer for event A
- Fire event A from a JPA post persist lifecycle listener for entity B
- Register an instance of B within the persistence context within a transactional method
- Invoke the method, so that the persistence context is flushed and B is persisted with committing the transaction
- Observe that the AFTER_SUCCESS observer is not invoked, but the AFTER_FAILURE observer is, but that the transaction did COMMIT and not ROLLBACK.
The work around is to simply prevent that firing the event is delayed until the commit phase, in my case it probably suffices to manually flush the entity manager prior to the transaction is being committed (through the TransactionInterceptor).
When firing an CDI event from another transaction synchronisation, the TransactionalObserverNotifier fails to defer the notification, because registering the TransactionSynchronizedRunnable fails with an "ARJUNA016082: Synchronizations are not allowed".
In my case this for example happens when I fire the event from a JPA Lifecycle listener, which is triggered by the flush call on the EntityManager through Hibernates JtaTransactionCoordinatorImpl bound in the JCAOrderedLastSynchronizationList.
The current behaviour has one very odd side effect: namely, the exception is swallowed in the TransactionalObserverNotifier, which then notifies a filtered set of observers, assuming that a rollback exception must have occurred. As a result, the AFTER_FAILURE observers are invoked and not the AFTER_SUCCESS observers, even though the transaction commits successfully (because the IllegalStateException was caused by not being able to register the synchronisation, and not due to the fact that the transaction failed altogether).
I think the error handling should be fixed, because it seems odd that AFTER_FAILURE observers get invoked for a successful transaction.
Furthermore it would be nice to actually support events with transactional observers to be fired from another synchronisation. I am not entirely sure whether it is feasible to support, but I am having a couple of ideas:
- an implementation where we just register a synchronisation by default and handle the synchronisation from there, rather than registering the synchronisation on the transaction just in time
- Hook on the JCAOrderedLastSynchronizationList instead of the Transaction, just as Hibernate seems to do
- Register an interposed synchronisation with the TransactionSynchronizationRegistry rather than a regular synchronisation.
Some more background (a stack trace) can be found in the Stackoverflow issue: https://stackoverflow.com/questions/47953827/defer-transactionalobservernotifier-fails-with-arjuna016082-synchronizations-ar
Also of interest can be the blogpost describing how to fire CDI events from JPA lifecycle listeners: http://blogs.bytecode.com.au/glen/2014/01/09/firing-cdi-events-from-a-jpa-entitylistener.html . Keep in mind that this has become much more natural to do since CDI injection is since allowed into EntityListeners as of JPA 2.1.