CDI : Portées avec WeldSE

Un article de JTips.
CDI : Portées avec WeldSE
Auteur : Alexis Hassler
Formation(s) sur le sujet :
* Formation CDI
Sewatech.png


Portées CDI

Les portées définies dans CDI sont

  • @RequestScoped : 1 instance rattachée à la requête HTTP
  • @SessionScoped : 1 instance rattachée à la session HTTP
  • @ConversationScoped : portée intermédiaire entre request et session
  • @ApplicationScoped : 1 instance unique pour l'application (ie le classloader)

De plus, il est possible de définir ses propres portées en créant des annotations marquées @ScopeType.


Portées Weld SE

En environnement SE, il n'y a pas de portée Request, Session ou Conversation pour les beans CDI ; ces portées ne sont présentes qu'avec l'API servlet.

Pour certains tests, si on veut se passer d'Arquillian, il peut être pratique de simuler ces portées. Je ne suis évidemment pas le premier à être confronté à ce problème ; ainsi, on peut trouver comment faire avec Weld 1.0. Mais ça ne fonctionne plus avec Weld 1.1, la classe AbstractThreadLocalMapContext a disparu. J'ai donc dû pousser les investigations, ce qui m'a mené au code suivant, qui a l'air de fonctionner avec Weld 1.1.0 CR1 et avec la version finale.

package org.jboss.weld.manager; // required for visibility to BeanManagerImpl#getContexts()

import java.lang.annotation.Annotation;
import java.util.HashMap;
import java.util.Map;

import javax.enterprise.context.ConversationScoped;
import javax.enterprise.context.RequestScoped;
import javax.enterprise.context.SessionScoped;
import javax.enterprise.event.Observes;
import javax.enterprise.inject.spi.AfterDeploymentValidation;
import javax.enterprise.inject.spi.BeanManager;
import javax.enterprise.inject.spi.Extension;

import org.jboss.weld.context.AbstractBoundContext;
import org.jboss.weld.context.bound.MutableBoundRequest;

public class WeldServletScopesSupportForSe implements Extension {

    public void afterDeployment(@Observes AfterDeploymentValidation event, BeanManager beanManager) {
        Map<String, Object> sessionMap = new HashMap<String, Object>();
        activateContext(beanManager, SessionScoped.class, sessionMap);
        
        Map<String, Object> requestMap = new HashMap<String, Object>();
        activateContext(beanManager, RequestScoped.class, requestMap);
        
        activateContext(beanManager, ConversationScoped.class, new MutableBoundRequest(requestMap, sessionMap));
    }

    private < S > void activateContext(BeanManager beanManager, Class<? extends Annotation> cls, S storage) {
        BeanManagerImpl beanManagerImpl = (BeanManagerImpl) beanManager;
        AbstractBoundContext< S > context = (AbstractBoundContext< S >) beanManagerImpl.getContexts().get(cls).get(0);
        
        context.associate(storage);
        context.activate();
    }
}

Évidemment, ce problème ne se pose que pour des tests unitaires standards et est résolu si on utilise Arquillian avec le conteneur Weld-EE.



Quelques autres articles sur CDI
  • WeldSE/Test - Tests de beans CDI avec WeldSE et Arquillian