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);

Reply via email to