2.3. 例外処理

概要

Apache Camel はいくつかの異なるメカニズムを提供しており、異なるレベルの粒度で例外を処理することができます。まず、doTrydoCatch、および 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 句は、ValidationExceptionIOException、および 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 句が試行され、一致するものが見つかるまで続きます。各々の一致するかどうかの試行は、以下のアルゴリズムで制御されます。

  1. 発生する例外が チェーン例外 (例外がキャッチされて別の例外として出力されたもの) である場合、最もネストされた例外型が最初に一致の基準となります。この例外は、以下のようにテストされます。

    1. テスト対象例外が正確に onException 句で指定された型を持っている場合 (instanceof によってテストされる) は、一致が起こります。
    2. テスト対象例外が onException 句で指定された型のサブタイプである場合、一致が起こります。
  2. 最もネストされた例外が一致しなかった場合、チェーンの次の例外 (ラップしている例外) がテストされます。このテストは、一致が起こるかチェーンの最後に到達するまで継続します。
注記

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「エラーハンドラーの種類」 では、定義可能なさまざまな種類のエラーハンドラーの概要を説明します。

表2.1 エラーハンドラーの種類

Java DSL ビルダーXML DSL Type 属性説明

defaultErrorHandler()

DefaultErrorHandler

例外を呼び出し元に戻し、再配信ポリシーをサポートしますが、デッドレターキューはサポートされません。

deadLetterChannel()

DeadLetterChannel

デフォルトのエラーハンドラーと同じ機能をサポートし、さらにデッドレターキューもサポートします。

loggingErrorChannel()

LoggingErrorChannel

例外が発生するたびに例外テキストをログに記録します。

noErrorHandler()

NoErrorHandler

エラーハンドラーを無効にするために使用できるダミーのハンドラー実装。

 

TransactionErrorHandler

トランザクションが有効化されたルートのエラーハンドラー。トランザクションが有効化されたルートでは、デフォルトのトランザクションエラーハンドラーインスタンスが自動的に使用されます。

2.3.3. doTry、doCatch、および doFinally

概要

ルート内で例外を処理するには、Java の trycatch、および finally ブロックと同様の方法で例外を処理する、doTrydoCatch、および 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() に固有のものです。

以下の例は、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 of handled(false) which is deprecated in a doTry/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);})

上記の例では、IOExceptiondoCatch() にキャッチされると、現在のエクスチェンジが 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 を含む場合にのみ、例外 IOExceptionIllegalStateException をキャッチします。

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 コンポーネントリファレンスガイドCXF を参照してください。

スタックトレース情報を伝播させる方法

Java の例外がサーバー側で発生したときに、例外のスタックトレースがフォールトメッセージにマーシャリングされてクライアントに返されるように、CXF エンドポイントを設定することができます。この機能を有効にするには、以下のように cxfEndpoint 要素で、dataFormatPAYLOAD に設定し、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 フラグは、テストおよび診断目的でのみ有効にしてください。サーバーにおいて例外の元の原因を隠すことで、敵対的なユーザーがサーバーを調査しにくくするのが、通常の実践的なやり方です。