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:49:04 UTC

[camel] branch CAMEL-18141/headers-dsl updated (4b579a6aca1 -> ead40103e93)

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


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

This update added new revisions after undoing existing revisions.
That is to say, some revisions that were in the old version of the
branch are not in the new version.  This situation occurs
when a user --force pushes a change and generates a repository
containing something like this:

 * -- * -- B -- O -- O -- O   (4b579a6aca1)
            \
             N -- N -- N   refs/heads/CAMEL-18141/headers-dsl (ead40103e93)

You should already have received notification emails for all of the O
revisions, and so the following emails describe only the N revisions
from the common base, B.

Any revisions marked "omit" are not gone; other references still
refer to them.  Any revisions marked "discard" are gone forever.

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.


Summary of changes:
 .../main/java/org/apache/camel/tooling/util/srcgen/JavaClass.java    | 5 ++---
 1 file changed, 2 insertions(+), 3 deletions(-)


[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 ead40103e9359796487d158feeabf9be28001622
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/maven/packaging/EndpointDslMojo.java     | 94 +++++++++++++++++++++-
 .../maven/packaging/generics/JavadocUtil.java      | 56 ++++++++-----
 3 files changed, 155 insertions(+), 27 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/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()));
+                    }
                 }
             }
         }