BEA Logo BEA Tuxedo Release 8.0

  BEA ホーム  |  イベント  |  ソリューション  |  パートナ  |  製品  |  サービス  |  ダウンロード  |  ディベロッパ・センタ  |  WebSUPPORT

 

   Tuxedo ホーム   |   C 言語を使用した BEA Tuxedo アプリケーションのプログラミング   |   先頭へ   |   前へ   |   次へ   |   目次

 


サービス・ルーチンの終了

tpreturn(3c)tpcancel(3c)、および tpforward(3c) 関数は、サービス ルーチンが完了したことをそれぞれ次の方法で通知します。

応答の送信

tpreturn(3c) 関数はサービス・ルーチンの終了を示し、要求元にメッセージを送ります。tpreturn() 関数の呼び出しには、次の文法を使用します。

void
tpreturn(int rval, int rcode, char *data, long len, long flags)

次の表は、tpreturn() 関数の引数を示しています。

tpreturn( ) 関数の引数

引数

説明

rval

サービスが正常に終了したかどうかをアプリケーション・レベルで示す値。この値は、シンボリック名で表される整数値です。有効な設定は、次のとおりです。

この引数の値がグローバル・トランザクションに与える影響については、「グローバル・トランザクションのコーディング」を参照してください。

rcode

アプリケーション定義の戻りコードを呼び出し元に返します。クライアントは、グローバル変数 tpurcode(5) を照会して、rcode に返された値にアクセスできます。成功または失敗に関係なく、このコードは関数から返されます。

data

クライアント・プロセスに返される応答メッセージを指すポインタ。メッセージ・バッファは、tpalloc() を使用して既に割り当てられていることが必要です。

SVCINFO 構造体のサービスに渡されたバッファと同じものを使用する場合は、バッファの割り当ておよび解放について意識する必要はありません。この 2 つの処理は、システムで提供される main() 関数で行われます。このバッファは、tpfree() コマンドを使用しても解放できません。このコマンドを使ってバッファの解放を試みると失敗します。tprealloc() 関数を使用すると、バッファのサイズを変更できます。

サービス・ルーチンに渡されたバッファとは別のバッファを使ってメッセージを返す場合、自分でそのバッファを割り当てる必要があります。アプリケーション側で tpreturn() 関数が呼び出されると、バッファは自動的に解放されます。

応答メッセージを返す必要がない場合は、この引数に NULL ポインタを設定します。

注記 クライアントに応答を返す必要がない場合、つまり TPNOREPLY が設定されている場合、tpreturn() 関数は datalen 引数を無視して main() に制御を戻します。

len

応答バッファの長さ。アプリケーションはこの引数の値に、tpcall() 関数の olen パラメータ、または tpgetrply() 関数の len パラメータを使用してアクセスできます。

クライアントとして動作するプロセスは、この戻り値を使用して、応答バッファのサイズが大きくなったかどうか調べることができます。

クライアントが応答を要求していて応答バッファにデータが存在していない場合、つまり data 引数に NULL ポインタが設定されている場合、クライアントのバッファを変更せずに長さがゼロの応答が返されます。

data 引数が指定されていない場合、この引数の値は無視されます。

flag

現在使用されていません。

サービス・ルーチンの主なタスクは、要求を処理してクライアント・プロセスに応答を返すことです。ただし、要求されたタスクを行うために必要なすべての処理を 1 つのサービスで行う必要はありません。サービスは要求元として動作し、クライアントが元の要求を行ったときと同じように、tpcall() または tpacall() を呼び出して要求を別のサービスに渡すことができます。

注記 tpcall()tpacall() 関数の詳細については、「クライアントおよびサーバへの要求/応答のコーディング」を参照してください。

tpreturn()が呼び出された場合、常に main()関数に制御が戻ります。非同期応答でサービスが要求を送信している場合、main() に制御を戻す前にすべての応答を受信するか、または tpcancel() を使用して既に送信した要求を無効にする必要があります。それ以外の場合、未処理の応答は BEA Tuxedo システムの main() で受信されると自動的に破棄され、呼び出し元にエラーが返されます。

