jackrabbit-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From k...@apache.org
Subject svn commit: r1867884 [3/6] - in /jackrabbit/commons/filevault/trunk: ./ .mvn/ parent/ vault-core/src/main/java/org/apache/jackrabbit/vault/fs/api/ vault-core/src/main/java/org/apache/jackrabbit/vault/fs/config/ vault-core/src/main/java/org/apache/jackr...
Date Wed, 02 Oct 2019 11:28:26 GMT
Copied: jackrabbit/commons/filevault/trunk/vault-validation/src/main/java/org/apache/jackrabbit/filevault/validation/spi/Validator.java (from r1867026, jackrabbit/commons/filevault/trunk/vault-core/src/main/java/org/apache/jackrabbit/vault/fs/api/package-info.java)
URL: http://svn.apache.org/viewvc/jackrabbit/commons/filevault/trunk/vault-validation/src/main/java/org/apache/jackrabbit/filevault/validation/spi/Validator.java?p2=jackrabbit/commons/filevault/trunk/vault-validation/src/main/java/org/apache/jackrabbit/filevault/validation/spi/Validator.java&p1=jackrabbit/commons/filevault/trunk/vault-core/src/main/java/org/apache/jackrabbit/vault/fs/api/package-info.java&r1=1867026&r2=1867884&rev=1867884&view=diff
==============================================================================
--- jackrabbit/commons/filevault/trunk/vault-core/src/main/java/org/apache/jackrabbit/vault/fs/api/package-info.java (original)
+++ jackrabbit/commons/filevault/trunk/vault-validation/src/main/java/org/apache/jackrabbit/filevault/validation/spi/Validator.java Wed Oct  2 11:28:25 2019
@@ -14,8 +14,29 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
+package org.apache.jackrabbit.filevault.validation.spi;
 
-@Version("2.6.1")
-package org.apache.jackrabbit.vault.fs.api;
+import java.util.Collection;
 
-import org.osgi.annotation.versioning.Version;
\ No newline at end of file
+import javax.annotation.CheckForNull;
+
+import org.osgi.annotation.versioning.ProviderType;
+
+/**
+ * A validator is created per Maven module individually and only used from a single thread.
+ * Also the instance is only used for one package at most (i.e. subpackages get another instance)
+ * Instead of implementing this generic interface each validator should rather implement one of the
+ * sub interfaces.
+ *
+ */
+@ProviderType
+public interface Validator {
+
+    /**
+     * Called when the validation is done for one {@link ValidationContext} (i.e. this instance is no longer needed)
+     * @return validation messages or {@code null}
+     */
+     @CheckForNull Collection<ValidationMessage> done();
+
+
+}

Added: jackrabbit/commons/filevault/trunk/vault-validation/src/main/java/org/apache/jackrabbit/filevault/validation/spi/ValidatorFactory.java
URL: http://svn.apache.org/viewvc/jackrabbit/commons/filevault/trunk/vault-validation/src/main/java/org/apache/jackrabbit/filevault/validation/spi/ValidatorFactory.java?rev=1867884&view=auto
==============================================================================
--- jackrabbit/commons/filevault/trunk/vault-validation/src/main/java/org/apache/jackrabbit/filevault/validation/spi/ValidatorFactory.java (added)
+++ jackrabbit/commons/filevault/trunk/vault-validation/src/main/java/org/apache/jackrabbit/filevault/validation/spi/ValidatorFactory.java Wed Oct  2 11:28:25 2019
@@ -0,0 +1,66 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.jackrabbit.filevault.validation.spi;
+
+import java.util.ServiceLoader;
+
+import javax.annotation.CheckForNull;
+import javax.annotation.Nonnull;
+
+import org.osgi.annotation.versioning.ProviderType;
+
+/**
+ * Each {@link Validator} is created via the according factory.
+ * The factories should be registered via the {@link ServiceLoader} mechanism
+ */
+@ProviderType
+public interface ValidatorFactory {
+    /**
+     * Reserved prefix for all validators integrated in this JAR
+     */
+    public static final String PREFIX_JACKRABBIT = "jackrabbit-";
+    /**
+     * 
+     * @param context the context of Validator (info about package)
+     * @param settings the validator settings
+     * @return a new validator instance (lifecycle bound to the package outlined in context and the current maven module) or {@code null} in case there is no validation relevant for the given context
+     */
+    @CheckForNull Validator createValidator(@Nonnull ValidationContext context, @Nonnull ValidatorSettings settings);
+
+    /**
+     * 
+     * @return {@code true} in case the validation with this validator should also happen for subpackages (recursively), otherwise {@code false}
+     */
+    boolean shouldValidateSubpackages();
+
+    /**
+     * Returns the validator ID. It should be unique i.e. not overlap between any two validators. To achieve that
+     * use a {@code <prefix>-<name>} as ID. Reserved prefixes are "jackrabbit" (used by all OOTB validators), "aem" and "sling".
+     * For custom validators use a company name as prefix. The name should not contain the string "validator".
+     * The id should only use lower case characters.
+     * @return the id of the validator returned by {@link #createValidator(ValidationContext, ValidatorSettings)}
+     */
+    @Nonnull String getId();
+    
+    /**
+     * The service ranking will influence the order in which the validators will be called.
+     * In general:
+     * The higher the ranking the earlier it will be executed
+     * @return the service ranking
+     */
+    int getServiceRanking();
+}

Copied: jackrabbit/commons/filevault/trunk/vault-validation/src/main/java/org/apache/jackrabbit/filevault/validation/spi/ValidatorSettings.java (from r1867026, jackrabbit/commons/filevault/trunk/vault-core/src/main/java/org/apache/jackrabbit/vault/fs/api/package-info.java)
URL: http://svn.apache.org/viewvc/jackrabbit/commons/filevault/trunk/vault-validation/src/main/java/org/apache/jackrabbit/filevault/validation/spi/ValidatorSettings.java?p2=jackrabbit/commons/filevault/trunk/vault-validation/src/main/java/org/apache/jackrabbit/filevault/validation/spi/ValidatorSettings.java&p1=jackrabbit/commons/filevault/trunk/vault-core/src/main/java/org/apache/jackrabbit/vault/fs/api/package-info.java&r1=1867026&r2=1867884&rev=1867884&view=diff
==============================================================================
--- jackrabbit/commons/filevault/trunk/vault-core/src/main/java/org/apache/jackrabbit/vault/fs/api/package-info.java (original)
+++ jackrabbit/commons/filevault/trunk/vault-validation/src/main/java/org/apache/jackrabbit/filevault/validation/spi/ValidatorSettings.java Wed Oct  2 11:28:25 2019
@@ -14,8 +14,35 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
+package org.apache.jackrabbit.filevault.validation.spi;
 
-@Version("2.6.1")
-package org.apache.jackrabbit.vault.fs.api;
+import java.util.Map;
 
-import org.osgi.annotation.versioning.Version;
\ No newline at end of file
+import javax.annotation.Nonnull;
+
+import org.osgi.annotation.versioning.ProviderType;
+/**
+ * Settings relevant for one {@link Validator}.
+ */
+@ProviderType
+public interface ValidatorSettings {
+
+    /**
+     * 
+     * @return the default severity for most {@link ValidationMessage}s being returned by the validator
+     */
+    @Nonnull ValidationMessageSeverity getDefaultSeverity();
+
+    /**
+     * 
+     * @return list of options relevant for this validator
+     */
+    @Nonnull Map<String, String> getOptions();
+
+    /**
+     * 
+     * @return {@code true} in case validator 
+     */
+    @Nonnull boolean isDisabled();
+
+}
\ No newline at end of file

