This is an automated email from the ASF dual-hosted git repository.
Croway 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 1f7f229fdc6d CAMEL-25270: Use catalog coordinates for Camel shorthand
dependencies (#27285)
1f7f229fdc6d is described below
commit 1f7f229fdc6d4d63e5d39caeea44845831778ba3
Author: Federico Mariani <[email protected]>
AuthorDate: Fri Oct 2 16:41:05 2026 +0200
CAMEL-25270: Use catalog coordinates for Camel shorthand dependencies
(#27285)
* CAMEL-25270: Resolve Camel shorthand dependencies from catalog
* CAMEL-25270: Share catalog resolution across dependency paths
* CAMEL-25270: Cache catalog dependency resolution
* CAMEL-25270: Avoid retaining catalog models for dependency lookups
---
.../modules/ROOT/pages/camel-jbang-running.adoc | 8 +-
.../main/download/CatalogDependencyResolver.java | 62 +++++++++++++++
.../download/CommandLineDependencyDownloader.java | 21 ++----
.../main/download/DependencyDownloaderKamelet.java | 14 ++--
.../download/DependencyDownloaderStrategy.java | 2 +-
.../main/download/KnownDependenciesResolver.java | 2 +-
.../CommandLineDependencyDownloaderTest.java | 88 ++++++++++++++++++++++
.../download/KnownDependenciesResolverTest.java | 21 ++++++
8 files changed, 194 insertions(+), 24 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 f4aa1f4799b8..2f5337b3e4ba 100644
--- a/docs/user-manual/modules/ROOT/pages/camel-jbang-running.adoc
+++ b/docs/user-manual/modules/ROOT/pages/camel-jbang-running.adoc
@@ -158,6 +158,10 @@ For Camel dependencies, use the shorthand syntax:
camel run foo.java --dep=camel-saxon
----
+The `camel:` and `camel-` shorthand forms use the dependency coordinates in
the Camel catalog when available.
+For artifacts absent from the catalog, they use the running Camel version.
+An explicit Maven coordinate keeps the version you specify.
+
Multiple dependencies can be separated by comma:
[source,bash]
@@ -270,8 +274,8 @@ The lookup walks up from the class name:
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
+3. The value is a Maven coordinate. A `camel:xxx` short form uses its Camel
catalog coordinates
+ when available, or the running Camel version otherwise. 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,
diff --git
a/dsl/camel-kamelet-main/src/main/java/org/apache/camel/main/download/CatalogDependencyResolver.java
b/dsl/camel-kamelet-main/src/main/java/org/apache/camel/main/download/CatalogDependencyResolver.java
new file mode 100644
index 000000000000..2749fb1df25d
--- /dev/null
+++
b/dsl/camel-kamelet-main/src/main/java/org/apache/camel/main/download/CatalogDependencyResolver.java
@@ -0,0 +1,62 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.camel.main.download;
+
+import java.util.Optional;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.ConcurrentMap;
+
+import org.apache.camel.catalog.CamelCatalog;
+import org.apache.camel.catalog.DefaultCamelCatalog;
+import org.apache.camel.tooling.maven.MavenGav;
+import org.apache.camel.tooling.model.ArtifactModel;
+
+final class CatalogDependencyResolver {
+
+ private static final CamelCatalog CATALOG = new DefaultCamelCatalog();
+ private static final ConcurrentMap<String, Optional<Coordinates>>
COORDINATES = new ConcurrentHashMap<>();
+
+ private CatalogDependencyResolver() {
+ }
+
+ static MavenGav resolve(String dependency, String defaultVersion) {
+ MavenGav gav = MavenGav.parseGav(dependency, defaultVersion);
+ if (isCamelShorthand(dependency)) {
+ COORDINATES.computeIfAbsent(gav.getArtifactId(),
CatalogDependencyResolver::lookup)
+ .ifPresent(coordinates -> {
+ gav.setGroupId(coordinates.groupId());
+ gav.setArtifactId(coordinates.artifactId());
+ gav.setVersion(coordinates.version());
+ });
+ }
+ return gav;
+ }
+
+ private static Optional<Coordinates> lookup(String artifactId) {
+ ArtifactModel<?> model = CATALOG.modelFromMavenGAV("org.apache.camel",
artifactId, null);
+ return Optional.ofNullable(model)
+ .map(m -> new Coordinates(m.getGroupId(), m.getArtifactId(),
m.getVersion()));
+ }
+
+ static boolean isCamelShorthand(String dependency) {
+ return dependency.startsWith("camel:")
+ || (dependency.startsWith("camel-") &&
!(dependency.contains(":") || dependency.contains("/")));
+ }
+
+ private record Coordinates(String groupId, String artifactId, String
version) {
+ }
+}
diff --git
a/dsl/camel-kamelet-main/src/main/java/org/apache/camel/main/download/CommandLineDependencyDownloader.java
b/dsl/camel-kamelet-main/src/main/java/org/apache/camel/main/download/CommandLineDependencyDownloader.java
index df053597081a..b50f8e12f3fd 100644
---
a/dsl/camel-kamelet-main/src/main/java/org/apache/camel/main/download/CommandLineDependencyDownloader.java
+++
b/dsl/camel-kamelet-main/src/main/java/org/apache/camel/main/download/CommandLineDependencyDownloader.java
@@ -41,34 +41,25 @@ public class CommandLineDependencyDownloader extends
ServiceSupport {
}
private void downloadDependencies() {
- final List<String> gavs = new ArrayList<>();
+ final List<MavenGav> gavs = new ArrayList<>();
for (String dep : dependencies.split(",")) {
- // trim whitespace
dep = dep.trim();
- String gav = dep;
- gav = gav.trim();
- if (dep.startsWith("camel:") || dep.startsWith("camel-")) {
- // it's a known camel component
- gav = "org.apache.camel:camel-" + dep.substring(6) + ":" +
camelContext.getVersion();
- }
+ MavenGav gav = CatalogDependencyResolver.resolve(dep,
camelContext.getVersion());
if (isValidGav(gav)) {
gavs.add(gav);
}
}
if (!gavs.isEmpty()) {
- for (String gav : gavs) {
- MavenGav mg = MavenGav.parseGav(gav,
camelContext.getVersion());
- downloader.downloadDependency(mg.getGroupId(),
mg.getArtifactId(),
- mg.getVersion());
+ for (MavenGav gav : gavs) {
+ downloader.downloadDependency(gav.getGroupId(),
gav.getArtifactId(), gav.getVersion());
}
}
}
- private boolean isValidGav(String gav) {
- MavenGav mg = MavenGav.parseGav(gav, camelContext.getVersion());
+ private boolean isValidGav(MavenGav gav) {
boolean exists
- = downloader.alreadyOnClasspath(mg.getGroupId(),
mg.getArtifactId(), mg.getVersion());
+ = downloader.alreadyOnClasspath(gav.getGroupId(),
gav.getArtifactId(), gav.getVersion());
// valid if not already on classpath
return !exists;
}
diff --git
a/dsl/camel-kamelet-main/src/main/java/org/apache/camel/main/download/DependencyDownloaderKamelet.java
b/dsl/camel-kamelet-main/src/main/java/org/apache/camel/main/download/DependencyDownloaderKamelet.java
index 4f189c6ec2da..cc16f250f10f 100644
---
a/dsl/camel-kamelet-main/src/main/java/org/apache/camel/main/download/DependencyDownloaderKamelet.java
+++
b/dsl/camel-kamelet-main/src/main/java/org/apache/camel/main/download/DependencyDownloaderKamelet.java
@@ -178,10 +178,14 @@ public final class DependencyDownloaderKamelet extends
ServiceSupport
private void downloadDependencies(List<String> dependencies) {
final List<String> gavs = new ArrayList<>();
for (String dep : dependencies) {
+ if ("camel:core".equals(dep) || "camel-core".equals(dep)
+ || "camel:kamelet".equals(dep) ||
"camel-kamelet".equals(dep)) {
+ continue;
+ }
String gav = dep;
- if (dep.startsWith("camel:")) {
- // it's a known camel component
- gav = "org.apache.camel:camel-" +
dep.substring("camel:".length()) + ":" + camelContext.getVersion();
+ if (CatalogDependencyResolver.isCamelShorthand(dep)) {
+ MavenGav resolved = CatalogDependencyResolver.resolve(dep,
camelContext.getVersion());
+ gav = resolved.getGroupId() + ":" +
resolved.getArtifactId() + ":" + resolved.getVersion();
} else if (dep.startsWith("camel-kamelets:")) {
// it's a known camel kamelets dependency
gav = "org.apache.camel.kamelets:camel-kamelets-" +
dep.substring("camel-kamelets:".length()) + ":"
@@ -194,7 +198,7 @@ public final class DependencyDownloaderKamelet extends
ServiceSupport
if (!gavs.isEmpty()) {
for (String gav : gavs) {
- MavenGav mg = MavenGav.parseGav(gav,
camelContext.getVersion());
+ MavenGav mg = CatalogDependencyResolver.resolve(gav,
camelContext.getVersion());
downloader.downloadDependency(mg.getGroupId(),
mg.getArtifactId(), mg.getVersion());
downloaded.add(gav);
}
@@ -212,7 +216,7 @@ public final class DependencyDownloaderKamelet extends
ServiceSupport
return false;
}
- MavenGav mg = MavenGav.parseGav(gav, camelContext.getVersion());
+ MavenGav mg = CatalogDependencyResolver.resolve(gav,
camelContext.getVersion());
boolean exists = downloader.alreadyOnClasspath(mg.getGroupId(),
mg.getArtifactId(), mg.getVersion());
// valid if not already on classpath
return !exists;
diff --git
a/dsl/camel-kamelet-main/src/main/java/org/apache/camel/main/download/DependencyDownloaderStrategy.java
b/dsl/camel-kamelet-main/src/main/java/org/apache/camel/main/download/DependencyDownloaderStrategy.java
index 118ef4ee2f84..cb122a0cdb93 100644
---
a/dsl/camel-kamelet-main/src/main/java/org/apache/camel/main/download/DependencyDownloaderStrategy.java
+++
b/dsl/camel-kamelet-main/src/main/java/org/apache/camel/main/download/DependencyDownloaderStrategy.java
@@ -32,7 +32,7 @@ public class DependencyDownloaderStrategy implements
DependencyStrategy {
@Override
public void onDependency(String dependency) {
- MavenGav gav = MavenGav.parseGav(dependency,
camelContext.getVersion());
+ MavenGav gav = CatalogDependencyResolver.resolve(dependency,
camelContext.getVersion());
if (!downloader.alreadyOnClasspath(gav.getGroupId(),
gav.getArtifactId(), gav.getVersion())) {
downloader.downloadDependency(gav.getGroupId(),
gav.getArtifactId(),
gav.getVersion());
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 771274d557cf..ced1bb4c50b2 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
@@ -128,7 +128,7 @@ public final class KnownDependenciesResolver {
private MavenGav toMavenGav(String gav) {
MavenGav answer = null;
if (gav != null) {
- answer = MavenGav.parseGav(gav, camelContext.getVersion());
+ answer = CatalogDependencyResolver.resolve(gav,
camelContext.getVersion());
}
if (answer != null) {
String v = answer.getVersion();
diff --git
a/dsl/camel-kamelet-main/src/test/java/org/apache/camel/main/download/CommandLineDependencyDownloaderTest.java
b/dsl/camel-kamelet-main/src/test/java/org/apache/camel/main/download/CommandLineDependencyDownloaderTest.java
new file mode 100644
index 000000000000..449ae9264369
--- /dev/null
+++
b/dsl/camel-kamelet-main/src/test/java/org/apache/camel/main/download/CommandLineDependencyDownloaderTest.java
@@ -0,0 +1,88 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.camel.main.download;
+
+import java.lang.reflect.Proxy;
+import java.util.ArrayList;
+import java.util.List;
+
+import org.apache.camel.catalog.CamelCatalog;
+import org.apache.camel.catalog.DefaultCamelCatalog;
+import org.apache.camel.impl.engine.SimpleCamelContext;
+import org.junit.jupiter.api.Test;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+
+class CommandLineDependencyDownloaderTest {
+
+ @Test
+ void
camelShorthandUsesCatalogCoordinatesAndFallsBackForUncatalogedArtifacts()
throws Exception {
+ CamelCatalog catalog = new DefaultCamelCatalog();
+ List<String> downloaded = new ArrayList<>();
+ try (SimpleCamelContext context = new SimpleCamelContext() {
+ @Override
+ public String getVersion() {
+ return "0.0.1";
+ }
+ }) {
+ context.addService(recordingDownloader(context, downloaded));
+
+ CommandLineDependencyDownloader command = new
CommandLineDependencyDownloader(
+ context,
+ "camel:whatsapp, camel-openapi-validator,
camel:uncataloged, org.apache.camel:camel-whatsapp:1.2.3");
+ command.init();
+ }
+
+ assertEquals(List.of(
+ catalog.componentModel("whatsapp").toGav(),
+ catalog.otherModel("openapi-validator").toGav(),
+ "org.apache.camel:camel-uncataloged:0.0.1",
+ "org.apache.camel:camel-whatsapp:1.2.3"), downloaded);
+ }
+
+ private static DependencyDownloader recordingDownloader(SimpleCamelContext
context, List<String> downloaded) {
+ return (DependencyDownloader) Proxy.newProxyInstance(
+ CommandLineDependencyDownloaderTest.class.getClassLoader(),
+ new Class[] { DependencyDownloader.class },
+ (proxy, method, args) -> {
+ switch (method.getName()) {
+ case "hashCode" -> {
+ return System.identityHashCode(proxy);
+ }
+ case "equals" -> {
+ return proxy == args[0];
+ }
+ case "toString" -> {
+ return "RecordingDependencyDownloader";
+ }
+ case "downloadDependency" -> {
+ downloaded.add(args[0] + ":" + args[1] + ":" +
args[2]);
+ return null;
+ }
+ case "alreadyOnClasspath" -> {
+ return false;
+ }
+ case "getCamelContext" -> {
+ return context;
+ }
+ default -> {
+ return method.getReturnType() == boolean.class ?
Boolean.FALSE : null;
+ }
+ }
+ });
+ }
+}
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 53df882bace7..369a60b7440f 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
@@ -16,6 +16,9 @@
*/
package org.apache.camel.main.download;
+import java.util.Map;
+
+import org.apache.camel.catalog.DefaultCamelCatalog;
import org.apache.camel.impl.engine.SimpleCamelContext;
import org.apache.camel.tooling.maven.MavenGav;
import org.junit.jupiter.api.Test;
@@ -27,6 +30,24 @@ import static org.junit.jupiter.api.Assertions.assertNull;
public class KnownDependenciesResolverTest {
+ @Test
+ void camelShorthandMappingsUseCatalogCoordinates() {
+ SimpleCamelContext context = new SimpleCamelContext() {
+ @Override
+ public String getVersion() {
+ return "0.0.1";
+ }
+ };
+ KnownDependenciesResolver resolver = new
KnownDependenciesResolver(context, null, null);
+ resolver.addMappings(Map.of(
+ "org.example.Cataloged", "camel:whatsapp",
+ "org.example.Uncataloged", "camel:uncataloged"));
+
+ assertEquals(new
DefaultCamelCatalog().componentModel("whatsapp").getVersion(),
+
resolver.mavenGavForClass("org.example.Cataloged").getVersion());
+ assertEquals("0.0.1",
resolver.mavenGavForClass("org.example.Uncataloged").getVersion());
+ }
+
@Test
void mavenGavForClass_returnsClassScopedDependency() {
KnownDependenciesResolver resolver = new KnownDependenciesResolver(new
SimpleCamelContext(), null, null);