Java injection inside @Asynchronous bean

徘徊边缘 提交于 2019-12-22 18:34:24

问题


I have 2 beans that use Injection to "pass" UserData info that is extracted from HttpRequest. If I remove @Asynchronous from WorkerBean then its all working and WorkerBean can access UserInfo thats injected down. However if I use @Asynchronous on WorkerBean then injection stops working.

What is the best way to manually create/pass UserInfo into WorkerBean if it has to be asynchronous?

// resource class
@Stateless
class MainRs {
    @Context
    protected HttpServletRequest request;
    @Inject
    protected UserData userData;
    @EJB
    WorkerBean job;

    @Path("/batch/job1")
    public function startJob() {
      // call on worker bean
      job.execute();
    }
}

// user data extracted from HttpRequest
@RequestScoped
@Default
class UserData {
  private HttpServletRequest request;
  private String userId;

  @Inject
  public UserData(HttpServletRequest request) {
    super();
    this.request = request;
    userId = request.getHeader("userId");
  }
  public int getUserId() {
    return userId;
  }
}

@Stateless
@Asynchronous
class WorkerBean {
    private UserData userData;

    // inject userData rom caller bean
    @Inject
    public WorkerBean(UserData userData) {
      super();
      this.userData = userData;
    }

    public function execute() {
      String userId = userData.getUserId();
      // do something
    }
}

回答1:


UserData is RequestScoped and bounded to the http request context, that implies that it is dependent on the current request, and hence on the current thread of execution. @Asynchronous would be implemented, mostly through the use of the server's thread-pool. Additional, CDI does not propagate contexts to another thread, at least for session and request context. In this case, it creates a new request-context, an ejb-invocation request context, which has nothing to do with the http-request-context. In a different thread therefore, you have lost all http session and http request context data. As at current CDI specs, there is no way around it.

My work around involved ditching the @Asynchronous annotation altogether, and using ManagedExecutionService, which based on certain criteria's, and for some data that I require, propagates some contexts data to the thread, through ThreadLocal. Thus:

@Stateless
public class AsynchronouseService {

   @Resource
   private ManagedExecutorService managedExecutorService;

   @EJB
   private AsynchronouseServiceDelegate asynchronousServiceDelegate;

   @Inject
   private ManagedContextData managedContextData;


   public void executeAsync(Runnable runnable) {

   managedExecutorService.submit(() ->  asynchronousServiceDelegate.execute(runnable, managedContextData));

   }

}

@Stateless
public class AsynchronouseServiceDelegate {

   @Inject
   private ManagedContextDataProvider managedContextDataProvider;

   public void execute(Runnable runnable, ManagedContextData managedContextData){

    try {

       managedContextDataProvider.setExecutionContextData(managedContextData)
    runnable.run();

    } finally {
       managedContextDataProvider.clearExecutionContextData();
    }

   }
}

```

@ApplicationScoped
public class ManagedContextDataProvider {

   private static final ThreadLocal<ManagedContextData> managedContextDataContext;

   @Inject
   private Instance<HttpSession> httpSession;

   @Produces
   @Depedent
   public ManagedContextData getManagedContextData() {
     firstNonNull(managedContextDataContext.get(), httpSession.get().getAttribute(context_data_key));
   }

  public void setExecutionContextData(ManagedContextData managedContextData) {
    managedContextDataContext.set(managedContextData);
  }

  public void clearExecutionContextData() {
    managedContextDataContext.remove();
  }

}

Some NOTE about threadlocals in managedexecutorservice. Threads are reused, you must be certain that the context you propagate are removed, otherwise on a different session with different userdata, you will get mixed-up data, and it will be a hard-to-debug scenario.

If you can avoid this scenario, and just pass through the UserData as method parameter, the better.



来源:https://stackoverflow.com/questions/38888007/java-injection-inside-asynchronous-bean

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!