2016-08-11 7 views
1

HttpRequest에서 추출 된 UserData 정보를 "전달"하기 위해 Injection을 사용하는 2 개의 Bean이 있습니다. WorkerBean에서 @Asynchronous을 제거하면 모든 작업 및 WorkerBean이 아래로 주입 된 UserInfo에 액세스 할 수 있습니다. 그러나 WorkerBean에서 @Asynchronous을 사용하면 주입이 중단됩니다.@Asynchronous bean 내부에 Java injection

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이 응용 프로그램의 시작에서 초기화되므로 WorkerBean이 생성 될 때 UserData가 없으므로 사용자가 찾고있는 것이 불가능하다고 생각합니다. –

+0

UserData를 수동으로 생성하여 MainRs에 채울 수 있다면. 이 UserData를 WorkerBean에 수동으로 삽입하는 방법이 있습니까? –

+0

또 다른 문제는 UserData에'HttpServletRequest'가 포함되어 있다는 것입니다. 서버 구현이 다른 클라이언트를 서비스하기 위해 해당 요청을 재사용하지 않는다고 보장 할 수는 없습니다. 그게 보안 문제를 야기합니다. –

답변

0

UserData를 RequestScoped은 그것이 현재의 요구에 의존한다는 것을 의미 HTTP 요청 컨텍스트에 묶여, 따라서 현재 실행 스레드이다. @Asynchronous은 주로 서버의 스레드 풀을 사용하여 구현됩니다. 추가적으로, CDI는 적어도 세션과 요청 문맥을 위해 다른 스레드로 컨텍스트를 전달하지 않습니다. 이 경우, http-request-context와는 아무런 관련이없는 ejb-invocation request context 인 새로운 요청 문맥을 생성한다. 따라서 다른 스레드에서 모든 http 세션 및 http 요청 컨텍스트 데이터가 손실되었습니다. 현재의 CDI 사양에서 볼 수 있듯이, 그 주위에는 방법이 없습니다.

내 해결책은 전부 @Asynchronous 주석을 죽겠다, 특정 기준의 기반으로 ManagedExecutionService을 사용하여 참여하고, 내가 필요로하는 일부 데이터, ThreadLocal를 통해 스레드에 어떤 컨텍스트 데이터를 전달합니다. 따라서 : managedexecutorservice에서 ThreadLocals을 약

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

} 

일부 참고. 스레드가 재사용되는 경우 전파하는 컨텍스트가 이고 그렇지 않으면 다른 사용자 데이터가있는 다른 세션에서 사용자가 이 혼합 데이터를 얻게되고 디버그하기 어려운 시나리오가됩니다.

이 시나리오를 피하고 메서드 매개 변수로 UserData을 전달하면 더 좋습니다.

+0

불행히도 @Asynchronous를 사용해야합니다. Context에 수동으로 개체를 삽입/파괴 할 방법이 없다는 것에 놀랐습니다. –