TX その10 CMTがトランザクションを終了させる方法(と開始させる方法)
TxManagerに行く前にもう1点だけ確認すべき点が.
TxInterceptorCMTクラスで"Required"などのトランザクション有りの設定で対象のEJBが新しいトランザクションを開始した場合によばれるendTransactionメソッドの一部
try { // Marked rollback if (tx.getStatus() == Status.STATUS_MARKED_ROLLBACK) { tx.rollback(); } else { // Commit tx // This will happen if // a) everything goes well // b) app. exception was thrown tx.commit(); } }
setRollbackOnlyが呼ばれてトランザクションコンテキストのstatusが"STATUS_MARKED_ROLLBACK"になっている場合,ここでロールバックする.そうでなければcommitする.
まあこんなもんでいいんじゃないとも思ったが,1つ疑問が・・・.複数スレッドからほぼ同時にsetRollbackOnlyを呼ばれても大丈夫なのかな?
"if...else"だけでrollbackとcommitのどちらかを必ず行うようにコーディングされているので気になりました.間違えてcommitしちゃったりしないのかな.
TxManagerに対してsetRollbackOnlyメソッドを呼ぶと,"org.jboss.tm.TransactionImpl"クラスのsetRollbackOnlyがさらに呼ばれる.このsetRollbackOnlyがほぼ同時に複数スレッドから呼ばれた場合,setRollbackOnlyが呼ばれた際にSTATUS_ACTIVE, STATUS_PREPARING, STATUS_PREPAREDのどれかの状態の場合にはSTATUS_MARKED_ROLLBACKに変更するけど,既にSTATUS_MARKED_ROLLBACKやSTATUS_ROLLING_BACKになっている場合には状態変更を行わないので.
気になるのがその前に呼ばれるlock()メソッド.後からきたスレッドはこのlock()メソッドでwait()するようだ.おそらくnotify()されるのは最初のスレッドによってtx.rollbackが行われた後だと推測される.その時点でnotify()されると仮定すると,lock()メソッドの中では・・・
ふむふむ.IllegalStateExceptionがスローされる仕組みになっているらしい.問題なしみたい.
これはCMTに限った話なわけだけど,おそらくBMTで複数スレッドがほぼ同時にrollbackを呼んだ場合でも同じ動きになると思われる.
しかしまあ複数スレッドで1つのトランザクションコンテキストを共有するのか,と言えば普通はそうではないからそもそもあまり心配せんで良いかも.
ついでにトランザクションの開始についても
private int startTransaction(final Invocation invocation) throws Exception { // Get the old timeout and set any new timeout int oldTimeout = -1; if (tm instanceof TransactionTimeoutConfiguration) { oldTimeout = ((TransactionTimeoutConfiguration) tm).getTransactionTimeout(); int newTimeout = container.getBeanMetaData().getTransactionTimeout(invocation.getMethod()); tm.setTransactionTimeout(newTimeout); } tm.begin(); return oldTimeout; }
tm.beginでさっくりOK.