Oracle Tuxedoシステムには、ATMIサーバーを簡単に開発できるように、サーバーのロード・モジュール用に定義済の
main()ルーチンが提供されています。
buildserverコマンドを実行すると、
main()ルーチンが自動的にサーバーの一部として組み込まれます。
注意:
|
main()はシステムで提供されたルーチンのため、変更することはできません。
|
定義済
main()ルーチンは、アプリケーションへの参加と終了の他に、サーバーにかわって次の操作を行います。
•
|
ハングアップを無視してプロセスを実行します。つまり、 SIGHUPシグナルを無視します。
|
•
|
標準オペレーティング・システム・ソフトウェアの終了シグナル( SIGTERM)を受信すると、終了処理を開始します。サーバーは停止され、必要な場合は再起動します。
|
•
|
掲示板サービスが参照できるように共用メモリーを割り当てます。
|
•
|
サーバーによって提供される初期サービスを通知します。初期サービスは、定義済の main()とリンクされたすべてのサービス、またはOracle Tuxedoシステムの管理者が構成ファイルに指定したサブセットです。
|
•
|
コマンド行に入力された2つのダッシュ( --)までの引数を処理します。2つのダッシュは、システムで認識される引数の終わりを示します。
|
•
|
tpsvrinit()関数を呼び出して、コマンド行で2つのダッシュ( --)の後に入力された引数を処理したり、リソース・マネージャをオープンします(オプション)。このようなコマンド行の引数は、アプリケーション固有の初期化に使用されます。
|
•
|
中止がリクエストされるまで、リクエスト・キューにサービス・リクエスト・メッセージがあるかどうかを確認します。
|
•
|
サービス・リクエスト・メッセージがリクエスト・キューに到着すると、中止がリクエストされるまで、 main()は次の処理を行います。
|
•
|
-rオプションが指定されている場合、サービス・リクエストの開始時間を記録します。
|
•
|
掲示板を更新して、サーバーが BUSYであることを示します。
|
•
|
リクエスト・メッセージ用にバッファを割り当て、サービスにディスパッチします(サービス・サブルーチンを呼び出します)。
|
•
|
サービスが入力に対する処理を終了して制御が戻ると、中止が要求されるまで、 main()は次の処理を行います。
|
•
|
-rオプションが指定されている場合、サービス・リクエストの終了時間を記録します。
|
•
|
掲示板を更新して、サーバーが IDLE状態であること、つまりサーバーの準備ができたことを示します。
|
•
|
キューに次のサービス・リクエストがあるかどうかを確認します。
|
•
|
サーバーの中止が要求されると、 tpsvrdone()を呼び出して必要な停止操作を実行します。
|
以上からわかるように、
main()ルーチンは、アプリケーションへの参加と終了、バッファやトランザクションの管理、および通信に関する詳細を扱っています。
main()ルーチンは、システムで提供される1つのATMIサーバー
AUTHSVR、および2つのサブルーチン
tpsvrinit()と
tpsvrdone()を提供します。次の項で説明するこれらの3つのデフォルト・バージョンについては、ご使用のアプリケーションに合せて変更できます。
注意:
|
tpsvrinit()と tpsvrdone()を独自にコーディングする場合、この2つのルーチンのデフォルト・バージョンが、それぞれ tx_open()と tx_close()を呼び出すことに注意してください。 tx_open()ではなく tpopen()を呼び出す tpsvrinit()の新しいバージョンをコーディングする場合は、 tpclose()を呼び出す tpsvrdone()もコーディングする必要があります。つまり、この2つの関数が呼び出すオープンとクローズの関数は対になっている必要があります。
|
システムで提供されるサーバー: AUTHSVR( )
AUTHSVR(5)サーバーを使用すると、アプリケーションで各クライアントの認証を行うことができます。このサーバーは、アプリケーションのセキュリティ・レベルが
TPAPPAUTHに設定されている場合に、
tpinit()関数によって呼び出されます。
AUTHSVRのサービスは、
TPINITバッファの
dataフィールドでユーザー・パスワードを確認します(
TPINITバッファの
passwdフィールドに指定されているアプリケーション・パスワードと混同しないでください)。デフォルトでは、システムによって
dataから文字列が取得され、それと合致する文字列が
/etc/passwdファイルで検索されます。
tpinit()がネイティブ・サイトのクライアントから呼び出された場合、受信した
dataフィールドはそのまま転送されます。そのため、アプリケーションでパスワードの暗号化が必要な場合、それに応じてクライアント・プログラムをコーディングする必要があります。
tpinit()がワークステーション・クライアントから呼び出された場合、データは暗号化されてからネットワークに送信されます。
システムで提供されるサービス: tpsvrinit( )関数
サーバーの起動時に、Oracle Tuxedoシステムの
main()はその初期化時、つまりサービス・リクエストの処理を開始する前に、
tpsvrinit(3c)を呼び出します。
アプリケーションがこの関数のカスタム・バージョンをサーバーに提供していない場合、
main()で提供されるデフォルトの関数が使用され、この関数は、リソース・マネージャをオープンし、エントリを中央イベント・ログに記録してサーバーの起動が成功したことを示します。中央ユーザー・ログは自動的に生成されるファイルで、
userlog(3c)関数を呼び出して、プロセスがこのファイルにメッセージを書き込みます。中央イベント・ログの詳細は、
11-1ページの「エラーの管理」を参照してください。
tpsvrinit()関数を使用すると、アプリケーションで要求される次のような初期化プロセスを行うことができます。
以降の項では、
tpsvrinit()を呼び出すことによって、これらの初期化タスクがどのように行われるのかをサンプル・コードで示します。次のサンプル・コードでは示していませんが、このルーチン内ではメッセージ交換を行うこともできます。ただし、非同期応答が未処理のままで
tpsvrinit()ルーチンが制御を戻すと、このルーチンは失敗します。その場合、Oracle Tuxedoシステムでは応答は無視されて、サーバーが正常に終了します。
tpsvrinit()関数の呼出しには、次のシグネチャを使用します。
int
tpsvrinit(int
argc, char **argv)
サーバーは、起動時に最初の処理として、構成ファイルに指定されているサーバー・オプションをEOFまで読み取ります。その場合、サーバーはーUNIX関数
getopt(3)を呼び出します。コマンド行に2つのダッシュ(
--)が出現した時点で、
getopt()関数はEOFを戻します。
getopt関数は、次に処理する引数の
argv索引を外部変数
optindに設定します。その後、定義済の
main()関数が
tpsvrinit()を呼び出します。
リスト5-1は、
tpsvrinit()関数を使用してコマンド行オプションを取得する方法を示しています。
リスト5-1
tpsvrinit()を使用したコマンド行オプションの取得
tpsvrinit(argc, argv)
int argc;
char **argv;
{
int c;
extern char *optarg;
extern int optind;
.
.
.
while((c = getopt(argc, argv, "f:x:")) != EOF)
switch(c){
.
.
.
}
.
.
.
}
main()が
tpsvrinit()を呼び出すとき、コマンド行の2つのダッシュ(
--)の後の引数を受け取ります。上の例では、オプション
fと
xそれぞれがコロンで示される引数を取ります。
optargが、オプションの引数の開始位置を示します。switch文のロジックは省略されています。
次のサンプル・コードは、
tpsvrinit()のもう1つの使用法として、リソース・マネージャをオープンする方法を示しています。Oracle Tuxedoシステムには、リソース・マネージャをオープンする関数として、
tpopen(3c)と
tx_open(3c)があります。また、これらと対の関数として、
tpclose(3c)と
tx_close(3c)があります。これらの関数を使用してリソース・マネージャをオープンしたりクローズするアプリケーションは、その意味では移植性があります。これらは、構成ファイルに設定されたリソース・マネージャのインスタンス固有の情報にアクセスすることによって動作します。
これらの関数呼出しは省略可能で、リソース・マネージャがデータベースの場合、リソース・マネージャ固有の呼出しがデータ操作言語(DML)の一部であるときは、その呼出しのかわりに使用できます。
userlog(3c)関数を使用して、中央イベント・ログに書き込んでいることに注目してください。
注意:
|
コマンド行オプションを受け取り、データベースをオープンする初期化関数を作成するには、 リスト5-2に示すサンプル・コードと前述のサンプル・コードを組み合せます。
|
リスト5-2
tpsvrinit()を使用したリソース・マネージャのオープン
tpsvrinit()
{
/* Open database */
if (tpopen() == -1) {
(void)userlog("tpsvrinit: failed to open database: ");
switch (tperrno) {
case TPESYSTEM:
(void)userlog("System error\n");
break;
case TPEOS:
(void)userlog("Unix error %d\n",Uunixerr);
break;
case TPEPROTO:
(void)userlog("Called in improper context\n");
break;
case TPERMERR:
(void)userlog("RM failure\n");
break;
}
return(-1); /* causes the server to exit */
}
return(0);
}
初期化時にエラーが発生しないように、サーバーを終了してからサービス・リクエストの処理を開始するように
tpsvrinit()をコーディングできます。
システムで提供されるサービス: tpsvrdone( )関数
tpsvrinit()が
tpopen()を呼び出してリソース・マネージャをオープンするのと同じように、
tpsvrdone()関数は
tpclose()を呼び出してリソース・マネージャをクローズします。
tpsvrdone()関数の呼出しには、次のシグネチャを使用します。
void
tpsvrdone
() /* Server termination routine */
tpsvrdone()関数に引数は必要ありません。
アプリケーションに
tpsvrdone()のクローズ用ルーチンが定義されていない場合、
main()で提供されるデフォルトのルーチンがOracle Tuxedoシステムによって呼び出されます。このルーチンは、
tx_close()と
userlog()を呼び出してリソース・マネージャをクローズし、中央イベント・ログに書き込みます。ログには、サーバーが間もなく終了することを伝えるメッセージが送信されます。
ttpsvrdone()は、サーバーがサービス・リクエストの処理を終了した後、サーバーが終了する前に呼び出されます。その場合、サーバーはまだシステムの一部なので、特定の規則に従っているかぎり、ルーチン内でさらに通信およびトランザクションが行われる場合があります。これらの規則については、
11-1ページの「エラーの管理」を参照してください。
リスト5-3は、
tpsvrdone()関数を使用して、リソース・マネージャをクローズして終了する方法を示しています。
リスト5-3
tpsvrdone()を使用したリソース・マネージャのクローズ
void
tpsvrdone()
{
/* Close the database */
if(tpclose() == -1)
(void)userlog("tpsvrdone: failed to close database: ");
switch (tperrno) {
case TPESYSTEM:
(void)userlog("BEA TUXEDO error\n");
break;
case TPEOS:
(void)userlog("Unix error %d\n",Uunixerr);
break;
case TPEPROTO:
(void)userlog("Called in improper context\n");
break;
case TPERMERR:
(void)userlog("RM failure\n");
break;
}
return;
}
return;
}
通信の詳細はOracle Tuxedoシステムの
main()ルーチンによって処理されるため、通信の実装よりもアプリケーション・サービスのロジックに集中できます。ただし、システムで提供される
main()と互換性を保つために、アプリケーション・サービスが特定の規則に従っている必要があります。これらの規則は、サービス・ルーチンをコーディングするためのサービス・テンプレートとも呼ばれます。次に、これらの規則についてまとめます。これらの規則の詳細は、
『Oracle Tuxedo ATMI C言語関数リファレンス』の
「tpservice(3c)」リファレンス・ページを参照してください。
•
|
リクエスト/レスポンス・サービスが一度に受信できるリクエストの数は1つだけであり、送信できる応答も1つだけです。
|
•
|
リクエスト/レスポンス・サービスが一度に処理するリクエストは1つだけです。別のリクエストを受け取ることができるのは、リクエスタに応答を送信した後、または別の処理を行うために別のサービスにリクエストを転送した後だけです。
|
•
|
サービス・ルーチンを終了するには、 tpreturn()または tpforward()関数のいずれかを呼び出す必要があります。これらの関数は、C言語の return文と同じように機能しますが、処理が終了すると、呼出し側の関数ではなくOracle Tuxedoシステムの main()関数に制御が戻ります。
|
•
|
サービス・ルーチンは、1つの引数、つまり svcinfoを使用して呼び出され、この引数は、サービス情報が定義された構造体( TPSVCINFO)を指すポインタです。
|
すべてのサービス・ルーチンは、
TPSVCINFO構造体を指すポインタで構成される1つの引数を受け取る関数として定義する必要があります。
TPSVCINFO構造体は、
atmi.hヘッダー・ファイルに定義され、次の情報が含まれています。
char
name[32];
long
flags;
char *
data;
long
len;
int
cd;
int
appkey;
CLIENTID
cltid;
表5-1は、
TPSVCINFOデータ構造体をまとめたものです。
|
|
|
リクエスト元プロセスがサービスの呼出しに使用する名前をサービス・ルーチンに対して指定します。
|
|
サービスがトランザクション・モードであるかどうか、または呼出し側が応答を要求しているかどうかをサービスに通知します。サービスをトランザクション・モードにする各方法については、 9-1ページの「グローバル・トランザクションのコーディング」を参照してください。
TPTRANフラグは、サービスがトランザクション・モードであることを示します。 tpcall()または tpacall()の flagsパラメータに TPNOTRANを設定してサービスを呼び出した場合、サービスは現在のトランザクションに参加できません。ただし、トランザクション・モードでサービスを実行することはできます。つまり、呼出し側によって TPNOTRAN通信フラグが設定されていても、 TPTRANを svcinfo->flagsに設定できます。このような状況の例については、 9-1ページの「グローバル・トランザクションのコーディング」を参照してください。
tpacall()で TPNOREPLY通信フラグが設定されてサービスが呼び出された場合、 flagsメンバーは TPNOREPLYに設定されます。呼び出されたサービスが、呼出し側プロセスと同じトランザクションに含まれる場合は、呼出し側に応答を戻す必要があります。
|
|
main()内で tpalloc()によってすでに割り当てられているバッファを指すポインタ。このバッファは、リクエスト・メッセージの受信に使用されます。ただし、応答メッセージの返信やリクエスト・メッセージの転送にも、このバッファを使用することをお薦めします。
|
|
dataフィールドによって参照されるバッファ内のリクエスト・データの長さを格納します。
|
|
|
|
アプリケーションで使用するために予約された値。アプリケーション固有の認証が設計に含まれている場合、クライアントがアプリケーションに参加するときに呼び出されるアプリケーション固有の認証サーバーによって、認証の成功または失敗を示す値、およびクライアント認証キーが戻される必要があります。Oracle Tuxedoシステムは、 appkeyをクライアントのために保持し、このフィールドに情報を格納して以降のサービス・リクエストに渡します。 appkeyがサービスに渡されたときは、クライアントの認証は終了しています。ただし、サービス内で appkeyフィールドを使用して、サービスを呼び出したユーザー、またはそのユーザーに関するその他のパラメータを識別できます。
このフィールドが使用されていない場合は、デフォルト値の -1が割り当てられます。
|
|
クライアントの識別子の保持に使用される CLIENTID型の構造体。この構造体は変更できません。
|
プロセスが
TPSVCINFO構造体の
dataフィールドにアクセスする場合、次のバッファ・タイプが合致する必要があります。
•
|
呼出し側のプロセスによって渡されるリクエスト・バッファのタイプ
|
•
|
呼び出されたサービスに定義された対応するバッファ・コードのタイプ
|
•
|
呼び出されたサービスに対して構成ファイルに定義された対応するバッファのタイプ
|
リスト5-4は、一般的なサービス定義を示しています。このコードは、Oracle Tuxedoソフトウェアで提供される銀行業務アプリケーションの
ABAL(残高照会)サービス・ルーチンから引用したものです。
ABALは
BALサーバーの一部です。
#include <stdio.h> /* UNIX */
#include <atmi.h> /* BEA Tuxedo System */
#include <sqlcode.h> /* BEA Tuxedo System */
#include "bank.flds.h" /* bankdb fields */
#include "aud.h" /* BANKING view defines */
EXEC SQL begin declare section;
static long branch_id; /* branch id */
static float bal; /* balance */
EXEC SQL end declare section;
/*
* Service to find sum of the account balances at a SITE
*/
void
#ifdef __STDC__
ABAL(TPSVCINFO *transb)
#else
ABAL(transb)
TPSVCINFO *transb;
#endif
{
struct aud *transv; /* view of decoded message */
/* Set pointer to TPSVCINFO data buffer */
transv = (struct aud *)transb->data;
set the consistency level of the transaction
/* Get branch id from message, do query */
EXEC SQL declare acur cursor for
select SUM(BALANCE) from ACCOUNT;
EXEC SQL open acur; /* open */
EXEC SQL fetch acur into :bal; /* fetch */
if (SQLCODE != SQL_OK) { /* nothing found */
(void)strcpy (transv->ermsg,"abal failed in sql aggregation");
EXEC SQL close acur;
tpreturn(TPFAIL, 0, transb->data, sizeof(struct aud), 0);
}
EXEC SQL close acur;
transv->balance = bal;
tpreturn (TPSUCCESS, 0, transb->data, sizeof(struct aud), 0);
}
前述の例では、アプリケーションがクライアント側にリクエスト・バッファを割り当てるために、
tpalloc()の呼出しで
typeパラメータに
VIEW、
subtypeに
audが設定されています。
ABALサービスは、
VIEW型付きバッファをサポートするサービスとして定義されています。
ABALには
BUFTYPEパラメータは指定されず、デフォルト値の
ALLが使用されます。
ABALサービスは
VIEW型のバッファを割り当て、このバッファへのポインタに
ABALサブルーチンが渡された
TPSVCINFO構造体の
dataメンバーを割り当てます。
ABALサーバーは、前述のサンプル・コードに示してあるように、対応する
dataメンバーにアクセスして適切なデータ・バッファを取得します。
この項のコード例は、サービスが
TPSVCINFO構造体で定義されたデータ・バッファにアクセスして、
tptypes()関数を使用して型を判別する方法を示します。(このプロセスは、
2-16ページの「バッファ・タイプの確認」で説明しています。)このサービスでは、バッファの領域を再割り当てするかどうかを確認するために、バッファの最大サイズもチェックされます。
このサンプル・コードは、Oracle Tuxedoソフトウェアで提供される銀行業務アプリケーションの
ABALサービスから引用したものです。これは、
aud VIEWまたは
FMLのいずれかのバッファとしてリクエストを受け入れるサービスをコーディングする方法を示しています。メッセージ・タイプの確認が失敗した場合、エラー・メッセージの文字列と該当の戻りコードが戻され、成功した場合は、バッファ・タイプに合致したコードのセグメントが実行されます。
tpreturn()関数の詳細は、
5-16ページの「サービス・ルーチンの終了」を参照してください。
#define TMTYPERR 1 /* return code indicating tptypes failed */
#define INVALMTY 2 /* return code indicating invalid message type */
void
ABAL(transb)
TPSVCINFO *transb;
{
struct aud *transv; /* view message */
FBFR *transf; /* fielded buffer message */
int repc; /* tpgetrply return code */
char typ[TMTYPELEN+1], subtyp[TMSTYPELEN+1]; /* type, subtype of message */
char *retstr; /* return string if tptypes fails */
/* find out what type of buffer sent */
if (tptypes((char *)transb->data, typ, subtyp) == -1) {
retstr=tpalloc("STRING", NULL, 100);
(void)sprintf(retstr,
"Message garbled; tptypes cannot tell what type message\n");
tpreturn(TPFAIL, TMTYPERR, retstr, 100, 0);
}
/* Determine method of processing service request based on type */
if (strcmp(typ, "FML") == 0) {
transf = (FBFR *)transb->data;
... code to do abal service for fielded buffer ...
tpreturn succeeds and sends FML buffer in reply
}
else if (strcmp(typ, "VIEW") == 0 && strcmp(subtyp, "aud") == 0) {
transv = (struct aud *)transb->data;
... code to do abal service for aud struct ...
tpreturn succeeds and sends aud view buffer in reply
}
else {
retstr=tpalloc("STRING", NULL, 100);
(void)sprintf(retstr,
"Message garbled; is neither FML buffer nor aud view\n");
tpreturn(TPFAIL, INVALMTY, retstr, 100, 0);
}
}
この項で示すサンプル・コードは、
PRINTERという名前のサービスが
tpgprio()関数を使用して、受信したばかりのリクエストの優先度を確認する方法を示しています。その後、その優先度に基づいて印刷ジョブが適切なプリンタに送られ、そのプリンタに
pbuf−>dataの内容がパイプされています。
#include <stdio.h>
#include "atmi.h"
char *roundrobin();
PRINTER(pbuf)
TPSVCINFO *pbuf; /* print buffer */
{
char prname[20], ocmd[30]; /* printer name, output command */
long rlen; /* return buffer length */
int prio; /* priority of request */
FILE *lp_pipe; /* pipe file pointer */
prio=tpgprio();
if (prio <= 20)
(void)strcpy(prname,"bigjobs"); /* send low priority (verbose)
jobs to big comp. center
laser printer where operator
sorts output and puts it
in a bin */
else if (prio <= 60)
(void)strcpy(prname,roundrobin()); /* assign printer on a
rotating basis to one of
many local small laser printers
where output can be picked
up immediately; roundrobin() cycles
through list of printers */
else
(void)strcpy(prname,"hispeed");
/* assign job to high-speed laser
printer; reserved for those who
need verbose output on a daily,
frequent basis */
(void)sprintf(ocmd, "lp -d%s", prname); /* output lp(1) command */
lp_pipe = popen(ocmd, "w"); /* create pipe to command */
(void)fprintf(lp_pipe, "%s", pbuf->data); /* print output there */
(void)pclose(lp_pipe); /* close pipe */
if ((pbuf->flags & TPNOREPLY))
tpreturn(TPSUCCESS, 0, NULL, 0, 0);
rlen = strlen(prname) + 1;
pbuf->data = tprealloc(pbuf->data, rlen); /* ensure enough space for name */
(void)strcpy(pbuf->data, prname);
tpreturn(TPSUCCESS, 0, pbuf->data, rlen, 0);
char *
roundrobin()
{
static char *printers[] = {"printer1", "printer2", "printer3", "printer4"};
static int p = 0;
if (p > 3)
p=0;
return(printers[p++]);
}
•
|
tpreturn()は、呼出し側クライアントに応答を送信します。
|
•
|
tpcancel()は、現在のリクエストを取り消します。
|
•
|
tpforward()は、別の処理を行うために別のサービスにサービス・リクエストを転送します。
|
tpreturn(3c)関数はサービス・ルーチンの終了を示し、リクエスタにメッセージを送ります。
tpreturn()関数の呼出しには、次のシグネチャを使用します。
void
tpreturn(int
rval, int rcode, char *data, long len, long flags)
表5-2は、
tpreturn()関数の引数を示しています。
|
|
|
サービスが正常に終了したかどうかをアプリケーション・レベルで示す値。この値は、シンボリック名で表される整数値です。有効な設定は、次のとおりです。
•
|
TPSUCCESS - 関数呼出しが成功したことを示します。関数は、応答メッセージを呼出し側のバッファに格納します。つまり、応答メッセージがある場合は、呼出し側のバッファ内にあります。
|
•
|
TPFAIL(デフォルト) - サービスが失敗したことを示します。関数は、応答を待つクライアント・プロセスにエラー・メッセージを通知します。その場合、クライアントが呼び出した tpcall()または tpgetrply()関数が失敗し、変数 tperrno(5)にアプリケーション定義の失敗を示す TPESVCFAILが設定されます。 応答メッセージが要求されている場合、呼出し側のバッファから取得できます。
|
•
|
TPEXIT - サービスが失敗したことを示します。関数は、応答を待つクライアント・プロセスにエラー・メッセージを通知して終了します。
|
この引数の値がグローバル・トランザクションに与える影響については、 9-1ページの「グローバル・トランザクションのコーディング」を参照してください。
|
|
アプリケーション定義の戻りコードを呼出し側に返します。クライアントは、グローバル変数 tpurcode(5)に対して問合せを実行して、 rcodeに返された値にアクセスできます。成功または失敗に関係なく、このコードは関数から返されます。
|
|
クライアント・プロセスに返される応答メッセージを指すポインタ。メッセージ・バッファは、 tpalloc()を使用して事前に割り当てられていることが必要です。
SVCINFO構造体のサービスに渡されたバッファと同じものを使用する場合は、バッファの割り当ておよび解放について意識する必要はありません。この2つの処理は、システムで提供される main()関数で行われます。このバッファは、 tpfree()コマンドを使用しても解放できません。このコマンドを使用してバッファの解放を試みると失敗します。 tprealloc()関数を使用すると、バッファのサイズを変更できます。
サービス・ルーチンに渡されたバッファとは別のバッファを使ってメッセージを返す場合、自分でそのバッファを割り当てる必要があります。アプリケーション側で tpreturn()関数が呼び出されると、バッファは自動的に解放されます。
応答メッセージを返す必要がない場合は、この引数にNULLポインタを設定します。
注意:
|
クライアントに応答を返す必要がない場合、つまり TPNOREPLYが設定されている場合、 tpreturn()関数は dataと len引数を無視して main()に制御を戻します。
|
|
|
応答バッファの長さ。アプリケーションはこの引数の値に、 tpcall()関数の olenパラメータ、または tpgetrply()関数の lenパラメータを使用してアクセスできます。
クライアントとして動作するプロセスは、この戻り値を使用して、応答バッファのサイズが大きくなったかどうか調べることができます。
クライアントが応答を要求していて応答バッファにデータが存在していない場合、つまり data引数にNULLポインタが設定されている場合、クライアントのバッファを変更せずに長さがゼロの応答が送信されます。
data引数が指定されていない場合、この引数の値は無視されます。
|
|
|
サービス・ルーチンの主なタスクは、リクエストを処理してクライアント・プロセスに応答を返すことです。ただし、要求されたタスクを行うために必要なすべての処理を1つのサービスで行う必要はありません。サービスはリクエスタとして動作し、クライアントが元のリクエストを行ったときと同じように、
tpcall()または
tpacall()を呼び出して、リクエスト呼出しを別のサービスに渡すことができます。
tpreturn()が呼び出された場合、常に
main()関数に制御が戻ります。非同期応答でサービスがリクエストを送信している場合、
main()に制御を戻す前にすべての応答を受信するか、または
tpcancel()を使用してすでに送信したリクエストを無効にする必要があります。それ以外の場合、未処理の応答はOracle Tuxedoシステムの
main()で受信されると自動的に破棄され、呼出し側にエラーが返されます。
応答が必要な場合に、
tpreturn()の引数の処理時にエラーが発生すると、呼出し側プロセスに
失敗を示すメッセージが送信されます。呼出し側は、
tperrnoに格納されている値を調べてエラーを検出します。失敗を示すメッセージが送信された場合、
tperrnoに
TPESVCERRが設定されます。この値は、グローバル変数
tpurcodeの値よりも優先されます。このようなエラーが発生した場合、応答データは返されず、呼出し側の出力バッファの内容と長さは変更されません。
tpreturn()が不明なタイプのバッファにメッセージを返すか、または呼出し側で使用できないバッファにメッセージを返した場合、つまり
flagsに
TPNOCHANGEが設定されて呼出しが行われた場合、
tperrno(5)に
TPEOTYPEが返されます。その場合、アプリケーションの成功また失敗は判定されず、呼出し側の出力バッファの内容と長さは変更されません。
ここで示すサンプル・コードは、
XFERサーバーの一部である
TRANSFERサービスを示しています。基本的に、
TRANSFERサービスは
WITHDRAWALおよび
DEPOSITサービスへの同期呼出しを行います。このサービスでは、
WITHDRAWALと
DEPOSITの両サービスの呼出しに同じリクエスト・バッファを使用する必要があるので、応答メッセージ用に別のバッファが割り当てられます。
WITHDRAWALの呼出しが失敗した場合、フォーム上のステータス行に
「cannot withdraw」というメッセージが出力され、応答バッファが解放され、
tpreturn()関数の
rval引数に
TPFAILが設定されます。呼出しが成功した場合、振替元口座の残高が応答バッファから取得されます。
注意:
|
次のサンプル・コードでは、フィールド化バッファ transf内の ACCOUNT_IDフィールドのゼロ番目のオカレンスに、アプリケーションが cr_id変数から取得した振替先口座の識別子を移動しています。このような移動が必要なのは、 FMLバッファ内のフィールドのこのオカレンスが、データ依存型ルーティングに使用されるからです。詳細は、 『Oracle Tuxedoアプリケーションの設定』を参照してください。
|
DEPOSITサービスへの呼出しは、同様のシナリオに従います。呼出しが成功すると、このサービスによってサービス・ルーチンに割り当てられていた応答バッファが解放され、
rval引数に
TPSUCCESSが設定されて、適切な残高情報がステータス行に返されます。
#include <stdio.h> /* UNIX */
#include <string.h> /* UNIX */
#include "fml.h" /* BEA Tuxedo System */
#include "atmi.h" /* BEA Tuxedo System */
#include "Usysflds.h" /* BEA Tuxedo System */
#include "userlog.h" /* BEA Tuxedo System */
#include "bank.h" /* BANKING #defines */
#include "bank.flds.h" /* bankdb fields */
/*
* Service to transfer an amount from a debit account to a credit
* account
*/
void
#ifdef __STDC__
TRANSFER(TPSVCINFO *transb)
#else
TRANSFER(transb)
TPSVCINFO *transb;
#endif
{
FBFR *transf; /* fielded buffer of decoded message */
long db_id, cr_id; /* from/to account id���s */
float db_bal, cr_bal; /* from/to account balances */
float tamt; /* amount of the transfer */
FBFR *reqfb; /* fielded buffer for request message*/
int reqlen; /* length of fielded buffer */
char t_amts[BALSTR]; /* string for transfer amount */
char db_amts[BALSTR]; /* string for debit account balance */
char cr_amts[BALSTR]; /* string for credit account balance */
/* Set pointr to TPSVCINFO data buffer */
transf = (FBFR *)transb->data;
/* Get debit (db_id) and credit (cr_id) account IDs */
/* must have valid debit account number */
if (((db_id = Fvall(transf, ACCOUNT_ID, 0)) < MINACCT) || (db_id > MAXACCT)) {
(void)Fchg(transf, STATLIN, 0,"Invalid debit account number",(FLDLEN)0);
tpreturn(TPFAIL, 0, transb->data, 0L, 0);
}
/* must have valid credit account number */
if ((cr_id = Fvall(transf, ACCOUNT_ID, 1)) < MINACCT || cr_id > MAXACCT) {
(void)Fchg(transf,STATLIN, 0,"Invalid credit account number",(FLDLEN)0);
tpreturn(TPFAIL, 0, transb->data, 0L, 0);
}
/* get amount to be withdrawn */
if (Fget(transf, SAMOUNT, 0, t_amts, < 0) 0 || strcmp(t_amts,"") == 0) {
(void)Fchg(transf, STATLIN, 0, "Invalid amount",(FLDLEN)0);
tpreturn(TPFAIL, 0, transb->data, 0L, 0);
}
(void)sscanf(t_amts,"%f",tamt);
/* must have valid amount to transfer */
if (tamt = 0.0) {
(void)Fchg(transf, STATLIN, 0,
"Transfer amount must be greater than $0.00",(FLDLEN)0);
tpreturn(TPFAIL, 0, transb->data, 0L, 0);
}
/* make withdraw request buffer */
if ((reqfb = (FBFR *)tpalloc("FML",NULL,transb->len)) == (FBFR *)NULL) {
(void)userlog("tpalloc failed in transfer\n");
(void)Fchg(transf, STATLIN, 0,
"unable to allocate request buffer", (FLDLEN)0);
tpreturn(TPFAIL, 0, transb->data, 0L, 0);
}
reqlen = Fsizeof(reqfb);
/* put ID in request buffer */
(void)Fchg(reqfb,ACCOUNT_ID,0,(char *)&db_id, (FLDLEN)0);
/* put amount in request buffer */
(void)Fchg(reqfb,SAMOUNT,0,t_amts, (FLDLEN)0);
/* increase the priority of withdraw call */
if (tpsprio(PRIORITY, 0L) == -1)
(void)userlog("Unable to increase priority of withdraw\n");
if (tpcall("WITHDRAWAL", (char *)reqfb,0, (char **)&reqfb,
(long *)&reqlen,TPSIGRSTRT) == -1) {
(void)Fchg(transf, STATLIN, 0,
"Cannot withdraw from debit account", (FLDLEN)0);
tpfree((char *)reqfb);
tpreturn(TPFAIL, 0,transb->data, 0L, 0);
}
/* get "debit" balance from return buffer */
(void)strcpy(db_amts, Fvals((FBFR *)reqfb,SBALANCE,0));
void)sscanf(db_amts,"%f",db_bal);
if ((db_amts == NULL) || (db_bal < 0.0)) {
(void)Fchg(transf, STATLIN, 0,
"illegal debit account balance", (FLDLEN)0);
tpfree((char *)reqfb);
tpreturn(TPFAIL, 0, transb->data, 0L, 0);
}
/* put deposit account ID in request buffer */
(void)Fchg(reqfb,ACCOUNT_ID,0,(char *)&cr_id, (FLDLEN)0);
/* put transfer amount in request buffer */
(void)Fchg(reqfb,SAMOUNT,0,t_amts, (FLDLEN)0);
/* Up the priority of deposit call */
if (tpsprio(PRIORITY, 0L) == -1)
(void)userlog("Unable to increase priority of deposit\n");
/* Do a tpcall to deposit to second account */
if (tpcall("DEPOSIT", (char *)reqfb, 0, (char **)&reqfb,
(long *)&reqlen, TPSIGRSTRT) == -1) {
(void)Fchg(transf, STATLIN, 0,
"Cannot deposit into credit account", (FLDLEN)0);
tpfree((char *)reqfb);
tpreturn(TPFAIL, 0,transb->data, 0L, 0);
}
/* get "credit" balance from return buffer */
(void)strcpy(cr_amts, Fvals((FBFR *)reqfb,SBALANCE,0));
(void)sscanf(cr_amts,"%f",&cr_bal);
if ((cr_amts == NULL) || (cr_bal 0.0)) {
(void)Fchg(transf, STATLIN, 0,
"Illegal credit account balance", (FLDLEN)0);
tpreturn(TPFAIL, 0, transb->data, 0L, 0);
}
/* set buffer for successful return */
(void)Fchg(transf, FORMNAM, 0, "CTRANSFER", (FLDLEN)0);
(void)Fchg(transf, SAMOUNT, 0, Fvals(reqfb,SAMOUNT,0), (FLDLEN)0);
(void)Fchg(transf, STATLIN, 0, "", (FLDLEN)0);
(void)Fchg(transf, SBALANCE, 0, db_amts, (FLDLEN)0);
(void)Fchg(transf, SBALANCE, 1, cr_amts, (FLDLEN)0);
tpfree((char *)reqfb);
tpreturn(TPSUCCESS, 0,transb->data, 0L, 0);
}
tpcancel()関数の呼出しには、次のシグネチャを使用します。
cd (呼出し記述子)引数には、取り消すプロセスを指定します。
tpcancel()はトランザクション応答、つまり
TPNOTRANフラグが設定されていない状態で呼び出されたリクエストへの応答には使用できません。トランザクション内では、
tpabort(3c)がトランザクションの呼出し記述子を無効にします。
次のサンプル・コードは、タイムアウト後の応答を無効にする方法を示しています。
int cd1;
.
.
.
if ((cd1=tpacall(sname, (char *)audv, sizeof(struct aud),
TPNOTRAN)) == -1) {
.
.
.
}
if (tpgetrply(cd1, (char **)&audv,&audrl, 0) == -1) {
if (tperrno == TPETIME) {
tpcancel(cd1);
.
.
.
}
}
tpreturn(TPSUCCESS, 0,NULL, 0L, 0);
tpforward()関数の呼出しには、次のシグネチャを使用します。
void
tpforward(char *
svc, char *data, long len, long flags)
表5-3は、
tpreturn()関数の引数を示しています。
|
|
|
リクエストが転送されるサービス名を指す文字型のポインタ。
|
|
クライアント・プロセスに返される応答メッセージを指すポインタ。メッセージ・バッファは、 tpalloc()を使用して事前に割り当てられていることが必要です。
SVCINFO構造体のサービスに渡されたバッファと同じものを使用する場合は、バッファの割り当ておよび解放について意識する必要はありません。この2つの処理は、システムで提供される main()関数で行われます。このバッファは、 tpfree()コマンドを使用しても解放できません。このコマンドを使用してバッファの解放を試みると失敗します。 tprealloc()関数を使用すると、バッファのサイズを変更できます。
サービス・ルーチンに渡されたバッファとは別のバッファを使ってメッセージを返す場合、自分でそのバッファを割り当てる必要があります。アプリケーション側で tpreturn()関数が呼び出されると、バッファは自動的に解放されます。
応答メッセージを返す必要がない場合は、この引数にNULLポインタを設定します。
注意:
|
クライアントに応答を返す必要がない場合、つまり TPNOREPLYが設定されている場合、 tpreturn()関数は dataと len引数を無視して main()に制御を戻します。
|
|
|
応答バッファの長さ。アプリケーションはこの引数の値に、 tpcall()関数の olenパラメータ、または tpgetrply()関数の lenパラメータを使用してアクセスできます。
クライアントとして動作するプロセスは、この戻り値を使用して、応答バッファのサイズが大きくなったかどうか調べることができます。
クライアントが応答を要求していて応答バッファにデータが存在していない場合、つまり data引数にNULLポインタが設定されている場合、クライアントのバッファを変更せずに長さがゼロの応答が送信されます。
data引数が指定されていない場合、この引数の値は無視されます。
|
|
|
tpforward()は、サービス呼出しとは異なります。つまり、リクエストの転送元サービスでは、応答はリクエストされていません。応答を返すのは、リクエストの転送先サービスです。 このサービスを転送されたサービスが、リクエストの発信元プロセスに応答を返します。後者のサービスが、リクエストの発信元プロセスに応答を送信します。転送が連鎖的に行われる場合、最後のサーバーが
tpreturn()を呼び出して、要求の発信元であるクライアントに応答を返します。
図5-1は、あるサービスから別のサービスにリクエストを転送したときのイベントの流れを示しています。ここでは、クライアントは
tpcall()関数を使用してリクエストを開始し、連鎖の最後のサービス(サービス
SVC_C)が
tpreturn()関数を使用して応答を返しています。
サービス・ルーチンは
tpsprio()関数を使用して、クライアント・プロセスがリクエストを送るのと同じように、指定された優先度に従ってリクエストを転送できます。
プロセスが
tpforward()を呼び出すと、システムで提供された
main()に制御が戻り、サーバー・プロセスは別のリクエストを処理できるようになります。
注意:
|
クライアントとして動作するサーバー・プロセスが応答をリクエストする場合、このサーバーが自分自身からサービスをリクエストすることはできません。つまり、必要なサービスの唯一のインスタンスがリクエストを行っているサーバー・プロセスからのみ提供される場合、その呼出しは失敗して再帰呼出しができないことが示されます。ただし、 TPNOREPLY通信フラグが設定された状態でサービス・ルーチンが自分宛てにリクエストを送信または転送した場合、サービスは自分からの応答を待機しないので、呼出しは失敗しません。
|
tpforward()呼出しを使用して、その呼出しを行った時点まで要求の処理が成功していたことを示すことができます。アプリケーション・エラーが検出されなかった場合、
tpforward()を呼び出します。エラーが検出された場合、
rvalに
TPFAILを設定して
tpreturn()を呼び出します。
リスト5-9は、
ACCTサーバーの一部である
OPEN_ACCTサービス・ルーチンから引用したものです。この例は、
tpforward()を呼び出して、サービスがそのデータ・バッファを
DEPOSITサービスに送る方法を示しています。このコードは、
SQLCODEをテストして、口座の挿入が成功したかどうかを調べています。新規口座の追加が成功した場合、支店レコードが更新されてその口座が反映され、データ・バッファが
DEPOSITサービスに転送されます。失敗した場合、
rvalに
TPFAILが設定されて
tpreturn()が呼び出され、失敗を示すメッセージがフォーム上のステータス行に出力されます。
...
/* set pointer to TPSVCINFO data buffer */
transf = (FBFR *)transb->data;
...
/* Insert new account record into ACCOUNT*/
account_id = ++last_acct; /* get new account number */
tlr_bal = 0.0; /* temporary balance of 0 */
EXEC SQL insert into ACCOUNT (ACCOUNT_ID, BRANCH_ID, BALANCE,
ACCT_TYPE, LAST_NAME, FIRST_NAME, MID_INIT, ADDRESS, PHONE) values
(:account_id, :branch_id, :tlr_bal, :acct_type, :last_name,
:first_name, :mid_init, :address, :phone);
if (SQLCODE != SQL_OK) { /* Failure to insert */
(void)Fchg(transf, STATLIN, 0,
"Cannot update ACCOUNT", (FLDLEN)0);
tpreturn(TPFAIL, 0, transb->data, 0L, 0);
}
/* Update branch record with new LAST_ACCT */
EXEC SQL update BRANCH set LAST_ACCT = :last_acct where BRANCH_ID = :branch_id;
if (SQLCODE != SQL_OK) { /* Failure to update */
(void)Fchg(transf, STATLIN, 0,
"Cannot update BRANCH", (FLDLEN)0);
tpreturn(TPFAIL, 0, transb->data, 0L, 0);
}
/* up the priority of the deposit call */
if (tpsprio(PRIORITY, 0L) == -1)
(void)userlog("Unable to increase priority of deposit\n");
/* tpforward same buffer to deposit service to add initial balance */
tpforward("DEPOSIT", transb->data, 0L, 0);
サーバーは起動時に、構成ファイルの
CLOPTパラメータに指定された値に基づいて、提供するサービスを通知します。
注意:
|
サーバーが通知するサービスは、 buildserverコマンドの実行時に最初に定義されます。 -sオプションを使用すると、複数のサービスをカンマ区切りで指定できます。また、通知されたサービスと異なる名前の関数を呼び出して、サービス・リクエストを処理できます。詳細は、 『Oracle Tuxedoコマンド・リファレンス』の 「buildserver(1)」を参照してください。
|
通知されたサービスでは掲示板のサービス表エントリが使用されるので、リソースが消費される場合があります。そのため、サーバーの起動時には、提供されるサービスのサブセットだけを利用できるようにします。アプリケーションで利用できるサービスを制限するには、構成ファイルの
SERVERSセクションで該当するエントリに
CLOPTパラメータを定義し、
-sオプションの後に必要なサービスをカンマで区切って指定します。また、
-sオプションを使用すると、サービス要求を処理するために呼び出される通知済のサービスと異なる名前の関数を呼び出すこともできます。詳細は、
『ファイル形式、データ記述、MIBおよびシステム・プロセス・リファレンス』の
「servopts(5)」リファレンス・ページを参照してください。
Oracle Tuxedoアプリケーションの管理者は、
tmadmin(1)の
advertiseおよび
unadvertiseコマンドを使用して、サーバーで提供されるサービスを管理できます。
tpadvertise()および
tpunadvertise()関数を使用すると、リクエスト/レスポンス型サーバーまたは会話型サーバーでのサービスの通知を動的に制御できます。ただし、通知されるサービス、または通知を解除するサービスは、リクエストを行うサービスと同じサーバー内になければなりません。
int
tpadvertise(char *
svcname, void *
func)(TPSVCINFO *)
表5-4は、
tpadvertise()関数の引数を示しています。
|
|
|
通知するサービス名を指すポインタ。サービス名は127文字以下の文字列で指定します。127文字を超える名前は切り詰められます。NULL文字列は指定できません。それが指定されると、エラー( TPEINVAL)になります。
|
|
サービスを実行するために呼び出されるOracle Tuxedoシステム関数のアドレスを指すポインタ。通常、この名前とサービス名は同じです。NULL文字列は指定できません。それが指定されると、エラーになります。
|
int
tpadvertisex(char *svcname, void (*func)(TPSVCINFO *), (long flags)
表5-5は、
tpadvertisex()関数の引数を示しています。
|
|
|
通知するサービス名を指すポインタ。サービス名は127文字以下の文字列で指定します。127文字を超える名前は切り詰められます。NULL文字列は指定できません。それが指定されると、エラー( TPEINVAL)になります。
|
|
サービスを実行するために呼び出されるOracle Tuxedoシステム関数のアドレスを指すポインタ。通常、この名前とサービス名は同じです。NULL文字列は指定できません。それが指定されると、エラーになります。
|
注意:
|
long flagsがnullの場合、この関数は tpadvertise()と同じです。
|
|
このサービスは、現在のドメイン内で、一度に1つのサーバーによってのみ通知できます。
このサービスは、プライマリ・リクエスト・キューではなく、セカンダリ・リクエスト・キューに通知されます。このフラグが設定されず、セカンダリ・リクエスト・キューがUBBで設定されている場合、Tuxedoサービスは、プライマリ・リクエスト・キューとセカンダリ・リクエスト・キューの両方に通知されます。
|
tpunadvertise()関数の呼出しには、次のシグネチャを使用します。
tpunadvertise(char *
svcname)
char *
svcname;
tpunadvertise()関数の引数は、
表5-6で説明する引数のみです。
表5-6
tpunadvertise()関数の引数
|
|
|
通知するサービス名を指すポインタ。サービス名は127文字以下の文字列で指定します。127文字を超える名前は切り詰められます。NULL文字列は指定できません。それが指定されると、エラー( TPEINVAL)になります。
|
リスト5-10は、
tpadvertise()関数の使用方法を示しています。このコードでは、サーバー
TLRが起動時に
TLR_INITサービスだけを提供するようにコーディングされています。初期化後、
TLR_INITは
DEPOSITと
WITHDRAWという2つのサービスを通知します。両方とも
tlr_funcs関数によって実行され、サーバー
TLRに組み込まれています。
DEPOSITと
WITHDRAWを通知した後、
TLR_INITは自分自身で通知を解除します。
extern void tlr_funcs()
.
.
.
if (tpadvertise("DEPOSIT", (tlr_funcs)(TPSVCINFO *)) == -1)
check for errors;
if (tpadvertise("WITHDRAW", (tlr_funcs)(TPSVCINFO *)) == -1)
check for errors;
if (tpunadvertise("TLR_INIT") == -1)
check for errors;
tpreturn(TPSUCCESS, 0, transb->data,0L, 0);
実行可能なATMIサーバーをビルドするには、
buildserver(1)コマンドを使用して、Oracle Tuxedoシステム・サーバー・アダプタなどすべての参照ファイルとともにアプリケーション・サービス・サブルーチンをコンパイルします。
注意:
|
Oracle Tuxedoサーバー・アダプタは、メッセージの受信、処理のディスパッチ、トランザクションが有効な場合は トランザクションの管理を行います。
|
buildserverコマンドには、次の構文を使用します。
buildserver -o
filename -f
filenames -l
filenames -s -v
表5-7は、
buildserverコマンド行オプションを示しています。
表5-7
buildserverコマンド行オプション
|
|
|
実行可能な出力ファイル名。デフォルトは a.outです。
|
|
Oracle Tuxedoシステム・ライブラリより先にリンクされるファイルのリスト。 -fオプションは複数回指定できます。また、各 -fオプションに複数のファイル名を指定することもできます。Cプログラム・ファイル( file.c)を指定すると、リンクされる前にコンパイルが行われます。ほかのオブジェクト・ファイル( file.o)を個別に、またはアーカイブ・ファイル( file.a)にまとめて指定することもできます。
|
|
Tuxedoシステム・ライブラリの後でリンクされるファイルのリスト。 -lオプションは複数回指定できます。また、各 -lに複数のファイル名を指定できます。Cプログラム・ファイル( file.c)を指定すると、リンクされる前にコンパイルが行われます。ほかのオブジェクト・ファイル( file.o)を個別に、またはアーカイブ・ファイル( file.a)にまとめて指定することもできます。
|
|
実行可能サーバーにリンクされるリソース・マネージャのアクセス・ライブラリのリスト。アプリケーション管理者は、 buildtms(1)コマンドを使用して、すべての有効なリソース・マネージャ情報を $TUXDIR/updataobj/RMファイルに事前に定義しておく必要があります。リソース・マネージャは1つしか指定できません。詳細は、 『Oracle Tuxedoアプリケーションの設定』を参照してください。
|
|
サーバーに提供されるサービス名、および各サービスを実行する関数名。 -sオプションは複数回指定できます。また、各 -sに複数のサービスを指定できます。サーバーは指定されたサービス名を使用して、クライアントにサービスを通知します。
通常、サービスとそのサービスを実行する関数には同じ名前を割り当てます。ただし、別の名前を指定することもできます。名前の割当てには、 service: functionという構文を使用します。
|
|
サーバーがスレッド・セーフな方法でコーディングされており、構成ファイルでマルチスレッドとして指定されている場合は、マルチスレッドとして起動することを示す値。
|
注意:
|
Oracle Tuxedoライブラリは自動的にリンクされます。コマンド行にOracle Tuxedoライブラリ名を指定する必要はありません。
|
リンクの編集は
buildserverコマンドを実行して行う必要があります。
リンクするライブラリ・ファイルの指定順序は重要です。関数を呼び出す順序と、それらの関数への参照を含むライブラリによって、この順序が決定されます。
デフォルトでは、
buildserverコマンドはUNIXの
ccコマンドを呼び出します。環境変数
CCを指定して別のコンパイル・コマンドを指定したり、
CFLAGSを指定してコンパイル・フェーズやリンク・フェーズに独自のフラグを指定することができます。詳細は、
3-5ページの「環境変数の設定」を参照してください。
次のコマンドは、
acct.oアプリケーション・ファイルを処理して、NEW_ACCTとCLOSE_ACCTという2つのサービスを含む
ACCTサーバーを作成しています。
NEW_ACCTは
OPEN_ACCT関数を呼び出し、
CLOSE_ACCTは同じ名前の関数を呼び出します。
buildserver ���o ACCT ���f acct.o ���s NEW_ACCT:OPEN_ACCT ���s CLOSE_ACCT
アプリケーションATMIサーバーの開発時にC++コンパイラを使用する場合と、Cコンパイラを使用する場合では、基本的に次の2点が異なります。
C++コンパイラでサービス関数を宣言する場合、
extern “C”を使用して、“C”リンクを持つサービス関数を宣言しなければなりません。次のように、関数のプロトタイプを指定します。
#ifdef __cplusplus
extern "C"
#endif
MYSERVICE(TPSVCINFO *tpsvcinfo)
"C"リンクを持つサービス名を宣言すると、C++コンパイラによって名前が
変更されなくなります。多くのC++コンパイラでは、パラメータおよび関数の戻り値の型情報を含むように関数名が変更されます。
•
|
CとC++の両方のサービス・ルーチンを1つのサーバーにリンクできます。その場合、各ルーチンのタイプを示す必要はありません。
|
•
|
動的なサービス通知を行うことができます。動的なサービス通知では、実行可能ファイルのシンボル表にアクセスして、関数名を取得する必要があります。
|
C++のコンストラクタは、クラス・オブジェクトを作成するときに呼び出されて、クラス・オブジェクトを初期化します。デストラクタは、クラス・オブジェクトを破棄するときに呼び出されます。コンストラクタとデストラクタを持つ自動(ローカルで非静的な)変数では、変数がスコープに入るときにコンストラクタが呼び出され、変数がスコープから出るときにデストラクタが呼び出されます。ただし、
tpreturn()または
tpforward()関数が呼び出されると、
longjmp(3)を使用して、非ローカルのgotoがコンパイラによって実行され、自動変数のデストラクタは呼び出されません。この問題を防ぐには、
tpreturn()や
tpforward()の呼出しが、サービス・ルーチンから呼び出される関数からではなく、サービス・ルーチンから直接行われるようにアプリケーションをコーディングします。また、次のいずれかの条件を満たす必要があります。
•
|
サービス・ルーチンがデストラクタ付きの自動変数を持たないようにします。自動変数は、サービス・ルーチンによって呼び出される関数内で宣言して使用します。
|
コンストラクタとデストラクタを持つグローバル変数および静的変数を正しく処理するために、多くのC++コンパイラでは
main()のコンパイルにC++コンパイラを使用する必要があります。
注意:
|
main()ルーチンには、プログラム開始時にコンストラクタ、プログラム終了時にデストラクタを確実に実行するための特別な処理が定義されています。
|
main()はOracle Tuxedoシステムで提供される関数なので、直接コンパイルされることはありません。ファイルがC++を使用してコンパイルされるには、
buildserverコマンドでC++コンパイラを使用する必要があります。デフォルトでは、
buildserverコマンドはUNIXの
ccコマンドを呼び出します。
buildserverコマンドからC++コンパイラが呼び出されるようにするには、
CC環境変数にC++コンパイラのフルパス名を設定します。また、C++のコマンド行で指定するオプションにフラグを設定するには、
CFLAGS環境変数を設定します。詳細は、
3-5ページの「環境変数の設定」を参照してください。