Top / J2EE / EJB

コンテンツ一覧

J2EEコンテナであるJBossを使ってEJBの勉強をしてみました。以下、作業メモです。

EJBの使い方

例えば、sayHello()メソッドでStringを返却するEJBを作る場合以下の物が必要です。

  • sayHello()メソッドをインタフェースに持つ、Helloインターフェース(extends EJBObject)
  • 実際にビジネスロジック*1を記述する、HelloBean?(implements SessionBean?)
  • HelloインタフェースをCreateするメソッドを持つ、HelloHome? インタフェース(extends EJBHome)

これとさまざまなDD(Deployment Descriptor)ファイルを作成し、コンテナにデプロイしておくことで、ビジネスロジックを使用したいヒト(プログラム)は、

  • JNDI経由で、Homeインタフェースの取得
  • Homeインタフェースから、実際のビジネスロジックをcreate
  • createしたビジネスロジックを実行 という手順となります。

このようにすることによって、ビジネスロジックがどこで実行されたかを気にしないで結果を得ることができるようになります。つまり可用性が向上するわけですね。

まずはJNDI名と、EJBたちの関係

さて、先ほどJNDI経由でHomeインタフェースを取得すると言いましたが、EJBではオブジェクトを取得する際、必ずInitialContext?を経由します。この機構をJNDIといいます。InitialContext?はあらかじめ、Stringのキー値とオブジェクトを対にしてコンテナに登録しておき、そのキー値でオブジェクトを検索し取得するのですが、そのキー値とオブジェクトの関係を理解しないと相当ややこしいです。

先の例で行くと、

initContext = new InitialContext();
Object ref = initContext.lookup("HelloEJBHoge");
HelloHome home = (HelloHome) PortableRemoteObject.narrow(ref,HelloHome.class);

とルックアップするのですが、この"HelloEJBHoge"がキー値で、HelloHome? が登録されているオブジェクトです。

話を整理すると、

  • コンテナには、HelloEJBHogeというキーで、hello.HelloHome?オブジェクトが登録されている。
  • InitialContext?をそのキー値でlookupすることで、hello.HelloHome?オブジェクトを取得する

ということです。そこでそもそもコンテナにオブジェクトを登録する方法は?、もっと具体的にいうとInitialContext?をlookupする際のキー"HelloEJBHoge"と、hello.HelloHome?オブジェクトはどうやって関連づけられるのでしょうか??という疑問がわき起こります。

考察

さて先の疑問を解決したいと思います。まだTry and Errorですが。。。

まず、hello.HelloHome?に名前を付けます。これはJ2EEの仕様内であるejb-jar.xmlによって行われます。

<?xml version="1.0"?>
<!DOCTYPE ejb-jar PUBLIC '-//Sun Microsystems, Inc.//DTD Enterprise JavaBeans 2.0//EN'
  'http://java.sun.com/dtd/ejb-jar_2_0.dtd'>
<ejb-jar>
  <enterprise-beans>
  <session>
    <ejb-name>HelloEJB</ejb-name>
    <home>hello.HelloHome</home> <-ココ
    <remote>hello.Hello</remote>
    <ejb-class>hello.HelloBean</ejb-class>
    <session-type>Stateless</session-type>
    <transaction-type>Container</transaction-type>
   </session>
  </enterprise-beans>
</ejb-jar>

これで、hello.HelloHome?にはHelloEJBというEJB名が付けられました。

さらに、このEJB名"HelloEJB"とコンテナ側に登録されている"HelloEJBHoge"は、JBoss独自ファイルである、jboss.xmlで関連づけられます。*2

<?xml version="1.0"?>
<jboss>
  <enterprise-beans>
  <session>
    <ejb-name>HelloEJB</ejb-name>
    <jndi-name>HelloEJBHoge</jndi-name>
  </session>
  </enterprise-beans>
</jboss>

これでHelloEJBHogeというキーとhello.HelloHome?が関連づけられました。これらのクラスファイルやDDファイルをコンテナにデプロイすることで、JNDIへの登録から何からが完了したことになります。ちなみにejb-jar.xmlとjboss.xmlはejbのjarの/META-INF内に配置します。

ようやくこれで、HelloEJBHogeでlookupすればhello.HelloHome?を取得できそうです。実際にやってみると

public static void main(String[] args) throws NamingException,
    RemoteException, CreateException {
  InitialContext initContext = null;
  try {
    initContext = new InitialContext();
    Object ref = initContext.lookup("HelloEJBHoge");
    HelloHome home = (HelloHome) PortableRemoteObject.narrow(ref,
        HelloHome.class);
    Hello hello = home.create();
    System.out.println(hello.sayHello());
  } finally {
    if (initContext != null) {
      initContext.close();
    }
  }
}

これで確かに稼動しました!。ちなみにEclipseから実行したときは、実行時のクラスパスに${JBOSS_HOME}/client/jbossall-client.jarにパスを通さないとダメみたい。。

