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/04/07 12:42:11 UTC

[camel] branch main updated: CAMEL-17918: Define the header name provider in case the headers class is an enum (#7382)

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

nfilotto 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 ec4700704cb CAMEL-17918: Define the header name provider in case the headers class is an enum (#7382)
ec4700704cb is described below

commit ec4700704cb5bf6013790e1c72c861edd5380345
Author: Nicolas Filotto <es...@users.noreply.github.com>
AuthorDate: Thu Apr 7 14:41:56 2022 +0200

    CAMEL-17918: Define the header name provider in case the headers class is an enum (#7382)
    
    ## Motivation
    
    When the headers class is an enum, so far the name of the headers can only be the name of the values of the enum which is not enough in some particular use cases. The idea of this improvement is to be able to provide the name of the field or method to invoke in order to get the name of the corresponding header.
    
     ## Modifications
    
    * Add a new element called `headersNameProvider` to the annotation `UriEndpoint` in order to be able to provide the name of the field to get or the name of the method to invoke to get the name of the header
    * Add a dedicated method allowing to get the name of an header from a field.
---
 .../java/org/apache/camel/spi/UriEndpoint.java     | 28 +++++++
 .../packaging/EndpointSchemaGeneratorMojo.java     | 96 ++++++++++++++++++----
 .../packaging/EndpointSchemaGeneratorMojoTest.java | 14 ++++
 .../SomeEndpointUsingEnumConstantsByField.java     | 24 ++++++
 .../SomeEndpointUsingEnumConstantsByMethod.java    | 24 ++++++
 .../endpoint/SomeEnumConstantsByField.java         | 30 +++++++
 .../endpoint/SomeEnumConstantsByMethod.java        | 28 +++++++
 .../java/org/apache/camel/spi/UriEndpoint.java     | 28 +++++++
 8 files changed, 254 insertions(+), 18 deletions(-)

diff --git a/core/camel-api/src/generated/java/org/apache/camel/spi/UriEndpoint.java b/core/camel-api/src/generated/java/org/apache/camel/spi/UriEndpoint.java
index d92ea81e46b..e9c64483bd9 100644
--- a/core/camel-api/src/generated/java/org/apache/camel/spi/UriEndpoint.java
+++ b/core/camel-api/src/generated/java/org/apache/camel/spi/UriEndpoint.java
@@ -187,4 +187,32 @@ public @interface UriEndpoint {
      * {@code String} constant representing its name and defined in the headers class.
      */
     Class<?> headersClass() default void.class;
+
+    /**
+     * The name of the field to get or the name of the method to invoke to get the name of the headers defined in an
+     * enum.
+     * <p/>
+     * Only took into account if and only if the class defined as {@code headersClass} is an enum.
+     * <p/>
+     * For example, assuming that {@code SomeEnum} has been configured as the {@code headersClass} of a given component,
+     * since the name of the header is actually the value of the field {@code headerName}, the element
+     * {@code headersNameProvider} should be set to {@code "headerName"} to get the expected header names.
+     *
+     * <pre>
+     * <code>
+     *
+     * public enum SomeEnum {
+     *    {@literal @}Metadata
+     *     FOO("fooKey");
+     *
+     *     public final String headerName;
+     *
+     *     SomeEnum(final String str) {
+     *         this.headerName = str;
+     *     }
+     * }
+     * </code>
+     * </pre>
+     */
+    String headersNameProvider() default "";
 }
diff --git a/tooling/maven/camel-package-maven-plugin/src/main/java/org/apache/camel/maven/packaging/EndpointSchemaGeneratorMojo.java b/tooling/maven/camel-package-maven-plugin/src/main/java/org/apache/camel/maven/packaging/EndpointSchemaGeneratorMojo.java
index 04ff602963d..116a48fdb9d 100644
--- a/tooling/maven/camel-package-maven-plugin/src/main/java/org/apache/camel/maven/packaging/EndpointSchemaGeneratorMojo.java
+++ b/tooling/maven/camel-package-maven-plugin/src/main/java/org/apache/camel/maven/packaging/EndpointSchemaGeneratorMojo.java
@@ -325,7 +325,7 @@ public class EndpointSchemaGeneratorMojo extends AbstractGeneratorMojo {
         classes.add(headersClass);
         Class<?> currentHeadersClass;
         while ((currentHeadersClass = classes.poll()) != null) {
-            foundHeader |= addEndpointHeaders(componentModel, scheme, currentHeadersClass);
+            foundHeader |= addEndpointHeaders(componentModel, scheme, currentHeadersClass, uriEndpoint.headersNameProvider());
             final Class<?> superclass = currentHeadersClass.getSuperclass();
             if (superclass != null && !superclass.equals(Object.class)) {
                 classes.add(superclass);
@@ -344,12 +344,15 @@ public class EndpointSchemaGeneratorMojo extends AbstractGeneratorMojo {
      * <p/>
      * Only headers applicable for the given scheme are added.
      *
-     * @param  componentModel the component model to which the headers should be added.
-     * @param  scheme         the scheme for which we want to add the headers.
-     * @param  headersClass   the class from which we extract the headers.
-     * @return                {@code true} if at least one header has been added, {@code false} otherwise.
+     * @param  componentModel      the component model to which the headers should be added.
+     * @param  scheme              the scheme for which we want to add the headers.
+     * @param  headersClass        the class from which we extract the headers.
+     * @param  headersNameProvider the name of the field to get or the name of the method to invoke to get the name of
+     *                             the headers.
+     * @return                     {@code true} if at least one header has been added, {@code false} otherwise.
      */
-    private boolean addEndpointHeaders(ComponentModel componentModel, String scheme, Class<?> headersClass) {
+    private boolean addEndpointHeaders(
+            ComponentModel componentModel, String scheme, Class<?> headersClass, String headersNameProvider) {
         final boolean isEnum = headersClass.isEnum();
         boolean foundHeader = false;
         for (Field field : headersClass.getDeclaredFields()) {
@@ -358,7 +361,7 @@ public class EndpointSchemaGeneratorMojo extends AbstractGeneratorMojo {
                 getLog().debug(
                         String.format("Trying to add the constant %s in the class %s as header.", field.getName(),
                                 headersClass.getName()));
-                if (addEndpointHeader(componentModel, field, scheme)) {
+                if (addEndpointHeader(componentModel, scheme, field, headersNameProvider)) {
                     foundHeader = true;
                     continue;
                 }
@@ -378,12 +381,14 @@ public class EndpointSchemaGeneratorMojo extends AbstractGeneratorMojo {
      * <p/>
      * The header is only added if it is applicable for the given scheme.
      * 
-     * @param  componentModel the component to which the header should be added.
-     * @param  field          the field corresponding to the constant from which the metadata should be extracted.
-     * @param  scheme         the scheme for which we want to add the header.
-     * @return                {@code true} if the header has been added, {@code false} otherwise.
+     * @param  componentModel      the component to which the header should be added.
+     * @param  scheme              the scheme for which we want to add the header.
+     * @param  field               the field corresponding to the constant from which the metadata should be extracted.
+     * @param  headersNameProvider the name of the field to get or the name of the method to invoke to get the name of
+     *                             the headers.
+     * @return                     {@code true} if the header has been added, {@code false} otherwise.
      */
-    private boolean addEndpointHeader(ComponentModel componentModel, Field field, String scheme) {
+    private boolean addEndpointHeader(ComponentModel componentModel, String scheme, Field field, String headersNameProvider) {
         final Metadata metadata = field.getAnnotation(Metadata.class);
         if (metadata == null) {
             getLog().debug(String.format("The field %s in class %s has no Metadata", field.getName(),
@@ -419,18 +424,73 @@ public class EndpointSchemaGeneratorMojo extends AbstractGeneratorMojo {
             getLog().debug(String.format("The java type %s could not be found", header.getJavaType()), e);
         }
         try {
-            field.trySetAccessible();
-            // The name of the header is either the name of the field in case of an enum, otherwise it is the value
-            // of the field as we assume that it is a String constant
-            header.setName(field.getType().isEnum() ? field.getName() : (String) field.get(null));
+            header.setName(getHeaderName(field, headersNameProvider));
             componentModel.addEndpointHeader(header);
-        } catch (IllegalAccessException e) {
-            getLog().debug(String.format("The field %s in class %s cannot be accessed", field.getName(),
+        } catch (Exception e) {
+            getLog().debug(String.format("The name of the header corresponding to the field %s in class %s cannot be retrieved",
+                    field.getName(),
                     field.getDeclaringClass().getName()));
         }
         return true;
     }
 
+    /**
+     * The name of the header is:
+     * <ul>
+     * <li>In case of an interface or a class: The value of the field as we assume that it is a {@code String}
+     * constant</li>
+     * <li>In case of an enum:
+     * <ul>
+     * <li>If headers name provider is set to a name of field: The value of this particular field for the corresponding
+     * enum constant</li>
+     * <li>If headers name provider is set to a name of method: The returned value of this particular method for the
+     * corresponding enum constant</li>
+     * <li>By default: The name of the enum constant</li>
+     * </ul>
+     * </li>
+     * </ul>
+     *
+     * @param  field               the field corresponding to the name of a header.
+     * @param  headersNameProvider the name of the field to get or the name of the method to invoke to get the name of
+     *                             the headers.
+     * @return                     the name of the header corresponding to the given field.
+     * @throws Exception           if an error occurred while getting the name of the header
+     */
+    private String getHeaderName(Field field, String headersNameProvider) throws Exception {
+        if (field.getType().isEnum()) {
+            if (!headersNameProvider.isEmpty()) {
+                final Class<?> declaringClass = field.getDeclaringClass();
+                final Optional<?> value = Arrays.stream(declaringClass.getEnumConstants())
+                        .filter(c -> ((Enum<?>) c).name().equals(field.getName()))
+                        .findAny();
+                if (value.isPresent()) {
+                    getLog().debug(String.format("The headers name provider has been set to %s", headersNameProvider));
+                    final Optional<Field> headersNameProviderField = Arrays.stream(declaringClass.getFields())
+                            .filter(f -> f.getName().equals(headersNameProvider))
+                            .findAny();
+                    if (headersNameProviderField.isPresent()) {
+                        getLog().debug("A field corresponding to the headers name provider has been found");
+                        return (String) headersNameProviderField.get().get(value.get());
+                    }
+                    getLog().debug(
+                            String.format("No field %s could be found in the class %s", headersNameProvider, declaringClass));
+                    final Optional<Method> headersNameProviderMethod = Arrays.stream(declaringClass.getMethods())
+                            .filter(m -> m.getName().equals(headersNameProvider) && m.getParameterCount() == 0)
+                            .findAny();
+                    if (headersNameProviderMethod.isPresent()) {
+                        getLog().debug("A method without parameters corresponding to the headers name provider has been found");
+                        return (String) headersNameProviderMethod.get().invoke(value.get());
+                    }
+                    getLog().debug(String.format("No method %s without parameters could be found in the class %s",
+                            headersNameProvider, declaringClass));
+                }
+            }
+            return field.getName();
+        }
+        field.trySetAccessible();
+        return (String) field.get(null);
+    }
+
     /**
      * @param  headerField the field for which we want to extract the related Javadoc.
      * @return             the Javadoc of the header field if any. An empty string otherwise.
diff --git a/tooling/maven/camel-package-maven-plugin/src/test/java/org/apache/camel/maven/packaging/EndpointSchemaGeneratorMojoTest.java b/tooling/maven/camel-package-maven-plugin/src/test/java/org/apache/camel/maven/packaging/EndpointSchemaGeneratorMojoTest.java
index ac3d9147fee..5e4fc847169 100644
--- a/tooling/maven/camel-package-maven-plugin/src/test/java/org/apache/camel/maven/packaging/EndpointSchemaGeneratorMojoTest.java
+++ b/tooling/maven/camel-package-maven-plugin/src/test/java/org/apache/camel/maven/packaging/EndpointSchemaGeneratorMojoTest.java
@@ -21,6 +21,8 @@ import java.util.List;
 
 import org.apache.camel.maven.packaging.endpoint.SomeEndpoint;
 import org.apache.camel.maven.packaging.endpoint.SomeEndpointUsingEnumConstants;
+import org.apache.camel.maven.packaging.endpoint.SomeEndpointUsingEnumConstantsByField;
+import org.apache.camel.maven.packaging.endpoint.SomeEndpointUsingEnumConstantsByMethod;
 import org.apache.camel.maven.packaging.endpoint.SomeEndpointUsingInterfaceConstants;
 import org.apache.camel.maven.packaging.endpoint.SomeEndpointWithBadHeaders;
 import org.apache.camel.maven.packaging.endpoint.SomeEndpointWithFilter;
@@ -165,4 +167,16 @@ class EndpointSchemaGeneratorMojoTest {
         assertEquals("header", header.getKind());
         assertEquals("KEY_FROM_COMMON", header.getName());
     }
+
+    @ParameterizedTest
+    @ValueSource(classes = {
+            SomeEndpointUsingEnumConstantsByField.class, SomeEndpointUsingEnumConstantsByMethod.class })
+    void testEndpointWithNameProvider(Class<?> clazz) {
+        mojo.addEndpointHeaders(model, clazz.getAnnotation(UriEndpoint.class), "some");
+        List<EndpointHeaderModel> endpointHeaders = model.getEndpointHeaders();
+        assertEquals(1, endpointHeaders.size());
+        EndpointHeaderModel header = endpointHeaders.get(0);
+        assertEquals("header", header.getKind());
+        assertEquals("SomeName", header.getName());
+    }
 }
diff --git a/tooling/maven/camel-package-maven-plugin/src/test/java/org/apache/camel/maven/packaging/endpoint/SomeEndpointUsingEnumConstantsByField.java b/tooling/maven/camel-package-maven-plugin/src/test/java/org/apache/camel/maven/packaging/endpoint/SomeEndpointUsingEnumConstantsByField.java
new file mode 100644
index 00000000000..24eb7c4ae46
--- /dev/null
+++ b/tooling/maven/camel-package-maven-plugin/src/test/java/org/apache/camel/maven/packaging/endpoint/SomeEndpointUsingEnumConstantsByField.java
@@ -0,0 +1,24 @@
+/*
+ * 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.endpoint;
+
+import org.apache.camel.spi.UriEndpoint;
+
+@UriEndpoint(scheme = "some", syntax = "some", title = "some", headersClass = SomeEnumConstantsByField.class,
+             headersNameProvider = "name")
+public class SomeEndpointUsingEnumConstantsByField {
+}
diff --git a/tooling/maven/camel-package-maven-plugin/src/test/java/org/apache/camel/maven/packaging/endpoint/SomeEndpointUsingEnumConstantsByMethod.java b/tooling/maven/camel-package-maven-plugin/src/test/java/org/apache/camel/maven/packaging/endpoint/SomeEndpointUsingEnumConstantsByMethod.java
new file mode 100644
index 00000000000..0ff4723d3bd
--- /dev/null
+++ b/tooling/maven/camel-package-maven-plugin/src/test/java/org/apache/camel/maven/packaging/endpoint/SomeEndpointUsingEnumConstantsByMethod.java
@@ -0,0 +1,24 @@
+/*
+ * 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.endpoint;
+
+import org.apache.camel.spi.UriEndpoint;
+
+@UriEndpoint(scheme = "some", syntax = "some", title = "some", headersClass = SomeEnumConstantsByMethod.class,
+             headersNameProvider = "getName")
+public class SomeEndpointUsingEnumConstantsByMethod {
+}
diff --git a/tooling/maven/camel-package-maven-plugin/src/test/java/org/apache/camel/maven/packaging/endpoint/SomeEnumConstantsByField.java b/tooling/maven/camel-package-maven-plugin/src/test/java/org/apache/camel/maven/packaging/endpoint/SomeEnumConstantsByField.java
new file mode 100644
index 00000000000..e8cacb89663
--- /dev/null
+++ b/tooling/maven/camel-package-maven-plugin/src/test/java/org/apache/camel/maven/packaging/endpoint/SomeEnumConstantsByField.java
@@ -0,0 +1,30 @@
+/*
+ * 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.endpoint;
+
+import org.apache.camel.spi.Metadata;
+
+public enum SomeEnumConstantsByField {
+    @Metadata
+    SOME_VALUE("SomeName");
+
+    public final String name;
+
+    SomeEnumConstantsByField(final String str) {
+        this.name = str;
+    }
+}
diff --git a/tooling/maven/camel-package-maven-plugin/src/test/java/org/apache/camel/maven/packaging/endpoint/SomeEnumConstantsByMethod.java b/tooling/maven/camel-package-maven-plugin/src/test/java/org/apache/camel/maven/packaging/endpoint/SomeEnumConstantsByMethod.java
new file mode 100644
index 00000000000..f86c3974dc7
--- /dev/null
+++ b/tooling/maven/camel-package-maven-plugin/src/test/java/org/apache/camel/maven/packaging/endpoint/SomeEnumConstantsByMethod.java
@@ -0,0 +1,28 @@
+/*
+ * 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.endpoint;
+
+import org.apache.camel.spi.Metadata;
+
+public enum SomeEnumConstantsByMethod {
+    @Metadata
+    SOME_VALUE;
+
+    public String getName() {
+        return "SomeName";
+    }
+}
diff --git a/tooling/spi-annotations/src/main/java/org/apache/camel/spi/UriEndpoint.java b/tooling/spi-annotations/src/main/java/org/apache/camel/spi/UriEndpoint.java
index d92ea81e46b..e9c64483bd9 100644
--- a/tooling/spi-annotations/src/main/java/org/apache/camel/spi/UriEndpoint.java
+++ b/tooling/spi-annotations/src/main/java/org/apache/camel/spi/UriEndpoint.java
@@ -187,4 +187,32 @@ public @interface UriEndpoint {
      * {@code String} constant representing its name and defined in the headers class.
      */
     Class<?> headersClass() default void.class;
+
+    /**
+     * The name of the field to get or the name of the method to invoke to get the name of the headers defined in an
+     * enum.
+     * <p/>
+     * Only took into account if and only if the class defined as {@code headersClass} is an enum.
+     * <p/>
+     * For example, assuming that {@code SomeEnum} has been configured as the {@code headersClass} of a given component,
+     * since the name of the header is actually the value of the field {@code headerName}, the element
+     * {@code headersNameProvider} should be set to {@code "headerName"} to get the expected header names.
+     *
+     * <pre>
+     * <code>
+     *
+     * public enum SomeEnum {
+     *    {@literal @}Metadata
+     *     FOO("fooKey");
+     *
+     *     public final String headerName;
+     *
+     *     SomeEnum(final String str) {
+     *         this.headerName = str;
+     *     }
+     * }
+     * </code>
+     * </pre>
+     */
+    String headersNameProvider() default "";
 }