Transactions 開発ガイド
JTA、JTS、XTS APIを利用したアプリケーション開発
エディッション 5.1.2
概要
パート I. JTA 開発
第1章 Java Transaction API (JTA) の紹介
- アプリケーションサーバー
- EJBサーバーなどトランザクションのステート管理を含むアプリケーションランタイム環境のサポートが必要なインフラストラクチャを提供します。
- Transaction Manager
- トランザクションのデマケーション、トランザクショナルなリソース管理、同期化、トランザクションコンテキスト伝播の対応を必要とするサービスと管理機能を提供します。
- リソースマネージャ
- (リソースアダプタ[1])を使い)、アプリケーションがリソースにアクセスできるようにします。トランザクションリソースインターフェースを実装することで、リソースマネージャは、拡散トランザクションに参加します。トランザクションマネージャは、このインターフェースを使い、トランザクションの関係、トランザクションの完了、リカバリを伝達します。
- 通信資源管理 (CRM: Communication Resource Manager)
- 送受信リクエストを受けるためにトランザクションサービスへのアクセス、トランザクションサービスにアクセストランザクションコンテキストの伝播をサポートします。
注記
第2章 JBoss JTA 実装
- アプリケーショントランザクションのデマケーションインターフェース (ハイレベル)
- アプリケーションサーバー向けのトランザクションマネージャインターフェース (ハイレベル)
- トランザクショナルなリソースマネージャに向けたX/Open XA protocol の標準Java マッピング
重要
2.1. UserTransaction
UserTransaction
インターフェースは、アプリケーションがトランザクション境界を制御できるようにします。また、このインターフェースはトップレベルのトランザクションを開始、コミット、ロールバックするためのメソッドを提供します。ネスト化されたトランザクションはサポートされておらず、呼出しているスレッドがすでにトランザクションと紐付いている場合begin
メソッドはNotSupportedException
をスローします。UserTransaction
は自動的に新しく作成されたトランザクションと呼出しているスレッドを自動で関連付けます。
注記
UserTransaction
を取得できます。
InitialContext ic = new InitialContext(); UserTransaction utx = ic.lookup("java:comp/UserTransaction")
- com.arjuna.ats.jta.jtaTMImplementation プロパティを
com.arjuna.ats.internal.jta.transaction.arjunacore.TransactionManagerImple
に設定します。 - com.arjuna.ats.jta.jtaUTImplementation プロパティを
com.arjuna.ats.internal.jta.transaction.arjunacore.UserTransactionImple
に設定します。
2.2. TransactionManager
TransactionManager
インターフェースにより、管理されているアプリケーションの代わりにアプリケーションサーバーがトランザクション境界を制御できるようになります。
注記
TransactionManager
を取得可能です。
InitialContext ic = new InitialContext(); TransactionManager utm = ic.lookup("java:/TransactionManager")
null
あるいは特定のグローバルトランザクションを参照します。複数のスレッドが同じグローバルトランザクションと紐付けられている場合もあります。ネスト化されたトランザクションには対応していません。
TransactionManager
のbegin
メソッドは、新しいトップレベルトランザクションを開始し、呼出中のスレッドとトランザクションコンテキストを関連づけます。呼出中のスレッドがすでにトランザクションと紐付いている場合、begin
メソッドはNotSupportedException
をスローします。
getTransaction
メソッドは、呼出中のスレッドと紐付けられているトランザクションコンテキストを示すトランザクションオブジェクトを返します。このオブジェクトを使い、対象のトランザクション上で様々な操作を実行可能です。これらの操作については別のセクションにて説明されています。
commit
メソッドは、呼出中のスレッドと紐付いているトランザクションを完了させます。メソッドが返された後、呼出中のスレッドはどのトランザクションとも関連付けがなくなります。スレッドがトランザクションコンテキストと関連付けがないときにcommit
が呼び出されると、例外がスローされます。実装によってはトランザクションの発信元のみがcommit
操作を利用可能となっています。呼出中のスレッドにトランザクション実行の許可がされていない場合、例外がスローされます。また、JBossJTAはスレッドのトランザクション終了機能に制限を課しません。
rollback
メソッドを使い、現在のスレッドに紐付いているトランザクションをロールバックします。rollback
メソッドの完了後は、このスレッドはトランザクションとの関連がなくなります。
注記
IllegalStateException
例外をスローすることで、コミットあるいはロールバック中にこの通知を実行します。
2.3. トランザクションの停止および再開
suspend
メソッドを呼出し、呼出中のスレッドに紐付いた現在のトランザクションを一時的に停止します。このスレッドがどのトランザクションとも紐付いていない場合、null
オブジェクト参照が返されます。そうでない場合は、有効なTransaction
オブジェクトが返されます。Transaction
オブジェクトはその後、resume
メソッドに渡されトランザクションコンテキストを復帰させます。
resume
メソッドは、指定したトランザクションコンテキストと呼出中のスレッドを紐付けます。指定されたトランザクションが有効な場合、トランザクションコンテキストは呼出中のスレッドと関連付けられます。そうでない場合は、スレッドはどのトランザクションとも紐付けられません。
注記
resume
メソッドが呼び出されるとトランザクションマネージャはIllegalStateException
例外をスローします。
Transaction tobj = TransactionManager.suspend(); .. TransactionManager.resume(tobj);
注記
2.4. トランザクションインターフェース
Transaction
インターフェースにより、対象オブジェクトと紐付いたトランザクション上で操作を実行できるようにします。トランザクションが作成されると、トップレベルのトランザクションはすべてTransaction
オブジェクト1つと紐付けられます。以下の目的でTransaction
オブジェクトを利用することができます。
- アプリケーションにより利用中のトランザクショナルリソースを登録
- トランザクション同期のコールバックを登録
- トランザクションのコミットあるいはロールバック
- トランザクションの状態を取得
commit
および rollback
メソッドにより、対象オブジェクトがコミットあるいはロールバックできるようになります。呼出中のスレッドでは、スレッドに同じトランザクションを紐付ける必要はありません。呼出中のスレッドでトランザクションのコミットができない場合、トランザクションマネージャは例外をスローします。JBossJTA はトランザクションを終了するスレッドに対して制限を課しません。
2.5. リソースの登録
XAResource
オブジェクトと合わせ、enlistResource
メソッドを呼び出します。XAResource
の実装が障害発生時のリカバリにどのように影響をあたえるかについては、別セクションにて説明しています。
XAResource.start
メソッド呼出しにて適切なフラグを渡します。
delistResource
メソッドを使い、対象オブジェクトのトランザクションコンテキストから指定のリソースとの関連付けを解除します。アプリケーションサーバーはこのメソッドを2つのパラメータで呼び出します。
- リソースを示す
XAResources
オブジェクト - オペレーションの原因が停止されたトランザクション (
TMSUSPEND
)であるか、作業の一部が失敗したのか(TMFAIL
)、アプリケーションにより通常のリソースがリリースされたのかを示すフラグ
XAResource
との関連付けを終了するように通知します。フラグの値により、アプリケーションサーバーが同じリソースに戻り、リソースの状態をそのまま保つ予定かを示すことができます。トランザクションマネージャは、基盤リソースマネージャへのXAResource.end
メソッドコールにて適切なフラグ値を渡します。
2.6. トランザクションの同期
Synchronization
コールバックオブジェクトを完了前あるいは後のいずれかに呼び出すように登録できます。
beforeCompletion
メソッドが、2相トランザクションの完了プロセスを開始する前に呼び出されます。この呼出は、TransactionManager.commit
を開始した呼出元と同じトランザクションコンテキストで実行されるか、あるいは、Transaction.commit
が利用されている場合はトランザクションコンテキストなしに実行されます。- トランザクションが完了すると、
afterCompletion
メソッドが呼び出されます。トランザクションの状態がパラメータで提供され、このメソッドはトランザクションコンテキストなしに実行されます。
2.7. Transaction Equality
Transaction
オブジェクトのequals
メソッドを実装し、対象オブジェクトと別のTransaction オブジェクトを比較できるようになります。対象オブジェクトとパラメータオブジェクトの両方が同じグローバルトランザクションを参照している場合、equals
メソッドはtrue
を返します。
Transaction txObj = TransactionManager.getTransaction(); Transaction someOtherTxObj = .. .. boolean isSame = txObj.equals(someOtherTxObj);
第3章 リソースマネージャ
3.1. XAResource インターフェース
javax.transaction.xa.XAResource
インターフェースは、 XA
インターフェースのJava マッピングで、拡散トランザクションの処理環境にてResource Manager (リソースマネージャ) と Transaction Manager (トランザクションマネージャ)の間のコントラクトを定義します。リソースアダプタは、XAResource
インターフェースを実装し、トップレベルのトランザクションをリソースに紐付けるサポートをします。関係データベースは、このようなリソースの一例となっています。
XAResource
インターフェースに対応可能です。アプリケーションは複数のデータベース接続を介してデータにアクセスすることができます。各データベースの接続は、基盤のリソースマネージャインスタンスに対するプロキシオブジェクトとして機能するXAResource
オブジェクトと紐付いています。また、トランザクションマネージャは、トップレベルトランザクションに参加するリソースマネージャごとにXAResource
を取得します。start
およびend
メソッドはリソースとトランザクションを紐付けし、紐付けを解除します。
start
と end
の呼出しのいずれかをデータに対して行う全作業とトランザクションを関連付けます。トランザクションのコミット時に、トランザクションマネージャはこれらのトランザクショナルなリソースマネージャに対し、2相コミットプロトコルに従いトランザクションを準備、コミットあるいはロールバックするよう指示を出します。
XAResource
は標準のXA
インターフェースと以下の点で異なります。
- リソースマネージャの初期化は、接続取得時にリソースアダプタにより暗黙的に実行されます。
xa_open
と同等となるものはありません。 Rmid
は、引数として渡されず、Rmid
はそれぞれ、別々のXAResource
オブジェクトにより表現されます。- Java がマルチスレッド処理に対応しており、データベースの多くが非同期操作に対応していないため、非同期操作は対応していません。
- トランザクションマネージャにより
XAResource
オブジェクトを正しく処理されない場合に出されるエラーの戻り値は、XAException
クラスによりJava の例外にマッピングされます。 - Thread of Control におけるDTP の概念では、
XAResource
とConnection
オブジェクトへのアクセスを全Java スレッドへマッピングします。例えば、2つの別スレッドは、同じXAResource
オブジェクト上でstart
とend
操作を実行できます。
3.1.1. XAResource 制御の継承
XAResource
オブジェクトがJTA準拠のトランザクションサービスに登録されると常に、XAResource
オブジェクトについて、2相コミットプロトコルの間に呼び出される順番の制御はできません。しかし、JBoss Transaction Service はcom.arjuna.ats.jta.resources.StartXAResource
と com.arjuna.ats.jta.resources.EndXAResource
のインターフェース2つの順番を制御するようサポートしています。これらのインターフェースからのXAResource
インスタンスを継承することで、ご利用中のクラスインスタンスがコミットプロトコルの最初、あるいは最後のどちらに呼び出すかを管理します。
注記
prepare
のサポートなし)を 2 相対応の参加者を操作するトランザクションへ参加させることができ、JBossTS は LRCO に対応しています。
XAResource
実装をcom.arjuna.ats.jta.resources.LastResourceCommitOptimisation
マーカーインターフェースに拡張する必要があります。Transaction.enlistResource
を介してリソースを登録する際、JBoss Transaction Service があるとLastResourceCommitOptimisation
参加者のみがそれぞれのトランザクション内で利用できるようにします。コミットプロトコールの最後にご利用中のリソースが駆動され、prepare
メソッドの呼出しはされません。
注記
LastResourceCommitOptimisation
クラスのインスタンスを複数参加させようとすると失敗し、false
がTransaction.enlistResource
から返されます。com.arjuna.ats.jta.allowMultipleLastResources プロパティをtrue
に設定することでこの動作をオーバーライドすることができます。詳細については、必ず1相対応リソースの複数登録に関する項を参照するようにしてください。
3.1.2. 1 相対応リソースの複数参加
prepare
ステートに入らないため、複数のリソース間でトランザクションの結果においてアトミック性を獲得することができません。他のリソースステートを認識せず、後続のリソースが異なる選択をした場合でもそれを取り消す方法なしに、トランザクションのコーディネータが指示を出すとすぐにコミットあるいはロールバックします。そのため、データの破損やヒューリスティックな結果が発生することがあります。
- 補正トランザクションのリソースをラップ
- レガシー実装を2相対応と同等なものに移行
重要
"You have chosen to enable multiple last resources in the transaction manager. This is transactionally unsafe and should not be relied upon.”
か、あるいは、このトランザクションに1相対応リソースが複数登録されている場合は“This is transactionally unsafe and should not be relied on.”
となります。
3.2. リソースマネージャを開く
XA
インターフェースは、他のxa_
呼出しを行う前にxa_open
を使い、トランザクションマネージャがリソースマネージャを初期化する必要があります。JTA では、リソースマネージャを表すリソースアダプタ内に埋め込まれるように、リソースマネージャを初期化する必要があります。トランザクションマネージャは、リソースマネージャを初期化する方法を知る必要はありません。トランザクション関連の作業開始時と終了時、そして、トランザクションの完了時にのみ、リソースマネージャに通知する必要があります。リソースアダプタは、リソースマネージャへの接続が確立されるとリソースマネージャを開きます (初期化します)。
3.3. リソースマネージャを閉じる
- トランザクションマネージャが利用中のリソースに紐付いたトランザクションを
start
およびend
するだけでなく、トランザクション完了プロセスを調整することができるXAResource
オブジェクト - アプリケーションが基盤リソース (RDBMS のJDBC 操作など) で操作を実行できるようにする接続オブジェクト
close
メソッドを呼び出すと、リソースアダプタはアプリケーションが持つ接続のオブジェクト参照を無効にし、アプリケーションサーバーにclose
の通知をします。トランザクションマネージャはXAResource.end
メソッドを呼出し、接続とトランザクションの紐付けを解消します。
close
の通知により、接続プーリングの場合は、アプリケーションサーバーが必要なガーベッジコレクションを実行し物理XA接続を再利用可能とマーキングできるようにします。
3.4. Thread of Control (スレッド)
XA
インターフェースは、トランザクションの紐付けに関連するXA を同じスレッドのコンテキストから呼び出す必要があると指定します。この thread-of-control の要件は、アプリケーションのスレッドがメソッドの呼出時に動的にディスパッチされるという、オブジェクト指向でコンポーネントベースのアプリケーションランタイム環境には適用できません。接続が複数のメソッド呼出しにわたる場合、別のスレッドが同じ接続リソースを使いリソースマネージャにアクセスする場合もあります。アプリケーションサーバーの実装によっては、異なるスレッドが同じXAResource
オブジェクトに関与する場合があります。リソースコンテキストやトランザクションコンテキストは、スレッドコンテキストから独立して機能する場合もあるため、異なるスレッドによりstart
や end
メソッドを呼び出すことができます。
XAResource
オブジェクトの使用やリソースマネージャへ関連付けられた接続の使用を許可する場合、アプリケーションサーバーは常時、トランザクションコンテキスト1つのみがそのリソースに紐付けられているようにしなければなりません。そのため、XAResource
インターフェースでは、リソースマネージャがいずれのスレッドコンテキストからの2相コミットプロトコルにも対応できるようにする必要があります。
3.5. トランザクションの関連付け
start
メソッドでトランザクションをトランザクショナルリソースと関連付け、end
メソッドでリソースから関連付けを解除します。リソースアダプタは内部で、リソースの接続オブジェクトとXAResource
オブジェクトの関連を保持します。どの時点であれ、接続は0あるいは1つのトランザクションと関連付けられています。JTAはネスト化されたトランザクションをサポートしないため、別のトランザクションが紐付いた接続上でstart
メソッドを呼び出すことができません。
start
および end
が各トランザクションコンテキストのスイッチに対して正しく呼び出されている限り、同じリソースを複数のトランザクションコンテキストとインターリーブすることができます。リソースが異なるトランザクションで利用されるたびに、そのリソースと紐付いた以前のトランザクションに対してend
メソッドを呼び出し、現在のトランザクションコンテキストに対してstart
メソッドを呼び出す必要があります。
3.6. 外部制御の接続
Transaction.enlistResource
メソッドを呼び出すことで、利用中のXAResource
オブジェクトをトランザクションと関連づける必要があります。
3.7. リソースの共有
commit
プロセスを開始することができます。2相コミットプロトコルに利用するリソースオブジェクトは、完了済みのトランザクションと関連付ける必要はありません。
commit
処理に対しXAResource
メソッドを同時に呼び出す複数のスレッドを処理する必要があります。以下のコードは、トランザクショナルリソースr1
を宣言しています。グローバルトランザクションxid1
は、r1
で開始、終了されます。その後、別のグローバルトランザクションxid2
がr1
と関連づけられます。一方で、トランザクションマネージャは、r1
あるいは、同じリソースマネージャに接続されている別のトランザクショナルリソースを利用して、xid1
に対し2相コミットプロセスを開始している可能性もあります。リソースアダプタは、リソースが別のグローバルトランザクションと関連付けられている間にコミットプロセスの実行を許可する必要があります。
XAResource xares = r1.getXAResource(); xares.start(xid1); // associate xid1 to the connection .. xares.end(xid1); // disassociate xid1 to the connection .. xares.start(xid2); // associate xid2 to the connection .. // While the connection is associated with xid2, // the TM starts the commit process for xid1 status = xares.prepare(xid1); .. xares.commit(xid1, false);
3.8. ローカルおよびグローバルのトランザクション
XAResource
インターフェースは使いません。同じ接続を利用してローカルおよびグローバルトランザクションの両方を実行する場合、以下のルールを適用します。
- 接続内でグローバルトランザクションを開始する前に、ローカルトランザクションをコミット (あるいはロールバック) する必要があります。
- グローバルトランザクションは、ローカルトランザクションが開始される前に接続との関連付けを解消する必要があります。
3.9. トランザクションのタイムアウト
XAResource
インターフェースは、リソースマネージャに伝播する現在のトランザクションと関連づいたタイムアウトができるような操作に対応しており、リソースマネージャに関連づいたデフォルトのタイムアウトをオーバーライドします (対応している場合)。長時間実行しているトランザクションのライフサイクルがデフォルトよりも長い場合に、これは便利です。タイムアウトが変更されないと、トランザクションが終了する前にリソースマネージャがロールバックするため、結果的にトランザクションもロールバックすることになります。
- ローカルのJTA
- com.arjuna.ats.arjuna.coordinator.defaultTimeout プロパティに秒数を設定します。デフォルト値は 60 秒。
- JTS
- com.arjuna.ats.jts.defaultTimeout プロパティに秒数を設定します。デフォルト値は0で、トランザクションのタイムアウトはありません。
setTransactionTimeout
をXAResource
インスタンスで呼び出すかどうかに関して、JBoss Transaction Service は all or nothing のアプローチを採っています。
true
(デフォルト) に設定されると、全インスタンスで呼び出されます。代わりに、com.arjuna.ats.jta.common.Configuration
のsetXATransactionTimeoutEnabled
メソッドを使うこともできます。
3.10. 動的な登録
XAResource
では動的登録に対応していません。理由は以下の通りです。
- Java コンポーネントベースのアプリケーションサーバー環境では、アプリケーションが接続を明示的にリクエストするとリソースマネージャへの接続は動的に取得されます。これらのリソースは随時、トランザクションマネージャに登録されます。
- リソースマネージャが動的にグローバルトランザクションへその作業を登録する必要がある場合、リソースアダプタと基盤のリソースマネージャの間にあるプライベートなインターフェースを介して、リソースアダプタのレベルで実行することができます。
第4章 トランザクションリカバリ
4.1. 障害回復
XAResource.recover
メソッドを使いprepared
あるいは heuristically completed
のステートにあるトランザクションの一覧をリトリーブします。通常、システム管理者は、システムにデプロイされているアプリケーションが使うトランザクションリソースファクトリをすべて設定します。例えば、JDBC XADataSource
オブジェクトはJDBC XAConnection
オブジェクトのファクトリです。
XAResource
オブジェクトは複数のシステム障害間で永続性がないため、トランザクションマネージャは、システム障害が発生する前にトランザクションに参加している可能性のあるリソースマネージャを示すXAResource
オブジェクトを獲得する機能が必要です。例えば、トランザクションマネージャはJNDI 検索メカニズムを利用し、各トランザクションリソースファクトリからの接続を取得してから、各接続に対し該当のXAResource
オブジェクトを取得しているなどです。トランザクションマネージャは、XAResource.recover
メソッドを呼出し、各リソースマネージャに対しprepared
あるいは heuristically completed
のステートにあるトランザクションに戻るよう指示します。
注記
*
の値は、ノード識別子が何であれ、全トランザクションのリカバリ (ロールバックも可能な場合もある) を強制します。これは慎重に利用するようにしてください。
XAResource
のクラッシュリカバリをすべて自動的に管理します。そうでない場合は、以下のリカバリメカニズムのうち1つを使います。
XAResource
がシリアル化可能な場合、トランザクションのコミット時にはシリアル化された形式が保存され、リカバリ時にはこの形式が利用されます。再作成されたXAResource
は有効で、紐付いたデータベース上でリカバリを駆動することができると想定されています。com.arjuna.ats.jta.recovery.XAResourceRecovery
、com.arjuna.ats.jta.recovery.XARecoveryResourceManager
、com.arjuna.ats.jta.recovery.XARecoveryResource
インターフェースを利用します。詳細については、障害回復に関するJDBCの項を参照してください。
4.2. XAConnectionsのリカバリ
com.arjuna.ats.jta.recovery.XAResourceRecovery
実装を1つ提供する必要があります。
注記
XAResourceRecovery
インスタンスについてリカバリシステムに通知するには、プロパティ経由でクラス名を指定します。properties
ファイルにあるプロパティあるいは、ランタイム時に登録されたプロパティでcom.arjuna.ats.jta.recovery.XAResourceRecovery の名で始まるものについてはこれらのプロパティを示すとみなされ、値は、com.arjuna.ats.jta.recovery.XAResourceRecoveryOracle=com.foo.barRecovery
などのクラス名となります。
com.arjuna.ats.jta.recovery.XAResourceRecoveryOracle=com.foo.barRecovery;myData=hello
などのように、セミコロンのあとに指定することができます。
注記
property
ファイルのJTAセクションに入れる必要があります。
public interface XAResourceRecovery { public XAResource getXAResource () throws SQLException; public boolean initialise (String p); public boolean hasMoreResources (); };
initialize
- このインスタンスが作成されてから、プロパティ値の定義にて最初のセミコロンの後にある追加情報はオブジェクトに渡されます。このオブジェクトは実装固有の形式でこの情報を利用することができます。
hasMoreResources
- 各
XAResourceRecovery
実装は、複数のXAResource
インタンスを提供可能です。getXAResource
へ呼び出す前に、hasMoreResources
を呼出し、さらに接続を取得する必要があるかを決定します。戻り値がfalse
の場合、このリカバリ一括処理時にgetXAResource
は再度呼び出されず、このインスタンスは次のリカバリスキャンまで無視されます。 getXAResource
XAResource
オブジェクトのインスタンスを返します。これがどのように作成されるか (また、コンストラクタに対するパラメータがどのように取得されるか) は、XAResourceRecovery
実装によって決まります。このクラスのコンストラクタに対するパラメータは、最初のドライバやデータソース作成時に利用されるパラメータと類似していなければならず、さらにはリカバリ実行に利用可能な新規のXAResources
インスタンス作成に十分な数が必要です。
注記
XAResourceRecovery
インスタンスを呼出したい場合、hasMoreResources
がfalse
を返し現在のスキャン作業を終了するように指示を出すと、次のリカバリスキャン時にはtrue
が必ず返されるようにします。
4.3. XAResouceRecovery の代わり
XAResourceRecovery
が利用する iterator ベースのアプローチは、ステート管理機能と併せて実装する必要があります。結果、必要のない複雑性を伴ってしまいます。JBoss Transaction Service では、パブリックインターフェースの実装を以下のように提供することができます。
com.arjuna.ats.jta.recovery.XAResourceRecoveryHelper { public boolean initialise(String p) throws Exception; public XAResource[] getXAResources() throws Exception; }
getXAResources
メソッドが呼び出され、アレイの要素ごとにリカバリを試行します。リソースマネージャの大半は、recover
メソッドが複数のXid を返すことができるため、アレイ内に必要なのはXAResource
を1つのみとなっています。
XAResourceRecovery
インターフェースのインスタンスとは違って、XAResourceRecoveryHelper
のインスタンスはアプリケーションコードで構築され、XARecoveryModule.addXAResourceRecoveryHelper
を呼び出すことでJBoss Transaction Service に登録されます。
initialize
メソッドはJBoss Transaction Service により呼び出されるわけではありませんが、このメソッドを提供し今後のリリースでさらに設定オプションを追加できるようにしています。
XAResourceRecoveryHelper
インスタンスの登録を解除することができます。リカバリスキャンが実行されている間はしばらく、登録解除がブロックされます。
XAResourceRecoveryHelper
インスタンスの追加、削除を動的に行う機能は便利です。このような場合、動作をクロスローディングする際は慎重に行ってください。
第5章 JDBC およびトランザクション
5.1. トランザクションJDBC ドライバの利用
5.1.1. トランザクションの管理
5.1.2. 制限
5.2. トランザクショナルドライバ
java.sql.Driver
インターフェースを実装するcom.arjuna.ats.jdbc.TransactionalDriver
インターフェースを使いドライバを呼び出します。
5.2.1. ドライバのロード
TransactionalDriver arjunaJDBC2Driver = new TransactionalDriver();
java.sql.DriverManager
) は、Java システムプロパティにドライバインスタンスを追加することでこのインスタンスを管理します。jdbc.drivers プロパティには、ドライバクラス名のリストが含まれており、コロンで区切られています。これの初期化時にJDBC ドライバマネージャはロードされます。
Class.forName()
メソッドを使い 1つあるいは複数のドライバをロードすることができます。
Class.forName("sun.jdbc.odbc.JdbcOdbcDriver");
Class.forName()
メソッドを呼び出すと、このドライバは自動的にJDBC ドライバマネージャに登録されます。JDBC ドライバのインスタンスを明示的に作成することも可能です。
sun.jdbc.odbc.JdbcOdbcDriver drv = new sun.jdbc.odbc.JdbcOdbcDriver(); DriverManager.registerDriver(drv);
5.3. 接続
5.3.1. 接続の確立
5.3.2. JBossJTA JDBC ドライバのプロパティ
com.arjuna.ats.jdbc.TransactionalDriver
クラスにこれらを設定します。
- userName
- データベースへ接続を試行する際に使うユーザ名
- password
- データベースへ接続を試行する際に使うパスワード
- createDb
- これを
true
に設定すると、接続時にこのドライバはデータベースを構築しようと試みます。これは、全JDBC 2.0 の実装で対応されていない可能性があります。 - dynamicClass
- dynamicClass: これは、JNDI を利用するのではなく、クラスを指定しデータベースへ接続するためにインスタンス化します。
5.3.3. XADataSources
XADataSources
から、拡散トランザクション内で参加するこれらの接続を取得します。JBossJTA は、データベースへの接続が確立されると、適切なDataSource を使い、XAResources
を取得しJTAインターフェースを使ってトランザクションに問うとくします。トランザクションサービスは、トランザクション終了時にこれらのXAResources
を利用することで、データベースをトリガーしJDBC 接続経由で追加された変更をコミットあるいはロールバックします。
5.3.3.1. Java Naming and Directory Interface (JNDI)
TransactionalDriver
クラスを作成するには、JNDI に登録されているXADataSource
(XADataSource
インタンスを作成し、適切なJNDI 実装に保存する必要があり) を使います。
XADataSource ds = MyXADataSource(); Hashtable env = new Hashtable(); String initialCtx = PropertyManager.getProperty("Context.INITIAL_CONTEXT_FACTORY"); env.put(Context.INITIAL_CONTEXT_FACTORY, initialCtx); initialContext ctx = new InitialContext(env); ctx.bind("jdbc/foo", ds);
Properties dbProps = new Properties(); dbProps.setProperty(TransactionalDriver.userName, "user"); dbProps.setProperty(TransactionalDriver.password, "password"); TransactionalDriver arjunaJDBC2Driver = new TransactionalDriver(); Connection connection = arjunaJDBC2Driver.connect("jdbc:arjuna:jdbc/foo", dbProps);
ArjunaJDBC2Driver
インターフェースが認識できるように、JNDI URL はjdbc:arjuna:
から始める必要があります。
5.3.3.2. 動的なクラスのインスタンス化
5.3.3.3. 接続の利用
close
メソッドの例外として、接続に対してアプリケーションレベルで実行された操作はすべて、このトランザクション固有の接続のみを使います。
Statement stmt = conn.createStatement(); try { stmt.executeUpdate("CREATE TABLE test_table (a INTEGER,b INTEGER)"); } catch (SQLException e) { // table already exists } stmt.executeUpdate("INSERT INTO test_table (a, b) VALUES (1,2)"); ResultSet res1 = stmt.executeQuery("SELECT * FROM test_table");
5.3.3.4. 接続プール
close
メソッドリクエストを発行するまで、リクエストは無視されます。
5.3.3.5. 接続の再利用
true
に設定することができます。
jdbc:arjuna:sequelink://host:port;databaseName=foo;reuseconnection=true
5.3.3.6. トランザクションの終了
5.3.3.7. AutoCommit
true
と設定されている場合、SQL ステートメントの実行は、別のトップレベルトランザクションとなっており、複数のステートメントをまとめて1つのOTSトランザクション内で管理することができません。そのため、JBossJTA は、JDBC 1.0 接続では利用前にAutoCommit を無効にします。AutoCommit が後にアプリケーションによりtrue
に設定された場合、JBossJTA はjava.sql.SQLException
の例外を出します。
5.3.3.8. 分離レベルの設定
注記
第6章 例
6.1. JDBC の例
例6.1 JDBCTest の例
public class JDBCTest { public static void main (String[] args) { /* */ Connection conn = null; Connection conn2 = null; Statement stmt = null; // non-tx statement Statement stmtx = null; // will be a tx-statement Properties dbProperties = new Properties(); try { System.out.println("\nCreating connection to database: "+url); /* * Create conn and conn2 so that they are bound to the JBossTS * transactional JDBC driver. The details of how to do this will * depend on your environment, the database you wish to use and * whether or not you want to use the Direct or JNDI approach. See * the appropriate chapter in the JTA Programmers Guide. */ stmt = conn.createStatement(); // non-tx statement try { stmt.executeUpdate("DROP TABLE test_table"); stmt.executeUpdate("DROP TABLE test_table2"); } catch (Exception e) { // assume not in database. } try { stmt.executeUpdate("CREATE TABLE test_table (a INTEGER,b INTEGER)"); stmt.executeUpdate("CREATE TABLE test_table2 (a INTEGER,b INTEGER)"); } catch (Exception e) { } try { System.out.println("Starting top-level transaction."); com.arjuna.ats.jta.UserTransaction.userTransaction().begin(); stmtx = conn.createStatement(); // will be a tx-statement System.out.println("\nAdding entries to table 1."); stmtx.executeUpdate("INSERT INTO test_table (a, b) VALUES (1,2)"); ResultSet res1 = null; System.out.println("\nInspecting table 1."); res1 = stmtx.executeQuery("SELECT * FROM test_table"); while (res1.next()) { System.out.println("Column 1: "+res1.getInt(1)); System.out.println("Column 2: "+res1.getInt(2)); } System.out.println("\nAdding entries to table 2."); stmtx.executeUpdate("INSERT INTO test_table2 (a, b) VALUES (3,4)"); res1 = stmtx.executeQuery("SELECT * FROM test_table2"); System.out.println("\nInspecting table 2."); while (res1.next()) { System.out.println("Column 1: "+res1.getInt(1)); System.out.println("Column 2: "+res1.getInt(2)); } System.out.print("\nNow attempting to rollback changes."); com.arjuna.ats.jta.UserTransaction.userTransaction().rollback(); com.arjuna.ats.jta.UserTransaction.userTransaction().begin(); stmtx = conn.createStatement(); ResultSet res2 = null; System.out.println("\nNow checking state of table 1."); res2 = stmtx.executeQuery("SELECT * FROM test_table"); while (res2.next()) { System.out.println("Column 1: "+res2.getInt(1)); System.out.println("Column 2: "+res2.getInt(2)); } System.out.println("\nNow checking state of table 2."); stmtx = conn.createStatement(); res2 = stmtx.executeQuery("SELECT * FROM test_table2"); while (res2.next()) { System.out.println("Column 1: "+res2.getInt(1)); System.out.println("Column 2: "+res2.getInt(2)); } com.arjuna.ats.jta.UserTransaction.userTransaction().commit(true); } catch (Exception ex) { ex.printStackTrace(); System.exit(0); } } catch (Exception sysEx) { sysEx.printStackTrace(); System.exit(0); } }
6.2. 障害回復に向けた BasicXARecovery の例
XAResources
に対しXAResourceRecovery
を実装します。setParametersで提供されるパラメータには、作成後のクラスを初期化するのに必要な任意情報が含まれています。ここでは、このファイルが把握しているデータベースの接続情報を含むプロパティファイル名、接続回数を含んでいます。値はセミコロンで区切られています。
XAResourceRecovery
の全機能が網羅されているわけではない点を理解していただくことが重要です。実際、raw テキストファイルにユーザ名やパスワードなどのデータベース接続情報をこの例のように保存することは推奨されません。
- DB_x_DatabaseURL=
- DB_x_DatabaseUser=
- DB_x_DatabasePassword=
- DB_x_DatabaseDynamicClass=
注記
例6.2 XAResourceRecovery の例
/* * Some XAResourceRecovery implementations will do their startup work here, * and then do little or nothing in setDetails. Since this one needs to know * dynamic class name, the constructor does nothing. */ public BasicXARecovery () throws SQLException { numberOfConnections = 1; connectionIndex = 0; props = null; } /* * The recovery module will have chopped off this class name already. The * parameter should specify a property file from which the url, user name, * password, etc. can be read. * * @message com.arjuna.ats.internal.jdbc.recovery.initexp An exception * occurred during initialisation. */ public boolean initialise (String parameter) throws SQLException { if (parameter == null) return true; int breakPosition = parameter.indexOf(BREAKCHARACTER); String fileName = parameter; if (breakPosition != -1) { fileName = parameter.substring(0, breakPosition - 1); try { numberOfConnections = Integer.parseInt(parameter.substring(breakPosition + 1)); } catch (NumberFormatException e) { return false; } } try { String uri = com.arjuna.common.util.FileLocator.locateFile(fileName); jdbcPropertyManager.propertyManager.load(XMLFilePlugin.class.getName(), uri); props = jdbcPropertyManager.propertyManager.getProperties(); } catch (Exception e) { return false; } return true; } /* * @message com.arjuna.ats.internal.jdbc.recovery.xarec {0} could not find * information for connection! */ public synchronized XAResource getXAResource () throws SQLException { JDBC2RecoveryConnection conn = null; if (hasMoreResources()) { connectionIndex++; conn = getStandardConnection(); if (conn == null) conn = getJNDIConnection(); } return conn.recoveryConnection().getConnection().getXAResource(); } public synchronized boolean hasMoreResources () { if (connectionIndex == numberOfConnections) return false; else return true; } private final JDBC2RecoveryConnection getStandardConnection () throws SQLException { String number = new String("" + connectionIndex); String url = new String(dbTag + number + urlTag); String password = new String(dbTag + number + passwordTag); String user = new String(dbTag + number + userTag); String dynamicClass = new String(dbTag + number + dynamicClassTag); Properties dbProperties = new Properties(); String theUser = props.getProperty(user); String thePassword = props.getProperty(password); if (theUser != null) { dbProperties.put(TransactionalDriver.userName, theUser); dbProperties.put(TransactionalDriver.password, thePassword); String dc = props.getProperty(dynamicClass); if (dc != null) dbProperties.put(TransactionalDriver.dynamicClass, dc); return new JDBC2RecoveryConnection(url, dbProperties); } else return null; } private final JDBC2RecoveryConnection getJNDIConnection () throws SQLException { String number = new String("" + connectionIndex); String url = new String(dbTag + jndiTag + number + urlTag); String password = new String(dbTag + jndiTag + number + passwordTag); String user = new String(dbTag + jndiTag + number + userTag); Properties dbProperties = new Properties(); String theUser = props.getProperty(user); String thePassword = props.getProperty(password); if (theUser != null) { dbProperties.put(TransactionalDriver.userName, theUser); dbProperties.put(TransactionalDriver.password, thePassword); return new JDBC2RecoveryConnection(url, dbProperties); } else return null; } private int numberOfConnections; private int connectionIndex; private Properties props; private static final String dbTag = "DB_"; private static final String urlTag = "_DatabaseURL"; private static final String passwordTag = "_DatabasePassword"; private static final String userTag = "_DatabaseUser"; private static final String dynamicClassTag = "_DatabaseDynamicClass"; private static final String jndiTag = "JNDI_"; /* * Example: * * DB2_DatabaseURL=jdbc\:arjuna\:sequelink\://qa02\:20001 * DB2_DatabaseUser=tester2 DB2_DatabasePassword=tester * DB2_DatabaseDynamicClass=com.arjuna.ats.internal.jdbc.drivers.sequelink_5_1 * * DB_JNDI_DatabaseURL=jdbc\:arjuna\:jndi DB_JNDI_DatabaseUser=tester1 * DB_JNDI_DatabasePassword=tester DB_JNDI_DatabaseName=empay * DB_JNDI_Host=qa02 DB_JNDI_Port=20000 */ private static final char BREAKCHARACTER = ';'; // delimiter for parameters
com.arjuna.ats.internal.jdbc.recovery.JDBC2RecoveryConnection
クラスは、初期接続の作成時に利用したパラメータを使いデータベースへの新規接続を構築できます。
第7章 JBossJTA の設定
7.1. オプションの設定
JTA 設定オプションとデフォルト値
- com.arjuna.ats.jta.supportSubtransactions
- デフォルト値:Yes/No
- com.arjuna.ats.jta.jtaTMImplementation
- com.arjuna.ats.jta.jtaUTImplementation
- デフォルト値: com.arjuna.ats.internal.jta.transaction.arjunacore.TransactionManagerImple/com.arjuna.ats.internal.jta.transaction.jts.TransactionManagerImple
- com.arjuna.ats.jta.xaBackoffPeriod
- com.arjuna.ats.jdbc.isolationLevel
- デフォルト値:対応のJDBC分離レベル
- com.arjuna.ats.jta.xaTransactionTimetouEnabled
- デフォルト値:true / false
第8章 JBoss Enterprise Application PlatformにてJBossJTA を利用
8.1. サービス設定
etc
ディレクトリに保存されているXML ファイルを使い行われます。ただし、JBoss サービスとして実行されている場合、追加の属性をいくつか設定可能です。
- TransactionTimeout
- 新規トランザクションに対して使われるデフォルトのトランザクションタイムアウト。整数値 (秒数)
- StatisticsEnabled
- トランザクションサービスが統計情報を収集するかを決定。
PerformanceStatistics
MBean を使い実行可能。デフォルト値がPerformanceStatistics
のBoolean 値 - PropagateFullContext
- 完全なトランザクションコンテキストをコンテキストインポータ / エクスポータにより伝播するか決定。
false
に設定されていると、現在のトランザクションコンテキストのみを伝播。true
に設定されていると、親トランザクションを含む、完全なトランザクションコンテキストを伝播します。
server/all/conf
ディレクトリにあるjboss-service.xml
ファイルにて、MBean 属性として指定します。例については例8.1「jboss-services.xml
の例」 を参照してください。
例8.1 jboss-services.xml
の例
<mbean code="com.arjuna.ats.jbossatx.jts.TransactionManagerService" name="jboss:service=TransactionManager"> <attribute name="TransactionTimeout">300</attribute> <attribute name="StatisticsEnabled>true</attribute>> </mbean>
etc
ディレクトリ内のJBoss Transaction Service install に置かれています。これらのファイルを手動あるいはJMXを使い編集することができます。各プロパティファイルは、com.arjuna.ts.properties
と、設定予定の属性を含むモジュール名と組み合わせから派生した名前を持つオブジェクトを使い公開します。例えば、com.arjuna.ts.properties:module=arjuna
などです。
8.2. ロギング
TransactionManagerService
サービスは、jbossjta-properties.xml
ファイルに提示されているcom.arjuna.common.util.logger プロパティの値をオーバーライドすることで、一部のログメッセージのレベルを修正します。そのため、このプロパティの値は、トランザクションサービスがJBoss Enterprise Application Platformに埋め込まれていると、ロギングの動作に影響をあたえません。log4j_releveler
ロガーの利用を強制することで、TransactionManagerService
サービスにより、トランザクションコード内にあるINFO
レベルのメッセージを全て、 DEBUG
メッセージとして表示されるように変更します。副次的な影響として、フィルターレベルがINFO
の場合、これらのメッセージはログファイルに表示されません。その他のログメッセージはすべて通常通り、動作します。
8.3. サービス
TransactionManagerService
のみとなっています。このサービスは、java:/TransactionManager
名を使い、リカバリマネージャが起動し、JBoss Transaction Service JTA トランザクションマネージャがJNDI プロバイダとバインドされるようにします。このサービスは、CORBA ORB
サービスの存在により左右され、基盤のORB
実装としてJacORB
を使う必要があります。
- 分散
- JBoss Transaction Service が使用可能なトランザクションマネージャの実装を使い、分散トランザクションとリカバリに対応しており、
com.arjuna.ats.jbossatx.jts.TransactionManagerService
クラスで設定されています。これはデフォルトの設定となっています。 - ローカル
- 純粋なローカルJTA 実装を使います。
com.arjuna.ats.jbossatx.jta.TransactionManagerService
クラスを使い設定されています。
8.4. トランザクションコンテキストを確実にサーバーへ伝播
JRMPInvokerProxy.setTPCFactory( new com.arjuna.ats.internal.jbossatx.jts.PropagationContextManager() );
パート II. JTS 開発
第9章 概要
9.1. はじめに
com.arjuna.ats.txoj
と com.arjuna.ats.arjuna
パッケージにあります。
9.2. JBoss Transaction Service
注記
図9.1 JBoss Transaction Service のクラス階層
9.2.1. オブジェクトステートを保存
InputObjectState
クラスや OutputObjectState
クラスといった同じ仕組みを使い実装されます。pack
と unpack
操作を使って標準タイプのインスタンスを継続的にパックおよびアンパックできる内部アレイを、これらのクラスは保持します。このバッファは随時、自動的にサイズの調整がなされます。これらのインスタンスは、ネットワークバイトオーダーと呼ばれる機械から独立した標準形式にてすべてバッファに格納されます。XDRやASN.1などアーキテクチャーに依存しない別の形式を実装するには、これらのクラスをパックおよびアンパック機能に相当するクラス (必要とされる形式) で置き換えます。
9.2.2. オブジェクトストア
read_committed
操作を使いステートを読み込み、write_committed
と write_uncommitted
操作を使い書き込みます。
9.2.3. リカバリおよび永続性
StateManager
クラスはクラス階層のルート部分に存在し、オブジェクトの有効化、無効化、および回復を行います。例9.1「StateManager
実装」を参照してください。
例9.1 StateManager
実装
public abstract class StateManager { public boolean activate (); public boolean deactivate (boolean commit); public Uid get_uid (); // object’s identifier. // methods to be provided by a derived class public boolean restore_state (InputObjectState os); public boolean save_state (OutputObjectState os); protected StateManager (); protected StateManager (Uid id); };
- 回復可能
StateManager
は、オブジェクトに関する適切なリカバリ情報を生成、保持しようとします。このようなオブジェクトの有効期間は、オブジェクトを作成したアプリケーションの有効期間を越えないものとします。- 回復可能かつ永続的
- オブジェクトの有効期間は、そのオブジェクトを作成する、あるいはアクセスするアプリケーションの有効期限よりも長くなっています。リカバリ情報を保持するだけでなく、
StateManager
は、適時activate
あるいはdeactivate
操作を呼び出すことで、オブジェクトの既存ステートおよび永続ステートを自動的にload
あるいはunload
しようとします。 - 回復可能でも永続的でもない
- リカバリ情報は保持されず、オブジェクトの有効化、無効化も自動試行されません。
StateManager
は、アプリケーション実行中の様々な時点に、deactivate
メソッド実行の一部としてsave_state
メソッドを呼び出し、activate
実行の一部としてrestore_state
を呼び出します。StateManager
はユーザーレベルのステート変更を検知することができないため、プログラマはこれらのメソッドを実装しなければなりません。そしてプログラマは、オブジェクトステートのどの部分を永続化するか決定します。例えば、スプレッドシートの場合、値を再計算できるのであれば、全エントリを保存する必要がありません。例9.2「save_state
の例」の例は、A、B、Cという整数のメンバ変数を含むExample
クラスのsave_state
実装について示しています。
例9.2 save_state
の例
public boolean save_state(OutputObjectState o) { if (!super.save_state(o)) return false; try { o.packInt(A); o.packInt(B); o.packInt(C)); } catch (Exception e) { return false; } return true; }
注記
save_state
と restore_state
メソッドはすべてsuper.save_state
および super.restore_state
を呼出す必要があります。
9.2.4. Transactional Object for Javaのライフサイクル
図9.2 TXOJ の永続オブジェクトの基本的なライフサイクル
- オブジェクトは最初はパッシブで、
OutputObjectState
クラスのインスタンスとしてオブジェクトストアに格納されます。 - アプリケーションが必要とする場合、オブジェクトは
read_committed
操作を使いストアから読み込むことで自動的に有効化され、オブジェクトのrestore_state
操作により、InputObjectState
インスタンスから完全なオブジェクトに変換されます。 - アプリケーションがオブジェクトを使い終わると、
save_state
操作を使い、OutputObjectState
インスタンスに変換しなおすことで無効にし、write_uncommitted
メソッドを利用しシャドーコピーとしてオブジェクトストアに格納しなおします。commit_state
操作を利用して、このシャドーコピーをコミットし以前のバージョンを上書きすることができます。通常トランザクションシステムがシャドーコピーの存在をプログラマには見せないようにしています。本来、有効化されたオブジェクト内でトップレベルトランザクションがコミットする時にのみ、オブジェクトの無効化が起こります。
注記
9.2.5. Concurrency Controller
LockManager
クラスにより実装され、適切なデフォルトの動作を提供します。このデフォルトの動作は、プログラム済みクラスの特定セマンティクスを使い必要であればプログラマが上書きすることができます。StateManager
クラスと永続性については、インターフェース経由で同時実行制御の実装へアクセスします。インターフェースに対して提供している同時実行制御に関する実装は今のところ、以下のようになっています。
- リモートサービスへのアクセス
- ローカルディスクおよびデータベース実装の両方 (これらでロックをローカルのファイルシステムかデータベースに書き込み永続化します)。
- 純粋なローカル実装 (この実装にてロックを作成した仮想マシンのメモリ内にてロックが保持されます)。この実装は、ローカルディスクへロックを書き込むよりもパフォーマンスが優れていますが、オブジェクトを仮想化マシン間で共有することができません。重要なのは、SecurityManagerの影響を受ける可能性のある要件を持たない、基本的なJava オブジェクトとなっています。
setlock
操作を介します。デフォルトでは、ランタイムシステムはオブジェクト毎のmultiple-reader/single writer ポリシーに従い厳密な2相ロッキングを強制します。しかし、図9.1「JBoss Transaction Service のクラス階層」で示されているように、Lock
クラスから継承することでプログラマは、自身のロック実装に別のロック矛盾ルールを提供しtype specific concurrency control (タイプ固有の同時実行制御)を有効にします。
StateManager
が操作によりオブジェクトが変更されるかを判断できないため、LockManager
が操作に読み込みあるいは書き込みロックが必要かを決定できないのです。しかし、ロック解除はシステムが制御しておりプログラマが介入する必要はありません。こうすることで、2相プロパティは正しく保持されるようにします。
public abstract class LockManager extends StateManager { public LockResult setlock (Lock toSet, int retry, int timeout); };
LockManager
クラスは、適時オブジェクトへのロック設定、解除に関するリクエストを管理します。StateManager
から取得しているあtめ、継承された機能の一部が呼び出された時は制御も可能です。例えば、LockManager
は、書き込みロックの設定がされると呼び出している操作はオブジェクトを変更することになることが分かり、オブジェクトが回復可能であればリカバリ情報の保存がトリガーされる可能性があることが分かります。同じように、ロックの取得に成功すると、activate が呼び出されます。
例9.3 書き込みロック取得の試行
public class Example extends LockManager { public boolean foobar () { AtomicAction A = new AtomicAction; boolean result = false; A.begin(); if (setlock(new Lock(LockMode.WRITE), 0) == Lock.GRANTED) { /* * Do some work, and TXOJ will * guarantee ACID properties. */ // automatically aborts if fails if (A.commit() == AtomicAction.COMMITTED) { result = true; } } else A.rollback(); return result; } }
9.2.6. トランザクションプロトコルエンジン
AtomicAction
クラスで表され、StateManager
を使用することで、クラッシュリカバリメカニズムに関する十分な情報を記録し、障害発生時にトランザクションを完了します。これにはトランザクション起動と終了メソッドが用意されています。プログラマが独自のリソース実装を必要とする場合もあるため、現在のトランザクションに登録するためのメソッドも提供されています。JBoss Transaction Service はサブトランザクションにも対応しているため、すでに実行中のトランザクションのスコープ内でトランザクションが開始されると、自動的にネスト化されます。
注記
9.2.7. 例
例9.4 アクティベーション、終了、コミットの関係
{ . . . O1 objct1 = new objct1(Name-A);/* (i) bind to "old" persistent object A */ O2 objct2 = new objct2(); /* create a "new" persistent object */ OTS.current().begin(); /* (ii) start of atomic action */ objct1.op(...); /* (iii) object activation and invocations */ objct2.op(...); . . . OTS.current().commit(true); /* (iv) tx commits & objects deactivated */ } /* (v) */
- バインドを作成しオブジェクトを永続化します。これには、スタブオブジェクトを作成し、リモートオブジェクトを呼び出す必要があるかもしれません。上記の例では、
Name-A
にて特定された既存の永続オブジェクトおよび新規永続オブジェクトを再度バインドしています。リモートオブジェクトのネーミングシステムにより、オブジェクト名と場所のマッピングを管理しており、これについては後の章で説明しています。 - アトミックトランザクションの開始
- 操作の呼出し。特定の呼出しの一部として、このオブジェクト実装が読み取りまたは書き込みモードでロックされ、必要があれば、ロックの矛盾がない前提でオブジェクトストアからの最新のコミット状態で初期化されるようにします。トランザクションのオブジェクトでロックを初めて取得したときに、オブジェクトステートもオブジェクトストアから取得します。
- トップレベルアクションのコミット。これにはオブジェクトストアにある変更オブジェクトのステートを更新することも含まれます。
- 以前に作成したバインドを破棄
9.2.8. クラス階層
例9.5 JBoss Transaction Service のクラス階層
StateManager // Basic naming, persistence and recovery control LockManager // Basic two-phase locking concurrency control service User-Defined Classes Lock // Standard lock type for multiple readers/single writer User-Defined Lock Classes AbstractRecord // Important utility class, similar to Resource RecoveryRecord // handles object recovery LockRecord // handles object locking RecordList // Intentions list other management record types AtomicAction // Implements transaction control abstraction TopLevelTransaction Input/OutputBuffer // Architecture neutral representation of an objects’ state Input/OutputObjectState // Convenient interface to Buffer ObjectStore // Interface to the object storage services
LockManager
、Lock
、AtomicAction
クラスが必要です。プログラマにとり重要なクラスは他に、Uid
やObjectState
があります。JBoss Transaction Service のほとんどのクラスはベースクラスStateManager
から派生しており、永続かつ回復可能オブジェクトを管理するのに必要な基本的機能を提供しています。これらの機能には、オブジェクトのアクティベーション、アクティベーション解除、ステートベースのオブジェクトリカバリへの対応が含まれています。LockManager
クラスはStateManager
と Lock
機能を使い、アトミックアクションのシリアル化プロパティ実装に必要な同時実行制御を提供します。アトミックアクション機能の実装は、AtomicAction
や TopLevelTransaction
により対応しています。
LockManager
から適切に派生したExample
がユーザ定義の永続クラスであると仮定します。アトミックなトランザクションTransを含むアプリケーションがop1という操作を呼び出すことでExample
タイプの (Oと呼ばれる) オブジェクトへアクセスします。すると、ステートが O に変更します。直列性プロパティでは、書き込みロックを変更前にOで取得する必要があるため、op1 のボディはConcurrency controller のsetlock
への呼出しが含まれているはずです。
public boolean op1 (...) { if (setlock (new Lock(LockMode.WRITE) == LockResult.GRANTED) { // actual state change operations follow ... } }
LockManager
クラス提供のsetlock
メソッドが以下の機能を実行します。
- 現在保持しているロックと書き込みロックとの互換性を確認します。可能であれば以下を行います。
StateManager
操作のactivate
を呼出すことで、まだ行っていない場合はオブジェクトストアからOの最新永続ステートをロードします。その後、StateManager
操作のmodified
を呼出し、Oが永続的かどうかにより、Oに対してRecoveryRecord
かPersistenceRecord
のインスタンスを作成し、このインスタンスをTrans
のRecordList
へ挿入します。- Trans のRecordList にLockRecord インスタンスを作成、挿入
Trans
が中断された場合、AtomicAction
のrollback
操作は、様々な記録上にある適切なAbort
操作を呼び出すことで、Trans
に紐付いたRecordList
インスタンスを処理します。LockRecord
クラスによる操作を実装することで、WRITE
ロックを解除し、RecoveryRecord
/PersistenceRecord
がOの以前のステートをリストアします。
第10章 JBoss Transaction Serviceの利用
10.1. はじめに
10.2. ステート管理
10.2.1. オブジェクトステート
Input/OutputObjectState
および Input/OutputBuffer
クラスを使い実装されます。
InputBuffer
クラスと例10.1「OutputBuffer の例」のOutputBuffer
クラスにて、pack (unpack) 操作を使い、標準Java タイプのインスタンスを連続的にパック (アンパック) できる内部アレイを維持します。このバッファは必要であれば自動的にサイズの調整がなされます。インスタンスはすべて、バッファ内にネットワークバイトオーダー (network byte order)と呼ばれる標準形式で格納され、マシンに依存しないようにします。
例10.1 OutputBuffer の例
public class OutputBuffer { public OutputBuffer (); public final synchronized boolean valid (); public synchronized byte[] buffer(); public synchronized int length (); /* pack operations for standard Java types */ public synchronized void packByte (byte b) throws IOException; public synchronized void packBytes (byte[] b) throws IOException; public synchronized void packBoolean (boolean b) throws IOException; public synchronized void packChar (char c) throws IOException; public synchronized void packShort (short s) throws IOException; public synchronized void packInt (int i) throws IOException; public synchronized void packLong (long l) throws IOException; public synchronized void packFloat (float f) throws IOException; public synchronized void packDouble (double d) throws IOException; public synchronized void packString (String s) throws IOException; };
例10.2 InputBuffer
public class InputBuffer { public InputBuffer (); public final synchronized boolean valid (); public synchronized byte[] buffer(); public synchronized int length (); /* unpack operations for standard Java types */ public synchronized byte unpackByte () throws IOException; public synchronized byte[] unpackBytes () throws IOException; public synchronized boolean unpackBoolean () throws IOException; public synchronized char unpackChar () throws IOException; public synchronized short unpackShort () throws IOException; public synchronized int unpackInt () throws IOException; public synchronized long unpackLong () throws IOException; public synchronized float unpackFloat () throws IOException; public synchronized double unpackDouble () throws IOException; public synchronized String unpackString () throws IOException; };
例10.3 OutputObjectState
class OutputObjectState extends OutputBuffer { public OutputObjectState (Uid newUid, String typeName); public boolean notempty (); public int size (); public Uidpublic class InputBuffer { public InputBuffer (); public final synchronized boolean valid (); public synchronized byte[] buffer(); public synchronized int length (); /* unpack operations for standard Java types */ public synchronized byte unpackByte () throws IOException; public synchronized byte[] unpackBytes () throws IOException; public synchronized boolean unpackBoolean () throws IOException; public synchronized char unpackChar () throws IOException; public synchronized short unpackShort () throws IOException; public synchronized int unpackInt () throws IOException; public synchronized long unpackLong () throws IOException; public synchronized float unpackFloat () throws IOException; public synchronized double unpackDouble () throws IOException; public synchronized String unpackString () throws IOException; };
例10.4 InputObjectState
class InputObjectState extends InputBuffer { public InputObjectState (Uid newUid, String typeName, byte[] b); public boolean notempty (); public int size (); public Uid stateUid (); public String type (); };
InputBuffer
と OutputBuffer
クラスの全機能を提供します。また、これらのクラスにより、インスタンス変数を2つ追加しますが、これらの変数はInputObjectState
あるいは OutputObjectState
インスタンスが圧縮イメージとなっているオブジェクトのUid およびタイプを表します。オブジェクトステートを格納あるいはリトリーブするときにオブジェクトストアにアクセスする場合はこれらを使います。
10.2.2. オブジェクトストア
注記
InputObjectState
と OutputObjectState
クラスのインスタンスを保持、リトリーブします。これらのインスタンスは、それらが表すオブジェクトのUid とタイプにより名前が付けられます。read_committed
メソッドを使い状態を読み取り、write_uncommitted
メソッドを使いステートを書き込みます。通常、新規オブジェクトステートは、古いオブジェクトステートを上書きせず、シャドーコピーとしてストアに書き込まれます。commit_state
メソッドが呼び出されたときにのみ、これらのシャドーは元のステートを置き換えます。オブジェクトストアとのやりとりはすべて、JBoss Transaction Service システムのコンポーネントにより適時実行され、オブジェクトのシャドーコピーの存在についてプログラマからは分からないようになっています。
public class ObjectStore { public static final int OS_COMMITTED; public static final int OS_UNCOMMITTED; public static final int OS_COMMITTED_HIDDEN; public static final int OS_UNCOMMITTED_HIDDEN; public static final int OS_UNKNOWN; /* The abstract interface */ public abstract boolean commit_state (Uid u, String name) throws ObjectStoreException; public abstract InputObjectState read_committed (Uid u, String name) throws ObjectStoreException; public abstract boolean write_uncommitted (Uid u, String name, OutputObjectState os) throws ObjectStoreException; . . . };
com.arjuna.ats.arjuna.objectstore.objectStoreSync
変数をOFFに設定します。
10.2.3. StateManager
StateManager
は、オブジェクトステートを管理し、基本的なステート管理サポートメカニズムをすべて提供します。StateManager
は、トランザクショナルオブジェクトが永続化、リカバリできるよう適切なリソースを作成、登録します。トランザクションがネスト化されると、StateManager
はcommit
フェーズ中に子トランザクションと親との間にあるこれらのリソースを伝播します。
StateManager
がオブジェクトに関する適切なリカバリ情報を生成、管理しようとし、Input/OutputObjectState
クラスのインスタンスにその情報を格納します。これらのオブジェクトの有効期間は、これらのオブジェクトを作成したアプリケーションよりも短いものと仮定されています。回復可能かつ永続的なオブジェクトは、これらを作成したアプリケーションよりも寿命が長いため、StateManager
はactivate
か deactivate
メソッドを呼び出すことで、オブジェクトの永続ステートをロードあるいはアンロードします。回復可能でも永続的でもないオブジェクトについては、ステートデータは何も保存されません。
public class ObjectStatus { public static final int PASSIVE; public static final int PASSIVE_NEW; public static final int ACTIVE; public static final int ACTIVE_NEW; public static final int UNKNOWN_STATUS; }; public class ObjectType { public static final int RECOVERABLE; public static final int ANDPERSISTENT; public static final int NEITHER; }; public abstract class StateManager { public synchronized boolean activate (); public synchronized boolean activate (String storeRoot); public synchronized boolean deactivate (); public synchronized boolean deactivate (String storeRoot, boolean commit); public synchronized void destroy (); public final Uid get_uid (); public boolean restore_state (InputObjectState, int ObjectType); public boolean save_state (OutputObjectState, int ObjectType); public String type (); . . . protected StateManager (); protected StateManager (int ObjectType, ObjectName attr); protected StateManager (Uid uid); protected StateManager (Uid uid, ObjectName attr); . . . protected final void modified (); . . . }; public class ObjectModel { public static final int SINGLE; public static final int MULTIPLE; };
StateManager
はdeactivation
メソッド実行時にsave_state
メソッドを呼び出します。activate
メソッド実行中にrestore_state
が呼び出されます。また、アプリケーション実行中の様々な時点でtype
が呼び出されます。StateManager
は任意のJava オブジェクトレイアウトのランタイム詳細にアクセスできないため、プログラマはこれらのメソッドを実装する必要があります。しかし、InputObjectState
と OutputObjectState
が提供する機能により、これらのルーチンの記述が単純化されています。例えば、A、B、Cというメンバ変数を持つExample
クラスのsave_state
実装は、例10.5「save_state
の例」に従っています。
例10.5 save_state
の例
public boolean save_state ( OutputObjectState os, int ObjectType ) { if (!super.save_state(os, ObjectType)) return false; try { os.packInt(A); os.packString(B); os.packFloat(C); return true; } catch (IOException e) { return false; } }
save_state
と restore_state
の全メソッドにより、super.save_state
と super.restore_state
を呼び出す必要があります。
注記
type
メソッドは、そのクラスインスタンスのステートを保存し最終的にリストアするオブジェクトストアの場所を決定します。これは有効なストリングであれば結構です。ただし、ハッシュ記号#は、JBoss Transaction Serviceで必要な特別ディレクトリ向けに確保されているため、#の利用を避けるようにしてください。
StateManager
のget_uid
メソッドは、オブジェクトの内部システム名へ読み取り専用でアクセスできます。内部システム名の値はオブジェクト作成時に明示的なパラメータとして提供するか、新規識別子として生成することで設定可能なため、オブジェクト作成時にだけ設定できるようになっています。
destroy
メソッドは、オブジェクトのステートをオブジェクトストアから削除します。これは、アトミックな操作で、呼出中のトランザクションがコミットされると状態のみが削除されます。プログラマは、この操作を呼び出す前にオブジェクトの排他的アクセスを確保していなければなりません。
StateManager
クラスは1つのメカニズムで両方を管理するように組み合わせています。つまり、リカバリおよび永続化両方に対して、クラスのInput/OutputObjectState クラスインスタンスを使うのです。追加の引数をsave_state
や restore_state
操作に渡すことで、プログラマは特定の呼出しの目的を判断することができ、リカバリや永続化するために様々な情報を保存することができます。
10.2.4. オブジェクトモデル
- シングル
- アプリケーションには、オブジェクトのコピーが1つのみ含まれています。このオブジェクトは1つのJVM内に置かれており、クライアントはすべてサーバーへの呼出しに対応する必要があります。単一モデルでは、パフォーマンスが優れていますが、SPOF (Single Point of Failure)を生み出します。マルチスレッドの環境では、1つのスレッドに障害が起こると、オブジェクトを障害から守ることができません。
- 複数
- 論理的には、オブジェクトのインスタンスは1つ存在します。オブジェクトの複製が複数のJVM間に分散されているのです。パフォーマンスはシングルモデルに比べると劣りますが、障害からはうまく隔離されます。
com.arjuna.ats.arjuna.gandiva.ObjectName
クラスの適切なインスタンスを提供することで、オブジェクト毎にこれをオーバーライドすることができます。
注記
ObjectName
クラスを提供します。例については例10.6「オブジェクトモデル」 を参照してください。
ObjectName
の新規インスタンスを作成します。com.arjuna.ats.arjuna.ArjunaNames.StateManager_objectModel()
名を使いオブジェクトモデルの属性を設定します。
例10.6 オブジェクトモデル
{ ObjectName attr = new ObjectName(“SNS:myObjectName”); attr.setLongAttribute(ArjunaNames.StateManager_objectModel(), ObjectModel.SINGLE); AtomicObject obj = new AtomicObject(ObjectType.ANDPERSISTENT, attr); }
10.2.5. JBoss Transaction Service のメソッド参照
StateManager
は、オブジェクトステートを管理し、リカバリ、永続性、あるいはリカバリと永続性両方で必要となる基本のサポートメカニズムをすべて提供します。操作によってはご自身で定義する必要があるものもあります。自身で定義する必要のある操作は、save_state
, restore_state
、type
です。
- boolean
save_state
(OutputObjectStatestate
, intObjectType
) - 今後のリカバリや永続化に向けオブジェクトの状態を保存するために呼び出します。
ObjectType
パラメータは、呼出しの理由を指定します。指定することおで、リカバリ、永続化のいずれを望んでいるかによって、最初のパラメータとして提供されているOutputObjectState
に様々な情報を保存することができます。例えば、他のJBoss Transaction Service オブジェクトに対するポインタを、リカバリ用のポインタとして、永続化にはUIDとして保存することができます。OutputObjectState
クラスは便利な操作を提供しており、Java の基本タイプすべてのインスタンスを保存することができます。永続オブジェクトのクラッシュリカバリに対応するには、save_state
メソッドすべてにより、super.save_state
を呼び出す必要があります。注記
save_state
メソッドは、オブジェクトが内部で一貫性を持ち、全保存変数が有効な値を持つと仮定しています。コードを記述、テストし、正しいかどうか確認します。 - boolean
restore_state
(InputObjectStatestate
, intObjectType
) - オブジェクトを指定したステートにリストアします。2番目のパラメータは、提供したステートに対し様々な解釈をすることができます。永続オブジェクトのクラッシュリカバリに対応するには、
restore_state
メソッドすべてにより、super.restore_state
を呼び出す必要があります。 - String
type ()
- JBoss Transaction Service の永続メカニズムには、オブジェクトのステートを保存しリストアできるように文字列としてオブジェクト型を確定する方法が必要です。慣例により階層内のクラスの位置を利用します。例えば、
StateManager
/LockManager
/Object
などです。注記
type
メソッドは、特定クラスインスタンスのステートをオブジェクトストアのどの部分に保存するか決定します。実際これは、有効な文字列であれば何でも結構です。しかし、ハッシュ記号#はJBoss Transaction Service 用の特別なディレクトリとして確保しているため、#の利用は避けてください。
10.2.6. 例
StateManager
クラスから取得した基本的なArray
クラスを指します。highestIndex
変数が0以外の値を持つアレイで最も高い要素についてトラッキングし、オブジェクトのステート保存、復元を図解しています。
例10.7 オブジェクトのステートを保存、復元
public class Array extends StateManager { public Array (); public Array (Uid objUid); public void finalize ( super.terminate(); }; /* Class specific operations. */ public boolean set (int index, int value); public int get (int index); /* State management specific operations. */ public boolean save_state (OutputObjectState os, int ObjectType); public boolean restore_state (InputObjectState os, int ObjectType); public String type (); public static final int ARRAY_SIZE = 10; private int[] elements = new int[ARRAY_SIZE]; private int highestIndex; };
save_state
、restore_state
、type
操作は以下のように定義することができます。
/* Ignore ObjectType parameter for simplicity */ public boolean save_state (OutputObjectState os, int ObjectType) { if (!super.save_state(os, ObjectType)) return false; try { packInt(highestIndex); /* * Traverse array state that we wish to save. Only save active elements */ for (int i = 0; i <= highestIndex; i++) os.packInt(elements[i]); return true; } catch (IOException e) { return false; } } public boolean restore_state (InputObjectState os, int ObjectType) { if (!super.restore_state(os, ObjectType)) return false; try { int i = 0; highestIndex = os.unpackInt(); while (i < ARRAY_SIZE) { if (i <= highestIndex) elements[i] = os.unpackInt(); else elements[i] = 0; i++; } return true; } catch (IOException e) { return false; } } public String type () { return '/StateManager/Array'; }
10.3. ロック管理と同時実行制御
注記
例10.8 LockStore クラスの例
public class LockStore { public abstract InputObjectState read_state (Uid u, String tName) throws LockStoreException; public abstract boolean remove_state (Uid u, String tname); public abstract boolean write_committed (Uid u, String tName, OutputObjectState state); };
10.3.1. ロックストアの情報を選択
com.arjuna.ats.txoj.lockstore.lockStoreType
プロパティを使い指定の実行環境内にて全オブジェクトに対して利用することができます。この変数は以下のいずれかになります。
- BasicLockStore
- これはインメモリ実装で、実行環境間における格納情報の共有には対応していません。必要であれば拡張しこの機能を含めることもできます。
- BasicPersistentLockStore
- これは、デフォルト実装です。ロック情報をローカルファイルシステムに格納します。同じファイルストアを共有する実行環境は、同時実行制御の情報を共有することができます。ロッキング情報が書き込まれるファイルシステムのルートは、Jboss Trasaction Service installationのディレクトリ内にある
LockStore/
ディレクトリとなっています。この場所をオーバーライドするには、適宜にcom.arjuna.ats.txoj.lockstore.lockStoreDir
を設定するか、CLASSPATH
に場所を含めます。lockStoreDir のプロパティをオーバーライドする方法
java -D com.arjuna.ats.txoj.lockstore.lockStoreDir=/var/tmp/LockStore myprogram
java –classpath $CLASSPATH;/var/tmp/LockStore myprogram
10.3.2. LockManager
LockManager
クラスにより実装され、適切なデフォルト動作を提供しますが、必要であればこの動作をオーバーライドすることもできます。setlock
メソッドは、同時実行制御に対する主なインターフェースとなっています。デフォルトでは、JBoss Transaction Service のランタイムシステムは、オブジェクト毎にmultiple reader, single writerポリシーに従うといった厳密な2相ロッキングを強制します。LockManager
クラスは操作が読み込みロックあるいは書き込みロックのいずれが必要なのか推測できないため、プログラマがロック取得を制御します。しかし、ロック解除は通常システムが制御しており、プログラマは何もする必要がありません。
LockManager
クラスは、オブジェクトへのロック設定あるいはロック解除に関するリクエストを管理します。しかし、StateManager
から取得しているため、継承した機能の一部に関する呼出しも制御することができます。例えば、書き込みロックを設定するリクエストが許可された場合、書き込みロックを設定するということは、呼出中のメソッドがオブジェクトの変更を行うであろうと推測できるため、LockManager
は、modified
メソッドを直接呼び出します。オブジェクトが回復可能であれば、こうすることでリカバリ情報が保存されます。ロックを問題なく取得すると、activate
の呼出しがトリガーされます。
LockManager
は永続オブジェクトのアクティベートおよび解除を行い、同時実行制御の管理に利用するResources
を登録します。また、StateManager
クラスを駆動することで、永続および回復可能なステートを操作し、オブジェクトのリカバリを行えるようにResources
を登録します。自身で行う操作は、適切なロックを設定し、トランザクションを開始・終了し、StateManager
クラスのsave_state
と restore_state
メソッドを継承するのみです。
例10.9 LockResult の例
public class LockResult { public static final int GRANTED; public static final int REFUSED; public static final int RELEASED; }; public class ConflictType { public static final int CONFLICT; public static final int COMPATIBLE; public static final int PRESENT; }; public abstract class LockManager extends StateManager { public static final int defaultTimeout; public static final int defaultRetry; public static final int waitTotalTimeout; public synchronized int setlock (Lock l); public synchronized int setlock (Lock l, int retry); public synchronized int setlock (Lock l, int retry, int sleepTime); public synchronized boolean releaselock (Uid uid); /* abstract methods inherited from StateManager */ public boolean restore_state (InputObjectState os, int ObjectType); public boolean save_state (OutputObjectState os, int ObjectType); public String type (); protected LockManager (); protected LockManager (int ObjectType, ObjectName attr); protected LockManager (Uid storeUid); protected LockManager (Uid storeUid, int ObjectType, ObjectName attr); . . . };
setlock
に渡し、ロックを取得する必要があります。タイプは、READ
あるいは WRITE
になっています。ロックの矛盾が起こると、以下のシナリオの1つのようになります。
- 再試行の値が
READ
orWRITE
と同じ場合、setlock
メソッドを呼び出したスレッドはロックが解除されるか、指定した合計タイムアウトを過ぎるまでブロックされます。タイムアウトになると、REFUSED
の値が返されます。 - ロックを最初に取得できない場合、
LockManager
が指定された回数を再試行し、試行に失敗してから次に失敗するまで指定のタイムアウト値の期間待機します。デフォルトの試行回数は100で、各試行の間隔は0.25秒となっています。
setlock
メソッドは、GRANTED
の値を返しますが、取得しなかった場合はREFUSED
の値を返します。ロックリクエストが許可された場合にのみ、操作に対するコードの残りが実行されるようにする必要があります。実用例については、例10.10「setlock
の例」を参照してください。
例10.10 setlock
の例
res = setlock(new Lock(WRITE), 10); // Attempts to set a write // lock 11 times (10 retries) // before giving up. res = setlock(new Lock(READ), 0); // Attempts to set a read lock // 1 time (no retries) before // giving up. res = setlock(new Lock(WRITE); // Attempts to set a write lock // 101 times (default of 100 // retries) before giving up.
releaselock
メソッドを使いロックを解除する必要があります。
10.3.3. ロックポリシー
Lock
クラスはStateManager
から派生してます。さらに、LockManager
クラスは実際のロックリクエスト許可ポリシーに関するセマンティクスを把握していません。Lock
クラスのインスタンスでこの情報を保持し、conflictsWith
メソッドを提供しています。LockManager
がこれを使い2つのロックが矛盾しているかを判断します。このように分けることにより、基本的なLock
クラスから新たなロックタイプを取得することができ、矛盾した操作に関する適切な定義を提供し同時実行レベルを改善することができます。
public class LockMode { public static final int READ; public static final int WRITE; }; public class LockStatus { public static final int LOCKFREE; public static final int LOCKHELD; public static final int LOCKRETAINED; }; public class Lock extends StateManager { public Lock (int lockMode); public boolean conflictsWith (Lock otherLock); public boolean modifiesObject (); public boolean restore_state (InputObjectState os, int ObjectType); public boolean save_state (OutputObjectState os, int ObjectType); public String type (); . . . };
Lock
クラスによりmodifiesObject
メソッドが提供され、LockManager
がそれを使いロッキングリクエストを許可するのに呼出しあるいはメソッドが必要かを判断します。こうすることで、単純な読み込みや書き込み以外のロッキングモードにも対応できるようになります。提供されたLock
クラスは従来の multiple reader/single writer ポリシーに対応しています。
10.3.4. オブジェクトの構築と破棄
LockManager
は取得したクラスが利用できるように、保護コンストラクタを2つ提供し、以下のような明確な目的を果たします。
LockManager ()
- 以前のステートを持たない新規オブジェクトの作成ができます。
LockManager
(intObjectType
, ObjectNameattr
)- 以前のステートを持たない新規オブジェクトを作成できます。
ObjectType
パラメータはオブジェクトは回復可能 (recoverable
)、回復可能かつ永続的 (ANDPERSISTENT
で示される)、いずれでもない (NEITHER
) で表します。オブジェクトが永続的と登録されると、その状態がオブジェクトストアの1つに格納されます。shared
パラメータは、オブジェクトがRECOVERABLE
の場合のみ意味を持ちます。attr
がnull でなく、オブジェクトモデルがSINGLE
(デフォルトの動作) であれば、オブジェクトの回復可能な状態がそのオブジェクト自体に保持されます。そうでない場合は、オブジェクトのステートは、各アトミックアクションの間にインメモリオブジェクトストアの中に格納されます。新規永続オブジェクトのコンストラクタは、そのコンストラクタ内でアトミックアクションを使うはずです。こうすることで、コンストラクタ内のアクションがコミットされるとオブジェクトのステートがオブジェクトストアに自動的に書き込まれるようにします。あるいは、コンストラクタ内にアクションが存在する場合は、適切なトップレベルアクションのコミット時にオブジェクトストアに自動書き込みされます。 LockManager
(UidobjUid
)objUid
パラメータにて指定された既存の永続オブジェクトへアクセスができるようになります。オブジェクトの以前のステート(objUid
パラメータの値により特定) は、オブジェクトストアから自動的にロードされます。LockManager
(UidobjUid
, ObjectNameattr
)objUid
パラメータで指定した既存の永続オブジェクトへアクセスできます。オブジェクトの以前のステートは、objUid
の値により特定しますが、この状態は自動的のオブジェクトストアからロードされます。attr
パラメータが nullではなく、かつオブジェクトモデルがSINGLE
(デフォルト動作) の場合、オブジェクトは各トップレベルトランザクションの開始時に再アクティベートされません。
terminate
を呼び出し、オブジェクトが破棄予定であることをステート管理メカニズムに通知する必要があります。そうでない場合は、予期せぬ結果が発生してしまう可能性があります。
LockManager
クラスは StateManager
から継承するため、提供されたObjectName
インスタンスをStateManager
クラスに渡します。同様に、StateManager
オブジェクトモデルを前述したように設定することができます。
第11章 一般的なトランザクションの問題
11.1. JBoss Transaction Serviceのトランザクションに関する高度な問題
11.1.1. トランザクションの確認
com.arjuna.ats.arjuna.coordinator.CheckedAction
クラスを提供し、スレッド/トランザクションの終了ポリシーをオーバーライドできるようになっています。各トランザクションはクラスインスタンスが1つ紐付けられており、トランザクションごとに独自の実装を提供することができます。
public class CheckedAction { public CheckedAction (); public synchronized void check (boolean isCommit, Uid actUid, BasicList list); };
CheckedAction
オブジェクトでcheck
メソッドを呼び出します。check メソッドに対するパラメータは以下の通りです。
isCommit
- トランザクションがコミットの処理中か、ロールバックの処理中かを示します。
- actUid
- トランザクションの識別子
- list
- トランザクション内で現在有効なスレッドの全一覧
check
メソッドを返すとトランザクションが終了されます。トランザクションのステートがcheck
メソッドが呼び出されたときの状態から変わっている可能性があります。
11.1.2. 統計の収集
com.arjuna.ats.arjuna.coordinator.enableStatistics
のプロパティ変数をYES
に設定することで履歴追跡を作動させることができます。履歴情報には、作成したトランザクション数、トランザクションの結果が含まれます。例11.1「トランザクションの統計」にある通り、com.arjuna.TxCore.Atomic.TxStats
クラスを使い、トランザクショナルアプリケーションの実行中にこの情報をリクエストすることができます。
例11.1 トランザクションの統計
public class TxStats { // Returns the number of transactions (top-level and nested) // created so far. public static int numberOfTransactions (); // Returns the number of nested (sub) transactions created so far. public static int numberOfNestedTransactions (); // Returns the number of transactions which have terminated with // heuristic outcomes. public static int numberOfHeuristics (); // Returns the number of committed transactions. public static int numberOfCommittedTransactions (); // Returns the number of transactions which have rolled back. public static int numberOfAbortedTransactions (); }
11.1.3. 最終リソースコミットの最適化 (LRCO:Last Resource Commit Optimization)
com.arjuna.ats.arjuna.coordinator.OnePhase
を実装し、BasicAction.add
メソッドでトランザクションに登録する必要があります。例11.2「BasicAction.add の例」にて説明されているように、このオペレーションでは、AbstractRecord
のインスタンスを期待するため、com.arjuna.ats.arjuna.LastResourceRecord
クラスのインスタンスを作成し、コンストラクタパラメータとしてパーティシパントに渡す必要があります。
例11.2 BasicAction.add の例
try { boolean success = false; AtomicAction A = new AtomicAction(); OnePhase opRes = new OnePhase(); // used OnePhase interface System.err.println("Starting top-level action."); A.begin(); A.add(new LastResourceRecord(opRes)); A.add(new ShutdownRecord(ShutdownRecord.FAIL_IN_PREPARE)); A.commit(); }
11.1.4. ネスト化されたトランザクション
11.1.5. トランザクションを非同期的にコミット
commit
プロトコルを実行します。1つのスレッドが登録済みの全リソースを順番に準備するために移動し、コミットあるいはロールバックを行います。これには、考えられるデメリットが何点かあります。
- 通常、
prepare
の操作は各リソースごとに並行して論理的に呼び出されます。デメリットですが、登録されているリソースにあるリストの前のほうに出てくるリソースが強制的にprepare
の段階でロールバックされてしまい、必要のないprepare
操作が実行されてしまうことがあります。 - 利用中のアプリケーションがヒューリスティックなレポーティングを必要とする場合、成功、失敗は特に重要でないため、
commit
プロトコルの2番目のフェーズを非同期的に呼び出すことができます。
com.arjuna.ats.arjuna.coordinator.asyncPrepare
YES
に設定されている場合、prepare
フェーズにてトランザクション内に登録パーティシパントごとに別のスレッドが作成されます。com.arjuna.ats.arjuna.coordinator.asyncCommit
YES
に設定されていると、ヒューリスティックな結果の情報を必要としない場合、別のスレッドが作成されトランザクションの2番目のフェーズを完了します。
11.1.6. 独立したトップレベルトランザクション
図11.1 独立したトップレベルアクション
Begin
操作が呼び出されるため、トランザクションCが論理的にアクションB内にネスト化されます。しかし、独立したトップレベルアクションであるため、構造内にある別のアクションから独立してcommit
あるいはabort
が行われます。独立したトップレベルアクションにこのような特徴があるため、慎重にテストや所見を行った後にのみ、注意して利用するようにしてください。
TopLevelTransaction
クラスのインスタンスを宣言し、他のトランザクションと全く同じ方法で利用することでアプリケーション内でトップレベルアクションを使うことができます。
11.1.7. save_state
や restore_state
メソッド内のトランザクション
save_state
と restore_state
メソッドを記述する際には注意が必要です。メソッド内で明示的に、あるいは他の操作を利用して暗黙的にアトミックなアクションを開始しないようにしてください。注意が必要な理由は、JBoss Transaction Service はコミットするとrestore_state
メソッドを呼び出す可能性があり、別アクションのcommit
あるいは abort
フェーズ中にトランザクション実行を試行してしまいます。こうすることで、コミットあるいは中断されたアクションのアトミックなプロパティを妨害する可能性があるため、アトミックアクションの開始はお薦めできません。
11.1.8. 例
set
と get
メソッドを実装します。このコードは単純化されており、エラーの条件や例外は無視しています。
例11.3 ネスト化されたトランザクションの例
public boolean set (int index, int value) { boolean result = false; AtomicAction A = new AtomicAction(); A.begin(); // We need to set a WRITE lock as we want to modify the state. if (setlock(new Lock(LockMode.WRITE), 0) == LockResult.GRANTED) { elements[index] = value; if ((value > 0) && (index > highestIndex)) highestIndex = index; A.commit(true); result = true; } else A.rollback(); return result; } public int get (int index) // assume -1 means error { AtomicAction A = new AtomicAction(); A.begin(); // We only need a READ lock as the state is unchanged. if (setlock(new Lock(LockMode.READ), 0) == LockResult.GRANTED) { A.commit(true); return elements[index]; } else A.rollback(); return -1; }
11.1.9. ガーベッジコレクションのオブジェクト
11.1.10. トランザクションのタイムアウト
AtomicAction
コンストラクタへのパラメータとして渡します。デフォルト値AtomicAction.NO_TIMEOUT
となっている場合、トランザクションは自動でタイムアウトされません。その他の正の値であれば、トランザクションが終了されるまで待機する秒数を示します。0
の値は、グローバルのデフォルトタイムアウトとして解釈され、プロパティcom.arjuna.ats.arjuna.coordinator.defaultTimeout
を使い提供できます。このプロパティのデフォルト値は、60
あるいは1分となっています。
JBossTS
は別の reaper スレッドを使い、ローカルで作成されたトランザクションをすべて監視することで、タイムアウトが過ぎると強制的にトランザクションをロールバックします。Reaper スレッドがアプリケーションの時間を消費しないように一定期間あけて実行されるようになっています。デフォルトのチェック期間は12万ミリ秒ですが、com.arjuna.ats.arjuna.coordinator.txReaperTimeout プロパティを別の値 (ミリ秒) に設定することで値をオーバーライドすることができます。また、com.arjuna.ats.arjuna.coordinator.txReaperModeプロパティをDYNAMIC
に設定すると、トランザクションがタイムアウトすると、トランザクション reaper が起動します。これにより、トランザクションを早めに終了できるという利点がありますが、継続的にReaper スレッドを再スケジュールしてしまう可能性があります。
0
の場合、あるいはタイムアウトの値が指定されていない場合、JBoss Transaction Service はそのトランザクションに対してタイムアウトを行わず、永遠に実行しつづけることが可能になります。com.arjuna.ats.arjuna.coordinator.defaultTimeoutプロパティを設定することで、このデフォルトのタイムアウトをオーバーライドできます。
第12章 ヒント
12.1. 一般的なコツ
12.1.1. コンストラクタでトランザクションを利用
例12.1 トランザクションが原因でシステムの不整合が発生
AtomicAction A = new AtomicAction(); Object obj1; Object obj2; obj1 = new Object(); // create new object obj2 = new Object("old"); // existing object A.begin(0); obj2.remember(obj1.get_uid()); // obj2 now contains reference to obj1 A.commit(true); // obj2 saved but obj1 is not
obj1
は新規オブジェクトで、obj2
は既存の古いオブジェクトとなっています。obj2
のremember
メソッドが呼び出されると、オブジェクトが有効になり、obj1
のUid が分かります。このアクションがコミットされると、obj2
の永続ステートにはobj1
のUid が含まれるようになるでしょう。しかし、アクション制御により操作されていないため、obj1
のステートはまだ保存されていません。実際、アプリケーション内で何らかのアクションの管理下で変更が加えられない限り、保存はされません。しかし、コンストラクタがアトミックアクションを利用している場合、obj1
の状況は構築時に自動的に保存され、矛盾を防ぎます。
12.1.2. save_state
および restore_state
メソッドに関する詳細情報
save_state
メソッドを呼び出すことができます。特にアトミックアクションを利用する場合に当てはまります。save_state
で保存した変数はすべて、正しく初期化されます。
save_state
および restore_state
メソッドを記述する際は、トランザクションが明示的あるいは暗黙的に開始されないよう十分に注意してください。JBoss Transaction Service は、処理中のcommit
の一部としてrestore_state
メソッドを呼び出し、別トランザクションのcommit
あるいは abort
フェーズ中にアトミックトランザクションを実行してしまう可能性があるためです。これにより、コミットあるいは中断されたトランザクションのアトミックプロパティが妨害されてしまう可能性があるため、推奨されません。
save_state
と restore_state
メソッドすべてがsuper.save_state
および super.restore_state
を呼び出す必要があります。
12.1.3. パッキングオブジェクト
InputObjectState
や OutputObjectState
で提供されるpack
および unpack
メソッドを利用することで、int や longなどの基本的なJava のタイプを全て保存し、InputObjectState
あるいは OutputObjectState
からリストア可能となっています。しかし、オブジェクトのパッキングによりエイリアスの別問題が発生してしまうため、オブジェクトのパッキングとアンパッキングについては違った方法で処理する必要があります。エイリアスとは、2種のオブジェクト参照が実際は同じアイテムを参照している可能性があることを意味します。例12.2「オブジェクトのパッキングにおけるエイリアスの問題」を参照してください。
例12.2 オブジェクトのパッキングにおけるエイリアスの問題
public class Test { public Test (String s); ... private String s1; private String s2; }; public Test (String s) { s1 = s; s2 = s; }
save_state
メソッドのナイーブな実装がこの string を2度コピーする可能性があります。save_state
メソッドから見ると、これは非効率なだけですが、restore_state
メソッドがこれら2つの string を別のメモリ領域にアンパックし、元のエイリアス情報を壊してしまうのです。JBoss Transaction Service では、オブジェクト参照を別々にパックおよびアンパックします。
12.2. StateManager
クラスを直接利用
LockManager
クラスからユーザクラスを取得しています。以下のように理由は2つあります。
- 最も重要な点ですが、アトミックアクションのシリアル化における制限で上記が必要になるため。
- 次にプログラマの介入の必要性が軽減されるため。
StateManager
からユーザクラスを直接取得可能です。
StateManager
から直接取得したクラスは、LockManager
に依存するのではなく、明示的に状況管理の仕組みを使う必要があります。StateManager
のコンストラクタは事実上、LockManager
と同一となっているため、activate
、deactivate
、modified
メソッドを適切に利用しなければなりません。
12.2.1. activate
メソッド
boolean activate () boolean activate (String storeRoot)
activate
メソッドは、オブジェクトストアからオブジェクトをロードします。オブジェクトのUIDは、コンストラクタ経由で事前に設定する必要があり、オブジェクトがストア内に存在していなければなりません。このオブジェクトが問題なく読み込まれると、restore_state
メソッドが呼び出されメモリ内にオブジェクトを構築します。activate
メソッドの動作方式は、オブジェクトが一旦有効になるとその後の呼出しは無視されるようになっています。パラメータはオブジェクトストアの root 名を指し、オブジェクトの検索を行います。値が null の場合、デフォルトのストアを利用します。
12.2.2. deactivate
メソッド
boolean deactivate () boolean deactivate (String storeRoot)
deactivate
は activate の逆で、save_state
メソッドをまず呼出し、オブジェクトの圧縮イメージを構築し、オブジェクトストアにオブジェクトを保存します。オブジェクトは、有効になってから変更された場合のみ保存されます。パラメータは、オブジェクトの保存先であるオブジェクトストアの root 名を示します。この値が nullの場合は、デフォルトのストアが利用されます。
12.2.3. modified
メソッド
void modified ()
modified
メソッドは、メモリ内のオブジェクトを変更する前に呼び出す必要があります。さもなければ、このオブジェクトはdeactivate
メソッドにより、オブジェクトストア内に保存されません。
第13章 ツール
13.1. はじめに
13.2. Transaction Service ツールの起動
- Windows
- Start メニューから、JBoss Transaction Serviceプログラムグループの Start Tools リンクをダブルクリックします。
- Linux およびUnix 系のオペレーティングシステム
- コマンドラインで
$JBOSS_HOME/run-tools.sh
とタイプしてください。
13.2.1. File メニュー
- Open JMX Browser (JMX ブラウザを開く)
- JMX ブラウザウィンドウを表示します。
- Open Object Store Browser (オブジェクトストアブラウザを開く)
- オブジェクトストアブラウザのウィンドウを表示します。
- Settings (設定)
- JBoss Transaction Service ツールの設定に利用する設定ダイアログを表示します。
- Exit (閉じる)
- JBoss Transaction Service ツールアプリケーションを閉じ、保存されていない変更は破棄されます。
13.2.2. Performance メニュー
- Open (開く)
- Performance のウィンドウを開きます。Performance ツールに関する詳細情報は「Performance ツールを利用」を参照してください。
- Close All (すべてを閉じる)
- 開いているPerformance ウィンドウ全てを閉じます。
13.2.3. Window メニュー
- Cascade Windows (カスケードウィンドウ)
- ウィンドウを斜めに並べて整列し、タイトルバーをすべて表示します。
- List of Individual Windows (個別ウィンドウの一覧)
- 現在表示されている各ウィンドウに対し追加のメニューオプションを表示します。選択時は、関連づいたウィンドウにフォーカスされます。
13.2.4. Help メニュー
- About (情報)
- 製品、バージョン、著者、ライセンスに関する情報を表示します。
13.3. Performance ツールを利用
Performance Tool Graphでの表示項目
- トランザクション数
- コミットされたトランザクション数
- 中断されたトランザクション数
- ネスト化されたトランザクション数
- ヒューリスティックがあげられた数
13.4. JMX ブラウザの利用
Details パネルに表示されている情報
- 本サーバーに登録されているMBean の合計数
- 本MBean にて公開されたコンストラクタ数
- 本MBean により公開された属性数
- 本MBean により公開された操作数
- 本MBean により公開された通知数
- MBean に関する概説
13.4.1. 属性と操作の利用
13.4.2. Object Store Browserの利用
- Object Store Roots
- 現在利用可能なオブジェクトストアルートのドロップダウンリスト。一覧からオプションを選択することで、選択したルートのコンテンツを階層形式で再生成します。
- Object Store Hierarchy
- 現在のオブジェクト階層を表示するツリー。このツリーからノードを選択すると、そのロケーションに保存されているオブジェクトが表示されます。
- Objects
- 選択したロケーションに保存されているオブジェクトを示すアイコン一覧。
- Object Details
- 現在選択されているオブジェクトに関する情報。ステートビューワーレポジトリにてオブジェクトのタイプが分かっている場合のみ表示されます。「OSVの記述」 を参照し、オブジェクトを正しく表示するようにしてください。
13.4.3. Object State Viewer (OSV)
13.4.3.1. OSVの記述
com.arjuna.ats.tools.objectstorebrowser.stateviewers.StateViewerInterface
インターフェースの実装クラスとなっています。
plugins/
ディレクトリ内のJARにてパッケージされています。例13.1「AbstractRecord
クラス」 の例は、AbstractRecord
クラスのOSVプラグインを作成しています。
例13.1 AbstractRecord
クラス
public class SimpleRecord extends AbstractRecord { private int _value = 0; ..... public void increase() { _value++; } public int get() { return _value; } public String type() { return “/StateManager/AbstractRecord/SimpleRecord”; } public boolean restore_state(InputObjectState os, int i) { boolean returnValue = true; try { _value = os.unpackInt(); } catch (java.io.IOException e) { returnValue = false; } return returnValue; } public boolean save_state(OutputObjectState os, int i) { boolean returnValue = true; try { os.packInt(_value); } catch (java.io.IOException e) { returnValue = false; } return returnValue; } }
getValue()
メソッドを呼出すことでこの目的を簡単に達成することができます。
public class SimpleRecordOSVPlugin implements StateViewerInterface { /** * A uid node of the type this viewer is registered against has been expanded. * @param os * @param type * @param manipulator * @param node * @throws ObjectStoreException */ public void uidNodeExpanded(ObjectStore os, String type, ObjectStoreBrowserTreeManipulationInterface manipulator, UidNode node, StatePanel infoPanel) throws ObjectStoreException { // Do nothing } /** * An entry has been selected of the type this viewer is registered against. * * @param os * @param type * @param uid * @param entry * @param statePanel * @throws ObjectStoreException */ public void entrySelected(ObjectStore os, String type, Uid uid, ObjectStoreViewEntry entry, StatePanel statePanel) throws ObjectStoreException { SimpleRecord rec = new SimpleRecord(); if ( rec.restore_state( os.read_committed(uid, type), ObjectType.ANDPERSISTENT ) ) { statePanel.setData( “Value”, rec.getValue() ); } } /** * Get the type this state viewer is intended to be registered against. * @return */ public String getType() { return “/StateManager/AbstractRecord/SimpleRecord”; } }
uidNodeExpanded
メソッドを呼び出します。このAbstract Record は直接オブジェクトストアにて見ることができません。アトミックアクションのリストの1つからでなければ閲覧できなくなっています。特定のタイプを持つオブジェクトを示すObject ビューからエントリーを選択すると、entrySelected
メソッドが呼び出されます。両方のメソッドにてStatePanel を使いオブジェクトステートについての情報を表示します。StatePanel にはStatePanel メソッドに記載されているメソッドが含まれており、この情報の表示がしやすくなっています。
StatePanel メソッド
setInfo
(Stringinfo
)- 一般情報を表示します。
setData
(Stringname
, Stringvalue
)- テーブルに情報を入力し、Object Store Browser ツールで表示します。
enableDetailsButton
(DetailsButtonListenerlistener
)- Details ボタンを有効にします。リスナインターフェースにより、ボタンを押すとプラグインに通知されるようになっています。開発者はこの情報の表示方法を管理します。
getValue()
メソッドで返された値を使いステートパネルテーブルに入力しています。getType()
メソッドは登録に関するプラグインタイプを返します。
例13.2 Ant でプラグインをパッキング
<jar jarfile="osbv-simplerecord.jar"> <fileset dir="build" includes="*.class”/> <manifest> <section name="arjuna-tools-objectstorebrowser"> <attribute name="plugin-classname-1" value=" SimpleRecordOSVPlugin "/> </section> </manifest> </jar>
bin/tools/plugins
ディレクトリに置きます。
第14章 Transactional Objects for Javaを利用するアプリケーションの構築
14.1. アプリケーションの構築
save_state
および restore_state
メソッドを開発するだけでなく、操作に適切なロックを設定し、見合ったJBoss Transaction Service クラスコンストラクタを呼び出すことに焦点をおく必要があります。反対にアプリケーション開発者が気を使うのは、特にアトミックな (atomic) アクションあるいはトランザクションの利用などの面でアプリケーションの一般的な構造を定義する点です。
14.1.1. キューの説明
QUEUE_SIZE
要素の制限が課されています。
例14.1 キュークラスのJava インターフェース定義
public class TransactionalQueue extends LockManager { public TransactionalQueue (Uid uid); public TransactionalQueue (); public void finalize (); public void enqueue (int v) throws OverFlow, UnderFlow, QueueError, Conflict; public int dequeue () throws OverFlow, UnderFlow, QueueError, Conflict; public int queueSize (); public int inspectValue (int i) throws OverFlow, UnderFlow, QueueError, Conflict; public void setValue (int i, int v) throws OverFlow, UnderFlow, QueueError, Conflict; public boolean save_state (OutputObjectState os, int ObjectType); public boolean restore_state (InputObjectState os, int ObjectType); public String type (); public static final int QUEUE_SIZE = 40; // maximum size of the queue private int[QUEUE_SIZE] elements; private int numberOfElements; };
14.1.2. コンストラクタおよびデコンストラクタ
例14.2 既存の永続オブジェクトを利用
public TransactionalQueue (Uid u) { super(u); numberOfElements = 0; }
例14.3 新規永続オブジェクトの作成
public TransactionalQueue () { super(ObjectType.ANDPERSISTENT); numberOfElements = 0; try { AtomicAction A = new AtomicAction(); A.begin(0); // Try to start atomic action // Try to set lock if (setlock(new Lock(LockMode.WRITE), 0) == LockResult.GRANTED) { A.commit(true); // Commit } else // Lock refused so abort the atomic action A.rollback(); } catch (Exception e) { System.err.println(“Object construction error: “+e); System.exit(1); } }
begin
メソッドを呼び出す必要があります。次に、その操作によりオブジェクトに対して適切なロックを設定しなければなりません。その後、コンストラクタの主要ボディ部分が実行されます。成功すると、アトミックアクションがコミットされ、失敗すると中断されます。
LockManager
メソッドのterminate
メソッドを呼び出すだけです。
例14.4 キュークラスのデストラクタ
public void finalize () { super.terminate(); }
14.1.3. save_state
、restore_state
、type
メソッド
例14.5 save_state
メソッド
public boolean save_state (OutputObjectState os, int ObjectType) { if (!super.save_state(os, ObjectType)) return false; try { os.packInt(numberOfElements); if (numberOfElements > 0) { for (int i = 0; i < numberOfElements; i++) os.packInt(elements[i]); } return true; } catch (IOException e) { return false; } }
例14.6 restore_state
メソッド
public boolean restore_state (InputObjectState os, int ObjectType) { if (!super.restore_state(os, ObjectType)) return false; try { numberOfElements = os.unpackInt(); if (numberOfElements > 0) { for (int i = 0; i < numberOfElements; i++) elements[i] = os.unpackInt(); } return true; } catch (IOException e) { return false; } }
例14.7 type
メソッド
LockManager
クラスから継承されているため、操作タイプはトランザクショナルキューを返すはずです。
public String type () { return "/StateManager/LockManager/TransactionalQueue"; }
14.1.4. enqueue/dequeue 操作
enqueue
メソッド」 のenqueue
操作が適切です。dequeue
も良く似た構造ですが、例には含まれていません。
例14.8 enqueue
メソッド
public void enqueue (int v) throws OverFlow, UnderFlow, QueueError { AtomicAction A = new AtomicAction(); boolean res = false; try { A.begin(0); if (setlock(new Lock(LockMode.WRITE), 0) == LockResult.GRANTED) { if (numberOfElements < QUEUE_SIZE) { elements[numberOfElements] = v; numberOfElements++; res = true; } else { A.rollback(); throw new UnderFlow(); } } if (res) A.commit(true); else { A.rollback(); throw new Conflict(); } } catch (Exception e1) { throw new QueueError(); } }
14.1.5. queueSize
メソッド
public int queueSize () throws QueueError, Conflict { AtomicAction A = new AtomicAction(); int size = -1; try { A.begin(0); if (setlock(new Lock(LockMode.READ), 0) == LockResult.GRANTED) size = numberOfElements; if (size != -1) A.commit(true); else { A.rollback(); throw new Conflict(); } } catch (Exception e1) { throw new QueueError(); } return size; }
14.1.6. inspectValue
および setValue
メソッド
注記
setValue
の実装は出ていませんが、提示されているinspectValue
メソッドから推測していただけるでしょう。
public int inspectValue (int index) throws UnderFlow, OverFlow, Conflict, QueueError { AtomicAction A = new AtomicAction(); boolean res = false; int val = -1; try { A.begin(); if (setlock(new Lock(LockMode.READ), 0) == LockResult.GRANTED) { if (index < 0) { A.rollback(); throw new UnderFlow(); } else { // array is 0 - numberOfElements -1 if (index > numberOfElements -1) { A.rollback(); throw new OverFlow(); } else { val = elements[index]; res = true; } } } if (res) A.commit(true); else { A.rollback(); throw new Conflict(); } } catch (Exception e1) { throw new QueueError(); } return val; }
14.1.7. クライアント
例14.9 TransactionalQueue
オブジェクトのインスタンスを作成
public static void main (String[] args) { TransactionalQueue myQueue = new TransactionalQueue();
queueSize
メソッドを使いトランザクションを開始します。
例14.10 queueSize
メソッド
AtomicAction A = new AtomicAction(); int size = 0; try { A.begin(0); s try { size = queue.queueSize(); } catch (Exception e) { } if (size >= 0) { A.commit(true); System.out.println(“Size of queue: “+size); } else A.rollback(); } catch (Exception e) { System.err.println(“Caught unexpected exception!”); }
14.1.8. 注記
enqueue
操作を実行する必要がある場合、別のアトミックアクションの中にenqueue
操作をネストすることができます。さらに、永続オブジェクトに対する同時操作をシリアル化することで、オブジェクトの状況に矛盾が発生しないようにします。キューオブジェクトの要素が個別で同時制御されているため、同時操作の呼出しで特定の組み合わせにおいては順番に実行されることもありますが、論理的には同時実行されているようになっています。これは、キューにある要素の状況を2種類変更する場合などに発生します。
第15章 設定オプション
15.1. オプション
表15.1 設定オプション
名称
|
値
|
詳細
| ||||
---|---|---|---|---|---|---|
com.arjuna.ats.arjuna.objectstore.storeSync
|
ON/OFF
|
オブジェクトストアの同期を有効化あるいは無効化。慎重に利用のこと。
| ||||
com.arjuna.ats.arjuna.objectstore.storeType
|
|
利用するオブジェクトストア実装の種類を指定
| ||||
com.arjuna.ats.arjuna.objectstore.hashedDirectories
|
整数
| HashedStore オブジェクトストア実装の場合に、ディレクトリ数を設定しオブジェクトステートをハッシュ
| ||||
com.arjuna.ats.txoj.lockstore.lockStoreType
|
|
使用するロックストア実装の種類を指定
| ||||
com.arjuna.ats.txoj.lockstore.lockStoreDir
|
|
ロックストアの場所を指定
| ||||
com.arjuna.ats.arjuna.objectstore.objectStoreDir
|
アプリケーションが書き込みできる場所
|
オブジェクトストアの場所を指定
| ||||
com.arjuna.ats.arjuna.objectstore.localOSRoot
|
defaultStore
|
オブジェクトストアの root 名を指定
| ||||
com.arjuna.ats.arjuna.coordinator.actionStore
|
|
使用するトランザクションログ実装
| ||||
com.arjuna.ats.arjuna.coordinator.asyncCommit
|
YES/NO
|
非同期の
commit をOn/Off。デフォルトでは off。
|
表15.2 設定オプション (パート2)
名称
|
値
|
詳細
|
---|---|---|
com.arjuna.ats.arjuna.coordinator.asyncPrepare
|
YES/NO
|
非同期の
prepare をOn/Off。デフォルトではOff。
|
com.arjuna.ats.arjuna.objectstore.transactionSync
|
ON/OFF
|
オブジェクトストアの同期をOn/Off。慎重に利用のこと。
|
com.arjuna.ats.arjuna.objectstore.jdbcUserDbAccess
|
JDBCAccess クラス名
|
JDBCAccess 実装を指定しユーザーレベルのオブジェクトストアを利用
|
com.arjuna.ats.arjuna.objectstore.jdbcTxDbAccess
|
JDBCAccess クラス名
|
トランザクションオブジェクトストアに利用する JDBCAccess実装の指定
|
com.arjuna.ats.arjuna.coordinator.commitOnePhase
|
YES/NO
|
1相
commit の最適化を有効化あるいは無効化
|
com.arjuna.ats.arjuna.coordinator.readonlyOptimisation
|
YES/NO
|
2相
abort に対する読み取り専用の最適化を有効化あるいは無効化
|
com.arjuna.ats.arjuna.coordinator.enableStatistics
|
YES/NO
|
トランザクション統計情報の収集停止あるいは開始
|
com.arjuna.ats.arjuna.coordinator.startDisabled
|
YES/NO
|
トランザクションシステムを有効な状態、あるいは無効な状態で開始。脚注に記載のクラスを使って調整。[a]
|
com.arjuna.ats.arjuna.coordinator.defaultTimeout
|
整数
|
タイムアウト (ミリ秒)
|
[a]
com.arjuna.ats.arjuna.coordinator.TxControl クラスを使い調整
|
付録A オブジェクトストア実装
A.1. ObjectStore
例A.1 オブジェクトストアの種類
/* * This is the base class from which all object store types are derived. * Note that because object store instances are stateless, to improve * efficiency we try to only create one instance of each type per process. * Therefore, the create and destroy methods are used instead of new * and delete. If an object store is accessed via create it *must* be * deleted using destroy. Of course it is still possible to make use of * new and delete directly and to create instances on the stack. */ public class ObjectStore { public static final int OS_COMMITTED; public static final int OS_COMMITTED_HIDDEN; public static final int OS_HIDDEN; public static final int OS_INVISIBLE; public static final int OS_ORIGINAL; public static final int OS_SHADOW; public static final int OS_UNCOMMITTED; public static final int OS_UNCOMMITTED_HIDDEN; public static final int OS_UNKNOWN; public ObjectStore (ClassName type); public ObjectStore (ClassName type, String osRoot); public ObjectStore (String osRoot); public synchronized boolean allObjUids (String s, InputObjectState buff) throws ObjectStoreException; public synchronized boolean allObjUids (String s, InputObjectState buff, int m) throws ObjectStoreException; public synchronized boolean allTypes (InputObjectState buff) throws ObjectStoreException; public synchronized int currentState(Uid u, String tn) throws ObjectStoreException; public synchronized boolean commit_state (Uid u, String tn) throws ObjectStoreException; public synchronized boolean hide_state (Uid u, String tn) throws ObjectStoreException; public synchronized boolean reveal_state (Uid u, String tn) throws ObjectStoreException; public synchronized InputObjectState read_committed (Uid u, String tn) throws ObjectStoreException; public synchronized InputObjectState read_uncommitted (Uid u, String tn) throws ObjectStoreException; public synchronized boolean remove_committed (Uid u, String tn) throws ObjectStoreException; public synchronized boolean remove_uncommitted (Uid u, String tn) throws ObjectStoreException; public synchronized boolean write_committed (Uid u, String tn, OutputObjectState buff) throws ObjectStoreException; public synchronized boolean write_uncommitted (Uid u, String tn, OutputObjectState buff) throws ObjectStoreException; public static void printState (PrintStream strm, int res); };
ObjectState
クラスのインスタンスを操作します。このObjectState クラスはオブジェクトのtype()
操作とUid から取得したタイプを利用し命名されています。このストア内にあるオブジェクトステートは通常、OS_COMMITTED
とOS_UNCOMMITTED
のように全く違った種類のステート2つのうち1つとなっています。オブジェクトステートは、OS_COMMITTED
のステートから開始しますが、atomic アクションの制御のもと変更されると、OS_UNCOMMITTED
のステートにある、2番目の新規オブジェクトのステートを書き込むことができます。このアクションがコミットされると、元のオブジェクトステートがこの2番目のオブジェクトステートで置き換えられ、OS_COMMITTED
になります。このアクションが中断されると、2番目の状態は破棄されます。このリリースで提供されている実装はすべて、シャドーコピーを使い、これらのステート移行を処理します。しかし、別の方法でこれらのステートを実装することも可能です。クラッシュリカバリシステムの制御のもと、オブジェクトステートが隠されアクセスできなくなります。
allTypes
および allObjUids
メソッドにより、ストアの内容を参照することができます。allTypes
メソッドが、ストア内にある全オブジェクトの全タイプ名を含むInputObjectState を返します。このメソッドは null 名で終了します。また、allObjUids
メソッドは、特定の種類の全オブジェクトに含まれるUid すべてで構成されるInputObjectStateを返し、特別なUid.nullUid()
タイプにより終了されます。
A.2. 永続オブジェクトストア
前述した機能に加え、同梱の永続オブジェクトストアはすべて以下のルールに従います。
- 各オブジェクトステートは独自ファイル内に格納され、オブジェクトの
Uid
を使い命名されます。 type()
オペレーションにより提供されるオブジェクトの種類は、オブジェクトがどのディレクトリに置かれるかを決定します。- 全ストアには、JBoss Transaction Service 設定により決定される共通の root ディレクトリがあり、このディレクトリ名は自動的にストア固有のroot 情報にプリペンドされます。
- また、ストアはすべてローカライズされた root ディレクトリを利用可能で、このディレクトリは最終的なディレクトリ名を確定するため、オブジェクトのタイプの先頭に自動的に追加されます。このローカライズされた root 名は、ストア作成時に指定されます。デフォルトのローカル root 名は、
defaultStore
となります。
表A.1 オブジェクトストア情報の例
項目
|
値の例
|
---|---|
ObjectStore root Directory from configure
| /JBossTS/ObjectStore/
|
ObjectStore Type 1
| FragmentedStore/
|
Default root
| defaultStore/
|
StateManager
| StateManager
|
LockManager
| LockManager/
|
User Types
|
|
Localised root 2
| myStore/
|
StateManager
| StateManager/
|
ObjectStore Type2
| ActionStore/
|
Default root
| defaultStore/
|
A.2.1. シャドーストア
ShadowingStore
クラスにより実装されます。これは、オブジェクトを表現する際にファイルをシャドー版とコミット版のペアで利用しており、シンプルですが動作が遅くなっています。オブジェクトストアとのやりとりの間、ファイルが開かれ、ファイルでロックや操作、ロック解除が行われ、ファイルを閉じます。こうするとファイルの開閉や名前変更だけに厳密に必要とされるリソースよりも多くのリソースが必要となります。
A.2.2. ファイルレベルのロッキングはなし
LockManager
メソッドで同時制御されているため、ファイルレベルで別途ロッキングの必要がありません。そのため、JBoss Transaction Serviceにおけるデフォルトのオブジェクトストア実装 ShadowNoFileLockStore
は、ユーザーレベルのロッキングに依存しており、ShadowingStore
実装よりもパフォーマンスを向上することができます。
A.2.3. Hashed Store
HashedStore
実装は、シャドーストアと同じオブジェクト状態の構造を利用しますが、同タイプのオブジェクトを大量に格納できるよう設計されている別のディレクトリ構造も使用します。オブジェクトの Uid を使うハッシュ機能により、オブジェクトは多くのディレクトリ全体に拡散されています。デフォルトでは、サブディレクトリを 255 個使いますが、HASHED_DIRECTORIES
環境変数を設定することでこれをオーバーライドすることができます。
A.2.4. JDBC ストア
JDBCStore
実装は、JDBC データーベース内に永続オブジェクトのステートを格納します。Transactional Objects for Java APIと併せてJBDCStore
を利用している場合、ネスト化されたトランザクションもサポートされます。オブジェクトのステートはすべて、単一テーブル内にBinary Large Objects (BLOB)として格納されます。オブジェクトステートのサイズは、64k までに制限されています。この制限を越えたオブジェクトステートを保存しようとすると、例外がスローされ、このステートは格納されません。このトランザクションは強制的にロールバックされます。
JDBCAccess
インターフェースの実装を提供する必要があります。例A.2「JDBCAccess 実装の例」を参照してください。
例A.2 JDBCAccess 実装の例
public interface JDBCAccess { public Connection getConnection () throws SQLException; public void putConnection (Connection conn) throws SQLException; public void initialise (ObjectName objName); }
JDBCAccess
クラスの実装により、JDBC ObjectStore で利用するConnection
が提供され、オブジェクトステートを保存、リストアできます。詳細についてはJDBCAccess
Connection
メソッド を参照してください。
JDBCAccess
Connection
メソッド
getConnection
- 使用するConnection を返します。接続が必要な場合はいつでも、このメソッドが呼び出され、この実装はどの接続を返すかを決定する際に必要なポリシーを使うはずです。このメソッドは同じConnection メソッドを1回以上返す必要はありません。
putConnection
- getConnectionから取得したConnection の1つを返します。これらを利用中にエラーが発生するとConnection が返されます。
initialise
- 実装に対する追加の任意情報を渡します。
Connections
の数をリクエストし、com.arjuna.ats.arjuna.objectstore.jdbcPoolSizeMaximum プロパティにて定義されているもののみ利用します。
JDBCAccess
インターフェースの実装は、com.arjuna.ats.arjuna.objectstore.jdbcUserDbAccess プロパティ変数にて設定する必要があります。
JDBCAccess
メソッドはcom.arjuna.ats.arjuna.objectstore.jdbcTxDbAccess プロパティ経由から提供します。デフォルトのテーブル名は、JBossTSTxTable
となっています。
注記
JDBCAccess
実装を利用することができます。
A.2.5. Cached Store
キャッシュストアの設定オプション
- com.arjuna.ats.internal.arjuna.objectstore.cacheStore.hash
- 内部のストア数を設定しステートをハッシュ。デフォルト値は
128
。 - com.arjuna.ats.internal.arjuna.objectstore.cacheStore.size
- フラッシュがトリガーされるまでにキャッシュの最大許容サイズ。デフォルト値は
10240
バイト。 - com.arjuna.ats.internal.arjuna.objectstore.cacheStore.removedItems
- フラッシュがトリガーされる前にキャッシュが含むことのできる削除アイテムの最大数。デフォルトでは、キャッシュ内のステートを削除するための呼出しのみがキャッシュからのステートを削除しエントリを空にすることで、パフォーマンスが向上。キャッシュがフラッシュされるとエントリは削除される。デフォルト値は、ハッシュサイズの2倍。
- com.arjuna.ats.internal.arjuna.objectstore.cacheStore.workItems
- フラッシュ前にキャッシュが保つことのできる最大アイテム数。デフォルト値は
100
。 - com.arjuna.ats.internal.arjuna.objectstore.cacheStore.scanPeriod
- キャッシュをフラッシュする間隔 (ミリ秒)。デフォルトは
120
秒。 - com.arjuna.ats.internal.arjuna.objectstore.cacheStore.sync
- キャッシュのフラッシュをディスクと同期するか決定。デフォルトは
OFF
で、別の入力可能値はON
。
付録B クラス定義
B.1. はじめに
B.2. クラスライブラリ
例B.1 Lock Manager
public class LockResult { public static final int GRANTED; public static final int REFUSED; public static final int RELEASED; }; public class ConflictType { public static final int CONFLICT; public static final int COMPATIBLE; public static final int PRESENT; }; public abstract class LockManager extends StateManager { public static final int defaultRetry; public static final int defaultTimeout; public static final int waitTotalTimeout; public final synchronized boolean releaselock (Uid lockUid); public final synchronized int setlock (Lock toSet); public final synchronized int setlock (Lock toSet, int retry); public final synchronized int setlock (Lock toSet, int retry, int sleepTime); public void print (PrintStream strm); public String type (); public boolean save_state (OutputObjectState os, int ObjectType); public boolean restore_state (InputObjectState os, int ObjectType); protected LockManager (); protected LockManager (int ot); protected LockManager (int ot, ObjectName attr); protected LockManager (Uid storeUid); protected LockManager (Uid storeUid, int ot); protected LockManager (Uid storeUid, int ot, ObjectName attr); protected void terminate (); };
例B.2 StateManager
public class ObjectStatus { public static final int PASSIVE; public static final int PASSIVE_NEW; public static final int ACTIVE; public static final int ACTIVE_NEW; }; public class ObjectType { public static final int RECOVERABLE; public static final int ANDPERSISTENT; public static final int NEITHER; }; public abstract class StateManager { public boolean restore_state (InputObjectState os, int ot); public boolean save_state (OutputObjectState os, int ot); public String type (); public synchronized boolean activate (); public synchronized boolean activate (String rootName); public synchronized boolean deactivate (); public synchronized boolean deactivate (String rootName); public synchronized boolean deactivate (String rootName, boolean commit); public synchronized int status (); public final Uid get_uid (); public void destroy (); public void print (PrintStream strm); protected void terminate (); protected StateManager (); protected StateManager (int ot); protected StateManager (int ot, ObjectName objName); protected StateManager (Uid objUid); protected StateManager (Uid objUid, int ot); protected StateManager (Uid objUid, int ot, ObjectName objName); protected synchronized final void modified (); };
例B.3 Input/OutputObjectState
class OutputObjectState extends OutputBuffer { public OutputObjectState (Uid newUid, String typeName); public boolean notempty (); public int size (); public Uid stateUid (); public String type (); }; class InputObjectState extends ObjectState { public OutputObjectState (Uid newUid, String typeName, byte[] b); public boolean notempty (); public int size (); public Uid stateUid (); public String type (); };
例B.4 Input/OutputBuffer
public class OutputBuffer { public OutputBuffer (); public final synchronized boolean valid (); public synchronized byte[] buffer(); public synchronized int length (); /* pack operations for standard Java types */ public synchronized void packByte (byte b) throws IOException; public synchronized void packBytes (byte[] b) throws IOException; public synchronized void packBoolean (boolean b) throws IOException; public synchronized void packChar (char c) throws IOException; public synchronized void packShort (short s) throws IOException; public synchronized void packInt (int i) throws IOException; public synchronized void packLong (long l) throws IOException; public synchronized void packFloat (float f) throws IOException; public synchronized void packDouble (double d) throws IOException; public synchronized void packString (String s) throws IOException; }; public class InputBuffer { public InputBuffer (); public final synchronized boolean valid (); public synchronized byte[] buffer(); public synchronized int length (); /* unpack operations for standard Java types */ public synchronized byte unpackByte () throws IOException; public synchronized byte[] unpackBytes () throws IOException; public synchronized boolean unpackBoolean () throws IOException; public synchronized char unpackChar () throws IOException; public synchronized short unpackShort () throws IOException; public synchronized int unpackInt () throws IOException; public synchronized long unpackLong () throws IOException; public synchronized float unpackFloat () throws IOException; public synchronized double unpackDouble () throws IOException; public synchronized String unpackString () throws IOException; };
例B.5 Uid
public class Uid implements Cloneable { public Uid (); public Uid (Uid copyFrom); public Uid (String uidString); public Uid (String uidString, boolean errorsOk); public synchronized void pack (OutputBuffer packInto) throws IOException; public synchronized void unpack (InputBuffer unpackFrom) throws IOException; public void print (PrintStream strm); public String toString (); public Object clone () throws CloneNotSupportedException; public synchronized void copy (Uid toCopy) throws UidException; public boolean equals (Uid u); public boolean notEquals (Uid u); public boolean lessThan (Uid u); public boolean greaterThan (Uid u); public synchronized final boolean valid (); public static synchronized Uid nullUid (); };
例B.6 AtomicAction
public class AtomicAction { public AtomicAction (); public void begin () throws SystemException, SubtransactionsUnavailable, NoTransaction; public void commit (boolean report_heuristics) throws SystemException, NoTransaction, HeuristicMixed, HeuristicHazard,TransactionRolledBack; public void rollback () throws SystemException, NoTransaction; public Control control () throws SystemException, NoTransaction; public Status get_status () throws SystemException; /* Allow action commit to be supressed */ public void rollbackOnly () throws SystemException, NoTransaction; public void registerResource (Resource r) throws SystemException, Inactive; public void registerSubtransactionAwareResource (SubtransactionAwareResource sr) throws SystemException, NotSubtransaction; public void registerSynchronization (Synchronization s) throws SystemException, Inactive; };
パート III. XTS開発
第16章 はじめに
XTS に含まれるプロトコル
- WS-Coordination (WS-C) はIBM、Microsoft、BEA により開発された一般的なコーディネーションフレームワークです。
- WS-Atomic Transaction (WS-AT) とWS-Business Activity (WS-BA) には、このフレームワークを使うWS-Transaction (WS-T) のトランザクションプロトコルが含まれます。
注記
Web Services のプロパティ
- Web Services として公開されているアプリケーションコンポーネントはサードパーティにより所有されている場合があり、保守コストの面で利点がありますが、その動作に排他的な制御があるかという面では欠点となってしまいます。
- Web Servcies は通常リモートに置かれており、呼出し時のネットワーク利用が増えるため、障害リスクが増えてしまいます。
16.1. サービスベース処理の管理
このアプリケーションではユーザが夜の外出を計画できるようになっており、レストランの予約、ショーのチケット予約などを行います。これらのアクティビティはクレジットカードで支払いをします。この例では、各サービスは別のサービスプロバイダーにより提供された公開Web Servcies を表していますが、XTSを使うことで劇場とレストランサービス間とのやりとりを長期にわたる (場合によっては) ビジネストランザクション1つにまとめます。1つのイベントに問題があれば、ユーザはイベント両方を却下できるようになっているため、両サービスを元のステートに戻します。量イベントが問題なく行われると、ユーザのクレジットカードに課金され、レストラン、ショー両方が予約されます。ご存じのように、両サービス間のやりとりを長期にわたり信頼できるかたちで管理する必要があります。さらに、リモートでデプロイされている複数のサードパーティサービスをすべて管理する必要があります。
16.2. サーブレット
注記
16.3. SOAP
16.4. Web Services Description Language (WDSL)
第17章 トランザクションの概要
注記
図17.1 XTS トランザクションに関わるコンポーネント
17.1. コーディネータ
create
要求をコーディネーションサービスにポストします。これによりコーディネータが作成され、クライアントにその詳細を返します。このサービスは独自のコンテナに配置されるか、アプリケーションクライアントあるいはトランザクショナルな Web Services のひとつと共に配置され、パフォーマンスを向上させます。コーディネーションサービスは通常多くのトランザクションを並行して管理する役割を果たすため、各コーディネータは一意のトランザクション識別子により特定されます。
17.2. トランザクションコンテキスト
コンテキストのコンテンツ
- トランザクション識別子
- 個々のトランザクションにグローバルな一意性を保証します。
- トランザクションコーディネータの場所
- パーティシパントが登録するためにコンタクトするエンドポイントアドレスです。
図17.2 Web Services とコンテキストフロー
17.3. パーティシパント
17.4. ACID トランザクション
ACID プロパティ
- アトミック性
- トランザクションは完全に実行されるか、あるいは全く実行しません。
- 一貫性
- トランザクションの結果、基礎となるデータ構造の内部一貫性を保持します。
- 独立性
- まるでトランザクションそれだけで実行していて、他には実行しているトランザクションがなく、他のトランザクションから見えません。
- 持続性
- トランザクションの結果は障害が発生しても失われません。
17.5. 2 相コミット
図17.3 2 相コミットの概要
注記
17.6. 同期プロトコル
手順17.1 同期により作成された「4 相プロトコル」
ステップ 1
トランザクションが 2 相コミットを開始する前に、すべての登録された同期は通知されます。この時点で障害が発生すると、トランザクションはロールバックされます。ステップ 2 と 3
次に、コーディネータは通常の 2 相コミットプロトコルを実行します。ステップ 4
トランザクションが終了した時点で、すべての登録された同期は通知されます。ただし、この段階で発生する障害はすべて無視されるためこれは任意の呼び出しです。つまりトランザクションは終了し、影響が及ぶものは何もありません。
17.7. プロトコルの最適化
表17.1 2 相コミットプロトコルのバリアント
バリアント
|
詳細
|
---|---|
Presumed Abort
|
トランザクションがロールバックする場合、コーディネータはこの情報をローカルに記録しすべての登録されたパーティシパントに伝えることができます。パーティシパントに連絡が取れなかった場合はトランザクションの結果に影響はありません。コーディネータは礼儀としてパーティシパントに通知しているだけです。すべてのパーティシパントと連絡が取れた時点で、トランザクションに関する情報は削除することが可能です。トランザクションのステータスに関する後続要求が発生すると、使用できる情報はなく、要求側はトランザクションが中断したと仮定できます。この最適化の利点は、トランザクションが
prepare フェーズの終わりまで進みコミットすると決定するまで、パーティシパントに関する情報は永続的にする必要がない点です。この時点より前のすべての障害はトランザクションの中断と見なされるためです。
|
One-Phase
|
単一のパーティシパントのみがトランザクションに関わっている場合、コーディネータはトランザクションを
prepare フェーズで通過させる必要はありません。従って、パーティシパントはコミットするよう指示され、コーディネータはその決定に関する情報を記録する必要はありません。トランザクションの結果はパーティシパントの責任であるためです。
|
Read-Only
|
パーティシパントが準備するよう指示されると、制御する情報やデータがトランザクションの間に修正されていないことをコーディネータに示します。そうしたパーティシパントはトランザクションの結果について通知される必要はありません。パーティシパントの結果はトランザクションに影響がないためです。そのため、read-only のパーティシパントはコミットプロトコルの第 2 フェーズから省くことができます。
|
注記
17.8. 非アトミックトランザクションとヒューリスティックな結果
prepare
フェーズを過ぎたパーティシパントはコミットまたはロールバックするか自発的に決定できます。そうしたパーティシパントが最終的に当初のトランザクションを完了する要求を行う場合にそうできるようにするため、決定を記録する必要があります。コーディネータは最終的にはトランザクションの結果をパーティシパントに通知します。結果がパーティシパントが決定したものと同じであれば、競合は存在しません。パーティシパントとコーディネータの決定が異なる場合は、その状況は非アトミックな結果、さらに具体的に言うと ヒューリスティックな結果 と呼ばれます。
表17.2 ヒューリスティックな結果
結果
|
詳細
|
---|---|
Heuristic Rollback
|
パーティシパントの一部またはすべてがトランザクションを一方的にロールバックしたため、コミット動作は失敗しました。
|
Heuristic Commit
|
すべてのパーティシパントが一方的にコミットしたため試行したロールバック動作は失敗しました。これが発生する可能性がある状況としては、コーディネータがトランザクションの
prepare に成功したが、そのトランザクションのログが更新できなかったためトランザクションをロールバックしようと決定する場合です。コーディネータが決定を行う一方で、パーティシパントはコミットするよう決定します。
|
Heuristic Mixed
|
一部のパーティシパントはコミットし、他のパーティシパントはロールバックされました。
|
Heuristic Hazard
|
一部の更新のディスポジションは既知ではありません。更新が既知の場合は、すべてコミットされるか、すべてロールバックされます。
|
17.9. 割り込み
図17.4 割り込み
prepare
するために、トップレベルのコーディネータが必要なことは、1 つの prepare
メッセージを下位のコーディネータに送り、1 つの prepared
または aborted
応答を受け取ることだけです。下位のコーディネータは prepare
を各パーティシパントにローカルに転送し、結果を組み合わせて単一の prepared
または aborted
応答を送るかどうか決定します。
17.10. 新しいトランザクションプロトコル
Web Services に ACID が適さない理由
- 典型的な ACID トランザクションは、アプリケーションを開発およびデプロイする組織はアプリケーションの全体的なインフラストラクチャを所有すると仮定しています。このインフラストラクチャは従来イントラネットの形式を取っていました。オーナーシップは信頼できる、予測可能な方法でトランザクションが動作することを暗示しています。ACID の性質を確保するために、潜在的に寿命の長いロックは 2 相コミットの間に基礎となるデータ構造で保管することが可能です。リソースはどの期間でも使用され、トランザクションが完了すると解放されます。Web Services ではこうした仮定は有効ではなくなりました。明らかな理由としては、Web サービスを通じて公開されたデータの所有者がデータが長期間ロックされることを拒否するためです。そのようなロックを許可すると Dos 攻撃を招きます。
- すべてのアプリケーションインフラストラクチャは一般的には単一のパーティが所有しています。典型的な ACID トランザクションを使用するシステムは、トランザクション内のパーティシパントはトランザクションマネージャの指示に従い、トランザクション内の他のパーティシパントに悪影響がある一方的な決定を行うのはまれだと通常は仮定しています。トランザクションに参加する Web Services はいつでもトランザクションを辞退する決定を効果的に行うことができ、概してサービスの消費者にはこれを防ぐサービス保証の質はほとんど持っていません。
17.10.1. 疎結合システムのトランザクション
Web Services の要件
- トランザクションに対し複数の成功する結果を処理し、操作の結果が独立も持続もできない操作に関わることができること。
- 自発的なパーティをコーディネートし、その関係が中央設計権限からの命令ではなくコントラクトで管理されている。
- パーティがライフタイムの間に停止を経験することが予想され、コーディネートした作業がそうした停止から存続できる不連続のサービス。
- 複数の通信プロトコルに対しXML を使用した相互動作。XTS は HTTP で送られる SOAP エンコーディングを使用します。
第18章 XTSで利用されるプロトコルの概要
注記
18.1. WS-Coordination
図18.1 WS-C Architecture
WS-C に対する一般要件
- 特定のアプリケーションインスタンスについて、特定のコーディネーションプロトコルへ新規コーディネータをインストールあるいはアクティベート
- コーディネータにパーティシパントを登録し、アプリケーション (アプリケーションの一部) が有効な間にコーディネータのプロトコルメッセージを受信
- アプリケーションを構成するWeb Services 間でコンテキスト情報を伝播
- 完了するまでコーディネーションプロトコルを駆動するエンティティ
図18.2 WS-Cでの4つの役割
18.1.1. アクティベーション
CreateCoordinationContext
操作を呼び出します。この操作は、必要なコーディネーションタイプ、タイムアウト、その他の関連情報など、作成すべきトランザクション詳細を指定するインプットを取得します。その後、トランザクション識別子、コーディネーションタイプ、登録サービスURLなど、新たに作成されたトランザクションコンテキストの詳細を含んだアウトプットを返します。
例18.1
<!-- Activation Service portType Declaration --> <wsdl:portType name="ActivationCoordinatorPortType"> <wsdl:operation name="CreateCoordinationContext"> <wsdl:input message="wscoor:CreateCoordinationContext"/> <wsdl:output message="wscoor:CreateCoordinationContextResponse"/> </wsdl:operation> </wsdl:portType>
注記
18.1.2. 登録
Register
操作を宣言している1つのポートを公開します。この操作で、パーティシパントのプロトコルタイプなど、パーティシパント詳細を指定するインプットを取得します。その後、該当のアウトプットレスポンスを返します。
例18.2 Registration ServiceWSDLインターフェース
<!-- Registration Service portType Declaration --> <wsdl:portType name="RegistrationCoordinatorPortType"> <wsdl:operation name="Register"> <wsdl:input message="wscoor:Register"/> <wsdl:output message="wscoor:RegisterResponse"/> </wsdl:operation> </wsdl:portType>
注記
18.1.3. 完了
18.2. WS-Transaction
18.2.1. WS-Transaction の基本
図18.3 WS-Coordination、WS-Transaction、WS-Business Activity
- WS-Cコンテキストを拡張しトランザクションコンテキストを作成します。
- 追加サービス (Completion, Volatile2PC, Durable2PC, BusinessAgreementWithParticipantCompletion, and BusinessAgreementWithCoordinatorCompletion) と2つのプロトコルメッセージセット (WS-Transaction で対応している各トランザクションモデルに対して1つずつ) でアクティベーションおよび登録サービスを強化し、WS-C プロトコルインフラストラクチャ上に本格的なトランザクションコーディネータを構築しています。
- 従来のトランザクションプロトコルとは異なるWS-Transactionの重要な側面は、同期リクエスト/レスポンスモデルを仮定にされていないことです。一方向メッセージの順番を使い、クライアント/パーティシパント間の通信やトランザクションのコーディネーションやパーティシパントのプロトコルに適したコーディネーションサービスを実装します。クライアントとパーティシパントコンテナはXTSサービスエンドポイントをデプロイしコーディネータサービスからメッセージを受信する必要があるという意味であるため、これは重要です。この要件は例18.2「Registration ServiceWSDLインターフェース」のRegistration Service WSDL で宣言されている
Register
およびRegisterResponse
メッセージ詳細にて見ることができます。Register
メッセージには、クライアントあるいはWeb サービスコンテナのエンドポイントURLを含みます。WS-Transaction コーディネーションサービスがメッセージをクライアントあるいはWeb サービスに送信したい場合にこのURLを使います。同様にRegisterResponse
メッセージにはURLが含まれており、このURLはクライアント/Web サービスが登録されているプロトコル固有のWS-Transaction コーディネーションサービスに対するエンドポイントを指定し、トランザクションコーディネータ宛てにメッセージを送ることができます。
18.2.2. WS-Transaction Architecture
図18.4 WS-Transaction グローバルビュー
図18.5 WS-Transaction Web Services およびパーティシパント
18.2.3. WS_Transaction モデル
18.2.3.1. アトミックトランザクション
手順18.1 アトミックトランザクションのプロセス
- アトミックトランザクションを開始するには、クライアントアプリケーションをまず、WS-Transactionに対応するWS-C Activation Coordinator のWeb サービスに設置します。
- クライアントは、http://schemas.xmlsoap.org/ws/2004/10/wsatをコーディネータタイプとして指定するWS-C
CreateCoordinationContext
メッセージをサービスに送信します。 - クライアントは、アクティベーションサービスから適切なWS-Transaction コンテキストを受信します。
CreateCoordinationContext
メッセージへのレスポンスである、トランザクションコンテキストは、CoordinationType
要素がWS-Atomic Transaction名前空間http://schemas.xmlsoap.org/ws/2004/10/wsatに設定されています。また、アトミックトランザクションのコーディネータエンドポイントであるWS-C Registration Service (ここでパーティシパントを登録可能) への参照を含みます。- クライアントは通常次に進みWeb Services を呼出し、Web サービスが変更した内容すべてをコミットするかロールバックすることで、トランザクションを完了します。この完了アクティビティを行うには、クライアントは
Register
メッセージをCoordination Context にてエンドポイントを返されたRegistration Service へ送信することで、自身をCompletion
プロトコルのパーティシパントとして登録する必要があります。 - 完了するよう登録されると、クライアントアプリケーションはWeb Services とやりとりを行いビジネスレベルの作業を遂行します。ビジネスのWeb サービスを呼び出すと毎回、クライアントは各呼出しが暗黙的にトランザクションによりスコープされているように、トランザクションコンテキストをSOAP ヘッダーブロックへ挿入します。WS-ATomic Transaction対応のWeb Services に対応するツールキットにより、SOAPヘッダブロックにあるコンテキストとバックエンド操作を相互に関連付けることができます。こうすることで、Web サービスが加えた変更がクライアントと同じトランザクションの範囲内で行われ、トランザクションコーディネータによるコミットあるいはロールバックの対象となるようにします。
- 必要とされるアプリケーションレベルの作業がすべて完了すると、クライアントは、サービスの状態を永続化する目的でトランザクションを終了することができます。完了パーティシパントは、コーディネータに対し、トランザクションのコミットあるいはロールバックを指示します。コミットあるいはロールバック操作が完了すると、状態はパーティシパントに返され、トランザクションの結果を示します。
- Volatile2pc
- 1つ目のプロトコルは、任意のVolatile2PC (2PCは2相コミットの略称)となっています。Volatile2PC プロトコルは、前述した同期プロトコルに相当するWS-Atomic Transaction です。通常、Web サービスがトランザクションのコミット前に揮発的な (キャッシュ) 状態をデータベースにフラッシュする必要がある場合に実行されます。これは通常アプリケーションのパフォーマンスを改善するために利用されています。フラッシュされると、2相対応のパーティシパントがこのデータを管理します。完了パーティシパントが
commit
操作を開始すると、prepare
メッセージ経由でトランザクションがもうすぐ完了する旨をVolatile2PC パーティシパントに通知します。パーティシパントはprepared
、aborted
あるいはreadonly
のメッセージ3つの内1つで返答することができます。この段階で失敗すると、トランザクションがロールバックされる原因になります。 - Durable2PC
- WS-Atomic Transactionの次のプロトコルは、Durable2PCで、Durable2PC プロトコルは、WS-Atomic Transaction の中核となっています。トランザクション内のパーティシパント間で必要なコンセンサスに持っていき、トランザクションが安全に終了できるようにします。Durable2PC プロトコルはパーティシパント間のアトミック性 (atomicity) を確保し、従来の技術推定アボート機能を持つ2 相コミット (two-phase commit with presumed abort)に基づいています。
手順18.2 Durable2PC Procedure
- 1番目のフェーズでは、コーディネータがprepare メッセージを送信すると、パーティシパントはトランザクション中に発生する状態の変更を永続化する必要があります。こうすることでこれらの変更を後でロールバックするか、コミットすることができます。この時点ではアトミックトランザクションがロールバックする可能性があるため、元の状態情報を失うことはできません。パーティシパントが
prepare
できない場合、コーディネータにaborted
メッセージで通知する必要があります。トランザクションは最終的にロールバックされます。また、パーティシパントがトランザクションのデータを何も変更しなかったサービスの対応をしている場合、readonly
メッセージを返し、コミットプロトコルの2番目のフェーズから省略されるようにすることが可能です。そうでない場合は、パーティシパントによりprepared
メッセージが送信されます。 - 1番目のフェーズで問題が発生しない場合、Durable2PC は2番目のフェーズに進み、ここではコーディネータが
commit
メッセージをパーティシパントに送信します。その後パーティシパントは、関連サービスが行った一時的な作業を永続化し、committed
メッセージをコーディネータに送信します。問題が発生した場合、コーディネータが全パーティシパントにrollback
メッセージを送信し、関連サービスにより行われた作業を破棄し、この段階に到達していない場合はprepare
で永続ストレージに保存された状態情報を削除します。パーティシパントはaborted
メッセージをコーディネータに送信することでロールバックに対応します。
注記
WS-Atomic Transaction プロトコルのセマンティクスには、1相コミットの最適化は含まれていません。1つのパーティシパントしか参加していない場合も、完全な2相コミットが常に利用されます。
図18.6 WS-Atomic 2相パーティシパントの状態移行
Completion
プロトコルを完了でき、クライアントアプリケーションにトランザクションがコミットされたか、ロールバックされたか通知することができます。さらに、Volatile2PC プロトコルを完了することができます。
prepare
フェーズのように、最後のフェーズは任意となっており、このフェーズを使いデータベース接続などリソースを開放できるように、パーティシパントにトランザクションの完了を通知可能です。
図18.7
18.2.3.2. Business Activities
手順18.3 BA プロセスの概要
- サービスは作業を行うように依頼されます。
- これらのサービスには、作業のやりなおし機能があり、BAが作業を後にキャンセルすると決定した場合にBAに通知します。BA が問題に直面した場合、このサービスに
undo
の動作を実行するように指示を出すことができます。
WS-BAの前提条件
- 状態の移行は、アプリケーションの状態やコーディネーションメタデータ (送受信メッセージの記録) など、すべて信頼できるかたちで記録されていること
- リクエストメッセージは、問題をできるだけ早く検出できるようにすべて確認されること。こうすることで、必要のない作業を行わずに済み、問題を早期発見できるため修正時はより簡単でコストも抑えることができます。
- アトミックトランザクションでは、レスポンス は、リクエストの出力としてではなく別の操作として定義されていること。メッセージの I/O 実装は通常タイムアウトの要件がありますが、BAレスポンスには短かすぎます。レスポンスがタイムアウト後に受信されない場合、レスポンスを受け取るまで繰り返し再送信されます。受信側は、受信したものの中で同一のリクエスト以外はすべて破棄します。
BusinessAgreementWithParticipantCompletion
と BusinessAgreementWithCoordinatorCompletion
のパーティシパントプロトコルが2つ存在します。コーディネータからパーティシパントへ駆動するATプロトコルとは違い、このプロトコルは反対のアプローチを使います。
- BusinessAgreementWithParticipantCompletion
- パーティシパントは最初にActive の状態で作成されています。
- 作業が終了しBAの範囲内で必要なくなった (例:不変データ上でアクティビティが行われている場合など) 場合、パーティシパントはコーディネータに
exited
メッセージを送信することで一方的に終了の決定を行うことができます。しかし、パーティシパントが終了しBAで継続したい場合は、実行してきた作業を補正できなければなりません。この場合、コーディネータにcompleted
メッセージを送信しコーディネータがBAの最終結果を通知するまで待機します。この結果はBAが問題なく完了したことを意味するclose
メッセージ、あるいはパーティシパントは作業をもとに戻す必要がある旨を示すcompensate
メッセージのいずれかになります。
- BusinessAgreementWithCoordinatorCompletion
BusinessAgreementWithCoordinatorCompletion
は、パーティシパントは補正可能な場合でさえ、自立的にBAへの参加を完了するよう決定できるところが、BusinessAgreementWithParticipantCompletion
とは違います。- その代わりに、BAを作成したクライアントが完了ステージを駆動し、
completed
メッセージをコーディネータに送信します。 - コーディネータは、
complete
メッセージを各パーティシパントに送信し、パーティシパントに関連するサービスへこれ以上リクエストを送信しないよう指示します。 - パーティシパントは、BusinessAgreementWithParticipantCompletion プロトコルと同じ形式で継続します。
BusinessAgreementWithParticipantCompletion
パーティシパントの状態移行やコーディネータとパーティシパント間のメッセージ交換について説明しています。コーディネータが生成したメッセージは実線で、パーティシパントのメッセージは点線で示されています。
図18.8
BusinessAgreementWithCoordinatorCompletion
パーティシパントの状態移行やコーディネータとパーティシパント間のメッセージ交換について説明しています。コーディネータが生成したメッセージは実線で、パーティシパントのメッセージは点線で示されています。
図18.9
18.2.4. アプリケーションメッセージ
18.2.4.1. WS-C、WS-Atomic Transaction、WS-Business Activity メッセージ
begin
や prepare
など、標準トランザクション操作の実行指示が含まれています。
注記
18.3. 概要
第19章 はじめに
19.1. XTS Service Archive をJBoss Transaction Service にインストール
$JBOSS_HOME/docs/examples/transactions/
にあるService Archive (.sar) としてパッケージされています。インストールするには、手順19.1「XTS モジュールのインストール」に従ってください。
手順19.1 XTS モジュールのインストール
$JBOSS_HOME/server/[name]/deploy/
ディレクトリにjbossxts.sar/
と呼ばれるサブディレクトリを作成します。- ZIPアーカイブのSARを新規ディレクトリに展開します。
- JBoss Enterprise Application Platformを再起動しモジュールを有効にします。
19.2. クライアントアプリケーションの作成
begin
、commit
、 rollback
などの単純なトランザクション命令を提供するため、クライアントアプリケーションはトランザクションを初期化、管理、終了する際に利用できます。内部では、コーディネータを作成しトランザクションを完了させるために、このAPIがSOAPを使って様々なWS-C、WS-AT、WS-BAのサービス上で操作を呼び出します。
19.2.1. ユーザトランザクション
UserTransactionFactory
と UserTransaction
クラスを使い、WS-ATトランザクションを作成、管理します。これらのクラスは、JTA API と似た方法で機能するシンプルなAPI を提供します。UserTransaction
クラスのbegin
メソッドを呼び出すことでWS-ATトランザクションを開始し、クライアントスレッドと関連付けます。commit
メソッドを呼び出すことでトランザクションをコミットでき、rollback
メソッドでロールバックが可能です。
TransactionManagerFactory
や TransactionManager
クラスによりサポートされています。
19.2.2. Business Activities
UserBusinessActivityFactory
と UserBusinessActivity
クラスを使いビジネスアクティビティを作成、管理します。UserBusinessActivity
クラスのbegin
メソッドを呼び出すことで、WS-BA アクティビティを開始し、クライアントスレッドと関連付けます。クライアントは、close
メソッドを呼び出すとビジネスアクティビティを終了でき、cancel
メソッドでキャンセルが可能になります。
BusinessActivityWithCoordinatorCompletion
プロトコルに登録されると、このクライアントはclose
メソッドを呼び出す前にcompleted
メソッドを呼び出し、サービスに現在のアクティビティでサービス呼出しが終了した旨を通知することができます。
BusinessActivityManagerFactory
や BusinessActivityManager
クラスによりサポートされています。
19.2.3. クライアント側のハンドラ設定
- Web サービスを呼び出す際に現在のトランザクション詳細をサーバーに転送する必要があります。
- サーバーからのレスポンスを正しいトランザクションのコンテキストで処理する必要があります。
注記
19.2.3.1. JAX-WSクライアントコンテキストハンドラ
javax.xml.ws.BindingProvider
と javax.xml.ws.Binding
により提供されるAPIを使用して、リモートエンドポントを呼び出すためのサービスプロキシ上にハンドラチェーンをインストールします。例についてはsrc/com/jboss/jbosstm/xts/demo/BasicClient.java
ファイルにあるアプリケーションクライアント実装例を参照してください。
javax.jws.HandlerChain
アノテーションを添付し、JAX-WSクライアントAPIを宣言します。このインターフェースは通常、Web サービスのWSDLポート定義から生成されます。
com.arjuna.mw.wst11.client.JaxWSHeaderContextProcessor
をインスタンス化する必要があります。
19.3. トランザクショナルなWeb Serviceを作成
19.3.1. パーティシパント
com.arjuna.wst11.Durable2PCParticipant
、com.arjuna.wst11.Volatile2PCParticipant
、com.arjuna.wst11.BusinessAgreementWithParticipantCompletionParticipant
, あるいはcom.arjuna.wst11.BusinessAgreementWithCoordinatorCompletionParticipant
インターフェースのいずれかを利用する必要があります。
19.3.2. サービス側のハンドラ設定
注記
注記
19.3.2.1. JAX-WSサービスコンテキストハンドラ
javax.jws.WebService
でアノテーションが付けられたエンドポイント実装のクラスアノテーションはjavax.jws.HandlerChain
アノテーションで補完する必要があります。このjavax.jws.HandlerChain
アノテーションでアプリケーションがデプロイしたハンドラ設定ファイルを特定します。例についてはdd/jboss/context-handlers.xml
にあるアプリケーション設定ファイル例と、src/com/jboss/jbosstm/xts/demo/services
にあるエンドポイント実装クラスを参照してください。
com.arjuna.mw.wst11.service.JaxWSHeaderContextProcessor
クラスをインスタンス化する必要があります。コーディネータ介入が必要な場合、代わりにcom.arjuna.mw.wst11.service.JaxWSSubordinateHeaderContextProcessor
を使います。
図19.1 SOAPサーバーに登録されているコンテキストハンドラ
19.4. 概要
第20章 パーティシパント
20.1. 概要
図20.1 トランザクション、パーティシパント、バックエンドトランザクションの制御
20.1.1. アトミックトランザクション
20.1.1.1. Durable2PCParticipant
com.arjuna.wst11.Durable2Participant
に従い、Durable2PCParticipant 署名に掲載の署名でWS-ATomic Transaction Durable2PC プロトコルに対応しています。
Durable2PCParticipant 署名
prepare
- パーティシパントは、トランザクションの範囲内でWeb サービスが実行する作業をコミットあるいはロールバックできるように必要な作業を実行する必要があります。この実装は、パーティシパントとコーディネータ間の暗黙的なコントラクトを達成するために、必要な事項を自由に行うことができます。パーティシパントは、以下の3つの値の内いずれかを使い、
com.arjuna.wst11.Vote
のインスタンスを返すことでprepare
可能かを示します。ReadOnly
は、ステート情報を更新しなかったため、パーティシパントがトランザクションの結果について知る必要がないという意味です。Prepared
は、最終的なトランザクションの結果によりパーティシパントがコミットあるいはロールバックの準備ができていることを示しています。これを達成するために、十分な数のステートアップデートを永続化しています。Aborted
は、パーティシパントが中断され、トランザクションも中断を試行しているという意味です。
commit
- パーティシパントは、作業を永続化する必要があります。これを達成する方法は各実装により変わってきます。たとえば、劇場の例ではチケットの予約がコミットされます。コミット処理が完了しないと、パーティシパントは
SystemException
エラーをスローしトランザクションがヒューリスティックな結果に陥ってしまう可能性もあります。 rollback
- パーティシパントは作業をやり直す必要があります。ロールバック処理が完了できない場合はパーティシパントが
SystemException
エラーをスローしトランザクションがヒューリスティックな結果に陥ってしまう可能性もあります。 unknown
- このメソッドは廃止予定となっており今後XTS から削除される予定です。
error
- システムクラッシュから回復時まれに、以前に準備されたパーティシパントを完了あるいはロールバックできない場合があり、
error
操作を呼び出します。
20.1.1.2. Volatile2PCParticipant
com.arjuna.wst11.Volatile2Participant
インターフェースに従い、Volatile2PCParticipant 署名に記載の署名でWS-ATomic Transaction Volatile2PC プロトコルに対応しています。
Volatile2PCParticipant 署名
prepare
- パーティシパントは、トランザクションの範囲内でWeb サービス作成の揮発性データをシステムストアへフラッシュするのに必要な作業を実行する必要があります。この実装は、パーティシパントとコーディネータ間の暗黙的なコントラクトを達成するために、必要な事項を自由に行うことができます。パーティシパントは、以下の3つの値の内いずれかを使い、
com.arjuna.wst11.Vote
のインスタンスを返すことでprepare
可能かを示します。ReadOnly
は、トランザクションの有効期間内でステートの情報に変更を加えられなかったため、パーティシパントはトランザクションの結果について知る必要がないと言う意味です。Prepared
は、パーティシパントは最終的なトランザクションの結果をcommit
かrollback
の呼出しで通知して欲しいという意味です。Aborted
は、パーティシパントが中断され、トランザクションも中断を試行しているという意味です。
- commit
- パーティシパントは、トランザクションのコミットに成功すると必要なクリーンアップアクティビティを実行する必要があります。これらのクリーンアップアクティビティは実装により左右されます。例えば、トランザクション中に変更されたデータのキャッシュバックアップはフラッシュされます。まれにコミット処理が完了できない時がありますがこういう場合は、パーティシパントは
SystemException
エラーをスローするはずです。これは、トランザクションの結果に影響を与えませんがエラーがログに残されることになります。コミット処理中にクラッシュが発生した場合、このメソッドは呼び出されない可能性があります。 - rollback
- パーティシパントは、トランザクションが中断されると、必要となるクリーンアップアクティビティを実行する必要があります。ロールバック処理が完了できないことがまれにありますが、こういう場合は、パーティシパントは
SystemException
エラーをスローするはずです。これはトランザクション結果に影響を与えませんがエラーがログに残されます。コミット処理中にクラッシュが発生した場合、このメソッドは呼び出されません。 - unknown
- このメソッドは廃止予定でXTSの今後のリリースからは削除されます。
- error
- 揮発性のパーティシパントはリカバリ処理に関わらないため、このメソッドが呼ばれることはありません。
20.1.2. ビジネスアクティビティ
20.1.2.1. BusinessAgreementWithParticipantCompletion
BusinessAgreementWithParticipantCompletion
インターフェースは、com.arjuna.wst11.BusinessAgreementWithParticipantCompletionParticipant
インターフェースに従い、BusinessAgreementWithParticipantCompletion
署名記載の署名でWS-Transactions BusinessAgreementWithParticipantCompletion
プロトコルに対応しています。
BusinessAgreementWithParticipantCompletion
署名
close
- トランザクションが問題なく完了しました。パーティシパントはすでに完了準備ができているとコーディネータに通知しています。
cancel
- トランザクションは取り消され、パーティパントは作業をもとに戻す必要があります。パーティシパントはコーディネータに完了済みであると通知できていません。
compensate
- トランザクションがキャンセルされました。パーティシパントはすでに作業は完了済みで必要であれば補正可能である旨をコーディネータに通知しており、この時点で依頼を投げます。補正が実行できない場合、パーティシパントは
FaultedException
エラーをスローし、当トランザクションに対しヒューリスティックな結果に陥る可能性があります。一時的な状況により補正処理が完了できない場合は、パーティシパントはSystemException
エラーをスローしますが、補正アクションはキャンセルされ、トランザクションがヒューリスティックな結果で終了する可能性があります。 status
- パーティシパントの状態を返します。
unknown
- このメソッドは廃止予定で今後のXTSリリースでは削除される予定です。
- error
- システムクラッシュから回復する際にまれに、以前に完了したパーティシパントを補正できない場合があります。このような場合は
error
操作が呼び出されます。
20.1.2.2. BusinessAgreementWithCoordinatorCompletion
com.arjuna.wst11.BusinessAgreementWithCoordinatorCompletionParticipant
インターフェースに従い、BusinessAgreementWithCoordinatorCompletion 署名に記載の署名でWS-Transactions BusinessAgreementWithCoordinatorCompletion
プロトコルに対応しています。
BusinessAgreementWithCoordinatorCompletion 署名
close
- トランザクションは問題なく完了しました。パーティシパントはすでに完了準備ができている旨をコーディネータに通知しています。
cancel
- トランザクションがキャンセルされ、パーティシパントは作業のやり直しを行う必要があります。
compensate
- トランザクションがキャンセルされました。パーティシパントは作業が完了しているが必要であれば補正を行える旨をコーディネータに通知済みで、補正を行うようリクエストをしています。まれに、補正が実行できない場合は、パーティシパントは
FaultedException
エラーをスローし、トランザクションがヒューリスティックな結果に陥る可能性があります。一時的な状況で補正処理が完了しない場合、パーティシパントはSystemException
エラーをスローする必要がありますが、この場合補正アクションがキャンセルされ、トランザクションがヒューリスティックな結果で終了してしまう可能性があります。 complete
- コーディネータはパーティシパントに対し、ビジネスアクティビティの範囲内で行う必要のある全作業が完了し、一時的な変更を永続化すべきであると通知しています。
status
- パーティシパントの状況を返します。
unknown
- このメソッドは廃止予定でXTSの今後のリリースからは削除されます。
error
- システムクラッシュから回復時まれに、以前に完了したパーティシパントの補正ができない場合があります。このような場合は
error
メソッドが呼び出されます。
20.1.2.3. BAParticipantManager
BAParticipantManager メソッド
exit
- パーティシパントは
exit
メソッドを使い、アクティビティから離れていることをコーディネータに通知します。ビジネスアクティビティの終了時、終了方法については通知されます。このメソッドは、パーティシパントがactive
な状態 (あるいはParticipantCompletion
プロトコルに登録されているパーティシパントの場合はcompleting
のステート)である間のみ、呼び出されます。パーティシパントがこれ以外の状態のときに呼び出されると、WrongStateException
エラーがスローされます。exit
はアクティビティ全体を終了あるいはキャンセル/補正できないようにするのではなく、離れたパーティシパントがアクティビティの完了、終了、補正に関わらないようにするだけです。 completed
- パーティシパントの作業は完了しているが、アクティビティ終了時、終了方法が最終的に通知されるようにビジネスアクティビティを継続したい場合などです。パーティシパントは、完了した作業の補正を後に依頼される場合や、アクティビティの終了を知る場合があります。
fault
- 通常のアクティベーション時にパーティシパントにエラーが発生し、アクティビティの補正を試行しました。
fault
メソッドは、ビジネスアクティビティを強制的にcancel-only
モードにします。障害のあるパーティシパントはアクティビティの完了、終了あるいは補正には関わらないようになります。
20.2. パーティシパントの作成とデプロイメント
20.2.1. パーティシパントの実装
注記
com.arjuna.wst
パッケージに置かれています。
20.2.2. パーティシパントのデプロイ
警告
.jar
、.war
、設定ファイルを使って、XTS アプリケーションはデプロイされていました。このデプロイメントの方法は、Enterprise Application Platform ではサポートされていません。
第21章 XTS API
21.1. アトミックトランザクションプロトコルの API
21.1.1. 投票
com.arjuna.wst11.Vote
のサブクラス に記載されている com.arjuna.wst11.Vote
のサブタイプのひとつのインスタンスを返す必要があります。
com.arjuna.wst11.Vote
のサブクラス
- Prepared
- コーディネータが要求するとパーティシパントが準備できることを示します。パーティシパントはトランザクションの最終結果を知らないため何もコミットされません。
- Aborted
- パーティシパントは準備できず、ロールバックしました。パーティシパントは 2 つ目のフェーズメッセージの取得を期待すべきではありません。
- ReadOnly
- パーティシパントは状態に変更を加えておらず、トランザクションの最終結果を知る必要もありません。本質的にはパーティシパントはトランザクションから辞退しています。
例21.1 2PC パーティシパントの prepare
メソッドのサンプル実装
public Vote prepare () throws WrongStateException, SystemException { // Some participant logic here if(/* some condition based on the outcome of the business logic */) { // Vote to confirm return new com.arjuna.wst.Prepared(); } else if(/*another condition based on the outcome of the business logic*/) { // Resign return new com.arjuna.wst.ReadOnly(); } else { // Vote to cancel return new com.arjuna.wst.Aborted(); } }
21.1.2. TXContext
TxContext の戻り値
- valid
- コンテンツが有効かどうか示します。
- equals
- 等値に対して 2 つのインスタンスを比較するために使用できます。
注記
21.1.3. UserTransaction
com.arjuna.mw.wst11.UserTransaction
は、クライアントが通常使用するクラスです。クライアントが新しいアトミックトランザクションを開始するためには、まず UserTransactionFactory
から UserTransaction
を取得する必要があります。このクラスはユーザーを XTS 実装の基礎となるプロトコル固有のアスペクトから分離します。UserTransaction
は特定のトランザクションを表していません。代わりに、JTA 仕様の UserTransaction
に似た、スレッドごとの暗黙的なトランザクションコンテキストへのアクセスを提供しています。すべての UserTransaction
メソッドは暗黙的に現在のスレッドで実行します。
userTransaction
メソッド
- begin
- 新しいトランザクションを開始し、それを呼び出しスレッドと関連付けるために使用します。
パラメータ
- timeout
- このオプションのパラメータはミリ単位で時間間隔を指定し、その後新しく作成されたトランザクションは自動的にコーディネータによりロールバックされます。
例外
WrongStateException
- トランザクションはすでにスレッドと関連付けられています。
- commit
- トランザクションに登録された Volatile2PC および Durable2PC パーティシパントは、まず準備を行い次に変更をコミットするよう要求されます。パーティシパントのいずれかが最初のフェーズで準備に失敗すると、他のすべてのパーティシパントは中断するよう要求されます。
例外
UnknownTransactionException
- 呼び出しスレッドに関連付けられているトランザクションはありません。
TransactionRolledBackException
- タイムアウトまたはパーティシパントがコミットできなかったかどちらかの理由により、トランザクションはロールバックされました。
- rollback
- トランザクションを終了します。完了時に
rollback
メソッドは、現在のスレッドからトランザクションの関連付けを解除し、すべてのトランザクションと現在関連付けないままにします。例外
UnknownTransactionException
- 呼び出しスレッドに関連付けられているトランザクションはありません。
21.1.4. UserTransactionFactory
21.1.5. TransactionManager
TransactionManager
は固有のトランザクションを表していません。代わりに、スレッドごとの暗黙的なトランザクションコンテキストへのアクセスを提供します。
メソッド
currentTransaction
- 現在のトランザクションに対する
TxContext
を返します。コンテキストがない場合は null となります。currentTransaction
メソッドを使用して、Web サービスが既存のトランザクション内から呼び出されたか決定します。戻り値を使用して、複数のスレッドを有効にし、同じトランザクションのスコープ内で実行することも可能です。currentTransaction
メソッドを呼び出すことで、トランザクションから現在のスレッドの関連付けの解除はしません。 suspend
- すべてのトランザクションからスレッドの関連付けを解除します。これにより固有のトランザクションに関連付けられていない作業をスレッドが行うことができます。
suspend
メソッドはTxContext
インスタンスを返します。これはトランザクションのハンドルです。 resume
TxContext
を使用して、スレッドのトランザクションとの関連付けまたは再度の関連付けを行います。関連付けまたは再度の関連付けの前に、スレッドは現在関連付けられているすべてのトランザクションからの関連付けを解除されます。TxContext
が null の場合、スレッドはトランザクションと関連付けられません。この方法では、結果はまるでsuspend
メソッドが代わりに使用されたかのような結果と同じになります。パラメータ
- txContext
suspend
からの返しとしての TxContext インスタンスで、再開されるトランザクションを指定します。
例外
UnknownTransactionException
TxContext
により参照されたトランザクションは、呼び出しスレッドのスコープでは無効です。
enlistForVolitaleTwoPhase
- 現在のトランザクションで特定のパーティシパントを登録します。これにより Volatile2PC プロトコルに参加することになります。パーティシパントに対し一意識別子を渡すことが必要です。
パラメータ
- participant
- インターフェース Volatile2PCParticipant の実装です。対応するコーディネータメッセージを受信すると、その prepare、commit、abort メソッドが呼び出されます。
- id
- パーティシパントの一意識別子です。この文字列値は登録されている各パーティシパントにより異なるはずです。また、所定の識別子は、同じコンテナにデプロイされた他の Web サービスではなく、登録している Web サービスにパーティシパントが属していると決定できるはずです。
例外
- UnknownTransactionException
- 呼び出しスレッドに関連付けられているトランザクションはありません。
WrongStateException
- トランザクションはパーティシパントを登録できる状態にはありません。例えば、終了のプロセスにある可能性もあります。
enlistForDurableTwoPhase
- 現在のトランザクションで特定のパーティシパントを登録します。これにより Durable2PC プロトコルに参加することになります。パーティシパントに対し一意識別子を渡すことが必要です。
例外
- UnknownTransactionException
- 呼び出しスレッドに関連付けられているトランザクションはありません。
WrongStateException
- トランザクションはパーティシパントを登録できる状態にはありません。例えば、終了のプロセスにある可能性もあります。
21.1.6. TransactionManagerFactory
21.2. ビジネスアクティビティプロトコルの API
21.2.1. 互換性
21.2.2. UserBusinessActivity
com.arjuna.wst11.UserBusinessActivity
は大部分のクライアントが使用するクラスです。クライアントが新しいビジネスアクティビティを開始するには、まず UserBusinessActivityFactory
から UserBusinessActivity
を取得します。このクラスは XTS 実装の基礎となるプロトコル固有のアスペクトからそれらを分離します。UserBusinessActivity は特定のビジネスアクティビティを表していません。代わりに、スレッドごとの暗黙的なアクティビティへのアクセスを提供しています。そのため、すべての UserBusinessActivity
メソッドは暗黙的に現在のスレッドで実行します。
メソッド
begin
- 新しいアクティビティを開始し、それを呼び出しスレッドに関連付けます。
パラメータ
- timeout
- アクティビティがタイムアウトする後のミリ単位の間隔です。これはオプションです。
例外
WrongStateException
- スレッドは既にビジネスアクティビティと関連付けられています。
close
- 最初に、アクティビティに登録されているすべての Coordinator Completion パーティシパントはアクティビティを完了するよう要求されます。次に、すべてのパーティシパントは、それらが Coordinator または Participant Completion に登録されていようと、アクティビティを閉じるように要求されます。すべての Coordinator Completion パーティシパントが最初の段階で完了に失敗すると、完了したすべてのパーティシパントはアクティビティを補正するよう指示されます。残っている未完了のすべてのパーティシパントはアクティビティをキャンセルするよう要求されます。
例外
- UnknownTransactionException
- 呼び出しスレッドに関連付けられているアクティビティはありません。
- TransactionRolledBackException
- Coordinator Completion パーティシパントのひとつが完了に失敗したため、アクティビティはキャンセルされました。クライアント呼び出しが閉じる前に Participant Completion パーティシパントのひとつが完了していない場合は、この例外も送出されることがあります。
- cancel
- ビジネスアクティビティを終了します。既に完了したアクティビティに登録されたすべての Participant Completion パーティシパントはアクティビティを補正するよう要求されます。すべての未完了の Participant Completion パーティシパントとすべての Coordinator Completion パーティシパントはアクティビティをキャンセルするよう要求されます。
例外
UnknownTransactionException
- 呼び出しスレッドに関連付けられているアクティビティはありません。以前に完了したすべてのパーティシパントは作業を補正するよう指示されます。
21.2.3. UserBusinessActivityFactory
getuserbusinessActivity
メソッドを使用し、userBusinessActivityFactory
から 「UserBusinessActivity」 インスタンスを取得します。
21.2.4. BusinessActivityManager
BusinessActivityManager
は特定のアクティビティを表していません。代わりに、スレッドごとの暗黙的なアクティビティへのアクセスを提供しています。
メソッド
currentTransaction
- 現在のビジネスアクティビティに対して
TxContext
を返します。TxContext
がない場合は、NULL
を返します。戻り値を使用して、複数のスレッドが同じビジネスアクティビティのスコープ内で実行可能なようにできます。currenTransaction
メソッドを呼び出すことで、そのアクティビティから現在のスレッドの関連付けの解除はしません。 suspend
- すべての現在のビジネスアクティビティからスレッドの関連付けを解除します。これにより特定のアクティビティと関連付けられていない作業を実行できます。
suspend
メソッドはTxContext
インスタンスを返します。これはトランザクションのハンドルです。これでスレッドはどのアクティビティとも関連付けられていません。 resume
TxContext
を使用して、ビジネスアクティビティとのスレッドの関連付けまたは再度の関連付けを行います。スレッドの関連付けまたは再度の関連付けの前に、現在関連付けられているすべてのビジネスアクティビティからの関連付けが解除されます。TxContext
がNULL
の場合、suspend
メソッドが呼び出されたかのように、スレッドはすべてのビジネスアクティビティとの関連付けが解除されます。パラメータ
- txContext
suspend
により返される TxContext インスタンスであり、再開されるトランザクションを特定します。
例外
UnknownTransactionException
TxContext
が参照するビジネスアクティビティは呼び出しスレッドのスコープでは無効です。
enlistForBusinessAgreementWithParticipantCompletion
- 現在のビジネスアクティビティで特定のパーティシパントを登録します。これにより
BusinessAgreementWithParticipantCompletion
プロトコルに参加することになります。パーティシパントの一意識別子も必要です。戻り値は BAParticipantManager のインスタンスであり、これを使用してパーティシパントの状態における変更をコーディネータに伝えます。特にパーティシパントは Participant Completion プロトコルに登録されているため、このアクティビティで行うことを期待するすべての作業を完了し、かつすべての変更を永久的にすると、この返されたインスタンスの completed メソッドを呼び出すことが期待されます。代わりに、パーティシパントが補正アクションを実行する必要がなく、例えば他のパーティシパントが失敗すると、返された BAParticipantManager インスタンスの exit メソッドを呼び出すことでアクティビティを終了できます。パラメータ
- participant
- インターフェース
BusinessAgreementWithParticipantCompletionParticipant
の実装です。対応するコーディネータメッセージを受信すると、そのclose
、cancel
、compensate
メソッドが呼び出されます。 - id
- パーティシパントの一意識別子です。この文字列値は登録されている各パーティシパントにより異なるはずです。また、所定の識別子は、同じコンテナにデプロイされた他の Web サービスではなく、登録している Web サービスにパーティシパントが属していると決定できるはずです。
例外
- UnknownTransactionException
- 呼び出しスレッドに関連付けられているトランザクションはありません。
- WrongStateException
- トランザクションは新しいパーティシパントが登録できる状態にありません。例えば、終了しているときなどです。
enlistForBusinessAgreementWithCoordinatorCompletion
- 現在のアクティビティで特定のパーティシパントを登録します。これにより
BusinessAgreementWithCoordinatorCompletion
プロトコルに参加することになります。パーティシパントの一意識別子も必要です。戻り値はBAParticipantManager
のインスタンスであり、これを使用してパーティシパントの状態における変更をコーディネータに伝えます。この場合、この返されたインスタンスのcompleted
メソッドの呼び出しはエラーです。Coordinator Completion プロトコルにより、パーティシパントはそれがすべての変更を永久的にする前にcompleted
メソッドが呼び出されるまで待つよう要求されます。別の方法として、加える変更がないとパーティシパントが決定すると、返されたBAParticipantManager
インスタンスのexit
メソッドを呼び出すことでアクティビティを終了できます。パラメータ
- participant
- インターフェース BusinessAgreementWithCoordinatorCompletionParticipant の実装です。対応するコーディネータメッセージを受信すると、その completed、close、cancel、compensate メソッドが呼び出されます。
- id
- パーティシパントの一意識別子です。この文字列値は登録されている各パーティシパントにより異なるはずです。また、所定の識別子は、同じコンテナにデプロイされた他の Web サービスではなく、登録している Web サービスにパーティシパントが属していると決定できるはずです。
例外
- >UnknownTransactionException
- 呼び出しスレッドに関連付けられているトランザクションはありません。
- WrongStateException
- トランザクションは新しいパーティシパントが登録できる状態にありません。例えば、終了しているときなどです。
21.2.5. BusinessActivityManagerFactory
getBusinessActivityManager
メソッドを使用し、BusinessActivityManagerFactory
から 「BusinessActivityManager」 インスタンスを取得します。
第22章 スタンドアローンのコーディネーション
22.1. はじめに
スタンドアローンのコーディネーションを選択する理由
- 効率性:クライアントアプリケーションがWeb Service をリモートのEnterprise Application Platform サーバー上で呼出した場合、コーディネータとパーティシパント間のプロトコル固有のメッセージがネットワーク上を移動する必要がないため、リモートサーバーからのトランザクションをコーディネートすることで効率性があがる可能性があります。
- 信頼性:コーディネータサービスが専用ホスト上で作動している場合、アプリケーションやサービスに障害が起きることでコーディネータへ影響を与え関係のないトランザクションで障害が起こる危険性がありません。
- 3つ目の理由は、他社ベンダー提供のコーディネーションサービスを利用できるためです。
22.2. Activation Coordinatorの設定
-D
オプションでシステムプロパティの設定を指定します。「-D
パラメータでコマンドラインオプションを渡し優先順位に並び替え」で行っているように設定オプションのいくつかを有効にすることができます。
22.2.1. -D
パラメータでコマンドラインオプションを渡し優先順位に並び替え
絶対URL
- プロパティ:
org.jboss.jbossts.xts11.coordinatorURL
- これらのURLに割り当てられた値は、リモートコーディネータホストの設定により変化します。コーディネータが別のJBoss Transaction Service XTS サービスであれば、プロパティ名にリストされたサンプル値は適切です。coord.host と coord.port の代わりにActivation Coordinator サービスを稼働するEnterprise Application Platform インスタンスに対する適切な値で置き換えてください。
コーディネータホストの情報
プロパティ
- org.jboss.jbossts.xts11.coordinator.host
- org.jboss.jbossts.xts11.coordinator.port
- org.jboss.jbossts.xts11.coordinator.path
形式
- server.bind.address
- jboss.web.bind.port
- ws-c11/ActivationService
- これら3つのコンポーネントのいずれかを設定した場合、定義したコンポーネント値を使い未定義のコンポーネントに指定されたデフォルト値を置き換えることで、コーディネータURLを構築します。server.bind.address と jboss.web.bind.port の値は、アプリケーションサーバーのコマンドラインあるいはサーバー設定ファイルのいずれかから取得したサーバーバインドアドレスとWeb サービスリスナポートを表します。
第23章 パーティシパンツのクラッシュリカバリ
手順23.1 推定アボートポリシー
- コーディネータがクラッシュすると、把握していないトランザクションが無効であると推測し、このようなトランザクションを参照するパーティシパントのリクエストを拒否します。
- パーティシパントがクラッシュした場合、暫定的な変更は無視され、コーディネータサービスからのトランザクション準備あるいはビジネスアクティビティ完了リクエストを拒否します。
- WS-AT トランザクション
- トランザクションは、暫定的な変更をすべてコミットするか、トランザクション開始前のステートまですべてをロールバックする必要があります。
- WS-Business Activityトランザクション
- パーティシパントはすべてアクティビティを終了するか、取り消す必要があり、必要な補正アクションを実行します。
23.1. WS-AT リカバリ
23.1.1. WS-ATコーディネータのクラッシュリカバリ
prepare
メッセージが送られ、パーティシパントすべてがprepared
メッセージを返すと、コーディネータは各パーティシパントの詳細を格納するログ記録を書き込み、トランザクションが完了可能であると示します。この地点以降でコーディネータサービスがクラッシュした場合は、再起動後にログファイルを読み取り、commit
メッセージを各パーティシパントに送信することで、2相コミットプロトコルの完了は保証されています。また、全パーティシパントがcommit
に対してcommitted
メッセージを返答すると、コーディネータはログエントリを何の支障もなく削除することができます。
prepared
メッセージが暫定的な変更をコミットし永続かする準備ができていると示しているため、このタイプのリカバリは安全です。さらに、コーディネータは、クラッシュ前に送信されたコミットメッセージの対応をし複数回クラッシュした場合にメッセージを再送信する必要がありません。XTS のパーティシパント実装はcommit
メッセージの再配信にも強くなっています。パーティシパントが「WS-AT パーティシパントのクラッシュリカバリAPI」にあるようなリカバリ機能を実装している場合、同時に両方がクラッシュし、さらにパーティシパントサービスのホストの1つ以上がクラッシュしても、コーディネータはcommit
メッセージの配信を保証することができます。
prepare
フェーズ完了前にクラッシュした場合、推定アボートプロトコルにより、確実にパーティシパントがロールバックされます。システムの再起動後、コーディネーションサービスはログ内のエントリがあるため、再起動前にcommit
フェーズに入れた可能性のある全トランザクションの情報を持っていることになります。また、再起動後に開始されたアクティブなトランザクションについても把握しています。パーティシパントがprepared
メッセージ送信後にレスポンスを待っている場合、等間隔で自動的にprepared
メッセージが再送されます。コーディネータがアクティブではないトランザクションを検出し、再起動後にログファイルのエントリがない場合、パーティシパントにアボートするよう指示をだし、Web サービスがトランザクションの代わりに加えられた暫定的なステートの変更をロールバックできるようにします。
23.1.2. WS-AT パーティシパントのクラッシュリカバリ
prepare
するように指示を受けると、Web サービスは永続ストレージにトランザクションのコミットあるいはロールバックが必要なトランザクションのステートを保存するはずです。保存する必要のある特定情報は、Web サービスのビジネスロジックや実装に依存します。しかし、prepare
の呼出しからPrepared
と返答する前に、パーティシパントはこの状態を保存する必要があります。パーティシパントが必要とされる状態を保存できない場合、あるいはクライアントからのリクエストに対応できない場合、Aborted
と返答する必要があります。
prepare
、commit
、rollback
メソッドの呼出しを行います。XTS 実装は、登録したパーティシパントすべてのローカルのステートをトラッキングします。prepare
の呼出しがPrepared
を返す場合、XTS 実装があると、コーディネータにprepared
メッセージが転送される前にパーティシパントの状態が確実にローカルのトランザクションログに記録されるようにします。
prepare
に失敗した場合、commit
プロセスを完了するまで継続するか、ロールバックすることができます。この情報は、パーティシパントが(Prepared の答えを返す前に永続化された)データの場所を突き止めることのできるString Keyのように単純な場合もあります。しかし、元のパーティシパントインスタンスやWeb サービスが作成した他のオブジェクトを含む直列化されたオブジェクトツリーのように複雑な場合もあります。
commit
あるいは rollback
メソッドが呼び出されてからでなければ、削除されません。
警告
commit
メソッド呼出し直後/直前に発生した場合、commit
あるいは rollback
メソッドは2度呼び出される場合があります。
23.1.2.1. WS-AT パーティシパントのクラッシュリカバリAPI
23.1.2.1.1. パーティシパントのリカバリステートを保存
java.lang.Serializable
インターフェースを実装可能です。あるいは、例23.1「PersistableATParticipant
インターフェース」を実装することもできます。
例23.1 PersistableATParticipant
インターフェース
public interface PersistableATParticipant { byte[] getRecoveryState() throws Exception; }
Serializable
インターフェースを実装している場合、XTSパーティシパントサービスの実装はシリアル化APIを使い、パーティシパントのログエントリに追記できるパーティシパントのバージョンを作成します。また、PersistableATParticipant
インターフェースを実装している場合、XTS パーティシパントサービスの実装はgetRecoveryState
メソッドを呼出してステートを取得し、パーティシパントのログエントリに追記します。
23.1.2.1.2. 再起動時のパーティシパントのリカバリ
XTSATRecoveryManager
クラスを利用し登録は行われます。
例23.2 リカバリ用に登録
public abstract class XTSATRecoveryManager { . . . public static XTSATRecoveryManager getRecoveryManager() ; public void registerRecoveryModule(XTSATRecoveryModule module); public abstract void unregisterRecoveryModule(XTSATRecoveryModule module) throws NoSuchElementException; . . . }
XTSATRecoveryModule
の実装をregister
と unregister
呼出し両方への引数として提供する必要があります。このインスタンスは保存したパーティシパントリカバリ記録を特定し新規のリカバリ済みパーティシパントインスタンスを再作成します。
例23.3 XTSATRecoveryModule
実装
public interface XTSATRecoveryModule { public Durable2PCParticipant deserialize(String id, ObjectInputStream stream) throws Exception; public Durable2PCParticipant recreate(String id, byte[] recoveryState) throws Exception; }
deserialize
メソッドがパーティシパントを再作成するために呼び出されます。通常、提供された入力ストリームからオブジェクトを読み取りキャストし返すにはリカバリモジュールが必要になります。PersistableATParticipant
インターフェースを使ってパーティシパントのリカバリ状態を保存した場合、リカバリモジュールのrecreate
メソッドを呼び出し、状態の保存時に提供されたバイトアレイからパーティシパントを再作成します。
null
を返すはずです。deserialize
あるいは recreate
に対する引数として提供されたパーティシパントの識別子は、元のパーティシパントがトランザクションに登録された際にWeb サービスが利用した識別子です。リカバリ処理に参加しているWeb Servcies は、パーティシパントの識別子がサービス毎に一意であるよう確認するはずです。モジュールがパーティシパント識別子がWeb サービスに所属していると認識しているがパーティシパントの再作成ができない場合、例外をスローするでしょう。サービスがパーティシパントとビジネスロジック固有のトランザクション情報を関連付けることができない場合にこの状況が発生する可能性があります。
deserialization
の操作は、Web サービス特有のクラスをローディング可能なクラスローダを使用する必要があります。XTS は、deserialize
操作の責任をリカバリモジュールに委譲することで、この要件を満たします。
23.2. WS-BA リカバリ
23.2.1. WS-BA コーディネータのクラッシュリカバリ
CoordinatorCompletion
のパーティシパントすべてがcomplete
メッセージを受信しcompleted
メッセージで返答すると、移行ポイントがアクティビティの終了中に発生します。この時点で、ParticipantCompletion
パーティシパントはすべてcompleted
メッセージを送信しているはずです。コーディネータは各パーティシパントの詳細を格納しトランザクション終了の準備ができていることを示すログを書き込みます。コーディネータサービスがログ記録の書き込み後にクラッシュした場合、close
操作は問題なく行えるよう保証されています。コーディネータはシステムが再起動しclose
メッセージを全パーティシパントに送信後ログをチェックします。全パーティシパントがclosed
メッセージでclose
に返答した後、コーディネータはログエントリを無事に削除することができます。
close
メッセージに対応する必要も、メッセージを再送信する必要もありません。XTS パーティシパントの実装はclose
メッセージの再送にも対応しています。以下に説明されているようにパーティシパントがリカバリ機能を実装しているとの前提で、これとパーティシパントのサービスホスト1つ以上が同時にクラッシュした場合でもコーディネータはclose
メッセージを確実に送信することができます。
compensate
メッセージが送信されるようにします。
cancel
やcompensate
リクエストに返答しない場合、コーディネータは警告ログを残しそのまま継続します。推定アボートプロトコルとパーティシパント主導のリカバリを組み合わせることで、パーティシパントホストがクラッシュした場合でも、適宜全パーティシパントが最終的にキャンセルあるいは補正されるようにします。
completed
レスポンスを数回再送した後にコーディネータからのレスポンスを検出しない場合、getstatus
メッセージの送信に切り替えコーディネータがまだ状態を把握しているかを判断します。ログ記録を書き込む前にクラッシュが発生した場合、コーディネータが再起動すると、コーディネータにはパーティシパントの記録がなく、getstatus
リクエストは問題があると返されます。パーティシパントのリカバリマネージャはこの状態で、このアクティビティがクライアントによりキャンセルされたかのようにパーティシパントを自動補正します。
getstatus
メッセージを各パーティシパントのコーディネータホストに送信し、アクティビティがまだ存在するか判断します。コーディネータがクラッシュしておらず、アクティビティが動作している場合、パーティシパントはcompleted
メッセージの再送に切り替え、close
あるいは compensate
のレスポンスを待ちます。コーディネータもクラッシュしているか、アクティビティがキャンセルされている場合、パーティシパントは自動でキャンセルされます。
23.2.2. WS-BAパーティシパントのクラッシュリカバリAPI
23.2.2.1. パーティシパントのリカバリステートを保存
java.lang.Serializable
インターフェースを実装することでリカバリ処理の実行ができるとのシグナルを送ることができます。あるいは例23.4「PersistableBAParticipant
インターフェース」を実装します。
例23.4 PersistableBAParticipant
インターフェース
public interface PersistableBAParticipant { byte[] getRecoveryState() throws Exception; }
Serializable
インターフェースを実装している場合、XTSパーティシパントサービス実装がシリアル化APIを使い、パーティシパントのログエントリに追記できるパーティシパントのバージョンを作成します。また、パーティシパントがPersistableBAParticipant
を実装している場合、XTSパーティシパントサービスの実装はgetRecoveryState
メソッドを呼出しステートを取得し、パーティシパントのログエントリへ追記します。
23.2.2.2. 再起動時のパーティシパントのリカバリ
XTSBARecoveryManager
を使い登録は実行されます。
例23.5 XTSBARecoveryManager
クラス
public abstract class XTSBARecoveryManager { . . . public static XTSBARecoveryManager getRecoveryManager() ; public void registerRecoveryModule(XTSBARecoveryModule module); public abstract void unregisterRecoveryModule(XTSBARecoveryModule module) throws NoSuchElementException; . . . }
XTSBARecoveryModule
実装をregister
と unregister
呼出しへの引数として提供する必要があります。このインスタンスは保存したパーティシパントのリカバリ記録を特定し、新規のリカバリ済みパーティシパントインスタンスを再作成します。
例23.6 XTSBARecoveryModule
インターフェース
public interface XTSATRecoveryModule { public BusinessAgreementWithParticipantCompletionParticipant deserializeParticipantCompletionParticipant(String id, ObjectInputStream stream) throws Exception; public BusinessAgreementWithParticipantCompletionParticipant recreateParticipantCompletionParticipant(String id, byte[] recoveryState) throws Exception; public BusinessAgreementWithCoordinatorCompletionParticipant deserializeCoordinatorCompletionParticipant(String id, ObjectInputStream stream) throws Exception; public BusinessAgreementWithCoordinatorCompletionParticipant recreateCoordinatorCompletionParticipant(String id, byte[] recoveryState) throws Exception; }
deserialize
メソッドの1つを呼出します。どのメソッドを使うかは、保存したパーティシパントがParticipantCompletion
プロトコルに実装されているか、CoordinatorCompletion
プロトコルに実装されているかで左右されます。通常、リカバリモジュールは提供した入力ストリームからオブジェクトを読み込み、キャストし返します。パーティシパントのリカバリ状態がPersistableBAParticipant
インターフェースを使って保存した場合、状態保存時に提供されたバイトアレイからパーティシパントを再作成できるように、リカバリモジュールのrecreate
メソッドの1つを呼び出します。利用メソッドは、保存パーティシパントがどのプロトコルを実装したかにより変わります。
null
を返すはずです。deserialize
あるいは recreate
呼出しへの引数として提供されたパーティシパントの識別子は、元のパーティシパントがトランザクションに登録された時にWeb サービスが使用した識別子です。そして、リカバリ処理に参加しているWeb Servcies は、パーティシパントの識別子がサービス毎に一意であるよう確認するはずです。モジュールがパーティシパント識別子がWeb サービスに所属していると認識しているがパーティシパントの再作成ができない場合、例外をスローします。サービスがパーティシパントとビジネスロジック固有のトランザクション情報を関連付けることができない場合にこの状況が発生する可能性があります。
deserialization
の操作は、Web サービス特有のクラスをローディング可能なクラスローダを使用する必要があります。XTS は、deserialize
操作の責任をリカバリモジュールに委譲することで、この要件を満たします。
付録C 改訂履歴
改訂履歴 | |||
---|---|---|---|
改訂 5.1.2-2.400 | 2013-10-31 | Rüdiger Landmann | |
| |||
改訂 5.1.2-2 | 2012-07-18 | Anthony Towns | |
| |||
改訂 5.1.2-100 | Thu 8 December 2011 | Russell Dickenson | |
|
索引
シンボル
- 1 相コミット
- 1PC, トランザクションの概要, XTSで利用されるプロトコルの概要
- 2 相コミット
- 2PC, トランザクションの概要, XTSで利用されるプロトコルの概要
- アクティブなコンポーネント, はじめに
- アクティベーション, XTSで利用されるプロトコルの概要
- アトミックなトランザクション
- atomicity (アトミック性), パーティシパント
- コマンドラインのオプション, スタンドアローンのコーディネーション
- コンテキストハンドラ, はじめに
- コーディネーションコンテキスト, XTSで利用されるプロトコルの概要
- コーディネータ, トランザクションの概要
- サービス側のハンドラ, はじめに
- サーブレット
- Java サーブレット, はじめに
- スタンドアローンのコーディネーション, スタンドアローンのコーディネーション
- デプロイメント, パーティシパント
- トランザクション, はじめに, トランザクションの概要
- トランザクションコンテキスト, トランザクションの概要
- トランザクションサービス, トランザクションの概要
- パーティシパント, トランザクションの概要, はじめに, パーティシパント
- トランザクションのパーティシパント, はじめに
- パーティシパントのリカバリ, パーティシパンツのクラッシュリカバリ
- ヒューリスティックな結果, トランザクションの概要
- ビジネスアクティビティ, はじめに
- ユーザートランザクション, はじめに
- リカバリ, パーティシパンツのクラッシュリカバリ
- 割り込み, トランザクションの概要
- 同期プロトコル, トランザクションの概要
- 同期プロトコルの最適化, トランザクションの概要
- 完了, XTSで利用されるプロトコルの概要
- 実装, パーティシパント
- 投票, XTS API
- 推定アボート (presumed abort) ポリシー, パーティシパンツのクラッシュリカバリ
- 望ましくない結果, はじめに
- 登録, XTSで利用されるプロトコルの概要
- 耐障害性, はじめに
- 非アトミック, トランザクションの概要
A
- ACID, トランザクションの概要
- Activation Coordinator, XTSで利用されるプロトコルの概要, スタンドアローンのコーディネーション
- API, XTS API
B
- BAParticipantManager, パーティシパント
- BusinessActivityManager, XTS API
- BusinessActivityManagerFactory, XTS API
- BusinessAgreementWithCoordinatorCompletion, XTSで利用されるプロトコルの概要, パーティシパント
- BusinessAgreementWithParticipantCompletion, XTSで利用されるプロトコルの概要, パーティシパント
C
- com.arjuna.mw.wst11
- XTS API, はじめに
D
- DE
- Document Exchange (ドキュメントエクスチェンジ), はじめに
- Durable2PC, XTSで利用されるプロトコルの概要
- Durable2PCParticipant, パーティシパント
J
- JAX-RPC, はじめに
R
- RPC
- Remote Procedure Calls (リモートプロシージャコール), はじめに
S
- SAR
- サービスアーカイブ, スタンドアローンのコーディネーション
- SOAP, はじめに
U
V
- Volatile2PC, XTSで利用されるプロトコルの概要
- Volatile2PCParticipant, パーティシパント
W
- Web Service, トランザクションの概要
- Web Services, はじめに
- WS-Atomic Transaction
- WS-AT, はじめに, XTSで利用されるプロトコルの概要
- WS-Business Activity
- WS-BA, はじめに, XTSで利用されるプロトコルの概要
- WS-Coordination
- WS-C, はじめに, XTSで利用されるプロトコルの概要
- WSDL
- Web Services Description Language (Web サービス記述言語), はじめに