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++のアプリケーション・コードを
TRY、
CATCH、
CATCH_ALL、および
ENDTRYステートメントを用いて、例外が生じる可能性のあるブロックに区切ります。
TRYはブロックの開始を示します。
CATCHは、特定の例外用の処理ブロックを示します。
CATCH_ALLは、対応する
CATCHステートメントを持たない例外を処理するために使われます。
ENDTRYでブロックを終了します。例外処理が低いレベルでは行なえず、
RERAISEステートメントを用いて高いレベルのブロックで例外処理を行う場合、
TRYブロックはネストされます。例外処理ブロックの外部で例外が生じた場合は、プログラムはログにメッセージを記録して終了します。例外処理マクロの詳細と使用例については、
『Oracle Tuxedo C言語関数リファレンス』のTRY(3c)を参照してください。
X/OPEN仕様では、100を越える膨大な数のランタイム・サポート関数が定義されています。これらの関数を、X/OPEN TxRPC IDL-only環境ですべてサポートする必要はありません。これらの関数の大部分は、ATMIのクライアントやサーバーに対して透過的に行われるバインドと管理に関連したものです。
アプリケーションの移植性に影響を及ぼす事柄の1つに、スタブ用の入出力パラメータと戻り値に割り当てられるメモリーの管理があります。Stub Memory Managementルーチンは、スレッドを扱う2つの関数を除きTxRPCランタイムでサポートされています。状態を返す関数には次のものがあります。
•
|
rpc_sm_set_client_alloc_free
|
•
|
rpc_sm_set_server_alloc_free
|
•
|
rpc_sm_swap_client_alloc_free
|
•
|
rpc_ss_set_client_alloc_free
|
•
|
rpc_ss_set_server_alloc_free
|
•
|
rpc_ss_swap_client_alloc_free
|
これらの関数の詳細は、
『Oracle Tuxedo C言語関数リファレンス』を参照してください。
ランタイム関数は、
libtrpcファイルに含まれます。RPCクライアントとサーバーの作成方法については次トピックで解説します。
•
|
デフォルトでは、ATMIクライアントはクライアント・スタブを呼び出すときに mallocと freeを使います。オペレーションの戻り値での暗黙的な [out]ポインタを含めて、 [out]ポインタに割り当てられた領域以外の全領域はクライアント・スタブからの復帰時に解放されます。 [out]ポインタの解放を容易にするために、 rpc_ss_enable_allocate()を呼び出します。さらに、RPCを呼び出す前に、 rpc_ss_set_client_alloc_free()を呼び出して、 alloc/ freeをそれぞれ rpc_ss_alloc()/ rpc_ss_free()に設定します。その後、関数 rpc_ss_disable_allocate()を用いて割り当てたメモリーをすべて解放します。たとえば、クライアント・スタブから返される領域の解放を簡素にするには次のように使用します。
|
rpc_ss_set_client_alloc_free(rpc_ss_allocate, rpc_ss_free);
ptr = remote_call_returns_pointer();
/* use returned pointer here */
...
rpc_ss_disable_allocate(); /* this frees ptr */
•
|
アプリケーション操作を呼び出すATMIサーバー・スタブの実行時には、サーバー・スタブ内では rpc_ss_allocateを用いたメモリー割当てが常に有効です。ACFファイル内の属性 [enable_allocate]は無効です。クライアントにレスポンスを返す前に全メモリーはサーバー内で解放されます。DCEでは、メモリー割当てが有効になるのは、 [ptr]フィールドまたはパラメータが存在する場合と、プログラマが [enable_allocate]を明示的に指定したときだけです。
|
•
|
サーバー・スタブがアプリケーション・オペレーションを呼びだし、さらにそのアプリケーション・オペレーションがクライアント・スタブを呼び出す場合、つまりRPCを呼び出すことで、サーバーがクライアントとして動作するときは、関数 rpc_ss_set_client_alloc_free()を呼び出してメモリー割当ての準備をする必要があります。その結果、オペレーションからの復帰時に割り当てられた領域がすべて解放されるようになります。次の呼出しを行います。
|
rpc_ss_set_client_alloc_free(rpc_ss_allocate, rpc_ss_free);
•
|
関数 rpc_ss_allocate()または関数 rpc_sm_allocate()を呼び出すときには、設定しているポインタのデータ型に一致するように出力をキャストするようにしてください。例:
|
long *ptr;
ptr = (long *)rpc_ss_allocate(sizeof(long));
DCE/RPCとTxRPC両方からのスタブが同一環境で確実にコンパイルできるようにするため、TxRPCの実装で用いるヘッダー・ファイル名は異なるものにします。これらのヘッダー・ファイルはIDLコンパイラが生成するインタフェース・ヘッダー・ファイルに自動的にインクルードされるので、アプリケーション・プログラマには影響を与えません。ただし、型や関数の定義方法を調べるために、アプリケーション・プログラムでこれらのヘッダー・ファイルを表示することは可能です。次に、新しいヘッダー・ファイル名のリストを示します。
•
|
dce/nbase.h、 dce/nbase.idl - rpc/tbase.hおよび rpc/tbase.idlを名称変更したヘッダー・ファイル。定義済の型、 error_status_t、 ISO_LATIN_1、 ISO_MULTI_LINGUAL、および ISO_UCSの宣言を含みます。
|
•
|
dce/idlbase.h - rpc/tidlbase.hを名称変更したヘッダー・ファイル。IDLの仕様に準拠した基本型(例: idl_boolean、 idl_long_int)を含みます。さらにスタブ関数用の関数プロトタイプも含みます。
|
•
|
dce/pthread_exc.h - rpc/texc.hを名称変更したヘッダー・ファイル。 TRY/ CATCH例外処理マクロを含みます。
|
•
|
dce/rpcsts.h - rpc/trpcsts.hを名称変更したヘッダー・ファイル。RPCインタフェース用の例外値とステータス値の定義を含みます。
|
ヘッダー・ファイルは
$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宣言が使用されます。こうしないと、クライアント・スタブとサーバー・スタブを同一の実行ファイルにコンパイルできないという制限を生じるか、二重定義エラーが発生することに注意してください。
•
|
typedefの名前と、構造体やユニオンのタグに使用する名前を同一にしないでください。 typedefの名前が struct名や union名と一致している場合は、この制限はありません。
|
struct t1 {
long s1;
};
typedef struct t1 t1; /* ok */
typedef long t1; /* error */
•
|
構造体やユニオン・タグの宣言をほかの構造体やユニオンの内部で行い、それを外部で参照することはできません。
|
struct t1 {
struct t2 {
long s2;
} s1;
} t1;
typedef struct t3 {
struct t2 s3; /* t2 undefined error */
} t3;
•
|
一部のコンパイラでは警告メッセージが生成される場合があります。次のようなものがあります:
|
•
|
次の場合のように変数が sizeof()関数で参照されているときなど、変数が設定される前に使用されていることを示す警告。
|
long *ptr;
ptr = (long *)malloc(sizeof(*ptr) * 4);
クライアントとサーバーのアプリケーション・ソフトウェアをコーディングしているときは、IDLコンパイラが作成したデータ型、すなわち
rpc/tidlbase.h内で定義されるデータ型を使う必要があります(rpc/tidlbase.h内での定義とは、次の表に「発行されるマクロ」として記載されているものです)。たとえば、
idl_long_intのかわりに
longを使用すると、データ型はプラットフォームによっては32ビットであったり64ビットであったりします。一方、
idl_long_intは全プラットフォームで32ビットです。生成されるデータ型の一覧を
表3-1に示します。
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
32ビット長のマシンの場合: long、64ビット長のマシンの場合: int
|
|
|
|
struct { long high; unsigned long low; }
struct { unsigned long low; long high; }
|
|
|
|
|
|
|
|
|
|
|
|
32ビット長のマシンの場合: long、64ビット長のマシンの場合: int
|
|
|
|
struct { unsigned long high; unsigned long low; }
struct { unsigned long low; unsigned long high; }
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
Cの場合と同様、IDLでも複数のクラスの識別子があります。スコープやネームスペースなどのクラス内では、名前をユニークにする必要があります。
•
|
定数、 typedef、オペレーション、列挙用メンバー名は、1つのネームスペース内にあります。
|
•
|
構造体、ユニオン、および列挙用のタグは、別のネームスペース内におかれます。
|
•
|
構造体やユニオンのメンバー名で同じレベルのものは、定義が行われた構造体やユニオンの中でユニークにする必要があります。
|
•
|
パラメータ名は、定義が行われたオペレーション・プロトタイプ内ではユニークにする必要があります。
|
タグを持たず、
typedefの一部として定義されていない匿名の構造体またはユニオンは、オペレーションの戻り値やパラメータとして使用できないことに注意してください。
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_begin(3c)と、tx_commit(3c)またはtx_rollback(3c)は、通信を含むトランザクション内のすべての作業をカプセル化します。その他のプリミティブとして、トランザクションのタイムアウト設定、連鎖または非連鎖としてのトランザクションの宣言、トランザクション情報の取得も提供されています。前述の詳細は、X/OPEN TX仕様に記載され、またX/OPEN TxRPC仕様にも解説されています。
X/OPEN TxRPC仕様には、TXとRPCの相互動作についての解説があります。
その要点を次に述べます。
•
|
インタフェースまたはオペレーションは、属性 [transaction_optional]を持つことができます。この属性は、あるトランザクション内でRPCを呼び出した場合、呼び出されるオペレーション内で行われる作業は、そのトランザクションの一部になることを示します。
|
•
|
インタフェースまたはオペレーションは、属性 [transaction_mandatory]を持つことができます。この属性は、RPC呼出しはトランザクション内で行う必要があることを示します。そうでなければ例外 txrpc_x_not_in_transactionが返されます。
|
•
|
前述の2つの属性がいずれも指定されない場合には、呼び出されるオペレーション内での作業は、呼出し側でアクティブになるトランザクションの一部にはなりません。
|
•
|
サーバー内でTxRPCオペレーションが呼び出され、関数tx_open(3c)が呼び出されていなければ、例外 txrpc_x_no_tx_open_doneが呼出し側に返されます。
|
•
|
TxRPCでは、オペレーションが関数tx_rollback(3c)を呼び出すことを認めています。同関数を呼び出すことで、トランザクションがロールバックのみとしてマークされ、トランザクションとして行われた作業は、すべてロールバックされます。この場合、アプリケーションも呼出し側にアプリケーション・レベルのエラーを返し、トランザクションがロールバックされることを示すことをお薦めします。
|
TxRPC仕様で定義されたIDLに対するその他の変更と制限については、IDLに関する項ですでに説明しました。