Injetar um EJB no JAX-RS (serviço RESTful)

Estou tentando injetar um EJB sem estado no meu serviço web JAX-RS via annotations. Infelizmente o EJB é apenas null e eu recebo um NullPointerException quando tento usá-lo.

 @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); } } 

O que estou fazendo de errado?

Aqui estão algumas informações sobre minha máquina:

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

Vocês podem mostrar algum exemplo de trabalho?

Não tenho certeza se isso deve funcionar. Então ou:

Opção 1: use o SPI do provedor de injeção

Implemente um provedor que faça a pesquisa e injete o EJB. Vejo:

  • Injeção @EJB .

Exemplo 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; } } } 

Opção 2: Tornar o BookResource um EJB

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

Vejo:

  • Como combinar serviços REST com o EJB 3.1
  • EJB 3.1 e REST – The Lightweight Hybrid

Opção 3: use o CDI

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

Vejo:

  • Injetando um EJB de um jarro em uma class de jax-rs em uma guerra

Este segmento é bastante antigo, no entanto eu lutei com o mesmo problema ontem. Aqui está a minha solução:

Apenas faça do BookResource um bean gerenciado através de @ javax.annotation.ManagedBean no nível da class.

Para que isso funcione, você precisa ativar o CDI com um beans.xml:

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

Este arquivo precisa estar em WEB-INF se o BookResource fizer parte de um arquivo war. Se o BookResource for empacotado com o ejbs, coloque-o no META-INF.

Se você quiser usar o @EJB, você está pronto. Se você deseja injetar o EJB através de @Inject, então um bean.xml deve ser colocado no arquivo jar do ejbs no META-INF também.

O que você está fazendo: Você está apenas informando ao contêiner que o recurso deve ser gerenciado por contêiner. Por isso, suporta injeção, bem como events de ciclo de vida. Então você tem sua fachada de negócios sem promovê-la para um EJB.

Você não precisa estender o javax.ws.rs.core.Application para que isso funcione. BookResource é como um recurso raiz solicitar automaticamente o escopo.

Testado com o Glassfish 3.1.2 e um projeto maven.

Codificação feliz.

Você poderá fazer a injeção no recurso JAX-RS sem torná-lo componente EJB ou CDI. Mas você precisa lembrar que seu recurso JAX-RS não deve ser singleton.

Então, você configura sua aplicação com este código. Isso torna o recurso JAX-RS da class BookResource por solicitação .

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

Com essa configuração, você está permitindo que o JAX-RS instancie o BookResource para você por solicitação e também injetar todas as dependencies necessárias. Se você fizer o recurso JAX-RS da class BookResource singleton , isto é, você colocará em getSingletons

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

Em seguida, você criou a instância que não é gerenciada pelo tempo de execução do JAX-RS e ninguém no contêiner se importa em injetar nada.

Infelizmente, minha resposta é muito longa para um comentário, então aqui vai. 🙂

Zeck, espero que você esteja ciente do que exatamente você está fazendo promovendo seu bean para um EJB, como sugerido por Pascal. Infelizmente, por mais fácil que seja hoje em dia com o Java EE para ‘tornar uma class um EJB’, você deve estar ciente das implicações de fazê-lo. Cada EJB cria sobrecarga junto com a funcionalidade adicional que fornece: eles são conscientes de transação, têm seus próprios contextos, participam do ciclo de vida completo do EJB, etc.

O que eu acho que você deveria estar fazendo para uma abordagem limpa e reutilizável é esta: extrair o access aos seus serviços de servidores (que esperamos que sejam acessados ​​através de um SessionFacade 🙂 em um BusinessDelegate . Esse delegado deve estar usando algum tipo de consulta JNDI (provavelmente um ServiceLocator – sim, eles ainda são válidos no Java EE!) Para acessar seu backend.

Ok, sem registro: se você realmente, realmente, precisar da injeção, porque você não quer escrever o access JNDI manualmente, você ainda pode fazer o seu delegado um EJB, embora … bem, isso só parece errado. 🙂 Dessa forma, pelo menos, será fácil substituí-lo mais tarde com outra coisa, se você decidir mudar para uma abordagem de pesquisa JNDI …

Eu estava tentando fazer exatamente a mesma coisa. Estou usando o EJB 3.1 e tenho um aplicativo implantado como um EAR com um projeto EJB separado. Como Jav_Rock apontou, eu uso a pesquisa 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); } } 

Veja o link abaixo para dicas de pesquisa JNDI muito úteis

JNDI procure dicas

Arjan está certo. Eu criei outra class para inicializar o EJB em vez de criar um bean para RS

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

para evitar o ponteiro nulo com:

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

na verdade funciona em GF

Eu tenho o mesmo problema, e eu resolvi chamando te EJB por uma pesquisa de contexto (a injeção era impossível, eu tive o mesmo erro NullPointerException).