Mule 4 の概要: エラーハンドラー

Java の例外処理プロセスでは何が起こったのかを理解するためにソースコードを確認したり、エラーを強制的に発生させたりする必要がありますが、Mule 4 ではこれ以外の方法も使用できるようになりました。Java の ​Throwable​ エラーや例外も引き続き使用できますが、Mule 4 では処理しやすいエラーの概念が正式に導入されています。スローされる可能性のあるエラーの種類を各コンポーネントで宣言して、設計の時点で潜在的なエラーを識別できるようになりました。

Mule エラー

実行に失敗すると、Mule に次のコンポーネントを含むエラーが示されます。

  • 問題の説明。

  • 問題の特性を表す種類。

  • 原因 (失敗を引き起こした根本的な Java ​Throwable​)。

  • エラーメッセージ (省略可能)。問題に関する Mule の適切なメッセージが記載されます。

たとえば、HTTP 要求に失敗して 401 状況コードが表示された場合、Mule エラーに次の情報が示されます。

Description: HTTP GET on resource ‘http://localhost:36682/testPath’ failed: unauthorized (401)
Type: HTTP:UNAUTHORIZED
Cause: a ResponseValidatorTypedException instance
Error Message:  { "message" : "Could not authorize the user." }

エラーの種類

上記の例では、エラーの種類は単純に ​UNAUTHORIZED​ ではなく ​HTTP:UNAUTHORIZED​ です。エラーの種類が名前空間と識別子の両方で構成されるため、ドメインからその種類を判別することができます (​HTTP:NOT_FOUND​、​FILE:NOT_FOUND​ など)。名前空間はそのコネクタによって定義されますが、コアランタイムエラーの名前空間は暗黙的なため、​MULE:EXPRESSION​ と ​EXPRESSION​ は同一とみなされます。

エラーの種類のもう一つの重要な特徴は、親種別のある可能性があることです。たとえば、​HTTP:UNAUTHORIZED​ は親として ​MULE:CLIENT_SECURITY​ を持ち、さらにその親として ​MULE:SECURITY​ があります。このため、エラーの種類がグローバルなエラーの細部であることがわかります。つまり、広範なセキュリティ問題の 1 つにクライアントセキュリティエラーがあり、その 1 つに HTTP 未承認エラーがあるということです。

こうした階層により、大まかなルーティングが可能になります。たとえば、​MULE:SECURITY​ のハンドラーが HTTP 未承認エラーと OAuth エラーの両方をキャッチします。以下は、コアランタイムがどのような階層になっているかを示しています。

エラー階層

どのエラーも一般的なエラーと ​CRITICAL​ エラーのどちらかに該当しますが、後者は極めて深刻なため処理することができません。一般的な階層の最上位が ​ANY​ で、その下位の全種類がこれに該当します。

ここで重要な点は、失敗の明確な理由が見つからない場合に使用される ​UNKNOWN​ という種類に注意することです。このエラーは ​ANY​ 種類経由でしか処理できないため、今後既存のアプリケーションの動作を変更することなく、不明なエラーを特定できるようになります。

コネクタについては、各コネクタがそのコアランタイムエラーを考慮して各自のエラー種類階層を定義しますが、​CONNECTIVITY​ と ​RETRY_EXHAUSTED​ の 2 種類はすべてのコネクタに共通するため、常に存在します。

エラーハンドラー

Mule 4 では、​error-handler​ コンポーネントを導入してエラー処理が再設計されています。このコンポーネントには任意の数の内部ハンドラーを含めることができ、最初に一致したハンドラーにエラーをルーティングできます。これらのハンドラーに該当するのが ​on-error-continue​ や ​on-error-propagate​ で、どちらもエラーの種類 (あるいは数種類のエラー) または式 (高度なユースケースの場合) との照合をサポートしています。こうしたハンドラーは、Mule 3 の選択例外 (​choice-exception-strategy​)、キャッチ例外 (​catch-exception-strategy​)、ロールバック例外 (​rollback-exception-strategy​) 戦略によく似ていますが、はるかにシンプルで一貫しています。

Mule 4 でエラーが生じると、エラーハンドラーが実行され、最初に一致したハンドラーにエラーがルーティングされます。この時点でエラーが検査されるため、使用するコンポーネント (Flow または Try スコープなど) に応じたハンドラーを実行して処理できます。

  • on-error-continue​ が実行され、実行の結果がそのオーナーの結果として (オーナーが実行を正常に完了したかのように) 使用されます。 また、この時点のすべてのトランザクションがコミットされます。

  • on-error-propagate​ はすべてのトランザクションをロールバックしてから実行され、その結果が使用されて既存のエラーが再度スローされます。つまり、そのオーナーは「失敗した」とみなされます。

