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 e9a09c926cc CAMEL-22098: camel-jbang - camel dependency runtime e9a09c926cc is described below commit e9a09c926cce01035df7562260270f5665adb8ed Author: Claus Ibsen <claus.ib...@gmail.com> AuthorDate: Wed May 21 19:44:17 2025 +0200 CAMEL-22098: camel-jbang - camel dependency runtime --- .../dsl/jbang/core/commands/CamelJBangMain.java | 3 +- .../dsl/jbang/core/commands/DependencyRuntime.java | 188 +++++++++++++++++++++ .../camel/dsl/jbang/core/commands/RunHelper.java | 9 +- .../camel/dsl/jbang/core/common/CatalogLoader.java | 56 ++++-- .../jbang/core/commands/DependencyListTest.java | 5 +- .../camel/main/download/DependencyDownloader.java | 11 ++ .../main/download/MavenDependencyDownloader.java | 17 +- 7 files changed, 264 insertions(+), 25 deletions(-) diff --git a/dsl/camel-jbang/camel-jbang-core/src/main/java/org/apache/camel/dsl/jbang/core/commands/CamelJBangMain.java b/dsl/camel-jbang/camel-jbang-core/src/main/java/org/apache/camel/dsl/jbang/core/commands/CamelJBangMain.java index 52834122c6f..b18d6cd299d 100644 --- a/dsl/camel-jbang/camel-jbang-core/src/main/java/org/apache/camel/dsl/jbang/core/commands/CamelJBangMain.java +++ b/dsl/camel-jbang/camel-jbang-core/src/main/java/org/apache/camel/dsl/jbang/core/commands/CamelJBangMain.java @@ -143,7 +143,8 @@ public class CamelJBangMain implements Callable<Integer> { .addSubcommand("dependency", new CommandLine(new DependencyCommand(main)) .addSubcommand("list", new CommandLine(new DependencyList(main))) .addSubcommand("copy", new CommandLine(new DependencyCopy(main))) - .addSubcommand("update", new CommandLine(new DependencyUpdate(main)))) + .addSubcommand("update", new CommandLine(new DependencyUpdate(main))) + .addSubcommand("runtime", new CommandLine(new DependencyRuntime(main)))) .addSubcommand("catalog", new CommandLine(new CatalogCommand(main)) .addSubcommand("component", new CommandLine(new CatalogComponent(main))) .addSubcommand("dataformat", new CommandLine(new CatalogDataFormat(main))) diff --git a/dsl/camel-jbang/camel-jbang-core/src/main/java/org/apache/camel/dsl/jbang/core/commands/DependencyRuntime.java b/dsl/camel-jbang/camel-jbang-core/src/main/java/org/apache/camel/dsl/jbang/core/commands/DependencyRuntime.java new file mode 100644 index 00000000000..20f6941e472 --- /dev/null +++ b/dsl/camel-jbang/camel-jbang-core/src/main/java/org/apache/camel/dsl/jbang/core/commands/DependencyRuntime.java @@ -0,0 +1,188 @@ +/* + * 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.dsl.jbang.core.commands; + +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.util.LinkedHashMap; +import java.util.Map; +import java.util.StringJoiner; + +import javax.xml.parsers.DocumentBuilder; +import javax.xml.parsers.DocumentBuilderFactory; + +import org.w3c.dom.Document; +import org.w3c.dom.Element; +import org.w3c.dom.NodeList; + +import org.apache.camel.catalog.CamelCatalog; +import org.apache.camel.dsl.jbang.core.common.CatalogLoader; +import org.apache.camel.dsl.jbang.core.common.CommandLineHelper; +import org.apache.camel.dsl.jbang.core.common.XmlHelper; +import org.apache.camel.util.json.Jsoner; +import picocli.CommandLine; + +@CommandLine.Command(name = "runtime", + description = "Display Camel runtime and version for given Maven project", sortOptions = false, + showDefaultValues = true) +public class DependencyRuntime extends CamelCommand { + + protected static final String EXPORT_DIR = CommandLineHelper.CAMEL_JBANG_WORK_DIR + "/export"; + + @CommandLine.Option(names = { "--json" }, + description = "Output in JSON Format") + boolean jsonOutput; + + public DependencyRuntime(CamelJBangMain main) { + super(main); + } + + @Override + public Integer doCall() throws Exception { + // read pom.xml + Path pom = Paths.get(".").resolve("pom.xml"); + if (Files.exists(pom)) { + DocumentBuilderFactory dbf = XmlHelper.createDocumentBuilderFactory(); + DocumentBuilder db = dbf.newDocumentBuilder(); + Document dom = db.parse(Files.newInputStream(pom)); + NodeList nl = dom.getElementsByTagName("dependency"); + String camelVersion = null; + String camelQuarkusVersion = null; + String springBootVersion = null; + String quarkusVersion = null; + String quarkusGroupId = "io.quarkus.platform"; + for (int i = 0; i < nl.getLength(); i++) { + Element node = (Element) nl.item(i); + + // must be child at <project/dependencyManagement> or <project/dependencies> + String p = node.getParentNode().getNodeName(); + String p2 = node.getParentNode().getParentNode().getNodeName(); + boolean accept = ("dependencyManagement".equals(p2) || "project".equals(p2)) && (p.equals("dependencies")); + if (!accept) { + continue; + } + + String g = node.getElementsByTagName("groupId").item(0).getTextContent(); + String a = node.getElementsByTagName("artifactId").item(0).getTextContent(); + String v = null; + NodeList vl = node.getElementsByTagName("version"); + if (vl.getLength() > 0) { + v = vl.item(0).getTextContent(); + } + + // BOMs + if ("org.apache.camel".equals(g) && "camel-bom".equals(a)) { + camelVersion = v; + continue; + } + if ("org.apache.camel.springboot".equals(g) && "camel-spring-boot-bom".equals(a)) { + camelVersion = v; + continue; + } + if ("org.springframework.boot".equals(g) && "spring-boot-dependencies".equals(a)) { + springBootVersion = v; + continue; + } + if (("${quarkus.platform.group-id}".equals(g) || "io.quarkus.platform".equals(g)) && + ("${quarkus.platform.artifact-id}".equals(a) || "quarkus-bom".equals(a))) { + if ("${quarkus.platform.version}".equals(v)) { + quarkusVersion = dom.getElementsByTagName("quarkus.platform.version").item(0).getTextContent(); + } else { + quarkusVersion = v; + } + continue; + } + if (("${quarkus.platform.group-id}".equals(g))) { + quarkusGroupId = dom.getElementsByTagName("quarkus.platform.group-id").item(0).getTextContent(); + } + } + + String repos = null; + StringJoiner sj = new StringJoiner(","); + nl = dom.getElementsByTagName("repository"); + for (int i = 0; i < nl.getLength(); i++) { + Element node = (Element) nl.item(i); + + // must be child at <repositories/repository> + String p = node.getParentNode().getNodeName(); + boolean accept = "repositories".equals(p); + if (!accept) { + continue; + } + String url = node.getElementsByTagName("url").item(0).getTextContent(); + sj.add(url); + } + if (sj.length() > 0) { + repos = sj.toString(); + } + + // its a bit harder to know the camel version from Quarkus because of the universal BOM + if (quarkusVersion != null && camelVersion == null) { + CamelCatalog catalog = CatalogLoader.loadQuarkusCatalog(repos, quarkusVersion, quarkusGroupId); + if (catalog != null) { + // find out the camel quarkus version via the constant language that are built-in camel-core + camelQuarkusVersion = catalog.languageModel("constant").getVersion(); + // okay so the camel version is also hard to resolve from quarkus + camelVersion = CatalogLoader.resolveCamelVersionFromQuarkus(repos, camelQuarkusVersion); + } + } + + String runtime = "camel-main"; + if (springBootVersion != null) { + runtime = "camel-spring-boot"; + } else if (quarkusVersion != null) { + runtime = "camel-quarkus"; + } + + if (jsonOutput) { + Map<String, String> map = new LinkedHashMap<>(); + map.put("runtime", runtime); + if (camelVersion != null) { + map.put("camelVersion", camelVersion); + } + if (camelQuarkusVersion != null) { + map.put("camelQuarkusVersion", camelQuarkusVersion); + } + if (springBootVersion != null) { + map.put("springBootVersion", springBootVersion); + } + if (quarkusVersion != null) { + map.put("quarkusVersion", quarkusVersion); + } + printer().println( + Jsoner.serialize(map)); + } else { + printer().println("Runtime: " + runtime); + if (camelVersion != null) { + printer().println("Camel Version: " + camelVersion); + } + if (camelQuarkusVersion != null) { + printer().println("Camel Quarkus Version: " + camelQuarkusVersion); + } + if (springBootVersion != null) { + printer().println("Spring Boot Version: " + springBootVersion); + } else if (quarkusVersion != null) { + printer().println("Quarkus Version: " + quarkusVersion); + } + } + } + + return 0; + } + +} diff --git a/dsl/camel-jbang/camel-jbang-core/src/main/java/org/apache/camel/dsl/jbang/core/commands/RunHelper.java b/dsl/camel-jbang/camel-jbang-core/src/main/java/org/apache/camel/dsl/jbang/core/commands/RunHelper.java index ca0b2631793..57f2c986327 100644 --- a/dsl/camel-jbang/camel-jbang-core/src/main/java/org/apache/camel/dsl/jbang/core/commands/RunHelper.java +++ b/dsl/camel-jbang/camel-jbang-core/src/main/java/org/apache/camel/dsl/jbang/core/commands/RunHelper.java @@ -61,6 +61,8 @@ public final class RunHelper { Path pomPath = Paths.get("pom.xml"); if (Files.exists(pomPath) && Files.isRegularFile(pomPath)) { + CamelCatalog catalog = new DefaultCamelCatalog(); + // find additional dependencies form pom.xml MavenXpp3Reader mavenReader = new MavenXpp3Reader(); try (Reader reader = Files.newBufferedReader(pomPath)) { @@ -89,7 +91,7 @@ public final class RunHelper { // camel dependencies String a = d.getArtifactId(); - if (!isInCamelCatalog(a)) { + if (!isInCamelCatalog(catalog, a)) { // not a known camel artifact continue; } @@ -170,7 +172,10 @@ public final class RunHelper { } public static boolean isInCamelCatalog(String artifactId) { - CamelCatalog catalog = new DefaultCamelCatalog(); + return isInCamelCatalog(new DefaultCamelCatalog(), artifactId); + } + + public static boolean isInCamelCatalog(CamelCatalog catalog, String artifactId) { for (String n : catalog.findComponentNames()) { String a = catalog.componentModel(n).getArtifactId(); if (artifactId.equals(a)) { diff --git a/dsl/camel-jbang/camel-jbang-core/src/main/java/org/apache/camel/dsl/jbang/core/common/CatalogLoader.java b/dsl/camel-jbang/camel-jbang-core/src/main/java/org/apache/camel/dsl/jbang/core/common/CatalogLoader.java index 8aac715a2cd..bca0712fcb9 100644 --- a/dsl/camel-jbang/camel-jbang-core/src/main/java/org/apache/camel/dsl/jbang/core/common/CatalogLoader.java +++ b/dsl/camel-jbang/camel-jbang-core/src/main/java/org/apache/camel/dsl/jbang/core/common/CatalogLoader.java @@ -19,6 +19,7 @@ package org.apache.camel.dsl.jbang.core.common; import java.io.File; import java.io.IOException; import java.io.InputStream; +import java.util.List; import javax.xml.parsers.DocumentBuilder; import javax.xml.parsers.DocumentBuilderFactory; @@ -193,20 +194,25 @@ public final class CatalogLoader { if (camelQuarkusVersion != null) { // download camel-quarkus-catalog we use to know if we have an extension or not - downloader.downloadDependency("org.apache.camel.quarkus", "camel-quarkus-catalog", camelQuarkusVersion); - - Class<RuntimeProvider> clazz = (Class<RuntimeProvider>) cl.loadClass(QUARKUS_CATALOG_PROVIDER); - if (clazz != null) { - Class<CamelCatalog> clazz2 = (Class<CamelCatalog>) cl.loadClass(DEFAULT_CAMEL_CATALOG); - if (clazz2 != null) { - answer = ObjectHelper.newInstance(clazz2); - } - RuntimeProvider provider = ObjectHelper.newInstance(clazz); - if (provider != null) { - answer.setRuntimeProvider(provider); + List<MavenArtifact> artifacts = downloader.downloadArtifacts("org.apache.camel.quarkus", + "camel-quarkus-catalog", camelQuarkusVersion, true); + if (artifacts != null) { + // this will add to classpath + downloader.downloadDependency("org.apache.camel.quarkus", "camel-quarkus-catalog", camelQuarkusVersion); + + Class<RuntimeProvider> clazz = (Class<RuntimeProvider>) cl.loadClass(QUARKUS_CATALOG_PROVIDER); + if (clazz != null) { + Class<CamelCatalog> clazz2 = (Class<CamelCatalog>) cl.loadClass(DEFAULT_CAMEL_CATALOG); + if (clazz2 != null) { + answer = ObjectHelper.newInstance(clazz2); + } + RuntimeProvider provider = ObjectHelper.newInstance(clazz); + if (provider != null) { + answer.setRuntimeProvider(provider); + } + // use classloader that loaded quarkus provider to ensure we can load its resources + answer.getVersionManager().setClassLoader(cl); } - // use classloader that loaded quarkus provider to ensure we can load its resources - answer.getVersionManager().setClassLoader(cl); } } answer.enableCache(); @@ -217,6 +223,30 @@ public final class CatalogLoader { return answer; } + public static String resolveCamelVersionFromQuarkus(String repos, String camelQuarkusVersion) throws Exception { + DependencyDownloaderClassLoader cl = new DependencyDownloaderClassLoader(CatalogLoader.class.getClassLoader()); + MavenDependencyDownloader downloader = new MavenDependencyDownloader(); + downloader.setRepositories(repos); + downloader.setClassLoader(cl); + try { + downloader.start(); + + List<MavenArtifact> artifacts = downloader.downloadArtifacts("org.apache.camel.quarkus", "camel-quarkus-catalog", + camelQuarkusVersion, true); + for (MavenArtifact ma : artifacts) { + String g = ma.getGav().getGroupId(); + String a = ma.getGav().getArtifactId(); + if ("org.apache.camel".equals(g) && "camel-catalog".equals(a)) { + return ma.getGav().getVersion(); + } + } + } finally { + downloader.stop(); + } + + return null; + } + private static final class DownloadCatalogVersionManager implements VersionManager { private ClassLoader classLoader; diff --git a/dsl/camel-jbang/camel-jbang-core/src/test/java/org/apache/camel/dsl/jbang/core/commands/DependencyListTest.java b/dsl/camel-jbang/camel-jbang-core/src/test/java/org/apache/camel/dsl/jbang/core/commands/DependencyListTest.java index 385a6952eed..47fa955555c 100644 --- a/dsl/camel-jbang/camel-jbang-core/src/test/java/org/apache/camel/dsl/jbang/core/commands/DependencyListTest.java +++ b/dsl/camel-jbang/camel-jbang-core/src/test/java/org/apache/camel/dsl/jbang/core/commands/DependencyListTest.java @@ -55,7 +55,7 @@ class DependencyListTest extends CamelCommandBaseTest { @ParameterizedTest @MethodSource("runtimeProvider") public void shouldDependencyList(RuntimeType rt) throws Exception { - Export command = createCommand(rt, new String[] { "classpath:route.yaml" }, + DependencyList command = createCommand(rt, new String[] { "classpath:route.yaml" }, "--gav=examples:route:1.0.0", "--dir=" + workingDir, "--quiet", "--camel-version=4.11.0", "--quarkus-version=3.22.2", "--spring-boot-version=3.4.5"); int exit = command.doCall(); @@ -75,9 +75,8 @@ class DependencyListTest extends CamelCommandBaseTest { } } - private Export createCommand(RuntimeType rt, String[] files, String... args) { + private DependencyList createCommand(RuntimeType rt, String[] files, String... args) { DependencyList command = new DependencyList(new CamelJBangMain().withPrinter(printer)); - ; CommandLine.populateCommand(command, "--gav=examples:route:1.0.0", "--dir=" + workingDir, "--quiet", "--runtime=%s".formatted(rt.runtime())); if (args != null) { diff --git a/dsl/camel-kamelet-main/src/main/java/org/apache/camel/main/download/DependencyDownloader.java b/dsl/camel-kamelet-main/src/main/java/org/apache/camel/main/download/DependencyDownloader.java index 840ca8b136b..fec871a2ae4 100644 --- a/dsl/camel-kamelet-main/src/main/java/org/apache/camel/main/download/DependencyDownloader.java +++ b/dsl/camel-kamelet-main/src/main/java/org/apache/camel/main/download/DependencyDownloader.java @@ -155,6 +155,17 @@ public interface DependencyDownloader extends CamelContextAware, StaticService { */ MavenArtifact downloadArtifact(String groupId, String artifactId, String version); + /** + * Downloads maven artifact (can also include transitive dependencies). + * + * @param groupId maven group id + * @param artifactId maven artifact id + * @param version maven version + * @param transitively whether to include transitive dependencies + * @return the artifacts, or null if none found + */ + List<MavenArtifact> downloadArtifacts(String groupId, String artifactId, String version, boolean transitively); + /** * Resolves the available versions for the given maven artifact * diff --git a/dsl/camel-kamelet-main/src/main/java/org/apache/camel/main/download/MavenDependencyDownloader.java b/dsl/camel-kamelet-main/src/main/java/org/apache/camel/main/download/MavenDependencyDownloader.java index 7b1954c33fd..47bda5971e1 100644 --- a/dsl/camel-kamelet-main/src/main/java/org/apache/camel/main/download/MavenDependencyDownloader.java +++ b/dsl/camel-kamelet-main/src/main/java/org/apache/camel/main/download/MavenDependencyDownloader.java @@ -330,24 +330,29 @@ public class MavenDependencyDownloader extends ServiceSupport implements Depende @Override public MavenArtifact downloadArtifact(String groupId, String artifactId, String version) { + List<MavenArtifact> artifacts = downloadArtifacts(groupId, artifactId, version, false); + if (artifacts != null && artifacts.size() == 1) { + return artifacts.get(0); + } + return null; + } + + @Override + public List<MavenArtifact> downloadArtifacts(String groupId, String artifactId, String version, boolean transitively) { String gav = groupId + ":" + artifactId + ":" + version; List<String> deps = List.of(gav); // include Apache snapshot to make it easy to use upcoming releases boolean useApacheSnapshots = "org.apache.camel".equals(groupId) && version.contains("SNAPSHOT"); - List<MavenArtifact> artifacts = resolveDependenciesViaAether(deps, null, false, useApacheSnapshots); + List<MavenArtifact> artifacts = resolveDependenciesViaAether(deps, null, transitively, useApacheSnapshots); if (verbose) { LOG.info("Dependencies: {} -> [{}]", gav, artifacts); } else { LOG.debug("Dependencies: {} -> [{}]", gav, artifacts); } - if (artifacts.size() == 1) { - return artifacts.get(0); - } - - return null; + return artifacts; } @Override