2.3. 异常处理

摘要

Apache Camel 提供了几种不同的机制,它可让您在不同粒度级别处理异常:您可以使用 doTry、doCatch、doCatch7 Finally、或者您可以指定每个异常类型采取哪些操作,并将该规则应用到 RouteBuilder 中的所有路由,并使用 onException; 或者,您可以指定 为所有 异常类型执行的操作,并使用 errorHandler 将此规则应用到 RouteBuilder 中的所有路由。

有关异常处理的详情,请参考 第 6.3 节 “死信频道”

2.3.1. onException Clause

概述

onException 子句是捕获一个或多个路由中的异常的强大机制:它特定于类型,允许您定义不同的操作来处理不同的异常类型;它允许您定义一个与路由相同的(实际上、稍微扩展)语法的操作,以处理异常的方式为您提供可考虑的灵活性;它基于陷阱模型处理不同异常的操作。这可让您在路由中处理任何例外情况。

使用 onException 陷阱异常

onException 子句是捕获 的机制,而不是捕获例外。也就是说,一旦定义了 onException 子句,它会捕获路由中任何点发生的异常。这与 Java try/catch 机制相反,只有在尝试块中 明确 包括特定的代码片段时,才会发现异常。

当您定义 onException 子句时,实际发生情况是 Apache Camel 运行时隐式将每个路由节点包括在 try 块中。这就是为什么 onException 子句能够在路由的任意点上捕获异常。但是,这个换行是自动进行的,它无法在路由定义中看到。

Java DSL 示例

在以下 Java DSL 示例中,onException 子句应用到 RouteBuilder 类中定义的所有路由。如果在处理其中一个路由(来自"seda:inputA")或 from ("seda:inputB") 时发生 ValidationException 异常,则 onException 子句会捕获异常,并将当前交换重定向到 验证Failed 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 示例

前面的示例也可以在 XML DSL 中表达,使用 onException 元素来定义 exception 子句,如下所示:

<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>

陷阱多个例外

您可以在 RouteBuilder 范围内定义多个 onException 子句来捕获异常。这可让您执行不同的操作来响应不同的异常。例如,Java DSL 中定义的以下一系列 onException 子句为 ValidationExceptionIOExceptionException 定义不同的 deadletter 目的地:

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. 如果 exception-to-test 完全在 onException 子句中指定类型(使用 instanceof测试),则触发匹配项。
    2. 如果 exception-to-test 是 onException 子句中指定的类型的子类型,则会触发匹配项。
  2. 如果嵌套的异常无法生成匹配项,则测试链中的下一个异常(嵌套例外)。测试将继续链,直到触发匹配项或链耗尽为止。
注意

throwException EIP 可让您从简单的语言表达式创建一个新的异常实例。您可以根据当前交换中的可用信息,使其动态化。例如,

<throwException exceptionType="java.lang.IllegalArgumentException" message="${body}"/>

Deadletter 频道

目前,onException 使用的基本示例都已利用 deadletter 频道 模式。也就是说,当 onException 子句陷阱异常时,当前交换会被路由到特殊的目的地(死字母通道)。deadletter 频道充当 尚未 处理的失败消息的存放区域。管理员可以稍后检查消息,并决定需要采取什么操作。

有关 deadletter 频道模式的详情,请参考 第 6.3 节 “死信频道”

使用原始消息

在路由中间引发异常时,交换中的消息可能已被显著修改(甚至不能被人读)。通常,管理员更容易决定要采取哪些纠正操作,如果 deadletter 队列中可见的消息是 原始消息,如路由开始时收到的消息。useOriginalMessage 选项默认为 false,但如果错误处理程序上配置了,则会自动启用。

注意

useOriginalMessage 选项可能会导致在应用到将消息发送到多个端点的 Camel 路由时意外行为,或者将消息拆分为部分。原始消息可能无法在多播、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 () 时原始消息可用。但是,如果 Camel 上下文上的 setAllowUseOriginalMessage () 选项被设置为 false (这是默认 ),则原始消息将无法访问,您无法调用 useOriginalMessage ()

