TX その3 Transaction Internals

4.2. JBoss Transaction Internalsについてのメモ

  • JBossではTransactionManager以下,全ての要素をJTAのインタフェースを通して利用している.よっぽど特殊な要望がなければやらないだろうが,JBossTXの代わりに別のJTA実装を突っ込むことも実際可能.JNDI名"java:/TransactionManager"で登録すればあとはインタフェースを通じて動くはず.

ふと思ったんだけど,インタフェースをばしばし切ることでプロダクトとしての柔軟性,移植性は格段にあがる.でもソースを読んで内部設計をリバースエンジニアリングする立場からするとインタフェースが1枚増えるごとに難しくなっていくような・・・.JMXとJNDIが犯人.蛇足ですが.



ソースからのリバースエンジニアリング

  • UserTransactionをJNDIに登録しているのは"jboss:service=ClientUserTransaction"MBean
  • こいつはXMBeanで実装クラスはこれ"org.jboss.tm.usertx.server.ClientUserTransactionService"クラス
  • UserTransactionの実装クラスは"org.jboss.tm.usertx.client.UserTransaction"クラス
  • UserTransactionインスタンスはSingletonによりJVM内で1つ.
  • UserTransaction.getSingleton()メソッドで取得可能.
  • ClientUserTransactionサービスの起動時にJNDI名"UserTransaction"でそのインスタンスがJNDIにbindされる.
  • このClientUserTransactionクラスがjavax.transaction.UserTransactionインタフェースを実装している.
  • begin, commit, rollbackその他いろいろ実装している.
  • インナークラスのThreadInfoクラスをjava.lang.ThreadLocalを使ってスレッドごとに格納.これによってTPC(Transaction Propagation Contexts)のリストをスレッドごとに保持している.
  • ClientUserTransactionクラスはいろいろ実装しているとはいっても実際には"org.jboss.tm.usertx.interfaces.UserTransactionSession"クラスに委譲している.
  • このUserTransactionSessionインスタンスはJNDI名"UserTransactionSessionFactory"で登録されているファクトリのnewInstanceメソッドで取得する.
  • UserTransactionSessionインスタンスへの参照をUserTransactionインスタンスごとに持っている.
  • このUserTransactionSessionとUserTransactionSessionFactoryは実はインタフェース.
  • 実装クラスはたぶん"org.jboss.tm.usertx.server.UserTransactionSessionImpl"クラスと"org.jboss.tm.usertx.server.UserTransactionSessionFactoryImpl"クラス

ほら追いかけていてもこうやってまた途切れてしまう.他に候補なさそうだから"たぶんそうだろう"という予測をしているだけ.誰がいつどの実装クラスを使って"UserTransactionSessionFactory"をbindしたのかを別経路から探さないと正確な答えは出ない.

→ソースディレクトリ全検索したがこいつをbindしている個所は発見できず.どーなっているのやら.



昼飯後,続投

  • "UserTransactionSessionFactory"をbindした犯人の手がかりが見つかった.jboss-service.xmlに定義されている"jboss:service=proxyFactory,target=ClientUserTransactionFactory"という名前を持つMBean.予想だが,これはの属性で指定した情報を元にあれこれしてくれる便利サービスのようだ.実装クラスは"org.jboss.invocation.jrmp.server.JRMPProxyFactory"クラス.ただしJRMPProxyFactoryに渡されているのは"org.jboss.tm.usertx.interfaces.UserTransactionSessionFactory"であって"org.jboss.tm.usertx.server.UserTransactionSessionFactoryImpl"ではない.
  • jmx-consoleのJNDI-VIEWのところには"UserTransactionSessionFactory (proxy: $Proxy11 implements interface org.jboss.tm.usertx.interfaces.UserTransactionSessionFactory,interface org.jboss.proxy.IClientContainer)"と書いてある.やっと意味が分かってきた.どうやらJNDIにbindされるのはこのJRMPProxyFactoryによって生成されたプロキシである模様.
  • JRMPProxyFactoryでは"java.lang.reflect.Proxy"をつかってプロキシを作っている.

