You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@camel.apache.org by pp...@apache.org on 2021/09/03 07:50:12 UTC

[camel-quarkus] branch main updated (510a4b9 -> 83dc1e2)

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

ppalaga pushed a change to branch main
in repository https://gitbox.apache.org/repos/asf/camel-quarkus.git.


    from 510a4b9  fix attribute syntax
     new 68be716  Allow extending an existing Catalog
     new 83dc1e2  Upgrade to cq-maven-plugin 1.0.0 - Ignore unlinked modules when listing extensions

The 2 revisions listed above as "new" are entirely new to this
repository and will be described in separate emails.  The revisions
listed as "add" were already present in the repository and have only
been added to this reference.


Summary of changes:
 catalog/pom.xml                                    |   7 +-
 pom.xml                                            |  37 +---
 tooling/maven-plugin/pom.xml                       |  10 +
 .../quarkus/maven/AbstractDocGeneratorMojo.java    |  16 +-
 .../quarkus/maven/AbstractExtensionListMojo.java   |  97 ++++++++++
 .../quarkus/maven/CheckExtensionPagesMojo.java     |  51 ++----
 .../org/apache/camel/quarkus/maven/CqCatalog.java  | 142 ++++++++++----
 .../org/apache/camel/quarkus/maven/CqUtils.java    |  45 ++++-
 .../camel/quarkus/maven/ExtensionModule.java       |  66 +++++++
 .../org/apache/camel/quarkus/maven/PatternSet.java |  39 ++--
 .../quarkus/maven/PrepareCatalogQuarkusMojo.java   | 203 ++++++++-------------
 11 files changed, 443 insertions(+), 270 deletions(-)
 create mode 100644 tooling/maven-plugin/src/main/java/org/apache/camel/quarkus/maven/AbstractExtensionListMojo.java
 create mode 100644 tooling/maven-plugin/src/main/java/org/apache/camel/quarkus/maven/ExtensionModule.java
 copy integration-tests-support/custom-routes-collector/runtime/src/main/java/org/apache/camel/quarkus/main/runtime/support/CustomRoutesCollector.java => tooling/maven-plugin/src/main/java/org/apache/camel/quarkus/maven/PatternSet.java (55%)

[camel-quarkus] 02/02: Upgrade to cq-maven-plugin 1.0.0 - Ignore unlinked modules when listing extensions

Posted by pp...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

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

commit 83dc1e2147c1a709d2efedc403e8d5c0bbc33397
Author: Peter Palaga <pp...@redhat.com>
AuthorDate: Thu Sep 2 21:48:28 2021 +0200

    Upgrade to cq-maven-plugin 1.0.0 - Ignore unlinked modules when listing
    extensions
---
 catalog/pom.xml |  7 ++-----
 pom.xml         | 34 ++--------------------------------
 2 files changed, 4 insertions(+), 37 deletions(-)

diff --git a/catalog/pom.xml b/catalog/pom.xml
index d888cef..8bd766d 100644
--- a/catalog/pom.xml
+++ b/catalog/pom.xml
@@ -4461,13 +4461,10 @@
                         </goals>
                         <phase>process-resources</phase>
                         <configuration>
-                            <extensionDirectories>
-                                <extensionDirectory>${maven.multiModuleProjectDirectory}/extensions-core</extensionDirectory>
-                                <extensionDirectory>${maven.multiModuleProjectDirectory}/extensions</extensionDirectory>
-                                <extensionDirectory>${maven.multiModuleProjectDirectory}/extensions-jvm</extensionDirectory>
-                            </extensionDirectories>
                             <skipArtifactIdBases>
                                 <skipArtifactIdBase>http-common</skipArtifactIdBase>
+                                <skipArtifactIdBase>support-.*</skipArtifactIdBase>
+                                <skipArtifactIdBase>integration-tests?-support-.*</skipArtifactIdBase>
                             </skipArtifactIdBases>
                         </configuration>
                     </execution>
diff --git a/pom.xml b/pom.xml
index 1c0f46f..42bfd29 100644
--- a/pom.xml
+++ b/pom.xml
@@ -145,7 +145,7 @@
         <maven-utils.version>0.1.0</maven-utils.version>
 
         <!-- Maven plugin versions (keep sorted alphabetically) -->
-        <cq-plugin.version>0.40.0</cq-plugin.version>
+        <cq-plugin.version>1.0.0</cq-plugin.version>
         <build-helper-maven-plugin.version>3.1.0</build-helper-maven-plugin.version>
         <exec-maven-plugin.version>3.0.0</exec-maven-plugin.version>
         <formatter-maven-plugin.version>2.11.0</formatter-maven-plugin.version>
@@ -394,17 +394,9 @@
                     <version>${cq-plugin.version}</version>
                     <configuration>
                         <simpleElementWhitespace>SPACE</simpleElementWhitespace>
-                        <extensionDirectories>
-                            <extensionDirectory>${project.basedir}/extensions-core</extensionDirectory>
-                            <extensionDirectory>${project.basedir}/extensions</extensionDirectory>
-                            <extensionDirectory>${project.basedir}/extensions-jvm</extensionDirectory>
-                        </extensionDirectories>
                         <skipArtifactIdBases>
                             <skipArtifactIdBase>http-common</skipArtifactIdBase>
-                            <skipArtifactIdBase>integration-test-support-custom-log-component</skipArtifactIdBase>
-                            <skipArtifactIdBase>integration-test-support-custom-main-listener</skipArtifactIdBase>
-                            <skipArtifactIdBase>integration-test-support-custom-routes-collector</skipArtifactIdBase>
-                            <skipArtifactIdBase>integration-test-support-custom-type-converter</skipArtifactIdBase>
+                            <skipArtifactIdBase>integration-tests?-support-.*</skipArtifactIdBase>
                         </skipArtifactIdBases>
                     </configuration>
                     <dependencies>
@@ -773,28 +765,6 @@
                                             </includes>
                                         </updateVirtualDependenciesAllExtension>
                                     </updateVirtualDependenciesAllExtensions>
