S2JMS 開発記 トランザクション境界と例外

S2JMS-Container 向けのメモ.
S2JMS-Container を使った受信メッセージ処理におけるトランザクション境界は,JMS メッセージをトランザクショナルに受信するかどうかの設定で異なります.


トランザクショナルにメッセージを受信する場合,トランザクション境界は S2JCA が設定します.
S2JMS-Container の MessageListener#onMessage(Message)トランザクション境界の中で呼び出されます.
ただし,S2JMS-Container 上のアプリケーションはこのトランザクション境界を前提にせず,DB アクセス等を行うなら適切なところに j2ee.txRequired 等のインターセプタを設定した方がいいと思います.


非トランザクショナルにメッセージを受信する場合,S2JCAトランザクション制御を行いません.
S2JMS-Container も行いません.
S2JMS-Container 上のアプリケーションは,適切なところに j2ee.txRequired 等のインターセプタを設定する必要があります.


S2JMS-Container が呼び出したアプリケーションのリスナーメソッドが例外をスローした場合の S2JMS-Container の対処は,メッセージをトランザクショナルに受信したかどうかで異なります.
トランザクショナルに受信したかどうかは,すでにトランザクションが開始されているかどうかで判断することができます.
TransactionManager#getTransaction() の戻り値が null でなければトランザクションが開始されています.


トランザクショナルに受信したメッセージの処理でアプリケーションが例外をスローした場合は,TransactionManager#setRollbackOnly() を呼び出して例外を破棄します.


非トランザクショナルに受信したメッセージの処理でアプリケーションが例外をスローした場合は...
どうしていいか分からない.(;_;)
メッセージの処理に失敗したことをリソースアダプタに伝えるには例外を投げるしかないはず.
でもでも,先にも書いたように少なくとも ActiveMQ は 10 回程度例外を投げるとそのコネクションではもうメッセージを受信できなくなってしまうわけで...
といって例外を投げなければ,リソースアダプタの設定次第では Ack が返されたり...
この辺は ActiveMQ 固有の設定の問題なのかなぁ.
このケースは継続して調査ということで.


出演予定 TV 番組

エビちゃんカレンダーの蛯原友里ちゃん

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

山田優
01/10 (火) 21:00〜22:48 TBS 「Goro'S Bar 新春ゴールデンSP」
桜井裕美
01/10 (火) 22:04〜22:10 CX 「旅美人」
香里奈
01/11 (水) 23:00〜23:30 CX 「空飛ぶグータン」

フレンドパークの香里奈見損ねた...
っていうか,優ちゃんと旅美人が重なってるじゃん...
おいら的には当然旅美人ですが.
っていうか,旅美人を優先予約にすれば概ね問題なし.


めざましテレビ

今日の早耳ムスメ澤野ひとみちゃん,お題は「今大人気!! あったかスイーツ」.
早くも今年二度目の登場のひとみちゃん,すっかりエース級.
それにしてもひとみちゃん,髪上げるとカワイイねぇ.
かなり魅力的だったかもぉ〜.
ヤヴァイ,この調子だとあさ美ちゃん・直ちんに続いて「ちゃーん」付けすることになるかも.
さすがは CanCam モデル,あなどれん!!

S2JMS 開発記 MessageEndpoint

MessageEndpoint のアクティブ化成功!! \(^o^)/
ActiveMQ のリソースアダプタを使って,Message Inflow によるメッセージの受信ができるようになりました.
これは S2JMS-Container のインフラとなる部分で,ここがちゃんと受信できるようになったのは個人的にとても意義深いのだ.
なんたって,JCA の仕様書読みながら試行錯誤って感じだったので,ちゃんとメッセージが受信できるまでは不安だったのです.
でもこれで一安心♪


S2JCA では,

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE components PUBLIC "-//SEASAR//DTD S2Container 2.3//EN"
	"http://www.seasar.org/dtd/components23.dtd">
