You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@camel.apache.org by da...@apache.org on 2019/10/09 07:37:58 UTC

[camel-quarkus] 01/03: Generate extension list readme file via tooling like we do at Apache Camel

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

davsclaus pushed a commit to branch extlist
in repository https://gitbox.apache.org/repos/asf/camel-quarkus.git

commit 349c8d9514bb794b459d61f17c5099d13485fb8f
Author: Claus Ibsen <cl...@gmail.com>
AuthorDate: Wed Oct 9 09:25:10 2019 +0200

    Generate extension list readme file via tooling like we do at Apache Camel
---
 catalog/camel-quarkus-catalog/pom.xml              |   2 +
 extensions/readme.adoc                             | 142 +++++
 tooling/maven/package-maven-plugin/pom.xml         |   5 +
 .../camel/quarkus/maven/JSonSchemaHelper.java      | 206 +++++++
 .../org/apache/camel/quarkus/maven/MvelHelper.java |  46 ++
 .../quarkus/maven/PrepareExtensionsReadmeMojo.java | 676 +++++++++++++++++++++
 .../apache/camel/quarkus/maven/StringHelper.java   | 200 ++++++
 .../camel/quarkus/maven/model/ComponentModel.java  | 190 ++++++
 .../camel/quarkus/maven/model/DataFormatModel.java | 155 +++++
 .../camel/quarkus/maven/model/LanguageModel.java   | 155 +++++
 .../camel/quarkus/maven/model/OtherModel.java      | 145 +++++
 .../src/main/resources/readme-components.mvel      |  14 +
 .../src/main/resources/readme-dataformats.mvel     |  12 +
 .../src/main/resources/readme-languages.mvel       |  12 +
 .../src/main/resources/readme-others.mvel          |  12 +
 15 files changed, 1972 insertions(+)

diff --git a/catalog/camel-quarkus-catalog/pom.xml b/catalog/camel-quarkus-catalog/pom.xml
index 4d2c64d..7ce0e6e 100644
--- a/catalog/camel-quarkus-catalog/pom.xml
+++ b/catalog/camel-quarkus-catalog/pom.xml
@@ -86,8 +86,10 @@
                 </dependencies>
                 <executions>
                     <execution>
+                        <!-- prepare the catalog, readme files, etc. -->
                         <goals>
                             <goal>prepare-catalog-quarkus</goal>
+                            <goal>prepare-extensions-readme</goal>
                         </goals>
                         <phase>process-resources</phase>
                     </execution>
diff --git a/extensions/readme.adoc b/extensions/readme.adoc
new file mode 100644
index 0000000..c70bded
--- /dev/null
+++ b/extensions/readme.adoc
@@ -0,0 +1,142 @@
+= Components
+
+// components: START
+Number of Components: 22 in 18 JAR artifacts (0 deprecated)
+
+[width="100%",cols="4,1,5",options="header"]
+|===
+| Component | Available From | Description
+
+| link:camel-quarkus-aws-eks/src/main/docs/aws-eks-component.adoc[AWS EKS] (camel-quarkus-aws-eks) +
+`aws-eks:label` | 3.0 | The aws-kms is used for managing Amazon EKS
+
+| link:camel-quarkus-aws-s3/src/main/docs/aws-s3-component.adoc[AWS S3 Storage Service] (camel-quarkus-aws-s3) +
+`aws-s3://bucketNameOrArn` | 2.8 | The aws-s3 component is used for storing and retrieving object from Amazon S3 Storage Service.
+
+| link:camel-quarkus-aws-sns/src/main/docs/aws-sns-component.adoc[AWS Simple Notification System] (camel-quarkus-aws-sns) +
+`aws-sns:topicNameOrArn` | 2.8 | The aws-sns component is used for sending messages to an Amazon Simple Notification Topic.
+
+| link:camel-quarkus-aws-sqs/src/main/docs/aws-sqs-component.adoc[AWS Simple Queue Service] (camel-quarkus-aws-sqs) +
+`aws-sqs:queueNameOrArn` | 2.6 | The aws-sqs component is used for sending and receiving messages to Amazon's SQS service.
+
+| link:camel-quarkus-bean/src/main/docs/bean-component.adoc[Bean] (camel-quarkus-bean) +
+`bean:beanName` | 1.0 | The bean component is for invoking Java beans from Camel.
+
+| link:camel-quarkus-bean/src/main/docs/class-component.adoc[Class] (camel-quarkus-bean) +
+`class:beanName` | 2.4 | The class component is for invoking Java classes (Java beans) from Camel.
+
+| link:camel-quarkus-direct/src/main/docs/direct-component.adoc[Direct] (camel-quarkus-direct) +
+`direct:name` | 1.0 | The direct component provides direct, synchronous call to another endpoint from the same CamelContext.
+
+| link:camel-quarkus-infinispan/src/main/docs/infinispan-component.adoc[Infinispan] (camel-quarkus-infinispan) +
+`infinispan:cacheName` | 2.13 | For reading/writing from/to Infinispan distributed key/value store and data grid.
+
+| link:camel-quarkus-jdbc/src/main/docs/jdbc-component.adoc[JDBC] (camel-quarkus-jdbc) +
+`jdbc:dataSourceName` | 1.2 | The jdbc component enables you to access databases through JDBC, where SQL queries are sent in the message body.
+
+| link:camel-quarkus-log/src/main/docs/log-component.adoc[Log] (camel-quarkus-log) +
+`log:loggerName` | 1.1 | The log component logs message exchanges to the underlying logging mechanism.
+
+| link:camel-quarkus-mail/src/main/docs/mail-component.adoc[Mail] (camel-quarkus-mail) +
+`imap:host:port` | 1.0 | To send or receive emails using imap/pop3 or smtp protocols.
+
+| link:camel-quarkus-microprofile-metrics/src/main/docs/microprofile-metrics-component.adoc[MicroProfile Metrics] (camel-quarkus-microprofile-metrics) +
+`microprofile-metrics:metricType:metricName` | 3.0 | Camel metrics exposed with Eclipse MicroProfile Metrics
+
+| link:camel-quarkus-netty-http/src/main/docs/netty-http-component.adoc[Netty HTTP] (camel-quarkus-netty-http) +
+`netty-http:protocol:host:port/path` | 2.14 | Netty HTTP server and client using the Netty 4.x library.
+
+| link:camel-quarkus-paho/src/main/docs/paho-component.adoc[Paho] (camel-quarkus-paho) +
+`paho:topic` | 2.16 | Component for communicating with MQTT M2M message brokers using Eclipse Paho MQTT Client.
+
+| link:camel-quarkus-rest/src/main/docs/rest-component.adoc[REST] (camel-quarkus-rest) +
+`rest:method:path:uriTemplate` | 2.14 | The rest component is used for either hosting REST services (consumer) or calling external REST services (producer).
+
+| link:camel-quarkus-rest/src/main/docs/rest-api-component.adoc[REST API] (camel-quarkus-rest) +
+`rest-api:path/contextIdPattern` | 2.16 | The rest-api component is used for providing Swagger API of the REST services which has been defined using the rest-dsl in Camel.
+
+| link:camel-quarkus-salesforce/src/main/docs/salesforce-component.adoc[Salesforce] (camel-quarkus-salesforce) +
+`salesforce:operationName:topicName` | 2.12 | The salesforce component is used for integrating Camel with the massive Salesforce API.
+
+| link:camel-quarkus-servlet/src/main/docs/servlet-component.adoc[Servlet] (camel-quarkus-servlet) +
+`servlet:contextPath` | 2.0 | To use a HTTP Servlet as entry for Camel routes when running in a servlet container.
+
+| link:camel-quarkus-timer/src/main/docs/timer-component.adoc[Timer] (camel-quarkus-timer) +
+`timer:timerName` | 1.0 | The timer component is used for generating message exchanges when a timer fires.
+
+| link:camel-quarkus-twitter/src/main/docs/twitter-directmessage-component.adoc[Twitter Direct Message] (camel-quarkus-twitter) +
+`twitter-directmessage:user` | 2.10 | The Twitter Direct Message Component consumes/produces user's direct messages.
+
+| link:camel-quarkus-twitter/src/main/docs/twitter-search-component.adoc[Twitter Search] (camel-quarkus-twitter) +
+`twitter-search:keywords` | 2.10 | The Twitter Search component consumes search results.
+
+| link:camel-quarkus-twitter/src/main/docs/twitter-timeline-component.adoc[Twitter Timeline] (camel-quarkus-twitter) +
+`twitter-timeline:timelineType` | 2.10 | The Twitter Timeline component consumes twitter timeline or update the status of specific user.
+
+|===
+// components: END
+
+
+== Data Formats
+
+// dataformats: START
+Number of Data Formats: 3 in 3 JAR artifacts (0 deprecated)
+
+[width="100%",cols="4,1,5",options="header"]
+|===
+| Data Format | Available From | Description
+
+| link:camel-quarkus-csv/src/main/docs/csv-dataformat.adoc[CSV] (camel-quarkus-csv) | 1.3 | The CSV data format is used for handling CSV payloads.
+
+| link:camel-quarkus-mail/src/main/docs/mime-multipart-dataformat.adoc[MIME Multipart] (camel-quarkus-mail) | 2.17 | The MIME Multipart data format can marshal a Camel message with attachments into a Camel message having a MIME-Multipart message as message body (and no attachments), and vise-versa when unmarshalling.
+
+| link:camel-quarkus-zipfile/src/main/docs/zipfile-dataformat.adoc[Zip File] (camel-quarkus-zipfile) | 2.11 | The Zip File data format is a message compression and de-compression format of zip files.
+|===
+// dataformats: END
+
+
+== Expression Languages
+
+// languages: START
+Number of Languages: 8 in 2 JAR artifacts (0 deprecated)
+
+[width="100%",cols="4,1,5",options="header"]
+|===
+| Language | Available From | Description
+
+| link:camel-quarkus-bean/src/main/docs/bean-language.adoc[Bean method] (camel-quarkus-bean) | 1.3 | To use a Java bean (aka method call) in Camel expressions or predicates.
+
+| link:camel-quarkus-core/src/main/docs/constant-language.adoc[Constant] (camel-quarkus-core) | 1.5 | To use a constant value in Camel expressions or predicates. Important: this is a fixed constant value that is only set once during starting up the route, do not use this if you want dynamic values during routing.
+
+| link:camel-quarkus-core/src/main/docs/exchangeProperty-language.adoc[ExchangeProperty] (camel-quarkus-core) | 2.0 | To use a Camel Exchange property in expressions or predicates.
+
+| link:camel-quarkus-core/src/main/docs/file-language.adoc[File] (camel-quarkus-core) | 1.1 | For expressions and predicates using the file/simple language
+
+| link:camel-quarkus-core/src/main/docs/header-language.adoc[Header] (camel-quarkus-core) | 1.5 | To use a Camel Message header in expressions or predicates.
+
+| link:camel-quarkus-core/src/main/docs/ref-language.adoc[Ref] (camel-quarkus-core) | 2.8 | Reference to an existing Camel expression or predicate, which is looked up from the Camel registry.
+
+| link:camel-quarkus-core/src/main/docs/simple-language.adoc[Simple] (camel-quarkus-core) | 1.1 | To use Camels built-in Simple language in Camel expressions or predicates.
+
+| link:camel-quarkus-core/src/main/docs/tokenize-language.adoc[Tokenize] (camel-quarkus-core) | 2.0 | To use Camel message body or header with a tokenizer in Camel expressions or predicates.
+|===
+// languages: END
+
+
+== Miscellaneous Components
+
+// others: START
+Number of Miscellaneous Components: 3 in 3 JAR artifacts (0 deprecated)
+
+[width="100%",cols="4,1,5",options="header"]
+|===
+| Component | Available From | Description
+
+| link:camel-quarkus-core-cloud/src/main/docs/core-cloud.adoc[Camel Quarkus Core Cloud] (camel-quarkus-core-cloud) | 0.2 | The Camel Quarkus core cloud module
+
+| link:camel-quarkus-platform-http/src/main/docs/platform-http.adoc[Camel Quarkus Platform HTTP] (camel-quarkus-platform-http) | 0.2.1 | HTTP platform component is used for integrating Camel HTTP with Quarkus HTTP layer
+
+| link:camel-quarkus-reactive-executor/src/main/docs/reactive-executor.adoc[Camel Quarkus Reactive Executor] (camel-quarkus-reactive-executor) | 0.2.1 | To use Quarkus reactive executor with Camel
+|===
+// others: END
+
diff --git a/tooling/maven/package-maven-plugin/pom.xml b/tooling/maven/package-maven-plugin/pom.xml
index 4130638..d9a1878 100644
--- a/tooling/maven/package-maven-plugin/pom.xml
+++ b/tooling/maven/package-maven-plugin/pom.xml
@@ -86,6 +86,11 @@
         </dependency>
 
         <dependency>
