You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@jackrabbit.apache.org by kw...@apache.org on 2021/10/13 07:45:27 UTC

[jackrabbit-filevault-package-maven-plugin] branch bugfix/JCRVLT-564-fix-default-value-for-packagetype-validator created (now 486863c)

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

kwin pushed a change to branch bugfix/JCRVLT-564-fix-default-value-for-packagetype-validator
in repository https://gitbox.apache.org/repos/asf/jackrabbit-filevault-package-maven-plugin.git.


      at 486863c  JCRVLT-564 fix default value for validator jackrabbit-packagetype

This branch includes the following new commits:

     new 486863c  JCRVLT-564 fix default value for validator jackrabbit-packagetype

The 1 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.


[jackrabbit-filevault-package-maven-plugin] 01/01: JCRVLT-564 fix default value for validator jackrabbit-packagetype

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

kwin pushed a commit to branch bugfix/JCRVLT-564-fix-default-value-for-packagetype-validator
in repository https://gitbox.apache.org/repos/asf/jackrabbit-filevault-package-maven-plugin.git

commit 486863cbe3c2b17c51bd0817f7f4259134c0ef7c
Author: Konrad Windszus <kw...@apache.org>
AuthorDate: Wed Oct 13 09:45:18 2021 +0200

    JCRVLT-564 fix default value for validator jackrabbit-packagetype
    
    merge validator settings for the same id correctly (from most specific
    to most generic key)
---
 .../maven/packaging/AbstractValidateMojo.java      |  83 ++++++-----
 .../maven/packaging/ValidatePackageMojo.java       |   8 +-
 .../maven/packaging/ValidatorSettings.java         |  56 ++++----
 .../maven/packaging/ValidatorSettingsKey.java      | 155 +++++++++++++++++++++
 .../maven/packaging/AbstractValidateMojoTest.java  |   8 +-
 .../maven/packaging/ValidatorSettingsKeyTest.java  |  62 +++++++++
 .../maven/packaging/ValidatorSettingsTest.java     |  44 ++++++
 7 files changed, 348 insertions(+), 68 deletions(-)

diff --git a/src/main/java/org/apache/jackrabbit/filevault/maven/packaging/AbstractValidateMojo.java b/src/main/java/org/apache/jackrabbit/filevault/maven/packaging/AbstractValidateMojo.java
index be332fd..49bfa92 100644
--- a/src/main/java/org/apache/jackrabbit/filevault/maven/packaging/AbstractValidateMojo.java
+++ b/src/main/java/org/apache/jackrabbit/filevault/maven/packaging/AbstractValidateMojo.java
@@ -24,6 +24,7 @@ import java.util.Collection;
 import java.util.Comparator;
 import java.util.HashMap;
 import java.util.LinkedList;
+import java.util.List;
 import java.util.Locale;
 import java.util.Map;
 import java.util.stream.Collectors;
@@ -40,6 +41,7 @@ import org.apache.jackrabbit.vault.validation.ValidationExecutorFactory;
 import org.apache.jackrabbit.vault.validation.spi.ValidationMessageSeverity;
 import org.apache.jackrabbit.vault.validation.spi.impl.AdvancedFilterValidatorFactory;
 import org.apache.jackrabbit.vault.validation.spi.impl.DependencyValidatorFactory;
+import org.apache.jackrabbit.vault.validation.spi.impl.PackageTypeValidatorFactory;
 import org.apache.maven.artifact.Artifact;
 import org.apache.maven.artifact.DefaultArtifact;
 import org.apache.maven.artifact.repository.DefaultRepositoryRequest;
