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 db3925713e8 camel-jbang - Add command to output dependency tree. db3925713e8 is described below commit db3925713e858408a4cb4a5ae9f126adb57ad0e9 Author: Claus Ibsen <claus.ib...@gmail.com> AuthorDate: Sat Nov 5 21:50:29 2022 +0100 camel-jbang - Add command to output dependency tree. --- .../modules/ROOT/pages/camel-jbang.adoc | 112 +++++++++++ .../dsl/jbang/core/commands/CamelJBangMain.java | 1 + .../dsl/jbang/core/commands/DependencyTree.java | 214 +++++++++++++++++++++ .../camel/dsl/jbang/core/commands/Export.java | 34 +++- .../dsl/jbang/core/commands/ExportBaseCommand.java | 4 + .../dsl/jbang/core/commands/ExportCamelMain.java | 14 +- .../dsl/jbang/core/commands/ExportQuarkus.java | 14 +- .../dsl/jbang/core/commands/ExportSpringBoot.java | 14 +- 8 files changed, 388 insertions(+), 19 deletions(-) diff --git a/docs/user-manual/modules/ROOT/pages/camel-jbang.adoc b/docs/user-manual/modules/ROOT/pages/camel-jbang.adoc index 24892eb42d0..ca443c4aae9 100644 --- a/docs/user-manual/modules/ROOT/pages/camel-jbang.adoc +++ b/docs/user-manual/modules/ROOT/pages/camel-jbang.adoc @@ -1294,6 +1294,118 @@ This make it quick to implement a Camel route for a given operation. See the https://github.com/apache/camel-kamelets-examples/tree/main/jbang/open-api[open-api example] for more details. +== Gathering list of dependencies + +When working with Camel JBang then dependencies are automatically resolved. This means that +you do not have to use a build system like Maven or Gradle to add every Camel components +as a dependency. + +However, you may want to know what dependencies are required to run the Camel integration. +To see this, you can use the `dependency-tree` command. The command output does not output a detailed +tree, such as `mvn dependencies:tree`, as the output is intended to list which Camel components, +and other JARs needed (when using Kamelets). + +The dependency output by default is _vanilla_ Apache Camel with the camel-main as runtime, as shown below: + +[source,bash] +---- +camel dependency-tree ✘ INT +org.apache.camel:camel-main:3.20.0 +org.apache.camel:camel-dsl-modeline:3.20.0 +org.apache.camel:camel-health:3.20.0 +org.apache.camel:camel-kamelet:3.20.0 +org.apache.camel:camel-log:3.20.0 +org.apache.camel:camel-rest:3.20.0 +org.apache.camel:camel-stream:3.20.0 +org.apache.camel:camel-timer:3.20.0 +org.apache.camel:camel-yaml-dsl:3.20.0 +org.apache.camel.kamelets:camel-kamelets-utils:0.9.3 +org.apache.camel.kamelets:camel-kamelets:0.9.3 +---- + +The output is by default a line per maven dependency in GAV format (_groupId:artifactId:version_). + +You can also specify the output should be in _Maven format_ as shown: + +[source,bash] +---- +camel dependency-tree --output=maven +<dependency> + <groupId>org.apache.camel</groupId> + <artifactId>camel-main</artifactId> + <version>3.20.0</version> +</dependency> +<dependency> + <groupId>org.apache.camel</groupId> + <artifactId>camel-dsl-modeline</artifactId> + <version>3.20.0</version> +</dependency> +<dependency> + <groupId>org.apache.camel</groupId> + <artifactId>camel-health</artifactId> + <version>3.20.0</version> +</dependency> +<dependency> + <groupId>org.apache.camel</groupId> + <artifactId>camel-kamelet</artifactId> + <version>3.20.0</version> +</dependency> +<dependency> + <groupId>org.apache.camel</groupId> + <artifactId>camel-log</artifactId> + <version>3.20.0</version> +</dependency> +<dependency> + <groupId>org.apache.camel</groupId> + <artifactId>camel-rest</artifactId> + <version>3.20.0</version> +</dependency> +<dependency> + <groupId>org.apache.camel</groupId> + <artifactId>camel-stream</artifactId> + <version>3.20.0</version> +</dependency> +<dependency> + <groupId>org.apache.camel</groupId> + <artifactId>camel-timer</artifactId> + <version>3.20.0</version> +</dependency> +<dependency> + <groupId>org.apache.camel</groupId> + <artifactId>camel-yaml-dsl</artifactId> + <version>3.20.0</version> +</dependency> +<dependency> + <groupId>org.apache.camel.kamelets</groupId> + <artifactId>camel-kamelets-utils</artifactId> + <version>0.9.3</version> +</dependency> +<dependency> + <groupId>org.apache.camel.kamelets</groupId> + <artifactId>camel-kamelets</artifactId> + <version>0.9.3</version> +</dependency> +---- + +You can also choose the target runtime as either _quarkus_ or _spring-boot_ as shown: + +[source,bash] +---- +camel dependency-tree --runtime=spring-boot +org.springframework.boot:spring-boot-starter-actuator:2.7.5 +org.springframework.boot:spring-boot-starter-web:2.7.5 +org.apache.camel.springboot:camel-spring-boot-engine-starter:3.20.0 +org.apache.camel.springboot:camel-dsl-modeline-starter:3.20.0 +org.apache.camel.springboot:camel-kamelet-starter:3.20.0 +org.apache.camel.springboot:camel-log-starter:3.20.0 +org.apache.camel.springboot:camel-rest-starter:3.20.0 +org.apache.camel.springboot:camel-stream-starter:3.20.0 +org.apache.camel.springboot:camel-timer-starter:3.20.0 +org.apache.camel.springboot:camel-yaml-dsl-starter:3.20 +org.apache.camel.kamelets:camel-kamelets-utils:0.9.3 +org.apache.camel.kamelets:camel-kamelets:0.9.3 +---- + == Creating Projects You can _export_ your Camel JBang integration to a traditional Java based project such as Spring Boot or Quarkus. 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 2122d081f12..f3474659540 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 @@ -114,6 +114,7 @@ public class CamelJBangMain implements Callable<Integer> { .addSubcommand("hawtio", new CommandLine(new Hawtio(main))) .addSubcommand("bind", new CommandLine(new Bind(main))) .addSubcommand("pipe", new CommandLine(new Pipe(main))) + .addSubcommand("dependency-tree", new CommandLine(new DependencyTree(main))) .addSubcommand("export", new CommandLine(new Export(main))); commandLine.getCommandSpec().versionProvider(() -> { diff --git a/dsl/camel-jbang/camel-jbang-core/src/main/java/org/apache/camel/dsl/jbang/core/commands/DependencyTree.java b/dsl/camel-jbang/camel-jbang-core/src/main/java/org/apache/camel/dsl/jbang/core/commands/DependencyTree.java new file mode 100644 index 00000000000..eb262c08c60 --- /dev/null +++ b/dsl/camel-jbang/camel-jbang-core/src/main/java/org/apache/camel/dsl/jbang/core/commands/DependencyTree.java @@ -0,0 +1,214 @@ +/* + * 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.io.File; +import java.util.ArrayList; +import java.util.List; +import java.util.Properties; + +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.dsl.jbang.core.common.RuntimeUtil; +import org.apache.camel.dsl.jbang.core.common.XmlHelper; +import org.apache.camel.main.download.MavenGav; +import org.apache.camel.util.CamelCaseOrderedProperties; +import org.apache.camel.util.FileUtil; +import picocli.CommandLine; + +public class DependencyTree extends Export { + + protected static final String EXPORT_DIR = ".camel-jbang/export"; + + @CommandLine.Option(names = { "--output" }, description = "Output format (gav or maven", defaultValue = "gav") + protected String output; + + public DependencyTree(CamelJBangMain main) { + super(main); + } + + @Override + protected Integer export() throws Exception { + this.quiet = true; // lets be quiet and generate from fresh data to ensure the output is up to date + + Integer answer = doExport(); + if (answer == 0) { + // read pom.xml + File pom = new File(EXPORT_DIR, "pom.xml"); + if (pom.exists()) { + DocumentBuilderFactory dbf = XmlHelper.createDocumentBuilderFactory(); + DocumentBuilder db = dbf.newDocumentBuilder(); + Document dom = db.parse(pom); + NodeList nl = dom.getElementsByTagName("dependency"); + List<MavenGav> gavs = new ArrayList<>(); + String camelVersion = null; + String springBootVersion = null; + String quarkusVersion = null; + for (int i = 0; i < nl.getLength(); i++) { + Element node = (Element) nl.item(i); + 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; + } + + // scope + String scope = null; + NodeList sl = node.getElementsByTagName("scope"); + if (sl.getLength() > 0) { + scope = sl.item(0).getTextContent(); + } + if ("test".equals(scope) || "import".equals(scope)) { + // skip test/BOM import scopes + continue; + } + + // version + if (v == null && g.equals("org.apache.camel")) { + v = camelVersion; + } + if (v == null && g.equals("org.apache.camel.springboot")) { + v = camelVersion; + } + if (v == null && g.equals("org.springframework.boot")) { + v = springBootVersion; + } + if (v == null && (g.equals("io.quarkus") || g.equals("org.apache.camel.quarkus"))) { + v = quarkusVersion; + } + + if (skipArtifact(g, a, v)) { + continue; + } + if (v != null) { + gavs.add(MavenGav.parseGav(g + ":" + a + ":" + v)); + } else { + gavs.add(MavenGav.parseGav(g + ":" + a)); + } + } + // sort GAVs + gavs.sort(mavenGavComparator()); + for (MavenGav gav : gavs) { + outputGav(gav); + } + } + // cleanup dir after complete + File buildDir = new File(EXPORT_DIR); + FileUtil.removeDir(buildDir); + } + return answer; + } + + protected void outputGav(MavenGav gav) { + if ("gav".equals(output)) { + System.out.println(gav.toString()); + } else if ("maven".equals(output)) { + System.out.println("<dependency>"); + System.out.printf(" <groupId>%s</groupId>%n", gav.getGroupId()); + System.out.printf(" <artifactId>%s</artifactId>%n", gav.getArtifactId()); + System.out.printf(" <version>%s</version>%n", gav.getVersion()); + System.out.println("</dependency>"); + } + } + + protected Integer doExport() throws Exception { + // read runtime and gav from profile if not configured + File profile = new File(getProfile() + ".properties"); + if (profile.exists()) { + Properties prop = new CamelCaseOrderedProperties(); + RuntimeUtil.loadProperties(prop, profile); + if (this.runtime == null) { + this.runtime = prop.getProperty("camel.jbang.runtime"); + } + if (this.gav == null) { + this.gav = prop.getProperty("camel.jbang.gav"); + } + // allow configuring versions from profile + this.javaVersion = prop.getProperty("camel.jbang.javaVersion", this.javaVersion); + this.kameletsVersion = prop.getProperty("camel.jbang.kameletsVersion", this.kameletsVersion); + this.localKameletDir = prop.getProperty("camel.jbang.localKameletDir", this.localKameletDir); + this.quarkusGroupId = prop.getProperty("camel.jbang.quarkusGroupId", this.quarkusGroupId); + this.quarkusArtifactId = prop.getProperty("camel.jbang.quarkusArtifactId", this.quarkusArtifactId); + this.quarkusVersion = prop.getProperty("camel.jbang.quarkusVersion", this.quarkusVersion); + this.springBootVersion = prop.getProperty("camel.jbang.springBootVersion", this.springBootVersion); + } + + // use temporary export dir + exportDir = EXPORT_DIR; + if (gav == null) { + gav = "org.apache.camel:camel-jbang-dummy:1.0"; + } + if (runtime == null) { + runtime = "camel-main"; + } + + if ("spring-boot".equals(runtime) || "camel-spring-boot".equals(runtime)) { + return export(new ExportSpringBoot(getMain())); + } else if ("quarkus".equals(runtime) || "camel-quarkus".equals(runtime)) { + return export(new ExportQuarkus(getMain())); + } else if ("main".equals(runtime) || "camel-main".equals(runtime)) { + return export(new ExportCamelMain(getMain())); + } else { + System.err.println("Unknown runtime: " + runtime); + return 1; + } + } + + protected boolean skipArtifact(String groupId, String artifactId, String version) { + // skip jansi which is used for color logging + if ("org.fusesource.jansi".equals(groupId)) { + return true; + } + // skip logging framework + if ("org.apache.logging.log4j".equals(groupId)) { + return true; + } + + return false; + } + +} diff --git a/dsl/camel-jbang/camel-jbang-core/src/main/java/org/apache/camel/dsl/jbang/core/commands/Export.java b/dsl/camel-jbang/camel-jbang-core/src/main/java/org/apache/camel/dsl/jbang/core/commands/Export.java index bfced95c678..dddca10f7c5 100644 --- a/dsl/camel-jbang/camel-jbang-core/src/main/java/org/apache/camel/dsl/jbang/core/commands/Export.java +++ b/dsl/camel-jbang/camel-jbang-core/src/main/java/org/apache/camel/dsl/jbang/core/commands/Export.java @@ -72,7 +72,7 @@ class Export extends ExportBaseCommand { return export(new ExportSpringBoot(getMain())); } else if ("quarkus".equals(runtime) || "camel-quarkus".equals(runtime)) { return export(new ExportQuarkus(getMain())); - } else if ("camel-main".equals(runtime)) { + } else if ("main".equals(runtime) || "camel-main".equals(runtime)) { return export(new ExportCamelMain(getMain())); } else { System.err.println("Unknown runtime: " + runtime); @@ -80,7 +80,7 @@ class Export extends ExportBaseCommand { } } - private Integer export(ExportBaseCommand cmd) throws Exception { + protected Integer export(ExportBaseCommand cmd) throws Exception { // copy properties from this to cmd cmd.profile = this.profile; cmd.dependencies = this.dependencies; @@ -99,6 +99,7 @@ class Export extends ExportBaseCommand { cmd.quarkusVersion = this.quarkusVersion; cmd.springBootVersion = this.springBootVersion; cmd.mavenWrapper = this.mavenWrapper; + cmd.quiet = this.quiet; // run export return cmd.export(); } @@ -121,14 +122,33 @@ class Export extends ExportBaseCommand { int rankGroupId(MavenGav o1) { String g1 = o1.getGroupId(); - if ("org.apache.camel.quarkus".equals(g1)) { - return 10; + if ("org.springframework.boot".equals(g1)) { + return 30; + } else if ("io.quarkus".equals(g1)) { + return 30; + } else if ("org.apache.camel.quarkus".equals(g1)) { + String a1 = o1.getArtifactId(); + // main/core/engine first + if ("camel-quarkus-core".equals(a1)) { + return 21; + } + return 20; } else if ("org.apache.camel.springboot".equals(g1)) { + String a1 = o1.getArtifactId(); + // main/core/engine first + if ("camel-spring-boot-engine-starter".equals(a1)) { + return 21; + } + return 20; + } else if ("org.apache.camel".equals(g1)) { + String a1 = o1.getArtifactId(); + // main/core/engine first + if ("camel-main".equals(a1)) { + return 11; + } return 10; } else if ("org.apache.camel.kamelets".equals(g1)) { - return 9; - } else if ("org.apache.camel".equals(g1)) { - return 8; + return 5; } else { return 0; } diff --git a/dsl/camel-jbang/camel-jbang-core/src/main/java/org/apache/camel/dsl/jbang/core/commands/ExportBaseCommand.java b/dsl/camel-jbang/camel-jbang-core/src/main/java/org/apache/camel/dsl/jbang/core/commands/ExportBaseCommand.java index ac13cdb0c0a..401420d77be 100644 --- a/dsl/camel-jbang/camel-jbang-core/src/main/java/org/apache/camel/dsl/jbang/core/commands/ExportBaseCommand.java +++ b/dsl/camel-jbang/camel-jbang-core/src/main/java/org/apache/camel/dsl/jbang/core/commands/ExportBaseCommand.java @@ -120,6 +120,10 @@ abstract class ExportBaseCommand extends CamelCommand { description = "Can be used to turn on logging (logs to file in <user home>/.camel directory)") boolean logging; + @CommandLine.Option(names = { "--quiet" }, defaultValue = "false", + description = "Will be quiet, only print when error occurs") + boolean quiet; + public ExportBaseCommand(CamelJBangMain main) { super(main); } diff --git a/dsl/camel-jbang/camel-jbang-core/src/main/java/org/apache/camel/dsl/jbang/core/commands/ExportCamelMain.java b/dsl/camel-jbang/camel-jbang-core/src/main/java/org/apache/camel/dsl/jbang/core/commands/ExportCamelMain.java index 2a0fc3495ef..ef9d01898a3 100644 --- a/dsl/camel-jbang/camel-jbang-core/src/main/java/org/apache/camel/dsl/jbang/core/commands/ExportCamelMain.java +++ b/dsl/camel-jbang/camel-jbang-core/src/main/java/org/apache/camel/dsl/jbang/core/commands/ExportCamelMain.java @@ -43,7 +43,7 @@ class ExportCamelMain extends Export { public Integer export() throws Exception { String[] ids = gav.split(":"); if (ids.length != 3) { - System.out.println("--gav must be in syntax: groupId:artifactId:version"); + System.err.println("--gav must be in syntax: groupId:artifactId:version"); return 1; } @@ -53,16 +53,22 @@ class ExportCamelMain extends Export { File settings = new File(Run.WORK_DIR + "/" + Run.RUN_SETTINGS_FILE); if (fresh || !settings.exists()) { // allow to automatic build - System.out.println("Generating fresh run data"); + if (!quiet) { + System.out.println("Generating fresh run data"); + } int silent = runSilently(); if (silent != 0) { return silent; } } else { - System.out.println("Reusing existing run data"); + if (!quiet) { + System.out.println("Reusing existing run data"); + } } - System.out.println("Exporting as Camel Main project to: " + exportDir); + if (!quiet) { + System.out.println("Exporting as Camel Main project to: " + exportDir); + } // use a temporary work dir File buildDir = new File(BUILD_DIR); diff --git a/dsl/camel-jbang/camel-jbang-core/src/main/java/org/apache/camel/dsl/jbang/core/commands/ExportQuarkus.java b/dsl/camel-jbang/camel-jbang-core/src/main/java/org/apache/camel/dsl/jbang/core/commands/ExportQuarkus.java index 5c3d94aff34..bd3c9cb4a71 100644 --- a/dsl/camel-jbang/camel-jbang-core/src/main/java/org/apache/camel/dsl/jbang/core/commands/ExportQuarkus.java +++ b/dsl/camel-jbang/camel-jbang-core/src/main/java/org/apache/camel/dsl/jbang/core/commands/ExportQuarkus.java @@ -63,7 +63,7 @@ class ExportQuarkus extends Export { public Integer export() throws Exception { String[] ids = gav.split(":"); if (ids.length != 3) { - System.out.println("--gav must be in syntax: groupId:artifactId:version"); + System.err.println("--gav must be in syntax: groupId:artifactId:version"); return 1; } @@ -73,16 +73,22 @@ class ExportQuarkus extends Export { File settings = new File(Run.WORK_DIR + "/" + Run.RUN_SETTINGS_FILE); if (fresh || !settings.exists()) { // allow to automatic build - System.out.println("Generating fresh run data"); + if (!quiet) { + System.out.println("Generating fresh run data"); + } int silent = runSilently(); if (silent != 0) { return silent; } } else { - System.out.println("Reusing existing run data"); + if (!quiet) { + System.out.println("Reusing existing run data"); + } } - System.out.println("Exporting as Quarkus project to: " + exportDir); + if (!quiet) { + System.out.println("Exporting as Quarkus project to: " + exportDir); + } // use a temporary work dir File buildDir = new File(BUILD_DIR); diff --git a/dsl/camel-jbang/camel-jbang-core/src/main/java/org/apache/camel/dsl/jbang/core/commands/ExportSpringBoot.java b/dsl/camel-jbang/camel-jbang-core/src/main/java/org/apache/camel/dsl/jbang/core/commands/ExportSpringBoot.java index c65bbe34f67..de59e716031 100644 --- a/dsl/camel-jbang/camel-jbang-core/src/main/java/org/apache/camel/dsl/jbang/core/commands/ExportSpringBoot.java +++ b/dsl/camel-jbang/camel-jbang-core/src/main/java/org/apache/camel/dsl/jbang/core/commands/ExportSpringBoot.java @@ -51,7 +51,7 @@ class ExportSpringBoot extends Export { public Integer export() throws Exception { String[] ids = gav.split(":"); if (ids.length != 3) { - System.out.println("--gav must be in syntax: groupId:artifactId:version"); + System.err.println("--gav must be in syntax: groupId:artifactId:version"); return 1; } @@ -61,16 +61,22 @@ class ExportSpringBoot extends Export { File settings = new File(Run.WORK_DIR + "/" + Run.RUN_SETTINGS_FILE); if (fresh || !settings.exists()) { // allow to automatic build - System.out.println("Generating fresh run data"); + if (!quiet) { + System.out.println("Generating fresh run data"); + } int silent = runSilently(); if (silent != 0) { return silent; } } else { - System.out.println("Reusing existing run data"); + if (!quiet) { + System.out.println("Reusing existing run data"); + } } - System.out.println("Exporting as Spring Boot project to: " + exportDir); + if (!quiet) { + System.out.println("Exporting as Spring Boot project to: " + exportDir); + } // use a temporary work dir File buildDir = new File(BUILD_DIR);