Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Allow programmatic registration of completion and error callbacks to transactional event listeners #24163

Closed
odrotbohm opened this issue Dec 8, 2019 · 1 comment
Assignees
Labels
in: core Issues in core modules (aop, beans, core, context, expression) in: data Issues in data modules (jdbc, orm, oxm, tx) type: enhancement A general enhancement
Milestone

Comments

@odrotbohm
Copy link
Member

odrotbohm commented Dec 8, 2019

Currently, event listeners bound to transaction commits, rollbacks or completion in general can only be used via the @TransactionalEventListener annotation. It would be nice if there was a way to programmatically register those via context.addApplicationListener(…), pointing to a method, configuring the transaction phase to apply the listener in etc.

After revisiting this with @jhoeller, here's the current state and derived ideas for what to improve on: ApplicationListenerMethodTransactionalAdapter is currently package protected and thus prevents programatic access to transactional event listeners as there's no type you can refer to even in instance of checks. We had the idea to introduce a public type that ALMTA would extend / implement that exposes the following API:

  • the transaction phase the listener is bound to
  • a (nullable) identifier whose default is derived from the annotated method's name, potentially overridable via a to be introduced annotation attribute on @TransactionalEventListener or via the to be introduced programatic API
  • a ….registerCompletionCallback(Consumer<ApplicationEvent>) that will be called for every successfully completed invocation of the listener
  • a ….registerErrorCallback(Consumer<Throwable>) that will be called in case the listener threw an exception. If that callback is registered the Throwable would not be propagated, so that TransactionSynchronizationUtils.invokeAfterCompletion(…) would not actually see the Throwable and avoid the logging.
  • if the type to be introduced would become an interface, it would be nice if it also exposed ApplicationListenerMethodAdapter.processEvent(…) to make sure the listener instance can be invoked explicitly. This is needed in the context of transactional even listeners as the already externally accessible onApplicationEvent(…) implements the event publication by registering a transaction synchronization, which does not immediately execute the listener.

Most of this is driven by a prototypical event publication log implementation that makes sure that events handled by transactional event listeners do not get lost in case of exceptions occurring during the handling or unanticipated application shutdowns during that work. That work can be found here. The implementation currently relies on deep reflection inspection of framework internals and AOP usage to implement the log and log entry completion. That implementation would become significantly simpler.

Another aspect that makes this worthwhile is the general ability to use event listener registrations programmatically to avoid reflection and annotation detection overhead which in turn aligns with our functional bean registration API efforts.

@odrotbohm odrotbohm added in: core Issues in core modules (aop, beans, core, context, expression) type: enhancement A general enhancement labels Dec 8, 2019
@jhoeller jhoeller changed the title Improve programatic usability of transactional event listeners Programmatic processing of transactional event listeners Dec 9, 2019
@jhoeller jhoeller added this to the 5.3 M1 milestone Dec 9, 2019
@odrotbohm odrotbohm assigned odrotbohm and unassigned jhoeller May 14, 2020
@jhoeller jhoeller modified the milestones: 5.3 M1, 5.3 M2 Jun 18, 2020
@jhoeller jhoeller modified the milestones: 5.3 M2, 5.3 RC1 Aug 8, 2020
@jhoeller jhoeller modified the milestones: 5.3 RC1, 5.3 RC2 Aug 25, 2020
odrotbohm added a commit to odrotbohm/spring-framework that referenced this issue Sep 16, 2020
Introduce TransactionalEventListenerMetadata that exposes metadata about ApplicationListeners derived from @TransactionalEventListener. That metadata allows registering completion callbacks and error handlers that will be triggered depending on the outcome of the invocation of the transactional event listener.

Open questions:

- Shall we use @TEL.value for the identifier instead of @TEL.id? @el has value already bound to event types to match.
- Shall standard @EventListeners get identifiers as well?

Original ticket: spring-projects#24163
@odrotbohm odrotbohm changed the title Programmatic processing of transactional event listeners Allow programmatic registration of completion and error callbacks to transactional event listeners Oct 1, 2020
@jhoeller jhoeller modified the milestones: 5.3 RC2, 5.x Backlog, 5.3 GA Oct 13, 2020
@jhoeller jhoeller assigned jhoeller and unassigned odrotbohm Oct 19, 2020
@jhoeller jhoeller added the in: data Issues in data modules (jdbc, orm, oxm, tx) label Oct 19, 2020
@jhoeller
Copy link
Contributor

jhoeller commented Oct 19, 2020

I've got a complete cut ready now which I intend to commit ASAP. This is based on a TransactionalApplicationListener interface implemented by both the annotation-based adapter and a new programmatic variant. As a common convenience, I've also introduced forPayload factory method on the TransactionalApplicationListener interface as well as on ApplicationListener itself, accepting a Consumer of the event payload type.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
in: core Issues in core modules (aop, beans, core, context, expression) in: data Issues in data modules (jdbc, orm, oxm, tx) type: enhancement A general enhancement
Projects
None yet
Development

No branches or pull requests

2 participants