S2JMS 開発記 S2JCA のパッケージ名

変更しますた.
これまで S2JCAorg.seasar.jca の後に,cm とか milmwm といった短い名前を使っていました.
これらはそれぞれ,Connection Management,Message Inflow,Lifecycle Management,Work Management の略称です.
これは JCA 仕様の「16. API Requirements」に出てくる由緒正しい略称なのですが,いかんせん短すぎて自分でも分からなくなりそうな気配濃厚.


そんなわけで (どんなわけで?),気持ち長めに変更しました.

カテゴリ
Connection Management cm outbound
Message Inflow mi inbound
Lifecycle Management lm lifecycle
Work Management wm work
Unit Test ut unit

最後のはついで.
もう一つついでに,Rar ファイル等を URL で扱うためのパッケージを org.seasar.jca.util.url から org.seasar.jca.url へ移動しました.


たぶん,S2JCA のクラスを直接使うことはほとんどなくて,あっても今回変更していない org.seasar.jca.deploy くらいだと思うので,あまり影響ないとは思いますが.
一応報告ということで.


P.S.
Eclipseリファクタリング機能でパッケージ名を変更したりしたのですが,親のパッケージと子のパッケージの名前を変更する順番なんかがまずかったのか,SVN にコミットする際にいっぱい怒られました...
コミットするだけで 1 時間近くかかったよ... 無念だ.
もし SVN からのアップデートでうまくいかない時は一度ワークスペースから S2JCA なんかを削除して,チェックアウトからやり直してください.m(_ _)m

S2JMS 開発記 S2JMS-Container と例外

この前書いた「トランザクション境界と例外」の続きです.
非トランザクショナルに受信したメッセージの処理で例外が発生した場合,デフォルトでは 10 件程度処理したところでメッセージが受信できなくなる現象について.


結局のところ,ActiveMQ のリソースアダプタはそういう動きをするということで FA っぽい.
org.activemq.ra.MessageEndpointProxy の内部クラス,MessageEndpointAliveonMessage(Message) メソッドは次のようになっています.

        public void onMessage(MessageEndpointProxy proxy, Message message) {
            try {
                ((MessageListener) proxy.endpoint).onMessage(message);
            } catch (RuntimeException e) {
                transition(proxy, GOING_TO_DIE);
                throw e;
            }            
        }

ここで proxy.endpointS2JCA が提供する MessageEndpoint で,その onMessage(Message) メソッドの先に本来は S2JMS-Container が,そのさらに先にはアプリケーションのリスナーメソッドがあることになります.
で,そこから例外が飛んでくると,transition(proxy, GOING_TO_DIE) ということで,こいつは死へと向かいます.
っていうか,GOING_TO_DIE はこの後エンドポイントの afterDelivery() を呼び出した後,DIE になります.
こうなるともう何が呼び出されてもひたすら InvalidMessageEndpointException をスローするのみです.


つ・ま・り
ActiveMQ のリソースアダプタは例外を飛ばすな,ということらしい.
それでいいのか? ActiveMQ リソースアダプタ.


JMS 仕様的には,「4.5.2 Asynchronous Delivery」に次のように記述されています.

It is possible for a listener to throw a RuntimeException; however, this is considered a client programming error. Well-behaved listeners should catch such exceptions and attempt to divert messages causing them to some form of application-specific ‘unprocessable message’ destination.

なので,例外を飛ばすのはバグだ,と.みたいなもんだ,と.
でもでも,それに続けて

The result of a listener throwing a RuntimeException depends on the session’s acknowledgment mode.

  • AUTO_ACKNOWLEDGE or DUPS_OK_ACKNOWLEDGE - the message will be immediately redelivered. The number of times a JMS provider will redeliver the same message before giving up is provider-dependent. The JMSRedelivered message header field will be set for a message redelivered under these circumstances.

となっていて,例外が飛んだ場合は再配信してくれてもよさげ.
ActiveMQ 本体は再配信してくれそうなので,ActiveMQ リソースアダプタがこれを無視してるって感じかなぁ.
うーみゅ,Sun の Generic JMS ResourceAdapter もチェックしてみるか.


