ヘッダーをスキップ
TxRPCを使用したOracle Tuxedoアプリケーションのプログラミング
  目次へ
目次

前
戻る
 
次
次へ
 

3 RPCクライアント/サーバー・プログラムの作成

ここでは、次の内容について説明します。

リモート処理への対応

TxRPCの目的は、あるアドレス空間にあるクライアントと別のアドレス空間にあるサーバーとの間で、透過的なプロシージャ・コールを提供することです。クライアントとサーバーは、別のマシンに存在する場合もあります。ただし、クライアントとサーバーのアドレス空間が異なるので、次のような点に注意する必要があります。

返されるステータスと例外の処理

X/OPEN RPC仕様では、アプリケーション以外のエラーは状態パラメータまたは状態復帰で返されます。RPCサーバーに障害があった場合にはfault_status値が返され、通信に障害があった場合にはcomm_status値が返されます。状態復帰を指定するには、IDLファイルでオペレーションの戻り値またはerror_status_t型の[out]パラメータを定義します。さらにACFファイルで、そのオペレーションまたはパラメータが[fault_status]または[comm_status]のいずれか、あるいは両方の属性を持つことを宣言します。

たとえば、IDLファイル内で次のようにオペレーションを定義します。

error_status_t op([in,out]long *parm1, [out]error_status_t *commstat);

対応するACFファイル内での定義は次のようになります。

[fault_status]op([comm_status]commstat);

サーバーからのエラーは、オペレーションの戻り値に、通信エラーは2番目のパラメータに返されます。クライアント・コードでは、以下のようにエラーを処理します。

if (op(&parm1, &commstat) != 0 ││ commstat != 0) /* handle error */

状態復帰を使用する利点は、エラー発生個所で対処が行え、きめ細かなエラー回復処理を実行できることです。

状態復帰の欠点は、ローカル版の関数には必要のないパラメータをリモート関数に付加することです。さらに、きめ細かなエラー回復処理は冗長になりがちで、エラーを引き起こす傾向があります(たとえば、場合分けに見落としが生じるなど)。

DCEは第2のメカニズムとして例外処理を定義しています。この例外処理はC++のものと類似しています。

CやC++のアプリケーション・コードをTRYCATCHCATCH_ALL、およびENDTRYステートメントを用いて、例外が生じる可能性のあるブロックに区切ります。TRYはブロックの開始を示します。CATCHは、特定の例外用の処理ブロックを示します。CATCH_ALLは、対応するCATCHステートメントを持たない例外を処理するために使われます。ENDTRYでブロックを終了します。例外処理が低いレベルでは行なえず、RERAISEステートメントを用いて高いレベルのブロックで例外処理を行う場合、TRYブロックはネストされます。例外処理ブロックの外部で例外が生じた場合は、プログラムはログにメッセージを記録して終了します。例外処理マクロの詳細と使用例については、『Oracle Tuxedo Cリファレンス』のTRY(3c)を参照してください。

RPCコールに対して通信とサーバーで生成される例外のほかに、さらに低いレベルの例外、特に、オペレーティング・システムのシグナルも生成されます。このような例外の詳細については、『Oracle Tuxedo Cリファレンス』のTRY(3c)を参照してください。

スタブ・サポート関数の使用

X/OPEN仕様では、100を越える膨大な数のランタイム・サポート関数が定義されています。これらの関数を、X/OPEN TxRPC IDL-only環境ですべてサポートする必要はありません。これらの関数の大部分は、ATMIのクライアントやサーバーに対して透過的に行われるバインドと管理に関連したものです。

アプリケーションの移植性に影響を及ぼす事柄の1つに、スタブ用の入出力パラメータと戻り値に割り当てられるメモリーの管理があります。Stub Memory Managementルーチンは、スレッドを扱う2つの関数を除きTxRPCランタイムでサポートされています。状態を返す関数には以下のものがあります。

同等の例外を返す関数には以下のものがあります。

これらの関数の詳細については、『Oracle Tuxedo Cリファレンス』を参照してください。

ランタイム関数は、libtrpcファイルに含まれます。RPCクライアントとサーバーの作成方法については次トピックで解説します。

メモリー管理を効果的に行う方法を次に示します。

RPCヘッダー・ファイルの使用

