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/11 15:22:17 UTC
[camel] 01/01: CAMEL-16628: Allow to generate doc about message headers
This is an automated email from the ASF dual-hosted git repository.
nfilotto pushed a commit to branch CAMEL-16628/headers-annotation
in repository https://gitbox.apache.org/repos/asf/camel.git
commit edcecc37267e3f21dce61ce99f86121494700877
Author: Nicolas Filotto <nf...@talend.com>
AuthorDate: Fri Mar 11 11:35:12 2022 +0100
CAMEL-16628: Allow to generate doc about message headers
---
.../java/org/apache/camel/spi/Metadata.java | 9 +
.../ROOT/partials/component-endpoint-headers.adoc | 24 +++
.../apache/camel/tooling/model/ComponentModel.java | 13 ++
.../org/apache/camel/tooling/model/JsonMapper.java | 20 ++
.../apache/camel/tooling/model/JsonMapperTest.java | 77 ++++++++
tooling/maven/camel-package-maven-plugin/pom.xml | 52 ++++++
.../src/it/HeaderSupport/pom.xml | 70 +++++++
.../camel/component/foo/ConstantsSamePackage.java | 28 +++
.../apache/camel/component/foo/FooComponent.java | 26 +++
.../apache/camel/component/foo/FooEndpoint.java | 71 ++++++++
.../src/it/HeaderSupport/verify.groovy | 25 +++
.../camel-package-maven-plugin/src/it/settings.xml | 53 ++++++
.../packaging/EndpointSchemaGeneratorMojo.java | 201 +++++++++++++++++++--
.../packaging/EndpointSchemaGeneratorMojoTest.java | 102 +++++++++++
.../packaging/endpoint/SamePackageConstants.java | 46 +++++
.../maven/packaging/endpoint/SomeEndpoint.java | 96 ++++++++++
.../endpoint/SomeEndpointWithBadHeaders.java | 29 +++
.../endpoint/SomeEndpointWithoutHeaders.java | 23 +++
.../endpoint/other/OtherPackageConstants.java | 49 +++++
.../main/java/org/apache/camel/spi/Metadata.java | 9 +
20 files changed, 1009 insertions(+), 14 deletions(-)
diff --git a/core/camel-api/src/generated/java/org/apache/camel/spi/Metadata.java b/core/camel-api/src/generated/java/org/apache/camel/spi/Metadata.java
index f37ea43..a099566 100644
--- a/core/camel-api/src/generated/java/org/apache/camel/spi/Metadata.java
+++ b/core/camel-api/src/generated/java/org/apache/camel/spi/Metadata.java
@@ -132,4 +132,13 @@ public @interface Metadata {
* specify which options each implementation only supports.
*/
String includeProperties() default "";
+
+ /**
+ * All the headers that are supported by the consumer and/or producer.
+ * <p/>
+ * The expected values are actually the constant expressions referring the name of the header like for example
+ * {@code KafkaConstants.PARTITION_KEY}. The metadata of each header are retrieved directly from the annotation
+ * {@code @Metadata} added to the corresponding constant.
+ */
+ String[] headers() default {};
}
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..86fe787 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));
@@ -527,6 +541,12 @@ public final class JsonMapper {
return prop;
}
+ public static JsonObject asJsonObject(EndpointHeaderModel header) {
+ JsonObject prop = new JsonObject();
+ prop.put("description", header.getDescription());
+ return prop;
+ }
+
public static MainModel generateMainModel(String json) {
JsonObject obj = deserialize(json);
return generateMainModel(obj);
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/ConstantsSamePackage.java b/tooling/maven/camel-package-maven-plugin/src/it/HeaderSupport/src/main/java/org/apache/camel/component/foo/ConstantsSamePackage.java
new file mode 100644
index 0000000..34977df
--- /dev/null
+++ b/tooling/maven/camel-package-maven-plugin/src/it/HeaderSupport/src/main/java/org/apache/camel/component/foo/ConstantsSamePackage.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 ConstantsSamePackage {
+
+ @Metadata(description = "My Constant in same package")
+ public static final String SAME_PACKAGE_KEY = "SomeKeyName";
+
+ private ConstantsSamePackage() {
+ }
+}
\ No newline at end of file
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..0e2d4e7
--- /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 {
+}
\ No newline at end of file
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..b994ecd
--- /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,71 @@
+/*
+ * 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 })
+@Metadata(headers = ConstantsSamePackage.SAME_PACKAGE_KEY)
+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;
+ }
+}
\ No newline at end of file
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..ab2dc14
--- /dev/null
+++ b/tooling/maven/camel-package-maven-plugin/src/it/HeaderSupport/verify.groovy
@@ -0,0 +1,25 @@
+/*
+ * 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
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..abfc771 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;
@@ -83,7 +84,10 @@ import org.jboss.forge.roaster._shade.org.eclipse.jdt.core.dom.ASTNode;
import org.jboss.forge.roaster._shade.org.eclipse.jdt.core.dom.Javadoc;
import org.jboss.forge.roaster.model.JavaDoc;
import org.jboss.forge.roaster.model.JavaDocCapable;
+import org.jboss.forge.roaster.model.StaticCapable;
+import org.jboss.forge.roaster.model.source.AnnotationSource;
import org.jboss.forge.roaster.model.source.FieldSource;
+import org.jboss.forge.roaster.model.source.Import;
import org.jboss.forge.roaster.model.source.JavaClassSource;
import org.jboss.forge.roaster.model.source.MethodSource;
import org.jboss.jandex.AnnotationInstance;
@@ -92,7 +96,7 @@ 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 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 +262,9 @@ public class EndpointSchemaGeneratorMojo extends AbstractGeneratorMojo {
}
}
+ // component headers
+ addEndpointHeaders(componentModel, classElement);
+
// endpoint options
findClassProperties(componentModel, classElement, new HashSet<>(), "", null, null, false);
@@ -291,6 +298,167 @@ public class EndpointSchemaGeneratorMojo extends AbstractGeneratorMojo {
return componentModel;
}
+ /**
+ * Retrieve the metadata added to the constants specified in the element {@code headers} of the annotation
+ * {@code Metadata}, convert the metadata retrieved 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 classElement the class from which the annotation {@code Metadata} will be retrieved.
+ */
+ void addEndpointHeaders(ComponentModel componentModel, Class<?> classElement) {
+ final Metadata componentMetadata = classElement.getAnnotation(Metadata.class);
+ if (componentMetadata == null || componentMetadata.headers().length == 0) {
+ return;
+ }
+ // There are headers to load
+ try {
+ final JavaClassSource source = javaClassSource(classElement.getName());
+ final AnnotationSource<?> annotationSource = source.getAnnotation(Metadata.class);
+ if (annotationSource == null) {
+ getLog().warn(String.format("The annotation @Metadata could not be found by the parser in the class %s",
+ classElement.getName()));
+ return;
+ }
+ final String[] headers = annotationSource.getStringArrayValue("headers");
+ if (headers == null) {
+ getLog().warn(String.format("The element headers of @Metadata could not be found by the parser in the class %s",
+ classElement.getName()));
+ return;
+ }
+ for (String headerRef : headers) {
+ getLog().debug(String.format("Header ref found %s", headerRef));
+ addEndpointHeader(componentModel, source, headerRef);
+ }
+ } catch (Exception e) {
+ getLog().warn(String.format("The headers of %s could not be loaded", classElement.getName()), e);
+ }
+ }
+
+ /**
+ * Identify the constant corresponding to the given header reference and retrieve the metadata added to it, convert
+ * the metadata retrieved 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 source the source of the file that contains the definition of the header references.
+ * @param headerRef the header reference to process.
+ */
+ private void addEndpointHeader(ComponentModel componentModel, JavaClassSource source, String headerRef) {
+ final int lastIndex = headerRef.lastIndexOf('.');
+ final String constantName;
+ final String qualifiedName;
+ if (lastIndex == -1) {
+ // No prefix
+ final Optional<Import> optionalImport = source.getImports().stream()
+ .filter(StaticCapable::isStatic)
+ .filter(imp -> imp.getSimpleName().equals(headerRef))
+ .findFirst();
+ if (optionalImport.isPresent()) {
+ constantName = headerRef;
+ final String importQualifiedName = optionalImport.get().getQualifiedName();
+ qualifiedName = importQualifiedName.substring(0, importQualifiedName.length() - headerRef.length() - 1);
+ } else {
+ getLog().debug(String.format("The header %s defined in the class %s could not be found", headerRef,
+ source.getQualifiedName()));
+ return;
+ }
+ } else if (headerRef.startsWith(source.getPackage())) {
+ // Fully qualified
+ constantName = headerRef.substring(lastIndex + 1);
+ qualifiedName = headerRef.substring(0, lastIndex);
+ } else {
+ // With prefix
+ final String prefix = headerRef.substring(0, lastIndex);
+ final int firstIndex = prefix.indexOf('.');
+ final String root = firstIndex == -1 ? prefix : prefix.substring(0, firstIndex);
+ final Optional<Import> optionalImport = source.getImports().stream()
+ .filter(imp -> !imp.isStatic())
+ .filter(imp -> imp.getQualifiedName().endsWith(root))
+ .findFirst();
+ constantName = headerRef.substring(lastIndex + 1);
+ if (optionalImport.isPresent()) {
+ // In another package
+ if (firstIndex == -1) {
+ // No inner class
+ qualifiedName = optionalImport.get().getQualifiedName();
+ } else {
+ // In inner class
+ qualifiedName = String.format("%s$%s", optionalImport.get().getQualifiedName(),
+ prefix.substring(firstIndex + 1).replace('.', '$'));
+ }
+ } else {
+ // In the same package
+ qualifiedName = String.format("%s.%s", source.getPackage(), prefix);
+ }
+ }
+ addEndpointHeader(componentModel, constantName, qualifiedName);
+ }
+
+ /**
+ * Retrieve the metadata added to the given constant of the given class, convert the metadata retrieved 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 constantName the name of the constant from which the metadata defining the header should be extracted.
+ * @param qualifiedName the full qualified name of the class containing the constant.
+ */
+ private void addEndpointHeader(ComponentModel componentModel, String constantName, String qualifiedName) {
+ getLog().debug(
+ String.format("Trying to add the constant %s in the class %s as header.", constantName, qualifiedName));
+ try {
+ addEndpointHeader(componentModel, loadClass(qualifiedName).getDeclaredField(constantName));
+ } catch (NoClassDefFoundError e) {
+ getLog().debug(String.format("The class %s could not be found", qualifiedName), e);
+ } catch (NoSuchFieldException e) {
+ getLog().debug(String.format("The field %s in class %s could not be found", constantName, qualifiedName), e);
+ }
+ }
+
+ /**
+ * Retrieve the metadata added to the given field, convert the metadata retrieved 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 +898,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 +995,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..98890b5
--- /dev/null
+++ b/tooling/maven/camel-package-maven-plugin/src/test/java/org/apache/camel/maven/packaging/EndpointSchemaGeneratorMojoTest.java
@@ -0,0 +1,102 @@
+/*
+ * 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.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);
+ List<EndpointHeaderModel> endpointHeaders = model.getEndpointHeaders();
+ assertEquals(18, endpointHeaders.size());
+ for (int i = 1; i < endpointHeaders.size(); i++) {
+ EndpointHeaderModel header = endpointHeaders.get(i);
+ assertEquals("header", header.getKind());
+ assertEquals(String.format("name-%d", i + 1), header.getName());
+ assertEquals(String.format("key%d desc", i + 1), header.getDescription());
+ assertTrue(header.getDisplayName().isEmpty());
+ assertTrue(header.getJavaType().isEmpty());
+ assertFalse(header.isRequired());
+ assertInstanceOf(String.class, header.getDefaultValue());
+ assertTrue(((String) header.getDefaultValue()).isEmpty());
+ assertFalse(header.isDeprecated());
+ assertTrue(header.getDeprecationNote().isEmpty());
+ assertFalse(header.isSecret());
+ assertTrue(header.getLabel().isEmpty());
+ assertNull(header.getEnums());
+ assertEquals("common", header.getGroup());
+ }
+ EndpointHeaderModel header = endpointHeaders.get(0);
+ assertEquals("header", header.getKind());
+ assertEquals("name-1", header.getName());
+ assertEquals("key1 desc", header.getDescription());
+ assertEquals("my display name", header.getDisplayName());
+ assertEquals("org.apache.camel.maven.packaging.endpoint.SomeEndpoint$MyEnum", header.getJavaType());
+ assertTrue(header.isRequired());
+ assertEquals("VAL1", header.getDefaultValue());
+ assertTrue(header.isDeprecated());
+ assertEquals("my deprecated note", header.getDeprecationNote());
+ assertTrue(header.isSecret());
+ assertEquals("my label", header.getLabel());
+ assertEquals(3, header.getEnums().size());
+ assertEquals("my label", header.getGroup());
+ }
+
+ @Test
+ void testHeadersNotProperlyDefinedAreIgnored() {
+ mojo.addEndpointHeaders(model, SomeEndpointWithBadHeaders.class);
+ assertEquals(0, model.getEndpointHeaders().size());
+ }
+
+ @Test
+ void testEndpointWithoutHeadersAreIgnored() {
+ mojo.addEndpointHeaders(model, SomeEndpointWithoutHeaders.class);
+ assertEquals(0, model.getEndpointHeaders().size());
+ }
+}
diff --git a/tooling/maven/camel-package-maven-plugin/src/test/java/org/apache/camel/maven/packaging/endpoint/SamePackageConstants.java b/tooling/maven/camel-package-maven-plugin/src/test/java/org/apache/camel/maven/packaging/endpoint/SamePackageConstants.java
new file mode 100644
index 0000000..1d1debb
--- /dev/null
+++ b/tooling/maven/camel-package-maven-plugin/src/test/java/org/apache/camel/maven/packaging/endpoint/SamePackageConstants.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;
+
+public final class SamePackageConstants {
+ @Metadata(description = "key2 desc")
+ static final String KEY_2 = "name-2";
+ @Metadata(description = "key17 desc")
+ static final String KEY_17 = "name-17";
+
+ private SamePackageConstants() {
+ }
+
+ public final class Inner {
+ @Metadata(description = "key7 desc")
+ public static final String KEY_7 = "name-7";
+ @Metadata(description = "key11 desc")
+ public static final String KEY_11 = "name-11";
+
+ private Inner() {
+ }
+ }
+
+ public static class InnerStatic {
+ @Metadata(description = "key8 desc")
+ public static final String KEY_8 = "name-8";
+ @Metadata(description = "key12 desc")
+ public static final String KEY_12 = "name-12";
+ }
+}
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..ba61373
--- /dev/null
+++ b/tooling/maven/camel-package-maven-plugin/src/test/java/org/apache/camel/maven/packaging/endpoint/SomeEndpoint.java
@@ -0,0 +1,96 @@
+/*
+ * 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.maven.packaging.endpoint.other.OtherPackageConstants;
+import org.apache.camel.spi.Metadata;
+import org.apache.camel.spi.UriPath;
+
+import static org.apache.camel.maven.packaging.endpoint.SamePackageConstants.Inner.KEY_7;
+import static org.apache.camel.maven.packaging.endpoint.SamePackageConstants.InnerStatic.KEY_8;
+import static org.apache.camel.maven.packaging.endpoint.SomeEndpoint.Inner.KEY_13;
+import static org.apache.camel.maven.packaging.endpoint.SomeEndpoint.InnerStatic.KEY_14;
+import static org.apache.camel.maven.packaging.endpoint.other.OtherPackageConstants.KEY_4;
+import static org.apache.camel.maven.packaging.endpoint.other.OtherPackageConstants.Inner.KEY_9;
+import static org.apache.camel.maven.packaging.endpoint.other.OtherPackageConstants.InnerStatic.KEY_10;
+
+@Metadata(
+ headers = {
+ SomeEndpoint.KEY_1,
+ SamePackageConstants.KEY_2,
+ OtherPackageConstants.KEY_3,
+ KEY_4,
+ OtherPackageConstants.Inner.KEY_5,
+ OtherPackageConstants.InnerStatic.KEY_6,
+ KEY_7,
+ KEY_8,
+ KEY_9,
+ KEY_10,
+ SamePackageConstants.Inner.KEY_11,
+ SamePackageConstants.InnerStatic.KEY_12,
+ KEY_13,
+ KEY_14,
+ SomeEndpoint.Inner.KEY_15,
+ SomeEndpoint.InnerStatic.KEY_16,
+ org.apache.camel.maven.packaging.endpoint.SamePackageConstants.KEY_17,
+ org.apache.camel.maven.packaging.endpoint.other.OtherPackageConstants.KEY_18
+ })
+public class SomeEndpoint {
+ @Deprecated
+ @Metadata(description = "key1 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_1 = "name-1";
+
+ @UriPath(description = "Hostname of the Foo server")
+ @Metadata(required = true)
+ private String host;
+
+ public final class Inner {
+ @Metadata(description = "key13 desc")
+ public static final String KEY_13 = "name-13";
+ @Metadata(description = "key15 desc")
+ public static final String KEY_15 = "name-15";
+
+ private Inner() {
+ }
+ }
+
+ public static class InnerStatic {
+ @Metadata(description = "key14 desc")
+ public static final String KEY_14 = "name-14";
+ @Metadata(description = "key16 desc")
+ public static final String KEY_16 = "name-16";
+ }
+
+ 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..94e2dec
--- /dev/null
+++ b/tooling/maven/camel-package-maven-plugin/src/test/java/org/apache/camel/maven/packaging/endpoint/SomeEndpointWithBadHeaders.java
@@ -0,0 +1,29 @@
+/*
+ * 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;
+
+@Metadata(
+ headers = { "non-existing", SomeEndpointWithBadHeaders.NO_METADATA })
+public final class SomeEndpointWithBadHeaders {
+ public static final String NO_METADATA = "no-metadata";
+
+ 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..0d7fa93
--- /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.Metadata;
+
+@Metadata
+public class SomeEndpointWithoutHeaders {
+}
diff --git a/tooling/maven/camel-package-maven-plugin/src/test/java/org/apache/camel/maven/packaging/endpoint/other/OtherPackageConstants.java b/tooling/maven/camel-package-maven-plugin/src/test/java/org/apache/camel/maven/packaging/endpoint/other/OtherPackageConstants.java
new file mode 100644
index 0000000..afdd9ef
--- /dev/null
+++ b/tooling/maven/camel-package-maven-plugin/src/test/java/org/apache/camel/maven/packaging/endpoint/other/OtherPackageConstants.java
@@ -0,0 +1,49 @@
+/*
+ * 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.other;
+
+import org.apache.camel.spi.Metadata;
+
+public final class OtherPackageConstants {
+ @Metadata(description = "key3 desc")
+ public static final String KEY_3 = "name-3";
+ @Metadata(description = "key4 desc")
+ public static final String KEY_4 = "name-4";
+ @Metadata(description = "key18 desc")
+ public static final String KEY_18 = "name-18";
+
+ private OtherPackageConstants() {
+
+ }
+
+ public final class Inner {
+ @Metadata(description = "key5 desc")
+ public static final String KEY_5 = "name-5";
+ @Metadata(description = "key9 desc")
+ public static final String KEY_9 = "name-9";
+
+ private Inner() {
+ }
+ }
+
+ public static class InnerStatic {
+ @Metadata(description = "key6 desc")
+ public static final String KEY_6 = "name-6";
+ @Metadata(description = "key10 desc")
+ public static final String KEY_10 = "name-10";
+ }
+}
diff --git a/tooling/spi-annotations/src/main/java/org/apache/camel/spi/Metadata.java b/tooling/spi-annotations/src/main/java/org/apache/camel/spi/Metadata.java
index f37ea43..a099566 100644
--- a/tooling/spi-annotations/src/main/java/org/apache/camel/spi/Metadata.java
+++ b/tooling/spi-annotations/src/main/java/org/apache/camel/spi/Metadata.java
@@ -132,4 +132,13 @@ public @interface Metadata {
* specify which options each implementation only supports.
*/
String includeProperties() default "";
+
+ /**
+ * All the headers that are supported by the consumer and/or producer.
+ * <p/>
+ * The expected values are actually the constant expressions referring the name of the header like for example
+ * {@code KafkaConstants.PARTITION_KEY}. The metadata of each header are retrieved directly from the annotation
+ * {@code @Metadata} added to the corresponding constant.
+ */
+ String[] headers() default {};
}