Camel拆分后,Exception不会升级

时间:2016-02-11 11:57:30

标签: java split apache-camel

我们在Camel中定义了一个路由,如果在处理器中抛出异常,必须找出它。当我们只有一个处理器时,Camel会在sendBody()方法中重新抛出异常。如果存在先前的分割/聚合,则不会抛出异常。所以下面例子的结果是

before throwing Exception

after sendBody

如果我省略从.split到.completionSize(1)的所有内容,则输出为

before throwing Exception

Exception thrown

任何想法如何找出,如果在拆分后发生异常?

private static final String DIRECT_START = "direct:start";

public static void main(String[] args) throws Exception {
    CamelContext context = new DefaultCamelContext();

    context.addRoutes(new RouteBuilder() {
        @Override
        public void configure() throws Exception {

            from(DIRECT_START)
            .split(body())
                .aggregate(constant(true), new AggregationStrategy() {
                    public Exchange aggregate(Exchange oldExchange, Exchange newExchange) {
                        return oldExchange == null ? newExchange : oldExchange;
                    }
                })
                .completionSize(1)
            .process(new Processor() {
                public void process(Exchange exchange) throws Exception {
                    System.out.println("before throwing Exception");
                    exchange.setException(new Exception());
                    throw new Exception("my Exception");
                }
            });
        }});        

    context.start();

    ProducerTemplate producer = context.createProducerTemplate();
    try {
        producer.sendBody(DIRECT_START, Integer.valueOf(42));
        System.out.println("after sendBody");
    } catch (Exception e) {
        System.out.println("Exception thrown");
    }

    context.stop();

}

为了检查之后的例外,我们找到了一个解决方案。我们在onException()中注册了一个ErrorProcessor,它将状态设置为上下文属性。

但这不会中断producer.sendBody(..)。我们有极端长时间运行的处理器,我们必须打断它们。

所以问题是,我们可以配置Camel在sendBody中抛出异常,还是可以在Exceptionhandler中执行此操作?

1 个答案:

答案 0 :(得分:10)

我强烈建议您参考Camel in Action(第8.3.5节)中的Splitter EIP和异常处理。该部分解释了:

  

在Splitter中使用自定义AggregationStrategy时,了解您负责处理异常非常重要。如果你没有传回异常,Splitter会认为你已经处理了异常,并忽略它。

您已使用Private Sub btnSave_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles btnSave.Click Dim sql As String For i As Integer = 0 To dgvContract.Rows.Count - 1 sql = "INSERT INTO ContractDetails(Customer_ID , Customer_Name , Contract_ID , Contract_Name ,Strat_Date , End_Date , Value , Description) VALUES(" & (dgvContract.Item("dgvCustomerID", i).Value) & ",'" & (dgvContract.Item("dgvCustomerName", i).Value) & "','" & (dgvContract.Item("dgvContractID", i).Value) & "','" & (dgvContract.Item("dgvContractName", i).Value) & "','" & Format(dgvContract.Item("dgvStratingDate", i).Value, "mm/dd/yyyy") & "','" & Format(dgvContract.Item("dgvEndDate", i).Value, " mm/dd / yy") & "','" & (dgvContract.Item("dgvValue", i).Value & "','" & (dgvContract.Item("dgvDescription", i).Value)) & "')" SqlHelper.ExecuteScalar(My.Settings.conn, CommandType.Text, sql) Next End Sub 方法而未指定聚合器。在Camel documentation中,他们指定

  

分割器默认返回原始输入消息

这意味着离开split()方法的交换没有异常,因此没有异常传播回您的调用代码。从处理器抛出的异常在技术上是在分离器内部。即使您使用了聚合器,但它与split()电话无关,并且您还没有明确地以split结束split。因此,当您的处理器抛出异常时,拆分器会忽略它,因为您还没有提供聚合器来处理和传播异常。

我们可以通过将聚合策略作为参数传递给end()调用来测试这一点,如下所示:

split

您将获得输出:

.split(body(), new AggregationStrategy() {
    public Exchange aggregate(Exchange oldExchange, Exchange newExchange) {
        System.out.println("Aggregating");
        return oldExchange == null ? newExchange : oldExchange;
    }
})
    .log("test") // inside the split/aggregator EIP
.end() // outside the split/aggregator EIP
.process(new Processor() {
    public void process(Exchange exchange) throws Exception {
        System.out.println("before throwing Exception");
        throw new Exception("my Exception");
    }
});

如果您希望处理器位于拆分/聚合器EIP内,请执行以下操作:

test
Aggregating
before throwing Exception
Exception thrown

您将获得输出:

.split(body(), new AggregationStrategy() {
    public Exchange aggregate(Exchange oldExchange, Exchange newExchange) {
        System.out.println("Aggregating");
        return oldExchange == null ? newExchange : oldExchange;
    }
})
    .process(new Processor() { // inside the split/aggregator EIP
        public void process(Exchange exchange) throws Exception {
            System.out.println("before throwing Exception");
            throw new Exception("my Exception");
        }
    })
.end(); // outside the split/aggregator EIP

请注意,在分割/聚合器EIP内部,如何抛出异常后运行聚合器?这很重要,因为没有聚合器传递异常,分割器将忽略它。为了使其工作,您需要在聚合器内正确传播异常。例如,在您的代码中,如果before throwing Exception Aggregating Exception thrown 要包含异常,那么它将被忽略,因为您没有传播它。您需要更改要添加的聚合器:

newExchange

注意:如果您在拆分EIP中进行了if (newExchange.getException() != null) { oldExchange.setException(newExchange.getException()); } 调用,并且您将异常设置为正在处理,则在您调用onException()时它将不再返回。因此,如果您想处理异常,但仍然通过聚合器传播它们,则可以使用getException()

您也可以使用exchange.getProperty(Exchange.EXCEPTION_CAUGHT);,如下所示:

.stopOnException()

这会导致拆分在异常上停止并传播它。但是,当您在.split(body()).stopOnException() .process(new Processor() { // inside the split/aggregator EIP public void process(Exchange exchange) throws Exception { System.out.println("before throwing Exception"); throw new Exception("my Exception"); } }); 之后放置聚合器时,它将不再有效。我不完全确定原因。我猜测它是因为聚合器改变了stopOnException()对象。

另请注意,您不需要在处理器中将异常设置为交换。当处理器抛出异常时,Camel会为您执行此操作。因此,处理器中的行exchange是不必要的。

tl; dr是的,您可以从分割内部向调用方法传播异常。您只需要确保通过与拆分相关联的聚合器完成它,或者设置exchange.setException(new Exception());。这取决于你试图通过分割/聚合/处理最佳方法来实现的目标。