2016-08-11 6 views
1

У меня есть 2 компонента, которые используют Injection для передачи данных UserData, которая извлекается из HttpRequest. Если я удалю @Asynchronous из WorkerBean, тогда все его рабочие и WorkerBean могут получить доступ к UserInfo, которые были введены вниз. Однако, если я использую @Asynchronous на WorkerBean, тогда инъекция перестает работать.Java-инъекция внутри @Asynchronous bean

Каков наилучший способ вручную создать/передать UserInfo в WorkerBean, если он должен быть асинхронным?

// 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 
    } 
} 
+0

Я думаю, что то, что вы ищете, не выполнимо, так как WorkerBean будет инициализирован в начале приложения, в то время как UserData будет инициализирован при получении запроса, поэтому в то время, когда WorkerBean создан, UserData не существует. –

+0

Если я могу вручную создать UserData и заполнить его в MainRs. Есть ли способ вручную ввести эту UserData в WorkerBean? –

+0

Другая проблема, с которой вы сталкиваетесь, заключается в том, что UserData имеет встроенный в нее HttpServletRequest. У вас нет гарантии, что реализация вашего сервера не будет повторно использовать этот запрос для обслуживания другого клиента. Это вызывает проблемы с безопасностью. –

ответ

0

UserData является RequestScoped и ограничена в контексте запроса HTTP, который подразумевает, что она зависит от текущего запроса, и, следовательно, на текущем потоке выполнения. @Asynchronous будет реализован, главным образом, с использованием пула потоков сервера. Дополнительно CDI не распространяет контексты на другой поток, по крайней мере, для контекста сеанса и запроса. В этом случае он создает новый запрос-контекст, контекст запроса ejb-invocation, который не имеет ничего общего с контекстом http-request-context. Поэтому в другом потоке вы потеряли все контекстные данные http и http-запроса. Как и в случае с текущими спецификациями CDI, нет никакого способа обойти это.

Моей работа вокруг участия канав @Asynchronous аннотации вообще, и используя ManagedExecutionService, который на основе определенных критериев х, и по некоторым данным, которые я требую, распространяется некоторыми данные контекстов к потоку, через ThreadLocal. Таким образом:

@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(); 
    } 

} 

Некоторые Примечание о threadlocals в managedexecutorservice. Темы повторно используются, вы должны быть уверены, что распространяемый вами контекст удаляется , в противном случае на другом сеансе с разными пользовательскими данными вы получите , и это будет сложный для отладки сценарий.

Если вы можете избежать этого сценария и просто пройти через параметр метода UserData, тем лучше.

+0

К сожалению, мне приходится использовать @Asynchronous. Я удивлен, что нет возможности вручную вставлять/уничтожать объекты в Контекст –