You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@camel.apache.org by da...@apache.org on 2026/07/22 11:22:49 UTC

(camel) 04/08: CAMEL-24237: Add doTry EIP documentation and skip when/otherwise from EIP list

This is an automated email from the ASF dual-hosted git repository.

davsclaus pushed a commit to branch feature/CAMEL-24237-catalog-docs
in repository https://gitbox.apache.org/repos/asf/camel.git

commit 9ff8d69c0e37a20f4a5a5e12c2ccf1fa66e40c8a
Author: Claus Ibsen <cl...@gmail.com>
AuthorDate: Wed Jul 22 13:07:14 2026 +0200

    CAMEL-24237: Add doTry EIP documentation and skip when/otherwise from EIP list
    
    Co-Authored-By: Claude Opus 4.6 <no...@anthropic.com>
    Signed-off-by: Claus Ibsen <cl...@gmail.com>
---
 .../org/apache/camel/catalog/docs.properties       |   1 +
 .../org/apache/camel/catalog/docs/doTry-eip.adoc   | 246 +++++++++++++++++++++
 .../main/docs/modules/eips/pages/doTry-eip.adoc    | 246 +++++++++++++++++++++
 .../dsl/jbang/core/commands/tui/CatalogTab.java    |   4 +
 4 files changed, 497 insertions(+)

diff --git a/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/docs.properties b/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/docs.properties
index 518dd4f70586..8b971e97b717 100644
--- a/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/docs.properties
+++ b/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/docs.properties
@@ -152,6 +152,7 @@ disruptor-component
 disruptor-vm-component
 djl-component
 dns-component
+doTry-eip
 docker-component
 docling-async-processing
 docling-batch-processing
