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/03/14 13:37:30 UTC

[camel] branch main updated: CAMEL-16628: Allow to generate doc about message headers (#7190)

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 5072d09  CAMEL-16628: Allow to generate doc about message headers (#7190)
5072d09 is described below

commit 5072d09284657bdeb3607ee497ad9f7e9d472104
Author: Nicolas Filotto <es...@users.noreply.github.com>
AuthorDate: Mon Mar 14 14:36:55 2022 +0100

    CAMEL-16628: Allow to generate doc about message headers (#7190)
    
    ## Motivation
    
    Like we have for endpoint options with @UriParam, we should have a new SPI annotation to markup headers that consumer/producers support.
    
    ## Modifications
    
    This PR adds a new element called `headersClass` to the annotation `@UriEndpoint` to provide the class that contains the constants corresponding to the name of the headers supported by the consumers/producers.
    
    In the example below, the endpoint `FooEndpoint` indicates that the class that contains the name of all the headers supported by the consumers/producers is `FooConstants`.
    
    ```
    @UriEndpoint(scheme = "foo", syntax = "foo", title = "foo", headersClass = FooConstants.class)
    public class FooEndpoint extends Endpoint {
        // Rest of the class
    }
    ```
    
    A headers class can be any class but by convention, we expect a name of type `xxxConstants` where `xxx` is the name of the corresponding component like for example `FtpConstants` for the component `camel-ftp`.
    
    The metadata of a given header are retrieved from the annotation `@Metadata` added to the `String` constant representing its name.
    
    In the example below, the header `FooConstants.FILE_NAME` specify a description, a default value and a java type.
    ```
    public final class FooConstants {
    
        @Metadata(description = "Some description of CamelFileName", defaultValue = "Some default value",
                            javaType = "java.lang.String")
        public static final String FILE_NAME = "CamelFileName";
        // Rest of the class
    }
    ```
    
    This PR adds a new asciidoctor template called `component-endpoint-headers.adoc` that can be called from a doc file with:
    ```
    // component headers: START
    include::partial$component-endpoint-headers.adoc[]
    // component headers: END
    ```
---
 .../java/org/apache/camel/spi/UriEndpoint.java     |  12 +++
 .../ROOT/partials/component-endpoint-headers.adoc  |  24 +++++
 .../apache/camel/tooling/model/ComponentModel.java |  13 +++
 .../org/apache/camel/tooling/model/JsonMapper.java |  14 +++
 .../apache/camel/tooling/model/JsonMapperTest.java |  77 ++++++++++++++
 tooling/maven/camel-package-maven-plugin/pom.xml   |  52 +++++++++
 .../src/it/HeaderSupport/pom.xml                   |  70 ++++++++++++
 .../apache/camel/component/foo/FooComponent.java   |  26 +++++
 .../apache/camel/component/foo/FooConstants.java   |  28 +++++
 .../apache/camel/component/foo/FooEndpoint.java    |  70 ++++++++++++
 .../src/it/HeaderSupport/verify.groovy             |  26 +++++
 .../camel-package-maven-plugin/src/it/settings.xml |  53 ++++++++++
 .../packaging/EndpointSchemaGeneratorMojo.java     | 117 ++++++++++++++++++---
 .../packaging/EndpointSchemaGeneratorMojoTest.java | 103 ++++++++++++++++++
 .../maven/packaging/endpoint/SomeConstants.java    |  32 ++++++
 .../maven/packaging/endpoint/SomeEndpoint.java     |  46 ++++++++
 .../endpoint/SomeEndpointWithBadHeaders.java       |  33 ++++++
 .../endpoint/SomeEndpointWithoutHeaders.java       |  23 ++++
 .../java/org/apache/camel/spi/UriEndpoint.java     |  12 +++
 19 files changed, 817 insertions(+), 14 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 a3e5a64..d92ea81 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
@@ -175,4 +175,16 @@ public @interface UriEndpoint {
      */
     String apiSyntax() default "";
 
+    /**
+     * The class that contains all the name of headers that are supported by the consumer and/or producer. The name of
+     * the headers are defined as {@code String} constants in the headers class.
+     * <p/>
+     * The class to provide can be any class but by convention, we would expect a class whose name is of type
+     * <i>xxxConstants</i> where <i>xxx</i> is the name of the corresponding component like for example
+     * <i>FtpConstants</i> for the component <i>camel-ftp</i>.
+     * <p/>
+     * The metadata of a given header are retrieved directly from the annotation {@code @Metadata} added to the
+     * {@code String} constant representing its name and defined in the headers class.
+     */
+    Class<?> headersClass() default void.class;
 }
diff --git a/docs/components/modules/ROOT/partials/component-endpoint-headers.adoc b/docs/components/modules/ROOT/partials/component-endpoint-headers.adoc
new file mode 100644
index 0000000..94b11e8
--- /dev/null
+++ b/docs/components/modules/ROOT/partials/component-endpoint-headers.adoc
@@ -0,0 +1,24 @@
+//component headers: START
+
+:tablespec: width="100%",cols="2,5a,^1,2",options="header"
+:cellformats: 'util.boldLink(path[2], "endpoint_header", value.group) \
+|util.description(value) \
+|util.valueAsString(value.defaultValue) \
+|util.javaSimpleName(value.javaType)'
+include::jsonpath$example$json/{shortname}.json[query='$.component',formats='name,scheme,pascalcasescheme=util.pascalCase(scheme),syntax,apiSyntax', requires={requires}]
+include::jsonpathcount$example$json/{shortname}.json[queries='headercount=nodes$.headers.*']
+
+ifeval::[{headercount} != 0]
+== Message Headers
+
+The {doctitle} component supports {headercount} message header(s), which is/are listed below:
+
+[{tablespec}]
+|===
+| Name | Description | Default | Type
+|===
+
+jsonpathTable::example$json/{shortname}.json['nodes$.headers.*',{cellformats},{requires}]
+
+endif::[]
+// component headers: END
\ No newline at end of file
diff --git a/tooling/camel-tooling-model/src/main/java/org/apache/camel/tooling/model/ComponentModel.java b/tooling/camel-tooling-model/src/main/java/org/apache/camel/tooling/model/ComponentModel.java
index f4e5978..dfe31c0 100644
--- a/tooling/camel-tooling-model/src/main/java/org/apache/camel/tooling/model/ComponentModel.java
+++ b/tooling/camel-tooling-model/src/main/java/org/apache/camel/tooling/model/ComponentModel.java
@@ -37,6 +37,7 @@ public class ComponentModel extends ArtifactModel<ComponentModel.ComponentOption
     protected boolean lenientProperties;
     protected String verifiers;
     protected final List<EndpointOptionModel> endpointOptions = new ArrayList<>();
+    protected final List<EndpointHeaderModel> headers = new ArrayList<>();
     // lets sort apis A..Z so they are always in the same order
     protected final Collection<ApiModel> apiOptions = new TreeSet<>(Comparators.apiModelComparator());
 
@@ -160,6 +161,14 @@ public class ComponentModel extends ArtifactModel<ComponentModel.ComponentOption
         endpointOptions.add(option);
     }
 
+    public List<EndpointHeaderModel> getEndpointHeaders() {
+        return headers;
+    }
+
+    public void addEndpointHeader(EndpointHeaderModel header) {
+        headers.add(header);
+    }
+
     public List<EndpointOptionModel> getEndpointParameterOptions() {
         return endpointOptions.stream()
                 .filter(o -> "parameter".equals(o.getKind()))
@@ -176,6 +185,10 @@ public class ComponentModel extends ArtifactModel<ComponentModel.ComponentOption
         return apiOptions;
     }
 
+    public static class EndpointHeaderModel extends BaseOptionModel {
+
+    }
+
     public static class ComponentOptionModel extends BaseOptionModel {
 
     }
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 fde2830..d828168 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
@@ -27,6 +27,7 @@ import java.util.TreeMap;
 import java.util.stream.Collectors;
 
 import org.apache.camel.tooling.model.ComponentModel.ComponentOptionModel;
+import org.apache.camel.tooling.model.ComponentModel.EndpointHeaderModel;
 import org.apache.camel.tooling.model.ComponentModel.EndpointOptionModel;
 import org.apache.camel.tooling.model.DataFormatModel.DataFormatOptionModel;
 import org.apache.camel.tooling.model.EipModel.EipOptionModel;
@@ -90,6 +91,15 @@ public final class JsonMapper {
                 model.addComponentOption(option);
             }
         }
+        JsonObject headers = (JsonObject) obj.get("headers");
+        if (headers != null) {
+            for (Map.Entry<String, Object> entry : headers.entrySet()) {
+                JsonObject mp = (JsonObject) entry.getValue();
+                EndpointHeaderModel header = new EndpointHeaderModel();
+                parseOption(mp, header, entry.getKey());
+                model.addEndpointHeader(header);
+            }
+        }
         JsonObject mprp = (JsonObject) obj.get("properties");
         if (mprp != null) {
             for (Map.Entry<String, Object> entry : mprp.entrySet()) {
@@ -213,6 +223,10 @@ public final class JsonMapper {
         JsonObject wrapper = new JsonObject();
         wrapper.put("component", obj);
         wrapper.put("componentProperties", asJsonObject(model.getComponentOptions()));
+        final List<EndpointHeaderModel> headers = model.getEndpointHeaders();
+        if (!headers.isEmpty()) {
+            wrapper.put("headers", asJsonObject(headers));
+        }
         wrapper.put("properties", asJsonObject(model.getEndpointOptions()));
         if (!model.getApiOptions().isEmpty()) {
             wrapper.put("apis", apiModelAsJsonObject(model.getApiOptions(), false));
diff --git a/tooling/camel-tooling-model/src/test/java/org/apache/camel/tooling/model/JsonMapperTest.java b/tooling/camel-tooling-model/src/test/java/org/apache/camel/tooling/model/JsonMapperTest.java
new file mode 100644
index 0000000..fc03afa
--- /dev/null
+++ b/tooling/camel-tooling-model/src/test/java/org/apache/camel/tooling/model/JsonMapperTest.java
@@ -0,0 +1,77 @@
+/*
+ * 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.tooling.model;
+
+import java.util.List;
+
+import org.junit.jupiter.api.Test;
+
+import static org.apache.camel.tooling.model.ComponentModel.EndpointHeaderModel;
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertFalse;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+
+/**
+ * The unit test class for {@link JsonMapper}.
+ */
+class JsonMapperTest {
+
+    @Test
+    void testShouldSerializeAndDeserializeComponentWithoutHeaders() {
+        ComponentModel model = new ComponentModel();
+        String json = JsonMapper.createParameterJsonSchema(model);
+        assertFalse(json.contains("\"headers\""));
+        ComponentModel model2 = JsonMapper.generateComponentModel(json);
+        assertTrue(model2.getEndpointHeaders().isEmpty());
+    }
+
+    @Test
+    void testShouldSerializeAndDeserializeComponentWithOneHeader() {
+        ComponentModel model = new ComponentModel();
+        EndpointHeaderModel header = new EndpointHeaderModel();
+        header.setName("Some Name");
+        header.setDescription("Some Description");
+        model.addEndpointHeader(header);
+        String json = JsonMapper.createParameterJsonSchema(model);
+        ComponentModel model2 = JsonMapper.generateComponentModel(json);
+        List<EndpointHeaderModel> headers = model2.getEndpointHeaders();
+        assertEquals(1, headers.size());
+        assertEquals(header.getName(), headers.get(0).getName());
+        assertEquals(header.getDescription(), headers.get(0).getDescription());
+    }
+
+    @Test
+    void testShouldSerializeAndDeserializeComponentWithSeveralHeaders() {
+        ComponentModel model = new ComponentModel();
+        EndpointHeaderModel header1 = new EndpointHeaderModel();
+        header1.setName("Some Name");
+        header1.setDescription("Some Description");
+        model.addEndpointHeader(header1);
+        EndpointHeaderModel header2 = new EndpointHeaderModel();
+        header2.setName("Some Name 2");
+        header2.setDescription("Some Description 2");
+        model.addEndpointHeader(header2);
+        String json = JsonMapper.createParameterJsonSchema(model);
+        ComponentModel model2 = JsonMapper.generateComponentModel(json);
+        List<EndpointHeaderModel> headers = model2.getEndpointHeaders();
+        assertEquals(2, headers.size());
+        assertEquals(header1.getName(), headers.get(0).getName());
+        assertEquals(header1.getDescription(), headers.get(0).getDescription());
+        assertEquals(header2.getName(), headers.get(1).getName());
+        assertEquals(header2.getDescription(), headers.get(1).getDescription());
+    }
+}
diff --git a/tooling/maven/camel-package-maven-plugin/pom.xml b/tooling/maven/camel-package-maven-plugin/pom.xml
index 29fcf2b..39c6839 100644
--- a/tooling/maven/camel-package-maven-plugin/pom.xml
+++ b/tooling/maven/camel-package-maven-plugin/pom.xml
@@ -188,6 +188,46 @@
                     </mojoDependencies>
                 </configuration>
             </plugin>
+            <plugin>
+                <groupId>org.apache.maven.plugins</groupId>
+                <artifactId>maven-invoker-plugin</artifactId>
+                <configuration>
+                    <cloneProjectsTo>${project.build.directory}/it</cloneProjectsTo>
+                    <pomIncludes>
+                        <pomInclude>*/pom.xml</pomInclude>
+                    </pomIncludes>
+                    <postBuildHookScript>verify</postBuildHookScript>
+                    <localRepositoryPath>${project.build.directory}/local-repo</localRepositoryPath>
+                    <settingsFile>src/it/settings.xml</settingsFile>
+                    <goals>
+                        <goal>clean</goal>
+                        <goal>verify</goal>
+                    </goals>
+                </configuration>
+                <executions>
+                    <execution>
+                        <id>integration-test</id>
+                        <phase>integration-test</phase>
+                        <goals>
+                            <goal>install</goal>
+                            <goal>integration-test</goal>
+                            <goal>verify</goal>
+                        </goals>
+                    </execution>
+                </executions>
+                <dependencies>
+                    <dependency>
+                        <groupId>org.codehaus.groovy</groupId>
+                        <artifactId>groovy</artifactId>
+                        <version>${groovy-version}</version>
+                    </dependency>
+                    <dependency>
+                        <groupId>org.apache.camel</groupId>
+                        <artifactId>camel-tooling-model</artifactId>
+                        <version>${project.version}</version>
+                    </dependency>
+                </dependencies>
+            </plugin>
         </plugins>
     </build>
 
@@ -205,5 +245,17 @@
                 </dependency>
             </dependencies>
         </profile>
+        <profile>
+            <id>fastinstall</id>
+            <activation>
+                <activeByDefault>true</activeByDefault>
+                <property>
+                    <name>skipTests</name>
+                </property>
+            </activation>
+            <properties>
+                <invoker.skip>true</invoker.skip>
+            </properties>
+        </profile>
     </profiles>
 </project>
diff --git a/tooling/maven/camel-package-maven-plugin/src/it/HeaderSupport/pom.xml b/tooling/maven/camel-package-maven-plugin/src/it/HeaderSupport/pom.xml
new file mode 100644
index 0000000..2c2e648
--- /dev/null
+++ b/tooling/maven/camel-package-maven-plugin/src/it/HeaderSupport/pom.xml
@@ -0,0 +1,70 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+
+    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.
+
+-->
+<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/maven-v4_0_0.xsd">
+    <modelVersion>4.0.0</modelVersion>
+    <groupId>org.apache.camel.integration.test</groupId>
+    <artifactId>header-support</artifactId>
+    <version>1.0-SNAPSHOT</version>
+    <packaging>jar</packaging>
+    <description>An IT ensuring that headers are supported.</description>
+    <properties>
+        <maven.compiler.source>11</maven.compiler.source>
+        <maven.compiler.target>11</maven.compiler.target>
+    </properties>
+    <dependencyManagement>
+        <dependencies>
+            <!-- Add Camel BOM -->
+            <dependency>
+                <groupId>org.apache.camel</groupId>
+                <artifactId>camel-bom</artifactId>
+                <version>@project.version@</version>
+                <type>pom</type>
+                <scope>import</scope>
+            </dependency>
+        </dependencies>
+    </dependencyManagement>
+
+    <dependencies>
+        <dependency>
+            <groupId>org.apache.camel</groupId>
+            <artifactId>camel-core</artifactId>
+        </dependency>
+    </dependencies>
+
+    <build>
+        <plugins>
+            <plugin>
+                <groupId>@project.groupId@</groupId>
+                <artifactId>@project.artifactId@</artifactId>
+                <version>@project.version@</version>
+                <executions>
+                    <execution>
+                        <id>generate</id>
+                        <goals>
+                            <goal>generate</goal>
+                        </goals>
+                        <phase>process-classes</phase>
+                    </execution>
+                </executions>
+            </plugin>
+        </plugins>
+    </build>
+
+</project>
diff --git a/tooling/maven/camel-package-maven-plugin/src/it/HeaderSupport/src/main/java/org/apache/camel/component/foo/FooComponent.java b/tooling/maven/camel-package-maven-plugin/src/it/HeaderSupport/src/main/java/org/apache/camel/component/foo/FooComponent.java
new file mode 100644
index 0000000..6a6fed3
--- /dev/null
+++ b/tooling/maven/camel-package-maven-plugin/src/it/HeaderSupport/src/main/java/org/apache/camel/component/foo/FooComponent.java
@@ -0,0 +1,26 @@
+/*
+ * 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.component.foo;
+
+import org.apache.camel.spi.annotations.Component;
+
+/**
+ * Foo Component
+ */
+@Component("foo")
+public class FooComponent {
+}
diff --git a/tooling/maven/camel-package-maven-plugin/src/it/HeaderSupport/src/main/java/org/apache/camel/component/foo/FooConstants.java b/tooling/maven/camel-package-maven-plugin/src/it/HeaderSupport/src/main/java/org/apache/camel/component/foo/FooConstants.java
new file mode 100644
index 0000000..3c7886a
--- /dev/null
+++ b/tooling/maven/camel-package-maven-plugin/src/it/HeaderSupport/src/main/java/org/apache/camel/component/foo/FooConstants.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.component.foo;
+
+import org.apache.camel.spi.Metadata;
+
+public final class FooConstants {
+
+    @Metadata(description = "My description of the SomeHeader")
+    public static final String SOME_HEADER = "SomeHeaderName";
+
+    private FooConstants() {
+    }
+}
diff --git a/tooling/maven/camel-package-maven-plugin/src/it/HeaderSupport/src/main/java/org/apache/camel/component/foo/FooEndpoint.java b/tooling/maven/camel-package-maven-plugin/src/it/HeaderSupport/src/main/java/org/apache/camel/component/foo/FooEndpoint.java
new file mode 100644
index 0000000..11c178a
--- /dev/null
+++ b/tooling/maven/camel-package-maven-plugin/src/it/HeaderSupport/src/main/java/org/apache/camel/component/foo/FooEndpoint.java
@@ -0,0 +1,70 @@
+/*
+ * 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.component.foo;
+
+import org.apache.camel.Category;
+import org.apache.camel.spi.Metadata;
+import org.apache.camel.spi.UriEndpoint;
+import org.apache.camel.spi.UriParam;
+import org.apache.camel.spi.UriPath;
+
+@UriEndpoint(firstVersion = "1.1.0", scheme = "foo", extendsScheme = "foo", title = "FOO",
+    syntax = "foo:host:port", alternativeSyntax = "foo:host@port",
+    category = { Category.FILE }, headersClass = FooConstants.class)
+public class FooEndpoint {
+
+    @UriPath(description = "Hostname of the Foo server")
+    @Metadata(required = true)
+    private String host;
+    @UriPath(description = "Port of the Foo server")
+    private int port;
+    @UriParam(label = "common", defaultValue = "5")
+    private int intervalSeconds = 5;
+
+    public int getIntervalSeconds() {
+        return intervalSeconds;
+    }
+
+    /**
+     * My interval in seconds.
+     */
+    public void setIntervalSeconds(int intervalSeconds) {
+        this.intervalSeconds = intervalSeconds;
+    }
+
+    public String getHost() {
+        return host;
+    }
+
+    /**
+     * Hostname of the Foo server
+     */
+    public void setHost(String host) {
+        this.host = host;
+    }
+
+    public int getPort() {
+        return port;
+    }
+
+    /**
+     * Port of the Foo server
+     */
+    public void setPort(int port) {
+        this.port = port;
+    }
+}
diff --git a/tooling/maven/camel-package-maven-plugin/src/it/HeaderSupport/verify.groovy b/tooling/maven/camel-package-maven-plugin/src/it/HeaderSupport/verify.groovy
new file mode 100644
index 0000000..a1ce29b
--- /dev/null
+++ b/tooling/maven/camel-package-maven-plugin/src/it/HeaderSupport/verify.groovy
@@ -0,0 +1,26 @@
+/*
+ * 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.
+ */
+
+import org.apache.camel.tooling.model.ComponentModel
+import org.apache.camel.tooling.model.JsonMapper
+
+File json = new File(basedir, "src/generated/resources/org/apache/camel/component/foo/foo.json")
+assert json.exists()
+
+ComponentModel component = JsonMapper.generateComponentModel(json.text)
+assert component.getEndpointHeaders().size() == 1
+assert "My description of the SomeHeader".equals(component.getEndpointHeaders().get(0).getDescription())
diff --git a/tooling/maven/camel-package-maven-plugin/src/it/settings.xml b/tooling/maven/camel-package-maven-plugin/src/it/settings.xml
new file mode 100644
index 0000000..c72c23c
--- /dev/null
+++ b/tooling/maven/camel-package-maven-plugin/src/it/settings.xml
@@ -0,0 +1,53 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+
+    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.
+
+-->
+<settings>
+  <profiles>
+    <profile>
+      <id>it-repo</id>
+      <activation>
+        <activeByDefault>true</activeByDefault>
+      </activation>
+      <repositories>
+        <repository>
+          <id>local.central</id>
+          <url>@localRepositoryUrl@</url>
+          <releases>
+            <enabled>true</enabled>
+          </releases>
+          <snapshots>
+            <enabled>true</enabled>
+          </snapshots>
+        </repository>
+      </repositories>
+      <pluginRepositories>
+        <pluginRepository>
+          <id>local.central</id>
+          <url>@localRepositoryUrl@</url>
+          <releases>
+            <enabled>true</enabled>
+          </releases>
+          <snapshots>
+            <enabled>true</enabled>
+          </snapshots>
+        </pluginRepository>
+      </pluginRepositories>
+    </profile>
+  </profiles>
+</settings>
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 6ff45ed..1bc68d8 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
@@ -64,6 +64,7 @@ import org.apache.camel.tooling.model.ApiModel;
 import org.apache.camel.tooling.model.BaseOptionModel;
 import org.apache.camel.tooling.model.ComponentModel;
 import org.apache.camel.tooling.model.ComponentModel.ComponentOptionModel;
+import org.apache.camel.tooling.model.ComponentModel.EndpointHeaderModel;
 import org.apache.camel.tooling.model.ComponentModel.EndpointOptionModel;
 import org.apache.camel.tooling.model.JsonMapper;
 import org.apache.camel.tooling.model.SupportLevel;
@@ -92,7 +93,8 @@ import org.jboss.jandex.DotName;
 import org.jboss.jandex.IndexReader;
 import org.jboss.jandex.IndexView;
 
-import static org.apache.camel.tooling.model.ComponentModel.*;
+import static java.lang.reflect.Modifier.isStatic;
+import static org.apache.camel.tooling.model.ComponentModel.ApiOptionModel;
 
 @Mojo(name = "generate-endpoint-schema", threadSafe = true, requiresDependencyResolution = ResolutionScope.COMPILE_PLUS_RUNTIME,
       defaultPhase = LifecyclePhase.PROCESS_CLASSES)
@@ -258,6 +260,9 @@ public class EndpointSchemaGeneratorMojo extends AbstractGeneratorMojo {
             }
         }
 
+        // component headers
+        addEndpointHeaders(componentModel, uriEndpoint);
+
         // endpoint options
         findClassProperties(componentModel, classElement, new HashSet<>(), "", null, null, false);
 
@@ -291,6 +296,85 @@ public class EndpointSchemaGeneratorMojo extends AbstractGeneratorMojo {
         return componentModel;
     }
 
+    /**
+     * Retrieve the metadata added to all the {@code String} constants defined in class corresponding to the element
+     * {@code headersClass} of the annotation {@code UriEndpoint}, convert the metadata found into instances of
+     * {@link EndpointHeaderModel} and finally add the instances of {@link EndpointHeaderModel} to the given component
+     * model.
+     * 
+     * @param componentModel the component model to which the headers should be added.
+     * @param uriEndpoint    the annotation from which the headers class is retrieved.
+     */
+    void addEndpointHeaders(ComponentModel componentModel, UriEndpoint uriEndpoint) {
+        final Class<?> headersClass = uriEndpoint.headersClass();
+        if (headersClass == void.class) {
+            getLog().debug(String.format("The endpoint %s has not defined any headers class", uriEndpoint.scheme()));
+            return;
+        }
+        // A header class has been defined
+        boolean foundHeader = false;
+        for (Field field : headersClass.getDeclaredFields()) {
+            if (isStatic(field.getModifiers()) && field.getType() == String.class
+                    && field.isAnnotationPresent(Metadata.class)) {
+                getLog().debug(
+                        String.format("Trying to add the constant %s in the class %s as header.", field.getName(),
+                                headersClass.getName()));
+                addEndpointHeader(componentModel, field);
+                foundHeader = true;
+                continue;
+            }
+            getLog().debug(
+                    String.format("The field %s of the class %s is not considered as a name of a header, thus it is skipped",
+                            field.getName(), headersClass.getName()));
+        }
+        if (!foundHeader) {
+            getLog().debug(String.format("No headers have been detected in the headers class %s", headersClass.getName()));
+        }
+    }
+
+    /**
+     * Retrieve the metadata added to the given field, convert the metadata found into an instance of
+     * {@link EndpointHeaderModel} and finally add the instance of {@link EndpointHeaderModel} to the given component
+     * model.
+     * 
+     * @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.
+     */
+    private void addEndpointHeader(ComponentModel componentModel, Field field) {
+        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(),
+                    field.getDeclaringClass().getName()));
+            return;
+        }
+        final EndpointHeaderModel header = new EndpointHeaderModel();
+        header.setDescription(metadata.description().trim());
+        header.setKind("header");
+        header.setDisplayName(metadata.displayName());
+        header.setJavaType(metadata.javaType());
+        header.setRequired(metadata.required());
+        header.setDefaultValue(metadata.defaultValue());
+        header.setDeprecated(field.isAnnotationPresent(Deprecated.class));
+        header.setDeprecationNote(metadata.deprecationNote());
+        header.setSecret(metadata.secret());
+        header.setGroup(EndpointHelper.labelAsGroupName(metadata.label(), componentModel.isConsumerOnly(),
+                componentModel.isProducerOnly()));
+        header.setLabel(metadata.label());
+        try {
+            header.setEnums(getEnums(metadata, header.getJavaType().isEmpty() ? null : loadClass(header.getJavaType())));
+        } catch (NoClassDefFoundError e) {
+            getLog().debug(String.format("The java type %s could not be found", header.getJavaType()), e);
+        }
+        try {
+            field.trySetAccessible();
+            header.setName((String) field.get(null));
+            componentModel.addEndpointHeader(header);
+        } catch (IllegalAccessException e) {
+            getLog().debug(String.format("The field %s in class %s cannot be accessed", field.getName(),
+                    field.getDeclaringClass().getName()));
+        }
+    }
+
     private String getExcludedEnd(Metadata classElement) {
         String excludedEndpointProperties = "";
         if (classElement != null) {
@@ -730,19 +814,7 @@ public class EndpointSchemaGeneratorMojo extends AbstractGeneratorMojo {
                 }
 
                 // gather enums
-                List<String> enums = null;
-                if (metadata != null && !Strings.isNullOrEmpty(metadata.enums())) {
-                    String[] values = metadata.enums().split(",");
-                    enums = Stream.of(values).map(String::trim).collect(Collectors.toList());
-                } else if (fieldType.isEnum()) {
-                    enums = new ArrayList<>();
-                    for (Object val : fieldType.getEnumConstants()) {
-                        String str = val.toString();
-                        if (!enums.contains(str)) {
-                            enums.add(str);
-                        }
-                    }
-                }
+                List<String> enums = getEnums(metadata, fieldType);
 
                 // the field type may be overloaded by another type
                 boolean isDuration = false;
@@ -839,6 +911,23 @@ public class EndpointSchemaGeneratorMojo extends AbstractGeneratorMojo {
         }
     }
 
+    private List<String> getEnums(Metadata metadata, Class<?> fieldType) {
+        List<String> enums = null;
+        if (metadata != null && !Strings.isNullOrEmpty(metadata.enums())) {
+            String[] values = metadata.enums().split(",");
+            enums = Stream.of(values).map(String::trim).collect(Collectors.toList());
+        } else if (fieldType != null && fieldType.isEnum()) {
+            enums = new ArrayList<>();
+            for (Object val : fieldType.getEnumConstants()) {
+                String str = val.toString();
+                if (!enums.contains(str)) {
+                    enums.add(str);
+                }
+            }
+        }
+        return enums;
+    }
+
     private Field getFieldElement(Class<?> classElement, String fieldName) {
         Field fieldElement;
         try {
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
new file mode 100644
index 0000000..8e65a7d
--- /dev/null
+++ b/tooling/maven/camel-package-maven-plugin/src/test/java/org/apache/camel/maven/packaging/EndpointSchemaGeneratorMojoTest.java
@@ -0,0 +1,103 @@
+/*
+ * 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.nio.file.Paths;
+import java.util.List;
+
+import org.apache.camel.maven.packaging.endpoint.SomeEndpoint;
+import org.apache.camel.maven.packaging.endpoint.SomeEndpointWithBadHeaders;
+import org.apache.camel.maven.packaging.endpoint.SomeEndpointWithoutHeaders;
+import org.apache.camel.spi.UriEndpoint;
+import org.apache.camel.tooling.model.ComponentModel;
+import org.apache.camel.tooling.model.ComponentModel.EndpointHeaderModel;
+import org.apache.maven.project.MavenProject;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertFalse;
+import static org.junit.jupiter.api.Assertions.assertInstanceOf;
+import static org.junit.jupiter.api.Assertions.assertNull;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+
+/**
+ * The unit test of {@link EndpointSchemaGeneratorMojo}.
+ */
+class EndpointSchemaGeneratorMojoTest {
+
+    private final EndpointSchemaGeneratorMojo mojo = new EndpointSchemaGeneratorMojo();
+    private final ComponentModel model = new ComponentModel();
+
+    @BeforeEach
+    void init() throws Exception {
+        mojo.sourceRoots = List.of(
+                Paths.get(EndpointSchemaGeneratorMojoTest.class.getResource("/").toURI())
+                        .resolve("../../src/test/java/"));
+        mojo.project = new MavenProject();
+    }
+
+    @Test
+    void testCanRetrieveMetadataOfHeaders() {
+        mojo.addEndpointHeaders(model, SomeEndpoint.class.getAnnotation(UriEndpoint.class));
+        List<EndpointHeaderModel> endpointHeaders = model.getEndpointHeaders();
+        assertEquals(2, endpointHeaders.size());
+        // Full
+        EndpointHeaderModel headerFull = endpointHeaders.get(0);
+        assertEquals("header", headerFull.getKind());
+        assertEquals("name-full", headerFull.getName());
+        assertEquals("key full desc", headerFull.getDescription());
+        assertEquals("my display name", headerFull.getDisplayName());
+        assertEquals("org.apache.camel.maven.packaging.endpoint.SomeEndpoint$MyEnum", headerFull.getJavaType());
+        assertTrue(headerFull.isRequired());
+        assertEquals("VAL1", headerFull.getDefaultValue());
+        assertTrue(headerFull.isDeprecated());
+        assertEquals("my deprecated note", headerFull.getDeprecationNote());
+        assertTrue(headerFull.isSecret());
+        assertEquals("my label", headerFull.getLabel());
+        assertEquals(3, headerFull.getEnums().size());
+        assertEquals("my label", headerFull.getGroup());
+        // Empty
+        EndpointHeaderModel headerEmpty = endpointHeaders.get(1);
+        assertEquals("header", headerEmpty.getKind());
+        assertEquals("name-empty", headerEmpty.getName());
+        assertTrue(headerEmpty.getDescription().isEmpty());
+        assertTrue(headerEmpty.getDisplayName().isEmpty());
+        assertTrue(headerEmpty.getJavaType().isEmpty());
+        assertFalse(headerEmpty.isRequired());
+        assertInstanceOf(String.class, headerEmpty.getDefaultValue());
+        assertTrue(((String) headerEmpty.getDefaultValue()).isEmpty());
+        assertFalse(headerEmpty.isDeprecated());
+        assertTrue(headerEmpty.getDeprecationNote().isEmpty());
+        assertFalse(headerEmpty.isSecret());
+        assertTrue(headerEmpty.getLabel().isEmpty());
+        assertNull(headerEmpty.getEnums());
+        assertEquals("common", headerEmpty.getGroup());
+    }
+
+    @Test
+    void testHeadersNotProperlyDefinedAreIgnored() {
+        mojo.addEndpointHeaders(model, SomeEndpointWithBadHeaders.class.getAnnotation(UriEndpoint.class));
+        assertEquals(0, model.getEndpointHeaders().size());
+    }
+
+    @Test
+    void testEndpointWithoutHeadersAreIgnored() {
+        mojo.addEndpointHeaders(model, SomeEndpointWithoutHeaders.class.getAnnotation(UriEndpoint.class));
+        assertEquals(0, model.getEndpointHeaders().size());
+    }
+}
diff --git a/tooling/maven/camel-package-maven-plugin/src/test/java/org/apache/camel/maven/packaging/endpoint/SomeConstants.java b/tooling/maven/camel-package-maven-plugin/src/test/java/org/apache/camel/maven/packaging/endpoint/SomeConstants.java
new file mode 100644
index 0000000..ae4bbb3
--- /dev/null
+++ b/tooling/maven/camel-package-maven-plugin/src/test/java/org/apache/camel/maven/packaging/endpoint/SomeConstants.java
@@ -0,0 +1,32 @@
+/*
+ * 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 final class SomeConstants {
+    @Deprecated
+    @Metadata(description = "key full desc", label = "my label", displayName = "my display name",
+              javaType = "org.apache.camel.maven.packaging.endpoint.SomeEndpoint$MyEnum", required = true,
+              defaultValue = "VAL1", deprecationNote = "my deprecated note", secret = true)
+    public static final String KEY_FULL = "name-full";
+    @Metadata
+    static final String KEY_EMPTY = "name-empty";
+
+    private SomeConstants() {
+    }
+}
diff --git a/tooling/maven/camel-package-maven-plugin/src/test/java/org/apache/camel/maven/packaging/endpoint/SomeEndpoint.java b/tooling/maven/camel-package-maven-plugin/src/test/java/org/apache/camel/maven/packaging/endpoint/SomeEndpoint.java
new file mode 100644
index 0000000..d421d44
--- /dev/null
+++ b/tooling/maven/camel-package-maven-plugin/src/test/java/org/apache/camel/maven/packaging/endpoint/SomeEndpoint.java
@@ -0,0 +1,46 @@
+/*
+ * 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;
+import org.apache.camel.spi.UriEndpoint;
+import org.apache.camel.spi.UriPath;
+
+@UriEndpoint(scheme = "some", syntax = "some", title = "some", headersClass = SomeConstants.class)
+public class SomeEndpoint {
+
+    @UriPath(description = "Hostname of the Foo server")
+    @Metadata(required = true)
+    private String host;
+
+    public String getHost() {
+        return host;
+    }
+
+    /**
+     * Hostname of the Foo server
+     */
+    public void setHost(String host) {
+        this.host = host;
+    }
+
+    public enum MyEnum {
+        VAL1,
+        VAL2,
+        VAL3
+    }
+}
diff --git a/tooling/maven/camel-package-maven-plugin/src/test/java/org/apache/camel/maven/packaging/endpoint/SomeEndpointWithBadHeaders.java b/tooling/maven/camel-package-maven-plugin/src/test/java/org/apache/camel/maven/packaging/endpoint/SomeEndpointWithBadHeaders.java
new file mode 100644
index 0000000..88b115d
--- /dev/null
+++ b/tooling/maven/camel-package-maven-plugin/src/test/java/org/apache/camel/maven/packaging/endpoint/SomeEndpointWithBadHeaders.java
@@ -0,0 +1,33 @@
+/*
+ * 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;
+import org.apache.camel.spi.UriEndpoint;
+
+@UriEndpoint(scheme = "some", syntax = "some", title = "some", headersClass = SomeEndpointWithBadHeaders.class)
+public final class SomeEndpointWithBadHeaders {
+    public static final String NO_METADATA = "no-metadata";
+    @Metadata(description = "some description")
+    public static final int NOT_A_STRING = 1;
+    @Metadata(description = "some description")
+    public final String notStatic = "not-static";
+
+    private SomeEndpointWithBadHeaders() {
+
+    }
+}
diff --git a/tooling/maven/camel-package-maven-plugin/src/test/java/org/apache/camel/maven/packaging/endpoint/SomeEndpointWithoutHeaders.java b/tooling/maven/camel-package-maven-plugin/src/test/java/org/apache/camel/maven/packaging/endpoint/SomeEndpointWithoutHeaders.java
new file mode 100644
index 0000000..13f04ca
--- /dev/null
+++ b/tooling/maven/camel-package-maven-plugin/src/test/java/org/apache/camel/maven/packaging/endpoint/SomeEndpointWithoutHeaders.java
@@ -0,0 +1,23 @@
+/*
+ * 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 = SomeEndpointWithoutHeaders.class)
+public class SomeEndpointWithoutHeaders {
+}
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 a3e5a64..d92ea81 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
@@ -175,4 +175,16 @@ public @interface UriEndpoint {
      */
     String apiSyntax() default "";
 
+    /**
+     * The class that contains all the name of headers that are supported by the consumer and/or producer. The name of
+     * the headers are defined as {@code String} constants in the headers class.
+     * <p/>
+     * The class to provide can be any class but by convention, we would expect a class whose name is of type
+     * <i>xxxConstants</i> where <i>xxx</i> is the name of the corresponding component like for example
+     * <i>FtpConstants</i> for the component <i>camel-ftp</i>.
+     * <p/>
+     * The metadata of a given header are retrieved directly from the annotation {@code @Metadata} added to the
+     * {@code String} constant representing its name and defined in the headers class.
+     */
+    Class<?> headersClass() default void.class;
 }