Added: jackrabbit/commons/filevault/trunk/vault-validation/src/main/java/org/apache/jackrabbit/filevault/validation/spi/impl/AdvancedFilterValidator.java
URL: http://svn.apache.org/viewvc/jackrabbit/commons/filevault/trunk/vault-validation/src/main/java/org/apache/jackrabbit/filevault/validation/spi/impl/AdvancedFilterValidator.java?rev=1867884&view=auto
==============================================================================
--- jackrabbit/commons/filevault/trunk/vault-validation/src/main/java/org/apache/jackrabbit/filevault/validation/spi/impl/AdvancedFilterValidator.java (added)
+++ jackrabbit/commons/filevault/trunk/vault-validation/src/main/java/org/apache/jackrabbit/filevault/validation/spi/impl/AdvancedFilterValidator.java Wed Oct  2 11:28:25 2019
@@ -0,0 +1,388 @@
+/*
+ * 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.validation.spi.impl;
+
+import java.io.IOException;
+import java.io.InputStream;
+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.Iterator;
+import java.util.LinkedHashMap;
+import java.util.LinkedHashSet;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+import java.util.regex.PatternSyntaxException;
+import java.util.stream.Collectors;
+
+import javax.annotation.CheckForNull;
+import javax.annotation.Nonnull;
+import javax.xml.XMLConstants;
+import javax.xml.parsers.DocumentBuilder;
+import javax.xml.parsers.DocumentBuilderFactory;
+import javax.xml.parsers.ParserConfigurationException;
+import javax.xml.transform.Source;
+import javax.xml.transform.stream.StreamSource;
+import javax.xml.validation.Schema;
+import javax.xml.validation.SchemaFactory;
+
+import org.apache.commons.lang3.StringUtils;
+import org.apache.jackrabbit.filevault.validation.ValidationViolation;
+import org.apache.jackrabbit.filevault.validation.impl.util.ValidationMessageErrorHandler;
+import org.apache.jackrabbit.filevault.validation.spi.FilterValidator;
+import org.apache.jackrabbit.filevault.validation.spi.GenericMetaInfDataValidator;
+import org.apache.jackrabbit.filevault.validation.spi.NodePathValidator;
+import org.apache.jackrabbit.filevault.validation.spi.ValidationMessage;
+import org.apache.jackrabbit.filevault.validation.spi.ValidationMessageSeverity;
+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.config.ConfigurationException;
+import org.apache.jackrabbit.vault.fs.config.DefaultWorkspaceFilter;
+import org.apache.jackrabbit.vault.fs.filter.DefaultPathFilter;
+import org.apache.jackrabbit.vault.packaging.PackageInfo;
+import org.apache.jackrabbit.vault.util.Constants;
+import org.apache.jackrabbit.vault.util.Text;
+import org.w3c.dom.Document;
+import org.xml.sax.SAXException;
+
+public final class AdvancedFilterValidator implements GenericMetaInfDataValidator, FilterValidator, NodePathValidator {
+
+    protected static final String MESSAGE_ORPHANED_FILTER_ENTRIES = "Found orphaned filter entries: %s";
+    protected static final String MESSAGE_INVALID_PATTERN = "Invalid pattern given ('%s') which will never match for any descendants of the root path '%s'.";
+    protected static final String MESSAGE_ROOT_PATH_NOT_ABSOLUTE = "Root path must be absolute, but does not start with a '/': '%s'.";
+    protected static final String MESSAGE_INVALID_FILTER_XML = "Invalid filter.xml";
+    protected static final String MESSAGE_FILTER_ROOT_ANCESTOR_COVERED_BUT_EXCLUDED = "Filter root's ancestor '%s' is covered by dependency '%s' but excluded by its patterns.";
+    protected static final String MESSAGE_FILTER_ROOT_ANCESTOR_UNCOVERED = "Filter root's ancestor '%s' is not covered by any of the specified dependencies nor a valid root.";
+    protected static final String MESSAGE_NODE_NOT_CONTAINED = "Node '%s' is not contained in any of the filter rules";
+    protected static final String MESSAGE_ANCESTOR_NODE_NOT_COVERED = "Ancestor node '%s' is not covered by any of the filter rules. Preferably depend on a package that provides this node or include it in the filter rules!";
+    protected static final String MESSAGE_ANCESTOR_NODE_NOT_COVERED_BUT_VALID_ROOT = "Ancestor node '%s' is not covered by any of the filter rules but that node is a given root (either by a dependency or by the known roots). Remove that node!";
+    protected static final String MESSAGE_NODE_BELOW_CLEANUP_FILTER = "Node '%s' is covered by a 'cleanup' filter rule. That filter type is only supposed to be used for removing nodes during import!";
+    
+    public static final ValidationMessageSeverity DEFAULT_SEVERITY_FOR_UNCOVERED_ANCESTOR_NODES = ValidationMessageSeverity.INFO;
+
+    static final Path FILTER_XML_PATH = Paths.get(Constants.VAULT_DIR, Constants.FILTER_XML);
+
+    private final Collection<String> validRoots;
+    private final ValidationMessageSeverity defaultSeverity;
+    private final ValidationMessageSeverity severityForUncoveredAncestorNode;
+    private final ValidationMessageSeverity severityForOrphanedFilterEntries;
+    private final Collection<PackageInfo> dependenciesMetaInfo;
+    private final WorkspaceFilter filter;
+    private Map<String, FilterValidator> filterValidators;
+    private final Collection<String> danglingNodePaths;
+    private final Map<PathFilterSet, List<Entry<PathFilter>>> orphanedFilterSets;
+
+    public AdvancedFilterValidator(@Nonnull ValidationMessageSeverity defaultSeverity, @Nonnull ValidationMessageSeverity severityForUncoveredAncestorNodes, @Nonnull ValidationMessageSeverity severityForOrphanedFilterEntries, @Nonnull Collection<PackageInfo> dependenciesMetaInfo, @Nonnull WorkspaceFilter filter, @Nonnull Collection<String> validRoots) {
+        
+        this.filterValidators = new HashMap<>();
+        this.defaultSeverity = defaultSeverity;
+        this.severityForUncoveredAncestorNode = severityForUncoveredAncestorNodes;
+        this.severityForOrphanedFilterEntries = severityForOrphanedFilterEntries;
+        this.dependenciesMetaInfo = dependenciesMetaInfo;
+        this.filter = filter;
+        this.validRoots = validRoots;
+        this.danglingNodePaths = new LinkedList<>();
+        
+        // all roots from dependencies are also potentially valid
+        for (PackageInfo dependencyInfo : dependenciesMetaInfo) {
+            for (PathFilterSet set : dependencyInfo.getFilter().getFilterSets()) {
+                    String root = set.getRoot();
+                    validRoots.add(root);
+            }
+        }
+        this.orphanedFilterSets = new LinkedHashMap<>();
+        for (PathFilterSet pathFilter : filter.getFilterSets()) {
+            if (!PathFilterSet.TYPE_CLEANUP.equals(pathFilter.getType())) {
+                List<Entry<PathFilter>> entries = pathFilter.getEntries().stream().filter(Entry<PathFilter>::isInclude).collect(Collectors.toList());
+                // add all includes to a new list
+                this.orphanedFilterSets.put(pathFilter, entries);
+            }
+        }
+    }
+
+    public void setFilterValidators(Map<String, FilterValidator> filterValidators) {
+        this.filterValidators.putAll(filterValidators);
+    }
+
+    @Override
+    public Collection<ValidationMessage> done() {
+        StringBuilder orphanEntries = new StringBuilder();
+        for (java.util.Map.Entry<PathFilterSet, List<Entry<PathFilter>>> entry : this.orphanedFilterSets.entrySet()) {
+            // separator!
+            if (orphanEntries.length() > 0) {
+                orphanEntries.append(", ");
+            }
+            if (entry.getValue().isEmpty()) {
+                orphanEntries.append("entry with root '").append(entry.getKey().getRoot()).append("'");
+            } else {
+                orphanEntries.append("includes [");
+                StringBuilder includeEntries = new StringBuilder();
+                for (Entry<PathFilter> pathFilterEntry : entry.getValue()) {
+                    if (includeEntries.length() > 0) {
+                        includeEntries.append(", ");
+                    }
+                    includeEntries.append(pathFilterEntry.getFilter().toString());
+                }
+                orphanEntries.append(includeEntries).append("] below root '").append(entry.getKey().getRoot()).append("'");
+            }
+        }
+        if (orphanEntries.length() > 0) {
+            return Collections.singleton(new ValidationMessage(severityForOrphanedFilterEntries, String.format(MESSAGE_ORPHANED_FILTER_ENTRIES, orphanEntries.toString())));
+        } else {
+            return null;
+        }
+    }
+
+    @Override
+    public Collection<ValidationMessage> validate(WorkspaceFilter filter) {
+        Collection<ValidationMessage> messages = new LinkedList<>();
+        messages.addAll(validatePathFilterSets(filter.getFilterSets(), true));
+        messages.addAll(validatePathFilterSets(filter.getPropertyFilterSets(), false));
+
+        // first collect all ancestors (except for the valid roots)
+        Set<String> ancestors = new LinkedHashSet<>();
+        for (PathFilterSet set : filter.getFilterSets()) {
+            if ("cleanup".equals(set.getType())) {
+                continue;
+            }
+            String root = StringUtils.substringBeforeLast(set.getRoot(), "/");
+            // ignore well known roots
+            if (validRoots.contains(root)) {
+                continue;
+            }
+
+            // check if this package already contains the ancestor
+            if (filter.contains(root)) {
+                continue;
+            }
+            ancestors.add(root);
+        }
+        // then check for each ancestor
+        for (String root : ancestors) {
+            String isCovered = null;
+            boolean isContained = false;
+            for (PackageInfo dependencyInfo : dependenciesMetaInfo) {
+                WorkspaceFilter dependencyFilter = dependencyInfo.getFilter();
+                if (dependencyFilter.contains(root)) {
+                    isContained = true;
+                }
+                if (dependencyFilter.covers(root)) {
+                    isCovered = dependencyInfo.getId().toString();
+                }
+            }
+            if (!isContained) {
+                String msg;
+                if (isCovered == null) {
+                    msg = String.format(MESSAGE_FILTER_ROOT_ANCESTOR_UNCOVERED, root);
+                } else {
+                    msg = String.format(MESSAGE_FILTER_ROOT_ANCESTOR_COVERED_BUT_EXCLUDED, root, isCovered);
+                }
+                messages.add(new ValidationMessage(severityForUncoveredAncestorNode, msg));
+            }
+        }
+        return messages;
+    }
+
+    public Collection<ValidationMessage> validatePathFilterSets(Collection<PathFilterSet> pathFilterSets, boolean checkRoots) {
+        Collection<ValidationMessage> messages = new LinkedList<>();
+        for (PathFilterSet pathFilterSet : pathFilterSets) {
+            // check for validity of root path
+            if (checkRoots && !pathFilterSet.getRoot().startsWith("/")) {
+                messages.add(new ValidationMessage(defaultSeverity,
+                        String.format(MESSAGE_ROOT_PATH_NOT_ABSOLUTE, pathFilterSet.getRoot())));
+            }
+            for (Entry<PathFilter> pathFilterEntry : pathFilterSet.getEntries()) {
+                if (!(pathFilterEntry.getFilter() instanceof DefaultPathFilter)) {
+                    throw new IllegalStateException(
+                            "Unexpected path filter found: " + pathFilterEntry.getFilter() + ". Must be of type DefaultPathFilter!");
+                }
+                DefaultPathFilter defaultPathFilter = DefaultPathFilter.class.cast(pathFilterEntry.getFilter());
+                defaultPathFilter.getPattern();
+                if (!isRegexValidForRootPath(defaultPathFilter.getPattern(), pathFilterSet.getRoot())) {
+                    messages.add(new ValidationMessage(defaultSeverity,
+                            String.format(MESSAGE_INVALID_PATTERN, defaultPathFilter.getPattern(), pathFilterSet.getRoot())));
+                }
+            }
+        }
+        return messages;
+    }
+
+    /** Checks if the regex would at least have the chance to match if the matching path starts with root path.
+     * 
+     * @param regex
+     * @param rootPath
+     * @return */
+    static boolean isRegexValidForRootPath(String regex, String rootPath) {
+        Pattern pattern = Pattern.compile(regex);
+        Matcher matcher = pattern.matcher(rootPath);
+        if (matcher.matches()) {
+            return true;
+        }
+        return matcher.hitEnd();
+    }
+
+    @Override
+    public Collection<ValidationMessage> validateMetaInfData(InputStream input, Path filePath) throws IOException {
+        DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
+        factory.setNamespaceAware(true);
+        try (InputStream xsdInput = getClass().getResourceAsStream("/filter.xsd")) {
+            SchemaFactory schemaFactory = SchemaFactory.newInstance(XMLConstants.W3C_XML_SCHEMA_NS_URI);
+
+            // load a WXS schema, represented by a Schema instance
+            Source schemaFile = new StreamSource(xsdInput);
+            Schema schema = schemaFactory.newSchema(schemaFile);
+            factory.setSchema(schema);
+            Collection<ValidationMessage> messages = new LinkedList<>();
+            if (xsdInput == null) {
+                throw new IllegalStateException("Can not load filter.xsd");
+            }
+            factory.setFeature(XMLConstants.FEATURE_SECURE_PROCESSING, true);
+            DocumentBuilder parser = factory.newDocumentBuilder();
+            ValidationMessageErrorHandler errorHandler = new ValidationMessageErrorHandler(defaultSeverity);
+            parser.setErrorHandler(errorHandler);
+            Document document = parser.parse(input, "");
+
+            messages.addAll(errorHandler.getValidationMessages());
+            DefaultWorkspaceFilter filter = new DefaultWorkspaceFilter();
+            try {
+                filter.load(document.getDocumentElement());
+                // call all registered filter validators
+                for (Map.Entry<String, FilterValidator> entry : filterValidators.entrySet()) {
+                    messages.add(new ValidationMessage(ValidationMessageSeverity.DEBUG,
+                            "Validating with validator " + entry.getKey() + "..."));
+                    Collection<ValidationMessage> filterValidatorMessages = entry.getValue().validate(filter);
+                    if (filterValidatorMessages != null) {
+                        messages.addAll(ValidationViolation.wrapMessages(entry.getKey(), filterValidatorMessages, null, null, null, 0, 0));
+                    }
+                }
+            } catch (ConfigurationException | PatternSyntaxException e) { // TODO: remove PatternSyntaxException once
+                                                                          // https://issues.apache.org/jira/browse/JCRVLT-368 is fixed
+                messages.add(new ValidationMessage(defaultSeverity, MESSAGE_INVALID_FILTER_XML, e));
+            }
+            return messages;
+        } catch (SAXException e) {
+            throw new IOException("Could not parse input as xml", e);
+        } catch (ParserConfigurationException e) {
+            throw new IllegalStateException("Could not instantiate DOM parser", e);
+        }
+    }
+
+    @Override
+    public boolean shouldValidateMetaInfData(Path filePath) {
+        return FILTER_XML_PATH.equals(filePath);
+    }
+
+    private void removeFromOrphanedFilterEntries(String nodePath) {
+        // find all filter roots which match
+        Iterator<java.util.Map.Entry<PathFilterSet, List<Entry<PathFilter>>>> iter = orphanedFilterSets.entrySet().iterator();
+        while (iter.hasNext()) {
+            java.util.Map.Entry<PathFilterSet, List<Entry<PathFilter>>> orphanedFilterEntry = iter.next();
+            if (orphanedFilterEntry.getKey().contains(nodePath)) {
+                Iterator<Entry<PathFilter>> includeIterator = orphanedFilterEntry.getValue().iterator();
+                // check all include and remove if they apply to the node path
+                while (includeIterator.hasNext()) {
+                    Entry<PathFilter> includeEntry = includeIterator.next();
+                    if (includeEntry.isInclude() && includeEntry.getFilter().matches(nodePath)) {
+                        includeIterator.remove();
+                    }
+                }
+                // remove the whole entry if no includes are left
+                if (orphanedFilterEntry.getValue().isEmpty()) {
+                    // remove it
+                    iter.remove();
+                }
+            }
+        }
+    }
+
+    @Override
+    public Collection<ValidationMessage> validate(String nodePath) {
+        // remove from orphaned list
+        removeFromOrphanedFilterEntries(nodePath);
+        
+        // now go through all includes
+        if (!filter.contains(nodePath)) {
+            if (filter.isAncestor(nodePath)) {
+                // consider valid roots
+                if (validRoots.contains(nodePath)) {
+                    return Collections.singleton(
+                            new ValidationMessage(severityForUncoveredAncestorNode,
+                                    String.format(MESSAGE_ANCESTOR_NODE_NOT_COVERED_BUT_VALID_ROOT, nodePath)));
+                } else {
+                    return Collections.singleton(
+                                new ValidationMessage(severityForUncoveredAncestorNode,
+                                        String.format(MESSAGE_ANCESTOR_NODE_NOT_COVERED, nodePath)));
+                }
+            } else {
+                return Collections
+                        .singleton(new ValidationMessage(defaultSeverity, String.format(MESSAGE_NODE_NOT_CONTAINED, nodePath)));
+            }
+        } else {
+            // is it a cleanup filter?
+            PathFilterSet pathFilterSet = filter.getCoveringFilterSet(nodePath);
+            if (pathFilterSet != null) {
+                if (PathFilterSet.TYPE_CLEANUP.equals(pathFilterSet.getType())) {
+                    return Collections
+                            .singleton(new ValidationMessage(defaultSeverity, String.format(MESSAGE_NODE_BELOW_CLEANUP_FILTER, nodePath)));
+                }
+            }
+        }
+        // check that all ancestor nodes till the root node are contained as well
+        String danglingNodePath = getDanglingAncestorNodePath(nodePath, filter);
+        if (danglingNodePath != null) {
+            return Collections.singleton(
+                    new ValidationMessage(defaultSeverity, "Ancestor node (" + danglingNodePath + ") of Node '" + nodePath +"' which is contained in a filter include element is not included!"));
+        }
+        return null;
+    }
+
+    /**
+     * 
+     * @param nodePath
+     * @return the path the ancestor node not contained in the filter or {@code null}
+     */
+     @CheckForNull String getDanglingAncestorNodePath(String nodePath, WorkspaceFilter filter) {
+        // check cache first (in that case the issue has already been emitted)
+        if (danglingNodePaths.contains(nodePath)) {
+            return null;
+        }
+        // check that all ancestor nodes till the filter root node are contained as well
+        for (PathFilterSet pathFilterSet : filter.getFilterSets()) {
+            if (pathFilterSet.contains(nodePath)) {
+                String parentNodePath = Text.getRelativeParent(nodePath, 1);
+                // make sure that all ancestors till the root node are contained as well
+                if (!nodePath.equals(pathFilterSet.getRoot()) && !parentNodePath.equals(pathFilterSet.getRoot())) {
+                    // ancestor might also be contained in another filter
+                    return getDanglingAncestorNodePath(parentNodePath, filter);
+                } else {
+                    // once the root level is reached this node path is contained
+                    return null;
+                }
+            } 
+        }
+        danglingNodePaths.add(nodePath);
+        return nodePath;
+    }
+}

