lundi 26.01.2009, 08:00
Injection de resources
Par Cédric Tabin - Java - Lien permanent
Comme d'habitude, le débuggage en Java EE est d'une prise de tête assez phénoménale pour qui n'est pas habitué aux messages incompréhensibles renvoyés par le serveur ! Cette fois-ci, la cause est un problème d'initialisation des resources injectées via les annotations...
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 
2 commentaires
Hum... je proteste!!! Ca veut rien dire tout simplement
Ciel ! Que vois-je ? De la médisance de la part d'un .netteur :D