1.2.3. Transactionサービス

本節ではTransactionサービスを使ったアプリケーションプログラムの作成の方法について説明します。アプリケーションプログラムの作成の説明には、作成の流れにそった説明と実装する機能ごとに分類した説明とがあります。新規にトランザクションを構築する場合は、作成の流れにそって読むことをお勧めします。Transactionサービス上のアプリケーションプログラムは、ORBを使ったアプリケーションプログラムと同じです。ORBを使ったプログラミングの詳細については、プログラミング・開発の「Object Broker」を参照してください。

1.2.3.1. アプリケーションプログラムを開発する前に

本節ではWebOTX Transactionサービス上のアプリケーションプログラムの作成において、必要となる概念の説明を行います。
WebOTX Transactionサービスのトランザクションモデル
WebOTX Transactionサービスは、CORBAトランザクションサービスにしたがって、トランザクションコンテキストを使ってトランザクションの制御・管理を行います。トランザクションコンテキストとは、トランザクションのさまざまな状態を管理する論理的な概念で、 Controlオブジェクトに対応します。
アプリケーションプログラムは、WebOTX Transactionサービスが提供するさまざまなインタフェースを利用してトランザクションコンテキストの状態の更新・参照を行い、トランザクションの動作の制御・管理を行います。
トランザクションコンテキストをアプリケーションが動作するスレッドに関連付けることで、アプリケーションはトランザクション内で動作します。トランザクションコンテキストとスレッドとの関連付けには次の方法があります。
表1.2.3.1-1
関連付けの方法 説明
間接的なコンテキスト管理 Currentオブジェクトや暗黙の伝播により、WebOTX Transactionサービスがトランザクションコンテキストをスレッドに関連付け、管理を行います(一般的にはこちらが利用されます)。
直接的なコンテキスト管理 アプリケーションが Controlオブジェクトとスレッドとの関連付け、管理を行います。
WebOTX Transactionサービスでは、WebOTX Transactionサービスを利用したトランザクションプログラムを次の3つのトランザクションモデルに分類しています。
表1.2.3.1-2
モデル 説明
間接的なコンテキスト管理: クライアントトランザクション クライアントプログラムが Currentオブジェクトの提供するインタフェースを使用してトランザクションの管理を行います。 したがって、クライアントプログラムはトランザクショナルクライアントとなります。 クライアントプログラムはトランザクショナルオブジェクトや、非トランザクショナルオブジェクトを呼び出してトランザクション処理を行います。 OTS1.2では、OTSPolicyにより振る舞いを決定します。 一般的には、サーバプログラムがデータベースを参照・更新するリカバラブルサーバとして動作します。 クライアントプログラム側にデータベースがあっても、サーバプログラム側にデータベースがなくてもかまいません。
間接的なコンテキスト管理: サーバトランザクション サーバプログラムが、Currentオブジェクトの提供するインタフェースを使用してトランザクションの管理を行います。 クライアントプログラムはトランザクションとは無関係な非トランザクショナルクライアントとなります。 このモデルでは、サーバプログラムはトランザクショナルクライアントとなるか、トランザクションとして通信を行わない単独のオブジェクトになります。 したがって、CORBAトランザクションサービスの概念としてはクライアントトランザクションと差はありませんが、サーバオブジェクトはマルチスレッドで動作するため、WebOTX Transactionサービスの管理は変わります。
直接的なコンテキスト管理 アプリケーションが Currentオブジェクトを利用しないで、直接 Controlオブジェクトを利用してトランザクションコンテキストの管理を行います。 この場合、WebOTX Transactionサービスはトランザクションコンテキストの伝播を支援しませんので、明示的な伝播が行われます。 そのため、クライアントプログラム・サーバプログラムの処理の分担はアプリケーションが自由に設定することができます。
OTSPolicyにもとづく振る舞い
CORBA の Object Transaction サービス 1.1 (OTS1.1)では、TransactionalObjectインタフェースの継承有無によりトランザクションコンテキストの伝播を行うか否かを決定していましたが、OTS1.2よりPOAのポリシーにOTSPolicyを設定することによりトランザクションの振る舞いを設定します。
このPOA上で動作するオブジェクトはこの影響を受けます。
OTSPolicy値とサーバ側でのトランザクションの振る舞いの関係は以下の表のとおりです。
表1.2.3.1-3
OTSPolicy Transaction有りで呼び出し Transaction無しで呼び出し
REQUIRES トランザクション内で動作 TRANSACTION_REQUIRED例外
FORBIDS INVALID_TRANSACTION例外 トランザクション無しで動作
ADAPTS トランザクション内で動作 トランザクション無しで動作
OTS1.1でのTransactionalObjectを継承したオブジェクトは、OTSPolicyをADAPTSとした場合に相当し、TransactionalObjectを継承しないオブジェクトはOTSPolicyに何も設定されず、この場合呼び出し時のTransactionの有無にかかわらずトランザクション無しで動作します。
スレッド処理方針とオブジェクト活性化方針
Object Brokerでは、アプリケーションの動作に関する処理方針を利用者が決定することができます。
WebOTX Transactionサービスを使ったアプリケーションの開発において、間接的なコンテキスト管理を使用する場合、スレッドをトランザクションコンテキストに関連付けます。そのため、Object Brokerの方針を利用できない場合があります。
次の表は、方針の利用の可否を示しています。
C++ スレッド処理方針
表1.2.3.1-4
処理方針 サポート 説明
スレッドを使用する プログラマが作成した実装部分をマルチスレッドで実行します。
スレッドを使用しない プログラマが作成した実装部分は必ずシングルスレッドで処理されます。トランザクションに参加させるリソースを実装するResourceオブジェクトやトランザクション完了時のコールバックを実装するSynchronizationオブジェクトはマルチスレッドで処理するようにしてください。


Java スレッド処理方針
表1.2.3.1-5
処理方針 サポート 説明
スレッドを使用する プログラマが作成した実装部分をマルチスレッドで実行します。
スレッドを使用しない プログラマが作成した実装部分は必ずシングルスレッドで処理されます。トランザクションに参加させるリソースを実装するResourceオブジェクトやトランザクション完了時のコールバックを実装するSynchronizationオブジェクトはマルチスレッドで処理するようにしてください。


オブジェクト処理方針
表1.2.3.1-6
処理方針 サポート 説明
スレッドを使用する 指定されたオブジェクトをマルチスレッドで処理します。スレッド処理方針がスレッドを使用する場合のみ有効です。
スレッドを使用しない 指定されたオブジェクトをシングルスレッドで処理します。スレッド処理方針がスレッドを使用する場合のみ有効です。トランザクションに参加させるリソースを実装するResourceオブジェクトやトランザクション完了時のコールバックを実装するSynchronizationオブジェクトはマルチスレッドで処理するようにしてください。


Memo
ResourceオブジェクトやSynchronizationオブジェクトで実装すべきメソッドについては[ リファレンス集 開発編(共通) > 4. CORBA > 4.6 Transactionサービス(C++)]を参照してください。



1.2.3.2. IDLファイルの作成

IDL言語を利用して、サーバアプリケーションが提供するインタフェースを定義します。IDL言語による定義により、C++で作成したサーバアプリケーションをJavaのクライアントから呼び出すなど、アプリケーションプログラムの実装言語を意識することなく通信を行うことができます。
本節では以降のアプリケーションプログラムの開発の説明において利用するIDL定義について記述します。IDLコンパイルの方法については、プログラミング・開発の「Object Broker」もしくは「WebOTX AS CORBAアプリケーション」を参照してください。
/* Test.idl */
 
module Test {
    interface interop {
        void write( in long a, in long b);
        long read( in long a);
    };
};


1.2.3.3. クライアントアプリケーションの開発(C++言語)

本節では、CORBAトランザクションサービスを利用したC++クライアントアプリケーションプログラムの開発手順について、「間接的なコンテキスト管理」を利用するケースについて説明します。「直接的なコンテキスト管理」のケースについては、[ リファレンス集 開発編(共通) > 4. CORBA > 4.6 Transactionサービス(C++)]を参照してください。
間接的なコンテキスト管理では、Currentオブジェクトを使ってトランザクションを管理します。
クライアントアプリケーションプログラムのメイン処理構成
クライアントアプリケーションプログラムのメイン処理部分には、次の処理を記述します。
ORBの初期化 ORBクラスの初期化メソッド(CORBA::ORB_init())を呼び出し、ORBを初期化します。
#include "orb.h"
#include "otxscur.h"
 
CORBA::ORB_ptr orb;
 
try{
    orb = CORBA::ORB_init(argc, argv, "");
}catch(CORBA::Exception& exc){
    exc._dump(stderr);
    return -1;
}
トランザクションサービスの初期化 間接的なコンテキスト管理を行うには、はじめにTransactionServiceクラスのinitメソッドを呼び出すことによりトランザクションサービスを初期化します。トランザクションサービスの初期化は、ユーザプロセスで1回だけ行います。
try{
   
OTXSCur::TransactionService::init(orb,argc,argv);
}catch(CORBA::Exception& exc){
    exc._dump(stderr);
    return -1;
}
サーバプログラムのオブジェクトリファレンスの取得 サーバアプリケーションプログラム側で生成した実装オブジェクトのリファレンスを取得します。
取得方法としては、(1)名前サービスに登録してあるものをresolveして取得する。(2)文字列化したリファレンスを、格納してあるファイルから取り出してstring_to_objectメソッドを使用して取得する。などが考えられます。ここでは(1)の方法を使用した場合について説明します。
Test::interop_var test;
// 名前コンテキストの取得
CosNaming::NamingContext_var namectx;
try {
    CORBA::Object_var nmobj = orb->resolve_initial_references("NameService");
    namectx = CosNaming::NamingContext::_narrow(nmobj);
    if (CORBA::is_nil((CosNaming::NamingContext_ptr)namectx)) {
           return -1;
    }
} catch (CORBA::Exception &exc) {
    exc._dump(stderr);
}
 
