ここでは、次の内容について説明します。
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)
を参照してください。
RPCコールに対して通信とサーバーで生成される例外のほかに、さらに低いレベルの例外、特に、オペレーティング・システムのシグナルも生成されます。このような例外の詳細については、『Oracle Tuxedo Cリファレンス』のTRY(3c)
を参照してください。
X/OPEN仕様では、100を越える膨大な数のランタイム・サポート関数が定義されています。これらの関数を、X/OPEN TxRPC IDL-only環境ですべてサポートする必要はありません。これらの関数の大部分は、ATMIのクライアントやサーバーに対して透過的に行われるバインドと管理に関連したものです。
アプリケーションの移植性に影響を及ぼす事柄の1つに、スタブ用の入出力パラメータと戻り値に割り当てられるメモリーの管理があります。Stub Memory Managementルーチンは、スレッドを扱う2つの関数を除きTxRPCランタイムでサポートされています。状態を返す関数には以下のものがあります。
rpc_sm_allocate
rpc_sm_client_free
rpc_sm_disable_allocate
rpc_sm_enable_allocate
rpc_sm_free
rpc_sm_set_client_alloc_free
rpc_sm_set_server_alloc_free
rpc_sm_swap_client_alloc_free
同等の例外を返す関数には以下のものがあります。
rpc_ss_allocate
rpc_ss_client_free
rpc_ss_disable_allocate
rpc_ss_enable_allocate
rpc_ss_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
宣言が使用されます。こうしないと、クライアント・スタブとサーバー・スタブを同一の実行ファイルにコンパイルできないという制限を生じるか、二重定義エラーが発生することに注意してください。
C++環境では、次のような制限があります。
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に示します。
表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ビット長のマシンの場合: |
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ビット長のマシンの場合: |
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
、オペレーション、列挙用メンバー名は、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に関する項ですでに説明しました。