Added: jackrabbit/commons/filevault/trunk/vault-validation/src/main/java/org/apache/jackrabbit/filevault/validation/spi/impl/AdvancedFilterValidatorFactory.java
URL: http://svn.apache.org/viewvc/jackrabbit/commons/filevault/trunk/vault-validation/src/main/java/org/apache/jackrabbit/filevault/validation/spi/impl/AdvancedFilterValidatorFactory.java?rev=1867884&view=auto
==============================================================================
--- jackrabbit/commons/filevault/trunk/vault-validation/src/main/java/org/apache/jackrabbit/filevault/validation/spi/impl/AdvancedFilterValidatorFactory.java (added)
+++ jackrabbit/commons/filevault/trunk/vault-validation/src/main/java/org/apache/jackrabbit/filevault/validation/spi/impl/AdvancedFilterValidatorFactory.java Wed Oct  2 11:28:25 2019
@@ -0,0 +1,92 @@
+/*
+ * 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.validation.spi.impl;
+
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.HashSet;
+import java.util.LinkedList;
+import java.util.Set;
+
+import org.apache.jackrabbit.filevault.validation.spi.ValidationContext;
+import org.apache.jackrabbit.filevault.validation.spi.ValidationMessageSeverity;
+import org.apache.jackrabbit.filevault.validation.spi.Validator;
+import org.apache.jackrabbit.filevault.validation.spi.ValidatorFactory;
+import org.apache.jackrabbit.filevault.validation.spi.ValidatorSettings;
+import org.apache.jackrabbit.vault.packaging.PackageType;
+import org.kohsuke.MetaInfServices;
+
+@MetaInfServices
+public final class AdvancedFilterValidatorFactory implements ValidatorFactory {
+
+    protected static final String OPTION_SEVERITY_FOR_UNCOVERED_ANCESTOR_NODES = "severityForUncoveredAncestorNodes";
+    protected static final String OPTION_SEVERITY_FOR_ORPHANED_FILTER_RULES = "severityForOrphanedFilterRules";
+    // should take comma-separated list of valid root paths
+    protected static final String OPTION_VALID_ROOTS = "validRoots";
+    
+    protected static final ValidationMessageSeverity DEFAULT_SEVERITY_FOR_UNCOVERED_ANCESTOR_NODES = ValidationMessageSeverity.INFO;
+    protected static final ValidationMessageSeverity DEFAULT_SEVERITY_FOR_ORPHANED_FILTER_RULES = ValidationMessageSeverity.INFO;
+    protected static final Collection<String> DEFAULT_VALID_ROOTS = new LinkedList<>(Arrays.asList("/","/libs","/apps","/etc","/var","/tmp","/content"));
+
+    @Override
+    public Validator createValidator(ValidationContext context, ValidatorSettings settings) {
+        final ValidationMessageSeverity messageSeverityForUncoveredAncestorNode;
+        if (PackageType.APPLICATION.equals(context.getProperties().getPackageType())) {
+            messageSeverityForUncoveredAncestorNode = ValidationMessageSeverity.ERROR;
+        } else {
+            if (settings.getOptions().containsKey(OPTION_SEVERITY_FOR_UNCOVERED_ANCESTOR_NODES)) {
+                String optionValue = settings.getOptions().get(OPTION_SEVERITY_FOR_UNCOVERED_ANCESTOR_NODES);
+                messageSeverityForUncoveredAncestorNode = ValidationMessageSeverity.valueOf(optionValue.toUpperCase());
+            } else {
+                messageSeverityForUncoveredAncestorNode = DEFAULT_SEVERITY_FOR_UNCOVERED_ANCESTOR_NODES;
+            }
+        }
+        final ValidationMessageSeverity messageSeverityForOrphanedFilterRules;
+        if (settings.getOptions().containsKey(OPTION_SEVERITY_FOR_ORPHANED_FILTER_RULES)) {
+            String optionValue = settings.getOptions().get(OPTION_SEVERITY_FOR_ORPHANED_FILTER_RULES);
+            messageSeverityForOrphanedFilterRules = ValidationMessageSeverity.valueOf(optionValue.toUpperCase());
+        } else {
+            messageSeverityForOrphanedFilterRules = DEFAULT_SEVERITY_FOR_ORPHANED_FILTER_RULES;
+        }
+        Set<String> validRoots = new HashSet<>();
+        validRoots.add("");
+        if (settings.getOptions().containsKey(OPTION_VALID_ROOTS)) {
+            String optionValue = settings.getOptions().get(OPTION_VALID_ROOTS);
+            validRoots.addAll(Arrays.asList(optionValue.split(",")));
+        } else {
+            validRoots.addAll(DEFAULT_VALID_ROOTS);
+        }
+        
+        return new AdvancedFilterValidator(settings.getDefaultSeverity(), messageSeverityForUncoveredAncestorNode, messageSeverityForOrphanedFilterRules, context.getDependenciesMetaInfo(), context.getFilter(), validRoots);
+    }
+
+    @Override
+    public boolean shouldValidateSubpackages() {
+        return false;
+    }
+
+    @Override
+    public String getId() {
+        return ValidatorFactory.PREFIX_JACKRABBIT + "filter";
+    }
+
+    @Override
+    public int getServiceRanking() {
+        return Integer.MAX_VALUE;
+    }
+
+}

Added: jackrabbit/commons/filevault/trunk/vault-validation/src/main/java/org/apache/jackrabbit/filevault/validation/spi/impl/AdvancedPropertiesValidator.java
URL: http://svn.apache.org/viewvc/jackrabbit/commons/filevault/trunk/vault-validation/src/main/java/org/apache/jackrabbit/filevault/validation/spi/impl/AdvancedPropertiesValidator.java?rev=1867884&view=auto
==============================================================================
--- jackrabbit/commons/filevault/trunk/vault-validation/src/main/java/org/apache/jackrabbit/filevault/validation/spi/impl/AdvancedPropertiesValidator.java (added)
+++ jackrabbit/commons/filevault/trunk/vault-validation/src/main/java/org/apache/jackrabbit/filevault/validation/spi/impl/AdvancedPropertiesValidator.java Wed Oct  2 11:28:25 2019
@@ -0,0 +1,87 @@
+/*
+ * 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.validation.spi.impl;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.InvalidPropertiesFormatException;
+import java.util.LinkedList;
+import java.util.Map;
+
+import org.apache.jackrabbit.filevault.validation.ValidationViolation;
+import org.apache.jackrabbit.filevault.validation.spi.GenericMetaInfDataValidator;
+import org.apache.jackrabbit.filevault.validation.spi.PropertiesValidator;
+import org.apache.jackrabbit.filevault.validation.spi.ValidationMessage;
+import org.apache.jackrabbit.filevault.validation.spi.ValidationMessageSeverity;
+import org.apache.jackrabbit.vault.packaging.PackageProperties;
+import org.apache.jackrabbit.vault.packaging.impl.DefaultPackageProperties;
+import org.apache.jackrabbit.vault.util.Constants;
+
+public final class AdvancedPropertiesValidator implements GenericMetaInfDataValidator {
+
+    protected static final String MESSAGE_INVALID_PROPERTIES_XML = "Invalid properties.xml";
+
+    static final Path PROPERTIES_XML_PATH = Paths.get(Constants.VAULT_DIR).resolve(Constants.PROPERTIES_XML);
+
+    private final Map<String, PropertiesValidator> propertiesValidators;
+    private final ValidationMessageSeverity severity;
+
+    public AdvancedPropertiesValidator(ValidationMessageSeverity severity) {
+        this.propertiesValidators = new HashMap<>();
+        this.severity = severity;
+    }
+
+    public void setPropertiesValidators(Map<String, PropertiesValidator> propertiesValidators) {
+        this.propertiesValidators.putAll(propertiesValidators);
+    }
+
+    @Override
+    public Collection<ValidationMessage> done() {
+        return null;
+    }
+
+    @Override
+    public Collection<ValidationMessage> validateMetaInfData(InputStream input, Path filePath) {
+        Collection<ValidationMessage> messages = new LinkedList<>();
+        try {
+            PackageProperties properties = DefaultPackageProperties.fromInputStream(input);
+            // call all registered properties validators
+            for (Map.Entry<String, PropertiesValidator> entry : propertiesValidators.entrySet()) {
+                messages.add(new ValidationMessage(ValidationMessageSeverity.DEBUG, "Validating with validator " + entry.getKey() + "..."));
+                Collection<ValidationMessage> propertiesValidatorMessages = entry.getValue().validate(properties);
+                if (propertiesValidatorMessages != null) {
+                    messages.addAll(ValidationViolation.wrapMessages(entry.getKey(), propertiesValidatorMessages, null, null, null, 0, 0));
+                }
+            }
+        } catch (InvalidPropertiesFormatException e) {
+            messages.add(new ValidationMessage(severity, MESSAGE_INVALID_PROPERTIES_XML, e));
+        } catch (IOException e) {
+            throw new IllegalStateException("Could not read from input stream " + filePath, e);
+        }
+        return messages;
+    }
+
+    @Override
+    public boolean shouldValidateMetaInfData(Path filePath) {
+        return PROPERTIES_XML_PATH.equals(filePath);
+    }
+
+}

Added: jackrabbit/commons/filevault/trunk/vault-validation/src/main/java/org/apache/jackrabbit/filevault/validation/spi/impl/AdvancedPropertiesValidatorFactory.java
URL: http://svn.apache.org/viewvc/jackrabbit/commons/filevault/trunk/vault-validation/src/main/java/org/apache/jackrabbit/filevault/validation/spi/impl/AdvancedPropertiesValidatorFactory.java?rev=1867884&view=auto
==============================================================================
--- jackrabbit/commons/filevault/trunk/vault-validation/src/main/java/org/apache/jackrabbit/filevault/validation/spi/impl/AdvancedPropertiesValidatorFactory.java (added)
+++ jackrabbit/commons/filevault/trunk/vault-validation/src/main/java/org/apache/jackrabbit/filevault/validation/spi/impl/AdvancedPropertiesValidatorFactory.java Wed Oct  2 11:28:25 2019
@@ -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.jackrabbit.filevault.validation.spi.impl;
+
+import org.apache.jackrabbit.filevault.validation.spi.ValidationContext;
+import org.apache.jackrabbit.filevault.validation.spi.Validator;
+import org.apache.jackrabbit.filevault.validation.spi.ValidatorFactory;
+import org.apache.jackrabbit.filevault.validation.spi.ValidatorSettings;
+import org.kohsuke.MetaInfServices;
+
+@MetaInfServices
+public final class AdvancedPropertiesValidatorFactory implements ValidatorFactory {
+
+    @Override
+    public Validator createValidator(ValidationContext context, ValidatorSettings settings) {
+        return new AdvancedPropertiesValidator(settings.getDefaultSeverity());
+    }
+
+    @Override
+    public boolean shouldValidateSubpackages() {
+        return false;
+    }
+
+    @Override
+    public String getId() {
+        return ValidatorFactory.PREFIX_JACKRABBIT + "properties";
+    }
+
+    @Override
+    public int getServiceRanking() {
+        return Integer.MAX_VALUE;
+    }
+
+}

Added: jackrabbit/commons/filevault/trunk/vault-validation/src/main/java/org/apache/jackrabbit/filevault/validation/spi/impl/DependencyValidator.java
URL: http://svn.apache.org/viewvc/jackrabbit/commons/filevault/trunk/vault-validation/src/main/java/org/apache/jackrabbit/filevault/validation/spi/impl/DependencyValidator.java?rev=1867884&view=auto
==============================================================================
--- jackrabbit/commons/filevault/trunk/vault-validation/src/main/java/org/apache/jackrabbit/filevault/validation/spi/impl/DependencyValidator.java (added)
+++ jackrabbit/commons/filevault/trunk/vault-validation/src/main/java/org/apache/jackrabbit/filevault/validation/spi/impl/DependencyValidator.java Wed Oct  2 11:28:25 2019
@@ -0,0 +1,88 @@
+/*
+ * 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.validation.spi.impl;
+
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.LinkedList;
+import java.util.Map;
+
+import org.apache.jackrabbit.filevault.validation.spi.PropertiesValidator;
+import org.apache.jackrabbit.filevault.validation.spi.ValidationMessage;
+import org.apache.jackrabbit.filevault.validation.spi.ValidationMessageSeverity;
+import org.apache.jackrabbit.vault.fs.api.PathFilterSet;
+import org.apache.jackrabbit.vault.packaging.Dependency;
+import org.apache.jackrabbit.vault.packaging.PackageInfo;
+import org.apache.jackrabbit.vault.packaging.PackageProperties;
+
+public final class DependencyValidator implements PropertiesValidator {
+
+    static final String MESSAGE_DEPENDENCIES_WITH_OVERLAPPING_FILTERS = "Dependency '%s' defines same filter root '%s' as dependency '%s'";
+    static final String MESSAGE_UNRESOLVED_DEPENDENCY = "Dependency '%s' is not using maven coordinates and cannot be used for analysis.";
+    private final Collection<PackageInfo> dependenciesMetaInfo;
+    private final ValidationMessageSeverity severity;
+    private final ValidationMessageSeverity severityForUnresolvedDependencies;
+    
+    public DependencyValidator(ValidationMessageSeverity severity, ValidationMessageSeverity severityForUnresolvedDependencies, Collection<PackageInfo> dependenciesMetaInfo) {
+        this.dependenciesMetaInfo = dependenciesMetaInfo;
+        this.severity = severity;
+        this.severityForUnresolvedDependencies = severityForUnresolvedDependencies;
+    }
+
+    @Override
+    public Collection<ValidationMessage> done() {
+        return null;
+    }
+
+    @Override
+    public Collection<ValidationMessage> validate(PackageProperties properties) {
+        
+        // use resolved dependencies
+        Collection<ValidationMessage> messages = new LinkedList<>();
+        
+        Map<String, PackageInfo> roots = new HashMap<>();
+        
+        // check for unresolved dependencies!
+        for (Dependency dependency : properties.getDependencies()) {
+            boolean isDependencyResolved = false;
+            for (PackageInfo resolvedDependency : dependenciesMetaInfo) {
+                if (dependency.matches(resolvedDependency.getId())) {
+                    for (PathFilterSet set : resolvedDependency.getFilter().getFilterSets()) {
+                        String root = set.getRoot();
+                        PackageInfo existing = roots.get(root);
+                        if (existing != null) {
+                            String msg = String.format(MESSAGE_DEPENDENCIES_WITH_OVERLAPPING_FILTERS,
+                                    resolvedDependency.getId(), root, existing.getId());
+                            messages.add(new ValidationMessage(severity, msg));
+                        }
+                        roots.put(root, resolvedDependency);
+                    }
+                    isDependencyResolved = true;
+                    break;
+                }
+            }
+            if (!isDependencyResolved) {
+                String msg = String.format(MESSAGE_UNRESOLVED_DEPENDENCY, dependency);
+                messages.add(new ValidationMessage(severityForUnresolvedDependencies, msg));
+                continue;
+            }
+            // TODO: check for overlapping roots with current filter.xml
+        }
+        return messages;
+    }
+
+}

Added: jackrabbit/commons/filevault/trunk/vault-validation/src/main/java/org/apache/jackrabbit/filevault/validation/spi/impl/DependencyValidatorFactory.java
URL: http://svn.apache.org/viewvc/jackrabbit/commons/filevault/trunk/vault-validation/src/main/java/org/apache/jackrabbit/filevault/validation/spi/impl/DependencyValidatorFactory.java?rev=1867884&view=auto
==============================================================================
--- jackrabbit/commons/filevault/trunk/vault-validation/src/main/java/org/apache/jackrabbit/filevault/validation/spi/impl/DependencyValidatorFactory.java (added)
+++ jackrabbit/commons/filevault/trunk/vault-validation/src/main/java/org/apache/jackrabbit/filevault/validation/spi/impl/DependencyValidatorFactory.java Wed Oct  2 11:28:25 2019
@@ -0,0 +1,61 @@
+/*
+ * 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.validation.spi.impl;
+
+import org.apache.jackrabbit.filevault.validation.spi.ValidationContext;
+import org.apache.jackrabbit.filevault.validation.spi.ValidationMessageSeverity;
+import org.apache.jackrabbit.filevault.validation.spi.Validator;
+import org.apache.jackrabbit.filevault.validation.spi.ValidatorFactory;
+import org.apache.jackrabbit.filevault.validation.spi.ValidatorSettings;
+import org.kohsuke.MetaInfServices;
+
+@MetaInfServices
+public final class DependencyValidatorFactory implements ValidatorFactory {
+
+    public static final String ID = PREFIX_JACKRABBIT + "dependencies";
+    
+    public static final String OPTION_SEVERITY_FOR_UNRESOLVED_DEPENDENCIES = "severityForUnresolvedDependencies";
+    public static final ValidationMessageSeverity DEFAULT_SEVERITY_FOR_UNRESOLVED_DEPENDENCIES = ValidationMessageSeverity.WARN;
+    
+    @Override
+    public Validator createValidator(ValidationContext context, ValidatorSettings settings) {
+        final ValidationMessageSeverity severityForUnresolvedDependencies;
+        if (settings.getOptions().containsKey(OPTION_SEVERITY_FOR_UNRESOLVED_DEPENDENCIES)) {
+            String optionValue = settings.getOptions().get(OPTION_SEVERITY_FOR_UNRESOLVED_DEPENDENCIES);
+            severityForUnresolvedDependencies = ValidationMessageSeverity.valueOf(optionValue.toUpperCase());
+        } else {
+            severityForUnresolvedDependencies = DEFAULT_SEVERITY_FOR_UNRESOLVED_DEPENDENCIES;
+        }
+        return new DependencyValidator(settings.getDefaultSeverity(), severityForUnresolvedDependencies, context.getDependenciesMetaInfo());
+    }
+
+    @Override
+    public boolean shouldValidateSubpackages() {
+        return false;
+    }
+
+    @Override
+    public String getId() {
+        return ID;
+    }
+
+    @Override
+    public int getServiceRanking() {
+        return 0;
+    }
+
+}

Added: jackrabbit/commons/filevault/trunk/vault-validation/src/main/java/org/apache/jackrabbit/filevault/validation/spi/impl/DocumentViewParserValidator.java
URL: http://svn.apache.org/viewvc/jackrabbit/commons/filevault/trunk/vault-validation/src/main/java/org/apache/jackrabbit/filevault/validation/spi/impl/DocumentViewParserValidator.java?rev=1867884&view=auto
==============================================================================
--- jackrabbit/commons/filevault/trunk/vault-validation/src/main/java/org/apache/jackrabbit/filevault/validation/spi/impl/DocumentViewParserValidator.java (added)
+++ jackrabbit/commons/filevault/trunk/vault-validation/src/main/java/org/apache/jackrabbit/filevault/validation/spi/impl/DocumentViewParserValidator.java Wed Oct  2 11:28:25 2019
@@ -0,0 +1,163 @@
+/*
+ * 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.validation.spi.impl;
+
+import java.io.BufferedInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Map;
+
+import javax.xml.parsers.SAXParser;
+
+import org.apache.commons.io.input.CloseShieldInputStream;
+import org.apache.jackrabbit.filevault.validation.ValidationExecutor;
+import org.apache.jackrabbit.filevault.validation.ValidationViolation;
+import org.apache.jackrabbit.filevault.validation.impl.util.DocumentViewXmlContentHandler;
+import org.apache.jackrabbit.filevault.validation.impl.util.EnhancedBufferedInputStream;
+import org.apache.jackrabbit.filevault.validation.spi.DocumentViewXmlValidator;
+import org.apache.jackrabbit.filevault.validation.spi.GenericJcrDataValidator;
+import org.apache.jackrabbit.filevault.validation.spi.ValidationMessage;
+import org.apache.jackrabbit.filevault.validation.spi.ValidationMessageSeverity;
+import org.apache.jackrabbit.vault.fs.api.SerializationType;
+import org.apache.jackrabbit.vault.fs.impl.io.XmlAnalyzer;
+import org.apache.jackrabbit.vault.util.Constants;
+import org.xml.sax.InputSource;
+import org.xml.sax.SAXException;
+import org.xml.sax.XMLReader;
+
+public class DocumentViewParserValidator implements GenericJcrDataValidator {
+
+    private final Map<String, DocumentViewXmlValidator> docViewValidators;
+    private final SAXParser saxParser;
+    private final ValidationMessageSeverity severity;
+    
+    public DocumentViewParserValidator(SAXParser saxParser, ValidationMessageSeverity severity) {
+        super();
+        this.docViewValidators = new HashMap<>();
+        this.saxParser = saxParser;
+        this.severity = severity;
+    }
+
+    public void setDocumentViewXmlValidators(Map<String, DocumentViewXmlValidator> documentViewXmlValidators) {
+        this.docViewValidators.putAll(documentViewXmlValidators);
+    }
+   
+    @Override
+    public Collection<ValidationMessage> done() {
+        return null;
+    }
+
+    @Override
+    public Collection<ValidationMessage> validateJcrData(InputStream input, Path filePath, Map<String, Integer> nodePathsAndLineNumbers) throws IOException {
+        Collection<ValidationMessage> messages = new LinkedList<>();
+        // TODO: support other formats like sysview xml or generic xml
+        // (https://jackrabbit.apache.org/filevault/vaultfs.html#Deserialization)
+
+        // wrap input stream as buffered input stream (to be able to reset it and for performance reasons)
+        final EnhancedBufferedInputStream bufferedInput = new EnhancedBufferedInputStream(input);
+
+        Path documentViewXmlRootPath = getDocumentViewXmlRootPath(bufferedInput, filePath);
+        if (documentViewXmlRootPath != null) {
+            try {
+                messages.addAll(validateDocumentViewXml(bufferedInput, filePath, ValidationExecutor.filePathToNodePath(documentViewXmlRootPath),
+                            nodePathsAndLineNumbers));
+            } catch (SAXException e) {
+                throw new IOException("Could not parse xml", e);
+            }
+        } else {
+            messages.add(new ValidationMessage(ValidationMessageSeverity.INFO, "This file is not detected as docview xml file and therefore treated as binary"));
+            nodePathsAndLineNumbers.put(ValidationExecutor.filePathToNodePath(filePath), 0);
+        }
+        
+       return messages;
+    }
+
+
+    /** @param input, the given input stream must be reset later on
+     * @param path
+     * @return either the path of the root node of the given docview xml or {@code null} if no docview xml given
+     * @throws IOException */
+    private static Path getDocumentViewXmlRootPath(BufferedInputStream input, Path path) throws IOException {
+        Path name = path.getFileName();
+        Path rootPath = null;
+
+        int nameCount = path.getNameCount();
+        if (name.equals(Paths.get(Constants.DOT_CONTENT_XML))) {
+            if (nameCount > 1) {
+                rootPath = path.subpath(0, nameCount - 1);
+            } else {
+                rootPath = Paths.get("");
+            }
+            // correct suffix matching
+        } else if (name.toString().endsWith(".xml")) {
+
+            // we need to rely on a buffered input stream to be able to reset it later
+            input.mark(1024);
+            // analyze content
+            // this closes the input source internally, therefore protect against closing
+            // make sure to initialize the SLF4J logger appropriately (for the XmlAnalyzer)
+            try {
+                SerializationType type = XmlAnalyzer.analyze(new InputSource(new CloseShieldInputStream(input)));
+                if (type == SerializationType.XML_DOCVIEW) {
+                    //  remove .xml extension
+                    String fileName = path.getFileName().toString();
+                    fileName = fileName.substring(0, fileName.length() - ".xml".length());
+                    if (nameCount > 1) {
+                        rootPath = path.subpath(0, nameCount - 1).resolve(fileName);
+                    } else {
+                        rootPath = Paths.get(fileName);
+                    }
+                }
+            } finally {
+                input.reset();
+            }
+        }
+        return rootPath;
+    }
+
+    protected Collection<ValidationMessage> validateDocumentViewXml(InputStream input, Path filePath, String rootNodePath,
+            Map<String, Integer> nodePathsAndLineNumbers) throws IOException, SAXException {
+        List<ValidationMessage> enrichedMessages = new LinkedList<>();
+        XMLReader xr = saxParser.getXMLReader();
+        final DocumentViewXmlContentHandler handler = new DocumentViewXmlContentHandler(filePath, rootNodePath,
+                docViewValidators);
+        enrichedMessages.add(new ValidationMessage(ValidationMessageSeverity.DEBUG, "Detected DocView..."));
+        xr.setContentHandler(handler);
+        try {
+            xr.parse(new InputSource(new CloseShieldInputStream(input)));
+            enrichedMessages.addAll(ValidationViolation.wrapMessages(null, handler.getViolations(), filePath, null, null, 0, 0));
+        } catch (SAXException e) {
+            enrichedMessages.add(new ValidationViolation(severity, "Invalid XML found: " + e.getMessage(), filePath, null, null, 0, 0, e));
+        }
+        nodePathsAndLineNumbers.putAll(handler.getNodePaths());
+        return enrichedMessages;
+    }
+
+    // support upper case extensions?
+    @Override
+    public boolean shouldValidateJcrData(Path filePath) {
+        return filePath.toString().endsWith(".xml");
+    }
+
+
+}

