This is an automated email from the ASF dual-hosted git repository. davsclaus pushed a commit to branch main in repository https://gitbox.apache.org/repos/asf/camel.git
The following commit(s) were added to refs/heads/main by this push: new 10301996622 CAMEL-18042: Rollback with doTry .. doCatch should execute entire doCatch block and not only first processor. 10301996622 is described below commit 1030199662201a965cce33a2231cba4fbf1c34d5 Author: Claus Ibsen <claus.ib...@gmail.com> AuthorDate: Mon Jun 6 12:18:49 2022 +0200 CAMEL-18042: Rollback with doTry .. doCatch should execute entire doCatch block and not only first processor. --- .../org/apache/camel/processor/CatchProcessor.java | 22 ++++- .../camel/processor/RollbackDoTryCatchTest.java | 102 +++++++++++++++++++++ 2 files changed, 123 insertions(+), 1 deletion(-) diff --git a/core/camel-core-processor/src/main/java/org/apache/camel/processor/CatchProcessor.java b/core/camel-core-processor/src/main/java/org/apache/camel/processor/CatchProcessor.java index 88bbccf26b1..8b7ad4df738 100644 --- a/core/camel-core-processor/src/main/java/org/apache/camel/processor/CatchProcessor.java +++ b/core/camel-core-processor/src/main/java/org/apache/camel/processor/CatchProcessor.java @@ -24,6 +24,7 @@ import org.apache.camel.ExchangePropertyKey; import org.apache.camel.ExtendedExchange; import org.apache.camel.Predicate; import org.apache.camel.Processor; +import org.apache.camel.RollbackExchangeException; import org.apache.camel.Traceable; import org.apache.camel.spi.IdAware; import org.apache.camel.spi.RouteIdAware; @@ -85,7 +86,7 @@ public class CatchProcessor extends DelegateAsyncProcessor implements Traceable, @Override public boolean process(final Exchange exchange, final AsyncCallback callback) { - Exception e = exchange.getException(); + final Exception e = exchange.getException(); Throwable caught = catches(exchange, e); // If a previous catch clause handled the exception or if this clause does not match, exit if (exchange.getProperty(ExchangePropertyKey.EXCEPTION_HANDLED) != null || caught == null) { @@ -97,6 +98,15 @@ public class CatchProcessor extends DelegateAsyncProcessor implements Traceable, e.getMessage()); } + // must remember some properties which we cannot use during doCatch processing + ExtendedExchange ee = (ExtendedExchange) exchange; + final boolean stop = ee.isRouteStop(); + ee.setRouteStop(false); + final boolean rollbackOnly = ee.isRollbackOnly(); + ee.setRollbackOnly(false); + final boolean rollbackOnlyLast = ee.isRollbackOnlyLast(); + ee.setRollbackOnlyLast(false); + // store the last to endpoint as the failure endpoint if (exchange.getProperty(ExchangePropertyKey.FAILURE_ENDPOINT) == null) { exchange.setProperty(ExchangePropertyKey.FAILURE_ENDPOINT, exchange.getProperty(ExchangePropertyKey.TO_ENDPOINT)); @@ -124,6 +134,16 @@ public class CatchProcessor extends DelegateAsyncProcessor implements Traceable, // always clear redelivery exhausted in a catch clause exchange.adapt(ExtendedExchange.class).setRedeliveryExhausted(false); + if (rollbackOnly || rollbackOnlyLast || stop) { + exchange.setRouteStop(stop); + exchange.setRollbackOnly(rollbackOnly); + exchange.setRollbackOnlyLast(rollbackOnlyLast); + // special for rollback as we need to restore that a rollback was triggered + if (e instanceof RollbackExchangeException) { + exchange.setException(e); + } + } + if (!doneSync) { // signal callback to continue routing async ExchangeHelper.prepareOutToIn(exchange); diff --git a/core/camel-core/src/test/java/org/apache/camel/processor/RollbackDoTryCatchTest.java b/core/camel-core/src/test/java/org/apache/camel/processor/RollbackDoTryCatchTest.java new file mode 100644 index 00000000000..e524e7bd71d --- /dev/null +++ b/core/camel-core/src/test/java/org/apache/camel/processor/RollbackDoTryCatchTest.java @@ -0,0 +1,102 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.camel.processor; + +import org.apache.camel.ContextTestSupport; +import org.apache.camel.Exchange; +import org.apache.camel.Processor; +import org.apache.camel.RollbackExchangeException; +import org.apache.camel.RuntimeCamelException; +import org.apache.camel.builder.RouteBuilder; +import org.junit.jupiter.api.Test; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.junit.jupiter.api.Assertions.fail; + +public class RollbackDoTryCatchTest extends ContextTestSupport { + + @Test + public void testOk() throws Exception { + getMockEndpoint("mock:result").expectedMessageCount(1); + getMockEndpoint("mock:rollback").expectedMessageCount(0); + getMockEndpoint("mock:doCatch").expectedMessageCount(0); + + template.requestBody("direct:start", "ok"); + + assertMockEndpointsSatisfied(); + } + + @Test + public void testRollback() throws Exception { + getMockEndpoint("mock:result").expectedMessageCount(0); + getMockEndpoint("mock:rollback").expectedMessageCount(1); + getMockEndpoint("mock:doCatch").expectedMessageCount(1); + + try { + template.requestBody("direct:start", "bad"); + fail("Should have thrown a RollbackExchangeException"); + } catch (RuntimeCamelException e) { + boolean b = e.getCause() instanceof RollbackExchangeException; + assertTrue(b); + } + + assertMockEndpointsSatisfied(); + } + + @Test + public void testRollbackWithExchange() throws Exception { + getMockEndpoint("mock:result").expectedMessageCount(0); + getMockEndpoint("mock:rollback").expectedMessageCount(1); + getMockEndpoint("mock:doCatch").expectedMessageCount(1); + + Exchange out = template.request("direct:start", new Processor() { + public void process(Exchange exchange) throws Exception { + exchange.getIn().setBody("bad"); + } + }); + assertMockEndpointsSatisfied(); + + assertNotNull(out.getException()); + assertIsInstanceOf(RollbackExchangeException.class, out.getException()); + assertEquals(true, out.isRollbackOnly(), "Should be marked as rollback"); + } + + @Override + protected RouteBuilder createRouteBuilder() throws Exception { + return new RouteBuilder() { + @Override + public void configure() throws Exception { + from("direct:start") + .doTry() + .to("mock:doTry") + .to("direct:rollback") + .doCatch(Throwable.class) + .log("doCatch") + .to("mock:doCatch") + .end() + .to("mock:result"); + + from("direct:rollback") + .choice().when(body().isNotEqualTo("ok")).to("mock:rollback") + .rollback("That do not work"); + + } + }; + } +}