エラーの処理

EGL を使用すると、エラーの場合のプログラムの動作を決定することができます。

エラーの処理とは、プログラムで発生する可能性のある問題の種類を予想し、それぞれについてコードのパスを提供することです。 エラーを処理しない場合、無視できる範囲のもの (このトピックで後述する『入出力エラー』を参照) を除き、エラーの発生したプログラムはすべて強制終了されます。

EGL でのエラー処理は、レコードに適用されるステレオタイプである、例外 の概念を発展させたものです。 EGL には、多数の定義済み例外 (「EGL 言語解説書」の付録『EGL 例外レコード』を参照) があり、独自の例外も定義できます。各例外レコードには、少なくとも以下のフィールドが含まれます。
messageID
その例外の EGL メッセージを含むストリングです。例えば、初期化されていない配列を使用しようとすると、EGL は NullValueException レコードの messageID を EGL0106E に設定します。
message
その問題の簡単な説明を含むストリングです。例えば、messageID が EGL0106E の場合 message は、「NULL 参照が使用されました。」です。

例外レコードには、必要に応じて追加のフィールドを含めることができます。例えば、IndexOutOfBoundsException には、indexValue の追加フィールドがあり、EGL が処理できなかった配列添字の値が含まれます。

try ブロック

EGL の try キーワードは、ユーザーが例外をキャッチして処理できるコードのブロックを導入します。 try ブロックの内部で例外が発生すると、EGL はその try ブロック内で、その例外タイプに一致する onException ステートメントを探します。 各 onException ステートメントには、例外変数の宣言が含まれます。 これは、EGL の他の部分で宣言する変数と同様のレコード変数であり、宣言は次のようになります。
onException(myEx NullValueException)
これは、EGL が NullValueException をこの try ブロック内でスローした場合、ユーザーはその例外レコードのフィールドにアクセスできることを意味します。 以下に、myEx.msgID 内の EGL メッセージ ID の例を示します。
try
  intArray[10] = 0;  // これは初期化されていない場合があります
onException(myEx NullValueException)
  writeStdErr(myEx);
  myErrorHandler(myEx);
end

myErrorHandler() 関数は、例えば、配列を初期化してから、もう一度代入を行います。

特別なタイプの例外である AnyException は、すべての例外をキャッチするために使用でき、ANY プリミティブ型に相当します。 AnyException にレコード変数を宣言すると、EGL が例外をスローしたときに、その変数は、実際の例外レコードの型になります。 以下の例を考えてください。
try
  get next mySerialRecord
onException(myEx AnyException)
  myErrorHandler(myEx);
end
EGL が FileIOException をスローしたとき、実行したい特定のコードのパスがある場合は、そのための特別なチェックを次のように追加することができます。
try
  get next mySerialRecord
onException(myEx FileIOException)
   myErrorHandler1(myEx);
onException(myEx AnyException)
  myErrorHandler2(myEx);
end

myErrorHandler2() は FileIOException 以外のすべての例外を処理します。

EGL が例外をスローしても、その例外をキャッチする onException ブロックがない場合、関数は直ちに終了し、エラーをスローした関数を呼び出した関数にコントロールが戻ります。 このように、onException ブロックで関数が例外をキャッチするか、例外が main 関数に到達するまで、EGL は例外を上方に渡します。 main 関数が例外のキャッチに失敗すると、プログラムは直ちに終了し、その例外のメッセージ・フィールドをログに書き込みます。 リモート側の呼び出し先プログラムで例外が発生すると、呼び出し側プログラムは、オリジナルの例外ではなく、InvocationException を受け取ります。 同様に、サービス関数で例外が発生すると、呼び出し側プログラムには ServiceInvocationException が渡されます。

つまり関数は、次の例のように、呼び出した関数内でスローされた例外をキャッチできます。
function FuncOne(myRec serialRecordType)
  try
    FuncTwo(myRec);
  onException(myEx AnyException)
    myErrorHandler2(myEx);
  end
end

function FuncTwo(myRec serialRecordType)
  get next myRec;
end
V6 例外互換モードでは、このように例外が関数から関数に渡されることはありません。これについては、後述します。

ハードとソフトの入出力エラー

ファイルの読み取りまたは書き込み時に、EGL はハードソフト の入出力エラーを区別します。 以下のエラーは、ソフト、つまりデータの損失は生じないと見なされます。

表 1. ソフト入出力エラー
エラー 意味
重複 索引付きレコードまたは相対レコードの場合で、このエラーは同じキーを持つ 2 つ目のレコードがあったことを意味します。
endOfFile シリアル・レコード、索引付きレコード、または相対レコードの場合で、このエラーはファイルの末尾を越えて読み取ろうとしたことを意味します。
noRecordFound あらゆるレコード・タイプの場合で、このエラーは見つからないレコードを読み取ろうとしたことを意味します。

無効なファイル・フォーマットやファイルがいっぱいであるような、その他のエラーはハード・エラーと見なされます。 索引ファイル、相対ファイル、またはシリアル・ファイルに対するハード入出力エラーでは、FileIOException がスローされます。SQL データベースに対するハード入出力エラーでは、SQLException がスローされます。

