EJB(Enterprise Java Beans)3.0は、アノテーションを使うことでxmlファイルを書く必要が無くなり、EJB2.Xよりも扱うのが簡単になった。
(EJB3.0はJavaEE5(1.5)の仕様のひとつ)
WebLogic10.0とJBoss4.2.3でステートレスビーンを試してみた。
EJBの呼び出しで使われるインターフェース:
package sample.ejb3.stateless; import javax.ejb.Local; import javax.ejb.Remote; @Remote →付けられるアノテーションの組み合わせ @Local public interface Ejb3Sample { public void execute(String text); }
インターフェースは、何の変哲も無い普通(POJI)のインターフェース。
自分で呼び出したいメソッドを宣言する。
また、このメソッドを@Statelessアノテーション付きのクラスでimplementsする。
EJB本体(呼び出される側):
package sample.ejb3.stateless; import javax.ejb.Stateless; @Stateless( →付けられるアノテーションの組み合わせ //WLS10.0Jの場合 // name = "ejb3sample", …あっても無くても関係無さそう // description = "EJB3サンプル", …あっても無くても関係無さそう mappedName = "ejb3_sample" //JBoss4.2.3の場合 name = "sample/Ejb3SampleBean" ) public class Ejb3SampleBean implements Ejb3Sample { //インターフェースEjb3Sampleの実装 public void execute(String text) { // WebLogicのコンソールに出力される System.out.println("Ejb3SampleBean#exec:" + text); } }
EJB3.0では、EJBの定義にアノテーションを使用する。このアノテーションを付けておけば、ejb-jar.xmlだの何だのは何も記述しなくてよい。
javax.ejbパッケージを使う為には、そのクラスが入っているjarファイルをクラスパスに加える必要がある。
WLS10では@Remote・@LocalアノテーションはBean側に付けてもインターフェース側に付けても動作はするが、Javadocを見るとインターフェースに付けるのが正しいような気がする。[2008-08-14]
→付けられるアノテーションの組み合わせ
これらのクラスをコンパイルしてデプロイすれば、呼び出せるようになる。[/2008-08-15]
→WLS10.0JのEJB3のデプロイ
→JBoss4.2.3のEJB3のデプロイ
呼び出し側は以下のようにコーディングする。
import javax.naming.Context;
import javax.naming.NamingException;
import sample.ejb3.stateless.Ejb3Sample; //EJB呼び出し用のインターフェース
public class Ejb3SampleBatch {
public static void main(String[] args) throws NamingException {
Context ctx = initContext(); //APサーバー毎に異なる
Ejb3Sample sample;
try {
sample = (Ejb3Sample) ctx.lookup("ejb3_sample#" + Ejb3Sample.class.getName()); //WLS10.0J
sample = (Ejb3Sample) ctx.lookup("sample/Ejb3SampleBean/remote"); //JBoss4.2.3
→JNDI名の違い
} finally {
ctx.close();
}
sample.execute("バッチから呼び出し");
}
〜
}
APサーバー(WebLogic(ドメイン)やJBossAS)が稼動している状態で普通に上記クラスを実行すればEjb3SampleBeanが呼び出せる。
サーブレットから実行する場合は、コンテキストの取得を「Context ctx = new
InitialContext();
」とすればいいだけ。(JMSの例と同じ)
JBossでもWebLogicと同じようにEJB3.0のアノテーションを試してみたところ、設定した内容の反映のされ方が違ってハマった。[2008-08-15]
EJBデプロイ方法 | JNDI確認方法 | リモートInitialContext | その他 | |
---|---|---|---|---|
WLS 10.0J | autodeploy/my_ejb | JNDIツリー | WLInitialContextFactory | |
JBoss 4.2.3 | deploy/my_ejb.jar | JNDIView | NamingContextFactory | EJB名はGlobal JNDIになる。 |
インターフェース | ビーン本体 | lookup | JNDI名 | 備考 | |
---|---|---|---|---|---|
WLS 10.0J |
package jp.sample; @Remote @Local public interface Sample |
@Stateless(
mappedName = "mapName"
)
public class Bean
|
ctx.lookup( "mapName#" + Sample.class.getName() ) |
mapName#jp/sample/Sample | @Remoteと@Localを両方ともインターフェースに付ける事が出来る。 |
package jp.sample; public interface Sample |
@Stateless(
mappedName = "mapName"
)
@Remote
@Local
public class Bean
|
ctx.lookup( "mapName#" + Sample.class.getName() ) |
mapName#jp/sample/Sample | @Remoteと@Localを両方ともビーン側に付ける事が出来る。 | |
JBoss 4.2.3 |
@Remote public interface Sample |
@Stateless(
name = "jp/sample/Sample"
)
@Local
public class Bean
|
ctx.lookup(
"jp/sample/Sample/local"
)
ctx.lookup(
"jp/sample/Sample/remote"
)
|
jp/sample/Sample/local jp/sample/Sample/remote |
JBossでは、インターフェース又はビーンのどちらか一方に@Remoteと@Localを同時に付けるとエラーになる。 また、インターフェースに@Local・ビーンに@Remoteを付けてもエラーになる。 |
@Remote public interface Sample |
@Stateless
@Local
public class Bean
|
ctx.lookup(
"Bean/local"
)
ctx.lookup(
"Bean/remote"
)
|
Bean/local Bean/remote |
@Statelessにnameを付けないと、ビーンのクラス名(パッケージ無し)になる。 | |
@Remote public interface Sample |
@Stateless
public class Bean
|
ctx.lookup(
"Bean/remote"
)
|
Bean/remote | @Remoteか@Localのどちらかしか指定しないと、そちらだけJNDIに登録される。 | |
@Local public interface Sample |
@Stateless
public class Bean
|
ctx.lookup(
"Bean/local"
)
|
Bean/local |
JBossの場合、earファイル化したEJBでは、JNDI名の先頭にearファイル名が付く。[2008-09-27]
つまりtest.earに入っているfoo.jarで「@Stateless(name = "jp/sample/Sample")」の場合、「ctx.lookup("test/jp/sample/Sample/local")」となる。
メッセージ駆動(メッセージドリブン)型EJBは、JMS経由で送られてくるメッセージを受け取る。
つまり、JMSのキューにメッセージが入れられると、用意したメッセージ駆動ビーン(Message Driven Bean:MDB)のonMessage()が呼ばれる。
package sample.ejb3.mdb; import javax.ejb.ActivationConfigProperty; import javax.ejb.MessageDriven; import javax.jms.JMSException; import javax.jms.Message; import javax.jms.MessageListener; import javax.jms.TextMessage; @MessageDriven( //WLS10.0Jの場合 mappedName = "MyQueue0" //JBoss4.2.3の場合 activationConfig = { @ActivationConfigProperty(propertyName = "destinationType", propertyValue = "javax.jms.Queue"), @ActivationConfigProperty(propertyName = "destination", propertyValue = "queue/MyQueue1") } ) public class MdbSampleBean implements MessageListener { // MessageListenerのメソッドを実装 public void onMessage(Message msg) { try { System.out.println("MDBで受け取った:" + msg); if (msg instanceof TextMessage) { TextMessage tm = (TextMessage) msg; System.out.println(tm.getText()); } } catch (JMSException e) { e.printStackTrace(); } } }
mappedNameやactivationConfigには、あらかじめAPサーバーに登録しておいたキューのJNDI名を指定する。
コンパイルしてデプロイする手順はステートレスセッションビーンのEJB本体と同様。
(JBoss4.2.3の場合、javax.jmsの為にJBOSS_HOME/server/default/lib/jboss-j2ee.jarが必要。[2008-08-16])
APサーバー(WebLogicのドメインやJBoss)を実行すると、このビーンはEJBコンテナに登録されて、待機に入る。[2008-08-04]
JMSのキューにメッセージが入れられると、当ビーンのインスタンスが作られてonMessage()が呼ばれる。
実行間隔が長ければ同じインスタンスが繰り返し使われるが、処理が終わらないうちに次のメッセージが来ると、新しいインスタンスが作られてそれが呼ばれる。
すなわちマルチスレッドで動作するので、EJBはスレッドセーフな作りにしなければならない。
POJOとは、Plain Old Java Objectのこと。[2008-08-24]
POJIとは、Plain Old Java Interfaceのこと。
要するに従来型のシンプルな(普通の)Javaのクラス・インターフェースのこと。
その反対は、EJB2のような、XMLファイルに定義を書く必要があるような複雑な定義方法をするクラス。
(XMLファイルに定義を書かないと動作させられないようなクラス)
EJB3.0ではPOJOやPOJIに対してアノテーションを付けるだけでよく、XMLファイルを書く必要がない(というが売り文句)。
参考: