Transactions 開発ガイド

JBoss Enterprise Application Platform 5

JTA、JTS、XTS APIを利用したアプリケーション開発

エディッション 5.1.2

Mark Little

Red Hat

Andrew Dinn

Red Hat

Kevin Connor

Red Hat

Jonathan Halliday

Red Hat

編集者

Misty Stanley-Jones

Red Hat

概要

本書の目的は、JTA、JTS、XTS APIのJBoss 実装を使ったトランザクショナルアプリケーション構築を行うにあたり、Java 開発者を支援する目的で作成されています。

パート I. JTA 開発

本項は、Java Transactions (JTA) API のJBoss 実装を使い、利用中のエンタープライズアプリケーションにトランザクショナルサポートを追加する方法を説明しています。

第1章 Java Transaction API (JTA) の紹介

トランザクションの規格は、アプリケーションプログラマが利用できるよう非常にローレベルのインターフェースを提供しています。Sun Microsystemsは、拡散トランザクショナルアプリケーションの開発支援にはハイレベルのインターフェースを指定しています。これらのインターフェースはそれでもローレベルであるため、プログラマがトランザクショナルアプリケーションのステート管理や同時処理を考慮していく必要があります。他のAPI でできるようなより一般的なリソースというよりは、XAリソースの統合機能を必要とするアプリケーションで最も役立ちます。
[JTA99]に関しては、拡散トランザクションサービスは通常、多数のパーティシパントを使います。
アプリケーションサーバー
EJBサーバーなどトランザクションのステート管理を含むアプリケーションランタイム環境のサポートが必要なインフラストラクチャを提供します。
Transaction Manager
トランザクションのデマケーション、トランザクショナルなリソース管理、同期化、トランザクションコンテキスト伝播の対応を必要とするサービスと管理機能を提供します。
リソースマネージャ
(リソースアダプタ[1])を使い)、アプリケーションがリソースにアクセスできるようにします。トランザクションリソースインターフェースを実装することで、リソースマネージャは、拡散トランザクションに参加します。トランザクションマネージャは、このインターフェースを使い、トランザクションの関係、トランザクションの完了、リカバリを伝達します。
通信資源管理 (CRM: Communication Resource Manager)
送受信リクエストを受けるためにトランザクションサービスへのアクセス、トランザクションサービスにアクセストランザクションコンテキストの伝播をサポートします。
トランザクションマネージャの観点からすると、実際のトランザクションサービス実装を公開する必要はありません。ハイレベルのインターフェースにより、トランザクションインターフェースのユーザがトランザクションのデマケーション、リソースの登録、同期化、リカバリプロセスを行えるようにします。このJTAはハイレベルのアプリケーションインターフェースで、トランザクショナルアプリケーションがトランザクション境界をデマーク化できるだけでなく、X/Open XAプロトコルのマッピングも含まれています。

注記

JBossJTA提供のJTAサポートは、JTA 1.0.1 仕様に準拠しています。


[1] リソースアダプタとは、アプリケーションサーバーあるいはクライアントがリソースマネージャに接続する際に利用されます。関係データベースに接続する際に利用される JDBC ドライバはリソースアダプタの一例です。

第2章 JBoss JTA 実装

Java Transaction API (JTA) には3つの要素が含まれています。
  • アプリケーショントランザクションのデマケーションインターフェース (ハイレベル)
  • アプリケーションサーバー向けのトランザクションマネージャインターフェース (ハイレベル)
  • トランザクショナルなリソースマネージャに向けたX/Open XA protocol の標準Java マッピング
JTA クラスおよびインターフェースはすべてjavax.transaction パッケージ内で宣言され、該当のJBossJTA 実装はcom.arjuna.ats.jta パッケージ内で定義されます。

重要

JBoss Transaction Serviceが作成したXid は、そのXid に埋め込まれた一意のノード識別子を必要とします。JBoss Transaction Service は、指定したノード識別子と一致するトランザクションおよびステートのみを回復します。このノード識別子は、com.arjuna.ats.arjuna.xa.nodeIdentifierプロパティを使い JBoss Transaction Service に提供する必要があります。この値は、お使いのJBoss Transaction Service インスタンス全体で固有のものであるように確認してください。値が提示されない場合、JBoss Transaction Service が値を生成し、ロギングインフラストラクチャを介してその値を報告します。このノード識別子は、英数字でなければなりません。

2.1. UserTransaction

UserTransaction インターフェースは、アプリケーションがトランザクション境界を制御できるようにします。また、このインターフェースはトップレベルのトランザクションを開始、コミット、ロールバックするためのメソッドを提供します。ネスト化されたトランザクションはサポートされておらず、呼出しているスレッドがすでにトランザクションと紐付いている場合begin メソッドはNotSupportedException をスローします。UserTransaction は自動的に新しく作成されたトランザクションと呼出しているスレッドを自動で関連付けます。

注記

JNDI からUserTransaction を取得できます。
	InitialContext ic = new InitialContext();
	UserTransaction utx = ic.lookup("java:comp/UserTransaction")
ローカルのJTA 実装を選択する方法
  1. com.arjuna.ats.jta.jtaTMImplementation プロパティをcom.arjuna.ats.internal.jta.transaction.arjunacore.TransactionManagerImpleに設定します。
  2. com.arjuna.ats.jta.jtaUTImplementation プロパティをcom.arjuna.ats.internal.jta.transaction.arjunacore.UserTransactionImpleに設定します。

2.2. TransactionManager

TransactionManager インターフェースにより、管理されているアプリケーションの代わりにアプリケーションサーバーがトランザクション境界を制御できるようになります。

注記

JNDI からTransactionManager を取得可能です。
	InitialContext ic = new InitialContext();
	TransactionManager utm = ic.lookup("java:/TransactionManager")
トランザクションマネージャは、内部のデータ構造の一部としてスレッドとトランザクションコンテキストの関連を保持します。スレッドのトランザクションコンテキストは、null あるいは特定のグローバルトランザクションを参照します。複数のスレッドが同じグローバルトランザクションと紐付けられている場合もあります。ネスト化されたトランザクションには対応していません。
各トランザクションコンテキストは、トランザクションオブジェクト内にカプセル化されていますが、このトランザクションオブジェクトを使い、呼出中のスレッドのトランザクションコンテキストに関係なく、対象のトランザクション固有の操作を実行することができます。
TransactionManagerbegin メソッドは、新しいトップレベルトランザクションを開始し、呼出中のスレッドとトランザクションコンテキストを関連づけます。呼出中のスレッドがすでにトランザクションと紐付いている場合、begin メソッドはNotSupportedExceptionをスローします。
getTransaction メソッドは、呼出中のスレッドと紐付けられているトランザクションコンテキストを示すトランザクションオブジェクトを返します。このオブジェクトを使い、対象のトランザクション上で様々な操作を実行可能です。これらの操作については別のセクションにて説明されています。
commit メソッドは、呼出中のスレッドと紐付いているトランザクションを完了させます。メソッドが返された後、呼出中のスレッドはどのトランザクションとも関連付けがなくなります。スレッドがトランザクションコンテキストと関連付けがないときにcommitが呼び出されると、例外がスローされます。実装によってはトランザクションの発信元のみがcommit操作を利用可能となっています。呼出中のスレッドにトランザクション実行の許可がされていない場合、例外がスローされます。また、JBossJTAはスレッドのトランザクション終了機能に制限を課しません。
rollbackメソッドを使い、現在のスレッドに紐付いているトランザクションをロールバックします。rollback メソッドの完了後は、このスレッドはトランザクションとの関連がなくなります。

注記

マルチスレッドの環境では、同一のトランザクション内で複数のスレッドが有効です。checked transaction semanticsが無効の場合、あるいはトランザクションがタイムアウトしている場合、トランザクションは作成者以外のスレッドがトランザクションを終了することができます。これが起こると、作成者に通知がいきます。JBoss Transaction Service はIllegalStateException 例外をスローすることで、コミットあるいはロールバック中にこの通知を実行します。

2.3. トランザクションの停止および再開

JTAは、一時的にトランザクションを停止、再開する スレッドという概念に対応しており、トランザクショナルな作業以外を実行することができます。suspend メソッドを呼出し、呼出中のスレッドに紐付いた現在のトランザクションを一時的に停止します。このスレッドがどのトランザクションとも紐付いていない場合、null オブジェクト参照が返されます。そうでない場合は、有効なTransaction オブジェクトが返されます。Transaction オブジェクトはその後、resume メソッドに渡されトランザクションコンテキストを復帰させます。
resume メソッドは、指定したトランザクションコンテキストと呼出中のスレッドを紐付けます。指定されたトランザクションが有効な場合、トランザクションコンテキストは呼出中のスレッドと関連付けられます。そうでない場合は、スレッドはどのトランザクションとも紐付けられません。

注記

呼出中のスレッドがすでに別のトランザクションと関連づいている場合にresume メソッドが呼び出されるとトランザクションマネージャはIllegalStateException 例外をスローします。
Transaction tobj = TransactionManager.suspend();
..
TransactionManager.resume(tobj);

注記

この機能は JTA 規格で必須ではないにも拘らず、JBossJTA では停止されたトランザクションを別のスレッドで再開することが可能です。
トランザクションが停止されると、アプリケーションサーバーは、停止中のトランザクションに関連するリソースの登録を解除し、空きを作ります。リソースがリストから外されると、トランザクションマネージャがリソースマネージャに対し指定のリソースオブジェクトからトランザクションの関連をなくすよう通知します。アプリケーションのトランザクションコンテキストが再開されると、アプリケーションサーバーはトランザクションに対してリソースを戻さなければなりません。トランザクションの再開によりリソースの登録をすると、トランザクションマネージャはリソースマネージャに対し、リソースオブジェクトと再開されたトランザクションを再度関連付けるように通知します。

2.4. トランザクションインターフェース

Transaction インターフェースにより、対象オブジェクトと紐付いたトランザクション上で操作を実行できるようにします。トランザクションが作成されると、トップレベルのトランザクションはすべてTransaction オブジェクト1つと紐付けられます。以下の目的でTransactionオブジェクトを利用することができます。
  • アプリケーションにより利用中のトランザクショナルリソースを登録
  • トランザクション同期のコールバックを登録
  • トランザクションのコミットあるいはロールバック
  • トランザクションの状態を取得
commit および rollback メソッドにより、対象オブジェクトがコミットあるいはロールバックできるようになります。呼出中のスレッドでは、スレッドに同じトランザクションを紐付ける必要はありません。呼出中のスレッドでトランザクションのコミットができない場合、トランザクションマネージャは例外をスローします。JBossJTA はトランザクションを終了するスレッドに対して制限を課しません。

2.5. リソースの登録

データベース接続などのトランザクションリソースは通常、リソースアダプタやアプリケーションサーバー、さらにオプションで接続プール最適化により管理されています。外部のトランザクションマネージャがリソースマネージャが実行するトランザクショナルな作業を調整できるように、アプリケーションサーバーはトランザクションで利用されるリソースを登録および登録解除する必要があります。これらのリソース (参加者) は、トランザクションの終了時に通知されるように、トランザクションに登録されます。
前述したように、JTAは任意のオブジェクトよりもXA のリソースコンセプトと密統合されています。アプリケーションにより利用中のリソースに対し、アプリケーションサーバーは使用中のリソースを特定する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 インターフェース

トランザクションの仕様やシステムのうち一般的なリソースを定義するものもあり、任意のリソースとトランザクションを登録するのに利用できます。JTA は、はるかにXA 固有のものとなっています。javax.transaction.xa.XAResourceインターフェースは、 XA インターフェースのJava マッピングで、拡散トランザクションの処理環境にてResource Manager (リソースマネージャ)Transaction Manager (トランザクションマネージャ)の間のコントラクトを定義します。リソースアダプタは、XAResource インターフェースを実装し、トップレベルのトランザクションをリソースに紐付けるサポートをします。関係データベースは、このようなリソースの一例となっています。
外部のトランザクションマネージャがトランザクションを制御する環境にて利用するためのトランザクショナルなリソースアダプタにより、XAResource インターフェースに対応可能です。アプリケーションは複数のデータベース接続を介してデータにアクセスすることができます。各データベースの接続は、基盤のリソースマネージャインスタンスに対するプロキシオブジェクトとして機能するXAResource オブジェクトと紐付いています。また、トランザクションマネージャは、トップレベルトランザクションに参加するリソースマネージャごとにXAResource を取得します。startおよびend メソッドはリソースとトランザクションを紐付けし、紐付けを解除します。
リソースマネージャは、startendの呼出しのいずれかをデータに対して行う全作業とトランザクションを関連付けます。トランザクションのコミット時に、トランザクションマネージャはこれらのトランザクショナルなリソースマネージャに対し、2相コミットプロトコルに従いトランザクションを準備、コミットあるいはロールバックするよう指示を出します。
Java とより良く統合するために、XAResource は標準のXA インターフェースと以下の点で異なります。
  • リソースマネージャの初期化は、接続取得時にリソースアダプタにより暗黙的に実行されます。xa_openと同等となるものはありません。
  • Rmid は、引数として渡されず、Rmid はそれぞれ、別々のXAResource オブジェクトにより表現されます。
  • Java がマルチスレッド処理に対応しており、データベースの多くが非同期操作に対応していないため、非同期操作は対応していません。
  • トランザクションマネージャによりXAResource オブジェクトを正しく処理されない場合に出されるエラーの戻り値は、XAException クラスによりJava の例外にマッピングされます。
  • Thread of Control におけるDTP の概念では、 XAResourceConnection オブジェクトへのアクセスを全Java スレッドへマッピングします。例えば、2つの別スレッドは、同じ XAResource オブジェクト上でstartend 操作を実行できます。

3.1.1. XAResource 制御の継承

デフォルトでは、XAResource オブジェクトがJTA準拠のトランザクションサービスに登録されると常に、XAResource オブジェクトについて、2相コミットプロトコルの間に呼び出される順番の制御はできません。しかし、JBoss Transaction Service はcom.arjuna.ats.jta.resources.StartXAResourcecom.arjuna.ats.jta.resources.EndXAResourceのインターフェース2つの順番を制御するようサポートしています。これらのインターフェースからのXAResource インスタンスを継承することで、ご利用中のクラスインスタンスがコミットプロトコルの最初、あるいは最後のどちらに呼び出すかを管理します。

注記

特定のトランザクションに登録できるのは、各インターフェースの種類でインスタンス1つのみとなっています。
最終リソースコミット最適化 (LRCO: Last Resource Commit Optimization)は、1 相のみ対応する単一のリソース(prepare のサポートなし)を 2 相対応の参加者を操作するトランザクションへ参加させることができ、JBossTS は LRCO に対応しています。
LRCO機能を利用するため、XAResource 実装をcom.arjuna.ats.jta.resources.LastResourceCommitOptimisationマーカーインターフェースに拡張する必要があります。Transaction.enlistResourceを介してリソースを登録する際、JBoss Transaction Service があるとLastResourceCommitOptimisation 参加者のみがそれぞれのトランザクション内で利用できるようにします。コミットプロトコールの最後にご利用中のリソースが駆動され、prepare メソッドの呼出しはされません。

注記

デフォルトでは、LastResourceCommitOptimisation クラスのインスタンスを複数参加させようとすると失敗し、falseTransaction.enlistResourceから返されます。com.arjuna.ats.jta.allowMultipleLastResources プロパティをtrueに設定することでこの動作をオーバーライドすることができます。詳細については、必ず1相対応リソースの複数登録に関する項を参照するようにしてください。
拡散環境でLRCOを利用するには、介入サポートを無効にする必要があります。ただし、暗黙的なコンテキスト伝播を利用することはできます。

3.1.2. 1 相対応リソースの複数参加

同一トランザクション内にある複数の参加者 (リソース)間で結果の一貫性 (アトミック性) を保つため、永続トランザクションログとともに2相コミットプロトコルを使います。前述した通り、1相対応リソースを処理する際、LRCO を利用するとリソース間でアトミックな結果 (all or nothing) を実現することができます。
しかし、同一のトランザクション内に複数の1相対応リソースを登録している場合もあるでしょう。例えば、同一のトランザクション内でレガシーのデータベースをレガシーのJMS 実装として実行している場合などです。このような場合、いずれのリソースもprepareステートに入らないため、複数のリソース間でトランザクションの結果においてアトミック性を獲得することができません。他のリソースステートを認識せず、後続のリソースが異なる選択をした場合でもそれを取り消す方法なしに、トランザクションのコーディネータが指示を出すとすぐにコミットあるいはロールバックします。そのため、データの破損やヒューリスティックな結果が発生することがあります。
このような場合、以下のいずれかの方法を採ってください:
  • 補正トランザクションのリソースをラップ
  • レガシー実装を2相対応と同等なものに移行
これらのオプションが実行できない場合、JBoss Transaction Service では、LRCO を使うことで複数の1相対応リソースを同一のトランザクション内に参加させることができます。LRCO については本項にて前述しています。

重要

LRCO サポートが有効になっていても、JBoss Transaction Service はこのサポートを検出すると警告を出します。このログメッセージは、"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. リソースマネージャを開く

X/Open XAインターフェースは、他のxa_ 呼出しを行う前にxa_openを使い、トランザクションマネージャがリソースマネージャを初期化する必要があります。JTA では、リソースマネージャを表すリソースアダプタ内に埋め込まれるように、リソースマネージャを初期化する必要があります。トランザクションマネージャは、リソースマネージャを初期化する方法を知る必要はありません。トランザクション関連の作業開始時と終了時、そして、トランザクションの完了時にのみ、リソースマネージャに通知する必要があります。リソースアダプタは、リソースマネージャへの接続が確立されるとリソースマネージャを開きます (初期化します)。

3.3. リソースマネージャを閉じる

トランザクショナルリソースが破棄されると、リソースアダプタがリソースマネージャを閉じます。リソースアダプタレベルのトランザクションリソースは、2種類のオブジェクトで形成されます。
  • トランザクションマネージャが利用中のリソースに紐付いたトランザクションをstart および endするだけでなく、トランザクション完了プロセスを調整することができるXAResource オブジェクト
  • アプリケーションが基盤リソース (RDBMS のJDBC 操作など) で操作を実行できるようにする接続オブジェクト
一度開くとリソースマネージャは、リソースが明示的に開放される (閉じられる)まで開いた状態のまま保たれます。アプリケーションがその接続のclose メソッドを呼び出すと、リソースアダプタはアプリケーションが持つ接続のオブジェクト参照を無効にし、アプリケーションサーバーにcloseの通知をします。トランザクションマネージャはXAResource.end メソッドを呼出し、接続とトランザクションの紐付けを解消します。
close の通知により、接続プーリングの場合は、アプリケーションサーバーが必要なガーベッジコレクションを実行し物理XA接続を再利用可能とマーキングできるようにします。

3.4. Thread of Control (スレッド)

X/Open XA インターフェースは、トランザクションの紐付けに関連するXA を同じスレッドのコンテキストから呼び出す必要があると指定します。この thread-of-control の要件は、アプリケーションのスレッドがメソッドの呼出時に動的にディスパッチされるという、オブジェクト指向でコンポーネントベースのアプリケーションランタイム環境には適用できません。接続が複数のメソッド呼出しにわたる場合、別のスレッドが同じ接続リソースを使いリソースマネージャにアクセスする場合もあります。アプリケーションサーバーの実装によっては、異なるスレッドが同じXAResourceオブジェクトに関与する場合があります。リソースコンテキストやトランザクションコンテキストは、スレッドコンテキストから独立して機能する場合もあるため、異なるスレッドによりstartend メソッドを呼び出すことができます。
アプリケーションサーバーが、複数スレッドによる単一の XAResource オブジェクトの使用やリソースマネージャへ関連付けられた接続の使用を許可する場合、アプリケーションサーバーは常時、トランザクションコンテキスト1つのみがそのリソースに紐付けられているようにしなければなりません。そのため、XAResource インターフェースでは、リソースマネージャがいずれのスレッドコンテキストからの2相コミットプロトコルにも対応できるようにする必要があります。

3.5. トランザクションの関連付け

start メソッドでトランザクションをトランザクショナルリソースと関連付け、end メソッドでリソースから関連付けを解除します。リソースアダプタは内部で、リソースの接続オブジェクトとXAResource オブジェクトの関連を保持します。どの時点であれ、接続は0あるいは1つのトランザクションと関連付けられています。JTAはネスト化されたトランザクションをサポートしないため、別のトランザクションが紐付いた接続上でstart メソッドを呼び出すことができません。
トランザクションマネージャは、start および endが各トランザクションコンテキストのスイッチに対して正しく呼び出されている限り、同じリソースを複数のトランザクションコンテキストとインターリーブすることができます。リソースが異なるトランザクションで利用されるたびに、そのリソースと紐付いた以前のトランザクションに対してend メソッドを呼び出し、現在のトランザクションコンテキストに対してstart メソッドを呼び出す必要があります。

3.6. 外部制御の接続

アプリケーションサーバーがトランザクショナルアプリケーションのトランザクションステートを管理する場合、トランザクションの関連付けが正しく行われるように、そのリソースもアプリケーションサーバーが管理する必要があります。アプリケーションがトランザクションと紐付けされている場合、接続のリソースオブジェクトにグローバルトランザクションを紐付けずに、この接続を使いアプリケーションがトランザクショナルな作業を実行するのは不正です。アプリケーションサーバーはTransaction.enlistResource メソッドを呼び出すことで、利用中のXAResource オブジェクトをトランザクションと関連づける必要があります。
サーバー側のトランザクショナルアプリケーションが複数のクライアントリクエスト間でデータベース接続を保持する場合、アプリケーションサーバーはアプリケーションの現トランザクションコンテキストにリソースを参加させる必要があります。こうすることで複数のメソッド呼出し間でアプリケーションサーバーは接続リソースの利用状況を管理します。

3.7. リソースの共有

同じトランザクショナルリソースを使い複数のトランザクションをインターリーブする場合、アプリケーションサーバーは常時、リソースに対してトランザクション1つのみを参加させるようにします。トランザクションマネージャは、同じリソースマネージャインスタンスに接続されたリソースオブジェクトのいずれかを利用し、トランザクションのcommit プロセスを開始することができます。2相コミットプロトコルに利用するリソースオブジェクトは、完了済みのトランザクションと関連付ける必要はありません。
リソースアダプタは、トランザクションのcommit 処理に対しXAResource メソッドを同時に呼び出す複数のスレッドを処理する必要があります。以下のコードは、トランザクショナルリソースr1を宣言しています。グローバルトランザクションxid1 は、r1で開始、終了されます。その後、別のグローバルトランザクションxid2r1と関連づけられます。一方で、トランザクションマネージャは、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 インターフェースは、リソースマネージャに伝播する現在のトランザクションと関連づいたタイムアウトができるような操作に対応しており、リソースマネージャに関連づいたデフォルトのタイムアウトをオーバーライドします (対応している場合)。長時間実行しているトランザクションのライフサイクルがデフォルトよりも長い場合に、これは便利です。タイムアウトが変更されないと、トランザクションが終了する前にリソースマネージャがロールバックするため、結果的にトランザクションもロールバックすることになります。
トランザクションに対しタイムアウトの値が明示的に設定されていない場合、あるいは値に 0 が指定されている場合、実装固有のデフォルト値が利用されます。JBoss Transaction Service の場合、このデフォルト値を設定する方法は、どのJTA 実装を使っているかにより左右されます。
ローカルのJTA
com.arjuna.ats.arjuna.coordinator.defaultTimeout プロパティに秒数を設定します。デフォルト値は 60 秒。
JTS
com.arjuna.ats.jts.defaultTimeout プロパティに秒数を設定します。デフォルト値は0で、トランザクションのタイムアウトはありません。
残念ながら、リソースマネージャでトランザクションと同じタイムアウトを採用する状況が適切でない場合もあります。例えば、システム管理者は外部のエンティティに制御を渡さずに、リソースマネージャのライフタイムを制御する必要がある場合もあります。setTransactionTimeoutXAResourceインスタンスで呼び出すかどうかに関して、JBoss Transaction Service は all or nothing のアプローチを採っています。
com.arjuna.ats.jta.xaTransactionTimeoutEnabled プロパティがtrue (デフォルト) に設定されると、全インスタンスで呼び出されます。代わりに、com.arjuna.ats.jta.common.ConfigurationsetXATransactionTimeoutEnabled メソッドを使うこともできます。

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 のステートにあるトランザクションに戻るよう指示します。