// オブジェクトリファレンスの取得
CosNaming::Name objname;
objname.length(1);
objname[0].id = (const char*)"test";
objname[0].kind = (const char*)"";
try {
    obj = namectx->resolve(objname);
    test = Test::interop::_narrow(obj);
} catch (CORBA::Exception &exc) {
    exc._dump(stderr);
}
Currentの取得 Currentオブジェクトは、プロセスに1つだけ存在します。以降Currentオブジェクトが提供するインタフェースを利用することでトランザクションの動作が可能になります。Currentオブジェクトを入手するには、ORBクラスのresolve_initial_referencesメソッドを使用します。
CORBA::Object_var obj;
CosTransactions::Current_ptr current;
try{
    obj = orb->resolve_initial_references("TransactionCurrent");
    current = CosTransactions::Current::_narrow(obj);
}catch(CORBA::Exception& exc){
    exc._dump(stderr);
    return -1;
}
データベースの登録 トランザクション内で使用するデータベースを必要となる数だけ登録します。
OTXSCur::TransactionService trans;
try{
    trans.__register("ORACLE");
}catch(CORBA::Exception& exc){
    exc._dump(stderr);
    return -1;
}
データベースの接続 トランザクション内で利用するデータベースと接続します。
try{
    trans.__open();
}catch(CORBA::Exception& exc){
    exc._dump(stderr);
    return -1;
}
トランザクションの開始 トランザクションを開始します。Currentオブジェクトの beginメソッドをコールすることで、新規トランザクションが生成されます。トランザクションが既に開始されている場合にトランザクションを開始すると、サブトランザクションが生成されます。
try{
    current->begin();
}catch(CORBA::Exception& exc){
    exc._dump(stderr);
    return -1;
}
サーバプログラムの呼び出し 「サーバプログラムのオブジェクトリファレンスの取得」で取得したリファレンスを使用してサーバプログラムの呼び出しを実施します。メソッドは、IDLで定義されたインタフェースをもとにします。
トランザクションコンテキストは、暗黙的にサーバプログラムに伝播されます。
try{
    test->write(a, b);
  }catch(CORBA::Exception& exc){
    exc._dump(stderr);
    return -1;
}
トランザクションの終了 トランザクション処理が終わったら、トランザクションを終了します。Currentオブジェクトの commitメソッドまたは rollbackメソッドをコールすることでトランザクションは終了します。コミットまたはロールバックが完了すると、トランザクションは破棄されます。サブトランザクションが終了した場合、そのサブトランザクションを開始する前のトランザクション(親トランザクション)が再開されます。commitの引数には、2フェーズコミットの完了を待ち合わせるか、待ち合せないかを指定します。
次のトランザクションを開始する場合には、再びCurrentオブジェクトの beginメソッドを呼び出します。
  try{
      current->commit(CORBA_TRUE);
  }catch(CORBA::Exception& exc){
      exc._dump(stderr);
      return -1;
  }
 
または
 
  try{
      current->rollback();
  }catch(CORBA::Exception& exc){
      exc._dump(stderr);
      return -1;
  }
トランザクションサービスの停止 トランザクションサービスを停止するために、TransactionServiceクラスのtermメソッドをコールします。データベース処理を開始していた場合には、トランザクションサービスを終了させる前にデータベースの終了処理をコールします。以降トランザクションサービスを開始しない限り、トランザクション処理を行うことはできません。
  try{
      OTXSCur::TransactionService::term();
    }catch(CORBA::Exception& exc){
      exc._dump(stderr);
      return -1;
  }
クライアントアプリケーションプログラムのコンパイルとリンク
アプリケーションプログラムのコンパイル、リンクを実行する前に次のことを確認します。
Windows版の場合
HP-UX(IPF LP64)版の場合
Linux版の場合
クライアントアプリケーションプログラムの実行
作成したアプリケーションプログラムを実行する前に次のことを確認してください。
クライアントアプリケーションでトランザクションを開始するためには、トランザクションを管理するサーバである、Proxy RCS、もしくはリカバリサーバとObject Brokerがあらかじめ動作している必要があります。
動作していない場合は、運用管理ツール、あるいは運用管理コマンドを利用して起動します。
これらの詳細については、ドメイン構築・基本設定ガイドおよび、ドメイン構築・基本設定ガイドの付録「Transactionサービス(リカバリサーバ利用時)」を参照してください。
その他のトランザクション機能
ここまでの説明で簡単な間接的なコンテキスト管理のプログラムを作成することが可能です。本節では、間接的なコンテキスト管理のその他機能について説明します。
rollback_only(ロールバックのマーク) トランザクション中に障害があったとき、トランザクション生成者はロールバックを行えば良いですが、トランザクション生成者でないとロールバックを発行できません。このような場合にトランザクションが最終的には必ずロールバックされるように設定することができます。トランザクションがロールバックするように設定するには、Currentインタフェースの rollback_onlyメソッドをコールします。
try{
  current->rollback_only();
}catch(CORBA::Exception& exc){
  exc._dump(stderr);
}
get_status(トランザクション状態の取得) トランザクションの状態を取得するには、Currentインタフェースの get_statusメソッドをコールします。
CosTransactions::Status status;
try{
  status = current->get_status();
}catch(CORBA::Exception& exc){
  exc._dump(stderr);
}
get_transaction_name(トランザクション識別名の取得) トランザクションを識別するための文字列を取得するには、Currentインタフェースの get_transaction_nameメソッドをコールします。
char* txname;
try{
    txname = current->get_transaction_name();
}catch(CORBA::exception& exc){
    exc._dump(stderr);
}
set_timeout(トランザクションタイムアウト時間の設定) トランザクションのタイムアウト時間を設定するには、Currentインタフェースの set_timeoutメソッドをコールします。トランザクションの処理が無限ループに入ってしまった場合でも、このインタフェースを利用することで、指定秒数経過後にロールバック処理が自動的に実行されトランザクション処理が戻ります。
set_timeoutはトランザクションを開始するbeginメソッドの前にコールします。
try{
    current->set_timeout(30);
}catch(CORBA::Exception& exc){
    exc._dump(stderr);
}
get_timeout(トランザクションタイムアウト時間の取得) トランザクションのタイムアウト時間を取得するには、Currentインタフェースの get_timeoutメソッドをコールします。
int timeout;

try{
    timeout =current->get_timeout();
}catch(CORBA::Exception& exc){
    exc._dump(stderr);
}
get_control(Controlオブジェクトの取得) Controlオブジェクトを取得するには、Currentインタフェースの get_controlメソッドをコールします。 Controlオブジェクトを使用して明示的なトランザクション処理を行う場合に使います。
Controlオブジェクトの機能については、直接的なコンテキスト管理または[ リファレンス集 開発編(共通) > 4. CORBA > 4.6 Transactionサービス(C++)]を参照してください。
CosTransactions::Control_ptr control;
try{
    control = current->get_control();
}catch(CORBA::Exception& exc){
    exc._dump(stderr);
}
suspend(トランザクションのサスペンド) Currentオブジェクトは、トランザクションをアプリケーション内で動作しているスレッドと対応させて管理しています。そのスレッドからトランザクションコンテキストを切り離す場合には、Currentインタフェースの suspendメソッドをコールします。 resumeまたはトランザクションを新たに開始するまで、スレッドはトランザクションと関連を持ちません。
CosTransactions::Control_ptr control;
try{
    control = current->suspend();
}catch(CORBA::Exception& exc){
    exc._dump(stderr);
}
resume(トランザクションのレジューム) 指定された Controlオブジェクトが示すトランザクションをスレッドに関連付けるには、Currentインタフェースの resumeメソッドをコールします。suspendしたトランザクションを再開する場合や、直接的なコンテキスト管理で入手したトランザクションを間接的なコンテキスト管理で利用する場合に使います。
try{
    current->resume(control);
}catch(CORBA::Exception& exc){
    exc._dump(stderr);
}


1.2.3.4. サーバアプリケーションの開発(C++言語)