利用默认行为的原因是在处理大型消息时优化性能。

在 2.18 之前的 Camel 版本中,allowUseOriginalMessage 的默认设置是 true。

重新发送策略

Apache Camel 为您提供在出现异常时尝试重新设计消息,而不是中断消息的处理,而是为您提供在发生异常时尝试 重新设计 消息的选项。在网络系统中,如果出现超时和临时错误,通常会成功处理失败的消息,如果它们在原始异常引发后很快被重新设计。

在发生异常后,Apache Camel 重新发送支持用于 redelivering 消息的各种策略。配置重新发送的一些最重要的选项如下:

maximumRedeliveries()
指定可以尝试重新发送的次数上限(默认为 0)。负值意味着始终尝试重新发送(等同于无限值)。
retryWhile()

指定 predicate (predicate 类型),它决定 Apache Camel ought 是否继续重新设计。如果 predicate 在当前交换上评估为 true,则会尝试重新传送;否则,重新传送将停止,且不会进行进一步重新发送尝试。

这个选项优先于 maximumRedeliveries () 选项。

在 Java DSL 中,重新传送策略选项使用 onException 子句中的 DSL 命令来指定。例如,您可以指定最多 6 个 redeliveries,之后交换发送到 validationFailed deadletter 队列,如下所示:

onException(ValidationException.class)
  .maximumRedeliveries(6)
  .retryAttemptedLogLevel(org.apache.camel.LogginLevel.WARN)
  .to("activemq:validationFailed");

在 XML DSL 中,redelivery 策略选项通过在 redeliveryPolicy 元素上设置属性来指定。例如,前面的路由可以在 XML DSL 中表示,如下所示:

<onException useOriginalMessage="true">
    <exception>com.mycompany.ValidationException</exception>
    <redeliveryPolicy maximumRedeliveries="6"/>
    <to uri="activemq:validationFailed"/>
</onException>

在重新传送选项后,路由的后的一个部分不会处理,直到最后一次重新发送尝试失败后才会处理。有关所有重新发送选项的详情,请参考 第 6.3 节 “死信频道”

另外,您可以在 redeliveryPolicyProfile 实例中指定 redelivery 策略选项。然后,您可以使用 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>
注意

如果要使用 redeliveryPolicyProfile,在多个 onException 子句中重新使用同一重新传送策略,使用 redeliveryPolicyProfile 的方法很有用。

条件陷阱

通过指定 onWhen 选项,可以进行带有 onException 的异常捕获情况。如果您在 onException 子句中指定 onWhen 选项,则仅在引发异常与 子句匹配时才会触发匹配,而 onWhen predicate 会根据当前交换评估为 true

例如,在以下 Java DSL 片段中,第一个 onException 子句触发,只有在抛出异常与 MyUserException 匹配 并且用户 标头在当前交换中是非null:

// 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 子句完成后,您可以选择阻止重新增长异常。换句话说,在这种情况下,异常 不会在 路由开始时传播到消费者端点。
  • 继续处理 mvapich-wagon 您可以选择从最初发生异常的点恢复交换正常处理。隐式,这种方法也会阻止重新增长异常。
  • 当路由开始时的消费者端点需要回复(即使用 InOut MEP),您可能希望构建自定义错误回复消息,而不是将异常传播到消费者端点。???
注意

使用自定义处理器时,Camel Exception Clause 和 Error Handler 会在使用新的 onExceptionOccurred 选项抛出异常后立即调用。

抑制异常增长

要防止当前例外被重新传输并传播到消费者端点,您可以在 Java DSL 中将 handled () 选项设置为 true,如下所示:

onException(ValidationException.class)
  .handled(true)
  .to("activemq:validationFailed");

在 Java DSL 中,指向 handled () 选项的参数可以是布尔值类型、predicate 类型或 Expression 类型(如果其评估为非布尔值,则任何非布尔值表达式将解释为 true )。

