11.33 全般的なサンプル・コード

トランザクションの整合性、メッセージの通信、およびリソースへのアクセスは、オンライン・トランザクション処理(OLTP)アプリケーションの主要な要件です。

ここでは、リソース・マネージャにアクセスするSQL文と共に動作する通信ルーチン、バッファ管理、ATMIトランザクションを示すサンプル・コードを示します。ここで示す例は、Oracle Tuxedo銀行業務アプリケーション(bankapp)のACCTサーバーから抜粋したCLOSE_ACCTサービスです。

この例は、最初のSQL文でデータベースにアクセスする前に、set transaction文(49行目)を使用して、トランザクションの整合性レベルとアクセス・モードを設定する方法を示しています。(読取り/書込みの権限が指定されている場合、整合性レベルはデフォルトで高い整合性に設定されます。)ACCOUNT_IDの値に基づいて口座を解約するために、SQL問合せで引出し額を決定します(50 - 58行目)。

tpalloc()は、WITHDRAWALサービスへのリクエスト・メッセージ用のバッファを割り当て、ACCOUNT_IDと引出し額がバッファに格納されます(62 - 74行目)。次に、tpcall()呼出しを介してWITHDRAWALサービスにリクエストが送信されます(79行目)。その後、SQLのdelete文で、該当する口座を削除してデータベースを更新します(86行目)。

すべての処理が成功すると、サービス内で割り当てられたバッファが解放され(98行目)、サービスに送信されたTPSVCINFOデータ・バッファが更新されて、トランザクションが正常に終了したことが示されます(99行目)。サービスがイニシエータであった場合、ここでトランザクションが自動的にコミットされます。tpreturn()は、口座の解約を要求したクライアント・プロセスに、TPSUCCESSと更新後のバッファを返します。最後に、要求されたサービスが正常に終了したことが、フォームのステータス行に示されます。

各関数呼出しの後、成功したか失敗したかが確認されます。エラーが発生した場合、サービスで割り当てられたバッファが解放され、サービスで開始されたトランザクションが中断され、TPSVCINFOバッファが更新されてエラーの原因が示されます(80 - 83行目)。最後に、tpreturn()TPFAILを返し、更新されたバッファ内のメッセージがフォームのステータス行に表示されます。

ノート:

サービス・ルーチンでグローバル・トランザクションの整合性レベルを指定する場合、同じトランザクションに参加するすべてのサービス・ルーチンで、同じようにレベルを定義する必要があります。

ACCTサーバーのリスト

001    #include <stdio.h>                         /* UNIX */
002    #include <string.h>                        /* UNIX */
003    #include <fml.h>                           /* ORACLE Tuxedo System */
004    #include <atmi.h>                          /* ORACLE Tuxedo System */
005    #include <Usysflds.h>                      /* ORACLE Tuxedo System */
006    #include <sqlcode.h>                       /* ORACLE Tuxedo System */
007    #include <userlog.h>                       /* ORACLE Tuxedo System */
008    #include "bank.h"                          /* BANKING #defines */
009    #include "bank.flds.h"                     /* bankdb fields */
010    #include "event.flds.h"                    /* event fields */
011
012
013    EXEC SQL begin declare section;
014    static long account_id;                    /* account id */
015    static long branch_id;                     /* branch id */
016    static float bal, tlr_bal;                 /* BALANCE */
017    static char acct_type;                     /* account type*/
018    static char last_name[20], first_name[20]; /* last name, first name */
019    static char mid_init;                      /* middle initial */
020    static char address[60];                   /* address */
021    static char phone[14];                     /* telephone */
022    static long last_acct;                     /* last account branch gave */
023    EXEC SQL end declare section;

024    static FBFR *reqfb;              /* fielded buffer for request message */
025    static long reqlen;              /* length of request buffer */
026    static char amts[BALSTR];        /* string representation of float */

027    code for OPEN_ACCT service

028    /*
029     * Service to close an account
030     */
031    void
032    #ifdef __STDC__
033    LOSE_ACCT(TPSVCINFO *transb)

