This is an automated email from the ASF dual-hosted git repository. davsclaus pushed a commit to branch dia3 in repository https://gitbox.apache.org/repos/asf/camel.git
commit 45fec41203d0ac127fb8e46edbbe399b363044fa Author: Claus Ibsen <[email protected]> AuthorDate: Thu May 7 18:24:49 2026 +0200 CAMEL-23400: camel-diagram - prepare for being used in maven tooling and also as SPI in camel-core --- .../apache/camel/diagram/RouteDiagramHelper.java | 20 ++---- .../org/apache/camel/diagram/RouteDiagramTest.java | 4 +- .../main/java/org/apache/camel/CamelContext.java | 5 ++ .../src/main/java/org/apache/camel/NamedRoute.java | 12 ++++ .../camel/impl/engine/AbstractCamelContext.java | 13 ++-- .../camel/impl/engine/SimpleCamelContext.java | 7 ++ .../impl/console/RouteStructureDevConsole.java | 75 +++++++++------------- .../org/apache/camel/impl/DefaultCamelContext.java | 6 ++ .../org/apache/camel/support/LoggerHelper.java | 28 +++++++- 9 files changed, 101 insertions(+), 69 deletions(-) diff --git a/components/camel-diagram/src/main/java/org/apache/camel/diagram/RouteDiagramHelper.java b/components/camel-diagram/src/main/java/org/apache/camel/diagram/RouteDiagramHelper.java index eaff690ceb4b..5a3fe8b5028e 100644 --- a/components/camel-diagram/src/main/java/org/apache/camel/diagram/RouteDiagramHelper.java +++ b/components/camel-diagram/src/main/java/org/apache/camel/diagram/RouteDiagramHelper.java @@ -21,6 +21,8 @@ import java.util.List; import org.apache.camel.diagram.RouteDiagramLayoutEngine.NodeInfo; import org.apache.camel.diagram.RouteDiagramLayoutEngine.RouteInfo; +import org.apache.camel.support.LoggerHelper; +import org.apache.camel.util.FileUtil; import org.apache.camel.util.json.JsonArray; import org.apache.camel.util.json.JsonObject; import org.apache.camel.util.json.Jsoner; @@ -86,21 +88,9 @@ public final class RouteDiagramHelper { if (source == null || source.isBlank()) { return null; } - // strip URI scheme prefix (e.g. "file:", "classpath:") — only if the part - // before the first colon is all letters (a valid scheme). This avoids - // stripping line numbers from sources like "cheese.java:9". - int colon = source.indexOf(':'); - if (colon > 0) { - String scheme = source.substring(0, colon); - if (scheme.chars().allMatch(Character::isLetter)) { - source = source.substring(colon + 1); - } - } - // return just the filename part - int slash = Math.max(source.lastIndexOf('/'), source.lastIndexOf('\\')); - if (slash >= 0 && slash < source.length() - 1) { - return source.substring(slash + 1); - } + source = LoggerHelper.sourceNameOnly(source); + source = LoggerHelper.stripScheme(source); + source = FileUtil.stripPath(source); return source; } } diff --git a/components/camel-diagram/src/test/java/org/apache/camel/diagram/RouteDiagramTest.java b/components/camel-diagram/src/test/java/org/apache/camel/diagram/RouteDiagramTest.java index a7ea8029bdd5..4f5e5cd86dca 100644 --- a/components/camel-diagram/src/test/java/org/apache/camel/diagram/RouteDiagramTest.java +++ b/components/camel-diagram/src/test/java/org/apache/camel/diagram/RouteDiagramTest.java @@ -818,12 +818,12 @@ class RouteDiagramTest { @Test void testExtractSourceNameWithLineNumber() { - assertEquals("cheese.java:9", RouteDiagramHelper.extractSourceName("cheese.java:9")); + assertEquals("cheese.java", RouteDiagramHelper.extractSourceName("cheese.java:9")); } @Test void testExtractSourceNameSchemeAndLineNumber() { - assertEquals("cheese.java:9", RouteDiagramHelper.extractSourceName("file:/path/to/cheese.java:9")); + assertEquals("cheese.java", RouteDiagramHelper.extractSourceName("file:/path/to/cheese.java:9")); } @Test diff --git a/core/camel-api/src/main/java/org/apache/camel/CamelContext.java b/core/camel-api/src/main/java/org/apache/camel/CamelContext.java index 46700aa30751..7f8ebdc2ac0e 100644 --- a/core/camel-api/src/main/java/org/apache/camel/CamelContext.java +++ b/core/camel-api/src/main/java/org/apache/camel/CamelContext.java @@ -790,6 +790,11 @@ public interface CamelContext extends CamelContextLifecycle, RuntimeConfiguratio */ List<RoutePolicyFactory> getRoutePolicyFactories(); + /** + * Gets a light-weight API for the route model defunitions. + */ + List<NamedRoute> getNamedRouteDefinitions(); + // Rest Methods //----------------------------------------------------------------------- diff --git a/core/camel-api/src/main/java/org/apache/camel/NamedRoute.java b/core/camel-api/src/main/java/org/apache/camel/NamedRoute.java index 047cd99be2db..017123bab68a 100644 --- a/core/camel-api/src/main/java/org/apache/camel/NamedRoute.java +++ b/core/camel-api/src/main/java/org/apache/camel/NamedRoute.java @@ -16,6 +16,8 @@ */ package org.apache.camel; +import org.apache.camel.spi.Resource; + /** * Represents a node in the {@link org.apache.camel.model routes} which is identified as a route. */ @@ -26,6 +28,11 @@ public interface NamedRoute { */ String getRouteId(); + /** + * Gets the route description. + */ + String getDescription(); + /** * Gets the node prefix id. */ @@ -51,4 +58,9 @@ public interface NamedRoute { */ NamedNode getInput(); + /** + * Gets the {@link Resource}. + */ + Resource getResource(); + } 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 7a50b8fdd244..a66f12105837 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 @@ -3176,6 +3176,12 @@ public abstract class AbstractCamelContext extends BaseService LOG.debug("Skip starting routes as CamelContext has been configured with autoStartup=false"); } + // dump routes when as we have model before creating the runtime models, which can help during troubleshooting + // when routes have problems starting + if (getDumpRoutes() != null && !"false".equals(getDumpRoutes())) { + doDumpRoutes(); + } + if (!getRouteController().isSupervising()) { // invoke this logic to warmup the routes and if possible also start the routes (using default route controller) StartupStep subStep @@ -3187,13 +3193,6 @@ public abstract class AbstractCamelContext extends BaseService startupStepRecorder.endStep(subStep); } - // TODO: json,png should be later - // dump routes when as we have model before creating the runtime models, which can help during troubleshooting - // when routes have problems starting - if (getDumpRoutes() != null && !"false".equals(getDumpRoutes())) { - doDumpRoutes(); - } - // ensure extra dev consoles is loaded in case additional JARs has been dynamically added to the classpath if (devConsole) { StartupStep step = startupStepRecorder.beginStep(CamelContext.class, null, "Scan DevConsoles (phase 2)"); 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 23db758d00d6..7bf67d739dd5 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 @@ -16,12 +16,14 @@ */ package org.apache.camel.impl.engine; +import java.util.List; import java.util.Map; import java.util.Optional; import java.util.concurrent.ScheduledExecutorService; import org.apache.camel.CamelContext; import org.apache.camel.Endpoint; +import org.apache.camel.NamedRoute; import org.apache.camel.Processor; import org.apache.camel.Route; import org.apache.camel.RouteTemplateContext; @@ -774,6 +776,11 @@ public class SimpleCamelContext extends AbstractCamelContext { throw new UnsupportedOperationException(); } + @Override + public List<NamedRoute> getNamedRouteDefinitions() { + throw new UnsupportedOperationException(); + } + @Override public String getTestExcludeRoutes() { throw new UnsupportedOperationException(); diff --git a/core/camel-console/src/main/java/org/apache/camel/impl/console/RouteStructureDevConsole.java b/core/camel-console/src/main/java/org/apache/camel/impl/console/RouteStructureDevConsole.java index 2e7188c4480f..38efd35994bb 100644 --- a/core/camel-console/src/main/java/org/apache/camel/impl/console/RouteStructureDevConsole.java +++ b/core/camel-console/src/main/java/org/apache/camel/impl/console/RouteStructureDevConsole.java @@ -18,13 +18,10 @@ package org.apache.camel.impl.console; import java.util.List; import java.util.Map; -import java.util.Objects; import java.util.function.Function; 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.NamedRoute; import org.apache.camel.spi.ModelDumpLine; import org.apache.camel.spi.ModelToStructureDumper; import org.apache.camel.spi.annotations.DevConsole; @@ -43,8 +40,6 @@ import static org.apache.camel.impl.console.ConsoleHelper.extractSourceLocationN @DevConsole(name = "route-structure", description = "Dump route structure") public class RouteStructureDevConsole extends AbstractDevConsole { - // TODO: no JMX but model only - /** * Filters the routes matching by route id, route uri, and source location */ @@ -69,15 +64,16 @@ public class RouteStructureDevConsole extends AbstractDevConsole { final String brief = (String) options.getOrDefault(BRIEF, "false"); final StringBuilder sb = new StringBuilder(); - Function<ManagedRouteMBean, Object> task = mrb -> { + Function<NamedRoute, Object> task = def -> { try { ModelToStructureDumper dumper = PluginHelper.getModelToStructureDumper(getCamelContext()); List<ModelDumpLine> lines - = dumper.dumpStructure(getCamelContext(), mrb.getRouteId(), "true".equalsIgnoreCase(brief)); + = dumper.dumpStructure(getCamelContext(), def.getRouteId(), "true".equalsIgnoreCase(brief)); - sb.append(String.format(" Id: %s", mrb.getRouteId())); - if (mrb.getSourceLocation() != null) { - sb.append(String.format("%n Source: %s", extractSourceLocationNoLineNumber(mrb.getSourceLocation()))); + sb.append(String.format(" Id: %s", def.getRouteId())); + if (def.getResource() != null) { + sb.append(String.format("%n Source: %s", + extractSourceLocationNoLineNumber(def.getResource().getLocation()))); } sb.append("\n\n"); for (ModelDumpLine line : lines) { @@ -108,27 +104,27 @@ public class RouteStructureDevConsole extends AbstractDevConsole { final JsonObject root = new JsonObject(); final JsonArray list = new JsonArray(); - Function<ManagedRouteMBean, Object> task = mrb -> { + Function<NamedRoute, Object> task = def -> { JsonObject jo = new JsonObject(); list.add(jo); - jo.put("routeId", mrb.getRouteId()); - jo.put("from", mrb.getEndpointUri()); - if (mrb.getSourceLocation() != null) { - jo.put("source", extractSourceLocationNoLineNumber(mrb.getSourceLocation())); - Integer line = extractSourceLocationLineNumber(mrb.getSourceLocation()); + jo.put("routeId", def.getRouteId()); + jo.put("from", def.getEndpointUrl()); + if (def.getResource() != null) { + jo.put("source", extractSourceLocationNoLineNumber(def.getResource().getLocation())); + Integer line = extractSourceLocationLineNumber(def.getResource().getLocation()); if (line != null) { jo.put("line", line); } } - if (mrb.getDescription() != null) { - jo.put("description", mrb.getDescription()); + if (def.getDescription() != null) { + jo.put("description", def.getDescription()); } try { ModelToStructureDumper dumper = PluginHelper.getModelToStructureDumper(getCamelContext()); List<ModelDumpLine> lines - = dumper.dumpStructure(getCamelContext(), mrb.getRouteId(), "true".equalsIgnoreCase(brief)); + = dumper.dumpStructure(getCamelContext(), def.getRouteId(), "true".equalsIgnoreCase(brief)); JsonArray code = dumpAsJSon(lines); jo.put("code", code); } catch (Exception e) { @@ -141,45 +137,36 @@ public class RouteStructureDevConsole extends AbstractDevConsole { return root; } - protected void doCall(Map<String, Object> options, Function<ManagedRouteMBean, Object> task) { + protected void doCall(Map<String, Object> options, Function<NamedRoute, Object> task) { String path = (String) options.get(Exchange.HTTP_PATH); String subPath = path != null ? StringHelper.after(path, "/") : null; String filter = (String) options.get(FILTER); String limit = (String) options.get(LIMIT); final int max = limit == null ? Integer.MAX_VALUE : Integer.parseInt(limit); - ManagedCamelContext mcc = getCamelContext().getCamelContextExtension().getContextPlugin(ManagedCamelContext.class); - if (mcc != null) { - List<Route> routes = getCamelContext().getRoutes(); - routes.sort((o1, o2) -> o1.getRouteId().compareToIgnoreCase(o2.getRouteId())); - routes.stream() - .map(route -> mcc.getManagedRoute(route.getRouteId())) - .filter(Objects::nonNull) - .filter(r -> accept(r, filter)) - .filter(r -> accept(r, subPath)) - .sorted(RouteStructureDevConsole::sort) - .limit(max) - .forEach(task::apply); - } + var routes = getCamelContext().getNamedRouteDefinitions(); + routes.sort((o1, o2) -> o1.getRouteId().compareToIgnoreCase(o2.getRouteId())); + routes.stream() + .filter(r -> accept(r, filter)) + .filter(r -> accept(r, subPath)) + .limit(max) + .forEach(task::apply); } - private static boolean accept(ManagedRouteMBean mrb, String filter) { + private static boolean accept(NamedRoute route, String filter) { if (filter == null || filter.isBlank()) { return true; } - String onlyName = LoggerHelper.sourceNameOnly(mrb.getSourceLocation()); - return PatternHelper.matchPattern(mrb.getRouteId(), filter) - || PatternHelper.matchPattern(mrb.getEndpointUri(), filter) - || PatternHelper.matchPattern(mrb.getSourceLocationShort(), filter) + String uri = route.getInput().getLabel(); + String loc = LoggerHelper.getLineNumberLoggerName(route); + String onlyName = LoggerHelper.sourceNameOnly(loc); + return PatternHelper.matchPattern(route.getRouteId(), filter) + || PatternHelper.matchPattern(uri, filter) + || PatternHelper.matchPattern(loc, filter) || PatternHelper.matchPattern(onlyName, filter); } - private static int sort(ManagedRouteMBean o1, ManagedRouteMBean o2) { - // sort by id - return o1.getRouteId().compareTo(o2.getRouteId()); - } - private static JsonArray dumpAsJSon(List<ModelDumpLine> lines) { JsonArray code = new JsonArray(); int counter = 0; diff --git a/core/camel-core-engine/src/main/java/org/apache/camel/impl/DefaultCamelContext.java b/core/camel-core-engine/src/main/java/org/apache/camel/impl/DefaultCamelContext.java index 8477fd158e1e..c42c04fd7e15 100644 --- a/core/camel-core-engine/src/main/java/org/apache/camel/impl/DefaultCamelContext.java +++ b/core/camel-core-engine/src/main/java/org/apache/camel/impl/DefaultCamelContext.java @@ -30,6 +30,7 @@ import org.apache.camel.CamelContext; import org.apache.camel.Expression; import org.apache.camel.FailedToStartRouteException; import org.apache.camel.LoggingLevel; +import org.apache.camel.NamedRoute; import org.apache.camel.Predicate; import org.apache.camel.Processor; import org.apache.camel.Route; @@ -429,6 +430,11 @@ public class DefaultCamelContext extends SimpleCamelContext implements ModelCame } } + @Override + public List<NamedRoute> getNamedRouteDefinitions() { + return new ArrayList<>(model.getRouteDefinitions()); + } + @Override public List<RestDefinition> getRestDefinitions() { return model.getRestDefinitions(); diff --git a/core/camel-support/src/main/java/org/apache/camel/support/LoggerHelper.java b/core/camel-support/src/main/java/org/apache/camel/support/LoggerHelper.java index 1cf3e282a892..48e37c40e2e5 100644 --- a/core/camel-support/src/main/java/org/apache/camel/support/LoggerHelper.java +++ b/core/camel-support/src/main/java/org/apache/camel/support/LoggerHelper.java @@ -123,7 +123,24 @@ public final class LoggerHelper { } public static String sourceNameOnly(String location) { - return stripScheme(stripSourceLocationLineNumber(location)); + int cnt = StringHelper.countChar(location, ':'); + if (cnt <= 1) { + // include pseudo scheme due to extract methods rely on scheme included + location = "file:" + location; + } + Integer line = extractSourceLocationLineNumber(location); + if (line != null) { + // remove line number + location = extractSourceLocationNoLineNumber(location); + // strip scheme so its only the name + if (location.contains(":")) { + location = stripScheme(location); + } + return location; + } else { + // no line number so strip scheme + return stripScheme(location); + } } public static Integer extractSourceLocationLineNumber(String location) { @@ -143,4 +160,13 @@ public final class LoggerHelper { return null; } + public static String extractSourceLocationNoLineNumber(String location) { + Integer line = extractSourceLocationLineNumber(location); + if (line != null) { + int pos = location.lastIndexOf(':'); + return location.substring(0, pos); + } + return location; + } + }