ソフト入出力エラーでは、レコードにエラーの値が関連付けられますが、例外はスローされません。 この状態をテストするには、is または not 演算子を使用します。 これらの演算子を使用するために、該当する入出力ステートメントを try ブロックの中に置く必要はありません。 ただし、その入出力ステートメントを try ブロックの中に置かない場合には、ハード入出力エラーを同時にチェックすることはできません。
while(TRUE)  // エンドレス・ループ
  try
    get next mySerialRecord;
    if(mySerialRecord is endOfFile)
      exit while;
    end
  onException(myEx AnyException)
    myErrorHandler(myEx);
  end
end

throwNrfEofExceptions プロパティー

デフォルトでは、throwNrfEofExceptions プログラム・プロパティーは NO に設定されており、これは次の例に示すように、ソフト入出力エラーの noRecordFound および endOfFiletry ブロックの外側で発生した場合でも、プログラムは続行することを意味します。
get myCustomer;
if(myCustomer is noRecordFound)
   add myCustomer;
end
ただし、これらのエラーのいずれかが発生したときに EGL に例外をスローさせたい場合もあります。 そのためには、throwNrfEofExceptions プログラム・プロパティーを YES に設定します。その場合、EGL は以下の例外のうちいずれかをスローします。
  • ファイル入出力の実行時には、EGL は RuntimeException をスローします。
  • SQL 入出力の実行時には、EGL は SQLException をスローします。
このエラーは、try ブロックでキャッチする必要があり、そうしないと、プログラムが強制終了されます。 上記の例では、get ステートメントに対するレコードが見つからないと、プログラムは強制終了されます。 ただし、次のいずれかの手法で、その例外を処理できます。
  • ソフト・エラー後に続行
    try
      get myCustomer; 
    end
    
    if (myCustomer is noRecordFound) 
      add myCustomer; 
    end
  • 例外をキャッチ (推奨手法)
    try
      get myCustomer; 
    onException (myEx FileIOException)
      if (myCustomer is noRecordFound) 
        add myCustomer; 
      else
        myErrorHandler(myEx);
      end
    end

独自の例外のスロー

EGL の throw ステートメントを使用して、定義済みのシステム例外をどれでもスローできます。 例外処理の場合と同様に、そのレコードに基づいて変数を宣言する必要があります。
nullEx NullValueException{};
 ... 
throw nullEx;
システムが定義する例外に加えて、独自の例外レコードを作成できます。 他のレコードと同様に、レコード・パーツを最初に定義してから、それに基づいて変数を宣言する必要があります。
Record CustomerException type Exception
  customerNumber INT;
end
 ... 
throw new customerException {
  customerNumber = custNum,
  message = "Illegal customer number" };
上記のようなカスタム例外レコードには、システム例外レコードと同様に、messageID および message フィールドが自動的に含まれます。

V6 例外互換性

以前のバージョンとの互換性のために、EGL のバージョン 6 のエラー処理メソッドを継続して使用できます。

プログラムの v60ExceptionCompatibility プロパティーを YES に設定する際に、プログラムごとに V6 例外モードを指定します。ただし、最適な結果を得るには、すべてのプログラムで同じ設定を使用してください。

V6 例外は通常、try ブロックを通して処理されます。 V6 例外の異なる点は、単一の onException ステートメントだけを許可しており、例外タイプを指定しない点です。 したがって、onException ステートメントに暗黙の AnyException 修飾子があるかのように動作します。
try
  posNum = abs(myVar);
onException
  if(sysVar.errorCode = "00000008") // 無効な入力
    myErrorHandler1();
  if(sysVar.errorCode = "00000012") // 値を代入できません
    myErrorHandler2();
  else
    myErrorHandler3();
end
V6 例外互換性は、例外レコードに基づくものではなく、sysVar.errorCode システム変数に依存します。 V6 例外モードで実行中の場合は、以下のような場合に sysVar.errorCode が設定されます。
  • call ステートメントの完了時
  • サービスの呼び出し後
  • getreplace などのファイル入出力ステートメントの後
  • 多数の EGL システム関数の呼び出し後
さらに SQL 入出力エラーの場合、V6 例外互換性は sysVar.sqlData の中のシステム変数に依存します。
vgVar.handleSysLibraryErrors システム変数 (システム・ライブラリーの関数の場合) または vgVar.handleHardIOErrors (ファイル入出力および SQL 入出力の場合) を 1 に設定している場合、V6 例外モードでは sysVar.errorCode にアクセスするために try ブロックは必要ありません。
vgVar.handleSysLibraryErrors = 1;
posNum = abs(myVar);
if(sysVar.errorCode == "00000008") // 無効な入力
  myErrorHandler1();
else
  if(sysVar.errorCode == "00000012") // 代入できません
    myErrorHandler2();
  else
    exit program (-1);
  end
end

ただし、vgVar.handleSysLibraryErrors が 0 (デフォルト) に設定されており、try ブロックを使用した例外のキャッチをしない場合、例外が発生すると、プログラムは強制終了されます。

詳しくは、例外処理を参照してください。


フィードバック