可以使用 handled 元素将相同的路由配置为阻止 XML DSL 中的 rethrown 异常,如下所示:

<onException>
    <exception>com.mycompany.ValidationException</exception>
    <handled>
        <constant>true</constant>
    </handled>
    <to uri="activemq:validationFailed"/>
</onException>

继续处理

要继续处理最初抛出异常的点中的当前消息,您可以在 Java DSL 中将 continue 选项设置为 true,如下所示:

onException(ValidationException.class)
  .continued(true);

在 Java DSL 中,continued () 选项的参数可以是布尔值类型、Predicate 类型或 Expression 类型(如果其评估为非布尔值,则任何非布尔值表达式将解释为 true )。

可以使用 continued 元素在 XML DSL 中配置相同的路由,如下所示:

<onException>
    <exception>com.mycompany.ValidationException</exception>
    <continued>
        <constant>true</constant>
    </continued>
</onException>

发送响应

当启动路由的消费者端点需要回复时,您可能需要构建自定义错误回复消息,而不是简单地将引发的异常传播到消费者。在这种情况下,您需要遵循两个基本步骤:使用 处理 的选项禁止重新增长异常;并使用自定义故障消息填充交换的 Out 消息插槽。

例如,每当出现 MyFunctionalException 异常时,以下 Java DSL 片段演示了如何发送包含文本字符串 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 () builder 方法访问当前异常消息的文本。例如,您可以在发生 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());

例外消息文本也可以通过 exception.message 变量从 Simple 语言访问。例如,您可以在回复消息中嵌入当前的异常文本,如下所示:

// 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 子句时抛出的异常)以特殊方式处理。此类异常由特殊的回退异常处理程序处理,该处理程序处理异常,如下所示:

  • 所有现有异常处理程序都会被忽略,并立即处理失败。
  • 新的异常被记录。
  • 在 exchange 对象上设置新的异常。

简单的策略避免出现复杂故障场景,否则可能会最终出现 onException 子句被锁定到无限循环中。

范围

onException 子句可以在以下其中一个范围中有效:

  • RouteBuilder.configure () 方法内定义为 standalone 语句的 RouteBuilder 范围 mvapich- onException 子句会影响该 RouteBuilder 实例中定义的所有路由。另一方面,这些 onException 子句对任何其他 RouteBuilder 实例中定义的路由 没有影响onException 子句 必须在 路由定义之前显示。

    此时所有示例都使用 RouteBuilder 范围来定义。

  • 也可以直接嵌入到路由中的路由 范围 mvapich- onException 子句。这些 onException 子句 仅影响 定义它们的路由。

路由范围

您可以在路由定义中嵌入 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 BuilderXML DSL 类型属性描述

defaultErrorHandler()

DefaultErrorHandler

将例外传播到调用者,并支持重新传送策略,但它不支持死信队列。

deadLetterChannel()

DeadLetterChannel

支持与默认错误处理程序相同的功能,另外,还支持死信队列。

loggingErrorChannel()

LoggingErrorChannel

每当出现异常时记录异常文本。

noErrorHandler()

NoErrorHandler

可用于禁用错误处理程序的虚拟处理程序实施。

 

TransactionErrorHandler

transacted 路由的错误处理程序。默认事务错误处理程序实例自动用于标记为 transacted 的路由。

2.3.3. dotry、doCatch 和 doFinally

概述

要在路由内处理异常,您可以使用 doTrydoCatchdoFinally 子句的组合,其处理例外的方式与 Java 的 尝试 类似、捕获和 最终 块类似。

doCatch 和 Java 捕获之间的相似性

通常,路由定义中的 doCatch () 子句的行为与 Java 代码中的 catch () 语句类似。特别是,doCatch () 子句支持以下功能:

  • 单个 do Try 块中的多个 doCatch 子句可以有多个 doCatch 子句。doCatch 子句按照它们出现的顺序进行测试,就像 Java catch () 语句一样。Apache Camel 执行与抛出异常匹配的第一个 doCatch 子句。

    注意

    此算法与 onException 子句的图形化匹配算法不同,详情请参阅 第 2.3.1 节 “onException Clause”

  • 使用 处理 的子句子来重新 浏览 来自 doCatch 子句中的当前异常(请参阅 “在 doCatch 中重新增长异常”一节)。

