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 23f6a24eb7e3 chore: camel-jbang - the bean class check honours the
known dependencies camel run downloads
23f6a24eb7e3 is described below
commit 23f6a24eb7e3e6002155c9c8336a10c1c9039e7b
Author: Claus Ibsen <[email protected]>
AuthorDate: Fri Sep 18 13:53:37 2026 +0200
chore: camel-jbang - the bean class check honours the known dependencies
camel run downloads
A bean declared in YAML with a class that camel run resolves through the
known dependencies and
downloads on demand (org.postgresql.ds.PGSimpleDataSource, the Artemis
connection factory) was
reported by camel validate and the MCP write tool as "class ... was not
found". The check now
reads the same mapping files as the runtime, matches the class and then
each enclosing package,
and stays silent for a mapped class. For a class in no mapping the message
says how to declare
the dependency: camel.jbang.dependencies in application.properties or --dep
on camel run.
Closes #26574
---
.../dsl/jbang/core/commands/ai/BeanRefChecks.java | 61 ++++++++++++++++++++--
.../commands/ai/SourceValidatorBeanRefsTest.java | 38 ++++++++++++++
2 files changed, 95 insertions(+), 4 deletions(-)
diff --git
a/dsl/camel-jbang/camel-jbang-core/src/main/java/org/apache/camel/dsl/jbang/core/commands/ai/BeanRefChecks.java
b/dsl/camel-jbang/camel-jbang-core/src/main/java/org/apache/camel/dsl/jbang/core/commands/ai/BeanRefChecks.java
index ad8d2ce43ac6..d20270a3f34a 100644
---
a/dsl/camel-jbang/camel-jbang-core/src/main/java/org/apache/camel/dsl/jbang/core/commands/ai/BeanRefChecks.java
+++
b/dsl/camel-jbang/camel-jbang-core/src/main/java/org/apache/camel/dsl/jbang/core/commands/ai/BeanRefChecks.java
@@ -16,14 +16,19 @@
*/
package org.apache.camel.dsl.jbang.core.commands.ai;
+import java.io.InputStream;
+import java.net.URL;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.ArrayList;
+import java.util.Enumeration;
+import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Locale;
import java.util.Map;
+import java.util.Properties;
import java.util.Set;
import java.util.TreeSet;
import java.util.regex.Matcher;
@@ -337,8 +342,51 @@ final class BeanRefChecks {
}
/**
- * A class named with its package that is neither next to the route nor on
the CLI classpath: the wrong package
- * (org.apache.camel.support.StringAggregationStrategy) or a missing
dependency. Null when the class is fine.
+ * The classes camel run resolves to a Maven dependency and downloads on
demand (camel-kamelet-main's
+ * camel-main-known-dependencies.properties,
camel-component-known-dependencies.properties and the generated
+ * camel-thirdparty-known-dependencies.properties of CAMEL-24809), so a
#class:org.postgresql.ds.PGSimpleDataSource
+ * bean is fine without a dependency declared even though the class is not
on the CLI classpath. Matched the way the
+ * runtime matches: the class name, then each enclosing package.
+ */
+ private static volatile Map<String, String> knownDependencies;
+
+ static String knownDependency(String fqcn) {
+ Map<String, String> known = knownDependencies;
+ if (known == null) {
+ known = new HashMap<>();
+ for (String name : new String[] {
+ "camel-main-known-dependencies.properties",
"camel-component-known-dependencies.properties",
+ "camel-thirdparty-known-dependencies.properties" }) {
+ try {
+ Enumeration<URL> resources =
BeanRefChecks.class.getClassLoader().getResources(name);
+ while (resources.hasMoreElements()) {
+ try (InputStream is =
resources.nextElement().openStream()) {
+ Properties prop = new Properties();
+ prop.load(is);
+ for (String key : prop.stringPropertyNames()) {
+ known.put(key, prop.getProperty(key));
+ }
+ }
+ }
+ } catch (Exception e) {
+ // the mapping is an optimisation of the message, not a
requirement
+ }
+ }
+ knownDependencies = known;
+ }
+ String prefix = fqcn;
+ String gav = known.get(prefix);
+ while (gav == null && prefix.lastIndexOf('.') != -1) {
+ prefix = prefix.substring(0, prefix.lastIndexOf('.'));
+ gav = known.get(prefix);
+ }
+ return gav;
+ }
+
+ /**
+ * A class named with its package that is neither next to the route, nor
on the CLI classpath, nor one camel run
+ * downloads: the wrong package
(org.apache.camel.support.StringAggregationStrategy) or a missing dependency.
Null
+ * when the class is fine.
*/
static String classNotFound(String fqcn, BeanDeclarations external) {
if (external == null || external == BeanDeclarations.NONE
@@ -366,6 +414,10 @@ final class BeanRefChecks {
} catch (Throwable e) {
// not on the classpath
}
+ if (knownDependency(fqcn) != null) {
+ // camel run downloads the dependency for this class; the runtime
and the validator must agree
+ return null;
+ }
String hint = "";
if (simple.endsWith("AggregationStrategy") &&
!fqcn.startsWith("org.apache.camel.processor.aggregate.")) {
String candidate = "org.apache.camel.processor.aggregate." +
simple;
@@ -377,8 +429,9 @@ final class BeanRefChecks {
}
}
if (hint.isEmpty()) {
- hint = " (check the package name; a class of your own goes in a
.java file next to the route, a class from"
- + " another library needs its dependency)";
+ hint = " (check the package name; a class of your own goes in a
.java file next to the route; a class from"
+ + " another library needs its dependency declared,
camel.jbang.dependencies=<groupId>:<artifactId>:<version>"
+ + " in application.properties or --dep on camel run)";
}
return "class " + fqcn + " was not found" + hint;
}
diff --git
a/dsl/camel-jbang/camel-jbang-core/src/test/java/org/apache/camel/dsl/jbang/core/commands/ai/SourceValidatorBeanRefsTest.java
b/dsl/camel-jbang/camel-jbang-core/src/test/java/org/apache/camel/dsl/jbang/core/commands/ai/SourceValidatorBeanRefsTest.java
index cb468d430b02..d9994e238ee2 100644
---
a/dsl/camel-jbang/camel-jbang-core/src/test/java/org/apache/camel/dsl/jbang/core/commands/ai/SourceValidatorBeanRefsTest.java
+++
b/dsl/camel-jbang/camel-jbang-core/src/test/java/org/apache/camel/dsl/jbang/core/commands/ai/SourceValidatorBeanRefsTest.java
@@ -350,6 +350,44 @@ public class SourceValidatorBeanRefsTest {
.contains("did you mean
org.apache.camel.processor.aggregate.StringAggregationStrategy?");
}
+ @Test
+ void aClassCamelRunDownloadsIsNotReportedAsMissing(@TempDir Path dir)
throws IOException {
+ // the Postgres datasource and the Artemis connection factory are not
on the CLI classpath, but camel run
+ // resolves them to their Maven dependency
(camel-main-known-dependencies.properties) and downloads it, so a
+ // bean of that type runs; the validator must not contradict the
runtime
+ List<String> msgs = SourceValidator.validate("r.camel.yaml", """
+ - beans:
+ - name: postgresDS
+ type: "#class:org.postgresql.ds.PGSimpleDataSource"
+ properties:
+ url: "jdbc:postgresql://localhost:5432/postgres"
+ - name: artemisCF
+ type:
"#class:org.apache.activemq.artemis.jms.client.ActiveMQConnectionFactory"
+ - route:
+ from:
+ uri: "timer:tick?period=1000"
+ steps:
+ - to:
+ uri: "sql:select 1?dataSource=#postgresDS"
+ """, CATALOG, null, dir);
+ assertThat(msgs).isEmpty();
+
assertThat(BeanRefChecks.knownDependency("org.postgresql.ds.PGSimpleDataSource"))
+ .startsWith("org.postgresql:postgresql");
+
assertThat(BeanRefChecks.knownDependency("com.example.NoSuchThing")).isNull();
+ }
+
+ @Test
+ void aClassFromAnUnknownLibrarySaysHowToDeclareTheDependency(@TempDir Path
dir) throws IOException {
+ // a package no mapping will ever name: com.zaxxer.hikari is mapped as
a package by CAMEL-24809
+ List<String> msgs = SourceValidator.validate("r.camel.yaml", """
+ - beans:
+ - name: pool
+ type: "#class:com.example.pool.HikariDataSourceX"
+ """, CATALOG, null, dir);
+ assertThat(msgs).hasSize(1);
+ assertThat(msgs.get(0)).contains("was not
found").contains("camel.jbang.dependencies=<groupId>:<artifactId>:<version>");
+ }
+
@Test
void aSiblingClassImportedFromTheWrongPackageIsNamed(@TempDir Path dir)
throws IOException {
Files.writeString(dir.resolve("MemoryLeakSimulator.java"), """