注記

XAリカバリを実行している場合、JBoss Transaction Service にどの種類のXid を回復できるか伝える必要があります。JBoss Transaction Service が作成するXid はそれぞれ、一意のノード識別子が埋め込まれており、JBoss Transaction Service はリクエストを受けたノード識別子と一致するトランザクションとステートを回復するだけです。利用するノード識別子は、JBoss Transaction Service にcom.arjuna.ats.jta.xaRecoveryNodeという名前で開始するプロパティにて提示する必要があります。複数の値を入れることが可能です。* の値は、ノード識別子が何であれ、全トランザクションのリカバリ (ロールバックも可能な場合もある) を強制します。これは慎重に利用するようにしてください。
JBossJTA JDBC 2.0ドライバが利用中の場合、JBossJTA は、XAResource のクラッシュリカバリをすべて自動的に管理します。そうでない場合は、以下のリカバリメカニズムのうち1つを使います。
  • XAResourceがシリアル化可能な場合、トランザクションのコミット時にはシリアル化された形式が保存され、リカバリ時にはこの形式が利用されます。再作成されたXAResource は有効で、紐付いたデータベース上でリカバリを駆動することができると想定されています。
  • com.arjuna.ats.jta.recovery.XAResourceRecoverycom.arjuna.ats.jta.recovery.XARecoveryResourceManagercom.arjuna.ats.jta.recovery.XARecoveryResourceインターフェースを利用します。詳細については、障害回復に関するJDBCの項を参照してください。

4.2. XAConnectionsのリカバリ

障害から回復時に、保留となっているトランザクションを解決するため、JBossJTS は障害発生前に利用していたデータベースに再接続する機能を必要とします。通常実行時にトランザクションサービスが接続情報の大半を保存しており、この情報をリカバリ時に使うことで接続の再構築が可能です。ただし、書き込み中に障害が発生すると、情報の一部を失ってしまう可能性もあります。これらの接続を再構築するために、アプリケーションが利用している可能性のある各データベースに対し、JBossJTA インターフェースcom.arjuna.ats.jta.recovery.XAResourceRecovery 実装を1つ提供する必要があります。

注記

JBossJTA提供のトランザクショナルJDBC 2.0 ドライバを利用している場合、リカバリを実行するために別途作業を行う必要はありません。
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インスタンスを呼出したい場合、hasMoreResourcesfalse を返し現在のスキャン作業を終了するように指示を出すと、次のリカバリスキャン時には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つのみとなっています。
XMLプロパティファイルで設定されJBoss Transaction Service によりインスタンス化されるXAResourceRecovery インターフェースのインスタンスとは違って、XAResourceRecoveryHelper のインスタンスはアプリケーションコードで構築され、XARecoveryModule.addXAResourceRecoveryHelperを呼び出すことでJBoss Transaction Service に登録されます。
現在、initialize メソッドはJBoss Transaction Service により呼び出されるわけではありませんが、このメソッドを提供し今後のリリースでさらに設定オプションを追加できるようにしています。
リカバリマネージャから呼出しされなくなると、XAResourceRecoveryHelper インスタンスの登録を解除することができます。リカバリスキャンが実行されている間はしばらく、登録解除がブロックされます。
アプリケーションサーバーなど、データソースをデプロイ、アンデプロイする環境では、XAResourceRecoveryHelperインスタンスの追加、削除を動的に行う機能は便利です。このような場合、動作をクロスローディングする際は慎重に行ってください。

第5章 JDBC およびトランザクション

5.1. トランザクションJDBC ドライバの利用

JBossJTAは、JDBC 2.0 APIを使いデータベースにアクセスするローカルおよび拡散トランザクショナルアプリケーションに対応しています。JDBC 2.0 はトランザクションの二相コミットに対応しており、XA X/Open standard に似ています。JDBC 2.0 はcom.arjuna.ats.jdbc パッケージにあります。
JDBC 2.0は、エンタープライズデータベースの中でも大半は現在のバージョンで認定を受けています。対応している構成については、http://www.jboss.com/products/platforms/application/supportedconfigurations/を参照してください。

5.1.1. トランザクションの管理

JBossJTAは、JDBC 接続で実行された作業と特定のトランザクションを関連付ける必要があります。そのため、アプリケーションは暗黙的なトランザクションの伝播と間接的なトランザクション管理を使う必要があります。JBossJTA はJDBC 接続ごとに、呼出しスレッドの現在のトランザクションコンテキストを決定できなければなりません。

5.1.2. 制限

JDBC 2.0 はネスト化されたトランザクションには対応していません。サブトランザクション内でJDBC 接続を利用しようとすると、JBossJTA は例外をスローし、その接続を使った作業は実行されなくなります。

5.2. トランザクショナルドライバ

JBossJTA はトランザクション内にJDBC 接続を組み込むため、JDBC ドライバを提供しています。これらのドライバは全ての呼出しを遮断し適切なトランザクションへ接続します。特定のJDBC ドライバを駆動できるのは、トランザクショナルドライバ1種類だけです。データベースはトランザクショナルで、ACID (atomicity, consistency, isolation, durability) プロパティの保証はありません。java.sql.Driver インターフェースを実装するcom.arjuna.ats.jdbc.TransactionalDriverインターフェースを使いドライバを呼び出します。

5.2.1. ドライバのロード

アプリケーション内でドライバをインスタンス化し利用することができます。例えば、
TransactionalDriver arjunaJDBC2Driver = new TransactionalDriver();
JDBC ドライバマネージャ (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);
ドライバをロードした後、DBMS と接続を確立することができます。

5.3. 接続

JBossJTA はトランザクショナルJDBC 接続の管理ができます。アプリケーション内で利用する際に受ける影響が何点かあり、開発者はこれらに注意する必要があります。

5.3.1. 接続の確立

JBossJTA は新規JDBC ドライバを提供するため、アプリケーションコードはトランザクションサポートの追加から受ける影響は軽度なものとなっています。トランザクションの開始と終了のみ、ご利用中のコードに実行する必要があります。

5.3.2. JBossJTA JDBC ドライバのプロパティ

以下のプロパティを設定し、JBossJTAドライバに渡すことができます。com.arjuna.ats.jdbc.TransactionalDriver クラスにこれらを設定します。
userName
データベースへ接続を試行する際に使うユーザ名
password
データベースへ接続を試行する際に使うパスワード
createDb
これをtrue に設定すると、接続時にこのドライバはデータベースを構築しようと試みます。これは、全JDBC 2.0 の実装で対応されていない可能性があります。
dynamicClass
dynamicClass: これは、JNDI を利用するのではなく、クラスを指定しデータベースへ接続するためにインスタンス化します。

5.3.3. XADataSources

適切なDataSource からJDBC 2.0 接続が作成されます。XADataSourcesから、拡散トランザクション内で参加するこれらの接続を取得します。JBossJTA は、データベースへの接続が確立されると、適切なDataSource を使い、XAResources を取得しJTAインターフェースを使ってトランザクションに問うとくします。トランザクションサービスは、トランザクション終了時にこれらのXAResourcesを利用することで、データベースをトリガーしJDBC 接続経由で追加された変更をコミットあるいはロールバックします。
JBossJTA JDBC 2.0 ドライバを取得するには2種類の方法があります。単純化するため、JDBC 2.0 ドライバはアプリケーションが直接インスタンス化するものと仮定されています。

5.3.3.1. Java Naming and Directory Interface (JNDI)

JDBC ドライバが実装固有の詳細を把握することなしに、任意のDataSource を利用できるようにJNDI を使います。特定の (XA) DataSource を作成し、適切なJNDI 実装に登録することができるため、アプリケーションかJDBC ドライバがそのDataSource をバインドあるいは利用することができます。アプリケーションが実装クラスのインスタンスではなくインターフェースのインタンスとして(XA)DataSource を見るようにJNDIが制限をかけているため、アプリケーションは特定の (XA)DataSource実装以外も利用できるようになっています。
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);
Context.INITIAL_CONTEXT_FACTORY プロパティは、利用したいJNDI 実装の種類をJNDIが指定する方法を示しています。
次のステップは、アプリケーションが適切な接続URLをJDBC 2.0 ドライバに渡す必要があります。
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);
このDataSource がトランザクションに参加し駆動する必要があるとArjunaJDBC2Driver インターフェースが認識できるように、JNDI URL はjdbc:arjuna:から始める必要があります。

5.3.3.2. 動的なクラスのインスタンス化

動的なクラスのインスタンス化は対応されておらず、推奨されていません。代わりにJNDI を使うようにしてください。詳細については、「Java Naming and Directory Interface (JNDI)」 を参照してください。

5.3.3.3. 接続の利用

適切なメソッドを使い接続が確立されると、JBossJTA はその接続で行われる全操作を監視します。この接続の利用時にトランザクションが行われていない場合、データベースに対し直接操作が行われます。
接続が無効になってしまった場合は、トランザクションのタイムアウトを使い自動的にトランザクションを終了することができます。
複数のトランザクションにて同時に JBossJTA 接続を使うことができます。JBossJTA はJDBC 接続内で各トランザクションに対して接続プーリングを行います。そのため、複数のスレッドがJDBC 接続の同じインスタンスを使っている場合でも、各トランザクションは内部で別の接続インスタンスを利用している場合もあります。close メソッドの例外として、接続に対してアプリケーションレベルで実行された操作はすべて、このトランザクション固有の接続のみを使います。
JBossJTA は、適切なリソースを使い、トランザクションにJDBC ドライバ接続を自動登録します。トランザクションが終了すると、このリソースは、JDBC ドライバ上で適切な呼出しを使い、基盤データベースに加えられた変更をコミットするか、あるいはロールバックします。
ドライバと接続が作成されると、これらは、他のJDBC ドライバあるいは接続と同じ方法で利用されます。
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. 接続プール

各ユーザ名、パスワードですが、JBossJTAは接続が利用されている間、各接続のインスタンスを維持します。同じ接続に対して出されたその後のリクエストについては、新規インスタンスではなく、元の接続への参照を取得します。接続を明示的に閉じることができますが、トランザクションも含め全ユーザがその接続を終了するか、あるいはclose メソッドリクエストを発行するまで、リクエストは無視されます。

5.3.3.5. 接続の再利用

複数のトランザクションに対して接続を再利用可能にするJDBC ドライバは非常に少ないです。ドライバの多くは、新規トランザクション毎に新規接続が必要です。デフォルトでは、JBossJTA のトランザクショナルドライバは常に、新規トランザクションごとに新規接続を取得します。しかし、既存の接続があり、現在利用されていない場合、JDBC URLでreuseconnection プロパティをtrue に設定することができます。
	  jdbc:arjuna:sequelink://host:port;databaseName=foo;reuseconnection=true

5.3.3.6. トランザクションの終了

トランザクションを終了し、JDBC 接続が登録されている場合はいつでも、JBossJTA JDBC ドライバはデータベースが保留中の変更をコミットあるいはロールバックするように指示を出します。これはアプリケーションの範囲外であるバックグラウンドで行われます。

5.3.3.7. AutoCommit

java.sql.Connection プロパティのAutoCommit プロパティがJDBC 1.0に対してtrueと設定されている場合、SQL ステートメントの実行は、別のトップレベルトランザクションとなっており、複数のステートメントをまとめて1つのOTSトランザクション内で管理することができません。そのため、JBossJTA は、JDBC 1.0 接続では利用前にAutoCommit を無効にします。AutoCommit が後にアプリケーションによりtrue に設定された場合、JBossJTA はjava.sql.SQLException の例外を出します。

5.3.3.8. 分離レベルの設定

JBossJTA JDBC ドライバを利用する場合、基盤のトランザクション分離レベルをXA接続に設定する必要がある可能性があります。デフォルト値はTRANSACTION_SERIALIZABLEですが、com.arjuna.ats.jdbc.isolationLevel プロパティを文字列の形式で適切な分離レベルに設定することで、ご利用中のアプリケーションにより適したものに設定することができます。ここで利用可能な値は、TRANSACTION_READ_COMMITTEDTRANSACTION_REPEATABLE_READなどとなっています。

注記

現在、このプロパティはJVM で作成されるXA 接続全てに適用されています。

第6章 例

6.1. JDBC の例

例6.1「JDBCTest の例」にて、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パラメータは以下のフォーマットであると仮定しています。
  • DB_x_DatabaseURL=
  • DB_x_DatabaseUser=
  • DB_x_DatabasePassword=
  • DB_x_DatabaseDynamicClass=
xは接続情報の数を指しています。

注記

簡潔にまとめるため、このテキストからエラー処理コードの一部を取り除いています。

例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 設定オプションとデフォルト値 にて、設定機能とデフォルト値を示し、詳細情報については該当の項番号を記載しています。

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. サービス設定

JBoss Transaction Service に対する設定の大半は、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>

トランザクションサービスも標準のJBoss Transaction Service プロパティファイルで設定可能です。etc ディレクトリ内のJBoss Transaction Service install に置かれています。これらのファイルを手動あるいはJMXを使い編集することができます。各プロパティファイルは、com.arjuna.ts.propertiesと、設定予定の属性を含むモジュール名と組み合わせから派生した名前を持つオブジェクトを使い公開します。例えば、com.arjuna.ts.properties:module=arjunaなどです。

8.2. ロギング

JBoss Transaction Service のロギングを意味的にJBoss Enterprise Application Platform と整合性をとるために、TransactionManagerService サービスは、jbossjta-properties.xml ファイルに提示されているcom.arjuna.common.util.logger プロパティの値をオーバーライドすることで、一部のログメッセージのレベルを修正します。そのため、このプロパティの値は、トランザクションサービスがJBoss Enterprise Application Platformに埋め込まれていると、ロギングの動作に影響をあたえません。log4j_relevelerロガーの利用を強制することで、TransactionManagerService サービスにより、トランザクションコード内にあるINFO レベルのメッセージを全て、 DEBUG メッセージとして表示されるように変更します。副次的な影響として、フィルターレベルがINFOの場合、これらのメッセージはログファイルに表示されません。その他のログメッセージはすべて通常通り、動作します。

8.3. サービス

JBoss Enterprise Application Platformとの統合で提示されるサービスは、TransactionManagerServiceのみとなっています。このサービスは、java:/TransactionManager名を使い、リカバリマネージャが起動し、JBoss Transaction Service JTA トランザクションマネージャがJNDI プロバイダとバインドされるようにします。このサービスは、CORBA ORB サービスの存在により左右され、基盤のORB 実装としてJacORBを使う必要があります。
このサービスには2つのインスタンスがあります。
分散
JBoss Transaction Service が使用可能なトランザクションマネージャの実装を使い、分散トランザクションとリカバリに対応しており、com.arjuna.ats.jbossatx.jts.TransactionManagerService クラスで設定されています。これはデフォルトの設定となっています。
ローカル
純粋なローカルJTA 実装を使います。com.arjuna.ats.jbossatx.jta.TransactionManagerService クラスを使い設定されています。

8.4. トランザクションコンテキストを確実にサーバーへ伝播

JBoss サーバーの外部にあるコーディネータがトランザクションを調整することができます。確実にJRMP 呼出しを使いトランザクションコンテキストが伝播されるように、トランザクション伝播のコンテキストファクトリをJRMP呼出元プロキシに明示的に設定する必要があります。例:JRMPInvokerProxy.setTPCFactory( new com.arjuna.ats.internal.jbossatx.jts.PropagationContextManager() );

パート II. JTS 開発

本項は、Java Transaction Service (JTS) のJBoss 実装を使い、利用中のエンタープライズアプリケーションにトランザクショナルサポートを追加する方法を説明しています。

第9章 概要

9.1. はじめに

本章は、JBoss Transaction Service と Transactional Objects for Java ツールキットの用途について説明しています。トランザクションを使った耐障害アプリケーションを記述する際に、本章で出てくるクラスは重要となってきます。これらについて説明後、単純なアプリケーションでの応用について説明していきます。本章で扱うクラスは、com.arjuna.ats.txojcom.arjuna.ats.arjuna パッケージにあります。

9.2. JBoss Transaction Service

オブジェクト指向の観点に従い、信頼のある分散アプリケーションを構築するのに必要な仕組みをオブジェクト指向の形式でプログラマに提示します。同時制御やステート管理などの仕組みは継承される必要があります。オブジェクト格納やトランザクションなどの仕組みは他のオブジェクトと同じように作成および操作されるJBoss Transaction Service オブジェクトとして実装されます。

注記

永続および同時制御機能を利用する場合、Transactional Objects for Java (TXOJ)クラスの利用を前提としています。ここでは他の仕組みについては触れません。
JBoss Transaction Service はオブジェクト指向の技術を使い、アプリケーションクラスを継承可能なJava クラスのツールキットをプログラマに提供し永続性や同時制御といった希望のプロパティを取得できるようになります。これらのクラスは階層を形成しますが、この階層の一部を本書にて詳しく説明し、例示しています。
JBoss Transaction Service のクラス階層

図9.1 JBoss Transaction Service のクラス階層

プログラマはトランザクションのスコープ指定およびオブジェクト内への適切なロック設定のみを行い、JBoss Transaction Service とTransactional Objects for Java (TXOJ) が適切なトランザクションへの登録、機能、また障害時のクラッシュリカバリを行います。

9.2.1. オブジェクトステートを保存

JBoss Transaction Serviceは、オブジェクトステートを記憶します。リカバリでは過去のオブジェクトステートを示し、永続性ではアプリケーション終了時のオブジェクトの最終ステートを指していますが、それぞれの情報を必要とします。これらの全要件は、 InputObjectState クラスや OutputObjectStateクラスといった同じ仕組みを使い実装されます。packunpack 操作を使って標準タイプのインスタンスを継続的にパックおよびアンパックできる内部アレイを、これらのクラスは保持します。このバッファは随時、自動的にサイズの調整がなされます。これらのインスタンスは、ネットワークバイトオーダーと呼ばれる機械から独立した標準形式にてすべてバッファに格納されます。XDRやASN.1などアーキテクチャーに依存しない別の形式を実装するには、これらのクラスをパックおよびアンパック機能に相当するクラス (必要とされる形式) で置き換えます。

9.2.2. オブジェクトストア

Java SecurityManagerは、永続性実装に関して制限を設定しており、JBoss Transaction Service 提供のオブジェクトストアはインターフェース/実装技術を使用します。JBoss Transaction Service の実装は、オブジェクトステートをローカルのファイルシステムあるいはデータベースに書き込むか、クライアントスタブを使いリモートサービスにインターフェースを実装します。
永続オブジェクトが作成されると、一意の識別子が渡されますが、これは実際のUid クラスインスタンスとなります。これらのUIDを使いオブジェクトストア内で識別が可能になります。read_committed 操作を使いステートを読み込み、write_committedwrite_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);
};
オブジェクトは、1)回復可能、2) 回復可能および永続的あるいは、3)回復可能でも永続的でもないというように分類可能です。
回復可能
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_staterestore_state メソッドはすべてsuper.save_state および super.restore_stateを呼出す必要があります。

9.2.4. Transactional Object for Javaのライフサイクル

使用されていない永続オブジェクトはオブジェクトストアに属するステートを持ち、オンデマンドで有効化されるというパッシブなステートであると仮定されています。図9.2「TXOJ の永続オブジェクトの基本的なライフサイクル」を参照してください。
TXOJ の永続オブジェクトの基本的なライフサイクル

図9.2 TXOJ の永続オブジェクトの基本的なライフサイクル

  • オブジェクトは最初はパッシブで、OutputObjectStateクラスのインスタンスとしてオブジェクトストアに格納されます。
  • アプリケーションが必要とする場合、オブジェクトはread_committed 操作を使いストアから読み込むことで自動的に有効化され、オブジェクトのrestore_state 操作により、InputObjectState インスタンスから完全なオブジェクトに変換されます。
  • アプリケーションがオブジェクトを使い終わると、save_state 操作を使い、OutputObjectState インスタンスに変換しなおすことで無効にし、write_uncommitted メソッドを利用しシャドーコピーとしてオブジェクトストアに格納しなおします。commit_state 操作を利用して、このシャドーコピーをコミットし以前のバージョンを上書きすることができます。通常トランザクションシステムがシャドーコピーの存在をプログラマには見せないようにしています。本来、有効化されたオブジェクト内でトップレベルトランザクションがコミットする時にのみ、オブジェクトの無効化が起こります。

注記

永続オブジェクトが有効な間、パッシブからアクティブへ、またアクティブからパッシブへ何度も変化します。

9.2.5. Concurrency Controller

Concurrency Controller はLockManagerクラスにより実装され、適切なデフォルトの動作を提供します。このデフォルトの動作は、プログラム済みクラスの特定セマンティクスを使い必要であればプログラマが上書きすることができます。StateManagerクラスと永続性については、インターフェース経由で同時実行制御の実装へアクセスします。インターフェースに対して提供している同時実行制御に関する実装は今のところ、以下のようになっています。
  • リモートサービスへのアクセス
  • ローカルディスクおよびデータベース実装の両方 (これらでロックをローカルのファイルシステムかデータベースに書き込み永続化します)。
  • 純粋なローカル実装 (この実装にてロックを作成した仮想マシンのメモリ内にてロックが保持されます)。この実装は、ローカルディスクへロックを書き込むよりもパフォーマンスが優れていますが、オブジェクトを仮想化マシン間で共有することができません。重要なのは、SecurityManagerの影響を受ける可能性のある要件を持たない、基本的なJava オブジェクトとなっています。
Concurrency Controller に対するプライマリAPIは、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 はサブトランザクションにも対応しているため、すでに実行中のトランザクションのスコープ内でトランザクションが開始されると、自動的にネスト化されます。

注記

JBoss Transaction Service はマルチスレッド認識となり、アプリケーション内の各スレッドが 1 つのトランザクションを共有するか、あるいはそれ自体のトランザクション内で実行できるようにします。したがって、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 &amp; objects deactivated */
      } 					/* (v) */