DCE/RPCとTxRPC両方からのスタブが同一環境で確実にコンパイルできるようにするため、TxRPCの実装で用いるヘッダー・ファイル名は異なるものにします。これらのヘッダー・ファイルはIDLコンパイラが生成するインタフェース・ヘッダー・ファイルに自動的にインクルードされるので、アプリケーション・プログラマには影響を与えません。ただし、型や関数の定義方法を調べるために、アプリケーション・プログラムでこれらのヘッダー・ファイルを表示することは可能です。以下に、新しいヘッダー・ファイル名のリストを示します。

ヘッダー・ファイルは$TUXDIR/include/rpcディレクトリにあります。デフォルトでは、TxRPC IDLコンパイラは「システムIDLディレクトリ」として$TUXDIR/includeを検索します。

コードの移植性

IDLコンパイラからの出力は、多数の環境でコンパイルできるように作成されます。コンパイル作業の説明は次章を参照してください。ただし、環境によっては動作に支障が起きる構成もあります。既知の問題点を以下に示します。

ANSIに準拠しない旧形式のC言語を使用してコンパイルする場合、「配列へのポインタ」は許可されません。例:

typedef long array[10][10];
func()
  {
  array t1;
  array *t2;
  t2 = &t1;   /* & ignored, invalid assignment */
  func2(&t1); /* & ignored */
  }

このため、移植を考慮した場合、「配列へのポインタ」をパラメータとして、オペレーションに渡すことが難しくなります。

文字列属性がマルチ・バイト構造体に適用されるところに、文字列の配列を使用すると、コンパイラが構造体に文字を埋め込むときに、期待する結果が得られないことがあります。これは通常は起こり得ません。ほとんどのコンパイラは文字フィールドのみを含む構造体には文字の埋め込みは行いません。しかし、この現象は少なくとも一度確認されています。

デフォルトでは、定数値の実装は各定数に対して#defineを作成することで行われます。このため、定数に用いる名前は、IDLファイル内やインポートされたすべてのIDLファイル内のほかのすべての名前と同じ名前にしてはいけません。ANSI C環境でこの問題を回避するには、tidlコンパイラのTxRPC固有のオプション-use_constを使用します。このオプションを使うと、#define定義の代わりに、const宣言が作成されます。定数値はクライアント・スタブとサーバー・スタブ内で宣言されます。ヘッダー・ファイルをインクルードするほかのすべてのソース・ファイルでは、単にextern const宣言が使用されます。こうしないと、クライアント・スタブとサーバー・スタブを同一の実行ファイルにコンパイルできないという制限を生じるか、二重定義エラーが発生することに注意してください。

C++環境では、次のような制限があります。

クライアントとサーバーのアプリケーション・ソフトウェアをコーディングしているときは、IDLコンパイラが作成したデータ型、すなわちrpc/tidlbase.h内で定義されるデータ型を使う必要があります(rpc/tidlbase.h内での定義とは、次の表に「発行されるマクロ」として記載されているものです)。たとえば、idl_long_intの代わりにlongを使うと、データ型はプラットフォームによっては32ビットであったり64ビットであったりします。一方、idl_long_intは全プラットフォームで32ビットです。生成されるデータ型の一覧を表3-1に示します。

表3-1生成されるデータ型

IDLの型 サイズ 発行されるマクロ Cの型

boolean

8ビット

idl_boolean

unsigned char

char

8ビット

idl_char

unsigned char

byte

8ビット

idl_byte

unsigned char

small

8ビット

idl_small_int

char

short

16ビット

idl_short_int

short

long

32ビット

idl_long_int

32ビット長のマシンの場合:long、64ビット長のマシンの場合: int

hyper

64ビット

idl_hyper_int

32ビット長のマシンの場合:

ビッグ・エンディアン

struct 
{
  long high;
  unsigned long low;
}

リトル・エンディアン

struct
 {
  unsigned long low;
  long high;
 }

64ビット長のマシンの場合:

long

unsigned small

8ビット

idl_usmall_int

unsigned char

unsigned short

16ビット

idl_ushort_int

short

unsigned long

32ビット

idl_ulong_int

32ビット長のマシンの場合:long、64ビット長のマシンの場合: int

unsigned hyper

64ビット

idl_uhyper_int

32ビット長のマシンの場合:

ビッグ・エンディアン

struct 
{
  unsigned long high;
  unsigned long low;
}

リトル・エンディアン

struct
 {
  unsigned long low;
  unsigned long high;
 }

64ビット長のマシンの場合:

unsigned long

float

32ビット

idl_short_float
float

double

64ビット

idl_long_float
double

void *

