- 追加された行はこの色です。
- 削除された行はこの色です。
// 下階層用テンプレート
#topicpath
----
//ここにコンテンツを記述します。
***コンテンツ一覧 [#we76f7eb]
#ls2
J2EEコンテナであるJBossを使ってEJBの勉強をしてみました。以下、作業メモです。
***EJBの使い方 [#l6778de5]
例えば、sayHello()メソッドでStringを返却するEJBを作る場合以下の物が必要です。
-sayHello()メソッドをインタフェースに持つ、Helloインターフェース(extends EJBObject)
-実際にビジネスロジック((っつってもHello EJBってかえすだけ))を記述する、HelloBean(implements SessionBean)
-HelloインタフェースをCreateするメソッドを持つ、HelloHome インタフェース(extends EJBHome)
これとさまざまなDD(Deployment Descriptor)ファイルを作成し、コンテナにデプロイしておくことで、ビジネスロジックを使用したいヒト(プログラム)は、
-JNDI経由で、Homeインタフェースの取得
-Homeインタフェースから、実際のビジネスロジックをcreate
-createしたビジネスロジックを実行
という手順となります。
このようにすることによって、ビジネスロジックがどこで実行されたかを気にしないで結果を得ることができるようになります。つまり可用性が向上するわけですね。
***まずはJNDI名と、EJBたちの関係 [#u1713768]
さて、先ほど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オブジェクトはどうやって関連づけられるのでしょうか??''という疑問がわき起こります。
***考察 [#p0b2925c]
さて先の疑問を解決したいと思います。まだ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で関連づけられます。((WebSphereにはWebSphereの独自ファイルがあります))
<?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)
なんてエラーが表示されしまうかも。。。
***クライアントからではなく、サーブレットなどからアクセスする [#b6921602]
さて、いわゆるローカルな(?)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を使う [#a0eab205]
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で関連づける、という決まりになってるみたいですね。
***関連リンク [#n768272e]
-[[Webコンテナ設定:http://neverbird.sourceforge.jp/manual/fancy/ch07s22.html]] ココには非常に詳しくjboss-web.xmlなどの設定が書いてあります。
----
この記事は
#vote(おもしろかった[1],そうでもない[0])
#vote(おもしろかった[9],そうでもない[7])
-作ったearファイルをRational Application Developerで動かしたらうごいたぞー。。。なんで??? -- [[きの]] &new{2005-11-10 19:25:06 (木)};
-JBossの掲示板 http://www.jbossgroup.com/index.html?module=bb&op=viewforum&f=47 を検索してみたところ、クラスローダがどうのとか、ようするにパッケージングに問題がありそうな感じ。 -- [[きの]] &new{2005-11-11 17:30:15 (金)};
-急がばまわれで、チュートリアルをダウンロードしてきて、一からAntでパッケージングしたところ、ちゃんと稼動しました!!!! -- [[きの]] &new{2005-11-11 17:32:00 (金)};
#comment
#topicpath
SIZE(10){現在のアクセス:&counter;}