This is an automated email from the ASF dual-hosted git repository. davsclaus pushed a commit to branch jt in repository https://gitbox.apache.org/repos/asf/camel.git
commit 8a3cb173b8ce72cb000d56f8051d1cc341138741 Author: Claus Ibsen <[email protected]> AuthorDate: Thu Aug 14 09:18:50 2025 +0200 CAMEL-22205: camel-jbang: Document all camel.jbang options for tooling --- dsl/camel-jbang/camel-jbang-core/pom.xml | 44 ++++- .../camel-jbang-configuration-metadata.json | 58 +++++++ .../org/apache/camel/tooling/model/JsonMapper.java | 44 ++++- .../camel/maven/packaging/PrepareJBangMojo.java | 182 +++++++++++++++++++++ 4 files changed, 319 insertions(+), 9 deletions(-) diff --git a/dsl/camel-jbang/camel-jbang-core/pom.xml b/dsl/camel-jbang/camel-jbang-core/pom.xml index 4d0fa06c58f..5b1a88f641f 100644 --- a/dsl/camel-jbang/camel-jbang-core/pom.xml +++ b/dsl/camel-jbang/camel-jbang-core/pom.xml @@ -17,7 +17,8 @@ limitations under the License. --> -<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> +<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> @@ -176,4 +177,45 @@ </dependency> </dependencies> + <build> + <plugins> + <!-- generate and include all components in the catalog --> + <plugin> + <groupId>org.apache.camel</groupId> + <artifactId>camel-package-maven-plugin</artifactId> + <executions> + <execution> + <goals> + <goal>prepare-jbang</goal> + </goals> + <phase>generate-resources</phase> + </execution> + </executions> + </plugin> + <plugin> + <groupId>org.codehaus.mojo</groupId> + <artifactId>build-helper-maven-plugin</artifactId> + <executions> + <execution> + <phase>generate-sources</phase> + <goals> + <goal>add-source</goal> + <goal>add-resource</goal> + </goals> + <configuration> + <sources> + <source>src/generated/java</source> + </sources> + <resources> + <resource> + <directory>src/generated/resources</directory> + </resource> + </resources> + </configuration> + </execution> + </executions> + </plugin> + </plugins> + </build> + </project> diff --git a/dsl/camel-jbang/camel-jbang-core/src/generated/resources/META-INF/camel-jbang-configuration-metadata.json b/dsl/camel-jbang/camel-jbang-core/src/generated/resources/META-INF/camel-jbang-configuration-metadata.json new file mode 100644 index 00000000000..3548b610312 --- /dev/null +++ b/dsl/camel-jbang/camel-jbang-core/src/generated/resources/META-INF/camel-jbang-configuration-metadata.json @@ -0,0 +1,58 @@ +{ + "groups": [ + { "name": "camel.jbang", "description": "Camel JBang configurations" } + ], + "properties": [ + { "name": "camel.jbang.background", "description": "Run in the background", "label": "internal", "type": "boolean", "javaType": "boolean", "defaultValue": "false" }, + { "name": "camel.jbang.backgroundWait", "description": "To wait for run in background to startup successfully, before returning", "label": "internal", "type": "boolean", "javaType": "boolean", "defaultValue": true }, + { "name": "camel.jbang.buildTool", "description": "Build tool to use (Maven or Gradle)", "type": "object", "javaType": "String", "defaultValue": "Maven" }, + { "name": "camel.jbang.camel-version", "description": "The version of Apache Camel to use", "type": "object", "javaType": "String" }, + { "name": "camel.jbang.camelSpringBootVersion", "description": "To use a custom Camel version when running or export to Spring Boot", "label": "spring-boot", "type": "object", "javaType": "String" }, + { "name": "camel.jbang.classpathFiles", "description": "Additional files to add to classpath (Use commas to separate multiple files).", "type": "object", "javaType": "String" }, + { "name": "camel.jbang.compileWorkDir", "description": "Work directory for compiler. Can be used to write compiled classes or other resources.", "label": "advanced", "type": "object", "javaType": "String", "defaultValue": ".camel-jbang\/compile" }, + { "name": "camel.jbang.console", "description": "Developer console at \/q\/dev on local HTTP server (port 8080 by default)", "type": "boolean", "javaType": "boolean", "defaultValue": "false" }, + { "name": "camel.jbang.debug", "description": "Run Camel in debugging mode", "label": "internal", "type": "boolean", "javaType": "boolean", "defaultValue": "false" }, + { "name": "camel.jbang.dependencies", "description": "Additional dependencies (Use commas to separate multiple dependencies).", "type": "object", "javaType": "String" }, + { "name": "camel.jbang.download", "description": "Whether to allow automatic downloading JAR dependencies (over the internet)", "type": "boolean", "javaType": "boolean", "defaultValue": true }, + { "name": "camel.jbang.excludes", "description": "Exclude files by name or pattern (Use commas to separate multiple files)", "type": "object", "javaType": "String" }, + { "name": "camel.jbang.export", "description": "Run Camel in export mode", "label": "internal", "type": "boolean", "javaType": "boolean", "defaultValue": "false" }, + { "name": "camel.jbang.exportDir", "description": "Directory where the project will be exported", "type": "object", "javaType": "String", "defaultValue": "." }, + { "name": "camel.jbang.gav", "description": "Maven coordinate (groupId:artifactId:version)", "type": "object", "javaType": "String" }, + { "name": "camel.jbang.gradleWrapper", "description": "Include Gradle Wrapper files in the exported project", "type": "boolean", "javaType": "boolean", "defaultValue": true }, + { "name": "camel.jbang.groovyFiles", "description": "Additional groovy source files to export to src\/main\/resources\/camel-groovy directory (Use commas to separate multiple files)", "type": "object", "javaType": "String" }, + { "name": "camel.jbang.health", "description": "Health check at \/observe\/health on local HTTP server (port 8080 by default)", "type": "boolean", "javaType": "boolean", "defaultValue": false, "deprecated": true }, + { "name": "camel.jbang.ignoreLoadingError", "description": "Whether to ignore route loading and compilation errors (use this with care!)", "label": "advanced", "type": "boolean", "javaType": "boolean", "defaultValue": "false" }, + { "name": "camel.jbang.javaVersion", "description": "Java version (17 or 21)", "type": "object", "javaType": "String", "defaultValue": "21", "enum": [ "17", "21" ] }, + { "name": "camel.jbang.jfr", "description": "Enables Java Flight Recorder saving recording to disk on exit", "type": "boolean", "javaType": "boolean", "defaultValue": "false" }, + { "name": "camel.jbang.jfr-profile", "description": "Java Flight Recorder profile to use (such as default or profile)", "type": "object", "javaType": "String", "defaultValue": "default" }, + { "name": "camel.jbang.jib-maven-plugin-version", "description": "Version to use for jib-maven-plugin if exporting to camel-main and have Kubernetes enabled (jkube.xxx options)", "label": "kubernetes", "type": "object", "javaType": "String", "defaultValue": "3.4.5" }, + { "name": "camel.jbang.jkube-maven-plugin-version", "description": "Version to use for jkube-maven-plugin if exporting to camel-main and have Kubernetes enabled (jkube.xxx options)", "label": "kubernetes", "type": "object", "javaType": "String", "defaultValue": "1.18.1" }, + { "name": "camel.jbang.jkubeFiles", "description": "Resource YAML fragments for Kubernetes using Eclipse JKube tool (Use commas to separate multiple files).", "label": "kubernetes", "type": "object", "javaType": "String" }, + { "name": "camel.jbang.jvmDebug", "description": "To enable JVM remote debugging on the given port.", "label": "internal", "type": "integer", "javaType": "int", "defaultValue": 4004 }, + { "name": "camel.jbang.kameletsVersion", "description": "Apache Camel Kamelets version. By default the Kamelets are the same version as Camel.", "type": "object", "javaType": "String" }, + { "name": "camel.jbang.lazyBean", "description": "Whether to use lazy bean initialization (can help with complex classloading issues)", "label": "advanced", "type": "boolean", "javaType": "boolean", "defaultValue": "false" }, + { "name": "camel.jbang.localKameletDir", "description": "Local file directory for loading custom Kamelets", "type": "object", "javaType": "String" }, + { "name": "camel.jbang.maven-apache-snapshot-enabled", "description": "Whether downloading JARs from ASF Maven Snapshot repository is enabled", "label": "maven", "type": "boolean", "javaType": "boolean", "defaultValue": true }, + { "name": "camel.jbang.maven-central-enabled", "description": "Whether downloading JARs from Maven Central repository is enabled", "label": "maven", "type": "boolean", "javaType": "boolean", "defaultValue": true }, + { "name": "camel.jbang.maven-settings", "description": "Optional location of Maven settings.xml file to configure servers, repositories, mirrors, and proxies. If set to false, not even the default \/.m2\/settings.xml will be used.", "label": "maven", "type": "object", "javaType": "String" }, + { "name": "camel.jbang.maven-settings-security", "description": "Optional location of Maven settings-security.xml file to decrypt Maven Settings (settings.xml) file", "label": "maven", "type": "object", "javaType": "String" }, + { "name": "camel.jbang.mavenWrapper", "description": "Include Maven Wrapper files in the exported project", "type": "boolean", "javaType": "boolean", "defaultValue": true }, + { "name": "camel.jbang.metrics", "description": "Metrics (Micrometer and Prometheus) at \/observe\/metrics on local HTTP server (port 8080 by default) when running standalone Camel", "type": "boolean", "javaType": "boolean", "defaultValue": false, "deprecated": true }, + { "name": "camel.jbang.openApi", "description": "File name of open-api spec file (JSON or YAML) to generate routes from the swagger\/openapi API spec file.", "type": "object", "javaType": "String" }, + { "name": "camel.jbang.packageScanJars", "description": "Whether to automatic package scan JARs for custom Spring or Quarkus beans making them available for Camel JBang", "label": "advanced", "type": "boolean", "javaType": "boolean", "defaultValue": "false" }, + { "name": "camel.jbang.prompt", "description": "Allow user to type in required parameters in prompt if not present in application", "label": "advanced", "type": "boolean", "javaType": "boolean", "defaultValue": "false" }, + { "name": "camel.jbang.quarkusArtifactId", "description": "Quarkus Platform Maven artifactId", "label": "quarkus", "type": "object", "javaType": "String" }, + { "name": "camel.jbang.quarkusGroupId", "description": "Quarkus Platform Maven groupId", "label": "quarkus", "type": "object", "javaType": "String" }, + { "name": "camel.jbang.quarkusVersion", "description": "Quarkus Platform version", "label": "quarkus", "type": "object", "javaType": "String" }, + { "name": "camel.jbang.repos", "description": "Additional Maven repositories for download on-demand (Use commas to separate multiple repositories)", "label": "maven", "type": "object", "javaType": "String" }, + { "name": "camel.jbang.repositories", "description": "Additional Maven repositories (Use commas to separate multiple repositories).", "label": "maven", "type": "object", "javaType": "String" }, + { "name": "camel.jbang.runtime", "description": "Which runtime to use (camel-main, spring-boot, quarkus)", "type": "object", "javaType": "String", "enum": [ "camel-main", "spring-boot", "quarkus" ] }, + { "name": "camel.jbang.scriptFiles", "description": "Additional shell script files to export to src\/main\/scripts directory", "type": "object", "javaType": "String" }, + { "name": "camel.jbang.sourceDir", "description": "Source directory for dynamically loading Camel file(s) to run. When using this, then files cannot be specified at the same time.", "label": "advanced", "type": "object", "javaType": "String" }, + { "name": "camel.jbang.springBootVersion", "description": "Spring Boot version", "label": "spring-boot", "type": "object", "javaType": "String" }, + { "name": "camel.jbang.stub", "description": "Stubs all the matching endpoint with the given component name or pattern. Multiple names can be separated by comma. (all = everything).", "type": "object", "javaType": "String" }, + { "name": "camel.jbang.tlsFiles", "description": "Additional SSL\/TLS files to export to src\/main\/tls directory", "type": "object", "javaType": "String" }, + { "name": "camel.jbang.transform", "description": "To run in transform mode", "label": "internal", "type": "boolean", "javaType": "boolean", "defaultValue": "false" }, + { "name": "camel.jbang.verbose", "description": "Verbose output of startup activity (dependency resolution and downloading", "type": "boolean", "javaType": "boolean", "defaultValue": "false" } + ] +} diff --git a/tooling/camel-tooling-model/src/main/java/org/apache/camel/tooling/model/JsonMapper.java b/tooling/camel-tooling-model/src/main/java/org/apache/camel/tooling/model/JsonMapper.java index f8ab96fb3cd..302afa0cc72 100644 --- a/tooling/camel-tooling-model/src/main/java/org/apache/camel/tooling/model/JsonMapper.java +++ b/tooling/camel-tooling-model/src/main/java/org/apache/camel/tooling/model/JsonMapper.java @@ -807,8 +807,12 @@ public final class JsonMapper { for (MainGroupModel group : model.getGroups()) { JsonObject j = new JsonObject(); j.put("name", group.getName()); - j.put("description", group.getDescription()); - j.put("sourceType", group.getSourceType()); + if (group.getDescription() != null) { + j.put("description", group.getDescription()); + } + if (group.getSourceType() != null) { + j.put("sourceType", group.getSourceType()); + } groups.add(j); } json.put("groups", groups); @@ -816,8 +820,18 @@ public final class JsonMapper { for (MainOptionModel prop : model.getOptions()) { JsonObject j = new JsonObject(); j.put("name", prop.getName()); - j.put("description", prop.getDescription()); - j.put("sourceType", prop.getSourceType()); + if (prop.getDescription() != null) { + j.put("description", prop.getDescription()); + } + if (prop.getGroup() != null) { + j.put("group", prop.getGroup()); + } + if (prop.getLabel() != null) { + j.put("label", prop.getLabel()); + } + if (prop.getSourceType() != null) { + j.put("sourceType", prop.getSourceType()); + } j.put("type", prop.getType()); j.put("javaType", prop.getJavaType()); if (prop.getDefaultValue() != null) { @@ -844,8 +858,12 @@ public final class JsonMapper { for (JBangGroupModel group : model.getGroups()) { JsonObject j = new JsonObject(); j.put("name", group.getName()); - j.put("description", group.getDescription()); - j.put("sourceType", group.getSourceType()); + if (group.getDescription() != null) { + j.put("description", group.getDescription()); + } + if (group.getSourceType() != null) { + j.put("sourceType", group.getSourceType()); + } groups.add(j); } json.put("groups", groups); @@ -853,8 +871,18 @@ public final class JsonMapper { for (JBangOptionModel prop : model.getOptions()) { JsonObject j = new JsonObject(); j.put("name", prop.getName()); - j.put("description", prop.getDescription()); - j.put("sourceType", prop.getSourceType()); + if (prop.getDescription() != null) { + j.put("description", prop.getDescription()); + } + if (prop.getGroup() != null) { + j.put("group", prop.getGroup()); + } + if (prop.getLabel() != null) { + j.put("label", prop.getLabel()); + } + if (prop.getSourceType() != null) { + j.put("sourceType", prop.getSourceType()); + } j.put("type", prop.getType()); j.put("javaType", prop.getJavaType()); if (prop.getDefaultValue() != null) { diff --git a/tooling/maven/camel-package-maven-plugin/src/main/java/org/apache/camel/maven/packaging/PrepareJBangMojo.java b/tooling/maven/camel-package-maven-plugin/src/main/java/org/apache/camel/maven/packaging/PrepareJBangMojo.java new file mode 100644 index 00000000000..10988d50b91 --- /dev/null +++ b/tooling/maven/camel-package-maven-plugin/src/main/java/org/apache/camel/maven/packaging/PrepareJBangMojo.java @@ -0,0 +1,182 @@ +/* + * 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.maven.packaging; + +import java.io.File; +import java.io.IOException; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; + +import javax.inject.Inject; + +import org.apache.camel.spi.Metadata; +import org.apache.camel.tooling.model.JBangModel; +import org.apache.camel.tooling.model.JsonMapper; +import org.apache.camel.tooling.util.JavadocHelper; +import org.apache.maven.plugin.MojoExecutionException; +import org.apache.maven.plugin.MojoFailureException; +import org.apache.maven.plugins.annotations.LifecyclePhase; +import org.apache.maven.plugins.annotations.Mojo; +import org.apache.maven.plugins.annotations.Parameter; +import org.apache.maven.plugins.annotations.ResolutionScope; +import org.apache.maven.project.MavenProject; +import org.apache.maven.project.MavenProjectHelper; +import org.codehaus.plexus.build.BuildContext; +import org.jboss.forge.roaster.Roaster; +import org.jboss.forge.roaster.model.StaticCapable; +import org.jboss.forge.roaster.model.source.AnnotationSource; +import org.jboss.forge.roaster.model.source.FieldSource; +import org.jboss.forge.roaster.model.source.JavaClassSource; + +/** + * Prepares camel-jbang by generating Camel JBang configuration metadata for tooling support. + */ +@Mojo(name = "prepare-jbang", defaultPhase = LifecyclePhase.PROCESS_CLASSES, threadSafe = true, + requiresDependencyResolution = ResolutionScope.COMPILE) +public class PrepareJBangMojo extends AbstractGeneratorMojo { + + @Parameter(defaultValue = "${project.basedir}/src/generated/resources") + protected File outFolder; + + @Inject + public PrepareJBangMojo(MavenProjectHelper projectHelper, BuildContext buildContext) { + super(projectHelper, buildContext); + } + + /** + * Parses the Camel JBang configuration java source file. + */ + public static List<JBangModel.JBangOptionModel> parseConfigurationSource(String fileName) throws IOException { + return parseConfigurationSource(new File(fileName)); + } + + /** + * Parses the Camel JBang configuration java source file. + */ + public static List<JBangModel.JBangOptionModel> parseConfigurationSource(File file) throws IOException { + final List<JBangModel.JBangOptionModel> answer = new ArrayList<>(); + + JavaClassSource clazz = (JavaClassSource) Roaster.parse(file); + List<FieldSource<JavaClassSource>> fields = clazz.getFields(); + fields = fields.stream().filter(StaticCapable::isStatic).toList(); + fields.forEach(f -> { + AnnotationSource<?> as = f.getAnnotation(Metadata.class); + if (as != null) { + String name = f.getStringInitializer(); + String javaType = as.getStringValue("javaType"); + String defaultValue = as.getStringValue("defaultValue"); + String desc = as.getStringValue("description"); + String label = as.getStringValue("label"); + boolean deprecated = clazz.getAnnotation(Deprecated.class) != null || f.getAnnotation(Deprecated.class) != null; + String type = fromMainToType(javaType); + JBangModel.JBangOptionModel model = new JBangModel.JBangOptionModel(); + model.setName(name); + model.setType(type); + model.setJavaType(javaType); + model.setDescription(JavadocHelper.sanitizeDescription(desc, false)); + model.setLabel(label); + model.setSourceType(null); + model.setDefaultValue(asDefaultValue(type, defaultValue)); + model.setDeprecated(deprecated); + List<String> enums = null; + String text = as.getStringValue("enums"); + if (text != null) { + enums = Arrays.asList(text.split(",")); + } + model.setEnums(enums); + answer.add(model); + } + }); + + return answer; + } + + private static String fromMainToType(String type) { + if ("boolean".equals(type) || "java.lang.Boolean".equals(type)) { + return "boolean"; + } else if ("int".equals(type) || "java.lang.Integer".equals(type)) { + return "integer"; + } else if ("long".equals(type) || "java.lang.Long".equals(type)) { + return "integer"; + } else if ("float".equals(type) || "java.lang.Float".equals(type)) { + return "number"; + } else if ("double".equals(type) || "java.lang.Double".equals(type)) { + return "number"; + } else if ("string".equals(type) || "java.lang.String".equals(type)) { + return "string"; + } else { + return "object"; + } + } + + private static Object asDefaultValue(String type, String defaultValue) { + if (defaultValue != null) { + if ("boolean".equals(type)) { + return Boolean.parseBoolean(defaultValue); + } else if ("integer".equals(type)) { + return Integer.parseInt(defaultValue); + } + } + if (defaultValue == null && "boolean".equals(type)) { + return "false"; + } + return defaultValue; + } + + @Override + public void execute(MavenProject project) throws MojoFailureException, MojoExecutionException { + outFolder = new File(project.getBasedir(), "src/generated/resources"); + super.execute(project); + } + + @Override + public void execute() throws MojoExecutionException, MojoFailureException { + // scan for configuration files + File[] files = new File(project.getBasedir(), "src/main/java/org/apache/camel/dsl/jbang/core/common") + .listFiles(f -> f.isFile() && f.getName().endsWith("Constants.java")); + if (files == null || files.length == 0) { + return; + } + + final List<JBangModel.JBangOptionModel> data = new ArrayList<>(); + + for (File file : files) { + getLog().info("Parsing Camel JBang configuration file: " + file); + try { + List<JBangModel.JBangOptionModel> model = parseConfigurationSource(file); + data.addAll(model); + } catch (Exception e) { + throw new MojoFailureException("Error parsing file " + file + " due " + e.getMessage(), e); + } + } + + // lets sort so they are always ordered + data.sort((o1, o2) -> o1.getName().compareToIgnoreCase(o2.getName())); + + if (!data.isEmpty()) { + JBangModel model = new JBangModel(); + model.getOptions().addAll(data); + model.getGroups().add(new JBangModel.JBangGroupModel( + "camel.jbang", "Camel JBang configurations", null)); + String json = JsonMapper.createJsonSchema(model); + + updateResource(outFolder.toPath(), "META-INF/camel-jbang-configuration-metadata.json", json); + } + } + +}
