This is an automated email from the ASF dual-hosted git repository.
davsclaus pushed a commit to branch camel-4.22.x
in repository https://gitbox.apache.org/repos/asf/camel.git
The following commit(s) were added to refs/heads/camel-4.22.x by this push:
new 5075e244f797 [camel-4.22.x] CAMEL-25222: Route dump - keep the YAML
list dash with source locations, and dump the exception of throwException
5075e244f797 is described below
commit 5075e244f797f6a5489fc70e41fbec99ac465945
Author: Claus Ibsen <[email protected]>
AuthorDate: Thu Oct 1 15:30:38 2026 +0200
[camel-4.22.x] CAMEL-25222: Route dump - keep the YAML list dash with
source locations, and dump the exception of throwException
Backport of #27198. When a route is dumped as YAML with source locations,
the
list dash of an element (such as a choice when) was dropped together with
the
source-location lines, so the dumped YAML was wrong. The dash is now
carried to
the first content line. throwException given an exception instance was
dumped
empty; the XML and YAML dumpers now write its type and message.
4.22.x has no typed CodeLine records in the route-dump console, so that
change
is ported to the JsonObject form used there, with the same logic.
Closes #27199
Co-Authored-By: Claude Opus 5.5 (1M context) <[email protected]>
Signed-off-by: Claus Ibsen <[email protected]>
---
.../camel/impl/console/RouteDumpDevConsole.java | 42 +++++++++---
.../impl/console/RouteDumpDevConsoleTest.java | 74 ++++++++++++++++++++++
.../util/DumpModelThrowExceptionInstanceTest.java | 67 ++++++++++++++++++++
.../org/apache/camel/xml/LwModelToXMLDumper.java | 20 ++++++
.../org/apache/camel/yaml/LwModelToYAMLDumper.java | 18 ++++++
5 files changed, 211 insertions(+), 10 deletions(-)
diff --git
a/core/camel-console/src/main/java/org/apache/camel/impl/console/RouteDumpDevConsole.java
b/core/camel-console/src/main/java/org/apache/camel/impl/console/RouteDumpDevConsole.java
index fb42ea5e9ea7..6a4523298822 100644
---
a/core/camel-console/src/main/java/org/apache/camel/impl/console/RouteDumpDevConsole.java
+++
b/core/camel-console/src/main/java/org/apache/camel/impl/console/RouteDumpDevConsole.java
@@ -226,8 +226,11 @@ public class RouteDumpDevConsole extends
AbstractDevConsole {
return code.isEmpty() ? null : code;
}
- private static JsonArray javaOrYamlLoadSourceAsJson(Reader reader) {
+ static JsonArray javaOrYamlLoadSourceAsJson(Reader reader) {
JsonArray code = new JsonArray();
+ // a dropped source location line that started a YAML list item: its
dash and line number go to the next line
+ int dashIndent = -1;
+ int dashLine = -1;
try {
LineNumberReader lnr = new LineNumberReader(reader);
String t;
@@ -235,23 +238,42 @@ public class RouteDumpDevConsole extends
AbstractDevConsole {
t = lnr.readLine();
if (t != null) {
// extra source location from code line
- if (t.contains("sourceLocation: ")) {
- // skip this line
- } else if (t.contains("sourceLineNumber: ")) {
- String idx = StringHelper.after(t, "sourceLineNumber:
").trim();
- if (!code.isEmpty()) {
- // assign line number to previous code line
- JsonObject c = (JsonObject) code.get(code.size() -
1);
+ boolean location = t.contains("sourceLocation: ");
+ boolean lineNumber = !location &&
t.contains("sourceLineNumber: ");
+ if (location || lineNumber) {
+ int idx = -1;
+ if (lineNumber) {
try {
- c.put("line", Integer.parseInt(idx));
+ idx = Integer.parseInt(StringHelper.after(t,
"sourceLineNumber: ").trim());
} catch (NumberFormatException e) {
// ignore
}
}
+ int indent = t.length() - t.stripLeading().length();
+ if (t.stripLeading().startsWith("- ")) {
+ // the first key of a list item (such as a when of
a choice): the next line takes the dash
+ dashIndent = indent;
+ dashLine = idx;
+ } else if (dashIndent >= 0) {
+ if (idx != -1) {
+ dashLine = idx;
+ }
+ } else if (idx != -1 && !code.isEmpty()) {
+ // assign line number to previous code line
+ JsonObject c = (JsonObject) code.get(code.size() -
1);
+ c.put("line", idx);
+ }
} else {
+ int line = -1;
+ if (dashIndent >= 0 && t.length() > dashIndent + 2 &&
t.startsWith(" ".repeat(dashIndent + 2))) {
+ t = " ".repeat(dashIndent) + "- " +
t.substring(dashIndent + 2);
+ line = dashLine;
+ }
+ dashIndent = -1;
+ dashLine = -1;
JsonObject c = new JsonObject();
c.put("code", Jsoner.escape(t));
- c.put("line", -1);
+ c.put("line", line);
code.add(c);
}
}
diff --git
a/core/camel-console/src/test/java/org/apache/camel/impl/console/RouteDumpDevConsoleTest.java
b/core/camel-console/src/test/java/org/apache/camel/impl/console/RouteDumpDevConsoleTest.java
new file mode 100644
index 000000000000..91fac44c1dbb
--- /dev/null
+++
b/core/camel-console/src/test/java/org/apache/camel/impl/console/RouteDumpDevConsoleTest.java
@@ -0,0 +1,74 @@
+/*
+ * 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.impl.console;
+
+import java.io.StringReader;
+import java.util.ArrayList;
+import java.util.List;
+
+import org.apache.camel.util.json.JsonArray;
+import org.apache.camel.util.json.JsonObject;
+import org.apache.camel.util.json.Jsoner;
+import org.junit.jupiter.api.Assertions;
+import org.junit.jupiter.api.Test;
+
+public class RouteDumpDevConsoleTest {
+
+ @Test
+ public void testYamlSourceLocationOfAListItem() {
+ // the source location of a when comes first in its list item:
dropping it must keep the dash
+ String yaml = """
+ - route:
+ from:
+ sourceLineNumber: 7
+ sourceLocation: OrderRoute.java
+ uri: timer:orders
+ steps:
+ - choice:
+ sourceLineNumber: 13
+ sourceLocation: OrderRoute.java
+ when:
+ - sourceLineNumber: 14
+ sourceLocation: OrderRoute.java
+ expression:
+ simple:
+ expression: "${body} == 9"
+ steps:
+ - throwException:
+ sourceLineNumber: 15
+ sourceLocation: OrderRoute.java
+ """;
+ JsonArray code = RouteDumpDevConsole.javaOrYamlLoadSourceAsJson(new
StringReader(yaml));
+ List<String> lines = new ArrayList<>();
+ for (Object o : code) {
+ JsonObject c = (JsonObject) o;
+ lines.add(c.getInteger("line") + " " +
Jsoner.unescape(c.getString("code")));
+ }
+ Assertions.assertEquals(List.of(
+ "-1 - route:",
+ "7 from:",
+ "-1 uri: timer:orders",
+ "-1 steps:",
+ "13 - choice:",
+ "-1 when:",
+ "14 - expression:",
+ "-1 simple:",
+ "-1 expression: \"${body} == 9\"",
+ "-1 steps:",
+ "15 - throwException:"), lines);
+ }
+}
diff --git
a/core/camel-core/src/test/java/org/apache/camel/util/DumpModelThrowExceptionInstanceTest.java
b/core/camel-core/src/test/java/org/apache/camel/util/DumpModelThrowExceptionInstanceTest.java
new file mode 100644
index 000000000000..073f8d008c19
--- /dev/null
+++
b/core/camel-core/src/test/java/org/apache/camel/util/DumpModelThrowExceptionInstanceTest.java
@@ -0,0 +1,67 @@
+/*
+ * 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.util;
+
+import org.apache.camel.builder.RouteBuilder;
+import org.apache.camel.support.PluginHelper;
+import org.junit.jupiter.api.Test;
+
+import static org.assertj.core.api.Assertions.assertThat;
+
+/**
+ * An exception given to throwException as an instance is dumped as its type
and message.
+ */
+public class DumpModelThrowExceptionInstanceTest extends
DumpModelAsYamlTestSupport {
+
+ @Test
+ public void testDumpModelAsYaml() throws Exception {
+ String out =
PluginHelper.getModelToYAMLDumper(context).dumpModelAsYaml(context,
context.getRouteDefinition("myRoute"));
+ assertThat(out).contains("- throwException:", """
+ message: Out of stock
+ exceptionType: java.lang.IllegalStateException
+ """);
+ String typed =
PluginHelper.getModelToYAMLDumper(context).dumpModelAsYaml(context,
context.getRouteDefinition("typed"));
+ assertThat(typed).contains("- throwException:", """
+ message: Sold out
+ exceptionType: java.lang.IllegalArgumentException
+ """);
+ }
+
+ @Test
+ public void testDumpModelAsXml() throws Exception {
+ String out =
PluginHelper.getModelToXMLDumper(context).dumpModelAsXml(context,
context.getRouteDefinition("myRoute"));
+ assertThat(out).contains(
+ "message=\"Out of stock\"
exceptionType=\"java.lang.IllegalStateException\"/>");
+ String typed =
PluginHelper.getModelToXMLDumper(context).dumpModelAsXml(context,
context.getRouteDefinition("typed"));
+ assertThat(typed).contains(
+ "message=\"Sold out\"
exceptionType=\"java.lang.IllegalArgumentException\"/>");
+ }
+
+ @Override
+ protected RouteBuilder createRouteBuilder() {
+ return new RouteBuilder() {
+ @Override
+ public void configure() {
+ from("direct:start").routeId("myRoute")
+ .throwException(new IllegalStateException("Out of
stock"));
+
+ from("direct:typed").routeId("typed")
+ .throwException(IllegalArgumentException.class, "Sold
out");
+ }
+ };
+ }
+}
diff --git
a/core/camel-xml-io/src/main/java/org/apache/camel/xml/LwModelToXMLDumper.java
b/core/camel-xml-io/src/main/java/org/apache/camel/xml/LwModelToXMLDumper.java
index 11f8aa7cb7a7..de7668158351 100644
---
a/core/camel-xml-io/src/main/java/org/apache/camel/xml/LwModelToXMLDumper.java
+++
b/core/camel-xml-io/src/main/java/org/apache/camel/xml/LwModelToXMLDumper.java
@@ -44,6 +44,7 @@ import org.apache.camel.model.RouteTemplateDefinition;
import org.apache.camel.model.RouteTemplatesDefinition;
import org.apache.camel.model.RoutesDefinition;
import org.apache.camel.model.SendDefinition;
+import org.apache.camel.model.ThrowExceptionDefinition;
import org.apache.camel.model.ToDynamicDefinition;
import org.apache.camel.model.dataformat.DataFormatsDefinition;
import org.apache.camel.model.language.ExpressionDefinition;
@@ -132,6 +133,25 @@ public class LwModelToXMLDumper implements
ModelToXMLDumper {
}
}
+ @Override
+ protected void doWriteThrowExceptionDefinition(String name,
ThrowExceptionDefinition def) throws IOException {
+ Exception e = def.getException();
+ String type = def.getExceptionClass() != null
+ ? def.getExceptionClass().getName() : e != null ?
e.getClass().getName() : null;
+ if (type == null || def.getExceptionType() != null ||
def.getRef() != null) {
+ super.doWriteThrowExceptionDefinition(name, def);
+ return;
+ }
+ // an exception given as a class or an instance, as in
throwException(new Exception("...")), is
+ // written as its type and message, as the class or instance
itself cannot be serialized
+ startElement(name);
+ doWriteProcessorDefinitionAttributes(def);
+ String message = def.getMessage() != null ? def.getMessage() :
e != null ? e.getMessage() : null;
+ doWriteAttribute("message", message, null);
+ doWriteAttribute("exceptionType", type, null);
+ endElement(name);
+ }
+
@Override
protected void doWriteValue(String value) throws IOException {
if (value != null && !value.isEmpty()) {
diff --git
a/core/camel-yaml-io/src/main/java/org/apache/camel/yaml/LwModelToYAMLDumper.java
b/core/camel-yaml-io/src/main/java/org/apache/camel/yaml/LwModelToYAMLDumper.java
index 589c62e2713d..12fd0707bda0 100644
---
a/core/camel-yaml-io/src/main/java/org/apache/camel/yaml/LwModelToYAMLDumper.java
+++
b/core/camel-yaml-io/src/main/java/org/apache/camel/yaml/LwModelToYAMLDumper.java
@@ -45,6 +45,7 @@ import org.apache.camel.model.RouteTemplateDefinition;
import org.apache.camel.model.RouteTemplatesDefinition;
import org.apache.camel.model.RoutesDefinition;
import org.apache.camel.model.SendDefinition;
+import org.apache.camel.model.ThrowExceptionDefinition;
import org.apache.camel.model.ToDynamicDefinition;
import org.apache.camel.model.dataformat.DataFormatsDefinition;
import org.apache.camel.model.language.ExpressionDefinition;
@@ -134,6 +135,23 @@ public class LwModelToYAMLDumper implements
ModelToYAMLDumper {
super.doWriteAttribute(jo, key, value, defaultValue);
}
+ @Override
+ protected JsonObject
doWriteThrowExceptionDefinition(ThrowExceptionDefinition def) {
+ JsonObject jo = super.doWriteThrowExceptionDefinition(def);
+ Exception e = def.getException();
+ String type = def.getExceptionClass() != null
+ ? def.getExceptionClass().getName() : e != null ?
e.getClass().getName() : null;
+ if (type != null && def.getExceptionType() == null &&
def.getRef() == null) {
+ // an exception given as a class or an instance, as in
throwException(new Exception("...")), is
+ // written as its type and message, as the class or
instance itself cannot be serialized
+ if (def.getMessage() == null && e != null) {
+ doWriteAttribute(jo, "message", e.getMessage(), null);
+ }
+ doWriteAttribute(jo, "exceptionType", type, null);
+ }
+ return jo;
+ }
+
@Override
protected void doWriteValue(JsonObject jo, String value) {
if (resolvePlaceholders && value != null) {