This is an automated email from the ASF dual-hosted git repository. davsclaus pushed a commit to branch lan-markup in repository https://gitbox.apache.org/repos/asf/camel.git
commit 341e04e8381fa61ed1211d8d965a514b96a2bafb Author: Claus Ibsen <claus.ib...@gmail.com> AuthorDate: Fri Aug 16 07:33:04 2024 +0200 CAMEL-20569: camel-catalog - Markup languages which functions they support for better doc and tooling. --- .../org/apache/camel/language/simple/simple.json | 22 ++++++++ .../camel/language/simple/SimpleConstants.java | 45 +++++++++------ .../packaging/AbstractGenerateConfigurerMojo.java | 10 ++++ .../camel/maven/packaging/PackageLanguageMojo.java | 66 +++++++++++++++++++++- .../camel/maven/packaging/generics/ClassUtil.java | 4 +- 5 files changed, 127 insertions(+), 20 deletions(-) diff --git a/core/camel-core-languages/src/generated/resources/META-INF/org/apache/camel/language/simple/simple.json b/core/camel-core-languages/src/generated/resources/META-INF/org/apache/camel/language/simple/simple.json index 223741d6576..09b0ba79cb7 100644 --- a/core/camel-core-languages/src/generated/resources/META-INF/org/apache/camel/language/simple/simple.json +++ b/core/camel-core-languages/src/generated/resources/META-INF/org/apache/camel/language/simple/simple.json @@ -20,5 +20,27 @@ "expression": { "index": 1, "kind": "value", "displayName": "Expression", "group": "common", "required": true, "type": "string", "javaType": "java.lang.String", "deprecated": false, "autowired": false, "secret": false, "description": "The expression value in your chosen language syntax" }, "resultType": { "index": 2, "kind": "attribute", "displayName": "Result Type", "group": "common", "required": false, "type": "string", "javaType": "java.lang.String", "deprecated": false, "autowired": false, "secret": false, "description": "Sets the class of the result type (type from output)" }, "trim": { "index": 3, "kind": "attribute", "displayName": "Trim", "group": "advanced", "label": "advanced", "required": false, "type": "boolean", "javaType": "java.lang.Boolean", "deprecated": false, "autowired": false, "secret": false, "defaultValue": true, "description": "Whether to trim the value to remove leading and trailing whitespaces and line breaks" } + }, + "functions": { + "body": { "index": 0, "kind": "function", "displayName": "Body", "group": "function", "label": "function", "required": false, "javaType": "Object", "deprecated": false, "deprecationNote": "", "autowired": false, "secret": false, "description": "The message body" }, + "prettyBody": { "index": 1, "kind": "function", "displayName": "Pretty Body", "group": "function", "label": "function", "required": false, "javaType": "String", "deprecated": false, "deprecationNote": "", "autowired": false, "secret": false, "description": "Converts the body to a String, and attempts to pretty print if JSon or XML; otherwise the body is returned as the String value." }, + "bodyOneLine": { "index": 2, "kind": "function", "displayName": "Body One Line", "group": "function", "label": "function", "required": false, "javaType": "String", "deprecated": false, "deprecationNote": "", "autowired": false, "secret": false, "description": "Converts the body to a String and removes all line-breaks, so the string is in one line." }, + "originalBody": { "index": 3, "kind": "function", "displayName": "Original Body", "group": "function", "label": "function", "required": false, "javaType": "Object", "deprecated": false, "deprecationNote": "", "autowired": false, "secret": false, "description": "The original incoming body (only available if allowUseOriginalMessage=true)." }, + "id": { "index": 4, "kind": "function", "displayName": "Id", "group": "function", "label": "function", "required": false, "javaType": "String", "deprecated": false, "deprecationNote": "", "autowired": false, "secret": false, "description": "The message id" }, + "messageTimestamp": { "index": 5, "kind": "function", "displayName": "Message Timestamp", "group": "function", "label": "function", "required": false, "javaType": "long", "deprecated": false, "deprecationNote": "", "autowired": false, "secret": false, "description": "The message timestamp (millis since epoc) that this message originates from. Some systems like JMS, Kafka, AWS have a timestamp on the event\/message that Camel received. This method returns the timestamp if a timestamp [...] + "exchangeId": { "index": 6, "kind": "function", "displayName": "Exchange Id", "group": "function", "label": "function", "required": false, "javaType": "String", "deprecated": false, "deprecationNote": "", "autowired": false, "secret": false, "description": "The exchange id" }, + "exchange": { "index": 7, "kind": "function", "displayName": "Exchange", "group": "function", "label": "function", "required": false, "javaType": "org.apache.camel.Exchange", "deprecated": false, "deprecationNote": "", "autowired": false, "secret": false, "description": "The exchange" }, + "exception": { "index": 8, "kind": "function", "displayName": "Exception", "group": "function", "label": "function", "required": false, "javaType": "java.lang.Exception", "deprecated": false, "deprecationNote": "", "autowired": false, "secret": false, "description": "The exception object on the exchange (also from caught exceptions), is null if no exception present." }, + "exception.message": { "index": 9, "kind": "function", "displayName": "Exception.message", "group": "function", "label": "function", "required": false, "javaType": "String", "deprecated": false, "deprecationNote": "", "autowired": false, "secret": false, "description": "The exception message (also from caught exceptions), is null if no exception present." }, + "exception.stackTrace": { "index": 10, "kind": "function", "displayName": "Exception.stack Trace", "group": "function", "label": "function", "required": false, "javaType": "String", "deprecated": false, "deprecationNote": "", "autowired": false, "secret": false, "description": "The exception stacktrace (also from caught exceptions), is null if no exception present." }, + "threadId": { "index": 11, "kind": "function", "displayName": "Thread Id", "group": "function", "label": "function", "required": false, "javaType": "long", "deprecated": false, "deprecationNote": "", "autowired": false, "secret": false, "description": "Returns the id of the current thread. Can be used for logging." }, + "threadName": { "index": 12, "kind": "function", "displayName": "Thread Name", "group": "function", "label": "function", "required": false, "javaType": "String", "deprecated": false, "deprecationNote": "", "autowired": false, "secret": false, "description": "Returns the name of the current thread. Can be used for logging." }, + "hostName": { "index": 13, "kind": "function", "displayName": "Host Name", "group": "function", "label": "function", "required": false, "javaType": "String", "deprecated": false, "deprecationNote": "", "autowired": false, "secret": false, "description": "Returns the local hostname (may be empty if not possible to resolve)." }, + "camelId": { "index": 14, "kind": "function", "displayName": "Camel Id", "group": "function", "label": "function", "required": false, "javaType": "String", "deprecated": false, "deprecationNote": "", "autowired": false, "secret": false, "description": "The name of the CamelContext" }, + "routeId": { "index": 15, "kind": "function", "displayName": "Route Id", "group": "function", "label": "function", "required": false, "javaType": "String", "deprecated": false, "deprecationNote": "", "autowired": false, "secret": false, "description": "The route id of the current route the Exchange is being routed" }, + "fromRouteId": { "index": 16, "kind": "function", "displayName": "From Route Id", "group": "function", "label": "function", "required": false, "javaType": "String", "deprecated": false, "deprecationNote": "", "autowired": false, "secret": false, "description": "The original route id where this exchange was created." }, + "routeGroup": { "index": 17, "kind": "function", "displayName": "Route Group", "group": "function", "label": "function", "required": false, "javaType": "String", "deprecated": false, "deprecationNote": "", "autowired": false, "secret": false, "description": "Returns the route group of the current route the Exchange is being routed. Not all routes have a group assigned, so this may be null." }, + "stepId": { "index": 18, "kind": "function", "displayName": "Step Id", "group": "function", "label": "function", "required": false, "javaType": "String", "deprecated": false, "deprecationNote": "", "autowired": false, "secret": false, "description": "Returns the id of the current step the Exchange is being routed." }, + "null": { "index": 19, "kind": "function", "displayName": "Null", "group": "function", "label": "function", "required": false, "javaType": "Object", "deprecated": false, "deprecationNote": "", "autowired": false, "secret": false, "description": "Represents a null value" } } } diff --git a/core/camel-core-languages/src/main/java/org/apache/camel/language/simple/SimpleConstants.java b/core/camel-core-languages/src/main/java/org/apache/camel/language/simple/SimpleConstants.java index 64c221ca50f..a4f2ce69140 100644 --- a/core/camel-core-languages/src/main/java/org/apache/camel/language/simple/SimpleConstants.java +++ b/core/camel-core-languages/src/main/java/org/apache/camel/language/simple/SimpleConstants.java @@ -20,13 +20,16 @@ import org.apache.camel.spi.Metadata; public final class SimpleConstants { - @Metadata(description = "The message body", label = "function") + @Metadata(description = "The message body", javaType = "Object", label = "function") public static final String BODY = "body"; - @Metadata(description = "Converts the body to a String, and attempts to pretty print if JSon or XML; otherwise the body is returned as the String value.", javaType = "String", label = "function") + @Metadata(description = "Converts the body to a String, and attempts to pretty print if JSon or XML; otherwise the body is returned as the String value.", + javaType = "String", label = "function") public static final String PRETTY_BODY = "prettyBody"; - @Metadata(description = "Converts the body to a String and removes all line-breaks, so the string is in one line.", javaType = "String", label = "function") + @Metadata(description = "Converts the body to a String and removes all line-breaks, so the string is in one line.", + javaType = "String", label = "function") public static final String BODY_ONE_LINE = "bodyOneLine"; - @Metadata(description = "The original incoming body (only available if allowUseOriginalMessage=true).", label = "function") + @Metadata(description = "The original incoming body (only available if allowUseOriginalMessage=true).", javaType = "Object", + label = "function") public static final String ORIGINAL_BODY = "originalBody"; @Metadata(description = "The message id", javaType = "String", label = "function") public static final String ID = "id"; @@ -35,35 +38,45 @@ public final class SimpleConstants { + " The message timestamp and exchange created are different. An exchange always has a created timestamp which is the" + " local timestamp when Camel created the exchange. The message timestamp is only available in some Camel components" + " when the consumer is able to extract the timestamp from the source event." - + " If the message has no timestamp, then 0 is returned.", javaType = "long", label = "function") + + " If the message has no timestamp, then 0 is returned.", + javaType = "long", label = "function") public static final String MESSAGE_TIMESTAMP = "messageTimestamp"; @Metadata(description = "The exchange id", javaType = "String", label = "function") public static final String EXCHANGE_ID = "exchangeId"; - @Metadata(description = "The exchange", javaType = "Exchange", label = "function") + @Metadata(description = "The exchange", javaType = "org.apache.camel.Exchange", label = "function") public static final String EXCHANGE = "exchange"; - @Metadata(description = "The exception object on the exchange (also from caught exceptions), is null if no exception present.", javaType = "Exception", label = "function") + @Metadata(description = "The exception object on the exchange (also from caught exceptions), is null if no exception present.", + javaType = "java.lang.Exception", label = "function") public static final String EXCEPTION = "exception"; - @Metadata(description = "The exception message (also from caught exceptions), is null if no exception present.", javaType = "String", label = "function") + @Metadata(description = "The exception message (also from caught exceptions), is null if no exception present.", + javaType = "String", label = "function") public static final String EXCEPTION_MESSAGE = "exception.message"; - @Metadata(description = "The exception stacktrace (also from caught exceptions), is null if no exception present.", javaType = "String", label = "function") + @Metadata(description = "The exception stacktrace (also from caught exceptions), is null if no exception present.", + javaType = "String", label = "function") public static final String EXCEPTION_STACKTRACE = "exception.stackTrace"; - @Metadata(description = "Returns the id of the current thread. Can be used for logging.", javaType = "long", label = "function") + @Metadata(description = "Returns the id of the current thread. Can be used for logging.", javaType = "long", + label = "function") public static final String THREAD_ID = "threadId"; - @Metadata(description = "Returns the name of the current thread. Can be used for logging.", javaType = "String", label = "function") + @Metadata(description = "Returns the name of the current thread. Can be used for logging.", javaType = "String", + label = "function") public static final String THREAD_NAME = "threadName"; - @Metadata(description = "Returns the local hostname (may be empty if not possible to resolve).", javaType = "String", label = "function") + @Metadata(description = "Returns the local hostname (may be empty if not possible to resolve).", javaType = "String", + label = "function") public static final String HOST_NAME = "hostName"; @Metadata(description = "The name of the CamelContext", javaType = "String", label = "function") public static final String CAMEL_ID = "camelId"; - @Metadata(description = "The route id of the current route the Exchange is being routed", javaType = "String", label = "function") + @Metadata(description = "The route id of the current route the Exchange is being routed", javaType = "String", + label = "function") public static final String ROUTE_ID = "routeId"; @Metadata(description = "The original route id where this exchange was created.", javaType = "String", label = "function") public static final String FROM_ROUTE_ID = "fromRouteId"; - @Metadata(description = "Returns the route group of the current route the Exchange is being routed. Not all routes have a group assigned, so this may be null.", javaType = "String", label = "function") + @Metadata(description = "Returns the route group of the current route the Exchange is being routed. Not all routes have a group assigned, so this may be null.", + javaType = "String", label = "function") public static final String ROUTE_GROUP = "routeGroup"; - @Metadata(description = "Returns the id of the current step the Exchange is being routed.", javaType = "String", label = "function") + @Metadata(description = "Returns the id of the current step the Exchange is being routed.", javaType = "String", + label = "function") public static final String STEP_ID = "stepId"; - @Metadata(description = "Represents a null value", label = "function") + @Metadata(description = "Represents a null value", label = "function", javaType = "Object") public static final String NULL = "null"; } diff --git a/tooling/maven/camel-package-maven-plugin/src/main/java/org/apache/camel/maven/packaging/AbstractGenerateConfigurerMojo.java b/tooling/maven/camel-package-maven-plugin/src/main/java/org/apache/camel/maven/packaging/AbstractGenerateConfigurerMojo.java index 504f774d1b2..f76a3e6fc3c 100644 --- a/tooling/maven/camel-package-maven-plugin/src/main/java/org/apache/camel/maven/packaging/AbstractGenerateConfigurerMojo.java +++ b/tooling/maven/camel-package-maven-plugin/src/main/java/org/apache/camel/maven/packaging/AbstractGenerateConfigurerMojo.java @@ -518,4 +518,14 @@ public abstract class AbstractGenerateConfigurerMojo extends AbstractGeneratorMo return false; } + private static String asString(AnnotationInstance ai, String name) { + if (ai != null) { + AnnotationValue av = ai.value(name); + if (av != null) { + return av.asString(); + } + } + return null; + } + } diff --git a/tooling/maven/camel-package-maven-plugin/src/main/java/org/apache/camel/maven/packaging/PackageLanguageMojo.java b/tooling/maven/camel-package-maven-plugin/src/main/java/org/apache/camel/maven/packaging/PackageLanguageMojo.java index 1fec1f4b1e5..c9c561e5929 100644 --- a/tooling/maven/camel-package-maven-plugin/src/main/java/org/apache/camel/maven/packaging/PackageLanguageMojo.java +++ b/tooling/maven/camel-package-maven-plugin/src/main/java/org/apache/camel/maven/packaging/PackageLanguageMojo.java @@ -18,6 +18,7 @@ package org.apache.camel.maven.packaging; import java.io.File; import java.io.IOException; +import java.lang.reflect.Field; import java.nio.file.Path; import java.util.Collections; import java.util.HashMap; @@ -26,7 +27,9 @@ import java.util.stream.Collectors; import java.util.stream.Stream; import org.apache.camel.maven.packaging.generics.ClassUtil; +import org.apache.camel.maven.packaging.generics.PackagePluginUtils; import org.apache.camel.spi.Metadata; +import org.apache.camel.spi.annotations.Language; import org.apache.camel.tooling.model.EipModel; import org.apache.camel.tooling.model.EipModel.EipOptionModel; import org.apache.camel.tooling.model.JsonMapper; @@ -34,6 +37,7 @@ import org.apache.camel.tooling.model.LanguageModel; import org.apache.camel.tooling.model.LanguageModel.LanguageOptionModel; import org.apache.camel.tooling.model.SupportLevel; import org.apache.camel.tooling.util.PackageHelper; +import org.apache.camel.tooling.util.Strings; import org.apache.maven.plugin.MojoExecutionException; import org.apache.maven.plugin.MojoFailureException; import org.apache.maven.plugin.logging.Log; @@ -42,6 +46,10 @@ import org.apache.maven.plugins.annotations.Parameter; import org.apache.maven.project.MavenProject; import org.apache.maven.project.MavenProjectHelper; import org.codehaus.plexus.build.BuildContext; +import org.jboss.jandex.DotName; +import org.jboss.jandex.IndexView; + +import static java.lang.reflect.Modifier.isStatic; /** * Analyses the Camel plugins in a project and generates extra descriptor information for easier auto-discovery in @@ -50,6 +58,9 @@ import org.codehaus.plexus.build.BuildContext; @Mojo(name = "generate-languages-list", threadSafe = true) public class PackageLanguageMojo extends AbstractGeneratorMojo { + private static final DotName LANGUAGE = DotName.createSimple(Language.class.getName()); + private IndexView indexView; + /** * The output directory for generated languages file */ @@ -203,7 +214,7 @@ public class PackageLanguageMojo extends AbstractGeneratorMojo { return count; } - protected static LanguageModel extractLanguageModel(MavenProject project, String json, String name, Class<?> javaType) { + protected LanguageModel extractLanguageModel(MavenProject project, String json, String name, Class<?> javaType) { EipModel def = JsonMapper.generateEipModel(json); LanguageModel model = new LanguageModel(); model.setName(name); @@ -259,11 +270,55 @@ public class PackageLanguageMojo extends AbstractGeneratorMojo { model.addOption(option); } - // TODO: read class and find functionClass and add each field as a function in the model + // read class and find functionClass and add each field as a function in the model + Class<?> classElement = loadClass(javaType.getName()); + final Language lan = classElement.getAnnotation(Language.class); + if (lan.functionsClass() != void.class) { + classElement = loadClass(lan.functionsClass().getName()); + if (classElement != null) { + for (Field field : classElement.getFields()) { + final boolean isEnum = classElement.isEnum(); + if ((isEnum || isStatic(field.getModifiers()) && field.getType() == String.class) + && field.isAnnotationPresent(Metadata.class)) { + try { + addFunction(model, field); + } catch (Exception e) { + getLog().warn(e); + } + } + } + } + } return model; } + private void addFunction(LanguageModel model, Field field) throws Exception { + final Class<?> declaringClass = field.getDeclaringClass(); + final Metadata metadata = field.getAnnotation(Metadata.class); + LanguageModel.LanguageFunctionModel fun = new LanguageModel.LanguageFunctionModel(); + fun.setConstantName(String.format("%s#%s", declaringClass.getName(), field.getName())); + fun.setName((String) field.get(null)); + fun.setDescription(metadata.description().trim()); + fun.setKind("function"); + String displayName = metadata.displayName(); + // compute a display name if we don't have anything + if (Strings.isNullOrEmpty(displayName)) { + displayName = Strings.asTitle(fun.getName()); + } + fun.setDisplayName(displayName); + fun.setJavaType(metadata.javaType()); + fun.setRequired(metadata.required()); + fun.setDefaultValue(metadata.defaultValue()); + fun.setDeprecated(field.isAnnotationPresent(Deprecated.class)); + fun.setDeprecationNote(metadata.deprecationNote()); + fun.setSecret(metadata.secret()); + String group = EndpointHelper.labelAsGroupName(metadata.label(), false, false); + fun.setGroup(group); + fun.setLabel(metadata.label()); + model.addFunction(fun); + } + private static String readClassFromCamelResource(File file, StringBuilder buffer, BuildContext buildContext) throws MojoExecutionException { // skip directories as there may be a sub .resolver directory such as in @@ -329,4 +384,11 @@ public class PackageLanguageMojo extends AbstractGeneratorMojo { return "META-INF/" + pckName.replace('.', '/'); } + private IndexView getIndex() { + if (indexView == null) { + indexView = PackagePluginUtils.readJandexIndexQuietly(project); + } + return indexView; + } + } diff --git a/tooling/maven/camel-package-maven-plugin/src/main/java/org/apache/camel/maven/packaging/generics/ClassUtil.java b/tooling/maven/camel-package-maven-plugin/src/main/java/org/apache/camel/maven/packaging/generics/ClassUtil.java index f15d3c1c401..6616cbcbbdd 100644 --- a/tooling/maven/camel-package-maven-plugin/src/main/java/org/apache/camel/maven/packaging/generics/ClassUtil.java +++ b/tooling/maven/camel-package-maven-plugin/src/main/java/org/apache/camel/maven/packaging/generics/ClassUtil.java @@ -81,8 +81,7 @@ public final class ClassUtil { * type otherwise it return the casted {@link Class} of the type argument. * </p> * - * @param type class or parametrized type - * @return + * @param type class or parametrized type */ public static Class<?> getClass(Type type) { return getClazz(type); @@ -209,4 +208,5 @@ public final class ClassUtil { .map(annotation -> annotation.annotationType().getName()) .anyMatch(fqAnnotationName::equals); } + }