-                                    <extensionDirs>
-                                        <extensionDir>
-                                            <path>extensions</path>
-                                            <artifactIdPrefix>camel-quarkus-</artifactIdPrefix>
-                                        </extensionDir>
-                                        <extensionDir>
-                                            <path>extensions-core</path>
-                                            <artifactIdPrefix>camel-quarkus-</artifactIdPrefix>
-                                        </extensionDir>
-                                        <extensionDir>
-                                            <path>extensions-jvm</path>
-                                            <artifactIdPrefix>camel-quarkus-</artifactIdPrefix>
-                                        </extensionDir>
-                                        <extensionDir>
-                                            <path>extensions-support</path>
-                                            <artifactIdPrefix>camel-quarkus-support-</artifactIdPrefix>
-                                        </extensionDir>
-                                        <extensionDir>
-                                            <path>integration-tests-support</path>
-                                            <artifactIdPrefix>camel-quarkus-integration-test-support-</artifactIdPrefix>
-                                        </extensionDir>
-                                    </extensionDirs>
                                     <removeEmptyApplicationProperties>
                                         <directory>${basedir}</directory>
                                         <includes>

[camel-quarkus] 01/02: Allow extending an existing Catalog

Posted by pp...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

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

commit 68be716bd4777db6889b12dc570adff3494b6934
Author: Peter Palaga <pp...@redhat.com>
AuthorDate: Thu Sep 2 21:44:21 2021 +0200

    Allow extending an existing Catalog
---
 pom.xml                                            |   3 +
 tooling/maven-plugin/pom.xml                       |  10 +
 .../quarkus/maven/AbstractDocGeneratorMojo.java    |  16 +-
 .../quarkus/maven/AbstractExtensionListMojo.java   |  97 ++++++++++
 .../quarkus/maven/CheckExtensionPagesMojo.java     |  51 ++----
 .../org/apache/camel/quarkus/maven/CqCatalog.java  | 142 ++++++++++----
 .../org/apache/camel/quarkus/maven/CqUtils.java    |  45 ++++-
 .../camel/quarkus/maven/ExtensionModule.java       |  66 +++++++
 .../org/apache/camel/quarkus/maven/PatternSet.java |  48 +++++
 .../quarkus/maven/PrepareCatalogQuarkusMojo.java   | 203 ++++++++-------------
 10 files changed, 465 insertions(+), 216 deletions(-)

diff --git a/pom.xml b/pom.xml
index 95cef43..1c0f46f 100644
--- a/pom.xml
+++ b/pom.xml
@@ -141,6 +141,9 @@
         <wiremock.version>2.27.2</wiremock.version>
         <zt-exec.version>1.11</zt-exec.version>
 
+        <!-- Tooling dependency versions (keep sorted alphabetically) -->
+        <maven-utils.version>0.1.0</maven-utils.version>
+
         <!-- Maven plugin versions (keep sorted alphabetically) -->
         <cq-plugin.version>0.40.0</cq-plugin.version>
         <build-helper-maven-plugin.version>3.1.0</build-helper-maven-plugin.version>
diff --git a/tooling/maven-plugin/pom.xml b/tooling/maven-plugin/pom.xml
index a2b70f4..f650245 100644
--- a/tooling/maven-plugin/pom.xml
+++ b/tooling/maven-plugin/pom.xml
@@ -46,6 +46,11 @@
                 <artifactId>quarkus-extension-processor</artifactId>
                 <version>${quarkus.version}</version>
             </dependency>
+            <dependency>
+                <groupId>org.l2x6.maven-utils</groupId>
+                <artifactId>maven-utils</artifactId>
+                <version>${maven-utils.version}</version>
+            </dependency>
         </dependencies>
     </dependencyManagement>
     <dependencies>
@@ -88,6 +93,11 @@
         </dependency>
 
         <dependency>
+            <groupId>org.l2x6.maven-utils</groupId>
+            <artifactId>maven-utils</artifactId>
+        </dependency>
+
+        <dependency>
             <groupId>org.junit.jupiter</groupId>
             <artifactId>junit-jupiter-api</artifactId>
             <scope>test</scope>
diff --git a/tooling/maven-plugin/src/main/java/org/apache/camel/quarkus/maven/AbstractDocGeneratorMojo.java b/tooling/maven-plugin/src/main/java/org/apache/camel/quarkus/maven/AbstractDocGeneratorMojo.java
index 7407509..c54eb2e 100644
--- a/tooling/maven-plugin/src/main/java/org/apache/camel/quarkus/maven/AbstractDocGeneratorMojo.java
+++ b/tooling/maven-plugin/src/main/java/org/apache/camel/quarkus/maven/AbstractDocGeneratorMojo.java
@@ -24,13 +24,12 @@ import java.util.Map;
 import freemarker.template.Configuration;
 import freemarker.template.Template;
 import freemarker.template.TemplateException;
-import org.apache.maven.plugin.AbstractMojo;
 import org.apache.maven.plugins.annotations.Parameter;
 
 /**
  * Base for {@link CheckExtensionPagesMojo} and {@link UpdateExtensionDocPageMojo}.
  */
