Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Request-scoped ApplicationEventListener fails to receive events

I have the need to register a separate application event listener for each request. The listener's purpose is to catch events coming in from other REST requests, while the listener's request is blocked awaiting all the required events to come in.

I have code such as this:

@Component
// @Scope(WebApplicationContext.SCOPE_REQUEST)
public static class WhistleEventListener implements ApplicationListener<WhistleEvent> {
  volatile Consumer<WhistleEvent> handler;
  @Override
  public void onApplicationEvent(WhistleEvent we) {
    final Consumer<WhistleEvent> h = handler;
    if (h != null) h.accept(we);
  }
}
@Autowired WhistleEventListener whistleEventListener;

This code receives events, but as soon as I uncomment the @Scope annotation, it stops receiving events.

Are request-scoped application event listeners supported, are they supposed to work? If so, can I do something to make my listener work?

like image 457
Marko Topolnik Avatar asked Nov 01 '22 20:11

Marko Topolnik


1 Answers

I suspect you have a misunderstanding of the application event dispatching mechanics: the event is dispatched against bean definitions, not bean instances, and each bean definition is resolved into an instance at the moment, and in the context, of event publication. That means that your event will be dispatched only to the request-scoped bean belonging to the request inside which the event is published, but you want the listeners of all current requests to be notified.

More generally, the purpose of a scope is to isolate scope instances, which contain separate bean instances. If you do not want isolation, you should use a scope that does not have separate instances, for instance the application scope.

That is, to dispatch events to other scope instances, you'd have to do the dispatching yourself, for instance like:

@Component
public class WhistleEventMediator implements ApplicationListener<WhistleEvent> {
    // TODO: make thread safe
    final Set<Consumer<WhistleEvent>> consumers; 

    void subscribe(Consumer<WhistleEvent> c) { ... }

    void unsubscribe(Consumer<WhistleEvent> c) { ... }

    @Override public void onApplicationEvent(WhistleEvent we) {
        // delegate to subscribed consumers
    }
}

@Component 
@Scope(WebApplicationContext.SCOPE_REQUEST)
public class WhateverBean implements Consumer<WhistleEvent> {
    @Inject
    WhistleEventMediator mediator;

    @PostConstruct
    void init() {
        mediator.subscribe(this);
    }

    @PreDestroy
    void destroy() {
        mediator.unsubscribe(this);
    }

    // handle whistle event
}
like image 165
meriton Avatar answered Nov 12 '22 17:11

meriton