Added: jackrabbit/commons/filevault/trunk/vault-validation/src/main/java/org/apache/jackrabbit/filevault/validation/spi/impl/DocumentViewParserValidatorFactory.java
URL: http://svn.apache.org/viewvc/jackrabbit/commons/filevault/trunk/vault-validation/src/main/java/org/apache/jackrabbit/filevault/validation/spi/impl/DocumentViewParserValidatorFactory.java?rev=1867884&view=auto
==============================================================================
--- jackrabbit/commons/filevault/trunk/vault-validation/src/main/java/org/apache/jackrabbit/filevault/validation/spi/impl/DocumentViewParserValidatorFactory.java (added)
+++ jackrabbit/commons/filevault/trunk/vault-validation/src/main/java/org/apache/jackrabbit/filevault/validation/spi/impl/DocumentViewParserValidatorFactory.java Wed Oct  2 11:28:25 2019
@@ -0,0 +1,64 @@
+/*
+ * 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.validation.spi.impl;
+
+import javax.xml.XMLConstants;
+import javax.xml.parsers.ParserConfigurationException;
+import javax.xml.parsers.SAXParser;
+import javax.xml.parsers.SAXParserFactory;
+
+import org.apache.jackrabbit.filevault.validation.spi.ValidationContext;
+import org.apache.jackrabbit.filevault.validation.spi.Validator;
+import org.apache.jackrabbit.filevault.validation.spi.ValidatorFactory;
+import org.apache.jackrabbit.filevault.validation.spi.ValidatorSettings;
+import org.kohsuke.MetaInfServices;
+import org.xml.sax.SAXException;
+
+@MetaInfServices
+public class DocumentViewParserValidatorFactory implements ValidatorFactory {
+
+    private SAXParser saxParser;
+
+    public DocumentViewParserValidatorFactory() throws ParserConfigurationException, SAXException {
+
+        SAXParserFactory spf = SAXParserFactory.newInstance();
+        spf.setNamespaceAware(true);
+        spf.setFeature(XMLConstants.FEATURE_SECURE_PROCESSING, true);
+        saxParser = spf.newSAXParser();
+    }
+    @Override
+    public Validator createValidator(ValidationContext context, ValidatorSettings settings) {
+        return new DocumentViewParserValidator(saxParser, settings.getDefaultSeverity());
+    }
+
+    @Override
+    public boolean shouldValidateSubpackages() {
+        return false;
+    }
+
+    @Override
+    public String getId() {
+        return ValidatorFactory.PREFIX_JACKRABBIT + "docviewparser";
+    }
+
+    @Override
+    public int getServiceRanking() {
+        return Integer.MAX_VALUE;
+    }
+
+    
+}

Added: jackrabbit/commons/filevault/trunk/vault-validation/src/main/java/org/apache/jackrabbit/filevault/validation/spi/impl/EmptyElementsValidator.java
URL: http://svn.apache.org/viewvc/jackrabbit/commons/filevault/trunk/vault-validation/src/main/java/org/apache/jackrabbit/filevault/validation/spi/impl/EmptyElementsValidator.java?rev=1867884&view=auto
==============================================================================
--- jackrabbit/commons/filevault/trunk/vault-validation/src/main/java/org/apache/jackrabbit/filevault/validation/spi/impl/EmptyElementsValidator.java (added)
+++ jackrabbit/commons/filevault/trunk/vault-validation/src/main/java/org/apache/jackrabbit/filevault/validation/spi/impl/EmptyElementsValidator.java Wed Oct  2 11:28:25 2019
@@ -0,0 +1,119 @@
+/*
+ * 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.validation.spi.impl;
+
+import java.io.InputStream;
+import java.nio.file.Path;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.LinkedHashMap;
+import java.util.LinkedList;
+import java.util.Map;
+import java.util.stream.Collectors;
+
+import org.apache.jackrabbit.filevault.validation.ValidationExecutor;
+import org.apache.jackrabbit.filevault.validation.spi.DocumentViewXmlValidator;
+import org.apache.jackrabbit.filevault.validation.spi.GenericJcrDataValidator;
+import org.apache.jackrabbit.filevault.validation.spi.ValidationMessage;
+import org.apache.jackrabbit.filevault.validation.spi.ValidationMessageSeverity;
+import org.apache.jackrabbit.vault.fs.api.ImportMode;
+import org.apache.jackrabbit.vault.fs.api.PathFilterSet;
+import org.apache.jackrabbit.vault.fs.api.WorkspaceFilter;
+import org.apache.jackrabbit.vault.util.DocViewNode;
+
+/**
+ *  Check for empty elements (used for ordering purposes)
+ *  which are included in the filter with import=replace as those are actually not replaced!
+ *  @see <https://issues.apache.org/jira/browse/JCRVLT-251>JCRVLT-251</a>
+ */
+public class EmptyElementsValidator implements DocumentViewXmlValidator, GenericJcrDataValidator {
+
+    protected static final String MESSAGE_EMPTY_NODES = "Found empty nodes: %s (used for ordering only) which are included in the filter with mode=merge. Rather use the according include/exclude patterns.";
+    private final ValidationMessageSeverity severity;
+    private final Map<String, Path> emptyNodePathsAndFiles;
+    private final Collection<String> nonEmptyNodePaths;
+    private final WorkspaceFilter filter;
+    
+    private Collection<String> affectedFilterRoots;
+    
+    
+    public EmptyElementsValidator(ValidationMessageSeverity severity, WorkspaceFilter filter) {
+        this.severity = severity;
+        this.emptyNodePathsAndFiles = new LinkedHashMap<>();
+        this.nonEmptyNodePaths = new LinkedList<>();
+        this.filter = filter;
+        // collect all filter roots with import mode == replace
+        affectedFilterRoots = new LinkedList<>();
+        for (PathFilterSet set : filter.getPropertyFilterSets()) {
+            if (set.getImportMode() == ImportMode.REPLACE) {
+                affectedFilterRoots.add(set.getRoot());
+            }
+        }
+    }
+
+    @Override
+    public Collection<ValidationMessage> done() {
+        emptyNodePathsAndFiles.keySet().removeAll(nonEmptyNodePaths);
+        if (!emptyNodePathsAndFiles.isEmpty()) {
+            String nodes = emptyNodePathsAndFiles.entrySet()
+                    .stream()
+                    .map(e -> "'" + e.getKey() + "' (in '" + e.getValue() + "')")
+                    .collect(Collectors.joining(", "));
+            return Collections.singleton(new ValidationMessage(severity, String.format(MESSAGE_EMPTY_NODES, nodes)));
+        }
+        return null;
+    }
+
+    @Override
+    public Collection<ValidationMessage> validate(DocViewNode node, String nodePath, Path filePath, boolean isRoot) {
+        if (isBelowAffectedFilterRoots(nodePath)) {
+            if (node.primary == null && node.mixins == null && node.props.isEmpty() && filter.contains(nodePath) && filter.getImportMode(nodePath) == ImportMode.REPLACE) {
+                // only relevant if no other merge mode
+                emptyNodePathsAndFiles.put(nodePath, filePath);
+            } else {
+                nonEmptyNodePaths.add(nodePath);
+            }
+        }
+        return null;
+    }
+
+    @Override
+    public Collection<ValidationMessage> validateJcrData(InputStream input, Path filePath, Map<String, Integer> nodePathsAndLineNumbers) {
+        // never validate actual input
+        // this should never be called
+        return null;
+    }
+
+    private boolean isBelowAffectedFilterRoots(String nodePath) {
+        for (String affectedFilterRoot : affectedFilterRoots) {
+            if (nodePath.startsWith(affectedFilterRoot)) {
+                return true;
+            }
+        }
+        return false;
+    }
+
+    @Override
+    public boolean shouldValidateJcrData(Path filePath) {
+        String nodePath = ValidationExecutor.filePathToNodePath(filePath);
+        if (isBelowAffectedFilterRoots(nodePath)) {
+            nonEmptyNodePaths.add(nodePath);
+        }
+        return false;
+    }
+
+}