本節では、CORBAトランザクションサービスを利用したC++サーバアプリケーションプログラムの開発手順について、「間接的なコンテキスト管理」を利用するケースについて説明します。「直接的なコンテキスト管理」のケースについては、[ リファレンス集 開発編(共通) > 4. CORBA > 4.6 Transactionサービス(C++)]を参照してください。
間接的なコンテキスト管理では、Currentオブジェクトを使ってトランザクションを管理します。
サーバアプリケーションはWebOTX上で動作させることになるため、IDLファイルの作成やアプリケーションのビルド、WebOTXへの登録などは一般的なWebOTX上のアプリケーションの開発を行う際と同じです。この詳細に関しては、プログラミング・開発の「WebOTX AS CORBAアプリケーション」を参照してください。
なお、WebOTX CORBAアプリケーションでTransactionサービスを利用する場合には運用管理ツールでの設定が必要となります。詳細については、ドメイン構築・基本設定ガイドを参照してください。
以下では、間接的なコンテキスト管理に必要なCurrentオブジェクトのサーバアプリケーションでの取得方法について説明します。
取得したCurrentオブジェクトの利用方法については「クライアントアプリケーションの開発(C++言語)」で説明したものと同じですのでそちらを参照してください。
またクライアントアプリケーション作成時に流れで説明したORBの初期化、Transactionサービスの初期化についてはWebOTXのアプリケーション起動時に自動的に実行され、データベースの登録、開始についても運用管理ツールで指定することにより自動的に実行されます。
Currentの取得
Currentオブジェクトを取得するためにはまずORBオブジェクトを取得する必要があります。ORBオブジェクトはTPSGetORB(orb)により取得します。Currentオブジェクトは、ORBクラスのresolve_initial_referencesメソッドを使用して取得します。
CORBA::ORB_ptr orb;
CORBA::Object_var obj;
CosTransactions::Current_ptr current;
try{
    TPSGetORB(orb);
    obj = orb->resolve_initial_references("TransactionCurrent");
    current = CosTransactions::Current::_narrow(obj);
}catch(CORBA::Exception& exc){
    exc._dump(stderr);
    return -1;
}


1.2.3.5. クライアントアプリケーションの開発(Java言語、JavaApplet)

本節では、CORBAトランザクションサービスを利用したJavaクライアントアプリケーションプログラムの開発手順について、「間接的なコンテキスト管理」を利用するケースについて説明します。「直接的なコンテキスト管理」のケースについては、[ リファレンス集 開発編(共通) > 4. CORBA > 4.3 Transactionサービス(Java)]を参照してください。
間接的なコンテキスト管理では、Currentオブジェクトを使ってトランザクションを管理します。
クライアントアプリケーションプログラムのメイン処理構成
クライアントアプリケーションプログラムのメイン処理部分には、次の処理を記述します。
ORBの初期化 ORBクラスの初期化メソッドinitを呼び出し、ORBを初期化します。
Java言語の場合
import org.omg.CORBA.*;

public static void main(String args[]) {
  try {
      // Initialize ORB.
      ORB orb = ORB.init(args, null);
  } catch (Exception e) {
    e.printStackTrace();
  }
  ……
}


JavaAppletの場合
  import org.omg.CORBA.*;
 
  ……
  try {
      // Initialize ORB.
      ORB orb = ORB.init(Applet, null);
  } catch (Exception e) {
      e.printStackTrace();
  }
  ……
}

サーバプログラムのオブジェクトリファレンスの取得 サーバアプリケーションプログラム側で生成した実装オブジェクトのリファレンスを取得します。
取得方法としては、(1)名前サービスに登録してあるものをresolveして取得する。(2)文字列化したリファレンスを、格納してあるファイルから取り出してstring_to_objectメソッドを使用して取得する。などが考えられます。ここでは(1)の方法を使用した場合について説明します。
  Test.interop sv;
  try {
    // 名前コンテキストの取得
    org.omg.CORBA.Object o =
        orb.resolve_initial_references("NameService");
    NamingContext ns = NamingContextHelper.narrow(o);
    // オブジェクトリファレンスの取得
    NameComponent nc = new NameComponent("test", "");
           NameComponent ncseq[] = {nc};
    sv = Test.interopHelper.narrow(ns.resolve(ncseq));
  } catch (Exception e) {
      e.printStackTrace();
  }
Currentオブジェクトの取得 Currentオブジェクトは、プロセスに1つだけ存在します。これによりトランザクションサービスが初期化され、以降Currentオブジェクトが提供するインタフェースを利用することでトランザクションの動作が可能になります。
Currentオブジェクトを入手するには、ORBクラスのresolve_initial_referencesメソッドを使用します。
  org.omg.CosTransactions.Current cur;
  try {
    cur = (Current)orb.resolve_initial_references("TransactionCurrent");
  } catch (Exception e) {
      e.printStackTrace();
  }
トランザクションの開始 トランザクションを開始します。Currentオブジェクトの beginメソッドをコールすることで、新規トランザクションが生成されます。トランザクションが既に開始されている場合にトランザクションを開始すると、サブトランザクションが生成されます。
  try {
    cur.begin();
  } catch (Exception e) {
      e.printStackTrace();
  }
サーバプログラムの呼び出し 「サーバプログラムのオブジェクトリファレンスの取得」で取得したリファレンスを使用してサーバプログラムの呼び出しを実施します。メソッドは、IDLで定義されたインタフェースをもとにします。
トランザクションコンテキストは、暗黙的にサーバプログラムに伝播されます。
  try {
      sv.write(a, b);
  } catch (Exception e) {
      e.printStackTrace();
  }
トランザクションの終了 トランザクション処理が終わったら、トランザクションを終了します。Currentオブジェクトの commitメソッドまたは rollbackメソッドをコールすることでトランザクションは終了します。コミットまたはロールバックが完了すると、トランザクションは破棄されます。サブトランザクションが終了した場合、そのサブトランザクションを開始する前のトランザクション(親トランザクション)が再開されます。commitの引数には、2フェーズコミットの完了を待ち合わせるか、待ち合せないかを指定します。
次のトランザクションを開始する場合には、再びCurrentオブジェクトの beginメソッドを呼び出します。
  try {
      cur.commit(true);
  } catch (Exception e) {
      e.printStackTrace();
  }
 
または
 
  try {
      cur.rollback();
  } catch (Exception e) {
      e.printStackTrace();
  }
クライアントアプリケーションプログラムのコンパイルとリンク
アプリケーションプログラムのコンパイルを実行するclasspathに以下のクラスが設定されていることを確認してください。
クライアントアプリケーションプログラムの実行
クライアントアプリケーションプログラム実行時にはコンパイル時のクラスパスに加え以下を設定してください。
実行時のプロパティとして以下を追加してください。
   プロパティ名:
org.omg.CORBA.ORBInitRef
   値:
TransactionCurrent=ospiref:jp.co.nec.WebOTX_S.OTXSCur.CurrentFactory
作成したアプリケーションプログラムを実行する前に次のことを確認してください。
クライアントアプリケーションでトランザクションを開始するためには、トランザクションを管理するサーバである、Proxy RCS、もしくはリカバリサーバとObject Brokerがあらかじめ動作している必要があります。
動作していない場合は、運用管理ツール、あるいは運用管理コマンドを利用して起動します。
これらの詳細については、ドメイン構築・基本設定ガイドおよび、ドメイン構築・基本設定ガイドの付録「Transactionサービス(リカバリサーバ利用時)」を参照してください。
その他のトランザクション機能
ここまでの説明で簡単な間接的なコンテキスト管理のプログラムを作成することが可能です。本節では、間接的なコンテキスト管理のその他機能について説明します。
rollback_only(ロールバックのマーク) トランザクション中に障害があったとき、トランザクション生成者はロールバックを行えば良いですが、トランザクション生成者でないとロールバックを発行できません。このような場合にトランザクションが最終的には必ずロールバックされるように設定することができます。トランザクションがロールバックするように設定するには、Currentインタフェースの rollback_onlyメソッドをコールします。
  try {
      cur.rollback_only();
  } catch (Exception e) {
      e.printStackTrace();
  }
get_status(トランザクション状態の取得) トランザクションの状態を取得するには、Currentインタフェースの get_statusメソッドをコールします。
  CosTransactions.Status status;
  try {
      status = cur.get_status();
  } catch (Exception e) {
      e.printStackTrace();
  }
get_transaction_name(トランザクション識別名の取得) トランザクションを識別するための文字列を取得するには、Currentインタフェースの get_transaction_nameメソッドをコールします。
  String txname;
  try {
      txname = cur.get_transaction_name();
  } catch (Exception e) {
      e.printStackTrace();
  }
set_timeout(トランザクションタイムアウト時間の設定) トランザクションのタイムアウト時間を設定するには、Currentインタフェースの set_timeoutメソッドをコールします。トランザクションの処理が無限ループに入ってしまった場合でも、このインタフェースを利用することで、指定秒数経過後にロールバック処理が自動的に実行されトランザクション処理が戻ります。
set_timeoutはトランザクションを開始するbeginメソッドの前にコールします。
  try {
      cur.set_timeout(30);
  } catch (Exception e) {
      e.printStackTrace();
  }
get_timeout(トランザクションタイムアウト時間の取得) トランザクションのタイムアウト時間を取得するには、Currentインタフェースの get_timeoutメソッドをコールします。
  int timeout;
  try {
      timeout = cur.get_timeout();
  } catch (Exception e) {
      e.printStackTrace();
  }
get_control(Controlオブジェクトの取得) Controlオブジェクトを取得するには、Currentインタフェースの get_controlメソッドをコールします。 Controlオブジェクトを使用して明示的なトランザクション処理を行う場合に使います。
Controlオブジェクトの機能については、直接的なコンテキスト管理または [リファレンス集 開発編(共通) > 4. CORBA > 4.3 Transactionサービス(Java)]を参照してください。
  org.omg.CosTransactions.Control control;
  try {
      control = cur.get_control();
  } catch (Exception e) {
      e.printStackTrace();
  }
