You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@camel.apache.org by nf...@apache.org on 2022/06/21 09:44:57 UTC

[camel] branch CAMEL-18141/headers-dsl created (now 4b579a6aca1)

This is an automated email from the ASF dual-hosted git repository.

nfilotto pushed a change to branch CAMEL-18141/headers-dsl
in repository https://gitbox.apache.org/repos/asf/camel.git


      at 4b579a6aca1 CAMEL-18141: camel-endpoint-dsl - Generate fluent builders for endpoint headers

This branch includes the following new commits:

     new 4b579a6aca1 CAMEL-18141: camel-endpoint-dsl - Generate fluent builders for endpoint headers

The 1 revisions listed above as "new" are entirely new to this
repository and will be described in separate emails.  The revisions
listed as "add" were already present in the repository and have only
been added to this reference.



[camel] 01/01: CAMEL-18141: camel-endpoint-dsl - Generate fluent builders for endpoint headers

Posted by nf...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

nfilotto pushed a commit to branch CAMEL-18141/headers-dsl
in repository https://gitbox.apache.org/repos/asf/camel.git

commit 4b579a6aca16bc113ed8d6960ba6e507f00dd3b9
Author: Nicolas Filotto <nf...@talend.com>
AuthorDate: Tue Jun 21 11:43:21 2022 +0200

    CAMEL-18141: camel-endpoint-dsl - Generate fluent builders for endpoint headers
---
 .../modules/ROOT/pages/Endpoint-dsl.adoc           | 32 ++++++--
 .../camel/tooling/util/srcgen/JavaClass.java       |  5 +-
 .../camel/maven/packaging/EndpointDslMojo.java     | 94 +++++++++++++++++++++-
 .../maven/packaging/generics/JavadocUtil.java      | 56 ++++++++-----
 4 files changed, 158 insertions(+), 29 deletions(-)

