レベル: 初級 Ivan Dubrov (dubrov@isg.axmor.com), System Architect, Axmor Artem Papkov (artem@us.ibm.com), IT Architect, IBM Jim Smith (jamessmi@us.ibm.com), Senior IT Architect/Consultant, IBM
2006年 2月 21日 この記事に関連して以前紹介した記事(「セキュアなエンタープライズ・インフラをGeronimoで構築する」と「Implement and deploy Web services in an Apache Geronimo application」(英文)では、Apache Geronimoアプリケーション・サーバーでのセキュリティー実装とWebサービスについて検証し、こうした機能を利用してエンタープライズ・アプリケーションが構築できることを示しました。この記事では、IBM Advanced Technology Solutionsチームが、Apache Geronimoでのトランザクション・サポートの概要を説明し、こうした機能を利用することでフェールセーフなトランザクショナル・アプリケーションが構築できることを示します。銀行シナリオを使用した現実的なGeronimoアプリケーションを通して、トランザクションについて学びましょう。
手法について
現代の企業は複雑なインフラを構成しており、データが致命的な役割を担っています。従って、いかなる状況においてもデータの一貫性を維持することが生命線を握っています。Apache Geronimoアプリケーション・サーバーは、小規模から中規模からのエンタープライズ・アプリケーションをサポートしており、また最新のJ2EE(Java™ 2 Platform, Enterprise Edition)仕様をサポートしています。Geronimoアプリケーション・サーバーによるトランザクション・サポートによって、例外条件を処理できるインフラを構成することができます。この記事ではGeronimoアプリケーション・サーバーによるトランザクション・サポートを利用することでデータ整合性が保証できることを示すために、以前の記事、「セキュアなエンタープライズ・インフラをGeronimoで構築する」(developerWorks, 2005年7月)と「Implement and deploy Web services in an Apache Geronimo application」(developerWorks, 2005年11月)で開発された単純な銀行シナリオを機能強化したエミュレーターを使用しました。
この記事で解説するビジネス・シナリオでは、送金動作を実装しています。このアプリケーションにアクセスする人は、自分の口座から他の口座へ送金しようとする銀行顧客(ユーザー)と、すべての銀行トランザクションを監督する監査担当です。最初の記事で開発したオリジナルのアプリケーションを機能強化するために、統計収集メッセージbeanと、エンティティーID生成用のインターリーブド・トランザクションが使われています。メッセージbeanとインターリーブ・トランザクションを使うことによって、IDの生成を別々のトランザクション行うことができるため、IDの固有性が保証されます。
トランザクションの概要
まず、トランザクションとは何かを復習しましょう。『トランザクション』は連続的な操作であって、すべて操作が完全に成功するか、あるいは、もし何らかの失敗があった場合にはシステムを元の状態のままにしなければなりません。トランザクションは、次のような要求をサポートする必要があります。通常、これらの要求は、英語の頭文字を取ってACIDと省略されます。
- 原子性(Atomic): トランザクションにおける全アクションは、完全に成功するか、あるいはまったく完了しないかのいずれかであることが保証される必要があります。
- 一貫性(Consistent):トランザクションが中断しても完了しても、システムの状態は一貫している必要があります。
- 独立性(Isolation): トランザクションが実行される間、そのトランザクションが行う変更は、他のトランザクションから見えてはなりません。
- 耐久性(Durability): トランザクションの結果には耐久性が必要です。
(ただし分散トランザクションに対しては、このリストが必ずしも完全には適用できないことに注意してください。例えば分散トランザクションの場合には、トランザクションがコミットされた後であっても、参加者(participants)の1つによってデータが失われてしまうかも知れません。)
現実には、高パフォーマンスを達成するために、独立性に対する要求は外される場合もあります。データベースの場合では、通常この要求は、トランザクションの独立性レベル属性で置き換えられます。こうしたレベルとしては、次のようなものが定義されています。
- Serializable: 最高レベルの独立性です。このレベルは、オリジナルのACID独立性プロパティーと同等です。
- Repeatable read: トランザクション中、トランザクションは常に同じデータを読み取ります。phantom readが起こる場合もあります。phantom readが発生するのは、トランザクションがクエリーを再実行する際に、前回のクエリーの後で新しいデータをコミットした別のトランザクションによって、一連の結果が変更されていることを見つけた場合です。
- Read committed: トランザクションは、別のトランザクションがコミットしたデータを読み取ることができます。unrepeatable readとphantom readが発生する可能性があります。Geronimoでは、このレベルがデフォルトです。unrepeatable readが発生するのは、トランザクションが一部のデータを再度読み取る際に、そのデータが前回の読み取り後に(別のトランザクションがこのデータを修正し、コミットしたことにより)変更されていることを見つけた場合です。
- Read uncommitted: これは最も弱いレベルの独立性です。unrepeatable read、phantom read、Read uncommittedが発生する可能性があります。
ACIDプロパティーのサポートは、J2EEコンテナーが提供します。Geronimoでは、特定なデータ・ソースに対するトランザクション独立性レベルを変更する方法を提供していないため、GeronimoのEJB(Enterprise JavaBeans)コンテナーでは、常にRead committed属性が使われます。もし、データベース・アクセスに単純なJDBC(Java Database Connectivity)を使用する場合には、#setTransactionIsolationを使って手動で独立性レベルを設定します。
J2EEトランザクション
J2EEコンテナー管理によるトランザクションの場合には、コンテナーがトランザクション境界の決定に責任を持ちます。どのようなトランザクション境界であるかは、デプロイ中にbeanに対して定義されるトランザクション属性によって決定されます。トランザクションをロールバックするには、システム例外(つまり、javax.ejb.EJBException)を投げます。また、javax.ejb.EJBContext#setRollbackOnly() メソッドを呼び出してロールバックすることもできます。コンテナー管理によるトランザクションを利用するためには、beanはEJBデプロイメント記述子の中のトランザクション・タイプ属性を、『コンテナー』に設定する必要があります。
トランザクション属性は、任意のセッション、あるいはエンティティーbeanメソッドに対して規定することができます。EJB 2.1仕様では、次のような属性が定義されています。
- NotSupported: そのbeanメソッドはトランザクションをサポートしていません。もしトランザクションが提供されている場合には、そのトランザクションが無視されます。
- Supports: トランザクションはオプションです。もしトランザクションが提供されていれば、そのトランザクションが使用されます。
- Required: もしクライアントがトランザクションを提供している場合には、そのトランザクションが使用されます。提供していない場合には、新しいトランザクションを生成されます。
- RequiresNew: もしトランザクションが提供されている場合には、そのトランザクションは使用停止されます。そのメソッドの最後で、必ず新しいトランザクションが生成され、コミットされます。
- Mandatory: トランザクションを提供しなければなりません。提供しないと、エラーが生成されます。
- Mandatory: トランザクションを提供してはなりません。提供すると、エラーが生成されます。
通常、トランザクション・サポートが必要な場合には、上記のリストのうち3つの属性のみが使われます。つまりステートレスbeanに対してはRequired、エンティティーbeanに対してはMandatory、新しいトランザクションが必要な場合にはRequiresNewです。他の属性はトランザクション・サポートを提供していない(あるいは禁止している)ため、一般的には使われません。
EJBデプロイメント記述子から引用したリスト1は、送金用のエンティティーEJBに対してトランザクション属性を設定する例です。
リスト1. 通常のエンティティーEJBに対してMandatoryトランザクション属性を設定する
<container-transaction>
<method>
<ejb-name>Counter</ejb-name>
<method-name>*</method-name>
</method>
<trans-attribute>Mandatory</trans-attribute>
</container-transaction>
|
他のEJBタイプ用のコードも、(ステートレスbeanではtrans-attribute属性がRequiredに、ID生成beanではRequiresNewに設定される以外は)似たものになります。
J2EE bean管理のトランザクションの場合は、beanがトランザクション境界の制御に全責任を持ちます。この制御は、javax.transaction.UserTransactionインターフェースのインスタンス上のメソッドを呼び出すことによって転送されます。このインスタンスは、javax.ejb.EJBContext#getUserTransaction() メソッドが返します。
リスト2は、bean管理によるトランザクションの場合でのビジネス・メソッドの実装例です(単純にするために例外処理が省略されていることに注意してください)。
リスト2. ビジネス・メソッドの実装例
UserTransaction tx = context.getUserTransaction();
tx.begin();
try {
// Perform some actions...
} catch(Throwable t) {
// Rollback if any exception is thrown
tx.setRollbackOnly ();
} finally {
tx.commit(); // commit (or rollback if setRollbackOnly() was called)
}
|
手動でトランザクションを制御するには、beanはEJBデプロイメント記述子の中のtransaction-type属性を、『Bean』に設定する必要があります。
インターリーブド・トランザクション
『インターリーブド』トランザクションというのは、あるトランザクションが、別のトランザクションを実行するために使用停止されている状況を意味します。後者のトランザクションがコミットされるか、あるいはロールバックされると、前者のトランザクションが再開されます。
銀行シナリオでの送金動作では、新しく作成されたエンティティーに対するIDの生成は、別のトランザクションで実行されます。これによって、たとえ発生元のビジネス・トランザクションが失敗した場合でも、生成されるIDが固有であることを保証することができます。親トランザクションの実行は、ID生成トランザクションがコミットされるまで中断されます。従って、ID生成に使用されるシーケンス・テーブル中のカウンター値は、いずれにせよ増加され、同じIDが何度も生成されるのを防ぐことができます。このトランザクションの振る舞いは、インターリーブド・トランザクションと言われます。インターリーブド・トランザクションは、デプロイメント記述子の中でbeanメソッドのトランザクション属性がRequiresNewに設定されるとイネーブルになります。
分散トランザクション
『分散』トランザクションには、幾つかの参加者が含まれます。こうした参加者は、ネットワークを介して分散されている場合もあり、あるいはJMS(Java Messaging Service)やデータベース・トランザクションなど、様々なサービスを表現している場合もあります。
銀行シナリオ・アプリケーションでの送金操作では、送金操作が実行されると分散トランザクションが実行されます。このトランザクションには、データベース操作とJMS操作が含まれます。データベース操作は新しい注文行をテーブルに挿入することであり、一方JMS操作は、送金用の情報(送金元の口座や送金先口座、数字など)をJMSトピックに対して公開することです。JMSトピックに公開された情報は、統計情報を収集するために使われます。統計情報は、MDB(message-driven bean)によって集められます。(MDBはトピックをリスンするように作られており 、メッセージを受信すると内部カウンターを増加します。)
送金トランザクションは、上記で定義したACID要求を満足します。ACID要求を満足することによって、新しい送金注文の作成が失敗した場合にはデータがJMSトピックに公開されないことが保証されます。
分散トランザクションをイネーブルにするためには、特別なコンフィギュレーションは必要ありません。分散トランザクション実行の制御は、J2EEコンテナーの責任なのです。
Apache Geronimoをコンフィギュレーションする
Geronimoは、EJBパーシスタンス・エンジン用のフレームワークとして、TranQLを使っています。TranQLは、トランザクションがコミットされるとデータベース操作の実行を管理します。TranQLはデフォルトで、潜在的な外部キー制約とは互換性のない、単純な実行戦略を使用します。
例えば、もし外部キー制約があり、TranQLがデフォルトの実行戦略を使用している場合には、TranQLはデータベース挿入の順序を、外部キーを含む子レコードが最初に挿入されるようにすることができます。つまり、(参照されたキーを持つレコードはまだ挿入されていないため)制約に違反できてしまうのです。
データベース・スキーマがそうした制約を含んでいる場合には、データベース例外を避けるために実行戦略を変更する必要があります。リスト3は、別の実行戦略を選択するOpenEJBデプロイメント記述子(openejb-jar.xml)を示しています。
リスト3. TranQLの実行戦略を変更する
<openejb-jar>
<!-- ...skipped connection factory configuration... -->
<enforce-foreign-key-constraints/>
<!-- ...other deployment information... ->
</openejb-jar> |
この実行戦略を使えば、データベース制約がある場合には、TranQLはデータベース操作の順序を変更することができます。例えばユーザー登録の場合であれば、(ACCOUNTSテーブルがUSERSテーブルを参照しているとすると)TranQLはデータベース操作の順序を変更し、ACCOUNTSテーブルの中に何かを挿入する前にUSERSテーブルにレコードを挿入します。
アプリケーションをテストする
では私達のアプリケーションの中で、トランザクションが実際にどのように動作するのかを見てみましょう。このデモでは、送金の金額が偶数の場合には必ずトランザクションをロールバックするように送金操作は修正されています。エンティティーが既に作成されており、JMSメッセージがトピックにポストされると、値が検証されます。これは検証の実行方法としては効率が良くないかも知れませんが、Geronimoアプリケーション・サーバーのトランザクション・サポートを見るためには適切なのです。
データベースの値を追跡するために、私達はサーブレットによる単純なWebページを開発しました。このページは、IDジェネレーターと統計カウンターの現在の値を示します。図1は、送金前でのアプリケーションの画面(送金操作のリストとカウンターのリスト)をキャプチャーしたものです。
図1.送金前のカウンター
送金操作によって、口座番号4への23,457ドルの送金が無事実行されると、両方のカウンターが1増加することが分かります(図2)。
図2. 送金後のカウンター
そして最後に、口座番号8へ23,458ドルを送金しようとする試みが失敗すると、図3から分かる通り、IDカウンターは増加されますが、JMSメッセージと送金挿入はロールバックされます。統計カウンターは増加されず、新しい注文はデータベースの中には置かれません。
図3. 失敗した送金操作後のカウンター
まとめ
この記事では、Apache Geronimoアプリケーション・サーバーが提供しているJ2EEトランザクション機能を使って、ACID要求を満足するアプリケーションを構築する方法について解説しました。そして、Apache Geronimoアプリケーション・サーバーがJ2EE 1.4仕様を基にしており、高信頼性アプリケーション構築のための機能を提供していることを示しました。
また、Geronimoにはまだ正式な参照ドキュメンテーションが無いことにも触れるべきでしょう。従って、必要な情報を即座に見つけるのは少しばかり困難です。しかし、Geronimoはオープンソースであり、サポート・ドキュメンテーションが無いという問題も、ソースコードが入手できることや、ユーザー・グループのサポートがあることで補うことができるでしょう。
全体として、Geronimoアプリケーション・サーバーは堅牢なJ2EE標準サポートを実証しており、エンタープライズITの中で足場を固めつつあると言うことができます。
参考文献 学ぶために
製品や技術を入手するために
議論するために
著者について  | |  | Ivan Dubrovは、ロシアのNovosibirskにあるIBMのATS(Advanced Technology Solutions)のソフトウェア・エンジニアです。彼はNovosibirsk State Universityにおいて、コンピューター・サイエンスでの(優等)学位を取得しています。昨年は、システム・アーキテクトあるいはソフトウェア・エンジニアとして、5つ以上のATSプロジェクトに参加しました。連絡先はdubrov@isg.axmor.comです。 |
 | |  | Artem Papkovは現在、IBMのClient Innovation Teamのソリューション・アーキテクトであり、顧客やビジネス・パートナーがSOAやWebサービスなど、新興技術を採用するための協力を行っています。1998年にコンピューター・サイエンスの修士としてBelarusian State University of Informatics and Radioelectronicsを卒業後、2000年にノースキャロライナ州Research Triangle ParkにあるIBMに入社しました。これまで経験した分野としては、新興技術を使用したマルチ階層ソリューションのソフトウェア開発や、アーキテクチャー設計、インターネット・ベース・ソリューションの統合などがあります。過去3年間は、顧客と緊密に協力しながら、IBMの戦略的統合技術としてのWebサービスと、統合手法としてのSOA採用促進を進めてきました。彼の連絡先はartem@us.ibm.comです。 |
 | |  | Jim Smithは、ソフトウェア開発に18年以上の経験を持っています。最初の仕事は、カリフォルニア州LivermoreのSandia National Labsにおいて、無数の既存レガシー・コードを使っての高速データ収集システムと分散コンピューティング・システムの設計でした。その後、Java言語に関する深い経験と顧客対応スキルを持つ彼はEmerging Internet Technologiesチームに移動し、IBMの顧客に対してJavaソリューションを現実のものとすることに焦点を当てています。彼はまた、ソフトウェア・サービスと開発のための世界的な組織である、ATS(Advanced Technology Solutions)の設立者の1人です。ATSのミッションは、IBMや、開発研究所、ビジネス・パートナー、顧客などに対して、先進技術と軽量ビジネス・プロセスを開発し、改善し、普及させることによって、標準技術やIBM製品の迅速な採用、展開を図ることです。現在、この組織の運営をJimが行っています。彼の連絡先はjamessmi@us.ibm.comです。 |
記事の評価
|