Inyectar un EJB en JAX-RS (servicio RESTful)
Estoy intentando inyectar un EJB sin estado en mi servicio web JAX-RS mediante anotaciones. Lamentablemente, el EJB es justo null
y aparece un mensaje NullPointerException
cuando intento 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:
- Pez de cristal 3.1
- Netbeans 6.9 RC 2
- JavaEE6
¿Pueden mostrar algún ejemplo práctico?
No estoy seguro de que esto funcione. Entonces cualquiera de las dos:
Opción 1: utilizar el proveedor de inyección SPI
Implemente un proveedor que realice la búsqueda e inyecte el EJB. Ver:
- Inyección @EJB .
Ejemplo de 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<EJB, Type> {
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<Object>() {
public Object getValue() {
return o;
}
};
} catch (Exception e) {
e.printStackTrace();
return null;
}
}
}
Opción 2: convertir BookResource en 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 desde un frasco en una clase jax-rs en una guerra
Este hilo es bastante antiguo, sin embargo ayer tuve el mismo problema. Aquí está mi solución:
Simplemente convierta BookResource en un bean administrado a través de @javax.annotation.ManagedBean a nivel de clase.
Para que esto funcione necesitas habilitar CDI con beans.xml:
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://java.sun.com/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/beans_1_0.xsd">
</beans>
Este archivo debe estar en WEB-INF si BookResource es parte de un archivo war. Si BookResource está empaquetado con ejbs, colóquelo en META-INF.
Si desea utilizar @EJB, ya está. Si desea inyectar el EJB a través de @Inject, también debe colocar beans.xml en el archivo jar ejbs en META-INF.
Qué estás haciendo: simplemente le estás diciendo al contenedor que el recurso debe ser administrado por contenedor. Por lo tanto, admite la inyección y los eventos del ciclo de vida. Así tendrás la fachada de tu negocio sin promocionarlo a un EJB.
No es necesario ampliar javax.ws.rs.core.Application para que esto funcione. BookResource tiene un alcance de solicitud automático como recurso raíz.
Probado con Glassfish 3.1.2 y un proyecto maven.
Feliz codificación.
Podrá realizar inyección 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 la clase BookResource sea un recurso JAX-RS por solicitud .
@javax.ws.rs.ApplicationPath("application")
public class InjectionApplication extends javax.ws.rs.core.Application {
private Set<Object> singletons = new HashSet<Object>();
private Set<Class<?>> classes = new HashSet<Class<?>>();
public InjectionApplication() {
// no instance is created, just class is listed
classes.add(BookResource.class);
}
@Override
public Set<Class<?>> getClasses() {
return classes;
}
@Override
public Set<Object> getSingletons() {
return singletons;
}
}
Con esta configuración, está permitiendo que JAX-RS cree una instancia de BookResource por usted y también inyecte todas las dependencias requeridas. Si crea un recurso JAX-RS singleton de la clase BookResource , es decir, coloca getSingletons
public Set<Object> 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 a nadie en el contenedor le importa inyectar nada.