
本記事は、IRIS/Cacheにおける%Statusについて解説したいと思います。
はじめに
本記事では、IRIS/Cacheのロジックで頻出する、「%Status(%Library.Status.cls)」の使い方について解説したいと思います。
%Statusは、例外処理や関数の戻り値として活用する場面が多いです。
また、他の言語とはやはり異なる側面もあり、新規ObjectScriptユーザーは少々抵抗感があるかと思います。
これから解説する内容で、それらの垣根が超えれれば幸いです。
では、開始します。
%Statusについて
基本情報
ObjectScriptでは、関数の戻り値やAPIの応答で「%Status」を利用しており、正常に終了したかどうかを判定しており、エラー処理の要となっています。
処理が正常に終了した場合は1を取得し、何らかの問題が発生した場合はエラー情報が取得できます。
%Status作成方法
%Statusを作成するには、3通りがあります。
s sts = $$$ERROR([エラー・コード], [メッセージ1], [メッセージ2], ...)
s sts = $$$ERR([エラー・コード])
s sts = $system.Status.Error([エラー・コード], [メッセージ1], [メッセージ2], ...)
マクロ「$$$ERROR」は、「%occStatus.inc」のインクルードが必要になります。
→「%RegisteredObject」にはデフォルトでインクルードが設定されています。
エラーコードに関しては、「%occErrors.inc」から取得すればよいと思います。
メッセージに関しては、ドキュメントを参照して確認して下さい。(「一般的なエラー・メッセージ」で検索して下さい)。
→3,000件くらい存在しています。
【サンプル】
%Statusから例外クラスを作成し、throwしています。
ClassMethod makeStatus()
{
try {
throw ##class(%Exception.StatusException).CreateFromStatus(
$$$ERROR($$$FileCanNotCopy, "D:\Temp\from\hoge.txt", "D:\Temp\to\hoge.txt")
)
} catch e {
w !, $system.Status.GetErrorText(e.AsStatus())
}
}
【実行結果】
エラー #5024: ファイル ‘D:\Temp\from\hoge.txt’ を ‘D:\Temp\to\hoge.txt’ にコピー できません
分かりやすいエラー内容ですね。
%Fileの関数は、成否が%boolean値で返って来る関数が多いので、throwしたい場合はこのような手段を使うと良いと思います。
%Statusの合成
複数のエラーが同時に発生した場合はどうしましょう?
今までのサンプルでは、1つのエラーに対し1つの%Statusしかthrowしていませんでした。
複数の%Statusをthrowしたい時はどうしましょう?
そんな時は、%Statusを合成しちゃいましょう!
配列で合成
サンプル
ClassMethod compStatus(errCode As %Integer, arg...)
{
#dim list as %Library.ListOfDataTypes
try {
s sts1 = $$$ERROR($$$FileCanNotOpen, "D:\Temp\hogehoge.txt")
, sts2 = $$$ERROR($$$FileNameInvalid, "D:\Temp\hogehoge.txt")
s cmpSts = $System.Status.AppendStatus(sts1,sts2)
throw ##class(%Exception.StatusException).CreateFromStatus(cmpSts)
} catch e {
w !, $system.Status.GetErrorText(e.AsStatus()),!
w $System.Status.GetOneStatusText(e.AsStatus(),1),! // 個別もOK
w $System.Status.GetOneStatusText(e.AsStatus(),2),!
}
}
【実行結果】
エラー #5005: ファイル ‘D:\Temp\hogehoge.txt’ を開けません
エラー #5006: ファイル名 ‘D:\Temp\hogehoge.txt’ が正しくありません
ファイル ‘D:\Temp\hogehoge.txt’ を開けません
ファイル名 ‘D:\Temp\hogehoge.txt’ が正しくありません
二つのエラーが表示されています。
合成関数のコーディングが大変だと思ったら、下記マクロでも合成が可能です。
s sts3 = $$$ERROR($$$DirectoryNameInvalid, "D:\Temp\")
s cmpSts = $$$ADDSC(cmpSts,sts3) // マクロ版
できれば多重エラーなんて見たくない光景ですが、エラーの対応時間が少なくなるのであれば、御の字ですね。
合成したエラーを分解する
複数のエラーがthrowされてきても困る!
1つづつ分解してロギングしたい!
そんな時は、関数「GetErrorText()」を使って、%Statusを分解してみましょう
【サンプル】
} catch e {
d $system.Status.DecomposeStatus(e.AsStatus(), .eList, "d")
f pos=1:1:eList {
w eList(pos),!
}
}
第3引数の「”d”」はお好みで。
これで1行1行ロギングが可能になりました。
埋め込んで合成
次は埋め込み構造で合成してみます。
先ほどとは異なり、合成を行う関数に「EmbedStatus」を使用しています。
【サンプル】
ClassMethod compStatus(errCode As %Integer, arg...)
{
#dim list as %Library.ListOfDataTypes
try {
s sts1 = $$$ERROR($$$FileCanNotOpen, "D:\Temp\hogehoge.txt")
, sts2 = $$$ERROR($$$FileNameInvalid, "D:\Temp\hogehoge.txt")
s cmpSts = $System.Status.EmbedStatus(sts1,sts2)
s sts3 = $$$ERROR($$$DirectoryNameInvalid, "D:\Temp\")
s cmpSts = $$$EMBEDSC(cmpSts,sts3) // マクロ版
throw ##class(%Exception.StatusException).CreateFromStatus(cmpSts)
} catch e {
w !, $system.Status.GetErrorText(e.AsStatus()),!
}
}
【実行結果】
エラー #5005: ファイル ‘D:\Temp\hogehoge.txt’ を開けません
> エラー #5006: ファイル名 ‘D:\Temp\hogehoge.txt’ が正しくありません
> エラー #5007: ディレクトリ名 ‘D:\Temp\’ が不正です
色々なthrow
%Statusをthrowするのであれば、マクロを活用するとコーディングが減って楽になります。
これらのマクロは、「%occStatus.inc」をインクルードすれば全て利用できます。
$$$ThrowOnError
筆者が、普段よく使っているマクロです。
本当にお世話になっています。
このマクロは、引数に%Statusか%Statusを返す処理を実行し、「%Status」を判定してエラーの場合throwします。
【サンプル】
ClassMethod getStatus() As %Status
{
q $$$ERR($$$TooManyErrors)
}
ClassMethod testThrow()
{
try {
$$$ThrowOnError(..getStatus())
} catch e {
w !, $system.Status.GetErrorText(e.AsStatus())
}
}
$$$ThrowOnError, $$$TOE
$$$ThrowOnErrorと$$$TOEは、全く同じ内容です。
戻り値「%Status」を変数に取得できるので、catch部でエラーを握りつぶして後続処理でエラーを解析する事も可能です。
サンプル
ClassMethod testThrow()
{
try {
$$$TOE(sts, ..getStatus())
} catch e {}
w !, $system.Status.GetErrorText(sts)
}
$$$ThrowStatus
%Statusが成功であろうと失敗であろうと、「##class(%Exception.StatusException).ThrowIfInterrupt()」を実行し、必ずエラーとしてthrowします。
$$$ThrowSQLCODE
これは、「##class(%Exception.SQL).CreateFromSQLCODE()」を実行してthrowします。
$$$ThrowSQLIfError
SQLCODEは、0, 100, 負の整数値で、このマクロは「SQLCODE<0」の条件時に「CreateFromSQLCODE()」を実行します。
%Statusの判定
%Statusを判定するとして「%Status=1」は推奨されていません。
%Statusを判定するためのマクロや関数が用意されているので、それらを活用しましょう。
$$$ISOK, $SYSTEM.Status.IsOK
%Statusが成功している時、「true」を返します。
→実際の処理は、「%Status」に対し、否定を重ねる「”%Status」をしています。
$$$ISERR, $SYSTEM.Status.IsError
%Statusが失敗している時、「true」を返します。
→これも「’%Status」しているだけです。
ClassMethod testCheck()
{
i ($$$ISERR(..getStatus())) {
w !,"エラー発生"
}
}
$$$QuitOnError
%Statusが失敗している時、quitを行い%Statusを返します。
ClassMethod testCheck() As %Status
{
$$$QuitOnError(..getStatus())
}
$$$QUITONERROR
引数に、%Statusの受け取りと処理を渡します。
%Statusが失敗している時、quitを行い%Statusを返します。
$$$QuitOnErrorの方が引数少ないし良いですね。
これは使わないと思います。
新規エラーコードの作成
システムによっては、既存のエラーメッセージでは物足りない時・・・
そんな時は、カスタムしたエラー・コードとメッセージを作成しましょう。

%MessageDictionary
カスタム・エラーを作成するには、%MessageDictionary(%Library.MessageDictionary.cls)を使用します。
では、オリジナルのカスタム・エラーを作成していきましょう!
xmlファイルの作成
新規作成するエラー情報を作成します。
ファイル名は適宜命名してください(今回はcustomError.xml)。
カスタム・エラー情報を作成するには、<Message>エレメントを編集する事になります。
※「Language」「Domain」以外は定型
項目 | 説明 |
---|---|
Id | エラーコード |
Name | 名称(用途不明 設定しなくても動くみたい?) |
値 | エラー・メッセージになります。 %1, %2…と設定する事で、エラー時の引数を充てる事が可能です |
<?xml version="1.0" encoding="UTF-8" ?>
<MsgFile Language="ja">
<MsgDomain Domain="UserErrors">
<Message Id="-1000" Name="CustomErr1">坊やだからさ。</Message>
<Message Id="-1001" Name="CustomErr2">すごい・・・%1が熱中するわけだ。</Message>
<Message Id="-1002" Name="CustomErr3">それでも%1ですか!%2</Message>
</MsgDomain>
</MsgFile>
xmlファイルのインポート
カスタム・エラーを作成したら、次は反映するネームスペースでインポートします。
インポートコマンド
zn "SAMPLE"
s path="[xml配置dir]\customError.xml"
w ##class(%MessageDictionary).Import(path)
インポート内容の確認
→ターミナルに張り付けて確認してみて下さい。
CheckMessage ;
s ms = ##class(%MessageDictionary).%New()
, ms.Domain = "UserErrors"
, ms.Language = "ja"
s id=""
f {
d ms.GetNextMessage(.id) q:id=""
w "エラー・コード:"_id_", メッセージ:"_ms.GetMessage(id)
}
d CheckMessage
【実行結果】
エラー・コード:-1002, メッセージ:それでも%1ですか!%2
エラー・コード:-1001, メッセージ:すごい・・・%1が熱中するわけだ。
エラー・コード:-1000, メッセージ:坊やだからさ
使用してみる
では、カスタム・エラーの運用を確認してみます。
【サンプル】
ClassMethod testCustomError(errCode As %Integer, arg...)
{
#dim list as %Library.ListOfDataTypes
try {
throw ##class(%Exception.StatusException).CreateFromStatus(
$$$ERROR(errCode, arg...)
)
} catch e {
w !, $system.Status.GetErrorText(e.AsStatus())
}
}
実行結果

あー、うん。
ちゃんとオリジナルのメッセージが出力されていますね。
これでシステム固有のエラーメッセージを表示する事が出来ました。
エラー処理も、より柔軟に対応できますね。
カスタム・エラーの削除
不要になったメッセージを削除するには下記コマンドを使用します。
w ##class(%MessageDictionary).Delete([Language]) // Language > "ja"
コマンドを実行すると、一切合切消えてしまいます。
おわりに
いかがだったでしょうか。
%Statusの正しい扱い方を理解する事で、ObjectScriptにおけるエラー処理の柔軟性や信頼度が大きく向上すると思います。
また、システムの安全な運用や保守性を高めるためにも、公式が用意しているユーティリティメソッドを活用していきましょう。
日々、より堅牢なコード設計を目指していきたいですね。