Added: jackrabbit/commons/filevault/trunk/vault-validation/src/main/java/org/apache/jackrabbit/filevault/validation/spi/impl/EmptyElementsValidatorFactory.java
URL: http://svn.apache.org/viewvc/jackrabbit/commons/filevault/trunk/vault-validation/src/main/java/org/apache/jackrabbit/filevault/validation/spi/impl/EmptyElementsValidatorFactory.java?rev=1867884&view=auto
==============================================================================
--- jackrabbit/commons/filevault/trunk/vault-validation/src/main/java/org/apache/jackrabbit/filevault/validation/spi/impl/EmptyElementsValidatorFactory.java (added)
+++ jackrabbit/commons/filevault/trunk/vault-validation/src/main/java/org/apache/jackrabbit/filevault/validation/spi/impl/EmptyElementsValidatorFactory.java Wed Oct  2 11:28:25 2019
@@ -0,0 +1,51 @@
+/*
+ * 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.validation.spi.impl;
+
+import javax.annotation.CheckForNull;
+
+import org.apache.jackrabbit.filevault.validation.spi.ValidationContext;
+import org.apache.jackrabbit.filevault.validation.spi.Validator;
+import org.apache.jackrabbit.filevault.validation.spi.ValidatorFactory;
+import org.apache.jackrabbit.filevault.validation.spi.ValidatorSettings;
+import org.kohsuke.MetaInfServices;
+
+@MetaInfServices
+public final class EmptyElementsValidatorFactory implements ValidatorFactory {
+
+    @Override
+    public @CheckForNull Validator createValidator(ValidationContext context, ValidatorSettings settings) {
+        return new EmptyElementsValidator(settings.getDefaultSeverity(), context.getFilter());
+    }
+
+    @Override
+    public boolean shouldValidateSubpackages() {
+        return false;
+    }
+
+    @Override
+    public String getId() {
+        return ValidatorFactory.PREFIX_JACKRABBIT + "emptyelements";
+    }
+
+    @Override
+    public int getServiceRanking() {
+        return 0;
+    }
+
+    
+}

Added: jackrabbit/commons/filevault/trunk/vault-validation/src/main/java/org/apache/jackrabbit/filevault/validation/spi/impl/MergeLimitationsValidator.java
URL: http://svn.apache.org/viewvc/jackrabbit/commons/filevault/trunk/vault-validation/src/main/java/org/apache/jackrabbit/filevault/validation/spi/impl/MergeLimitationsValidator.java?rev=1867884&view=auto
==============================================================================
--- jackrabbit/commons/filevault/trunk/vault-validation/src/main/java/org/apache/jackrabbit/filevault/validation/spi/impl/MergeLimitationsValidator.java (added)
+++ jackrabbit/commons/filevault/trunk/vault-validation/src/main/java/org/apache/jackrabbit/filevault/validation/spi/impl/MergeLimitationsValidator.java Wed Oct  2 11:28:25 2019
@@ -0,0 +1,68 @@
+/*
+ * 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.validation.spi.impl;
+
+import java.nio.file.Path;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.LinkedList;
+
+import org.apache.jackrabbit.filevault.validation.spi.DocumentViewXmlValidator;
+import org.apache.jackrabbit.filevault.validation.spi.ValidationMessage;
+import org.apache.jackrabbit.filevault.validation.spi.ValidationMessageSeverity;
+import org.apache.jackrabbit.vault.fs.api.ImportMode;
+import org.apache.jackrabbit.vault.fs.api.PathFilterSet;
+import org.apache.jackrabbit.vault.fs.api.WorkspaceFilter;
+import org.apache.jackrabbit.vault.util.DocViewNode;
+
+/**
+ * @see <a href="https://issues.apache.org/jira/browse/JCRVLT-255">JCRVLT-255</a>
+ */
+public class MergeLimitationsValidator implements DocumentViewXmlValidator {
+
+    protected static final String PACKAGE_NON_ROOT_NODE_MERGED = "Non-empty Node '%s' is supposed to be imported with mode 'merge' but it is not the aggregator's root node. This is currently not supported by FileVault (https://issues.apache.org/jira/browse/JCRVLT-255).";
+    private final ValidationMessageSeverity severity;
+    private final Collection<String> rootNodePathsOfMergeRules;
+
+    public MergeLimitationsValidator(ValidationMessageSeverity severity, WorkspaceFilter filter) {
+        super();
+        this.severity = severity;
+        this.rootNodePathsOfMergeRules = new LinkedList<>();
+
+        // go through all filters,
+        for (PathFilterSet pathFilterSet : filter.getFilterSets()) {
+            // find those with mode=merge
+            if (pathFilterSet.getImportMode() == ImportMode.MERGE) {
+                rootNodePathsOfMergeRules.add(pathFilterSet.getRoot());
+            }
+        }
+    }
+
+    @Override
+    public Collection<ValidationMessage> done() {
+        return null;
+    }
+
+    @Override
+    public Collection<ValidationMessage> validate(DocViewNode node, String nodePath, Path filePath, boolean isRoot) {
+        // find out if one of the filter roots is pointing to any of the aggregator's non-root nodes
+        if (!isRoot && !node.props.isEmpty() && rootNodePathsOfMergeRules.contains(nodePath)) {
+            return Collections.singleton(new ValidationMessage(severity, String.format(PACKAGE_NON_ROOT_NODE_MERGED, nodePath)));
+        }
+        return null;
+    }
+}