クライアントが tpcall() を使用してサービスを呼び出した場合、tpreturn() の呼び出しが成功すると、応答メッセージが *odata ポインタで示されるバッファ内に格納されます。tpacall() を使用して要求を送信し、tpreturn() から正常に制御が戻されると、応答メッセージは *data ポインタが指す tpgetrply() のバッファ内に格納されます。

応答が必要な場合に、tpreturn() の引数の処理時にエラーが発生すると、呼び出し元プロセスに失敗を示すメッセージが送信されます。呼び出し元は、tperrno に格納されている値を調べてエラーを検出します。失敗を示すメッセージが送信された場合、tperrnoTPESVCERR が設定されます。この値は、グローバル変数 tpurcode の値よりも優先されます。このようなエラーが発生した場合、応答データは返されず、呼び出し元の出力バッファの内容と長さは変更されません。

tpreturn() が不明なタイプのバッファにメッセージを返すか、または呼び出し元で使用できないバッファにメッセージを返した場合、つまり flagsTPNOCHANGE が設定されて呼び出しが行われた場合、tperrno(5)TPEOTYPE が返されます。その場合、アプリケーションの成功また失敗は判定されず、呼び出し元の出力バッファの内容と長さは変更されません。

tpreturn() が呼び出され、呼び出し元が応答を待っている間にタイムアウトが発生した場合、グローバル変数 tpurcode(5) に返される値は意味を持ちません。この状況は、tperrno(5) に値が返されるどの状況よりも優先します。その場合、tperrno(5)TPETIME が設定され、応答データは送信されず、呼び出し元の応答バッファの内容と長さは変更されません。BEA Tuxedo システムでは、ブロッキング・タイムアウトとトランザクション・タイムアウトの 2 種類のタイムアウトが発生します。詳細については、「グローバル・トランザクションのコーディング」を参照してください。

ここで示すコード例は、XFER サーバの一部である TRANSFER サービスを示しています。基本的に、TRANSFER サービスは WITHDRAWAL および DEPOSIT サービスへの同期呼び出しを行います。このサービスでは、WITHDRAWALDEPOSIT の両サービスの呼び出しに同じ要求バッファを使用する必要があるので、応答メッセージ用に別のバッファが割り当てられます。WITHDRAWAL の呼び出しが失敗した場合、フォーム上のステータス行に「cannot withdraw」というメッセージが出力され、応答バッファが解放され、 tpreturn() 関数の rval 引数に TPFAIL が設定されます。呼び出しが成功した場合、振替元口座の残高が応答バッファから取得されます。

注記 次のコード例では、フィールド化バッファ transf 内の ACCOUNT_ID フィールドのゼロ番目のオカレンスに、アプリケーションが cr_id 変数から取得した「振替先口座」の識別子を移動しています。このような移動が必要なのは、FML バッファ内のフィールドのこのオカレンスが、データ依存型ルーティングに使用されるからです。詳細については、『BEA Tuxedo アプリケーションの設定』を参照してください。

withdrawal サービス呼び出しのシナリオは、DEPOSIT サービスへの呼び出しにも適用できます。呼び出しが成功すると、このサービスによってサービス・ルーチンに割り当てられていた応答バッファが解放され、rval 引数に TPSUCCESS が設定されて、適切な残高情報がステータス行に返されます。

tpreturn( ) 関数

#include <stdio.h>      /* UNIX */
#include <string.h> /* UNIX */
#include "fml.h" /* BEA Tuxedo システム */
#include "atmi.h" /* BEA Tuxedo システム */
#include "Usysflds.h" /* BEA Tuxedo システム */
#include "userlog.h" /* BEA Tuxedo システム */
#include "bank.h" /* 銀行業務アプリケーションのマクロ定義 */
#include "bank.flds.h" /* bankdb フィールド */


/* 
*振替元口座から振替先口座に
*振り替えるサービス
*/

void
#ifdef __STDC__
TRANSFER(TPSVCINFO *transb)

#else

TRANSFER(transb)
TPSVCINFO *transb;
#endif

