C言語を使用したOracle Tuxedo ATMIアプリケーションのプログラミング

     前  次    新規ウィンドウで目次を開く    PDFとして表示 - 新規ウィンドウ  Adobe Readerを入手 - 新規ウィンドウ
コンテンツはここから始まります

サーバーのコーディング

このトピックには次の項が含まれます:

 


Oracle Tuxedoシステムのmain()

Oracle Tuxedoシステムには、ATMIサーバーを簡単に開発できるように、サーバーのロード・モジュール用に定義済のmain()ルーチンが提供されています。buildserverコマンドを実行すると、main()ルーチンが自動的にサーバーの一部として組み込まれます。

注: main()はシステムで提供されたルーチンのため、変更することはできません。

定義済main()ルーチンは、アプリケーションへの参加と終了の他に、サーバーにかわって次の操作を行います。

以上からわかるように、main()ルーチンは、アプリケーションへの参加と終了、バッファやトランザクションの管理、および通信に関する詳細を扱っています。

注: システムで提供されるmain()は、アプリケーションへの参加と終了を行うため、tpinit()またはtpterm()関数の呼出しをコードに記述しないでください。これらの関数を呼び出すとエラーが発生して、tperrnoTPEPROTOが戻されます。tpinit()またはtpterm()関数の詳細は、「クライアントのコーディング」を参照してください。

 


システムで提供されるサーバーおよびサービス

main()ルーチンは、システムで提供される1つのATMIサーバーAUTHSVR、および2つのサブルーチンtpsvrinit()tpsvrdone()を提供します。次の項で説明するこれらの3つのデフォルト・バージョンについては、ご使用のアプリケーションに合せて変更できます。

注: tpsvrinit()tpsvrdone()を独自にコーディングする場合、この2つのルーチンのデフォルト・バージョンが、それぞれtx_open()tx_close()を呼び出すことに注意してください。tx_open()ではなくtpopen()を呼び出すtpsvrinit()の新しいバージョンをコーディングする場合は、tpclose()を呼び出すtpsvrdone()もコーディングする必要があります。つまり、この2つの関数が呼び出すオープンとクローズの関数は対になっている必要があります。
注: ここで説明するサブルーチンの他に、tpsvrthrinit(3c)tpsvrthrdone(3c)の2つのサブルーチンがシステムで提供されています。詳細は、「マルチスレッドおよびマルチコンテキストATMIアプリケーションのプログラミング」を参照してください。

システムで提供されるサーバー: 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)関数を呼び出して、プロセスがこのファイルにメッセージを書き込みます。中央イベント・ログの詳細は、「エラーの管理」を参照してください。

tpsvrinit()関数を使用すると、アプリケーションで要求される次のような初期化プロセスを行うことができます。

以降の項では、tpsvrinit()を呼び出すことによって、これらの初期化タスクがどのように行われるのかをサンプル・コードで示します。次のサンプル・コードでは示していませんが、このルーチン内ではメッセージ交換を行うこともできます。ただし、非同期応答が未処理のままでtpsvrinit()ルーチンが制御を戻すと、このルーチンは失敗します。その場合、Oracle Tuxedoシステムでは応答は無視されて、サーバーが正常に終了します。

また、「エラーの管理」で説明されているように、tpsvrinit()関数で、トランザクションを開始したり終了することもできます。

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つのダッシュ(--)の後の引数を受け取ります。上の例では、オプションfxそれぞれがコロンで示される引数を取ります。optargが、オプションの引数の開始位置を示します。switch文のロジックは省略されています。

リソース・マネージャのオープン

次のサンプル・コードは、tpsvrinit()のもう1つの使用法として、リソース・マネージャをオープンする方法を示しています。Oracle Tuxedoシステムで提供される関数によって、リソース・マネージャ、tpopen(3c)およびtx_open(3c)がオープンされます。補助的な関数tpclose(3c)とtx_close(3c)も提供されます。これらの関数を使用してリソース・マネージャをオープンしたりクローズするアプリケーションは、その意味では移植性があります。これらは、構成ファイルに設定されたリソース・マネージャのインスタンス固有の情報にアクセスすることによって動作します。

注: マルチスレッド・サーバーのコーディングでは、「マルチスレッドおよびマルチコンテキストATMIアプリケーションのプログラミング」に説明されているように、tpsvrthrinit()関数を使用して、リソース・マネージャを開く必要があります。