suspend(トランザクションのサスペンド) Currentオブジェクトは、トランザクションをアプリケーション内で動作しているスレッドと対応させて管理しています。そのスレッドからトランザクションコンテキストを切り離す場合には、Currentインタフェースの suspendメソッドをコールします。 resumeまたはトランザクションを新たに開始するまで、スレッドはトランザクションと関連を持ちません。
  org.omg.CosTransactions.Control control;
  try {
      cur.suspend();
  } catch (Exception e) {
      e.printStackTrace();
  }
resume(トランザクションのレジューム) 指定された Controlオブジェクトが示すトランザクションをスレッドに関連付けるには、Currentインタフェースの resumeメソッドをコールします。suspendしたトランザクションを再開する場合や、直接的なコンテキスト管理で入手したトランザクションを間接的なコンテキスト管理で利用する場合に使います。
  try {
      cur.resume(control);
  } catch (Exception e) {
      e.printStackTrace();
  }


1.2.3.6. サーバアプリケーションの開発(Java言語)

本節では、CORBAトランザクションサービスを利用したJavaサーバアプリケーションプログラムの開発手順について、「間接的なコンテキスト管理」を利用するケースについて説明します。「直接的なコンテキスト管理」のケースについては、[リファレンス集 開発編(共通) > 4. CORBA > 4.3 Transactionサービス(Java)]を参照してください。
間接的なコンテキスト管理では、Currentオブジェクトを使ってトランザクションを管理します。
サーバアプリケーションはWebOTX上で動作させることになるため、IDLファイルの作成やアプリケーションのビルド、WebOTXへの登録などは一般的なWebOTX上のアプリケーションの開発を行う際と同じです。この詳細に関しては、プログラミング・開発の「WebOTX AS CORBAアプリケーション」を参照してください。
なお、WebOTX CORBAアプリケーションでTransactionサービスを利用する場合には運用管理ツールでの設定が必要となります。詳細については、ドメイン構築・基本設定ガイドを参照してください。
以下では、間接的なコンテキスト管理に必要なCurrentオブジェクトのサーバアプリケーションでの取得方法について説明します。
取得したCurrentオブジェクトの利用方法については「クライアントアプリケーションの開発(Java言語、JavaApplet)」で説明したものと同じですのでそちらを参照してください。
またクライアントアプリケーション作成時に流れで説明したORBの初期化についてはWebOTXのアプリケーション起動時に自動的に実行されます。
Currentの取得
Currentオブジェクトを取得するためにはまずORBオブジェクトを取得する必要があります。ORBオブジェクトはWoServerWrapper.TPSGetORB()により取得します。Currentオブジェクトは、ORBクラスのresolve_initial_referencesメソッドを使用して取得します。
org.omg.CORBA.ORB orb;
org.omg.CosTransactions.Current cur;
try {
  orb = jp.co.nec.WebOTX.orbsvwpr.WoServerWrapper.TPSGetORB();
  cur = (Current)orb.resolve_initial_references("TransactionCurrent");
} catch (Exception e) {
    e.printStackTrace();
}

1.2.3.7. データベースアプリケーションを開発する前に(C++言語)

本節では、Transactionサービスを利用したC++言語のデータベースアプリケーションプログラムの開発において、必要となる概念の説明を行います。
TransactionサービスではOracleデータベースとMicrosoft SQL Server、IBM WebSphere MQのC++(C)言語のインタフェースをサポートしています。 これらのデータベースとの間では、X/Open XAインタフェースを使用してグローバルトランザクションの制御を行っています。グローバルトランザクションは、1つ以上のトランザクションブランチから成り立っています。特に、そのトランザクションブランチの制御について理解して頂く必要があります。
トランザクションブランチについて
X/Open分散トランザクション処理では、アプリケーションは、トランザクションブランチ内でトランザクション処理(SQL命令)を実行することで「グローバルトランザクション」に参加します。1つのグローバルトランザクション内で作成されるトランザクションブランチ間の関係には、「疎結合」と「密結合」があります。
疎結合と密結合では、トランザクションブランチ間の動作において、次のような違いがあります。
表1.2.3.7-1
動作 疎結合での動作 密結合での動作
SQL命令実行時のデータ共有可否 トランザクションブランチ間では、SQL命令の実行時に表ロックや行ロックによる排他が行われます。このため、同じテーブルなどの資源を共有することはできません。 トランザクションブランチ間では、SQL命令の実行時に表ロックや行ロックによる排他が行われません。このため、同じテーブルなどの資源を共有することができます。更新結果は、トランザクションブランチ間で引き継がれます。
SQL命令実行時にデッドロックが発生する可能性の有無 あります。 ありません。
    密結合での並列動作について

OracleやIBM WebSphere MQを利用する場合、密結合のトランザクションブランチ間では、トランザクションブランチが順番に1つずつ動作します。このため、複数のプロセスやスレッドから同じデータを同時に更新することで、データを破壊してしまう恐れはありません。

Microsoft SQL Serverを利用する場合は、密結合のトランザクションブランチ間で、トランザクションブランチを同時並行して動作させることができます。このため、複数のプロセスやスレッドから同じデータを同時に更新すると、データを破壊してしまう恐れがありますが、同じグローバルトランザクションに参加するCORBAのアプリケーションオブジェクト間では、メソッド呼び出しが順番に行われるため、データを破壊するようなことはありません。
    疎結合での例外の動作について

疎結合のトランザクションブランチ間では、本来、同じテーブルなどの資源を共有することはできませんが、Oracleデータベースでは、デフォルトの動作の場合、疎結合のトランザクションブランチ間で同じテーブルにアクセスできます。オープン文字列中のLoose_Couplingパラメータにtrueを指定した場合は、疎結合のトランザクションブランチ間で同じテーブルにアクセスできなくなります。
トランザクションブランチの開始および終了方法 トランザクションブランチの開始・終了方法には次のものがあります。
表1.2.3.7-2
動作 開始・終了方法 説明
開始 新規作成 トランザクションブランチを新規に作成します。
連結 以前終了したトランザクションブランチに参加します。
再開 以前中断されたトランザクションブランチに参加します。
終了 正常終了 トランザクションブランチを正常終了します。
中断 トランザクションブランチを一時的に中断します。
結合移行 トランザクションブランチを他のスレッドに移行するために、一時的に中断します。
グローバルトランザクションに最初に参加する場合、トランザクションブランチを新規作成します。トランザクションブランチに対応する処理が終了するとトランザクションブランチを正常終了します。
正常終了したトランザクションブランチを再度実行する場合は、トランザクションブランチに連結します。連結したトランザクションブランチについても対応する処理が終了すると正常終了します。
トランザクションブランチを終了する時に中断させることができます。中断されたトランザクションブランチは同一のスレッド上で再開しなければなりません。この時、中断ではなく結合移行を行うと、トランザクションブランチを別のスレッドで再開させることができます。
トランザクションブランチの開始および終了契機 Transactionサービスでは、次の契機でトランザクションブランチを開始および終了します。
表1.2.3.7-3
  契機 開始・終了方法
開始契機  
  トランザクションの開始   Current:: begin()
  新規作成
  サーバメソッドの呼び出し開始直前
  新規作成: 1回目の呼び出し   連結: 2回目以降の呼び出し   再開: 結合移行指定時
  メソッド呼び出しの応答受信直後
  再開
  トランザクションの再開   Current::resume()
  再開
終了契機  
  トランザクションの終了   Current::commit()またはrollback()
  正常終了
  サーバメソッド終了直後
  正常終了: 通常動作   中断: 中断指定時   結合移行: 結合移行指定時
  サーバメソッド呼び出しの要求送信直前
  中断: 通常動作   結合移行: 結合移行指定時
  トランザクションの中断   Current::suspend()
  中断: 通常動作   結合移行: 結合移行指定時
新規作成の契機が2つあり、これにより、異なるプロセスで動作するクライアント、サーバオブジェクトごとに、ブランチ識別子が異なる、別のトランザクションブランチが作成されます。それらのトランザクションブランチ間の関係は、疎結合となります。
サーバオブジェクトが同一トランザクション内で2回目以降に呼び出された場合は、作成済みのトランザクションブランチと連結します。その場合のトランザクションブラン置換の関係は、密結合となります。


異なるプロセス上で動作するサーバオブジェクトを呼び出すと、トランザクションブランチは中断されます。また、サーバオブジェクトから呼び出しが戻ってくるとトランザクションブランチは再開されます。これに対して、同一プロセス内のサーバオブジェクトの呼び出しはトランザクションブランチの状態を変更しません。この場合、呼び出し元と呼び出し先のサーバオブジェクトは同一のトランザクションブランチを共有します。


