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 2023/08/16 17:03:44 UTC
[jackrabbit-filevault] branch feature/overlapping-filter-validator created (now 951f1511)
This is an automated email from the ASF dual-hosted git repository.
kwin pushed a change to branch feature/overlapping-filter-validator
in repository https://gitbox.apache.org/repos/asf/jackrabbit-filevault.git
at 951f1511 JCRVLT-469 Validator for overlapping filter rules
This branch includes the following new commits:
new 951f1511 JCRVLT-469 Validator for overlapping filter rules
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] 01/01: JCRVLT-469 Validator for overlapping filter rules
Posted by kw...@apache.org.
This is an automated email from the ASF dual-hosted git repository.
kwin pushed a commit to branch feature/overlapping-filter-validator
in repository https://gitbox.apache.org/repos/asf/jackrabbit-filevault.git
commit 951f1511d9918e5f06d2e9d68c6f04b733decdd6
Author: Konrad Windszus <kw...@apache.org>
AuthorDate: Wed Aug 16 19:03:38 2023 +0200
JCRVLT-469 Validator for overlapping filter rules
---
src/site/markdown/validation.md | 1 +
.../vault/packaging/PackageProperties.java | 2 +-
.../context/ArchiveValidationContext.java | 20 +++
.../vault/validation/context/package-info.java | 2 +-
.../vault/validation/spi/ValidationContext.java | 33 +++-
.../spi/impl/OverlappingFilterValidator.java | 182 +++++++++++++++++++++
.../impl/OverlappingFilterValidatorFactory.java | 72 ++++++++
.../vault/validation/spi/package-info.java | 2 +-
.../it/PackageFolderValidationContext.java | 20 ++-
.../spi/impl/OverlappingFilterValidatorTest.java | 93 +++++++++++
10 files changed, 421 insertions(+), 6 deletions(-)
diff --git a/src/site/markdown/validation.md b/src/site/markdown/validation.md
index f4c6f6b8..f1d7a829 100644
--- a/src/site/markdown/validation.md
+++ b/src/site/markdown/validation.md
@@ -62,6 +62,7 @@ ID | Description | Options | Incremental Execution Limitations
`jackrabbit-nodetypes` | Checks if all non empty elements within [DocView files](docview.html) have the mandatory property `jcr:primaryType` set and follow the [node type definition of their given type](https://jackrabbit.apache.org/jcr/node-types.html). | *cnds*: A URI pointing to one or multiple [CNDs](https://jackrabbit.apache.org/jcr/node-type-notation.html) (separated by `,`) which define the additional namespaces and node types used apart from the [default ones defined in JCR 2.0]( [...]
`jackrabbit-accesscontrol` | Checks that [access control list nodes (primary type `rep:ACL`, `rep:CugPolicy` and `rep:PrincipalPolicy`)](https://jackrabbit.apache.org/oak/docs/security/accesscontrol/default.html#Representation_in_the_Repository) are only used when the [package property's](./properties.html) `acHandling` is set to something but `ignore` or `clear` and also that there is at least one access control list node otherwise. | none | Validation message in case no access control [...]
`jackrabbit-duplicateuuid` | Checks that every value of property `jcr:uuid` is unique. Compare with [Referenceable Nodes](./referenceablenodes.html). | none | might emit false negatives (i.e. not detect duplicates)
+`jackrabbit-overlappingfilter` | Checks that filters of two distinct content packages never overlap. In order for this validator to work, the conflicting packages must be included as subpackages (even nested ones) in the same container package | none | does not work
### Custom Validators
diff --git a/vault-core/src/main/java/org/apache/jackrabbit/vault/packaging/PackageProperties.java b/vault-core/src/main/java/org/apache/jackrabbit/vault/packaging/PackageProperties.java
index ef0b6142..a8f4f31e 100644
--- a/vault-core/src/main/java/org/apache/jackrabbit/vault/packaging/PackageProperties.java
+++ b/vault-core/src/main/java/org/apache/jackrabbit/vault/packaging/PackageProperties.java
@@ -324,7 +324,7 @@ public interface PackageProperties {
String getProperty(String name);
/**
- * Returns the package type or {@code null} if not package type was specified for this package.
+ * Returns the package type or {@code null} if no package type was specified for this package.
* @return the package type
*/
@Nullable
diff --git a/vault-validation/src/main/java/org/apache/jackrabbit/vault/validation/context/ArchiveValidationContext.java b/vault-validation/src/main/java/org/apache/jackrabbit/vault/validation/context/ArchiveValidationContext.java
index 5a0d592e..14e62d92 100644
--- a/vault-validation/src/main/java/org/apache/jackrabbit/vault/validation/context/ArchiveValidationContext.java
+++ b/vault-validation/src/main/java/org/apache/jackrabbit/vault/validation/context/ArchiveValidationContext.java
@@ -19,6 +19,9 @@ package org.apache.jackrabbit.vault.validation.context;
import java.io.IOException;
import java.nio.file.Path;
import java.util.Collection;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Set;
import org.apache.jackrabbit.vault.fs.api.WorkspaceFilter;
import org.apache.jackrabbit.vault.fs.io.Archive;
@@ -37,6 +40,7 @@ public class ArchiveValidationContext implements ValidationContext {
@NotNull private final PackageProperties properties;
@NotNull private final Path archivePath;
@NotNull private final Collection<PackageInfo> resolvedDependencies;
+ @NotNull private final Map<String, Object> attributes;
public ArchiveValidationContext(@NotNull Archive archive, @NotNull Path archivePath, @NotNull DependencyResolver resolver) throws IOException {
this.archivePath = archivePath;
@@ -46,6 +50,7 @@ public class ArchiveValidationContext implements ValidationContext {
throw new IllegalStateException("Archive '" + archivePath + "' does not contain a filter.xml.");
}
this.resolvedDependencies = resolver.resolvePackageInfo(getProperties().getDependencies(), getProperties().getDependenciesLocations());
+ this.attributes = new HashMap<>();
}
@Override
@@ -72,4 +77,19 @@ public class ArchiveValidationContext implements ValidationContext {
return this.resolvedDependencies;
}
+ @Override
+ public Object setAttribute(String name, Object value) {
+ return attributes.put(name, value);
+ }
+
+ @Override
+ public Object getAttribute(String name) {
+ return attributes.get(name);
+ }
+
+ @Override
+ public Set<String> getAttributeNames() {
+ return attributes.keySet();
+ }
+
}
diff --git a/vault-validation/src/main/java/org/apache/jackrabbit/vault/validation/context/package-info.java b/vault-validation/src/main/java/org/apache/jackrabbit/vault/validation/context/package-info.java
index 11a8500c..5e15c33f 100644
--- a/vault-validation/src/main/java/org/apache/jackrabbit/vault/validation/context/package-info.java
+++ b/vault-validation/src/main/java/org/apache/jackrabbit/vault/validation/context/package-info.java
@@ -17,7 +17,7 @@
/**
* Provides common implementations of {@link org.apache.jackrabbit.vault.validation.spi.ValidationContext}
*/
-@Version("1.0.1")
+@Version("1.1.0")
package org.apache.jackrabbit.vault.validation.context;
import org.osgi.annotation.versioning.Version;
diff --git a/vault-validation/src/main/java/org/apache/jackrabbit/vault/validation/spi/ValidationContext.java b/vault-validation/src/main/java/org/apache/jackrabbit/vault/validation/spi/ValidationContext.java
index f4e2c54d..d2ead592 100644
--- a/vault-validation/src/main/java/org/apache/jackrabbit/vault/validation/spi/ValidationContext.java
+++ b/vault-validation/src/main/java/org/apache/jackrabbit/vault/validation/spi/ValidationContext.java
@@ -18,6 +18,7 @@ package org.apache.jackrabbit.vault.validation.spi;
import java.nio.file.Path;
import java.util.Collection;
+import java.util.Set;
import org.apache.jackrabbit.vault.fs.api.WorkspaceFilter;
import org.apache.jackrabbit.vault.packaging.PackageInfo;
@@ -58,9 +59,9 @@ public interface ValidationContext {
@NotNull Path getPackageRootPath();
/**
- * PackageInfo for all resolved package dependencies.
+ * {@link PackageInfo} for all resolved package dependencies.
* In contrast to {@link PackageProperties#getDependencies()} the resolved dependencies also
- * carry the main metadata of the dependencies.
+ * carry the main metadata (next to their coordinates).
* @return the package info of all resolved package dependencies (i.e. the ones for which an artifact was found).
*/
@NotNull Collection<PackageInfo> getDependenciesPackageInfo();
@@ -72,4 +73,32 @@ public interface ValidationContext {
default boolean isIncremental() {
return false;
}
+
+ /**
+ *
+ * @param name
+ * @param value
+ * @return
+ * @see #getAttribute(String)
+ * @since 3.7.1
+ */
+ Object setAttribute(String name, Object value);
+
+ /**
+ * @param name
+ * @return
+ * @see #setAttribute(String, Object)
+ * @see #getAttributeNames()
+ * @since 3.7.1
+ */
+ Object getAttribute(String name);
+
+ /**
+ *
+ * @return
+ * @see #getAttribute(String)
+ * @see #setAttribute(String, Object)
+ * @since 3.7.1
+ */
+ Set<String> getAttributeNames();
}
diff --git a/vault-validation/src/main/java/org/apache/jackrabbit/vault/validation/spi/impl/OverlappingFilterValidator.java b/vault-validation/src/main/java/org/apache/jackrabbit/vault/validation/spi/impl/OverlappingFilterValidator.java
new file mode 100644
index 00000000..4f5813df
--- /dev/null
+++ b/vault-validation/src/main/java/org/apache/jackrabbit/vault/validation/spi/impl/OverlappingFilterValidator.java
@@ -0,0 +1,182 @@
+/*
+ * 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.vault.validation.spi.impl;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.List;
+import java.util.Map;
+import java.util.NavigableMap;
+import java.util.Optional;
+import java.util.SortedMap;
+import java.util.TreeMap;
+import java.util.function.Predicate;
+import java.util.stream.Collectors;
+
+import org.apache.jackrabbit.vault.fs.api.FilterSet.Entry;
+import org.apache.jackrabbit.vault.fs.api.PathFilter;
+import org.apache.jackrabbit.vault.fs.api.PathFilterSet;
+import org.apache.jackrabbit.vault.fs.api.WorkspaceFilter;
+import org.apache.jackrabbit.vault.fs.filter.DefaultPathFilter;
+import org.apache.jackrabbit.vault.validation.spi.FilterValidator;
+import org.apache.jackrabbit.vault.validation.spi.ValidationMessage;
+import org.apache.jackrabbit.vault.validation.spi.ValidationMessageSeverity;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+
+public class OverlappingFilterValidator implements FilterValidator {
+
+ static final String MESSAGE_OVERLAPPING_FILTERS = "Filter root '%s' in package '%s' potentially overlapping with filter root '%s' in package '%s'.";
+ private static final String PATTERN_OPTIONAL_SUBNODE_WILDCARD = "(/.*)?";
+ /**
+ * key = path of package (might be a nested subpackage)
+ */
+ private final NavigableMap<String, List<PathFilterSet>> filtersPerPackages = new TreeMap<>();
+
+ @Override
+ public @Nullable Collection<ValidationMessage> done() {
+ return validateFilters(filtersPerPackages);
+ }
+
+ private static Collection<ValidationMessage> validateFilters(NavigableMap<String, List<PathFilterSet>> filtersPerPackages) {
+ Collection<ValidationMessage> validationMessages = new ArrayList<>();
+ // go through each package individually
+ for (Map.Entry<String, List<PathFilterSet>> filtersPerPackage : filtersPerPackages.entrySet())
+ {
+ String higherKey = filtersPerPackages.higherKey(filtersPerPackage.getKey());
+ if (higherKey == null) {
+ break;
+ }
+ // and compare with rest of packages
+ validationMessages.addAll(
+ validateFiltersOfPackage(
+ filtersPerPackage.getValue(),
+ filtersPerPackage.getKey(),
+ filtersPerPackages.tailMap(higherKey))
+ );
+ }
+ return validationMessages;
+ }
+
+ private static Collection<ValidationMessage> validateFiltersOfPackage(List<PathFilterSet> filtersOfPackage, String packagePath, SortedMap<String, List<PathFilterSet>> otherPackageFiltersPerPackages )
+ {
+ Collection<ValidationMessage> validationMessages = new ArrayList<>();
+ int index = 0;
+ while (index < filtersOfPackage.size()) {
+ PathFilterSet set1 = filtersOfPackage.get(index);
+ // go through all other packages
+ for (Map.Entry<String, List<PathFilterSet>> otherPackageFiltersPerPackage : otherPackageFiltersPerPackages.entrySet()) {
+ // find one that is nested
+ Optional<PathFilterSet> set2 = otherPackageFiltersPerPackage.getValue().stream()
+ .filter(new NestedPathPredicate(set1))
+ .findAny();
+ if (set2.isPresent()) {
+ // 2. only allow if the canonical includes are excludes in the other rule
+ if (areFilterSetsPotentiallyOverlapping(set1, set2.get())) {
+ validationMessages.add(
+ new ValidationMessage(ValidationMessageSeverity.ERROR,
+ String.format(MESSAGE_OVERLAPPING_FILTERS,
+ set1.getRoot(), packagePath, set2.get().getRoot(), otherPackageFiltersPerPackage.getKey())));
+ }
+ }
+ }
+ index++;
+ }
+ return validationMessages;
+ }
+
+
+ private static final class NestedPathPredicate implements Predicate<PathFilterSet> {
+
+ private final String path;
+
+ NestedPathPredicate(PathFilterSet filterSet) {
+ this.path = filterSet.getRoot();
+ }
+
+ @Override
+ public boolean test(PathFilterSet t) {
+ String path2 = t.getRoot();
+ if (path.startsWith(path2 + "/") ||
+ path2.startsWith(path + "/") ||
+ path.equals(path2)) {
+ return true;
+ }
+ return false;
+ }
+
+ }
+
+ /**
+ * This is just a heuristic which might emit false positives.
+ * It is impossible to come up with a better estimation without knowing the importing repository and the package contents.
+ * @param filterSet1
+ * @param filterSet2
+ * @return
+ */
+ static boolean areFilterSetsPotentiallyOverlapping(PathFilterSet filterSet1, PathFilterSet filterSet2) {
+ for (PathFilter includeFilter : getIncludeFilters(filterSet1.getEntries())) {
+ if (!hasExcludeFilter(filterSet2.getEntries(), getNormalizedPattern(includeFilter))) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ static List<PathFilter> getIncludeFilters(List<Entry<PathFilter>> entries) {
+ if (entries.isEmpty()) {
+ return Collections.singletonList(PathFilter.ALL);
+ }
+ return entries.stream().filter(Entry::isInclude).map(Entry::getFilter).collect(Collectors.toList());
+ }
+
+ static boolean hasExcludeFilter(List<Entry<PathFilter>> entries, String pattern) {
+ return entries.stream()
+ // only exclude entries
+ .filter(((Predicate<Entry<PathFilter>>)Entry::isInclude).negate())
+ // having the same pattern
+ .anyMatch(f -> OverlappingFilterValidator.getNormalizedPattern(f.getFilter()).equals(pattern) );
+ }
+
+ static String getNormalizedPattern(PathFilter filter) {
+ String pattern;
+ if (filter instanceof DefaultPathFilter) {
+ pattern = DefaultPathFilter.class.cast(filter).getPattern();
+ } else if (filter == PathFilter.ALL || filter == PathFilter.NONE) {
+ pattern = ".*";
+ } else {
+ // unsupported filter type
+ throw new IllegalArgumentException("Unsupported filter type " + filter);
+ }
+ if (pattern.endsWith(PATTERN_OPTIONAL_SUBNODE_WILDCARD)) {
+ pattern = pattern.substring(0, pattern.length() - PATTERN_OPTIONAL_SUBNODE_WILDCARD.length());
+ }
+ return pattern;
+ }
+
+ @Override
+ public @Nullable Collection<ValidationMessage> validate(@NotNull WorkspaceFilter filter) {
+ addFilter(filter, "");
+ return null;
+ }
+
+ public void addFilter(@NotNull WorkspaceFilter filter, String pathInContainer) {
+ filtersPerPackages.put(pathInContainer, filter.getFilterSets());
+ }
+
+}
diff --git a/vault-validation/src/main/java/org/apache/jackrabbit/vault/validation/spi/impl/OverlappingFilterValidatorFactory.java b/vault-validation/src/main/java/org/apache/jackrabbit/vault/validation/spi/impl/OverlappingFilterValidatorFactory.java
new file mode 100644
index 00000000..a21ad1ad
--- /dev/null
+++ b/vault-validation/src/main/java/org/apache/jackrabbit/vault/validation/spi/impl/OverlappingFilterValidatorFactory.java
@@ -0,0 +1,72 @@
+/*
+ * 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.vault.validation.spi.impl;
+
+import org.apache.jackrabbit.vault.validation.spi.ValidationContext;
+import org.apache.jackrabbit.vault.validation.spi.Validator;
+import org.apache.jackrabbit.vault.validation.spi.ValidatorFactory;
+import org.apache.jackrabbit.vault.validation.spi.ValidatorSettings;
+import org.jetbrains.annotations.NotNull;
+import org.kohsuke.MetaInfServices;
+
+@MetaInfServices
+public final class OverlappingFilterValidatorFactory implements ValidatorFactory {
+
+ public static final String ID = ValidatorFactory.ID_PREFIX_JACKRABBIT + "overlappingfilter";
+
+ @Override
+ public Validator createValidator(@NotNull ValidationContext context, @NotNull ValidatorSettings settings) {
+
+ if (context.getContainerValidationContext() == null) {
+ // create new validator for every root container (which may contain additional subpackages)
+ OverlappingFilterValidator validator = new OverlappingFilterValidator();
+ // store in context
+ context.setAttribute("overlappingValidator", validator);
+ } else {
+ // for each sub package (even nested ones)
+ // find topmost container
+ OverlappingFilterValidator validator = (OverlappingFilterValidator)getRootContainerContext(context).getAttribute("overlappingValidator");
+ // add filter
+ validator.addFilter(context.getFilter(), context.getPackageRootPath().toString());
+ }
+ return new OsgiConfigurationParserValidator();
+ }
+
+ private static ValidationContext getRootContainerContext(final ValidationContext context) {
+ ValidationContext rootContext = context;
+ while (rootContext.getContainerValidationContext() != null) {
+ rootContext = rootContext.getContainerValidationContext();
+ }
+ return rootContext;
+ }
+
+ @Override
+ public boolean shouldValidateSubpackages() {
+ return true;
+ }
+
+ @Override
+ public @NotNull String getId() {
+ return ID;
+ }
+
+ @Override
+ public int getServiceRanking() {
+ return 0;
+ }
+
+}
diff --git a/vault-validation/src/main/java/org/apache/jackrabbit/vault/validation/spi/package-info.java b/vault-validation/src/main/java/org/apache/jackrabbit/vault/validation/spi/package-info.java
index 3aa5fe5d..0d87164d 100644
--- a/vault-validation/src/main/java/org/apache/jackrabbit/vault/validation/spi/package-info.java
+++ b/vault-validation/src/main/java/org/apache/jackrabbit/vault/validation/spi/package-info.java
@@ -18,7 +18,7 @@
/**
* The FileVault validation framework SPI. Provides classes/interfaces to implement validators on FileVault packages.
*/
-@Version("1.7.0")
+@Version("1.8.0")
package org.apache.jackrabbit.vault.validation.spi;
import org.osgi.annotation.versioning.Version;
diff --git a/vault-validation/src/test/java/org/apache/jackrabbit/vault/validation/it/PackageFolderValidationContext.java b/vault-validation/src/test/java/org/apache/jackrabbit/vault/validation/it/PackageFolderValidationContext.java
index 43267b1a..e9b200f9 100644
--- a/vault-validation/src/test/java/org/apache/jackrabbit/vault/validation/it/PackageFolderValidationContext.java
+++ b/vault-validation/src/test/java/org/apache/jackrabbit/vault/validation/it/PackageFolderValidationContext.java
@@ -23,6 +23,9 @@ import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.Collection;
import java.util.Collections;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Set;
import org.apache.jackrabbit.vault.fs.api.WorkspaceFilter;
import org.apache.jackrabbit.vault.fs.config.ConfigurationException;
@@ -40,6 +43,7 @@ public class PackageFolderValidationContext implements ValidationContext {
private final Path rootFolder;
private final DefaultWorkspaceFilter filter;
private final PackageProperties properties;
+ private final Map<String, Object> attributes;
public PackageFolderValidationContext(Path rootFolder) throws IOException, ConfigurationException {
this.rootFolder = rootFolder;
@@ -48,6 +52,7 @@ public class PackageFolderValidationContext implements ValidationContext {
filter.load(input);
}
properties = DefaultPackageProperties.fromFile(rootFolder.resolve(Paths.get(Constants.META_DIR,Constants.PROPERTIES_XML)));
+ attributes = new HashMap<>();
}
@Override
@@ -72,8 +77,21 @@ public class PackageFolderValidationContext implements ValidationContext {
@Override
public @NotNull Collection<PackageInfo> getDependenciesPackageInfo() {
- // TODO Auto-generated method stub
return Collections.emptyList();
}
+ @Override
+ public Object setAttribute(String name, Object value) {
+ return attributes.put(name, value);
+ }
+
+ @Override
+ public Object getAttribute(String name) {
+ return attributes.get(name);
+ }
+
+ @Override
+ public Set<String> getAttributeNames() {
+ return attributes.keySet();
+ }
}
diff --git a/vault-validation/src/test/java/org/apache/jackrabbit/vault/validation/spi/impl/OverlappingFilterValidatorTest.java b/vault-validation/src/test/java/org/apache/jackrabbit/vault/validation/spi/impl/OverlappingFilterValidatorTest.java
new file mode 100644
index 00000000..921d8573
--- /dev/null
+++ b/vault-validation/src/test/java/org/apache/jackrabbit/vault/validation/spi/impl/OverlappingFilterValidatorTest.java
@@ -0,0 +1,93 @@
+/*
+ * 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.vault.validation.spi.impl;
+
+import java.io.ByteArrayInputStream;
+import java.io.IOException;
+import java.nio.charset.StandardCharsets;
+import java.util.Collection;
+
+import org.apache.jackrabbit.vault.fs.config.ConfigurationException;
+import org.apache.jackrabbit.vault.fs.config.DefaultWorkspaceFilter;
+import org.apache.jackrabbit.vault.validation.AnyValidationViolationMessageMatcher;
+import org.apache.jackrabbit.vault.validation.ValidationExecutorTest;
+import org.apache.jackrabbit.vault.validation.spi.ValidationMessage;
+import org.apache.jackrabbit.vault.validation.spi.ValidationMessageSeverity;
+import org.hamcrest.MatcherAssert;
+import org.junit.Test;
+
+public class OverlappingFilterValidatorTest {
+
+ @Test
+ public void testNonOverlappingPackages() throws IOException, ConfigurationException {
+ OverlappingFilterValidator validator = new OverlappingFilterValidator();
+
+ String rules1 = "<workspaceFilter version=\"1.0\">\n"
+ + " <filter root=\"/apps/test\"/>\n"
+ + " <filter root=\"/apps/test2\">\n"
+ + " <include pattern=\".*/valid\" />\n"
+ + " <include pattern=\".*/anothervalid\" />\n"
+ + " </filter>\n"
+ + "</workspaceFilter>";
+ String rules2 = "<workspaceFilter version=\"1.0\">\n"
+ + " <filter root=\"/apps/test3\"/>\n"
+ + " <filter root=\"/apps/test4\">\n"
+ + " <include pattern=\".*/valid\" />\n"
+ + " <include pattern=\".*/anothervalid\" />\n"
+ + " </filter>\n"
+ + "</workspaceFilter>";
+ validator.addFilter(createWorkspaceFilter(rules1), "package1");
+ validator.addFilter(createWorkspaceFilter(rules2), "package2");
+ Collection<ValidationMessage> messages = validator.done();
+ MatcherAssert.assertThat(messages, AnyValidationViolationMessageMatcher.noValidationViolationMessageInCollection());
+ }
+
+ @Test
+ public void testOverlappingPackages() throws IOException, ConfigurationException {
+ OverlappingFilterValidator validator = new OverlappingFilterValidator();
+
+ String rules1 = "<workspaceFilter version=\"1.0\">\n"
+ + " <filter root=\"/apps/test\"/>\n"
+ + " <filter root=\"/apps/test2\">\n"
+ + " <include pattern=\".*/valid\" />\n"
+ + " <include pattern=\".*/anothervalid\" />\n"
+ + " </filter>\n"
+ + "</workspaceFilter>";
+ String rules2 = "<workspaceFilter version=\"1.0\">\n"
+ + " <filter root=\"/apps/test3\"/>\n"
+ + " <filter root=\"/apps/test2\">\n"
+ + " <include pattern=\".*/valid\" />\n"
+ + " <include pattern=\".*/anothervalid\" />\n"
+ + " </filter>\n"
+ + "</workspaceFilter>";
+ validator.addFilter(createWorkspaceFilter(rules1), "package1");
+ validator.addFilter(createWorkspaceFilter(rules2), "package2");
+ Collection<ValidationMessage> messages = validator.done();
+ ValidationExecutorTest.assertViolation(messages,
+ new ValidationMessage(ValidationMessageSeverity.ERROR,
+ String.format(OverlappingFilterValidator.MESSAGE_OVERLAPPING_FILTERS, "/apps/test2", "package1", "/apps/test2", "package2")));
+
+ }
+
+ static DefaultWorkspaceFilter createWorkspaceFilter(String filterRules) throws IOException, ConfigurationException {
+ DefaultWorkspaceFilter filter = new DefaultWorkspaceFilter();
+ try ( ByteArrayInputStream input = new ByteArrayInputStream(filterRules.getBytes(StandardCharsets.US_ASCII))) {
+ filter.load(input);
+ }
+ return filter;
+ }
+}