これらの関数呼出しは省略可能で、リソース・マネージャがデータベースの場合、リソース・マネージャ固有の呼出しがデータ操作言語(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()を呼び出してリソース・マネージャをクローズします。

注: マルチスレッド・サーバーのコーディングでは、「マルチスレッドおよびマルチコンテキストATMIアプリケーションのプログラミング」に説明されているように、tpsvrthrdone()コマンドを使用して、リソース・マネージャを開く必要があります。

tpsvrdone()関数の呼出しには、次のシグネチャを使用します。

void
tpsvrdone() /* Server termination routine */

tpsvrdone()関数に引数は必要ありません。

アプリケーションにtpsvrdone()のクローズ用ルーチンが定義されていない場合、main()で提供されるデフォルトのルーチンがOracle Tuxedoシステムによって呼び出されます。このルーチンは、tx_close()とuserlog()を呼び出してリソース・マネージャをクローズし、中央イベント・ログに書き込みます。ログには、サーバーが間もなく終了することを伝えるメッセージが送信されます。

ttpsvrdone()は、サーバーがサービス・リクエストの処理を終了した後、サーバーが終了する前に呼び出されます。その場合、サーバーはまだシステムの一部なので、特定の規則に従っているかぎり、ルーチン内でさらに通信およびトランザクションが行われる場合があります。これらの規則については、「エラーの管理」を参照してください。

リスト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)」リファレンス・ページを参照してください。

 


サービスの定義

すべてのサービス・ルーチンは、TPSVCINFO構造体を指すポインタで構成される1つの引数を受け取る関数として定義する必要があります。TPSVCINFO構造体は、atmi.hヘッダー・ファイルに定義され、次の情報が含まれています。

char      name[32];
long flags;
char *data;
long len;
int cd;
int appkey;
CLIENTID cltid;

表5-1は、TPSVCINFOデータ構造体をまとめたものです。

表5-1 TPSVCINFOデータ構造体
フィールド
説明
name
リクエスト元プロセスがサービスの呼出しに使用する名前をサービス・ルーチンに対して指定します。
flags
サービスがトランザクション・モードであるかどうか、または呼出し側が応答を要求しているかどうかをサービスに通知します。サービスをトランザクション・モードにする各方法については、「グローバル・トランザクションのコーディング」を参照してください。
TPTRANフラグは、サービスがトランザクション・モードであることを示します。tpcall()またはtpacall()flagsパラメータにTPNOTRANを設定してサービスを呼び出した場合、サービスは現在のトランザクションに参加できません。ただし、トランザクション・モードでサービスを実行することはできます。つまり、呼出し側によってTPNOTRAN通信フラグが設定されていても、TPTRANsvcinfo->flagsに設定できます。このような状況の例については、「グローバル・トランザクションのコーディング」を参照してください。
tpacall()TPNOREPLY通信フラグが設定されてサービスが呼び出された場合、flagsメンバーはTPNOREPLYに設定されます。呼び出されたサービスが、呼出し側プロセスと同じトランザクションに含まれる場合は、呼出し側に応答を戻す必要があります。
data
main()内でtpalloc()によってすでに割り当てられているバッファを指すポインタ。このバッファは、リクエスト・メッセージの受信に使用されます。ただし、応答メッセージの返信やリクエスト・メッセージの転送にも、このバッファを使用することをお薦めします。
len
dataフィールドによって参照されるバッファ内のリクエスト・データの長さを格納します。
cd
会話型通信の場合は、接続記述子を指定します。
appkey
アプリケーションで使用するために予約された値。アプリケーション固有の認証が設計に含まれている場合、クライアントがアプリケーションに参加するときに呼び出されるアプリケーション固有の認証サーバーによって、認証の成功または失敗を示す値、およびクライアント認証キーが戻される必要があります。Oracle Tuxedoシステムは、appkeyをクライアントのために保持し、このフィールドに情報を格納して以降のサービス・リクエストに渡します。appkeyがサービスに渡されたときは、クライアントの認証は終了しています。ただし、サービス内でappkeyフィールドを使用して、サービスを呼び出したユーザー、またはそのユーザーに関するその他のパラメータを識別できます。
このフィールドが使用されていない場合は、デフォルト値の-1が割り当てられます。
cltid
クライアントの識別子の保持に使用されるCLIENTID型の構造体。この構造体は変更できません。

プロセスがTPSVCINFO構造体のdataフィールドにアクセスする場合、次のバッファ・タイプが合致する必要があります。