ポインタ

idl_void_p_t
void *

handle_t

ポインタ

handle_t
handle_t

Cの場合と同様、IDLでも複数のクラスの識別子があります。スコープやネームスペースなどのクラス内では、名前をユニークにする必要があります。

タグを持たず、typedefの一部として定義されていない匿名の構造体またはユニオンは、オペレーションの戻り値やパラメータとして使用できないことに注意してください。

ATMIとの対話

TxRPC実行プログラムはOracle Tuxedoシステムを使ってRPC通信を行います。その他のOracle Tuxedoのインタフェースや通信メカニズムは、RPC呼出しを使用している同一のクライアントおよびサーバー内で使用できます。このため、単一のクライアントがリクエスト/レスポンス呼出し(例: tpcall(3c)tpacall(3c)tpgetrply(3c))、会話型呼出し(tpconnect(3c)tpsend(3c)tprecv(3c)tpdiscon(3c))を行い、安定キューにアクセス(tpenqueue(3c)およびtpdequeue(3c))することが可能です。クライアントが最初に行う呼出しは、RPC呼び出し、その他の通信呼び出し、ATMI呼出し(例:バッファ割り当て、任意通知など)です。 クライアントが、Oracle Tuxedoソフトウェアを最初に呼び出すときに、自動的にアプリケーションに参加します。ただし、アプリケーションがセキュリティをオンにした状態で実行していたり、あるいはクライアントが特定のリソース・マネージャ・グループの一部として稼働する必要がある場合は、関数tpinit(3c)を明示的に呼び出してアプリケーションに参加しなければなりません。明示的に設定できるオプションの詳細と一覧については、『Oracle Tuxedo Cリファレンス』のtpinit(3c)を参照してください。アプリケーションがOracle Tuxedoシステムを使用した作業を完了する場合には、関数tpterm(3c)を明示的に呼び出してアプリケーションを終了し、関連のあるリソースをすべて解放する必要があります。ワークステーション以外のネイティブなクライアントに対してこの明示的な呼出しが行われなかった場合、モニターがこれを検知し、userlog(3c)に警告を出力し、リソースを解放します。クライアントがワークステーションの場合は、リソースは解放されず、結果的にワークステーションのリスナーまたはハンドラがリソースを使い果たし、新たなクライアントを受け付けることができなくなります。

クライアントと同様に、サーバーはクライアントのロール内の任意の通信パラダイムを使用できます。ただし、同一サーバー内では会話型サービスとRPCサービスの両方を提供(宣言)することはできません。後で説明するように、RPCサーバーは非会話型として指定する必要があります。ATMIリクエスト/レスポンスとRPCサービスを同一サーバー内で混用することは可能ですが、お薦めできません。その他の制限として、RPCオペレーションは関数tpreturn(3c)や関数tpforward(3c)を呼び出すことはできません。RPCオペレーションは、ローカルに呼び出された場合と同様の復帰動作で復帰する必要があります。RPCオペレーションから関数tpreturn(3c)や関数tpforward(3c)の呼出しを試みると、その呼出しはインターセプトされ、クライアントにエラーが返されます(例外rpc_x_fault_unspecまたは状態rpc_s_fault_unspec)。

サーバーでは利用できますが、クライアントでは利用できない関数が2つあります。関数tpsvrinit(3c)と関数tpsvrdone(3c)がこれにあたります。それぞれサーバーの起動時と停止時に呼び出されます。サーバーは、TxRPCオペレーション・リクエストを受け取る前に、tx_open(3c)を呼び出す必要があるので、tpsvrinit()でtx_openを呼び出すことをお薦めします。デフォルトのtpsvrinit()関数は関数tx_open()を呼び出しています。

TXとの対話

TX関数は、トランザクションの境界用のインタフェースを提供します。 関数tx_begin(3c)と、tx_commit(3c)またはtx_rollback(3c)は、通信を含むトランザクション内のすべての作業をカプセル化します。その他のプリミティブとして、トランザクションのタイムアウト設定、連鎖または非連鎖としてのトランザクションの宣言、トランザクション情報の取得も提供されています。以上についての詳細は、X/OPEN TX仕様に記載され、またX/OPEN TxRPC仕様にも解説されています。X/OPEN TxRPC仕様には、TXとRPCの相互動作についての解説があります。その要点を次に述べます。

TxRPC仕様で定義されたIDLに対するその他の変更と制限については、IDLに関する項ですでに説明しました。