S2JMS 開発記 ExecutionContext と TransactionManager

WorkManager の簡単な実装が概ね終了.
「簡単な実装」というのはスレッドプールをきめ細かく制御とかしていないため.例えばスレッドのプライオリティとか,スレッドプールにリクエストするキューの最大長を設定したりプライオリティキューにしたりなどなど.
大がかりなアプリケーションサーバならこの辺もちゃんとしないといけないのかもしれませんが,スタンドアロンS2JMS-Container での利用を前提にするならあまり必要な状況にはならないような気のせいが.
そんなわけで (どんなわけで?),概ね終了.


「概ね終了」ということはまだ終了には至っていないわけですが,その大きなものが javax.resource.spi.work.ExecutionContext のサポート.これがやっかいなんですよ.
ExecutionContext というのは,リソースアダプタが受信したメッセージをワーカスレッド上で実行する際のトランザクションコンテキストを指定するもので,以下の二つのプロパティがあります.

  • transactionTimeout
  • xid

前者はまぁどうでもいいとして (いいのか?),問題は後者の xid.もちろん javax.transaction.xa.Xid です.


昨日も書いたように,JCA + JMS + MDB というか要するに J2EE 改め JavaEE なプラットフォームでは,JMS メッセージを受信するスレッドとそれを処理する MDB を実行するスレッドが異なるモデルを想定している模様.
これはちょうど,ネットワークサービスを提供するサーバアプリケーションなどで,listen()〜accept() するスレッドと,接続したコネクションからデータを受信して処理するスレッドを別にするのと同じようなものかな,と思います.
が,しかし.
JMS では接続待ちの代わりにメッセージの受信を行うわけですが,トランザクショナルにメッセージングを行う場合,その受信もトランザクションの一部になるという点に注意が必要.


んで,ですね.
JCA + JMS + MDB ではワーカスレッドで MDBMessageListener#onMessage(Message) を呼び出すわけですが,トランザクション境界はこの周囲に設定することになるっぽい.
つまり,ワーカスレッドで

  • TransactionManager#begin()
  • MessageListener#onMessage(Message)
  • TransactionManager#commit()

ってことになるみたいなんですが (これを実際に行うのが先に書いた MessageEndpoint),これより前,ワーカスレッドに処理が届く前に JMS 実装はメッセージを受信しちゃってるわけで,それをどうやってこのトランザクションの一部にしたらよかとですか?


そんなわけで (どんなわけで?),ExecutionContext です.
通常の JTA トランザクション制御では,リソースマネージャはトランザクションマネージャから渡された Xid を使うのですが,JCA の Message Inflow なシナリオではリソースマネージャはトランザクションマネージャと関わることなく自分で勝手に Xid を割り当ててトランザクションを開始してメッセージを受信しちゃう模様.
んで,その XidExecutionContext に詰め込んでアプリケーションサーバに渡します.
そしてアプリケーションサーバはリソースマネージャが割り当てた Xidトランザクションマネージャに「インポート」するみたいなんですねぇ.
といっても,JTA 仕様では TransactionManager あるいは Transaction にそんなインタフェースは用意されていないので,トランザクションマネージャ固有のやり方で実装しろってことみたい.うーみゅ...


当然,現在の S2JTA ではそんな機能を持っていないので,手を加えないといけない感じ.
たぶん,TransactionManagerImplbegin(Xid, XAResource) みたいなメソッドを追加するのかなぁ.
この XAResource はもちろんメッセージを受信したリソースアダプタなのですが,そいつの start(Xid, int) を呼び出すべきかどうかはまだ不明.どこに書いてあるの?
可能性としては,

  • リソースは開始されていない.TMNOFLAGSstart(Xid, int) を呼び出す.
  • リソースはサスペンドしている.TMRESUMEstart(Xid, int) を呼び出す.
  • リソースは開始されている.prepare(Xid) 等を呼び出すまでやることなし.

といった辺りかなぁ.
最悪 JBossGeronimo のコードを追っかけて同じような振る舞いにするしかないかも〜.あ,ActiveMQ のリソースアダプタをチェックした方がいいかも.


そんなわけで (どんなわけで?),ちょっと時間がかかりそうですが,どっちみちこの辺の制御は WorkManager ではなく MessageEndpoin で対応することになりそうなので,WorkManager としては一区切りかな.
ともあれ (JW),最新の S2JMSS2JCA を明日 (01/04) 中に seasar.org の SVN に突っ込みたいと思います.


出演予定 TV 番組

この近辺 (どこ?) で話題のモデルが出演するテレビ番組を分かるだけ掲載します.
新規分は赤字で (レギュラー除く).直近分は太字で.

香里奈
01/04 (水) 18:30〜20:54 TBS 「さんま玉緒のあんたの夢をかなえたろかSP」

さ〜て,新年最初の早耳ムスメは誰でしょうか?
直ちんではないっぽい?
まぁ,あまり期待しないでおこう.


某巨大掲示板

