Injecter une EJB dans JAX-RS (RESTful service)

j'essaie d'injecter un apatride EJB dans mon JAX-RS webservice via des annotations. Malheureusement L'EJB est juste null et je reçois un NullPointerException quand j'essaie de l'utiliser.

@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'est-ce que je fais de mal?

voici quelques informations sur ma machine:

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

pouvez-vous montrer un exemple?

68
demandé sur Jens Piegsa 2010-06-12 11:09:14

7 réponses

Je ne suis pas sûr que cela fonctionne. Alors soit:

Option 1: Utiliser le fournisseur d'injection SPI

implémenter un fournisseur qui fera la recherche et injectera L'EJB. Voir:

exemple pour com.soleil.jersey:jersey-serveur: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;
        }
    }
}

Option 2: Faire de la BookResource un EJB

@Stateless
@Path("book")
public class BookResource {

    @EJB
    private BookEJB bookEJB;

    //...
}

voir:

Option 3: utiliser CDI

@Path("book")
@RequestScoped
public class BookResource {

    @Inject
    private BookEJB bookEJB;

    //...
}

voir:

107
répondu Pascal Thivent 2017-01-10 12:53:26

ce fil est assez vieux, néanmoins j'ai combattu le même problème hier. Voici ma solution:

il suffit de faire le BookResource un bean managé à travers @javax.annotation. au niveau de la classe.

pour que cela fonctionne, vous devez permettre au CDI avec des haricots.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>

ce fichier doit être dans WEB-INF si BookResource fait partie d'un fichier war. Si le BookResource est empaqueté avec les ejbs le met dans META-INF.

si vous voulez utiliser @EJB vous avez terminé. Si vous voulez injecter l 'EJB par l' intermédiaire de @injecter qu ' un haricot.xml doit également être mis dans le fichier jar ejbs dans META-INF.

ce que vous faites: vous dites simplement au conteneur que la ressource doit être gérée conteneur. Par conséquent, il prend en charge l'injection ainsi que les événements du cycle de vie. Vous avez donc votre façade commerciale sans la promouvoir à un EJB.

vous n'avez pas besoin d'étendre javax.ws.rs.noyau.Application for this to work. BookResource est comme une ressource racine demande automatiquement scoped.

Testé avec Glassfish 3.1.2 et un projet maven.

Heureux de codage.

11
répondu Michael Simons 2012-10-12 08:06:32

vous devez être en mesure de faire l'injection dans la ressource JAX-RS sans la faire composante EJB ou CDI. Mais vous devez vous rappeler que votre ressource JAX-RS ne doit pas être singleton.

donc, vous configurez votre application avec ce code. Cela rend BookResource classe par la demande JAX-RS ressource.

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

avec cette configuration, vous laissez JAX-RS instantiate BookResource pour vous sur la base de la demande et injecter également toutes les dépendances requises. Si vous faites BookResource classe singleton JAX-RS de ressources, c'est, vous le mettez dans getSingletons

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

ensuite, vous avez créé instance qui n'est pas gérée par JAX-RS runtime et personne dans container ne se soucie d'injecter quoi que ce soit.

10
répondu Martin 2012-03-07 17:36:21

malheureusement, ma réponse est trop longue pour un commentaire, Alors voilà. :)

Zeck, j'espère que vous êtes conscient de ce que vous faites exactement en promouvant votre haricot à un EJB, comme suggéré par Pascal. Malheureusement, aussi facile qu'il soit de nos jours avec Java EE de "faire une classe un EJB", vous devriez être conscient des implications de le faire. Chaque EJB crée un overhead avec les fonctionnalités supplémentaires qu'il fournit: ils sont sensibles aux transactions, ont leurs propres contextes, ils participent à l'ensemble du cycle de vie des EJB, etc.

ce que je pense que vous devriez faire pour une approche propre et réutilisable est ceci: extraire l'accès à vos services de serveurs (qui, nous l'espérons, sont accessibles via un SessionFacade :) dans un Business Delegate . Ce délégué devrait utiliser une sorte de recherche JNDI (probablement un ServiceLocator - oui, ils sont toujours valables en Java EE! pour avoir accès à vos backends.

d'accord, officieusement: si vous vraiment, vraiment, vraiment avez besoin de l'injection parce que vous ne voulez pas écrire l'accès JNDI manuellement, vous pouvez toujours faire votre délégué un EJB, bien qu'il ... eh bien, il se sent mal. :) De cette façon au moins il sera facile de le remplacer plus tard par quelque chose d'autre si vous décidez de passer à une approche de recherche JNDI...

5
répondu LeChe 2013-07-26 20:36:02

j'essayais de faire exactement la même chose. J'utilise EJB 3.1 et j'ai déployé mon application comme une oreille avec un projet EJB séparé. Comme Jav_Rock l'a souligné, j'utilise la recherche de contexte.

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

voir le lien ci-dessous pour des conseils de recherche JNDI très utiles ""

JNDI chercher des conseils

3
répondu duvo 2015-05-28 18:12:47

Arjan a raison. J'ai créé une autre classe pour initialiser L'EJB au lieu de créer un haricot pour RS

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

pour éviter le pointeur nul avec:

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

il fonctionne en fait sur GF

1
répondu Lukasz Ronikier 2015-08-21 08:47:05

j'ai le même problème, et je l'ai résolu en appelant te EJB par une recherche de contexte (l'injection était impossible, j'ai eu la même erreur NullPointerException).

0
répondu NIkoas 2012-10-11 06:53:51