@@ -77,6 +79,13 @@ public abstract class AbstractValidateMojo extends AbstractMojo {
      *      &lt;/options&gt;
      *  &lt;/jackrabbit-filter&gt;
      * </pre>
+     * As potentially multiple map entries may affect the same validator id (due to different suffixes) the settings for a single validator id are merged in the order from more generic to more specific keys:
+     * <ol>
+     * <li>settings without restrictions</li>
+     * <li>settings for subpackages</li>
+     * <li>settings for any groupId (*) and a specific artifactId</li>
+     * <li>settings for a specific groupId and artifactId</li>
+     * </ol>
      */
     @Parameter
     private Map<String, ValidatorSettings> validatorsSettings;
@@ -219,6 +228,7 @@ public abstract class AbstractValidateMojo extends AbstractMojo {
             return;
         }
         translateLegacyParametersToValidatorParameters();
+        fixWrongDefaultForValidatorParameters();
         final Collection<PackageInfo> resolvedDependencies = new LinkedList<>();
         
         // repository structure only defines valid roots
@@ -301,7 +311,8 @@ public abstract class AbstractValidateMojo extends AbstractMojo {
             }
 
             // do no fully disable but emit violations with level DEBUG
-            ValidatorSettings dependencyValidatorSettings = new ValidatorSettings(ValidationMessageSeverity.DEBUG);
+            ValidatorSettings dependencyValidatorSettings = new ValidatorSettings();
+            dependencyValidatorSettings.setDefaultSeverity(ValidationMessageSeverity.DEBUG);
             validatorsSettings.put(DependencyValidatorFactory.ID, dependencyValidatorSettings);
 
             ValidatorSettings filterValidatorSettings = validatorsSettings.containsKey(AdvancedFilterValidatorFactory.ID) ? validatorsSettings.get(AdvancedFilterValidatorFactory.ID) : new ValidatorSettings();
@@ -313,51 +324,51 @@ public abstract class AbstractValidateMojo extends AbstractMojo {
         }
     }
 
+    // https://issues.apache.org/jira/browse/JCRVLT-564
+    // TODO: remove once depending on FileVault 3.5.5 or newer
+    private void fixWrongDefaultForValidatorParameters() throws MojoExecutionException {
+       if (validatorsSettings == null) {
+           validatorsSettings = new HashMap<>();
+       }
+    
+       ValidatorSettings packageTypeValidatorSettings = validatorsSettings.get("jackrabbit-packagetype");
+       if (packageTypeValidatorSettings == null) {
+           packageTypeValidatorSettings = new ValidatorSettings();
+           validatorsSettings.put("jackrabbit-packagetype", packageTypeValidatorSettings);
+       }
+       if (!packageTypeValidatorSettings.getOptions().containsKey(PackageTypeValidatorFactory.OPTION_JCR_INSTALLER_ADDITIONAL_FILE_NODE_PATH_REGEX)) {
+           packageTypeValidatorSettings.addOption(PackageTypeValidatorFactory.OPTION_JCR_INSTALLER_ADDITIONAL_FILE_NODE_PATH_REGEX, ".*\\.(config|cfg|cfg\\.json|jar)");
+       }
+    }
+
     public abstract void doExecute(ValidationHelper validationHelper) throws MojoExecutionException, MojoFailureException;
 