結合移行を使うと、異なるスレッド間で同一のトランザクションブランチを共有することができます。この場合、サーバオブジェクトを呼び出すと、サーバメソッドの終了直後に結合移行が行われ、同一トランザクション内で再度サーバオブジェクトが呼び出された場合に、どのスレッド上で動作してもトランザクションブランチが再開されます。
サブトランザクションの動作
X/Open分散トランザクション処理にはサブトランザクションという概念がなく、仕様の規定もありません。Transactionサービスでは、独自にサブトランザクションを新たなグローバルトランザクションにマッピングしています。したがって、親トランザクションとサブトランザクションの間では、データベースのデータを共有することができません。 サブトランザクションのコミットは、親トランザクションのコミットと連動して実行されますので、データの一貫性を保つことができます。
データベースとの接続
データベースに対してトランザクションを実行する前に、XAインタフェースを利用して、データベースとの接続を行う必要があります。Transactionサービスでは、そのためのAPIを提供しています。
WebOTXのアプリケーションサーバでは、そのデータベースとの接続を自動的に行います。このため、アプリケーションサーバ上で動作するアプリケーションでは、Transactionサービスが提供するデータベースとの接続を行うためのAPIを呼び出す必要はありません。
単独で動作するアプリケーションでは、次のいずれかのインタフェースを利用してデータベースとの接続を行ってください。 詳細は、[ リファレンス集 開発編(共通) > 4. CORBA > 4.6 Transactionサービス(C++)]を参照してください。
TXAPI X/OpenのTXインタフェースライクな関数インタフェースです。主に動作スレッドを特定できるクライアントアプリケーションで使用します。アプリケーションは、プロセス毎にデータベース識別名を登録します。また、スレッド毎にデータベースとの接続を行います。
TransactionServiceインタフェース TransactionServiceインタフェースは、アプリケーションプロセス内でTransactionサービスの初期化を行うためのクラスインタフェースです。TXAPIの関数をラップしたメソッドを提供します。アプリケーションは、プロセス毎にデータベース識別名を登録します。また、スレッド毎にデータベースとの接続を行います。
トランザクションブランチ内の動作
アプリケーションでは、トランザクションブランチ内でデータベースにアクセスすることでグローバルトランザクションに参加します。グローバルトランザクションを中断(Current::suspend()呼び出し)すると、トランザクションブランチも中断されます。同様に、他のプロセスのサーバメソッド呼び出しを行った場合もトランザクションブランチは中断されます。
トランザクションブランチが中断された後では、トランザクションに参加しているデータベースに対するアクセスは失敗します。トランザクションブランチが中断している場合も、ローカルトランザクションとして動作させているデータベースに対するアクセスは問題なく行えます。
トランザクションの中断後に再開した場合、中断前の状態からSQLカーソルなどの資源が引き継がれるかどうかはデータベースに依存します。SQLカーソルの再設定が必要なデータベースがほとんどです。データベースのマニュアルを参照して確認してください。
CosTransactions::Current_ptr current; 
current->begin();   // トランザクションの開始 
current->suspend(); // トランザクションの中断

// ここではグローバルトランザクションに参加していない 

current->resume(); // トランザクションの再開

// ここでSQLカーソルが引き継がれるかはデータベース依存 

サーバのメソッド呼び出し        // トランザクションを中断後、再開

// ここでSQLカーソルが引き継がれるかはデータベース依存 

current->commit(CORBA_TRUE); // トランザクションのコミット
サーバアプリケーションは、クライアントから呼び出されたメソッドが終了する度にトランザクションから切り離された状態になります。このため、通常は、サーバアプリケーションのメソッド間でSQLカーソルなどの資源を引き継ぐことはできません。一般に、各メソッドの先頭でSQLカーソルなどの資源を再設定する必要があります。
Microsoft SQL Serverでのエンリストについて
エンリストとは、データベースアクセス用のハンドルをトランザクションに関連付けるMicrosoft SQL Server固有の操作です。
エンリストは、データベースアクセス用のハンドル毎に行う必要があります。1つのハンドルは同時に1つのトランザクションに対してしかエンリストすることができません。エンリストを行うと、エンリストされたトランザクションがコミットまたはロールバックされるまで、そのハンドルを次のトランザクションにエンリストすることはできません。
トランザクション実行中に他のトランザクションにエンリストしようとすると、エンリスト操作は実行中のトランザクションが完了するまで待ち合せます。このため、データベースとの接続を1つしか持たないサーバアプリケーションでは、複数のトランザクションを同時に実行することはできません。 複数のトランザクションを同時に実行するためには、トランザクション毎にデータベースアクセス用の接続を行う必要があります。
また、同時に1つのトランザクションにしかエンリストできないため、同一トランザクション内で複数のメソッドにまたがって処理を行う場合には、ハンドルがエンリスト済みかどうかを確認する必要があります。
Microsoft SQL Serverでのコネクション管理
Microsoft SQL Serverを使ったトランザクションシステムで複数のクライアントからの要求を容易に同時処理できるように、Transactionサービスではコネクション管理機能を提供しています。
コネクション管理機能には次の3つの機能があります。
コネクションのプール管理 データベースアクセス用のコネクションをプール管理することができます。
アプリケーションでは、プールされているコネクションがない場合に、Microsoft SQL ServerのAPIを利用してコネクションを新規に開設し、エンリストした後で、作成したコネクションをTransactionサービスに登録します。
登録されたコネクションはアプリケーションオブジェクト毎、かつ、トランザクション毎にプール管理されます。トランザクションが完了した際には、コネクションプールに返却されます。このため、アプリケーションは、適切な排他制御のもと、コネクションを再利用することができます。
コネクションは、同時実行したトランザクションの数だけプールされます。 いったん登録されたコネクションは、データベースを終了するまで有効ですので、複数回のトランザクションで再利用することができ、データベースとの接続および切断に要する時間を節約することができます。
プールされているコネクションは、データベースとの切断処理時に自動的に解放されます。
コネクションの自動切断 コネクションのプール管理を行わない場合は、トランザクションにエンリストされたコネクションを、トランザクション完了後に解放する必要があります。
クライアント制御トランザクションのように、サーバアプリケーションでのトランザクション終了契機がわからない場合に、この機能を使用してコネクションを自動的に切断します。
エンリスト要否の問い合わせ 同一トランザクション内でメソッドが複数回呼び出されるような場合に、アプリケーションオブジェクトからエンリストを実施すべきかどうかを問い合わせることができます。
コネクション管理機能を利用するためには、Microsoft SQL Serverのコネクション管理インタフェースであるRmSqlインタフェースを利用してください。 RmSqlインタフェースには、次の関数があります。
表1.2.3.7-4
関数名 説明
is_new_sqlsession() 同一トランザクション内で既にハンドルがエンリストされているかどうかを問い合わせます。
get_sqlsession() is_new_sqlsession()の結果、エンリスト実施済みの場合に、既にトランザクションに登録済みのハンドルを取得します。 エンリスト実施未の場合は、コネクションプールに登録されているハンドルを取得します。
register_sqlsession() 新規にデータベース接続ハンドルをトランザクションに登録します。引数で、コネクションをプールするか、自動切断するかを指定します。

Memo
RmSqlインタフェースの詳細は、[ リファレンス集 開発編(共通) > 4. CORBA > 4.6 Transactionサービス(C++)] を参照してください。

1.2.3.8. データベースアプリケーションのプログラミング例(C++言語)

本節では、CORBAトランザクションサービスを利用したC++言語のデータベースアプリケーションのプログラミング例について説明します。
  1. OraclePro*C/C++のプログラミング
  2. Oracle OCIのプログラミング
  3. Microsoft SQL Server DB Libraryのプログラミング
  4. Microsoft SQL Server ODBCのプログラミング
  5. IBM WebSphere MQ MQIのプログラミング
TransactionサービスではOracleデータベースとMicrosoft SQL Server、IBM WebSphere MQのC++(C)言語のインタフェースをサポートしています。 これらのデータベースとの間では、X/Open XAインタフェースを使用してグローバルトランザクションの制御を行っています。データベース固有の情報については、各データベースのマニュアルで内容を確認してください。
Java言語のアプリケーションプログラムからデータベースにアクセスする場合は、[ アプリケーション開発ガイド(共通) > 3. JDBCアプリケーションの開発 ] を参照してください。
OraclePro*C/C++のプログラミング
Pro*C/C++を使用して、Oracleデータベースへのアクセスを行うためのプログラミング例について説明します。Pro*C/C++では、SQL命令を記述したpcファイルから、Oracleのプリコンパイラを使用してCまたはC++のソースを生成します。そのpcファイルに、プログラミング例のような記述を行います。
なお、この例では、Pro*C/C++のエラー処理を省略しています。詳細については、Oracleのリファレンスを参照してください。
Oracle Pro*C/C++でのグローバルトランザクションの実行例 グローバルトランザクションを実行するためには、次の例のように記述します。データベースとの接続処理は不要です。
EXEC SQL BEGIN DECLARE SECTION;
 // ホスト変数の宣言
 DB_NAME     CHARACTER(16);
 long         accountno;
 long         balance;
 EXEC SQL END DECLARE SECTION;
 int   actno, bal;
 CosTransactions::Current_ptr  current;
 
  SET DB_NAME = ‘ORCL’;
 
  try {  //グローバルトランザクションを開始
    current->begin();
  } catch( CORBA::Exception& exc ){
    return -1;
  }
  accountno = actno;
  balance   = bal;
  //SQL命令の実行
  EXEC SQL AT :DB_NAME UPDATE a_account
           SET BALANCE = :balance WHERE ACCOUNTNO = :accountno;
 
  try {  //グローバルトランザクションをコミット
    current->commit(CORBA_TRUE);
  } catch( CORBA::Exception& exc ){
    return -1;
  }

