This is an automated email from the ASF dual-hosted git repository. davsclaus pushed a commit to branch lines in repository https://gitbox.apache.org/repos/asf/camel.git
commit 971f2deba25fd408ed49ef3156e530d5abe144c9 Author: Claus Ibsen <[email protected]> AuthorDate: Mon Oct 27 20:12:56 2025 +0100 CAMEL-22605: camel-core - Dump model structure dev console --- .../apache/camel/catalog/dev-consoles.properties | 1 + .../catalog/dev-consoles/route-structure.json | 15 +++ .../java/org/apache/camel/spi/ModelDumpLine.java | 26 ++++ .../apache/camel/spi/ModelToStructureDumper.java | 44 +++++++ .../org/apache/camel/spi/ModelToXMLDumper.java | 10 -- .../org/apache/camel/spi/ModelToYAMLDumper.java | 10 -- .../camel/impl/engine/AbstractCamelContext.java | 4 + .../camel/impl/engine/SimpleCamelContext.java | 17 +++ .../apache/camel/dev-console/route-structure.json | 15 +++ .../org/apache/camel/dev-console/route-structure | 2 + .../org/apache/camel/dev-consoles.properties | 2 +- .../apache/camel/impl/console/ConsoleHelper.java | 18 +++ .../camel/impl/console/RouteDumpDevConsole.java | 35 +----- ...vConsole.java => RouteStructureDevConsole.java} | 137 ++++++++++----------- .../org/apache/camel/modelstructure-dumper | 2 + .../camel/impl/DefaultModelToStructureDumper.java | 91 ++++++++++++++ .../management/mbean/ManagedCamelContextMBean.java | 9 -- .../api/management/mbean/ManagedRouteMBean.java | 9 -- .../management/mbean/ManagedCamelContext.java | 49 -------- .../camel/management/mbean/ManagedRoute.java | 69 ----------- .../camel/management/DumpRouteStructureTest.java | 103 ++++++++++++++++ ...edCamelContextDumpRouteStructureAsTextTest.java | 86 ------------- ...gedCamelContextDumpRouteStructureAsXmlTest.java | 89 ------------- ...edCamelContextDumpRouteStructureAsYamlTest.java | 74 ----------- .../org/apache/camel/support/PluginHelper.java | 15 +++ .../org/apache/camel/xml/LwModelToXMLDumper.java | 75 +++++++---- .../java/org/apache/camel/xml/io/XMLWriter.java | 22 +--- .../java/org/apache/camel/xml/out/BaseWriter.java | 6 +- .../camel/xml/jaxb/JaxbModelToXMLDumper.java | 5 - .../org/apache/camel/yaml/LwModelToYAMLDumper.java | 55 --------- .../camel/cli/connector/LocalCliConnector.java | 3 +- .../core/commands/action/CamelRouteDumpAction.java | 15 ++- 32 files changed, 494 insertions(+), 619 deletions(-) diff --git a/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/dev-consoles.properties b/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/dev-consoles.properties index 920a75432776..7f97cf277718 100644 --- a/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/dev-consoles.properties +++ b/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/dev-consoles.properties @@ -41,6 +41,7 @@ route route-controller route-dump route-group +route-structure send service sftp diff --git a/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/dev-consoles/route-structure.json b/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/dev-consoles/route-structure.json new file mode 100644 index 000000000000..a881d65b63d4 --- /dev/null +++ b/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/dev-consoles/route-structure.json @@ -0,0 +1,15 @@ +{ + "console": { + "kind": "console", + "group": "camel", + "name": "route-structure", + "title": "Route Structure", + "description": "Dump route structure", + "deprecated": false, + "javaType": "org.apache.camel.impl.console.RouteStructureDevConsole", + "groupId": "org.apache.camel", + "artifactId": "camel-console", + "version": "4.16.0-SNAPSHOT" + } +} + diff --git a/core/camel-api/src/main/java/org/apache/camel/spi/ModelDumpLine.java b/core/camel-api/src/main/java/org/apache/camel/spi/ModelDumpLine.java new file mode 100644 index 000000000000..06ce1ab0e038 --- /dev/null +++ b/core/camel-api/src/main/java/org/apache/camel/spi/ModelDumpLine.java @@ -0,0 +1,26 @@ +/* + * 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.spi; + +/** + * Represents a line in a model dumper + * + * @param location line source location:line (if present) + * @param code dump code such as YAML, XML, or text + */ +public record ModelDumpLine(String location, String type, String id, int level, String code) { +} diff --git a/core/camel-api/src/main/java/org/apache/camel/spi/ModelToStructureDumper.java b/core/camel-api/src/main/java/org/apache/camel/spi/ModelToStructureDumper.java new file mode 100644 index 000000000000..8b4b2d359308 --- /dev/null +++ b/core/camel-api/src/main/java/org/apache/camel/spi/ModelToStructureDumper.java @@ -0,0 +1,44 @@ +/* + * 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.spi; + +import java.util.List; + +import org.apache.camel.CamelContext; +import org.apache.camel.Route; + +/** + * SPI for dumping model definitions into textual structure + */ +public interface ModelToStructureDumper { + + /** + * Service factory key. + */ + String FACTORY = "modelstructure-dumper"; + + /** + * Dumps the route (structure only) with source code lines + * + * @param context the CamelContext + * @param route the route + * @param brief whether to include fewer details (brief mode) + * @return the output in textual structure + */ + List<ModelDumpLine> dumpStructure(CamelContext context, Route route, boolean brief) throws Exception; + +} diff --git a/core/camel-api/src/main/java/org/apache/camel/spi/ModelToXMLDumper.java b/core/camel-api/src/main/java/org/apache/camel/spi/ModelToXMLDumper.java index c526c5e26266..c9a210b33e6e 100644 --- a/core/camel-api/src/main/java/org/apache/camel/spi/ModelToXMLDumper.java +++ b/core/camel-api/src/main/java/org/apache/camel/spi/ModelToXMLDumper.java @@ -76,14 +76,4 @@ public interface ModelToXMLDumper { */ String dumpDataFormatsAsXml(CamelContext context, Map<String, Object> dataFormats) throws Exception; - /** - * Dumps the definition (structure only) as XML - * - * @param context the CamelContext - * @param definition the definition, such as a {@link NamedNode} - * @return the output in XML (is formatted) - * @throws Exception is throw if error marshalling to XML - */ - String dumpStructureModelAsXml(CamelContext context, NamedNode definition) throws Exception; - } diff --git a/core/camel-api/src/main/java/org/apache/camel/spi/ModelToYAMLDumper.java b/core/camel-api/src/main/java/org/apache/camel/spi/ModelToYAMLDumper.java index ac26a9dfb79b..ab8b5b67f064 100644 --- a/core/camel-api/src/main/java/org/apache/camel/spi/ModelToYAMLDumper.java +++ b/core/camel-api/src/main/java/org/apache/camel/spi/ModelToYAMLDumper.java @@ -78,14 +78,4 @@ public interface ModelToYAMLDumper { */ String dumpDataFormatsAsYaml(CamelContext context, Map<String, Object> dataFormats) throws Exception; - /** - * Dumps the definition (structure only) as YAML - * - * @param context the CamelContext - * @param definition the definition, such as a {@link NamedNode} - * @return the output in YAML (is formatted) - * @throws Exception is throw if error marshalling to YAML - */ - String dumpStructureModelAsYaml(CamelContext context, NamedNode definition) throws Exception; - } diff --git a/core/camel-base-engine/src/main/java/org/apache/camel/impl/engine/AbstractCamelContext.java b/core/camel-base-engine/src/main/java/org/apache/camel/impl/engine/AbstractCamelContext.java index 681e00ad5a08..6d5b81affab0 100644 --- a/core/camel-base-engine/src/main/java/org/apache/camel/impl/engine/AbstractCamelContext.java +++ b/core/camel-base-engine/src/main/java/org/apache/camel/impl/engine/AbstractCamelContext.java @@ -136,6 +136,7 @@ import org.apache.camel.spi.ManagementNameStrategy; import org.apache.camel.spi.ManagementStrategy; import org.apache.camel.spi.MessageHistoryFactory; import org.apache.camel.spi.ModelJAXBContextFactory; +import org.apache.camel.spi.ModelToStructureDumper; import org.apache.camel.spi.ModelToXMLDumper; import org.apache.camel.spi.ModelToYAMLDumper; import org.apache.camel.spi.ModelineFactory; @@ -392,6 +393,7 @@ public abstract class AbstractCamelContext extends BaseService camelContextExtension.lazyAddContextPlugin(BeanProcessorFactory.class, this::createBeanProcessorFactory); camelContextExtension.lazyAddContextPlugin(ModelToXMLDumper.class, this::createModelToXMLDumper); camelContextExtension.lazyAddContextPlugin(ModelToYAMLDumper.class, this::createModelToYAMLDumper); + camelContextExtension.lazyAddContextPlugin(ModelToStructureDumper.class, this::createModelToStructureDumper); camelContextExtension.lazyAddContextPlugin(DeferServiceFactory.class, this::createDeferServiceFactory); camelContextExtension.lazyAddContextPlugin(AnnotationBasedProcessorFactory.class, this::createAnnotationBasedProcessorFactory); @@ -4458,6 +4460,8 @@ public abstract class AbstractCamelContext extends BaseService protected abstract ModelToYAMLDumper createModelToYAMLDumper(); + protected abstract ModelToStructureDumper createModelToStructureDumper(); + protected abstract RestBindingJaxbDataFormatFactory createRestBindingJaxbDataFormatFactory(); protected abstract RestBindingJacksonXmlDataFormatFactory createRestBindingJacksonXmlDataFormatFactory(); diff --git a/core/camel-base-engine/src/main/java/org/apache/camel/impl/engine/SimpleCamelContext.java b/core/camel-base-engine/src/main/java/org/apache/camel/impl/engine/SimpleCamelContext.java index 0a4a0a53fbe1..9b855ffac68b 100644 --- a/core/camel-base-engine/src/main/java/org/apache/camel/impl/engine/SimpleCamelContext.java +++ b/core/camel-base-engine/src/main/java/org/apache/camel/impl/engine/SimpleCamelContext.java @@ -67,6 +67,7 @@ import org.apache.camel.spi.LanguageResolver; import org.apache.camel.spi.ManagementNameStrategy; import org.apache.camel.spi.MessageHistoryFactory; import org.apache.camel.spi.ModelJAXBContextFactory; +import org.apache.camel.spi.ModelToStructureDumper; import org.apache.camel.spi.ModelToXMLDumper; import org.apache.camel.spi.ModelToYAMLDumper; import org.apache.camel.spi.ModelineFactory; @@ -623,6 +624,22 @@ public class SimpleCamelContext extends AbstractCamelContext { } } + @Override + protected ModelToStructureDumper createModelToStructureDumper() { + Optional<ModelToStructureDumper> result = ResolverHelper.resolveService( + getCamelContextReference(), + getCamelContextExtension().getBootstrapFactoryFinder(), + ModelToStructureDumper.FACTORY, + ModelToStructureDumper.class); + + if (result.isPresent()) { + return result.get(); + } else { + throw new IllegalArgumentException( + "Cannot find ModelToStructureDumper on classpath. Add camel-core-engine to classpath."); + } + } + @Override protected RestBindingJaxbDataFormatFactory createRestBindingJaxbDataFormatFactory() { Optional<RestBindingJaxbDataFormatFactory> result = ResolverHelper.resolveService( diff --git a/core/camel-console/src/generated/resources/META-INF/org/apache/camel/dev-console/route-structure.json b/core/camel-console/src/generated/resources/META-INF/org/apache/camel/dev-console/route-structure.json new file mode 100644 index 000000000000..a881d65b63d4 --- /dev/null +++ b/core/camel-console/src/generated/resources/META-INF/org/apache/camel/dev-console/route-structure.json @@ -0,0 +1,15 @@ +{ + "console": { + "kind": "console", + "group": "camel", + "name": "route-structure", + "title": "Route Structure", + "description": "Dump route structure", + "deprecated": false, + "javaType": "org.apache.camel.impl.console.RouteStructureDevConsole", + "groupId": "org.apache.camel", + "artifactId": "camel-console", + "version": "4.16.0-SNAPSHOT" + } +} + diff --git a/core/camel-console/src/generated/resources/META-INF/services/org/apache/camel/dev-console/route-structure b/core/camel-console/src/generated/resources/META-INF/services/org/apache/camel/dev-console/route-structure new file mode 100644 index 000000000000..b561c90fe179 --- /dev/null +++ b/core/camel-console/src/generated/resources/META-INF/services/org/apache/camel/dev-console/route-structure @@ -0,0 +1,2 @@ +# Generated by camel build tools - do NOT edit this file! +class=org.apache.camel.impl.console.RouteStructureDevConsole diff --git a/core/camel-console/src/generated/resources/META-INF/services/org/apache/camel/dev-consoles.properties b/core/camel-console/src/generated/resources/META-INF/services/org/apache/camel/dev-consoles.properties index c37795b44382..af617cb7354a 100644 --- a/core/camel-console/src/generated/resources/META-INF/services/org/apache/camel/dev-consoles.properties +++ b/core/camel-console/src/generated/resources/META-INF/services/org/apache/camel/dev-consoles.properties @@ -1,5 +1,5 @@ # Generated by camel build tools - do NOT edit this file! -dev-consoles=bean blocked browse circuit-breaker consumer context debug endpoint event gc health inflight internal-tasks java-security jvm log memory processor properties receive reload rest route route-controller route-dump route-group send service source startup-recorder system-properties thread top trace transformers type-converters variables +dev-consoles=bean blocked browse circuit-breaker consumer context debug endpoint event gc health inflight internal-tasks java-security jvm log memory processor properties receive reload rest route route-controller route-dump route-group route-structure send service source startup-recorder system-properties thread top trace transformers type-converters variables groupId=org.apache.camel artifactId=camel-console version=4.16.0-SNAPSHOT diff --git a/core/camel-console/src/main/java/org/apache/camel/impl/console/ConsoleHelper.java b/core/camel-console/src/main/java/org/apache/camel/impl/console/ConsoleHelper.java index 0a6efe362aab..30dca7af3f7f 100644 --- a/core/camel-console/src/main/java/org/apache/camel/impl/console/ConsoleHelper.java +++ b/core/camel-console/src/main/java/org/apache/camel/impl/console/ConsoleHelper.java @@ -27,6 +27,7 @@ import org.apache.camel.spi.Resource; import org.apache.camel.support.LoggerHelper; import org.apache.camel.support.PluginHelper; import org.apache.camel.util.IOHelper; +import org.apache.camel.util.StringHelper; import org.apache.camel.util.json.JsonObject; import org.apache.camel.util.json.Jsoner; @@ -112,4 +113,21 @@ public final class ConsoleHelper { return null; } + private static Integer extractSourceLocationLineNumber(String location) { + int cnt = StringHelper.countChar(location, ':'); + if (cnt > 0) { + int pos = location.lastIndexOf(':'); + // in case pos is end of line + if (pos < location.length() - 1) { + String num = location.substring(pos + 1); + try { + return Integer.valueOf(num); + } catch (Exception e) { + return null; + } + } + } + return null; + } + } 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 01d9df8b0342..2332f1037cbe 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 @@ -57,11 +57,6 @@ public class RouteDumpDevConsole extends AbstractDevConsole { */ public static final String URI_AS_PARAMETERS = "uriAsParameters"; - /** - * Whether to dump in brief mode (only overall structure, and no detailed options or expressions) - */ - public static final String BRIEF = "brief"; - public RouteDumpDevConsole() { super("camel", "route-dump", "Route Dump", "Dump route in XML or YAML format"); } @@ -69,7 +64,6 @@ public class RouteDumpDevConsole extends AbstractDevConsole { @Override protected String doCallText(Map<String, Object> options) { final String uriAsParameters = (String) options.getOrDefault(URI_AS_PARAMETERS, "false"); - final String brief = (String) options.getOrDefault(BRIEF, "false"); final StringBuilder sb = new StringBuilder(); Function<ManagedRouteMBean, Object> task = mrb -> { @@ -77,19 +71,9 @@ public class RouteDumpDevConsole extends AbstractDevConsole { try { String format = (String) options.get(FORMAT); if (format == null || "xml".equals(format)) { - if ("true".equals(brief)) { - dump = mrb.dumpStructureRouteAsXml(); - } else { - dump = mrb.dumpRouteAsXml(true); - } + dump = mrb.dumpRouteAsXml(true); } else if ("yaml".equals(format)) { - if ("true".equals(brief)) { - dump = mrb.dumpStructureRouteAsYaml(); - } else { - dump = mrb.dumpRouteAsYaml(true, "true".equals(uriAsParameters)); - } - } else if ("text".equals(format)) { - dump = mrb.dumpStructureRouteAsText("true".equals(brief)); + dump = mrb.dumpRouteAsYaml(true, "true".equals(uriAsParameters)); } } catch (Exception e) { // ignore @@ -116,7 +100,6 @@ public class RouteDumpDevConsole extends AbstractDevConsole { @Override protected JsonObject doCallJson(Map<String, Object> options) { final String uriAsParameters = (String) options.getOrDefault(URI_AS_PARAMETERS, "false"); - final String brief = (String) options.getOrDefault(BRIEF, "false"); final JsonObject root = new JsonObject(); final List<JsonObject> list = new ArrayList<>(); @@ -136,20 +119,10 @@ public class RouteDumpDevConsole extends AbstractDevConsole { String format = (String) options.get(FORMAT); if (format == null || "xml".equals(format)) { jo.put("format", "xml"); - if ("true".equals(brief)) { - dump = mrb.dumpStructureRouteAsXml(); - } else { - dump = mrb.dumpRouteAsXml(true); - } + dump = mrb.dumpRouteAsXml(true); } else if ("yaml".equals(format)) { jo.put("format", "yaml"); - if ("true".equals(brief)) { - dump = mrb.dumpStructureRouteAsYaml(); - } else { - dump = mrb.dumpRouteAsYaml(true, "true".equals(uriAsParameters)); - } - } else if ("text".equals(format)) { - dump = mrb.dumpStructureRouteAsText("true".equals(brief)); + dump = mrb.dumpRouteAsYaml(true, "true".equals(uriAsParameters)); } if (dump != null) { List<JsonObject> code = ConsoleHelper.loadSourceAsJson(new StringReader(dump), null); 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/RouteStructureDevConsole.java similarity index 62% copy from core/camel-console/src/main/java/org/apache/camel/impl/console/RouteDumpDevConsole.java copy to core/camel-console/src/main/java/org/apache/camel/impl/console/RouteStructureDevConsole.java index 01d9df8b0342..95648b29a065 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/RouteStructureDevConsole.java @@ -16,7 +16,6 @@ */ package org.apache.camel.impl.console; -import java.io.StringReader; import java.util.ArrayList; import java.util.List; import java.util.Map; @@ -27,20 +26,19 @@ import org.apache.camel.Exchange; import org.apache.camel.Route; import org.apache.camel.api.management.ManagedCamelContext; import org.apache.camel.api.management.mbean.ManagedRouteMBean; +import org.apache.camel.spi.ModelDumpLine; +import org.apache.camel.spi.ModelToStructureDumper; import org.apache.camel.spi.annotations.DevConsole; import org.apache.camel.support.LoggerHelper; import org.apache.camel.support.PatternHelper; +import org.apache.camel.support.PluginHelper; import org.apache.camel.support.console.AbstractDevConsole; import org.apache.camel.util.StringHelper; import org.apache.camel.util.json.JsonObject; +import org.apache.camel.util.json.Jsoner; -@DevConsole(name = "route-dump", description = "Dump route in XML or YAML format") -public class RouteDumpDevConsole extends AbstractDevConsole { - - /** - * To output in either xml, yaml, or text format - */ - public static final String FORMAT = "format"; +@DevConsole(name = "route-structure", description = "Dump route structure") +public class RouteStructureDevConsole extends AbstractDevConsole { /** * Filters the routes matching by route id, route uri, and source location @@ -52,58 +50,43 @@ public class RouteDumpDevConsole extends AbstractDevConsole { */ public static final String LIMIT = "limit"; - /** - * Whether to expand URIs into separated key/value parameters - */ - public static final String URI_AS_PARAMETERS = "uriAsParameters"; - /** * Whether to dump in brief mode (only overall structure, and no detailed options or expressions) */ public static final String BRIEF = "brief"; - public RouteDumpDevConsole() { - super("camel", "route-dump", "Route Dump", "Dump route in XML or YAML format"); + public RouteStructureDevConsole() { + super("camel", "route-structure", "Route Structure", "Dump route structure"); } @Override protected String doCallText(Map<String, Object> options) { - final String uriAsParameters = (String) options.getOrDefault(URI_AS_PARAMETERS, "false"); final String brief = (String) options.getOrDefault(BRIEF, "false"); final StringBuilder sb = new StringBuilder(); Function<ManagedRouteMBean, Object> task = mrb -> { - String dump = null; try { - String format = (String) options.get(FORMAT); - if (format == null || "xml".equals(format)) { - if ("true".equals(brief)) { - dump = mrb.dumpStructureRouteAsXml(); - } else { - dump = mrb.dumpRouteAsXml(true); - } - } else if ("yaml".equals(format)) { - if ("true".equals(brief)) { - dump = mrb.dumpStructureRouteAsYaml(); - } else { - dump = mrb.dumpRouteAsYaml(true, "true".equals(uriAsParameters)); - } - } else if ("text".equals(format)) { - dump = mrb.dumpStructureRouteAsText("true".equals(brief)); + ModelToStructureDumper dumper = PluginHelper.getModelToStructureDumper(getCamelContext()); + Route route = getCamelContext().getRoute(mrb.getRouteId()); + List<ModelDumpLine> lines = dumper.dumpStructure(getCamelContext(), route, "true".equalsIgnoreCase(brief)); + + sb.append(String.format(" Id: %s", mrb.getRouteId())); + if (mrb.getSourceLocation() != null) { + sb.append(String.format("\n Source: %s", mrb.getSourceLocation())); } - } catch (Exception e) { - // ignore - } - sb.append(String.format(" Id: %s", mrb.getRouteId())); - if (mrb.getSourceLocation() != null) { - sb.append(String.format("\n Source: %s", mrb.getSourceLocation())); - } - if (dump != null && !dump.isEmpty()) { sb.append("\n\n"); - for (String line : dump.split("\n")) { - sb.append(" ").append(line).append("\n"); + for (ModelDumpLine line : lines) { + String pad = StringHelper.padString(line.level()); + String num = " "; + Integer idx = extractSourceLocationLineNumber(line.location()); + if (idx != null) { + num = String.format("%4d: ", idx); + } + sb.append(num).append(pad).append(line.code()).append("\n"); } sb.append("\n"); + } catch (Exception e) { + // ignore } sb.append("\n"); @@ -115,7 +98,6 @@ public class RouteDumpDevConsole extends AbstractDevConsole { @Override protected JsonObject doCallJson(Map<String, Object> options) { - final String uriAsParameters = (String) options.getOrDefault(URI_AS_PARAMETERS, "false"); final String brief = (String) options.getOrDefault(BRIEF, "false"); final JsonObject root = new JsonObject(); @@ -132,31 +114,11 @@ public class RouteDumpDevConsole extends AbstractDevConsole { } try { - String dump = null; - String format = (String) options.get(FORMAT); - if (format == null || "xml".equals(format)) { - jo.put("format", "xml"); - if ("true".equals(brief)) { - dump = mrb.dumpStructureRouteAsXml(); - } else { - dump = mrb.dumpRouteAsXml(true); - } - } else if ("yaml".equals(format)) { - jo.put("format", "yaml"); - if ("true".equals(brief)) { - dump = mrb.dumpStructureRouteAsYaml(); - } else { - dump = mrb.dumpRouteAsYaml(true, "true".equals(uriAsParameters)); - } - } else if ("text".equals(format)) { - dump = mrb.dumpStructureRouteAsText("true".equals(brief)); - } - if (dump != null) { - List<JsonObject> code = ConsoleHelper.loadSourceAsJson(new StringReader(dump), null); - if (code != null) { - jo.put("code", code); - } - } + ModelToStructureDumper dumper = PluginHelper.getModelToStructureDumper(getCamelContext()); + Route route = getCamelContext().getRoute(mrb.getRouteId()); + List<ModelDumpLine> lines = dumper.dumpStructure(getCamelContext(), route, "true".equalsIgnoreCase(brief)); + List<JsonObject> code = dumpAsJSon(lines); + jo.put("code", code); } catch (Exception e) { // ignore } @@ -183,7 +145,7 @@ public class RouteDumpDevConsole extends AbstractDevConsole { .filter(Objects::nonNull) .filter(r -> accept(r, filter)) .filter(r -> accept(r, subPath)) - .sorted(RouteDumpDevConsole::sort) + .sorted(RouteStructureDevConsole::sort) .limit(max) .forEach(task::apply); } @@ -206,4 +168,41 @@ public class RouteDumpDevConsole extends AbstractDevConsole { return o1.getRouteId().compareTo(o2.getRouteId()); } + private static List<JsonObject> dumpAsJSon(List<ModelDumpLine> lines) { + List<JsonObject> code = new ArrayList<>(); + int counter = 0; + for (var line : lines) { + counter++; + JsonObject c = new JsonObject(); + Integer idx = extractSourceLocationLineNumber(line.location()); + if (idx == null) { + idx = counter; + } + c.put("line", idx); + c.put("type", line.type()); + c.put("id", line.id()); + c.put("level", line.level()); + c.put("code", Jsoner.escape(line.code())); + code.add(c); + } + return code; + } + + private static Integer extractSourceLocationLineNumber(String location) { + int cnt = StringHelper.countChar(location, ':'); + if (cnt > 0) { + int pos = location.lastIndexOf(':'); + // in case pos is end of line + if (pos < location.length() - 1) { + String num = location.substring(pos + 1); + try { + return Integer.valueOf(num); + } catch (Exception e) { + return null; + } + } + } + return null; + } + } diff --git a/core/camel-core-engine/src/generated/resources/META-INF/services/org/apache/camel/modelstructure-dumper b/core/camel-core-engine/src/generated/resources/META-INF/services/org/apache/camel/modelstructure-dumper new file mode 100644 index 000000000000..a4f4667c775c --- /dev/null +++ b/core/camel-core-engine/src/generated/resources/META-INF/services/org/apache/camel/modelstructure-dumper @@ -0,0 +1,2 @@ +# Generated by camel build tools - do NOT edit this file! +class=org.apache.camel.impl.DefaultModelToStructureDumper diff --git a/core/camel-core-engine/src/main/java/org/apache/camel/impl/DefaultModelToStructureDumper.java b/core/camel-core-engine/src/main/java/org/apache/camel/impl/DefaultModelToStructureDumper.java new file mode 100644 index 000000000000..b430c8e313d8 --- /dev/null +++ b/core/camel-core-engine/src/main/java/org/apache/camel/impl/DefaultModelToStructureDumper.java @@ -0,0 +1,91 @@ +/* + * 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; + +import java.util.ArrayList; +import java.util.Comparator; +import java.util.List; +import java.util.Set; + +import javax.management.MBeanServer; +import javax.management.ObjectName; + +import org.apache.camel.CamelContext; +import org.apache.camel.Route; +import org.apache.camel.api.management.mbean.ManagedProcessorMBean; +import org.apache.camel.spi.ModelDumpLine; +import org.apache.camel.spi.ModelToStructureDumper; +import org.apache.camel.spi.annotations.JdkService; + +@JdkService(ModelToStructureDumper.FACTORY) +public class DefaultModelToStructureDumper implements ModelToStructureDumper { + + @Override + public List<ModelDumpLine> dumpStructure(CamelContext context, Route def, boolean brief) throws Exception { + List<ModelDumpLine> answer = new ArrayList<>(); + + String loc = def.getSourceLocationShort(); + answer.add(new ModelDumpLine(loc, "route", def.getRouteId(), 0, "route[" + def.getRouteId() + "]")); + String uri = brief ? def.getEndpoint().getEndpointBaseUri() : def.getEndpoint().getEndpointUri(); + answer.add(new ModelDumpLine(loc, "from", def.getRouteId(), 1, "from[" + uri + "]")); + + MBeanServer server = context.getManagementStrategy().getManagementAgent().getMBeanServer(); + if (server != null) { + String jmxDomain = context.getManagementStrategy().getManagementAgent().getMBeanObjectDomainName(); + // get all the processor mbeans and sort them accordingly to their index + String prefix = context.getManagementStrategy().getManagementAgent().getIncludeHostName() ? "*/" : ""; + ObjectName query = ObjectName.getInstance( + jmxDomain + ":context=" + prefix + context.getManagementName() + ",type=processors,*"); + Set<ObjectName> names = server.queryNames(query, null); + List<ManagedProcessorMBean> mps = new ArrayList<>(); + for (ObjectName on : names) { + ManagedProcessorMBean processor = context.getManagementStrategy().getManagementAgent().newProxyClient(on, + ManagedProcessorMBean.class); + // the processor must belong to this route + if (def.getRouteId().equals(processor.getRouteId())) { + mps.add(processor); + } + } + // sort by index + mps.sort(new OrderProcessorMBeans()); + + // dump in text format padded by level + for (ManagedProcessorMBean processor : mps) { + loc = processor.getSourceLocationShort(); + String kind = processor.getProcessorName(); + String id = processor.getProcessorId(); + int level = processor.getLevel() + 1; + String code = brief ? processor.getProcessorName() : processor.getModelLabel(); + answer.add(new ModelDumpLine(loc, kind, id, level, code)); + } + } + + return answer; + } + + /** + * Used for sorting the processor mbeans accordingly to their index. + */ + private static final class OrderProcessorMBeans implements Comparator<ManagedProcessorMBean> { + + @Override + public int compare(ManagedProcessorMBean o1, ManagedProcessorMBean o2) { + return o1.getIndex().compareTo(o2.getIndex()); + } + } + +} diff --git a/core/camel-management-api/src/main/java/org/apache/camel/api/management/mbean/ManagedCamelContextMBean.java b/core/camel-management-api/src/main/java/org/apache/camel/api/management/mbean/ManagedCamelContextMBean.java index 734b5151e493..c23394bbd767 100644 --- a/core/camel-management-api/src/main/java/org/apache/camel/api/management/mbean/ManagedCamelContextMBean.java +++ b/core/camel-management-api/src/main/java/org/apache/camel/api/management/mbean/ManagedCamelContextMBean.java @@ -237,15 +237,6 @@ public interface ManagedCamelContextMBean extends ManagedPerformanceCounterMBean @ManagedOperation(description = "Dumps the route templates as XML") String dumpRouteTemplatesAsXml() throws Exception; - @ManagedOperation(description = "Dumps the structure of routes as YAML") - String dumpStructureRoutesAsYaml() throws Exception; - - @ManagedOperation(description = "Dumps the structure of routes as XML") - String dumpStructureRoutesAsXml() throws Exception; - - @ManagedOperation(description = "Dumps the structure of routes as text") - String dumpStructureRoutesAsText(boolean brief) throws Exception; - @ManagedOperation(description = "Dumps the routes as YAML") String dumpRoutesAsYaml() throws Exception; diff --git a/core/camel-management-api/src/main/java/org/apache/camel/api/management/mbean/ManagedRouteMBean.java b/core/camel-management-api/src/main/java/org/apache/camel/api/management/mbean/ManagedRouteMBean.java index e1b97738e10e..32f0b6470a84 100644 --- a/core/camel-management-api/src/main/java/org/apache/camel/api/management/mbean/ManagedRouteMBean.java +++ b/core/camel-management-api/src/main/java/org/apache/camel/api/management/mbean/ManagedRouteMBean.java @@ -163,15 +163,6 @@ public interface ManagedRouteMBean extends ManagedPerformanceCounterMBean { @ManagedOperation(description = "Dumps the route with mappings between node ids and their source location/line-number (currently only XML and YAML routes supported) as XML") String dumpRouteSourceLocationsAsXml() throws Exception; - @ManagedOperation(description = "Dumps the structure of the route as XML") - String dumpStructureRouteAsXml() throws Exception; - - @ManagedOperation(description = "Dumps the structure of the route as YAML") - String dumpStructureRouteAsYaml() throws Exception; - - @ManagedOperation(description = "Dumps the structure of the route as text") - String dumpStructureRouteAsText(boolean brief) throws Exception; - @ManagedOperation(description = "Reset counters") void reset(boolean includeProcessors) throws Exception; diff --git a/core/camel-management/src/main/java/org/apache/camel/management/mbean/ManagedCamelContext.java b/core/camel-management/src/main/java/org/apache/camel/management/mbean/ManagedCamelContext.java index b6d599785962..3ba1d21bdf8e 100644 --- a/core/camel-management/src/main/java/org/apache/camel/management/mbean/ManagedCamelContext.java +++ b/core/camel-management/src/main/java/org/apache/camel/management/mbean/ManagedCamelContext.java @@ -585,55 +585,6 @@ public class ManagedCamelContext extends ManagedPerformanceCounter implements Ti return PluginHelper.getModelToXMLDumper(context).dumpModelAsXml(context, def, resolvePlaceholders, generatedIds); } - @Override - public String dumpStructureRoutesAsYaml() throws Exception { - List<RouteDefinition> routes = context.getCamelContextExtension().getContextPlugin(Model.class).getRouteDefinitions(); - if (routes.isEmpty()) { - return null; - } - - // use routes definition to dump the routes - RoutesDefinition def = new RoutesDefinition(); - def.setRoutes(routes); - - return PluginHelper.getModelToYAMLDumper(context).dumpStructureModelAsYaml(context, def); - } - - @Override - public String dumpStructureRoutesAsXml() throws Exception { - List<RouteDefinition> routes = context.getCamelContextExtension().getContextPlugin(Model.class).getRouteDefinitions(); - if (routes.isEmpty()) { - return null; - } - - // use routes definition to dump the routes - RoutesDefinition def = new RoutesDefinition(); - def.setRoutes(routes); - - return PluginHelper.getModelToXMLDumper(context).dumpStructureModelAsXml(context, def); - } - - @Override - public String dumpStructureRoutesAsText(boolean brief) throws Exception { - StringBuilder sb = new StringBuilder(); - MBeanServer server = getContext().getManagementStrategy().getManagementAgent().getMBeanServer(); - if (server != null) { - // gather all the routes for this CamelContext, which requires JMX - String prefix = getContext().getManagementStrategy().getManagementAgent().getIncludeHostName() ? "*/" : ""; - ObjectName query = ObjectName - .getInstance(jmxDomain + ":context=" + prefix + getContext().getManagementName() + ",type=routes,*"); - Set<ObjectName> routes = server.queryNames(query, null); - for (ObjectName on : routes) { - ManagedRouteMBean route - = context.getManagementStrategy().getManagementAgent().newProxyClient(on, ManagedRouteMBean.class); - String dump = route.dumpStructureRouteAsText(brief); - sb.append(dump); - sb.append("\n"); - } - } - return sb.toString(); - } - @Override public String dumpRoutesAsYaml() throws Exception { return dumpRoutesAsYaml(false, false); diff --git a/core/camel-management/src/main/java/org/apache/camel/management/mbean/ManagedRoute.java b/core/camel-management/src/main/java/org/apache/camel/management/mbean/ManagedRoute.java index 37b177277ef6..eecd4a03a659 100644 --- a/core/camel-management/src/main/java/org/apache/camel/management/mbean/ManagedRoute.java +++ b/core/camel-management/src/main/java/org/apache/camel/management/mbean/ManagedRoute.java @@ -64,7 +64,6 @@ import org.apache.camel.spi.ManagementStrategy; import org.apache.camel.spi.RoutePolicy; import org.apache.camel.support.PluginHelper; import org.apache.camel.util.ObjectHelper; -import org.apache.camel.util.StringHelper; import org.apache.camel.util.json.JsonArray; import org.apache.camel.util.json.JsonObject; import org.apache.camel.xml.LwModelHelper; @@ -460,74 +459,6 @@ public class ManagedRoute extends ManagedPerformanceCounter implements TimerList return null; } - @Override - public String dumpStructureRouteAsXml() throws Exception { - String id = route.getId(); - RouteDefinition def = context.getCamelContextExtension().getContextPlugin(Model.class).getRouteDefinition(id); - if (def != null) { - return PluginHelper.getModelToXMLDumper(context).dumpStructureModelAsXml(context, def); - } - - return null; - } - - @Override - public String dumpStructureRouteAsYaml() throws Exception { - String id = route.getId(); - RouteDefinition def = context.getCamelContextExtension().getContextPlugin(Model.class).getRouteDefinition(id); - if (def != null) { - return PluginHelper.getModelToYAMLDumper(context).dumpStructureModelAsYaml(context, def); - } - - return null; - } - - @Override - public String dumpStructureRouteAsText(boolean brief) throws Exception { - StringBuilder sb = new StringBuilder(); - sb.append("route[").append(route.getRouteId()).append("]\n"); - if (brief) { - sb.append(" from[").append(route.getEndpoint().getEndpointBaseUri()).append("]\n"); - } else { - sb.append(" from[").append(route.getEndpoint()).append("]\n"); - } - - MBeanServer server = getContext().getManagementStrategy().getManagementAgent().getMBeanServer(); - if (server != null) { - // get all the processor mbeans and sort them accordingly to their index - String prefix = getContext().getManagementStrategy().getManagementAgent().getIncludeHostName() ? "*/" : ""; - ObjectName query = ObjectName.getInstance( - jmxDomain + ":context=" + prefix + getContext().getManagementName() + ",type=processors,*"); - Set<ObjectName> names = server.queryNames(query, null); - List<ManagedProcessorMBean> mps = new ArrayList<>(); - for (ObjectName on : names) { - ManagedProcessorMBean processor = context.getManagementStrategy().getManagementAgent().newProxyClient(on, - ManagedProcessorMBean.class); - // the processor must belong to this route - if (getRouteId().equals(processor.getRouteId())) { - mps.add(processor); - } - } - // sort by index - mps.sort(new OrderProcessorMBeans()); - - // dump in text format padded by level - for (ManagedProcessorMBean processor : mps) { - int index = processor.getLevel() + 1; - String pad = StringHelper.padString(index); - sb.append(pad); - if (brief) { - sb.append(processor.getProcessorName()); - } else { - sb.append(processor.getModelLabel()); - } - sb.append("\n"); - } - } - - return sb.toString(); - } - @Override public String dumpRouteAsYaml() throws Exception { return dumpRouteAsYaml(false, false); diff --git a/core/camel-management/src/test/java/org/apache/camel/management/DumpRouteStructureTest.java b/core/camel-management/src/test/java/org/apache/camel/management/DumpRouteStructureTest.java new file mode 100644 index 000000000000..ba221c4b1494 --- /dev/null +++ b/core/camel-management/src/test/java/org/apache/camel/management/DumpRouteStructureTest.java @@ -0,0 +1,103 @@ +/* + * 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.management; + +import java.util.List; + +import org.apache.camel.builder.RouteBuilder; +import org.apache.camel.spi.ModelDumpLine; +import org.apache.camel.spi.ModelToStructureDumper; +import org.apache.camel.support.PluginHelper; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.condition.DisabledOnOs; +import org.junit.jupiter.api.condition.OS; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +@DisabledOnOs(OS.AIX) +public class DumpRouteStructureTest extends ManagementTestSupport { + + @Test + public void testDump() throws Exception { + ModelToStructureDumper dumper = PluginHelper.getModelToStructureDumper(context); + List<ModelDumpLine> lines = dumper.dumpStructure(context, context.getRoute("myOtherRoute"), false); + assertEquals(5, lines.size()); + assertEquals(0, lines.get(0).level()); + assertEquals("route", lines.get(0).type()); + assertEquals("myOtherRoute", lines.get(0).id()); + assertEquals("route[myOtherRoute]", lines.get(0).code()); + assertEquals(1, lines.get(1).level()); + assertEquals("from", lines.get(1).type()); + assertEquals("myOtherRoute", lines.get(1).id()); + assertEquals("from[seda://bar?multipleConsumers=true&size=1234]", lines.get(1).code()); + assertEquals(2, lines.get(2).level()); + assertEquals("filter", lines.get(2).type()); + assertEquals("myfilter", lines.get(2).id()); + assertEquals("filter[header{bar}]", lines.get(2).code()); + assertEquals(3, lines.get(3).level()); + assertEquals("to", lines.get(3).type()); + assertEquals("mybar", lines.get(3).id()); + assertEquals("to[mock:bar]", lines.get(3).code()); + assertEquals(2, lines.get(4).level()); + assertEquals("to", lines.get(4).type()); + assertEquals("myend", lines.get(4).id()); + assertEquals("to[log:end]", lines.get(4).code()); + } + + @Test + public void testDumpBrief() throws Exception { + ModelToStructureDumper dumper = PluginHelper.getModelToStructureDumper(context); + List<ModelDumpLine> lines = dumper.dumpStructure(context, context.getRoute("myOtherRoute"), true); + assertEquals(5, lines.size()); + assertEquals(0, lines.get(0).level()); + assertEquals("route", lines.get(0).type()); + assertEquals("myOtherRoute", lines.get(0).id()); + assertEquals("route[myOtherRoute]", lines.get(0).code()); + assertEquals(1, lines.get(1).level()); + assertEquals("from", lines.get(1).type()); + assertEquals("myOtherRoute", lines.get(1).id()); + assertEquals("from[seda://bar]", lines.get(1).code()); + assertEquals(2, lines.get(2).level()); + assertEquals("filter", lines.get(2).type()); + assertEquals("myfilter", lines.get(2).id()); + assertEquals("filter", lines.get(2).code()); + assertEquals(3, lines.get(3).level()); + assertEquals("to", lines.get(3).type()); + assertEquals("mybar", lines.get(3).id()); + assertEquals("to", lines.get(3).code()); + assertEquals(2, lines.get(4).level()); + assertEquals("to", lines.get(4).type()); + assertEquals("myend", lines.get(4).id()); + assertEquals("to", lines.get(4).code()); + } + + protected RouteBuilder createRouteBuilder() { + return new RouteBuilder() { + @Override + public void configure() { + context.setDebugging(true); + + from("seda:bar?size=1234&multipleConsumers=true").routeId("myOtherRoute") + .filter().header("bar").id("myfilter") + .to("mock:bar").id("mybar") + .end() + .to("log:end").id("myend"); + } + }; + } + +} diff --git a/core/camel-management/src/test/java/org/apache/camel/management/ManagedCamelContextDumpRouteStructureAsTextTest.java b/core/camel-management/src/test/java/org/apache/camel/management/ManagedCamelContextDumpRouteStructureAsTextTest.java deleted file mode 100644 index a6ccb0502629..000000000000 --- a/core/camel-management/src/test/java/org/apache/camel/management/ManagedCamelContextDumpRouteStructureAsTextTest.java +++ /dev/null @@ -1,86 +0,0 @@ -/* - * 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.management; - -import javax.management.MBeanServer; -import javax.management.ObjectName; - -import org.apache.camel.builder.RouteBuilder; -import org.junit.jupiter.api.Test; -import org.junit.jupiter.api.condition.DisabledOnOs; -import org.junit.jupiter.api.condition.OS; - -import static org.junit.jupiter.api.Assertions.assertFalse; -import static org.junit.jupiter.api.Assertions.assertNotNull; -import static org.junit.jupiter.api.Assertions.assertTrue; - -@DisabledOnOs(OS.AIX) -public class ManagedCamelContextDumpRouteStructureAsTextTest extends ManagementTestSupport { - - @Test - public void testDumpAsText() throws Exception { - MBeanServer mbeanServer = getMBeanServer(); - - ObjectName on = getContextObjectName(); - - String text = (String) mbeanServer.invoke(on, "dumpStructureRoutesAsText", new Object[] { false }, - new String[] { "boolean" }); - assertNotNull(text); - log.info(text); - - assertTrue(text.contains("route[myRoute]")); - assertTrue(text.contains("to[mock:result]")); - assertTrue(text.contains("filter[header{bar}]")); - } - - @Test - public void testDumpAsTextBrief() throws Exception { - MBeanServer mbeanServer = getMBeanServer(); - - ObjectName on = getContextObjectName(); - - String text = (String) mbeanServer.invoke(on, "dumpStructureRoutesAsText", new Object[] { true }, - new String[] { "boolean" }); - assertNotNull(text); - log.info(text); - - assertTrue(text.contains("route[myRoute]")); - assertFalse(text.contains("to[mock:result]")); - assertTrue(text.contains("to")); - assertFalse(text.contains("filter[header{bar}]")); - assertTrue(text.contains("filter")); - } - - protected RouteBuilder createRouteBuilder() { - return new RouteBuilder() { - @Override - public void configure() { - context.setDebugging(true); - - from("direct:start").routeId("myRoute") - .log("Got ${body}") - .to("mock:result"); - - from("seda:bar?size=1234&multipleConsumers=true").routeId("myOtherRoute") - .filter().header("bar") - .to("mock:bar") - .end(); - } - }; - } - -} diff --git a/core/camel-management/src/test/java/org/apache/camel/management/ManagedCamelContextDumpRouteStructureAsXmlTest.java b/core/camel-management/src/test/java/org/apache/camel/management/ManagedCamelContextDumpRouteStructureAsXmlTest.java deleted file mode 100644 index 7d56a419b9d4..000000000000 --- a/core/camel-management/src/test/java/org/apache/camel/management/ManagedCamelContextDumpRouteStructureAsXmlTest.java +++ /dev/null @@ -1,89 +0,0 @@ -/* - * 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.management; - -import javax.management.MBeanServer; -import javax.management.ObjectName; - -import org.apache.camel.builder.RouteBuilder; -import org.apache.camel.model.RoutesDefinition; -import org.apache.camel.xml.jaxb.JaxbModelToXMLDumper; -import org.junit.jupiter.api.Assertions; -import org.junit.jupiter.api.Test; -import org.junit.jupiter.api.condition.DisabledOnOs; -import org.junit.jupiter.api.condition.OS; - -import static org.junit.jupiter.api.Assertions.assertFalse; -import static org.junit.jupiter.api.Assertions.assertNotNull; -import static org.junit.jupiter.api.Assertions.assertTrue; - -@DisabledOnOs(OS.AIX) -public class ManagedCamelContextDumpRouteStructureAsXmlTest extends ManagementTestSupport { - - @Test - public void testDumpAsXml() throws Exception { - MBeanServer mbeanServer = getMBeanServer(); - - ObjectName on = getContextObjectName(); - - String xml = (String) mbeanServer.invoke(on, "dumpStructureRoutesAsXml", null, null); - assertNotNull(xml); - log.info(xml); - - assertTrue(xml.contains("route")); - assertTrue(xml.contains("myRoute")); - assertTrue(xml.contains("myOtherRoute")); - assertTrue(xml.contains("direct:start")); - assertTrue(xml.contains("mock:result")); - assertTrue(xml.contains("seda:bar?size=1234&multipleConsumers=true")); - assertTrue(xml.contains("mock:bar")); - assertTrue(xml.contains("message")); - assertTrue(xml.contains("sourceLineNumber")); - assertTrue(xml.contains("sourceLocation")); - assertFalse(xml.contains("<header>")); - } - - @Test - public void testDumpAsXmlUsingJaxb() { - JaxbModelToXMLDumper dumper = new JaxbModelToXMLDumper(); - RoutesDefinition def = new RoutesDefinition(); - def.setRoutes(context.getRouteDefinitions()); - Assertions.assertThrows(UnsupportedOperationException.class, () -> { - dumper.dumpStructureModelAsXml(context, def); - }); - } - - @Override - protected RouteBuilder createRouteBuilder() { - return new RouteBuilder() { - @Override - public void configure() { - context.setDebugging(true); - - from("direct:start").routeId("myRoute") - .log("Got ${body}") - .to("mock:result"); - - from("seda:bar?size=1234&multipleConsumers=true").routeId("myOtherRoute") - .filter().header("bar") - .to("mock:bar") - .end(); - } - }; - } - -} diff --git a/core/camel-management/src/test/java/org/apache/camel/management/ManagedCamelContextDumpRouteStructureAsYamlTest.java b/core/camel-management/src/test/java/org/apache/camel/management/ManagedCamelContextDumpRouteStructureAsYamlTest.java deleted file mode 100644 index c21a79a9b8f9..000000000000 --- a/core/camel-management/src/test/java/org/apache/camel/management/ManagedCamelContextDumpRouteStructureAsYamlTest.java +++ /dev/null @@ -1,74 +0,0 @@ -/* - * 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.management; - -import javax.management.MBeanServer; -import javax.management.ObjectName; - -import org.apache.camel.builder.RouteBuilder; -import org.junit.jupiter.api.Test; -import org.junit.jupiter.api.condition.DisabledOnOs; -import org.junit.jupiter.api.condition.OS; - -import static org.junit.jupiter.api.Assertions.assertNotNull; -import static org.junit.jupiter.api.Assertions.assertTrue; - -@DisabledOnOs(OS.AIX) -public class ManagedCamelContextDumpRouteStructureAsYamlTest extends ManagementTestSupport { - - @Test - public void testDumpAsYaml() throws Exception { - MBeanServer mbeanServer = getMBeanServer(); - - ObjectName on = getContextObjectName(); - - String yaml = (String) mbeanServer.invoke(on, "dumpStructureRoutesAsYaml", null, null); - assertNotNull(yaml); - log.info(yaml); - - assertTrue(yaml.contains("route")); - assertTrue(yaml.contains("myRoute")); - assertTrue(yaml.contains("myOtherRoute")); - assertTrue(yaml.contains("direct:start")); - assertTrue(yaml.contains("mock:result")); - assertTrue(yaml.contains("seda:bar?size=1234&multipleConsumers=true")); - assertTrue(yaml.contains("mock:bar")); - assertTrue(yaml.contains("message")); - assertTrue(yaml.contains("sourceLineNumber")); - assertTrue(yaml.contains("sourceLocation")); - } - - @Override - protected RouteBuilder createRouteBuilder() { - return new RouteBuilder() { - @Override - public void configure() { - context.setDebugging(true); - - from("direct:start").routeId("myRoute") - .log("Got ${body}") - .to("mock:result"); - - from("seda:bar?size=1234&multipleConsumers=true").routeId("myOtherRoute") - .filter().header("bar") - .to("mock:bar") - .end(); - } - }; - } - -} diff --git a/core/camel-support/src/main/java/org/apache/camel/support/PluginHelper.java b/core/camel-support/src/main/java/org/apache/camel/support/PluginHelper.java index 3de356481544..1ee27042bfa9 100644 --- a/core/camel-support/src/main/java/org/apache/camel/support/PluginHelper.java +++ b/core/camel-support/src/main/java/org/apache/camel/support/PluginHelper.java @@ -41,6 +41,7 @@ import org.apache.camel.spi.InterceptEndpointFactory; import org.apache.camel.spi.InternalProcessorFactory; import org.apache.camel.spi.LanguageResolver; import org.apache.camel.spi.ModelJAXBContextFactory; +import org.apache.camel.spi.ModelToStructureDumper; import org.apache.camel.spi.ModelToXMLDumper; import org.apache.camel.spi.ModelToYAMLDumper; import org.apache.camel.spi.ModelineFactory; @@ -565,6 +566,20 @@ public final class PluginHelper { return extendedCamelContext.getContextPlugin(ModelToYAMLDumper.class); } + /** + * Gets the {@link ModelToStructureDumper} to be used. + */ + public static ModelToStructureDumper getModelToStructureDumper(CamelContext camelContext) { + return getModelToStructureDumper(camelContext.getCamelContextExtension()); + } + + /** + * Gets the {@link ModelToStructureDumper} to be used. + */ + public static ModelToStructureDumper getModelToStructureDumper(ExtendedCamelContext extendedCamelContext) { + return extendedCamelContext.getContextPlugin(ModelToStructureDumper.class); + } + /** * Gets the {@link DeferServiceFactory} to use. */ 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 a66d776a16eb..b063234783e4 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 @@ -26,6 +26,8 @@ import java.util.List; import java.util.Map; import java.util.Properties; import java.util.function.Consumer; +import java.util.regex.Matcher; +import java.util.regex.Pattern; import org.apache.camel.CamelContext; import org.apache.camel.CamelContextAware; @@ -45,6 +47,7 @@ import org.apache.camel.model.SendDefinition; import org.apache.camel.model.ToDynamicDefinition; import org.apache.camel.model.dataformat.DataFormatsDefinition; import org.apache.camel.model.language.ExpressionDefinition; +import org.apache.camel.spi.ModelDumpLine; import org.apache.camel.spi.ModelToXMLDumper; import org.apache.camel.spi.NamespaceAware; import org.apache.camel.spi.annotations.JdkService; @@ -60,6 +63,8 @@ import static org.apache.camel.model.ProcessorDefinitionHelper.filterTypeInOutpu @JdkService(ModelToXMLDumper.FACTORY) public class LwModelToXMLDumper implements ModelToXMLDumper { + private static final Pattern SOURCE_LOCATION_PATTERN = Pattern.compile("(\\ssourceLocation=\"(.*?)\")"); + @Override public String dumpModelAsXml(CamelContext context, NamedNode definition) throws Exception { return dumpModelAsXml(context, definition, false, true); @@ -472,52 +477,70 @@ public class LwModelToXMLDumper implements ModelToXMLDumper { } } - @Override - public String dumpStructureModelAsXml(CamelContext context, NamedNode definition) throws Exception { - StringWriter buffer = new StringWriter(); + // TODO: Remove me + public List<ModelDumpLine> dumpStructureModel(CamelContext context, NamedNode definition) throws Exception { + List<ModelDumpLine> answer = new ArrayList<>(); + doDumpStructureModelAsXml(answer, context, definition); + return answer; + } + + protected void doDumpStructureModelAsXml(List<ModelDumpLine> answer, CamelContext context, NamedNode definition) + throws Exception { + final StringWriter buffer = new StringWriter(); ModelWriter writer = new ModelWriter(buffer, BaseWriter.DEFAULT_NAMESPACE) { - @Override protected void doWriteOptionalIdentifiedDefinitionAttributes(OptionalIdentifiedDefinition<?> def) throws IOException { - if (Boolean.TRUE.equals(def.getCustomId())) { // write custom id doWriteAttribute("id", def.getId()); } - // write location information - if (context.isDebugging()) { - String loc = (def instanceof RouteDefinition ? ((RouteDefinition) def).getInput() : def).getLocation(); - int line = (def instanceof RouteDefinition ? ((RouteDefinition) def).getInput() : def).getLineNumber(); - if (line != -1) { - writer.addAttribute("sourceLineNumber", Integer.toString(line)); - writer.addAttribute("sourceLocation", loc); - } + + // remember source locations + String loc = (def instanceof RouteDefinition ? ((RouteDefinition) def).getInput() : def).getLocation(); + int line = (def instanceof RouteDefinition ? ((RouteDefinition) def).getInput() : def).getLineNumber(); + if (line != -1) { + doWriteAttribute("sourceLocation", loc + ":" + line); } + // we do not want any other attributes } protected void doWriteExpressionNodeElements(ExpressionNode def) throws IOException { // do not include expressions in brief mode } - @Override - protected void doWriteValue(String value) throws IOException { - if (value != null && !value.isEmpty()) { - super.doWriteValue(value); - } - } - @Override protected void attribute(String name, Object value) throws IOException { - // limit what we want to see in brief mode - if ("id".equals(name) || "uri".equals(name) || "message".equals(name)) { - super.attribute(name, value); + // limit what we want to see in structure mode + if ("sourceLocation".equals(name) || "id".equals(name) || "uri".equals(name) || "message".equals(name)) { + String s = value != null ? value.toString() : null; + // clip long text + if (s != null && s.length() > 80) { + s = s.substring(0, 80) + " ... (clipped value)"; + } + super.attribute(name, s); } } }; - writer.writeOptionalIdentifiedDefinitionRef((OptionalIdentifiedDefinition) definition); - - return buffer.toString(); + writer.setEmptyTagNewLine(true); + writer.writeOptionalIdentifiedDefinitionRef((OptionalIdentifiedDefinition<?>) definition); + + String data = buffer.toString(); + for (String line : data.split(System.lineSeparator())) { + if (!line.isBlank()) { + String loc = null; + Matcher m = SOURCE_LOCATION_PATTERN.matcher(line); + if (m.find()) { + loc = m.group(2); + line = m.replaceAll(""); + } + // fake no line number + if (loc == null) { + loc = ":-1"; + } + answer.add(new ModelDumpLine(loc, null, null, 0, line)); + } + } } } diff --git a/core/camel-xml-io/src/main/java/org/apache/camel/xml/io/XMLWriter.java b/core/camel-xml-io/src/main/java/org/apache/camel/xml/io/XMLWriter.java index 51d67d259d4b..64ca776b753e 100644 --- a/core/camel-xml-io/src/main/java/org/apache/camel/xml/io/XMLWriter.java +++ b/core/camel-xml-io/src/main/java/org/apache/camel/xml/io/XMLWriter.java @@ -39,6 +39,7 @@ public class XMLWriter { private int depth; private boolean readyForNewLine; private boolean tagIsEmpty; + private boolean emptyTagNewLine; /** * @param writer not null @@ -93,6 +94,10 @@ public class XMLWriter { } } + public void setEmptyTagNewLine(boolean emptyTagNewLine) { + this.emptyTagNewLine = emptyTagNewLine; + } + private static String validateLineSeparator(String lineSeparator) { String ls = lineSeparator != null ? lineSeparator : System.lineSeparator(); if (!(ls.equals("\n") || ls.equals("\r") || ls.equals("\r\n"))) { @@ -101,9 +106,6 @@ public class XMLWriter { return ls; } - /** - * {@inheritDoc} - */ public void startElement(String name) throws IOException { tagIsEmpty = false; finishTag(); @@ -116,16 +118,10 @@ public class XMLWriter { tagIsEmpty = true; } - /** - * {@inheritDoc} - */ public void writeText(String text) throws IOException { writeText(text, true); } - /** - * {@inheritDoc} - */ public void writeMarkup(String text) throws IOException { writeText(text, false); } @@ -187,9 +183,6 @@ public class XMLWriter { return text; } - /** - * {@inheritDoc} - */ public void addAttribute(String key, String value) throws IOException { write(" "); write(key); @@ -198,15 +191,12 @@ public class XMLWriter { write("\""); } - /** - * {@inheritDoc} - */ public void endElement(String name) throws IOException { depth--; if (tagIsEmpty) { write("/"); - readyForNewLine = false; + readyForNewLine = emptyTagNewLine ? true : false; finishTag(); elements.removeLast(); } else { diff --git a/core/camel-xml-io/src/main/java/org/apache/camel/xml/out/BaseWriter.java b/core/camel-xml-io/src/main/java/org/apache/camel/xml/out/BaseWriter.java index 4f1e559f9da8..2cfc68583e95 100644 --- a/core/camel-xml-io/src/main/java/org/apache/camel/xml/out/BaseWriter.java +++ b/core/camel-xml-io/src/main/java/org/apache/camel/xml/out/BaseWriter.java @@ -35,8 +35,8 @@ public class BaseWriter { public static final String DEFAULT_NAMESPACE = "http://camel.apache.org/schema/xml-io"; - protected final XMLWriter writer; protected final Deque<String> namespacesStack = new LinkedList<>(); + protected final XMLWriter writer; protected boolean namespaceWritten; protected boolean skipCustomId = true; @@ -49,6 +49,10 @@ public class BaseWriter { } } + public void setEmptyTagNewLine(boolean emptyTagNewLine) { + writer.setEmptyTagNewLine(emptyTagNewLine); + } + protected void startElement(String name) throws IOException { writer.startElement(name); if (!namespaceWritten && namespacesStack.peek() != null) { diff --git a/core/camel-xml-jaxb/src/main/java/org/apache/camel/xml/jaxb/JaxbModelToXMLDumper.java b/core/camel-xml-jaxb/src/main/java/org/apache/camel/xml/jaxb/JaxbModelToXMLDumper.java index cadc02810729..f7c64e45eb47 100644 --- a/core/camel-xml-jaxb/src/main/java/org/apache/camel/xml/jaxb/JaxbModelToXMLDumper.java +++ b/core/camel-xml-jaxb/src/main/java/org/apache/camel/xml/jaxb/JaxbModelToXMLDumper.java @@ -442,9 +442,4 @@ public class JaxbModelToXMLDumper implements ModelToXMLDumper { } } - @Override - public String dumpStructureModelAsXml(CamelContext context, NamedNode definition) throws Exception { - throw new UnsupportedOperationException( - "dumpStructureModelAsXml is not supported by camel-xml-jaxb. Use camel-xml-io instead."); - } } 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 b9b4f172912b..f5a24b09900b 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 @@ -448,59 +448,4 @@ public class LwModelToYAMLDumper implements ModelToYAMLDumper { } } - @Override - public String dumpStructureModelAsYaml(CamelContext context, NamedNode definition) throws Exception { - StringWriter buffer = new StringWriter(); - ModelWriter writer = new ModelWriter(buffer) { - @Override - protected void doWriteOptionalIdentifiedDefinitionAttributes(OptionalIdentifiedDefinition<?> def) - throws IOException { - - if (Boolean.TRUE.equals(def.getCustomId())) { - // write custom id - doWriteAttribute("id", def.getId()); - } - // write location information - if (context.isDebugging()) { - String loc = (def instanceof RouteDefinition ? ((RouteDefinition) def).getInput() : def).getLocation(); - int line = (def instanceof RouteDefinition ? ((RouteDefinition) def).getInput() : def).getLineNumber(); - if (line != -1) { - writer.addAttribute("sourceLineNumber", Integer.toString(line)); - writer.addAttribute("sourceLocation", loc); - } - } - } - - protected void doWriteExpressionNodeElements(ExpressionNode def) throws IOException { - // do not include expressions in brief mode - } - - @Override - protected void doWriteValue(String value) throws IOException { - if (value != null && !value.isEmpty()) { - super.doWriteValue(value); - } - } - - @Override - protected void attribute(String name, Object value) throws IOException { - // limit what we want to see in brief mode - if ("id".equals(name) || "uri".equals(name) || "message".equals(name)) { - super.attribute(name, value); - } - } - }; - - writer.setUriAsParameters(false); - writer.setCamelContext(context); - writer.start(); - try { - writer.writeOptionalIdentifiedDefinitionRef((OptionalIdentifiedDefinition) definition); - } finally { - writer.stop(); - } - - return buffer.toString(); - } - } diff --git a/dsl/camel-cli-connector/src/main/java/org/apache/camel/cli/connector/LocalCliConnector.java b/dsl/camel-cli-connector/src/main/java/org/apache/camel/cli/connector/LocalCliConnector.java index 4230f7194055..64b8c7352ab3 100644 --- a/dsl/camel-cli-connector/src/main/java/org/apache/camel/cli/connector/LocalCliConnector.java +++ b/dsl/camel-cli-connector/src/main/java/org/apache/camel/cli/connector/LocalCliConnector.java @@ -608,11 +608,10 @@ public class LocalCliConnector extends ServiceSupport implements CliConnector, C if (dc != null) { String filter = root.getString("filter"); String format = root.getString("format"); - String brief = root.getString("brief"); String uriAsParameters = root.getString("uriAsParameters"); JsonObject json = (JsonObject) dc.call(DevConsole.MediaType.JSON, - Map.of("filter", filter, "format", format, "uriAsParameters", uriAsParameters, "brief", brief)); + Map.of("filter", filter, "format", format, "uriAsParameters", uriAsParameters)); LOG.trace("Updating output file: {}", outputFile); IOHelper.writeText(json.toJson(), outputFile); } else { diff --git a/dsl/camel-jbang/camel-jbang-core/src/main/java/org/apache/camel/dsl/jbang/core/commands/action/CamelRouteDumpAction.java b/dsl/camel-jbang/camel-jbang-core/src/main/java/org/apache/camel/dsl/jbang/core/commands/action/CamelRouteDumpAction.java index b4ba37902e94..4919e997dd30 100644 --- a/dsl/camel-jbang/camel-jbang-core/src/main/java/org/apache/camel/dsl/jbang/core/commands/action/CamelRouteDumpAction.java +++ b/dsl/camel-jbang/camel-jbang-core/src/main/java/org/apache/camel/dsl/jbang/core/commands/action/CamelRouteDumpAction.java @@ -58,7 +58,7 @@ public class CamelRouteDumpAction extends ActionBaseCommand { @Override public Iterator<String> iterator() { - return List.of("yaml", "xml", "text").iterator(); + return List.of("yaml", "xml").iterator(); } } @@ -67,17 +67,13 @@ public class CamelRouteDumpAction extends ActionBaseCommand { String name = "*"; @CommandLine.Option(names = { "--format" }, completionCandidates = FormatCompletionCandidates.class, - description = "Output format (xml, yaml, or text)", defaultValue = "yaml") + description = "Output format (xml, or yaml)", defaultValue = "yaml") String format; @CommandLine.Option(names = { "--raw" }, description = "To output raw without metadata") boolean raw; - @CommandLine.Option(names = { "--brief" }, - description = "To output route structure only (without options) and only applicable for xml or yaml format") - boolean brief; - @CommandLine.Option(names = { "--uri-as-parameters" }, description = "Whether to expand URIs into separated key/value parameters (only in use for YAML format)", defaultValue = "true") @@ -120,7 +116,6 @@ public class CamelRouteDumpAction extends ActionBaseCommand { root.put("action", "route-dump"); root.put("filter", "*"); root.put("format", format); - root.put("brief", brief); root.put("uriAsParameters", uriAsParameters); Path file = getActionFile(Long.toString(pid)); try { @@ -220,7 +215,11 @@ public class CamelRouteDumpAction extends ActionBaseCommand { if (raw) { printer().printf("%s%n", c); } else { - printer().printf("%4d: %s%n", code.line, c); + if (code.line != -1) { + printer().printf("%4d: %s%n", code.line, c); + } else { + printer().printf(" %s%n", c); + } } } printer().println();
