web-dev-qa-db-ja.com

EJBをJAX-RS(RESTfulサービス)に注入する

アノテーションを介してJAX-RS WebサービスにStateless EJBを注入しようとしています。残念ながら、EJBはnullであり、使用しようとするとNullPointerExceptionを取得します。

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

私は何を間違えていますか?

ここに私のマシンに関するいくつかの情報があります:

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

いくつかの実例を示すことができますか?

70
Zeck

これが機能するかどうかはわかりません。どちらか:

オプション1:インジェクションプロバイダーSPIを使用する

ルックアップを行い、EJBを注入するプロバイダーを実装します。見る:

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

オプション2:BookResourceをEJBにする

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

    @EJB
    private BookEJB bookEJB;

    //...
}

見る:

オプション3:CDIを使用する

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

    @Inject
    private BookEJB bookEJB;

    //...
}

見る:

111
Pascal Thivent

このスレッドはかなり古いですが、昨日も同じ問題に取り組みました。私の解決策は次のとおりです。

クラスレベルで @ javax.annotation.ManagedBean を使用して、BookResourceをマネージドBeanにします。

これを機能させるには、beans.xmlでCDIを有効にする必要があります。

<?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>

BookResourceがwarファイルの一部である場合、このファイルはWEB-INFにある必要があります。 BookResourceがejbsとともにパッケージ化されている場合、META-INFに配置します。

@EJBを使用する場合は、これで完了です。 @Injectを介してEJBを注入する場合、beans.xmlをejbs jarファイルに入れてMETA-INFに入れる必要があります。

あなたがしていること:リソースをコンテナ管理する必要があることをコンテナに伝えているだけです。そのため、ライフサイクルイベントだけでなく注入もサポートします。したがって、EJBに昇格させずにビジネスファサードを作成できます。

これを機能させるためにjavax.ws.rs.core.Applicationを拡張する必要はありません。 BookResourceは、ルートリソースとして自動的にスコープを要求します。

Glassfish 3.1.2およびMavenプロジェクトでテスト済み。

ハッピーコーディング。

14
Michael Simons

EJBまたはCDIコンポーネントにすることなく、JAX-RSリソースでインジェクションを実行できるようにします。ただし、JAX-RSリソースはシングルトンであってはならないことを覚えておく必要があります。

したがって、このコードを使用してアプリケーションをセットアップします。これにより、BookResource class per-request JAX-RSリソースになります。

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

このセットアップでは、リクエストごとにBookResourceをJAX-RSでインスタンス化し、必要な依存関係もすべて注入します。 BookResource class singleton JAX-RSリソースを作成する場合、これはgetSingletons

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

次に、JAX-RSランタイムによって管理されないインスタンスを作成しました。コンテナ内の誰も何も注入しません。

10
Martin

残念ながら、私の答えはコメントするには長すぎますので、ここに行きます。 :)

Zeck、Pascalが提案するように、BeanをEJBにプロモートすることによって、あなたが何をしているかを正確に知っていることを願っています。残念ながら、今日ではJava EEを「クラスをEJBにする」ことで簡単にできますが、そうすることの意味に注意する必要があります。各EJBは追加機能とともにオーバーヘッドを作成します提供:トランザクションを認識し、独自のコンテキストを持ち、EJBライフサイクル全体に参加します。

私はあなたがきれいで再利用可能なアプローチのためにすべきだと思うことはこれです:あなたのサーバーサービスへのアクセスを抽出します(願わくば SessionFacade :)から BusinessDelegate にアクセスします。このデリゲートは、何らかの種類のJNDIルックアップを使用する必要があります(おそらく ServiceLocator -はい、それらはJava EE!)で引き続き有効で、バックエンドにアクセスします。

わかりました、オフレコ:本当に、本当に、really手動でJNDIアクセスを記述したくないので、インジェクションが必要な場合、 EJBを委任しますが、それは...まあ、間違っているように感じます。 :)そうすれば、少なくともJNDIルックアップアプローチに切り替えることにした場合は、後で簡単に別のものに置き換えることができます...

5
LeChe

私はまったく同じことをしようとしていました。私はEJB 3.1を使用しており、別個のEJBプロジェクトを使用してEARとしてアプリケーションをデプロイしています。 Jav_Rockが指摘したように、コンテキストルックアップを使用します。

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

非常に役立つJNDIルックアップのヒントについては、以下のリンクを参照してください

JNDI検索のヒント

3
duvo

アルジャンは正しい。 RS用のBeanを作成する代わりに、EJBを初期化する別のクラスを作成しました

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

nullポインターを回避するには:

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

実際にGFで動作します

1
Lukasz Ronikier

私は同じ問題を抱えており、コンテキストルックアップによってEJBを呼び出すことで解決しました(注入は不可能で、同じエラーNullPointerExceptionが発生しました)。

0
NIkoas