上記のコードを実行すると、以下の順序でアクティビティが行われます。
  1. バインドを作成しオブジェクトを永続化します。これには、スタブオブジェクトを作成し、リモートオブジェクトを呼び出す必要があるかもしれません。上記の例では、Name-Aにて特定された既存の永続オブジェクトおよび新規永続オブジェクトを再度バインドしています。リモートオブジェクトのネーミングシステムにより、オブジェクト名と場所のマッピングを管理しており、これについては後の章で説明しています。
  2. アトミックトランザクションの開始
  3. 操作の呼出し。特定の呼出しの一部として、このオブジェクト実装が読み取りまたは書き込みモードでロックされ、必要があれば、ロックの矛盾がない前提でオブジェクトストアからの最新のコミット状態で初期化されるようにします。トランザクションのオブジェクトでロックを初めて取得したときに、オブジェクトステートもオブジェクトストアから取得します。
  4. トップレベルアクションのコミット。これにはオブジェクトストアにある変更オブジェクトのステートを更新することも含まれます。
  5. 以前に作成したバインドを破棄

9.2.8. クラス階層

JBoss Transaction Service のクラス階層を構成する主なクラスは例9.5「JBoss Transaction Service のクラス階層」にて詳しく説明しています。

例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
      
耐障害性アプリケーションのプログラマは、LockManagerLockAtomicAction クラスが必要です。プログラマにとり重要なクラスは他に、UidObjectStateがあります。JBoss Transaction Service のほとんどのクラスはベースクラスStateManagerから派生しており、永続かつ回復可能オブジェクトを管理するのに必要な基本的機能を提供しています。これらの機能には、オブジェクトのアクティベーション、アクティベーション解除、ステートベースのオブジェクトリカバリへの対応が含まれています。LockManager クラスはStateManagerLock機能を使い、アトミックアクションのシリアル化プロパティ実装に必要な同時実行制御を提供します。アトミックアクション機能の実装は、AtomicActionTopLevelTransactionにより対応しています。
単純な例を見てみます。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に対してRecoveryRecordPersistenceRecord のインスタンスを作成し、このインスタンスをTransRecordList へ挿入します。
  • Trans のRecordList にLockRecord インスタンスを作成、挿入
ロックを取得後しばらくしてからTransが中断された場合、AtomicActionrollback 操作は、様々な記録上にある適切なAbort 操作を呼び出すことで、Trans に紐付いたRecordList インスタンスを処理します。LockRecord クラスによる操作を実装することで、WRITE ロックを解除し、RecoveryRecord/PersistenceRecord がOの以前のステートをリストアします。
上記の作業はすべてアプリケーションプログラマの代わりに自動的にJBoss Transactionにより実行されます。プログラマはトランザクションを開始し、適切なロックを設定するだけで、JBoss Transaction Service とTransactional Objects for Java がパーティシパントの登録、永続性、同時実行制御、リカバリを行います。

第10章 JBoss Transaction Serviceの利用

10.1. はじめに

本項では、JBoss Transation Service とTransactional Ojects for Javaに関する詳細、またこれを使いトランザクショナルアプリケーションを構築する方法について説明しています。

10.2. ステート管理

10.2.1. オブジェクトステート

JBoss Transaction Serviceは、リカバリや永続化できるようにオブジェクトステートを記憶します。リカバリの場合、ステートは過去のオブジェクトステートを指し、永続性の場合、ステートはアプリケーション終了時の最終的なオブジェクトステートを指します。リカバリや永続化には共通の機能が含まれているため、両方ともInput/OutputObjectState および Input/OutputBufferクラスを使い実装されます。
例10.2「InputBuffer」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 ();
};
例10.4「InputObjectState」 および 例10.3「OutputObjectState」クラスは、継承を使いInputBufferOutputBufferクラスの全機能を提供します。また、これらのクラスにより、インスタンス変数を2つ追加しますが、これらの変数はInputObjectState あるいは OutputObjectState インスタンスが圧縮イメージとなっているオブジェクトのUid およびタイプを表します。オブジェクトステートを格納あるいはリトリーブするときにオブジェクトストアにアクセスする場合はこれらを使います。

10.2.2. オブジェクトストア

JBoss Transaction Service が提供するオブジェクトストアには制限のかなりあるインターフェースが存在するため、様々な方法で実装可能です。例えば、オブジェクトストアは、ローカルファイルシステムでも、リモートのデータベースでも、共有メモリでも常駐可能です。JBoss Transaction Service で提供されているオブジェクトストアに関する詳細情報は、添付資料にて確認できます。

注記

すべてのJBoss Transaction Service クラスについては、デフォルトのオブジェクトストアは pure Java 実装となっています。ネーティブのメソッドを使い共有メモリやより複雑な他のオブジェクトストア実装にアクセスする必要があります。
オブジェクトストアはすべて、InputObjectStateOutputObjectState クラスのインスタンスを保持、リトリーブします。これらのインスタンスは、それらが表すオブジェクトの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;
    . . .
};
トランザクショナルオブジェクトのコミット中は、障害発生時にリカバリーし、ロールバックするか、そのままコミットするかできるように特定のステート変更を永続化する必要があります。Transactional Objects for Java を使用している場合、JBoss Transaction Service はこの永続性を自動管理します。ACID プロパティを確保するため、トランザクションのコミット前にこれらのステート変更は永続ストア実装にフラッシュされます。そうでなければ、アプリケーションは、システム障害が発生しやすいオペレーティングシステムのキャッシュにステートの変化が存在している可能性があるにも拘らず、トランザクションがコミットされているとみなします。デフォルトでは、JBoss Transaction Service はこのようなステートの変化をフラッシュします。結果、この動作によりアプリケーションのパフォーマンスに大きなペナルティを課す可能性があります。トランザクショナルオブジェクトのステートをフラッシュしないようにするには、com.arjuna.ats.arjuna.objectstore.objectStoreSync変数をOFFに設定します。

10.2.3. StateManager

JBoss Transaction Service クラスのStateManagerは、オブジェクトステートを管理し、基本的なステート管理サポートメカニズムをすべて提供します。StateManager は、トランザクショナルオブジェクトが永続化、リカバリできるよう適切なリソースを作成、登録します。トランザクションがネスト化されると、StateManagercommit フェーズ中に子トランザクションと親との間にあるこれらのリソースを伝播します。
JBoss Transaction Service のオブジェクトは、1)回復可能、2)永続的、3)回復可能で永続的、あるいは4)回復不可かつ永続的でないかとなっています。回復可能な場合、StateManager がオブジェクトに関する適切なリカバリ情報を生成、管理しようとし、Input/OutputObjectStateクラスのインスタンスにその情報を格納します。これらのオブジェクトの有効期間は、これらのオブジェクトを作成したアプリケーションよりも短いものと仮定されています。回復可能かつ永続的なオブジェクトは、これらを作成したアプリケーションよりも寿命が長いため、StateManageractivatedeactivateメソッドを呼び出すことで、オブジェクトの永続ステートをロードあるいはアンロードします。回復可能でも永続的でもないオブジェクトについては、ステートデータは何も保存されません。
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;
};
オブジェクトが回復可能あるいは永続的である場合、StateManagerdeactivation メソッド実行時にsave_state メソッドを呼び出します。activateメソッド実行中にrestore_stateが呼び出されます。また、アプリケーション実行中の様々な時点でtype が呼び出されます。StateManager は任意のJava オブジェクトレイアウトのランタイム詳細にアクセスできないため、プログラマはこれらのメソッドを実装する必要があります。しかし、InputObjectStateOutputObjectState が提供する機能により、これらのルーチンの記述が単純化されています。例えば、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_staterestore_state の全メソッドにより、super.save_statesuper.restore_stateを呼び出す必要があります。

注記

type メソッドは、そのクラスインスタンスのステートを保存し最終的にリストアするオブジェクトストアの場所を決定します。これは有効なストリングであれば結構です。ただし、ハッシュ記号#は、JBoss Transaction Serviceで必要な特別ディレクトリ向けに確保されているため、#の利用を避けるようにしてください。
StateManagerget_uid メソッドは、オブジェクトの内部システム名へ読み取り専用でアクセスできます。内部システム名の値はオブジェクト作成時に明示的なパラメータとして提供するか、新規識別子として生成することで設定可能なため、オブジェクト作成時にだけ設定できるようになっています。
destroyメソッドは、オブジェクトのステートをオブジェクトストアから削除します。これは、アトミックな操作で、呼出中のトランザクションがコミットされると状態のみが削除されます。プログラマは、この操作を呼び出す前にオブジェクトの排他的アクセスを確保していなければなりません。
オブジェクトのリカバリおよび永続性には補足要件があり、StateManager クラスは1つのメカニズムで両方を管理するように組み合わせています。つまり、リカバリおよび永続化両方に対して、クラスのInput/OutputObjectState クラスインスタンスを使うのです。追加の引数をsave_staterestore_state 操作に渡すことで、プログラマは特定の呼出しの目的を判断することができ、リカバリや永続化するために様々な情報を保存することができます。

10.2.4. オブジェクトモデル

JBoss Transaction Service はオブジェクトに対して2種のモデルをサポートしています。ステートおよび同時実行制御の実装は、どのモデルを利用しているかにより変化します。
シングル
アプリケーションには、オブジェクトのコピーが1つのみ含まれています。このオブジェクトは1つのJVM内に置かれており、クライアントはすべてサーバーへの呼出しに対応する必要があります。単一モデルでは、パフォーマンスが優れていますが、SPOF (Single Point of Failure)を生み出します。マルチスレッドの環境では、1つのスレッドに障害が起こると、オブジェクトを障害から守ることができません。
シングルオブジェクトモデル
複数
論理的には、オブジェクトのインスタンスは1つ存在します。オブジェクトの複製が複数のJVM間に分散されているのです。パフォーマンスはシングルモデルに比べると劣りますが、障害からはうまく隔離されます。
複数オブジェクトモデル
singleモデルがデフォルトとなっています。オブジェクト作成時にcom.arjuna.ats.arjuna.gandiva.ObjectNameクラスの適切なインスタンスを提供することで、オブジェクト毎にこれをオーバーライドすることができます。

注記

オブジェクトのインスタンス化前にモデルを変更することができますが、オブジェクトが有効な間、同じ状態に保つのであれば変更は必要ありません。
以下のメソッドを使い適切なObjectName クラスを提供します。例については例10.6「オブジェクトモデル」 を参照してください。
  1. ObjectNameの新規インスタンスを作成します。
  2. 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 のメソッド参照

JBoss Transaction Service クラスのStateManager は、オブジェクトステートを管理し、リカバリ、永続性、あるいはリカバリと永続性両方で必要となる基本のサポートメカニズムをすべて提供します。操作によってはご自身で定義する必要があるものもあります。自身で定義する必要のある操作は、save_state, restore_statetypeです。
boolean save_state(OutputObjectState state, int ObjectType)
今後のリカバリや永続化に向けオブジェクトの状態を保存するために呼び出します。ObjectType パラメータは、呼出しの理由を指定します。指定することおで、リカバリ、永続化のいずれを望んでいるかによって、最初のパラメータとして提供されているOutputObjectStateに様々な情報を保存することができます。例えば、他のJBoss Transaction Service オブジェクトに対するポインタを、リカバリ用のポインタとして、永続化にはUIDとして保存することができます。OutputObjectState クラスは便利な操作を提供しており、Java の基本タイプすべてのインスタンスを保存することができます。永続オブジェクトのクラッシュリカバリに対応するには、 save_stateメソッドすべてにより、super.save_stateを呼び出す必要があります。

注記

save_state メソッドは、オブジェクトが内部で一貫性を持ち、全保存変数が有効な値を持つと仮定しています。コードを記述、テストし、正しいかどうか確認します。
boolean restore_state(InputObjectState state, int ObjectType)
オブジェクトを指定したステートにリストアします。2番目のパラメータは、提供したステートに対し様々な解釈をすることができます。永続オブジェクトのクラッシュリカバリに対応するには、restore_state メソッドすべてにより、super.restore_stateを呼び出す必要があります。
String type ()
JBoss Transaction Service の永続メカニズムには、オブジェクトのステートを保存しリストアできるように文字列としてオブジェクト型を確定する方法が必要です。慣例により階層内のクラスの位置を利用します。例えば、StateManager/LockManager/Objectなどです。

注記

typeメソッドは、特定クラスインスタンスのステートをオブジェクトストアのどの部分に保存するか決定します。実際これは、有効な文字列であれば何でも結構です。しかし、ハッシュ記号#はJBoss Transaction Service 用の特別なディレクトリとして確保しているため、#の利用は避けてください。

10.2.6. 例