java.lang.reflect.Proxyなんて始めて見ました.こういうところで使うんだ〜,へ〜,驚き.

  • プロキシインスタンスはUserTransactionSessionFactoryインタフェースで定義されたメソッドの呼び出しに対応するメソッドを全て持っている.動的プロキシとか言うらしい.そして呼ばれるとハンドラのinvokeという汎用メソッドを呼び出す.ハンドラは"org.jboss.invocation.jrmp.server.ClientContainer"クラス.
  • このハンドラのinvoke汎用メソッドで呼ばれるのは最初に定義されたインターセプタのinvoke汎用メソッド.

→インターセプタはjboss-service.xmlのmbean定義のとこに書いてあったやつね.最初のインターセプタは"org.jboss.proxy.ClientMethodInterceptor"これかな? これはたいしたことはやってない.
→その次は"org.jboss.invocation.InvokerInterceptor"これ.setterメソッドでどこからか貰ったInvokerインスタンスへさらに呼び出しを委譲している.またここでdecouple(分離)ですか.なんかもう迷宮のようになってきた.この線はここであきらめます・・・



UserTransactionSessionImplとUserTransactionSessionFactoryImplは使われていない

  • ログを追加で出力するようにしてみたが,使われているようすが確認できない.とりあえず使われていることが確認できれば良い,との立場で最後の手段を使ってみた.UserTransactionSessionFactoryImplがUserTransactionSessionを生成するところに"throw new RuntimeException()"を突っ込む.しかしまったく例外が発生しないままJBossが起動し,そしてその次は例の負荷テストを流してみたもののまったく反応なし.



ServerVMClientUserTransactionが正解でした.orz
ついに捕まえた.

2005-06-15 15:18:48,538 INFO  [STDOUT] ikdttr3 hogehogehogehoge
2005-06-15 15:18:48,538 ERROR [org.jboss.resource.connectionmanager.CachedConnectionManager] Starting failed jboss.jca:service=CachedConnectionManager
java.lang.RuntimeException: ikdttr3 daffa
	at org.jboss.tm.usertx.client.ServerVMClientUserTransaction.getSingleton(ServerVMClientUserTransaction.java:61)
	at org.jboss.resource.connectionmanager.CachedConnectionManager.startService(CachedConnectionManager.java:218)
	at org.jboss.system.ServiceMBeanSupport.jbossInternalStart(ServiceMBeanSupport.java:272)
	at org.jboss.system.ServiceMBeanSupport.jbossInternalLifecycle(ServiceMBeanSupport.java:222)
	at sun.reflect.GeneratedMethodAccessor2.invoke(Unknown Source)
	at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
	at java.lang.reflect.Method.invoke(Method.java:324)
	at org.jboss.mx.interceptor.ReflectedDispatcher.invoke(ReflectedDispatcher.java:144)
	at org.jboss.mx.server.Invocation.dispatch(Invocation.java:80)
	at org.jboss.mx.server.Invocation.invoke(Invocation.java:72)
	at org.jboss.mx.server.AbstractMBeanInvoker.invoke(AbstractMBeanInvoker.java:249)
	at org.jboss.mx.server.MBeanServerImpl.invoke(MBeanServerImpl.java:642)
	at org.jboss.system.ServiceController$ServiceProxy.invoke(ServiceController.java:891)

EJBのgetSessionContext().getUserTransaction()で得られるやつは"org.jboss.tm.usertx.client.ServerVMClientUserTransaction"だった.Factoryを使わず,static変数にクラスロード時に初期化するタイプのSingletonオブジェクト.

JBoss&MySQLのXA単体テストとかをする場合には,このクラスを使ってあげればいいわけですね.

しかし,よーーーーく思い出してみると・・・

4.2.2. The Default Transaction Manager

JBoss is by default configured to use the fast in-VM transaction manager. This transaction manager is very fast, but does have two limitations.

in-VMのやつがデフォルトで使われると言っているじゃん.これのことだよ(泪
UserTransactionSessionImplはRemoteのときだけ!