Inyectar un EJB en JAX-RS (servicio RESTful)

Intento inyectar un EJB sin estado en mi servicio web JAX-RS a través de anotaciones. Lamentablemente, el EJB es simplemente null y obtengo una NullPointerException cuando bash usarlo.

 @Path("book") public class BookResource { @EJB private BookEJB bookEJB; public BookResource() { } @GET @Produces("application/xml") @Path("/{bookId}") public Book getBookById(@PathParam("bookId") Integer id) { return bookEJB.findById(id); } } 

¿Qué estoy haciendo mal?

Aquí hay información sobre mi máquina:

  • Glassfish 3.1
  • Netbeans 6.9 RC 2
  • Java EE 6

¿Pueden mostrar algún ejemplo de trabajo?

    No estoy seguro de que esto funcione. Entonces o bien:

    Opción 1: usar el proveedor de inyección SPI

    Implemente un proveedor que hará la búsqueda e inyectará el EJB. Ver:

    • @EJB inyección .

    Ejemplo para com.sun.jersey: jersey-server: 1.17:

     import com.sun.jersey.core.spi.component.ComponentContext; import com.sun.jersey.core.spi.component.ComponentScope; import com.sun.jersey.spi.inject.Injectable; import com.sun.jersey.spi.inject.InjectableProvider; import javax.ejb.EJB; import javax.naming.Context; import javax.naming.InitialContext; import javax.ws.rs.ext.Provider; import java.lang.reflect.Type; /** * JAX-RS EJB Injection provider. */ @Provider public class EJBProvider implements InjectableProvider { public ComponentScope getScope() { return ComponentScope.Singleton; } public Injectable getInjectable(ComponentContext cc, EJB ejb, Type t) { if (!(t instanceof Class)) return null; try { Class c = (Class)t; Context ic = new InitialContext(); final Object o = ic.lookup(c.getName()); return new Injectable() { public Object getValue() { return o; } }; } catch (Exception e) { e.printStackTrace(); return null; } } } 

    Opción 2: hacer que BookResource sea un EJB

     @Stateless @Path("book") public class BookResource { @EJB private BookEJB bookEJB; //... } 

    Ver:

    • Cómo combinar servicios REST con EJB 3.1
    • EJB 3.1 y REST – El híbrido ligero

    Opción 3: Usar CDI

     @Path("book") @RequestScoped public class BookResource { @Inject private BookEJB bookEJB; //... } 

    Ver:

    • Inyectar un EJB de un jar en una clase jax-rs en una guerra

    Este hilo es bastante viejo, sin embargo, luché el mismo problema ayer. Aquí está mi solución:

    Simplemente haga de BookResource un bean administrado a través de @ javax.annotation.ManagedBean a nivel de clase.

    Para que esto funcione, debes habilitar CDI con beans.xml:

     < ?xml version="1.0" encoding="UTF-8"?>   

    Este archivo debe estar en WEB-INF si BookResource es parte de un archivo war. Si BookResource está empaquetado con los ejbs, póngalo en META-INF.

    Si quieres usar @EJB ya terminaste. Si desea inyectar el EJB a través de @Inject, un beans.xml también debe colocarse en el archivo jar de ejbs en META-INF.

    Lo que estás haciendo: solo le estás diciendo al contenedor que el recurso debe administrarse en contenedores. Por lo tanto, es compatible con la inyección, así como los eventos del ciclo de vida. Entonces tiene la fachada de su empresa sin promocionarla en un EJB.

    No necesita extender javax.ws.rs.core.Application para que esto funcione. BookResource es como un recurso raíz que solicita automáticamente un ámbito.

    Probado con Glassfish 3.1.2 y un proyecto maven.

    Feliz encoding.

    Deberá poder inyectar en el recurso JAX-RS sin convertirlo en componente EJB o CDI. Pero debe recordar que su recurso JAX-RS no debe ser singleton.

    Entonces, configura su aplicación con este código. Esto hace que el recurso JAX-RS de la clase BookResource por solicitud .

     @javax.ws.rs.ApplicationPath("application") public class InjectionApplication extends javax.ws.rs.core.Application { private Set singletons = new HashSet(); private Set> classes = new HashSet>(); public InjectionApplication() { // no instance is created, just class is listed classes.add(BookResource.class); } @Override public Set> getClasses() { return classes; } @Override public Set getSingletons() { return singletons; } } 

    Con esta configuración, le está permitiendo a JAX-RS crear instancias de BookResource por solicitud y también inyectar todas las dependencias requeridas. Si haces un recurso JAX-RS singleton de la clase BookResource , esto es, pones getSingletons

     public Set getSingletons() { singletons.add(new BookResource()); return singletons; } 

    luego, creó una instancia que no está administrada por el tiempo de ejecución de JAX-RS y nadie en el contenedor se preocupa por inyectar nada.

    Desafortunadamente, mi respuesta es demasiado larga para un comentario, así que aquí va. 🙂

    Zeck, espero que estés al tanto de lo que estás haciendo exactamente promocionando tu bean a un EJB, como lo sugiere Pascal. Desafortunadamente, tan fácil como lo es hoy en día con Java EE para ‘hacer que una clase sea EJB’, debe ser consciente de las implicaciones de hacerlo. Cada EJB crea una sobrecarga junto con la funcionalidad adicional que proporciona: son conscientes de las transacciones, tienen sus propios contextos, participan en el ciclo de vida EJB completo, etc.

    Lo que creo que debería hacer para un enfoque limpio y reutilizable es este: extraer el acceso a los servicios de sus servidores (que con suerte se accede a través de una SessionFacade 🙂 a un BusinessDelegate . Este delegado debería utilizar algún tipo de búsqueda JNDI (probablemente un ServiceLocator , sí, ¡todavía son válidos en Java EE!) Para acceder a su back-end.

    De acuerdo, fuera del registro: si realmente, realmente, realmente necesitas la inyección porque no quieres escribir el acceso JNDI manualmente, aún podrías hacer que tu delegado sea un EJB, aunque … bueno, simplemente se siente mal. 🙂 De esa manera al menos será fácil reemplazarlo más tarde con otra cosa si decides cambiar a un enfoque de búsqueda JNDI …

    Estaba tratando de hacer exactamente lo mismo. Estoy usando EJB 3.1 y tengo una aplicación implementada como un EAR con proyecto EJB separado. Como Jav_Rock señaló, utilizo la búsqueda de contexto.

     @Path("book") public class BookResource { @EJB BookEJB bookEJB; public BookResource() { try { String lookupName = "java:global/my_app/my_ejb_module/BookEJB"; bookEJB = (BookEJB) InitialContext.doLookup(lookupName); } catch (NamingException e) { e.printStackTrace(); } } @GET @Produces("application/xml") @Path("/{bookId}") public Book getBookById(@PathParam("bookId") Integer id) { return bookEJB.findById(id); } } 

    Consulte el siguiente enlace para obtener consejos de búsqueda de JNDI muy útiles

    JNDI buscar consejos

    Arjan tiene razón. Creé otra clase para inicializar el EJB en lugar de crear un bean para RS

     @Singleton @LocalBean public class Mediator { @EJB DatabaseInterface databaseFacade; 

    para evitar el puntero nulo con:

     @Path("stock") public class StockResource { @EJB DatabaseInterface databaseFacade; ... 

    en realidad funciona en GF

    Tengo el mismo problema, y ​​lo resolví llamando al EJB por una búsqueda de contexto (la inyección era imposible, tuve el mismo error NullPointerException).