2016-08-11 4 views
1

J'ai 2 beans qui utilisent Injection pour "passer" les informations UserData qui sont extraites de HttpRequest. Si je supprime @Asynchronous de WorkerBean alors tout fonctionne et WorkerBean peut accéder à UserInfo qui est injecté vers le bas. Toutefois, si j'utilise @Asynchronous sur WorkerBean, l'injection cesse de fonctionner.Injection Java à l'intérieur @Bean asynchrone

Quelle est la meilleure façon de créer/transmettre manuellement UserInfo dans WorkerBean s'il doit être asynchrone?

// 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

Je pense que ce que vous cherchez n'est pas faisable, car WorkerBean sera initialisé au début de l'application tandis que UserData sera initialisé lors de l'obtention de la requête, donc au moment où WorkerBean est créé il n'y a pas UserData. –

+0

Si je peux créer manuellement UserData et le peupler dans MainRs. Est-il possible d'injecter manuellement cette UserData dans WorkerBean? –

+0

L'autre problème que vous avez est que UserData a un 'HttpServletRequest' intégré. Vous n'avez aucune garantie que votre implémentation serveur ne réutilisera pas cette demande pour desservir un autre client. Cela soulève des problèmes de sécurité –

Répondre

0

UserData est RequestScoped et délimitée au contexte de demande de HTTP, ce qui implique qu'il est dépendant de la demande en cours, et donc sur le fil en cours d'exécution. @Asynchronous serait implémenté, principalement grâce à l'utilisation du pool de threads du serveur. De plus, CDI ne propage pas les contextes à un autre thread, au moins pour le contexte de session et de requête. Dans ce cas, il crée un nouveau contexte de requête, un contexte de requête d'invocation ejb, qui n'a rien à voir avec le contexte de requête http. Dans un thread différent donc, vous avez perdu toutes les données de contexte http et http. Comme aux spécifications CDI actuelles, il n'y a pas moyen de contourner cela.

Mon travail impliqué dans amerrissage l'annotation @Asynchronous tout à fait, et en utilisant ManagedExecutionService, qui repose sur certains critères de et pour certaines données que je requiers, certaines données propage contextes au fil, par ThreadLocal. Ainsi:

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

} 

Certains NOTE à propos threadlocals à managedexecutorservice. Les threads sont réutilisés, vous devez être certain que le contexte que vous propagez est enlevé, sinon sur une session différente avec des données utilisateur différentes, obtiendra des données mélangées, et ce sera un scénario difficile à déboguer.

Si vous pouvez éviter ce scénario et passer simplement par le UserData comme paramètre de méthode, c'est le meilleur.

+0

Malheureusement, je dois utiliser @ Asynchronous. Je suis surpris qu'il n'y ait aucun moyen d'insérer/détruire manuellement des objets dans le contexte –