Caché は、特定のイベントが発生したときに、ユーザが記述したルーチンを呼び出すメカニズムを提供します。各イベントの処理は、通常システム自体のサブルーチンとして扱われます。Caché には、
^%ZSTART および
^%ZSTOP という 2 つのルーチンがあります。それぞれが、システムが事前に定義した 4 組の中の 1 つ以上のラベルを定義します。これらのラベルは、イベントのクラスが起きた際に呼び出される各ルーチンへのエントリ・ポイントになります。
-
SYSTEM:Caché がシステムとして開始または停止します
-
LOGIN:ユーザがログインまたはログアウトを実行します
-
-
CALLIN:外部プログラムが開始または
CALLIN が完了します
これらのエントリ・ポイントは、Caché のサブルーチンとして機能しますが、複雑な計算をしたり、長時間実行するためのものではありません。ネットワーク・アクセスなどのように長期的に計算したり、潜在的に長時間わたって実行すると、呼び出されたルーチンが返されるまで動作の完了が遅れます。この場合、ユーザのログインに長い時間がかかったり、時間がかかりすぎるために JOB の処理能力が削減される場合があります。
-
ルーチンは Caché ObjectScript で記述しなければなりません。
-
任意のルーチン・エントリ・ポイントが呼び出されたときに、引数として渡される値はありません。様々な状況で異なるアルゴリズムが適用できる場合は、呼び出されたエントリ・ポイントはルーチンの外部、つまりグローバルやシステム変数や
$ZUTIL ルーチンからの戻り値などのデータを調査して、何を実行するかを決定する必要があります。
-
あらゆる条件下で、ルーチンがうまく動作することを確認してください。ルーチンは防御的に記述します。つまり、タスクの完了に必要なすべてのリソースがすぐ使用できる場所にあり、可能であれば演算処理が始まる前にルーチンに予約されることを、これらのルーチンが確認します。発生したエラーはそのシステム関数の失敗として報告されるので、エラー抑制と処理の観点から設計を考えることが重要です。リソースが見つからない、あるいはエラーが起こる際に適切に回復できなければ、Caché の開始の失敗、スタジオなどの主要な機能の動作の不具合、またはすぐには検知されないようなわずかな内部的な影響など、さまざまな結果を引き起こします。これらのルーチンは、細心の注意を払って記述し、シミュレート条件下でデバッグし、製品に使用する前にシミュレート環境下でテストすることを推奨します。
-
以前の呼び出しの間に見つかった条件や、異なるエントリ・ポイントが有効であるとは考えないでください。例えば、
JOB^%ZSTART への連続呼び出し間に、次の呼び出しが発生する前に前回使用したファイルが削除される可能性もあります。
-
各エントリ・ポイントはタスクを効率的に実行します。タスクの一部が、潜在的に長時間実行するもので完了に十分な情報であれば、ユーザのアプリケーションの別の部分によって後から処理するように、待ち行列に入れられます。
-
エントリ・ポイントが統計的に使用する目的でデータを永続的に保持したい場合、データの保持にはグローバルや外部ファイルなどを使用する必要があります。
-
ルーチンを実行する環境についての条件は、最小限にしてください。例えば、このようなルーチンの開発者は、プログラムが常に特定のジョブ数以下で実行されるとは予測できませんし、設計者は、特定の命令でさまざまなエントリ・ポイントが呼び出されると想定できません。Caché を実装する複数のプロセスを呼び出す配列が、決定性である (次のステップが 1 つに決まる) ことはほとんどありません。
-
ルーチンは、システムの開始中の特定の時点で呼び出されるとは限りません。開始中のイベントの配列は、リリースの度に、または再開の度に変更する可能性があります。
-
%ZSTART および %ZSTOP を有効にする
^%ZSTART と
^%ZSTOP は、
%SYS ネームスペースに属さなければ (したがってコンパイルされなければ) なりません。しかし、Caché がこれらを使用して自動的に開始するには、ただそこにあるだけでは不十分です。代わりに、構成マネージャから個々のエントリ・ポイントを有効または無効にします。
一旦ルーチンを設計、開発、コンパイルし、テストができる段階になると、構成マネージャから個々のエントリ・ポイントを有効にすることができます。構成マネージャを開始し、[詳細] タブを選択します。表示されたパネルで [開始] から、[ユーザ・コード有効] を開くと、8 項目のリストが表示されます。このリストは、4 つのイベント・クラスの動作開始または停止の、すべての組み合わせに対応しています。
これらの各項目を、有効にする (= はい) または無効にする (= いいえ) に指定します。設定を変更するには、項目を強調表示にして [変更] オプションを選択します。項目の有効 (チェックあり)/ 無効 (チェックなし) を示すチェック・ボックスを持つダイアログ・ボックスが表示されるので、必要に応じて変更し、設定を変更した場合は [OK] をクリックして適用します。
構成マネージャの終了時に、ユーザはこの設定を適用するか、適用しないかを選択することができます。場合によっては、この設定を有効にするために Caché を再起動する必要があります。
例えば、バックグラウンド・プロセスが開始するたびに何かを得たいとします。以下の
すべてに該当する場合、適切なエントリ・ポイント
JOB%^ZSTART が呼び出されます。
-
構成マネージャで、希望するルーチンのエントリ・ポイントが有効になっていること。つまり、構成マネージャの [詳細] タブの [開始] 項目の [ユーザ・コード有効] リストを展開すると、
ジョブ開始用 = はい と表示されています。
-
構成マネージャに制御されるローカルの Caché システムに、
^%ZSTART がコンパイルされロードされること。
-
ルーチンが
JOB エントリ・ポイントを含むこと。
^%ZSTART および
^%ZSTOP のデバッグ
最終的な環境において
^%ZSTART と
^%ZSTOP をデバッグする機会はほとんどありません。エラーが発生した際にインタラクティブ・セッションでターミナルに記述されるエラー・メッセージは、オペレータのコンソール・ログに転送されます。これは Caché 管理者ディレクトリにある
cconsole.log ファイルです。
メッセージは、失敗の原因とエラーが検出された位置を示します。この位置は、プログラム・ロジックまたはフローで実際にエラーが発生した場所とは異なる場合があります。開発者は、提供された情報からエラーの特性と場所を推定し、ルーチンを修正してください。これにより、今後のテストでは、発生するエラーの特性についてより多くの情報を得ることができるようになります。
極端な失敗が発生した場合でない限り、他の Caché の機能が使用できなくなっていても、構成マネージャは開始したり、実行することができます。つまり、原因の関数の使用を無効にし Caché を再起動することにより、そのエラー状態から回復し分析することができます。
1 つ以上のエントリ・ポイント呼び出しを無効にするには、以下を実行します。
-
-
-
[開始] の [ユーザ・コード有効] を開きます。
-
それぞれの項目を選択し、[変更] をクリックして、各オプションの設定を変更します。
-
場合によっては、この設定を有効にするために Caché を再起動する必要があります。
^%ZSTART および
^%ZSTOP (および、サポートする任意のルーチン) が正確に格納されるように注意してください。これらのトレースをすべて削除するには、以下を実行します。
-
-
-
-
右側のパネル (ルーチンのコンテンツ) で、削除するルーチンを探します。
-
-
[ファイル] メニューから [削除] を選択します。次に表示されるダイアログ・ボックスで、[はい] を選択すると、削除を確認します。
Note:
必ず、ルーチンを削除する
前 に、Caché 構成マネージャ経由でエントリ・ポイントを無効にしてください。この変更を有効にするために、構成マネージャが Caché を再起動するように警告した場合は、次に進む前に再起動も実行してください。これにより、エントリ・ポイントが削除される間に、実行されることはありません。
Note:
以下の 3 つのコードは、ユーザの理解を深めるために記述されたものです。ObjectScript の便利な利用法や簡潔な記述方法を示したものではありません。
このルーチンには、2 つのエントリ・ポイントがあります。一方は、オペレータのコンソール・ログに 1 行記述し、他方は、ローカルのログ・ファイルに名前と値の組み合わせのリストを記述します。オペレータのコンソール・ログもローカルのログ・ファイルも Caché の管理者のディレクトリにあります。このディレクトリの名前は、
ManagerDirectory メソッドまたは
%Library.File クラスにより返されます。
%ZSSUtil ;
; this routine packages a set of subroutines
; used by the %ZSTART and %ZSTOP entrypoints
;
; does not do anything if invoked directly
quit
#Define Empty ""
#Define OprLog 1
WriteConsole(LineText) PUBLIC ;
; write the line to the console log
; by default the file cconsole.log in the MGR directory
new SaveIO
; save the current device and open the operator console
; set up error handling to cope with errors
; there is little to do if an error happens
set SaveIO = $IO
set $ZTRAP = "WriteConsoleExit"
open $$$OprLog
use $$$OprLog
; we do not need an "!" for line termination
; because each WRITE statement becomes its
; own console record (implicit end of line)
write LineText
; restore the previous io device
close $$$OprLog
; pick up here in case of an error
WriteConsoleExit ;
set $ZTRAP = ""
use SaveIO
quit
WriteLog(rtnname, entryname, items) PUBLIC ;
; write entries into the log file
; the log is presumed to be open as
; the default output device
;
; rtnname: distinguishes between ZSTART & ZSTOP
; entryname: the name of the entrypoint we came from
; items: a $LIST of name-value pairs
new ThisIO, ThisLog
new i, DataString
; preserve the existing $IO device reference
; set up error handling to cope with errors
; there is little to do if an error happens
set ThisIO = $IO
set $ZTRAP = "WriteLogExit"
; construct the name of the file
; use the month and day as part of the name so that
; it will create a separate log file each day
set ThisLog = "ZSS"
_ "-"
_ $EXTRACT($ZDATE($HOROLOG, 3), 6, 10)
_".log"
; and change $IO to point to our file
open ThisLog:"AWS":0
use ThisLog
; now loop over the items writing one line per item pair
for i = 1 : 2 : $LISTLENGTH(items)
{
set DataString = $LISTGET(items, i, "*MISSING*")
if ($LISTGET(items, (i + 1), $$$Empty) '= $$$Empty)
{
set DataString = DataString
_ ": "
_ $LISTGET(items, (i + 1))
}
write $ZDATETIME($HOROLOG, 3, 1),
?21, rtnname,
?28, entryname,
?35, DataString, !
}
; stop using the log file and switch $IO back
; to the value saved on entry
close $IO
; pick up here in case of an error
WriteLogExit ;
set $ZTRAP = ""
use ThisIO
quit
このルーチンは、他のルーチンと同様に、まず QUIT コマンドを実行して、以下のコマンドから呼び出されると良好な結果が得られます。
#DEFINE 配列は、外観をそろえるために、プログラムの本文に指定された制約を提供します。このインスタンスでは、空文字列とオペレータのコンソール・ログのデバイス番号を指定します。
ラベル:
WriteConsole^%ZSSUtil
このエントリ・ポイントは非常に単純です。容量の少ない出力用に、またデバッグの出力に使用するための最小限のルーチンとして、設計されたものです。
引数として 1 つの文字列を持ち、これをオペレータのコンソール・ログに記述します。しかし、現在の呼び出しの
$IO 装置の保存とリストアは、注意して実行しなければなりません。
コンソール・デバイスでは、デバイス各項目が送信された結果、コンソール・ログには別々のレコードが記述されます。以下にコードの例があります。
上記は、4 つのレコードを記述します。最初の 3 つは 1 桁の数字からなり、4 つめは空白行からなります。1 行に複数の項目を記述したい場合は、呼び出し元がこれらを文字列に連結させなければなりません。
これは、上記のルーチンよりも複雑です。
^%ZSTART または
^%ZSTOP 内の任意のエントリ・ポイントから呼び出すことができます。最初の 2 つの引数は、このラベルが何に対して実行しているかについてのリポートに必要な情報を提供します。3 番目の引数は、ログに記述される名前と値の組み合わせの
$LIST です。
このエントリ・ポイントは、まず使用するファイルの名前を作成します。ログ管理を簡素化するため、この名前にはルーチンが入力された月日が含まれます。したがって、WriteLog への呼び出しは、現地時間が深夜 12 時をすぎる度に、新規のファイルを作成します。その名前は呼び出し時にのみ決定されるため、引数として渡される名前と値の組み合わせはすべて同じファイルに表示されます。
一旦名前が作成されると、
$IO の現在値を後で使用できるように保存し、出力デバイスを指定されたログ・ファイルに切り替えます。
OPEN コマンドに使用するパラメータによって、そのファイルがなければ作成するように指定されています。timeout がゼロの場合、Caché がファイルを 1 回だけ開こうとします。 もし開くことができなければ失敗します。
そのファイルが一旦開かれると、コードは名前と値の組み合わせをループします。各組み合わせに対し、その行に呼び出しルーチン名とエントリ・ポイント名、続けて名前と値の組み合わせが記述されます (値の部分が空文字列の場合は、名前のみ記述)。組み合わせは 1 行に 1 個ずつ記述されます。読みやすいように、各行の最初の 3 つの値は一列に並ぶようになっています。
すべての組み合わせの記入後、ログ・ファイルを終了し、先ほど保存した
$IO の値がリストアされ、コントロールは呼び出し元に返されます。
このルーチンには、実際に Caché に呼び出されるエントリ・ポイントを含みます。上記の
^%ZSSUtil の機能も使用します。すべてのエントリ・ポイントは、事実上同じように動作し情報をログに配置します。SYSTEM エントリ・ポイントは、他に比べるとやや複雑ですが、同様にオペレータのコンソール・ログに情報を配置します。
%ZSTART ; User startup routine.
#Define ME "ZSTART"
#Define BgnSet "Start"
#Define Empty ""
; cannot be invoked directly
quit
SYSTEM ;
; Cache starting
new EntryPoint, Items
set EntryPoint = "SYSTEM"
; record the fact we got started in the console log
do WriteConsole^%ZSSUtil((EntryPoint
_ "^%"
_ $$$ME
_ " called @ "
_ $ZDATETIME($HOROLOG, 3)))
; log the data accumulate results
set Items = $LISTBUILD($$$BgnSet, $ZDATETIME($HOROLOG, 3),
"Job", $JOB,
"Computer", $ZUTIL(110),
"Version", $ZVERSION,
"StdIO", $PRINCIPAL,
"Namespace", $ZUTIL(5),
"CurDirPath", $ZUTIL(12),
"CurNSPath", $ZUTIL(12, ""),
"CurDevName", $ZUTIL(67, 7, $JOB),
"JobType", $ZUTIL(67, 10, $JOB),
"JobStatus", $ZHEX($ZJOB),
"StackFrames", $STACK,
"AvailStorage", $STORAGE,
"UserName", $ZUTIL(67, 11, $JOB))
do WriteLog^%ZSSUtil($$$ME, EntryPoint, Items)
quit
LOGIN ;
; a user logs into Cache (user account or telnet)
new EntryPoint, Items
set EntryPoint = "LOGIN"
set Items = $LISTBUILD($$$BgnSet, $ZDATETIME($HOROLOG, 3))
do WriteLog^%ZSSUtil($$$ME, EntryPoint, Items)
quit
JOB ;
; JOB'd process begins
new EntryPoint, Items
set EntryPoint = "JOB"
set Items = $LISTBUILD($$$BgnSet, $ZDATETIME($HOROLOG, 3))
do WriteLog^%ZSSUtil($$$ME, EntryPoint, Items)
quit
CALLIN ;
; a process enters via CALLIN interface
new EntryPoint, Items
set EntryPoint = "CALLIN"
set Items = $LISTBUILD($$$BgnSet, $ZDATETIME($HOROLOG, 3))
do WriteLog^%ZSSUtil($$$ME, EntryPoint, Items)
quit
^%ZSSUtil の説明でも述べたように、このルーチンはまず
QUIT コマンドを実行します。 エントリ・ポイントの 1 つから実行を開始するのではなく、ルーチンとして呼び出す方が良好な結果を得ることができるためです。
このルーチンは、そのルーチン自体の名前、開始文字列および空文字列に対し、指定された定数 (マクロとして) を定義します。
後で、表示したい名前と値の組み合わせのリストを作成し、これを
WriteLog^%ZSSUtil に渡してローカルのログ・ファイルに配置します。その後、呼び出し元に戻ります。
ラベル:
LOGIN^%ZSTART、
JOB^%ZSTART、
CALLIN^%ZSTART
SYSTEM エントリ・ポイントとは異なり、これらはオペレータのコンソール・ログに情報を置きません。呼び出す際に識別するための簡単な項目のリストを作成し、
WriteLog^%ZSSUtil を使用して記録します。
このルーチンは、実際に Caché に呼び出されるエントリ・ポイントを含みます。
^%ZSTART と同様に、
^%ZSSUtil の機能を使用します。
^%ZSTOP のエントリ・ポイントは、
%ZSTART のエントリ・ポイントと同じ名前と同様の機能を持ち、SYSTEM エントリ・ポイントのみがオペレータのコンソール・ログに記述します。すべてのルーチンは、ローカルのログに動作状況を記録します。
%ZSTOP ; User shutdown routine.
#Define ME "ZSTOP"
#Define EndSet "End"
#Define Empty ""
; cannot be invoked directly
quit
SYSTEM ; Cache stopping
new EntryPoint
set EntryPoint = "SYSTEM"
; record termination in the console log
do WriteConsole^%ZSSUtil((EntryPoint
_ "^%"
_ $$$ME
_ " called @ "
_ $ZDATETIME($HOROLOG, 3)))
; write the standard log information
do Logit(EntryPoint, $$$ME)
quit
LOGIN ; a user logs out of Cache (user account or telnet)
new EntryPoint
set EntryPoint = "LOGIN"
do Logit(EntryPoint, $$$ME)
quit
JOB ; JOB'd process exits.
new EntryPoint
set EntryPoint = "JOB"
do Logit(EntryPoint, $$$ME)
quit
CALLIN ; process exits via CALLIN interface.
new EntryPoint
set EntryPoint = "CALLIN"
do Logit(EntryPoint, $$$ME)
quit
Logit(entrypoint, caller) PRIVATE ;
; common logging for exits
new items
set items = $LISTBUILD($$$EndSet, $ZDATETIME($HOROLOG, 3))
do WriteLog^%ZSSUtil(caller, entrypoint, items)
quit