doCatch 的特殊功能

但是,doCatch () 子句的一些特殊功能在 Java catch () 语句中没有 analogue。以下功能特定于 doCatch ()

  • 通过捕获多个例外 criu-wagonthe doCatch 子句,您可以指定一个例外列表来捕获 Java catch () 语句,该语句只捕获一个异常(请参阅 “示例”一节)。
  • 通过 把 onWhen sub-clause 附加到 doCatch 子句(请参阅 “使用 onWhen 的条件异常”一节),您可以有条件地捕获异常。

示例

以下示例演示了如何在 Java DSL 中编写 doTry 块,其中将执行 doCatch () 子句,如果 IOException 异常或 IllegalStateException 异常被引发,d Finally () 子句 始终被 执行,无论是否引发异常。

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 中重新增长异常

可以通过调用 handled () 子化,并将其参数设置为 false 来重新增长 doCatch () 子句中的异常,如下所示:

from("direct:start")
    .doTry()
        .process(new ProcessorFail())
        .to("mock:result")
    .doCatch(IOException.class)
        // mark this as NOT handled, eg the caller will also get the exception
        .handled(false)
        .to("mock:io")
    .doCatch(Exception.class)
        // and catch all other exceptions
        .to("mock:error")
    .end();

在前面的示例中,如果 doCatch () 检测到 IOException,则当前交换将发送到 mock:io 端点,然后重新箭头 IOException。这为路由开始时的消费者端点(在 from () 命令中)提供了处理异常的机会。

以下示例演示了如何在 Spring XML 中定义相同的路由:

<route>
    <from uri="direct:start"/>
    <doTry>
        <process ref="processorFail"/>
        <to uri="mock:result"/>
        <doCatch>
            <exception>java.io.IOException</exception>
            <!-- mark this as NOT handled, eg the caller will also get the exception -->
            <handled>
                <constant>false</constant>
            </handled>
            <to uri="mock:io"/>
        </doCatch>
        <doCatch>
            <!-- and catch all other exceptions they are handled by default (ie handled = true) -->
            <exception>java.lang.Exception</exception>
            <to uri="mock:error"/>
        </doCatch>
    </doTry>
</route>

使用 onWhen 的条件异常

Apache Camel doCatch () 子句的一个特殊功能是,您可以根据运行时评估的表达式对例外的捕获有条件。换句话说,如果您使用表单的子句来捕获异常,doCatch (ExceptionList.doWhen (Expression),仅当 predicate 表达式( Expression )在运行时评估为 true 时,会发现异常。

例如,以下 doTry 块会捕获例外,IOExceptionIllegalStateException,只有在异常消息包含单词 Severe:

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 () 创建用于处理异常的尝试或捕获块,并可用于路由特定的错误处理。

如果要捕获 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 Exceptions

概述

Camel CXF 组件提供与 Apache CXF 集成,可让您从 Apache Camel 端点发送和接收 SOAP 消息。您可以在 XML 中轻松定义 Apache Camel 端点,然后使用端点的 bean ID 在路由中引用该端点。如需了解更多详细信息,请参阅 Apache Camel 组件参考指南 中的 CXF

如何传播堆栈追踪信息

可以配置 CXF 端点,以便在服务器端抛出 Java 异常时,异常的堆栈追踪会放入故障消息并返回到客户端。要启用此 feaure,请将 dataFormat 设置为 PAYLOAD,并将 cxfEndpoint 元素中的 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>

出于安全考虑,堆栈追踪不包括导致的异常(即,被 导致的堆栈追踪的一部分)。如果要在堆栈追踪中包含导致异常,请在 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 标志进行测试和诊断目的。服务器正常做法是让服务器传达了最初的例外原因,使恶意用户更难以探测服务器。