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 a958d015a6c9 CAMEL-25367: retain YAML doTry property clauses in route
dumps (#27483)
a958d015a6c9 is described below
commit a958d015a6c9521632c259445df689e378cf08b7
Author: Andrea Cosentino <[email protected]>
AuthorDate: Wed Oct 7 12:26:17 2026 +0200
CAMEL-25367: retain YAML doTry property clauses in route dumps (#27483)
YAML doCatch/doFinally property clauses were executed but lost when routes
were dumped, so dump and reload could drop exception handling. The XML, YAML
and Java DSL writer templates (and generated writers) now include the
separately stored clauses without duplicating ones already in the outputs. Adds
regression tests and an upgrade-guide note.
Closes #27483
Co-authored-by: Codex <[email protected]>
---
.../apache/camel/java/out/JavaDslModelWriter.java | 19 +++-
.../apache/camel/java/out/TryModelWriterTest.java | 111 ++++++++++++++++++++
.../java/org/apache/camel/xml/out/ModelWriter.java | 19 +++-
.../apache/camel/xml/out/TryModelWriterTest.java | 114 +++++++++++++++++++++
.../org/apache/camel/yaml/out/YamlModelWriter.java | 18 +++-
.../apache/camel/yaml/out/TryModelWriterTest.java | 112 ++++++++++++++++++++
.../apache/camel/yaml/out/YamlModelWriterTest.java | 19 +++-
.../ROOT/pages/camel-4x-upgrade-guide-4_23.adoc | 4 +
.../org/apache/camel/dsl/yaml/TryYamlDumpTest.java | 65 ++++++++++++
.../resources/velocity/model-java-dsl-writer.vm | 21 ++++
.../src/main/resources/velocity/model-writer.vm | 21 ++++
.../main/resources/velocity/model-yaml-writer.vm | 20 ++++
12 files changed, 536 insertions(+), 7 deletions(-)
diff --git
a/core/camel-java-io/src/generated/java/org/apache/camel/java/out/JavaDslModelWriter.java
b/core/camel-java-io/src/generated/java/org/apache/camel/java/out/JavaDslModelWriter.java
index 6f017f27a13f..7fd3b343b54d 100644
---
a/core/camel-java-io/src/generated/java/org/apache/camel/java/out/JavaDslModelWriter.java
+++
b/core/camel-java-io/src/generated/java/org/apache/camel/java/out/JavaDslModelWriter.java
@@ -20,6 +20,7 @@
*/
package org.apache.camel.java.out;
+import java.util.ArrayList;
import java.util.Base64;
import java.util.List;
import java.util.function.BiConsumer;
@@ -2719,7 +2720,23 @@ public class JavaDslModelWriter extends
JavaDslModelWriterSupport {
}
protected void doWriteTryDefinition(StringBuilder sb, TryDefinition def) {
doWriteProcessorDefinitionAttributes(sb, def);
- doWriteOutputs(sb, def.getOutputs(),
this::doWriteProcessorDefinitionRef);
+ List<ProcessorDefinition<?>> outputs = new
ArrayList<>(def.getOutputs());
+ for (CatchDefinition clause : def.getCatchClauses()) {
+ if (!outputs.contains(clause)) {
+ int index = outputs.size();
+ for (int i = 0; i < outputs.size(); i++) {
+ if (outputs.get(i) instanceof FinallyDefinition) {
+ index = i;
+ break;
+ }
+ }
+ outputs.add(index, clause);
+ }
+ }
+ if (def.getFinallyClause() != null &&
!outputs.contains(def.getFinallyClause())) {
+ outputs.add(def.getFinallyClause());
+ }
+ doWriteOutputs(sb, outputs, this::doWriteProcessorDefinitionRef);
}
protected void doWriteUnmarshalDefinition(StringBuilder sb,
UnmarshalDefinition def) {
doWriteProcessorDefinitionAttributes(sb, def);
diff --git
a/core/camel-java-io/src/test/java/org/apache/camel/java/out/TryModelWriterTest.java
b/core/camel-java-io/src/test/java/org/apache/camel/java/out/TryModelWriterTest.java
new file mode 100644
index 000000000000..aab120acefb9
--- /dev/null
+++
b/core/camel-java-io/src/test/java/org/apache/camel/java/out/TryModelWriterTest.java
@@ -0,0 +1,111 @@
+/*
+ * 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.java.out;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.stream.Stream;
+
+import org.apache.camel.model.CatchDefinition;
+import org.apache.camel.model.FinallyDefinition;
+import org.apache.camel.model.ToDefinition;
+import org.apache.camel.model.TryDefinition;
+import org.junit.jupiter.api.Assertions;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.params.ParameterizedTest;
+import org.junit.jupiter.params.provider.Arguments;
+import org.junit.jupiter.params.provider.MethodSource;
+
+class TryModelWriterTest {
+ static Stream<Arguments> clauses() {
+ return Stream.of(false, true).flatMap(properties -> Stream.of(
+ Arguments.of(properties, 2, true), Arguments.of(properties, 2,
false),
+ Arguments.of(properties, 0, true)));
+ }
+
+ @ParameterizedTest
+ @MethodSource("clauses")
+ void clausesSurviveDump(boolean properties, int catchCount, boolean
hasFinally) throws Exception {
+ TryDefinition def = new TryDefinition();
+ def.addOutput(new ToDefinition("mock:try"));
+ List<CatchDefinition> catches = new ArrayList<>();
+ for (int i = 0; i < catchCount; i++) {
+ CatchDefinition clause = new CatchDefinition();
+ clause.getExceptions().add(i == 0 ?
"java.lang.IllegalStateException" : "java.lang.Exception");
+ clause.addOutput(new ToDefinition("mock:catch" + i));
+ catches.add(clause);
+ if (!properties) {
+ def.addOutput(clause);
+ }
+ }
+ if (properties) {
+ def.setCatchClauses(catches);
+ }
+ if (hasFinally) {
+ FinallyDefinition clause = new FinallyDefinition();
+ clause.addOutput(new ToDefinition("mock:finally"));
+ if (properties) {
+ def.setFinallyClause(clause);
+ } else {
+ def.addOutput(clause);
+ }
+ }
+ List<?> originalOutputs = new ArrayList<>(def.getOutputs());
+ String output = dump(def);
+ Assertions.assertTrue(output.contains("mock:try"), output);
+ int previous = output.indexOf("mock:try");
+ for (int i = 0; i < catchCount; i++) {
+ String uri = "mock:catch" + i;
+ int position = output.indexOf(uri);
+ Assertions.assertTrue(position > previous, output);
+ Assertions.assertEquals(position, output.lastIndexOf(uri), output);
+ previous = position;
+ }
+ if (hasFinally) {
+ int position = output.indexOf("mock:finally");
+ Assertions.assertTrue(position > previous, output);
+ Assertions.assertEquals(position,
output.lastIndexOf("mock:finally"), output);
+ }
+ Assertions.assertEquals(originalOutputs, def.getOutputs());
+ }
+
+ @Test
+ void dumpingAfterAddingOutputPreservesNewStepAndMixedClauses() throws
Exception {
+ TryDefinition def = new TryDefinition();
+ def.addOutput(new ToDefinition("mock:try"));
+ CatchDefinition clause = new CatchDefinition();
+ clause.getExceptions().add("java.lang.Exception");
+ clause.addOutput(new ToDefinition("mock:catch"));
+ def.setCatchClauses(List.of(clause));
+ FinallyDefinition finallyClause = new FinallyDefinition();
+ finallyClause.addOutput(new ToDefinition("mock:finally"));
+ def.addOutput(finallyClause);
+ String first = dump(def);
+ Assertions.assertTrue(first.indexOf("mock:catch") >
first.indexOf("mock:try"), first);
+ Assertions.assertTrue(first.indexOf("mock:finally") >
first.indexOf("mock:catch"), first);
+ def.addOutput(new ToDefinition("mock:later"));
+ String second = dump(def);
+ Assertions.assertTrue(second.contains("mock:later"), second);
+ Assertions.assertEquals(second.indexOf("mock:catch"),
second.lastIndexOf("mock:catch"), second);
+ Assertions.assertEquals(second.indexOf("mock:finally"),
second.lastIndexOf("mock:finally"), second);
+ Assertions.assertEquals(3, def.getOutputs().size());
+ }
+
+ private String dump(TryDefinition def) throws Exception {
+ return new JavaDslModelWriter().writeTryDefinition(def);
+ }
+}
diff --git
a/core/camel-xml-io/src/generated/java/org/apache/camel/xml/out/ModelWriter.java
b/core/camel-xml-io/src/generated/java/org/apache/camel/xml/out/ModelWriter.java
index 232cae56d81e..141f9bc7fbf1 100644
---
a/core/camel-xml-io/src/generated/java/org/apache/camel/xml/out/ModelWriter.java
+++
b/core/camel-xml-io/src/generated/java/org/apache/camel/xml/out/ModelWriter.java
@@ -22,6 +22,7 @@ package org.apache.camel.xml.out;
import java.io.IOException;
import java.io.Writer;
+import java.util.ArrayList;
import java.util.Base64;
import java.util.List;
import javax.annotation.processing.Generated;
@@ -1987,7 +1988,23 @@ public class ModelWriter extends BaseWriter {
protected void doWriteTryDefinition(String name, TryDefinition def) throws
IOException {
startElement(name);
doWriteProcessorDefinitionAttributes(def);
- doWriteList(null, null, def.getOutputs(),
this::doWriteProcessorDefinitionRef);
+ List<ProcessorDefinition<?>> outputs = new
ArrayList<>(def.getOutputs());
+ for (CatchDefinition clause : def.getCatchClauses()) {
+ if (!outputs.contains(clause)) {
+ int index = outputs.size();
+ for (int i = 0; i < outputs.size(); i++) {
+ if (outputs.get(i) instanceof FinallyDefinition) {
+ index = i;
+ break;
+ }
+ }
+ outputs.add(index, clause);
+ }
+ }
+ if (def.getFinallyClause() != null &&
!outputs.contains(def.getFinallyClause())) {
+ outputs.add(def.getFinallyClause());
+ }
+ doWriteList(null, null, outputs, this::doWriteProcessorDefinitionRef);
endElement(name);
}
protected void doWriteUnmarshalDefinition(String name, UnmarshalDefinition
def) throws IOException {
diff --git
a/core/camel-xml-io/src/test/java/org/apache/camel/xml/out/TryModelWriterTest.java
b/core/camel-xml-io/src/test/java/org/apache/camel/xml/out/TryModelWriterTest.java
new file mode 100644
index 000000000000..e00df9728617
--- /dev/null
+++
b/core/camel-xml-io/src/test/java/org/apache/camel/xml/out/TryModelWriterTest.java
@@ -0,0 +1,114 @@
+/*
+ * 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.xml.out;
+
+import java.io.StringWriter;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.stream.Stream;
+
+import org.apache.camel.model.CatchDefinition;
+import org.apache.camel.model.FinallyDefinition;
+import org.apache.camel.model.ToDefinition;
+import org.apache.camel.model.TryDefinition;
+import org.junit.jupiter.api.Assertions;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.params.ParameterizedTest;
+import org.junit.jupiter.params.provider.Arguments;
+import org.junit.jupiter.params.provider.MethodSource;
+
+class TryModelWriterTest {
+ static Stream<Arguments> clauses() {
+ return Stream.of(false, true).flatMap(properties -> Stream.of(
+ Arguments.of(properties, 2, true), Arguments.of(properties, 2,
false),
+ Arguments.of(properties, 0, true)));
+ }
+
+ @ParameterizedTest
+ @MethodSource("clauses")
+ void clausesSurviveDump(boolean properties, int catchCount, boolean
hasFinally) throws Exception {
+ TryDefinition def = new TryDefinition();
+ def.addOutput(new ToDefinition("mock:try"));
+ List<CatchDefinition> catches = new ArrayList<>();
+ for (int i = 0; i < catchCount; i++) {
+ CatchDefinition clause = new CatchDefinition();
+ clause.getExceptions().add(i == 0 ?
"java.lang.IllegalStateException" : "java.lang.Exception");
+ clause.addOutput(new ToDefinition("mock:catch" + i));
+ catches.add(clause);
+ if (!properties) {
+ def.addOutput(clause);
+ }
+ }
+ if (properties) {
+ def.setCatchClauses(catches);
+ }
+ if (hasFinally) {
+ FinallyDefinition clause = new FinallyDefinition();
+ clause.addOutput(new ToDefinition("mock:finally"));
+ if (properties) {
+ def.setFinallyClause(clause);
+ } else {
+ def.addOutput(clause);
+ }
+ }
+ List<?> originalOutputs = new ArrayList<>(def.getOutputs());
+ String output = dump(def);
+ Assertions.assertTrue(output.contains("mock:try"), output);
+ int previous = output.indexOf("mock:try");
+ for (int i = 0; i < catchCount; i++) {
+ String uri = "mock:catch" + i;
+ int position = output.indexOf(uri);
+ Assertions.assertTrue(position > previous, output);
+ Assertions.assertEquals(position, output.lastIndexOf(uri), output);
+ previous = position;
+ }
+ if (hasFinally) {
+ int position = output.indexOf("mock:finally");
+ Assertions.assertTrue(position > previous, output);
+ Assertions.assertEquals(position,
output.lastIndexOf("mock:finally"), output);
+ }
+ Assertions.assertEquals(originalOutputs, def.getOutputs());
+ }
+
+ @Test
+ void dumpingAfterAddingOutputPreservesNewStepAndMixedClauses() throws
Exception {
+ TryDefinition def = new TryDefinition();
+ def.addOutput(new ToDefinition("mock:try"));
+ CatchDefinition clause = new CatchDefinition();
+ clause.getExceptions().add("java.lang.Exception");
+ clause.addOutput(new ToDefinition("mock:catch"));
+ def.setCatchClauses(List.of(clause));
+ FinallyDefinition finallyClause = new FinallyDefinition();
+ finallyClause.addOutput(new ToDefinition("mock:finally"));
+ def.addOutput(finallyClause);
+ String first = dump(def);
+ Assertions.assertTrue(first.indexOf("mock:catch") >
first.indexOf("mock:try"), first);
+ Assertions.assertTrue(first.indexOf("mock:finally") >
first.indexOf("mock:catch"), first);
+ def.addOutput(new ToDefinition("mock:later"));
+ String second = dump(def);
+ Assertions.assertTrue(second.contains("mock:later"), second);
+ Assertions.assertEquals(second.indexOf("mock:catch"),
second.lastIndexOf("mock:catch"), second);
+ Assertions.assertEquals(second.indexOf("mock:finally"),
second.lastIndexOf("mock:finally"), second);
+ Assertions.assertEquals(3, def.getOutputs().size());
+ }
+
+ private String dump(TryDefinition def) throws Exception {
+ StringWriter buffer = new StringWriter();
+ new ModelWriter(buffer).writeTryDefinition(def);
+ return buffer.toString();
+ }
+}
diff --git
a/core/camel-yaml-io/src/generated/java/org/apache/camel/yaml/out/YamlModelWriter.java
b/core/camel-yaml-io/src/generated/java/org/apache/camel/yaml/out/YamlModelWriter.java
index c19cd64a6c01..1f82a92f49d5 100644
---
a/core/camel-yaml-io/src/generated/java/org/apache/camel/yaml/out/YamlModelWriter.java
+++
b/core/camel-yaml-io/src/generated/java/org/apache/camel/yaml/out/YamlModelWriter.java
@@ -1987,7 +1987,23 @@ public class YamlModelWriter extends
YamlModelWriterSupport {
protected JsonObject doWriteTryDefinition(TryDefinition def) {
JsonObject jo = new JsonObject();
doWriteProcessorDefinitionAttributes(jo, def);
- doWriteOutputs(jo, def.getOutputs(),
this::doWriteProcessorDefinitionRef);
+ List<ProcessorDefinition<?>> outputs = new
ArrayList<>(def.getOutputs());
+ for (CatchDefinition clause : def.getCatchClauses()) {
+ if (!outputs.contains(clause)) {
+ int index = outputs.size();
+ for (int i = 0; i < outputs.size(); i++) {
+ if (outputs.get(i) instanceof FinallyDefinition) {
+ index = i;
+ break;
+ }
+ }
+ outputs.add(index, clause);
+ }
+ }
+ if (def.getFinallyClause() != null &&
!outputs.contains(def.getFinallyClause())) {
+ outputs.add(def.getFinallyClause());
+ }
+ doWriteOutputs(jo, outputs, this::doWriteProcessorDefinitionRef);
return jo;
}
protected JsonObject doWriteUnmarshalDefinition(UnmarshalDefinition def) {
diff --git
a/core/camel-yaml-io/src/test/java/org/apache/camel/yaml/out/TryModelWriterTest.java
b/core/camel-yaml-io/src/test/java/org/apache/camel/yaml/out/TryModelWriterTest.java
new file mode 100644
index 000000000000..dff4a6e3ad73
--- /dev/null
+++
b/core/camel-yaml-io/src/test/java/org/apache/camel/yaml/out/TryModelWriterTest.java
@@ -0,0 +1,112 @@
+/*
+ * 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.yaml.out;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.stream.Stream;
+
+import org.apache.camel.model.CatchDefinition;
+import org.apache.camel.model.FinallyDefinition;
+import org.apache.camel.model.ToDefinition;
+import org.apache.camel.model.TryDefinition;
+import org.junit.jupiter.api.Assertions;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.params.ParameterizedTest;
+import org.junit.jupiter.params.provider.Arguments;
+import org.junit.jupiter.params.provider.MethodSource;
+
+class TryModelWriterTest {
+ static Stream<Arguments> clauses() {
+ return Stream.of(false, true).flatMap(properties -> Stream.of(
+ Arguments.of(properties, 2, true), Arguments.of(properties, 2,
false),
+ Arguments.of(properties, 0, true)));
+ }
+
+ @ParameterizedTest
+ @MethodSource("clauses")
+ void clausesSurviveDump(boolean properties, int catchCount, boolean
hasFinally) throws Exception {
+ TryDefinition def = new TryDefinition();
+ def.addOutput(new ToDefinition("mock:try"));
+ List<CatchDefinition> catches = new ArrayList<>();
+ for (int i = 0; i < catchCount; i++) {
+ CatchDefinition clause = new CatchDefinition();
+ clause.getExceptions().add(i == 0 ?
"java.lang.IllegalStateException" : "java.lang.Exception");
+ clause.addOutput(new ToDefinition("mock:catch" + i));
+ catches.add(clause);
+ if (!properties) {
+ def.addOutput(clause);
+ }
+ }
+ if (properties) {
+ def.setCatchClauses(catches);
+ }
+ if (hasFinally) {
+ FinallyDefinition clause = new FinallyDefinition();
+ clause.addOutput(new ToDefinition("mock:finally"));
+ if (properties) {
+ def.setFinallyClause(clause);
+ } else {
+ def.addOutput(clause);
+ }
+ }
+ List<?> originalOutputs = new ArrayList<>(def.getOutputs());
+ String output = dump(def);
+ Assertions.assertTrue(output.contains("mock:try"), output);
+ int previous = output.indexOf("mock:try");
+ for (int i = 0; i < catchCount; i++) {
+ String uri = "mock:catch" + i;
+ int position = output.indexOf(uri);
+ Assertions.assertTrue(position > previous, output);
+ Assertions.assertEquals(position, output.lastIndexOf(uri), output);
+ previous = position;
+ }
+ if (hasFinally) {
+ int position = output.indexOf("mock:finally");
+ Assertions.assertTrue(position > previous, output);
+ Assertions.assertEquals(position,
output.lastIndexOf("mock:finally"), output);
+ }
+ Assertions.assertEquals(originalOutputs, def.getOutputs());
+ }
+
+ @Test
+ void dumpingAfterAddingOutputPreservesNewStepAndMixedClauses() throws
Exception {
+ TryDefinition def = new TryDefinition();
+ def.addOutput(new ToDefinition("mock:try"));
+ CatchDefinition clause = new CatchDefinition();
+ clause.getExceptions().add("java.lang.Exception");
+ clause.addOutput(new ToDefinition("mock:catch"));
+ def.setCatchClauses(List.of(clause));
+ FinallyDefinition finallyClause = new FinallyDefinition();
+ finallyClause.addOutput(new ToDefinition("mock:finally"));
+ def.addOutput(finallyClause);
+ String first = dump(def);
+ Assertions.assertTrue(first.indexOf("mock:catch") >
first.indexOf("mock:try"), first);
+ Assertions.assertTrue(first.indexOf("mock:finally") >
first.indexOf("mock:catch"), first);
+ def.addOutput(new ToDefinition("mock:later"));
+ String second = dump(def);
+ Assertions.assertTrue(second.contains("mock:later"), second);
+ Assertions.assertEquals(second.indexOf("mock:catch"),
second.lastIndexOf("mock:catch"), second);
+ Assertions.assertEquals(second.indexOf("mock:finally"),
second.lastIndexOf("mock:finally"), second);
+ Assertions.assertEquals(3, def.getOutputs().size());
+ }
+
+ private String dump(TryDefinition def) throws Exception {
+ YamlModelWriter writer = new YamlModelWriter();
+ return writer.printAsYaml(List.of(writer.writeTryDefinition(def)));
+ }
+}
diff --git
a/core/camel-yaml-io/src/test/java/org/apache/camel/yaml/out/YamlModelWriterTest.java
b/core/camel-yaml-io/src/test/java/org/apache/camel/yaml/out/YamlModelWriterTest.java
index 9b34b518b958..cf310417dc82 100644
---
a/core/camel-yaml-io/src/test/java/org/apache/camel/yaml/out/YamlModelWriterTest.java
+++
b/core/camel-yaml-io/src/test/java/org/apache/camel/yaml/out/YamlModelWriterTest.java
@@ -114,6 +114,8 @@ import
org.apache.camel.model.validator.ValidatorsDefinition;
import org.apache.camel.util.json.JsonObject;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.Test;
+import org.junit.jupiter.params.ParameterizedTest;
+import org.junit.jupiter.params.provider.ValueSource;
import static org.apache.camel.util.IOHelper.stripLineComments;
@@ -681,8 +683,9 @@ public class YamlModelWriterTest {
Assertions.assertEquals(expected, out);
}
- @Test
- public void testTryCatchFinally() throws Exception {
+ @ParameterizedTest
+ @ValueSource(booleans = { false, true })
+ public void testTryCatchFinally(boolean propertyClauses) throws Exception {
YamlModelWriter writer = new YamlModelWriter();
RouteDefinition route = new RouteDefinition();
@@ -695,11 +698,19 @@ public class YamlModelWriterTest {
CatchDefinition catchDef = new CatchDefinition();
catchDef.getExceptions().add("java.io.IOException");
catchDef.addOutput(new ToDefinition("mock:catch"));
- tryDef.addOutput(catchDef);
+ if (propertyClauses) {
+ tryDef.setCatchClauses(List.of(catchDef));
+ } else {
+ tryDef.addOutput(catchDef);
+ }
FinallyDefinition finallyDef = new FinallyDefinition();
finallyDef.addOutput(new ToDefinition("mock:finally"));
- tryDef.addOutput(finallyDef);
+ if (propertyClauses) {
+ tryDef.setFinallyClause(finallyDef);
+ } else {
+ tryDef.addOutput(finallyDef);
+ }
route.addOutput(tryDef);
diff --git
a/docs/user-manual/modules/ROOT/pages/camel-4x-upgrade-guide-4_23.adoc
b/docs/user-manual/modules/ROOT/pages/camel-4x-upgrade-guide-4_23.adoc
index 8fd78b02134d..0bd219a9d19a 100644
--- a/docs/user-manual/modules/ROOT/pages/camel-4x-upgrade-guide-4_23.adoc
+++ b/docs/user-manual/modules/ROOT/pages/camel-4x-upgrade-guide-4_23.adoc
@@ -3916,6 +3916,10 @@ reported.
=== camel-core - doTry, doCatch and doFinally
+Route dumps now retain `doCatch` and `doFinally` blocks declared as properties
of a `doTry` in YAML.
+Previously these blocks ran correctly but were omitted from the dumped route.
If you compare or process
+route dumps, account for the previously missing exception-handling blocks in
the output.
+
A `doTry` must have one or more `doCatch` or `doFinally` blocks, otherwise the
route fails to start with
`doTry must have one or more doCatch or doFinally blocks`. Previously such a
`doTry` was accepted (which turned off
the route error handler for its steps).
diff --git
a/dsl/camel-yaml-dsl/camel-yaml-dsl/src/test/java/org/apache/camel/dsl/yaml/TryYamlDumpTest.java
b/dsl/camel-yaml-dsl/camel-yaml-dsl/src/test/java/org/apache/camel/dsl/yaml/TryYamlDumpTest.java
new file mode 100644
index 000000000000..1fa4f4093533
--- /dev/null
+++
b/dsl/camel-yaml-dsl/camel-yaml-dsl/src/test/java/org/apache/camel/dsl/yaml/TryYamlDumpTest.java
@@ -0,0 +1,65 @@
+/*
+ * 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.dsl.yaml;
+
+import org.apache.camel.dsl.yaml.support.YamlTestSupport;
+import org.apache.camel.model.TryDefinition;
+import org.apache.camel.yaml.LwModelToYAMLDumper;
+import org.junit.jupiter.params.ParameterizedTest;
+import org.junit.jupiter.params.provider.ValueSource;
+
+import static org.assertj.core.api.Assertions.assertThat;
+
+class TryYamlDumpTest extends YamlTestSupport {
+ @ParameterizedTest
+ @ValueSource(booleans = { false, true })
+ void propertyClausesSurviveDump(boolean started) throws Exception {
+ loadRoutes("""
+ - route:
+ id: original
+ from:
+ uri: direct:start
+ steps:
+ - doTry:
+ steps:
+ - to: mock:try
+ doCatch:
+ - exception:
+ - java.lang.IllegalStateException
+ steps:
+ - to: mock:catch
+ doFinally:
+ steps:
+ - to: mock:finally
+ """);
+ var route = context.getRouteDefinition("original");
+ if (started) {
+ context.start();
+ }
+ String yaml = new LwModelToYAMLDumper().dumpModelAsYaml(context,
route);
+ assertThat(yaml).contains("doCatch:", "doFinally:", "mock:catch",
"mock:finally");
+ if (started) {
+ context.stop();
+ }
+ context.removeRouteDefinition(route);
+ loadRoutes(yaml);
+ TryDefinition restored = (TryDefinition)
context.getRouteDefinition("original").getOutputs().get(0);
+ assertThat(restored.getCatchClauses()).hasSize(1);
+
assertThat(restored.getCatchClauses().get(0).getExceptions()).containsExactly("java.lang.IllegalStateException");
+ assertThat(restored.getFinallyClause()).isNotNull();
+ }
+}
diff --git
a/tooling/maven/camel-package-maven-plugin/src/main/resources/velocity/model-java-dsl-writer.vm
b/tooling/maven/camel-package-maven-plugin/src/main/resources/velocity/model-java-dsl-writer.vm
index 12dc6184d1c6..ce06ff42ae16 100644
---
a/tooling/maven/camel-package-maven-plugin/src/main/resources/velocity/model-java-dsl-writer.vm
+++
b/tooling/maven/camel-package-maven-plugin/src/main/resources/velocity/model-java-dsl-writer.vm
@@ -37,6 +37,7 @@
*/
package ${package};
+import java.util.ArrayList;
import java.util.Base64;
import java.util.List;
import java.util.function.BiConsumer;
@@ -150,7 +151,27 @@ public class JavaDslModelWriter extends
JavaDslModelWriterSupport {
#set( $foo = $elementRefs.add($root) )
#if( $list )
#if( $member.name == "outputs" )
+ #if( $clazz.simpleName == "TryDefinition" )
+ List<ProcessorDefinition<?>> outputs = new
ArrayList<>(def.getOutputs());
+ for (CatchDefinition clause : def.getCatchClauses()) {
+ if (!outputs.contains(clause)) {
+ int index = outputs.size();
+ for (int i = 0; i < outputs.size(); i++) {
+ if (outputs.get(i) instanceof FinallyDefinition) {
+ index = i;
+ break;
+ }
+ }
+ outputs.add(index, clause);
+ }
+ }
+ if (def.getFinallyClause() != null &&
!outputs.contains(def.getFinallyClause())) {
+ outputs.add(def.getFinallyClause());
+ }
+ doWriteOutputs(sb, outputs, this::doWrite${root.simpleName}Ref);
+ #else
doWriteOutputs(sb, def.${member.getter}(),
this::doWrite${root.simpleName}Ref);
+ #end
#else
#set( $refName = $member.xmlElementRef.name() )
#if( $refName && $refName != $default &&
$refName != "" )
diff --git
a/tooling/maven/camel-package-maven-plugin/src/main/resources/velocity/model-writer.vm
b/tooling/maven/camel-package-maven-plugin/src/main/resources/velocity/model-writer.vm
index 4b15a7fce29a..8fb03247d608 100644
---
a/tooling/maven/camel-package-maven-plugin/src/main/resources/velocity/model-writer.vm
+++
b/tooling/maven/camel-package-maven-plugin/src/main/resources/velocity/model-writer.vm
@@ -39,6 +39,7 @@ package ${package};
import java.io.IOException;
import java.io.Writer;
+import java.util.ArrayList;
import java.util.Base64;
import java.util.List;
import javax.annotation.processing.Generated;
@@ -144,7 +145,27 @@ public class ModelWriter extends BaseWriter {
#elseif( $member.xmlElementRef )
#set( $foo = $elementRefs.add($root) )
#if( $list )
+ #if( $clazz.simpleName == "TryDefinition" &&
$member.name == "outputs" )
+ List<ProcessorDefinition<?>> outputs = new
ArrayList<>(def.getOutputs());
+ for (CatchDefinition clause : def.getCatchClauses()) {
+ if (!outputs.contains(clause)) {
+ int index = outputs.size();
+ for (int i = 0; i < outputs.size(); i++) {
+ if (outputs.get(i) instanceof FinallyDefinition) {
+ index = i;
+ break;
+ }
+ }
+ outputs.add(index, clause);
+ }
+ }
+ if (def.getFinallyClause() != null &&
!outputs.contains(def.getFinallyClause())) {
+ outputs.add(def.getFinallyClause());
+ }
+ doWriteList(null, null, outputs, this::doWrite${root.simpleName}Ref);
+ #else
doWriteList(null, null, def.${member.getter}(),
this::doWrite${root.simpleName}Ref);
+ #end
#else
doWriteElement(null, def.${member.getter}(),
this::doWrite${root.simpleName}Ref);
#end
diff --git
a/tooling/maven/camel-package-maven-plugin/src/main/resources/velocity/model-yaml-writer.vm
b/tooling/maven/camel-package-maven-plugin/src/main/resources/velocity/model-yaml-writer.vm
index c9153ee6f1a6..ab82780d74be 100644
---
a/tooling/maven/camel-package-maven-plugin/src/main/resources/velocity/model-yaml-writer.vm
+++
b/tooling/maven/camel-package-maven-plugin/src/main/resources/velocity/model-yaml-writer.vm
@@ -145,7 +145,27 @@ public class YamlModelWriter extends
YamlModelWriterSupport {
#if( $list )
#set( $yamlKey =
$yamlKeys.get("${clazz.simpleName}.${member.name}") )
#if( $member.name == "outputs" )
+ #if( $clazz.simpleName == "TryDefinition" )
+ List<ProcessorDefinition<?>> outputs = new
ArrayList<>(def.getOutputs());
+ for (CatchDefinition clause : def.getCatchClauses()) {
+ if (!outputs.contains(clause)) {
+ int index = outputs.size();
+ for (int i = 0; i < outputs.size(); i++) {
+ if (outputs.get(i) instanceof FinallyDefinition) {
+ index = i;
+ break;
+ }
+ }
+ outputs.add(index, clause);
+ }
+ }
+ if (def.getFinallyClause() != null &&
!outputs.contains(def.getFinallyClause())) {
+ outputs.add(def.getFinallyClause());
+ }
+ doWriteOutputs(jo, outputs, this::doWrite${root.simpleName}Ref);
+ #else
doWriteOutputs(jo, def.${member.getter}(),
this::doWrite${root.simpleName}Ref);
+ #end
#elseif( $clazz.simpleName == "RestDefinition" &&
$member.name == "verbs" )
doWriteVerbs(jo, def.${member.getter}(),
this::doWrite${root.simpleName});
#elseif( $yamlKey )