Memo
この例では、"ORCL"というデータベース名を使用しています。デフォルトデータベースを使用する場合は、データベースの宣言とAT句の記述は必要ありません。


Caution
Oracle OCIのプログラミング
OCIを使用して、Oracleデータベースへのアクセスを行うためのプログラミング例について説明します。
なお、この例では、OCIのエラー処理を省略しています。詳細については、Oracleのリファレンスを参照してください。
Oracle OCIでの初期化処理 OCIインタフェースを初期化するために、プロセスで1度だけ、次の関数を呼び出します。
LOGINREC    *login;
  dbinit();
  login = dblogin();
  DBSETLUSER( login, "SCOTT" );
  DBSETLPWD( login, "TIGER" );
Oracle OCIでのグローバルトランザクションの実行例 グローバルトランザクションを実行するためには、次のように記述します。
  text *sqlstmt = (text *) "update a_account set
BALANCE=:bal where ACCOUNTNO=:actno";
  int       actno,
bal;
  text *dbname = "ORCL";
  OCIStmt   *stmthp;
  OCIEnv    *envhp;
  OCIError  *errhp;
  OCISvcCtx *svchp;
  OCIStmt   *stmthp;
  OCIBind   *bnd1p = (OCIBind *) 0;
  OCIBind   *bnd2p = (OCIBind *) 0;
  CosTransactions::Current_ptr  current;
 
  try { //グローバルトランザクションを開始
    current->begin();
  } catch( CORBA::Exception& exc ){
    return -1;
  }
  //環境ハンドルとサービスコンテキストの取得
  envhp = xaoEnv( dbname );
  svchp = xaoSvcCtx( dbname );
  //エラーハンドルの初期化
  (void) OCIHandleAlloc( (dvoid *)envhp, (dvoid **)&errhp,
          OCI_HTYPE_ERROR,  (size_t) 0, (dvoid **) 0);
  //ステートメントハンドルの初期化
  OCIHandleAlloc( (dvoid *)envhp, (dvoid **)&stmthp,
          OCI_HTYPE_STMT, (size_t)0, (dvoid **)0) );
  //SQL命令の実行準備
  OCIStmtPrepare( stmthp, errhp, selectp, (ub4)strlen((char *)selectp),
          (ub4)OCI_NTV_SYNTAX, (ub4)OCI_DEFAULT) );
  //変数の割り当て
  OCIBindByName( stmthp1, &bnd1p, errhp, (text *)":bal", -1,
          (dvoid *)&bal, (sword)sizeof(bal), 
          SQLT_INT, (dvoid *)0, (ub2 *)0, (ub2 *)0,
          (ub4)0, (ub4)0, OCI_DEFAULT );
  OCIBindByName( stmthp1, &bnd2p, errhp, (text *)":actno", -1,
          (dvoid *)&actno, (sword)sizeof(actno), 
          SQLT_INT, (dvoid *)0, (ub2 *)0, (ub2 *)0,
          (ub4)0, (ub4 *)0, OCI_DEFAULT );
  // SQL命令の実行
  OCIStmtExecute( svchp, stmthp, errhp, (ub4)1, (ub4)0,
          (CONST OCISnapshot *)NULL, (OCISnapshot *)NULL, OCI_DEFAULT );

  try {  //グローバルトランザクションをコミット
    current->commit(CORBA_TRUE);
  } catch( CORBA::Exception& exc ){
    return -1;
  }

Memo
この例では、"ORCL"というデータベース名を使用しています。デフォルトデータベースを使用する場合は、データベース名として空文字””を指定してください。


Caution
  • XAインタフェースでの接続を利用する場合、グローバルトランザクションとローカルトランザクションのどちらの場合も、次のSQL命令は使用できません。 トランザクションを完了させるOCITransCommit()とOCITransRollback()
    トランザクションを制御するためのOCITransStart()、OCITransDetach()、OCITransPrepare()とOCITransForget()
    データベースとの接続、切断を行うOCILogon()、OCILogoff()、OCISessionBegin()とOCISessionEnd()
  • カーソルは、トランザクションを完了させる前にクローズしてください。
  • トランザクションブランチ間で、カーソルやグローバルパッケージ変数を共有することはできません。このため、1つのグローバルトランザクション内で、サーバアプリケーションのメソッドを複数回呼び出す場合には、メソッドが呼び出される度にカーソルやパッケージ変数を初期化し直してください。
  • サービスコンテキストは、XAインタフェースでデータベースとの接続を行った際に初期化され、各スレッドに1つ割り当てられています。アプリケーションでは、必ず動作中のスレッドに割り当てられたサービスコンテキストを使用してください。マルチスレッド動作を行うサーバアプリケーションでは、メソッド呼び出しのたびに、サービスコンテキストを取得してください。
  • xaoEnv()とxaoSvcCtx()のパラメータで指定するデータベース名は、オープン文字列中のDBパラメータで指定する名前と一致させる必要があります。
  • ローカルトランザクションを開始する場合、グローバルトランザクションを完了させておく必要があります。また、グローバルトランザクションを開始する場合、ローカルトランザクションを完了させておく必要があります。
Microsoft SQL Server DB Libraryのプログラミング
DB Libraryを使用して、Microsoft SQL Serverへのアクセスを行うためのプログラミング例について説明します。
なお、この例では、DB Libraryのエラー処理を省略しています。詳細については、Microsoft SQL Serverのリファレンスを参照してください。
DB Libraryでの初期化処理 DB Libraryを初期化し、ログイン情報を作成するために、プロセスで1度だけ、次の処理を実行します。
LOGINREC    *login;
  dbinit();
  login = dblogin();
  DBSETLUSER( login, "SCOTT" );
  DBSETLPWD( login, "TIGER" );
      
DB Libraryでのグローバルトランザクションの実行例 グローバルトランザクションを実行するためには、次のように記述します。
static char *sqlstmt = "update a_account set balance = %d where accountno = %d", bal, actno"; 
int err_handler( DBPROCESS*, int, int, int, char*, char* );
int msg_handler( DBPROCESS*, DBINT, int, int, char* );
int actno, bal;
DBPROCESS   *dbproc; 
CosTransactions::Current_ptr  current;
 
  //データベースとの接続
  dbproc = dbopen( login, NULL );
  //ハンドラの登録
  dbprocmsghandle( dbproc, (DBMSGHANDLE_PROC)msg_handler );
  dbprocerrhandle( dbproc, (DBERRHANDLE_PROC)err_handler );
  //データベースの指定
  dbuse( dbproc, "pubs" );
 
  try {  //グローバルトランザクションを開始
    current->begin();
  } catch( CORBA::Exception& exc ){
    return -1;
  }
  //エンリストの実施
  dbenlistxatrans ( dbproc, TRUE );
 
  //SQL命令のフォーマット
  dbfcmd( dbproc, sqlstmt, bal, actno );
  //SQL命令の実行
  dbsqlexec( dbproc );
  dbresults( dbproc );
 
  try {  //グローバルトランザクションをコミット
    current->commit(CORBA_TRUE);
  } catch( CORBA::Exception& exc ){
    return -1;
  }
  //コネクションの切断
  dbclose();

Caution
  • グローバルトランザクションを実行する場合、2フェーズコミット用の次の関数は使用できません。 build_xact_string()、remove_xact()、scan_xact()、 start_xact()、stat_xact()、pen_commit()、 commit_xact()、close_commit()
  • カーソルは、トランザクションを完了させる前にクローズしてください。
  • 複数のスレッド間でハンドルを共有する場合には、dbmsghandle()とdberrhandle()ではなくdbprocmsghandle()とdbprocerrhandle()を使って、ハンドル毎にメッセージハンドラを登録する必要があります。
DB Libraryでのコネクションのプール管理 CORBAトランザクションサービスが提供するコネクション管理機能(RmSqlインタフェース)を利用するためには、次のように記述します。
int  err_handler( DBPROCESS*, int, int, int, char*, char* );
int  msg_handler( DBPROCESS*, DBINT, int, int, char* );
LOGINREC    *login;
DBPROCESS *dbproc; 
long   sessid = 0;
 
  // トランザクションに登録済みのハンドル有無を確認
  if (::is_new_sqlsession(sessid) == false) { 
    // プールからハンドルを取得
    handle = ::get_sqlsession(sessid, WEBOTXS_SQL_SESSPOOL); 
    if (handle == NULL) {
      // 新規ハンドルの取得
      dbproc = dbopen( login, NULL );
      dbprocmsghandle( dbproc, (DBMSGHANDLE_PROC)msg_handler );
      dbprocerrhandle( dbproc, (DBERRHANDLE_PROC)err_handler );
      dbuse( dbproc, "pubs" );
    }
    // エンリストの実施
    dbenlistxatrans ( dbproc, TRUE );
    // トランザクションに新規ハンドルを登録
    ::register_sqlsession(sessid, handle, WEBOTXS_SQL_DBLIB, WEBOTXS_SQL_SESSPOOL);
  } else {
    // トランザクションに登録済みのハンドルを取得
    handle = ::get_sqlsession(sessid, WEBOTXS_SQL_NONE); 
  } 
  // SQL命令の実行

