S2JMS 開発記 S2JMS-Core

S2JMS の最大目標は MDB 的な非同期メッセージ受信アプリケーションのお手軽なプラットフォームを提供することですが,例えば Web コンテナ上のアプリケーションから非同期メッセージを送信する場合に便利なクラスなども S2JMS-Core として提供します.
そんなわけで (どんなわけで?),S2JMS-Core について簡単に紹介.


S2JMS-Core を使ってメッセージを送信するには

  • org.seasar.jms.core.MessageSender

というインタフェースを使います.実装クラスはこれ.

  • org.seasar.jms.core.impl.MessageSenderImpl

こいつには単純にデータを送信するために次のメソッドがあります.

    • void send(String)
    • void send(byte[])
    • void send(Serializable)
    • void send(Map<String, Object>)

それぞれ,TextMessageBytesMessageObjectMessageMapMessage を作成して送信します.


OracleAQ の PayloadMessage など非標準のメッセージを送信するとか,メッセージのヘッダやプロパティを設定したい場合は

    • <MSGTYPE extends Message> void send(MessageFactory<MSGTYPE>)

を使います.引数の

  • org.seasar.jms.core.message.MessageFactory<MSGTYPE>

は送信するメッセージを作成するもので,メソッド

    • MSGTYPE createMessage(Session)

が返すメッセージがそのまま送信されます.
標準の実装クラスとして

  • org.seasar.jms.core.message.impl.TextMessageFactory
  • org.seasar.jms.core.message.impl.BytesMessageFactory
  • org.seasar.jms.core.message.impl.ObjectMessageFactory
  • org.seasar.jms.core.message.impl.MapMessageFactory

を用意しています.


メッセージを送信先MessageSenderImpl のプロパティ

    • void setConnectionFactory(ConnectionFactory)
    • void setDestinationFactory(DestinationFactory)

で設定します.
ConnectionFactory は JMS 標準のインタフェース.
S2JCAS2JMS-Connector によるコネクションプーリングを使うことができます.これは当然 JTA 管理のトランザクションと連携します.

  • org.seasar.jms.core.destination.DestinationFactory

送信先javax.jms.Destination を提供するもので,以下の実装クラスを用意しています.

  • org.seasar.jms.core.destination.QueueFactory
  • org.seasar.jms.core.destination.TopicFactory
  • org.seasar.jms.core.destination.SimpleDestinationFactory
  • org.seasar.jms.core.destination.JndiDestinationFactory
  • org.seasar.jms.core.destination.ReplyToDestinationFactory

QueueFactoryTopicFactory はそれぞれ QueueSession#createQueue(String)TopicSession#createTopic(String)Destination を作成します.
SimpleDestinationFactory は単にプロパティに設定された QueueTipicインスタンスを宛先とします.Queue なんかを JavaBeans として使える場合これがよいかと.
JndiDestinationFactory は文字通り JNDI から Queue なんかをルックアップします.Tomcat 上などで 宛先を JNDI で管理したい場合に使えるかもしれませんが,あまり使わないと思う.OracleAQ で必要になるかも? って感じ.
ReplyToDestinationFactory は返信用で,プロパティに設定された MessagegetJMSReplyTo() の戻り値を宛先とします.




メッセージの受信も似ていて,

  • org.seasar.jms.core.MessageReceiver

というインタフェースを使います.実装クラスはこれ.

  • org.seasar.jms.core.impl.MessageReceiverImpl

こいつには単純にメッセージを受信するために次のメソッドがあります.

    • String receiveText()
    • byte[] receiveBytes()
    • Serializable receiveObject()
    • Map receiveMap()

それぞれ,TextMessageBytesMessageObjectMessageMapMessage を受信してそのボディを返します.期待と異なるメッセージを受信するとキャストできずに例外が吹っ飛びます.たぶん.
プロパティの

    • void setTimeout(long)

タイムアウト時間を設定することができます.
不特定あるいは非標準のメッセージを受信するとか,受信したメッセージのヘッダやプロパティも扱いたい場合は

    • <MSGTYPE extends Message, T> T receive(MessageHandler<MSGTYPE, T>)

を使います.引数の

  • MessageHandler<MSGTYPE extends Message, T>

は受信したメッセージを処理するもので,こいつのメソッド

    • T handleMessage(MSGTYPE)

にメッセージが渡されます.標準の実装として

  • org.seasar.jms.core.message.impl.TextMessageHandler
  • org.seasar.jms.core.message.impl.BytesMessageHandler
  • org.seasar.jms.core.message.impl.ObjectMessageHandler
  • org.seasar.jms.core.message.impl.MapMessageHandler

を用意しています.


ということで,簡単な非同期メッセージの送受信は

public class Hoge {
    private MessageSender sender;
    private MessageReceiver receiver;

    public String requestReply(String text) {
        sender.send(text);
        return receiver.receiveText();
    }
    ・・・
}

って感じでできちゃいます.たぶん.
ただし,上記のコードはトランザクショナルなコネクションではうまくいかないはず.
トランザクションをコミットするまで送信は完結しないので,その返信が返ってくるはずがないという.




S2JMS-Core は AOP でメッセージを送信するためのインターセプタも提供します.

  • org.seasar.jms.core.interceptor.SendMessageInterceptor

これは MessageSenderImpl を継承したクラスで,任意のメソッドに適用するとそのメソッドが正常終了した後にメッセージを送信します.どんなメッセージを送信するかはプロパティに設定された MessageFactory 次第.
こいつを既存の Web アプリケーションに適用することで,普通の Web アプリがサクッと非同期メッセージングアプリケーションに.


当然,S2JMS-Container 上のアプリケーションに対して SendMessageInterceptor を適用することもできます.
それにより,JMS API を使うことなく,要求メッセージを受信して応答メッセージを返すアプリケーションを作成することも可能になります.
という感じです>こもりさん


TextMessage をサポートするクラスとして,Velocity を使って文字列を編集するクラスがあります.

  • org.seasar.jms.core.message.text.VelocityTextFormatter

こいつの

    • void setTemplateText(String)

にテンプレート文字列を設定すると,編集結果を

    • String getText()

から得ることができます.テンプレート文字列中の変数は S2 コンテナ中のコンポーネントとして解釈されます.
なので,

<component name="sendInterceptor" class="org.seasar.jms.core.interceptor.SendMessageInterceptor">

<component class="org.seasar.jms.core.message.impl.TextMessageFactory"/>

<component class="org.seasar.jms.core.message.text.VelocityTextFormatter">
    <property name="templateText">$employeeDto.empno $employeeDto.ename</property>
</component>

って感じの dicon を用意すると,SendMessageInterceptor を適用したメソッドが終了するたびに VelocityTextFormatter が編集したテキストが送信されます.


以下業務連絡.
ぼうず (id:bowez) さんにお願いです.
テンプレートを dicon に文字列として書くバージョンに加えて,外部のファイル (.vm ファイルっていうの?) のパスを指定するバージョンをお願いします.
あと,Velocity って 2004 年の春に 1.4 がリリースされているのに 1.3.1 を使っているのは何か理由があるのですか?