6

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.

yaroska
  • 375
  • 2
  • 5
  • Besides, you have a RequestScoped bean inside an ApplicationScoped. You might want to use `@Inject Instance currentUserBean` instead. – jpkroehling Dec 22 '15 at 15:07
  • @Gimby, you are right. It's possible that new threads will live longer than original one. As you see I've wrapped the call with ContextService proxy. I expected that proxy will provide needed context info. Anyway, how to solve the problem? I need to access request data from new thread. Can I somehow pass it? – yaroska Dec 22 '15 at 15:37
  • @jpkrohling, thanks. I've tried but with no luck :( – yaroska Dec 22 '15 at 15:41
  • Your `Service.getToken()` method is fundamentally synchronous. It calls `get` on a future, which will block until the future has been fulfilled. So all that async machinery you have there will not accomplish anything anyway. – Steve C Dec 23 '15 at 04:43
  • @Steve C, this is only an example. – yaroska Dec 23 '15 at 10:53

1 Answers1

4

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.

Steve C
  • 18,876
  • 5
  • 34
  • 37
  • 1
    thanks for reply. In my case it's guaranteed that the tasks will complete before request scope will be destroyed. Looks like that this is a bug in Wildfly. I'll try to fire bugreport. Thanks for help. – yaroska Dec 25 '15 at 22:59
  • It is [under discussion](https://issues.jboss.org/browse/CDI-452) to be supported in CDI 2.1. – Steven Dec 14 '18 at 10:23