Memo
RmSqlインタフェースの詳細は、[ リファレンス集 開発編(共通) > 4. CORBA > 4.6 Transactionサービス(C++)]を参照してください。


Caution
loginは、::dblogin()を呼び出して予め初期化しておく必要があります。


DB Libraryでのコネクション自動切断 CORBAトランザクションサービスが提供するコネクションの自動切断機能(RmSqlインタフェース)を利用するためには、次のように記述します。 コネクションは、トランザクションの完了時に自動的に切断されます。
int  err_handler( DBPROCESS*, int, int, int, char*, char* );
int  msg_handler( DBPROCESS*, DBINT, int, int, char* );
LOGINREC    *login;
DBPROCESS *dbproc; 
long   sessid = 0;
 
  // トランザクションに登録済みのハンドル有無を確認
  if (::is_new_sqlsession(sessid) == false) {
    // 新規ハンドルの取得
    dbproc = dbopen( login, NULL );
    dbprocmsghandle( dbproc, (DBMSGHANDLE_PROC)msg_handler );
    dbprocerrhandle( dbproc, (DBERRHANDLE_PROC)err_handler );
    dbuse( dbproc, "pubs" );
 
    // エンリストの実施
    dbenlistxatrans ( dbproc, TRUE );
    // トランザクションに新規ハンドルを登録
    ::register_sqlsession(sessid, (void *)dbproc, WEBOTXS_SQL_DBLIB, WEBOTXS_SQL_SESSDELETE);
  } else {
    // トランザクションに登録済みのハンドルを取得
    handle = ::get_sqlsession(sessid, WEBOTXS_SQL_NONE);
  }
  // SQL命令の実行

Memo
RmSqlインタフェースの詳細は、[ リファレンス集 開発編(共通) > 4. CORBA > 4.6 Transactionサービス(C++)]を参照してください。


Caution
loginは::dblogin()を呼び出して予め初期化しておく必要があります。



Microsoft SQL Server ODBCのプログラミング
ODBCを使用して、Microsoft SQL Serverへのアクセスを行うためのプログラミング例について説明します。
なお、この例では、ODBCのエラー処理を省略しています。詳細については、Microsoft SQL Serverのリファレンスを参照してください。
ODBCでの初期化処理 ODBCを初期化するために、プロセスで1度だけ、次の処理を実行します。
ENV    henv;
  SQLAllocEnv( &henv );
Microsoft SQL Server ODBCでのグローバルトランザクションの実行例 グローバルトランザクションを実行するためには、次のように記述します。
static char  *sqlstmt = (text *)"update a_account set balance = ? where accountno = ?";
HENV    henv;
HDBC    hdbc;
SDWORD  cbact;
SDWORD  cbbal;
SDWORD  sdact;
SDWORD  sdbal;
int   bal, actno;
CosTransactions::Current_ptr  current;
 
  //hdbcの初期化
  SQLAllocConnect( henv, &hdbc );
  //データベースとの接続
  SQLConnect( hdbc, "ACCOUNT", SQL_NTS,
              "SCOTT", SQL_NTS, "TIGER", SQL_NTS );
 
  try {  //グローバルトランザクションを開始
    current->begin();
  } catch( CORBA::Exception& exc ){
    return -1;
  }
  //エンリストの実施
  SQLSetConnectOption( hdbc, SQL_COPT_SS_ENLIST_IN_XA, TRUE );
 
  //ステートメントの初期化
  SQLAllocStmt( hdbc, &hstmt );
  //SQL命令の実行準備
  SQLPrepare( hstmt, sqlstmt, SQL_NTS );
  //変数の割り当て
  SQLBindParameter( hstmt, 1, SQL_PARAM_INPUT,
                    SQL_C_LONG, SQL_INTEGER,0, 0, &sdbal,0,&cbbal );
  SQLBindParameter( hstmt, 2, SQL_PARAM_INPUT,
                    SQL_C_LONG, SQL_INTEGER,0, 0, &sdact,0,&cbact );
  sdbal = bal;
  sdact = actno;
  //SQL命令の実行
  SQLExecute( hstmt );
  //ステートメントの解放
  SQLFreeStmt( hstmt, SQL_DROP );
 
  try {  //グローバルトランザクションをコミット
    current->commit(CORBA_TRUE);
  } catch( CORBA::Exception& exc ){
    return -1;
  }
  //コネクションの切断
  SQLDisconnect( hdbc );
  SQLFreeConnect( hdbc );

Caution
  • グローバルトランザクションを実行する場合、次の関数は使用できません。 トランザクションを完了させるSQLEndTran()
  • カーソルは、トランザクションを完了させる前にクローズしてください。
  • トランザクションブランチ間で、ステートメントやカーソルなどを共有することはできません。このため、1つのグローバルトランザクション内で、サーバアプリケーションのメソッドを複数回呼び出す場合には、メソッドが呼び出される度にステートメントやカーソルなどを初期化し直してください。
ODBCでのコネクションのプール管理 CORBAトランザクションサービスが提供するコネクション管理機能(RmSqlインタフェース)を利用するためには、次のように記述します。
HENV henv;
long   sessid = 0;
void   *handle;
 
  // トランザクションに登録済みのハンドル有無を確認
  if (::is_new_sqlsession(sessid) == false) { 
    // プールからハンドルを取得
    handle = ::get_sqlsession(sessid, WEBOTXS_SQL_SESSPOOL);
    if (handle == NULL) {
      // 新規ハンドルの取得
      ::SQLAllocConnect(henv, &handle);
      ::SQLConnect(handle, "ODBCSourceName", SQL_NTS,
                  "username", SQL_NTS, "password", SQL_NTS);
    }
    // エンリストの実施
    ::SQLSetConnectOption(handle, SQL_COPT_SS_ENLIST_IN_XA, TRUE);
    // トランザクションに新規ハンドルを登録
    ::register_sqlsession(sessid, handle, WEBOTXS_SQL_ODBC, WEBOTXS_SQL_SESSPOOL);
  } else {
    // トランザクションに登録済みのハンドルを取得
    handle = ::get_sqlsession(sessid, WEBOTXS_SQL_NONE); 
  } 
  // SQL命令の実行

Memo
RmSqlインタフェースの詳細は、[ リファレンス集 開発編(共通) > 4. CORBA > 4.6 Transactionサービス(C++)]を参照してください。


Caution
henvは::SQLAllocEnv()を呼び出して予め初期化しておく必要があります。


ODBCでのコネクション自動切断 CORBAトランザクションサービスが提供するコネクションの自動切断機能(RmSqlインタフェース)を利用するためには、次のように記述します。コネクションは、トランザクションの完了時に自動的に切断されます。
HENV  henv;
long   sessid = 0;
void   *handle;
 
  // トランザクションに登録済みのハンドル有無を確認
  if (::is_new_sqlsession(sessid) == false) { 
    // 新規ハンドルの取得
    ::SQLAllocConnect( henv, &handle );
    ::SQLConnect(handle, "ODBCSourceName", SQL_NTS,
                "username", SQL_NTS, "password", SQL_NTS);
    // エンリストの実施
    ::SQLSetConnectOption(handle, SQL_COPT_SS_ENLIST_IN_XA, TRUE);
    // トランザクションに新規ハンドルを登録
    ::register_sqlsession(sessid, handle, WEBOTXS_SQL_ODBC, WEBOTXS_SQL_SESSDELETE);
  } else {
    // トランザクションに登録済みのハンドルを取得
    handle = ::get_sqlsession(sessid, WEBOTXS_SQL_NONE);
  } 
  // SQL命令の実行

Memo
RmSqlインタフェースの詳細は、[ リファレンス集 開発編(共通) > 4. CORBA > 4.6 Transactionサービス(C++)]を参照してください。

Caution
henvは::SQLAllocEnv()を呼び出して予め初期化しておく必要があります。

IBM WebSphere MQ MQIのプログラミング
MQIを使用して、IBM WebSphere MQへのアクセスを行うためのプログラミング例について説明します。
なお、この例では、MQIのエラー処理を省略しています。詳細については、IBM WebSphere MQのリファレンスを参照してください。
IBM WebSphere MQ MQIでのグローバルトランザクションの実行例 グローバルトランザクションを実行するためには、次の例のように記述します。    
  #define MYQMGR "MQM"
 
  MQOD    od = {MQOD_DEFAULT}; 
  MQPMO   pmo = {MQPMO_DEFAULT}; 
  MQHCONN Hcon; 
  MQHOBJ  Hobj; 
  MQLONG  CompCode, Reason, CReason; 
  MQMD    PMsgDescriptor= { MQMD_DEFAULT }; 
  char    QMName[50];
  char    MyMsg[16];
  int     mode;
  CosTransactions::Current_ptr  current;
 
  // コネクションハンドルの取得
  strcpy(QMName, MYQMGR);
  MQCONN( QMName, &Hcon, &CompCode, &CReason );
  // キューのオープン
  strcpy(od.ObjectName, "SYSTEM.DEFAULT.LOCAL.QUEUE");
  mode = MQOO_OUTPUT+MQOO_FAIL_IF_QUIESCING;
  MQOPEN( Hcon, &od, mode, &Hobj, &CompCode, &Reason );
 
  try {  //グローバルトランザクションを開始
    current->begin();
  } catch( CORBA::Exception& exc ){
    return -1;
  }
  // SYNCPOINTオプションの設定
  pmo.Options |= MQPMO_SYNCPOINT;
 
  // メッセージの送信
  strcpy( MyMsg, "TESTMessage!" );
  MQPUT( Hcon, Hobj, &PMsgDescriptor, &pmo,
         (long int)strlen(MyMsg)+1, MyMsg, &CompCode, &Reason );
 
  try {  //グローバルトランザクションをコミット
    current->commit(CORBA_TRUE);
  } catch( CORBA::Exception& exc ){
    return -1;
  }
  // キューのクローズ
  // コネクションハンドルの解放
  MQDISC(&Hcon, &CompCode, &Reason);

