Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Pass request scope data to async methods in CDI

Java EE 7 application is running on Wildfly 9.0.2.Final. There is a problem to access request scoped data from within @Asynchronous methods.

In web filter data (e.g. token) is set into RequestScoped CDI bean. Later we want to access this data. Everything works fine if we work in one thread. But if there is need to run code asynchronously the problem appears. CDI injects empty bean and request data is lost.

Here is the example:

@RequestScoped
public class CurrentUserService implements Serializable {
  public String token;
}

@Stateless
public class Service {
   @Inject
   private RestClient client;

    @Resource
    private ManagedExecutorService executorService;

    @Resource
    private ContextService contextService;

    @Asynchronous
    private <T> Future<T> getFuture(Supplier<T> supplier) {
        Callable<T> task = supplier::get;
        Callable<T> callable = contextService.createContextualProxy(task, Callable.class);
        return executorService.submit(callable);
    }

   public String getToken() throws Exception {
      return getFuture(client::getToken).get();
   }
}

@ApplicationScoped
public class RestClient {
    @Inject
    private CurrentUserService currentUserBean;

    public String getToken() {
        return currentUserBean.token;
    }
}

In the given example we want to access current user token (CurrentUserService#token) from asynchronous Service.getToken method. As the result we will receive null.

It's expected that 'request scoped' data should be accessible from tasks executed within request scope. Something like InheritableThreadLocal should be used to allow assess to original thread data from new threads.

Is it a bug? May be I'm doing something wrong? If yes - what it the correct way to propagate such data into async calls?

Thanks in advance.

like image 280
Iaroslav Savytskyi Avatar asked Dec 22 '15 14:12

Iaroslav Savytskyi


1 Answers

According to §2.3.2.1 of the Java EE Concurrency Utilities specification, you should not attempt to do this:

  • Tasks that are submitted to a managed instance of ExecutorService may still be running after the lifecycle of the submitting component. Therefore, CDI beans with a scope of @RequestScoped, @SessionScoped, or @ConversationScoped are not recommended to use as tasks as it cannot be guaranteed that the tasks will complete before the CDI context is destroyed.

You need to collect your request scoped data and pass it to your asynchronous task when you create it, whether you use concurrency utilities or @Asynchronous methods.

like image 76
Steve C Avatar answered Oct 04 '22 09:10

Steve C