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

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


The following commit(s) were added to refs/heads/main by this push:
     new feb1e3ed47a7 yaml dsl validator maven plugin (#21403)
feb1e3ed47a7 is described below

commit feb1e3ed47a7bfed733d4dd56142ae0d82285a20
Author: Claus Ibsen <[email protected]>
AuthorDate: Wed Feb 11 14:07:08 2026 +0100

    yaml dsl validator maven plugin (#21403)
    
    * CAMEL-22931: Maven Plugin for YAML DSL validator
---
 bom/camel-bom/pom.xml                              |   5 +
 docs/components/modules/others/nav.adoc            |   1 +
 .../camel-yaml-dsl-validator-maven-plugin.adoc     |   1 +
 docs/pom.xml                                       |   3 +
 docs/user-manual/modules/ROOT/nav.adoc             |  11 +-
 .../camel-yaml-dsl-validator-maven-plugin.adoc     | 119 +++++++++
 .../maven/dsl/yaml/GenerateYamlSchemaMojo.java     |   5 +-
 .../camel-yaml-dsl-validator-maven-plugin/pom.xml  | 117 +++++++++
 .../camel-yaml-dsl-validator-maven-plugin.adoc     | 119 +++++++++
 .../camel/dsl/yaml/validator/ValidateMojo.java     | 291 +++++++++++++++++++++
 .../camel-yaml-dsl-validator/pom.xml               |  80 ++++++
 .../camel/dsl/yaml/validator/YamlValidator.java    |  82 ++++++
 .../dsl/yaml/validator/YamlValidatorTest.java      |  48 ++++
 .../src/test/resources/bad.yaml                    |  26 ++
 .../src/test/resources/foo.yaml                    |  26 ++
 .../src/test/resources/log4j2.properties           |  31 +++
 dsl/camel-yaml-dsl/pom.xml                         |   2 +
 parent/pom.xml                                     |   5 +
 18 files changed, 966 insertions(+), 6 deletions(-)

diff --git a/bom/camel-bom/pom.xml b/bom/camel-bom/pom.xml
index 13bb1c80c79c..dda32ab0f532 100644
--- a/bom/camel-bom/pom.xml
+++ b/bom/camel-bom/pom.xml
@@ -2567,6 +2567,11 @@
         <artifactId>camel-yaml-dsl-deserializers</artifactId>
         <version>4.18.0-SNAPSHOT</version>
       </dependency>
+      <dependency>
+        <groupId>org.apache.camel</groupId>
+        <artifactId>camel-yaml-dsl-validator</artifactId>
+        <version>4.18.0-SNAPSHOT</version>
+      </dependency>
       <dependency>
         <groupId>org.apache.camel</groupId>
         <artifactId>camel-yaml-io</artifactId>
diff --git a/docs/components/modules/others/nav.adoc 
b/docs/components/modules/others/nav.adoc
index 5ccb0459539f..138e6ddf9ef3 100644
--- a/docs/components/modules/others/nav.adoc
+++ b/docs/components/modules/others/nav.adoc
@@ -5,6 +5,7 @@
 ** xref:attachments.adoc[Attachments]
 *** xref:aws-xray.adoc[AWS XRay]
 *** xref:azure-schema-registry.adoc[Azure Schema Registry]
+** xref:camel-yaml-dsl-validator-maven-plugin.adoc[Camel YAML DSL Validator 
Maven Plugin]
 ** xref:cli-connector.adoc[CLI Connector]
 ** xref:cli-debug.adoc[CLI Debug]
 ** xref:cloudevents.adoc[Cloudevents]
diff --git 
a/docs/components/modules/others/pages/camel-yaml-dsl-validator-maven-plugin.adoc
 
b/docs/components/modules/others/pages/camel-yaml-dsl-validator-maven-plugin.adoc
new file mode 120000
index 000000000000..1ab0b4936762
--- /dev/null
+++ 
b/docs/components/modules/others/pages/camel-yaml-dsl-validator-maven-plugin.adoc
@@ -0,0 +1 @@
+../../../../../dsl/camel-yaml-dsl/camel-yaml-dsl-validator-maven-plugin/src/main/docs/camel-yaml-dsl-validator-maven-plugin.adoc
\ No newline at end of file
diff --git a/docs/pom.xml b/docs/pom.xml
index 55b34dcc8b91..c50fc522a7d4 100644
--- a/docs/pom.xml
+++ b/docs/pom.xml
@@ -123,6 +123,9 @@
                                         <resource>
                                             
<directory>../catalog/camel-report-maven-plugin/src/main/docs</directory>
                                         </resource>
+                                        <resource>
+                                            
<directory>../dsl/camel-yaml-dsl/camel-yaml-dsl-validator-maven-plugin/src/main/docs</directory>
+                                        </resource>
                                     </resources>
                                 </configuration>
                             </execution>
diff --git a/docs/user-manual/modules/ROOT/nav.adoc 
b/docs/user-manual/modules/ROOT/nav.adoc
index eb09a11eadaa..62a471a755e0 100644
--- a/docs/user-manual/modules/ROOT/nav.adoc
+++ b/docs/user-manual/modules/ROOT/nav.adoc
@@ -8,13 +8,14 @@
 ** xref:camel-jbang.adoc[Camel JBang]
 *** xref:jbang-commands/camel-jbang-commands.adoc[Camel JBang Command 
Reference]
 *** xref:camel-jbang-launcher.adoc[Camel JBang Launcher]
-*** xref:camel-jbang-kubernetes.adoc[Camel JBang Kubernetes plugin]
-*** xref:camel-jbang-test.adoc[Camel JBang Testing plugin]
+*** xref:camel-jbang-kubernetes.adoc[Camel JBang Kubernetes Plugin]
+*** xref:camel-jbang-test.adoc[Camel JBang Testing Plugin]
 *** xref:camel-jbang-mcp.adoc[Camel MCP Server]
 ** xref:camel-maven-plugin.adoc[Camel Maven Plugin]
-** xref:camel-component-maven-plugin.adoc[Camel Component Maven Plugin]
-** xref:camel-report-maven-plugin.adoc[Camel Maven Report Plugin]
-** xref:camel-maven-archetypes.adoc[Camel Maven Archetypes]
+*** xref:camel-component-maven-plugin.adoc[Camel Component Maven Plugin]
+*** xref:camel-report-maven-plugin.adoc[Camel Maven Report Plugin]
+*** xref:camel-yaml-dsl-validator-maven-plugin.adoc[Camel Maven YAML DSL 
Validator Plugin]
+*** xref:camel-maven-archetypes.adoc[Camel Maven Archetypes]
 ** xref:configuring-route-startup-ordering-and-autostartup.adoc[Configuring 
route startup ordering and autostartup]
 ** xref:component-dsl.adoc[Component DSL]
 ** xref:Endpoint-dsl.adoc[Endpoint DSL]
diff --git 
a/docs/user-manual/modules/ROOT/pages/camel-yaml-dsl-validator-maven-plugin.adoc
 
b/docs/user-manual/modules/ROOT/pages/camel-yaml-dsl-validator-maven-plugin.adoc
new file mode 100644
index 000000000000..99678fafadfd
--- /dev/null
+++ 
b/docs/user-manual/modules/ROOT/pages/camel-yaml-dsl-validator-maven-plugin.adoc
@@ -0,0 +1,119 @@
+= Camel YAML DSL Validator Maven Plugin
+
+The Camel YAML DSL Validator Maven Plugin supports the following goals
+
+ - camel-yaml-dsl-validator:validate - To validate YAML routes are correct 
according to spec
+
+== camel-yaml-dsl-validator:validate
+
+For validating the YAML routes for syntax errors according to the spec.
+
+Then you can run the `validate` goal from the command line or from within your 
Java editor such as IDEA or Eclipse.
+
+[source,bash]
+----
+mvn camel-yaml-dsl-validator:validate
+----
+
+You can also enable the plugin to run automatically as part of the build to 
catch these errors.
+
+[source,xml]
+----
+<plugin>
+  <groupId>org.apache.camel</groupId>
+  <artifactId>camel-yaml-dsl-validate-maven-plugin</artifactId>
+  <executions>
+    <execution>
+      <phase>process-classes</phase>
+      <goals>
+        <goal>validate</goal>
+      </goals>
+    </execution>
+  </executions>
+</plugin>
+----
+
+The phase determines when the plugin runs. In the sample above the phase is 
`process-classes` which runs after
+the compilation of the main source code.
+
+The maven plugin can also be configured to validate the test source code, 
which means that the phase should be
+changed accordingly to `process-test-classes` as shown below:
+
+[source,xml]
+----
+<plugin>
+  <groupId>org.apache.camel</groupId>
+  <artifactId>camel-yaml-dsl-validate-maven-plugin</artifactId>
+  <executions>
+    <execution>
+      <configuration>
+        <includeTest>true</includeTest>
+      </configuration>
+      <phase>process-test-classes</phase>
+      <goals>
+        <goal>validate</goal>
+      </goals>
+    </execution>
+  </executions>
+</plugin>
+----
+
+=== Running the goal on any Maven project
+
+You can also run the validate goal on any Maven project without having to add 
the plugin to the `pom.xml` file.
+Doing so requires to specify the plugin using its fully qualified name. For 
example to run the goal on
+the `main-yaml` from Apache Camel you can run
+
+[source,bash]
+----
+$cd main-yaml
+$mvn org.apache.camel:camel-yaml-dsl-validator-maven-plugin:4.18.0:validate
+----
+
+Which for example outputs (with a forced error)
+[source,text]
+----
+[INFO] --- camel-yaml-dsl-validator:4.18.0:validate (default-cli) @ 
camel-example-main-yaml ---
+[INFO] Found 
[/Users/davsclaus/workspace/camel-examples/main-yaml/src/main/resources/routes/my-route.camel.yaml]
 YAML files ...
+[INFO] Validating 1 YAML files ...
+[WARNING]
+
+Validation error detected in 1 files
+
+       File: my-route.camel.yaml
+               /0/route/from: property 'step' is not defined in the schema and 
the schema does not allow additional properties
+               /0/route/from: required property 'steps' not found
+----
+
+=== Options
+
+The maven plugin *validate* goal supports the following options which can be 
configured from the command line (use `-D` syntax), or defined in the `pom.xml` 
file in the `<configuration>` tag.
+
+|===
+| Parameter | Default Value | Description
+| skip | false | Skip the validation execution.
+| failOnError | false | Whether to fail if invalid Camel endpoints was found. 
By default the plugin logs the errors at WARN level.
+| includeTest | false | Whether to include test source code.
+| includes | | To filter the names of YAML files to only include files 
matching any of the given list of patterns (wildcard and regular expression). 
Multiple values can be separated by comma.
+| excludes | | To filter the names of YAML files to exclude files matching any 
of the given list of patterns (wildcard and regular expression). Multiple 
values can be separated by comma.
+| onlyCamelYamlExt | false | Whether to only accept files with xxx.camel.yaml 
as file name. By default, all .yaml files are accepted.
+|===
+
+For example to excludes a specific file:
+
+[source,bash]
+----
+$mvn camel-yaml-dsl-validator:validate -Dcamel.excludes=cheese.yaml
+----
+
+Notice that you must prefix the `-D` command argument with `camel.`, eg 
`camel.excludes` as the option name.
+
+=== Validating include test
+
+If you have a Maven project then you can run the plugin to validate the 
endpoints in the unit test source code as well.
+You can pass in the options using `-D` style as shown:
+
+----
+$cd myproject
+$mvn org.apache.camel:camel-yaml-dsl-validator:4.18.0:validate 
-Dcamel.includeTest=true
+----
diff --git 
a/dsl/camel-yaml-dsl/camel-yaml-dsl-maven-plugin/src/main/java/org/apache/camel/maven/dsl/yaml/GenerateYamlSchemaMojo.java
 
b/dsl/camel-yaml-dsl/camel-yaml-dsl-maven-plugin/src/main/java/org/apache/camel/maven/dsl/yaml/GenerateYamlSchemaMojo.java
index 5f1389eb43f3..46c3f031f91a 100644
--- 
a/dsl/camel-yaml-dsl/camel-yaml-dsl-maven-plugin/src/main/java/org/apache/camel/maven/dsl/yaml/GenerateYamlSchemaMojo.java
+++ 
b/dsl/camel-yaml-dsl/camel-yaml-dsl-maven-plugin/src/main/java/org/apache/camel/maven/dsl/yaml/GenerateYamlSchemaMojo.java
@@ -66,6 +66,9 @@ import org.jboss.jandex.DotName;
       threadSafe = true,
       requiresProject = false)
 public class GenerateYamlSchemaMojo extends GenerateYamlSupportMojo {
+
+    private static final String DRAFT = 
"http://json-schema.org/draft-04/schema#";;
+
     @Parameter(required = true)
     private File outputFile;
     @Parameter(defaultValue = "true")
@@ -84,7 +87,7 @@ public class GenerateYamlSchemaMojo extends 
GenerateYamlSupportMojo {
 
         final ObjectNode root = mapper.createObjectNode();
 
-        root.put("$schema", "http://json-schema.org/draft-04/schema#";);
+        root.put("$schema", DRAFT);
         root.put("type", "array");
 
         items = root.putObject("items");
diff --git a/dsl/camel-yaml-dsl/camel-yaml-dsl-validator-maven-plugin/pom.xml 
b/dsl/camel-yaml-dsl/camel-yaml-dsl-validator-maven-plugin/pom.xml
new file mode 100644
index 000000000000..09a91b578249
--- /dev/null
+++ b/dsl/camel-yaml-dsl/camel-yaml-dsl-validator-maven-plugin/pom.xml
@@ -0,0 +1,117 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+
+    Licensed to the Apache Software Foundation (ASF) under one or more
+    contributor license agreements.  See the NOTICE file distributed with
+    this work for additional information regarding copyright ownership.
+    The ASF licenses this file to You under the Apache License, Version 2.0
+    (the "License"); you may not use this file except in compliance with
+    the License.  You may obtain a copy of the License at
+
+         http://www.apache.org/licenses/LICENSE-2.0
+
+    Unless required by applicable law or agreed to in writing, software
+    distributed under the License is distributed on an "AS IS" BASIS,
+    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+    See the License for the specific language governing permissions and
+    limitations under the License.
+
+-->
+<project xmlns="http://maven.apache.org/POM/4.0.0"; 
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"; 
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 
http://maven.apache.org/xsd/maven-4.0.0.xsd";>
+    <modelVersion>4.0.0</modelVersion>
+
+    <parent>
+        <groupId>org.apache.camel</groupId>
+        <artifactId>camel-yaml-dsl-parent</artifactId>
+        <version>4.18.0-SNAPSHOT</version>
+    </parent>
+
+    <artifactId>camel-yaml-dsl-validator-maven-plugin</artifactId>
+    <packaging>maven-plugin</packaging>
+
+    <name>Camel :: YAML DSL :: Validator :: Maven Plugins</name>
+    <description>Maven plugin to validate YAML DSL</description>
+
+    <properties>
+        <firstVersion>4.18.0</firstVersion>
+        <camel-prepare-component>false</camel-prepare-component>
+    </properties>
+
+    <dependencies>
+        <dependency>
+            <groupId>org.apache.camel</groupId>
+            <artifactId>camel-yaml-dsl-validator</artifactId>
+        </dependency>
+
+        <dependency>
+            <groupId>org.apache.maven</groupId>
+            <artifactId>maven-core</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.apache.maven</groupId>
+            <artifactId>maven-plugin-api</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.apache.maven</groupId>
+            <artifactId>maven-compat</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.apache.maven.plugin-tools</groupId>
+            <artifactId>maven-plugin-annotations</artifactId>
+        </dependency>
+
+        <dependency>
+            <groupId>org.apache.maven</groupId>
+            <artifactId>maven-settings-builder</artifactId>
+            <version>${maven-version}</version>
+            <scope>provided</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.apache.maven</groupId>
+            <artifactId>maven-builder-support</artifactId>
+            <version>${maven-version}</version>
+            <scope>provided</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.apache.maven</groupId>
+            <artifactId>maven-model-builder</artifactId>
+            <version>${maven-version}</version>
+            <scope>provided</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.apache.maven</groupId>
+            <artifactId>maven-resolver-provider</artifactId>
+            <version>${maven-version}</version>
+            <scope>provided</scope>
+        </dependency>
+
+    </dependencies>
+
+    <build>
+        <plugins>
+            <plugin>
+                <groupId>org.apache.maven.plugins</groupId>
+                <artifactId>maven-plugin-plugin</artifactId>
+                <configuration>
+                    <goalPrefix>camel-yaml-dsl-validator</goalPrefix>
+                    <mojoDependencies>
+                        <dep>org.codehaus.mojo:exec-maven-plugin</dep>
+                        <dep>org.apache.maven:maven-plugin-api</dep>
+                    </mojoDependencies>
+                </configuration>
+            </plugin>
+            <plugin>
+                <groupId>org.eclipse.sisu</groupId>
+                <artifactId>sisu-maven-plugin</artifactId>
+                <executions>
+                    <execution>
+                        <id>generate-index</id>
+                        <goals>
+                            <goal>main-index</goal>
+                        </goals>
+                    </execution>
+                </executions>
+            </plugin>
+        </plugins>
+    </build>
+</project>
diff --git 
a/dsl/camel-yaml-dsl/camel-yaml-dsl-validator-maven-plugin/src/main/docs/camel-yaml-dsl-validator-maven-plugin.adoc
 
b/dsl/camel-yaml-dsl/camel-yaml-dsl-validator-maven-plugin/src/main/docs/camel-yaml-dsl-validator-maven-plugin.adoc
new file mode 100644
index 000000000000..99678fafadfd
--- /dev/null
+++ 
b/dsl/camel-yaml-dsl/camel-yaml-dsl-validator-maven-plugin/src/main/docs/camel-yaml-dsl-validator-maven-plugin.adoc
@@ -0,0 +1,119 @@
+= Camel YAML DSL Validator Maven Plugin
+
+The Camel YAML DSL Validator Maven Plugin supports the following goals
+
+ - camel-yaml-dsl-validator:validate - To validate YAML routes are correct 
according to spec
+
+== camel-yaml-dsl-validator:validate
+
+For validating the YAML routes for syntax errors according to the spec.
+
+Then you can run the `validate` goal from the command line or from within your 
Java editor such as IDEA or Eclipse.
+
+[source,bash]
+----
+mvn camel-yaml-dsl-validator:validate
+----
+
+You can also enable the plugin to run automatically as part of the build to 
catch these errors.
+
+[source,xml]
+----
+<plugin>
+  <groupId>org.apache.camel</groupId>
+  <artifactId>camel-yaml-dsl-validate-maven-plugin</artifactId>
+  <executions>
+    <execution>
+      <phase>process-classes</phase>
+      <goals>
+        <goal>validate</goal>
+      </goals>
+    </execution>
+  </executions>
+</plugin>
+----
+
+The phase determines when the plugin runs. In the sample above the phase is 
`process-classes` which runs after
+the compilation of the main source code.
+
+The maven plugin can also be configured to validate the test source code, 
which means that the phase should be
+changed accordingly to `process-test-classes` as shown below:
+
+[source,xml]
+----
+<plugin>
+  <groupId>org.apache.camel</groupId>
+  <artifactId>camel-yaml-dsl-validate-maven-plugin</artifactId>
+  <executions>
+    <execution>
+      <configuration>
+        <includeTest>true</includeTest>
+      </configuration>
+      <phase>process-test-classes</phase>
+      <goals>
+        <goal>validate</goal>
+      </goals>
+    </execution>
+  </executions>
+</plugin>
+----
+
+=== Running the goal on any Maven project
+
+You can also run the validate goal on any Maven project without having to add 
the plugin to the `pom.xml` file.
+Doing so requires to specify the plugin using its fully qualified name. For 
example to run the goal on
+the `main-yaml` from Apache Camel you can run
+
+[source,bash]
+----
+$cd main-yaml
+$mvn org.apache.camel:camel-yaml-dsl-validator-maven-plugin:4.18.0:validate
+----
+
+Which for example outputs (with a forced error)
+[source,text]
+----
+[INFO] --- camel-yaml-dsl-validator:4.18.0:validate (default-cli) @ 
camel-example-main-yaml ---
+[INFO] Found 
[/Users/davsclaus/workspace/camel-examples/main-yaml/src/main/resources/routes/my-route.camel.yaml]
 YAML files ...
+[INFO] Validating 1 YAML files ...
+[WARNING]
+
+Validation error detected in 1 files
+
+       File: my-route.camel.yaml
+               /0/route/from: property 'step' is not defined in the schema and 
the schema does not allow additional properties
+               /0/route/from: required property 'steps' not found
+----
+
+=== Options
+
+The maven plugin *validate* goal supports the following options which can be 
configured from the command line (use `-D` syntax), or defined in the `pom.xml` 
file in the `<configuration>` tag.
+
+|===
+| Parameter | Default Value | Description
+| skip | false | Skip the validation execution.
+| failOnError | false | Whether to fail if invalid Camel endpoints was found. 
By default the plugin logs the errors at WARN level.
+| includeTest | false | Whether to include test source code.
+| includes | | To filter the names of YAML files to only include files 
matching any of the given list of patterns (wildcard and regular expression). 
Multiple values can be separated by comma.
+| excludes | | To filter the names of YAML files to exclude files matching any 
of the given list of patterns (wildcard and regular expression). Multiple 
values can be separated by comma.
+| onlyCamelYamlExt | false | Whether to only accept files with xxx.camel.yaml 
as file name. By default, all .yaml files are accepted.
+|===
+
+For example to excludes a specific file:
+
+[source,bash]
+----
+$mvn camel-yaml-dsl-validator:validate -Dcamel.excludes=cheese.yaml
+----
+
+Notice that you must prefix the `-D` command argument with `camel.`, eg 
`camel.excludes` as the option name.
+
+=== Validating include test
+
+If you have a Maven project then you can run the plugin to validate the 
endpoints in the unit test source code as well.
+You can pass in the options using `-D` style as shown:
+
+----
+$cd myproject
+$mvn org.apache.camel:camel-yaml-dsl-validator:4.18.0:validate 
-Dcamel.includeTest=true
+----
diff --git 
a/dsl/camel-yaml-dsl/camel-yaml-dsl-validator-maven-plugin/src/main/java/org/apache/camel/dsl/yaml/validator/ValidateMojo.java
 
b/dsl/camel-yaml-dsl/camel-yaml-dsl-validator-maven-plugin/src/main/java/org/apache/camel/dsl/yaml/validator/ValidateMojo.java
new file mode 100644
index 000000000000..bea0fe93e0bb
--- /dev/null
+++ 
b/dsl/camel-yaml-dsl/camel-yaml-dsl-validator-maven-plugin/src/main/java/org/apache/camel/dsl/yaml/validator/ValidateMojo.java
@@ -0,0 +1,291 @@
+/*
+ * 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.dsl.yaml.validator;
+
+import java.io.File;
+import java.util.ArrayList;
+import java.util.LinkedHashMap;
+import java.util.LinkedHashSet;
+import java.util.List;
+import java.util.Locale;
+import java.util.Map;
+import java.util.Set;
+
+import javax.inject.Inject;
+
+import com.networknt.schema.ValidationMessage;
+import org.apache.camel.support.PatternHelper;
+import org.apache.camel.util.FileUtil;
+import org.apache.maven.execution.MavenSession;
+import org.apache.maven.model.Resource;
+import org.apache.maven.plugin.AbstractMojo;
+import org.apache.maven.plugin.MojoExecutionException;
+import org.apache.maven.plugins.annotations.Mojo;
+import org.apache.maven.plugins.annotations.Parameter;
+import org.apache.maven.project.MavenProject;
+import org.eclipse.aether.RepositorySystem;
+import org.eclipse.aether.RepositorySystemSession;
+
+@Mojo(name = "validate", threadSafe = true)
+public class ValidateMojo extends AbstractMojo {
+
+    private final YamlValidator validator = new YamlValidator();
+
+    private static final String IGNORE_FILE = "application.yml";
+
+    /**
+     * The maven project.
+     */
+    @Parameter(property = "project", required = true, readonly = true)
+    protected MavenProject project;
+
+    @Parameter(defaultValue = "${session}", readonly = true, required = true)
+    private MavenSession session;
+
+    /**
+     * Skip the validation execution.
+     */
+    @Parameter(property = "camel.skipValidation", defaultValue = "false")
+    private boolean skip;
+
+    /**
+     * Whether to fail if validation reported errors. By default, the plugin 
logs the errors at WARN level
+     */
+    @Parameter(property = "camel.failOnError", defaultValue = "false")
+    private boolean failOnError;
+
+    @Parameter(defaultValue = "${project.build.directory}")
+    private String projectBuildDir;
+
+    /**
+     * Whether to only accept files with xxx.camel.yaml as file name. By 
default, all .yaml files are accepted.
+     */
+    @Parameter(property = "camel.onlyCamelYamlExt")
+    private boolean onlyCamelYamlExt;
+
+    /**
+     * Whether to include test source code
+     */
+    @Parameter(property = "camel.includeTest", defaultValue = "false")
+    private boolean includeTest;
+
+    /**
+     * To filter the names of YAML files to only include files matching any of 
the given list of patterns (wildcard and
+     * regular expression). Multiple values can be separated by comma.
+     */
+    @Parameter(property = "camel.includes")
+    private String includes;
+
+    /**
+     * To filter the names of YAML files to exclude files matching any of the 
given pattern in the list (wildcard and
+     * regular expression). Multiple values can be separated by comma.
+     */
+    @Parameter(property = "camel.excludes")
+    private String excludes;
+
+    /**
+     * yamlFiles in memory cache, useful for multi modules maven projects
+     */
+    private static final Set<File> yamlFiles = new LinkedHashSet<>();
+
+    private final RepositorySystem repositorySystem;
+
+    @Parameter(defaultValue = "${repositorySystemSession}", readonly = true)
+    private RepositorySystemSession repositorySystemSession;
+
+    @Inject
+    public ValidateMojo(RepositorySystem repositorySystem) {
+        this.repositorySystem = repositorySystem;
+    }
+
+    @Override
+    public void execute() throws MojoExecutionException {
+        if (skip) {
+            getLog().info("skipping YAML DSL validation as per configuration");
+            return;
+        }
+
+        // find all XML routes
+        String ext = onlyCamelYamlExt ? ".camel.yaml" : ".yaml";
+        findYamlRouters(yamlFiles, includeTest, ext, project);
+        getLog().debug("Found " + yamlFiles.size() + " YAML files ...");
+
+        Map<File, List<ValidationMessage>> reports = new LinkedHashMap<>();
+        List<File> matched = new ArrayList<>();
+        for (File file : yamlFiles) {
+            if (matchFile(file)) {
+                matched.add(file);
+            }
+        }
+        if (!matched.isEmpty()) {
+            getLog().info("Validating " + matched.size() + " YAML files ...");
+            try {
+                validator.init();
+                for (File file : matched) {
+                    var report = validateYamlRoute(file);
+                    reports.put(file, report);
+                }
+            } catch (Exception e) {
+                throw new MojoExecutionException(e);
+            }
+        }
+
+        validateResults(reports);
+    }
+
+    private void validateResults(Map<File, List<ValidationMessage>> reports) 
throws MojoExecutionException {
+        int count = errorCounts(reports);
+        if (count == 0) {
+            getLog().info("Validation success");
+            return;
+        }
+        StringBuilder sb = new StringBuilder();
+        sb.append("Validation error detected in ").append(count).append(" 
files\n\n");
+
+        for (var e : reports.entrySet()) {
+            String name = e.getKey().getName();
+            var report = e.getValue();
+
+            sb.append("\tFile: ").append(name).append("\n");
+            for (var r : report) {
+                sb.append("\t\t").append(r.toString()).append("\n");
+            }
+            sb.append("\n");
+        }
+        getLog().warn("\n\n" + sb + "\n\n");
+
+        if (failOnError) {
+            throw new MojoExecutionException(sb.toString());
+        }
+    }
+
+    private int errorCounts(Map<File, List<ValidationMessage>> reports) {
+        int count = 0;
+        for (List<ValidationMessage> list : reports.values()) {
+            if (!list.isEmpty()) {
+                count++;
+            }
+        }
+        return count;
+    }
+
+    private List<ValidationMessage> validateYamlRoute(File file) throws 
Exception {
+        getLog().debug("Validating YAML DSL in file: " + file);
+        return validator.validate(file);
+    }
+
+    private boolean matchFile(File file) {
+        String no = FileUtil.onlyName(file.getName());
+        if (IGNORE_FILE.equals(no)) {
+            return false;
+        }
+        return matchRouteFile(file, excludes, includes, project);
+    }
+
+    public static boolean matchRouteFile(File file, String excludes, String 
includes, MavenProject project) {
+        if (excludes == null && includes == null) {
+            return true;
+        } else if (excludes != null && fileListMatchesPattern(excludes, file, 
project)) {
+            return false;
+        } else {
+            return includes != null ? fileListMatchesPattern(includes, file, 
project) : true;
+        }
+    }
+
+    public static boolean fileListMatchesPattern(String fileList, File file, 
MavenProject project) {
+        for (String fileName : fileList.split(",")) {
+            fileName = fileName.trim();
+            String fqn = stripRootPath(asRelativeFile(file.getAbsolutePath(), 
project), project);
+            boolean match = PatternHelper.matchPattern(fqn, fileName) || 
PatternHelper.matchPattern(file.getName(), fileName);
+            if (match) {
+                return true;
+            }
+        }
+        return false;
+    }
+
+    private static String stripRootPath(String name, MavenProject project) {
+        for (String dir : project.getCompileSourceRoots()) {
+            dir = asRelativeFile(dir, project);
+            if (name.startsWith(dir)) {
+                return name.substring(dir.length() + 1);
+            }
+        }
+
+        for (String dir : project.getTestCompileSourceRoots()) {
+            dir = asRelativeFile(dir, project);
+            if (name.startsWith(dir)) {
+                return name.substring(dir.length() + 1);
+            }
+        }
+
+        for (Resource resource : project.getResources()) {
+            String dir = asRelativeFile(resource.getDirectory(), project);
+            if (name.startsWith(dir)) {
+                return name.substring(dir.length() + 1);
+            }
+        }
+
+        for (Resource resource : project.getTestResources()) {
+            String dir = asRelativeFile(resource.getDirectory(), project);
+            if (name.startsWith(dir)) {
+                return name.substring(dir.length() + 1);
+            }
+        }
+
+        return name;
+    }
+
+    private static String asRelativeFile(String name, MavenProject project) {
+        String answer = name;
+        String base = project.getBasedir().getAbsolutePath();
+        if (name.startsWith(base)) {
+            answer = name.substring(base.length());
+            if (answer.startsWith(File.separator)) {
+                answer = answer.substring(1);
+            }
+        }
+
+        return answer;
+    }
+
+    private static void findYamlRouters(Set<File> yamlFiles, boolean 
includeTest, String ext, MavenProject project) {
+        for (Resource dir : project.getResources()) {
+            finYamlFiles(new File(dir.getDirectory()), ext, yamlFiles);
+        }
+        if (includeTest) {
+            for (Resource dir : project.getTestResources()) {
+                finYamlFiles(new File(dir.getDirectory()), ext, yamlFiles);
+            }
+        }
+    }
+
+    private static void finYamlFiles(File dir, String ext, Set<File> 
yamlFiles) {
+        File[] files = dir.isDirectory() ? dir.listFiles() : null;
+        if (files != null) {
+            for (File file : files) {
+                String name = file.getName().toLowerCase(Locale.ROOT);
+                if (name.endsWith(ext)) {
+                    yamlFiles.add(file);
+                } else if (file.isDirectory()) {
+                    finYamlFiles(file, ext, yamlFiles);
+                }
+            }
+        }
+    }
+
+}
diff --git a/dsl/camel-yaml-dsl/camel-yaml-dsl-validator/pom.xml 
b/dsl/camel-yaml-dsl/camel-yaml-dsl-validator/pom.xml
new file mode 100644
index 000000000000..d508387a7358
--- /dev/null
+++ b/dsl/camel-yaml-dsl/camel-yaml-dsl-validator/pom.xml
@@ -0,0 +1,80 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+
+    Licensed to the Apache Software Foundation (ASF) under one or more
+    contributor license agreements.  See the NOTICE file distributed with
+    this work for additional information regarding copyright ownership.
+    The ASF licenses this file to You under the Apache License, Version 2.0
+    (the "License"); you may not use this file except in compliance with
+    the License.  You may obtain a copy of the License at
+
+         http://www.apache.org/licenses/LICENSE-2.0
+
+    Unless required by applicable law or agreed to in writing, software
+    distributed under the License is distributed on an "AS IS" BASIS,
+    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+    See the License for the specific language governing permissions and
+    limitations under the License.
+
+-->
+<project xmlns="http://maven.apache.org/POM/4.0.0"; 
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"; 
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 
http://maven.apache.org/maven-v4_0_0.xsd";>
+
+    <modelVersion>4.0.0</modelVersion>
+
+    <parent>
+        <groupId>org.apache.camel</groupId>
+        <artifactId>camel-yaml-dsl-parent</artifactId>
+        <version>4.18.0-SNAPSHOT</version>
+    </parent>
+
+    <artifactId>camel-yaml-dsl-validator</artifactId>
+    <packaging>jar</packaging>
+    <name>Camel :: YAML DSL :: Validator</name>
+    <description>Camel DSL with YAML Validator</description>
+
+    <properties>
+        <firstVersion>4.18.0</firstVersion>
+        <camel-prepare-component>false</camel-prepare-component>
+    </properties>
+
+    <dependencies>
+        <dependency>
+            <groupId>org.apache.camel</groupId>
+            <artifactId>camel-yaml-dsl</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>com.networknt</groupId>
+            <artifactId>json-schema-validator</artifactId>
+            <version>${networknt-json-schema-validator-version}</version>
+        </dependency>
+
+        <dependency>
+            <groupId>org.junit.jupiter</groupId>
+            <artifactId>junit-jupiter</artifactId>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.assertj</groupId>
+            <artifactId>assertj-core</artifactId>
+            <scope>test</scope>
+        </dependency>
+
+        <dependency>
+            <groupId>org.apache.logging.log4j</groupId>
+            <artifactId>log4j-core</artifactId>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.apache.logging.log4j</groupId>
+            <artifactId>log4j-slf4j2-impl</artifactId>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.apache.logging.log4j</groupId>
+            <artifactId>log4j-jcl</artifactId>
+            <scope>test</scope>
+        </dependency>
+
+    </dependencies>
+
+</project>
diff --git 
a/dsl/camel-yaml-dsl/camel-yaml-dsl-validator/src/main/java/org/apache/camel/dsl/yaml/validator/YamlValidator.java
 
b/dsl/camel-yaml-dsl/camel-yaml-dsl-validator/src/main/java/org/apache/camel/dsl/yaml/validator/YamlValidator.java
new file mode 100644
index 000000000000..c28edeef540b
--- /dev/null
+++ 
b/dsl/camel-yaml-dsl/camel-yaml-dsl-validator/src/main/java/org/apache/camel/dsl/yaml/validator/YamlValidator.java
@@ -0,0 +1,82 @@
+/*
+ * 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.dsl.yaml.validator;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.InputStream;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Locale;
+
+import com.fasterxml.jackson.databind.JsonNode;
+import com.fasterxml.jackson.databind.ObjectMapper;
+import com.fasterxml.jackson.dataformat.yaml.YAMLFactory;
+import com.networknt.schema.JsonMetaSchema;
+import com.networknt.schema.JsonSchema;
+import com.networknt.schema.JsonSchemaFactory;
+import com.networknt.schema.NonValidationKeyword;
+import com.networknt.schema.SchemaValidatorsConfig;
+import com.networknt.schema.SpecVersionDetector;
+import com.networknt.schema.ValidationMessage;
+
+public class YamlValidator {
+
+    private static final String DRAFT = 
"http://json-schema.org/draft-04/schema#";;
+
+    // TODO: yaml-dsl-parser to see if its validate camel
+
+    private ObjectMapper mapper = new ObjectMapper(new YAMLFactory());
+    private JsonNode model;
+    private JsonSchemaFactory factory;
+    private SchemaValidatorsConfig config;
+    private JsonSchema schema;
+
+    private String jsonSchema = "/schema/camelYamlDsl.json";
+
+    public String getJsonSchema() {
+        return jsonSchema;
+    }
+
+    /**
+     * The schema to use for validating
+     */
+    public void setJsonSchema(String jsonSchema) {
+        this.jsonSchema = jsonSchema;
+    }
+
+    public List<ValidationMessage> validate(File file) throws Exception {
+        if (schema == null) {
+            init();
+        }
+        try (InputStream is = new FileInputStream(file)) {
+            var target = mapper.readTree(is);
+            return new ArrayList<>(schema.validate(target));
+        }
+    }
+
+    public void init() throws Exception {
+        model = 
mapper.readTree(YamlValidator.class.getResourceAsStream(jsonSchema));
+        factory = 
JsonSchemaFactory.getInstance(SpecVersionDetector.detect(model));
+        config = 
SchemaValidatorsConfig.builder().locale(Locale.ENGLISH).build();
+        // include deprecated as an unknown keyword so the validator does not 
WARN log about this
+        JsonMetaSchema jms = factory.getMetaSchema(DRAFT, null);
+        jms.getKeywords().put("deprecated", new 
NonValidationKeyword("deprecated"));
+        schema = factory.getSchema(model, config);
+    }
+
+}
diff --git 
a/dsl/camel-yaml-dsl/camel-yaml-dsl-validator/src/test/java/org/apache/camel/dsl/yaml/validator/YamlValidatorTest.java
 
b/dsl/camel-yaml-dsl/camel-yaml-dsl-validator/src/test/java/org/apache/camel/dsl/yaml/validator/YamlValidatorTest.java
new file mode 100644
index 000000000000..d53e7a107dc3
--- /dev/null
+++ 
b/dsl/camel-yaml-dsl/camel-yaml-dsl-validator/src/test/java/org/apache/camel/dsl/yaml/validator/YamlValidatorTest.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.dsl.yaml.validator;
+
+import java.io.File;
+
+import org.junit.jupiter.api.Assertions;
+import org.junit.jupiter.api.BeforeAll;
+import org.junit.jupiter.api.Test;
+
+public class YamlValidatorTest {
+
+    private static YamlValidator validator;
+
+    @BeforeAll
+    public static void setup() throws Exception {
+        validator = new YamlValidator();
+        validator.init();
+    }
+
+    @Test
+    public void testValidateOk() throws Exception {
+        Assertions.assertTrue(validator.validate(new 
File("src/test/resources/foo.yaml")).isEmpty());
+    }
+
+    @Test
+    public void testValidateBad() throws Exception {
+        var report = validator.validate(new 
File("src/test/resources/bad.yaml"));
+        Assertions.assertFalse(report.isEmpty());
+        Assertions.assertEquals(1, report.size());
+        
Assertions.assertTrue(report.get(0).getMessage().contains("setCheese"));
+        System.out.println(report);
+    }
+}
diff --git 
a/dsl/camel-yaml-dsl/camel-yaml-dsl-validator/src/test/resources/bad.yaml 
b/dsl/camel-yaml-dsl/camel-yaml-dsl-validator/src/test/resources/bad.yaml
new file mode 100644
index 000000000000..5e660e31edd0
--- /dev/null
+++ b/dsl/camel-yaml-dsl/camel-yaml-dsl-validator/src/test/resources/bad.yaml
@@ -0,0 +1,26 @@
+#
+# Licensed to the Apache Software Foundation (ASF) under one or more
+# contributor license agreements.  See the NOTICE file distributed with
+# this work for additional information regarding copyright ownership.
+# The ASF licenses this file to You under the Apache License, Version 2.0
+# (the "License"); you may not use this file except in compliance with
+# the License.  You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+- route:
+    from:
+      uri: timer:yaml
+      parameters:
+        period: "1000"
+      steps:
+        - setCheese:
+            simple: Hello Camel from ${routeId}
+        - log: ${body}
\ No newline at end of file
diff --git 
a/dsl/camel-yaml-dsl/camel-yaml-dsl-validator/src/test/resources/foo.yaml 
b/dsl/camel-yaml-dsl/camel-yaml-dsl-validator/src/test/resources/foo.yaml
new file mode 100644
index 000000000000..d239b6ba3fc1
--- /dev/null
+++ b/dsl/camel-yaml-dsl/camel-yaml-dsl-validator/src/test/resources/foo.yaml
@@ -0,0 +1,26 @@
+#
+# Licensed to the Apache Software Foundation (ASF) under one or more
+# contributor license agreements.  See the NOTICE file distributed with
+# this work for additional information regarding copyright ownership.
+# The ASF licenses this file to You under the Apache License, Version 2.0
+# (the "License"); you may not use this file except in compliance with
+# the License.  You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+- route:
+    from:
+      uri: timer:yaml
+      parameters:
+        period: "1000"
+      steps:
+        - setBody:
+            simple: Hello Camel from ${routeId}
+        - log: ${body}
\ No newline at end of file
diff --git 
a/dsl/camel-yaml-dsl/camel-yaml-dsl-validator/src/test/resources/log4j2.properties
 
b/dsl/camel-yaml-dsl/camel-yaml-dsl-validator/src/test/resources/log4j2.properties
new file mode 100644
index 000000000000..492c47dd3f71
--- /dev/null
+++ 
b/dsl/camel-yaml-dsl/camel-yaml-dsl-validator/src/test/resources/log4j2.properties
@@ -0,0 +1,31 @@
+## ---------------------------------------------------------------------------
+## 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.
+## ---------------------------------------------------------------------------
+
+appender.file.type = File
+appender.file.name = file
+appender.file.fileName = target/camel-yaml-dsl-validator-test.log
+appender.file.layout.type = PatternLayout
+appender.file.layout.pattern = %d [%-15.15t] %-5p %-30.30c{1} - %m%n
+
+appender.out.type = Console
+appender.out.name = out
+appender.out.layout.type = PatternLayout
+appender.out.layout.pattern = [%30.30t] %-30.30c{1} %-5p %m%n
+
+rootLogger.level = INFO
+rootLogger.appenderRef.file.ref = file
+#rootLogger.appenderRef.out.ref = out
\ No newline at end of file
diff --git a/dsl/camel-yaml-dsl/pom.xml b/dsl/camel-yaml-dsl/pom.xml
index f449b5f0d6c2..5becaa137159 100644
--- a/dsl/camel-yaml-dsl/pom.xml
+++ b/dsl/camel-yaml-dsl/pom.xml
@@ -37,6 +37,8 @@
         <module>camel-yaml-dsl-common</module>
         <module>camel-yaml-dsl-deserializers</module>
         <module>camel-yaml-dsl</module>
+        <module>camel-yaml-dsl-validator</module>
+        <module>camel-yaml-dsl-validator-maven-plugin</module>
     </modules>
 
 </project>
\ No newline at end of file
diff --git a/parent/pom.xml b/parent/pom.xml
index fe4a4e0a0c79..b09d582d9c73 100644
--- a/parent/pom.xml
+++ b/parent/pom.xml
@@ -3081,6 +3081,11 @@
                 <artifactId>camel-yaml-dsl-deserializers</artifactId>
                 <version>${project.version}</version>
             </dependency>
+            <dependency>
+                <groupId>org.apache.camel</groupId>
+                <artifactId>camel-yaml-dsl-validator</artifactId>
+                <version>${project.version}</version>
+            </dependency>
             <dependency>
                 <groupId>org.apache.camel</groupId>
                 <artifactId>camel-jbang-core</artifactId>


Reply via email to