diff --git a/docs/user-manual/modules/ROOT/pages/Endpoint-dsl.adoc b/docs/user-manual/modules/ROOT/pages/Endpoint-dsl.adoc
index 5937fd506e5..7ec437b115f 100644
--- a/docs/user-manual/modules/ROOT/pages/Endpoint-dsl.adoc
+++ b/docs/user-manual/modules/ROOT/pages/Endpoint-dsl.adoc
@@ -13,11 +13,11 @@ The following is an example of an FTP route using the standard `RouteBuilder` Ja
 ----
 public class MyRoutes extends RouteBuilder {
     @Override
-    public void configure() throws Exception {
-        from("ftp://foo@myserver?password=secret&recursive=true&
-               ftpClient.dataTimeout=30000&
-               ftpClientConfig.serverLanguageCode=fr")
-        .to("bean:doSomething");
+    public void configure() {
+        from("ftp://foo@myserver?password=secret&recursive=true&" +
+                "ftpClient.dataTimeout=30000&" +
+                "ftpClientConfig.serverLanguageCode=fr")
+                .to("bean:doSomething");
     }
 }
 ----
@@ -63,6 +63,28 @@ from(jms("myWMQ", "cheese").concurrentConsumers(5))
 Notice how we can refer to their names as the first parameter in the `jms` fluent builder.
 The example would then consume messages from WebSphereMQ queue named cheese and route to ActiveMQ on a queue named smelly.
 
+=== Headers' name
+
+The endpoint-dsl can also be used to be assisted when selecting the name of a header to set or to get. The headers' name builder
+is accessible directly from the method of the class `EndpointRouteBuilder` without argument whose name is the scheme of
+the target component.
+
+In the example below the method `file()` available from `EndpointRouteBuilder`, gives access to the methods corresponding to the name of the headers of the file component. Here the method `fileName()` is called to get the name of the header for the name of the file.
+
+[source,java]
+----
+public class MyRoutes extends EndpointRouteBuilder {
+    @Override
+    public void configure() {
+        from(/*some endpoint*/)
+            // Some route start
+            .setHeader(file().fileName(), constant("foo.txt"))
+            // Some route end
+            ;
+    }
+}
+----
+
 === Using Endpoint-DSL outside route builders
 
 You can use the type-safe endpoint-dsl outside route builders with:
diff --git a/tooling/camel-tooling-util/src/main/java/org/apache/camel/tooling/util/srcgen/JavaClass.java b/tooling/camel-tooling-util/src/main/java/org/apache/camel/tooling/util/srcgen/JavaClass.java
index 4ebf5b2707c..6cb7862e2f1 100644
--- a/tooling/camel-tooling-util/src/main/java/org/apache/camel/tooling/util/srcgen/JavaClass.java
+++ b/tooling/camel-tooling-util/src/main/java/org/apache/camel/tooling/util/srcgen/JavaClass.java
@@ -19,6 +19,7 @@ package org.apache.camel.tooling.util.srcgen;
 import java.util.ArrayList;
 import java.util.Comparator;
 import java.util.LinkedHashMap;
+import java.util.LinkedHashSet;
 import java.util.List;
 import java.util.Map;
 import java.util.Objects;
@@ -34,7 +35,7 @@ public class JavaClass {
     String name;
     String extendsName = "java.lang.Object";
     List<String> implementNames = new ArrayList<>();
-    List<String> imports = new ArrayList<>();
+    Set<String> imports = new LinkedHashSet<>();
     List<Annotation> annotations = new ArrayList<>();
     List<Property> properties = new ArrayList<>();
     List<Field> fields = new ArrayList<>();
@@ -134,7 +135,7 @@ public class JavaClass {
         return this;
     }
 
-    public List<String> getImports() {
+    public Set<String> getImports() {
         return imports;
     }
 
diff --git a/tooling/maven/camel-package-maven-plugin/src/main/java/org/apache/camel/maven/packaging/EndpointDslMojo.java b/tooling/maven/camel-package-maven-plugin/src/main/java/org/apache/camel/maven/packaging/EndpointDslMojo.java
index 9708ae905ff..986fe6ebb6b 100644
--- a/tooling/maven/camel-package-maven-plugin/src/main/java/org/apache/camel/maven/packaging/EndpointDslMojo.java
+++ b/tooling/maven/camel-package-maven-plugin/src/main/java/org/apache/camel/maven/packaging/EndpointDslMojo.java
@@ -34,14 +34,17 @@ import javax.annotation.Generated;
 import org.apache.camel.maven.packaging.dsl.DslHelper;
 import org.apache.camel.maven.packaging.generics.JavadocUtil;
 import org.apache.camel.tooling.model.BaseModel;
+import org.apache.camel.tooling.model.BaseOptionModel;
 import org.apache.camel.tooling.model.ComponentModel;
 import org.apache.camel.tooling.model.ComponentModel.EndpointOptionModel;
 import org.apache.camel.tooling.model.JsonMapper;
 import org.apache.camel.tooling.util.JavadocHelper;
 import org.apache.camel.tooling.util.Strings;
+import org.apache.camel.tooling.util.srcgen.Field;
 import org.apache.camel.tooling.util.srcgen.GenericType;
 import org.apache.camel.tooling.util.srcgen.JavaClass;
 import org.apache.camel.tooling.util.srcgen.Method;
+import org.apache.commons.text.CaseUtils;
 import org.apache.maven.plugin.MojoExecutionException;
 import org.apache.maven.plugin.MojoFailureException;
 import org.apache.maven.plugins.annotations.LifecyclePhase;
@@ -361,6 +364,11 @@ public class EndpointDslMojo extends AbstractGeneratorMojo {
             method.addAnnotation(Deprecated.class);
         }
 
+        final JavaClass headerNameBuilderClass = addHeaderNameBuilderClass(javaClass, model);
+        if (headerNameBuilderClass != null) {
+            addHeaderNameBuilderMethod(dslClass, headerNameBuilderClass, model);
+        }
+
         if (aliases.size() == 1) {
             processAliases(model, staticBuilders, javaClass, builderClass, dslClass);
         } else {
@@ -371,6 +379,84 @@ public class EndpointDslMojo extends AbstractGeneratorMojo {
                 false);
     }
 
+    /**
+     * Adds the static inner class allowing to have access to all the headers of the given component.
+     *
+     * @param  javaClass the main class into which the static inner class is added.
+     * @param  model     the model from which the information of the component are extracted.
+     * @return           the static inner class that has been added if any, {@code null} otherwise.
+     */
+    private JavaClass addHeaderNameBuilderClass(JavaClass javaClass, ComponentModel model) {
+        final List<ComponentModel.EndpointHeaderModel> endpointHeaders = model.getEndpointHeaders();
+        if (endpointHeaders.isEmpty()) {
+            return null;
+        }
+        final JavaClass builderClass = javaClass.addNestedType();
+        builderClass.setName(getComponentNameFromType(model.getJavaType()) + "HeaderNameBuilder")
+                .setPublic()
+                .setStatic(true);
+        builderClass.getJavaDoc().setText("The builder of headers' name for the " + model.getTitle() + " component.");
+        generateDummyClass(builderClass.getCanonicalName());
+        final Field singleton = builderClass.addField();
+        singleton.setPrivate().setStatic(true).setFinal(true).setName("INSTANCE")
+                .setType(loadClass(builderClass.getCanonicalName()))
+                .setLiteralInitializer(String.format("new %s()", builderClass.getName()));
+        singleton.getJavaDoc().setText(
+                "The internal instance of the builder used to access to all the methods representing the name of headers.");
+
+        for (ComponentModel.EndpointHeaderModel header : endpointHeaders) {
+            addHeaderNameMethod(builderClass, header);
+        }
+        return builderClass;
+    }
+
+    /**
+     * Adds the method allowing to retrieve the header name of the given header.
+     *
+     * @param builderClass the static inner class to which the method is added.
+     * @param header       the header whose name is returned by the method
+     */
+    private void addHeaderNameMethod(JavaClass builderClass, ComponentModel.EndpointHeaderModel header) {
+        String headerName = header.getName();
+        final String camelPrefix = "camel";
+        if (headerName.toLowerCase().startsWith(camelPrefix)) {
+            headerName = headerName.substring(camelPrefix.length());
+        }
+        final String name;
+        if (headerName.chars().anyMatch(c -> c == ':' || c == '-' || c == '.' || c == '_')) {
+            name = CaseUtils.toCamelCase(headerName, false, ':', '-', '.', '_');
+        } else {
+            name = headerName.substring(0, 1).toLowerCase() + headerName.substring(1);
+        }
+        final Method method = builderClass.addMethod().setPublic().setReturnType(String.class)
+                .setName(name);
+        String javaDoc = createBaseDescription(header, "header", true).replace("@@REPLACE_ME@@",
+                "\nThe option is a: {@code " + header.getJavaType() + "} type.");
+        javaDoc += String.format("%n%n@return the name of the header {@code %s}.%n", headerName);
+        method.getJavaDoc().setText(javaDoc);
+        method.setBodyF("return \"%s\";", headerName);
+        if (header.isDeprecated()) {
+            method.addAnnotation(Deprecated.class);
+        }
+    }
+
+    /**
+     * Adds the method to the DSL class allowing to have access to all the headers of the component.
+     *
+     * @param dslClass     the DSL class into which the method should be added.
+     * @param builderClass the builder class that gives access to all the headers of the component.
+     * @param model        the model from which the information of the component are extracted.
+     */
+    private void addHeaderNameBuilderMethod(JavaClass dslClass, JavaClass builderClass, ComponentModel model) {
+        final Method method = dslClass.addMethod();
+        method.setDefault().setReturnType(loadClass(builderClass.getCanonicalName()))
+                .setName(camelCaseLower(model.getScheme()))
+                .setBodyF("return %s.INSTANCE;", builderClass.getName());
+        String javaDoc = getMainDescription(model, false);
+        javaDoc += "\n\n@return the dsl builder for the headers' name.\n";
+        method.getJavaDoc().setText(javaDoc);
+    }
+
     private void processMasterScheme(
             List<ComponentModel> aliases, List<Method> staticBuilders, JavaClass javaClass, JavaClass builderClass,
             JavaClass dslClass) {
@@ -616,7 +702,11 @@ public class EndpointDslMojo extends AbstractGeneratorMojo {
         fluent.getJavaDoc().setText(sb.toString());
     }
 
-    private String createBaseDescription(EndpointOptionModel option) {
+    private String createBaseDescription(BaseOptionModel option) {
+        return createBaseDescription(option, "parameter", false);
+    }
+
+    private String createBaseDescription(BaseOptionModel option, String kind, boolean ignoreMultiValue) {
         String baseDesc = option.getDescription();
         if (Strings.isEmpty(baseDesc)) {
             return baseDesc;
@@ -641,7 +731,7 @@ public class EndpointDslMojo extends AbstractGeneratorMojo {
         // context-path and not as individual options
         // so lets only mark query parameters that are required as
         // required
-        if ("parameter".equals(option.getKind()) && option.isRequired()) {
+        if (kind.equals(option.getKind()) && option.isRequired()) {
             baseDescBuilder.append("\nRequired: true");
         }
         // include default value (if any)
diff --git a/tooling/maven/camel-package-maven-plugin/src/main/java/org/apache/camel/maven/packaging/generics/JavadocUtil.java b/tooling/maven/camel-package-maven-plugin/src/main/java/org/apache/camel/maven/packaging/generics/JavadocUtil.java
index c2942581126..1467992c867 100644
--- a/tooling/maven/camel-package-maven-plugin/src/main/java/org/apache/camel/maven/packaging/generics/JavadocUtil.java
+++ b/tooling/maven/camel-package-maven-plugin/src/main/java/org/apache/camel/maven/packaging/generics/JavadocUtil.java
@@ -31,7 +31,21 @@ public final class JavadocUtil {
 
     }
 
+    /**
+     * @param  model the model from which the information are extracted.
+     * @return       the description of the given component with all the possible details.
+     */
     public static String getMainDescription(ComponentModel model) {
+        return getMainDescription(model, true);
+    }
+
+    /**
+     * @param  model                    the model from which the information are extracted.
+     * @param  withPathParameterDetails indicates whether the information about the path parameters should be added to
+     *                                  the description.
+     * @return                          the description of the given component.
+     */
+    public static String getMainDescription(ComponentModel model, boolean withPathParameterDetails) {
         StringBuilder descSb = new StringBuilder(512);
 
         descSb.append(model.getTitle()).append(" (").append(model.getArtifactId()).append(")");
@@ -40,26 +54,28 @@ public final class JavadocUtil {
         descSb.append("\nSince: ").append(model.getFirstVersionShort());
         descSb.append("\nMaven coordinates: ").append(model.getGroupId()).append(":").append(model.getArtifactId());
 
-        // include javadoc for all path parameters and mark which are required
-        descSb.append("\n\nSyntax: <code>").append(model.getSyntax()).append("</code>");
-        for (ComponentModel.EndpointOptionModel option : model.getEndpointOptions()) {
-            if ("path".equals(option.getKind())) {
-                descSb.append("\n\nPath parameter: ").append(option.getName());
-                if (option.isRequired()) {
-                    descSb.append(" (required)");
-                }
-                if (option.isDeprecated()) {
-                    descSb.append(" <strong>deprecated</strong>");
-                }
-                descSb.append("\n").append(option.getDescription());
-                if (option.getDefaultValue() != null) {
-                    descSb.append("\nDefault value: ").append(option.getDefaultValue());
-                }
-                // TODO: default value note ?
-                if (option.getEnums() != null && !option.getEnums().isEmpty()) {
-                    descSb.append("\nThere are ").append(option.getEnums().size())
-                            .append(" enums and the value can be one of: ")
-                            .append(wrapEnumValues(option.getEnums()));
+        if (withPathParameterDetails) {
+            // include javadoc for all path parameters and mark which are required
+            descSb.append("\n\nSyntax: <code>").append(model.getSyntax()).append("</code>");
+            for (ComponentModel.EndpointOptionModel option : model.getEndpointOptions()) {
+                if ("path".equals(option.getKind())) {
+                    descSb.append("\n\nPath parameter: ").append(option.getName());
+                    if (option.isRequired()) {
+                        descSb.append(" (required)");
+                    }
+                    if (option.isDeprecated()) {
+                        descSb.append(" <strong>deprecated</strong>");
+                    }
+                    descSb.append("\n").append(option.getDescription());
+                    if (option.getDefaultValue() != null) {
+                        descSb.append("\nDefault value: ").append(option.getDefaultValue());
+                    }
+                    // TODO: default value note ?
+                    if (option.getEnums() != null && !option.getEnums().isEmpty()) {
+                        descSb.append("\nThere are ").append(option.getEnums().size())
+                                .append(" enums and the value can be one of: ")
+                                .append(wrapEnumValues(option.getEnums()));
+                    }
                 }
             }
         }