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 dd61fa5b82ab CAMEL-25222: Route dump - keep the YAML list dash with
source locations, and dump the exception of throwException (#27198)
dd61fa5b82ab is described below
commit dd61fa5b82ab2c3aa6a09db761cf251ccdf03075
Author: Claus Ibsen <[email protected]>
AuthorDate: Thu Oct 1 13:59:06 2026 +0200
CAMEL-25222: Route dump - keep the YAML list dash with source locations,
and dump the exception of throwException (#27198)
* CAMEL-25222: Route dump - keep the list dash of a YAML item whose source
location comes first, and dump the exception of throwException
The route-dump console drops the sourceLineNumber and sourceLocation lines
of a YAML dump. When they started a list
item, such as a when of a choice, the dash went with them and the item
became a map. The dash and the line number
now go to the next line.
An exception given to throwException as a class or an instance was dumped
empty in YAML and XML; the dumpers now
write its exceptionType and message.
Co-Authored-By: Claude Opus 5.5 (1M context) <[email protected]>
Signed-off-by: Claus Ibsen <[email protected]>
* CAMEL-25222: complete the truncated comment in the route dumpers
Co-Authored-By: Claude Opus 5.5 (1M context) <[email protected]>
Signed-off-by: Claus Ibsen <[email protected]>
---------
Signed-off-by: Claus Ibsen <[email protected]>
Co-authored-by: Claude Opus 5.5 (1M context) <[email protected]>
---
.../camel/impl/console/RouteDumpDevConsole.java | 42 ++++++++++----
.../impl/console/RouteDumpDevConsoleTest.java | 44 ++++++++++++++
.../util/DumpModelThrowExceptionInstanceTest.java | 67 ++++++++++++++++++++++
.../org/apache/camel/xml/LwModelToXMLDumper.java | 20 +++++++
.../org/apache/camel/yaml/LwModelToYAMLDumper.java | 18 ++++++
5 files changed, 181 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 30e6fc79a37e..7e4d707d2b4e 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
@@ -231,8 +231,11 @@ public class RouteDumpDevConsole extends
AbstractDevConsole {
return code.isEmpty() ? null : code;
}
- private static List<CodeLine> javaOrYamlLoadSourceAsJson(Reader reader) {
+ static List<CodeLine> javaOrYamlLoadSourceAsJson(Reader reader) {
List<CodeLine> code = new ArrayList<>();
+ // 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;
@@ -240,21 +243,40 @@ 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
- CodeLine prev = 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 {
- code.set(code.size() - 1, new
CodeLine(Integer.parseInt(idx), prev.code()));
+ 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
+ CodeLine prev = code.get(code.size() - 1);
+ code.set(code.size() - 1, new CodeLine(idx,
prev.code()));
+ }
} else {
- code.add(new CodeLine(-1, Jsoner.escape(t)));
+ if (dashIndent >= 0 && t.length() > dashIndent + 2 &&
t.startsWith(" ".repeat(dashIndent + 2))) {
+ t = " ".repeat(dashIndent) + "- " +
t.substring(dashIndent + 2);
+ code.add(new CodeLine(dashLine, Jsoner.escape(t)));
+ } else {
+ code.add(new CodeLine(-1, Jsoner.escape(t)));
+ }
+ dashIndent = -1;
+ dashLine = -1;
}
}
} while (t != null);
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
index 0b4013cdd491..06ff94760b68 100644
---
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
@@ -16,12 +16,16 @@
*/
package org.apache.camel.impl.console;
+import java.io.StringReader;
+import java.util.List;
+
import org.apache.camel.ContextTestSupport;
import org.apache.camel.builder.RouteBuilder;
import org.apache.camel.console.DevConsole;
import org.apache.camel.support.PluginHelper;
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;
@@ -63,4 +67,44 @@ public class RouteDumpDevConsoleTest extends
ContextTestSupport {
JsonArray routes = out.getCollection("routes");
Assertions.assertNotNull(routes);
}
+
+ @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
+ """;
+ List<RouteDumpDevConsole.CodeLine> code =
RouteDumpDevConsole.javaOrYamlLoadSourceAsJson(new StringReader(yaml));
+ List<String> lines = code.stream().map(c -> c.line() + " " +
Jsoner.unescape(c.code())).toList();
+ 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 9ebc1f233868..915e4cceb4a1 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
@@ -45,6 +45,7 @@ import org.apache.camel.model.RouteTemplatesDefinition;
import org.apache.camel.model.RoutesDefinition;
import org.apache.camel.model.SendDefinition;
import org.apache.camel.model.SwitchDefinition;
+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;
@@ -138,6 +139,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 318e20db2cf9..c5db9efafe79 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
@@ -46,6 +46,7 @@ import org.apache.camel.model.RouteTemplatesDefinition;
import org.apache.camel.model.RoutesDefinition;
import org.apache.camel.model.SendDefinition;
import org.apache.camel.model.SwitchDefinition;
+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;
@@ -137,6 +138,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) {