Added: jackrabbit/commons/filevault/trunk/vault-validation/src/main/java/org/apache/jackrabbit/filevault/validation/spi/impl/MergeLimitationsValidatorFactory.java
URL: http://svn.apache.org/viewvc/jackrabbit/commons/filevault/trunk/vault-validation/src/main/java/org/apache/jackrabbit/filevault/validation/spi/impl/MergeLimitationsValidatorFactory.java?rev=1867884&view=auto
==============================================================================
--- jackrabbit/commons/filevault/trunk/vault-validation/src/main/java/org/apache/jackrabbit/filevault/validation/spi/impl/MergeLimitationsValidatorFactory.java (added)
+++ jackrabbit/commons/filevault/trunk/vault-validation/src/main/java/org/apache/jackrabbit/filevault/validation/spi/impl/MergeLimitationsValidatorFactory.java Wed Oct  2 11:28:25 2019
@@ -0,0 +1,49 @@
+/*
+ * 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.validation.spi.impl;
+
+import javax.annotation.CheckForNull;
+
+import org.apache.jackrabbit.filevault.validation.spi.ValidationContext;
+import org.apache.jackrabbit.filevault.validation.spi.Validator;
+import org.apache.jackrabbit.filevault.validation.spi.ValidatorFactory;
+import org.apache.jackrabbit.filevault.validation.spi.ValidatorSettings;
+import org.kohsuke.MetaInfServices;
+
+@MetaInfServices
+public final class MergeLimitationsValidatorFactory implements ValidatorFactory {
+
+    @Override
+    public @CheckForNull Validator createValidator(ValidationContext context, ValidatorSettings settings) {
+        return new MergeLimitationsValidator(settings.getDefaultSeverity(), context.getFilter());
+    }
+
+    @Override
+    public boolean shouldValidateSubpackages() {
+        return false;
+    }
+
+    @Override
+    public String getId() {
+        return ValidatorFactory.PREFIX_JACKRABBIT + "mergelimitations";
+    }
+
+    @Override
+    public int getServiceRanking() {
+        return 0;
+    }
+}

Added: jackrabbit/commons/filevault/trunk/vault-validation/src/main/java/org/apache/jackrabbit/filevault/validation/spi/impl/OakIndexDefinitionValidator.java
URL: http://svn.apache.org/viewvc/jackrabbit/commons/filevault/trunk/vault-validation/src/main/java/org/apache/jackrabbit/filevault/validation/spi/impl/OakIndexDefinitionValidator.java?rev=1867884&view=auto
==============================================================================
--- jackrabbit/commons/filevault/trunk/vault-validation/src/main/java/org/apache/jackrabbit/filevault/validation/spi/impl/OakIndexDefinitionValidator.java (added)
+++ jackrabbit/commons/filevault/trunk/vault-validation/src/main/java/org/apache/jackrabbit/filevault/validation/spi/impl/OakIndexDefinitionValidator.java Wed Oct  2 11:28:25 2019
@@ -0,0 +1,90 @@
+/*
+ * 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.validation.spi.impl;
+
+import java.nio.file.Path;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.LinkedList;
+import java.util.List;
+
+import javax.annotation.CheckForNull;
+
+import org.apache.jackrabbit.filevault.validation.spi.DocumentViewXmlValidator;
+import org.apache.jackrabbit.filevault.validation.spi.FilterValidator;
+import org.apache.jackrabbit.filevault.validation.spi.ValidationMessage;
+import org.apache.jackrabbit.filevault.validation.spi.ValidationMessageSeverity;
+import org.apache.jackrabbit.oak.plugins.index.IndexConstants;
+import org.apache.jackrabbit.oak.spi.security.authorization.accesscontrol.AccessControlConstants;
+import org.apache.jackrabbit.vault.fs.api.PathFilterSet;
+import org.apache.jackrabbit.vault.fs.api.WorkspaceFilter;
+import org.apache.jackrabbit.vault.packaging.PackageProperties;
+import org.apache.jackrabbit.vault.util.DocViewNode;
+
+/** Validates that packages not having the property {@code allowIndexDefinitions=true} must not contain index definitions.
+ *  NPR-14102 - Automated check for index definition 
+ */
+public final class OakIndexDefinitionValidator implements FilterValidator, DocumentViewXmlValidator {
+
+    static final String MESSAGE_POTENTIAL_INDEX_IN_FILTER = "Package '%s' contains filter rule overwriting a potential index definition below '%s' but the according property " + PackageProperties.NAME_ALLOW_INDEX_DEFINITIONS + " is not set to 'true'";
+    static final String MESSAGE_INDEX_AT_NODE = "Package '%s' contains index definition at node '%s but the according property " + PackageProperties.NAME_ALLOW_INDEX_DEFINITIONS + " is not set to 'true'";
+    
+    private final Path packageRootPathOfNotAllowedIndexDefinition;
+    private final ValidationMessageSeverity defaultMessageSeverity;
+
+    public OakIndexDefinitionValidator(Path path, ValidationMessageSeverity defaultMessageSeverity) {
+        this.packageRootPathOfNotAllowedIndexDefinition = path;
+        this.defaultMessageSeverity = defaultMessageSeverity;
+    }
+    @Override
+    public @CheckForNull Collection<ValidationMessage> validate(WorkspaceFilter filter) {
+        Collection<ValidationMessage> violations = new LinkedList<>();
+        violations.addAll(collectIndexPaths(filter.getFilterSets()));
+        return violations;
+    }
+
+    public Collection<ValidationMessage> collectIndexPaths(List<PathFilterSet> pathFilters) {
+        Collection<ValidationMessage> violations = new LinkedList<>();
+        for (PathFilterSet pathFilter : pathFilters) {
+            // support other index roots (https://jackrabbit.apache.org/oak/docs/query/indexing.html#Index_Definition_Location)
+            if (pathFilter.isAncestor("/" + IndexConstants.INDEX_DEFINITIONS_NAME) || pathFilter.getRoot().contains("/" + IndexConstants.INDEX_DEFINITIONS_NAME +"/") || pathFilter.getRoot().endsWith("/" + IndexConstants.INDEX_DEFINITIONS_NAME)) {
+                // exclude ACL only entries (only a heuristic because the name might differ)
+                if (pathFilter.getRoot().contains("/" + AccessControlConstants.REP_POLICY)) {
+                    violations.add(new ValidationMessage(ValidationMessageSeverity.DEBUG, "Ignoring filter entry " + pathFilter  + " as it is referring to an ACL"));
+                } else {
+                    violations.add(new ValidationMessage(defaultMessageSeverity, String.format(MESSAGE_POTENTIAL_INDEX_IN_FILTER, packageRootPathOfNotAllowedIndexDefinition, pathFilter.getRoot())));
+                }
+            }
+        }
+        return violations;
+    }
+
+    @Override
+    public @CheckForNull Collection<ValidationMessage> validate(DocViewNode node, String nodePath, Path filePath, boolean isRoot) {
+        ValidationMessage violation = null;
+        if (IndexConstants.INDEX_DEFINITIONS_NODE_TYPE.equals(node.primary)) {
+            violation = new ValidationMessage(defaultMessageSeverity, String.format(MESSAGE_INDEX_AT_NODE, packageRootPathOfNotAllowedIndexDefinition, nodePath));
+        }
+        return violation != null ? Collections.singleton(violation) : null;
+    }
+
+    @Override
+    public @CheckForNull Collection<ValidationMessage> done() {
+        return null;
+    }
+
+}