ぐはぁっ,またこっちに風向きが変わってる...
まぁ,年末年始は平穏だったからいいか.
それにしても 53 さん,あなたおもしろすぎます.超うけたよ.(^^;
そんなわけで (どんなわけで?),記念こぴぺ

53 名前:デフォルトの名無しさん[sage] 投稿日:2006/01/03(火) 20:34:30
 日本国内的にはもちろん、おそらく東京でもシェアにおいては伊東美咲の圧勝。


 東京近辺の技術的な集まりあるいは技術系ブログに蛯原友里のファンが出没するので
 「日本国内では蛯原友里が優勢」のような錯覚をそこに居合わせた技術者(or 東京滞在中の
 技術者)が抱くのかもしれない。


 おたくへのアプローチについては電車男への出演等を行わなかった"空白の三ヶ月"
 を巻き返すのは至難の業、その間に伊東美咲があきばで着実にシェアを伸ばしてしまった。


 「その透明感は大人気ない」だけで伊東美咲での優勢を覆すのはおそらく無理。


 あなたのblog以外で蛯原友里関連の書き込みが少ないのもマイナス要因
 かもしれない。


 #あなたが、他のモデルの写真を貼り付けているのををみたことはありませんね。逆にあなたに
 #問いたい、何故いつも蛯原友里なのですか?答えなくて結構です。
 #あなたの煽り文句(やっぱり CanCam 買うしか!!)を見ていると
 #ユーザをCanCamに意図的に誘導しようとしている"印象を受けます。


 #[蛇足 : 2005/12/29 14:00]
 #http://d.hatena.ne.jp/koichik/20051228#1135792832の書き込みで
 #Rayの陸守絵麻ちゃんを取り上げていますね。
 #これで"やっぱり CanCam 買うしか!!"と書かれてもあまり説得力が...。

なんていうか,説得力があるような気のせいがしてしまいました (苦笑).
はい,確かに私は多くの人を CanCam に意図的に誘導していました.心より恥じる.
確かに説得力はないかも.無念だ.


でも大丈夫です.今年のエビちゃんはきっとみーたんの領域まで飛躍します.間違いない.
そんなわけで (どんなわけで?),やっぱり CanCam 買うしか!!

S2JMS 開発記 S2JMS-Container

S2JMS-Container をこもりさんにお願いするべく,簡単なイメージを書いておくテスト.


まずは基本的な位置づけを.
JMS と JCA を含めた J2EE アーキテクチャでは,非同期メッセージの受信はリソースアダプタが行います.
リソースアダプタは通常 JMS プロバイダが提供します.ActiveMQ だと lib/optional の下にある activemq-ra-v.r.z.rar がそれです.
リソースアダプタはメッセージを受信するとアプリケーションサーバに渡します.そのための「端点」を表すのが javax.resource.spi.endpoint.MessageEndpoing です.
アプリケーションサーバMessageEndpoint の実装クラスでトランザクション制御など諸々のシステム的なサービスを付加します.
そして MessageEndpoint はアプリケーションを呼び出します.
JMS の場合はアプリケーションは javax.jms.MessageListener を実装します.


そんなわけで (どんなわけで?),

  • リソースアダプタ → MessageEndpointMessageListener

という流れが見えてきます.
んで,この MessageEndpointS2JCA で実装します.
そして MessageListener を実装するのが S2JMS-Container になります.


S2JMS-Container は MessageEndpoint から受け取った javax.jms.Message を本当のアプリケーションコンポーネントバインディングして,本当のリスナーメソッドを呼び出します.
中核の機能はこれだけ.たぶん.
まずは簡単な実装から始めるとすると,メッセージをバインディングするコンポーネントとリスナーメソッドを持つコンポーネントは同じだとして,

<component class="org.seasar.jms.container.JmsContainerImpl">
	<property name="componentName">hogeAction</componentName>
</component>

みたいにコンポーネントのキーをプロパティで受け取って,MessageListener#onMessage(Message) が呼び出されたらその都度 S2 コンテナからコンポーネント (たぶん instance="request") を取得して,アノテーションの付けられたプロパティに受信したメッセージのヘッダやプロパティ,ボディを設定し,同じくアノテーションで示されたリスナーメソッドを呼び出します.


S2JMS-Container は Web コンテナの外で動くので,そのままだと instance="request" なスコープは使えません.
そこで,独自の HttpServletRequest の実装を用意します.
最初は S2 に含まれているモックでもいいかも.いずれは HttpServletRequestgetHeader(String)Message のヘッダ (もしかしたらプロパティ) にアクセスできたら便利かも.
あ,受信した Message 自身も request スコープのコンポーネントとして登録して欲しいです.
最悪 Message を参照しないとできないケースはあると思うので.
他にも Message#getJMSReplyTo() の宛先に返信メッセージを送信したりとか.


といった感じです.
他にもアプリに便利なこととか思いついたらどんどん追加しちゃってください.
先の話としては,アプリケーションの処理が失敗 (例外がスローされた) した場合に更新した DB はロールバックするけど,メッセージは受信したことにする (つまり破棄する) 機能をサポートしたいと考えています.
ただしこれ,悩ましい課題が多いので,基本的なところができてからでいいかな,と.たぶん MethodInterceptor で実装できると思うので.


そんなわけで (どんなわけで?),よろしくお願いします.