javax.naming.NoInitialContextException: Cannot instantiate class: 
org.jnp.interfaces.NamingContextFactory [Root exception is 
java.lang.ClassNotFoundException: org.jnp.interfaces.NamingContextFactory]
 at javax.naming.spi.NamingManager.getInitialContext(Unknown Source)
 at javax.naming.InitialContext.getDefaultInitCtx(Unknown Source)
 at javax.naming.InitialContext.init(Unknown Source)

なんてエラーが表示されしまうかも。。。

クライアントからではなく、サーブレットなどからアクセスする

さて、いわゆるローカルな(?)Lookupはこれで良さそうですが、サーブレットなどからEJBにアクセスするためにはjava:comp/envを使ったENCによるJNDI検索をする必要があるみたいです。つまりInitialContext?をlookupするときに

context.lookup("java:comp/env/ejb/HelloEJB");

てやる例のヤツです。

さて、

context.lookup("java:comp/env/ejb/HelloEJB");

と指定すると、web.xml内のejb-refを ejb/HelloEJB で検索します。web.xmlには以下のEJB参照を記述しました。。

 <ejb-ref>
   <ejb-ref-name>ejb/HelloEJB</ejb-ref-name>
   <ejb-ref-type>Session</ejb-ref-type>
   <home>hello.HelloHome</home>
   <remote>hello.Hello</remote>
   <ejb-link>HelloEJB</ejb-link>
 </ejb-ref>

これで、ejb-linkのところでjboss.xmlのejb-nameを見に行って、そこのjndi-nameでlookupする、と理解したんだけど、うまくいかない。。。。。。以下のエラーが表示されてしまいます。

EJBException in method: public abstract hello.Hello hello.HelloHome.create() 
throws java.rmi.RemoteException,javax.ejb.CreateException:
javax.ejb.EJBException: Invalid invocation, check your deployment packaging, 
method=public abstract hello.Hello hello.HelloHome.create() throws
java.rmi.RemoteException,javax.ejb.CreateException

でも、わざと

 <ejb-link>HelloEJB</ejb-link>

をjboss.xmlにないHelloEJBaとかすると

javax.naming.NamingException: ejb-ref: ejb/HelloEJB, no ejb-link in web.xml 
and no jndi-name in jboss-web.xml)

なんていわれるから、DDたちはあってるような気がする。。。

また、ENCを使わないでクライアントでやったように直接

Object boundObject = context.lookup("HelloEJBHoge");

とやっても、ENCでやったときと同じエラーが出ちゃいます。

2005/11/22追記
どうも、EARファイルへのパッケージングのやり方が悪かったみたいです。WARファイルの中にEJBのファイルも入れてたんだけど、それがNGのようです。ちゃんとWARはWEBコンテナにデプロイしたいもののみ、EJB-JARはEJBコンテナにデプロイしたいもののみを入れなくてはいけないようです。

jboss-web.xmlを使う

JBossでは先に書いた <ejb-link>HelloEJB</ejb-link> とjboss.xmlのejb-nameによるJNDI名との関連づけだけではなく、jboss-web.xmlによる関連づけも可能です。たぶんweb.xmlにはejb-linkを書かないで、jboss-web.xmlに

<ejb-ref>
  <ejb-ref-name>ejb/HelloEJB</ejb-ref-name>
  <jndi-name>HelloEJBHoge</jndi-name>
</ejb-ref>

と書くだけのようです。こっちの方が、きれいですね。ちなみにjboss-web.xmlはwarのWEB-INF/直下に配置します。web.xmlの隣に置いておきましょう。

2005/11/22追記
「JBossでは」というよりも、同じEARに入れられたEJBを参照するときは<ejb-link>で、外部のEJBを参照するときはjboss-web.xmlで関連づける、という決まりになってるみたいですね。

関連リンク

  • Webコンテナ設定 ココには非常に詳しくjboss-web.xmlなどの設定が書いてあります。

この記事は

選択肢 投票
おもしろかった 9  
そうでもない 7  
  • 作ったearファイルをRational Application Developerで動かしたらうごいたぞー。。。なんで??? -- きの? 2005-11-10 19:25:06 (木)
  • JBossの掲示板 http://www.jbossgroup.com/index.html?module=bb&op=viewforum&f=47 を検索してみたところ、クラスローダがどうのとか、ようするにパッケージングに問題がありそうな感じ。 -- きの? 2005-11-11 17:30:15 (金)
  • 急がばまわれで、チュートリアルをダウンロードしてきて、一からAntでパッケージングしたところ、ちゃんと稼動しました!!!! -- きの? 2005-11-11 17:32:00 (金)
Top / J2EE / EJB

現在のアクセス:25053


*1 っつってもHello EJBってかえすだけ
*2 WebSphereにはWebSphereの独自ファイルがあります

トップ   編集 凍結 差分 バックアップ 添付 複製 名前変更 リロード   新規 一覧 単語検索 最終更新   ヘルプ   最終更新のRSS
Last-modified: 2017-06-07 (水) 20:41:27 (808d)