-abstract class AbstractDocGeneratorMojo extends AbstractMojo {
+abstract class AbstractDocGeneratorMojo extends AbstractExtensionListMojo {
     public static final String DEFAULT_TEMPLATES_URI_BASE = "classpath:/doc-templates";
     /**
      */
@@ -42,19 +41,6 @@ abstract class AbstractDocGeneratorMojo extends AbstractMojo {
     @Parameter(property = "camel-quarkus.basedir", defaultValue = "${project.basedir}")
     File baseDir;
 
-    /**
-     * Encoding to read and write files in the current source tree
-     */
-    @Parameter(defaultValue = "${project.build.sourceEncoding}", required = true, property = "camel-quarkus.encoding")
-    String encoding;
-    /**
-     * The root directory of the Camel Quarkus source tree.
-     *
-     * @since 1.0.0-M8
-     */
-    @Parameter(defaultValue = "${maven.multiModuleProjectDirectory}", required = true, property = "camel-quarkus.multiModuleProjectDirectory")
-    File multiModuleProjectDirectory;
-
     protected static <T extends Writer> T evalTemplate(Configuration cfg, String templateUri, Map<String, Object> model,
             T out) {
         try {
diff --git a/tooling/maven-plugin/src/main/java/org/apache/camel/quarkus/maven/AbstractExtensionListMojo.java b/tooling/maven-plugin/src/main/java/org/apache/camel/quarkus/maven/AbstractExtensionListMojo.java
new file mode 100644
index 0000000..4fde01d
--- /dev/null
+++ b/tooling/maven-plugin/src/main/java/org/apache/camel/quarkus/maven/AbstractExtensionListMojo.java
@@ -0,0 +1,97 @@
+/*
+ * 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.nio.charset.Charset;
+import java.nio.file.Path;
+import java.util.List;
+import java.util.stream.Stream;
+
+import org.apache.maven.plugin.AbstractMojo;
+import org.apache.maven.plugins.annotations.Parameter;
+import org.l2x6.maven.utils.MavenSourceTree;
+
+public abstract class AbstractExtensionListMojo extends AbstractMojo {
+
+    /**
+     * A set of regular expressions to match against artifactIdBases. The matching extensions will not be processed by
+     * this mojo.
+     *
+     * @since 0.1.0
+     */
+    @Parameter(property = "cq.skipArtifactIdBases")
+    protected List<String> skipArtifactIdBases;
+    private PatternSet skipArtifactIdBasePatterns;
+
+    /**
+     * The root directory of the Camel Quarkus source tree.
+     *
+     * @since 0.41.0
+     */
+    @Parameter(defaultValue = "${maven.multiModuleProjectDirectory}", readonly = true)
+    protected File multiModuleProjectDirectory;
+    private Path rootModuleDirectory;
+
+    /**
+     * Encoding to read and write files in the current source tree
+     *
+     * @since 0.18.0
+     */
+    @Parameter(defaultValue = "utf-8", required = true, property = "cq.encoding")
+    String encoding;
+    private Charset charset;
+
+    private MavenSourceTree tree;
+
+    Path getRootModuleDirectory() {
+        if (rootModuleDirectory == null) {
+            rootModuleDirectory = multiModuleProjectDirectory.toPath().toAbsolutePath().normalize();
+        }
+        return rootModuleDirectory;
+    }
+
+    Charset getCharset() {
+        if (charset == null) {
+            charset = Charset.forName(encoding);
+        }
+        return charset;
+    }
+
+    Stream<ExtensionModule> findExtensions() {
+        getSkipArtifactIdBases();
+        return CqUtils.findExtensions(
+                getRootModuleDirectory(),
+                getTree().getModulesByGa().values(),
+                artifactIdBase -> !skipArtifactIdBasePatterns.matchesAny(artifactIdBase));
+    }
+
+    PatternSet getSkipArtifactIdBases() {
+        if (skipArtifactIdBasePatterns == null) {
+            skipArtifactIdBasePatterns = skipArtifactIdBases == null ? PatternSet.empty() : new PatternSet(skipArtifactIdBases);
+        }
+        return skipArtifactIdBasePatterns;
+    }
+
+    public MavenSourceTree getTree() {
+        if (tree == null) {
+            tree = MavenSourceTree.of(getRootModuleDirectory().resolve("pom.xml"), getCharset());
+        }
+        return tree;
+    }
+
+}
diff --git a/tooling/maven-plugin/src/main/java/org/apache/camel/quarkus/maven/CheckExtensionPagesMojo.java b/tooling/maven-plugin/src/main/java/org/apache/camel/quarkus/maven/CheckExtensionPagesMojo.java
index b8fe771..8cb2377 100644
--- a/tooling/maven-plugin/src/main/java/org/apache/camel/quarkus/maven/CheckExtensionPagesMojo.java
+++ b/tooling/maven-plugin/src/main/java/org/apache/camel/quarkus/maven/CheckExtensionPagesMojo.java
@@ -23,10 +23,8 @@ import java.nio.file.Files;
 import java.nio.file.Path;
 import java.nio.file.Paths;
 import java.util.Arrays;
-import java.util.Collections;
 import java.util.Comparator;
 import java.util.HashSet;
-import java.util.List;
 import java.util.Set;
 import java.util.TreeSet;
 import java.util.regex.Matcher;
@@ -62,7 +60,7 @@ public class CheckExtensionPagesMojo extends AbstractDocGeneratorMojo {
     /**
      * The directory relative to which the catalog data is read.
      */
-    @Parameter(defaultValue = "${project.build.directory}/classes", property = "camel-quarkus.catalogBaseDir")
+    @Parameter(defaultValue = "${project.build.directory}/classes", property = "cq.catalogBaseDir")
     File catalogBaseDir;
 
     /**
@@ -72,18 +70,6 @@ public class CheckExtensionPagesMojo extends AbstractDocGeneratorMojo {
     File docsBaseDir;
 
     /**
-     * List of directories that contain extensions
-     */
-    @Parameter(property = "cq.extensionDirectories", required = true)
-    List<File> extensionDirectories;
-
-    /**
-     * A set of artifactIdBases that are not extensions and should be excluded from the catalog
-     */
-    @Parameter(property = "cq.skipArtifactIdBases")
-    Set<String> skipArtifactIdBases;
-
-    /**
      * The version of Camel we depend on
      */
     @Parameter(property = "camel.version")
@@ -108,9 +94,6 @@ public class CheckExtensionPagesMojo extends AbstractDocGeneratorMojo {
     @Override
     public void execute() throws MojoExecutionException, MojoFailureException {
         final Path docsBasePath = docsBaseDir.toPath();
-        if (skipArtifactIdBases == null) {
-            skipArtifactIdBases = Collections.emptySet();
-        }
 
         camelBits(docsBasePath);
         extensions(docsBasePath);
@@ -186,17 +169,13 @@ public class CheckExtensionPagesMojo extends AbstractDocGeneratorMojo {
 
         final Set<String> artifactIdBases = new HashSet<>();
         final Set<CamelQuarkusExtension> extensions = new TreeSet<>(Comparator.comparing(e -> e.getName().get()));
-        extensionDirectories.stream()
-                .map(File::toPath)
-                .forEach(extDir -> {
-                    CqUtils.findExtensionArtifactIdBases(extDir)
-                            .filter(artifactIdBase -> !skipArtifactIdBases.contains(artifactIdBase))
-                            .forEach(artifactIdBase -> {
-                                artifactIdBases.add(artifactIdBase);
-                                final Path runtimePomXmlPath = extDir.resolve(artifactIdBase).resolve("runtime/pom.xml")
-                                        .toAbsolutePath().normalize();
-                                extensions.add(CamelQuarkusExtension.read(runtimePomXmlPath));
-                            });
+        findExtensions()
+                .forEach(ext -> {
+                    final String artifactIdBase = ext.getArtifactIdBase();
+                    artifactIdBases.add(artifactIdBase);
+                    final Path runtimePomXmlPath = ext.getExtensionDir().resolve("runtime/pom.xml")
+                            .toAbsolutePath().normalize();
+                    extensions.add(CamelQuarkusExtension.read(runtimePomXmlPath));
                 });
 
         final String extLinks = extensions.stream()
@@ -223,12 +202,14 @@ public class CheckExtensionPagesMojo extends AbstractDocGeneratorMojo {
 
     void replace(Path path, String replacementKey, String value) {
         try {
-            String document = new String(Files.readAllBytes(path), encoding);
-            document = replace(document, path, replacementKey, value);
-            try {
-                Files.write(path, document.getBytes(encoding));
-            } catch (IOException e) {
-                throw new RuntimeException("Could not write to " + path, e);
+            final String oldDocument = new String(Files.readAllBytes(path), encoding);
+            final String newDocument = replace(oldDocument, path, replacementKey, value);
+            if (!oldDocument.equals(newDocument)) {
+                try {
+                    Files.write(path, newDocument.getBytes(encoding));
+                } catch (IOException e) {
+                    throw new RuntimeException("Could not write to " + path, e);
+                }
             }
         } catch (IOException e) {
             throw new RuntimeException("Could not read from " + path, e);
diff --git a/tooling/maven-plugin/src/main/java/org/apache/camel/quarkus/maven/CqCatalog.java b/tooling/maven-plugin/src/main/java/org/apache/camel/quarkus/maven/CqCatalog.java
index ac0663e..18dc539 100644
--- a/tooling/maven-plugin/src/main/java/org/apache/camel/quarkus/maven/CqCatalog.java
+++ b/tooling/maven-plugin/src/main/java/org/apache/camel/quarkus/maven/CqCatalog.java
@@ -18,12 +18,14 @@ package org.apache.camel.quarkus.maven;
 
 import java.io.IOException;
 import java.io.InputStream;
+import java.nio.charset.StandardCharsets;
 import java.nio.file.FileSystem;
 import java.nio.file.FileSystems;
 import java.nio.file.Files;
 import java.nio.file.Path;
 import java.util.ArrayList;
 import java.util.List;
+import java.util.function.Function;
 import java.util.function.Predicate;
 import java.util.stream.Collectors;
 import java.util.stream.Stream;
@@ -38,17 +40,20 @@ import org.apache.camel.catalog.impl.CatalogHelper;
 import org.apache.camel.tooling.model.ArtifactModel;
 import org.apache.camel.tooling.model.ComponentModel;
 import org.apache.camel.tooling.model.DataFormatModel;
+import org.apache.camel.tooling.model.JsonMapper;
+import org.apache.camel.tooling.model.LanguageModel;
+import org.apache.camel.tooling.model.OtherModel;
 
 public class CqCatalog {
 
     public enum Flavor {
-        camel("org.apache.camel", "camel-catalog") {
+        camel("org.apache.camel", "camel-catalog", "org/apache/camel/catalog") {
             @Override
             public RuntimeProvider createRuntimeProvider(DefaultCamelCatalog c) {
                 return new DefaultRuntimeProvider(c);
             }
         },
-        camelQuarkus("org.apache.camel.quarkus", "camel-quarkus-catalog") {
+        camelQuarkus("org.apache.camel.quarkus", "camel-quarkus-catalog", CQ_CATALOG_DIR) {
             @Override
             public RuntimeProvider createRuntimeProvider(DefaultCamelCatalog c) {
                 return new CqRuntimeProvider(c);
@@ -57,10 +62,12 @@ public class CqCatalog {
 
         private final String groupId;
         private final String artifactId;
+        private final String catalogDir;
 
-        private Flavor(String groupId, String artifactId) {
+        private Flavor(String groupId, String artifactId, String catalogDir) {
             this.groupId = groupId;
             this.artifactId = artifactId;
+            this.catalogDir = catalogDir;
         }
 
         public abstract RuntimeProvider createRuntimeProvider(DefaultCamelCatalog c);
@@ -75,6 +82,8 @@ public class CqCatalog {
     }
 
     private final DefaultCamelCatalog catalog;
+    private final Flavor flavor;
+    public static final String CQ_CATALOG_DIR = "org/apache/camel/catalog/quarkus";
 
     private static final ThreadLocal<CqCatalog> threadLocalCamelCatalog = ThreadLocal.withInitial(CqCatalog::new);
 
@@ -82,16 +91,38 @@ public class CqCatalog {
         return threadLocalCamelCatalog.get();
     }
 
+    public static CqCatalog findFirstFromClassPath() {
+        try {
+            Class.forName("org.apache.camel.catalog.quarkus.QuarkusRuntimeProvider");
+            return new CqCatalog(Flavor.camelQuarkus,
+                    name -> Thread.currentThread().getContextClassLoader().getResourceAsStream(name));
+        } catch (ClassNotFoundException e) {
+            return new CqCatalog(Flavor.camel,
+                    name -> Thread.currentThread().getContextClassLoader().getResourceAsStream(name));
+        }
+    }
+
     public CqCatalog(Path baseDir, Flavor flavor) {
-        super();
+        this(flavor, name -> {
+            try {
+                return Files.newInputStream(baseDir.resolve(name));
+            } catch (IOException e) {
+                throw new RuntimeException(e);
+            }
+        });
+    }
+
+    public CqCatalog(Flavor flavor, Function<String, InputStream> resourceLocator) {
+        this.flavor = flavor;
         final DefaultCamelCatalog c = new DefaultCamelCatalog(true);
         c.setRuntimeProvider(flavor.createRuntimeProvider(c));
-        c.setVersionManager(new CqVersionManager(c, baseDir));
+        c.setVersionManager(new CqVersionManager(c, resourceLocator));
         this.catalog = c;
     }
 
     public CqCatalog() {
         super();
+        this.flavor = Flavor.camel;
         this.catalog = new DefaultCamelCatalog(true);
     }
 
@@ -107,17 +138,26 @@ public class CqCatalog {
 
     public Stream<ArtifactModel<?>> filterModels(String cqArtifactIdBase) {
         final Predicate<ArtifactModel<?>> filter;
-        if ("core".equals(cqArtifactIdBase)) {
-            filter = model -> ("camel-base".equals(model.getArtifactId())
-                    || "camel-core-languages".equals(model.getArtifactId())) && !"csimple".equals(model.getName());
-        } else if ("csimple".equals(cqArtifactIdBase)) {
-            filter = model -> "camel-core-languages".equals(model.getArtifactId()) && "csimple".equals(model.getName());
-        } else if ("reactive-executor".equals(cqArtifactIdBase)) {
-            filter = model -> "camel-reactive-executor-vertx".equals(model.getArtifactId());
-        } else if ("qute".equals(cqArtifactIdBase)) {
-            filter = model -> "camel-quarkus-qute-component".equals(model.getArtifactId());
-        } else {
-            filter = model -> ("camel-" + cqArtifactIdBase).equals(model.getArtifactId());
+        switch (flavor) {
+        case camel:
+            if ("core".equals(cqArtifactIdBase)) {
+                filter = model -> ("camel-base".equals(model.getArtifactId())
+                        || "camel-core-languages".equals(model.getArtifactId())) && !"csimple".equals(model.getName());
+            } else if ("csimple".equals(cqArtifactIdBase)) {
+                filter = model -> "camel-core-languages".equals(model.getArtifactId()) && "csimple".equals(model.getName());
+            } else if ("reactive-executor".equals(cqArtifactIdBase)) {
+                filter = model -> "camel-reactive-executor-vertx".equals(model.getArtifactId());
+            } else if ("qute".equals(cqArtifactIdBase)) {
+                filter = model -> "camel-quarkus-qute-component".equals(model.getArtifactId());
+            } else {
+                filter = model -> ("camel-" + cqArtifactIdBase).equals(model.getArtifactId());
+            }
+            break;
+        case camelQuarkus:
+            filter = model -> ("camel-quarkus-" + cqArtifactIdBase).equals(model.getArtifactId());
+            break;
+        default:
+            throw new IllegalStateException("Unexpected " + Flavor.class.getName() + " " + flavor);
         }
         return models().filter(filter);
     }
@@ -135,6 +175,43 @@ public class CqCatalog {
         catalog.addComponent(name, className, jsonSchema);
     }
 
+    public void store(Path baseDir) {
+        models().forEach(m -> serialize(baseDir.resolve(flavor.catalogDir), m));
+    }
+
+    static void serialize(final Path catalogPath, ArtifactModel<?> model) {
+        final Path out = catalogPath.resolve(model.getKind() + "s")
+                .resolve(model.getName() + ".json");
+        try {
+            Files.createDirectories(out.getParent());
+        } catch (IOException e) {
+            throw new RuntimeException("Could not create " + out.getParent(), e);
+        }
+        String rawJson;
+        switch (Kind.valueOf(model.getKind())) {
+        case component:
+            rawJson = JsonMapper.createParameterJsonSchema((ComponentModel) model);
+            break;
+        case language:
+            rawJson = JsonMapper.createParameterJsonSchema((LanguageModel) model);
+            break;
+        case dataformat:
+            rawJson = JsonMapper.createParameterJsonSchema((DataFormatModel) model);
+            break;
+        case other:
+            rawJson = JsonMapper.createJsonSchema((OtherModel) model);
+            break;
+        default:
+            throw new IllegalStateException("Cannot serialize kind " + model.getKind());
+        }
+
+        try {
+            Files.write(out, rawJson.getBytes(StandardCharsets.UTF_8));
+        } catch (IOException e) {
+            throw new RuntimeException("Could not write to " + out, e);
+        }
+    }
+
     public static Stream<Kind> kinds() {
         return Stream.of(Kind.values())
                 .filter(kind -> kind != org.apache.camel.catalog.Kind.eip);
@@ -192,7 +269,10 @@ public class CqCatalog {
     public static List<ArtifactModel<?>> primaryModel(Stream<ArtifactModel<?>> input) {
         final List<ArtifactModel<?>> models = input
                 .filter(CqCatalog::isFirstScheme)
-                .filter(m -> !m.getName().startsWith("google-") || !m.getName().endsWith("-stream")) // ignore the google stream component variants
+                .filter(m -> !m.getName().startsWith("google-") || !m.getName().endsWith("-stream")) // ignore the
+                // google stream
+                // component
+                // variants
                 .collect(Collectors.toList());
         if (models.size() > 1) {
             List<ArtifactModel<?>> componentModels = models.stream()
@@ -222,34 +302,30 @@ public class CqCatalog {
     }
 
     static class CqVersionManager extends DefaultVersionManager {
-        private final Path baseDir;
+        private final Function<String, InputStream> resourceLocator;
 
-        public CqVersionManager(CamelCatalog camelCatalog, Path baseDir) {
+        public CqVersionManager(CamelCatalog camelCatalog, Function<String, InputStream> resourceLocator) {
             super(camelCatalog);
-            this.baseDir = baseDir;
+            this.resourceLocator = resourceLocator;
         }
 
         @Override
         public InputStream getResourceAsStream(String name) {
-            try {
-                return Files.newInputStream(baseDir.resolve(name));
-            } catch (IOException e) {
-                throw new RuntimeException(e);
-            }
+            return resourceLocator.apply(name);
         }
 
     }
 
     static class CqRuntimeProvider implements RuntimeProvider {
 
-        private static final String COMPONENT_DIR = PrepareCatalogQuarkusMojo.CQ_CATALOG_DIR + "/components";
-        private static final String DATAFORMAT_DIR = PrepareCatalogQuarkusMojo.CQ_CATALOG_DIR + "/dataformats";
-        private static final String LANGUAGE_DIR = PrepareCatalogQuarkusMojo.CQ_CATALOG_DIR + "/languages";
-        private static final String OTHER_DIR = PrepareCatalogQuarkusMojo.CQ_CATALOG_DIR + "/others";
-        private static final String COMPONENTS_CATALOG = PrepareCatalogQuarkusMojo.CQ_CATALOG_DIR + "/components.properties";
-        private static final String DATA_FORMATS_CATALOG = PrepareCatalogQuarkusMojo.CQ_CATALOG_DIR + "/dataformats.properties";
-        private static final String LANGUAGE_CATALOG = PrepareCatalogQuarkusMojo.CQ_CATALOG_DIR + "/languages.properties";
-        private static final String OTHER_CATALOG = PrepareCatalogQuarkusMojo.CQ_CATALOG_DIR + "/others.properties";
+        private static final String COMPONENT_DIR = CQ_CATALOG_DIR + "/components";
+        private static final String DATAFORMAT_DIR = CQ_CATALOG_DIR + "/dataformats";
+        private static final String LANGUAGE_DIR = CQ_CATALOG_DIR + "/languages";
+        private static final String OTHER_DIR = CQ_CATALOG_DIR + "/others";
+        private static final String COMPONENTS_CATALOG = CQ_CATALOG_DIR + "/components.properties";
+        private static final String DATA_FORMATS_CATALOG = CQ_CATALOG_DIR + "/dataformats.properties";
+        private static final String LANGUAGE_CATALOG = CQ_CATALOG_DIR + "/languages.properties";
+        private static final String OTHER_CATALOG = CQ_CATALOG_DIR + "/others.properties";
 
         private CamelCatalog camelCatalog;
 
diff --git a/tooling/maven-plugin/src/main/java/org/apache/camel/quarkus/maven/CqUtils.java b/tooling/maven-plugin/src/main/java/org/apache/camel/quarkus/maven/CqUtils.java
index d744903..b22e83d 100644
--- a/tooling/maven-plugin/src/main/java/org/apache/camel/quarkus/maven/CqUtils.java
+++ b/tooling/maven-plugin/src/main/java/org/apache/camel/quarkus/maven/CqUtils.java
@@ -19,13 +19,16 @@ package org.apache.camel.quarkus.maven;
 import java.io.IOException;
 import java.io.InputStream;
 import java.io.OutputStream;
+import java.io.Reader;
 import java.net.URL;
+import java.nio.charset.StandardCharsets;
 import java.nio.file.Files;
 import java.nio.file.Path;
 import java.util.Collection;
 import java.util.LinkedHashSet;
 import java.util.List;
 import java.util.Set;
+import java.util.function.Predicate;
 import java.util.stream.Collectors;
 import java.util.stream.Stream;
 
@@ -43,7 +46,10 @@ import org.apache.camel.tooling.model.JsonMapper;
 import org.apache.camel.tooling.model.LanguageModel;
 import org.apache.camel.tooling.model.OtherModel;
 import org.apache.maven.model.Model;
+import org.apache.maven.model.io.xpp3.MavenXpp3Reader;
 import org.apache.maven.plugin.logging.Log;
+import org.codehaus.plexus.util.xml.pull.XmlPullParserException;
+import org.l2x6.maven.utils.MavenSourceTree.Module;
 
 public class CqUtils {
     public static final String CLASSPATH_PREFIX = "classpath:";
@@ -80,16 +86,39 @@ public class CqUtils {
         }
     }
 
+    public static Stream<ExtensionModule> findExtensions(Path basePath, Collection<Module> modules,
+            Predicate<String> artifactIdBaseFilter) {
+        return modules.stream()
+                .filter(p -> p.getGav().getArtifactId().asConstant().endsWith("-deployment"))
+                .map(p -> {
+                    final Path extensionDir = basePath.resolve(p.getPomPath()).getParent().getParent().toAbsolutePath()
+                            .normalize();
+                    final String deploymentArtifactId = p.getGav().getArtifactId().asConstant();
+                    if (!deploymentArtifactId.startsWith("camel-quarkus-")) {
+                        throw new IllegalStateException("Should start with 'camel-quarkus-': " + deploymentArtifactId);
+                    }
+                    final String artifactIdBase = deploymentArtifactId.substring("camel-quarkus-".length(),
+                            deploymentArtifactId.length() - "-deployment".length());
+                    return new ExtensionModule(extensionDir, artifactIdBase);
+                })
+                .filter(e -> artifactIdBaseFilter.test(e.getArtifactIdBase()))
+                .sorted();
+    }
+
     public static Stream<String> findExtensionArtifactIdBases(Path extensionDir) {
-        try {
-            return Files.list(extensionDir)
-                    .filter(path -> Files.isDirectory(path)
-                            && Files.exists(path.resolve("pom.xml"))
-                            && Files.exists(path.resolve("runtime")))
-                    .map(dir -> dir.getFileName().toString());
-        } catch (IOException e) {
-            throw new RuntimeException(e);
+        final Path extListPomPath = extensionDir.resolve("pom.xml");
+        final List<String> modules;
+        try (Reader r = Files.newBufferedReader(extListPomPath, StandardCharsets.UTF_8)) {
+            final MavenXpp3Reader rxppReader = new MavenXpp3Reader();
+            final Model extListPom = rxppReader.read(r);
+            modules = extListPom.getModules();
+        } catch (IOException | XmlPullParserException e) {
+            throw new RuntimeException("Could not read " + extListPomPath);
         }
+
+        return modules.stream()
+                .filter(m -> Files.isRegularFile(extensionDir.resolve(m + "/pom.xml"))
+                        && Files.isDirectory(extensionDir.resolve(m + "/runtime")));
     }
 
     public static Configuration getTemplateConfig(Path basePath, String defaultUriBase, String templatesUriBase,
diff --git a/tooling/maven-plugin/src/main/java/org/apache/camel/quarkus/maven/ExtensionModule.java b/tooling/maven-plugin/src/main/java/org/apache/camel/quarkus/maven/ExtensionModule.java
new file mode 100644
index 0000000..4e617e8
--- /dev/null
+++ b/tooling/maven-plugin/src/main/java/org/apache/camel/quarkus/maven/ExtensionModule.java
@@ -0,0 +1,66 @@
+/*
+ * 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.nio.file.Path;
+
+public class ExtensionModule implements Comparable<ExtensionModule> {
+
+    private final Path extensionDir;
+    private final String artifactIdBase;
+
+    public ExtensionModule(Path extensionDir, String artifactIdBase) {
+        super();
+        this.extensionDir = extensionDir;
+        this.artifactIdBase = artifactIdBase;
+    }
+
+    /**
+     * @return the absolute canonical path of the directory whose children are the runtime and deployment directories of
+     *         this {@link ExtensionModule}
+     */
+    public Path getExtensionDir() {
+        return extensionDir;
+    }
+
+    public String getArtifactIdBase() {
+        return artifactIdBase;
+    }
+
+    @Override
+    public String toString() {
+        return artifactIdBase + " (" + extensionDir + ")";
+    }
+
+    public boolean isNativeSupported() {
+        return !extensionDir.getParent().getFileName().toString().equals("extensions-jvm");
+    }
+
+    @Override
+    public int compareTo(ExtensionModule other) {
+        return this.artifactIdBase.compareTo(other.artifactIdBase);
+    }
+
+    public Path getRuntimePomPath() {
+        return extensionDir.resolve("runtime/pom.xml");
+    }
+
+    public Path getPomPath() {
+        return extensionDir.resolve("pom.xml");
+    }
+
+}
diff --git a/tooling/maven-plugin/src/main/java/org/apache/camel/quarkus/maven/PatternSet.java b/tooling/maven-plugin/src/main/java/org/apache/camel/quarkus/maven/PatternSet.java
new file mode 100644
index 0000000..2c03ef3
--- /dev/null
+++ b/tooling/maven-plugin/src/main/java/org/apache/camel/quarkus/maven/PatternSet.java
@@ -0,0 +1,48 @@
+/*
+ * 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 java.util.Collections;
+import java.util.List;
+import java.util.regex.Pattern;
+import java.util.stream.Collectors;
+
+public class PatternSet {
+
+    private static final PatternSet EMPTY = new PatternSet(Collections.emptyList());
+
+    public static PatternSet empty() {
+        return EMPTY;
+    }
+
+    private final List<Pattern> patterns;
+
+    PatternSet(Collection<String> rawPatterns) {
+        this.patterns = rawPatterns.stream()
+                .map(Pattern::compile)
+                .collect(Collectors.toList());
+    }
+
+    public boolean matchesAny(String string) {
+        if (patterns.isEmpty()) {
+            return false;
+        }
+        return patterns.stream()
+                .anyMatch(pat -> pat.matcher(string).matches());
+    }
+}
diff --git a/tooling/maven-plugin/src/main/java/org/apache/camel/quarkus/maven/PrepareCatalogQuarkusMojo.java b/tooling/maven-plugin/src/main/java/org/apache/camel/quarkus/maven/PrepareCatalogQuarkusMojo.java
index dc19c29..c0e9c45 100644
--- a/tooling/maven-plugin/src/main/java/org/apache/camel/quarkus/maven/PrepareCatalogQuarkusMojo.java
+++ b/tooling/maven-plugin/src/main/java/org/apache/camel/quarkus/maven/PrepareCatalogQuarkusMojo.java
@@ -21,7 +21,6 @@ import java.io.IOException;
 import java.nio.charset.StandardCharsets;
 import java.nio.file.Files;
 import java.nio.file.Path;
-import java.util.Collections;
 import java.util.LinkedHashMap;
 import java.util.List;
 import java.util.Map;
@@ -33,11 +32,9 @@ import org.apache.camel.catalog.Kind;
 import org.apache.camel.tooling.model.ArtifactModel;
 import org.apache.camel.tooling.model.ComponentModel;
 import org.apache.camel.tooling.model.DataFormatModel;
-import org.apache.camel.tooling.model.JsonMapper;
 import org.apache.camel.tooling.model.LanguageModel;
 import org.apache.camel.tooling.model.OtherModel;
 import org.apache.camel.tooling.model.SupportLevel;
-import org.apache.maven.plugin.AbstractMojo;
 import org.apache.maven.plugin.MojoExecutionException;
 import org.apache.maven.plugin.MojoFailureException;
 import org.apache.maven.plugins.annotations.Mojo;
@@ -49,9 +46,8 @@ import org.apache.maven.plugins.annotations.Parameter;
  * @since 0.1.0
  */
 @Mojo(name = "prepare-catalog-quarkus", threadSafe = true)
-public class PrepareCatalogQuarkusMojo extends AbstractMojo {
+public class PrepareCatalogQuarkusMojo extends AbstractExtensionListMojo {
 
-    public static final String CQ_CATALOG_DIR = "org/apache/camel/catalog/quarkus";
     public static final String CAMEL_ARTIFACT = "camelArtifact";
     /**
      * The output directory where the catalog files should be written.
@@ -60,104 +56,94 @@ public class PrepareCatalogQuarkusMojo extends AbstractMojo {
     File catalogBaseDir;
 
     /**
-     * List of directories that contain extensions
+     * If {@code true}, the Catalog available in the class path will be first dumped to {@link #catalogBaseDir}
+     * and then some of its options will be overwritten by this mojo; otherwise no dump happens and this mojo writes to
+     * {@link #catalogBaseDir} as usual.
+     *
+     * @since 2.3.0
      */
-    @Parameter(property = "cq.extensionDirectories", required = true)
-    List<File> extensionDirectories;
-
-    /**
-     * A set of artifactIdBases that are not extensions and should be excluded from the catalog
-     */
-    @Parameter(property = "cq.skipArtifactIdBases")
-    Set<String> skipArtifactIdBases;
+    @Parameter(property = "cq.extendClassPathCatalog", defaultValue = "false")
+    boolean extendClassPathCatalog;
 
     @Override
     public void execute() throws MojoExecutionException, MojoFailureException {
-        final Path catalogPath = catalogBaseDir.toPath().resolve(CQ_CATALOG_DIR);
-        if (skipArtifactIdBases == null) {
-            skipArtifactIdBases = Collections.emptySet();
-        }
+        final Path catalogPath = catalogBaseDir.toPath().resolve(CqCatalog.CQ_CATALOG_DIR);
 
         final Map<String, Set<String>> schemesByKind = new LinkedHashMap<>();
         CqCatalog.kinds().forEach(kind -> schemesByKind.put(kind.name(), new TreeSet<>()));
 
-        final CqCatalog catalog = new CqCatalog();
-        extensionDirectories.stream()
-                .map(File::toPath)
-                .forEach(extDir -> {
+        final CqCatalog catalog = CqCatalog.findFirstFromClassPath();
+        if (extendClassPathCatalog) {
+            catalog.store(catalogBaseDir.toPath());
+        }
 
-                    /* Discover local components, such as Qute and add them to the catalog */
-                    CqUtils.findExtensionArtifactIdBases(extDir)
-                            .filter(artifactIdBase -> !skipArtifactIdBases.contains(artifactIdBase))
-                            .forEach(artifactIdBase -> {
-                                final Path schemaFile = extDir
-                                        .resolve(artifactIdBase)
-                                        .resolve("component/src/generated/resources/org/apache/camel/component/"
-                                                + artifactIdBase + "/" + artifactIdBase + ".json")
-                                        .toAbsolutePath().normalize();
-                                if (Files.isRegularFile(schemaFile)) {
-                                    try {
-                                        final String schema = new String(Files.readAllBytes(schemaFile),
-                                                StandardCharsets.UTF_8);
-                                        final String capBase = artifactIdBase.substring(0, 1).toUpperCase()
-                                                + artifactIdBase.substring(1);
-                                        getLog().debug("Adding an extra component " + artifactIdBase + " " +
-                                                "org.apache.camel.component." + artifactIdBase + "." + capBase + "Component " +
-                                                schema);
-                                        catalog.addComponent(artifactIdBase,
-                                                "org.apache.camel.component." + artifactIdBase + "." + capBase + "Component",
-                                                schema);
-                                    } catch (IOException e) {
-                                        throw new RuntimeException("Could not read " + schemaFile, e);
-                                    }
-                                }
-                            });
+        findExtensions()
+                .forEach(ext -> {
+                    final String artifactIdBase = ext.getArtifactIdBase();
+                    final Path schemaFile = ext
+                            .getExtensionDir()
+                            .resolve("component/src/generated/resources/org/apache/camel/component/"
+                                    + artifactIdBase + "/" + artifactIdBase + ".json")
+                            .toAbsolutePath().normalize();
+                    if (Files.isRegularFile(schemaFile)) {
+                        try {
+                            final String schema = new String(Files.readAllBytes(schemaFile),
+                                    StandardCharsets.UTF_8);
+                            final String capBase = artifactIdBase.substring(0, 1).toUpperCase()
+                                    + artifactIdBase.substring(1);
+                            getLog().debug("Adding an extra component " + artifactIdBase + " " +
+                                    "org.apache.camel.component." + artifactIdBase + "." + capBase + "Component " +
+                                    schema);
+                            catalog.addComponent(artifactIdBase,
+                                    "org.apache.camel.component." + artifactIdBase + "." + capBase + "Component",
+                                    schema);
+                        } catch (IOException e) {
+                            throw new RuntimeException("Could not read " + schemaFile, e);
+                        }
+                    }
                 });
 
-        extensionDirectories.stream()
-                .map(File::toPath)
-                .forEach(extDir -> {
-                    CqUtils.findExtensionArtifactIdBases(extDir)
-                            .filter(artifactIdBase -> !skipArtifactIdBases.contains(artifactIdBase))
-                            .forEach(artifactIdBase -> {
-                                final List<ArtifactModel<?>> models = catalog.filterModels(artifactIdBase)
-                                        .collect(Collectors.toList());
-                                final Path runtimePomXmlPath = extDir.resolve(artifactIdBase).resolve("runtime/pom.xml")
-                                        .toAbsolutePath().normalize();
-                                final CamelQuarkusExtension ext = CamelQuarkusExtension.read(runtimePomXmlPath);
-                                final boolean nativeSupported = ext.isNativeSupported();
-                                if (models.isEmpty()) {
-                                    final ArtifactModel<?> model;
-                                    final Kind extKind = ext.getKind();
-                                    if (extKind == Kind.component) {
-                                        model = new ComponentModel();
-                                    } else if (extKind == Kind.language) {
-                                        model = new LanguageModel();
-                                    } else if (extKind == Kind.dataformat) {
-                                        model = new DataFormatModel();
-                                    } else {
-                                        model = new OtherModel();
-                                    }
-                                    final String name = ext.getRuntimeArtifactId().replace("camel-quarkus-", "");
-                                    model.setName(name);
-                                    final String title = ext.getName().orElseThrow(() -> new RuntimeException(
-                                            "name is missing in " + ext.getRuntimePomXmlPath()));
-                                    model.setTitle(title);
-                                    model.setDescription(ext.getDescription().orElseThrow(() -> new RuntimeException(
-                                            "description is missing in " + ext.getRuntimePomXmlPath())));
-                                    model.setDeprecated(CqUtils.isDeprecated(title, models));
-                                    model.setLabel(ext.getLabel().orElse("quarkus"));
-                                    update(model, ext, nativeSupported);
-                                    serialize(catalogPath, model);
-                                    schemesByKind.get(model.getKind()).add(model.getName());
-                                } else {
-                                    for (ArtifactModel<?> model : models) {
-                                        update(model, ext, nativeSupported);
-                                        serialize(catalogPath, model);
-                                        schemesByKind.get(model.getKind()).add(model.getName());
-                                    }
-                                }
-                            });
+        findExtensions()
+                .forEach(extPath -> {
+                    final String artifactIdBase = extPath.getArtifactIdBase();
+                    final List<ArtifactModel<?>> models = catalog.filterModels(artifactIdBase)
+                            .collect(Collectors.toList());
+                    final Path runtimePomXmlPath = extPath
+                            .getExtensionDir().resolve("runtime/pom.xml")
+                            .toAbsolutePath().normalize();
+                    final CamelQuarkusExtension ext = CamelQuarkusExtension.read(runtimePomXmlPath);
+                    final boolean nativeSupported = ext.isNativeSupported();
+                    if (models.isEmpty()) {
+                        final ArtifactModel<?> model;
+                        final Kind extKind = ext.getKind();
+                        if (extKind == Kind.component) {
+                            model = new ComponentModel();
+                        } else if (extKind == Kind.language) {
+                            model = new LanguageModel();
+                        } else if (extKind == Kind.dataformat) {
+                            model = new DataFormatModel();
+                        } else {
+                            model = new OtherModel();
+                        }
+                        final String name = ext.getRuntimeArtifactId().replace("camel-quarkus-", "");
+                        model.setName(name);
+                        final String title = ext.getName().orElseThrow(() -> new RuntimeException(
+                                "name is missing in " + ext.getRuntimePomXmlPath()));
+                        model.setTitle(title);
+                        model.setDescription(ext.getDescription().orElseThrow(() -> new RuntimeException(
+                                "description is missing in " + ext.getRuntimePomXmlPath())));
+                        model.setDeprecated(CqUtils.isDeprecated(title, models));
+                        model.setLabel(ext.getLabel().orElse("quarkus"));
+                        update(model, ext, nativeSupported);
+                        CqCatalog.serialize(catalogPath, model);
+                        schemesByKind.get(model.getKind()).add(model.getName());
+                    } else {
+                        for (ArtifactModel<?> model : models) {
+                            update(model, ext, nativeSupported);
+                            CqCatalog.serialize(catalogPath, model);
+                            schemesByKind.get(model.getKind()).add(model.getName());
+                        }
+                    }
                 });
 
         CqCatalog.kinds().forEach(kind -> {
@@ -174,39 +160,6 @@ public class PrepareCatalogQuarkusMojo extends AbstractMojo {
 
     }
 
-    private void serialize(final Path catalogPath, ArtifactModel<?> model) {
-        final Path out = catalogPath.resolve(model.getKind() + "s")
-                .resolve(model.getName() + ".json");
-        try {
-            Files.createDirectories(out.getParent());
-        } catch (IOException e) {
-            throw new RuntimeException("Could not create " + out.getParent(), e);
-        }
-        String rawJson;
-        switch (Kind.valueOf(model.getKind())) {
-        case component:
-            rawJson = JsonMapper.createParameterJsonSchema((ComponentModel) model);
-            break;
-        case language:
-            rawJson = JsonMapper.createParameterJsonSchema((LanguageModel) model);
-            break;
-        case dataformat:
-            rawJson = JsonMapper.createParameterJsonSchema((DataFormatModel) model);
-            break;
-        case other:
-            rawJson = JsonMapper.createJsonSchema((OtherModel) model);
-            break;
-        default:
-            throw new IllegalStateException("Cannot serialize kind " + model.getKind());
-        }
-
-        try {
-            Files.write(out, rawJson.getBytes(StandardCharsets.UTF_8));
-        } catch (IOException e) {
-            throw new RuntimeException("Could not write to " + out);
-        }
-    }
-
     private static void update(ArtifactModel<?> model, CamelQuarkusExtension ext, boolean nativeSupported) {
         final String firstVersion = ext.getJvmSince()
                 .orElseThrow(() -> new RuntimeException(