CDI - Portées avec WeldSE


Portées CDI

Les portées définies dans CDI sont

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.