Memo
この例では、"MQM"というキュー管理プログラム名を使用しています。デフォルトのキュー管理プログラムを使用する場合は、名前に空文字””を指定してください。

Caution
グローバルトランザクションの実行時には、メッセージ送信の場合はMQPMO構造体のOptionsメンバにMQPMO_SYNCPOINTを、また、メッセージ受信の場合はMQGMO構造体のOptionsメンバにMQGMO_SYNCPOINTを必ず指定してください。

1.2.4. 画面テンプレート

WebOTX 画面テンプレートは、Webブラウザからの入力情報をWebサーバを通してWebOTXへ渡し、HTTPパラメータの制御やHTML画面生成を WebOTXサーバ上の Javaプログラム(サーバAP)で行う機能を提供します。

本節では画面テンプレートのサーバAP部分のプログラミングについて説明します。

Memo
画面テンプレートの詳細は、[ 画面テンプレートとは ]を参照してください。

サーバAPの構造

WebOTX画面テンプレートでは、サーバAPのソースやバッチファイルのテンプレートを提供しており、これらを利用することにより効率的に開発作業を行うことができます。


図1.2.4.1-1

表1.2.4.1-1
作業 説明
CORBAインタフェースクラス
WebOTX画面テンプレートが提供するCORBAインタフェースクラスを継承して作成します。コンストラクタを実装するだけでWebサーバ上で動作するWebOTX画面テンプレートとの連携が可能になります。
APロジッククラス
WebOTX画面テンプレートが提供するAPロジッククラスを継承して作成します。doMethod メソッドをオーバライドして、ビジネスロジックを実装する各メソッドを呼び出すためのディスパッチ処理を実装します。
開発手順
WebOTX画面テンプレートでは、サーバAPのソースやバッチファイルのテンプレートを提供しており、これらを利用することにより効率的に開発作業を行うことができます。
WebOTX画面テンプレートを利用してサーバAPを開発する際の手順を以下に示します。
表1.2.4.1-2
作業 説明
サーバAPの雛形を作成
テンプレートをコピーして作成します。
ビジネスロジッククラスの作成
ビジネスロジックを追加します。
コンパイル
サーバAPのインターフェース名に合わせて、テンプレートのバッチファイルを修正した後実行すると、WebOTXに登録するサーバAP(二つのjarファイルから構成)ができます。
WebOTXへの配備
コンパイルして作成されたjarファイルとifファイルを、WebOTXへ配備します。
テンプレートファイル一覧
表1.2.4.1-3
テンプレートファイル 説明
make.BAT
コンパイル用のバッチファイル(Windows 2003/2008/XP/Vista用)
makefile
コンパイル用のmakeファイル(HP-UX、Linux用)
makejar
jarファイル作成用のmakeファイル(HP-UX、Linux用)
Template.idl
IDLファイル
AppLogic1.java
ビジネスロジッククラス
WebGWObj.java
CORBAインタフェースクラス
サーバAPの雛形作成
<WebOTXインストールディレクトリ>\template\ServerAP\template ディレクトリ(HP-UX、Solaris、Linuxでは、<WebOTXインストールディレクトリ>/template/ServerAP/template)全体を任意のディレクトリにコピーします。必要に応じてディレクトリ名も変更してください。
サーバAPのインタフェース名を変更したい場合には、以下の点を変更してください。
表1.2.4.1-4
サーバAPのインタフェースを変更する場合の変更個所
IDLファイルのファイル名と、interface宣言
CORBAインタフェースクラスのファイル名
CORBAインタフェースクラスのクラス名
WebGWOperationsの名称コンストラクタの名称
ビジネスロジッククラスの作成
ビジネスロジッククラスのクラスファイル名と、クラスファイル内のclass宣言文のクラス名を任意のクラス名に変更します。
初期化処理(initメソッド)に必要な初期化処理を追加します。
メイン処理(doMethodメソッド)にWebブラウザから受信したディスパッチキー(methodid)を元にしてそれぞれの処理を呼び出す処理を記述します。
メイン処理に記述した処理を記述します。
必要であればエラー時のHTMLを作成する処理を記述します。
コンパイル(Windowsの場合)
サーバAPのコンポーネントおよびビジネスロジックのコンポーネント作成用バッチファイルを修正します。
make.BATをご使用の環境に応じて修正します。
WebGWpackage.jarとWebGWBASE62.jarのパスを環境に合わせて変更してください。
set CLASSPATH=%CLASSPATH%;..\package\WebGWpackage.jar;..\BASE62\WebGWBASE62.jar
WebGWBASE62.idlのパスを環境に合わせて変更してください。
call woi2j.bat -tie -rp jp.co.nec.WebOTX.WebGWBS -R ..\BASE62\WebGWBASE62.idl Template
IDLファイル名を変更した場合は、「call woi2j.bat ・・・ WebGW」のWebGWの部分を新しいファイル名に合わせて変更します。
サーバAPおよびビジネスロジックのjarファイル名を任意のファイル名に変更します。
jar cvf TemplateApp.jarjp\co\nec\WebOTX\ObjectCreator Logic %REDIRECT%
jar cvf Template.jar jp template Logic %REDIRECT%

バッチファイルを実行します。
修正したバッチファイルを実行すると、サーバAPとビジネスロジックのコンポーネントが生成されますのでmakecpkコマンドを実行しWebOTXに配備可能なcpkファイルを作成してください。
> makecpk Template.cpk module=classes\Template.jar if=Template.if
> makecpk TemplateApp.spk module=classes\TemplateApp.jar
コンパイル(HP-UX、Solaris、Linuxの場合)
サーバAPのコンポーネントおよびビジネスロジックのコンポーネント作成用バッチファイルを修正します。
makefileをご使用の環境に応じて修正します。
以下の環境変数のパスを正しいパスに変更します。
SHELL      = /usr/bin/sh
RM         = /usr/bin/rm
IDLBASEDIR = ../BASE62
WebGWpackage.jarとWebGWBASE62.jarのパスを環境に合わせて変更してください。
CLASSPATH  = .:/opt/WebOTX/dev/javalib/svsimj.jar:/opt/WebOTX/dev/javalib/WOTXBS90.jar:/opt/WebOTX/dev/javalib/WOTXTRM.jar:../BASE62/WebGWBASE62.jar:../package/WebGWpackage.jar
IDLファイル名を変更した場合は、「call woi2j.bat ・・・ WebGW」のWebGWの部分を新しいファイル名に合わせて変更します。
makejarをご使用の環境に応じて修正します。
サーバAPおよびビジネスロジックのjarファイル名を任意のファイル名に変更します。
jSVOBJ     = Template.jar
APPOBJ     = TemplateApp.jar
makeを実行します。
修正したmakefileを実行すると、サーバAPとビジネスロジックのコンポーネントが生成されますのでmakecpkコマンドを実行しWebOTXに配備可能なcpkファイルを作成します。
> makecpk Template.cpk module=classes/Template.jar if=Template.if
> makecpk TemplateApp.spk module=classes/TemplateApp.jar
共有コンポーネントの作成
Windowsの場合 以下のコマンドを実行して共有コンポーネントを作成します。
> cd <BASE62ディレクトリ>
> makecpk WebGWBASE62.spk module= WebGWBASE62.jar if= WebGWBASE62.if
> cd <packageディレクトリ>
> makecpk WebGWpackage.spk module= WebGWpackage.jar
> makecpk classes12.spk module=<JDBCライブラリファイル>
HP-UX, Solaris, Linuxの場合 以下のコマンドを実行して共有コンポーネントを作成します。
> cd <BASE62ディレクトリ>
> /opt/WebOTX/bin/makecpk WebGWBASE62.spk module= WebGWBASE62.jar if= WebGWBASE62.if
> cd <packageディレクトリ>
> /opt/WebOTX/bin/makecpk WebGWpackage.spk module= WebGWpackage.jar
> makecpk classes12.spk module=<JDBCライブラリファイル>
WebOTXへの配備
以下のファイルを共有コンポーネントに配備します。
表1.2.4.1-5
ファイル名 説明
WebGWBASE62.spk
画面テンプレートのフレームワーククラスをspkアーカイブしたファイルです。
WebGWpackage.spk
画面テンプレートの使用するクラスをspkアーカイブしたファイルです。
TemplateApp.spk
ビジネスロジッククラスをspkアーカイブしたファイルです。
classes12.spk
データベースベンダの提供するJDBCライブラリをspkアーカイブしたファイルです。
以下のファイルをCORBAコンポーネントに配備します。
表1.2.4.1-6
ファイル名 説明
Template.cpk
サーバAPクラスをcpkアーカイブしたファイルです。