034    #else

035    CLOSE_ACCT(transb)
036    TPSVCINFO *transb;
037    #endif

038 {
039    FBFR *transf;                 /* fielded buffer of decoded message */

040    /* set pointer to TPSVCINFO data buffer */
041       transf = (FBFR *)transb->data;

042    /* must have valid account number */
043    if (((account_id = Fvall(transf, ACCOUNT_ID, 0)) < MINACCT) ||
044     (account_id > MAXACCT)) {
045      (void)Fchg(transf, STATLIN, 0, "Invalid account number", (FLDLEN)0);
046       tpreturn(TPFAIL, 0, transb->data, 0L, 0);
047 }

048    /* Set transaction level */
049    EXEC SQL set transaction read write;

050    /* Retrieve AMOUNT to be deleted */
051    EXEC SQL declare ccur cursor for
052     select BALANCE from ACCOUNT where ACCOUNT_ID = :account_id;
053    EXEC SQL open ccur;
054    EXEC SQL fetch ccur into :bal;
055    if (SQLCODE != SQL_OK) {                 /* nothing found */
056     (void)Fchg(transf, STATLIN, 0, getstr("account",SQLCODE), (FLDLEN)0);
057     EXEC SQL close ccur;
058     tpreturn(TPFAIL, 0, transb->data, 0L, 0);
059   }

060    /* Do final withdrawal */

061    /* make withdraw request buffer */
062   if ((reqfb = (FBFR *)tpalloc("FML",NULL,transb->len)) == (FBFR *)NULL) {
063      (void)userlog("tpalloc failed in close_acct\n");
064      (void)Fchg(transf, STATLIN, 0,
065           "Unable to allocate request buffer", (FLDLEN)0);
066       tpreturn(TPFAIL, 0, transb->data, 0L, 0);
067       }
068       reqlen = Fsizeof(reqfb);
069      (void)Finit(reqfb,reqlen);
070    /* put ID in request buffer */
071    (void)Fchg(reqfb,ACCOUNT_ID,0,(char *)&account_id, (FLDLEN)0);

072    /* put amount into request buffer */
073    (void)sprintf(amts,"%.2f",bal);
074    (void)Fchg(reqfb,SAMOUNT,0,amts, (FLDLEN)0);

075    /* increase the priority of this withdraw */
076    if (tpsprio(PRIORITY, 0L) == -1)
077      (void)userlog("Unable to increase priority of withdraw");

078    /* tpcall to withdraw service to remove remaining balance */
079    if (tpcall("WITHDRAWAL", (char *)reqfb, 0L, (char **)&reqfb,
080       (long *)&reqlen,TPSIGRSTRT) == -1) {
081       (void)Fchg(transf, STATLIN, 0,"Cannot make withdrawal", (FLDLEN)0);
082       tpfree((char *)reqfb);
083       tpreturn(TPFAIL, 0,transb->data, 0L, 0);
084       }

085       /* Delete account record */

086       EXEC SQL delete from ACCOUNT where current of ccur;
087       if (SQLCODE != SQL_OK) {               /* Failure to delete */
088       (void)Fchg(transf, STATLIN, 0,"Cannot close account", (FLDLEN)0);
089        EXEC SQL close ccur;
090        tpfree((char *)reqfb);
091        tpreturn(TPFAIL, 0, transb->data, 0L, 0);
092    }
093    EXEC SQL close ccur;

094    /* prepare buffer for successful return */
095    (void)Fchg(transf, SBALANCE, 0, Fvals(reqfb,SAMOUNT,0), (FLDLEN)0);
096    (void)Fchg(transf, FORMNAM, 0, "CCLOSE", (FLDLEN)0);
097    (void)Fchg(transf, STATLIN, 0, " ", (FLDLEN)0);
098    tpfree((char *)reqfb);
099    tpreturn(TPSUCCESS, 0, transb->data, 0L, 0);
100 }