{
FBFR *transf; /* 復号化されたメッセージのフィールド化バッファ */
long db_id, cr_id; /* 振替元口座と振替先口座の番号 */
float db_bal, cr_bal; /* 振替元口座と振替先口座の残高 */
float tamt; /* 振替額 */
FBFR *reqfb; /* 要求メッセージ用のフィールド化バッファ*/
int reqlen; /* フィールド化バッファの長さ */
char t_amts[BALSTR]; /* 振替額を表す文字列 */
char db_amts[BALSTR]; /* 振替元の口座残高を表す文字列 */
char cr_amts[BALSTR]; /* 振替先の口座残高を示す文字列 */

/* tpsvcinfo データ・バッファを指すポインタを設定します。 */
transf = (FBFR *)transb->data;

/* 振替元と振替先の口座番号を取得します。 */

/* 振替元の口座番号が有効かどうかを確認します。 */
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);
}
/* 振替先の口座番号が妥当かどうかを確認します。 */
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);
}

/* 振替金額 (払い戻し金額) を取得します。 */
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);

/* 振替金額が妥当かどうかを確認します。 */
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);
}

/* 引出要求バッファを作成します。 */
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);

/* 要求バッファに口座番号を格納します。 */
(void)Fchg(reqfb,ACCOUNT_ID,0,(char *)&db_id, (FLDLEN)0);

/* 要求バッファに振替金額を格納します。 */
(void)Fchg(reqfb,SAMOUNT,0,t_amts, (FLDLEN)0);

/* WITHDRAWAL サービス呼び出しの優先順位を上げます。 */
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);
}

/* 応答バッファから振替元口座の残高を取得します。 */

(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);
}

/* 要求バッファに振替先の口座番号を格納します。 */
(void)Fchg(reqfb,ACCOUNT_ID,0,(char *)&cr_id, (FLDLEN)0);

/* 要求バッファに振替金額を格納します。 */
(void)Fchg(reqfb,SAMOUNT,0,t_amts, (FLDLEN)0);

/* 振替 (預入) の関数の呼び出しの優先順位を上げます。 */
if (tpsprio(PRIORITY, 0L) == -1)
(void)userlog("Unable to increase priority of deposit\n");

/* tpcall を実行して、振替先口座に振替金額を移動します。 */
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);
}

/* 応答バッファから振替先口座の残高を取得します。 */

(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);
}

/* 振替の成功を示す値を格納する応答バッファを設定します。 */
(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);
}

記述子の無効化

tpgetrply() を呼び出したサービス (「クライアントおよびサーバへの要求/応答のコーディング」を参照) が TPETIME で失敗して要求を取り消す場合、tpcancel(3c) を呼び出して記述子を無効にできます。以降、応答が届いても自動的に破棄されます。

tpcancel() 関数の呼び出しには、次の文法を使用します。

void
tpcancel(int cd)

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(3c) 関数を使用すると、サービス要求をほかのサービスに転送して、別の処理を行うことができます。

tpforward() 関数の呼び出しには、次の文法を使用します。

void
tpforward(char *svc, char *data, long len, long flags)

次の表は、tpreturn() 関数の引数を示しています。

tpreturn() 関数の引数

引数

説明

svc

要求が転送されるサービス名を指す文字型のポインタ。

data

クライアント・プロセスに返される応答メッセージを指すポインタ。メッセージ・バッファは、tpalloc() を使用して既に割り当てられていることが必要です。

SVCINFO 構造体のサービスに渡されたバッファと同じものを使用する場合は、バッファの割り当ておよび解放について意識する必要はありません。この 2 つの処理は、システムで提供される main() 関数で行われます。このバッファは、tpfree() コマンドを使用しても解放できません。このコマンドを使ってバッファの解放を試みると失敗します。tprealloc() 関数を使用すると、バッファのサイズを変更できます。

サービス・ルーチンに渡されたバッファとは別のバッファを使ってメッセージを返す場合、自分でそのバッファを割り当てる必要があります。アプリケーション側で tpreturn() 関数が呼び出されると、バッファは自動的に解放されます。

応答メッセージを返す必要がない場合は、この引数に NULL ポインタを設定します。

注記 クライアントに応答を返す必要がない場合、つまり TPNOREPLY が設定されている場合、tpreturn() 関数は datalen 引数を無視して main() に制御を戻します。

len

応答バッファの長さ。アプリケーションはこの引数の値に、tpcall() 関数の olen パラメータ、または tpgetrply() 関数の len パラメータを使用してアクセスできます。

