このトピックではメッセージ駆動型 Bean の開発の概要について説明します。内容は以下のとおりです。
メッセージ駆動型 Bean とは、JMS メッセージの処理専用のサーバサイド オブジェクトです。これらの Bean は、各メソッド呼び出しが他のメソッド呼び出しに依存しないという点において、ステートレスです。セッション Bean およびエンティテイ Bean とは異なり、メッセージ駆動型 Bean は他の Bean やクライアント アプリケーションによって呼び出されることはありません。その代わり、メッセージ駆動型 Bean は JMS メッセージに応答します。
メッセージ駆動型 Bean は、他の EJB やクライアントによって呼び出されることがないため、インタフェースがありません。各メッセージ駆動型 Bean に、JMS メッセージを処理するための単一のメソッド onMessage が定義されます。メッセージ駆動型 Bean を他の EJB で呼び出すことはできませんが、メッセージ駆動型 Bean は他の EJB を呼び出せます。また、メッセージ駆動型 Bean は、JMS メッセージを送信することもできます。他のタイプの EJB の場合と同様に、EJB コンテナが、処理およびメッセージ確認応答に利用するのに十分なインスタンスを作成するなど、Bean 環境の管理を行います。
Workshop では、weblogic.ejb.GenericMessageDrivenBean を拡張し、javax.ejb.MessageDrivenBean と javax.jms.MessageListener を実装するクラスを作成することによってメッセージ駆動型 Bean を開発します。このクラスに、EJB の特性を指定する @MessageDriven アノテーションを追加します。
[WebLogic メッセージ駆動型 Bean] テンプレートを使用することによって、IDE で簡単に開発を開始できます。テンプレートを使用する場合、IDE では次のようなコードが生成されます。
/**
* Workshop によって自動生成される GenericMessageDrivenBean
* サブクラス。
*
* onMessage() メソッドを完成させ、MessageDriven アノテーションを確認して、
* 設定値が目的の用途に適合していることを確認する。
*/
@MessageDriven(ejbName = "MyMessageDrivenBean",
destinationJndiName = "MyMessageDrivenBeanJndiName",
destinationType = "javax.jms.Queue")
public class MyMessageDrivenBean
extends GenericMessageDrivenBean
implements MessageDrivenBean, MessageListener {
private static final long serialVersionUID = 1L;
/*
* (non-Javadoc)
*
* @see javax.jms.MessageListener#onMessage(javax.jms.Message)
*/
public void onMessage(Message msg) {
// 重要 : ここにコードを追加する
}
}
注意 : [WebLogic メッセージ駆動型 Bean] テンプレートを使用するには、[プロジェクト・エクスプローラ] でその Bean が存在するパッケージを右クリックし、[新規|その他] を選択して [EJB] を展開してから [WebLogic メッセージ駆動型 Bean] をクリックします。
@MessageDriven アノテーションを使用して、メッセージ駆動型 Bean が対話する JMS 送り先のタイプと JNDI 名を指定します。詳細については、下記の「トピックおよびキュー」を参照してください。
メッセージ駆動型 Bean の中核となる特徴は、非同期処理という概念です。クライアント アプリケーションは、JMS メッセージを送信して、ある特定のビジネス タスクを実行することができます。メッセージ送信後、クライアント アプリケーションは即座に処理を続行でき、JMS メッセージが受信されて処理されるのを待つ必要がありません。これは、ビジネス タスクが複雑であったり、エンティティ (およびセッション) Bean を使用する必要があったり、完了までに長い時間がかかったりする場合に、特に有益です。それに対して、この同じクライアント アプリケーションがセッション Bean を使用してある特定のビジネス タスクを実行する場合は、セッション Bean メソッドが完了してクライアント アプリケーションにコントロールを返すまで、待機する必要があります。メッセージ ファサード設計パターンが、このメッセージ駆動型 Bean の使用を形式化して、クライアント アプリケーションとエンティティ Bean とが非同期を実現するための仲介役とします。
メッセージ駆動型 Bean のもう 1 つの重要な特徴は、各 JMS メッセージが同時に処理されるということです。つまり、各 Bean インスタンスが処理するメッセージは一度に 1 つずつですが、EJB コンテナが、ある瞬間におけるメッセージのロードを処理するのに十分な Bean インスタンスの作成を行います。WebLogic では、コンテナが作成する Bean インスタンスの最初の数と最大数を設定できます。詳細については、「weblogic.ejbgen.MessageDriven」を参照してください。
メッセージ駆動型 Bean がステートレスであり、JMS メッセージの処理が非同期メッセージで起こるため、メッセージが送信順に処理される保証はありません。したがって、あるメッセージが別のメッセージの処理の正常な完了に依存する形で、複数のメッセージを送信することによって、予期しない結果が生じることがあります。むしろ、ある 1 つのメッセージがタスクの一部を処理することなどによって実行を開始でき、その後、ビジネス タスクの残りの部分について、別のメッセージ駆動型 Bean によって処理される JMS メッセージを送信するように、ビジネス タスクのきめ細かさを再検討する必要があります。
メッセージ駆動型 Bean は、JMS メッセージの特定のチャネル (「送り先」) をリスンします。チャネルには、トピックとキューの、2 種類があります。トピックは、特定のメッセージが多様なサブクスライバ、つまり同じトピックをリスンしている多様なメッセージ駆動型 Bean クラス (インスタンスではない) によって受信され得る、パブリッシュ/サブスクライブ メッセージング モデルを実装しています。それに対してキューは、複数のクラスがこのキューをリスンしているときでも、特定のメッセージがただ 1 つのメッセージ駆動型 Bean クラスによって受信される、ポイント ツー ポイント メッセージング モデルを実装しています。
送り先のタイプ (トピックまたはキュー) と JNDI 名は、@MessageDrive アノテーションの属性で指定します。
EJB コンテナは、メッセージ駆動型 Bean インスタンスのプールを作成します。インスタンス作成時に、setMessageDrivenContext() メソッドおよび ejbCreate() メソッドを呼び出します。この時点で、メッセージ駆動型 Bean ではメッセージを受信する準備が整っています。Bean インスタンスが JMS メッセージを処理しているときに、onMessage メソッドが呼び出されます。Bean インスタンスを削除する際、インスタンスのガベージ コレクションの準備が整う前に、EJB コンテナはまず ejbRemove メソッドを呼び出します。メッセージ駆動型 Bean のライフ サイクルを、次の図に示します。
WebLogic でメッセージ駆動型 Bean を定義する際、多くの場合は特定のビジネス タスクを実行するための onMessage メソッドを実装し、ejbCreate メソッドを使用して、メッセージ駆動型 Bean の onMessage メソッドによって呼び出されるエンティティ Bean のホーム インタフェースをルックアップするなど、一度だけ実行が必要なアクションを実装します。簡単なメッセージ駆動型 Bean の一般的な例を以下に示します。ejbCreate メソッドは Recording エンティティ Bean のホーム インタフェースを見つけるのに使用され、onMessage メソッドはメッセージを処理して Recording Bean を呼び出します。
@EjbLocalRefs( { @EjbLocalRef(link = "Recording") }) @MessageDriven(ejbName = "Statistics", destinationJndiName = "jms.EJBTutorialSampleJmsQ", destinationType = "javax.jms.Queue") public class StatisticsBean extends GenericMessageDrivenBean implements MessageDrivenBean, MessageListener { private static final long serialVersionUID = 1L; private RecordingHome recordingHome; public void ejbCreate() { try { javax.naming.Context ic = new InitialContext(); recordingHome = (RecordingHome) ic .lookup("java:/comp/env/ejb/Recording"); } catch (NamingException ne) { System.out.println("Encountered the following naming exception: " + ne.getMessage()); } } public void onMessage(Message msg) { try { // メッセージを読み取る MapMessage recordingMsg = (MapMessage) msg; String bandName = recordingMsg.getString("bandName"); String recordingTitle = recordingMsg.getString("recordingTitle"); // レーティングのためのプレースホルダ ロジック Random randomGenerator = new Random(); String rating = new Integer(randomGenerator.nextInt(5)).toString(); // レコーディングと共にレーティングを保存する Recording album = recordingHome .findByPrimaryKey(new RecordingBeanPK(bandName, recordingTitle)); album.setRating(rating); } catch (Exception e) { System.out.println("Encountered the following exception: " + e.getMessage()); } } }
オブジェクトを削除する前にクリーンアップが必要な場合は、ejbRemove メソッドを実装できます。また、EJB コンテナが提供する javax.ejb.MessageDrivenContext へのアクセスが必要な場合は、setMessageDrivenContext メソッドを実装できます。MessageDrivenContext にはコンテナに関する情報、具体的にはコンテナのトランザクション メソッドが含まれています。詳細については、お好きな J2EE ドキュメントを参照してください。WebLogic でデフォルトで定義されているメッセージ駆動型 Bean は weblogic.ejb.GenericMessageDrivenBean を拡張しています。これは、onMessage メソッド以外のすべてのメソッドに対して空の定義を提供します。したがって、Bean の定義で onMessage メソッドを実装する必要があります。