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 c7eaecba6f48 CAMEL-24809: camel-kamelet-main - known dependencies
mapped by package, generated from a curated list
c7eaecba6f48 is described below
commit c7eaecba6f4886845e458ec610dd1d8edfafa7a4
Author: Claus Ibsen <[email protected]>
AuthorDate: Fri Sep 18 13:53:07 2026 +0200
CAMEL-24809: camel-kamelet-main - known dependencies mapped by package,
generated from a curated list
camel run resolves the class of a bean or a property to a Maven dependency
through the known
dependencies of camel-kamelet-main and downloads it on demand. The mapping
listed a handful of
classes one by one. It now maps third-party libraries by package, one line
per library, from a
curated input file (src/main/known-third-party-libraries.properties, 146
libraries: JDBC drivers
and pools, messaging clients, databases and search, vector databases, AWS,
Azure, Google Cloud,
Kubernetes, SaaS clients, data formats, templates, network, security,
observability, AI SDKs).
The prepare-kamelet-main mojo resolves every version from camel-parent's
properties or a BOM at
build time, fails on a missing property or a literal version, and writes
the resolved file into
target/classes; -Dcamel.known-dependencies.verify=true also checks each
package against its jar.
The four literal driver versions became parent properties. The user manual
gains an advanced
section on how a class becomes a dependency.
Closes #26575
---
.../modules/ROOT/pages/camel-jbang-running.adoc | 50 ++++-
.../main/download/KnownDependenciesResolver.java | 2 +
.../main/known-third-party-libraries.properties | 212 ++++++++++++++++++++
.../camel-main-known-dependencies.properties | 16 +-
.../download/KnownDependenciesResolverTest.java | 38 ++++
parent/pom.xml | 4 +
.../maven/packaging/PrepareKameletMainMojo.java | 222 ++++++++++++++++++++-
7 files changed, 530 insertions(+), 14 deletions(-)
diff --git a/docs/user-manual/modules/ROOT/pages/camel-jbang-running.adoc
b/docs/user-manual/modules/ROOT/pages/camel-jbang-running.adoc
index a835b0948e79..5ef0ed5f678f 100644
--- a/docs/user-manual/modules/ROOT/pages/camel-jbang-running.adoc
+++ b/docs/user-manual/modules/ROOT/pages/camel-jbang-running.adoc
@@ -130,8 +130,10 @@ It does not support `camel-servlet` or `camel-jetty`.
== Adding custom JARs
-Camel CLI automatically detects and downloads dependencies for Camel
components.
-For 3rd-party JARs, use `--dep` with Maven GAV syntax:
+Camel CLI automatically detects and downloads dependencies for Camel
components, and for the
+well-known third-party libraries a route may name in a bean, such as a JDBC
datasource or a JMS
+connection factory (see <<advanced-class-to-dependency,how a class becomes a
dependency>>).
+For other 3rd-party JARs, use `--dep` with Maven GAV syntax:
[source,bash]
----
@@ -234,6 +236,50 @@ To disable automatic downloading:
camel run foo.java --download=false
----
+[[advanced-class-to-dependency]]
+=== Advanced: how a class becomes a dependency
+
+When a route, a bean declaration, a `#class:` value in
`application.properties`, or an `import`
+in a Java file next to the route names a class that is not on the classpath,
Camel CLI looks the
+class up in three mapping files shipped in `camel-kamelet-main` and downloads
the dependency it
+maps to. The files are read into one table, in this order (a later file wins
for an identical key):
+
+1. `camel-main-known-dependencies.properties`, hand-written: Spring and
Quarkus annotations,
+ `camel.*` switches, the LangChain4j model providers.
+2. `camel-component-known-dependencies.properties`, generated from the
catalog: every Camel
+ component class maps to its `camel-` artifact.
+3. `camel-thirdparty-known-dependencies.properties`, generated at build time
from the curated
+ list `known-third-party-libraries.properties` in `camel-kamelet-main`: JDBC
drivers and
+ connection pools, messaging clients, cloud SDKs, JSON, XML and CSV
libraries, and other
+ libraries a route commonly names, each mapped by package.
+
+The lookup walks up from the class name:
+
+1. The exact class, for example `org.postgresql.ds.PGSimpleDataSource`.
+2. Each enclosing package in turn, `org.postgresql.ds`, then `org.postgresql`,
until a key
+ matches or no package is left. The deepest key wins, so
`org.apache.activemq.artemis` is found
+ before `org.apache.activemq`.
+3. The value is a Maven coordinate. A `camel:xxx` short form becomes
`org.apache.camel:camel-xxx`
+ at the running Camel version; a `${...}` placeholder in the hand-written
file is resolved
+ from the `camel-dependencies` POM of that version; the generated
third-party file carries the
+ versions already resolved.
+4. The dependency and its transitive dependencies are downloaded and added to
the classpath,
+ and the class is loaded again.
+
+A dependency you declare yourself, with `--dep`, `camel.jbang.dependencies`,
or in the project's
+POM, is on the classpath before any lookup, so it always takes precedence. Use
that when a
+mapped library is not the one you want, for example the ActiveMQ 5 client
where the mapping
+picks the ActiveMQ 6 client.
+
+`camel validate` and the write tools of the Camel MCP server consult the same
three files, so a
+bean whose class Camel CLI would download is not reported as missing.
+
+To add a library to the mapping, add one line to
`known-third-party-libraries.properties`
+in `camel-kamelet-main`: the library's own package, its `groupId:artifactId`,
and a version
+property of `camel-parent`. The build resolves the version and fails on a
property that does
+not exist; with `-Dcamel.known-dependencies.verify=true` it also resolves
every JAR and checks
+that the package is in it.
+
== Runtimes
By default `camel run` runs the integration in-process, inside the JVM of the
Camel CLI itself.
diff --git
a/dsl/camel-kamelet-main/src/main/java/org/apache/camel/main/download/KnownDependenciesResolver.java
b/dsl/camel-kamelet-main/src/main/java/org/apache/camel/main/download/KnownDependenciesResolver.java
index cae1d090987a..79f8999be675 100644
---
a/dsl/camel-kamelet-main/src/main/java/org/apache/camel/main/download/KnownDependenciesResolver.java
+++
b/dsl/camel-kamelet-main/src/main/java/org/apache/camel/main/download/KnownDependenciesResolver.java
@@ -42,6 +42,8 @@ public final class KnownDependenciesResolver {
public void loadKnownDependencies() {
doLoadKnownDependencies("camel-main-known-dependencies.properties");
doLoadKnownDependencies("camel-component-known-dependencies.properties");
+ // third-party libraries mapped by package, generated from
src/main/known-third-party-libraries.properties
+
doLoadKnownDependencies("camel-thirdparty-known-dependencies.properties");
}
public void loadKnownFactoryFinderDependencies() {
diff --git
a/dsl/camel-kamelet-main/src/main/known-third-party-libraries.properties
b/dsl/camel-kamelet-main/src/main/known-third-party-libraries.properties
new file mode 100644
index 000000000000..dce610efc6d9
--- /dev/null
+++ b/dsl/camel-kamelet-main/src/main/known-third-party-libraries.properties
@@ -0,0 +1,212 @@
+## ---------------------------------------------------------------------------
+## 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.
+## ---------------------------------------------------------------------------
+
+# Third-party libraries camel run downloads on demand when a bean, a property
or a Java import names one of
+# their classes (CAMEL-24809). The input of
camel-package-maven-plugin:prepare-kamelet-main, which generates
+# camel-thirdparty-known-dependencies.properties into target/classes with the
versions resolved; that file is
+# shipped in the jar and never committed, this one is the source.
+#
+# One line per library: <package> = <groupId>:<artifactId>:<version>
+# <version> is ${a-version-property} of parent/pom.xml (checked at build
time; a literal version is refused),
+# or @bom(<groupId>:<artifactId>:${bom-version-property}) for a library
whose version a BOM manages.
+# The runtime matches the class name and then each enclosing package, so map
the library's own package and
+# never a shared parent such as org.apache.commons or com.google.cloud, or the
walk up the package picks the
+# wrong jar. With -Dcamel.known-dependencies.verify=true the build resolves
every jar and checks the package.
+
+# JDBC drivers, datasources and connection pools
+org.postgresql = org.postgresql:postgresql:${pgjdbc-driver-version}
+com.mysql.cj = com.mysql:mysql-connector-j:${debezium-mysql-connector-version}
+org.mariadb.jdbc = org.mariadb.jdbc:mariadb-java-client:${mariadb-version}
+com.microsoft.sqlserver.jdbc =
com.microsoft.sqlserver:mssql-jdbc:${mssql-jdbc-version}
+oracle.jdbc = com.oracle.database.jdbc:ojdbc17:${ojdbc-version}
+com.amazon.redshift =
com.amazon.redshift:redshift-jdbc42:${redshift-jdbc-version}
+org.h2 = com.h2database:h2:${h2-version}
+org.hsqldb = org.hsqldb:hsqldb:${hsqldb-version}
+com.clickhouse.client = com.clickhouse:client-v2:${clickhouse-client-version}
+com.zaxxer.hikari = com.zaxxer:HikariCP:${hikaricp-version}
+com.mchange.v2.c3p0 = com.mchange:c3p0:${c3p0-version}
+org.apache.commons.dbcp2 =
org.apache.commons:commons-dbcp2:${commons-dbcp2-version}
+
+# ORM, SQL and scheduling
+org.hibernate = org.hibernate.orm:hibernate-core:${hibernate-version}
+org.hibernate.validator =
org.hibernate.validator:hibernate-validator:${hibernate-validator-version}
+org.jooq = org.jooq:jooq:${jooq-version}
+org.apache.ibatis = org.mybatis:mybatis:${mybatis-version}
+org.quartz = org.quartz-scheduler:quartz:${quartz-version}
+
+# Messaging clients. The Artemis package is deeper than the classic ActiveMQ
one and is matched first. The classic
+# ActiveMQ 5 and 6 clients share the org.apache.activemq package and only one
can be mapped: it is the ActiveMQ 6
+# client, the Jakarta one Camel 4 needs, which speaks OpenWire to 5.x brokers
too. A project that must use the 5.x
+# Jakarta client declares org.apache.activemq:activemq-client-jakarta itself;
a declared dependency puts the class
+# on the classpath and no download is attempted.
+org.apache.activemq.artemis =
org.apache.activemq:artemis-jakarta-client-all:${activemq-artemis-version}
+org.apache.activemq = org.apache.activemq:activemq-client:${activemq6-version}
+org.apache.qpid.jms =
org.apache.qpid:qpid-jms-client:${qpid-jms-client-version}
+org.messaginghub.pooled.jms = org.messaginghub:pooled-jms:${pooled-jms-version}
+com.ibm.mq =
com.ibm.mq:com.ibm.mq.jakarta.client:${com-ibm-mq-jakarta-client-version}
+org.springframework.amqp =
org.springframework.amqp:spring-rabbit:${spring-rabbitmq-version}
+org.apache.kafka.clients = org.apache.kafka:kafka-clients:${kafka-version}
+org.apache.kafka.common = org.apache.kafka:kafka-clients:${kafka-version}
+org.apache.pulsar.client = org.apache.pulsar:pulsar-client:${pulsar-version}
+org.apache.rocketmq = org.apache.rocketmq:rocketmq-client:${rocketmq-version}
+io.nats.client = io.nats:jnats:${jnats-version}
+org.eclipse.paho.client.mqttv3 =
org.eclipse.paho:org.eclipse.paho.client.mqttv3:${paho-version}
+org.eclipse.paho.mqttv5.client =
org.eclipse.paho:org.eclipse.paho.mqttv5.client:${paho-version}
+com.hivemq.client = com.hivemq:hivemq-mqtt-client:${hivemq-mqtt-client-version}
+
+# Databases, search and caches
+com.mongodb = org.mongodb:mongodb-driver-sync:${mongo-java-driver-version}
+redis.clients.jedis = redis.clients:jedis:${jedis-client-version}
+com.datastax.oss.driver =
org.apache.cassandra:java-driver-core:${cassandra-driver-version}
+com.couchbase.client =
com.couchbase.client:java-client:${couchbase-client-version}
+org.neo4j.driver = org.neo4j.driver:neo4j-java-driver:${neo4j-version}
+co.elastic.clients =
co.elastic.clients:elasticsearch-java:${elasticsearch-java-client-version}
+org.elasticsearch.client =
org.elasticsearch.client:elasticsearch-rest-client:${elasticsearch-java-client-version}
+org.opensearch.client =
org.opensearch.client:opensearch-java:${opensearch-java-client-version}
+org.apache.solr.client.solrj = org.apache.solr:solr-solrj:${solr-version}
+org.apache.lucene = org.apache.lucene:lucene-core:${lucene-version}
+org.apache.ignite = org.apache.ignite:ignite-core:${ignite-version}
+com.github.benmanes.caffeine =
com.github.ben-manes.caffeine:caffeine:${caffeine-version}
+com.hazelcast = com.hazelcast:hazelcast:${hazelcast-version}
+org.infinispan.client.hotrod =
org.infinispan:infinispan-client-hotrod:${infinispan-version}
+org.infinispan = org.infinispan:infinispan-core:${infinispan-version}
+org.ehcache = org.ehcache:ehcache:${ehcache3-version}
+org.jgroups = org.jgroups:jgroups:${jgroups-version}
+
+# Vector databases
+io.milvus = io.milvus:milvus-sdk-java:${milvus-client-version}
+io.qdrant.client = io.qdrant:client:${qdrant-client-version}
+io.weaviate.client6 = io.weaviate:client6:${weaviate-client-version}
+io.pinecone = io.pinecone:pinecone-client:${pinecone-client-version}
+
+# Cloud: AWS SDK v2, one artifact per service
+software.amazon.awssdk.services.s3 =
software.amazon.awssdk:s3:${aws-java-sdk2-version}
+software.amazon.awssdk.services.sqs =
software.amazon.awssdk:sqs:${aws-java-sdk2-version}
+software.amazon.awssdk.services.sns =
software.amazon.awssdk:sns:${aws-java-sdk2-version}
+software.amazon.awssdk.services.dynamodb =
software.amazon.awssdk:dynamodb:${aws-java-sdk2-version}
+software.amazon.awssdk.services.kinesis =
software.amazon.awssdk:kinesis:${aws-java-sdk2-version}
+software.amazon.awssdk.services.lambda =
software.amazon.awssdk:lambda:${aws-java-sdk2-version}
+software.amazon.awssdk.services.secretsmanager =
software.amazon.awssdk:secretsmanager:${aws-java-sdk2-version}
+software.amazon.awssdk.services.eventbridge =
software.amazon.awssdk:eventbridge:${aws-java-sdk2-version}
+software.amazon.awssdk.services.ses =
software.amazon.awssdk:ses:${aws-java-sdk2-version}
+
+# Cloud: Azure, versions managed by the Azure SDK BOM
+com.azure.storage.blob =
com.azure:azure-storage-blob:@bom(com.azure:azure-sdk-bom:${azure-sdk-bom-version})
+com.azure.storage.queue =
com.azure:azure-storage-queue:@bom(com.azure:azure-sdk-bom:${azure-sdk-bom-version})
+com.azure.storage.file.share =
com.azure:azure-storage-file-share:@bom(com.azure:azure-sdk-bom:${azure-sdk-bom-version})
+com.azure.storage.file.datalake =
com.azure:azure-storage-file-datalake:@bom(com.azure:azure-sdk-bom:${azure-sdk-bom-version})
+com.azure.messaging.servicebus =
com.azure:azure-messaging-servicebus:@bom(com.azure:azure-sdk-bom:${azure-sdk-bom-version})
+com.azure.messaging.eventhubs =
com.azure:azure-messaging-eventhubs:@bom(com.azure:azure-sdk-bom:${azure-sdk-bom-version})
+com.azure.messaging.eventgrid =
com.azure:azure-messaging-eventgrid:@bom(com.azure:azure-sdk-bom:${azure-sdk-bom-version})
+com.azure.cosmos =
com.azure:azure-cosmos:@bom(com.azure:azure-sdk-bom:${azure-sdk-bom-version})
+com.azure.identity =
com.azure:azure-identity:@bom(com.azure:azure-sdk-bom:${azure-sdk-bom-version})
+com.azure.security.keyvault.secrets =
com.azure:azure-security-keyvault-secrets:@bom(com.azure:azure-sdk-bom:${azure-sdk-bom-version})
+
+# Cloud: Google
+com.google.cloud.storage =
com.google.cloud:google-cloud-storage:${google-cloud-storage-version}
+com.google.cloud.pubsub =
com.google.cloud:google-cloud-pubsub:${google-cloud-pubsub-version}
+com.google.cloud.bigquery =
com.google.cloud:google-cloud-bigquery:${google-cloud-bigquery-version}
+com.google.cloud.firestore =
com.google.cloud:google-cloud-firestore:${google-cloud-firestore-version}
+com.google.cloud.secretmanager =
com.google.cloud:google-cloud-secretmanager:${google-cloud-secretmanager-version}
+com.google.cloud.functions =
com.google.cloud:google-cloud-functions:${google-cloud-functions-version}
+com.google.genai = com.google.genai:google-genai:${google-genai-version}
+
+# Cloud: Kubernetes, object storage, service discovery
+io.fabric8.kubernetes.client =
io.fabric8:kubernetes-client:${kubernetes-client-version}
+io.fabric8.openshift.client =
io.fabric8:openshift-client:${kubernetes-client-version}
+io.minio = io.minio:minio:${minio-version}
+org.kiwiproject.consul = org.kiwiproject:consul-client:${consul-client-version}
+org.apache.zookeeper = org.apache.zookeeper:zookeeper:${zookeeper-version}
+org.springframework.vault =
org.springframework.vault:spring-vault-core:${spring-vault-core-version}
+
+# SaaS clients
+com.slack.api = com.slack.api:slack-api-client:${slack-api-model-version}
+com.twilio = com.twilio.sdk:twilio:${twilio-version}
+com.stripe = com.stripe:stripe-java:${stripe-java-version}
+
+# Data: JSON, YAML, CSV, XML, binary formats and transformations
+com.fasterxml.jackson.databind =
com.fasterxml.jackson.core:jackson-databind:${jackson2-version}
+com.fasterxml.jackson.dataformat.xml =
com.fasterxml.jackson.dataformat:jackson-dataformat-xml:${jackson2-version}
+com.fasterxml.jackson.dataformat.yaml =
com.fasterxml.jackson.dataformat:jackson-dataformat-yaml:${jackson2-version}
+com.fasterxml.jackson.dataformat.csv =
com.fasterxml.jackson.dataformat:jackson-dataformat-csv:${jackson2-version}
+com.google.gson = com.google.code.gson:gson:${gson-version}
+com.alibaba.fastjson = com.alibaba:fastjson:${fastjson-version}
+org.eclipse.yasson = org.eclipse:yasson:${yasson-version}
+org.yaml.snakeyaml = org.yaml:snakeyaml:${snakeyaml-version}
+org.apache.commons.csv = org.apache.commons:commons-csv:${commons-csv-version}
+com.univocity.parsers =
com.univocity:univocity-parsers:${univocity-parsers-version}
+org.apache.avro = org.apache.avro:avro:${avro-version}
+com.google.protobuf = com.google.protobuf:protobuf-java:${protobuf-version}
+org.apache.thrift = org.apache.thrift:libthrift:${libthrift-version}
+com.bazaarvoice.jolt = com.bazaarvoice.jolt:jolt-core:${jolt-version}
+com.schibsted.spt.data.jslt = com.schibsted.spt.data:jslt:${jslt-version}
+net.thisptr.jackson.jq = net.thisptr:jackson-jq:${jackson-jq-version}
+org.xmlunit = org.xmlunit:xmlunit-core:${xmlunit-version}
+
+# Commons and utilities
+org.apache.commons.lang3 =
org.apache.commons:commons-lang3:${commons-lang3-version}
+org.apache.commons.io = commons-io:commons-io:${commons-io-version}
+com.google.common = com.google.guava:guava:${guava-version}
+org.jsoup = org.jsoup:jsoup:${jsoup-version}
+
+# Templates and documents
+freemarker = org.freemarker:freemarker:${freemarker-version}
+org.apache.velocity =
org.apache.velocity:velocity-engine-core:${velocity-version}
+org.thymeleaf = org.thymeleaf:thymeleaf:${thymeleaf-version}
+com.github.mustachejava =
com.github.spullara.mustache.java:compiler:${mustache-java-version}
+org.apache.pdfbox = org.apache.pdfbox:pdfbox:${pdfbox-version}
+org.apache.tika = org.apache.tika:tika-core:${tika-version}
+
+# HTTP, network and reactive
+org.apache.hc.client5 =
org.apache.httpcomponents.client5:httpclient5:${httpclient-version}
+org.apache.hc.core5 =
org.apache.httpcomponents.core5:httpcore5:${httpcore-version}
+io.vertx.core = io.vertx:vertx-core:${vertx-version}
+io.vertx.ext.web = io.vertx:vertx-web:${vertx-version}
+io.undertow = io.undertow:undertow-core:${undertow-version}
+io.grpc.stub = io.grpc:grpc-stub:${grpc-version}
+io.grpc.protobuf = io.grpc:grpc-protobuf:${grpc-version}
+io.grpc.netty = io.grpc:grpc-netty-shaded:${grpc-version}
+reactor.core = io.projectreactor:reactor-core:${reactor-version}
+io.reactivex = io.reactivex.rxjava2:rxjava:${rxjava2-version}
+org.apache.mina = org.apache.mina:mina-core:${mina-version}
+org.apache.sshd = org.apache.sshd:sshd-core:${sshd-version}
+com.jcraft.jsch = com.github.mwiede:jsch:${jsch-version}
+com.hierynomus.smbj = com.hierynomus:smbj:${smbj-version}
+org.eclipse.angus.mail = org.eclipse.angus:angus-mail:${angus-mail-version}
+
+# Security
+org.bouncycastle.openpgp =
org.bouncycastle:bcpg-jdk18on:${bouncycastle-version}
+org.bouncycastle.bcpg = org.bouncycastle:bcpg-jdk18on:${bouncycastle-version}
+org.bouncycastle.cms = org.bouncycastle:bcpkix-jdk18on:${bouncycastle-version}
+org.bouncycastle.cert = org.bouncycastle:bcpkix-jdk18on:${bouncycastle-version}
+org.bouncycastle.mail = org.bouncycastle:bcmail-jdk18on:${bouncycastle-version}
+org.bouncycastle = org.bouncycastle:bcprov-jdk18on:${bouncycastle-version}
+org.keycloak.admin.client =
org.keycloak:keycloak-admin-client:${keycloak-client-version}
+org.jasypt = org.jasypt:jasypt:${jasypt-version}
+
+# Observability and scripting
+io.micrometer = io.micrometer:micrometer-core:${micrometer-version}
+io.opentelemetry.api =
io.opentelemetry:opentelemetry-api:${opentelemetry-version}
+groovy = org.apache.groovy:groovy:${groovy-version}
+org.apache.groovy = org.apache.groovy:groovy:${groovy-version}
+org.codehaus.groovy = org.apache.groovy:groovy:${groovy-version}
+
+# AI (the model providers are mapped in
camel-main-known-dependencies.properties; this is the core and the SDKs)
+dev.langchain4j = dev.langchain4j:langchain4j:${langchain4j-version}
+com.openai.client.okhttp =
com.openai:openai-java-client-okhttp:${openai-java-version}
+com.openai = com.openai:openai-java-core:${openai-java-version}
+ai.docling.serve = ai.docling:docling-serve-client:${docling-java-version}
diff --git
a/dsl/camel-kamelet-main/src/main/resources/camel-main-known-dependencies.properties
b/dsl/camel-kamelet-main/src/main/resources/camel-main-known-dependencies.properties
index 7a43b2dc61bf..2f1b4675f4f9 100644
---
a/dsl/camel-kamelet-main/src/main/resources/camel-main-known-dependencies.properties
+++
b/dsl/camel-kamelet-main/src/main/resources/camel-main-known-dependencies.properties
@@ -57,22 +57,11 @@ META-INF/services/org/apache/camel/modelyaml-dumper =
camel:yaml-io
META-INF/services/org/apache/camel/modeljava-dumper = camel:java-io
META-INF/services/org/apache/camel/cron/cron-service = camel:quartz
-com.amazon.redshift.jdbc.Driver = com.amazon.redshift:redshift-jdbc42:2.1.0.33
-com.microsoft.sqlserver.jdbc.SQLServerDriver =
com.microsoft.sqlserver:mssql-jdbc:12.10.0.jre11
-com.mysql.cj.jdbc.Driver =
com.mysql:mysql-connector-j:${debezium-mysql-connector-version}
-com.zaxxer.hikari.HikariDataSource = com.zaxxer:HikariCP:6.3.0
net.sf.saxon.xpath.XPathFactoryImpl = camel:saxon
-oracle.jdbc.driver.OracleDriver = com.oracle.database.jdbc:ojdbc17:23.8.0.25.04
-org.apache.activemq.artemis.jms.client.ActiveMQConnectionFactory =
org.apache.activemq:artemis-jakarta-client-all:${activemq-artemis-version}
org.apache.camel.component.activemq.ActiveMQComponent\:embedded\=true =
org.apache.activemq:activemq-broker:${activemq-version}
org.apache.camel.component.activemq6.ActiveMQComponent\:embedded\=true =
org.apache.activemq:activemq-broker:${activemq6-version}
org.apache.camel.component.cxf.jaxrs.CxfRsEndpoint = camel:cxf-rest
org.apache.camel.component.cxf.jaxws.CxfEndpoint = camel:cxf-soap
-org.apache.commons.dbcp2.BasicDataSource =
org.apache.commons:commons-dbcp2:${commons-dbcp2-version}
-org.apache.qpid.jms.JmsConnectionFactory =
org.apache.qpid:qpid-jms-client:${qpid-jms-client-version}
-org.messaginghub.pooled.jms.JmsPoolConnectionFactory =
org.messaginghub:pooled-jms:${pooled-jms-version}
-org.postgresql.Driver = org.postgresql:postgresql:${pgjdbc-driver-version}
-org.postgresql.ds.PGSimpleDataSource =
org.postgresql:postgresql:${pgjdbc-driver-version}
dev.langchain4j.model.ollama =
dev.langchain4j:langchain4j-ollama:${langchain4j-version}
dev.langchain4j.model.openai =
dev.langchain4j:langchain4j-open-ai:${langchain4j-version}
@@ -88,3 +77,8 @@ dev.langchain4j.model.embedding.onnx =
dev.langchain4j:langchain4j-embeddings:${
org.apache.camel.component.ai.observability.GenAiObservabilityImpl =
camel:ai-observability
# camel-main property prefix (same pattern as camel.opentelemetry) — resolves
ai-observability when GenAI observability config is accessed
camel.aiObservability = camel:ai-observability
+
+# Third-party libraries (JDBC drivers, clients, SDKs) are mapped by package in
+# src/main/known-third-party-libraries.properties, generated with the versions
resolved into
+# target/classes/camel-thirdparty-known-dependencies.properties by
camel-package-maven-plugin:prepare-kamelet-main
+# (CAMEL-24809).
diff --git
a/dsl/camel-kamelet-main/src/test/java/org/apache/camel/main/download/KnownDependenciesResolverTest.java
b/dsl/camel-kamelet-main/src/test/java/org/apache/camel/main/download/KnownDependenciesResolverTest.java
index 3eba6ffc2f58..7ad7b83430e2 100644
---
a/dsl/camel-kamelet-main/src/test/java/org/apache/camel/main/download/KnownDependenciesResolverTest.java
+++
b/dsl/camel-kamelet-main/src/test/java/org/apache/camel/main/download/KnownDependenciesResolverTest.java
@@ -21,6 +21,7 @@ import org.apache.camel.tooling.maven.MavenGav;
import org.junit.jupiter.api.Test;
import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertFalse;
import static org.junit.jupiter.api.Assertions.assertNotNull;
public class KnownDependenciesResolverTest {
@@ -53,4 +54,41 @@ public class KnownDependenciesResolverTest {
public static class SomeClass {
}
+
+ @Test
+ void theShippedMappingResolvesThirdPartyClassesByPackage() {
+ // CAMEL-24809: one line per library, matched by walking up the
package; the Artemis package sits under the
+ // classic ActiveMQ one and must win for its own classes
+ KnownDependenciesResolver resolver = new KnownDependenciesResolver(new
SimpleCamelContext(), null, null);
+ resolver.loadKnownDependencies();
+
+ assertGav(resolver, "org.postgresql.ds.PGSimpleDataSource",
"org.postgresql", "postgresql");
+ assertGav(resolver, "org.postgresql.ds.PGConnectionPoolDataSource",
"org.postgresql", "postgresql");
+ assertGav(resolver, "org.h2.jdbcx.JdbcDataSource", "com.h2database",
"h2");
+ assertGav(resolver, "com.zaxxer.hikari.HikariConfig", "com.zaxxer",
"HikariCP");
+ assertGav(resolver,
"org.apache.activemq.artemis.jms.client.ActiveMQConnectionFactory",
"org.apache.activemq",
+ "artemis-jakarta-client-all");
+ assertGav(resolver, "org.apache.activemq.ActiveMQConnectionFactory",
"org.apache.activemq", "activemq-client");
+ assertGav(resolver, "org.apache.qpid.jms.JmsConnectionFactory",
"org.apache.qpid", "qpid-jms-client");
+ assertGav(resolver, "com.fasterxml.jackson.databind.ObjectMapper",
"com.fasterxml.jackson.core", "jackson-databind");
+ assertGav(resolver, "com.fasterxml.jackson.dataformat.xml.XmlMapper",
"com.fasterxml.jackson.dataformat",
+ "jackson-dataformat-xml");
+ assertGav(resolver, "org.apache.commons.csv.CSVFormat",
"org.apache.commons", "commons-csv");
+ assertGav(resolver, "software.amazon.awssdk.services.sqs.SqsClient",
"software.amazon.awssdk", "sqs");
+ assertGav(resolver, "org.infinispan.client.hotrod.RemoteCacheManager",
"org.infinispan", "infinispan-client-hotrod");
+ assertGav(resolver, "org.infinispan.manager.DefaultCacheManager",
"org.infinispan", "infinispan-core");
+ assertGav(resolver, "freemarker.template.Configuration",
"org.freemarker", "freemarker");
+ // a shared parent package is deliberately not mapped
+ assertEquals(null,
resolver.mavenGavForClass("org.apache.commons.Anything"));
+ }
+
+ private static void assertGav(KnownDependenciesResolver resolver, String
className, String groupId, String artifactId) {
+ MavenGav gav = resolver.mavenGavForClass(className);
+ assertNotNull(gav, className);
+ assertEquals(groupId, gav.getGroupId(), className);
+ assertEquals(artifactId, gav.getArtifactId(), className);
+ String version = gav.getVersion();
+ assertNotNull(version, className + " version is null");
+ assertFalse(version.startsWith("${"), className + " version is an
unresolved placeholder: " + version);
+ }
}
diff --git a/parent/pom.xml b/parent/pom.xml
index 3ca3b71227ac..8ef72df096f2 100644
--- a/parent/pom.xml
+++ b/parent/pom.xml
@@ -337,6 +337,7 @@
<!-- NullAway / Error Prone (used by -Pnullcheck profile, requires JDK
21+) -->
<error-prone-version>2.50.0</error-prone-version>
+ <hikaricp-version>6.3.0</hikaricp-version>
<nullaway-version>0.14.1</nullaway-version>
<jt400-version>21.0.7</jt400-version>
<jte-version>3.2.4</jte-version>
@@ -416,6 +417,8 @@
<opa-version>2.1.1</opa-version>
<opa-wasm-version>1.1.0</opa-wasm-version>
<mcp-java-sdk-version>2.0.1</mcp-java-sdk-version>
+ <mssql-jdbc-version>12.10.0.jre11</mssql-jdbc-version>
+ <ojdbc-version>23.8.0.25.04</ojdbc-version>
<openai-java-version>4.63.2</openai-java-version>
<openapi-generator-version>7.25.0</openapi-generator-version>
<openjpa-version>4.1.1</openjpa-version>
@@ -468,6 +471,7 @@
<reactor-version>3.8.7</reactor-version>
<reactor-netty-version>1.3.7</reactor-netty-version>
<redisson-version>4.7.0</redisson-version>
+ <redshift-jdbc-version>2.1.0.33</redshift-jdbc-version>
<resilience4j-version>2.4.0</resilience4j-version>
<rest-assured-version>6.0.1</rest-assured-version>
<roaster-version>2.31.1.Final</roaster-version>
diff --git
a/tooling/maven/camel-package-maven-plugin/src/main/java/org/apache/camel/maven/packaging/PrepareKameletMainMojo.java
b/tooling/maven/camel-package-maven-plugin/src/main/java/org/apache/camel/maven/packaging/PrepareKameletMainMojo.java
index 19af7daae8a2..1180b0c23340 100644
---
a/tooling/maven/camel-package-maven-plugin/src/main/java/org/apache/camel/maven/packaging/PrepareKameletMainMojo.java
+++
b/tooling/maven/camel-package-maven-plugin/src/main/java/org/apache/camel/maven/packaging/PrepareKameletMainMojo.java
@@ -17,21 +17,36 @@
package org.apache.camel.maven.packaging;
import java.io.File;
+import java.io.FileInputStream;
import java.io.IOError;
import java.io.IOException;
import java.io.InputStream;
+import java.nio.file.Files;
import java.nio.file.Path;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
+import java.util.LinkedHashMap;
import java.util.List;
+import java.util.Locale;
import java.util.Map;
import java.util.Objects;
+import java.util.Properties;
+import java.util.TreeMap;
import java.util.TreeSet;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
import java.util.stream.Collectors;
+import java.util.zip.ZipFile;
import javax.inject.Inject;
+import javax.xml.parsers.DocumentBuilderFactory;
+
+import org.w3c.dom.Document;
+import org.w3c.dom.Element;
+import org.w3c.dom.Node;
+import org.w3c.dom.NodeList;
import org.apache.camel.tooling.model.ArtifactModel;
import org.apache.camel.tooling.model.BaseModel;
@@ -45,6 +60,11 @@ import org.apache.maven.plugins.annotations.Mojo;
import org.apache.maven.plugins.annotations.Parameter;
import org.apache.maven.project.MavenProject;
import org.codehaus.plexus.build.BuildContext;
+import org.eclipse.aether.RepositorySystem;
+import org.eclipse.aether.RepositorySystemSession;
+import org.eclipse.aether.artifact.DefaultArtifact;
+import org.eclipse.aether.repository.RemoteRepository;
+import org.eclipse.aether.resolution.ArtifactRequest;
import static org.apache.camel.tooling.util.PackageHelper.loadText;
@@ -77,12 +97,38 @@ public class PrepareKameletMainMojo extends AbstractMojo {
@Parameter(defaultValue = "src/generated/")
protected File genDir;
+ /**
+ * The third-party libraries camel run downloads on demand, mapped by
package (CAMEL-24809): the input of
+ * camel-thirdparty-known-dependencies.properties.
+ */
+ @Parameter(defaultValue =
"src/main/known-third-party-libraries.properties")
+ protected File thirdPartyLibraries;
+
+ /**
+ * Resolve every third-party jar and check that the mapped package is in
it. Downloads the jars, so it is off by
+ * default and meant for CI: -Dcamel.known-dependencies.verify=true
+ */
+ @Parameter(defaultValue = "false", property =
"camel.known-dependencies.verify")
+ protected boolean verifyThirdPartyJars;
+
+ private final RepositorySystem repoSystem;
+
+ @Parameter(defaultValue = "${repositorySystemSession}", readonly = true,
required = true)
+ private RepositorySystemSession repoSession;
+
+ @Parameter(defaultValue = "${project.remoteProjectRepositories}", readonly
= true, required = true)
+ private List<RemoteRepository> repositories;
+
+ private static final Pattern PROPERTY_VERSION =
Pattern.compile("^\\$\\{([^}]+)\\}$");
+ private static final Pattern BOM_VERSION =
Pattern.compile("^@bom\\(([^:]+):([^:]+):\\$\\{([^}]+)\\}\\)$");
+
private final Map<Path, BaseModel<?>> allModels = new HashMap<>();
private String licenseHeader;
@Inject
- public PrepareKameletMainMojo(BuildContext buildContext) {
+ public PrepareKameletMainMojo(BuildContext buildContext, RepositorySystem
repoSystem) {
this.buildContext = buildContext;
+ this.repoSystem = repoSystem;
}
/**
@@ -104,6 +150,180 @@ public class PrepareKameletMainMojo extends AbstractMojo {
} catch (Exception e) {
throw new MojoFailureException("Error updating
camel-factoryfinder-known-dependencies.properties", e);
}
+ try {
+ updateKnownThirdPartyDependencies();
+ } catch (MojoFailureException e) {
+ throw e;
+ } catch (Exception e) {
+ throw new MojoFailureException("Error updating
camel-thirdparty-known-dependencies.properties", e);
+ }
+ }
+
+ /**
+ * Generates camel-thirdparty-known-dependencies.properties from the
curated list of third-party libraries
+ * (CAMEL-24809). Each input line maps a package to
groupId:artifactId:version, where the version is a ${property}
+ * of the project (inherited from camel-parent) or
@bom(groupId:artifactId:${property}) for a library whose version
+ * a BOM manages. The version is resolved here, so the runtime needs no
lookup, and a property that does not exist
+ * or a literal version fails the build. With verifyThirdPartyJars every
jar is resolved and the mapped package must
+ * be found in it.
+ */
+ protected void updateKnownThirdPartyDependencies() throws Exception {
+ File input = thirdPartyLibraries.isAbsolute()
+ ? thirdPartyLibraries : new File(project.getBasedir(),
thirdPartyLibraries.getPath());
+ if (!input.exists()) {
+ getLog().info("No " + input + ":
camel-thirdparty-known-dependencies.properties not generated");
+ return;
+ }
+ Properties in = new Properties();
+ try (InputStream is = new FileInputStream(input)) {
+ in.load(is);
+ }
+ Map<String, String> boms = new LinkedHashMap<>();
+ List<String> problems = new ArrayList<>();
+ Map<String, String> resolved = new TreeMap<>();
+ for (String pkg : in.stringPropertyNames()) {
+ String gav = in.getProperty(pkg).trim();
+ int i = gav.indexOf(':');
+ int j = gav.indexOf(':', i + 1);
+ if (i < 0 || j < 0) {
+ problems.add(pkg + " = " + gav + ": expected
groupId:artifactId:version");
+ continue;
+ }
+ String groupId = gav.substring(0, i);
+ String artifactId = gav.substring(i + 1, j);
+ String version = gav.substring(j + 1);
+ Matcher pm = PROPERTY_VERSION.matcher(version);
+ Matcher bm = BOM_VERSION.matcher(version);
+ if (pm.matches()) {
+ String value =
project.getProperties().getProperty(pm.group(1));
+ if (value == null) {
+ problems.add(pkg + ": no property " + pm.group(1) + " in
parent/pom.xml");
+ continue;
+ }
+ version = value;
+ } else if (bm.matches()) {
+ String bomVersion =
project.getProperties().getProperty(bm.group(3));
+ if (bomVersion == null) {
+ problems.add(pkg + ": no property " + bm.group(3) + " in
parent/pom.xml");
+ continue;
+ }
+ String bomKey = bm.group(1) + ":" + bm.group(2) + ":" +
bomVersion;
+ version = managedVersion(boms, bomKey, groupId, artifactId);
+ if (version == null) {
+ problems.add(pkg + ": " + groupId + ":" + artifactId + "
is not managed by " + bomKey);
+ continue;
+ }
+ } else {
+ problems.add(pkg + " = " + gav + ": a literal version; add a
<" + artifactId.toLowerCase(Locale.ROOT)
+ + "-version> property to parent/pom.xml and use
${...}");
+ continue;
+ }
+ resolved.put(pkg, groupId + ":" + artifactId + ":" + version);
+ }
+ if (!problems.isEmpty()) {
+ throw new MojoFailureException("Problems in " + input + ":\n " +
String.join("\n ", problems));
+ }
+ if (verifyThirdPartyJars) {
+ verifyPackagesInJars(resolved);
+ }
+ List<String> lines = new ArrayList<>();
+ lines.add("# Generated by
camel-package-maven-plugin:prepare-kamelet-main from src/main/"
+ + input.getName() + " (CAMEL-24809). Do not edit.");
+ lines.add("# Third-party libraries camel run downloads on demand,
mapped by package; the resolver matches the class"
+ + " and then each enclosing package.");
+ for (Map.Entry<String, String> e : resolved.entrySet()) {
+ lines.add(e.getKey() + " = " + e.getValue());
+ }
+ try (InputStream is =
getClass().getClassLoader().getResourceAsStream("license-header.txt")) {
+ this.licenseHeader = loadText(is);
+ } catch (Exception e) {
+ throw new MojoFailureException("Error loading license-header.txt
file", e);
+ }
+ // into target/classes, not src/generated: the file is the input with
the versions resolved, so keeping both
+ // in the repository would duplicate 146 lines; it is regenerated on
every build and shipped in the jar
+ Path out = Path.of(project.getBuild().getOutputDirectory(),
"camel-thirdparty-known-dependencies.properties");
+ Files.createDirectories(out.getParent());
+ updateResource(buildContext, out, licenseHeader + "\n" +
String.join("\n", lines) + "\n");
+ getLog().info("Generated " + out.getFileName() + " with " +
resolved.size() + " libraries");
+ }
+
+ private String managedVersion(Map<String, String> cache, String bomKey,
String groupId, String artifactId)
+ throws Exception {
+ String key = bomKey + "->" + groupId + ":" + artifactId;
+ if (cache.containsKey(key)) {
+ return cache.get(key);
+ }
+ String[] parts = bomKey.split(":");
+ ArtifactRequest req = new
ArtifactRequest().setRepositories(repositories)
+ .setArtifact(new DefaultArtifact(parts[0], parts[1], "pom",
parts[2]));
+ File pom = repoSystem.resolveArtifact(repoSession,
req).getArtifact().getFile();
+ DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
+ dbf.setFeature("http://apache.org/xml/features/disallow-doctype-decl",
true);
+ Document doc = dbf.newDocumentBuilder().parse(pom);
+ String found = null;
+ NodeList deps = doc.getElementsByTagName("dependency");
+ for (int n = 0; n < deps.getLength() && found == null; n++) {
+ Element d = (Element) deps.item(n);
+ if (groupId.equals(text(d, "groupId")) &&
artifactId.equals(text(d, "artifactId"))) {
+ found = text(d, "version");
+ }
+ }
+ if (found != null && found.startsWith("${") && found.endsWith("}")) {
+ // a BOM that versions its artifacts through its own properties
+ String prop = found.substring(2, found.length() - 1);
+ String value = "project.version".equals(prop) ? parts[2] : null;
+ NodeList props = doc.getElementsByTagName("properties");
+ for (int n = 0; n < props.getLength() && value == null; n++) {
+ value = text((Element) props.item(n), prop);
+ }
+ found = value;
+ }
+ cache.put(key, found);
+ return found;
+ }
+
+ private static String text(Element parent, String child) {
+ for (Node n = parent.getFirstChild(); n != null; n =
n.getNextSibling()) {
+ if (n.getNodeType() == Node.ELEMENT_NODE &&
child.equals(n.getNodeName())) {
+ return n.getTextContent().trim();
+ }
+ }
+ return null;
+ }
+
+ private void verifyPackagesInJars(Map<String, String> resolved) throws
Exception {
+ List<String> problems = new ArrayList<>();
+ Map<String, File> jars = new LinkedHashMap<>();
+ for (Map.Entry<String, String> e : resolved.entrySet()) {
+ String gav = e.getValue();
+ File jar = jars.get(gav);
+ if (jar == null) {
+ String[] parts = gav.split(":");
+ ArtifactRequest req = new
ArtifactRequest().setRepositories(repositories)
+ .setArtifact(new DefaultArtifact(parts[0], parts[1],
"jar", parts[2]));
+ try {
+ jar = repoSystem.resolveArtifact(repoSession,
req).getArtifact().getFile();
+ } catch (Exception ex) {
+ problems.add(e.getKey() + ": cannot resolve " + gav + ": "
+ ex.getMessage());
+ continue;
+ }
+ jars.put(gav, jar);
+ }
+ String dir = e.getKey().replace('.', '/') + "/";
+ boolean found;
+ try (ZipFile zip = new ZipFile(jar)) {
+ found = zip.stream().anyMatch(z -> z.getName().startsWith(dir)
&& z.getName().endsWith(".class"));
+ }
+ if (!found) {
+ problems.add(e.getKey() + ": no classes under " + dir + " in "
+ gav);
+ }
+ }
+ if (!problems.isEmpty()) {
+ throw new MojoFailureException(
+ "Third-party known dependencies do not match their jars:\n
"
+ + String.join("\n ", problems));
+ }
+ getLog().info("Verified " + resolved.size() + " third-party packages
against " + jars.size() + " jars");
}
protected void updateKnownDependencies() throws Exception {