HTTP リスナーが、HTTP 要求を実行する別のフローへの Flow Reference コンポーネントをトリガーする、次のアプリケーションについて考えてみます。メッセージの受信時にすべてうまくいけば (下図の 1)、参照がトリガーされ (2)、要求が実行されて (3)、応答を正常に受信します (HTTP 状況コード 200) (4)。

エラー処理の例 1

エラーハンドラーの設定が ​inner-flow​ であるため HTTP 要求に失敗し、​HTTP:NOT_FOUND​ エラーが表示された場合 (下図の 3)、エラーが伝搬され (4)、Flow Reference コンポーネントが失敗します (2)。ただし、​primary-flow​ によって ​on-error-continue​ を使用してエラーが処理されるため、含まれている Logger が実行され (5)、正常な応答 (HTTP 状況コード 200) が返されます (6)。

エラー処理の例 1

要求に失敗し、未承認エラーが表示された場合は (3)、​on-error-continue​ により ​inner-flow​ が処理を行い、ファイルから静的コンテンツを取得します (4)。そして、Flow Reference コンポーネントも正常に行われ (2)、正常な応答 (HTTP 状況コード 200) が返されます (5)。

エラー処理の例 3

では、HTTP 要求で別のエラーが生じた場合はどうでしょうか? このフローには ​NOT_FOUND​ エラーと ​UNAUTHORIZED​ エラーのハンドラーしかありませんが、デフォルトで他のエラーも伝搬します。つまり、スローされたエラーに一致するハンドラーがなければ、エラーが再びスローされます。たとえば、要求に失敗し、「メソッドが許可されていません」というエラーが表示された場合 (3)、このエラーが伝搬され、Flow Reference コンポーネントが失敗し (2)、この伝搬により応答も失敗します (4)。

エラー処理の例 4

上記のシナリオを回避するためには、最後のハンドラーを ​HTTP:UNAUTHORIZED​ ではなく、​ANY​ と照合するようにします。以下は、​HTTP​ 要求で生じる可能性のある全エラーがどのように提示されるかを示しています。

エラー処理の Any

式を使用してエラーを照合することもできます。たとえば、エラーの処理時に Mule エラーを使用できるため、このエラーを使用してすべてのエラーを HTTP 名前空間と照合することができます。

エラー処理の式

Try スコープ

Mule 3 では概してエラーをフローレベルでしか処理できないため、エラーに対処するためにはフローにロジックを抽出する必要があります。Mule 4 では、フロー内で内部コンポーネントのエラーのみを処理する Try スコープが導入されました。このスコープは旧来の Transactional スコープの後継で、トランザクションもサポートします。

Try スコープ

前の例では、On Error Propagate コンポーネントがデータベース接続エラーを伝播するように設定されているため、​try​ が失敗して、フローのエラーハンドラーが実行されます。 On Error Continue コンポーネントは、他のすべてのエラーを処理するように設定されています。この場合、Try スコープの実行は正常に完了し、フローの実行が次のプロセッサーである HTTP Request 操作に引き継がれます。

エラーのマッピング

Mule 4 では、デフォルトのエラーをカスタムエラーにマップできるようになりました。Try スコープは便利ですが、同一のコンポーネントがいくつかあり、それぞれのエラーを区別したい場合、これらのエラーに Try を使用するとアプリケーションがわかりづらくなることがあります。この場合は代わりに、各コンポーネントにエラーマッピングを追加することが考えられます。つまり、コンポーネントからのすべてまたは一定のエラーストリーミングが任意の別のエラーにマップされるようにします。たとえば、それぞれ HTTP 要求コンポーネントを使用する 2 つの API の結果を集約する場合、デフォルトでは API 1 と API 2 のエラーが同じため、API ごとに区別したいことがあります。

最初の要求からのエラーをカスタムの ​API_1​ エラーにマップし、2 つ目の要求のエラーを ​API_2​ にマップすれば、これらのエラーを異なるハンドラーにルーティングできます。次の例では、​HTTP:INTERNAL_SERVER_ERROR​ がマップされるため、API がダウンしたときにそれぞれ異なる処理ポリシーが適用されます (1 つ目の API はエラーの伝搬、2 つ目の API はエラーの処理)。

エラー処理のマッピング