<components namespace="jms-activemq-cf">
	<include path="j2ee.dicon"/>
	<include path="jca.dicon"/>

	<component class="org.seasar.jca.deploy.impl.RarResourceAdapterDeployer">
		<property name="path">
			"ra/activemq-ra-3.2.1.rar"
		</property>
		<initMethod name="start"/>
		<destroyMethod name="stop"/>
	</component>
	
	<component class="org.seasar.jca.deploy.impl.MessageEndpointDeployer">
		<property name="activationSpecClassName">
			"org.activemq.ra.ActiveMQActivationSpec"
		</property>
		<initMethod name="setProperty">
			<arg>"destinationType"</arg>
			<arg>"javax.jms.Queue"</arg>
		</initMethod>
		<initMethod name="setProperty">
			<arg>"destination"</arg>
			<arg>"foo"</arg>
		</initMethod>
		<initMethod name="activate"/>
		<destroyMethod name="deactivate"/>
	</component>

	<component class="org.seasar.jca.mi.MessageEndpointFactory">
		<property name="endpointClass">
			@org.seasar.jca.mi.JmsMessageEndpointImpl@class
		</property>
	</component>

	<component class="org.seasar.jca.cm.jms.ActiveMQTest$MessageListenerImpl"/>
</components>

こんな dicon を用意すると,一番下に定義した ActiveMQTest$MessageListenerImpl (文字通り javax.jms.MessageListener を実装したクラス) の onMessage(Message) メソッドにメッセージが飛んできます.
ConnectionSession なんてまったく気にする必要なし!!
送信よりカッコいいじゃん♪
当然 JTA によるトランザクション制御付きで,スレッドプールを使って複数メッセージを並行にバッサバッサ捌いちゃいます.


最終的には,ActiveMQTest$MessageListenerImpl のところが S2JMS-Container の提供するクラスになります.
まだ動いたっていうレベルですが,ここまでできれば一安心.
まだ例外の扱いやロギングが手抜きしまくりなのですが,今日中 (一眠りした後) にコミットしようと思います.


S2JMS 開発記 続 MessageEndpoint

コミットしますた.
S2JCA-ActiveMQactivemq/bin/activemq.bat を実行しておいて,org.seasar.jca.mi.jms.ActiveMQTest で動作確認ができます.
量が多いので分かりにくいですが,コンソール出力を見ると複数のスレッドで受信メッセージをバッサバッサと捌いている様子が窺えるはず.


ちょっと気になったことがあって,ActiveMQ は受信メッセージの処理が一定回数以上失敗に終わる (MessageLister#onMessage(Message) から例外がスローされる) と,そのコネクションからのメッセージ配信を中断してしまうようです.
これは特定のメッセージを何回失敗したらということではなく,コネクション単位で数えているっぽい.
自分の環境だと,連続じゃなくても通算 11 回 (たしか) 失敗するとそれ以降メッセージが来なくなります.
そうなると,その MessageEndpoint にはもはやメッセージが届かないため,受信アプリケーションとしては全くの役立たずになってしまうということに.
そんなわけで (どんなわけで?),その状況になったら MessageEndpoint を非アクティブ化して,新しい MessageEndpoint をアクティブ化すべきのような気のせいがしますが,その状況になっていることを知る手段ってある?
JCA 仕様には見当たらないような...


例外を伝えず単にロールバックするだけだと,そのメッセージは 5 回ばかり再配信される (通算 6 回 MessageListener#onMessage(Message) が呼び出される) っぽい.
この場合は何回ロールバックしてもコネクションは配信を続けてくれる模様.500 回くらいしか試してないけど.
そんなわけで (どんなわけで?),ActiveMQ に例外が伝わらないようにすればいいのかとも思ったのですが,JCA 仕様ではアプリケーションで発生した例外はリソースアダプタに渡せと書いてあるような.


ってことは,S2JMS-Container (JCA 仕様的にはアプリの一部) で例外をハンドリングすればいいのかも.
トランザクショナルなメッセージに限ると,本当のアプリケーションから例外が飛んできたら,S2JMS-Container は TransactionManager#setRollbackOnly() を呼び出して例外を捨ててしまえばいい,みたいな (もちろんロギングくらいはする).
そうすると ActiveMQ のデフォルトでは 5 回までリトライできるよ,と.
OracleAQ なんかもロールバックされたメッセージをリトライする上限を設定できるので,こういう場合は S2JMS-Container でリトライの制御をする必要はないわけですが,そうじゃない JMS 実装のことも一応考えておくテスト.