Added: jackrabbit/commons/filevault/trunk/vault-validation/src/main/java/org/apache/jackrabbit/filevault/validation/spi/impl/OakIndexDefinitionValidatorFactory.java
URL: http://svn.apache.org/viewvc/jackrabbit/commons/filevault/trunk/vault-validation/src/main/java/org/apache/jackrabbit/filevault/validation/spi/impl/OakIndexDefinitionValidatorFactory.java?rev=1867884&view=auto
==============================================================================
--- jackrabbit/commons/filevault/trunk/vault-validation/src/main/java/org/apache/jackrabbit/filevault/validation/spi/impl/OakIndexDefinitionValidatorFactory.java (added)
+++ jackrabbit/commons/filevault/trunk/vault-validation/src/main/java/org/apache/jackrabbit/filevault/validation/spi/impl/OakIndexDefinitionValidatorFactory.java Wed Oct  2 11:28:25 2019
@@ -0,0 +1,76 @@
+/*
+ * 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.validation.spi.impl;
+
+import java.nio.file.Path;
+
+import javax.annotation.CheckForNull;
+
+import org.apache.jackrabbit.filevault.validation.spi.ValidationContext;
+import org.apache.jackrabbit.filevault.validation.spi.Validator;
+import org.apache.jackrabbit.filevault.validation.spi.ValidatorFactory;
+import org.apache.jackrabbit.filevault.validation.spi.ValidatorSettings;
+import org.apache.jackrabbit.vault.packaging.PackageProperties;
+import org.kohsuke.MetaInfServices;
+
+@MetaInfServices
+public final class OakIndexDefinitionValidatorFactory implements ValidatorFactory {
+
+    @Override
+    public @CheckForNull Validator createValidator(ValidationContext context, ValidatorSettings settings) {
+        // only validate in case allowIndexDefinitions is not set for any of the validated packages
+        Path path = getPathOfNotAllowedIndexDefinition(context);
+        if (path != null) {
+            return new OakIndexDefinitionValidator(path, settings.getDefaultSeverity());
+        }
+        return null;
+    }
+
+    private static Path getPathOfNotAllowedIndexDefinition(ValidationContext context) {
+        if (!areIndexDefinitionsAllowed(context.getProperties()))  {
+            return context.getPackageRootPath();
+        } else {
+            ValidationContext containerContext = context.getContainerValidationContext();
+            if (containerContext != null) {
+                return getPathOfNotAllowedIndexDefinition(containerContext);
+            } else {
+                return null;
+            }
+        }
+    }
+    
+    static boolean areIndexDefinitionsAllowed(PackageProperties properties) {
+        return "true".equals(properties.getProperty(PackageProperties.NAME_ALLOW_INDEX_DEFINITIONS));
+    }
+
+    @Override
+    public boolean shouldValidateSubpackages() {
+        return true;
+    }
+
+    @Override
+    public String getId() {
+        return ValidatorFactory.PREFIX_JACKRABBIT + "oakindex";
+    }
+
+    @Override
+    public int getServiceRanking() {
+        return 0;
+    }
+    
+    
+}



Mime
View raw message