リスト5-4は、一般的なサービス定義を示しています。このコードは、Oracle Tuxedoソフトウェアで提供される銀行業務アプリケーションのABAL(残高照会)サービス・ルーチンから引用したものです。ABALBALサーバーの一部です。

リスト5-4 一般的なサービス定義
#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パラメータにVIEWsubtypeaudが設定されています。ABALサービスは、VIEW型付きバッファをサポートするサービスとして定義されています。ABALにはBUFTYPEパラメータは指定されず、デフォルト値のALLが使用されます。ABALサービスはVIEW型のバッファを割り当て、このバッファへのポインタにABALサブルーチンが渡されたTPSVCINFO構造体のdataメンバーを割り当てます。ABALサーバーは、前述のサンプル・コードに示してあるように、対応するdataメンバーにアクセスして適切なデータ・バッファを取得します。

注: このバッファが取得された後、データベースへの最初のアクセスを行う前に、サービスでトランザクションの整合性レベルを指定する必要があります。トランザクションの整合性レベルの詳細は、「グローバル・トランザクションのコーディング」を参照してください。

例: バッファ・タイプの確認

この項のコード例は、サービスがTPSVCINFO構造体で定義されたデータ・バッファにアクセスして、tptypes()関数を使用して型を判別する方法を示します。(このプロセスは、「バッファ・タイプの確認」で説明しています。)このサービスでは、バッファの領域を再割り当てするかどうかを確認するために、バッファの最大サイズもチェックされます。

このサンプル・コードは、Oracle Tuxedoソフトウェアで提供される銀行業務アプリケーションのABALサービスから引用したものです。これは、aud VIEWまたはFMLのいずれかのバッファとしてリクエストを受け入れるサービスをコーディングする方法を示しています。メッセージ・タイプの確認が失敗した場合、エラー・メッセージの文字列と該当の戻りコードが戻され、成功した場合は、バッファ・タイプに合致したコードのセグメントが実行されます。tpreturn()関数の詳細は、「サービス・ルーチンの終了」を参照してください。

リスト5-5 バッファ・タイプの確認
#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);
   }
}

例: サービス・リクエストの優先度の確認

注: 優先度を取得するtpgprio()、および優先度を設定するtpsprio()関数の詳細は、「メッセージの優先度の設定および取得」を参照してください。

この項で示すサンプル・コードは、PRINTERという名前のサービスがtpgprio()関数を使用して、受信したばかりのリクエストの優先度を確認する方法を示しています。その後、その優先度に基づいて印刷ジョブが適切なプリンタに送られ、そのプリンタにpbuf矢印記号dataの内容がパイプされています。

アプリケーションはpbuf矢印記号flagsに対して問合せを実行し、応答が必要かどうかを判定します。その場合は、宛先プリンタ名をクライアントに返します。tpreturn()関数の詳細は、「サービス・ルーチンの終了」を参照してください。

リスト5-6 受信したリクエストの優先度の確認
#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(3c)tpcancel(3c)、およびtpforward(3c)関数は、サービス・ルーチンが完了したことをそれぞれ次の方法で通知します。

応答の送信

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

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

表5-2は、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()関数はdatalen引数を無視して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が設定されて、適切な残高情報がステータス行に返されます。

リスト5-7 tpreturn()関数
#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);
}

記述子の無効化

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

tpcancel()関数の呼出しには、次のシグネチャを使用します。

void
tpcancel(int cd)

cd (呼出し記述子)引数には、取り消すプロセスを指定します。

tpcancel()はトランザクション応答、つまりTPNOTRANフラグが設定されていない状態で呼び出されたリクエストへの応答には使用できません。トランザクション内では、tpabort(3c)がトランザクションの呼出し記述子を無効にします。

次のサンプル・コードは、タイムアウト後の応答を無効にする方法を示しています。

リスト5-8 タイムアウト後の応答の無効化
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)

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

表5-3 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()を呼び出して、要求の発信元であるクライアントに応答を返します。

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

図5-1 リクエストの転送

リクエストの転送

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

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

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

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

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

リスト5-9 tpforward()関数
 ...