例10.7「オブジェクトのステートを保存、復元」は、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_staterestore_statetype 操作は以下のように定義することができます。
/* 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. ロック管理と同時実行制御

JBoss Transaction Sevice の同時実行制御に関する情報はロックにより保持されます。これらのロックの一部は、複数のオブジェクトにより様々なプロセスで利用されます。このロックは、ステート情報に利用するオブジェクトストアと同じように、ロックストアに保存することができます。JBoss Transaction Service で利用するロックストアには制限付きのインターフェースがあり、実装に柔軟性を保ちます。ロックストアは、Unix ファイルシステムで様々な形式にて、あるいはリモートでもアクセスできるストアとして、共有メモリに実装することができます。

注記

JBoss Transaction Service クラスでは、デフォルトのロックストアは pure Java 実装となっており、複雑なロック実装を利用したい場合、ネーティブのメソッドを使う必要があります。

例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. ロックストアの情報を選択

JBoss Transaction Service は複数のオブジェクトストア実装に対応しています。利用しているオブジェクトモデルがSingleの場合、オブジェクトの情報がロックストアからエクスポートされないため、ロックの保持にロックストアは必要ありません。しかし、Multiple モデルを使う場合、様々なランタイム環境により同時実行制御の情報を共有する必要がある場合があります。ロックストアの実装タイプを指定し、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_staterestore_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 or WRITEと同じ場合、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. ロックポリシー

JBoss Transaction Service のロックは、特別なシステムタイプではなく、別のJBoss Transaction Service のオブジェクトインスタンスとなっています。ロックを永続化し、単純な形式にて命名できるように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. オブジェクトの構築と破棄

JBoss Transaction Serviceオブジェクトは、1)回復可能、2)永続的、3)回復可能かつ永続的、あるいは4)回復可能でも永続的でもないの内のいずれかとなっています。これらの属性はオブジェクト構築時にのみ設定可能です。そのため、LockManagerは取得したクラスが利用できるように、保護コンストラクタを2つ提供し、以下のような明確な目的を果たします。
LockManager ()
以前のステートを持たない新規オブジェクトの作成ができます。
LockManager(int ObjectType, ObjectName attr)
以前のステートを持たない新規オブジェクトを作成できます。ObjectTypeパラメータはオブジェクトは回復可能 (recoverable)、回復可能かつ永続的 (ANDPERSISTENTで示される)、いずれでもない (NEITHER) で表します。オブジェクトが永続的と登録されると、その状態がオブジェクトストアの1つに格納されます。shared パラメータは、オブジェクトがRECOVERABLEの場合のみ意味を持ちます。attrがnull でなく、オブジェクトモデルがSINGLE (デフォルトの動作) であれば、オブジェクトの回復可能な状態がそのオブジェクト自体に保持されます。そうでない場合は、オブジェクトのステートは、各アトミックアクションの間にインメモリオブジェクトストアの中に格納されます。
新規永続オブジェクトのコンストラクタは、そのコンストラクタ内でアトミックアクションを使うはずです。こうすることで、コンストラクタ内のアクションがコミットされるとオブジェクトのステートがオブジェクトストアに自動的に書き込まれるようにします。あるいは、コンストラクタ内にアクションが存在する場合は、適切なトップレベルアクションのコミット時にオブジェクトストアに自動書き込みされます。
LockManager(Uid objUid)
objUid パラメータにて指定された既存の永続オブジェクトへアクセスができるようになります。オブジェクトの以前のステート(objUid パラメータの値により特定) は、オブジェクトストアから自動的にロードされます。
LockManager(Uid objUid, ObjectName attr)
objUid パラメータで指定した既存の永続オブジェクトへアクセスできます。オブジェクトの以前のステートは、objUidの値により特定しますが、この状態は自動的のオブジェクトストアからロードされます。attrパラメータが nullではなく、かつオブジェクトモデルがSINGLE (デフォルト動作) の場合、オブジェクトは各トップレベルトランザクションの開始時に再アクティベートされません。
プログラマ定義クラスのデストラクタは、継承した操作であるterminateを呼び出し、オブジェクトが破棄予定であることをステート管理メカニズムに通知する必要があります。そうでない場合は、予期せぬ結果が発生してしまう可能性があります。
LockManager クラスは StateManagerから継承するため、提供されたObjectName インスタンスをStateManager クラスに渡します。同様に、StateManagerオブジェクトモデルを前述したように設定することができます。

第11章 一般的なトランザクションの問題

11.1. JBoss Transaction Serviceのトランザクションに関する高度な問題

アプリケーションプログラマ、クラス開発者両方がトランザクションを利用します。オペレーション全体あるいは一部をトランザクショナルにすることができます。本章は一般的にトランザクションを使うことで起こる問題で若干扱いにくいもの、またJBoss Transaction Service の詳細について説明しています。

11.1.1. トランザクションの確認

マルチスレッドのアプリケーションでは、トランザクションが有効な間、複数のスレッドが1つのトランザクションに紐付けされて、同じコンテキストを共有します。また、1つのスレッドがトランザクションを終了した場合、他のスレッドはそのトランザクション内で有効な状態を保ちます。分散環境であれば、トランザクションが終了するとトランザクションを必要とするスレッドがないことを確認するのが困難です。JBoss Transaction Service はデフォルトで、あるスレッドがトランザクションを終了しようとすると、別のスレッドがそのトランザクションを利用している場合は警告を発行します。ただし、トランザクション自体は終了することができます。その他に考えられる動作として、別のスレッドがすべてトランザクションコンテキストとの紐付けを解除するまでスレッドを終了することができないようになっています。そのため、JBoss Transaction Service は、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. 統計の収集

デフォルトでは、JBoss Transaction Service はトランザクションの履歴情報を保持しません。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)

場合によってはtwo-phase commit aware (2相コミット対応) でないパーティシパントを2相コミットトランザクションに登録する必要がある場合もあります。単一リソースのみであれば、2相コミットの必要はありません。トランザクションに複数のトランザクションが含まれている場合、Last Resource Commit Optimization (LRCO) が該当してきます。1相対応の単一リソースであれば、準備フェースなしでコミットあるいはロールバックのみとなっています。このようなリソースが2相コミット対応リソースを持つトランザクションに登録されている場合もあります。コーディネータは、他の全リソースに対して準備フェーズを先に実行することで、1相対応リソースと若干違った形式で処理をします。その後、1相対応のトランザクションは、そのトランザクションをコミットする必要があるため、コーディネータがトランザクションに制御を渡します。コミットすると、コーディネータはコミットが決定されたことをログに残し、他のリソースのコミットも試行します。
LRCO を利用するには、利用中のパーティシパントが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. ネスト化されたトランザクション

トランザクションをネストするために特別なことをする必要はありません。アクションが実行されているときに別のアクションが開始されると、自動にネスト化されます。こうすることで、アプリケーションにモジュラー構造を提供するため、オブジェクトのプログラマはオブジェクトを利用するアプリケーションもトランザクショナルかどうかに関して心配する必要がありません。
ネスト化されたアクションが中断されると、その作業がすべてロールバックされます。しかし、厳密な2相ロッキングでは、終了トランザクションが持つロックがトップレベルのアクションがコミットあるいは中断されるまで保持されるのです。ネスト化されたアクションがコミットされ、トップレベルのアクションがコミットされると、システムにより実行された作業のみがコミットされます。トップレベルのアクションが中断されると、作業はすべてロールバックされます。
ネスト化されたアクションをコミットあるいは中断した場合、親アクションの結果も自動的に影響を受けるわけではありません。この動作をプログラム的に実装するように選択し、障害を阻止する方法あるいは作業をやり直す方法などを制御することができます。

11.1.5. トランザクションを非同期的にコミット

デフォルトでは、JBoss Transaction Service は、1つのスレッドで同期するような形でトップレベルトランザクションのcommit プロトコルを実行します。1つのスレッドが登録済みの全リソースを順番に準備するために移動し、コミットあるいはロールバックを行います。これには、考えられるデメリットが何点かあります。
  • 通常、prepare の操作は各リソースごとに並行して論理的に呼び出されます。デメリットですが、登録されているリソースにあるリストの前のほうに出てくるリソースが強制的にprepareの段階でロールバックされてしまい、必要のないprepare 操作が実行されてしまうことがあります。
  • 利用中のアプリケーションがヒューリスティックなレポーティングを必要とする場合、成功、失敗は特に重要でないため、commit プロトコルの2番目のフェーズを非同期的に呼び出すことができます。
そのため、JBoss Transaction Service は特定の変数を設定することで、ランタイムオプションを提供し、スレッドの最適化を2つ有効にします。
com.arjuna.ats.arjuna.coordinator.asyncPrepare
YESに設定されている場合、prepareフェーズにてトランザクション内に登録パーティシパントごとに別のスレッドが作成されます。
com.arjuna.ats.arjuna.coordinator.asyncCommit
YESに設定されていると、ヒューリスティックな結果の情報を必要としない場合、別のスレッドが作成されトランザクションの2番目のフェーズを完了します。

11.1.6. 独立したトップレベルトランザクション

通常のネスト化されたトップレベルトランザクションに加え、JBoss Transaction Service は独立したトップレベルアクションをサポートし、厳密な直列化可能性を制御しながら緩和できます。もう1つのトランザクション内であればどこからでも独立したトップレベルアクションを実行可能で、通常のトップレベルアクションと全く同じように動作します。親アクションが中断された場合にやり直さず、コミットされた場合、その結果は永続化されます。
独立したトップレベルアクション

図11.1 独立したトップレベルアクション

図11.1「独立したトップレベルアクション」 では、アクションBがアクションAの中にネスト化されているといった、一般的なトランザクションのネスト化について説明しています。Bが有効な間にBegin 操作が呼び出されるため、トランザクションCが論理的にアクションB内にネスト化されます。しかし、独立したトップレベルアクションであるため、構造内にある別のアクションから独立してcommitあるいはabortが行われます。独立したトップレベルアクションにこのような特徴があるため、慎重にテストや所見を行った後にのみ、注意して利用するようにしてください。
TopLevelTransaction クラスのインスタンスを宣言し、他のトランザクションと全く同じ方法で利用することでアプリケーション内でトップレベルアクションを使うことができます。

11.1.7. save_staterestore_state メソッド内のトランザクション

save_staterestore_state メソッドを記述する際には注意が必要です。メソッド内で明示的に、あるいは他の操作を利用して暗黙的にアトミックなアクションを開始しないようにしてください。注意が必要な理由は、JBoss Transaction Service はコミットするとrestore_state メソッドを呼び出す可能性があり、別アクションのcommit あるいは abort フェーズ中にトランザクション実行を試行してしまいます。こうすることで、コミットあるいは中断されたアクションのアトミックなプロパティを妨害する可能性があるため、アトミックアクションの開始はお薦めできません。

11.1.8. 例

例11.3「ネスト化されたトランザクションの例」 にあるコードは前述した例10.7「オブジェクトのステートを保存、復元」 の例に基づいており、setgetメソッドを実装します。このコードは単純化されており、エラーの条件や例外は無視しています。

例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. ガーベッジコレクションのオブジェクト

Java オブジェクトは必要なくなると、ガーベッジコレクタにより削除されます。トランザクションの制御下にあるオブジェクトを削除する場合は注意が必要です。オブジェクトがトランザクション内で操作されている場合、トランザクションがオブジェクトに何が起こるか制御します。そのため、アプリケーションが保持するトランザクショナルオブジェクトに対する参照に関係なく、JBoss Transaction Service は常に独自の参照を保持しており、関連するトランザクションがすべて終了されるまで、オブジェクトがガーベッジコレクタにより削除されないようにします。

11.1.10. トランザクションのタイムアウト

デフォルトでは、トランザクションを作成したアプリケーションがトランザクションを削除するまで、あるいは障害が発生するまで、トランザクションは有効です。しかし、トランザクションごとにタイムアウトを設定することができ、タイムアウト失効前に終了しないトランザクションがロールバックされるようになります。タイムアウトの値は秒数で表現します。
JBoss Transaction Service では、タイムアウト値をAtomicAction コンストラクタへのパラメータとして渡します。デフォルト値AtomicAction.NO_TIMEOUTとなっている場合、トランザクションは自動でタイムアウトされません。その他の正の値であれば、トランザクションが終了されるまで待機する秒数を示します。0の値は、グローバルのデフォルトタイムアウトとして解釈され、プロパティcom.arjuna.ats.arjuna.coordinator.defaultTimeoutを使い提供できます。このプロパティのデフォルト値は、60あるいは1分となっています。
タイムアウトが0以外のトップレベルトランザクションを作成すると、トランザクションのタイムアウト時にロールバックされます。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
ここでは、トップレベルのアクションAの制御外にオブジェクトを2つ作成されており、obj1は新規オブジェクトで、obj2 は既存の古いオブジェクトとなっています。obj2remember メソッドが呼び出されると、オブジェクトが有効になり、obj1 のUid が分かります。このアクションがコミットされると、obj2 の永続ステートにはobj1のUid が含まれるようになるでしょう。しかし、アクション制御により操作されていないため、obj1のステートはまだ保存されていません。実際、アプリケーション内で何らかのアクションの管理下で変更が加えられない限り、保存はされません。しかし、コンストラクタがアトミックアクションを利用している場合、obj1 の状況は構築時に自動的に保存され、矛盾を防ぎます。

12.1.2. save_state および restore_state メソッドに関する詳細情報

JBoss Transaction Serviceは、オブジェクトコンストラクタのボディを実行するときなどオブジェクトが有効であればいつでも、オブジェクトにあるユーザ定義のsave_state メソッドを呼び出すことができます。特にアトミックアクションを利用する場合に当てはまります。save_stateで保存した変数はすべて、正しく初期化されます。
save_state および restore_state メソッドを記述する際は、トランザクションが明示的あるいは暗黙的に開始されないよう十分に注意してください。JBoss Transaction Service は、処理中のcommit の一部としてrestore_state メソッドを呼び出し、別トランザクションのcommit あるいは abort フェーズ中にアトミックトランザクションを実行してしまう可能性があるためです。これにより、コミットあるいは中断されたトランザクションのアトミックプロパティが妨害されてしまう可能性があるため、推奨されません。
永続オブジェクトの障害回復をサポートするには、ユーザオブジェクトのsave_staterestore_state メソッドすべてがsuper.save_state および super.restore_stateを呼び出す必要があります。

12.1.3. パッキングオブジェクト

InputObjectStateOutputObjectStateで提供されるpack および unpack メソッドを利用することで、intlongなどの基本的な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;
}
ここでは、s1およびs2は同じstringを指定し、save_state メソッドのナイーブな実装がこの string を2度コピーする可能性があります。save_state メソッドから見ると、これは非効率なだけですが、restore_state メソッドがこれら2つの string を別のメモリ領域にアンパックし、元のエイリアス情報を壊してしまうのです。JBoss Transaction Service では、オブジェクト参照を別々にパックおよびアンパックします。

12.2. StateManager クラスを直接利用

本書の例は、LockManager クラスからユーザクラスを取得しています。以下のように理由は2つあります。
  • 最も重要な点ですが、アトミックアクションのシリアル化における制限で上記が必要になるため。
  • 次にプログラマの介入の必要性が軽減されるため。
しかし、JBoss Transaction Service の永続性やリカバリの仕組みだけを利用する必要がある場合は、StateManagerからユーザクラスを直接取得可能です。
StateManager から直接取得したクラスは、LockManagerに依存するのではなく、明示的に状況管理の仕組みを使う必要があります。StateManager のコンストラクタは事実上、LockManagerと同一となっているため、activatedeactivatemodified メソッドを適切に利用しなければなりません。

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 ツールの起動

JBoss Transaction Service ツールを起動するには、ご利用中のオペレーティングシステムに見合ったセクションを参照してください。
Windows
Start メニューから、JBoss Transaction Serviceプログラムグループの Start Tools リンクをダブルクリックします。
Linux およびUnix 系のオペレーティングシステム
コマンドラインで$JBOSS_HOME/run-tools.shとタイプしてください。
表示されたToolsのウィンドウがJBoss Transaction Service に同梱されている全ツールの起動エリアとなります。メニューバーは、Tools ウィンドウの上部に位置しており、以下の4つのアイテムが含まれています。

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 JMX bean を使いこの情報を集めるため、パフォーマンス情報を提供するにはトランザクションサービスをアプリケーションサーバーに統合する必要があります。
マルチシリーズグラフを使いこの情報を表示します。このグラフを表示するには、PerformanceOpenを選択し、パフォーマンスウィンドウを開きます。

Performance Tool Graphでの表示項目

  • トランザクション数
  • コミットされたトランザクション数
  • 中断されたトランザクション数
  • ネスト化されたトランザクション数
  • ヒューリスティックがあげられた数
これらの項目をOn / Offするには、Series メニューのメニューオプションを選択します。
上記の項目が有効な場合、グラフ内で使われているそれぞれの色とともに、グラフの一番下にあるキャプション部分に表示されます。
Y軸は、トランザクション数を、X軸は時間を表します。
Samplingメニューを使いデータのサンプリングを停止、再開した時点で、Data メニューのSave to .csvメニューオプションを使いデータをスプレッドシートアプリケーションにインポートできるように、現在グラフ内で表示されているデータはCSV ファイルへ保存することができます。

13.4. JMX ブラウザの利用

JMX ブラウザウィンドウを開くには、FileOpen JMX Browser オプションを選びます。
このウィンドウは、MBean パネルとDetails パネルで構成されており、MBean パネルは、MBean サーバーにより公開されたMBeanをドメイン別に分類表示します。また、Details パネルは現在選択されているMBean に関する情報を表示します。MBean を選択するには、マウスで選択します。

Details パネルに表示されている情報

  • 本サーバーに登録されているMBean の合計数
  • 本MBean にて公開されたコンストラクタ数
  • 本MBean により公開された属性数
  • 本MBean により公開された操作数
  • 本MBean により公開された通知数
  • MBean に関する概説
View メニューへのリンクからMBean が公開する属性や操作を表示することができます。読み込み可能な属性を表示、書き込み可能な属性を変更、操作の呼び出しを行うことができます。

13.4.1. 属性と操作の利用

View リンクをクリックすると、View JMX Attributes and Operations ウィンドウが開きます。このウィンドウを使い、選択したMBeanにより公開された読み込み可能な属性をすべて表示し、書き込み可能な属性を変更します。属性の値を変更するには、現在の値をダブルクリックし新しい値を入力します。... ボタンが有効な場合は、そのボタンをクリックし拡張エディタを有効にしてください。属性がJMX オブジェクト名の場合、このボタンをクリックすることでそのオブジェクトに対するJMX属性と操作が表示されます。
いつでも Refresh ボタンをクリックし属性値を更新することができます。属性値のリトリーブ中に例外が発生すると、属性値の部分に例外が表示されます。
属性一覧の下に表示されている操作一覧から実行したい操作を選択しInvokeボタンをクリックすることで、MBean に対して操作を呼び出すこともできます。その操作にパラメータが必要な場合は、さらにウィンドウが表示され、このウィンドウから必要な各パラメータの値を指定しなければなりません。JMX 属性値を指定したのと同じ方法でパラメータ値を指定します。各パラメータに値を指定してから、Invoke ボタンを押し呼出しを実行します。この呼出しが完了すると、戻り値が表示されます。

13.4.2. Object Store Browserの利用

Object Store browserウィンドウを開くには、FileOpen Object Store Browserオプションをクリックします。
object store browserウィンドウは4つの部分に分かれています。
Object Store Roots
現在利用可能なオブジェクトストアルートのドロップダウンリスト。一覧からオプションを選択することで、選択したルートのコンテンツを階層形式で再生成します。
Object Store Hierarchy
現在のオブジェクト階層を表示するツリー。このツリーからノードを選択すると、そのロケーションに保存されているオブジェクトが表示されます。
Objects
選択したロケーションに保存されているオブジェクトを示すアイコン一覧。
Object Details
現在選択されているオブジェクトに関する情報。ステートビューワーレポジトリにてオブジェクトのタイプが分かっている場合のみ表示されます。「OSVの記述」 を参照し、オブジェクトを正しく表示するようにしてください。

13.4.3. Object State Viewer (OSV)

メインウィンドウのObjectsペインでオブジェクトを選択すると、オブジェクトタイプに登録したObject State Viewer (OSV)が呼び出されます。OSVは選択したオブジェクトに関する情報をユーザーインターフェースで利用できるようにします。Atomic Action (トランザクション)のOSVは標準のツールで分配されます。Abstract Recordsの情報をヒューリスティック、失敗、読み取り専用、その他など、メソッド一覧に表示します。独自のOSVを記述し、自身で定義したオブジェクトタイプの情報を表示することができます。

13.4.3.1. OSVの記述

OSV プラグインを記述し、Object Store Browser がユーザ定義のAbstract Recordの状況を表示することができます。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;
	  }
	  }
目的は、Object Store Browser にて表示した時にAbstract Record の現在値を表示することです。ステートをAbstract Record のインスタンスに読み込み、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”;
	  }
	  }
特定のタイプを示すUnique Identification (UID) がObject Store Hierarchy ツリーで展開されると、uidNodeExpanded メソッドを呼び出します。このAbstract Record は直接オブジェクトストアにて見ることができません。アトミックアクションのリストの1つからでなければ閲覧できなくなっています。特定のタイプを持つオブジェクトを示すObject ビューからエントリーを選択すると、entrySelected メソッドが呼び出されます。両方のメソッドにてStatePanel を使いオブジェクトステートについての情報を表示します。StatePanel にはStatePanel メソッドに記載されているメソッドが含まれており、この情報の表示がしやすくなっています。

StatePanel メソッド

setInfo(String info)
一般情報を表示します。
setData(String name, String value)
テーブルに情報を入力し、Object Store Browser ツールで表示します。
enableDetailsButton(DetailsButtonListener listener)
Details ボタンを有効にします。リスナインターフェースにより、ボタンを押すとプラグインに通知されるようになっています。開発者はこの情報の表示方法を管理します。
この例では、ステートをオブジェクトストアから読み取りgetValue() メソッドで返された値を使いステートパネルテーブルに入力しています。getType() メソッドは登録に関するプラグインタイプを返します。
このプラグインをObject Store Browser に追加するには、osbv-というプリフィックスのついた名をつけ、JAR ファイルにパッケージします。JARファイルににはマニフェストファイル内に特定の情報を含むことで、クラスにプラグインを持つObject Store Browser に通知する必要があります。Apache Ant スクリプトを利用してこれを実行するには、例13.2「Ant でプラグインをパッキング」を参照してください。

例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>
マニフェストファイルにて正しい情報を用いてJAR を作成した後、 bin/tools/plugins ディレクトリに置きます。

第14章 Transactional Objects for Javaを利用するアプリケーションの構築

14.1. アプリケーションの構築

JBoss Transaction Service アプリケーションの開発は主に2つのフェーズから構成されています。まず、クラス開発者が新規クラスを記述しますが、これらのクラスは永続性があるか、回復可能であるか、あるいは同時制御されている必要があります。次にアプリケーション開発者がアプリケーション内に作成したクラスを使います。これらの開発者は同一人物の場合もありますが、それぞれの役割で違った懸念が出てきます。クラス開発者は、適切なsave_state および restore_state メソッドを開発するだけでなく、操作に適切なロックを設定し、見合ったJBoss Transaction Service クラスコンストラクタを呼び出すことに焦点をおく必要があります。反対にアプリケーション開発者が気を使うのは、特にアトミックな (atomic) アクションあるいはトランザクションの利用などの面でアプリケーションの一般的な構造を定義する点です。
本項では、単純なアプリケーション、整数値に対するFIFO Queue クラスを説明します。Queue は双方向連結リスト構造を使い、単一オブジェクトとして実装されています。この例を本書の残りの部分でも利用し、JBoss Transaction Service が提供する様々なメカニズムを説明します。この例は単純ですが、アプリケーションコードに関する豊富な知識を必要とせずにJBoss Transaction Service に対し行える修正点についてすべて説明しています。
本章の例は、アプリケーションが分散されていないという前提で提供されています。分散アプリケーションの場合、コンテキスト情報を暗黙的あるいは明示的に伝播する必要があります。

14.1.1. キューの説明

キューは従来のFIFO キューで、一番前の要素を追加し、最後から削除します。キュークラスが提供するオペレーションにより、キューに値を置き (enqueue) 、キューから削除(dequeue)できるだけでなく、キューにある要素値を変更あるいは確認することができます。この実装例では、アレイを使いキューを表現しています。この例では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 既存の永続オブジェクトを利用

既存の永続オブジェクトを利用するには、永続オブジェクトのUid を取る必要のある特別なコンストラクタを使う必要があります。
	  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_staterestore_statetype メソッド

例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 操作

キュークラスの操作がアトミックなアクションの場合、ガイドラインのように例14.8「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();
キューの操作の1つを呼び出す前に、クライアントは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「設定オプション」 では設定オプションと可能な値を示しています。各オプションに関する詳細は本書内の該当セクションを参照してください。

表15.1 設定オプション

名称
詳細
com.arjuna.ats.arjuna.objectstore.storeSync
ON/OFF
オブジェクトストアの同期を有効化あるいは無効化。慎重に利用のこと。
com.arjuna.ats.arjuna.objectstore.storeType
ShadowStore
ShadowNoFileLockStore
JDBCStore
HashedStore
利用するオブジェクトストア実装の種類を指定
com.arjuna.ats.arjuna.objectstore.hashedDirectories
整数
HashedStoreオブジェクトストア実装の場合に、ディレクトリ数を設定しオブジェクトステートをハッシュ
com.arjuna.ats.txoj.lockstore.lockStoreType
BasicLockStore
BasicPersistentLockStore
使用するロックストア実装の種類を指定
com.arjuna.ats.txoj.lockstore.lockStoreDir
Windows: .\LockStore
Unix: ./LockStore
ロックストアの場所を指定
com.arjuna.ats.arjuna.objectstore.objectStoreDir
アプリケーションが書き込みできる場所
オブジェクトストアの場所を指定
com.arjuna.ats.arjuna.objectstore.localOSRoot
defaultStore
オブジェクトストアの root 名を指定
com.arjuna.ats.arjuna.coordinator.actionStore
ActionStore
HashedActionStore
JDBCActionStore
使用するトランザクションログ実装
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

本項では、様々なJBoss Transaction Service オブジェクトストア実装について触れており、新規実装の作成方法やアプリケーションで利用方法を説明しています。
JBoss Transaction Service には、基本的なオブジェクトストアの様々種類の実装が含まれています。各実装は、特定の目的用に最適化されています。実装はすべてObjectStore インターフェースから来ており、JBoss Trasaction Service が使用するオブジェクトストア実装に必要最小限の操作を定義しています。com.arjuna.ats.arjuna.objectstore.objectStoreType プロパティを例A.1「オブジェクトストアの種類」にて説明している種類の1つに設定することで、デフォルトのオブジェクトストア実装をランタイム時にオーバーライドすることができます。

例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_COMMITTEDOS_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 クラスにより実装されます。これは、オブジェクトを表現する際にファイルをシャドー版とコミット版のペアで利用しており、シンプルですが動作が遅くなっています。オブジェクトストアとのやりとりの間、ファイルが開かれ、ファイルでロックや操作、ロック解除が行われ、ファイルを閉じます。こうするとファイルの開閉や名前変更だけに厳密に必要とされるリソースよりも多くのリソースが必要となります。
このオブジェクトストアの種類は、ShadowingStoreとなっています。

A.2.2. ファイルレベルのロッキングはなし

トランザクションオブジェクトがLockManagerメソッドで同時制御されているため、ファイルレベルで別途ロッキングの必要がありません。そのため、JBoss Transaction Serviceにおけるデフォルトのオブジェクトストア実装 ShadowNoFileLockStore は、ユーザーレベルのロッキングに依存しており、ShadowingStore 実装よりもパフォーマンスを向上することができます。
このオブジェクトストアの種類は、ShadowNoFileLockStoreとなっています。

A.2.3. Hashed Store

HashedStore 実装は、シャドーストアと同じオブジェクト状態の構造を利用しますが、同タイプのオブジェクトを大量に格納できるよう設計されている別のディレクトリ構造も使用します。オブジェクトの Uid を使うハッシュ機能により、オブジェクトは多くのディレクトリ全体に拡散されています。デフォルトでは、サブディレクトリを 255 個使いますが、HASHED_DIRECTORIES 環境変数を設定することでこれをオーバーライドすることができます。
このオブジェクトストアの種類は、HashedStoreです。

A.2.4. JDBC ストア

JDBCStore実装は、JDBC データーベース内に永続オブジェクトのステートを格納します。Transactional Objects for Java APIと併せてJBDCStoreを利用している場合、ネスト化されたトランザクションもサポートされます。オブジェクトのステートはすべて、単一テーブル内にBinary Large Objects (BLOB)として格納されます。オブジェクトステートのサイズは、64k までに制限されています。この制限を越えたオブジェクトステートを保存しようとすると、例外がスローされ、このステートは格納されません。このトランザクションは強制的にロールバックされます。
JDBC オブジェクトストアを利用すると、アプリケーションは、com.arjuna.ats.arjuna.objectstoreパッケージに置かれている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
実装に対する追加の任意情報を渡します。
JDBC オブジェクトストアは最初に、com.arjuna.ats.arjuna.objectstore.jdbcPoolSizeInitialプロパティで定義されているConnections の数をリクエストし、com.arjuna.ats.arjuna.objectstore.jdbcPoolSizeMaximum プロパティにて定義されているもののみ利用します。
使用するJDBCAccessインターフェースの実装は、com.arjuna.ats.arjuna.objectstore.jdbcUserDbAccess プロパティ変数にて設定する必要があります。
このオブジェクトストアの種類は、JDBCStoreです。
JDBCオブジェクトストアは、トランザクションログを管理できます。トランザクションログの実装は、 JDBCActionStoreに設定し、JDBCAccess メソッドはcom.arjuna.ats.arjuna.objectstore.jdbcTxDbAccess プロパティ経由から提供します。デフォルトのテーブル名は、JBossTSTxTableとなっています。

注記

ユーザーオブジェクトストアとトランザクションログの両方に対してJDBCAccess 実装を利用することができます。

A.2.5. Cached Store

キャッシュストアは、ハッシュされるオブジェクトストアを利用しますが、永続バックストアにステートの読み込みや書き込みを即座に実行しません。揮発性のメモリキャッシュにこのステートは保持され、定期的にあるいはキャッシュがいっぱいになると、キャッシュをフラッシュします。ステートデータは障害発生時に失われてしまう可能性があるため、オブジェクトストアに関連付けられている障害のセマンティクスは、通常の永続オブジェクトストアで利用されるセマンティクスと異なります。
このオブジェクトストアの種類は、CachedStoreとなっています。

キャッシュストアの設定オプション

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. はじめに

この添付資料には、JBoss Transaction Service のクイックリファレンスガイドとして最も良く利用されるクラスについての概要が含まれています。分かりやすくするため、クラスのパブリックインターフェースと保護インターフェースのみを提供しています。

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開発

本項は、XML Transactions (XTS) API のJBoss 実装を使い、利用中のWeb Servicesアプリケーションにトランザクショナルサポートを追加し、拡散 Web 環境全体でデータの安全性を守り、かつ信頼できる方法にて他社のアプリケーションとやりとりを行う方法を説明しています。

第16章 はじめに

JBoss Transaction Service のXML Transaction Service (XTS) コンポーネントは、ビジネストランザクションにおいてプライベートとパブリックのWeb Services をコーディネートするサポートをします。そのため、XTS を理解するには、まずWeb Services を熟知し、トランザクションについても理解する必要があります。本章では、XTSを説明し、Web Services 規格を形成する技術について簡単に説明していきます。さらに、本章ではトランザクション技術の基本について一部みていき、さらにWeb Services に応用する方法を説明します。本章で提供している内容は、本書全体で詳しく説明していますが、Web Services については概要情報のみを提供しています。Web Services 作成に詳しくない場合は、ご利用中のWeb Services プラットフォームに関する文書を参照してください。
JBoss Transaction ServiceはWeb Services 向けのトランザクションソリューションとしてXTS コンポーネントを提供しています。ビジネスパートナーはXTS を使うことで複雑なビジネストランザクションを制御かつ信頼できるかたちでコーディネートすることができます。XTS API はWS-CoordinationWS-Atomic TransactionWS-Business Activity 仕様に基づき、トラザクショナルなコーディネーションモデルに対応しています。

XTS に含まれるプロトコル

  • WS-Coordination (WS-C) はIBM、Microsoft、BEA により開発された一般的なコーディネーションフレームワークです。
  • WS-Atomic Transaction (WS-AT) とWS-Business Activity (WS-BA) には、このフレームワークを使うWS-Transaction (WS-T) のトランザクションプロトコルが含まれます。
JBoss Transaction Service は、これら3つの仕様のバージョン1.0、1.1、1.2を実装し、バージョン仕様についてはhttp://www.oasis-open.org/specs/から入手できます。

注記

1.0、1.1、1.2仕様は、それぞれ少しずつ違っています。本書の他の部分は、コード例と説明を提供しているところではこれらの仕様のバージョン1.1を使っています。1.1仕様に適用する必要のある修正が明確でない場合は、注釈が提供されています。
Web Services はモジュラー形式で再利用可能なソフトウェアコンポーネントで、Web サービスインターフェースを使い公開ビジネス機能により作成されています。Web Services は、SOAPやHTTPなどの規格ベースの技術を使い他のWeb Services と直接通信します。これらの規格ベースの通信技術により、ハードウェアのオペレーティングシステムやプログラミング環境に依存せずに顧客、サプライヤ、取引相手がWeb Servicees にアクセスできるようになります。結果、現在のEDIやbusiness-to-business (B2B) ソリューションと比較すると連携環境が大幅に改善されており、ビジネスが今後のビジネスアプリケーションを外部のパートナーが簡単に見つけ、アクセスできるようなWeb Services として公開することができる環境となっています。
Web Services だけでは耐障害性に優れているとは言えません。実はWeb Services モデルが魅力的な開発ソリューションである理由の中で、サービスベースのアプリケーションの欠点となっているものもあります。

Web Services のプロパティ

  • Web Services として公開されているアプリケーションコンポーネントはサードパーティにより所有されている場合があり、保守コストの面で利点がありますが、その動作に排他的な制御があるかという面では欠点となってしまいます。
  • Web Servcies は通常リモートに置かれており、呼出し時のネットワーク利用が増えるため、障害リスクが増えてしまいます。
依存要件の高いアプリケーションは、アプリケーションがWeb Services を消費する際に発生するエラーからの影響を最低限に抑える方法が必要になります。このような障害から守る方法には、トランザクションのコンテキスト内でアプリケーションのWeb Servcies とやりとりを行うことが挙げられます。トランザクションは最後まで完了させるという作業単位、あるいは障害時に事前の同意した矛盾のないステートへ戻すといった作業の単位です。通常障害時の目的は、このような作業がもともと発生していないかのように見せることです。XTS では、トランザクションは複数のWeb Services に渡り発生しているため、複数のエンタープライズで実施した作業はトランザクショナルサポートで管理されることがあります。

16.1. サービスベース処理の管理

XTS では、複数のWeb Servcies 間にわたる複雑なビジネス処理を行うトランザクションを作成することができます。現在のWeb Services 規格は、ハイレベルのサービスコーディネーション要件には対応していません。これは、1つの request/receive インターラクションを使う現在のWeb Services アプリケーションでは通常、コーディネーションは問題にはなりません。しかし、複数のビジネスパートナーの間で複数のサービスに携わるアプリケーションにとっては、得られたインターラクションのコーディネートや管理は必須となっています。サードパーティのWeb Services とやりとりを行う際に通常、正式な保証の方法がほとんどない点に気づくと、この点はさらに明確になってきます。
XTS は業務プロセス中のサービスをコーディネートするインフラストラクチャを提供しています。トランザクションとしてプロセスを整理することで、ビジネスパートナーは複雑なビジネスインターラクションでも信頼できるかたちで連携することができます。結果、通常1つのデータベースに複数の変更が加えられるような環境で、従来のトランザクション処理エンジンをWeb に直接公開する際のオーバーヘッドや問題なしにデータの整合性を保ちます。An Evening On the Town では、アプリケーションがサービスベースのプロセスをトランザクションとして管理する方法を説明しています。
An Evening On the Town

このアプリケーションではユーザが夜の外出を計画できるようになっており、レストランの予約、ショーのチケット予約などを行います。これらのアクティビティはクレジットカードで支払いをします。この例では、各サービスは別のサービスプロバイダーにより提供された公開Web Servcies を表していますが、XTSを使うことで劇場とレストランサービス間とのやりとりを長期にわたる (場合によっては) ビジネストランザクション1つにまとめます。1つのイベントに問題があれば、ユーザはイベント両方を却下できるようになっているため、両サービスを元のステートに戻します。量イベントが問題なく行われると、ユーザのクレジットカードに課金され、レストラン、ショー両方が予約されます。ご存じのように、両サービス間のやりとりを長期にわたり信頼できるかたちで管理する必要があります。さらに、リモートでデプロイされている複数のサードパーティサービスをすべて管理する必要があります。

トランザクションのバックアップがないと、望まない結果に陥ってしまう可能性があります。例えば、どちらか、あるいは両方の予約に問題があっても、クレジットカードに課金されてしまうなどです。
An Evening On the Town は、XTS が複数の企業間にわたるビジネスプロセス対応に長けている状況について説明しています。本書全体でこの例について詳しくみていき、XTS ディストリビューションの標準例 (ソースコードも含む) として表示します。

16.2. サーブレット

WS-Coordination、WS-Atomic Transaction、WS-Business Activityプロトコルは、従来の同期型リクエスト/レスポンスのRPCスタイルインターラクションではなく、エンティティの一方向インターラクションに基づいています。トランザクションパーティシパントと呼ばれるエンティティの1グループが、リクエストへのレスポンスを返すため、トランザクションコーディネータなど他のエンティティで操作を呼び出します。このプログラミングモデルは、ピアツーピアの関係をもとにしており、最終的にサービスがパーティシパント、コーディネータ、クライアントのいずれであっても、未承諾のメッセージを受信できるアクティブコンポーネント が全サービスにないといけません。
XTS では、JaxWS エンドポイントをデプロイすることで、このアクティブコンポーネントを取得します。SOAP/XMLを使うと到達可能なXTS エンドポイントはそれぞれ、開発者が介入することなくJaxWS 経由で公開されます。唯一必要なのは、アプリケーションサーバーなどのJaxWS エンドポイントをホスト可能なドメイン内にトランザクショナルなクライアントアプリケーションとWeb サービスが存在することです。Enterprise Application Platformの場合、JBoss Application Server はこの機能を提供しています。

注記

XTS 1.0プロトコル実装はサーブレットをベースにしています。

16.3. SOAP

SOAP は、Web Services の領域にXMLベース通信向けのde facto メッセージ形式として登場しました。SOAPは軽量プロトコルで、ユーザがメッセージのコンテンツを定義し、受取側がそのメッセージをどのように処理すべきかについてヒントを提示することができます。

16.4. Web Services Description Language (WDSL)

Web Services Description Language (WSDL)は、Web サービスインターフェース定義に利用するためのXMLベースの言語です。Web サービスを消費するアプリケーションは、サービスのWSDL 文書を解析しサービスの場所、サービスがサポートする操作やプロトコルバインディング (SOAP、HTTPなど)、アクセス方法を検出します。各操作に対し、WSDLはクライアントが従うべき形式を記述します。

第17章 トランザクションの概要

注記

本章ではトランザクショナル Web Services の理論について説明します。この原則をすでにお分かりの場合は、本章は参考程度とお考えください。
トランザクションは、分散システムのパーティ間でのやりとりをコーディネートする有力なパラダイムとして出現しました。特に、共有データへの同時アクセスが必要なアプリケーションを管理することが目的です。JBoss Transaction Service Web Service API の多くは、コンテンポラリトランザクション API をベースにしています。これを理解することで、開発者の生産性は向上し、短期間で習得できます。次項ではトランザクショナル Web Services を構築するにあたって XTS を使用し始める前に知っておくべき必要な情報を提供していますが、すべてのトランザクションの技術に最も信頼できる参照と考えるべきではありません。
典型的なトランザクションとは、完全に成功する作業単位か、部分的に完了した作業で失敗し元の状態に戻す作業単位です。トランザクションがコミットされると、関連付けられた要求によるすべての変更は、通常データーベースに作業結果をコミットすることで永続的になります。トランザクションが失敗しロールバックされると、関連付けられた作業によるすべての変更は元に戻ります。分散システムのトランザクションでは通常、トランザクションの一部であるすべてのパーティシパントをまとめる役割があるトランザクションマネージャを使用しなければなりません。
XTS を使用したトランザクショナルな Web Services の使用および定義に関わる主要コンポーネントについては 図17.1「XTS トランザクションに関わるコンポーネント」 で図解されています。
XTS トランザクションに関わるコンポーネント

図17.1 XTS トランザクションに関わるコンポーネント

17.1. コーディネータ

あらゆるトランザクションはコーディネータと関連付けられています。コーディネータはトランザクションの結果を管理します。クライアントは Web Service トランザクションを開始すると、create 要求をコーディネーションサービスにポストします。これによりコーディネータが作成され、クライアントにその詳細を返します。このサービスは独自のコンテナに配置されるか、アプリケーションクライアントあるいはトランザクショナルな Web Services のひとつと共に配置され、パフォーマンスを向上させます。コーディネーションサービスは通常多くのトランザクションを並行して管理する役割を果たすため、各コーディネータは一意のトランザクション識別子により特定されます。
コーディネータの役割は、クライアントにより呼び出された Web サービスが一貫性のある結果に必ず到達できるようにすることです。クライアントがコーディネータにトランザクションを完了するよう指示すると、コーディネータは各 Web サービスが確実にトランザクションのスコープ内で行ったすべての仮の変更を確認する準備ができているようにします。次にそれらの変更を永久的にするよう指示します。いずれかの Web サービスが確認段階で問題を指摘した場合は、コーディネータはすべての Web サービスが仮の変更を拒否するようにし、トランザクションが開始する前の状態に戻します。また、クライアントがトランザクションをキャンセルするよう指示するとコーディネータはすべての変更を取り消します。
コーディネータまたは Web サービスのひとつがトランザクション中にクラッシュする場合でも、コーディネータと Web サービス間の交渉はまとめられ、すべてのサービスが変更を永久的なものにするか、またはすべてが前の状態に戻るようにします。

17.2. トランザクションコンテキスト

トランザクションが多くのサービスにまたがるためには、サービス間で特定の情報が共有される必要があります。これによりトランザクションに関する情報を伝播します。この情報は コンテキスト と呼ばれます。コーディネーションサービスは、トランザクションを開始するとアプリケーションクライアントにコンテキストを手渡しで返します。クライアントがトランザクショナルな Web サービスを呼び出すと、常にこのコンテキストが追加の隠しパラメータとして渡されます。XTS 実装は自動的にこのコンテキストを保存して伝播するため、クライアントの一部から必要なのは最小限の関与だけです。ただし、コンテキスト内にキャプチャされている情報を理解することは、なお役立ちます。この情報は コンテキストのコンテンツ に記載されています。

コンテキストのコンテンツ

トランザクション識別子
個々のトランザクションにグローバルな一意性を保証します。
トランザクションコーディネータの場所
パーティシパントが登録するためにコンタクトするエンドポイントアドレスです。
Web Services とコンテキストフロー

図17.2 Web Services とコンテキストフロー

17.3. パーティシパント

コーディネータは各トランザクショナルなサービスがどのように実装されているか詳細を知ることはできません。実際、この知識はコーディネータがトランザクショナルな結果を交渉するために必要なわけでもありません。コーディネータはトランザクション内でパーティシパントとして役目を果たす各サービスを扱い、トランザクションのタイプに該当する事前定義されたパーティシパントのコーディネーションモデルに従って通信します。Web サービスが所定のトランザクションで最初のサービス要求を受け取ると、パーティシパントとしてコーディネータを登録し、沿うパーティシパントモデルを指定します。コンテキストには登録要求を処理するコーディネーションサービスのエンドポイントに対する URL が含まれています。そのため、パーティシパントという用語はただ特定のパーティシパントモデルを使用した固有のトランザクションで登録されたトランザクショナルなサービスを指しているだけです。

17.4. ACID トランザクション

これまでシステムを処理するトランザクションは ACID プロパティに対応しています。ACID は Atomic, Consistent, Isolated, and Durable の頭字語です。ACID プロパティ で記載されているとおり、作業単位は従来 ACID プロパティが管理される場合にのみトランザクショナルと考えられていました。

ACID プロパティ

アトミック性
トランザクションは完全に実行されるか、あるいは全く実行しません。
一貫性
トランザクションの結果、基礎となるデータ構造の内部一貫性を保持します。
独立性
まるでトランザクションそれだけで実行していて、他には実行しているトランザクションがなく、他のトランザクションから見えません。
持続性
トランザクションの結果は障害が発生しても失われません。

17.5. 2 相コミット

典型的な 2 相コミットの方法は JBoss Transaction Service の基盤であり、さらに一般的には Web Services トランザクションです。2 相コミットはトランザクションに関わるパーティをコーディネートします。2 相コミットトランザクションの一般的な流れは 図17.3「2 相コミットの概要」 を参照してください。
2 相コミットの概要

図17.3 2 相コミットの概要

注記

2 相コミットトランザクションの間に、コーディネータとリソースが非揮発性データストアのアクティビティを追跡することで、障害が発生しても回復できます。

17.6. 同期プロトコル

2 相コミットプロトコルに加えて、従来のトランザクション処理システムは追加のプロトコルを使用します。多くの場合、同期プロトコル と呼ばれます。もともとの ACID プロパティにより、持続性が重要なのは障害発生にも関わらず状態変更が利用できなければならない場合です。アプリケーションはデーターベースなど何らかの永続性ストアと連携します。ディスクアクセスはコンピュータのメインメモリにアクセスするよりはるかに遅いため、この連携は相当なオーバーヘッドを課す可能性があります。
ディスクアクセス時間の問題の解決策のひとつとして、メインメモリに状態をキャッシュし、トランザクションの期間にキャッシュのみで動作することです。残念ながら、この解決策にはトランザクションが終了する前に永続ストアに状態をフラッシュバックする方法が必要です。そうしなければ、ACID プロパティ全体を失うリスクを負うことになります。これは同期プロトコルが 同期パーティシパント と行うことです。
トランザクションがコミット寸前であることが同期に知らされます。この時点でキャッシュされた状態をトランザクションのコミット前に永続的な表現にフラッシュします。これはアプリケーションのパフォーマンスを向上するために使用されることがあります。次に、トランザクションが完了した時およびその完了状態が同期に知らされます。

手順17.1 同期により作成された「4 相プロトコル」

実質的に同期は 2 相コミットプロトコルを 4 相プロトコルに変えます。
  1. ステップ 1

    トランザクションが 2 相コミットを開始する前に、すべての登録された同期は通知されます。この時点で障害が発生すると、トランザクションはロールバックされます。
  2. ステップ 2 と 3

    次に、コーディネータは通常の 2 相コミットプロトコルを実行します。
  3. ステップ 4

    トランザクションが終了した時点で、すべての登録された同期は通知されます。ただし、この段階で発生する障害はすべて無視されるためこれは任意の呼び出しです。つまりトランザクションは終了し、影響が及ぶものは何もありません。
同期プロトコルには従来の 2 相コミットプロトコルと同じ障害要件はありません。例えば、同期パーティシパントには障害が発生した場合のリカバリ機能は必要ありません。その理由は、2 相コミットプロトコルの完了前のすべての障害によってトランザクションはロールバックされ、完了後の障害は同期パーティシパントが関与するデータには影響を及ぼさないためです。

17.7. プロトコルの最適化

パフォーマンスや障害回復に影響を及ぼすことがあるため知っておくとためになる標準 2 相コミットプロトコルのバリアントが数点あります。それぞれのバリアントの詳細情報は 表17.1「2 相コミットプロトコルのバリアント」 を参照してください。

表17.1 2 相コミットプロトコルのバリアント

バリアント
詳細
Presumed Abort
トランザクションがロールバックする場合、コーディネータはこの情報をローカルに記録しすべての登録されたパーティシパントに伝えることができます。パーティシパントに連絡が取れなかった場合はトランザクションの結果に影響はありません。コーディネータは礼儀としてパーティシパントに通知しているだけです。すべてのパーティシパントと連絡が取れた時点で、トランザクションに関する情報は削除することが可能です。トランザクションのステータスに関する後続要求が発生すると、使用できる情報はなく、要求側はトランザクションが中断したと仮定できます。この最適化の利点は、トランザクションが prepare フェーズの終わりまで進みコミットすると決定するまで、パーティシパントに関する情報は永続的にする必要がない点です。この時点より前のすべての障害はトランザクションの中断と見なされるためです。
One-Phase
単一のパーティシパントのみがトランザクションに関わっている場合、コーディネータはトランザクションを prepare フェーズで通過させる必要はありません。従って、パーティシパントはコミットするよう指示され、コーディネータはその決定に関する情報を記録する必要はありません。トランザクションの結果はパーティシパントの責任であるためです。
Read-Only
パーティシパントが準備するよう指示されると、制御する情報やデータがトランザクションの間に修正されていないことをコーディネータに示します。そうしたパーティシパントはトランザクションの結果について通知される必要はありません。パーティシパントの結果はトランザクションに影響がないためです。そのため、read-only のパーティシパントはコミットプロトコルの第 2 フェーズから省くことができます。

注記

WS-Atomic Transaction プロトコルは 1 相コミットの最適化に対応していません。

17.8. 非アトミックトランザクションとヒューリスティックな結果

アトミック性を保証するために、2 相コミットプロトコルは ブロッキングしています。障害回復のメカニズムが存在していても、障害の結果として、パーティシパントは不定期間ブロックされた状態でいることができます。アプリケーションとパーティシパントの中にはこのブロッキングを許容できないものもあります。
このブロックする性質を打破するためには、prepare フェーズを過ぎたパーティシパントはコミットまたはロールバックするか自発的に決定できます。そうしたパーティシパントが最終的に当初のトランザクションを完了する要求を行う場合にそうできるようにするため、決定を記録する必要があります。コーディネータは最終的にはトランザクションの結果をパーティシパントに通知します。結果がパーティシパントが決定したものと同じであれば、競合は存在しません。パーティシパントとコーディネータの決定が異なる場合は、その状況は非アトミックな結果、さらに具体的に言うと ヒューリスティックな結果 と呼ばれます。
ヒューリスティックな結果の解決およびアプリケーションへの報告は、通常複雑な手動式システム管理ツールが行う領域です。自動解決の試行には、トランザクションに関わるパーティシパントの性質の意味情報が必要なためです。
パーティシパントがヒューリスティックな決定を行う正確な時は固有の実装によります。同様に、パーティシパントがコミットまたはロールバックするかの決定は実装によって決まり、またアプリケーションやパーティシパント自身がある環境による可能性があります。起こりうるヒューリスティックな結果については 表17.2「ヒューリスティックな結果」 で説明します。

表17.2 ヒューリスティックな結果

結果
詳細
Heuristic Rollback
パーティシパントの一部またはすべてがトランザクションを一方的にロールバックしたため、コミット動作は失敗しました。
Heuristic Commit
すべてのパーティシパントが一方的にコミットしたため試行したロールバック動作は失敗しました。これが発生する可能性がある状況としては、コーディネータがトランザクションの prepare に成功したが、そのトランザクションのログが更新できなかったためトランザクションをロールバックしようと決定する場合です。コーディネータが決定を行う一方で、パーティシパントはコミットするよう決定します。
Heuristic Mixed
一部のパーティシパントはコミットし、他のパーティシパントはロールバックされました。
Heuristic Hazard
一部の更新のディスポジションは既知ではありません。更新が既知の場合は、すべてコミットされるか、すべてロールバックされます。
ヒューリスティックな決定は、例外的な状況でのみ注意して使用されるべきです。トランザクションサービスによる決定とは異なる可能性があるためです。このタイプの違いは、システム内の整合性を失う恐れがあります。ヒューリスティックを発生させないサービスやパーティシパントと連携したり、解決プロセスで役立つトランザクションサービスを使用することにより、ヒューリスティックな解決を行う必要性をなくすようにしてください。

17.9. 割り込み

割り込み はコーディネータの階層全体にトランザクションのコーディネーションを委譲させることができるスコーピングメカニズムです。この概念を図解したものが 図17.4「割り込み」 です。
割り込み

図17.4 割り込み

コーディネーションに必要なネットワークトラフィック量を制限する方法として、割り込みは Web Services トランザクションに特に役立ちます。例えば、ネットワークトラフィックまたは距離が原因でトップレベルのコーディネータと Web サービス間の通信が遅い場合、Web サービスはローカルなコーディネータサービスを使用する下位のトランザクションで実行すると役立つことがあります。図17.4「割り込み」 では prepare するために、トップレベルのコーディネータが必要なことは、1 つの prepare メッセージを下位のコーディネータに送り、1 つの prepared または aborted 応答を受け取ることだけです。下位のコーディネータは prepare を各パーティシパントにローカルに転送し、結果を組み合わせて単一の prepared または aborted 応答を送るかどうか決定します。

17.10. 新しいトランザクションプロトコル

多くのコンポーネント技術は、2 相コミットセマンティックスをベースにした ACID トランザクションをコーディネートするメカニズムを備えています。CORBA/OTS、JTS/JTA、MTS/MSDTC がその一部です。Web Services に ACID が適さない理由 での説明のとおり、ACID トランザクションはすべての Web Services トランザクションに適しているとは限りません。

Web Services に ACID が適さない理由

  • 典型的な ACID トランザクションは、アプリケーションを開発およびデプロイする組織はアプリケーションの全体的なインフラストラクチャを所有すると仮定しています。このインフラストラクチャは従来イントラネットの形式を取っていました。オーナーシップは信頼できる、予測可能な方法でトランザクションが動作することを暗示しています。ACID の性質を確保するために、潜在的に寿命の長いロックは 2 相コミットの間に基礎となるデータ構造で保管することが可能です。リソースはどの期間でも使用され、トランザクションが完了すると解放されます。
    Web Services ではこうした仮定は有効ではなくなりました。明らかな理由としては、Web サービスを通じて公開されたデータの所有者がデータが長期間ロックされることを拒否するためです。そのようなロックを許可すると Dos 攻撃を招きます。
  • すべてのアプリケーションインフラストラクチャは一般的には単一のパーティが所有しています。典型的な ACID トランザクションを使用するシステムは、トランザクション内のパーティシパントはトランザクションマネージャの指示に従い、トランザクション内の他のパーティシパントに悪影響がある一方的な決定を行うのはまれだと通常は仮定しています。
    トランザクションに参加する Web Services はいつでもトランザクションを辞退する決定を効果的に行うことができ、概してサービスの消費者にはこれを防ぐサービス保証の質はほとんど持っていません。

17.10.1. 疎結合システムのトランザクション

ACID プロパティを緩和する拡張トランザクションモデルは数年に渡り提案され続けてきました。WS-T は新しいトランザクションプロトコルを提供し、Web Services アーキテクチャに対してこの概念を実装します。XTS は Web Services のような疎結合アーキテクチャに特有の基礎となる 4 つの要件に対応するように作られています。その要件については Web Services の要件 で説明します。

Web Services の要件

  • トランザクションに対し複数の成功する結果を処理し、操作の結果が独立も持続もできない操作に関わることができること。
  • 自発的なパーティをコーディネートし、その関係が中央設計権限からの命令ではなくコントラクトで管理されている。
  • パーティがライフタイムの間に停止を経験することが予想され、コーディネートした作業がそうした停止から存続できる不連続のサービス。
  • 複数の通信プロトコルに対しXML を使用した相互動作。XTS は HTTP で送られる SOAP エンコーディングを使用します。

第18章 XTSで利用されるプロトコルの概要

本章では、各プロトコル仕様にて定義されているように、WS-Coordination、WS-Atomic Transaction、WS-Business Activity プロトコルに関連する基本的なコンセプトについて見ていきます。これらのプロトコルに関する基本情報は、本書で触れている残りの内容を理解する際に重要になってきます。

注記

WS-Coordination、WS-Atomic Transaction、WS-Business Activityについて熟知している場合は、本章は簡単に読み進めていただくだけで結構です。

18.1. WS-Coordination

一般的に、coordinationは、コーディネータとして知られるエンティティにより行われ、ドメイン固有の理由で、多くのパーティシパントに情報を提供します。この理由には、分散トランザクションプロトコルによる決定でコンセンサスを得るため、あるいは、信頼できるマルチキャスト環境などで全パーティシパントが特定のメッセージを取得するようにするためなどです。パーティがコーディネートされると、情報 (coordination contextと呼ばれる)を伝播し、論理的には調整済みの同作業あるいは同アクティビティの一部である操作を統合します。このコンテキスト情報は通常のアプリケーションメッセージと流されるか、あるいはメッセージ交換の明示的部分となります。これは、実行したコーディネーションタイプに固有のものです。
WS-Coordination (WS-C)を支える基本的なアイデアは、コーディネーションインフラストラクチャがWeb Services 環境にて必要であるという点です。WS-C仕様は、様々なコーディネーションプロトコルをプラグインできるフレームワークを定義しており、図18.1「WS-C Architecture」で示されているようにクライアント、サービス、パーティシパント間の作業をコーディネートします。
WS-C Architecture

図18.1 WS-C Architecture

どのコーディネーションプロトコルを利用し、どのドメインでデプロイされていても、同じような一般的な要件が存在します。

WS-C に対する一般要件

  • 特定のアプリケーションインスタンスについて、特定のコーディネーションプロトコルへ新規コーディネータをインストールあるいはアクティベート
  • コーディネータにパーティシパントを登録し、アプリケーション (アプリケーションの一部) が有効な間にコーディネータのプロトコルメッセージを受信
  • アプリケーションを構成するWeb Services 間でコンテキスト情報を伝播
  • 完了するまでコーディネーションプロトコルを駆動するエンティティ
WS-C に対する一般要件 の最初から3つのポイントは、WS-Cが直接担当しており、4番目はサードパーティのエンティティが管理しています。サードパーティのエンティティは通常、全アプリケーションの中のクライアントコンポーネントとなっています。これら4つのWS-Cの役割および関係は図18.2「WS-Cでの4つの役割」にて説明されています。
WS-Cでの4つの役割

図18.2 WS-Cでの4つの役割

18.1.1. アクティベーション

WS-C フレームワークは、特定のコーディネーションプロトコルに対しコーディネータを作成し、関連コンテキストをリトリーブするActivation Service を公開します。RPC スタイルの交換を使い同期的にActivation Services を呼び出します。そのため、サービスWSDLは、単一のポートを定義し、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> 

注記

1.0 Activation Coordinator サービスは、1方向メッセージ2つから成る非同期メッセージ交換を採用しており、Activation Requester サービスも必要になります。

18.1.2. 登録

Activation Service により返されたコンテキストには、Registration Service のURLが含まれています。Web サービスがトランザクションコンテキストに添付されたサービスリクエストを受け取ると、Registration Service に連絡をし、そのトランザクション内でパーティシパントとして登録します。登録リクエストには、Web サービスがトランザクション内で行いたい役割を定義したパーティシパントプロトコルが含まれています。コーディネーションプロトコルにより、パーティシパントプロトコルを1種類以上の中から選択することができます。
Activation Service のように、Registration Service は同期通信を前提としています。そのため、サービスWSDLは、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> 
このRegistration Service を使いコーディネータにパーティシパントが登録されると、コーディネーションメッセージをコーディネータから受信します。2相プロトコルを使っている場合、通常のメッセージには“prepare to complete” や “complete” などのメッセージが含まれます。コーディネータのプロトコルが対応している場合、パーティシパントもコーディネータにメッセージを返信することができます。

注記

1.0 Registration Coordinator サービスは、一方向メッセージ2つで構成された非同期メッセージ交換を採用しており、Registration Requester サービスも必要となります。

18.1.3. 完了

ターミネータの役割は通常、クライアントアプリケーションが果たします。適切な時点で、クライアントがコーディネータに対し、登録したパーティシパントとともに特定のコーディネーション機能を実行するように依頼し、完了までプロトコルを駆動します。完了後、クライアントアプリケーションはアクティビティの結果を通知される場合があります。この結果は、単純な成功通知や失敗通知からアクティビティの状態の詳細を示す構造データなどの範囲であればあらゆる形式を取ることができます。

18.2. WS-Transaction

WS-Transaction (WS-T) は、トランザクションコーディネーションプロトコルのペア、WS-Atomic Transaction (WS-AT)WS-Business Activity (WS-BA)で構成されており、WS-Coordination (WS-C)で提供されているコーディネーションフレームワークを活用します。
WS-Transactionsは、システムの独自機能に変更を加えることなくお互いの間で信頼性を保ちつつ通信できるように、従来のトランザクション処理システムを統合するために開発されました。

18.2.1. WS-Transaction の基本

図18.3「WS-Coordination、WS-Transaction、WS-Business Activity」にあるように、WS-TransactionはWS-Coordination プロトコル上に階層化されています。
WS-Coordination、WS-Transaction、WS-Business Activity

図18.3 WS-Coordination、WS-Transaction、WS-Business Activity

WS-C は、WS-Transactionなど、特定のコーディネーションプロトコルに対しモジュラー形式で利用する一般的なフレームワークを提供しています。WS-Cはコンテキストを作成しアクティビティをこれらのコンテキストに登録できるようにコンテキスト管理のみを提供しています。WS-Transaction は、WS-C 提供のコンテキスト管理フレームワークを2つの方法で活用します。
  1. WS-Cコンテキストを拡張しトランザクションコンテキストを作成します。
  2. 追加サービス (Completion, Volatile2PC, Durable2PC, BusinessAgreementWithParticipantCompletion, and BusinessAgreementWithCoordinatorCompletion) と2つのプロトコルメッセージセット (WS-Transaction で対応している各トランザクションモデルに対して1つずつ) でアクティベーションおよび登録サービスを強化し、WS-C プロトコルインフラストラクチャ上に本格的なトランザクションコーディネータを構築しています。
  3. 従来のトランザクションプロトコルとは異なる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

WS-Transactionは、ビジネスロジックを実行する役割を果たすトランザクション対応のWeb サービスとトランザクションでパーティシパントとしての役割を果たすWebサービスを区別し、トランザクションコーディネータと通信しレスポンスを返します。図18.4「WS-Transaction グローバルビュー」にあるように、パーティシパントが基盤のWS-Transaction プロトコルに対応している間に、トランザクション対応のWeb サービスは、ビジネスレベルのプロトコルを使いアプリケーションクライアントを処理します。
WS-Transaction グローバルビュー

図18.4 WS-Transaction グローバルビュー

トランザクション対応のWeb サービスは、ビジネスロジックあるいはトランザクションの範囲内で実行する必要のある作業をカプセル化します。トランザクションもコミットしない限り、アプリケーションによりこの作業を確定することはできません。そのため、アプリケーションによる制御は最終的に削除され、トランザクションが制御するようになっています。
パーティシパントは、トランザクションコーディネータの指示のもと、トランザクション対応のWeb サービスが実行した作業結果を制御するエンティティです。図18.4「WS-Transaction グローバルビュー」では、Web サービスはそれぞれ関連するパーティシパント1つと併せて示されており、このパーティシパントはWeb サービスの代わりにトランザクションプロトコルのメッセージを管理します。しかし、図18.5「WS-Transaction Web Services およびパーティシパント」ではWeb サービス1つのクローズアップビュー、および関連づいたパーティシパントと併せてクライアントアプリケーションを表示します。
WS-Transaction Web Services およびパーティシパント

図18.5 WS-Transaction Web Services およびパーティシパント

トランザクション対応のWeb サービスはJDBCドライバ経由でアクセスされるバックエンドのデータベースを採用しており、処理を行うために、データベースにSQLステートメントを送信します。しかし、これらのステートメントは、Web サービスのトランザクションがコミットする場合にのみコミットすべきです。これを機能させるには、Webサービスはトランザクションのブリッジングを採用する必要があります。トランザクションブリッジングは、パーティシパントをこのWeb サービストランザクションのコーディネータに登録し、一致するXAトランザクションを作成し、このトランザクションにてドライバを呼出し一時的な変更をデータベースに加えることができます。Web サービスは、特定のWeb サービストランザクションに紐付いたサービスリクエストが該当するXAトランザクション内で実行されるようにし、特定のトランザクションに共通の変更を分類し、別のトランザクションに属する変更を切り離します。パーティシパントは、同じ操作を基盤のXAトランザクションコーディネータに転送することで、Web サービストランザクションコーディネータから関連づいたprepare、commit、rollbackリクエストに対応します。こうすることで、データベースにおけるローカルの結果がWeb サービストランザクション全体のグローバルな結果と一致するようにしています。
クライアントは比較的単純になっています。APIを使い、クライアントアプリケーションはパーティシパントをトランザクションに登録し、このパーティシパントを使いトランザクション終了の制御を行います。

18.2.3. WS_Transaction モデル

従来のトランザクションモデルはWeb Services には適していないと考えられています。多くの場合、Web サービストランザクションが利用される可能性が高く、特定のプロトコル1つでは十分ではありません。WS-Transaction 仕様では、2種類のモデルを提唱しており、特別なB2Bインターラクションのセマンティクスにも対応しています。
以下で、クライアント、Web サービス、トランザクションコーディネータ間のインターラクションについて触れていますが、多くの場合解説を目的としています。このアクティビティの多くは、裏側で自動的に行われています。トランザクションの開始、完了やパーティシパントの登録、コミットや中断プロセスを駆動するのに利用する実際のAPIについては、 21章XTS APIで説明されています。

18.2.3.1. アトミックトランザクション

atomic transaction (AT)は従来のACIDトランザクションに似ており、ACIDセマンティクスが適しているような短期間のインターラクションに対応するように設計されています。ATの範囲内で、Web サービスは通常ブリッジングを採用し、Web サービストランザクションの制御の下、データベースやメッセージキューなどXAリソースにアクセスできるようにしています。トランザクションが終了すると、パーティシパントはATの結果に関する判断をXA リソースに伝播し、適切なコミットあるいはロールバックアクションがそれぞれのリソースにより実行されます。
全サービスおよび関連のパーティシパントは、ACIDセマンティクスを提供すると考えられており、ACIDが適切な環境や状況でアトミックトランザクションが利用されると推測されます。通常、この環境は、短期で信頼できるドメインとなっています。

手順18.1 アトミックトランザクションのプロセス

  1. アトミックトランザクションを開始するには、クライアントアプリケーションをまず、WS-Transactionに対応するWS-C Activation Coordinator のWeb サービスに設置します。
  2. クライアントは、http://schemas.xmlsoap.org/ws/2004/10/wsatをコーディネータタイプとして指定するWS-C CreateCoordinationContext メッセージをサービスに送信します。
  3. クライアントは、アクティベーションサービスから適切なWS-Transaction コンテキストを受信します。
  4. CreateCoordinationContextメッセージへのレスポンスである、トランザクションコンテキストは、CoordinationType要素がWS-Atomic Transaction名前空間http://schemas.xmlsoap.org/ws/2004/10/wsatに設定されています。また、アトミックトランザクションのコーディネータエンドポイントであるWS-C Registration Service (ここでパーティシパントを登録可能) への参照を含みます。
  5. クライアントは通常次に進みWeb Services を呼出し、Web サービスが変更した内容すべてをコミットするかロールバックすることで、トランザクションを完了します。この完了アクティビティを行うには、クライアントはRegisterメッセージをCoordination Context にてエンドポイントを返されたRegistration Service へ送信することで、自身をCompletionプロトコルのパーティシパントとして登録する必要があります。
  6. 完了するよう登録されると、クライアントアプリケーションはWeb Services とやりとりを行いビジネスレベルの作業を遂行します。ビジネスのWeb サービスを呼び出すと毎回、クライアントは各呼出しが暗黙的にトランザクションによりスコープされているように、トランザクションコンテキストをSOAP ヘッダーブロックへ挿入します。WS-ATomic Transaction対応のWeb Services に対応するツールキットにより、SOAPヘッダブロックにあるコンテキストとバックエンド操作を相互に関連付けることができます。こうすることで、Web サービスが加えた変更がクライアントと同じトランザクションの範囲内で行われ、トランザクションコーディネータによるコミットあるいはロールバックの対象となるようにします。
  7. 必要とされるアプリケーションレベルの作業がすべて完了すると、クライアントは、サービスの状態を永続化する目的でトランザクションを終了することができます。完了パーティシパントは、コーディネータに対し、トランザクションのコミットあるいはロールバックを指示します。コミットあるいはロールバック操作が完了すると、状態はパーティシパントに返され、トランザクションの結果を示します。
完了プロトコルの説明は単純ですが、トランザクションを解決し結果を出すには他のパーティシパントプロトコルにも従う必要があります。
Volatile2pc
1つ目のプロトコルは、任意のVolatile2PC (2PCは2相コミットの略称)となっています。Volatile2PC プロトコルは、前述した同期プロトコルに相当するWS-Atomic Transaction です。通常、Web サービスがトランザクションのコミット前に揮発的な (キャッシュ) 状態をデータベースにフラッシュする必要がある場合に実行されます。これは通常アプリケーションのパフォーマンスを改善するために利用されています。フラッシュされると、2相対応のパーティシパントがこのデータを管理します。
完了パーティシパントがcommit操作を開始すると、prepare メッセージ経由でトランザクションがもうすぐ完了する旨をVolatile2PC パーティシパントに通知します。パーティシパントはpreparedabortedあるいは 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. 1番目のフェーズでは、コーディネータがprepare メッセージを送信すると、パーティシパントはトランザクション中に発生する状態の変更を永続化する必要があります。こうすることでこれらの変更を後でロールバックするか、コミットすることができます。この時点ではアトミックトランザクションがロールバックする可能性があるため、元の状態情報を失うことはできません。パーティシパントがprepareできない場合、コーディネータにabortedメッセージで通知する必要があります。トランザクションは最終的にロールバックされます。また、パーティシパントがトランザクションのデータを何も変更しなかったサービスの対応をしている場合、readonlyメッセージを返し、コミットプロトコルの2番目のフェーズから省略されるようにすることが可能です。そうでない場合は、パーティシパントによりprepared メッセージが送信されます。
  2. 1番目のフェーズで問題が発生しない場合、Durable2PC は2番目のフェーズに進み、ここではコーディネータがcommit メッセージをパーティシパントに送信します。その後パーティシパントは、関連サービスが行った一時的な作業を永続化し、committed メッセージをコーディネータに送信します。問題が発生した場合、コーディネータが全パーティシパントにrollbackメッセージを送信し、関連サービスにより行われた作業を破棄し、この段階に到達していない場合はprepareで永続ストレージに保存された状態情報を削除します。パーティシパントはaborted メッセージをコーディネータに送信することでロールバックに対応します。

注記

WS-Atomic Transaction プロトコルのセマンティクスには、1相コミットの最適化は含まれていません。1つのパーティシパントしか参加していない場合も、完全な2相コミットが常に利用されます。
図18.6「WS-Atomic 2相パーティシパントの状態移行」では、WS-Atomic Transaction の状態移行、およびコーディネータとパーティシパント間のメッセージ交換について説明しています。コーディネータが生成したメッセージは実線で示され、パーティシパントのメッセージは点線を使います。
WS-Atomic 2相パーティシパントの状態移行

図18.6 WS-Atomic 2相パーティシパントの状態移行

Durable2PCプロトコルが完了すると、最初にトランザクションの終了を開始したCompletion プロトコルを完了でき、クライアントアプリケーションにトランザクションがコミットされたか、ロールバックされたか通知することができます。さらに、Volatile2PC プロトコルを完了することができます。
Volatile2PCのprepareフェーズのように、最後のフェーズは任意となっており、このフェーズを使いデータベース接続などリソースを開放できるように、パーティシパントにトランザクションの完了を通知可能です。
コーディネータにより、登録済みのVolatile2PC パーティシパントがトランザクション終了後に呼び出され、トランザクション完了の状態に関する通知を受けます。トランザクションが終了したため、この時点でパーティシパントに問題が発生しても結果に影響がないため無視されます。
個別のプロトコルが複雑に絡み合いAT全体を構成していますが、図18.7「」では、これらの個別プロトコルについて図解で説明しています。

図18.7

18.2.3.2. Business Activities

B2B アプリケーションの多くは、一貫性のある結果を確保し、正しく実行できるようにトランザクションサポートが必要です。これらのアプリケーションは頻繁に、長期間にわたるコンピューティング、疎結合のシステム、データを共有しないコンポーネント、ロケーション、あるいは管理などが関連しており、アトミックトランザクションをこのようなアーキテクチャに組み込むのは困難です。
例えば、オンラインの書店が特定の期間、1個人のために本を予約したとします。しかし、この個人はその本をこの期間内に購入しなかった場合、別の顧客が購入できるよう在庫に並びます。在庫を無限に共有することはできないため、ユーザの視点から見ると、オンラインショップは本を各顧客のために予約しているように見えますが、実際は別の顧客がこの予約を取ることもできます。そして、ユーザは残念ながら、予約した本の在庫がないと気づくことになる場合もあります。
Business Activity (BA)は、リソースを排他的にロックすることが不可能あるいは実用的でないこのような長期的なインターラクションを行うために設計されています。

手順18.3 BA プロセスの概要

  1. サービスは作業を行うように依頼されます。
  2. これらのサービスには、作業のやりなおし機能があり、BAが作業を後にキャンセルすると決定した場合にBAに通知します。BA が問題に直面した場合、このサービスにundo の動作を実行するように指示を出すことができます。
BA にとって重要なのは、サービスの作業実行方法、補正メカニズムの提供方法がWS-BA仕様の責任下にないということです。これはサービスプロバイダに委譲されるのです。
WS-BA は、Web Services ベースのアプリケーションに対するプロトコルを定義し、既存のビジネス処理やワークフローシステムを有効にし、専用のメカニズムをまとめ、実装やビジネス境界全体で同時利用できるようにしています。
WS-ATプロトコルモデルのように、パーティシパントがコーディネータに対してパーティシパントの状態を聞かれたときにのみ通知するわけではなく、BA内の子アクティビティがリクエストを待たずにその結果をコーディネータへ直接指定できます。パーティシパントはアクティビティから退出すること、あるいはどの時点でも問題をコーディネータに通知することができます。当機能は、問題の特定のためにトランザクションの最後まで待機する必要なく、この通知を使い目標の変更を行い処理を進めていくことができるため、タスクが失敗に終わったときに有用です。優れた設計を持つBusiness Activity は事前対応型でなければなりません。
BA プロトコルは補正ベースのトランザクションモデルを採用しています。ビジネスアクティビティのパーティシパントが作業を完了すると、アクティビティから離れるか選択できます。この選択により、その後のロールバックができなくなります。あるいは、パーティシパントがそのアクティビティを完了し、コーディネータに作業が終わり後に別のパーティシパントがコーディネータに問題発生を通知した際に補正可能であるとの合図を送ります。後者の場合、コーディネータは、アクティビティから離れていないパーティシパントそれぞえrに問題補正を依頼し、適切であるとみなした作業を実行する機会を与えます。例えば、パーティシパントが以前に引き落とした銀行口座に入金する場合があります。パーティシパントがすべて問題なしに離れるか完了している場合、コーディネータは完了済みのパーティシパントそれぞれにアクティビティが終了した旨を通知します。
WS-BAの前提条件で説明されている基本的な前提条件3つがこれらすべての基盤となっています。

WS-BAの前提条件

  • 状態の移行は、アプリケーションの状態やコーディネーションメタデータ (送受信メッセージの記録) など、すべて信頼できるかたちで記録されていること
  • リクエストメッセージは、問題をできるだけ早く検出できるようにすべて確認されること。こうすることで、必要のない作業を行わずに済み、問題を早期発見できるため修正時はより簡単でコストも抑えることができます。
  • アトミックトランザクションでは、レスポンス は、リクエストの出力としてではなく別の操作として定義されていること。メッセージの I/O 実装は通常タイムアウトの要件がありますが、BAレスポンスには短かすぎます。レスポンスがタイムアウト後に受信されない場合、レスポンスを受け取るまで繰り返し再送信されます。受信側は、受信したものの中で同一のリクエスト以外はすべて破棄します。
BA モデルには、BusinessAgreementWithParticipantCompletionBusinessAgreementWithCoordinatorCompletionのパーティシパントプロトコルが2つ存在します。コーディネータからパーティシパントへ駆動するATプロトコルとは違い、このプロトコルは反対のアプローチを使います。
BusinessAgreementWithParticipantCompletion
  1. パーティシパントは最初にActive の状態で作成されています。
  2. 作業が終了しBAの範囲内で必要なくなった (例:不変データ上でアクティビティが行われている場合など) 場合、パーティシパントはコーディネータにexitedメッセージを送信することで一方的に終了の決定を行うことができます。しかし、パーティシパントが終了しBAで継続したい場合は、実行してきた作業を補正できなければなりません。この場合、コーディネータに completedメッセージを送信しコーディネータがBAの最終結果を通知するまで待機します。この結果はBAが問題なく完了したことを意味するcloseメッセージ、あるいはパーティシパントは作業をもとに戻す必要がある旨を示すcompensateメッセージのいずれかになります。
BusinessAgreementWithCoordinatorCompletion
BusinessAgreementWithCoordinatorCompletion は、パーティシパントは補正可能な場合でさえ、自立的にBAへの参加を完了するよう決定できるところが、BusinessAgreementWithParticipantCompletionとは違います。
  1. その代わりに、BAを作成したクライアントが完了ステージを駆動し、completed メッセージをコーディネータに送信します。
  2. コーディネータは、completeメッセージを各パーティシパントに送信し、パーティシパントに関連するサービスへこれ以上リクエストを送信しないよう指示します。
  3. パーティシパントは、BusinessAgreementWithParticipantCompletion プロトコルと同じ形式で継続します。
ATモデルと比較してBAモデルの利点は、長期間リソースをロックすることができないサービスが参加できる点です。
完全なACID セマンティクスはBAにより保持されていませんが、補正により整合性を保つことができます。BA制御のもと各サービスの開発者が責任を持って正確な補正アクションを記述するタスクを実行しシステム全体の整合性を保ちます。このような補正は、後方エラーリカバリを使いますが、前方リカバリがより一般的です。
図18.8「」では、WS-BA BusinessAgreementWithParticipantCompletionパーティシパントの状態移行やコーディネータとパーティシパント間のメッセージ交換について説明しています。コーディネータが生成したメッセージは実線で、パーティシパントのメッセージは点線で示されています。

図18.8

図18.9「」 では、WS-BA BusinessAgreementWithCoordinatorCompletionパーティシパントの状態移行やコーディネータとパーティシパント間のメッセージ交換について説明しています。コーディネータが生成したメッセージは実線で、パーティシパントのメッセージは点線で示されています。

図18.9

18.2.4. アプリケーションメッセージ

アプリケーションメッセージ は、パーティ間で送信されるリクエストやレスポンスのことで、これらのパーティがビジネスプロセスの作業を構成しています。このようなメッセージはXTSにとっては不透明とみなされており、必須のメッセージ形式、プロトコルバインディングあるいはエンコーディングスタイルはありません。つまり、適切なWeb Services プロトコルをいつでも利用可能なのです。XTSでは、トランザクションコンテキストがSOAPメッセージのヘッダー内で伝播されます。
XTS は、WS-Transactions 対応サービスをJBoss Enterprise Application Platform 上で構築しているサービス開発者にサポートが提供されています。クライアント、サービスの両方で自動コンテキスト処理を行うためにインターセプタが提供されており、開発が大幅に簡素化されトランザクションのインフラストラクチャへ焦点がずれることなくビジネスロジックの記述に集中することができます。このインターセプタは、メッセージ自体のセマンティクスを変更することなく、コンテキスト要素をアプリケーションメッセージに追加、削除します。WS-C コンテキストで何を行うか把握しているサービスは、これを使うことができます。WS-C、WS-Atomic Transaction、WS-Business Activity を認識していないサービスはこのコンテキストを無視することができます。XTS はユーザが介入することなくコンテキストを管理します。

18.2.4.1. WS-C、WS-Atomic Transaction、WS-Business Activity メッセージ

アプリケーションあるいはサービス開発者はトランザクションインフラストラクチャが交換するメッセージに関心を持たれていない場合が多いですが、基盤モデルがアーキテクチャ全体に組み込むことができるよう、どのような交換が行われているか理解すると便利です。
WS-Coordination、WS-ATomic Transaction、WS-Business Activity 固有のメッセージは、SOAPメッセージを使いHTTP上で配信されます。伝播されるメッセージの種類には、beginprepareなど、標準トランザクション操作の実行指示が含まれています。

注記

XTS メッセージはアプリケーションからのメッセージと干渉せず、アプリケーションは、トランザクション固有のメッセージと同じトランスポートを使う必要がありません。例えば、XTS メッセージは別のメカニズムを使い配信されますが、クライアントアプリケーションはSOAP RPC over SMTP を使いアプリケーション固有のメッセージを配信する場合があります。

18.3. 概要

XTSは、コーディネーションインフラストラクチャを提供しており、別のビジネスにより所有されているサービス間のトランザクションがインターネット上で実行可能になります。このインフラストラクチャはWS-C、WS-Atomic Transaction、WS-Business Activity 仕様に基づいています。これはアトミックトランザクションおよびビジネスアクティビティの2種のトランザクションに対応しており、潜在的な問題に対するトランザクション要件へうまくマッピングするために任意的に組み合わせることができます。単純なトランザクションAPI でこの機能は公開されているため、インフラストラクチャ全体を使うのは簡単です。XTS はアプリケーションやアプリケーションのトランザクション部分を分ける必要のあるものをすべて提供し、システムがトランザクションを利用する際にシステム自体の機能と干渉しないようします。

第19章 はじめに

19.1. XTS Service Archive をJBoss Transaction Service にインストール

JBoss Transaction ServiceのWeb Services コンポーネントであるXTS はEnterprise Application PlatformでホストされているWeb Servicesに対し、WS-ATとWS-BAサポートを提供します。モジュールは $JBOSS_HOME/docs/examples/transactions/にあるService Archive (.sar) としてパッケージされています。インストールするには、手順19.1「XTS モジュールのインストール」に従ってください。

手順19.1 XTS モジュールのインストール

  1. $JBOSS_HOME/server/[name]/deploy/ ディレクトリにjbossxts.sar/と呼ばれるサブディレクトリを作成します。
  2. ZIPアーカイブのSARを新規ディレクトリに展開します。
  3. JBoss Enterprise Application Platformを再起動しモジュールを有効にします。

19.2. クライアントアプリケーションの作成

XTSの利用に際してクライアントアプリケーションには、トランザクション宣言とビジネスロジックといった2つの側面があります。ビジネスロジックには、Web Services の呼出しが含まれています。
トランザクション宣言については自動的にXTS クライアントAPIで処理されます。このAPI は、begincommitrollbackなどの単純なトランザクション命令を提供するため、クライアントアプリケーションはトランザクションを初期化、管理、終了する際に利用できます。内部では、コーディネータを作成しトランザクションを完了させるために、このAPIがSOAPを使って様々なWS-C、WS-AT、WS-BAのサービス上で操作を呼び出します。

19.2.1. ユーザトランザクション

クライアントは、UserTransactionFactoryUserTransaction クラスを使い、WS-ATトランザクションを作成、管理します。これらのクラスは、JTA API と似た方法で機能するシンプルなAPI を提供します。UserTransaction クラスのbegin メソッドを呼び出すことでWS-ATトランザクションを開始し、クライアントスレッドと関連付けます。commit メソッドを呼び出すことでトランザクションをコミットでき、rollbackメソッドでロールバックが可能です。
トランザクションの停止や再開など、より複雑なトランザクション管理はTransactionManagerFactoryTransactionManagerクラスによりサポートされています。
WS-AT APIに関する完全な詳細は、21章XTS APIに提供されています。

19.2.2. Business Activities

クライアントは、UserBusinessActivityFactoryUserBusinessActivityクラスを使いビジネスアクティビティを作成、管理します。UserBusinessActivity クラスのbeginメソッドを呼び出すことで、WS-BA アクティビティを開始し、クライアントスレッドと関連付けます。クライアントは、close メソッドを呼び出すとビジネスアクティビティを終了でき、cancelメソッドでキャンセルが可能になります。
クライアントが呼び出したWeb Services がBusinessActivityWithCoordinatorCompletionプロトコルに登録されると、このクライアントはcloseメソッドを呼び出す前にcompletedメソッドを呼び出し、サービスに現在のアクティビティでサービス呼出しが終了した旨を通知することができます。
ビジネスアクティビティの停止、再開など、より複雑なビジネスアクティビティ管理は、BusinessActivityManagerFactoryBusinessActivityManagerクラスによりサポートされています。
WS-AT APIに関する完全な詳細は、21章XTS APIに提供されています。

19.2.3. クライアント側のハンドラ設定

XTS では、特定のAPIを使ってトランザクショナルなWeb Services で呼出しを実行する場合クライアントアプリケーションを必要としません。クライアントは該当のAPI をどれでも利用でき、SOAPメッセージをサーバーに送信しSOAPのレスポンスを受信できます。クライアント側に課される要件は、以下のみとなっています。
  • Web サービスを呼び出す際に現在のトランザクション詳細をサーバーに転送する必要があります。
  • サーバーからのレスポンスを正しいトランザクションのコンテキストで処理する必要があります。
これを達成するには、クライアントは現在のXTSコンテキストの詳細を送信SOAPメッセージのヘッダーに挿入し、受信メッセージのヘッダからコンテキストの詳細を抽出しこのコンテキストと現在のスレッドを関連づける必要があります。このプロセスを簡素化するには、XTSモジュールは、このタスクを自動で実行できるハンドラを含みます。これらのハンドラはJAX-WSクライアントと機能できるよう設計されています。

注記

ビジネスサービスの呼出しに別のSOAPクライアント/サーバーインフラストラクチャを選んだ場合、ヘッダー処理を提供する必要があります。XTS はインターセプタあるいはJAX-WSのみを提供します。1.0 実装にのみJAX-RPCハンドラは提供されています。

19.2.3.1. JAX-WSクライアントコンテキストハンドラ

JAX-WSクライアント側のコンテキストハンドラを登録するには、クライアントアプリケーションはjavax.xml.ws.BindingProviderjavax.xml.ws.Bindingにより提供されるAPIを使用して、リモートエンドポントを呼び出すためのサービスプロキシ上にハンドラチェーンをインストールします。例についてはsrc/com/jboss/jbosstm/xts/demo/BasicClient.javaファイルにあるアプリケーションクライアント実装例を参照してください。
アプリケーションでデプロイした設定ファイルを使いハンドラを指定することができます。このファイルを特定するには、インターフェースクラスにjavax.jws.HandlerChain アノテーションを添付し、JAX-WSクライアントAPIを宣言します。このインターフェースは通常、Web サービスのWSDLポート定義から生成されます。
JAX-WS クライアントのコンテキストハンドラを登録する際にcom.arjuna.mw.wst11.client.JaxWSHeaderContextProcessor をインスタンス化する必要があります。

19.3. トランザクショナルなWeb Serviceを作成

XTS を使ったWeb サービスの実装は、トランザクション管理とビジネスロジックの2種類から形成されています。
トランザクション管理のアスペクトは、XTS のParticipant API (パーティシパントAPI) を使い、分かりやすく、かつ簡単に実装できるモデルに整理されますが、これは、Web サービスとトランザクションコーディネータ間の交渉に対し構造モデルを提供します。これにより、Web サービスがビジネスロジックのニーズに従い、自身のローカルトランザクションデータを管理できるだけでなく、そのアクティビティが、トランザクションに関連するクライアントとその他のサービスに対応しているようにします。内部では、このAPIがSOAPを使い、様々なWS-C、WS-AT、WS-BAサービスで操作を呼出し、トランザクションが完了するよう駆動します。

19.3.1. パーティシパント

participant (パーティシパント)は、Web サービスの代わりにトランザクションマネージャにより動かされるソフトウェアエンティティです。Web サービスが特定のトランザクションに参加したい場合、パーティシパントが次に行うコーディネータとの交渉にて当サービスに対するプロキシとして機能するように登録しなければなりません。パーティシパントは、登録したトランザクションタイプに適したAPI、および登録時のパーティシパントモデルを実装します。例えば、WS-Atomic Transaction の一部であるDurable2PC パーティシパントは、Durable2PCParticipant インターフェースを実装します。パーティシパントを利用することで、Web サービスのビジネスロジックやプライベートなトランザクションデータ管理の残りとは分けたまま、Web サービスのトランザクション制御管理面をパーティシパント実装に組み込むことができます。
パーティシパントの作成は、Web サービスのバックエンド処理機能のステート、つまり通常企業のITインフラストラクチャに関連付けられたアスペクトを最終的に反映するため、簡単ではありません。実装は、参加するプロトコルに従い、com.arjuna.wst11.Durable2PCParticipantcom.arjuna.wst11.Volatile2PCParticipantcom.arjuna.wst11.BusinessAgreementWithParticipantCompletionParticipant, あるいはcom.arjuna.wst11.BusinessAgreementWithCoordinatorCompletionParticipantインターフェースのいずれかを利用する必要があります。

19.3.2. サービス側のハンドラ設定

トランザクショナルなWeb サービスは、サービス呼出しが適切なトランザクションに含まれているように確認しなければなりません。通常、これはパーティシパントの操作に影響を与えるだけで、残りのWeb サービスの操作には影響を与えません。XTS は、トランザクションクライアントに対する方法とほぼ同じ方法で、このタスクを簡素化しビジネスロジックから切り離します。XTS は、受信SOAPのヘッダからコンテキストの詳細を検知、抽出するハンドラを提供し、Web サービスのスレッドとトランザクションを関連付けます。このハンドラは、SOAPのレスポンスをディスパッチするとこの関係を削除し、コンテキストを送信メッセージのヘッダーに書き込みます。これについては、図19.1「SOAPサーバーに登録されているコンテキストハンドラ」で説明しています。
JAX-WSに対するサービス側のハンドラには2つのバージョンがあります。通常のハンドラは、サービスの呼出し時に受信コンテキストにより特定されたトランザクションを再開し、こサービスの呼出しが完了するとこのトランザクションを停止します。他のハンドラを使いローカルコーディネータの介入を行います。受信した親コンテキストが初めて表示されるときに、ローカルのコーディネータサービスが、Webサービスが呼び出される前に再開する従属トランザクションを作成します。当ハンドラは、同じ親コンテキストでサービスの呼び出しが行われるたびにこの従属トランザクションが再開されるようにします。従属のトランザクションが完了すると、親トランザクションと従属トランザクションの関係はなくなります。

注記

従属サービス側のハンドラはAtomic Transaction の従属コーディネータのみを介入することができます。

注記

JAX-RPC は、1.0実装に対してのみ提供されます。

19.3.2.1. JAX-WSサービスコンテキストハンドラ

JAX-WS サーバー側のコンテキストハンドラをデプロイされたWeb Servicesに登録するには、サーバーエンドポイントの実装クラスにハンドラチェーンをインストールする必要があります。javax.jws.WebServiceでアノテーションが付けられたエンドポイント実装のクラスアノテーションはjavax.jws.HandlerChain アノテーションで補完する必要があります。このjavax.jws.HandlerChain アノテーションでアプリケーションがデプロイしたハンドラ設定ファイルを特定します。例についてはdd/jboss/context-handlers.xmlにあるアプリケーション設定ファイル例と、src/com/jboss/jbosstm/xts/demo/servicesにあるエンドポイント実装クラスを参照してください。
通常のJAX-WSサービスのコンテキストハンドラを登録するときに、com.arjuna.mw.wst11.service.JaxWSHeaderContextProcessor クラスをインスタンス化する必要があります。コーディネータ介入が必要な場合、代わりにcom.arjuna.mw.wst11.service.JaxWSSubordinateHeaderContextProcessorを使います。
SOAPサーバーに登録されているコンテキストハンドラ

図19.1 SOAPサーバーに登録されているコンテキストハンドラ

19.4. 概要

本章は、JBoss Transaction Service のWeb Services トランザクションコンポーネントにて利用される主なソフトウェアについての概要を提供しています。JBoss Transaction Service にて提供されているWeb Services トランザクションマネージャはアーキテクチャのハブで、ソフトウェアの中でユーザーレベルのソフトウェアが直接バインドしない唯一の箇所となっています。XTS は必要な文書処理コードとともに、トランザクションパーティシパントの開発に向けた単純なインターフェースを提供しています。
本章は概要のみで、Web Service のプログラミングの複雑で、繊細な点については対応していません。コンポーネントに関するより完全な説明については、以下の章にも目を通すようにしてください。

第20章 パーティシパント

20.1. 概要

パーティシパントはアプリケーションに関わるビジネスサービスの代わりにトランザクション管理に付随する作業を実行するエンティティです。Web サービス (サンプルコードでは劇場予約システム) には、座席の予約や空席紹介用のビジネスロジックがいくつか含まれていますが、永続的に情報を保持するものからのサポートが必要です。通常、これはデータベースですが、ファイルシステム、NVRAMや他のストレージメカニズムの場合があります。
サービスはバックエンドのデータベースと直接対話をする可能性がありますが、トランザクションが最終的にコミットやロールバックを制御しているため、変更のコミットややり直しはできません。トランザクションがこの制御を実行するには、データベースとやりとりを行う必要があります。図20.1「トランザクション、パーティシパント、バックエンドトランザクションの制御」にあるようにXTS ではパーティシパントがこのようなやりとりを行います。
トランザクション、パーティシパント、バックエンドトランザクションの制御

図20.1 トランザクション、パーティシパント、バックエンドトランザクションの制御

20.1.1. アトミックトランザクション

アトミックトランザクションのパーティシパントは「Durable2PCParticipant」あるいは 「Volatile2PCParticipant」のインスタンスです。

20.1.1.1. Durable2PCParticipant

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は、パーティシパントは最終的なトランザクションの結果をcommitrollbackの呼出しで通知して欲しいという意味です。
  • Abortedは、パーティシパントが中断され、トランザクションも中断を試行しているという意味です。
commit
パーティシパントは、トランザクションのコミットに成功すると必要なクリーンアップアクティビティを実行する必要があります。これらのクリーンアップアクティビティは実装により左右されます。例えば、トランザクション中に変更されたデータのキャッシュバックアップはフラッシュされます。まれにコミット処理が完了できない時がありますがこういう場合は、パーティシパントはSystemException エラーをスローするはずです。これは、トランザクションの結果に影響を与えませんがエラーがログに残されることになります。コミット処理中にクラッシュが発生した場合、このメソッドは呼び出されない可能性があります。
rollback
パーティシパントは、トランザクションが中断されると、必要となるクリーンアップアクティビティを実行する必要があります。ロールバック処理が完了できないことがまれにありますが、こういう場合は、パーティシパントはSystemException エラーをスローするはずです。これはトランザクション結果に影響を与えませんがエラーがログに残されます。コミット処理中にクラッシュが発生した場合、このメソッドは呼び出されません。
unknown
このメソッドは廃止予定でXTSの今後のリリースからは削除されます。
error
揮発性のパーティシパントはリカバリ処理に関わらないため、このメソッドが呼ばれることはありません。

20.1.2. ビジネスアクティビティ

ビジネスアクティビティのパーティシパントは、「BusinessAgreementWithParticipantCompletion」 or 「BusinessAgreementWithCoordinatorCompletion」 で説明されているインターフェースのいずれかのインスタンスです。

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

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

Business Activity プロトコルを正常に機能させるには、パーティシパントが自発的にコーディネータに対して状況の変化について通知できなければなりません。トランザクションが終了した場合、コーディネータがコーディネータとパーティシパント間のインターラクションを開始させるAtomic Transaction プロトコルとは違い、BAParticipantManager インターラクションのパターンでは、ビジネスアクティビティが有効な間であればいつでもパーティシパントがコーディネータと対話できなければなりません。
パーティシパントがビジネスアクティビティに登録された場合は常に、コーディネータに対するハンドルを取得します。このハンドルは、BAParticipantManager メソッドに記載されているメソッドを持つcom.arjuna.wst11.BAParticipantManager インターフェースインスタンスです。

BAParticipantManager メソッド

exit
パーティシパントはexitメソッドを使い、アクティビティから離れていることをコーディネータに通知します。ビジネスアクティビティの終了時、終了方法については通知されます。このメソッドは、パーティシパントがactive な状態 (あるいはParticipantCompletionプロトコルに登録されているパーティシパントの場合はcompleting のステート)である間のみ、呼び出されます。パーティシパントがこれ以外の状態のときに呼び出されると、WrongStateException エラーがスローされます。exit はアクティビティ全体を終了あるいはキャンセル/補正できないようにするのではなく、離れたパーティシパントがアクティビティの完了、終了、補正に関わらないようにするだけです。
completed
パーティシパントの作業は完了しているが、アクティビティ終了時、終了方法が最終的に通知されるようにビジネスアクティビティを継続したい場合などです。パーティシパントは、完了した作業の補正を後に依頼される場合や、アクティビティの終了を知る場合があります。
fault
通常のアクティベーション時にパーティシパントにエラーが発生し、アクティビティの補正を試行しました。fault メソッドは、ビジネスアクティビティを強制的にcancel-only モードにします。障害のあるパーティシパントはアクティビティの完了、終了あるいは補正には関わらないようになります。

20.2. パーティシパントの作成とデプロイメント

パーティシパントは、サービスのトランザクション部分を駆動する配管構成を提供します。本項では、具体的なパーティシパントのプログラミングと用途について見ていきます。

20.2.1. パーティシパントの実装

パーティシパントの実装は比較的わかりやすい作業です。しかし、パーティシパントの管理が必要なトランザクションインフラストラクチャの複雑性によっては、この作業は複雑性や範囲両方において大きく異なります。使用されている実装には、com.arjuna.wst11にあるインターフェースの1つを実装する必要があります。

注記

1.0 プロトコル実装にて利用している該当のパーティシパントインターフェースは、com.arjuna.wstパッケージに置かれています。

20.2.2. パーティシパントのデプロイ

トランザクションWeb サービスおよびトランザクションクライアントは、XTS サービスアーカイブ (SAR)と並行して、アプリケーションサーバーのdeploy ディレクトリに置くことでデプロイされます。SAR は、トランザクションの管理やパーティシパントWeb サービスの登録、管理に必要なクライアントとWeb サービスAPI クラスをすべてエクスポートするようにします。コーディネーションサービスだけでなく、全WS-CやWS-Tコーディネーションサービスの実装を提供します。特に、コーディネータ発の受信メッセージを受け取る際に必要となるクライアントとWeb サービスのパーティシパントエンドポイントを公開します。
通常、トランザクショナルなアプリケーションクライアントや呼び出したトランザクションWeb サービスは、別のアプリケーションサーバーにデプロイされます。XTS SAR がこれらのコンテナのいずれかにデプロイされていれば、XTS は透過的にコーディネーションメッセージをクライアントあるいはWeb services からコーディネータ、またその逆にルーティングします。クライアントがトランザクションを開始すると、デフォルトではローカルコンテナのコーディネーションサービスを使いコンテキストを作成します。コンテキストは、ローカルのRegistration Service に対する参照を保持しているため、トランザクション内に登録されているWeb Services はいずれも、同じコンテナでコーディネーションサービスに登録されることになります。
コーディネータは、クライアントアプリケーションと同じコンテナに置かれる必要はありません。クライアントのデプロイメントを適切に設定するには、Web サービスの1つと共存するコーディネータサービス、あるいは別の専用コンテナにデプロイされているサービスをも使うことができます。別のコンテナに置かれたコーディネータをクライアントに設定する方法については、8章のスタンドアローンコーディネーションを参照してください。

警告

以前のリリースでは、同アプリケーションに同梱されている適切なXTS やTransaction Manager の.jar.war、設定ファイルを使って、XTS アプリケーションはデプロイされていました。このデプロイメントの方法は、Enterprise Application Platform ではサポートされていません。

第21章 XTS API

本章では XTS API について説明します。XTS API を使用することで、トランザクショナルな Web Services を消費し、バックエンドシステムをコーディネートするクライアントやサーバーアプリケーションを記述することができます。

21.1. アトミックトランザクションプロトコルの API

21.1.1. 投票

2 相コミットプロトコルの間、パーティシパントは制御する作業を確認する準備ができるかどうか投票するよう指示されます。パーティシパントは 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

com.arjuna.mw.wst11.TxContext はトランザクションコンテキストのあいまいな表現です。TxContext の戻り値 で記載されているとおり 2 つの可能な値のうちひとつを返します。

TxContext の戻り値

valid
コンテンツが有効かどうか示します。
equals
等値に対して 2 つのインスタンスを比較するために使用できます。

注記

1.0 プロトコル実装で使用される該当のパーティシパントインターフェースは com.arjuna.wst パッケージにあります。

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

getUserTransaction メソッドを呼び出し、UserTransactionFactory から 「UserTransaction」 インスタンスを取得します。

21.1.5. TransactionManager

トランザクショナルな Web サービスと基礎となるトランザクションサービス実装の間のやりとりを定義します。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

getTransactionManager メソッドを使用して、TransactionManagerFactory から 「TransactionManager」 を取得します。

21.2. ビジネスアクティビティプロトコルの API

21.2.1. 互換性

XTS の以前の実装は、com.arjuna.mw.wst パッケージの Business Activity Protocol クラスを検索します。現在の実装ではこれらのクラスは com.arjuna.mw.wst11 パッケージにあります。

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

com.arjuna.mw.wst11.BusinessActivityManager は Web サービスが通常使用するクラスです。Web サービスが基礎となるビジネスアクティビティサービス実装とやりとりする方法を定義します。BusinessActivityManager は特定のアクティビティを表していません。代わりに、スレッドごとの暗黙的なアクティビティへのアクセスを提供しています。

メソッド

currentTransaction
現在のビジネスアクティビティに対して TxContext を返します。 TxContext がない場合は、NULL を返します。戻り値を使用して、複数のスレッドが同じビジネスアクティビティのスコープ内で実行可能なようにできます。currenTransaction メソッドを呼び出すことで、そのアクティビティから現在のスレッドの関連付けの解除はしません。
suspend
すべての現在のビジネスアクティビティからスレッドの関連付けを解除します。これにより特定のアクティビティと関連付けられていない作業を実行できます。suspend メソッドは TxContext インスタンスを返します。これはトランザクションのハンドルです。これでスレッドはどのアクティビティとも関連付けられていません。
resume
TxContext を使用して、ビジネスアクティビティとのスレッドの関連付けまたは再度の関連付けを行います。スレッドの関連付けまたは再度の関連付けの前に、現在関連付けられているすべてのビジネスアクティビティからの関連付けが解除されます。TxContextNULL の場合、suspend メソッドが呼び出されたかのように、スレッドはすべてのビジネスアクティビティとの関連付けが解除されます。

パラメータ

txContext
suspend により返される TxContext インスタンスであり、再開されるトランザクションを特定します。

例外

UnknownTransactionException
TxContext が参照するビジネスアクティビティは呼び出しスレッドのスコープでは無効です。
enlistForBusinessAgreementWithParticipantCompletion
現在のビジネスアクティビティで特定のパーティシパントを登録します。これにより BusinessAgreementWithParticipantCompletion プロトコルに参加することになります。パーティシパントの一意識別子も必要です。
戻り値は BAParticipantManager のインスタンスであり、これを使用してパーティシパントの状態における変更をコーディネータに伝えます。特にパーティシパントは Participant Completion プロトコルに登録されているため、このアクティビティで行うことを期待するすべての作業を完了し、かつすべての変更を永久的にすると、この返されたインスタンスの completed メソッドを呼び出すことが期待されます。代わりに、パーティシパントが補正アクションを実行する必要がなく、例えば他のパーティシパントが失敗すると、返された BAParticipantManager インスタンスの exit メソッドを呼び出すことでアクティビティを終了できます。

パラメータ

participant
インターフェース BusinessAgreementWithParticipantCompletionParticipant の実装です。対応するコーディネータメッセージを受信すると、その closecancelcompensate メソッドが呼び出されます。
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. はじめに

XTS サービスはJBoss Service Archive (SAR) としてデプロイされます。Transaction Serviceが提供するサービスアーカイブのバージョンは、WS-C、WS-AT、WS-BA サービスのバージョン 1.1 を実装しています。XTS サービスアーカイブを再構築し、1.0と 1.1 実装の両方を含め、並行してデプロイすることができます。詳細情報については、サービスアーカイブの構築スクリプトを参照してください。
このリリースのサービスアーカイブは、デプロイ済みのホスト上で動作しているActivation Coordinator サービスからコーディネーションコンテキストを取得します。そのため、ローカルでデプロイされたクライアントアプリケーションが作成したWS-ATトランザクションあるいはWS-BA アクティビティが、クライアントマシンで稼働するRegistration Service を指定するコンテキストとともに提供されます。クライアントにより呼び出されたWeb Services は、クライアントのホストで稼働するTransaction Protocol サービスと調整します。これは、Web Services がローカルあるいはリモートで実行されている場合です。このような設定は、local coordinationと呼ばれています。
全クライアントに対しグローバルにこの設定を再構成することができ、コンテキスト作成リクエストがリモートホストで稼働するActivation Coorinator Service に転送されます。通常、残りのコーディネーションプロセスはリモートホストから実行され、この設定はスタンドアローンのコーディネーション (stand-alone coordination) と呼ばれます。

スタンドアローンのコーディネーションを選択する理由

  • 効率性:クライアントアプリケーションがWeb Service をリモートのEnterprise Application Platform サーバー上で呼出した場合、コーディネータとパーティシパント間のプロトコル固有のメッセージがネットワーク上を移動する必要がないため、リモートサーバーからのトランザクションをコーディネートすることで効率性があがる可能性があります。
  • 信頼性:コーディネータサービスが専用ホスト上で作動している場合、アプリケーションやサービスに障害が起きることでコーディネータへ影響を与え関係のないトランザクションで障害が起こる危険性がありません。
  • 3つ目の理由は、他社ベンダー提供のコーディネーションサービスを利用できるためです。

22.2. Activation Coordinatorの設定

スタンドアローンのコーディネータを設定する最も単純な方法は、Enterprise Application Platform起動時にコマンドラインスイッチを提供することです。-D オプションでシステムプロパティの設定を指定します。-D パラメータでコマンドラインオプションを渡し優先順位に並び替え」で行っているように設定オプションのいくつかを有効にすることができます。

22.2.1. -D パラメータでコマンドラインオプションを渡し優先順位に並び替え

絶対URL

  • プロパティ:org.jboss.jbossts.xts11.coordinatorURL
  • これらのURLに割り当てられた値は、リモートコーディネータホストの設定により変化します。コーディネータが別のJBoss Transaction Service XTS サービスであれば、プロパティ名にリストされたサンプル値は適切です。coord.hostcoord.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.addressjboss.web.bind.port の値は、アプリケーションサーバーのコマンドラインあるいはサーバー設定ファイルのいずれかから取得したサーバーバインドアドレスとWeb サービスリスナポートを表します。

第23章 パーティシパンツのクラッシュリカバリ

トランザクションサービスの主な要件は、パーティシパントを作動するホストやトランザクションコーディネーションサービスを作動するホストによりシステムクラッシュからも回復できることです。トランザクションの停止前あるいはビジネスアクティビティの完了前に発生したクラッシュは比較的簡単に対応できます。トランザクションサービスやパーティシパントは推定アボート (presumed abort) ポリシーを採用することができます。

手順23.1 推定アボートポリシー

  1. コーディネータがクラッシュすると、把握していないトランザクションが無効であると推測し、このようなトランザクションを参照するパーティシパントのリクエストを拒否します。
  2. パーティシパントがクラッシュした場合、暫定的な変更は無視され、コーディネータサービスからのトランザクション準備あるいはビジネスアクティビティ完了リクエストを拒否します。
トランザクションが操作のコミット中あるいは、ビジネスアクティビティを完了し閉じるまでの間であった場合はクラッシュリカバリは複雑になります。トランザクションサービスは、パーティシパントがトランザクションに対してできる限り矛盾のない結果に到達できるようにしなければなりません。
WS-AT トランザクション
トランザクションは、暫定的な変更をすべてコミットするか、トランザクション開始前のステートまですべてをロールバックする必要があります。
WS-Business Activityトランザクション
パーティシパントはすべてアクティビティを終了するか、取り消す必要があり、必要な補正アクションを実行します。
まれに上記が行われない場合、トランザクションサービスはトランザクションに問題があるとのログを残し、レポートします。
XTS は、コーディネータやパーティシパントのホストのいずれか、あるいは両方がクラッシュした場合のWS-ATやWS-BAトランザクションの自動リカバリに対応しています。XTS リカバリマネージャは、XTS サービスが再開されるとコーディネータやパーティシパントホストへの実行を開始します。コーディネータのホスト上で、リカバリマネージャが準備済みでコミットされていないWS-ATトランザクションおよび完了済みで閉じられていないWS-BAトランザクションを検出します。そして、パーティシパントすべてが最初のケースでは回復され、二番目では閉じられるようにします。
パーティシパントホストでは、リカバリマネージャが準備済みでトランザクションロールバックに返答していないWS-ATパーティシパントと、完了済みでアクティビティ取消リクエストに返答していないWS-BA パーティシパントを検出し、WS-ATパーティシパントがロールバックされ、WS-BAパーティシパントが補正されるようにします。このリカバリサービスは、WS-ATコーディネータが介入したホストでクラッシュが発生した場合、従属のWS-ATトランザクションとそのパーティシパントもリカバリ可能です。

23.1. WS-AT リカバリ

23.1.1. WS-ATコーディネータのクラッシュリカバリ

WS-ATコーディネーションサービスは、2相コミットを通してトランザクションが進むにつれ、トランザクションにおける各パーティシパントの状態をトラッキングします。全パーティシパントにprepareメッセージが送られ、パーティシパントすべてがpreparedメッセージを返すと、コーディネータは各パーティシパントの詳細を格納するログ記録を書き込み、トランザクションが完了可能であると示します。この地点以降でコーディネータサービスがクラッシュした場合は、再起動後にログファイルを読み取り、commitメッセージを各パーティシパントに送信することで、2相コミットプロトコルの完了は保証されています。また、全パーティシパントがcommitに対してcommittedメッセージを返答すると、コーディネータはログエントリを何の支障もなく削除することができます。
パーティシパントが返したpreparedメッセージが暫定的な変更をコミットし永続かする準備ができていると示しているため、このタイプのリカバリは安全です。さらに、コーディネータは、クラッシュ前に送信されたコミットメッセージの対応をし複数回クラッシュした場合にメッセージを再送信する必要がありません。XTS のパーティシパント実装はcommitメッセージの再配信にも強くなっています。パーティシパントが「WS-AT パーティシパントのクラッシュリカバリAPI」にあるようなリカバリ機能を実装している場合、同時に両方がクラッシュし、さらにパーティシパントサービスのホストの1つ以上がクラッシュしても、コーディネータはcommit メッセージの配信を保証することができます。
コーディネーションサービスがprepare フェーズ完了前にクラッシュした場合、推定アボートプロトコルにより、確実にパーティシパントがロールバックされます。システムの再起動後、コーディネーションサービスはログ内のエントリがあるため、再起動前にcommitフェーズに入れた可能性のある全トランザクションの情報を持っていることになります。また、再起動後に開始されたアクティブなトランザクションについても把握しています。パーティシパントがprepared メッセージ送信後にレスポンスを待っている場合、等間隔で自動的にprepared メッセージが再送されます。コーディネータがアクティブではないトランザクションを検出し、再起動後にログファイルのエントリがない場合、パーティシパントにアボートするよう指示をだし、Web サービスがトランザクションの代わりに加えられた暫定的なステートの変更をロールバックできるようにします。
Web サービスは、特定の期間レスポンスがない場合タイムアウトするように設定すると、特定のパーティシパントに関連する暫定的な変更を一方的にコミットあるいはロールバックするように決定する場合もあります。この場合、Web サービスはこのアクションを記録し、メッセージを永続ストレージに記録します。パーティシパントがコミットあるいはロールバックするようにリクエストを受け取ると、一方的な決定アクションがリクエストしたアクションと一致しない場合は例外をスローするはずです。コーディネータは例外を検出し、メッセージをログに残しヒューリスティックとして結果をマーキングします。また、管理者が調査、調整できるようにトランザクションのステートをトランザクションログに永久に保存します。

23.1.2. WS-AT パーティシパントのクラッシュリカバリ

パーティシパントが準備を指示される前にWeb サービスのホストマシンがクラッシュした場合、トランザクショナルなWeb サービスに関連しているWS-AT パーティシパントはクラッシュリカバリに関わる必要はありません。コーディネータは、トランザクションを中断されたと推定し、Web サービスは準備がまだのトランザクションに関する情報を破棄することができます。
パーティシパントがprepareするように指示を受けると、Web サービスは永続ストレージにトランザクションのコミットあるいはロールバックが必要なトランザクションのステートを保存するはずです。保存する必要のある特定情報は、Web サービスのビジネスロジックや実装に依存します。しかし、prepareの呼出しからPrepared と返答する前に、パーティシパントはこの状態を保存する必要があります。パーティシパントが必要とされる状態を保存できない場合、あるいはクライアントからのリクエストに対応できない場合、Aborted と返答する必要があります。
Webサービスのホストマシンで稼働するXTS パーティシパントサービスはWeb サービス実装と連携しパーティシパントのクラッシュリカバリを容易化します。これらのパーティシパントサービスは、パーティシパントのpreparecommitrollback メソッドの呼出しを行います。XTS 実装は、登録したパーティシパントすべてのローカルのステートをトラッキングします。prepare の呼出しがPrepared を返す場合、XTS 実装があると、コーディネータにprepared メッセージが転送される前にパーティシパントの状態が確実にローカルのトランザクションログに記録されるようにします。
パーティシパントのログ記録にはパーティシパント、トランザクション、コーディネータを特定する情報が含まれています。パーティシパントが登録され、prepared のステートに駆動されているにも拘らず、この情報があれば、再起動したXTS 実装がアクティブとしてパーティシパントを復帰し、コーディネータとのやりとりを十分に継続できます。ただし、プロセスのコミットあるいはロールバックを継続するにはパーティシパントインスタンスが必要です。
完全なリカバリを行うには、パーティシパントを登録したWeb サービスが必要とする情報がログ記録に含まれている必要になります。この情報があれば、同等のパーティシパントインスタンスを再構築可能になるはずですので、別のWeb Service がprepareに失敗した場合、commit プロセスを完了するまで継続するか、ロールバックすることができます。この情報は、パーティシパントが(Prepared の答えを返す前に永続化された)データの場所を突き止めることのできるString Keyのように単純な場合もあります。しかし、元のパーティシパントインスタンスやWeb サービスが作成した他のオブジェクトを含む直列化されたオブジェクトツリーのように複雑な場合もあります。
パーティシパントインスタンスが関連インターフェースを実装する場合、XTS実装は永続ストレージに書き込む前にこのパーティシパントのリカバリステートをログ記録に添付します。クラッシュ発生時、パーティシパントのリカバリステートはログから取得され、パーティシパントを作成したWeb Service に渡されます。Web Service はこのステートを使い新規パーティシパントを作成し、XTS 実装がこれを使いトランザクションを完了させます。ログ記録はパーティシパントの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 メソッドを呼出してステートを取得し、パーティシパントのログエントリに追記します。
これらのAPIがいずれも実装されていない場合、XTS 実装は警告メッセージをログに残しリカバリのステートを保存せずに進みます。コミット中にWeb サービス用のホストマシンでクラッシュが発生した場合、トランザクションはリカバリできず、ヒューリスティックな結果になってしまう可能性があります。この結果はコーディネータサービスが稼働するホストにログが残されます。
23.1.2.1.2. 再起動時のパーティシパントのリカバリ
Web サービスは、リカバリ処理に参加するためにデプロイ時にXTS 実装に登録し、アンデプロイ時に登録解除する必要があります。org.jboss.jbossts.xts.recovery.participant.atに定義されているXTSATRecoveryManager クラスを利用し登録は行われます。

例23.2 リカバリ用に登録

public abstract class XTSATRecoveryManager {
	. . .
 	public static XTSATRecoveryManager getRecoveryManager() ;
 	public void registerRecoveryModule(XTSATRecoveryModule module);
	public abstract void unregisterRecoveryModule(XTSATRecoveryModule module)
		throws NoSuchElementException;
	. . .
}
Web サービスは、org.jboss.jbossts.xts.recovery.participant.atにあるXTSATRecoveryModuleの実装をregisterunregister 呼出し両方への引数として提供する必要があります。このインスタンスは保存したパーティシパントリカバリ記録を特定し新規のリカバリ済みパーティシパントインスタンスを再作成します。

例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 メソッドを呼び出し、状態の保存時に提供されたバイトアレイからパーティシパントを再作成します。
XTS 実装はどのパーティシパントがどのリカバリモジュールに所属しているか特定できません。リカバリのステートがモジュールのWeb サービスに所属する場合、モジュールはパーティシパントインスタンスを返すだけでよくなっています。パーティシパントが別のWeb サービスにより作成された場合、モジュールはnullを返すはずです。deserialize あるいは recreate に対する引数として提供されたパーティシパントの識別子は、元のパーティシパントがトランザクションに登録された際にWeb サービスが利用した識別子です。リカバリ処理に参加しているWeb Servcies は、パーティシパントの識別子がサービス毎に一意であるよう確認するはずです。モジュールがパーティシパント識別子がWeb サービスに所属していると認識しているがパーティシパントの再作成ができない場合、例外をスローするでしょう。サービスがパーティシパントとビジネスロジック固有のトランザクション情報を関連付けることができない場合にこの状況が発生する可能性があります。
モジュールがシリアル化に依存し、XTS実装が保存したパーティシパントのリカバリステートを作成する場合でも、アプリケーションに再登録する必要があります。deserialization の操作は、Web サービス特有のクラスをローディング可能なクラスローダを使用する必要があります。XTS は、deserialize 操作の責任をリカバリモジュールに委譲することで、この要件を満たします。

23.2. WS-BA リカバリ

23.2.1. WS-BA コーディネータのクラッシュリカバリ

WS-BA コーディネーションサービスの実装は、アクティビティが完了/終了するまでの各パーティシパントの状態をトラッキングします。CoordinatorCompletion のパーティシパントすべてがcomplete メッセージを受信しcompleted メッセージで返答すると、移行ポイントがアクティビティの終了中に発生します。この時点で、ParticipantCompletionパーティシパントはすべてcompleted メッセージを送信しているはずです。コーディネータは各パーティシパントの詳細を格納しトランザクション終了の準備ができていることを示すログを書き込みます。コーディネータサービスがログ記録の書き込み後にクラッシュした場合、close 操作は問題なく行えるよう保証されています。コーディネータはシステムが再起動しclose メッセージを全パーティシパントに送信後ログをチェックします。全パーティシパントがclosed メッセージでclose に返答した後、コーディネータはログエントリを無事に削除することができます。
複数回クラッシュする場合、コーディネータはクラッシュ前に送信されたclose メッセージに対応する必要も、メッセージを再送信する必要もありません。XTS パーティシパントの実装はclose メッセージの再送にも対応しています。以下に説明されているようにパーティシパントがリカバリ機能を実装しているとの前提で、これとパーティシパントのサービスホスト1つ以上が同時にクラッシュした場合でもコーディネータはclose メッセージを確実に送信することができます。
コーディネータサービスがログ記録に書き込む前にクラッシュした場合、明示的に完了したパーティシパントを補正する必要はありません。推定アボートプロトコルが確実に、完了済みパーティシパントすべてにcompensate メッセージが送信されるようにします。
アクティビティがキャンセルされた場合はログ記録を書き込む必要がありません。パーティシパントがcancelcompensate リクエストに返答しない場合、コーディネータは警告ログを残しそのまま継続します。推定アボートプロトコルとパーティシパント主導のリカバリを組み合わせることで、パーティシパントホストがクラッシュした場合でも、適宜全パーティシパントが最終的にキャンセルあるいは補正されるようにします。
完了済みのパーティシパントが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メソッドを呼出しステートを取得し、パーティシパントのログエントリへ追記します。
APIのいずれかが実装されている場合、XTS実装は警告メッセージをログに残しリカバリのステートを保存することなく先に進みます。Web サービスのホストマシンがアクティビティの終了中にクラッシュした場合、アクティビティはリカバリできず、ヒューリスティックな結果がコーディネータのホストマシンにログで残されるでしょう。アクティビティがキャンセルされると、パーティシパントは補正されず、コーディネータのホストマシンは、アクティビティに関するヒューリスティックな結果をログに残すことがあります。

23.2.2.2. 再起動時のパーティシパントのリカバリ

デプロイ時にWeb サービスをXTS 実装に登録し、アンデプロイ時に登録解除する必要があります。そうすることで、リカバリ処理に参加できるようになります。
org.jboss.jbossts.xts.recovery.participant.ba パッケージで定義された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;
    . . .
}
Web サービスは org.jboss.jbossts.xts.recovery.participant.baXTSBARecoveryModule実装をregisterunregister 呼出しへの引数として提供する必要があります。このインスタンスは保存したパーティシパントのリカバリ記録を特定し、新規のリカバリ済みパーティシパントインスタンスを再作成します。

例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つを呼び出します。利用メソッドは、保存パーティシパントがどのプロトコルを実装したかにより変わります。
XTS 実装は、どのパーティシパントがどのリカバリモジュールに属しているかトラッキングしません。モジュールは、リカバリのステートがWeb サービスに属しているとの特定ができるとパーティシパントインスタンスを返すだけであると考えられています。パーティシパントが別のWeb サービスにより作成された場合、モジュールはnullを返すはずです。deserialize あるいは recreate呼出しへの引数として提供されたパーティシパントの識別子は、元のパーティシパントがトランザクションに登録された時にWeb サービスが使用した識別子です。そして、リカバリ処理に参加しているWeb Servcies は、パーティシパントの識別子がサービス毎に一意であるよう確認するはずです。モジュールがパーティシパント識別子がWeb サービスに所属していると認識しているがパーティシパントの再作成ができない場合、例外をスローします。サービスがパーティシパントとビジネスロジック固有のトランザクション情報を関連付けることができない場合にこの状況が発生する可能性があります。
モジュールがシリアル化に依存しXTS実装が保存したパーティシパントのリカバリステートを作成する場合でも、アプリケーションがモジュールの再登録を行う必要があります。deserialization の操作は、Web サービス特有のクラスをローディング可能なクラスローダを使用する必要があります。XTS は、deserialize 操作の責任をリカバリモジュールに委譲することで、この要件を満たします。

付録C 改訂履歴

改訂履歴
改訂 5.1.2-2.4002013-10-31Rüdiger Landmann
Rebuild with publican 4.0.0
改訂 5.1.2-22012-07-18Anthony Towns
Rebuild for Publican 3.0
改訂 5.1.2-100Thu 8 December 2011Russell Dickenson
JBoss Enterprise Application Platform 5.1.2 GA の変更が含まれます。このガイドの内容の変更については、『『リリースノート 5.1.2 (Release Notes 5.1.2)』』を参照してください。

索引

シンボル

1 相コミット
1PC, トランザクションの概要, XTSで利用されるプロトコルの概要
2 相コミット
2PC, トランザクションの概要, XTSで利用されるプロトコルの概要
アクティブなコンポーネント, はじめに
アクティベーション, XTSで利用されるプロトコルの概要
アトミックなトランザクション
atomicity (アトミック性), パーティシパント
コマンドラインのオプション, スタンドアローンのコーディネーション
コンテキストハンドラ, はじめに
コーディネーションコンテキスト, XTSで利用されるプロトコルの概要
コーディネータ, トランザクションの概要
サービス側のハンドラ, はじめに
サーブレット
Java サーブレット, はじめに
スタンドアローンのコーディネーション, スタンドアローンのコーディネーション
デプロイメント, パーティシパント
トランザクション, はじめに, トランザクションの概要
トランザクションコンテキスト, トランザクションの概要
トランザクションサービス, トランザクションの概要
パーティシパント, トランザクションの概要, はじめに, パーティシパント
トランザクションのパーティシパント, はじめに
パーティシパントのリカバリ, パーティシパンツのクラッシュリカバリ
ヒューリスティックな結果, トランザクションの概要
ビジネスアクティビティ, はじめに
ユーザートランザクション, はじめに
リカバリ, パーティシパンツのクラッシュリカバリ
割り込み, トランザクションの概要
同期プロトコル, トランザクションの概要
同期プロトコルの最適化, トランザクションの概要
完了, XTSで利用されるプロトコルの概要
実装, パーティシパント
投票, XTS API
推定アボート (presumed abort) ポリシー, パーティシパンツのクラッシュリカバリ
望ましくない結果, はじめに
登録, XTSで利用されるプロトコルの概要
耐障害性, はじめに
非アトミック, トランザクションの概要

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 (リモートプロシージャコール), はじめに

T

TransactionManager, XTS API
TransactionManagerFactory, XTS API
TXContext, XTS API

U

UserBusinessActivity, XTS API
UserBusinessActivityFactory, XTS API
UserTransaction, XTS API
UserTransactionFactory, XTS API

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 サービス記述言語), はじめに

X

XTS
XML Transaction Service, はじめに
XTS 1.0
XTS 1.1, はじめに

法律上の通知

Copyright © 2011 Red Hat, Inc.
This document is licensed by Red Hat under the Creative Commons Attribution-ShareAlike 3.0 Unported License. If you distribute this document, or a modified version of it, you must provide attribution to Red Hat, Inc. and provide a link to the original. If the document is modified, all Red Hat trademarks must be removed.
Red Hat, as the licensor of this document, waives the right to enforce, and agrees not to assert, Section 4d of CC-BY-SA to the fullest extent permitted by applicable law.
Red Hat, Red Hat Enterprise Linux, the Shadowman logo, JBoss, OpenShift, Fedora, the Infinity logo, and RHCE are trademarks of Red Hat, Inc., registered in the United States and other countries.
Linux® is the registered trademark of Linus Torvalds in the United States and other countries.
Java® is a registered trademark of Oracle and/or its affiliates.
XFS® is a trademark of Silicon Graphics International Corp. or its subsidiaries in the United States and/or other countries.
MySQL® is a registered trademark of MySQL AB in the United States, the European Union and other countries.
Node.js® is an official trademark of Joyent. Red Hat Software Collections is not formally related to or endorsed by the official Joyent Node.js open source or commercial project.
The OpenStack® Word Mark and OpenStack logo are either registered trademarks/service marks or trademarks/service marks of the OpenStack Foundation, in the United States and other countries and are used with the OpenStack Foundation's permission. We are not affiliated with, endorsed or sponsored by the OpenStack Foundation, or the OpenStack community.
All other trademarks are the property of their respective owners.