2.3. 例外処理
概要
Apache Camel はいくつかの異なるメカニズムを提供しており、異なるレベルの粒度で例外を処理することができます。まず、doTry
、doCatch
、および doFinally
を使用してルート内で例外を処理できます。また、onException
を使用して、各例外型に対して実行するアクションを指定し、RouteBuilder
内のすべてのルートにそのルールを適用することもできます。または、errorHandler
を使用して、すべての 例外型に対して実行するアクションを指定し、そのルールを RouteBuilder
内のすべてのルートに適用することもできます。
例外処理の詳細は、「Dead Letter Channel」 を参照してください。
2.3.1. onException 句
概要
onException
句は、1 つ以上のルートで発生する例外をトラップするための強力なメカニズムです。これは型固有のもので、異なる例外型を処理するための個別のアクションを定義することができます。基本的にルートと同じ (実際には、若干拡張された) 構文でアクションを定義できるため、例外を処理する方法にかなりの柔軟性が得られます。また、トラップモデルをベースにしていることにより、1 つの onException
句で任意のルート内の任意のノードで発生した例外を処理できます。
onException を使用した例外のトラップ
onException
句は、例外をキャッチするのではなく、トラップ するメカニズムです。つまり、一度 onException
句を定義すると、ルート内の任意の地点で発生する例外がトラップされます。これは、特定のコードフラグメントが try ブロックで 明示的 に囲まれている場合にのみ例外がキャッチされる、Java の try/catch メカニズムとは対照的です。
onException
句を定義すると、Apache Camel ランタイムが各ルートノードを暗黙的に try ブロックで囲んでしまいます。このため、onException
句はルートの任意の地点で例外をトラップすることができます。ただし、このラッピングは自動的に行われ、ルート定義には表示されません。
Java DSL の例
以下の Java DSL の例では、onException
句は RouteBuilder
クラスで定義されているすべてのルートに適用されます。いずれかのルート (from("seda:inputA")
または from("seda:inputB")
) の処理中に ValidationException
例外が発生すると、onException
句はその例外をトラップし、現在のエクスチェンジを validationFailed
JMS キュー (デッドレターキューとして機能する) にリダイレクトします。
// Java public class MyRouteBuilder extends RouteBuilder { public void configure() { onException(ValidationException.class) .to("activemq:validationFailed"); from("seda:inputA") .to("validation:foo/bar.xsd", "activemq:someQueue"); from("seda:inputB").to("direct:foo") .to("rnc:mySchema.rnc", "activemq:anotherQueue"); } }
XML DSL の例
上記の例は、exception 句を定義する onException
要素を使用して、以下のように XML DSL で表すこともできます。
<beans xmlns="http://www.springframework.org/schema/beans" xmlns:camel="http://camel.apache.org/schema/spring" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation=" http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.0.xsd http://camel.apache.org/schema/spring http://camel.apache.org/schema/spring/camel-spring.xsd"> <camelContext xmlns="http://camel.apache.org/schema/spring"> <onException> <exception>com.mycompany.ValidationException</exception> <to uri="activemq:validationFailed"/> </onException> <route> <from uri="seda:inputA"/> <to uri="validation:foo/bar.xsd"/> <to uri="activemq:someQueue"/> </route> <route> <from uri="seda:inputB"/> <to uri="rnc:mySchema.rnc"/> <to uri="activemq:anotherQueue"/> </route> </camelContext> </beans>
複数の例外のトラップ
複数の onException
句を定義して、RouteBuilder
スコープ内で例外をトラップすることができます。これにより、例外に応じて異なるアクションを実行できます。たとえば、以下の Java DSL で定義された一連の onException
句は、ValidationException
、IOException
、および Exception
の異なるデッドレター宛先を定義します。
onException(ValidationException.class).to("activemq:validationFailed"); onException(java.io.IOException.class).to("activemq:ioExceptions"); onException(Exception.class).to("activemq:exceptions");
以下のように、XML DSL で同じ一連の onException
句を定義することができます。
<onException> <exception>com.mycompany.ValidationException</exception> <to uri="activemq:validationFailed"/> </onException> <onException> <exception>java.io.IOException</exception> <to uri="activemq:ioExceptions"/> </onException> <onException> <exception>java.lang.Exception</exception> <to uri="activemq:exceptions"/> </onException>
また、複数の例外をグループ化して、同じ onException
句でトラップすることもできます。Java DSL では、以下のように複数の例外をグループ化できます。
onException(ValidationException.class, BuesinessException.class) .to("activemq:validationFailed");
XML DSL では、以下のように onException
要素内に複数の exception
要素を定義することで、複数の例外をグループ化できます。
<onException> <exception>com.mycompany.ValidationException</exception> <exception>com.mycompany.BuesinessException</exception> <to uri="activemq:validationFailed"/> </onException>
複数の例外をトラップする場合、onException
句の順序は重要です。Apache Camel はまず、発生した例外を最初の句に対して一致しようと試みます。最初の句が一致しない場合、次の onException
句が試行され、一致するものが見つかるまで続きます。各々の一致するかどうかの試行は、以下のアルゴリズムで制御されます。
発生する例外が チェーン例外 (例外がキャッチされて別の例外として出力されたもの) である場合、最もネストされた例外型が最初に一致の基準となります。この例外は、以下のようにテストされます。
-
テスト対象例外が正確に
onException
句で指定された型を持っている場合 (instanceof
によってテストされる) は、一致が起こります。 -
テスト対象例外が
onException
句で指定された型のサブタイプである場合、一致が起こります。
-
テスト対象例外が正確に
- 最もネストされた例外が一致しなかった場合、チェーンの次の例外 (ラップしている例外) がテストされます。このテストは、一致が起こるかチェーンの最後に到達するまで継続します。
throwException EIP を使用すると、Simple 言語の式から新しい例外インスタンスを生成できます。現在のエクスチェンジから利用可能な情報に基づいて、動的に生成することができます。以下に例を示します。
<throwException exceptionType="java.lang.IllegalArgumentException" message="${body}"/>
デッドレターチャネル
これまでの基本的な onException
の使用例は、すべて デッドレターチャネル パターンを利用していました。つまり、onException
句が例外をトラップすると、現在のエクスチェンジは特別な宛先 (デッドレターチャネル) にルーティングされます。デッドレターチャネルは、処理されて いない 失敗したメッセージの保持領域として機能します。管理者は後でメッセージを検査し、どのようなアクションを取る必要があるかを決定できます。
チャネルパターンの詳細は、「Dead Letter Channel」 を参照してください。
元のメッセージの使用
ルートの途中で例外が発生した時点では、エクスチェンジ内のメッセージが大幅に変更されている可能性があります (人間には判読できなくなっている場合もあります) 。多くの場合、管理者にとっては、デッドレターキューに表示されるメッセージがルートの開始時に受信したままの 元 のメッセージであれば、どのような修正アクションをとるべきか決定するのが簡単になります。useOriginalMessage
オプションはデフォルトでは false
に設定されますが、エラーハンドラーに設定されている場合には自動的に有効になります。
useOriginalMessage
オプションは、メッセージを複数のエンドポイントに送信する Camel ルートに適用したり、メッセージを分割したりすると、予期せぬ動作をすることがあります。中間処理ステップが元のメッセージを変更する Multicast、Splitter、または RecipientList のルートでは、元のメッセージは保持されない場合があります。
Java DSL では、エクスチェンジのメッセージを元のメッセージで置き換えることができます。setAllowUseOriginalMessage()
を true
に設定し、以下のように useOriginalMessage()
DSL コマンドを使用します。
onException(ValidationException.class) .useOriginalMessage() .to("activemq:validationFailed");
XML DSL では、以下のように onException
要素の useOriginalMessage
属性を設定することで、元のメッセージを取得できます。
<onException useOriginalMessage="true"> <exception>com.mycompany.ValidationException</exception> <to uri="activemq:validationFailed"/> </onException>
setAllowUseOriginalMessage()
オプションが true
に設定されている場合、Camel はルートの開始時に元のメッセージのコピーを作成します。これにより、useOriginalMessage()
の呼び出し時に元のメッセージが利用できることを保証します。しかし、setAllowUseOriginalMessage()
オプションが Camel コンテキストで false
(デフォルト) に設定されている場合、元のメッセージにはアクセス できず、useOriginalMessage()
を呼び出すことができません。
デフォルトの動作がこうなっている理由は、大きなメッセージを処理する際にパフォーマンスを最適化するためです。
2.18 より前の Camel バージョンでは、allowUseOriginalMessage
のデフォルト設定は true です。
再配信ポリシー
例外が発生したらすぐにメッセージの処理を中断して諦める代わりに、Apache Camel では例外が発生した時点でメッセージを 再送 するオプションを利用できます。タイムアウトが発生したり、一時的な障害が発生したりするネットワークシステムでは、元の例外が発生してからすぐに再送することで、失敗したメッセージが正常に処理されることがよくあります。
Apache Camel の再配信は、例外の発生後にメッセージを再送するさまざまなストラテジーをサポートします。再配信を設定する際に最も重要なオプションには、以下のものがあります。
maximumRedeliveries()
-
再配信を試行できる最大回数を指定します (デフォルトは
0
)。負の値は、再配信がいつまでも試行されることを意味します (無限の値と同等です) 。 retryWhile()
Apache Camel が再配信を続行すべきかどうかを決定する述語 (
Predicate
型) を指定します。述語が現在のエクスチェンジ上でtrue
と評価されると、再配信が試行されます。そうでない場合は再配信が停止され、それ以上の再配信の試みは行われません。このオプションは
maximumRedeliveries()
オプションよりも優先されます。
Java DSL では、再配信ポリシーのオプションは、onException
句内の DSL コマンドを使用して指定します。たとえば、以下のように、エクスチェンジが validationFailed
デッドレターキューに送信される前に、最大 6 回の再配信を指定できます。
onException(ValidationException.class) .maximumRedeliveries(6) .retryAttemptedLogLevel(org.apache.camel.LogginLevel.WARN) .to("activemq:validationFailed");
XML DSL では、redeliveryPolicy
要素に属性を設定することで再配信ポリシーオプションを指定します。たとえば、上記のルートは以下のように XML DSL で表現できます。
<onException useOriginalMessage="true"> <exception>com.mycompany.ValidationException</exception> <redeliveryPolicy maximumRedeliveries="6"/> <to uri="activemq:validationFailed"/> </onException>
再配信オプションが設定された後のルートの後半部分は、最後の再配信の試みが失敗するまで処理されません。すべての再配信オプションの詳細については、「Dead Letter Channel」 を参照してください。
もう 1 つの方法として、redeliveryPolicyProfile
インスタンスで再配信ポリシーオプションを指定することもできます。その後、onException
要素の redeliverPolicyRef
属性を使用して、redeliveryPolicyProfile
インスタンスを参照できます。たとえば、上記のルートは以下のように表現できます。
<redeliveryPolicyProfile id="redelivPolicy" maximumRedeliveries="6" retryAttemptedLogLevel="WARN"/> <onException useOriginalMessage="true" redeliveryPolicyRef="redelivPolicy"> <exception>com.mycompany.ValidationException</exception> <to uri="activemq:validationFailed"/> </onException>
複数の onException
句で同じ再配信ポリシーを再利用する場合は、redeliveryPolicyProfile
を使用するアプローチが便利です。
条件付きトラップ
onWhen
オプションを指定することで、onException
による例外のトラップを条件付きにすることができます。onException
句で onWhen
オプションを指定すると、発生した例外が句と一致し、かつ、onWhen
述語が現在のエクスチェンジで true
に評価された場合にのみ一致が起こります。
たとえば、以下の Java DSL フラグメントでは、発生する例外が MyUserException
に一致し、user
ヘッダーが現在のエクスチェンジで null でない場合にのみ、最初の onException
句が実行されます。
// Java // Here we define onException() to catch MyUserException when // there is a header[user] on the exchange that is not null onException(MyUserException.class) .onWhen(header("user").isNotNull()) .maximumRedeliveries(2) .to(ERROR_USER_QUEUE); // Here we define onException to catch MyUserException as a kind // of fallback when the above did not match. // Noitce: The order how we have defined these onException is // important as Camel will resolve in the same order as they // have been defined onException(MyUserException.class) .maximumRedeliveries(2) .to(ERROR_QUEUE);
上記の onException
句は、以下のように XML DSL で表現できます。
<redeliveryPolicyProfile id="twoRedeliveries" maximumRedeliveries="2"/> <onException redeliveryPolicyRef="twoRedeliveries"> <exception>com.mycompany.MyUserException</exception> <onWhen> <simple>${header.user} != null</simple> </onWhen> <to uri="activemq:error_user_queue"/> </onException> <onException redeliveryPolicyRef="twoRedeliveries"> <exception>com.mycompany.MyUserException</exception> <to uri="activemq:error_queue"/> </onException>
例外の処理
デフォルトでは、ルートの途中で例外が発生すると、現在のエクスチェンジの処理が中断され、発生した例外がルート先頭のコンシューマーエンドポイントに伝播されます。onException
句がトリガーされても、発生した例外が伝播される前に onException
句がいくつかの処理を実行することを除き、この動作は基本的に同じです。
しかし、このデフォルトの動作が例外を処理する唯一の方法ではありません。以下のように、onException
には例外処理の動作を変更するさまざまなオプションが用意されています。
-
例外再出力の抑制 -
onException
句が完了した後に、再出力された例外を抑制するオプションがあります。つまり、この場合、例外はルート先頭のコンシューマーエンドポイントまで伝播しません。 - 継続的な処理 - 例外が発生した時点からエクスチェンジの通常の処理を再開するオプションがあります。このアプローチでは、暗黙的に例外の再出力も抑制されます。
- レスポンスの送信 - ルート先頭にあるコンシューマーエンドポイントがリプライを期待する (つまり InOut MEP を持つ) 特別なケースでは、例外をコンシューマーエンドポイントに伝播するのではなく、カスタムのフォールトリプライメッセージを作成する場合があります。
例外再出力の抑制
現在の例外が再出力され、コンシューマーエンドポイントに伝播されないようにするには、以下のように Java DSL で handled()
オプションを true
に設定します。
onException(ValidationException.class) .handled(true) .to("activemq:validationFailed");
Java DSL では、handled()
オプションの引数はブール型、Predicate
型、または Expression
型のいずれかを取ります (非ブール型の式は、それが非 null 値として評価された場合には true
と解釈されます)。
以下のように handled
要素を使用して、XML DSL で同じルートを設定して再出力した例外を抑制できます。
<onException> <exception>com.mycompany.ValidationException</exception> <handled> <constant>true</constant> </handled> <to uri="activemq:validationFailed"/> </onException>
処理の継続
例外が最初に発生したルート内のポイントから現在のメッセージの処理を続行するには、以下のように Java DSL で continued
オプションを true
に設定します。
onException(ValidationException.class) .continued(true);
Java DSL では、continued()
オプションの引数はブール型、Predicate
型、または Expression
型のいずれかを取ります (非ブール型の式は、それが非 null 値として評価された場合には true
と解釈されます)。
以下のように continued
要素を使用して、XML DSL で同じルートを設定できます。
<onException> <exception>com.mycompany.ValidationException</exception> <continued> <constant>true</constant> </continued> </onException>
レスポンスの送信
ルートを開始するコンシューマーエンドポイントがリプライを期待している場合、単に発生した例外をコンシューマーに伝播するのではなく、カスタムのフォールトリプライメッセージを作成する場合があります。この場合、2 つのステップが必要になります。まず、handled
オプションを使用して再出力例外を抑制し、次にエクスチェンジの Out メッセージスロットにカスタムのフォールトメッセージを設定します。
たとえば、以下の Java DSL フラグメントは、MyFunctionalException
例外が発生するたびに、テキスト文字列 Sorry
を含むリプライメッセージを送信する方法を示しています。
// we catch MyFunctionalException and want to mark it as handled (= no failure returned to client) // but we want to return a fixed text response, so we transform OUT body as Sorry. onException(MyFunctionalException.class) .handled(true) .transform().constant("Sorry");
クライアントにフォールトレスポンスを送信する場合、例外メッセージのテキストをレスポンスに組み込みたいことがよくあります。exceptionMessage()
ビルダーメソッドを使用して、現在の例外メッセージのテキストにアクセスできます。たとえば、以下のように MyFunctionalException
例外が発生するたびに、例外メッセージのテキストのみを含むリプライを送信できます。
// we catch MyFunctionalException and want to mark it as handled (= no failure returned to client) // but we want to return a fixed text response, so we transform OUT body and return the exception message onException(MyFunctionalException.class) .handled(true) .transform(exceptionMessage());
例外メッセージのテキストは、Simple 言語からも exception.message
変数を介してアクセスできます。たとえば、以下のように現在の例外のテキストをリプライメッセージに埋め込むことができます。
// we catch MyFunctionalException and want to mark it as handled (= no failure returned to client) // but we want to return a fixed text response, so we transform OUT body and return a nice message // using the simple language where we want insert the exception message onException(MyFunctionalException.class) .handled(true) .transform().simple("Error reported: ${exception.message} - cannot process this message.");
上記の onException
句は、以下のように XML DSL で表現できます。
<onException> <exception>com.mycompany.MyFunctionalException</exception> <handled> <constant>true</constant> </handled> <transform> <simple>Error reported: ${exception.message} - cannot process this message.</simple> </transform> </onException>
例外処理中に発生した例外
既存の例外の処理中に発生した例外 (つまり、onException
句の処理中に発生した例外) は、特別な方法で処理されます。このような例外は、特別なフォールバック例外ハンドラーによって処理されます。例外は以下のように処理されます。
- 既存の例外ハンドラーはすべて無視され、処理は直ちに失敗します。
- 新しい例外がログに記録されます。
- 新しい例外がエクスチェンジオブジェクトに設定されます。
このシンプルな戦略は、onException
句が無限ループに閉じ込められるような複雑な障害のシナリオを回避します。
スコープ
OnException
句は、以下のスコープのいずれかで有効になります。
RouteBuilder scope:
RouteBuilder.configure()
メソッド内で独立した文として定義されたonException
句は、そのRouteBuilder
インスタンスで定義されたすべてのルートに影響します。一方、これらのonException
句は他のRouteBuilder
インスタンス内で定義されたルートに対する 影響はありません。onException
句は、ルート定義の前に表示する 必要があります。この時点までのすべての例は、
RouteBuilder
スコープを使用して定義されます。-
Route スコープ -
onException
句をルート内に直接埋め込むこともできます。onException 句は、それらが定義されているルートに のみ 影響します。
Route スコープ
ルート定義内のどこにでも onException
句を埋め込むことができますが、end()
DSL コマンドを使用して埋め込んだ onException
句を終了する必要があります。
たとえば、以下のように Java DSL で埋め込み onException
句を定義できます。
// Java from("direct:start") .onException(OrderFailedException.class) .maximumRedeliveries(1) .handled(true) .beanRef("orderService", "orderFailed") .to("mock:error") .end() .beanRef("orderService", "handleOrder") .to("mock:result");
XML DSL では、埋め込み onException
句を以下のように定義できます。
<route errorHandlerRef="deadLetter"> <from uri="direct:start"/> <onException> <exception>com.mycompany.OrderFailedException</exception> <redeliveryPolicy maximumRedeliveries="1"/> <handled> <constant>true</constant> </handled> <bean ref="orderService" method="orderFailed"/> <to uri="mock:error"/> </onException> <bean ref="orderService" method="handleOrder"/> <to uri="mock:result"/> </route>
2.3.2. エラーハンドラー
概要
errorHandler()
句は、このメカニズムが異なる例外型を識別 できない 点を除いて、onException
句と同様の機能を提供します。errorHandler()
句は、Apache Camel が提供する元々の例外処理メカニズムで、onException
句が実装される前から利用可能でした。
Java DSL の例
errorHandler()
句は RouteBuilder
クラスで定義され、その RouteBuilder
クラスのすべてのルートに適用されます。これは、該当するルートのいずれかで例外が その種類に関わらず 発生するたびに実行されます。たとえば、失敗したすべてのエクスチェンジを ActiveMQ の deadLetter
キューにルーティングするエラーハンドラーを定義するには、以下のように RouteBuilder
を定義します。
public class MyRouteBuilder extends RouteBuilder { public void configure() { errorHandler(deadLetterChannel("activemq:deadLetter")); // The preceding error handler applies // to all of the following routes: from("activemq:orderQueue") .to("pop3://fulfillment@acme.com"); from("file:src/data?noop=true") .to("file:target/messages"); // ... } }
ただし、デッドレターチャネルへのリダイレクトは、再配信の試行が終了するまで発生しません。
XML DSL の例
XML DSL では、errorHandler
要素を使用して、camelContext
スコープ内にエラーハンドラーを定義します。たとえば、失敗したすべてのエクスチェンジを ActiveMQ deadLetter
キューにルーティングするエラーハンドラーを定義するには、以下のように errorHandler
要素を定義します。
<beans xmlns="http://www.springframework.org/schema/beans" xmlns:camel="http://camel.apache.org/schema/spring" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation=" http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.0.xsd http://camel.apache.org/schema/spring http://camel.apache.org/schema/spring/camel-spring.xsd"> <camelContext xmlns="http://camel.apache.org/schema/spring"> <errorHandler type="DeadLetterChannel" deadLetterUri="activemq:deadLetter"/> <route> <from uri="activemq:orderQueue"/> <to uri="pop3://fulfillment@acme.com"/> </route> <route> <from uri="file:src/data?noop=true"/> <to uri="file:target/messages"/> </route> </camelContext> </beans>
エラーハンドラーの種類
表2.1「エラーハンドラーの種類」 では、定義可能なさまざまな種類のエラーハンドラーの概要を説明します。
Java DSL ビルダー | XML DSL Type 属性 | 説明 |
---|---|---|
|
| 例外を呼び出し元に戻し、再配信ポリシーをサポートしますが、デッドレターキューはサポートされません。 |
|
| デフォルトのエラーハンドラーと同じ機能をサポートし、さらにデッドレターキューもサポートします。 |
|
| 例外が発生するたびに例外テキストをログに記録します。 |
|
| エラーハンドラーを無効にするために使用できるダミーのハンドラー実装。 |
| トランザクションが有効化されたルートのエラーハンドラー。トランザクションが有効化されたルートでは、デフォルトのトランザクションエラーハンドラーインスタンスが自動的に使用されます。 |
2.3.3. doTry、doCatch、および doFinally
概要
ルート内で例外を処理するには、Java の try
、catch
、および finally
ブロックと同様の方法で例外を処理する、doTry
、doCatch
、および doFinally
句の組み合わせを使用できます。
doCatch と Java における catch の類似点
通常、ルート定義内の doCatch()
句は、Java コードの catch()
文と同様の動作をします。具体的には、以下の機能が doCatch()
句でサポートされています。
複数の doCatch 句 - 1 つの
doTry
ブロック内に複数のdoCatch
句を持たせることができます。このdoCatch
句は、Java のcatch()
文と同様に、表示される順序でテストされます。Apache Camel は、出力された例外に一致する最初のdoCatch
句を実行します。注記このアルゴリズムは、
onException
句で使用される例外一致アルゴリズムとは異なります。詳細は 「onException 句」 を参照してください。-
例外 の再出力 — コンストラクトを使用して
doCatch
句内から現在の例外を再出力できます (「doCatch での例外の再出力」)。
doCatch の特別機能
ただし、doCatch()
句には Java の catch()
文に類似するものがない特別な機能がいくつかあります。以下の機能は、doCatch()
に固有のものです。
-
条件キャッチ -
doCatch
句にonWhen
サブ句を追加することで、例外を条件付きでキャッチできます (「onWhen による条件付き例外キャッチ」 を参照)。
例
以下の例は、Java DSL で doTry
ブロックを書く方法を示しています。doCatch()
句は、IOException
例外または IllegalStateException
例外のいずれかが発生した場合に実行され、doFinally()
句は例外が発生したかどうかに関係なく、常に 実行されます。
from("direct:start") .doTry() .process(new ProcessorFail()) .to("mock:result") .doCatch(IOException.class, IllegalStateException.class) .to("mock:catch") .doFinally() .to("mock:finally") .end();
または、Spring XML で同等のものを記述するとこのようになります。
<route> <from uri="direct:start"/> <!-- here the try starts. its a try .. catch .. finally just as regular java code --> <doTry> <process ref="processorFail"/> <to uri="mock:result"/> <doCatch> <!-- catch multiple exceptions --> <exception>java.io.IOException</exception> <exception>java.lang.IllegalStateException</exception> <to uri="mock:catch"/> </doCatch> <doFinally> <to uri="mock:finally"/> </doFinally> </doTry> </route>
doCatch での例外の再出力
次のように、コンストラクトを使用して、doCatch ()
句で例外を再出力することができます。
from("direct:start") .doTry() .process(new ProcessorFail()) .to("mock:result") .doCatch(IOException.class) .to("mock:io") // Rethrow the exception using a construct instead ofhandled(false)
which is deprecated in adoTry/doCatch
clause. .throwException(new IllegalArgumentException("Forced")) .doCatch(Exception.class) // Catch all other exceptions. .to("mock:error") .end();
doTry/doCatch
句で非推奨となった handled (false)
の代わりにプロセッサーを使用して例外を再出力することもできます。
.process(exchange -> {throw exchange.getProperty(Exchange.EXCEPTION_CAUGHT, Exception.class);})
上記の例では、IOException
が doCatch()
にキャッチされると、現在のエクスチェンジが mock:io
エンドポイントに送信され、その後に IOException
が再出力されます。これにより、ルートの先頭 (from()
コマンド) にあるコンシューマーエンドポイントにも例外を処理する機会が与えられます。
以下の例では、Spring XML で同じルートを定義する方法を示しています。
<route> <from uri="direct:start"/> <doTry> <process ref="processorFail"/> <to uri="mock:result"/> <doCatch> <to uri="mock:io"/> <throwException message="Forced" exceptionType="java.lang.IllegalArgumentException"/> </doCatch> <doCatch> <!-- Catch all other exceptions. --> <exception>java.lang.Exception</exception> <to uri="mock:error"/> </doCatch> </doTry> </route>
onWhen による条件付き例外キャッチ
Apache Camel の doCatch()
句の特別な機能として、実行時に評価される式に基づいて例外のキャッチを条件付けすることができます。つまり、doCatch(ExceptionList).doWhen(Expression)
の形式の句を使用して例外をキャッチした場合、述語の式 Expression が実行時に true
に評価された場合にのみ例外がキャッチされます。
たとえば、以下の doTry
ブロックは、例外メッセージが単語 Severe
を含む場合にのみ、例外 IOException
と IllegalStateException
をキャッチします。
from("direct:start")
.doTry()
.process(new ProcessorFail())
.to("mock:result")
.doCatch(IOException.class, IllegalStateException.class)
.onWhen(exceptionMessage().contains("Severe"))
.to("mock:catch")
.doCatch(CamelExchangeException.class)
.to("mock:catchCamel")
.doFinally()
.to("mock:finally")
.end();
または、Spring XML で同等のものを記述するとこのようになります。
<route> <from uri="direct:start"/> <doTry> <process ref="processorFail"/> <to uri="mock:result"/> <doCatch> <exception>java.io.IOException</exception> <exception>java.lang.IllegalStateException</exception> <onWhen> <simple>${exception.message} contains 'Severe'</simple> </onWhen> <to uri="mock:catch"/> </doCatch> <doCatch> <exception>org.apache.camel.CamelExchangeException</exception> <to uri="mock:catchCamel"/> </doCatch> <doFinally> <to uri="mock:finally"/> </doFinally> </doTry> </route>
doTry のネストされた条件
Camel の例外処理を JavaDSL ルートに追加するためのオプションは複数あります。dotry()
は例外を処理するための try または catch ブロックを作成します。これはルート固有のエラー処理に役立ちます。
ChoiceDefinition
内部で例外をキャッチする場合は、以下のように doTry
ブロックを使用できます。
from("direct:wayne-get-token").setExchangePattern(ExchangePattern.InOut) .doTry() .to("https4://wayne-token-service") .choice() .when().simple("${header.CamelHttpResponseCode} == '200'") .convertBodyTo(String.class) .setHeader("wayne-token").groovy("body.replaceAll('\"','')") .log(">> Wayne Token : ${header.wayne-token}") .endChoice() .doCatch(java.lang.Class (java.lang.Exception>) .log(">> Exception") .endDoTry(); from("direct:wayne-get-token").setExchangePattern(ExchangePattern.InOut) .doTry() .to("https4://wayne-token-service") .doCatch(Exception.class) .log(">> Exception") .endDoTry();
2.3.4. SOAP 例外の伝播
概要
Camel CXF コンポーネントは Apache CXF とのインテグレーションを提供し、Apache Camel のエンドポイントから SOAP メッセージを送受信できます。XML で Apache Camel のエンドポイントを簡単に定義でき、それをエンドポイントの Bean ID を使用してルート内で参照できます。詳細は、Apache Camel Component Reference Guide の CXF を参照してください。
スタックトレース情報を伝播させる方法
Java の例外がサーバー側で発生したときに、例外のスタックトレースがフォールトメッセージにマーシャリングされてクライアントに返されるように、CXF エンドポイントを設定することができます。この機能を有効にするには、以下のように cxfEndpoint
要素で、dataFormat
を PAYLOAD
に設定し、faultStackTraceEnabled
プロパティーを true
に設定します。
<cxf:cxfEndpoint id="router" address="http://localhost:9002/TestMessage" wsdlURL="ship.wsdl" endpointName="s:TestSoapEndpoint" serviceName="s:TestService" xmlns:s="http://test"> <cxf:properties> <!-- enable sending the stack trace back to client; the default value is false--> <entry key="faultStackTraceEnabled" value="true" /> <entry key="dataFormat" value="PAYLOAD" /> </cxf:properties> </cxf:cxfEndpoint>
セキュリティー上の理由から、スタックトレースには原因となる例外 (つまりスタックトレースの Caused by
以降の部分) は含まれません。スタックトレースに原因となる例外を含めたい場合は、以下のように cxfEndpoint
要素の exceptionMessageCauseEnabled
プロパティーを true
に設定します。
<cxf:cxfEndpoint id="router" address="http://localhost:9002/TestMessage" wsdlURL="ship.wsdl" endpointName="s:TestSoapEndpoint" serviceName="s:TestService" xmlns:s="http://test"> <cxf:properties> <!-- enable to show the cause exception message and the default value is false --> <entry key="exceptionMessageCauseEnabled" value="true" /> <!-- enable to send the stack trace back to client, the default value is false--> <entry key="faultStackTraceEnabled" value="true" /> <entry key="dataFormat" value="PAYLOAD" /> </cxf:properties> </cxf:cxfEndpoint>
exceptionMessageCauseEnabled
フラグは、テストおよび診断目的でのみ有効にしてください。サーバーにおいて例外の元の原因を隠すことで、敵対的なユーザーがサーバーを調査しにくくするのが、通常の実践的なやり方です。