クライアントとして動作するプロセスは、この戻り値を使用して、応答バッファのサイズが大きくなったかどうか調べることができます。

クライアントが応答を要求していて応答バッファにデータが存在していない場合、つまり data 引数に NULL ポインタが設定されている場合、クライアントのバッファを変更せずに長さがゼロの応答が返されます。

data 引数が指定されていない場合、この引数の値は無視されます。

flag

現在使用されていません。

tpforward() は、サービス呼び出しとは異なります。つまり、要求の転送元サービスでは、応答は要求されていません。応答を返すのは、要求の転送先サービスです。このサービスを転送されたサービスが、要求の発信元プロセスに応答を返します。転送が連鎖的に行われる場合、最後のサーバが tpreturn() を呼び出して、要求の発信元であるクライアントに応答を返します。

次の図は、あるサーバから別のサーバに要求を転送したときのイベントの流れを示しています。この例では、クライアントは tpcall() 関数を使用して要求を開始し、連鎖の最後のサービス (サービス C) が tpreturn() 関数を使用して応答を返しています。

要求の転送


 

サービス・ルーチンは tpsprio() 関数を使用して、クライアント・プロセスが要求を送るのと同じように、指定された優先順位に従って要求を転送できます。

プロセスが tpforward() を呼び出すと、システムで提供された main() に制御が戻り、サーバ・プロセスは別の要求を処理できるようになります。

注記 クライアントとして動作するサーバ・プロセスが応答を要求する場合、このサーバが自分自身からサービスを要求することはできません。つまり、必要なサービスの唯一のインスタンスが要求を行っているサーバ・プロセスからのみ提供される場合、その呼び出しは失敗して再帰呼び出しができないことが示されます。ただし、TPNOREPLY 通信フラグが設定された状態でサービス・ルーチンが自分宛てに要求を送信または転送した場合、サービスは自分からの応答を待機しないので、呼び出しは失敗しません。

tpforward() 呼び出しを使用して、その呼び出しを行った時点まで要求の処理が成功していたことを示すことができます。アプリケーション・エラーが検出されなかった場合、tpforward() を呼び出します。エラーが検出された場合、 rvalTPFAIL を設定して tpreturn() を呼び出します。

次のコード例は、ACCT サーバの一部である OPEN_ACCT サービス・ルーチンから引用したものです。この例は、tpforward() を呼び出して、サービスがそのデータ・バッファを DEPOSIT サービスに送る方法を示しています。このコードは、SQLCODE をテストして、口座の挿入が成功したかどうかを調べています。新規口座の追加が成功した場合、支店レコードが更新されてその口座が反映され、データ・バッファが DEPOSIT サービスに転送されます。失敗した場合、rvalTPFAIL が設定されて tpreturn() が呼び出され、失敗を示すメッセージがフォーム上のステータス行に出力されます。

tpforward( ) 関数

 ...
/* tpsvcinfo データ・バッファへのポインタを設定します。 */
transf = (FBFR *)transb->data;
...
/* account に新規口座レコードを挿入します。 */
account_id = ++last_acct; /* 新規口座番号を取得します。 */
tlr_bal = 0.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) { /* レコードの挿入が失敗しました。 */
(void)Fchg(transf, STATLIN, 0,
"Cannot update ACCOUNT", (FLDLEN)0);
tpreturn(TPFAIL, 0, transb->data, 0L, 0);
}

/* 新規 last_acct で支店レコードを更新します。 */

EXEC SQL update BRANCH set LAST_ACCT = :last_acct where BRANCH_ID = :branch_id;
if (SQLCODE != SQL_OK) { /* レコードの更新が失敗しました。 */
(void)Fchg(transf, STATLIN, 0,
"Cannot update BRANCH", (FLDLEN)0);
tpreturn(TPFAIL, 0, transb->data, 0L, 0);
}
/* 振替 (預金) の関数の呼び出しの優先順位を上げます。 */
if (tpsprio(PRIORITY, 0L) == -1)
(void)userlog("Unable to increase priority of deposit\n");

/* tpforward を使用して同じバッファを預金サービスに転送し、最初の残高を設定します。 */
tpforward("DEPOSIT", transb->data, 0L, 0);

 

先頭へ戻る 前のトピックへ 次のトピックへ