|
Oracle Tuxedo システムには、サーバを簡単に開発できるように、サーバのロード モジュール用に定義済み main() ルーチンが提供されています。buildserver コマンドを実行すると、main() ルーチンが自動的にサーバの一部として組み込まれます。
| 注意 : | main() はシステムで提供されたルーチンなので、変更することはできません。 |
定義済み main() ルーチンは、アプリケーションへの参加と終了のほかに、サーバに代わって次の操作を行います。
SIGHUP シグナルを無視します。 SIGTERM) を受信すると、終了処理を開始します。サーバはシャットダウンされ、必要な場合は再起動します。main() 関数とリンクされたすべてのサービス、または Oracle Tuxedo システムの管理者がコンフィグレーション ファイルに指定したサブセットです。--) までの引数を処理します。2 つのダッシュは、システムで認識される引数の終わりを示します。--) の後に入力された引数を処理したり、リソース マネージャをオープンします (省略可能)。このようなコマンドラインの引数は、アプリケーション固有の初期化に使用されます。main() は次の処理を行います。 main() は次の処理を行います。
以上からわかるように、main() ルーチンは、アプリケーションへの参加と終了、バッファやトランザクションの管理、および通信に関する詳細を扱っています。
| 注意 : | システムで提供される main() は、アプリケーションへの参加と終了を行います。そのため、tpinit() または tpterm() 関数への呼び出しをコードに記述しないでください。これらの関数を呼び出すとエラーが発生して、tperrno に TPEPROTO が返されます。tpinit() または tpterm() 関数の詳細については、「クライアントのコーディング」を参照してください。 |
main() ルーチンは、システムで提供される 1 つのサーバ AUTHSVR、および 2 つのサブルーチン tpsvrinit() と tpsvrdone() を提供します。これらの 3 つのデフォルト バージョンについては、以降の節で説明します。これらのサーバとサブルーチンは、ご使用のアプリケーションに合わせて変更することができます。
| 注意: | tpsvrinit() と tpsvrdone() を独自にコーディングする場合、この 2 つのルーチンのデフォルト バージョンが、それぞれ tx_open() と tx_close() を呼び出すことに注意してください。tx_open() ではなく tpopen() を呼び出す tpsvrinit() の新しいバージョンをコーディングする場合は、tpclose() を呼び出す tpsvrdone() もコーディングする必要があります。つまり、この 2 つの関数が呼び出すオープンとクローズの関数は対になっていなければなりません。 |
| 注意 : | ここで説明するサブルーチンのほかに、tpsvrthrinit(3c) と tpsvrthrdone(3c) の 2 つのサブルーチンがシステムで提供されています。詳細については、「マルチスレッドおよびマルチコンテキスト アプリケーションのプログラミング」を参照してください。 |
AUTHSVR(5) を使用すると、アプリケーションで各クライアントの認証を行うことができます。このサーバは、アプリケーションのセキュリティ レベルが TPAPPAUTH に設定されている場合に、tpinit() によって呼び出されます。
AUTHSVR のサービスは、TPINIT 型バッファの data フィールドでユーザ パスワードを確認します。このユーザ パスワードは、TPINIT 型バッファの passwd フィールドにあるアプリケーション パスワードとは異なります。デフォルトでは、システムによって data から文字列が取得され、それと合致する文字列が /etc/passwd ファイルで検索されます。
tpinit() がネイティブ サイトのクライアントから呼び出された場合、受信した data フィールドはそのまま転送されます。そのため、アプリケーションでパスワードの暗号化が必要な場合、それに応じてクライアント プログラムをコーディングする必要があります。
tpinit() がワークステーション クライアントから呼び出された場合、データは暗号化されてからネットワークに送信されます。
サーバの起動時に、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() を呼び出します。
次のサンプル コードは、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) があります。これらの関数を呼び出してリソース マネージャをオープンしたりクローズするアプリケーションは、その意味では移植性があります。これらのアプリケーションは、コンフィグレーション ファイルに設定されたリソース マネージャのインスタンス固有の情報にアクセスすることによって動作します。
| 注意 : | マルチスレッド サーバのコーディングでは、tpsvrthrinit() 関数を使用して、リソース マネージャを開く必要があります。「マルチスレッドおよびマルチコンテキスト アプリケーションのプログラミング」を参照してください。 |
これらの関数呼び出しは省略可能です。また、リソース マネージャがデータベースの場合、リソース マネージャ固有の呼び出しがデータ操作言語 (DML) の一部であるときは、その呼び出しの代わりに使用できます。userlog(3c) 関数を使用して、中央イベント ログに書き込んでいることに注目してください。
| 注意 : | コマンドライン オプションを受け取り、データベースを開く初期化関数を作成するには、次のサンプル コードと前述のサンプル コードを組み合わせます。 |
tpsvrinit()
{
/* データベースのオープン */
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); /* サーバの終了 */
}
return(0);
}
初期化時にエラーが発生しないように、サーバを終了してからサービス要求の処理を開始するように tpsvrinit() 関数をコーディングできます。
tpsvrinit() が tpopen() を呼び出してリソース マネージャを開くのと同じように、tpsvrdone() 関数は tpclose() を呼び出してリソース マネージャをクローズします。
| 注意 : | マルチスレッド サーバのコーディングでは、tpsvrthrdone() コマンドを使用して、リソース マネージャを開く必要があります。「マルチスレッドおよびマルチコンテキスト アプリケーションのプログラミング」を参照してください。 |
tpsvrdone() 関数の呼び出しには、次の文法を使用します。
void
tpsvrdone()/* サーバ終了ルーチン */
アプリケーションに tpsvrdone() 関数のクローズ用ルーチンが定義されていない場合、main() で提供されるデフォルトのルーチンが Oracle Tuxedo システムによって呼び出されます。このルーチンは、tx_close() と userlog() を呼び出してリソース マネージャをクローズし、中央イベント ログへに書き込みます。ログには、サーバが間もなく終了することを伝えるメッセージが書き込まれます。
tpsvrdone() 関数は、サーバがサービス要求の処理を終了した後、サーバが終了する前に呼び出されます。その場合、サーバはまだシステムの一部なので、特定の規則に従っている限り、ルーチン内でさらに通信およびトランザクションが行われる場合があります。これらの規則については、「エラーの管理」を参照してください。
次のサンプル コードは、tpsvrdone() 関数を使用して、リソース マネージャをクローズして終了する方法を示しています。
void
tpsvrdone()
{
/* データベースのクローズ */
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 C リファレンス』のリファレンス ページ「tpservice(3c)」を参照してください。
return 文と同じように機能します。ただし、処理が終了すると、呼び出し側の関数ではなく Oracle Tuxedo システムの main() 関数に制御が戻ります。svcinfo を使用して呼び出します。この引数は、サービス情報が定義された構造体 (TPSVCINFO) を指すポインタです。
すべてのサービス ルーチンは、1 つの引数、つまり TPSVCINFO 構造体を指すポインタを受け取る関数として定義する必要があります。TPSVCINFO 構造体は、atmi.h ヘッダ ファイルに定義され、次の情報が含まれています。
charname[32];
longflags;
char *data;
longlen;
intcd;
intappkey;
CLIENTIDcltid;
TPTRAN フラグは、サービスがトランザクション モードであることを示します。tpcall() または tpacall() の flags パラメータに TPNOTRAN を設定してサービスを呼び出した場合、サービスは現在のトランザクションに参加できません。ただし、トランザクション モードでサービスを実行することはできます。つまり、呼び出し側によって TPNOTRAN 通信フラグが設定されていても、TPTRAN を svcinfo->flags に設定できます。このような状況の例については、「グローバル トランザクションのコーディング」を参照してください。
TPNOREPLY 通信フラグが設定されてサービスが呼び出された場合、flags メンバーは TPNOREPLY に設定されます。ただし、呼び出されたサービスが、呼び出し側プロセスと同じトランザクションに含まれる場合、呼び出し側に応答を返す必要があります。
|
|
main() 内で tpalloc() を使用して既に割り当てられているバッファを指すポインタ。このバッファは、要求メッセージの受信に使用されます。応答メッセージの返信や要求メッセージの転送にも、このバッファを使用することをお勧めします。
|
|
appkey をクライアントのために保持し、このフィールドに格納して以降のサービス要求に渡します。appkey がサービスに渡されたときは、クライアントの認証は終了しています。ただし、サービス内で appkey フィールドを使用して、サービスを呼び出したユーザ、またはそのユーザに関するそのほかのパラメータを識別できます。
|
|
プロセスが TPSVCINFO 構造体の data フィールドにアクセスする場合、次のバッファ タイプが合致する必要があります。
次のサンプル コードは、一般的なサービス定義を示しています。このコードは、Oracle Tuxedo ソフトウェアで提供される銀行業務アプリケーションの ABAL (残高照会) サービス ルーチンから引用したものです。ABAL サービスは BAL サーバの一部です。
#include <stdio.h> /* UNIX */
#include <atmi.h> /* BEA Tuxedo システム */
#include <sqlcode.h> /* BEA Tuxedo システム */
#include "bank.flds.h" /* bankdb フィールド */
#include "aud.h" /* 銀行業務アプリケーションの VIEW 定義 */
EXEC SQL begin declare section;
static long branch_id; /* 支店番号 */
static float bal; /* 残高 */
EXEC SQL end declare section;
/*
* あるサイトの口座残高の合計を算出するサービス
*/
void
#ifdef __STDC__
ABAL(TPSVCINFO *transb)
#else
ABAL(transb)
TPSVCINFO *transb;
#endif
{
struct aud *transv; /* デコードされたメッセージの VIEW */
/* tpsvcinfo データ バッファへのポインタの設定 */
transv = (struct aud *)transb->data;
set the consistency level of the transaction
/* メッセージから支店番号を受け取り、照会を行う */
EXEC SQL declare acur cursor for
select SUM(BALANCE) from ACCOUNT;
EXEC SQL open acur; /* オープン */
EXEC SQL fetch acur into :bal; /* フェッチ */
if (SQLCODE != SQL_OK) { /* 何もなし */
(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() 関数を使ってそのタイプを確認する方法を示しています。この処理については、「バッファ タイプの確認」を参照してください。また、このサービスはバッファの最大サイズを確認して、そのバッファに領域を再割り当てするかどうかを判定します。
このサンプル コードは、Oracle Tuxedo ソフトウェアで提供される銀行業務アプリケーションの ABAL サービスから引用したものです。このコードでは、aud VIEW または FML のいずれかのバッファとして要求を受け入れるサービスをコーディングする方法を示しています。メッセージ タイプの確認が失敗した場合、エラー メッセージの文字列と戻りコードが返されます。成功した場合、バッファ タイプに合致したコードが実行されます。tpreturn() 関数の詳細については、「サービス ルーチンの終了」を参照してください。
#define TMTYPERR 1 /* tptypes が失敗したことを示す戻りコード */
#define INVALMTY 2 /* 無効なメッセージ タイプであることを示す戻りコード */
void
ABAL(transb)
TPSVCINFO *transb;
{
struct aud *transv; /* VIEW メッセージ */
FBFR *transf; /* フィールド化バッファ メッセージ */
int repc; /* tpgetrply の戻りコード */
char typ[TMTYPELEN+1], subtyp[TMSTYPELEN+1]; /* メッセージのタイプとサブタイプ */
char *retstr; /* tptypes が失敗した場合に返される文字列 */
/* 送信されたバッファ タイプを確認する。 */
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);
}
/* タイプに基づいてサービス要求を処理する方法を決定する。 */
if (strcmp(typ, "FML") == 0) {
transf = (FBFR *)transb->data;
... フィールド化バッファのための abal サービスを実行するコード...
tpreturn が成功し、応答として FML バッファを送信する
}
else if (strcmp(typ, "VIEW") == 0 && strcmp(subtyp, "aud") == 0) {
transv = (struct aud *)transb->data;
... aud 構造体のための abal サービスを実行するコード ...
tpreturn の実行が成功し、応答として aud VIEW バッファを送信
}
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() 関数の詳細については、「サービス ルーチンの終了」を参照してください。
#include <stdio.h
#include "atmi.h"
char *roundrobin();
PRINTER(pbuf)
TPSVCINFO *pbuf; /* 印刷バッファ */
{
char prname[20], ocmd[30]; /* プリンタ名、出力コマンド */
long rlen; /* 応答バッファの長さ */
int prio; /* 要求の優先順位 */
FILE *lp_pipe; /* パイプ ファイルへのポインタ */
prio=tpgprio();
if (prio <= 20)
(void)strcpy(prname,"bigjobs"); /* 優先順位の低い (冗長的) ジョブを
大型コンピュータの中央
レーザ プリンタに送信する。そこで
出力をソートし、
bin に保存する。 */
else if (prio <= 60)
(void)strcpy(prname,roundrobin()); /* ローカルの小型レーザ プリンタの 1 つに
プリンタを順に割り当てる。
そのプリンタで直ちに出力される。
プリンタの
リストに従って、roundrobin() が
実行される。 */
else
(void)strcpy(prname,"hispeed");
/* ジョブを高速レーザ
プリンタに割り当てる。
冗長出力を毎日行う人のために
予約されている。 */
(void)sprintf(ocmd, "lp -d%s", prname); /* lp(1) 出力コマンド */
lp_pipe = popen(ocmd, "w"); /* コマンドへのパイプの作成 */
(void)fprintf(lp_pipe, "%s", pbuf->data); /* 出力の印刷 */
(void)pclose(lp_pipe); /* パイプのクローズ */
if ((pbuf->flags & TPNOREPLY))
tpreturn(TPSUCCESS, 0, NULL, 0, 0);
rlen = strlen(prname) + 1;
pbuf->data = tprealloc(pbuf->data, rlen); /* 名前のために十分な領域を確保する。 */
(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)
|
|||
rcode に返された値にアクセスできます。成功または失敗に関係なく、このコードは関数から返されます。
|
|||
SVCINFO 構造体のサービスに渡されたバッファと同じものを使用する場合は、バッファの割り当ておよび解放について意識する必要はありません。この 2 つの処理は、システムで提供される main() 関数で行われます。このバッファは、tpfree() コマンドを使用しても解放できません。このコマンドを使ってバッファの解放を試みると失敗します。tprealloc() 関数を使用すると、バッファのサイズを変更できます。
|
|||
サービス ルーチンの主なタスクは、要求を処理してクライアント プロセスに応答を返すことです。ただし、要求されたタスクを行うために必要なすべての処理を 1 つのサービスで行う必要はありません。サービスは要求元として動作し、クライアントが元の要求を行ったときと同じように、tpcall() または tpacall() を呼び出して要求を別のサービスに渡すことができます。
| 注意 : | tpcall() と tpacall() 関数の詳細については、「クライアントおよびサーバへの要求/応答のコーディング」を参照してください。 |
tpreturn() が呼び出された場合、常に main() 関数に制御が戻ります。非同期応答でサービスが要求を送信している場合、main() に制御を戻す前にすべての応答を受信するか、または tpcancel() を使用して既に送信した要求を無効にする必要があります。それ以外の場合、未処理の応答は Oracle Tuxedo システムの main() で受信されると自動的に破棄され、呼び出し側にエラーが返されます。
クライアントが tpcall() を使用してサービスを呼び出した場合、tpreturn() の呼び出しが成功すると、応答メッセージが *odata ポインタで示されるバッファ内に格納されます。tpacall() を使用して要求を送信し、tpreturn() から正常に制御が戻されると、応答メッセージは *data ポインタが指す tpgetrply() のバッファ内に格納されます。
応答が必要な場合に、tpreturn() の引数の処理時にエラーが発生すると、呼び出し側プロセスに失敗を示すメッセージが送信されます。呼び出し側は、tperrno に格納されている値を調べてエラーを検出します。失敗を示すメッセージが送信された場合、tperrno に TPESVCERR が設定されます。この値は、グローバル変数 tpurcode の値よりも優先されます。このようなエラーが発生した場合、応答データは返されず、呼び出し側の出力バッファの内容と長さは変更されません。
tpreturn() が不明なタイプのバッファにメッセージを返すか、または呼び出し側で使用できないバッファにメッセージを返した場合、つまり flags に TPNOCHANGE が設定されて呼び出しが行われた場合、tperrno(5) に TPEOTYPE が返されます。その場合、アプリケーションの成功また失敗は判定されず、呼び出し側の出力バッファの内容と長さは変更されません。
tpreturn() が呼び出され、呼び出し側が応答を待っている間にタイムアウトが発生した場合、グローバル変数 tpurcode(5) に返される値は意味を持ちません。この状況は、tperrno(5) に値が返されるどの状況よりも優先します。その場合、tperrno(5) に TPETIME が設定され、応答データは送信されず、呼び出し側の応答バッファの内容と長さは変更されません。詳細については、「グローバル トランザクションのコーディング」を参照してください。
ここで示すサンプル コードは、XFER サーバの一部である TRANSFER サービスを示しています。基本的に、TRANSFER サービスは WITHDRAWAL および DEPOSIT サービスへの同期呼び出しを行います。このサービスでは、WITHDRAWAL と DEPOSIT の両サービスの呼び出しに同じ要求バッファを使用する必要があるので、応答メッセージ用に別のバッファが割り当てられます。WITHDRAWAL の呼び出しが失敗した場合、フォーム上のステータス行に「cannot withdraw」というメッセージが出力され、応答バッファが解放され、tpreturn() 関数の rval 引数に TPFAIL が設定されます。呼び出しが成功した場合、振替元口座の残高が応答バッファから取得されます。
| 注意 : | 次のサンプル コードでは、フィールド化バッファ transf 内の ACCOUNT_ID フィールドのゼロ番目のオカレンスに、アプリケーションが cr_id 変数から取得した「振替先口座」の識別子を移動しています。このような移動が必要なのは、FML バッファ内のフィールドのこのオカレンスが、データ依存型ルーティングに使用されるからです。詳細については、『Oracle Tuxedo アプリケーションの設定』を参照してください。 |
withdrawal サービス呼び出しのシナリオは、DEPOSIT サービスへの呼び出しにも適用できます。呼び出しが成功すると、このサービスによってサービス ルーチンに割り当てられていた応答バッファが解放され、rval 引数に TPSUCCESS が設定されて、適切な残高情報がステータス行に返されます。
#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" /* BANKING #defines */
#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;
/* 振替元 (db_id) と振替先 (cr_id) の口座番号を取得 */
/* 振替元の口座番号が有効かどうかを確認 */
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);
/* この引き出しの優先順位を上げる */
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)
SVCINFO 構造体のサービスに渡されたバッファと同じものを使用する場合は、バッファの割り当ておよび解放について意識する必要はありません。この 2 つの処理は、システムで提供される main() 関数で行われます。このバッファは、tpfree() コマンドを使用しても解放できません。このコマンドを使ってバッファの解放を試みると失敗します。tprealloc() 関数を使用すると、バッファのサイズを変更できます。
|
|||
tpforward() は、サービス呼び出しとは異なります。つまり、要求の転送元サービスでは、応答は要求されていません。応答を返すのは、要求の転送先サービスです。このサービスを転送されたサービスが、要求の発信元プロセスに応答を返します。転送が連鎖的に行われる場合、最後のサーバが tpreturn() を呼び出して、要求の発信元であるクライアントに応答を返します。
次の図は、あるサーバから別のサーバに要求を転送したときのイベントの流れを示しています。この例では、クライアントは tpcall() 関数を使用して要求を開始し、連鎖の最後のサービス (サービス C) が tpreturn() 関数を使用して応答を返しています。

サービス ルーチンは tpsprio() 関数を使用して、クライアント プロセスが要求を送るのと同じように、指定された優先順位に従って要求を転送できます。
プロセスが tpforward() を呼び出すと、システムで提供された main() に制御が戻り、サーバ プロセスは別の要求を処理できるようになります。
| 注意 : | クライアントとして動作するサーバ プロセスが応答を要求する場合、このサーバが自分自身からサービスを要求することはできません。つまり、必要なサービスの唯一のインスタンスが要求を行っているサーバ プロセスからのみ提供される場合、その呼び出しは失敗して再帰呼び出しができないことが示されます。ただし、TPNOREPLY 通信フラグが設定された状態でサービス ルーチンが自分宛てに要求を送信または転送した場合、サービスは自分からの応答を待機しないので、呼び出しは失敗しません。 |
tpforward() 呼び出しを使用して、その呼び出しを行った時点まで要求の処理が成功していたことを示すことができます。アプリケーション エラーが検出されなかった場合、tpforward() を呼び出します。エラーが検出された場合、rval に TPFAIL を設定して tpreturn() を呼び出します。
次のサンプル コードは、ACCT サーバの一部である OPEN_ACCT サービス ルーチンから引用したものです。この例は、tpforward() を呼び出して、サービスがそのデータ バッファを DEPOSIT サービスに送る方法を示しています。このコードは、SQLCODE をテストして、口座の挿入が成功したかどうかを調べています。新規口座の追加が成功した場合、支店レコードが更新されてその口座が反映され、データ バッファが DEPOSIT サービスに転送されます。失敗した場合、rval に TPFAIL が設定されて tpreturn() が呼び出され、失敗を示すメッセージがフォーム上のステータス行に出力されます。
...
/* 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);
サーバは起動時に、コンフィグレーション ファイルの CLOPT パラメータに指定された値に基づいて、提供するサービスを宣言します。
| 注意 : | サーバが宣言するサービスは、buildserver コマンドの実行時に最初に定義されます。-s オプションを使用すると、複数のサービスをカンマ区切りで指定できます。また、宣言されたサービスと異なる名前の関数を呼び出して、サービス要求を処理できます。詳細については、『Tuxedo コマンド リファレンス』の「buildserver(1)」を参照してください。 |
デフォルトでは、サーバに組み込まれたすべてのサービスをそのサーバが宣言します。詳細については、『Oracle Tuxedo のファイル形式とデータ記述方法』の「UBBCONFIG(5)」または「servopts(5)」リファレンス ページを参照してください。
宣言されたサービスでは掲示板のサービス テーブル エントリが使用されるので、リソースが消費される場合があります。そのため、サーバの起動時には、提供されるサービスのサブセットだけを利用できるようにします。アプリケーションで利用できるサービスを制限するには、コンフィグレーション ファイルの SERVERS セクションで該当するエントリに CLOPT パラメータを定義し、-s オプションの後に必要なサービスをカンマで区切って指定します。また、-s オプションを使用すると、サービス要求を処理するために呼び出される宣言済みのサービスと異なる名前の関数を呼び出すこともできます。詳細については、『Oracle Tuxedo のファイル形式とデータ記述方法』の「servopts(5)」リファレンス ページを参照してください。
Oracle Tuxedo アプリケーションの管理者は、tmadmin(1) の advertise および unadvertise コマンドを使用して、サーバで提供されるサービスを管理できます。tpadvertise() および tpunadvertise() 関数を使用すると、要求/応答型サーバまたは会話型サーバでのサービスの宣言を動的に制御できます。ただし、宣言されるサービス、または宣言を取り消すサービスは、要求を行うサービスと同じサーバ内になければなりません。
tpadvertise(3c) 関数の呼び出しには、次の文法を使用します。
int
tpadvertise(char *svcname, void *func)
次の表は、tpadvertise() 関数の引数を示しています。
tpunadvertise(3c) 関数は、掲示板のサービス テーブルからサービス名を削除します。サービス名が削除されたサービスは、宣言されていない状態になります。
tpunadvertise() 関数の呼び出しには、次の文法を使用します。
tpunadvertise(char *svcname)
char *svcname;
tpunadvertise() 関数の引数は、次の表で説明する svcname だけです。
次のサンプル コードは、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);
実行可能なサーバをビルドするには、buildserver(1) コマンドを使用して、Oracle Tuxedo System サーバ アダプタなどすべての参照ファイルと共にアプリケーション サービス サブルーチンをコンパイルします。
| 注意 : | Oracle Tuxedo サーバ アダプタは、メッセージの受信、処理のディスパッチ、トランザクションが有効な場合はトランザクションの管理を行います。 |
buildserver コマンドには、次の構文を使用します。
buildserver -ofilename-ffilenames-lfilenames-s -v
次の表は、buildserver コマンドライン オプションを示しています。
$TUXDIR/updataobj/RM ファイルに事前に定義しておく必要があります。リソース マネージャは 1 つしか指定できません。詳細については、『Oracle Tuxedo アプリケーションの設定』を参照してください。
|
|
| 注意 : | Oracle Tuxedo ライブラリは自動的にリンクされます。コマンドラインに Oracle Tuxedo ライブラリ名を指定する必要はありません。 |
リンクするライブラリ ファイルの指定順序は重要です。関数を呼び出す順序と、それらの関数への参照を含むライブラリによって、この順序が決定されます。
デフォルトでは、buildserver コマンドは UNIX の cc コマンドを呼び出します。環境変数 CC を指定して別のコンパイル コマンドを指定したり、CFLAGS を指定してコンパイル フェーズやリンク フェーズに独自のフラグを指定することができます。詳細については、「環境変数の設定」を参照してください。
次のコマンドは、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
アプリケーション サーバの開発時に 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 環境変数を設定します。詳細については、「環境変数の設定」を参照してください。
|