ともあれ (JW),S2JCAS2JMS-Container としてはやはり例外は全部捕まえましょう.
んで,メッセージの再配信が必要なアプリケーションではトランザクショナルに受信してもらうということで.
そもそも非トランザクショナルってことは大して信頼できないわけだしね.


S2JMS 開発記 訂正の訂正:ExecutionContext と TransactionManager

01/03 に書いた「ExecutionContext と TransactionManager」は大間違いだったと 01/05 に訂正したのですが,結局最初の方が正しかったみたい.
少なくとも ActiveMQ はメッセージの受信を TcpTransportChannel とかいうスレッドで受信して,内部的なキューを通してワーカスレッドにディスパッチしています.


じゃあ,その TcpTransportChannel はどういうトランザクションコンテキストで実行されているのか謎ですが,とりあえずは気にしなくていいみたい.
結局,ActiveMQXAResourceトランザクションに enlist してあげれば受信したメッセージもそのトランザクションの一部になります.


ActiveMQ は XA に対応しているわけですが,これがどんな風に実装されているかもちょっと見ておいた方がいいのかなぁ.
新年会ではぶさんが気にしていたし.
個人的にはっていうか自分の仕事的には IBM Websphere MQ と Oracle AQ がメインで (非トランザクショナルだと Tibco 製品も),実案件で ActiveMQ を使う可能性は皆無に近そうなのであまり気にならないのですが.


S2JMS 開発記 S2JMS-Server

S2JMS-Container の検討はこもりさんが進めてくれているので,手つかずで残っている S2JMS-Server をちょっと検討.
S2JMS-Server は非同期メッセージを受信して処理するスタンドアプリケーションを起動するために提供するもので,実行可能 Jar にしたいなと考えています.
つまり,コマンドラインから

java -jar s2jms-server-1.0.0.jar

なんてすると S2JMS-Server が起動して,メッセージの受信が始まってアプリで処理される,みたいな.


自分的にはこういうのに求める機能ってあまりないのですが,何か必要でしょうか?
何かって何かさっぱりですが,要望があればコメントなりトラックバックなり ML へのメールなりお願いします.


普通,実行可能 Jar って依存する Jar を MANIFEST.MF に記述すると思うのですが,S2JMS-Server の場合は依存する Jar 自体が多いのと,依存する S2 なんかのバージョンがどんどん変わることを考えると MANIFEST.MF に記述するのはちょっとイマイチ.
アプリの Jar なんて事前には分からないし.
といって,普通に CLASSPATH 環境変数 (あるいは JVM 引数の -classpath) で指定してもらうのも面倒かも.


そんなわけで (どんなわけで?),その辺をちょっとだけラクチンにしたい感じ.
例えば S2JMS-Server の Jar 自身と同じディレクトリにある Jar は勝手にクラスパスに追加するとか.
あと,引数でディレクトリを指定したらそのディレクトリにある Jar は勝手に以下同文.
そうやって見つけた Jar を持つ URLClassLoader を作成してコンテキストクラスローダーに設定してから S2 コンテナを初期化してくれれば,ちょっと楽かも〜.
そんなもんスクリプトでって声は聞こえない (笑).
あ,dicon ファイルも引数で指定できた方がいいのかな.省略時は app.dicon ってことで.


なので,

java -jar s2jms-server-1.0.0.jar [-classpath classpath] [-dicon dicon]

とか.--classpath--dicon でも可.
classpath は普通に File#pathSeparator で区切られたパスで,ただしパスがディレクトリで,その直下に Jar ファイルがある場合はそのディレクトリでなく,Jar を指定したものと見なすってことで.
もし

java -jar s2jms-server-1.0.0.jar -classpath classes:lib

ってやったら classes ディレクトリと lib の下にある Jar がクラスパスに含まれる感じ.


あ,S2JMS-Server の main(String[]) メソッド (っていうか main スレッド) は S2 コンテナを初期化した後はやることなさそうですが,終了するとプロセスも終了しちゃう (他は通常デーモンスレッドのみ) ので,ひたすら待機する必要があります.
プロセスの終了はシャットダウンフックで.


こんなものかなぁ.
んで,この S2JMS-Server,ぼうずさん (id:bowez) にお願いしたいなぁ〜.
Velocity といい,全然 JMS と関係ないところばかりお願いして申し訳ないんですが.