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 d2d55e2ad750 CAMEL-25199: camel-jbang - read Java routes with the Java 
DSL parser only, retire the regex readers (#27147)
d2d55e2ad750 is described below

commit d2d55e2ad7502f497f8d4a19968fac957e3e3478
Author: Claus Ibsen <[email protected]>
AuthorDate: Thu Oct 1 09:12:48 2026 +0200

    CAMEL-25199: camel-jbang - read Java routes with the Java DSL parser only, 
retire the regex readers (#27147)
    
    - the parser tells a call of configure() from its declaration, so an 
anonymous
      RouteBuilder passed to camel.configure().addRoutesBuilder(...) is read
    - the project overview reads Java with the parser only; the regex reader is
      removed (over the example projects the parser now reads every route it 
did)
    - the consumer check of camel validate reads the endpoints Java routes 
consume
      with the parser: constants, String.format and the endpoint DSL are 
resolved,
      a route template or an input known only at runtime still keeps it quiet
    
    Co-Authored-By: Claude Opus 5.5 (1M context) <[email protected]>
    Signed-off-by: Claus Ibsen <[email protected]>
---
 .../org/apache/camel/java/in/JavaChainParser.java  |   5 +-
 .../org/apache/camel/java/in/LwJavaParserTest.java |  23 +++
 design/java-dsl-parser.adoc                        |  11 ++
 .../core/commands/ai/EndpointConsumerChecks.java   |  49 ++++--
 .../dsl/jbang/core/commands/ai/ProjectRoutes.java  | 184 +--------------------
 .../jbang/core/commands/ai/SourceValidator.java    |   2 +-
 .../ai/SourceValidatorEndpointConsumersTest.java   |  51 ++++++
 7 files changed, 125 insertions(+), 200 deletions(-)

diff --git 
a/core/camel-java-io/src/main/java/org/apache/camel/java/in/JavaChainParser.java
 
b/core/camel-java-io/src/main/java/org/apache/camel/java/in/JavaChainParser.java
index c90ba3fbc0f9..9950092f7b11 100644
--- 
a/core/camel-java-io/src/main/java/org/apache/camel/java/in/JavaChainParser.java
+++ 
b/core/camel-java-io/src/main/java/org/apache/camel/java/in/JavaChainParser.java
@@ -251,7 +251,10 @@ final class JavaChainParser {
             } else if (t.is("}")) {
                 depth--;
                 pos++;
-            } else if ((t.isIdent("configure") || t.isIdent("configuration")) 
&& at(1).is("(") && at(2).is(")")) {
+            } else if ((t.isIdent("configure") || t.isIdent("configuration")) 
&& at(1).is("(") && at(2).is(")")
+                    && (at(3).is("{") || at(3).isIdent("throws"))) {
+                // the declaration of configure(), not a call such as 
camel.configure().addRoutesBuilder(...), whose
+                // anonymous RouteBuilder has a configure() of its own
                 int p = pos + 3;
                 // throws clause
                 while (tokens.get(p).kind() != Kind.EOF && 
!tokens.get(p).is("{") && !tokens.get(p).is(";")) {
diff --git 
a/core/camel-java-io/src/test/java/org/apache/camel/java/in/LwJavaParserTest.java
 
b/core/camel-java-io/src/test/java/org/apache/camel/java/in/LwJavaParserTest.java
index 4939cb8ccea0..a9a2b6ff5cc5 100644
--- 
a/core/camel-java-io/src/test/java/org/apache/camel/java/in/LwJavaParserTest.java
+++ 
b/core/camel-java-io/src/test/java/org/apache/camel/java/in/LwJavaParserTest.java
@@ -267,6 +267,29 @@ class LwJavaParserTest {
         assertThat(cb.getConfiguration()).isEqualTo("myConfig");
     }
 
+    @Test
+    void anAnonymousRouteBuilderPassedToACall() {
+        // CAMEL-25199: camel.configure() is a call, the configure() of the 
anonymous RouteBuilder the declaration
+        JavaParseResult result = new LwJavaParser().parse("""
+                public final class Application {
+                    public static void main(String[] args) throws Exception {
+                        try (Main camel = new Main()) {
+                            camel.configure().addRoutesBuilder(new 
RouteBuilder() {
+                                @Override
+                                public void configure() throws Exception {
+                                    
from("timer:foo?repeatCount=1").to("direct:aggregator");
+                                    from("direct:aggregator").to("log:out");
+                                }
+                            });
+                            camel.run();
+                        }
+                    }
+                }
+                """);
+        assertThat(result.routes().getRoutes()).extracting(r -> 
r.getInput().getUri())
+                .containsExactly("timer:foo?repeatCount=1", 
"direct:aggregator");
+    }
+
     @Test
     void aRouteKeptInALocalVariable() {
         // a route built in steps: RouteDefinition route = from(...); 
route.to(...)
diff --git a/design/java-dsl-parser.adoc b/design/java-dsl-parser.adoc
index 500bcf7f9fa9..1a9c187194b9 100644
--- a/design/java-dsl-parser.adoc
+++ b/design/java-dsl-parser.adoc
@@ -91,6 +91,17 @@ the most specific; candidates are tried in a stable order.
   source files, and the catalog's component header constants 
(`KafkaConstants.KEY`) and enum values
   (`HazelcastOperation.PUT`).
 
+=== Where it is used
+
+camel-jbang reads Java routes with the parser only; the pattern matching on 
string literals it had before is gone
+(CAMEL-25199):
+
+* the project overview (`camel overview`, the `camel_project_overview` MCP 
tool, the TUI Architecture and Topology
+  views);
+* the consumer check of `camel validate`: which `direct:` and `seda:` 
endpoints the Java routes of an application
+  consume, with constants, formats and the endpoint DSL resolved;
+* the jump links between routes in the TUI Source tab.
+
 == Security
 
 The parser reads sources nobody vetted: a cloned example opened in the TUI, 
documentation, a project an AI agent
diff --git 
a/dsl/camel-jbang/camel-jbang-core/src/main/java/org/apache/camel/dsl/jbang/core/commands/ai/EndpointConsumerChecks.java
 
b/dsl/camel-jbang/camel-jbang-core/src/main/java/org/apache/camel/dsl/jbang/core/commands/ai/EndpointConsumerChecks.java
index ed9c848ff39f..23a8de47fab6 100644
--- 
a/dsl/camel-jbang/camel-jbang-core/src/main/java/org/apache/camel/dsl/jbang/core/commands/ai/EndpointConsumerChecks.java
+++ 
b/dsl/camel-jbang/camel-jbang-core/src/main/java/org/apache/camel/dsl/jbang/core/commands/ai/EndpointConsumerChecks.java
@@ -32,7 +32,11 @@ import java.util.Set;
 import java.util.regex.Matcher;
 import java.util.regex.Pattern;
 
+import org.apache.camel.catalog.CamelCatalog;
 import org.apache.camel.dsl.yaml.validator.EndpointConsumers;
+import org.apache.camel.java.in.JavaParseResult;
+import org.apache.camel.java.in.LwJavaParser;
+import org.apache.camel.model.RouteDefinition;
 
 /**
  * A {@code direct:} or {@code seda:} endpoint a YAML route sends to, and no 
route of the application consumes
@@ -41,13 +45,8 @@ import org.apache.camel.dsl.yaml.validator.EndpointConsumers;
  */
 public final class EndpointConsumerChecks {
 
-    /**
-     * A Java DSL route input: from( not called on something else, so not 
Instant.from( or List.from(; its endpoint is
-     * read only when a string literal is the whole argument, not the start of 
from("direct:" + NAME).
-     */
-    private static final Pattern JAVA_FROM = 
Pattern.compile("(?<![.\\w])from\\s*\\(\\s*(\"([^\"]*)\"\\s*(?=[),]))?");
-    /** Route inputs the scan cannot read: a route template's from( and fromF( 
with a format. */
-    private static final Pattern JAVA_UNREADABLE = 
Pattern.compile("\\b(routeTemplate|fromF)\\s*\\(");
+    /** A Java DSL route input: from( not called on something else, so not 
Instant.from( or List.from(. */
+    private static final Pattern JAVA_FROM = 
Pattern.compile("(?<![.\\w])from\\s*\\(");
     /** The build files of a project whose routes are spread over 
src/main/java and src/main/resources. */
     private static final List<String> BUILD_FILES = List.of("pom.xml", 
"build.gradle", "build.gradle.kts");
     private static final Pattern XML_FROM = 
Pattern.compile("<from\\s[^>]*?\\buri\\s*=\\s*[\"']([^\"']*)[\"']");
@@ -62,7 +61,15 @@ public final class EndpointConsumerChecks {
      * @return             the messages, one per endpoint no route consumes
      */
     public static List<String> validateYamlConsumers(String content, Path 
directory, String excludeFile) {
-        return EndpointConsumers.check(content, consumed(directory, 
excludeFile));
+        return validateYamlConsumers(content, directory, excludeFile, null);
+    }
+
+    /**
+     * @param catalog for the endpoint DSL of Java routes, may be null
+     */
+    public static List<String> validateYamlConsumers(
+            String content, Path directory, String excludeFile, CamelCatalog 
catalog) {
+        return EndpointConsumers.check(content, consumed(directory, 
excludeFile, catalog));
     }
 
     /**
@@ -71,7 +78,7 @@ public final class EndpointConsumerChecks {
      * Gradle project, more files than the scan looks at, or a route input the 
scan cannot read (a Java {@code from(}
      * with no literal, a placeholder, a route template), which could be any 
endpoint.
      */
-    static Set<String> consumed(Path directory, String excludeFile) {
+    static Set<String> consumed(Path directory, String excludeFile, 
CamelCatalog catalog) {
         if (directory == null || !Files.isDirectory(directory)) {
             return null;
         }
@@ -91,7 +98,7 @@ public final class EndpointConsumerChecks {
                 if (lower.endsWith(".yaml") || lower.endsWith(".yml")) {
                     found = EndpointConsumers.consumed(Files.readString(p));
                 } else if (lower.endsWith(".java")) {
-                    found = javaConsumed(Files.readString(p));
+                    found = javaConsumed(Files.readString(p), catalog);
                 } else {
                     found = xmlConsumed(Files.readString(p));
                 }
@@ -177,15 +184,25 @@ public final class EndpointConsumerChecks {
                 && (lower.endsWith(".yaml") || lower.endsWith(".yml") || 
lower.endsWith(".java") || lower.endsWith(".xml"));
     }
 
-    static Set<String> javaConsumed(String src) {
-        if (JAVA_UNREADABLE.matcher(src).find()) {
+    /**
+     * The endpoints the routes of a Java source consume, read into the model 
by the Java DSL parser (CAMEL-25199), so
+     * constants, String.format and the endpoint DSL are resolved; null when 
they cannot be known: a route template,
+     * whose input is a parameter, an input known only at runtime, or a from( 
the parser could not read.
+     */
+    static Set<String> javaConsumed(String src, CamelCatalog catalog) {
+        JavaParseResult result = JavaRouteReader.parse(src, catalog, null);
+        if (!result.routeTemplates().getRouteTemplates().isEmpty()) {
+            return null;
+        }
+        List<RouteDefinition> routes = result.routes().getRoutes();
+        if (routes.isEmpty() && JAVA_FROM.matcher(src).find()) {
+            // a from( the parser did not read as a route: what it consumes is 
not known
             return null;
         }
         Set<String> answer = new HashSet<>();
-        Matcher m = JAVA_FROM.matcher(src);
-        while (m.find()) {
-            // from(someConstant) or from(direct("x")): the endpoint is not in 
the source as a literal
-            if (m.group(2) == null || !add(m.group(2), answer)) {
+        for (RouteDefinition r : routes) {
+            String uri = r.getInput() != null ? r.getInput().getUri() : null;
+            if (uri == null || uri.contains(LwJavaParser.UNRESOLVED_PREFIX) || 
!add(uri, answer)) {
                 return null;
             }
         }
diff --git 
a/dsl/camel-jbang/camel-jbang-core/src/main/java/org/apache/camel/dsl/jbang/core/commands/ai/ProjectRoutes.java
 
b/dsl/camel-jbang/camel-jbang-core/src/main/java/org/apache/camel/dsl/jbang/core/commands/ai/ProjectRoutes.java
index f71bb9b25539..5bd573654a33 100644
--- 
a/dsl/camel-jbang/camel-jbang-core/src/main/java/org/apache/camel/dsl/jbang/core/commands/ai/ProjectRoutes.java
+++ 
b/dsl/camel-jbang/camel-jbang-core/src/main/java/org/apache/camel/dsl/jbang/core/commands/ai/ProjectRoutes.java
@@ -78,19 +78,6 @@ public final class ProjectRoutes {
     /** The id given to an onException or error handler outside a route, 
followed by where it is written. */
     public static final String ERROR_HANDLER_PREFIX = "error-handler@";
 
-    private static final Pattern JAVA_FROM = 
Pattern.compile("(?<![.\\w])from\\s*\\(\\s*\"((?:[^\"\\\\]|\\\\.)*)\"");
-    private static final Pattern JAVA_CALL = Pattern.compile(
-            
"\\.(to|toD|wireTap|enrich|pollEnrich|poll|routeId|routeDescription|routeNote|routeGroup|kamelet|recipientList|routingSlip"
-                                                             + 
"|dynamicRouter)\\s*\\(\\s*(\"((?:[^\"\\\\]|\\\\.)*)\")?");
-    private static final Pattern JAVA_STEP = 
Pattern.compile("\\.(\\w+)\\s*\\(");
-    private static final Pattern JAVA_ON_EXCEPTION = 
Pattern.compile("(?<![.\\w])onException\\s*\\(");
-    private static final Pattern JAVA_DEAD_LETTER = Pattern.compile(
-            "deadLetterChannel\\s*\\(\\s*\"((?:[^\"\\\\]|\\\\.)*)\"");
-    private static final Pattern JAVA_TO = 
Pattern.compile("\\.to\\s*\\(\\s*\"((?:[^\"\\\\]|\\\\.)*)\"");
-    /** Java DSL calls that do not change what a route does: naming and 
describing it. */
-    private static final Set<String> JAVA_NAMING
-            = Set.of("routeId", "routeDescription", "routeNote", "routeGroup", 
"id", "description", "note");
-
     private ProjectRoutes() {
     }
 
@@ -184,9 +171,8 @@ public final class ProjectRoutes {
                 return new XmlReader(file, content, catalog).read();
             }
             if (lower.endsWith(".java")) {
-                // the Java DSL parser gives the model of the routes; the 
patterns only when it finds none
-                List<Route> routes = JavaRouteReader.read(file, content, 
catalog, constants);
-                return !routes.isEmpty() ? routes : readJava(file, content, 
catalog);
+                // read into the model by the Java DSL parser, without 
compiling the source
+                return JavaRouteReader.read(file, content, catalog, constants);
             }
         } catch (RuntimeException e) {
             // an overview skips what it cannot read; the validator reports 
what is wrong with the file
@@ -1095,172 +1081,6 @@ public final class ProjectRoutes {
         return line;
     }
 
-    // ---- Java ----
-
-    /**
-     * The routes of a Java RouteBuilder, one per {@code from("...")}: every 
string literal passed to an endpoint method
-     * until the next from belongs to it.
-     */
-    private static List<Route> readJava(String file, String content, 
CamelCatalog catalog) {
-        String code = stripComments(content);
-        List<Route> routes = new ArrayList<>();
-        Matcher from = JAVA_FROM.matcher(code);
-        List<int[]> starts = new ArrayList<>();
-        List<String> uris = new ArrayList<>();
-        while (from.find()) {
-            starts.add(new int[] { from.start(), from.end() });
-            uris.add(unescape(from.group(1)));
-        }
-        for (int i = 0; i < starts.size(); i++) {
-            int end = i + 1 < starts.size() ? starts.get(i + 1)[0] : 
code.length();
-            String body = code.substring(starts.get(i)[1], end);
-            // a statement ends the route: stop at the first semicolon outside 
a string
-            int stop = statementEnd(body);
-            body = body.substring(0, stop);
-            Endpoint fromEp = endpoint(uris.get(i), null, false, catalog);
-            List<Endpoint> consumes = new ArrayList<>();
-            List<Endpoint> produces = new ArrayList<>();
-            add(consumes, fromEp);
-            String id = null;
-            String description = null;
-            String note = null;
-            String group = null;
-            Matcher m = JAVA_CALL.matcher(body);
-            while (m.find()) {
-                String method = m.group(1);
-                String literal = m.group(3) != null ? unescape(m.group(3)) : 
null;
-                switch (method) {
-                    case "routeId" -> id = literal;
-                    case "routeDescription" -> description = literal;
-                    case "routeNote" -> note = literal;
-                    case "routeGroup" -> group = literal;
-                    case "kamelet" ->
-                        add(produces, literal != null ? endpoint("kamelet:" + 
literal, null, false, catalog) : null);
-                    case "poll", "pollEnrich" -> add(consumes,
-                            literal != null ? endpoint(literal, null, true, 
catalog) : dynamicEndpoint(method));
-                    case "recipientList", "routingSlip", "dynamicRouter" -> 
produces.add(dynamicEndpoint(method));
-                    default -> add(produces, literal != null
-                            ? endpoint(literal, null, !"to".equals(method), 
catalog) : dynamicEndpoint(method));
-                }
-            }
-            int steps = 0;
-            Matcher s = JAVA_STEP.matcher(body);
-            while (s.find()) {
-                steps++;
-            }
-            routes.add(new Route(
-                    id, "route", description, group, file, lineAt(content, 
starts.get(i)[0]),
-                    "java", true, fromEp, consumes, produces, steps, -1, 0, 
null, null,
-                    javaLogOnly(body, produces), note));
-        }
-        javaErrorHandlers(file, content, code, catalog, routes);
-        return routes;
-    }
-
-    /** onException statements and dead letter channels outside a route, as 
error handler routes. */
-    private static void javaErrorHandlers(
-            String file, String content, String code, CamelCatalog catalog, 
List<Route> routes) {
-        List<Endpoint> produces = new ArrayList<>();
-        int first = -1;
-        Matcher on = JAVA_ON_EXCEPTION.matcher(code);
-        while (on.find()) {
-            String statement = code.substring(on.start(), on.start() + 
statementEnd(code.substring(on.start())));
-            Matcher to = JAVA_TO.matcher(statement);
-            while (to.find()) {
-                add(produces, endpoint(unescape(to.group(1)), null, false, 
catalog));
-                first = first < 0 ? on.start() : first;
-            }
-        }
-        Matcher dlc = JAVA_DEAD_LETTER.matcher(code);
-        while (dlc.find()) {
-            add(produces, endpoint(unescape(dlc.group(1)), null, false, 
catalog));
-            first = first < 0 ? dlc.start() : Math.min(first, dlc.start());
-        }
-        if (!produces.isEmpty()) {
-            int line = lineAt(content, first);
-            routes.add(new Route(
-                    ERROR_HANDLER_PREFIX + file + ":" + line, "errorHandler", 
null, null, file, line, "java", true, null,
-                    List.of(), 
produces.stream().map(Endpoint::asOnError).toList(), 0, -1, 0, null, null, 
false, null));
-        }
-    }
-
-    /** Whether a Java route only logs: its calls are log, to a log endpoint, 
or naming the route. */
-    private static boolean javaLogOnly(String body, List<Endpoint> produces) {
-        boolean logs = false;
-        Matcher m = JAVA_STEP.matcher(body);
-        while (m.find()) {
-            String method = m.group(1);
-            if ("log".equals(method) || "to".equals(method)) {
-                logs = true;
-            } else if (!JAVA_NAMING.contains(method)) {
-                return false;
-            }
-        }
-        return logs && produces.stream().allMatch(e -> 
"log".equals(e.scheme()));
-    }
-
-    /** Comments are replaced by spaces so offsets in the code still match the 
file. */
-    private static String stripComments(String content) {
-        StringBuilder sb = new StringBuilder(content.length());
-        int i = 0;
-        boolean inString = false;
-        while (i < content.length()) {
-            char c = content.charAt(i);
-            if (inString) {
-                sb.append(c);
-                if (c == '\\' && i + 1 < content.length()) {
-                    sb.append(content.charAt(++i));
-                } else if (c == '"') {
-                    inString = false;
-                }
-                i++;
-            } else if (c == '"') {
-                inString = true;
-                sb.append(c);
-                i++;
-            } else if (c == '/' && i + 1 < content.length() && 
content.charAt(i + 1) == '/') {
-                while (i < content.length() && content.charAt(i) != '\n') {
-                    sb.append(' ');
-                    i++;
-                }
-            } else if (c == '/' && i + 1 < content.length() && 
content.charAt(i + 1) == '*') {
-                int end = content.indexOf("*/", i + 2);
-                end = end < 0 ? content.length() : end + 2;
-                while (i < end) {
-                    sb.append(content.charAt(i) == '\n' ? '\n' : ' ');
-                    i++;
-                }
-            } else {
-                sb.append(c);
-                i++;
-            }
-        }
-        return sb.toString();
-    }
-
-    private static int statementEnd(String body) {
-        boolean inString = false;
-        for (int i = 0; i < body.length(); i++) {
-            char c = body.charAt(i);
-            if (inString) {
-                if (c == '\\') {
-                    i++;
-                } else if (c == '"') {
-                    inString = false;
-                }
-            } else if (c == '"') {
-                inString = true;
-            } else if (c == ';') {
-                return i;
-            }
-        }
-        return body.length();
-    }
-
-    private static String unescape(String literal) {
-        return literal.replace("\\\"", "\"").replace("\\\\", "\\");
-    }
-
     /**
      * An endpoint URI for showing or sending to a model: the query is 
sanitized so a password or token in it is masked.
      */
diff --git 
a/dsl/camel-jbang/camel-jbang-core/src/main/java/org/apache/camel/dsl/jbang/core/commands/ai/SourceValidator.java
 
b/dsl/camel-jbang/camel-jbang-core/src/main/java/org/apache/camel/dsl/jbang/core/commands/ai/SourceValidator.java
index 459be17c7fe7..f0102539d63d 100644
--- 
a/dsl/camel-jbang/camel-jbang-core/src/main/java/org/apache/camel/dsl/jbang/core/commands/ai/SourceValidator.java
+++ 
b/dsl/camel-jbang/camel-jbang-core/src/main/java/org/apache/camel/dsl/jbang/core/commands/ai/SourceValidator.java
@@ -127,7 +127,7 @@ public final class SourceValidator {
                 
msgs.addAll(GroovyImportChecks.validateYamlGroovyImports(content, null, 
declarations.javaClasses()));
                 if (checkConsumers) {
                     // a direct: or seda: endpoint no route of the application 
consumes (CAMEL-24955)
-                    
msgs.addAll(EndpointConsumerChecks.validateYamlConsumers(content, directory, 
fileName));
+                    
msgs.addAll(EndpointConsumerChecks.validateYamlConsumers(content, directory, 
fileName, catalog));
                 }
             }
             return msgs;
diff --git 
a/dsl/camel-jbang/camel-jbang-core/src/test/java/org/apache/camel/dsl/jbang/core/commands/ai/SourceValidatorEndpointConsumersTest.java
 
b/dsl/camel-jbang/camel-jbang-core/src/test/java/org/apache/camel/dsl/jbang/core/commands/ai/SourceValidatorEndpointConsumersTest.java
index c752979d5f63..703c3c777e20 100644
--- 
a/dsl/camel-jbang/camel-jbang-core/src/test/java/org/apache/camel/dsl/jbang/core/commands/ai/SourceValidatorEndpointConsumersTest.java
+++ 
b/dsl/camel-jbang/camel-jbang-core/src/test/java/org/apache/camel/dsl/jbang/core/commands/ai/SourceValidatorEndpointConsumersTest.java
@@ -144,6 +144,57 @@ class SourceValidatorEndpointConsumersTest {
         }
     }
 
+    @Test
+    void theJavaRoutesAreReadByTheParser() throws Exception {
+        // CAMEL-25199: a constant, a format and the endpoint DSL are 
resolved, so the check knows what they consume
+        for (String configure : List.of(
+                "from(OTHER).log(\"found\");",
+                "fromF(\"direct:%s\", \"other\").log(\"found\");",
+                "from(direct(\"other\")).log(\"found\");")) {
+            Files.writeString(dir.resolve("Other.java"), """
+                    import 
org.apache.camel.builder.endpoint.EndpointRouteBuilder;
+
+                    public class Other extends EndpointRouteBuilder {
+                        static final String OTHER = "direct:other";
+
+                        @Override
+                        public void configure() {
+                            %s
+                        }
+                    }
+                    """.formatted(configure));
+            assertThat(SourceValidator.validate("r.camel.yaml", ROUTE, 
CATALOG, null, dir)).as(configure)
+                    .singleElement().asString().startsWith("route tick: sends 
to direct:lookup, and no route consumes it");
+        }
+        // the same with the endpoint DSL naming the endpoint the YAML route 
sends to: consumed
+        Files.writeString(dir.resolve("Other.java"), """
+                import org.apache.camel.builder.endpoint.EndpointRouteBuilder;
+
+                public class Other extends EndpointRouteBuilder {
+                    @Override
+                    public void configure() {
+                        from(direct("lookup")).log("found");
+                    }
+                }
+                """);
+        assertThat(SourceValidator.validate("r.camel.yaml", ROUTE, CATALOG, 
null, dir)).isEmpty();
+    }
+
+    @Test
+    void aJavaRouteInputKnownOnlyAtRuntimeKeepsTheCheckQuiet() throws 
Exception {
+        Files.writeString(dir.resolve("Lookup.java"), """
+                import org.apache.camel.builder.RouteBuilder;
+
+                public class Lookup extends RouteBuilder {
+                    @Override
+                    public void configure() {
+                        from(inputFromConfig()).log("found");
+                    }
+                }
+                """);
+        assertThat(SourceValidator.validate("r.camel.yaml", ROUTE, CATALOG, 
null, dir)).isEmpty();
+    }
+
     @Test
     void aRouteFileInASubdirectoryConsumesIt() throws Exception {
         Files.createDirectories(dir.resolve("routes"));

Reply via email to