CDI - Portées avec WeldSE

Cette page a été rédigée il y a fort fort longtemps, et n'a pas tellement été mise à jour.

 

Vous savez, moi je ne crois pas qu'il y ait de bonne ou de mauvaise page. Moi, si je devais résumer mon wiki aujourd'hui avec vous, je dirais que c'est d'abord des rencontres. Des gens qui m'ont tendu la main, peut-être à un moment où je ne pouvais pas, où j'étais seul chez moi. Et c'est assez curieux de se dire que les hasards, les rencontres forgent une destinée... Parce que quand on a le goût de la chose, quand on a le goût de la chose bien faite, le beau geste, parfois on ne trouve pas l'interlocuteur en face je dirais, le miroir qui vous aide à avancer. Alors ça n'est pas mon cas, comme je disais là, puisque moi au contraire, j'ai pu ; et je dis merci au wiki, je lui dis merci, je chante le wiki, je danse le wiki... je ne suis qu'amour ! Et finalement, quand des gens me disent « Mais comment fais-tu pour avoir cette humanité ? », je leur réponds très simplement que c'est ce goût de l'amour, ce goût donc qui m'a poussé aujourd'hui à entreprendre une construction logicielle... mais demain qui sait ? Peut-être simplement à me mettre au service de la communauté, à faire le don, le don de soi.

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.