/* 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)」を参照してください。

デフォルトでは、サーバーに組み込まれたすべてのサービスをそのサーバーが通知します。詳細は、『ファイル形式、データ記述、MIBおよびシステム・プロセス・リファレンス』「UBBCONFIG(5)」または「servopts(5)」リファレンス・ページを参照してください。

通知されたサービスでは掲示板のサービス表エントリが使用されるので、リソースが消費される場合があります。そのため、サーバーの起動時には、提供されるサービスのサブセットだけを利用できるようにします。アプリケーションで利用できるサービスを制限するには、構成ファイルのSERVERSセクションで該当するエントリにCLOPTパラメータを定義し、-sオプションの後に必要なサービスをカンマで区切って指定します。また、-sオプションを使用すると、サービス要求を処理するために呼び出される通知済のサービスと異なる名前の関数を呼び出すこともできます。詳細は、『ファイル形式、データ記述、MIBおよびシステム・プロセス・リファレンス』「servopts(5)」リファレンス・ページを参照してください。

Oracle Tuxedoアプリケーションの管理者は、tmadmin(1)advertiseおよびunadvertiseコマンドを使用して、サーバーで提供されるサービスを管理できます。tpadvertise()およびtpunadvertise()関数を使用すると、リクエスト/レスポンス型サーバーまたは会話型サーバーでのサービスの通知を動的に制御できます。ただし、通知されるサービス、または通知を解除するサービスは、リクエストを行うサービスと同じサーバー内になければなりません。

サービスの通知

tpadvertise(3c)関数の呼出しには、次のシグネチャを使用します。

int
tpadvertise(char *svcname, void *func)

表5-4は、tpadvertise()関数の引数を示しています。

表5-4 tpadvertise()関数の引数
引数
説明
svcname
通知するサービス名を指すポインタ。サービス名は127文字以下の文字列で指定します。127文字を超える名前は切り詰められます。NULL文字列は指定できません。それが指定されると、エラー(TPEINVAL)になります。
func
サービスを実行するために呼び出されるOracle Tuxedoシステム関数のアドレスを指すポインタ。通常、この名前とサービス名は同じです。NULL文字列は指定できません。それが指定されると、エラーになります。

サービスの通知解除

tpunadvertise(3c)関数は、掲示板のサービス表からサービス名を削除します。サービス名が削除されたサービスは、通知されていない状態になります。

tpunadvertise()関数の呼出しには、次のシグネチャを使用します。

tpunadvertise(char *svcname)
char *svcname;

tpunadvertise()関数の引数は、表5-5で説明する引数だけです。

表5-5 tpunadvertise()関数の引数
引数
説明
svcname
通知するサービス名を指すポインタ。サービス名は127文字以下の文字列で指定します。127文字を超える名前は切り詰められます。NULL文字列は指定できません。それが指定されると、エラー(TPEINVAL)になります。

例:サービスの動的な公開と通知解除

リスト5-10は、tpadvertise()関数の使用方法を示しています。このコードでは、サーバーTLRが起動時にTLR_INITサービスだけを提供するようにコーディングされています。初期化後、TLR_INITDEPOSITWITHDRAWという2つのサービスを通知します。両方ともtlr_funcs関数によって実行され、サーバーTLRに組み込まれています。

DEPOSITWITHDRAWを通知した後、TLR_INITは自分自身で通知を解除します。

リスト5-10 動的な公開と通知解除
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-6は、buildserverコマンド行オプションを示しています。

表5-6 buildserverコマンド行オプション
このオプションは...
次を指定することを可能にします...
-o filename
実行可能な出力ファイル名。デフォルトはa.outです。
-f filenames
Oracle Tuxedoシステム・ライブラリより先にリンクされるファイルのリスト。-fオプションは複数回指定できます。また、各-fオプションに複数のファイル名を指定することもできます。Cプログラム・ファイル(file.c)を指定すると、リンクされる前にコンパイルが行われます。ほかのオブジェクト・ファイル(file.o)を個別に、またはアーカイブ・ファイル(file.a)にまとめて指定することもできます。
-l filenames
Tuxedoシステム・ライブラリの後でリンクされるファイルのリスト。-lオプションは複数回指定できます。また、各-lに複数のファイル名を指定できます。Cプログラム・ファイル(file.c)を指定すると、リンクされる前にコンパイルが行われます。ほかのオブジェクト・ファイル(file.o)を個別に、またはアーカイブ・ファイル(file.a)にまとめて指定することもできます。
-r filenames
実行可能サーバーにリンクされるリソース・マネージャのアクセス・ライブラリのリスト。アプリケーション管理者は、buildtms(1)コマンドを使用して、すべての有効なリソース・マネージャ情報を$TUXDIR/updataobj/RMファイルに事前に定義しておく必要があります。リソース・マネージャは1つしか指定できません。詳細は、『Oracle Tuxedoアプリケーションの設定』を参照してください。
-s [service:]function
サーバーに提供されるサービス名、および各サービスを実行する関数名。-sオプションは複数回指定できます。また、各-sに複数のサービスを指定できます。サーバーは指定されたサービス名を使用して、クライアントにサービスを通知します。
通常、サービスとそのサービスを実行する関数には同じ名前を割り当てます。ただし、別の名前を指定することもできます。名前の割当てには、service:functionという構文を使用します。
-t
サーバーがスレッド・セーフな方法でコーディングされており、構成ファイルでマルチスレッドとして指定されている場合は、マルチスレッドとして起動することを示す値。

注: Oracle Tuxedoライブラリは自動的にリンクされます。コマンド行にOracle Tuxedoライブラリ名を指定する必要はありません。
注: リンクの編集はbuildserverコマンドを実行して行う必要があります。

リンクするライブラリ・ファイルの指定順序は重要です。関数を呼び出す順序と、それらの関数への参照を含むライブラリによって、この順序が決定されます。

デフォルトでは、buildserverコマンドはUNIXのccコマンドを呼び出します。環境変数CCを指定して別のコンパイル・コマンドを指定したり、CFLAGSを指定してコンパイル・フェーズやリンク・フェーズに独自のフラグを指定することができます。詳細は、「環境変数の設定」を参照してください。

次のコマンドは、acct.oアプリケーション・ファイルを処理して、NEW_ACCTとCLOSE_ACCTという2つのサービスを含むACCTサーバーを作成しています。NEW_ACCTOPEN_ACCT関数を呼び出し、CLOSE_ACCTは同じ名前の関数を呼び出します。

buildserver – o ACCT – f acct.o – s NEW_ACCT:OPEN_ACCT – s CLOSE_ACCT

関連項目

 


C++コンパイラの使用

アプリケーションATMIサーバーの開発時にC++コンパイラを使用する場合と、Cコンパイラを使用する場合では、基本的に次の2点が異なります。

サービス関数の宣言

C++コンパイラでサービス関数を宣言する場合、extern “C”を使用して、“C”リンクを持つサービス関数を宣言しなければなりません。次のように、関数のプロトタイプを指定します。

#ifdef __cplusplus
extern "C"
#endif
MYSERVICE(TPSVCINFO *tpsvcinfo)

"C"リンクを持つサービス名を宣言すると、C++コンパイラによって名前が変更されなくなります。多くのC++コンパイラでは、パラメータおよび関数の戻り値の型情報を含むように関数名が変更されます。

このように宣言すると、次の操作が可能になります。

コンストラクタとデストラクタの使用

C++のコンストラクタは、クラス・オブジェクトを作成するときに呼び出されて、クラス・オブジェクトを初期化します。デストラクタは、クラス・オブジェクトを破棄するときに呼び出されます。コンストラクタとデストラクタを持つ自動(ローカルで非静的な)変数では、変数がスコープに入るときにコンストラクタが呼び出され、変数がスコープから出るときにデストラクタが呼び出されます。ただし、tpreturn()またはtpforward()関数が呼び出されると、longjmp(3)を使用して、非ローカルのgotoがコンパイラによって実行され、自動変数のデストラクタは呼び出されません。この問題を防ぐには、tpreturn()tpforward()の呼出しが、サービス・ルーチンから呼び出される関数からではなく、サービス・ルーチンから直接行われるようにアプリケーションをコーディングします。また、次のいずれかの条件を満たす必要があります。

つまり、tpreturn()またはtpforward()関数を呼び出すときに、現在の関数のスコープ内またはスタック上にデストラクタを持つ自動変数が存在しないように、アプリケーションを定義します。

コンストラクタとデストラクタを持つグローバル変数および静的変数を正しく処理するために、多くのC++コンパイラではmain()のコンパイルにC++コンパイラを使用する必要があります。

注: main()ルーチンには、プログラム開始時にコンストラクタ、プログラム終了時にデストラクタを確実に実行するための特別な処理が定義されています。

main()はOracle Tuxedoシステムで提供される関数なので、直接コンパイルされることはありません。ファイルがC++を使用してコンパイルされるには、buildserverコマンドでC++コンパイラを使用する必要があります。デフォルトでは、buildserverコマンドはUNIXのccコマンドを呼び出します。buildserverコマンドからC++コンパイラが呼び出されるようにするには、CC環境変数にC++コンパイラのフルパス名を設定します。また、C++のコマンド行で指定するオプションにフラグを設定するには、CFLAGS環境変数を設定します。詳細は、「環境変数の設定」を参照してください。


  先頭に戻る       前  次