5.5.1 応答の送信

tpreturn(3c)関数はサービス・ルーチンの終了を示し、リクエスタにメッセージを送ります。tpreturn()関数の呼出しには、次のシグネチャを使用します:

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

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

表5-2 tpreturn()関数の引数

引数 説明
rval サービスが正常に終了したかどうかをアプリケーション・レベルで示す値。この値は、シンボリック名で表される整数値です。有効な設定は、次のとおりです:
  • TPSUCCESS - 関数呼出しが成功したことを示します。関数は、応答メッセージを呼出し側のバッファに格納します。つまり、応答メッセージがある場合は、呼出し側のバッファ内にあります。
  • TPFAIL (デフォルト) - サービスが失敗したことを示します。関数は、応答を待つクライアント・プロセスにエラー・メッセージを通知します。その場合、クライアントが呼び出したtpcall()またはtpgetrply()関数が失敗し、変数tperrno(5)にアプリケーション定義の失敗を示すTPESVCFAILが設定されます。応答メッセージが要求されている場合、呼出し側のバッファから取得できます。
  • TPEXIT - サービスが失敗したことを示します。関数は、応答を待つクライアント・プロセスにエラー・メッセージを通知して終了します。
この引数の値がグローバル・トランザクションに与える影響については、「グローバル・トランザクションのコーディング」を参照してください。
rcode アプリケーション定義の戻りコードを呼出し側に返します。クライアントは、グローバル変数tpurcode(5)に対して問合せを実行して、rcodeに返された値にアクセスできます。成功または失敗に関係なく、このコードは関数から返されます。
data クライアント・プロセスに返される応答メッセージを指すポインタ。メッセージ・バッファは、tpalloc()を使用して事前に割り当てられていることが必要です。SVCINFO構造体のサービスに渡されたバッファと同じものを使用する場合は、バッファの割当ておよび解放について意識する必要はありません。この2つの処理は、システムで提供されるmain()関数で行われます。このバッファは、tpfree()コマンドを使用しても解放できません。このコマンドを使用してバッファの解放を試みると失敗します。tprealloc()関数を使用すると、バッファのサイズを変更できます。サービス・ルーチンに渡されたバッファとは別のバッファを使ってメッセージを返す場合、自分でそのバッファを割り当てる必要があります。アプリケーション側でtpreturn()関数が呼び出されると、バッファは自動的に解放されます。応答メッセージを返す必要がない場合は、この引数にNULLポインタを設定します。

ノート:

クライアントに応答を返す必要がない場合、つまりTPNOREPLYが設定されている場合、tpreturn()関数はdataとlen引数を無視してmain()に制御を戻します。
len 応答バッファの長さ。アプリケーションはこの引数の値に、tpcall()関数のolenパラメータ、またはtpgetrply()関数のlenパラメータを使用してアクセスできます。クライアントとして動作するプロセスは、この戻り値を使用して、応答バッファのサイズが大きくなったかどうか調べることができます。クライアントが応答を要求していて応答バッファにデータが存在していない場合、つまりdata引数にNULLポインタが設定されている場合、クライアントのバッファを変更せずに長さがゼロの応答が送信されます。data引数が指定されていない場合、この引数の値は無視されます。
flag 現在使用されていません。

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

ノート:

tpcall()tpacall()関数の詳細は、「クライアントおよびサーバーへのリクエスト/レスポンスのコーディング」を参照してください。

tpreturn()が呼び出された場合、常にmain()関数に制御が戻ります。非同期応答でサービスがリクエストを送信している場合、main()に制御を戻す前にすべての応答を受信するか、またはtpcancel()を使用してすでに送信したリクエストを無効にする必要があります。それ以外の場合、未処理の応答はOracle 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が設定され、応答データは送信されず、呼出し側の応答バッファの内容と長さは変更されません。Oracle Tuxedoシステムには、ブロッキング・タイムアウトとトランザクション・タイムアウトの2種類のタイムアウトがあります(詳細は、「グローバル・トランザクションのコーディング」を参照)。

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

ノート:

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

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

tpreturn( )関数のリスト

#include <stdio.h>          /* UNIX */
#include <string.h>         /* UNIX */
#include "fml.h"            /* ORACLE Tuxedo System */
#include "atmi.h"           /* ORACLE Tuxedo System */
#include "Usysflds.h"       /* ORACLE Tuxedo System */
#include "userlog.h"        /* ORACLE 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);
}