Home > Back-end >  Keep @RequestScoped context when receiving an async CDI event
Keep @RequestScoped context when receiving an async CDI event

Time:10-13

I want to switch from firing CDI beans synchronous to asynchronous to be able to work stuff parallel.

event.fire(myObject) -> event.fireAsync(myObject)

As I currently use the request context to know what tenant the current process is about, I am confronted with the problem, that the @RequestScoped context is lost in a @ObservesAsync method. Therefor I don't know anymore to what db to persist etc. I could provide the necessary information in the cdi event object and recreate the requestcontext manually after recieving, but this would bloat my object and clutter my code.

Is there a way to simply keep the request context for a async cdi event?

CodePudding user response:

Request scoped objects are not required to be thread-safe and usually are not. For that reason, request context is never automatically propagated across threads. For asynchronous events, indeed you should put all the necessary data into the event object.

You are of course not the first person to ask about this. There's been attempts to define an API/SPI for context propagation (MicroProfile Context Propagation, Jakarta Concurrency), including CDI request context, but they only work correctly in case of sequential processing with thread jumps (common in non-blocking/reactive programming). If you try to [ab]use context propagation for concurrent processing, you're signing up for troubles. For the latest discussion about this, see https://github.com/jakartaee/cdi/issues/474

CodePudding user response:

I actually switched to using interfaces. This gives me more control and makes the code more understandable:

abstract class Publisher<T>{

    @All
    @Inject
    private List<EventConsumer<T>> eventConsumers;

    @Inject
    private ContextInfo contextInfo;

    @Inject
    private MutableContextInfo mutableContextInfo;

    ...

    public void publishEvent(T event){
        String myContextInfo= contextInfo.getMyContextInfo();
        eventConsumers.forEach(consumer -> notifyAsync(consumer, receivedObject, myContextInfo))
    }

    private void notifyAsync(EventConsumer<T> consumer, T object, String myContextInfo) {
        Uni.createFrom()
                .voidItem()
                .subscribeAsCompletionStage()
                .thenAccept(voidItem -> notifyConsumer(consumer, object, myContextInfo));
    }

    /**
     * Method needs to be public to be able to activate request context on self invocation
     */
    @ActivateRequestContext
    public void notifyConsumer(EventConsumer<T> consumer, T object, String myContextInfo) {
        mutableContextInfo.setMyContextInfo(myContextInfo);
        try {
            consumer.onEvent(object);
        } catch (RuntimeException ex) {
            log.error("Error while promoting object to eventconsumer", ex);
        }
    }
}
  • Related