+            <groupId>org.apache.camel</groupId>
+            <artifactId>camel-util-json</artifactId>
+            <version>${camel.version}</version>
+        </dependency>
+        <dependency>
             <groupId>org.mvel</groupId>
             <artifactId>mvel2</artifactId>
             <version>2.4.4.Final</version>
diff --git a/tooling/maven/package-maven-plugin/src/main/java/org/apache/camel/quarkus/maven/JSonSchemaHelper.java b/tooling/maven/package-maven-plugin/src/main/java/org/apache/camel/quarkus/maven/JSonSchemaHelper.java
new file mode 100644
index 0000000..22ad577
--- /dev/null
+++ b/tooling/maven/package-maven-plugin/src/main/java/org/apache/camel/quarkus/maven/JSonSchemaHelper.java
@@ -0,0 +1,206 @@
+/*
+ * 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.quarkus.maven;
+
+import java.util.ArrayList;
+import java.util.LinkedHashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.stream.Collectors;
+
+import org.apache.camel.util.json.JsonObject;
+import org.apache.camel.util.json.Jsoner;
+
+public final class JSonSchemaHelper {
+
+    private JSonSchemaHelper() {
+    }
+
+    /**
+     * Parses the json schema to split it into a list or rows, where each row contains key value pairs with the metadata
+     *
+     * @param group the group to parse from such as <tt>component</tt>, <tt>componentProperties</tt>, or <tt>properties</tt>.
+     * @param json the json
+     * @return a list of all the rows, where each row is a set of key value pairs with metadata
+     */
+    public static List<Map<String, String>> parseJsonSchema(String group, String json, boolean parseProperties) {
+        List<Map<String, String>> answer = new ArrayList<>();
+        if (json == null) {
+            return answer;
+        }
+
+        // convert into a List<Map<String, String>> structure which is expected as output from this parser
+        try {
+            JsonObject output = (JsonObject) Jsoner.deserialize(json);
+            for (String key : output.keySet()) {
+                Map<?, ?> row = output.getMap(key);
+                if (key.equals(group)) {
+                    if (parseProperties) {
+                        // flattern each entry in the row with name as they key, and its value as the content (its a map also)
+                        for (Object obj : row.entrySet()) {
+                            Map.Entry<?, ?> entry = (Map.Entry<?, ?>) obj;
+                            Map<String, String> newRow = new LinkedHashMap<>();
+                            newRow.put("name", entry.getKey().toString());
+
+                            Map<String, String> newData = transformMap((Map<?, ?>) entry.getValue());
+                            newRow.putAll(newData);
+                            answer.add(newRow);
+                        }
+                    } else {
+                        // flattern each entry in the row as a list of single Map<key, value> elements
+                        Map<?, ?> newData = transformMap(row);
+                        for (Object obj : newData.entrySet()) {
+                            Map.Entry<?, ?> entry = (Map.Entry<?, ?>) obj;
+                            Map<String, String> newRow = new LinkedHashMap<>();
+                            newRow.put(entry.getKey().toString(), entry.getValue().toString());
+                            answer.add(newRow);
+                        }
+                    }
+                }
+            }
+        } catch (Exception e) {
+            // wrap parsing exceptions as runtime
+            throw new RuntimeException("Cannot parse json", e);
+        }
+
+        return answer;
+    }
+
+    private static Map<String, String> transformMap(Map<?, ?> jsonMap) {
+        Map<String, String> answer = new LinkedHashMap<>();
+
+        for (Object rowObj : jsonMap.entrySet()) {
+            Map.Entry<?, ?> rowEntry = (Map.Entry<?, ?>) rowObj;
+            // if its a list type then its an enum, and we need to parse it as a single line separated with comma
+            // to be backwards compatible
+            Object newValue = rowEntry.getValue();
+            if (newValue instanceof List) {
+                List<?> list = (List<?>) newValue;
+                newValue = list.stream().map(Object::toString)
+                        .collect(Collectors.joining(","));
+            }
+            // ensure value is escaped
+            String value = escapeJson(newValue.toString());
+            answer.put(rowEntry.getKey().toString(), value);
+        }
+
+        return answer;
+    }
+
+    private static String escapeJson(String value) {
+        // need to safe encode \r as \\r so its escaped
+        // need to safe encode \n as \\n so its escaped
+        // need to safe encode \t as \\t so its escaped
+        return value
+            .replaceAll("\\\\r", "\\\\\\r")
+            .replaceAll("\\\\n", "\\\\\\n")
+            .replaceAll("\\\\t", "\\\\\\t");
+    }
+
+    /**
+     * Gets the value with the key in a safe way, eg returning an empty string if there was no value for the key.
+     */
+    public static String getSafeValue(String key, List<Map<String, String>> rows) {
+        for (Map<String, String> row : rows) {
+            String value = row.get(key);
+            if (value != null) {
+                return value;
+            }
+        }
+        return "";
+    }
+
+    /**
+     * Gets the value with the key in a safe way, eg returning an empty string if there was no value for the key.
+     */
+    public static String getSafeValue(String key, Map<String, String> rows) {
+        String value = rows.get(key);
+        if (value != null) {
+            return value;
+        }
+        return "";
+    }
+
+    public static String getPropertyDefaultValue(List<Map<String, String>> rows, String name) {
+        for (Map<String, String> row : rows) {
+            String defaultValue = null;
+            boolean found = false;
+            if (row.containsKey("name")) {
+                found = name.equals(row.get("name"));
+            }
+            if (row.containsKey("defaultValue")) {
+                defaultValue = row.get("defaultValue");
+            }
+            if (found) {
+                return defaultValue;
+            }
+        }
+        return null;
+    }
+
+    public static String getPropertyDescriptionValue(List<Map<String, String>> rows, String name) {
+        for (Map<String, String> row : rows) {
+            String description = null;
+            boolean found = false;
+            if (row.containsKey("name")) {
+                found = name.equals(row.get("name"));
+            }
+            if (row.containsKey("description")) {
+                description = row.get("description");
+            }
+            if (found) {
+                return description;
+            }
+        }
+        return null;
+    }
+
+    public static String getPropertyJavaType(List<Map<String, String>> rows, String name) {
+        for (Map<String, String> row : rows) {
+            String javaType = null;
+            boolean found = false;
+            if (row.containsKey("name")) {
+                found = name.equals(row.get("name"));
+            }
+            if (row.containsKey("javaType")) {
+                javaType = row.get("javaType");
+            }
+            if (found) {
+                return javaType;
+            }
+        }
+        return null;
+    }
+
+    public static String getPropertyType(List<Map<String, String>> rows, String name) {
+        for (Map<String, String> row : rows) {
+            String type = null;
+            boolean found = false;
+            if (row.containsKey("name")) {
+                found = name.equals(row.get("name"));
+            }
+            if (row.containsKey("type")) {
+                type = row.get("type");
+            }
+            if (found) {
+                return type;
+            }
+        }
+        return null;
+    }
+
+}
diff --git a/tooling/maven/package-maven-plugin/src/main/java/org/apache/camel/quarkus/maven/MvelHelper.java b/tooling/maven/package-maven-plugin/src/main/java/org/apache/camel/quarkus/maven/MvelHelper.java
new file mode 100644
index 0000000..6ecb3fc
--- /dev/null
+++ b/tooling/maven/package-maven-plugin/src/main/java/org/apache/camel/quarkus/maven/MvelHelper.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.quarkus.maven;
+
+import java.util.regex.Pattern;
+
+public final class MvelHelper {
+
+    public static final MvelHelper INSTANCE = new MvelHelper();
+
+    private static final Pattern DOLLAR_ESCAPE = Pattern.compile("\\$");
+
+    private static final Pattern CURLY_BRACKET_ESCAPE = Pattern.compile("(\\{[a-zA-Z0-9]+?)\\}");
+
+    private static final Pattern URL_ESCAPE = Pattern.compile("(?<!href=\")(http(:?s)?://|(:?s)?ftp(?:s)?)");
+
+    private MvelHelper() {
+        // utility class
+    }
+
+    public static String escape(final String raw) {
+        if (raw == null) {
+            return null;
+        }
+
+        final String escapedDollars = DOLLAR_ESCAPE.matcher(raw).replaceAll("\\\\\\$");
+        final String escapedCurlyBrackets = CURLY_BRACKET_ESCAPE.matcher(escapedDollars).replaceAll("\\\\$1\\\\}");
+        final String escapedUrls = URL_ESCAPE.matcher(escapedCurlyBrackets).replaceAll("\\\\$1");
+
+        return escapedUrls;
+    }
+}
diff --git a/tooling/maven/package-maven-plugin/src/main/java/org/apache/camel/quarkus/maven/PrepareExtensionsReadmeMojo.java b/tooling/maven/package-maven-plugin/src/main/java/org/apache/camel/quarkus/maven/PrepareExtensionsReadmeMojo.java
new file mode 100644
index 0000000..e9ee00a
--- /dev/null
+++ b/tooling/maven/package-maven-plugin/src/main/java/org/apache/camel/quarkus/maven/PrepareExtensionsReadmeMojo.java
@@ -0,0 +1,676 @@
+/*
+ * 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.quarkus.maven;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.TreeSet;
+
+import org.apache.camel.quarkus.maven.model.ComponentModel;
+import org.apache.camel.quarkus.maven.model.DataFormatModel;
+import org.apache.camel.quarkus.maven.model.LanguageModel;
+import org.apache.camel.quarkus.maven.model.OtherModel;
+import org.apache.maven.plugin.AbstractMojo;
+import org.apache.maven.plugin.MojoExecutionException;
+import org.apache.maven.plugin.MojoFailureException;
+import org.apache.maven.plugins.annotations.Component;
+import org.apache.maven.plugins.annotations.Mojo;
+import org.apache.maven.plugins.annotations.Parameter;
+import org.apache.maven.project.MavenProject;
+import org.apache.maven.project.MavenProjectHelper;
+import org.mvel2.templates.TemplateRuntime;
+
+import static java.util.stream.Collectors.toSet;
+import static org.apache.camel.quarkus.maven.PackageHelper.loadText;
+import static org.apache.camel.quarkus.maven.PackageHelper.writeText;
+
+/**
+ * Prepares the extensions/readme.adoc files content up to date with all the extensions that Apache Camel Quarkus ships.
+ */
+@Mojo(name = "prepare-extensions-readme", threadSafe = true)
+public class PrepareExtensionsReadmeMojo extends AbstractMojo {
+
+    /**
+     * The maven project.
+     */
+    @Parameter(property = "project", required = true, readonly = true)
+    protected MavenProject project;
+
+    /**
+     * The directory for components catalog
+     */
+    @Parameter(defaultValue = "${project.build.directory}/classes/org/apache/camel/catalog/quarkus/components")
+    protected File componentsDir;
+
+    /**
+     * The directory for data formats catalog
+     */
+    @Parameter(defaultValue = "${project.build.directory}/classes/org/apache/camel/catalog/quarkus/dataformats")
+    protected File dataFormatsDir;
+
+    /**
+     * The directory for languages catalog
+     */
+    @Parameter(defaultValue = "${project.build.directory}/classes/org/apache/camel/catalog/quarkus/languages")
+    protected File languagesDir;
+
+    /**
+     * The directory for others catalog
+     */
+    @Parameter(defaultValue = "${project.build.directory}/classes/org/apache/camel/catalog/quarkus/others")
+    protected File othersDir;
+
+    /**
+     * The directory for components
+     */
+    @Parameter(defaultValue = "${project.directory}/../../../extensions")
+    protected File readmeComponentsDir;
+
+    /**
+     * Maven ProjectHelper.
+     */
+    @Component
+    private MavenProjectHelper projectHelper;
+
+    /**
+     * Execute goal.
+     *
+     * @throws MojoExecutionException execution of the main class or one of the
+     *                                threads it generated failed.
+     * @throws MojoFailureException   something bad happened...
+     */
+    @Override
+    public void execute() throws MojoExecutionException, MojoFailureException {
+        // update readme file in extensions
+        executeComponentsReadme();
+        executeLanguagesReadme();
+        executeDataFormatsReadme();
+        executeOthersReadme();
+    }
+
+    protected void executeOthersReadme() throws MojoExecutionException, MojoFailureException {
+        Set<File> otherFiles = new TreeSet<>();
+
+        if (othersDir != null && othersDir.isDirectory()) {
+            File[] files = othersDir.listFiles();
+            if (files != null) {
+                otherFiles.addAll(Arrays.asList(files));
+            }
+        }
+
+        try {
+            List<OtherModel> others = new ArrayList<>();
+            for (File file : otherFiles) {
+                String json = loadText(new FileInputStream(file));
+                OtherModel model = generateOtherModel(json);
+                others.add(model);
+            }
+
+            // sort the models
+            Collections.sort(others, new OtherComparator());
+
+            // how many different artifacts
+            int count = others.stream()
+                    .map(OtherModel::getArtifactId)
+                    .collect(toSet()).size();
+
+            // how many deprecated
+            long deprecated = others.stream()
+                    .filter(o -> "true".equals(o.getDeprecated()))
+                    .count();
+
+            // update the big readme file in the components dir
+            File file = new File(readmeComponentsDir, "readme.adoc");
+
+            // update regular components
+            boolean exists = file.exists();
+            String changed = templateOthers(others, count, deprecated);
+            boolean updated = updateOthers(file, changed);
+
+            if (updated) {
+                getLog().info("Updated readme.adoc file: " + file);
+            } else if (exists) {
+                getLog().debug("No changes to readme.adoc file: " + file);
+            } else {
+                getLog().warn("No readme.adoc file: " + file);
+            }
+
+        } catch (IOException e) {
+            throw new MojoFailureException("Error due " + e.getMessage(), e);
+        }
+    }
+
+    protected void executeComponentsReadme() throws MojoExecutionException, MojoFailureException {
+        Set<File> componentFiles = new TreeSet<>();
+
+        if (componentsDir != null && componentsDir.isDirectory()) {
+            File[] files = componentsDir.listFiles();
+            if (files != null) {
+                componentFiles.addAll(Arrays.asList(files));
+            }
+        }
+
+        try {
+            List<ComponentModel> models = new ArrayList<>();
+            for (File file : componentFiles) {
+                String json = loadText(new FileInputStream(file));
+                ComponentModel model = generateComponentModel(json);
+
+                // filter out alternative schemas which reuses documentation
+                boolean add = true;
+                if (!model.getAlternativeSchemes().isEmpty()) {
+                    String first = model.getAlternativeSchemes().split(",")[0];
+                    if (!model.getScheme().equals(first)) {
+                        add = false;
+                    }
+                }
+                if (add) {
+                    models.add(model);
+
+                    // special for camel-mail where we want to refer its imap scheme to mail so its mail.adoc in the doc link
+                    if ("imap".equals(model.getScheme())) {
+                        model.setScheme("mail");
+                        model.setTitle("Mail");
+                    }
+                }
+            }
+
+            // sort the models
+            Collections.sort(models, new ComponentComparator());
+
+            // filter out unwanted components
+            List<ComponentModel> components = new ArrayList<>();
+            for (ComponentModel model : models) {
+                components.add(model);
+            }
+
+            // how many different artifacts
+            int count = components.stream()
+                    .map(ComponentModel::getArtifactId)
+                    .collect(toSet()).size();
+
+            // how many deprecated
+            long deprecated = components.stream()
+                    .filter(c -> "true".equals(c.getDeprecated()))
+                    .count();
+
+            // update the big readme file in the core/components dir
+            File file;
+            file = new File(readmeComponentsDir, "readme.adoc");
+
+            // update regular components
+            boolean exists = file.exists();
+            String changed = templateComponents(components, count, deprecated);
+            boolean updated = updateComponents(file, changed);
+
+            if (updated) {
+                getLog().info("Updated readme.adoc file: " + file);
+            } else if (exists) {
+                getLog().debug("No changes to readme.adoc file: " + file);
+            } else {
+                getLog().warn("No readme.adoc file: " + file);
+            }
+
+        } catch (IOException e) {
+            throw new MojoFailureException("Error due " + e.getMessage(), e);
+        }
+    }
+
+    protected void executeDataFormatsReadme() throws MojoExecutionException, MojoFailureException {
+        Set<File> dataFormatFiles = new TreeSet<>();
+
+        if (dataFormatsDir != null && dataFormatsDir.isDirectory()) {
+            File[] files = dataFormatsDir.listFiles();
+            if (files != null) {
+                dataFormatFiles.addAll(Arrays.asList(files));
+            }
+        }
+
+        try {
+            List<DataFormatModel> models = new ArrayList<>();
+            for (File file : dataFormatFiles) {
+                String json = loadText(new FileInputStream(file));
+                DataFormatModel model = generateDataFormatModel(json);
+
+                // special for bindy as we have one common file
+                if (model.getName().startsWith("bindy")) {
+                    model.setName("bindy");
+                }
+
+                models.add(model);
+            }
+
+            // sort the models
+            Collections.sort(models, new DataFormatComparator());
+
+            // how many different artifacts
+            int count = models.stream()
+                        .map(DataFormatModel::getArtifactId)
+                        .collect(toSet()).size();
+
+            // how many deprecated
+            long deprecated = models.stream()
+                    .filter(m -> "true".equals(m.getDeprecated()))
+                    .count();
+
+            // filter out camel-core
+            List<DataFormatModel> dataFormats = new ArrayList<>();
+            for (DataFormatModel model : models) {
+                dataFormats.add(model);
+            }
+
+            // update the big readme file in the core/components dir
+            File file;
+            file = new File(readmeComponentsDir, "readme.adoc");
+
+            // update regular data formats
+            boolean exists = file.exists();
+            String changed = templateDataFormats(dataFormats, count, deprecated);
+            boolean updated = updateDataFormats(file, changed);
+
+            if (updated) {
+                getLog().info("Updated readme.adoc file: " + file);
+            } else if (exists) {
+                getLog().debug("No changes to readme.adoc file: " + file);
+            } else {
+                getLog().warn("No readme.adoc file: " + file);
+            }
+
+        } catch (IOException e) {
+            throw new MojoFailureException("Error due " + e.getMessage(), e);
+        }
+    }
+
+    protected void executeLanguagesReadme() throws MojoExecutionException, MojoFailureException {
+        Set<File> languageFiles = new TreeSet<>();
+
+        if (languagesDir != null && languagesDir.isDirectory()) {
+            File[] files = languagesDir.listFiles();
+            if (files != null) {
+                languageFiles.addAll(Arrays.asList(files));
+            }
+        }
+
+        try {
+            List<LanguageModel> models = new ArrayList<>();
+            for (File file : languageFiles) {
+                String json = loadText(new FileInputStream(file));
+                LanguageModel model = generateLanguageModel(json);
+                models.add(model);
+            }
+
+            // sort the models
+            Collections.sort(models, new LanguageComparator());
+
+            // filter out camel-core
+            List<LanguageModel> languages = new ArrayList<>();
+            for (LanguageModel model : models) {
+                languages.add(model);
+            }
+
+            // how many different artifacts
+            int count = languages.stream()
+                    .map(LanguageModel::getArtifactId)
+                    .collect(toSet()).size();
+
+            // how many deprecated
+            long deprecated = languages.stream()
+                    .filter(l -> "true".equals(l.getDeprecated()))
+                    .count();
+
+            // update the big readme file in the core/components dir
+            File file;
+            file = new File(readmeComponentsDir, "readme.adoc");
+
+            // update regular data formats
+            boolean exists = file.exists();
+            String changed = templateLanguages(languages, count, deprecated);
+            boolean updated = updateLanguages(file, changed);
+
+            if (updated) {
+                getLog().info("Updated readme.adoc file: " + file);
+            } else if (exists) {
+                getLog().debug("No changes to readme.adoc file: " + file);
+            } else {
+                getLog().warn("No readme.adoc file: " + file);
+            }
+
+        } catch (IOException e) {
+            throw new MojoFailureException("Error due " + e.getMessage(), e);
+        }
+    }
+
+    private String templateComponents(List<ComponentModel> models, int artifacts, long deprecated) throws MojoExecutionException {
+        try {
+            String template = loadText(PrepareExtensionsReadmeMojo.class.getClassLoader().getResourceAsStream("readme-components.mvel"));
+            Map<String, Object> map = new HashMap<>();
+            map.put("components", models);
+            map.put("numberOfArtifacts", artifacts);
+            map.put("numberOfDeprecated", deprecated);
+            String out = (String) TemplateRuntime.eval(template, map, Collections.singletonMap("util", MvelHelper.INSTANCE));
+            return out;
+        } catch (Exception e) {
+            throw new MojoExecutionException("Error processing mvel template. Reason: " + e, e);
+        }
+    }
+
+    private String templateOthers(List<OtherModel> models, int artifacts, long deprecated) throws MojoExecutionException {
+        try {
+            String template = loadText(PrepareExtensionsReadmeMojo.class.getClassLoader().getResourceAsStream("readme-others.mvel"));
+            Map<String, Object> map = new HashMap<>();
+            map.put("others", models);
+            map.put("numberOfArtifacts", artifacts);
+            map.put("numberOfDeprecated", deprecated);
+            String out = (String) TemplateRuntime.eval(template, map, Collections.singletonMap("util", MvelHelper.INSTANCE));
+            return out;
+        } catch (Exception e) {
+            throw new MojoExecutionException("Error processing mvel template. Reason: " + e, e);
+        }
+    }
+
+    private String templateDataFormats(List<DataFormatModel> models, int artifacts, long deprecated) throws MojoExecutionException {
+        try {
+            String template = loadText(PrepareExtensionsReadmeMojo.class.getClassLoader().getResourceAsStream("readme-dataformats.mvel"));
+            Map<String, Object> map = new HashMap<>();
+            map.put("dataformats", models);
+            map.put("numberOfArtifacts", artifacts);
+            map.put("numberOfDeprecated", deprecated);
+            String out = (String) TemplateRuntime.eval(template, map, Collections.singletonMap("util", MvelHelper.INSTANCE));
+            return out;
+        } catch (Exception e) {
+            throw new MojoExecutionException("Error processing mvel template. Reason: " + e, e);
+        }
+    }
+
+    private String templateLanguages(List<LanguageModel> models, int artifacts, long deprecated) throws MojoExecutionException {
+        try {
+            String template = loadText(PrepareExtensionsReadmeMojo.class.getClassLoader().getResourceAsStream("readme-languages.mvel"));
+            Map<String, Object> map = new HashMap<>();
+            map.put("languages", models);
+            map.put("numberOfArtifacts", artifacts);
+            map.put("numberOfDeprecated", deprecated);
+            String out = (String) TemplateRuntime.eval(template, map, Collections.singletonMap("util", MvelHelper.INSTANCE));
+            return out;
+        } catch (Exception e) {
+            throw new MojoExecutionException("Error processing mvel template. Reason: " + e, e);
+        }
+    }
+
+    private boolean updateComponents(File file, String changed) throws MojoExecutionException {
+        if (!file.exists()) {
+            return false;
+        }
+
+        try {
+            String text = loadText(new FileInputStream(file));
+
+            String existing = StringHelper.between(text, "// components: START", "// components: END");
+            if (existing != null) {
+                // remove leading line breaks etc
+                existing = existing.trim();
+                changed = changed.trim();
+                if (existing.equals(changed)) {
+                    return false;
+                } else {
+                    String before = StringHelper.before(text, "// components: START");
+                    String after = StringHelper.after(text, "// components: END");
+                    text = before + "// components: START\n" + changed + "\n// components: END" + after;
+                    writeText(file, text);
+                    return true;
+                }
+            } else {
+                getLog().warn("Cannot find markers in file " + file);
+                getLog().warn("Add the following markers");
+                getLog().warn("\t// components: START");
+                getLog().warn("\t// components: END");
+                return false;
+            }
+        } catch (Exception e) {
+            throw new MojoExecutionException("Error reading file " + file + " Reason: " + e, e);
+        }
+    }
+
+    private boolean updateOthers(File file, String changed) throws MojoExecutionException {
+        if (!file.exists()) {
+            return false;
+        }
+
+        try {
+            String text = loadText(new FileInputStream(file));
+
+            String existing = StringHelper.between(text, "// others: START", "// others: END");
+            if (existing != null) {
+                // remove leading line breaks etc
+                existing = existing.trim();
+                changed = changed.trim();
+                if (existing.equals(changed)) {
+                    return false;
+                } else {
+                    String before = StringHelper.before(text, "// others: START");
+                    String after = StringHelper.after(text, "// others: END");
+                    text = before + "// others: START\n" + changed + "\n// others: END" + after;
+                    writeText(file, text);
+                    return true;
+                }
+            } else {
+                getLog().warn("Cannot find markers in file " + file);
+                getLog().warn("Add the following markers");
+                getLog().warn("\t// others: START");
+                getLog().warn("\t// others: END");
+                return false;
+            }
+        } catch (Exception e) {
+            throw new MojoExecutionException("Error reading file " + file + " Reason: " + e, e);
+        }
+    }
+
+    private boolean updateDataFormats(File file, String changed) throws MojoExecutionException {
+        if (!file.exists()) {
+            return false;
+        }
+
+        try {
+            String text = loadText(new FileInputStream(file));
+
+            String existing = StringHelper.between(text, "// dataformats: START", "// dataformats: END");
+            if (existing != null) {
+                // remove leading line breaks etc
+                existing = existing.trim();
+                changed = changed.trim();
+                if (existing.equals(changed)) {
+                    return false;
+                } else {
+                    String before = StringHelper.before(text, "// dataformats: START");
+                    String after = StringHelper.after(text, "// dataformats: END");
+                    text = before + "// dataformats: START\n" + changed + "\n// dataformats: END" + after;
+                    writeText(file, text);
+                    return true;
+                }
+            } else {
+                getLog().warn("Cannot find markers in file " + file);
+                getLog().warn("Add the following markers");
+                getLog().warn("\t// dataformats: START");
+                getLog().warn("\t// dataformats: END");
+                return false;
+            }
+        } catch (Exception e) {
+            throw new MojoExecutionException("Error reading file " + file + " Reason: " + e, e);
+        }
+    }
+
+    private boolean updateLanguages(File file, String changed) throws MojoExecutionException {
+        if (!file.exists()) {
+            return false;
+        }
+
+        try {
+            String text = loadText(new FileInputStream(file));
+
+            String existing = StringHelper.between(text, "// languages: START", "// languages: END");
+            if (existing != null) {
+                // remove leading line breaks etc
+                existing = existing.trim();
+                changed = changed.trim();
+                if (existing.equals(changed)) {
+                    return false;
+                } else {
+                    String before = StringHelper.before(text, "// languages: START");
+                    String after = StringHelper.after(text, "// languages: END");
+                    text = before + "// languages: START\n" + changed + "\n// languages: END" + after;
+                    writeText(file, text);
+                    return true;
+                }
+            } else {
+                getLog().warn("Cannot find markers in file " + file);
+                getLog().warn("Add the following markers");
+                getLog().warn("\t// languages: START");
+                getLog().warn("\t// languages: END");
+                return false;
+            }
+        } catch (Exception e) {
+            throw new MojoExecutionException("Error reading file " + file + " Reason: " + e, e);
+        }
+    }
+
+    private static class ComponentComparator implements Comparator<ComponentModel> {
+
+        @Override
+        public int compare(ComponentModel o1, ComponentModel o2) {
+            // lets sort by title
+            return o1.getTitle().compareToIgnoreCase(o2.getTitle());
+        }
+    }
+
+    private static class OtherComparator implements Comparator<OtherModel> {
+
+        @Override
+        public int compare(OtherModel o1, OtherModel o2) {
+            // lets sort by title
+            return o1.getTitle().compareToIgnoreCase(o2.getTitle());
+        }
+    }
+
+    private static class DataFormatComparator implements Comparator<DataFormatModel> {
+
+        @Override
+        public int compare(DataFormatModel o1, DataFormatModel o2) {
+            // lets sort by title
+            return o1.getTitle().compareToIgnoreCase(o2.getTitle());
+        }
+    }
+
+    private static class LanguageComparator implements Comparator<LanguageModel> {
+
+        @Override
+        public int compare(LanguageModel o1, LanguageModel o2) {
+            // lets sort by title
+            return o1.getTitle().compareToIgnoreCase(o2.getTitle());
+        }
+    }
+
+    private ComponentModel generateComponentModel(String json) {
+        List<Map<String, String>> rows = JSonSchemaHelper.parseJsonSchema("component", json, false);
+
+        ComponentModel component = new ComponentModel();
+        component.setScheme(JSonSchemaHelper.getSafeValue("scheme", rows));
+        component.setSyntax(JSonSchemaHelper.getSafeValue("syntax", rows));
+        component.setAlternativeSyntax(JSonSchemaHelper.getSafeValue("alternativeSyntax", rows));
+        component.setAlternativeSchemes(JSonSchemaHelper.getSafeValue("alternativeSchemes", rows));
+        component.setTitle(JSonSchemaHelper.getSafeValue("title", rows));
+        component.setDescription(JSonSchemaHelper.getSafeValue("description", rows));
+        component.setFirstVersion(JSonSchemaHelper.getSafeValue("firstVersion", rows));
+        component.setLabel(JSonSchemaHelper.getSafeValue("label", rows));
+        component.setDeprecated(JSonSchemaHelper.getSafeValue("deprecated", rows));
+        component.setDeprecationNote(JSonSchemaHelper.getSafeValue("deprecationNote", rows));
+        component.setConsumerOnly(JSonSchemaHelper.getSafeValue("consumerOnly", rows));
+        component.setProducerOnly(JSonSchemaHelper.getSafeValue("producerOnly", rows));
+        component.setJavaType(JSonSchemaHelper.getSafeValue("javaType", rows));
+        component.setGroupId(JSonSchemaHelper.getSafeValue("groupId", rows));
+        component.setArtifactId(JSonSchemaHelper.getSafeValue("artifactId", rows));
+        component.setVersion(JSonSchemaHelper.getSafeValue("version", rows));
+
+        return component;
+    }
+
+    private OtherModel generateOtherModel(String json) {
+        List<Map<String, String>> rows = JSonSchemaHelper.parseJsonSchema("other", json, false);
+
+        OtherModel other = new OtherModel();
+        other.setName(JSonSchemaHelper.getSafeValue("name", rows));
+        other.setTitle(JSonSchemaHelper.getSafeValue("title", rows));
+        other.setDescription(JSonSchemaHelper.getSafeValue("description", rows));
+        other.setFirstVersion(JSonSchemaHelper.getSafeValue("firstVersion", rows));
+        other.setLabel(JSonSchemaHelper.getSafeValue("label", rows));
+        other.setDeprecated(JSonSchemaHelper.getSafeValue("deprecated", rows));
+        other.setDeprecationNote(JSonSchemaHelper.getSafeValue("deprecationNote", rows));
+        other.setGroupId(JSonSchemaHelper.getSafeValue("groupId", rows));
+        other.setArtifactId(JSonSchemaHelper.getSafeValue("artifactId", rows));
+        other.setVersion(JSonSchemaHelper.getSafeValue("version", rows));
+
+        return other;
+    }
+
+    private DataFormatModel generateDataFormatModel(String json) {
+        List<Map<String, String>> rows = JSonSchemaHelper.parseJsonSchema("dataformat", json, false);
+
+        DataFormatModel dataFormat = new DataFormatModel();
+        dataFormat.setName(JSonSchemaHelper.getSafeValue("name", rows));
+        dataFormat.setTitle(JSonSchemaHelper.getSafeValue("title", rows));
+        dataFormat.setModelName(JSonSchemaHelper.getSafeValue("modelName", rows));
+        dataFormat.setDescription(JSonSchemaHelper.getSafeValue("description", rows));
+        dataFormat.setFirstVersion(JSonSchemaHelper.getSafeValue("firstVersion", rows));
+        dataFormat.setLabel(JSonSchemaHelper.getSafeValue("label", rows));
+        dataFormat.setDeprecated(JSonSchemaHelper.getSafeValue("deprecated", rows));
+        dataFormat.setDeprecationNote(JSonSchemaHelper.getSafeValue("deprecationNote", rows));
+        dataFormat.setJavaType(JSonSchemaHelper.getSafeValue("javaType", rows));
+        dataFormat.setGroupId(JSonSchemaHelper.getSafeValue("groupId", rows));
+        dataFormat.setArtifactId(JSonSchemaHelper.getSafeValue("artifactId", rows));
+        dataFormat.setVersion(JSonSchemaHelper.getSafeValue("version", rows));
+
+        return dataFormat;
+    }
+
+    private LanguageModel generateLanguageModel(String json) {
+        List<Map<String, String>> rows = JSonSchemaHelper.parseJsonSchema("language", json, false);
+
+        LanguageModel language = new LanguageModel();
+        language.setTitle(JSonSchemaHelper.getSafeValue("title", rows));
+        language.setName(JSonSchemaHelper.getSafeValue("name", rows));
+        language.setModelName(JSonSchemaHelper.getSafeValue("modelName", rows));
+        language.setDescription(JSonSchemaHelper.getSafeValue("description", rows));
+        language.setFirstVersion(JSonSchemaHelper.getSafeValue("firstVersion", rows));
+        language.setLabel(JSonSchemaHelper.getSafeValue("label", rows));
+        language.setDeprecated(JSonSchemaHelper.getSafeValue("deprecated", rows));
+        language.setDeprecationNote(JSonSchemaHelper.getSafeValue("deprecationNote", rows));
+        language.setJavaType(JSonSchemaHelper.getSafeValue("javaType", rows));
+        language.setGroupId(JSonSchemaHelper.getSafeValue("groupId", rows));
+        language.setArtifactId(JSonSchemaHelper.getSafeValue("artifactId", rows));
+        language.setVersion(JSonSchemaHelper.getSafeValue("version", rows));
+
+        return language;
+    }
+
+}
diff --git a/tooling/maven/package-maven-plugin/src/main/java/org/apache/camel/quarkus/maven/StringHelper.java b/tooling/maven/package-maven-plugin/src/main/java/org/apache/camel/quarkus/maven/StringHelper.java
new file mode 100644
index 0000000..02274bf
--- /dev/null
+++ b/tooling/maven/package-maven-plugin/src/main/java/org/apache/camel/quarkus/maven/StringHelper.java
@@ -0,0 +1,200 @@
+/*
+ * 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.quarkus.maven;
+
+import java.util.Collection;
+
+import com.google.common.base.CaseFormat;
+
+public final class StringHelper {
+
+    private StringHelper() {
+        // Utils Class
+    }
+
+    public static boolean isEmpty(String s) {
+        return s == null || s.trim().isEmpty();
+    }
+
+    public static String after(String text, String after) {
+        if (!text.contains(after)) {
+            return null;
+        }
+        return text.substring(text.indexOf(after) + after.length());
+    }
+
+    public static String before(String text, String before) {
+        if (!text.contains(before)) {
+            return null;
+        }
+        return text.substring(0, text.indexOf(before));
+    }
+
+    public static String between(String text, String after, String before) {
+        text = after(text, after);
+        if (text == null) {
+            return null;
+        }
+        return before(text, before);
+    }
+
+    public static String indentCollection(String indent, Collection<String> list) {
+        StringBuilder sb = new StringBuilder();
+        for (String text : list) {
+            sb.append(indent).append(text);
+        }
+        return sb.toString();
+    }
+
+    /**
+     * Converts the value to use title style instead of dash cased
+     */
+    public static String camelDashToTitle(String value) {
+        StringBuilder sb = new StringBuilder(value.length());
+        boolean dash = false;
+
+        for (char c : value.toCharArray()) {
+            if ('-' == c) {
+                dash = true;
+                continue;
+            }
+
+            if (dash) {
+                sb.append(' ');
+                sb.append(Character.toUpperCase(c));
+            } else {
+                // upper case first
+                if (sb.length() == 0) {
+                    sb.append(Character.toUpperCase(c));
+                } else {
+                    sb.append(c);
+                }
+            }
+            dash = false;
+        }
+        return sb.toString();
+    }
+
+    public static String cutLastZeroDigit(String version) {
+        String answer = version;
+        // cut last digit so its not 2.18.0 but 2.18
+        String[] parts = version.split("\\.");
+        if (parts.length == 3 && parts[2].equals("0")) {
+            answer = parts[0] + "." + parts[1];
+        }
+        return answer;
+    }
+
+    /**
+     * To wrap long camel cased texts by words.
+     *
+     * @param option  the option which is camel cased.
+     * @param watermark a watermark to denote the size to cut after
+     * @param newLine the new line to use when breaking into a new line
+     */
+    public static String wrapCamelCaseWords(String option, int watermark, String newLine) {
+        String text = CaseFormat.UPPER_CAMEL.to(CaseFormat.LOWER_HYPHEN, option);
+        text = text.replace('-', ' ');
+        text = wrapWords(text, "\n", watermark, false);
+        text = text.replace(' ', '-');
+        text = CaseFormat.LOWER_HYPHEN.to(CaseFormat.UPPER_CAMEL, text);
+
+        // upper case first char on each line
+        String[] lines = text.split("\n");
+        StringBuilder sb = new StringBuilder();
+        for (int i = 0; i < lines.length; i++) {
+            String line = lines[i];
+            line = Character.toUpperCase(line.charAt(0)) + line.substring(1);
+            sb.append(line);
+            if (i < lines.length - 1) {
+                sb.append(newLine);
+            }
+        }
+        return sb.toString();
+    }
+
+    /**
+     * To wrap a big line by words.
+     *
+     * @param line the big line
+     * @param newLine the new line to use when breaking into a new line
+     * @param watermark a watermark to denote the size to cut after
+     * @param wrapLongWords whether to wrap long words
+     */
+    public static String wrapWords(String line, String newLine, int watermark, boolean wrapLongWords) {
+        if (line == null) {
+            return null;
+        } else {
+            if (newLine == null) {
+                newLine = System.lineSeparator();
+            }
+
+            if (watermark < 1) {
+                watermark = 1;
+            }
+
+            int inputLineLength = line.length();
+            int offset = 0;
+            StringBuilder sb = new StringBuilder(inputLineLength + 32);
+
+            while (inputLineLength - offset > watermark) {
+                if (line.charAt(offset) == 32) {
+                    ++offset;
+                } else {
+                    int spaceToWrapAt = line.lastIndexOf(32, watermark + offset);
+                    if (spaceToWrapAt >= offset) {
+                        sb.append(line.substring(offset, spaceToWrapAt));
+                        sb.append(newLine);
+                        offset = spaceToWrapAt + 1;
+                    } else if (wrapLongWords) {
+                        sb.append(line.substring(offset, watermark + offset));
+                        sb.append(newLine);
+                        offset += watermark;
+                    } else {
+                        spaceToWrapAt = line.indexOf(32, watermark + offset);
+                        if (spaceToWrapAt >= 0) {
+                            sb.append(line.substring(offset, spaceToWrapAt));
+                            sb.append(newLine);
+                            offset = spaceToWrapAt + 1;
+                        } else {
+                            sb.append(line.substring(offset));
+                            offset = inputLineLength;
+                        }
+                    }
+                }
+            }
+
+            sb.append(line.substring(offset));
+            return sb.toString();
+        }
+    }
+
+    /**
+     * Returns the base class name, i.e. without package and generic related
+     * information.
+     * 
+     * @param className The class name which base class is to be computed.
+     * @return the base class name, i.e. without package and generic related
+     *         information.
+     */
+    public static String getClassShortName(String className) {
+        if (className != null) {
+            return className.replaceAll("<.*>", "").replaceAll(".*[.]([^.]+)", "$1");
+        }
+        return className;
+    }
+}
diff --git a/tooling/maven/package-maven-plugin/src/main/java/org/apache/camel/quarkus/maven/model/ComponentModel.java b/tooling/maven/package-maven-plugin/src/main/java/org/apache/camel/quarkus/maven/model/ComponentModel.java
new file mode 100644
index 0000000..dcf8831
--- /dev/null
+++ b/tooling/maven/package-maven-plugin/src/main/java/org/apache/camel/quarkus/maven/model/ComponentModel.java
@@ -0,0 +1,190 @@
+/*
+ * 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.quarkus.maven.model;
+
+import org.apache.camel.quarkus.maven.StringHelper;
+
+import static org.apache.camel.quarkus.maven.StringHelper.cutLastZeroDigit;
+
+public class ComponentModel {
+
+    private String kind;
+    private String scheme;
+    private String syntax;
+    private String alternativeSyntax;
+    private String alternativeSchemes;
+    private String title;
+    private String description;
+    private String firstVersion;
+    private String label;
+    private String deprecated;
+    private String deprecationNote;
+    private String consumerOnly;
+    private String producerOnly;
+    private String javaType;
+    private String groupId;
+    private String artifactId;
+    private String version;
+
+    public String getKind() {
+        return kind;
+    }
+
+    public void setKind(String kind) {
+        this.kind = kind;
+    }
+
+    public String getScheme() {
+        return scheme;
+    }
+
+    public void setScheme(String scheme) {
+        this.scheme = scheme;
+    }
+
+    public String getSyntax() {
+        return syntax;
+    }
+
+    public void setSyntax(String syntax) {
+        this.syntax = syntax;
+    }
+
+    public String getAlternativeSyntax() {
+        return alternativeSyntax;
+    }
+
+    public void setAlternativeSyntax(String alternativeSyntax) {
+        this.alternativeSyntax = alternativeSyntax;
+    }
+
+    public String getAlternativeSchemes() {
+        return alternativeSchemes;
+    }
+
+    public void setAlternativeSchemes(String alternativeSchemes) {
+        this.alternativeSchemes = alternativeSchemes;
+    }
+
+    public String getTitle() {
+        return title;
+    }
+
+    public void setTitle(String title) {
+        this.title = title;
+    }
+
+    public String getDescription() {
+        return description;
+    }
+
+    public void setDescription(String description) {
+        this.description = description;
+    }
+
+    public String getFirstVersion() {
+        return firstVersion;
+    }
+
+    public void setFirstVersion(String firstVersion) {
+        this.firstVersion = firstVersion;
+    }
+
+    public String getLabel() {
+        return label;
+    }
+
+    public void setLabel(String label) {
+        this.label = label;
+    }
+
+    public String getDeprecated() {
+        return deprecated;
+    }
+
+    public void setDeprecated(String deprecated) {
+        this.deprecated = deprecated;
+    }
+
+    public String getDeprecationNote() {
+        return deprecationNote;
+    }
+
+    public void setDeprecationNote(String deprecationNote) {
+        this.deprecationNote = deprecationNote;
+    }
+
+    public String getConsumerOnly() {
+        return consumerOnly;
+    }
+
+    public void setConsumerOnly(String consumerOnly) {
+        this.consumerOnly = consumerOnly;
+    }
+
+    public String getProducerOnly() {
+        return producerOnly;
+    }
+
+    public void setProducerOnly(String producerOnly) {
+        this.producerOnly = producerOnly;
+    }
+
+    public String getJavaType() {
+        return javaType;
+    }
+
+    public void setJavaType(String javaType) {
+        this.javaType = javaType;
+    }
+
+    public String getGroupId() {
+        return groupId;
+    }
+
+    public void setGroupId(String groupId) {
+        this.groupId = groupId;
+    }
+
+    public String getArtifactId() {
+        return artifactId;
+    }
+
+    public void setArtifactId(String artifactId) {
+        this.artifactId = artifactId;
+    }
+
+    public String getVersion() {
+        return version;
+    }
+
+    public void setVersion(String version) {
+        this.version = version;
+    }
+
+    public String getShortJavaType() {
+        return StringHelper.getClassShortName(javaType);
+    }
+
+    public String getDocLink() {
+        return artifactId + "/src/main/docs";
+    }
+
+    public String getFirstVersionShort() {
+        return cutLastZeroDigit(firstVersion);
+    }
+}
diff --git a/tooling/maven/package-maven-plugin/src/main/java/org/apache/camel/quarkus/maven/model/DataFormatModel.java b/tooling/maven/package-maven-plugin/src/main/java/org/apache/camel/quarkus/maven/model/DataFormatModel.java
new file mode 100644
index 0000000..4b3f517
--- /dev/null
+++ b/tooling/maven/package-maven-plugin/src/main/java/org/apache/camel/quarkus/maven/model/DataFormatModel.java
@@ -0,0 +1,155 @@
+/*
+ * 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.quarkus.maven.model;
+
+import org.apache.camel.quarkus.maven.StringHelper;
+
+import static org.apache.camel.quarkus.maven.StringHelper.cutLastZeroDigit;
+
+public class DataFormatModel {
+
+    private String kind;
+    private String name;
+    private String modelName;
+    private String title;
+    private String description;
+    private String firstVersion;
+    private String label;
+    private String deprecated;
+    private String deprecationNote;
+    private String javaType;
+    private String groupId;
+    private String artifactId;
+    private String version;
+
+    public String getKind() {
+        return kind;
+    }
+
+    public void setKind(String kind) {
+        this.kind = kind;
+    }
+
+    public String getName() {
+        return name;
+    }
+
+    public void setName(String name) {
+        this.name = name;
+    }
+
+    public String getModelName() {
+        return modelName;
+    }
+
+    public void setModelName(String modelName) {
+        this.modelName = modelName;
+    }
+
+    public String getTitle() {
+        return title;
+    }
+
+    public void setTitle(String title) {
+        this.title = title;
+    }
+
+    public String getDescription() {
+        return description;
+    }
+
+    public void setDescription(String description) {
+        this.description = description;
+    }
+
+    public String getFirstVersion() {
+        return firstVersion;
+    }
+
+    public void setFirstVersion(String firstVersion) {
+        this.firstVersion = firstVersion;
+    }
+
+    public String getLabel() {
+        return label;
+    }
+
+    public void setLabel(String label) {
+        this.label = label;
+    }
+
+    public String getDeprecated() {
+        return deprecated;
+    }
+
+    public void setDeprecated(String deprecated) {
+        this.deprecated = deprecated;
+    }
+
+    public String getDeprecationNote() {
+        return deprecationNote;
+    }
+
+    public void setDeprecationNote(String deprecationNote) {
+        this.deprecationNote = deprecationNote;
+    }
+
+    public String getJavaType() {
+        return javaType;
+    }
+
+    public void setJavaType(String javaType) {
+        this.javaType = javaType;
+    }
+
+    public String getGroupId() {
+        return groupId;
+    }
+
+    public void setGroupId(String groupId) {
+        this.groupId = groupId;
+    }
+
+    public String getArtifactId() {
+        return artifactId;
+    }
+
+    public void setArtifactId(String artifactId) {
+        this.artifactId = artifactId;
+    }
+
+    public String getVersion() {
+        return version;
+    }
+
+    public void setVersion(String version) {
+        this.version = version;
+    }
+
+    public String getShortJavaType() {
+        return StringHelper.getClassShortName(javaType);
+    }
+
+    public String getDocLink() {
+        return artifactId + "/src/main/docs";
+    }
+
+    public String getFirstVersionShort() {
+        return cutLastZeroDigit(firstVersion);
+    }
+
+}
diff --git a/tooling/maven/package-maven-plugin/src/main/java/org/apache/camel/quarkus/maven/model/LanguageModel.java b/tooling/maven/package-maven-plugin/src/main/java/org/apache/camel/quarkus/maven/model/LanguageModel.java
new file mode 100644
index 0000000..6813056
--- /dev/null
+++ b/tooling/maven/package-maven-plugin/src/main/java/org/apache/camel/quarkus/maven/model/LanguageModel.java
@@ -0,0 +1,155 @@
+/*
+ * 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.quarkus.maven.model;
+
+import org.apache.camel.quarkus.maven.StringHelper;
+
+import static org.apache.camel.quarkus.maven.StringHelper.cutLastZeroDigit;
+
+public class LanguageModel {
+
+    private String kind;
+    private String name;
+    private String modelName;
+    private String title;
+    private String description;
+    private String firstVersion;
+    private String label;
+    private String deprecated;
+    private String deprecationNote;
+    private String javaType;
+    private String groupId;
+    private String artifactId;
+    private String version;
+
+    public String getKind() {
+        return kind;
+    }
+
+    public void setKind(String kind) {
+        this.kind = kind;
+    }
+
+    public String getName() {
+        return name;
+    }
+
+    public void setName(String name) {
+        this.name = name;
+    }
+
+    public String getModelName() {
+        return modelName;
+    }
+
+    public void setModelName(String modelName) {
+        this.modelName = modelName;
+    }
+
+    public String getTitle() {
+        return title;
+    }
+
+    public void setTitle(String title) {
+        this.title = title;
+    }
+
+    public String getDescription() {
+        return description;
+    }
+
+    public void setDescription(String description) {
+        this.description = description;
+    }
+
+    public String getFirstVersion() {
+        return firstVersion;
+    }
+
+    public void setFirstVersion(String firstVersion) {
+        this.firstVersion = firstVersion;
+    }
+
+    public String getLabel() {
+        return label;
+    }
+
+    public void setLabel(String label) {
+        this.label = label;
+    }
+
+    public String getDeprecated() {
+        return deprecated;
+    }
+
+    public void setDeprecated(String deprecated) {
+        this.deprecated = deprecated;
+    }
+
+    public String getDeprecationNote() {
+        return deprecationNote;
+    }
+
+    public void setDeprecationNote(String deprecationNote) {
+        this.deprecationNote = deprecationNote;
+    }
+
+    public String getJavaType() {
+        return javaType;
+    }
+
+    public void setJavaType(String javaType) {
+        this.javaType = javaType;
+    }
+
+    public String getGroupId() {
+        return groupId;
+    }
+
+    public void setGroupId(String groupId) {
+        this.groupId = groupId;
+    }
+
+    public String getArtifactId() {
+        return artifactId;
+    }
+
+    public void setArtifactId(String artifactId) {
+        this.artifactId = artifactId;
+    }
+
+    public String getVersion() {
+        return version;
+    }
+
+    public void setVersion(String version) {
+        this.version = version;
+    }
+
+    public String getShortJavaType() {
+        return StringHelper.getClassShortName(javaType);
+    }
+
+    public String getDocLink() {
+        return artifactId + "/src/main/docs";
+    }
+
+    public String getFirstVersionShort() {
+        return cutLastZeroDigit(firstVersion);
+    }
+
+}
diff --git a/tooling/maven/package-maven-plugin/src/main/java/org/apache/camel/quarkus/maven/model/OtherModel.java b/tooling/maven/package-maven-plugin/src/main/java/org/apache/camel/quarkus/maven/model/OtherModel.java
new file mode 100644
index 0000000..05b9788
--- /dev/null
+++ b/tooling/maven/package-maven-plugin/src/main/java/org/apache/camel/quarkus/maven/model/OtherModel.java
@@ -0,0 +1,145 @@
+/*
+ * 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.quarkus.maven.model;
+
+import org.apache.camel.quarkus.maven.StringHelper;
+
+import static org.apache.camel.quarkus.maven.StringHelper.cutLastZeroDigit;
+
+public class OtherModel {
+
+    private String kind;
+    private String name;
+    private String title;
+    private String description;
+    private String firstVersion;
+    private String label;
+    private String deprecated;
+    private String deprecationNote;
+    private String groupId;
+    private String artifactId;
+    private String version;
+    private String javaType;
+
+    public String getJavaType() {
+        return javaType;
+    }
+
+    public void setJavaType(String javaType) {
+        this.javaType = javaType;
+    }
+
+    public String getKind() {
+        return kind;
+    }
+
+    public void setKind(String kind) {
+        this.kind = kind;
+    }
+
+    public String getName() {
+        return name;
+    }
+
+    public void setName(String name) {
+        this.name = name;
+    }
+
+    public String getTitle() {
+        return title;
+    }
+
+    public void setTitle(String title) {
+        this.title = title;
+    }
+
+    public String getDescription() {
+        return description;
+    }
+
+    public void setDescription(String description) {
+        this.description = description;
+    }
+
+    public String getFirstVersion() {
+        return firstVersion;
+    }
+
+    public void setFirstVersion(String firstVersion) {
+        this.firstVersion = firstVersion;
+    }
+
+    public String getLabel() {
+        return label;
+    }
+
+    public void setLabel(String label) {
+        this.label = label;
+    }
+
+    public String getDeprecated() {
+        return deprecated;
+    }
+
+    public void setDeprecated(String deprecated) {
+        this.deprecated = deprecated;
+    }
+
+    public String getDeprecationNote() {
+        return deprecationNote;
+    }
+
+    public void setDeprecationNote(String deprecationNote) {
+        this.deprecationNote = deprecationNote;
+    }
+
+    public String getGroupId() {
+        return groupId;
+    }
+
+    public void setGroupId(String groupId) {
+        this.groupId = groupId;
+    }
+
+    public String getArtifactId() {
+        return artifactId;
+    }
+
+    public void setArtifactId(String artifactId) {
+        this.artifactId = artifactId;
+    }
+
+    public String getVersion() {
+        return version;
+    }
+
+    public void setVersion(String version) {
+        this.version = version;
+    }
+
+    public String getDocLink() {
+        return artifactId + "/src/main/docs";
+    }
+
+    public String getFirstVersionShort() {
+        return cutLastZeroDigit(firstVersion);
+    }
+
+    public String getShortJavaType() {
+        return StringHelper.getClassShortName(javaType);
+    }
+}
diff --git a/tooling/maven/package-maven-plugin/src/main/resources/readme-components.mvel b/tooling/maven/package-maven-plugin/src/main/resources/readme-components.mvel
new file mode 100644
index 0000000..16d3573
--- /dev/null
+++ b/tooling/maven/package-maven-plugin/src/main/resources/readme-components.mvel
@@ -0,0 +1,14 @@
+@if{!components.isEmpty()}
+
+Number of Components: @{components.size} in @{numberOfArtifacts} JAR artifacts (@{numberOfDeprecated} deprecated)
+
+[width="100%",cols="4,1,5",options="header"]
+|===
+| Component | Available From | Description
+@foreach{row : components}
+| link:@{row.docLink}/${row.scheme}-component.adoc[@{row.title}] (@{row.artifactId}) +
+`@{row.syntax}` | @{row.firstVersionShort} | @if{row.deprecated == "true"}*deprecated* @end{}@{util.escape(row.description)}
+@end{}
+|===
+
+@end{}
\ No newline at end of file
diff --git a/tooling/maven/package-maven-plugin/src/main/resources/readme-dataformats.mvel b/tooling/maven/package-maven-plugin/src/main/resources/readme-dataformats.mvel
new file mode 100644
index 0000000..72d12c1
--- /dev/null
+++ b/tooling/maven/package-maven-plugin/src/main/resources/readme-dataformats.mvel
@@ -0,0 +1,12 @@
+@if{!dataformats.isEmpty()}
+
+Number of Data Formats: @{dataformats.size} in @{numberOfArtifacts} JAR artifacts (@{numberOfDeprecated} deprecated)
+
+[width="100%",cols="4,1,5",options="header"]
+|===
+| Data Format | Available From | Description
+@foreach{row : dataformats}
+| link:@{row.docLink}/${row.name}-dataformat.adoc[@{row.title}] (@{row.artifactId}) | @{row.firstVersionShort} | @if{row.deprecated == "true"}*deprecated* @end{}@{util.escape(row.description)}
+@end{}|===
+
+@end{}
\ No newline at end of file
diff --git a/tooling/maven/package-maven-plugin/src/main/resources/readme-languages.mvel b/tooling/maven/package-maven-plugin/src/main/resources/readme-languages.mvel
new file mode 100644
index 0000000..ba0e1ca
--- /dev/null
+++ b/tooling/maven/package-maven-plugin/src/main/resources/readme-languages.mvel
@@ -0,0 +1,12 @@
+@if{!languages.isEmpty()}
+
+Number of Languages: @{languages.size} in @{numberOfArtifacts} JAR artifacts (@{numberOfDeprecated} deprecated)
+
+[width="100%",cols="4,1,5",options="header"]
+|===
+| Language | Available From | Description
+@foreach{row : languages}
+| link:@{row.docLink}/${row.name}-language.adoc[@{row.title}] (@{row.artifactId}) | @{row.firstVersionShort} | @if{row.deprecated == "true"}*deprecated* @end{}@{util.escape(row.description)}
+@end{}|===
+
+@end{}
\ No newline at end of file
diff --git a/tooling/maven/package-maven-plugin/src/main/resources/readme-others.mvel b/tooling/maven/package-maven-plugin/src/main/resources/readme-others.mvel
new file mode 100644
index 0000000..cbd06ce
--- /dev/null
+++ b/tooling/maven/package-maven-plugin/src/main/resources/readme-others.mvel
@@ -0,0 +1,12 @@
+@if{!others.isEmpty()}
+
+Number of Miscellaneous Components: @{others.size} in @{numberOfArtifacts} JAR artifacts (@{numberOfDeprecated} deprecated)
+
+[width="100%",cols="4,1,5",options="header"]
+|===
+| Component | Available From | Description
+@foreach{row : others}
+| link:@{row.docLink}/${row.name}.adoc[@{row.title}] (@{row.artifactId}) | @{row.firstVersionShort} | @if{row.deprecated == "true"}*deprecated* @end{}@{util.escape(row.description)}
+@end{}|===
+
+@end{}
\ No newline at end of file