-    protected Map<String, ValidatorSettings> getValidatorSettingsForPackage(PackageId packageId, boolean isSubPackage) {
-        return getValidatorSettingsForPackage(getLog(), validatorsSettings, packageId, isSubPackage);
+    protected Map<String, ValidatorSettings> getValidatorSettingsForPackage(PackageId packageId, boolean isSubPackage) throws MojoFailureException {
+        try {
+            return getValidatorSettingsForPackage(getLog(), validatorsSettings, packageId, isSubPackage);
+        }
+        catch (IllegalArgumentException e) {
+            throw new MojoFailureException("Invalid value for 'validatorsSettings': " + e.getMessage(), e);
+        }
     }
 
-    static Map<String, ValidatorSettings> getValidatorSettingsForPackage(Log log, Map<String, ValidatorSettings> validatorsSettings, PackageId packageId, boolean isSubPackage) {
+    protected static Map<String, ValidatorSettings> getValidatorSettingsForPackage(Log log, Map<String, ValidatorSettings> validatorsSettings, PackageId packageId, boolean isSubPackage) {
         Map<String, ValidatorSettings> validatorSettingsById = new HashMap<>();
         if (validatorsSettings == null) {
             return validatorSettingsById;
         }
-        for (Map.Entry<String, ValidatorSettings> validatorSettingByIdAndPackage : validatorsSettings.entrySet()) {
-            // does this setting belong to this package?
-            boolean shouldAdd = false;
-            String[] parts = validatorSettingByIdAndPackage.getKey().split(":", 3);
-            final String validatorId = parts[0];
-            
-            if (parts.length == 2) {
-                if (parts[1].equals("subpackage")) {
-                    shouldAdd = isSubPackage;
+        // from most specific to least specific
+        List<ValidatorSettingsKey> sortedKeys = validatorsSettings.keySet().stream().map(ValidatorSettingsKey::fromString).sorted(Comparator.reverseOrder()).collect(Collectors.toList());
+        
+        for (ValidatorSettingsKey key : sortedKeys) {
+            if (key.matchesForPackage(packageId, isSubPackage)) {
+                ValidatorSettings settings = validatorSettingsById.get(key.getValidatorId());
+                if (settings != null) {
+                    settings = settings.merge(validatorsSettings.get(key.getKey()));
                 } else {
-                    log.warn("Invalid validatorSettings key '" + validatorSettingByIdAndPackage.getKey() +"'" );
-                    continue;
+                    settings = validatorsSettings.get(key.getKey());
                 }
-            }
-            // does it contain a package id filter?
-            else if (parts.length == 3) {
-                String group = parts[1];
-                String name = parts[2];
-                if (!"*".equals(group)) {
-                    if (!group.equals(packageId.getGroup())) {
-                        log.debug("Not applying validator settings with id '" + validatorSettingByIdAndPackage.getKey() +"' as it does not match the package " + packageId);
-                        continue;
-                    }
-                }
-                if (!name.equals(packageId.getName())) {
-                    log.debug("Not applying validator settings with id '" + validatorSettingByIdAndPackage.getKey() +"' as it does not match the package " + packageId);
-                    continue;
-                }
-                shouldAdd = true;
-            } else {
-                shouldAdd = true;
-            }
-            if (shouldAdd) {
-                validatorSettingsById.put(validatorId, validatorSettingByIdAndPackage.getValue());
+                validatorSettingsById.put(key.getValidatorId(), settings);
             }
         }
         return validatorSettingsById;
diff --git a/src/main/java/org/apache/jackrabbit/filevault/maven/packaging/ValidatePackageMojo.java b/src/main/java/org/apache/jackrabbit/filevault/maven/packaging/ValidatePackageMojo.java
index ea495ee..707227f 100644
--- a/src/main/java/org/apache/jackrabbit/filevault/maven/packaging/ValidatePackageMojo.java
+++ b/src/main/java/org/apache/jackrabbit/filevault/maven/packaging/ValidatePackageMojo.java
@@ -121,7 +121,7 @@ public class ValidatePackageMojo extends AbstractValidateMojo {
         validationHelper.failBuildInCaseOfViolations(failOnValidationWarnings);
     }
 
-    private void validatePackage(ValidationHelper validationHelper, Path file) throws MojoExecutionException {
+    private void validatePackage(ValidationHelper validationHelper, Path file) throws MojoExecutionException, MojoFailureException {
         getLog().info("Start validating package " + getProjectRelativeFilePath(file) + "...");
 
         // open file to extract the meta data for the validation context
@@ -144,13 +144,13 @@ public class ValidatePackageMojo extends AbstractValidateMojo {
     }
 
     private void validateArchive(ValidationHelper validationHelper, Archive archive, Path path, ArchiveValidationContextImpl context,
-            ValidationExecutor executor) throws IOException, SAXException, ParserConfigurationException {
+            ValidationExecutor executor) throws IOException, SAXException, ParserConfigurationException, MojoFailureException {
         validateEntry(validationHelper, archive, archive.getRoot(), Paths.get(""), path, context, executor);
         validationHelper.printMessages(executor.done(), getLog(), buildContext, path);
     }
 
     private void validateEntry(ValidationHelper validationHelper, Archive archive, Archive.Entry entry, Path entryPath, Path packagePath, ArchiveValidationContextImpl context,
-            ValidationExecutor executor) throws IOException, SAXException, ParserConfigurationException {
+            ValidationExecutor executor) throws IOException, SAXException, ParserConfigurationException, MojoFailureException {
         // sort children to make sure that .content.xml comes first!
         List<Archive.Entry> sortedEntryList = new ArrayList<>(entry.getChildren());
         sortedEntryList.sort(Comparator.comparing(Archive.Entry::getName, new DotContentXmlFirstComparator()));
@@ -168,7 +168,7 @@ public class ValidatePackageMojo extends AbstractValidateMojo {
     }
 
     private void validateInputStream(ValidationHelper validationHelper, @Nullable InputStream inputStream, Path entryPath, Path packagePath, ArchiveValidationContextImpl context,
-            ValidationExecutor executor) throws IOException, SAXException, ParserConfigurationException {
+            ValidationExecutor executor) throws IOException, SAXException, ParserConfigurationException, MojoFailureException {
         Collection<ValidationViolation> messages = new LinkedList<>();
         if (entryPath.startsWith(Constants.META_INF)) {
             messages.addAll(executor.validateMetaInf(inputStream, Paths.get(Constants.META_INF).relativize(entryPath), packagePath.resolve(Constants.META_INF)));
diff --git a/src/main/java/org/apache/jackrabbit/filevault/maven/packaging/ValidatorSettings.java b/src/main/java/org/apache/jackrabbit/filevault/maven/packaging/ValidatorSettings.java
index e7c32cc..c5c959d 100644
--- a/src/main/java/org/apache/jackrabbit/filevault/maven/packaging/ValidatorSettings.java
+++ b/src/main/java/org/apache/jackrabbit/filevault/maven/packaging/ValidatorSettings.java
@@ -18,25 +18,37 @@ package org.apache.jackrabbit.filevault.maven.packaging;
 
 import java.util.HashMap;
 import java.util.Map;
+import java.util.Objects;
 
 import org.apache.jackrabbit.vault.validation.spi.ValidationMessageSeverity;
 
+/**
+ * Mutable implementation of org.apache.jackrabbit.vault.validation.spi.ValidatorSettings. Used as mojo parameter type.
+ */
 public class ValidatorSettings implements org.apache.jackrabbit.vault.validation.spi.ValidatorSettings {
 
-    private final boolean isDisabled;
+    private Boolean isDisabled;
     
     private ValidationMessageSeverity defaultSeverity;
 
     private final Map<String, String> options;
     
     public ValidatorSettings() {
-        isDisabled = false;
         options = new HashMap<>();
     }
-    
-    public ValidatorSettings(ValidationMessageSeverity defaultSeverity) {
-        this();
-        this.defaultSeverity = defaultSeverity;
+
+    public ValidatorSettings merge(ValidatorSettings otherSettings) {
+        // fields of current object take precedence (if not null)
+        ValidatorSettings mergedSettings = new ValidatorSettings();
+        mergedSettings.isDisabled = isDisabled != null ? isDisabled : otherSettings.isDisabled;
+        mergedSettings.defaultSeverity = defaultSeverity != null ? defaultSeverity : otherSettings.defaultSeverity;
+        mergedSettings.options.putAll(options);
+        for (Map.Entry<String, String> entry : otherSettings.getOptions().entrySet()) {
+            if (!options.containsKey(entry.getKey())) {
+                mergedSettings.addOption(entry.getKey(), entry.getValue());
+            }
+        }
+        return mergedSettings;
     }
 
     public ValidatorSettings setDefaultSeverity(String defaultSeverity) {
@@ -46,6 +58,10 @@ public class ValidatorSettings implements org.apache.jackrabbit.vault.validation
         return this;
     }
 
+    public void setDefaultSeverity(ValidationMessageSeverity defaultSeverity) {
+        this.defaultSeverity = defaultSeverity;
+    }
+
     protected ValidatorSettings addOption(String key, String value) {
         options.put(key, value);
         return this;
@@ -63,24 +79,23 @@ public class ValidatorSettings implements org.apache.jackrabbit.vault.validation
 
     @Override
     public boolean isDisabled() {
-        return isDisabled;
+        return (isDisabled != null) && isDisabled.booleanValue();
+    }
+
+    public void setIsDisabled(boolean isDisabled) {
+        this.isDisabled = isDisabled;
     }
 
     @Override
     public String toString() {
-        return "ValidatorSettings [isDisabled=" + isDisabled + ", "
+        return "ValidatorSettings [" + (isDisabled != null ? "isDisabled=" + isDisabled + ", " : "")
                 + (defaultSeverity != null ? "defaultSeverity=" + defaultSeverity + ", " : "")
                 + (options != null ? "options=" + options : "") + "]";
     }
 
     @Override
     public int hashCode() {
-        final int prime = 31;
-        int result = 1;
-        result = prime * result + ((defaultSeverity == null) ? 0 : defaultSeverity.hashCode());
-        result = prime * result + (isDisabled ? 1231 : 1237);
-        result = prime * result + ((options == null) ? 0 : options.hashCode());
-        return result;
+        return Objects.hash(defaultSeverity, isDisabled, options);
     }
 
     @Override
@@ -92,16 +107,9 @@ public class ValidatorSettings implements org.apache.jackrabbit.vault.validation
         if (getClass() != obj.getClass())
             return false;
         ValidatorSettings other = (ValidatorSettings) obj;
-        if (defaultSeverity != other.defaultSeverity)
-            return false;
-        if (isDisabled != other.isDisabled)
-            return false;
-        if (options == null) {
-            if (other.options != null)
-                return false;
-        } else if (!options.equals(other.options))
-            return false;
-        return true;
+        return defaultSeverity == other.defaultSeverity && Objects.equals(isDisabled, other.isDisabled)
+                && Objects.equals(options, other.options);
     }
 
+    
 }
diff --git a/src/main/java/org/apache/jackrabbit/filevault/maven/packaging/ValidatorSettingsKey.java b/src/main/java/org/apache/jackrabbit/filevault/maven/packaging/ValidatorSettingsKey.java
new file mode 100644
index 0000000..9a18f6f
--- /dev/null
+++ b/src/main/java/org/apache/jackrabbit/filevault/maven/packaging/ValidatorSettingsKey.java
@@ -0,0 +1,155 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.jackrabbit.filevault.maven.packaging;
+
+import java.util.Objects;
+
+import org.apache.jackrabbit.vault.packaging.PackageId;
+
+/** 
+ * The parsed key as used on {@link AbstractValidateMojo#validatorsSettings}.
+ * Implements a comparator which sorts the more generic settings first and the more specific ones later.
+ * The exact order is
+ * <ol>
+ * <li>settings without restrictions</li>
+ * <li>settings for subpackages</li>
+ * <li>settings for any groupId (*) and a specific artifactId</li>
+ * <li>settings for a specific groupId and artifactId</li>
+ * </ol>
+ * 
+ */
+public class ValidatorSettingsKey implements Comparable<ValidatorSettingsKey>{
+
+    private static final int LESS_THAN = -1;
+    private static final int GREATER_THEN = 1;
+    private static final String WILDCARD_FILTER_VALUE = "*";
+    final String key;
+    final String validatorId;
+    final boolean isForSubPackagesOnly;
+    final String packageNameFilter;
+    final String packageGroupFilter;
+
+    public static ValidatorSettingsKey fromString(String key) {
+        String[] parts = key.split(":", 3);
+        final String validatorId = parts[0];
+        boolean isForSubPackagesOnly = false;
+        String packageGroupFilter = null;
+        String packageNameFilter = null;
+        if (parts.length == 2) {
+            if (parts[1].equals("subpackage")) {
+                isForSubPackagesOnly = true;
+            } else {
+                throw new IllegalArgumentException("Invalid validatorSettings key '" + key +"'" );
+            }
+        }
+        // does it contain a package id filter?
+        else if (parts.length == 3) {
+            packageGroupFilter = parts[1];
+            packageNameFilter = parts[2];
+        }
+        return new ValidatorSettingsKey(key, validatorId, isForSubPackagesOnly, packageGroupFilter, packageNameFilter);
+    }
+ 
+    ValidatorSettingsKey(String key, String validatorId, boolean isForSubPackagesOnly, String packageGroupFilter, String packageNameFilter) {
+        super();
+        this.key = key;
+        this.validatorId = validatorId;
+        this.isForSubPackagesOnly = isForSubPackagesOnly;
+        this.packageGroupFilter = packageGroupFilter;
+        this.packageNameFilter = packageNameFilter;
+    }
+
+    public String getKey() {
+        return key;
+    }
+
+    public String getValidatorId() {
+        return validatorId;
+    }
+
+    public boolean matchesForPackage(PackageId packageId, boolean isSubPackage) {
+        if (!isSubPackage && isForSubPackagesOnly) {
+            return false;
+        }
+        if (packageNameFilter != null) {
+            if (!packageNameFilter.equals(packageId.getName())) {
+                return false;
+            }
+        }
+        if (packageGroupFilter != null && !WILDCARD_FILTER_VALUE.equals(packageGroupFilter)) {
+            if (!packageGroupFilter.equals(packageId.getGroup())) {
+                return false;
+            }
+        }
+        return true;
+    }
+
+    @Override
+    public int compareTo(ValidatorSettingsKey o) {
+        if (packageNameFilter != null && o.packageNameFilter != null) {
+            if (WILDCARD_FILTER_VALUE.equals(packageGroupFilter)) {
+                if (WILDCARD_FILTER_VALUE.equals(o.packageGroupFilter)) {
+                    return packageNameFilter.compareTo(o.packageNameFilter);
+                } else {
+                    return LESS_THAN;
+                }
+            } else {
+                if (WILDCARD_FILTER_VALUE.equals(o.packageGroupFilter)) {
+                    return GREATER_THEN;
+                } else {
+                    return packageNameFilter.compareTo(o.packageNameFilter);
+                }
+            }
+        } else if (packageNameFilter == null && o.packageNameFilter != null) {
+            return LESS_THAN;
+        } else if (packageNameFilter != null && o.packageNameFilter == null) {
+            return GREATER_THEN;
+        } else if (!isForSubPackagesOnly && o.isForSubPackagesOnly) {
+            return LESS_THAN;
+        } else if (isForSubPackagesOnly && !o.isForSubPackagesOnly) {
+            return GREATER_THEN;
+        }
+        return validatorId.compareTo(o.validatorId);
+    }
+
+    @Override
+    public int hashCode() {
+        return Objects.hash(isForSubPackagesOnly, key, packageGroupFilter, packageNameFilter, validatorId);
+    }
+
+    @Override
+    public boolean equals(Object obj) {
+        if (this == obj)
+            return true;
+        if (obj == null)
+            return false;
+        if (getClass() != obj.getClass())
+            return false;
+        ValidatorSettingsKey other = (ValidatorSettingsKey) obj;
+        return isForSubPackagesOnly == other.isForSubPackagesOnly && Objects.equals(key, other.key)
+                && Objects.equals(packageGroupFilter, other.packageGroupFilter)
+                && Objects.equals(packageNameFilter, other.packageNameFilter) && Objects.equals(validatorId, other.validatorId);
+    }
+
+    @Override
+    public String toString() {
+        return "ValidatorSettingsKey [" + (key != null ? "key=" + key + ", " : "")
+                + (validatorId != null ? "validatorId=" + validatorId + ", " : "") + "isForSubPackagesOnly=" + isForSubPackagesOnly + ", "
+                + (packageNameFilter != null ? "packageNameFilter=" + packageNameFilter + ", " : "")
+                + (packageGroupFilter != null ? "packageGroupFilter=" + packageGroupFilter : "") + "]";
+    }
+}
diff --git a/src/test/java/org/apache/jackrabbit/filevault/maven/packaging/AbstractValidateMojoTest.java b/src/test/java/org/apache/jackrabbit/filevault/maven/packaging/AbstractValidateMojoTest.java
index 2c11fb9..cbe1db9 100644
--- a/src/test/java/org/apache/jackrabbit/filevault/maven/packaging/AbstractValidateMojoTest.java
+++ b/src/test/java/org/apache/jackrabbit/filevault/maven/packaging/AbstractValidateMojoTest.java
@@ -66,15 +66,15 @@ public class AbstractValidateMojoTest {
         validatorsSettings.put("id1", new ValidatorSettings().addOption("id1", "foo"));
         validatorsSettings.put("id2:mygroup:myname", new ValidatorSettings().addOption("id2", "foo"));
         validatorsSettings.put("id3:subpackage", new ValidatorSettings().addOption("id3", "foo"));
-        validatorsSettings.put("id4:invalid", new ValidatorSettings().addOption("id4", "foo"));
         validatorsSettings.put("id5:othergroup:myname", new ValidatorSettings().addOption("id5", "foo"));
         validatorsSettings.put("id6:mygroup:myothername", new ValidatorSettings().addOption("id3", "foo"));
-        validatorsSettings.put("id7:", new ValidatorSettings().addOption("id7", "foo"));
-        
+        validatorsSettings.put("id1:*:myname", new ValidatorSettings().addOption("id2", "bar"));
+        validatorsSettings.put("id3", new ValidatorSettings().addOption("id3", "bar"));
         Map<String, ValidatorSettings> actualValidatorSettings = AbstractValidateMojo.getValidatorSettingsForPackage(new SystemStreamLog(), validatorsSettings, PackageId.fromString("mygroup:myname:1.0.0"), false);
         Map<String, ValidatorSettings> expectedValidatorSettings = new HashMap<>();
-        expectedValidatorSettings.put("id1", new ValidatorSettings().addOption("id1", "foo"));
+        expectedValidatorSettings.put("id1", new ValidatorSettings().addOption("id1", "foo").addOption("id2", "bar"));
         expectedValidatorSettings.put("id2", new ValidatorSettings().addOption("id2", "foo"));
+        expectedValidatorSettings.put("id3", new ValidatorSettings().addOption("id3", "bar"));
         MatcherAssert.assertThat(actualValidatorSettings, Matchers.equalTo(expectedValidatorSettings));
         
         actualValidatorSettings = AbstractValidateMojo.getValidatorSettingsForPackage(new SystemStreamLog(), validatorsSettings, PackageId.fromString("mygroup:myname:1.0.0"), true);
diff --git a/src/test/java/org/apache/jackrabbit/filevault/maven/packaging/ValidatorSettingsKeyTest.java b/src/test/java/org/apache/jackrabbit/filevault/maven/packaging/ValidatorSettingsKeyTest.java
new file mode 100644
index 0000000..3880d03
--- /dev/null
+++ b/src/test/java/org/apache/jackrabbit/filevault/maven/packaging/ValidatorSettingsKeyTest.java
@@ -0,0 +1,62 @@
+/*
+ * 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.jackrabbit.filevault.maven.packaging;
+
+import java.util.SortedSet;
+import java.util.TreeSet;
+import java.util.stream.Collectors;
+
+import org.junit.Assert;
+import org.junit.Test;
+
+public class ValidatorSettingsKeyTest {
+
+    @Test
+    public void testFromString() {
+        Assert.assertEquals(new ValidatorSettingsKey("id1", "id1", false, null, null), ValidatorSettingsKey.fromString("id1"));
+        Assert.assertEquals(new ValidatorSettingsKey("id2:subpackage", "id2", true, null, null), ValidatorSettingsKey.fromString("id2:subpackage"));
+        Assert.assertThrows(IllegalArgumentException.class, () -> {ValidatorSettingsKey.fromString("id1:invalid");} );
+        Assert.assertEquals(new ValidatorSettingsKey("id3:othergroup:myname", "id3", false, "othergroup", "myname"),  ValidatorSettingsKey.fromString("id3:othergroup:myname"));
+        Assert.assertEquals(new ValidatorSettingsKey("id4:*:othername", "id4", false, "*", "othername"),  ValidatorSettingsKey.fromString("id4:*:othername"));
+    }
+
+    @Test
+    public void testComparator() {
+        SortedSet<ValidatorSettingsKey> keys = new TreeSet<>();
+        
+        ValidatorSettingsKey key1 = new ValidatorSettingsKey("id1", "id1", false, null, null);
+        ValidatorSettingsKey key2 = new ValidatorSettingsKey("id1:subpackage", "id1", true, null, null);
+        ValidatorSettingsKey key3 = new ValidatorSettingsKey("id1:*:mypackage", "id1", false, "*", "mypackage");
+        ValidatorSettingsKey key4 = new ValidatorSettingsKey("id1:mygroup:mypackage", "id1", false, "mygroup", "mypackage");
+        ValidatorSettingsKey key5 = new ValidatorSettingsKey("id1:mygroup:otherpackage", "id1", false, "mygroup", "otherpackage");
+        Assert.assertEquals(-1, Integer.signum(key1.compareTo(key2)));
+        Assert.assertEquals(1, Integer.signum(key2.compareTo(key1)));
+        Assert.assertEquals(-1, Integer.signum(key2.compareTo(key3)));
+        Assert.assertEquals(1, Integer.signum(key3.compareTo(key2)));
+        Assert.assertEquals(-1, Integer.signum(key3.compareTo(key4)));
+        Assert.assertEquals(1, Integer.signum(key4.compareTo(key3)));
+        Assert.assertEquals(-1, Integer.signum(key4.compareTo(key5)));
+        Assert.assertEquals(1, Integer.signum(key5.compareTo(key4)));
+        Assert.assertEquals(0, Integer.signum(key5.compareTo(key5)));
+        keys.add(key5);
+        keys.add(key4);
+        keys.add(key3);
+        keys.add(key2);
+        keys.add(key1);
+        Assert.assertEquals("id1,id1:subpackage,id1:*:mypackage,id1:mygroup:mypackage,id1:mygroup:otherpackage", keys.stream().map(ValidatorSettingsKey::getKey).collect(Collectors.joining(",")));
+    }
+}
diff --git a/src/test/java/org/apache/jackrabbit/filevault/maven/packaging/ValidatorSettingsTest.java b/src/test/java/org/apache/jackrabbit/filevault/maven/packaging/ValidatorSettingsTest.java
new file mode 100644
index 0000000..654e48f
--- /dev/null
+++ b/src/test/java/org/apache/jackrabbit/filevault/maven/packaging/ValidatorSettingsTest.java
@@ -0,0 +1,44 @@
+/*
+ * 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.jackrabbit.filevault.maven.packaging;
+
+import org.junit.Assert;
+import org.junit.Test;
+
+public class ValidatorSettingsTest {
+
+    @Test
+    public void testMerge() {
+        ValidatorSettings settings1 = new ValidatorSettings();
+        settings1.setIsDisabled(true);
+        settings1.addOption("option1", "from1");
+        settings1.addOption("option3", "from1");
+        ValidatorSettings settings2 = new ValidatorSettings();
+        settings2.setIsDisabled(false);
+        settings2.addOption("option1", "from2");
+        settings2.addOption("option2", "from2");
+        settings2.addOption("option3", "from2");
+        settings2.setDefaultSeverity("WARN");
+        ValidatorSettings mergedSettings = new ValidatorSettings();
+        mergedSettings.setIsDisabled(true);
+        mergedSettings.addOption("option1", "from1");
+        mergedSettings.addOption("option2", "from2");
+        mergedSettings.addOption("option3", "from1");
+        mergedSettings.setDefaultSeverity("WARN");
+        Assert.assertEquals(mergedSettings, settings1.merge(settings2));
+    }
+}