diff --git a/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/docs/doTry-eip.adoc b/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/docs/doTry-eip.adoc
new file mode 100644
index 000000000000..0b43a808862f
--- /dev/null
+++ b/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/docs/doTry-eip.adoc
@@ -0,0 +1,246 @@
+= Do Try EIP
+:doctitle: Do Try
+:shortname: doTry
+:description: Wraps route steps in a try-catch-finally block for fine-grained exception handling within the route
+:since: 
+:supportlevel: Stable
+:tabs-sync-option:
+
+Camel supports the Java equivalent of try, catch, and finally directly in the DSL.
+It aims to work like its Java sisters but with more power.
+
+In Camel, we prefix the keywords with `do` to avoid having same keyword as Java.
+So we have:
+
+- `doTry`
+- `doCatch`
+- `doFinally`
+- `end` to end the block in Java DSL
+
+[IMPORTANT]
+====
+When using `doTry ... doCatch ... doFinally`, the regular Camel
+xref:manual::error-handler.adoc[Error Handler] is not in use; meaning any
+`onException` or the likes does not trigger.
+
+The reason is that
+`doTry ... doCatch ... doFinally` is in fact its own error handler and mimics how try/catch/finally works in Java.
+====
+
+== EIP options
+
+// eip options: START
+include::partial$eip-options.adoc[]
+// eip options: END
+
+== Using doTry ... doCatch ... doFinally
+
+In the route below we have all of them in action:
+
+[tabs]
+====
+
+Java::
++
+[source,java]
+----
+from("direct:start")
+    .doTry()
+        .process(new ProcessorFail())
+        .to("mock:result")
+    .doCatch(IOException.class, IllegalStateException.class)
+        .to("mock:catch")
+    .doFinally()
+        .to("mock:finally")
+    .end();
+----
+
+XML::
++
+[source,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>
+      <to uri="mock:catch"/>
+    </doCatch>
+    <doFinally>
+       <to uri="mock:finally"/>
+    </doFinally>
+  </doTry>
+</route>
+----
+
+YAML::
++
+[source,yaml]
+----
+- route:
+    from:
+      uri: direct:start
+      steps:
+        - doTry:
+            steps:
+              - process:
+                  ref: processorFail
+              - to:
+                  uri: mock:result
+              - doCatch:
+                  exception:
+                    - java.io.IOException
+                    - java.lang.IllegalStateException
+                  steps:
+                    - to:
+                        uri: mock:catch
+              - doFinally:
+                  steps:
+                    - to:
+                        uri: mock:finally
+----
+====
+
+=== Using onWhen with doCatch
+
+You can use xref:manual::predicate.adoc[Predicates] with `doCatch` to make it determine at runtime if the block should be triggered or not.
+In this example, we only want to trigger if the caused exception message contains the word *Damn*.
+
+[tabs]
+====
+
+Java::
++
+[source,java]
+----
+from("direct:start")
+    .doTry()
+        .process(new ProcessorFail())
+        .to("mock:result")
+    .doCatch(IOException.class, IllegalStateException.class)
+        .onWhen(exceptionMessage().contains("Damn"))
+        .to("mock:catch")
+    .doCatch(CamelExchangeException.class)
+        .to("mock:catchCamel")
+    .doFinally()
+        .to("mock:finally")
+    .end();
+----
+
+XML::
++
+[source,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 'Damn'</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>
+----
+
+YAML::
++
+[source,yaml]
+----
+- route:
+    from:
+      uri: direct:start
+      steps:
+        - doTry:
+            steps:
+              - process:
+                  ref: processorFail
+              - to:
+                  uri: mock:result
+              - doCatch:
+                  exception:
+                    - java.io.IOException
+                    - java.lang.IllegalStateException
+                  steps:
+                    - onWhen:
+                        expression:
+                          simple:
+                            expression: "${exception.message} contains 'Damn'"
+                    - to:
+                        uri: mock:catch
+              - doCatch:
+                  exception:
+                    - org.apache.camel.CamelExchangeException
+                  steps:
+                    - to:
+                        uri: mock:catchCamel
+              - doFinally:
+                  steps:
+                    - to:
+                        uri: mock:finally
+----
+====
+
+=== Use end() to end the block
+
+When using Java DSL you must use `end()` to indicate where the try ... catch ... finally block ends.
+If the route has a `doFinally`, then the `end()` should be at the end of the finally block.
+If there is no `doFinally`, then the `end()` should be at the end of the last `doCatch`.
+
+TIP: Instead of `end()` you can use `endDoTry()` to end and _return back_ to the try ... catch scope.
+
+=== Nested doTry ... doCatch
+
+When nesting `doTry ... doCatch` from an outer `doTry ... doCatch` EIP,
+pay extra attention when using Java DSL as the Java programming language is not _indent aware_
+so you may write code that is indented in a way where you think a catch block is associated with the other doTry, but it is not.
+
+Given the following Java DSL:
+
+[source,java]
+----
+from("direct:test")
+    .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();
+----
+
+Both `docatch 1` and `docatch 2` are in the inner `doTry`, and the outer `doTry` has no catch blocks.
+Use `endDoTry()` to end the blocks correctly:
+
+[source,java]
+----
+from("direct:test")
+    .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();
+----
diff --git a/core/camel-core-engine/src/main/docs/modules/eips/pages/doTry-eip.adoc b/core/camel-core-engine/src/main/docs/modules/eips/pages/doTry-eip.adoc
new file mode 100644
index 000000000000..0b43a808862f
--- /dev/null
+++ b/core/camel-core-engine/src/main/docs/modules/eips/pages/doTry-eip.adoc
@@ -0,0 +1,246 @@
+= Do Try EIP
+:doctitle: Do Try
+:shortname: doTry
+:description: Wraps route steps in a try-catch-finally block for fine-grained exception handling within the route
+:since: 
+:supportlevel: Stable
+:tabs-sync-option:
+
+Camel supports the Java equivalent of try, catch, and finally directly in the DSL.
+It aims to work like its Java sisters but with more power.
+
+In Camel, we prefix the keywords with `do` to avoid having same keyword as Java.
+So we have:
+
+- `doTry`
+- `doCatch`
+- `doFinally`
+- `end` to end the block in Java DSL
+
+[IMPORTANT]
+====
+When using `doTry ... doCatch ... doFinally`, the regular Camel
+xref:manual::error-handler.adoc[Error Handler] is not in use; meaning any
+`onException` or the likes does not trigger.
+
+The reason is that
+`doTry ... doCatch ... doFinally` is in fact its own error handler and mimics how try/catch/finally works in Java.
+====
+
+== EIP options
+
+// eip options: START
+include::partial$eip-options.adoc[]
+// eip options: END
+
+== Using doTry ... doCatch ... doFinally
+
+In the route below we have all of them in action:
+
+[tabs]
+====
+
+Java::
++
+[source,java]
+----
+from("direct:start")
+    .doTry()
+        .process(new ProcessorFail())
+        .to("mock:result")
+    .doCatch(IOException.class, IllegalStateException.class)
+        .to("mock:catch")
+    .doFinally()
+        .to("mock:finally")
+    .end();
+----
+
+XML::
++
+[source,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>
+      <to uri="mock:catch"/>
+    </doCatch>
+    <doFinally>
+       <to uri="mock:finally"/>
+    </doFinally>
+  </doTry>
+</route>
+----
+
+YAML::
++
+[source,yaml]
+----
+- route:
+    from:
+      uri: direct:start
+      steps:
+        - doTry:
+            steps:
+              - process:
+                  ref: processorFail
+              - to:
+                  uri: mock:result
+              - doCatch:
+                  exception:
+                    - java.io.IOException
+                    - java.lang.IllegalStateException
+                  steps:
+                    - to:
+                        uri: mock:catch
+              - doFinally:
+                  steps:
+                    - to:
+                        uri: mock:finally
+----
+====
+
+=== Using onWhen with doCatch
+
+You can use xref:manual::predicate.adoc[Predicates] with `doCatch` to make it determine at runtime if the block should be triggered or not.
+In this example, we only want to trigger if the caused exception message contains the word *Damn*.
+
+[tabs]
+====
+
+Java::
++
+[source,java]
+----
+from("direct:start")
+    .doTry()
+        .process(new ProcessorFail())
+        .to("mock:result")
+    .doCatch(IOException.class, IllegalStateException.class)
+        .onWhen(exceptionMessage().contains("Damn"))
+        .to("mock:catch")
+    .doCatch(CamelExchangeException.class)
+        .to("mock:catchCamel")
+    .doFinally()
+        .to("mock:finally")
+    .end();
+----
+
+XML::
++
+[source,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 'Damn'</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>
+----
+
+YAML::
++
+[source,yaml]
+----
+- route:
+    from:
+      uri: direct:start
+      steps:
+        - doTry:
+            steps:
+              - process:
+                  ref: processorFail
+              - to:
+                  uri: mock:result
+              - doCatch:
+                  exception:
+                    - java.io.IOException
+                    - java.lang.IllegalStateException
+                  steps:
+                    - onWhen:
+                        expression:
+                          simple:
+                            expression: "${exception.message} contains 'Damn'"
+                    - to:
+                        uri: mock:catch
+              - doCatch:
+                  exception:
+                    - org.apache.camel.CamelExchangeException
+                  steps:
+                    - to:
+                        uri: mock:catchCamel
+              - doFinally:
+                  steps:
+                    - to:
+                        uri: mock:finally
+----
+====
+
+=== Use end() to end the block
+
+When using Java DSL you must use `end()` to indicate where the try ... catch ... finally block ends.
+If the route has a `doFinally`, then the `end()` should be at the end of the finally block.
+If there is no `doFinally`, then the `end()` should be at the end of the last `doCatch`.
+
+TIP: Instead of `end()` you can use `endDoTry()` to end and _return back_ to the try ... catch scope.
+
+=== Nested doTry ... doCatch
+
+When nesting `doTry ... doCatch` from an outer `doTry ... doCatch` EIP,
+pay extra attention when using Java DSL as the Java programming language is not _indent aware_
+so you may write code that is indented in a way where you think a catch block is associated with the other doTry, but it is not.
+
+Given the following Java DSL:
+
+[source,java]
+----
+from("direct:test")
+    .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();
+----
+
+Both `docatch 1` and `docatch 2` are in the inner `doTry`, and the outer `doTry` has no catch blocks.
+Use `endDoTry()` to end the blocks correctly:
+
+[source,java]
+----
+from("direct:test")
+    .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();
+----
diff --git a/dsl/camel-jbang/camel-jbang-plugin-tui/src/main/java/org/apache/camel/dsl/jbang/core/commands/tui/CatalogTab.java b/dsl/camel-jbang/camel-jbang-plugin-tui/src/main/java/org/apache/camel/dsl/jbang/core/commands/tui/CatalogTab.java
index 65852839d7bd..113b4a8d269a 100644
--- a/dsl/camel-jbang/camel-jbang-plugin-tui/src/main/java/org/apache/camel/dsl/jbang/core/commands/tui/CatalogTab.java
+++ b/dsl/camel-jbang/camel-jbang-plugin-tui/src/main/java/org/apache/camel/dsl/jbang/core/commands/tui/CatalogTab.java
@@ -578,7 +578,11 @@ class CatalogTab extends AbstractTableTab {
                 }
             }
 
+            Set<String> skipEips = Set.of("when", "otherwise");
             for (String name : eipNames) {
+                if (skipEips.contains(name)) {
+                    continue;
+                }
                 EipModel model = catalog.eipModel(name);
                 if (model == null) {
                     continue;