Il arrive parfois qu'un traitement doive être exécuté à l'initialisation d'un SessionBean. Pour ce faire, j'utilise généralement une méthode qui va s'en occuper :

@Stateless
public class InjectionTestBean implements InjectionTestLocal {
 
    @PersistenceUnit
    private EntityManager entityManager;
 
    public InjectionTestBean()
    {
        init();
    }
 
    private void init()
    {
        Query q = entityManager.createQuery("SELECT * FROM MyTable");
        //suite du code d'initialisation
    }
 
    //méthode déclarée dans l'interface
    public String test()
    {
        return "Test";
    }
}

L'appel à cet EJB est fait le plus simplement du monde par un WebService déclaré comme ceci :

@WebService()
public class InjectionWS {
 
    @EJB
    private InjectionTestLocal injectionTest;
 
    @WebMethod
    public String test()
    {
        return injectionTest.test();
    }
}

Rien de spécial à noter, le projet compile bien et se déploie proprement (ce qui ne veut rien dire en Java EE)... Et pourtant, lorsque la méthode test() du WebService est exécutée, voici ce que le serveur nous remonte :

javax.ejb.EJBException: nested exception is: javax.ejb.EJBException: nested exception is: javax.ejb.CreateException: Could not create stateless EJB
        at com.sun.ejb.containers.StatelessSessionContainer._getContext(StatelessSessionContainer.java:491)
        at com.sun.ejb.containers.BaseContainer.getContext(BaseContainer.java:1675)
        at com.sun.ejb.containers.BaseContainer.preInvoke(BaseContainer.java:1229)
        at com.sun.ejb.containers.EJBLocalObjectInvocationHandler.invoke(EJBLocalObjectInvocationHandler.java:195)
        at com.sun.ejb.containers.EJBLocalObjectInvocationHandlerDelegate.invoke(EJBLocalObjectInvocationHandlerDelegate.java:83)
        at $Proxy93.test(Unknown Source)
        at ch.capi.test.InjectionWS.test(InjectionWS.java:25)
        at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
        at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39)
        at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
        at java.lang.reflect.Method.invoke(Method.java:597)
        at com.sun.enterprise.webservice.InstanceResolverImpl$1.invoke(InstanceResolverImpl.java:112)
        at com.sun.xml.ws.server.InvokerTube$2.invoke(InvokerTube.java:146)
        at com.sun.xml.ws.server.sei.EndpointMethodHandler.invoke(EndpointMethodHandler.java:257)
        at com.sun.xml.ws.server.sei.SEIInvokerTube.processRequest(SEIInvokerTube.java:93)
        at com.sun.xml.ws.api.pipe.Fiber.__doRun(Fiber.java:595)
        at com.sun.xml.ws.api.pipe.Fiber._doRun(Fiber.java:554)
        ... et encore un sacré bloc :)

Dans ces cas-la, il est important de garder son sang-froid et d'approfondir la lecture de ce bloc d'erreur :

java.lang.NullPointerException
        at ch.capi.test.InjectionTestBean.init(InjectionTestBean.java:30)
        at ch.capi.test.InjectionTestBean.<init>(InjectionTestBean.java:25)
        at sun.reflect.NativeConstructorAccessorImpl.newInstance0(Native Method)
        at sun.reflect.NativeConstructorAccessorImpl.newInstance(NativeConstructorAccessorImpl.java:39)
        at sun.reflect.DelegatingConstructorAccessorImpl.newInstance(DelegatingConstructorAccessorImpl.java:27)
        at java.lang.reflect.Constructor.newInstance(Constructor.java:513)
        at java.lang.Class.newInstance0(Class.java:355)
        at java.lang.Class.newInstance(Class.java:308)
        at com.sun.ejb.containers.StatelessSessionContainer.createStatelessEJB(StatelessSessionContainer.java:509)
        at com.sun.ejb.containers.StatelessSessionContainer.access$100(StatelessSessionContainer.java:111)
        at com.sun.ejb.containers.StatelessSessionContainer$SessionContextFactory.create(StatelessSessionContainer.java:772)
        at com.sun.ejb.containers.util.pool.NonBlockingPool.getObject(NonBlockingPool.java:199)
        at com.sun.ejb.containers.StatelessSessionContainer._getContext(StatelessSessionContainer.java:486)
        at com.sun.ejb.containers.BaseContainer.getContext(BaseContainer.java:1675)
        at com.sun.ejb.containers.BaseContainer.preInvoke(BaseContainer.java:1229)
        ...

Visiblement, il semble que ce soit la ligne d'initialisation qui pose problème (entityManager à null) :

Query q = entityManager.createQuery("SELECT * FROM MyTable");

Effecitvement, étant donné que c'est le constructeur qui appelle cette méthode, les resources injectées (ici via @PersistenceUnit) ne sont pas encore disponibles... Pour remédier à cela, il faut utiliser une autre annotation qui permet de contourner cela :

@Stateless
public class InjectionTestBean implements InjectionTestLocal {
 
    @PersistenceUnit
    private EntityManager entityManager;
 
    @PostConstruct
    private void init()
    {
        Query q = entityManager.createQuery("SELECT * FROM MyTable");
        //suite du code d'initialisation
    }
 
    //méthode déclarée dans l'interface
    public String test()
    {
        return "Test";
    }
}

Et désormais c'est le serveur qui appellera la méthode init après y avoir mis les injections nécessaires ! Encore merci à Raphaël Tagliani pour avoir soulevé et résolu le problème :)