この前書いた「トランザクション境界と例外」の続きです.
非トランザクショナルに受信したメッセージの処理で例外が発生した場合,デフォルトでは 10 件程度処理したところでメッセージが受信できなくなる現象について.
結局のところ,ActiveMQ のリソースアダプタはそういう動きをするということで FA っぽい.
org.activemq.ra.MessageEndpointProxy
の内部クラス,MessageEndpointAlive
の onMessage(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.endpoint
が S2JCA が提供する 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),S2JCA & S2JMS-Container としてはやはり例外は全部捕まえましょう.
んで,メッセージの再配信が必要なアプリケーションではトランザクショナルに受信してもらうということで.
そもそも非トランザクショナルってことは大して信頼できないわけだしね.