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 92f799e CAMEL-16534: camel-core - Multiple doTry ..doCatch in Java DSL - issue setting outer doCatch blocks 92f799e is described below commit 92f799ec62f1b3d375ae488f6ea1d2ad14c315ff Author: Claus Ibsen <claus.ib...@gmail.com> AuthorDate: Wed Apr 21 08:59:33 2021 +0200 CAMEL-16534: camel-core - Multiple doTry ..doCatch in Java DSL - issue setting outer doCatch blocks --- .../apache/camel/model/ProcessorDefinition.java | 4 +- .../java/org/apache/camel/model/TryDefinition.java | 12 ++++ .../TwoDoTryAndThrowInInnerCatchIssueTest.java | 60 ++++++++++-------- .../modules/ROOT/pages/try-catch-finally.adoc | 72 +++++++++++++++++++++- 4 files changed, 120 insertions(+), 28 deletions(-) diff --git a/core/camel-core-model/src/main/java/org/apache/camel/model/ProcessorDefinition.java b/core/camel-core-model/src/main/java/org/apache/camel/model/ProcessorDefinition.java index 0743a44..0cfe946 100644 --- a/core/camel-core-model/src/main/java/org/apache/camel/model/ProcessorDefinition.java +++ b/core/camel-core-model/src/main/java/org/apache/camel/model/ProcessorDefinition.java @@ -1112,7 +1112,9 @@ public abstract class ProcessorDefinition<Type extends ProcessorDefinition<Type> // are we already a try? if (def instanceof TryDefinition) { - return (TryDefinition) def; + // then we need special logic to end + TryDefinition td = (TryDefinition) def; + return (TryDefinition) td.onEndDoTry(); } // okay end this and get back to the try diff --git a/core/camel-core-model/src/main/java/org/apache/camel/model/TryDefinition.java b/core/camel-core-model/src/main/java/org/apache/camel/model/TryDefinition.java index 069769c..97bf952 100644 --- a/core/camel-core-model/src/main/java/org/apache/camel/model/TryDefinition.java +++ b/core/camel-core-model/src/main/java/org/apache/camel/model/TryDefinition.java @@ -49,6 +49,8 @@ public class TryDefinition extends OutputDefinition<TryDefinition> { private boolean initialized; @XmlTransient private List<ProcessorDefinition<?>> outputsWithoutCatches; + @XmlTransient + protected int endCounter; public TryDefinition() { } @@ -185,9 +187,19 @@ public class TryDefinition extends OutputDefinition<TryDefinition> { @Override public void addOutput(ProcessorDefinition<?> output) { initialized = false; + endCounter = 0; super.addOutput(output); } + protected ProcessorDefinition<?> onEndDoTry() { + if (endCounter > 0) { + return end(); + } else { + endCounter++; + } + return this; + } + @Override public void preCreateProcessor() { // force re-creating initialization to ensure its up-to-date diff --git a/core/camel-core/src/test/java/org/apache/camel/issues/TwoDoTryAndThrowInInnerCatchIssueTest.java b/core/camel-core/src/test/java/org/apache/camel/issues/TwoDoTryAndThrowInInnerCatchIssueTest.java index 869525f..26c1363 100644 --- a/core/camel-core/src/test/java/org/apache/camel/issues/TwoDoTryAndThrowInInnerCatchIssueTest.java +++ b/core/camel-core/src/test/java/org/apache/camel/issues/TwoDoTryAndThrowInInnerCatchIssueTest.java @@ -16,11 +16,12 @@ */ package org.apache.camel.issues; +import java.io.IOException; +import java.net.MalformedURLException; + import org.apache.camel.ContextTestSupport; import org.apache.camel.ExtendedCamelContext; -import org.apache.camel.LoggingLevel; import org.apache.camel.builder.RouteBuilder; -import org.apache.camel.model.TryDefinition; import org.junit.jupiter.api.Test; import static org.junit.jupiter.api.Assertions.fail; @@ -35,12 +36,21 @@ public class TwoDoTryAndThrowInInnerCatchIssueTest extends ContextTestSupport { ExtendedCamelContext ecc = context.adapt(ExtendedCamelContext.class); String xml = ecc.getModelToXMLDumper().dumpModelAsXml(context, context.getRouteDefinition("myroute")); log.info(xml); + System.out.println(xml); + + getMockEndpoint("mock:catch1").expectedMessageCount(0); + getMockEndpoint("mock:catch2").expectedMessageCount(0); + getMockEndpoint("mock:catch3").expectedMessageCount(0); + getMockEndpoint("mock:catch4").expectedMessageCount(1); + getMockEndpoint("mock:catch5").expectedMessageCount(1); try { template.requestBody("direct:test", "test", String.class); } catch (Exception e) { fail("Should not fail"); } + + assertMockEndpointsSatisfied(); } @Override @@ -50,36 +60,34 @@ public class TwoDoTryAndThrowInInnerCatchIssueTest extends ContextTestSupport { public void configure() throws Exception { errorHandler(noErrorHandler()); - TryDefinition try1 = from("direct:test").routeId("myroute").doTry(); - - TryDefinition try2 = try1.doTry(); - - try2.throwException(new IllegalArgumentException("Forced by me")) - .doCatch(Exception.class) - .log(LoggingLevel.INFO, TwoDoTryAndThrowInInnerCatchIssueTest.class.getName(), "docatch 1") - .throwException(new IllegalArgumentException("Second forced by me")) - .end(); - - try1.doCatch(Exception.class) - .log(LoggingLevel.INFO, TwoDoTryAndThrowInInnerCatchIssueTest.class.getName(), "docatch 3") - .end(); - - // stacked doTry in Java DSL has a flaw so you can do as above - /* - from("direct:test"). - doTry(). + from("direct:test").routeId("myroute") + .doTry(). doTry(). throwException(new IllegalArgumentException("Forced by me")) + .doCatch(IOException.class) + .to("mock:catch1") + .log("docatch 1") + // end this doCatch block + .endDoTry() + .doCatch(NullPointerException.class) + .to("mock:catch2") + .log("docatch 2") + // no end this catch block as Camel can fix this itself + .doCatch(MalformedURLException.class) + .to("mock:catch3") + .log("docatch 3") + // end this doCatch block + .endDoTry() .doCatch(Exception.class) - .log(LoggingLevel.INFO,DoThrowInCatchIssueTest.class.getName(), "docatch 1") + .to("mock:catch4") + .log("docatch 4") .throwException(new IllegalArgumentException("Second forced by me")) - .endDoTry() // end catch block - .endDoTry() // end inner try + .endDoTry() // end catch block + .endDoTry() // end inner doTry block .doCatch(Exception.class) - .log(LoggingLevel.INFO,DoThrowInCatchIssueTest.class.getName(), "docatch 3") + .to("mock:catch5") + .log("docatch 5") .end(); - } - };*/ } }; } diff --git a/docs/user-manual/modules/ROOT/pages/try-catch-finally.adoc b/docs/user-manual/modules/ROOT/pages/try-catch-finally.adoc index 385b4cc..7b4cda7 100644 --- a/docs/user-manual/modules/ROOT/pages/try-catch-finally.adoc +++ b/docs/user-manual/modules/ROOT/pages/try-catch-finally.adoc @@ -74,8 +74,78 @@ the `end()` should be at the end of the finally block. If we are not using a finally, then the `end()` should be at the end of the `doCatch` to indicate the end there. +TIP: Instead of `end()` you can use `endDoTry()` to end and return back to try .. catch scope. + +=== Using nested doTry .. doCatch EIPs + +When nesting doTry .. doCatch from an outer doTry .. doCatch EIP, then pay extra attention +when using Java DSL as the Java programming language is not _indent aware_ so you may write +Java code that are indented in a way where you think that a catch block is associated with +the other doTry but it is not. + +Given the following Java DSL: + +[source,java] +---- +from("direct:test").routeId("myroute") + .doTry(). + doTry(). + throwException(new IllegalArgumentException("Forced by me")) + .doCatch(Exception.class) + .log("docatch 1") + .throwException(new IllegalArgumentException("Second forced by me")) + .doCatch(Exception.class) + .log("docatch 2") + .end(); +---- + +Then you may think that _docatch2_ is associated on the outer doTry because of how the code is formatted. +But it is **not**, both __docatch1__ and __docatch2__ are in the inner doTry, and the outer doTry has no catch blocks. + +So in this example the route will throw the 1st exception which is then handled in __docatch1__ which +then throw a 2nd exception, that is not caught. + +So what you must do is to end the doCatch block correct (notice how we use `doEndTry()` two times) as shown below: + +[source,java] +---- +from("direct:test").routeId("myroute") + .doTry(). + doTry(). + throwException(new IllegalArgumentException("Forced by me")) + .doCatch(Exception.class) + .log("docatch 1") + .throwException(new IllegalArgumentException("Second forced by me")) + .endDoTry() // end this doCatch block + .endDoTry() // end the inner doTry + .doCatch(Exception.class) + .log("docatch 2") + .end(); +---- + +And by using the `doEndTry()` we can end the block correctly, and an XML representation of the route would be as follows: +[source,xml] +---- +<?xml version="1.0" encoding="UTF-8" standalone="yes"?> +<route xmlns="http://camel.apache.org/schema/spring"> + <from uri="direct:test"/> + <doTry> + <doTry> + <throwException id="throwException1"/> + <doCatch id="doCatch1"> + <log id="log1" message="docatch 1"/> + <throwException id="throwException2"/> + </doCatch> + </doTry> + <doCatch id="doCatch2"> + <log id="log2" message="docatch 2"/> + </doCatch> + </doTry> +</route> +---- + [[TryCatchFinally-Usingtry..catch..finallyinSpringDSL]] -== Using try .. catch .. finally in Spring DSL +== Using try .. catch .. finally in XML DSL In the route below we have all keywords in action. As the code is based on a unit test we route using xref:components::mock-component.adoc[Mock].