[Glue!] 一般的なエージェント


一般にエージェントのプログラムは以下のようになる:
#!/usr/local/bin/perl5

use GlueLogic;
&GlueLogicParseArgs();
&GlueLogicConnect("agent#", undef, "foobar");
&GlueLogicAccess("!AddInformTo $GlueLogicAnchor.a $GlueLogicAgent");
&GlueLogicAccess("!AddInformTo $GlueLogicAnchor.bb $GlueLogicAgent");
&GlueLogicAccess("!AddInformTo $GlueLogicAnchor.zzz $GlueLogicAgent");

$stem = quotemeta $GlueLogicAnchor;
while (1){
    &GlueLogicWaitForMessage() unless @GlueLogicMessageQueue;
    &GlueLogicEnqueueMessage();
    next unless @GlueLogicMessageQueue;
    last unless defined ( $_ = shift(@GlueLogicMessageQueue) );
    if    ( /^Changed $stem\.a$/o )   { &ProcessEventA(); }
    elsif ( /^Changed $stem\.bb$/o )  { &ProcessEventBB(); }
    elsif ( /^Changed $stem\.zzz$/o ) { last; }
}

&GlueLogicAccess("!DelInformTo $GlueLogicAnchor.a $GlueLogicAgent");
&GlueLogicAccess("!DelInformTo $GlueLogicAnchor.bb $GlueLogicAgent");
&GlueLogicAccess("!DelInformTo $GlueLogicAnchor.zzz $GlueLogicAgent");
&GlueLogicDisconnect();
exit 0;
このプログラムは三つの部分から構成されており、 それぞれ初期化部, イベントループ, 終結部とでも呼ぶべき機能を果たしている。

このエージェントでは、 メッセージに対する動作の指定をメッセージを書き込む名前によって区別しており、 そのメッセージに対する引数を対応する名前に書き込むこととしている。 また、このメッセージ対する戻り値を GlueLogic に書き込むのは メッセージの処理ルーチンの責任である。 相互にメッセージの交換を行なうことを原則とする場合には、 メッセージ交換を行なうエージェント を参照のこと。

なお、実際に Glue Logic とデータをやり取りするコードは イベントループ内から呼び出される関数の中に含まれることになるが、 その詳細に関しては サーバから受け取った情報の処理 を参照のこと。


初期化部

最初に GlueLogic パッケージの利用を宣言した後に、 GlueLogicParseArgs 関数によって 標準コマンドライン・オプション の値を取り込む。

その後に GlueLogicConnect 関数によって サーバと通信路を開設するが、この時に与えられる引数は 通信路開設時にはもっとも優先度の低い情報源として利用される。
すなわち、これらの引数よりは環境変数の指定の方が優先され、 さらにそれらよりは $GlueLogicAgent, $GlueLogicAnchor, $GlueLogicServer の 各変数の値の方が優先される。 このため、 GlueLogicParseArgs 関数が既に実行されておれば、 コマンドライン引数として指定された値が最優先で使われる。

初期化の最後としてこのエージェントが監視しようとする名前について、 変更通知メッセージの要求登録を行なう。


イベントループ

イベントループは、サーバからの通知メッセージを待ち合わせ、 メッセージが届けばそれに対応したメッセージ処理関数を呼び出す。 上記の例では、 アンカーで指定された名前で示される構造の二つの要素に対して、 値の変化があった時に対応する動作を行ない、 他の一つの要素の値が変化した時にはエージェントの処理を停止する。

まずループに入る前に、ループ内での文字列比較に備えて、 Agent ID を正規表現のテンプレートに変換しておく。 これをやっておかないと、例えば名前の中に含まれるピリオドは、 正規表現として解釈されると任意の一文字になってしまうため、 正しい比較ができなくなってしまう。 quotemeta は perl5 から新しく追加された標準関数である。

エンドレス・ループの先頭では、 変更通知メッセージを容れる待ち行列である @GlueLogicMessageQueue の 内容を確認し、もしも一つもメッセージが入っていない場合には GlueLogicWaitForMessage 関数を呼び出す。 この関数は変更通知メッセージまたは標準入力からの入力行のどちらか一方が 確認されるまで待ち合わせる。

その後、 GlueLogicEnqueueMessage 関数を用いて、 確認された変更通知メッセージまたは入力行が、 それぞれ @GlueLogicMessageQueue または @GlueLogicStdinQueue に push される。

これで、新しい事象の発生が確認され、その事象の解析を行なえる状態になったが、 この例では変更通知メッセージだけが注目され、 標準入力は特に問題とはされていないので、 @GlueLogicMessageQueue が一つも要素を持っていない場合には、 次の繰り返しに入る。

もし万一、サーバが異常終了していた場合には、 @GlueLogicMessageQueue には undef が入っている。 この場合には必ずイベントループから抜け出すようにしておかなければならない。 なお、サーバの異常終了によって通信路が閉鎖されてしまった後では、 どの API も何もしないようになるので、 以降の処理で特に場合分けを行なう必要は無い。

以降の連続する if 文によって行なわれる処理が、 値の変更があった事が通知された名前毎に、 対応する処理関数を呼び出す部分である。 この関数の中では、必要に応じて、新しい値の取得、 対応する内部処理、 GlueLogic に対するデータの書き込みが行なわれる。

この例では、 イベントとしてサーバからの通知メッセージの到着のみを対象としているが、 GlueLogicEnqueueMessage 関数は 原則として標準入力からの入力行の到着も通知メッセージの到着と同等に扱う。 このため、どちらか一方だけを問題にしたい場合には、 この例のように他方のイベントの発生を適切に無視するように注意しなければならない。


終結部

イベントループでの処理が完了すると、 それまで要求していた変更通知メッセージの登録を抹消し、 GlueLogicDisconnect 関数によってサーバとの通信路を閉鎖する。

自分が変更要求を出していた名前の全てに対して、 忘れずに必ず要求登録を抹消しなければならない。


 [M.T. HomePage]  [written & copyrighted by Masayuki Takata]