You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@jackrabbit.apache.org by kw...@apache.org on 2021/06/16 19:12:33 UTC

[jackrabbit-filevault] branch bugfix/JCRVLT-527-deferred-validation-of-folders updated (e73cbd3 -> 68661a9)

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

kwin pushed a change to branch bugfix/JCRVLT-527-deferred-validation-of-folders
in repository https://gitbox.apache.org/repos/asf/jackrabbit-filevault.git.


 discard e73cbd3  JCRVLT-527 deferred validation of child nodes
     new 68661a9  JCRVLT-527 deferred validation of child nodes

This update added new revisions after undoing existing revisions.
That is to say, some revisions that were in the old version of the
branch are not in the new version.  This situation occurs
when a user --force pushes a change and generates a repository
containing something like this:

 * -- * -- B -- O -- O -- O   (e73cbd3)
            \
             N -- N -- N   refs/heads/bugfix/JCRVLT-527-deferred-validation-of-folders (68661a9)

You should already have received notification emails for all of the O
revisions, and so the following emails describe only the N revisions
from the common base, B.

Any revisions marked "omit" are not gone; other references still
refer to them.  Any revisions marked "discard" are gone forever.

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.


Summary of changes:
 .../validation/spi/impl/nodetype/JcrNodeTypeMetaDataImpl.java  | 10 +++++-----
 1 file changed, 5 insertions(+), 5 deletions(-)

[jackrabbit-filevault] 01/01: JCRVLT-527 deferred validation of child nodes

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

kwin pushed a commit to branch bugfix/JCRVLT-527-deferred-validation-of-folders
in repository https://gitbox.apache.org/repos/asf/jackrabbit-filevault.git

commit 68661a9dbb98e15e3d77b140aa9de686d28dc47b
Author: Konrad Windszus <kw...@apache.org>
AuthorDate: Wed Jun 16 21:06:38 2021 +0200

    JCRVLT-527 deferred validation of child nodes
---
 vault-validation/pom.xml                           |   5 -
 .../spi/impl/nodetype/JcrNodeTypeMetaData.java     |   9 +-
 .../spi/impl/nodetype/JcrNodeTypeMetaDataImpl.java | 136 ++++++++-------
 .../spi/impl/nodetype/NodeTypeValidator.java       |  33 ++--
 .../impl/nodetype/JcrNodeTypeMetaDataImplTest.java | 182 ++++++++++-----------
 .../spi/impl/nodetype/NodeTypeValidatorTest.java   |  61 +++++--
 6 files changed, 235 insertions(+), 191 deletions(-)

diff --git a/vault-validation/pom.xml b/vault-validation/pom.xml
index ab6f608..195aa16 100644
--- a/vault-validation/pom.xml
+++ b/vault-validation/pom.xml
@@ -91,11 +91,6 @@
         </dependency>
 
         <dependency>
-            <groupId>org.apache.commons</groupId>
-            <artifactId>commons-lang3</artifactId>
-        </dependency>
-
-        <dependency>
             <groupId>org.apache.jackrabbit</groupId>
             <artifactId>jackrabbit-spi-commons</artifactId>
         </dependency>
diff --git a/vault-validation/src/main/java/org/apache/jackrabbit/vault/validation/spi/impl/nodetype/JcrNodeTypeMetaData.java b/vault-validation/src/main/java/org/apache/jackrabbit/vault/validation/spi/impl/nodetype/JcrNodeTypeMetaData.java
index ea41212..d30e8e4 100644
--- a/vault-validation/src/main/java/org/apache/jackrabbit/vault/validation/spi/impl/nodetype/JcrNodeTypeMetaData.java
+++ b/vault-validation/src/main/java/org/apache/jackrabbit/vault/validation/spi/impl/nodetype/JcrNodeTypeMetaData.java
@@ -42,7 +42,7 @@ import org.jetbrains.annotations.Nullable;
 
 public interface JcrNodeTypeMetaData {
 
-    void addProperty(@NotNull NodeContext nodeContext, @NotNull NamePathResolver namePathResolver, @NotNull EffectiveNodeTypeProvider effectiveNodeTypeProvider,
+    Collection<ValidationMessage> addProperty(@NotNull NodeContext nodeContext, @NotNull NamePathResolver namePathResolver, @NotNull EffectiveNodeTypeProvider effectiveNodeTypeProvider,
             @NotNull NodeTypeDefinitionProvider nodeTypeDefinitionProvider, @NotNull ItemDefinitionProvider itemDefinitionProvider,
             @NotNull ValidationMessageSeverity severity, String name, boolean isMultiValue, Value... values) throws RepositoryException;
     @NotNull JcrNodeTypeMetaData addImplicitChildNode(@NotNull NameResolver nameResolver,
@@ -53,17 +53,16 @@ public interface JcrNodeTypeMetaData {
             @NotNull NodeTypeDefinitionProvider nodeTypeDefinitionProvider, @NotNull ItemDefinitionProvider itemDefinitionProvider,
             @NotNull ValidationMessageSeverity severity, @NotNull NodeContext nodeContext, @NotNull String primaryType, String... mixinTypes)
                     throws RepositoryException, NamespaceExceptionInNodeName;
-    @NotNull JcrNodeTypeMetaData addUnknownChildNode(@NotNull NameResolver nameResolver, @NotNull String name) throws IllegalNameException, NamespaceException;
+    @NotNull JcrNodeTypeMetaData addUnknownChildNode(@NotNull NameResolver nameResolver, @NotNull NodeContext nodeContext, @NotNull String name) throws IllegalNameException, NamespaceException;
     
     // navigate
     @NotNull Collection<@NotNull ? extends JcrNodeTypeMetaData> getChildren();
     Optional<JcrNodeTypeMetaData> getNode(NamePathResolver nameResolver, String path)
             throws ItemNotFoundException, RepositoryException;
-    @NotNull JcrNodeTypeMetaData getOrCreateNode(NamePathResolver nameResolver, String path) throws RepositoryException;
+    @NotNull JcrNodeTypeMetaData getOrCreateNode(NamePathResolver nameResolver, @NotNull NodeContext nodeContext, String path) throws RepositoryException;
     
     @NotNull Collection<ValidationMessage> finalizeValidation(@NotNull NamePathResolver nameResolver,
-            @NotNull ValidationMessageSeverity severity, @NotNull WorkspaceFilter filter) throws NamespaceException;
-    void fetchAndClearValidationMessages(Collection<ValidationMessage> messages);
+            @NotNull NodeTypeDefinitionProvider nodeTypeDefinitionProvider, @NotNull ItemDefinitionProvider itemDefinitionProvider, @NotNull ValidationMessageSeverity severity, @NotNull WorkspaceFilter filter) throws NamespaceException;
     @NotNull Name getPrimaryNodeType();
     String getQualifiedPath(NamePathResolver resolver) throws NamespaceException;
     void setNodeTypes(@NotNull NameResolver nameResolver, @NotNull EffectiveNodeTypeProvider effectiveNodeTypeProvider,
diff --git a/vault-validation/src/main/java/org/apache/jackrabbit/vault/validation/spi/impl/nodetype/JcrNodeTypeMetaDataImpl.java b/vault-validation/src/main/java/org/apache/jackrabbit/vault/validation/spi/impl/nodetype/JcrNodeTypeMetaDataImpl.java
index 144bc43..e62d5ca 100644
--- a/vault-validation/src/main/java/org/apache/jackrabbit/vault/validation/spi/impl/nodetype/JcrNodeTypeMetaDataImpl.java
+++ b/vault-validation/src/main/java/org/apache/jackrabbit/vault/validation/spi/impl/nodetype/JcrNodeTypeMetaDataImpl.java
@@ -82,10 +82,10 @@ public class JcrNodeTypeMetaDataImpl implements JcrNodeTypeMetaData {
     static final String MESSAGE_CHILD_NODE_NOT_ALLOWED = "Node '%s [%s]' is not allowed as child of node with types [%s]: %s";
     static final String MESSAGE_PROPERTY_OF_NOT_CONTAINED_PARENT_POTENTIALLY_NOT_ALLOWED = "Property '%s' [%s] is not allowed in node with potential default types [%s]: %s";;
     static final String MESSAGE_PROPERTY_NOT_ALLOWED = "Property '%s' [%s] is not allowed in node with types [%s]: %s";
-    static final String MESSAGE_MANDATORY_CHILD_NODE_MISSING = "Mandatory child node missing: %s inside node with types [%s] at %s";
-    static final String MESSAGE_MANDATORY_UNCONTAINED_CHILD_NODE_MISSING = "Mandatory child node missing: %s inside node with types [%s] at %s (outside of filter rules)";
-    static final String MESSAGE_MANDATORY_PROPERTY_MISSING = "Mandatory property '%s' missing in node with types [%s] at %s";
-    static final String MESSAGE_MANDATORY_PROPERTY_WITH_WRONG_TYPE = "Mandatory property '%s' has type '%s' while it should have '%s' in node with types [%s] at %s";
+    static final String MESSAGE_MANDATORY_CHILD_NODE_MISSING = "Mandatory child node missing: %s inside node with types [%s]";
+    static final String MESSAGE_MANDATORY_UNCONTAINED_CHILD_NODE_MISSING = "Mandatory child node missing: %s inside node with types [%s] (outside of filter rules)";
+    static final String MESSAGE_MANDATORY_PROPERTY_MISSING = "Mandatory property '%s' missing in node with types [%s]";
+    static final String MESSAGE_MANDATORY_PROPERTY_WITH_WRONG_TYPE = "Mandatory property '%s' has type '%s' while it should have '%s' in node with types [%s]";
 
     // do not validate protected JCR system properties that are handled by FileVault specially in https://github.com/apache/jackrabbit-filevault/blob/f785fcb24d4cbd01c734e9273310a925c29ae15b/vault-core/src/main/java/org/apache/jackrabbit/vault/fs/impl/io/DocViewSAXImporter.java#L123 and 
     // https://github.com/apache/jackrabbit-filevault/blob/f785fcb24d4cbd01c734e9273310a925c29ae15b/vault-core/src/main/java/org/apache/jackrabbit/vault/fs/impl/io/DocViewSAXImporter.java#L140
@@ -100,28 +100,28 @@ public class JcrNodeTypeMetaDataImpl implements JcrNodeTypeMetaData {
     private static final QValueFactory QVALUE_FACTORY = QValueFactoryImpl.getInstance();
 
     private final @NotNull Name name;
+    private final @NotNull NodeContext context;
     private @Nullable Name primaryNodeType; // the effectiveNodeType does not remember which one was the primary one!
     private @Nullable EffectiveNodeType effectiveNodeType;
     private final @NotNull Map<Name, Integer> propertyTypesByName;
     private final @NotNull Map<Name, JcrNodeTypeMetaDataImpl> childNodesByName;
     private final @Nullable JcrNodeTypeMetaDataImpl parentNode;
-    private final Collection<ValidationMessage> messages;
     private boolean isAuthenticationOrAuthorizationContext;
     private final boolean isImplicit; // if this is true, the node type is set implicitly (not explicitly set in package, used as is in the
                                       // repository)
     private boolean isValidationDone;
     private final boolean isIncremental;
 
-    private JcrNodeTypeMetaDataImpl(boolean isIncremental, @NotNull Name name, @Nullable Name primaryNodeType, @Nullable EffectiveNodeType effectiveNodeType,
+    private JcrNodeTypeMetaDataImpl(boolean isIncremental, @NotNull NodeContext context, @NotNull Name name, @Nullable Name primaryNodeType, @Nullable EffectiveNodeType effectiveNodeType,
             JcrNodeTypeMetaDataImpl parentNode, boolean isAuthenticationOrAuthorizationContext, boolean isImplicit) {
         super();
+        this.context = context;
         this.name = name; // fully namespaced (taking into account local namespace declaration for Docview XML)
         this.primaryNodeType = primaryNodeType;
         this.effectiveNodeType = effectiveNodeType;
         this.parentNode = parentNode;
         this.propertyTypesByName = new HashMap<>();
         this.childNodesByName = new HashMap<>();
-        this.messages = new ArrayList<>();
         this.isAuthenticationOrAuthorizationContext = isAuthenticationOrAuthorizationContext;
         this.isImplicit = isImplicit;
         this.isValidationDone = false;
@@ -135,7 +135,6 @@ public class JcrNodeTypeMetaDataImpl implements JcrNodeTypeMetaData {
                 + "propertyTypesByName=" + propertyTypesByName + ", "
                 + "childNodes=" + childNodesByName.keySet() + ", "
                 // + "parentNode path="+(parentNode != null ? + parentNode.getPath() + ", " : "")
-                + (messages != null ? "messages=" + messages + ", " : "")
                 + "isAuthenticationOrAuthorizationContext=" + isAuthenticationOrAuthorizationContext + "]";
     }
 
@@ -213,7 +212,7 @@ public class JcrNodeTypeMetaDataImpl implements JcrNodeTypeMetaData {
             @NotNull NodeTypeDefinitionProvider nodeTypeDefinitionProvider, @NotNull ItemDefinitionProvider itemDefinitionProvider,
             @NotNull NodeContext nodeContext, @Nullable Name implicitNodeType) throws RepositoryException {
         JcrNodeTypeMetaDataImpl childNode = addChildNode(nameResolver, effectiveNodeTypeProvider, nodeTypeDefinitionProvider,
-                itemDefinitionProvider, true, Text.getName(nodeContext.getNodePath()), implicitNodeType);
+                itemDefinitionProvider, true, nodeContext, Text.getName(nodeContext.getNodePath()), implicitNodeType);
         // now validate for validity
         Optional<String> constraintViolation = childNode.validateAgainstParentNodeType(effectiveNodeType, nodeTypeDefinitionProvider,
                 itemDefinitionProvider);
@@ -224,13 +223,13 @@ public class JcrNodeTypeMetaDataImpl implements JcrNodeTypeMetaData {
     }
 
     @Override
-    public @NotNull JcrNodeTypeMetaData addUnknownChildNode(@NotNull NameResolver nameResolver, @NotNull String name)
+    public @NotNull JcrNodeTypeMetaData addUnknownChildNode(@NotNull NameResolver nameResolver, @NotNull NodeContext context, @NotNull String name)
             throws IllegalNameException, NamespaceException {
-        return addUnknownChildNode(getQName(nameResolver, name, NameType.NODE_NAME));
+        return addUnknownChildNode(context, getQName(nameResolver, name, NameType.NODE_NAME));
     }
 
-    private @NotNull JcrNodeTypeMetaDataImpl addUnknownChildNode(@NotNull Name name) throws IllegalNameException {
-        JcrNodeTypeMetaDataImpl childNode = new JcrNodeTypeMetaDataImpl(this.isIncremental, name, null, null, this, false, false);
+    private @NotNull JcrNodeTypeMetaDataImpl addUnknownChildNode(@NotNull NodeContext context, @NotNull Name name) throws IllegalNameException {
+        JcrNodeTypeMetaDataImpl childNode = new JcrNodeTypeMetaDataImpl(this.isIncremental, context, name, null, null, this, false, false);
         childNodesByName.put(name, childNode);
         return childNode;
     }
@@ -241,26 +240,13 @@ public class JcrNodeTypeMetaDataImpl implements JcrNodeTypeMetaData {
             @NotNull NodeTypeDefinitionProvider nodeTypeDefinitionProvider, @NotNull ItemDefinitionProvider itemDefinitionProvider,
             @NotNull ValidationMessageSeverity severity, @NotNull NodeContext nodeContext, @NotNull String primaryType,
             String... mixinTypes)
-            throws IllegalNameException, NoSuchNodeTypeException, RepositoryException, NamespaceExceptionInNodeName {
+            throws IllegalNameException, RepositoryException, NamespaceExceptionInNodeName {
 
         List<Name> types = getTypes(nameResolver, primaryType, mixinTypes);
         String nodeName = Text.getName(nodeContext.getNodePath());
         JcrNodeTypeMetaDataImpl childNode = addChildNode(nameResolver, effectiveNodeTypeProvider, nodeTypeDefinitionProvider,
-                itemDefinitionProvider, false, nodeName, types.toArray(new Name[0]));
-        // now validate for validity
-        Optional<String> constraintViolation = childNode.validateAgainstParentNodeType(effectiveNodeType, nodeTypeDefinitionProvider,
-                itemDefinitionProvider);
-        if (constraintViolation.isPresent()) {
-            childNode.messages.add(new ValidationMessage(severity,
-                    String.format(
-                            isImplicit ? MESSAGE_CHILD_NODE_OF_NOT_CONTAINED_PARENT_POTENTIALLY_NOT_ALLOWED
-                                    : MESSAGE_CHILD_NODE_NOT_ALLOWED,
-                            nodeName, primaryType,
-                            getEffectiveNodeTypeLabel(nameResolver, effectiveNodeType),
-                            constraintViolation.get()),
-                    nodeContext));
-
-        }
+                itemDefinitionProvider, false, nodeContext, nodeName, types.toArray(new Name[0]));
+        // defer validation
         return childNode;
     }
 
@@ -279,7 +265,7 @@ public class JcrNodeTypeMetaDataImpl implements JcrNodeTypeMetaData {
     private @NotNull JcrNodeTypeMetaDataImpl addChildNode(@NotNull NameResolver nameResolver,
             @NotNull EffectiveNodeTypeProvider effectiveNodeTypeProvider,
             @NotNull NodeTypeDefinitionProvider nodeTypeDefinitionProvider, @NotNull ItemDefinitionProvider itemDefinitionProvider,
-            boolean isImplicit, @NotNull String name, @Nullable Name... nodeTypes)
+            boolean isImplicit, @NotNull NodeContext context, @NotNull String name, @Nullable Name... nodeTypes)
             throws ConstraintViolationException, NoSuchNodeTypeException, NamespaceExceptionInNodeName, NamespaceException,
             IllegalNameException {
 
@@ -301,7 +287,7 @@ public class JcrNodeTypeMetaDataImpl implements JcrNodeTypeMetaData {
         if (!isAuthenticationOrAuthorizationContext) {
             isAuthenticationOrAuthorizationContext = this.isAuthenticationOrAuthorizationContext;
         }
-        JcrNodeTypeMetaDataImpl newNode = new JcrNodeTypeMetaDataImpl(this.isIncremental, qName, newPrimaryNodeType, newEffectiveNodeType, this,
+        JcrNodeTypeMetaDataImpl newNode = new JcrNodeTypeMetaDataImpl(this.isIncremental, context, qName, newPrimaryNodeType, newEffectiveNodeType, this,
                 isAuthenticationOrAuthorizationContext, isImplicit);
         childNodesByName.put(qName, newNode);
         return newNode;
@@ -348,15 +334,15 @@ public class JcrNodeTypeMetaDataImpl implements JcrNodeTypeMetaData {
     }
 
     @Override
-    public @NotNull Collection<ValidationMessage> finalizeValidation(@NotNull NamePathResolver namePathResolver,
+    public @NotNull Collection<ValidationMessage> finalizeValidation(@NotNull NamePathResolver namePathResolver,  @NotNull NodeTypeDefinitionProvider nodeTypeDefinitionProvider, @NotNull ItemDefinitionProvider itemDefinitionProvider,
             @NotNull ValidationMessageSeverity severity, @NotNull WorkspaceFilter filter) throws NamespaceException {
         if (!isValidationDone) {
             Collection<ValidationMessage> messages = new LinkedList<>();
             // in incremental validations ignore missing mandatory properties and child nodes (as they might not be visible to the validator)
             if (!isIncremental) {
                 messages.add(new ValidationMessage(ValidationMessageSeverity.DEBUG,
-                        "Validate mandatory children and properties of " + getQualifiedPath(namePathResolver)));
-                messages.addAll(validateMandatoryChildNodes(namePathResolver, severity, filter));
+                        "Validate children and mandatory properties of " + getQualifiedPath(namePathResolver)));
+                messages.addAll(validateChildNodes(namePathResolver, nodeTypeDefinitionProvider, itemDefinitionProvider, severity, filter));
                 messages.addAll(validateMandatoryProperties(namePathResolver, severity));
             }
             // only remove child nodes on 2nd level to be able to validate mandatory properties of parent
@@ -371,14 +357,36 @@ public class JcrNodeTypeMetaDataImpl implements JcrNodeTypeMetaData {
         }
     }
 
-    private Collection<ValidationMessage> validateMandatoryChildNodes(@NotNull NamePathResolver namePathResolver,
+    private Collection<ValidationMessage> validateChildNodes(@NotNull NamePathResolver namePathResolver,  @NotNull NodeTypeDefinitionProvider nodeTypeDefinitionProvider, @NotNull ItemDefinitionProvider itemDefinitionProvider,
             @NotNull ValidationMessageSeverity severity, @NotNull WorkspaceFilter filter) {
         if (effectiveNodeType == null) {
             return Collections.emptyList();
         }
 
-        // validate mandatory child nodes of children
         Collection<ValidationMessage> messages = new LinkedList<>();
+        // validate child nodes against parent node type definition
+        for (JcrNodeTypeMetaDataImpl childNode : childNodesByName.values()) {
+            Optional<String> constraintViolation;
+            try {
+                constraintViolation = childNode.validateAgainstParentNodeType(effectiveNodeType, nodeTypeDefinitionProvider,
+                        itemDefinitionProvider);
+                if (constraintViolation.isPresent()) {
+                    messages.add(new ValidationMessage(severity,
+                            String.format(
+                                    isImplicit ? MESSAGE_CHILD_NODE_OF_NOT_CONTAINED_PARENT_POTENTIALLY_NOT_ALLOWED
+                                            : MESSAGE_CHILD_NODE_NOT_ALLOWED,
+                                    namePathResolver.getJCRName(childNode.name), namePathResolver.getJCRName(childNode.primaryNodeType),
+                                    getEffectiveNodeTypeLabel(namePathResolver, effectiveNodeType),
+                                    constraintViolation.get()), childNode.context));
+        
+                }
+            } catch (RepositoryException e) {
+                throw new IllegalStateException("Could not validate child node " + childNode.name + " against parent node definition", e);
+            }
+            
+        }
+
+        // validate mandatory child nodes of children
         for (QNodeDefinition mandatoryNodeType : effectiveNodeType.getMandatoryQNodeDefinitions()) {
             // skip auto created ones
             if (mandatoryNodeType.isAutoCreated()) {
@@ -396,11 +404,12 @@ public class JcrNodeTypeMetaDataImpl implements JcrNodeTypeMetaData {
                     if (filter.contains(namePathResolver.getJCRPath(pathBuilder.getPath()))) {
                         messages.add(new ValidationMessage(severity, String.format(MESSAGE_MANDATORY_CHILD_NODE_MISSING,
                                 getNodeDefinitionLabel(namePathResolver, mandatoryNodeType),
-                                getEffectiveNodeTypeLabel(namePathResolver, effectiveNodeType), getQualifiedPath(namePathResolver))));
+                                getEffectiveNodeTypeLabel(namePathResolver, effectiveNodeType))));
                     } else {
-                        messages.add(new ValidationMessage(severity, String.format(MESSAGE_MANDATORY_UNCONTAINED_CHILD_NODE_MISSING,
+                        // if mandatory child nodes are missing outside filter rules, this is not an issue
+                        messages.add(new ValidationMessage(ValidationMessageSeverity.DEBUG, String.format(MESSAGE_MANDATORY_UNCONTAINED_CHILD_NODE_MISSING,
                                 getNodeDefinitionLabel(namePathResolver, mandatoryNodeType),
-                                getEffectiveNodeTypeLabel(namePathResolver, effectiveNodeType), getQualifiedPath(namePathResolver))));
+                                getEffectiveNodeTypeLabel(namePathResolver, effectiveNodeType)), context));
                     }
                 } catch (NamespaceException | MalformedPathException e) {
                     throw new IllegalStateException("Could not give out node types and name for " + mandatoryNodeType, e);
@@ -436,16 +445,16 @@ public class JcrNodeTypeMetaDataImpl implements JcrNodeTypeMetaData {
     }
 
     @Override
-    public @NotNull JcrNodeTypeMetaData getOrCreateNode(NamePathResolver nameResolver, String path) throws RepositoryException {
-        return getNode(nameResolver, path, true).get();
+    public @NotNull JcrNodeTypeMetaData getOrCreateNode(NamePathResolver nameResolver, @NotNull NodeContext nodeContext, String path) throws RepositoryException {
+        return getNode(nameResolver, nodeContext, path, true).get();
     }
 
     @Override
     public Optional<JcrNodeTypeMetaData> getNode(NamePathResolver nameResolver, String path) throws RepositoryException {
-        return getNode(nameResolver, path, false);
+        return getNode(nameResolver, null, path, false);
     }
 
-    private Optional<JcrNodeTypeMetaData> getNode(NamePathResolver nameResolver, String path, boolean shouldCreateIfMissing)
+    private Optional<JcrNodeTypeMetaData> getNode(NamePathResolver nameResolver, @Nullable NodeContext nodeContext, String path, boolean shouldCreateIfMissing)
             throws RepositoryException {
         // convert to fully namespaced path
         Path qPath = nameResolver.getQPath(path);
@@ -473,7 +482,10 @@ public class JcrNodeTypeMetaDataImpl implements JcrNodeTypeMetaData {
             JcrNodeTypeMetaDataImpl childNode = currentNode.childNodesByName.get(element.getName());
             if (childNode == null) {
                 if (shouldCreateIfMissing) {
-                    childNode = currentNode.addUnknownChildNode(element.getName());
+                    if (nodeContext == null) {
+                        throw new IllegalArgumentException("Node context must be given in case node is created but is null");
+                    }
+                    childNode = currentNode.addUnknownChildNode(nodeContext, element.getName());
                 } else {
                     return Optional.empty();
                 }
@@ -505,7 +517,7 @@ public class JcrNodeTypeMetaDataImpl implements JcrNodeTypeMetaData {
                     messages.add(new ValidationMessage(severity,
                             String.format(MESSAGE_MANDATORY_PROPERTY_MISSING,
                                     nameResolver.getJCRName(mandatoryPropertyDefinition.getName()),
-                                    getEffectiveNodeTypeLabel(nameResolver, effectiveNodeType), getQualifiedPath(nameResolver))));
+                                    getEffectiveNodeTypeLabel(nameResolver, effectiveNodeType)), context));
                 } else {
                     // check type
                     int actualPropertyType = propertyTypesByName.get(mandatoryPropertyDefinition.getName());
@@ -516,7 +528,7 @@ public class JcrNodeTypeMetaDataImpl implements JcrNodeTypeMetaData {
                                         nameResolver.getJCRName(mandatoryPropertyDefinition.getName()),
                                         PropertyType.nameFromValue(actualPropertyType),
                                         PropertyType.nameFromValue(mandatoryPropertyDefinition.getRequiredType()),
-                                        getEffectiveNodeTypeLabel(nameResolver, effectiveNodeType), getQualifiedPath(nameResolver))));
+                                        getEffectiveNodeTypeLabel(nameResolver, effectiveNodeType)), context));
                     }
                 }
             } catch (NamespaceException e) {
@@ -528,10 +540,11 @@ public class JcrNodeTypeMetaDataImpl implements JcrNodeTypeMetaData {
     }
 
     @Override
-    public void addProperty(@NotNull NodeContext nodeContext, @NotNull NamePathResolver namePathResolver,
+    public Collection<ValidationMessage> addProperty(@NotNull NodeContext nodeContext, @NotNull NamePathResolver namePathResolver,
             @NotNull EffectiveNodeTypeProvider effectiveNodeTypeProvider,
             @NotNull NodeTypeDefinitionProvider nodeTypeDefinitionProvider, @NotNull ItemDefinitionProvider itemDefinitionProvider,
             @NotNull ValidationMessageSeverity severity, String name, boolean isMultiValue, Value... values) throws RepositoryException {
+        Collection<ValidationMessage> messages = new ArrayList<>();
         // some sanity checks on multivalue
         if (!isMultiValue && values.length > 1) {
             throw new IllegalArgumentException("isMultiValue is only supposed to be false if exactly one value is passed but "
@@ -540,7 +553,7 @@ public class JcrNodeTypeMetaDataImpl implements JcrNodeTypeMetaData {
 
         if (values.length == 0) {
             // unable to proceed when no value is present
-            return;
+            return messages;
         }
 
         Name qName;
@@ -565,6 +578,7 @@ public class JcrNodeTypeMetaDataImpl implements JcrNodeTypeMetaData {
                             constraintViolation.get()),
                     nodeContext));
         }
+        return messages;
     }
 
     private @NotNull Optional<String> validatePropertyConstraints(@NotNull NamePathResolver namePathResolver,
@@ -631,7 +645,25 @@ public class JcrNodeTypeMetaDataImpl implements JcrNodeTypeMetaData {
 
     public static @NotNull JcrNodeTypeMetaDataImpl createRoot(boolean isIncremental, @NotNull EffectiveNodeTypeProvider effectiveNodeTypeProvider)
             throws ConstraintViolationException, NoSuchNodeTypeException {
-        return new JcrNodeTypeMetaDataImpl(isIncremental, NameConstants.ROOT, NameConstants.REP_ROOT, effectiveNodeTypeProvider.getEffectiveNodeType(
+        return new JcrNodeTypeMetaDataImpl(isIncremental, new NodeContext() {
+
+            @Override
+            public @NotNull String getNodePath() {
+                return null;
+            }
+
+            @Override
+            @NotNull
+            public java.nio.file.@NotNull Path getFilePath() {
+                return null;
+            }
+
+            @Override
+            public java.nio.file.@NotNull Path getBasePath() {
+                return null;
+            }
+            
+        }, NameConstants.ROOT, NameConstants.REP_ROOT, effectiveNodeTypeProvider.getEffectiveNodeType(
                 new Name[] {
                         NameConstants.REP_ROOT,
                         NameConstants.REP_ACCESS_CONTROLLABLE,
@@ -639,12 +671,6 @@ public class JcrNodeTypeMetaDataImpl implements JcrNodeTypeMetaData {
                 null, false, false);
     }
 
-    @Override
-    public void fetchAndClearValidationMessages(Collection<ValidationMessage> messages) {
-        messages.addAll(this.messages);
-        this.messages.clear();
-    }
-
     private Path getPath() {
         if (parentNode == null) {
             return PathFactoryImpl.getInstance().getRootPath();
diff --git a/vault-validation/src/main/java/org/apache/jackrabbit/vault/validation/spi/impl/nodetype/NodeTypeValidator.java b/vault-validation/src/main/java/org/apache/jackrabbit/vault/validation/spi/impl/nodetype/NodeTypeValidator.java
index 2bacdcf..4add3c5 100644
--- a/vault-validation/src/main/java/org/apache/jackrabbit/vault/validation/spi/impl/nodetype/NodeTypeValidator.java
+++ b/vault-validation/src/main/java/org/apache/jackrabbit/vault/validation/spi/impl/nodetype/NodeTypeValidator.java
@@ -121,7 +121,7 @@ public class NodeTypeValidator implements DocumentViewXmlValidator, JcrPathValid
             }
         }
         Collection<ValidationMessage> messages = new LinkedList<>();
-        messages.addAll(getOrCreateNewNode(nodeContext, isImplicit(nodeContext.getNodePath()), node.primary, node.mixins));
+        messages.addAll(getOrCreateNewNode(nodeContext, false, isImplicit(nodeContext.getNodePath()), node.primary, node.mixins));
 
         for (DocViewProperty property : node.props.values()) {
             try {
@@ -131,8 +131,6 @@ public class NodeTypeValidator implements DocumentViewXmlValidator, JcrPathValid
                         String.format(MESSAGE_INVALID_PROPERTY_VALUE, property.name, e.getLocalizedMessage())));
             }
         }
-        // emit messages
-        currentNodeTypeMetaData.fetchAndClearValidationMessages(messages);
 
         // defer checking for missing mandatory properties (as those might be added by some other files)
         return messages;
@@ -145,9 +143,9 @@ public class NodeTypeValidator implements DocumentViewXmlValidator, JcrPathValid
     private Collection<ValidationMessage> addProperty(NodeContext nodeContext, String propertyName, boolean isMultiValue, Value... values) {
         Collection<ValidationMessage> messages = new ArrayList<>();
         try {
-            currentNodeTypeMetaData.addProperty(nodeContext, ntManagerProvider.getNamePathResolver(),
+            messages.addAll(currentNodeTypeMetaData.addProperty(nodeContext, ntManagerProvider.getNamePathResolver(),
                     ntManagerProvider.getEffectiveNodeTypeProvider(), ntManagerProvider.getNodeTypeDefinitionProvider(),
-                    ntManagerProvider.getItemDefinitionProvider(), defaultSeverity, propertyName, isMultiValue, values);
+                    ntManagerProvider.getItemDefinitionProvider(), defaultSeverity, propertyName, isMultiValue, values));
         } catch (NoSuchNodeTypeException | NamespaceException e) {
             // log each unknown node type/namespace only once!
             if (!loggedUnknownNodeTypeMessages.contains(e.getMessage())) {
@@ -171,7 +169,7 @@ public class NodeTypeValidator implements DocumentViewXmlValidator, JcrPathValid
         }
     }
 
-    private @NotNull Collection<ValidationMessage> getOrCreateNewNode(NodeContext nodeContext, boolean isImplicit, String primaryType,
+    private @NotNull Collection<ValidationMessage> getOrCreateNewNode(NodeContext nodeContext, boolean isFolder, boolean isImplicit, String primaryType,
             String... mixinTypes) {
         Optional<JcrNodeTypeMetaData> node = getNode(nodeContext.getNodePath());
         if (node.isPresent()) {
@@ -193,11 +191,11 @@ public class NodeTypeValidator implements DocumentViewXmlValidator, JcrPathValid
             }
             return Collections.emptyList();
         } else {
-            return createNewNode(nodeContext, isImplicit, primaryType, mixinTypes);
+            return createNewNode(nodeContext, isFolder, isImplicit, primaryType, mixinTypes);
         }
     }
 
-    private @NotNull Collection<ValidationMessage> createNewNode(NodeContext nodeContext, boolean isImplicit, String primaryType,
+    private @NotNull Collection<ValidationMessage> createNewNode(NodeContext nodeContext, boolean isFolder, boolean isImplicit, String primaryType,
             String... mixinTypes) {
         Collection<ValidationMessage> messages = new ArrayList<>();
         String nodePath = nodeContext.getNodePath();
@@ -209,7 +207,7 @@ public class NodeTypeValidator implements DocumentViewXmlValidator, JcrPathValid
         try {
             //
             JcrNodeTypeMetaData parentNode = currentNodeTypeMetaData.getOrCreateNode(ntManagerProvider.getNamePathResolver(),
-                    parentNodePath);
+                    nodeContext, parentNodePath);
             try {
                 if (isImplicit) {
                     if (!nodePath.equals("/")) {
@@ -224,6 +222,7 @@ public class NodeTypeValidator implements DocumentViewXmlValidator, JcrPathValid
                     currentNodeTypeMetaData = parentNode.addChildNode(ntManagerProvider.getNameResolver(),
                             ntManagerProvider.getEffectiveNodeTypeProvider(), ntManagerProvider.getNodeTypeDefinitionProvider(),
                             ntManagerProvider.getItemDefinitionProvider(), defaultSeverity, nodeContext, primaryType, mixinTypes);
+                    
                 }
             } catch (NoSuchNodeTypeException | NamespaceException e) {
                 // TODO: NoSuchNodeTypeException might be thrown due to previous registration of the namespace for a node name
@@ -238,9 +237,9 @@ public class NodeTypeValidator implements DocumentViewXmlValidator, JcrPathValid
                     // now register namespace with an arbitrary namespace url
                     NameParser.parse(nodeName, new OnDemandRegisterNamespaceResolverWrapper(ntManagerProvider),
                             NameFactoryImpl.getInstance());
-                    messages.addAll(createNewNode(nodeContext, isImplicit, primaryType, mixinTypes));
+                    messages.addAll(createNewNode(nodeContext, isFolder, isImplicit, primaryType, mixinTypes));
                 } else {
-                    currentNodeTypeMetaData = parentNode.addUnknownChildNode(ntManagerProvider.getNameResolver(), nodeName);
+                    currentNodeTypeMetaData = parentNode.addUnknownChildNode(ntManagerProvider.getNameResolver(),nodeContext, nodeName);
                 }
             }
         } catch (RepositoryException e) {
@@ -285,9 +284,11 @@ public class NodeTypeValidator implements DocumentViewXmlValidator, JcrPathValid
 
     private @Nullable Collection<ValidationMessage> finalizeValidationForSubtree(JcrNodeTypeMetaData node, NodeContext nodeContext) throws NamespaceException {
         Collection<ValidationMessage> messages = new ArrayList<>();
+        messages.add(new ValidationMessage(ValidationMessageSeverity.DEBUG, "Finalize validation for subtree at " + nodeContext));
         for (JcrNodeTypeMetaData child : node.getChildren()) {
             messages.addAll(finalizeValidationForSubtree(child, nodeContext));
-            messages.addAll(child.finalizeValidation(ntManagerProvider.getNamePathResolver(), defaultSeverity, filter));
+            messages.addAll(child.finalizeValidation(ntManagerProvider.getNamePathResolver(), ntManagerProvider.getNodeTypeDefinitionProvider(),
+                    ntManagerProvider.getItemDefinitionProvider(), defaultSeverity, filter));
         }
         return messages;
     }
@@ -299,7 +300,7 @@ public class NodeTypeValidator implements DocumentViewXmlValidator, JcrPathValid
         List<ValidationMessage> messages = new ArrayList<>();
         boolean isImplicit = isImplicit(nodeContext.getNodePath());
         if (isFolder) {
-            messages.addAll(getOrCreateNewNode(nodeContext, isImplicit, JcrConstants.NT_FOLDER));
+            messages.addAll(getOrCreateNewNode(nodeContext, isFolder, isImplicit, JcrConstants.NT_FOLDER));
             //
             if (!nodeContext.getNodePath().equals("/")) {
                 messages.addAll(finalizeValidationForSiblings(nodeContext));
@@ -311,19 +312,19 @@ public class NodeTypeValidator implements DocumentViewXmlValidator, JcrPathValid
                 // https://jackrabbit.apache.org/filevault/vaultfs.html#Binary_Properties
                 if (fileName.endsWith(ValidationExecutor.EXTENSION_BINARY)) {
                     // create parent if it does not exist yet
-                    messages.addAll(getOrCreateNewNode(nodeContext, isImplicit, JcrConstants.NT_FOLDER));
+                    messages.addAll(getOrCreateNewNode(nodeContext, isFolder, isImplicit, JcrConstants.NT_FOLDER));
                     String propertyName = fileName.substring(0, fileName.length() - ValidationExecutor.EXTENSION_BINARY.length());
                     messages.addAll(addProperty(nodeContext, propertyName, false, DUMMY_BINARY_VALUE));
                 } else {
                     // if binary node is not yet there
-                    messages.addAll(getOrCreateNewNode(nodeContext, isImplicit, JcrConstants.NT_FILE));
+                    messages.addAll(getOrCreateNewNode(nodeContext, isFolder, isImplicit, JcrConstants.NT_FILE));
                     // if a NT_FILE create a jcr:content sub node of type NT_RESOURCE
                     if (currentNodeTypeMetaData.getPrimaryNodeType().equals(NameConstants.NT_FILE)) {
                         // create new node context
                         nodeContext = new NodeContextImpl(nodeContext.getNodePath() + "/" + JcrConstants.JCR_CONTENT,
                                 nodeContext.getFilePath(), nodeContext.getBasePath());
                         messages.addAll(
-                                getOrCreateNewNode(nodeContext, isImplicit(nodeContext.getNodePath()), JcrConstants.NT_RESOURCE));
+                                getOrCreateNewNode(nodeContext, isFolder, isImplicit(nodeContext.getNodePath()), JcrConstants.NT_RESOURCE));
                     }
                     messages.addAll(addProperty(nodeContext, JcrConstants.JCR_DATA, false, DUMMY_BINARY_VALUE));
                     messages.addAll(addProperty(nodeContext, JcrConstants.JCR_MIMETYPE, false, DUMMY_STRING_VALUE));
diff --git a/vault-validation/src/test/java/org/apache/jackrabbit/vault/validation/spi/impl/nodetype/JcrNodeTypeMetaDataImplTest.java b/vault-validation/src/test/java/org/apache/jackrabbit/vault/validation/spi/impl/nodetype/JcrNodeTypeMetaDataImplTest.java
index f69d870..0eef044 100644
--- a/vault-validation/src/test/java/org/apache/jackrabbit/vault/validation/spi/impl/nodetype/JcrNodeTypeMetaDataImplTest.java
+++ b/vault-validation/src/test/java/org/apache/jackrabbit/vault/validation/spi/impl/nodetype/JcrNodeTypeMetaDataImplTest.java
@@ -22,7 +22,6 @@ import java.io.InputStreamReader;
 import java.io.Reader;
 import java.nio.charset.StandardCharsets;
 import java.nio.file.Paths;
-import java.util.ArrayList;
 import java.util.Collection;
 
 import javax.jcr.NamespaceException;
@@ -48,7 +47,6 @@ import org.apache.jackrabbit.vault.validation.spi.ValidationMessage;
 import org.apache.jackrabbit.vault.validation.spi.ValidationMessageSeverity;
 import org.apache.jackrabbit.vault.validation.spi.util.NodeContextImpl;
 import org.hamcrest.MatcherAssert;
-import org.hamcrest.Matchers;
 import org.junit.Assert;
 import org.junit.Before;
 import org.junit.Test;
@@ -79,19 +77,16 @@ public class JcrNodeTypeMetaDataImplTest {
                 ntManagerProvider.getNodeTypeDefinitionProvider(), ntManagerProvider.getItemDefinitionProvider(), severity,
                 createSimpleNodeContext("my"),
                 NodeType.NT_FOLDER);
-        assertNoValidationErrors(child);
         JcrNodeTypeMetaData grandChild = child.addChildNode(ntManagerProvider.getNameResolver(),
                 ntManagerProvider.getEffectiveNodeTypeProvider(),
                 ntManagerProvider.getNodeTypeDefinitionProvider(), ntManagerProvider.getItemDefinitionProvider(), severity,
                 createSimpleNodeContext("test"),
                 NodeType.NT_FOLDER);
-        assertNoValidationErrors(grandChild);
         JcrNodeTypeMetaData child2 = root.addChildNode(ntManagerProvider.getNameResolver(),
                 ntManagerProvider.getEffectiveNodeTypeProvider(),
                 ntManagerProvider.getNodeTypeDefinitionProvider(), ntManagerProvider.getItemDefinitionProvider(), severity,
                 createSimpleNodeContext("test2"),
                 NodeType.NT_FOLDER);
-        assertNoValidationErrors(child2);
         Assert.assertEquals(child2,
                 grandChild.getNode(ntManagerProvider.getNamePathResolver(), "/test2").get());
     }
@@ -142,81 +137,87 @@ public class JcrNodeTypeMetaDataImplTest {
                         StandardCharsets.US_ASCII)) {
             ntManagerProvider.registerNodeTypes(reader);
         }
-
-        // add child node with mixin type as primary
+        DefaultWorkspaceFilter filter = new DefaultWorkspaceFilter();
+        
+        // add node with mixin type as primary
         NodeContext nodeContext = createSimpleNodeContext("name");
+        
         JcrNodeTypeMetaData node = root.addChildNode(ntManagerProvider.getNamePathResolver(),
                 ntManagerProvider.getEffectiveNodeTypeProvider(), ntManagerProvider.getNodeTypeDefinitionProvider(),
                 ntManagerProvider.getItemDefinitionProvider(), ValidationMessageSeverity.ERROR, nodeContext,
                 "mix:mimeType");
-        assertValidationMessage(node, new ValidationMessage(ValidationMessageSeverity.ERROR,
+        ValidationExecutorTest.assertViolation(root.finalizeValidation(ntManagerProvider.getNamePathResolver(), ntManagerProvider.getNodeTypeDefinitionProvider(),
+                ntManagerProvider.getItemDefinitionProvider(),
+                ValidationMessageSeverity.ERROR, filter),
+                new ValidationMessage(ValidationMessageSeverity.ERROR,
                 String.format(JcrNodeTypeMetaDataImpl.MESSAGE_CHILD_NODE_NOT_ALLOWED, "name", "mix:mimeType", ROOT_NODE_TYPES,
-                        JcrNodeTypeMetaDataImpl.CONSTRAINT_MIXIN_TYPE_AS_PRIMARY_TYPE),
-                nodeContext));
+                        JcrNodeTypeMetaDataImpl.CONSTRAINT_MIXIN_TYPE_AS_PRIMARY_TYPE), nodeContext));
 
-        // add child node with abstract type
+        // add node with abstract type
+        root = JcrNodeTypeMetaDataImpl.createRoot(false, ntManagerProvider.getEffectiveNodeTypeProvider());
         node = root.addChildNode(ntManagerProvider.getNamePathResolver(),
                 ntManagerProvider.getEffectiveNodeTypeProvider(), ntManagerProvider.getNodeTypeDefinitionProvider(),
                 ntManagerProvider.getItemDefinitionProvider(), ValidationMessageSeverity.ERROR, nodeContext,
                 "nt:hierarchyNode");
-        assertValidationMessage(node, new ValidationMessage(ValidationMessageSeverity.ERROR,
+        ValidationExecutorTest.assertViolation(root.finalizeValidation(ntManagerProvider.getNamePathResolver(), ntManagerProvider.getNodeTypeDefinitionProvider(),
+                ntManagerProvider.getItemDefinitionProvider(),
+                ValidationMessageSeverity.ERROR, filter),
+                new ValidationMessage(ValidationMessageSeverity.ERROR,
                 String.format(JcrNodeTypeMetaDataImpl.MESSAGE_CHILD_NODE_NOT_ALLOWED, "name", "nt:hierarchyNode", ROOT_NODE_TYPES,
-                        JcrNodeTypeMetaDataImpl.CONSTRAINT_ABSTRACT_TYPE_AS_PRIMARY_TYPE),
-                nodeContext));
+                        JcrNodeTypeMetaDataImpl.CONSTRAINT_ABSTRACT_TYPE_AS_PRIMARY_TYPE), nodeContext));
 
+        // add node for version storage
+        root = JcrNodeTypeMetaDataImpl.createRoot(false, ntManagerProvider.getEffectiveNodeTypeProvider());
         node = root.addChildNode(ntManagerProvider.getNamePathResolver(), ntManagerProvider.getEffectiveNodeTypeProvider(),
                 ntManagerProvider.getNodeTypeDefinitionProvider(), ntManagerProvider.getItemDefinitionProvider(),
                 ValidationMessageSeverity.ERROR,
                 createSimpleNodeContext("versionedNode"), "rep:versionStorage");
-        assertNoValidationErrors(node);
-
         // add child node with protected node which is ACL (i.e. accepted)
-        JcrNodeTypeMetaData childNode = node.addChildNode(ntManagerProvider.getNamePathResolver(),
+        node.addChildNode(ntManagerProvider.getNamePathResolver(),
                 ntManagerProvider.getEffectiveNodeTypeProvider(),
                 ntManagerProvider.getNodeTypeDefinitionProvider(), ntManagerProvider.getItemDefinitionProvider(),
                 ValidationMessageSeverity.ERROR,
                 createSimpleNodeContext("rep:policy"), "rep:Policy");
-        assertNoValidationErrors(childNode);
-
-        // add child node with protected node which is not ACL (i.e. accepted)
-        childNode = node.addChildNode(ntManagerProvider.getNamePathResolver(), ntManagerProvider.getEffectiveNodeTypeProvider(),
+        // add child node with protected node which is not ACL (i.e. not accepted)
+        root = JcrNodeTypeMetaDataImpl.createRoot(false, ntManagerProvider.getEffectiveNodeTypeProvider());
+        node.addChildNode(ntManagerProvider.getNamePathResolver(), ntManagerProvider.getEffectiveNodeTypeProvider(),
                 ntManagerProvider.getNodeTypeDefinitionProvider(), ntManagerProvider.getItemDefinitionProvider(),
                 ValidationMessageSeverity.ERROR,
                 nodeContext, "nt:versionHistory");
-        assertValidationMessage(childNode, new ValidationMessage(ValidationMessageSeverity.ERROR,
+        ValidationExecutorTest.assertViolation(node.finalizeValidation(ntManagerProvider.getNamePathResolver(), ntManagerProvider.getNodeTypeDefinitionProvider(),
+                ntManagerProvider.getItemDefinitionProvider(),
+                ValidationMessageSeverity.ERROR, filter), new ValidationMessage(ValidationMessageSeverity.ERROR,
                 String.format(JcrNodeTypeMetaDataImpl.MESSAGE_CHILD_NODE_NOT_ALLOWED, "name", "nt:versionHistory", "rep:versionStorage",
-                        JcrNodeTypeMetaDataImpl.CONSTRAINT_CHILD_NODE_PROTECTED),
-                nodeContext));
+                        JcrNodeTypeMetaDataImpl.CONSTRAINT_CHILD_NODE_PROTECTED), nodeContext));
 
         // add valid child node
+        root = JcrNodeTypeMetaDataImpl.createRoot(false, ntManagerProvider.getEffectiveNodeTypeProvider());
         node = root.addChildNode(ntManagerProvider.getNamePathResolver(),
                 ntManagerProvider.getEffectiveNodeTypeProvider(), ntManagerProvider.getNodeTypeDefinitionProvider(),
                 ntManagerProvider.getItemDefinitionProvider(), ValidationMessageSeverity.ERROR, createSimpleNodeContext("name"),
                 "my:nodeType1");
-        assertNoValidationErrors(node);
-
-        // add auto-created child node
+        // below that add auto-created child node
         nodeContext = createSimpleNodeContext("my:autoCreatedChild1");
-        childNode = node.addChildNode(ntManagerProvider.getNamePathResolver(),
+        node.addChildNode(ntManagerProvider.getNamePathResolver(),
                 ntManagerProvider.getEffectiveNodeTypeProvider(), ntManagerProvider.getNodeTypeDefinitionProvider(),
                 ntManagerProvider.getItemDefinitionProvider(), ValidationMessageSeverity.ERROR,
                 nodeContext, "my:nodeType2");
-        assertValidationMessage(childNode, new ValidationMessage(ValidationMessageSeverity.ERROR,
-                String.format(JcrNodeTypeMetaDataImpl.MESSAGE_CHILD_NODE_NOT_ALLOWED, "my:autoCreatedChild1", "my:nodeType2",
-                        "my:nodeType1",
-                        JcrNodeTypeMetaDataImpl.CONSTRAINT_CHILD_NODE_AUTO_CREATED),
-                nodeContext));
-
-        // below that add child node which is not allowed
-        nodeContext = createSimpleNodeContext("name2");
-        childNode = node.addChildNode(ntManagerProvider.getNamePathResolver(),
+        // and next to it a child node which is not allowed
+        NodeContext nodeContext2 = createSimpleNodeContext("name2");
+        node.addChildNode(ntManagerProvider.getNamePathResolver(),
                 ntManagerProvider.getEffectiveNodeTypeProvider(), ntManagerProvider.getNodeTypeDefinitionProvider(),
-                ntManagerProvider.getItemDefinitionProvider(), ValidationMessageSeverity.ERROR, nodeContext,
+                ntManagerProvider.getItemDefinitionProvider(), ValidationMessageSeverity.ERROR, nodeContext2,
                 "my:nodeType1");
-        assertValidationMessage(childNode, new ValidationMessage(ValidationMessageSeverity.ERROR,
+        ValidationExecutorTest.assertViolation(node.finalizeValidation(ntManagerProvider.getNamePathResolver(), ntManagerProvider.getNodeTypeDefinitionProvider(),
+                ntManagerProvider.getItemDefinitionProvider(),
+                ValidationMessageSeverity.ERROR, filter), 
+                new ValidationMessage(ValidationMessageSeverity.ERROR,
+                        String.format(JcrNodeTypeMetaDataImpl.MESSAGE_CHILD_NODE_NOT_ALLOWED, "my:autoCreatedChild1", "my:nodeType2",
+                                "my:nodeType1",
+                                JcrNodeTypeMetaDataImpl.CONSTRAINT_CHILD_NODE_AUTO_CREATED), nodeContext),
+                new ValidationMessage(ValidationMessageSeverity.ERROR,
                 String.format(JcrNodeTypeMetaDataImpl.MESSAGE_CHILD_NODE_NOT_ALLOWED, "name2", "my:nodeType1", "my:nodeType1",
-                        JcrNodeTypeMetaDataImpl.CONSTRAINT_CHILD_NODE_NOT_ALLOWED),
-                nodeContext));
+                        JcrNodeTypeMetaDataImpl.CONSTRAINT_CHILD_NODE_NOT_ALLOWED), nodeContext2));
     }
 
     @Test
@@ -232,16 +233,14 @@ public class JcrNodeTypeMetaDataImplTest {
                 ntManagerProvider.getEffectiveNodeTypeProvider(), ntManagerProvider.getNodeTypeDefinitionProvider(),
                 ntManagerProvider.getItemDefinitionProvider(), ValidationMessageSeverity.ERROR, createSimpleNodeContext("name"),
                 "my:nodeType1");
-        assertNoValidationErrors(node);
 
         DefaultWorkspaceFilter filter = new DefaultWorkspaceFilter();
 
         // mandatory child node missing outside filter
-        Collection<ValidationMessage> messages = node.finalizeValidation(ntManagerProvider.getNamePathResolver(),
+        Collection<ValidationMessage> messages = root.finalizeValidation(ntManagerProvider.getNamePathResolver(), ntManagerProvider.getNodeTypeDefinitionProvider(),
+                ntManagerProvider.getItemDefinitionProvider(),
                 ValidationMessageSeverity.ERROR, filter);
-        ValidationExecutorTest.assertViolation(messages, new ValidationMessage(ValidationMessageSeverity.ERROR,
-                String.format(JcrNodeTypeMetaDataImpl.MESSAGE_MANDATORY_UNCONTAINED_CHILD_NODE_MISSING, "my:namedChild1 [my:nodeType1]", "my:nodeType1",
-                        "/name")));
+        MatcherAssert.assertThat(messages, AnyValidationMessageMatcher.noValidationInCollection());
 
         node = root.addChildNode(ntManagerProvider.getNamePathResolver(),
                 ntManagerProvider.getEffectiveNodeTypeProvider(), ntManagerProvider.getNodeTypeDefinitionProvider(),
@@ -250,13 +249,15 @@ public class JcrNodeTypeMetaDataImplTest {
         
         // mandatory child node missing inside filter
         filter.add(new PathFilterSet("/"));
-        messages = node.finalizeValidation(ntManagerProvider.getNamePathResolver(), ValidationMessageSeverity.ERROR, filter);
+        messages = node.finalizeValidation(ntManagerProvider.getNamePathResolver(), ntManagerProvider.getNodeTypeDefinitionProvider(),
+                ntManagerProvider.getItemDefinitionProvider(),ValidationMessageSeverity.ERROR, filter);
         ValidationExecutorTest.assertViolation(messages, new ValidationMessage(ValidationMessageSeverity.ERROR,
                 String.format(JcrNodeTypeMetaDataImpl.MESSAGE_MANDATORY_CHILD_NODE_MISSING, "my:namedChild1 [my:nodeType1]", "my:nodeType1",
                         "/name2")));
 
         // calling a second time will not lead to anything
-        messages = node.finalizeValidation(ntManagerProvider.getNamePathResolver(), ValidationMessageSeverity.ERROR, filter);
+        messages = node.finalizeValidation(ntManagerProvider.getNamePathResolver(), ntManagerProvider.getNodeTypeDefinitionProvider(),
+                ntManagerProvider.getItemDefinitionProvider(), ValidationMessageSeverity.ERROR, filter);
         MatcherAssert.assertThat(messages, AnyValidationMessageMatcher.noValidationInCollection());
         
         // now add mandatory child node
@@ -269,40 +270,40 @@ public class JcrNodeTypeMetaDataImplTest {
                 ntManagerProvider.getEffectiveNodeTypeProvider(), ntManagerProvider.getNodeTypeDefinitionProvider(),
                 ntManagerProvider.getItemDefinitionProvider(), ValidationMessageSeverity.ERROR, createSimpleNodeContext("my:namedChild1"),
                 "my:nodeType1");
-        messages = node.finalizeValidation(ntManagerProvider.getNamePathResolver(), ValidationMessageSeverity.ERROR,
+        messages = node.finalizeValidation(ntManagerProvider.getNamePathResolver(), ntManagerProvider.getNodeTypeDefinitionProvider(),
+                ntManagerProvider.getItemDefinitionProvider(), ValidationMessageSeverity.ERROR,
                 new DefaultWorkspaceFilter());
         MatcherAssert.assertThat(messages, AnyValidationMessageMatcher.noValidationInCollection());
 
         // add arbitrary property to root
-        root.addProperty(createSimpleNodeContext("/"), ntManagerProvider.getNamePathResolver(),
+        MatcherAssert.assertThat(root.addProperty(createSimpleNodeContext("/"), ntManagerProvider.getNamePathResolver(),
                 ntManagerProvider.getEffectiveNodeTypeProvider(), ntManagerProvider.getNodeTypeDefinitionProvider(),
                 ntManagerProvider.getItemDefinitionProvider(), ValidationMessageSeverity.ERROR, "property", false,
-                ValueFactoryImpl.getInstance().createValue("foo"));
-        assertNoValidationErrors(root);
-
+                ValueFactoryImpl.getInstance().createValue("foo")), AnyValidationMessageMatcher.noValidationInCollection());
+        
         NodeContext nodeContext = createSimpleNodeContext("nodeForMandatoryProperties");
         node = root.addChildNode(ntManagerProvider.getNamePathResolver(),
                 ntManagerProvider.getEffectiveNodeTypeProvider(), ntManagerProvider.getNodeTypeDefinitionProvider(),
                 ntManagerProvider.getItemDefinitionProvider(), ValidationMessageSeverity.ERROR, nodeContext, "my:nodeType2");
-        assertNoValidationErrors(node);
-        messages = node.finalizeValidation(ntManagerProvider.getNamePathResolver(), ValidationMessageSeverity.ERROR, filter);
+        messages = node.finalizeValidation(ntManagerProvider.getNamePathResolver(), ntManagerProvider.getNodeTypeDefinitionProvider(),
+                ntManagerProvider.getItemDefinitionProvider(),ValidationMessageSeverity.ERROR, filter);
         ValidationExecutorTest.assertViolation(messages, new ValidationMessage(ValidationMessageSeverity.ERROR,
                 String.format(JcrNodeTypeMetaDataImpl.MESSAGE_MANDATORY_PROPERTY_MISSING, "my:mandatoryProperty", "my:nodeType2",
-                        "/nodeForMandatoryProperties")));
+                        "/nodeForMandatoryProperties"), nodeContext));
 
         nodeContext = createSimpleNodeContext("nodeForMandatoryProperties2");
         node = root.addChildNode(ntManagerProvider.getNamePathResolver(),
                 ntManagerProvider.getEffectiveNodeTypeProvider(), ntManagerProvider.getNodeTypeDefinitionProvider(),
                 ntManagerProvider.getItemDefinitionProvider(), ValidationMessageSeverity.ERROR, nodeContext, "my:nodeType2");
-        assertNoValidationErrors(node);
         node.addProperty(nodeContext, ntManagerProvider.getNamePathResolver(), ntManagerProvider.getEffectiveNodeTypeProvider(),
                 ntManagerProvider.getNodeTypeDefinitionProvider(), ntManagerProvider.getItemDefinitionProvider(),
                 ValidationMessageSeverity.ERROR,
                 "my:mandatoryProperty", false, ValueFactoryImpl.getInstance().createValue("foo"));
-        messages = node.finalizeValidation(ntManagerProvider.getNamePathResolver(), ValidationMessageSeverity.ERROR, filter);
+        messages = node.finalizeValidation(ntManagerProvider.getNamePathResolver(), ntManagerProvider.getNodeTypeDefinitionProvider(),
+                ntManagerProvider.getItemDefinitionProvider(), ValidationMessageSeverity.ERROR, filter);
         ValidationExecutorTest.assertViolation(messages, new ValidationMessage(ValidationMessageSeverity.ERROR,
                 String.format(JcrNodeTypeMetaDataImpl.MESSAGE_MANDATORY_PROPERTY_WITH_WRONG_TYPE, "my:mandatoryProperty", "String", "Date",
-                        "my:nodeType2", "/nodeForMandatoryProperties2")));
+                        "my:nodeType2"), nodeContext));
     }
 
     @Test
@@ -318,7 +319,6 @@ public class JcrNodeTypeMetaDataImplTest {
                 ntManagerProvider.getEffectiveNodeTypeProvider(), ntManagerProvider.getNodeTypeDefinitionProvider(),
                 ntManagerProvider.getItemDefinitionProvider(), ValidationMessageSeverity.ERROR, createSimpleNodeContext("name"),
                 "my:nodeType1");
-        assertNoValidationErrors(node);
 
         DefaultWorkspaceFilter filter = new DefaultWorkspaceFilter();
 
@@ -329,7 +329,8 @@ public class JcrNodeTypeMetaDataImplTest {
         
         // mandatory child node missing but not reported due to incremental validation
         filter.add(new PathFilterSet("/"));
-        Collection<ValidationMessage> messages = node.finalizeValidation(ntManagerProvider.getNamePathResolver(), ValidationMessageSeverity.ERROR, filter);
+        Collection<ValidationMessage> messages = node.finalizeValidation(ntManagerProvider.getNamePathResolver(), ntManagerProvider.getNodeTypeDefinitionProvider(),
+                ntManagerProvider.getItemDefinitionProvider(),  ValidationMessageSeverity.ERROR, filter);
         MatcherAssert.assertThat(messages, AnyValidationMessageMatcher.noValidationInCollection());
 
         // mandatory property missing but not reported due to incremental validation
@@ -337,8 +338,8 @@ public class JcrNodeTypeMetaDataImplTest {
         node = root.addChildNode(ntManagerProvider.getNamePathResolver(),
                 ntManagerProvider.getEffectiveNodeTypeProvider(), ntManagerProvider.getNodeTypeDefinitionProvider(),
                 ntManagerProvider.getItemDefinitionProvider(), ValidationMessageSeverity.ERROR, nodeContext, "my:nodeType2");
-        assertNoValidationErrors(node);
-        messages = node.finalizeValidation(ntManagerProvider.getNamePathResolver(), ValidationMessageSeverity.ERROR, filter);
+        messages = node.finalizeValidation(ntManagerProvider.getNamePathResolver(), ntManagerProvider.getNodeTypeDefinitionProvider(),
+                ntManagerProvider.getItemDefinitionProvider(), ValidationMessageSeverity.ERROR, filter);
         MatcherAssert.assertThat(messages, AnyValidationMessageMatcher.noValidationInCollection());
     }
     
@@ -361,72 +362,65 @@ public class JcrNodeTypeMetaDataImplTest {
 
         NodeContext nodeContext = createSimpleNodeContext("/");
         // add arbitrary property to root
-        root.addProperty(nodeContext, ntManagerProvider.getNamePathResolver(),
+        MatcherAssert.assertThat(root.addProperty(nodeContext, ntManagerProvider.getNamePathResolver(),
                 ntManagerProvider.getEffectiveNodeTypeProvider(), ntManagerProvider.getNodeTypeDefinitionProvider(),
                 ntManagerProvider.getItemDefinitionProvider(), ValidationMessageSeverity.ERROR, "property", false,
-                ValueFactoryImpl.getInstance().createValue("foo"));
-        assertNoValidationErrors(root);
+                ValueFactoryImpl.getInstance().createValue("foo")), AnyValidationMessageMatcher.noValidationInCollection());
 
         JcrNodeTypeMetaData node = root.addChildNode(ntManagerProvider.getNamePathResolver(),
                 ntManagerProvider.getEffectiveNodeTypeProvider(), ntManagerProvider.getNodeTypeDefinitionProvider(),
                 ntManagerProvider.getItemDefinitionProvider(), ValidationMessageSeverity.ERROR, createSimpleNodeContext("child"),
                 "my:nodeType3");
-        assertNoValidationErrors(node);
 
         // not allowed (wrong type)
-        node.addProperty(nodeContext, ntManagerProvider.getNamePathResolver(),
+        ValidationExecutorTest.assertViolation( node.addProperty(nodeContext, ntManagerProvider.getNamePathResolver(),
                 ntManagerProvider.getEffectiveNodeTypeProvider(), ntManagerProvider.getNodeTypeDefinitionProvider(),
                 ntManagerProvider.getItemDefinitionProvider(), ValidationMessageSeverity.ERROR, "property", false,
-                ValueFactoryImpl.getInstance().createValue("foo"));
-        assertValidationMessage(node,
+                ValueFactoryImpl.getInstance().createValue("foo")),
                 new ValidationMessage(ValidationMessageSeverity.ERROR,
                         String.format(JcrNodeTypeMetaDataImpl.MESSAGE_PROPERTY_NOT_ALLOWED, "property",
                                 "String", "my:nodeType3", JcrNodeTypeMetaDataImpl.CONSTRAINT_PROPERTY_NOT_ALLOWED),
                         nodeContext));
 
         // protected but nevertheless allowed
-        node.addProperty(nodeContext, ntManagerProvider.getNamePathResolver(),
+        MatcherAssert.assertThat(node.addProperty(nodeContext, ntManagerProvider.getNamePathResolver(),
                 ntManagerProvider.getEffectiveNodeTypeProvider(), ntManagerProvider.getNodeTypeDefinitionProvider(),
                 ntManagerProvider.getItemDefinitionProvider(), ValidationMessageSeverity.ERROR, "jcr:primaryType", false,
-                ValueFactoryImpl.getInstance().createValue("foo"));
-        assertNoValidationErrors(node);
+                ValueFactoryImpl.getInstance().createValue("foo")), AnyValidationMessageMatcher.noValidationInCollection());
 
         // protected
-        node.addProperty(nodeContext, ntManagerProvider.getNamePathResolver(), ntManagerProvider.getEffectiveNodeTypeProvider(),
+        ValidationExecutorTest.assertViolation(node.addProperty(nodeContext, ntManagerProvider.getNamePathResolver(), ntManagerProvider.getEffectiveNodeTypeProvider(),
                 ntManagerProvider.getNodeTypeDefinitionProvider(), ntManagerProvider.getItemDefinitionProvider(),
                 ValidationMessageSeverity.ERROR,
-                "my:protected", false, ValueFactoryImpl.getInstance().createValue("foo"));
-        assertValidationMessage(node,
+                "my:protected", false, ValueFactoryImpl.getInstance().createValue("foo")),
                 new ValidationMessage(ValidationMessageSeverity.ERROR, String.format(JcrNodeTypeMetaDataImpl.MESSAGE_PROPERTY_NOT_ALLOWED,
                         "my:protected", "String", "my:nodeType3", JcrNodeTypeMetaDataImpl.CONSTRAINT_PROPERTY_PROTECTED), nodeContext));
 
         // multi value where single value is required
-        node.addProperty(nodeContext, ntManagerProvider.getNamePathResolver(),
+        ValidationExecutorTest.assertViolation(node.addProperty(nodeContext, ntManagerProvider.getNamePathResolver(),
                 ntManagerProvider.getEffectiveNodeTypeProvider(), ntManagerProvider.getNodeTypeDefinitionProvider(),
                 ntManagerProvider.getItemDefinitionProvider(), ValidationMessageSeverity.ERROR, "my:property1", true,
-                ValueFactoryImpl.getInstance().createValue("foo"), ValueFactoryImpl.getInstance().createValue("bar"));
-        assertValidationMessage(node,
+                ValueFactoryImpl.getInstance().createValue("foo"), ValueFactoryImpl.getInstance().createValue("bar")),
                 new ValidationMessage(ValidationMessageSeverity.ERROR, String.format(JcrNodeTypeMetaDataImpl.MESSAGE_PROPERTY_NOT_ALLOWED,
                         "my:property1", "String", "my:nodeType3", JcrNodeTypeMetaDataImpl.CONSTRAINT_PROPERTY_NOT_ALLOWED), nodeContext));
 
         // constrained property
-        node.addProperty(nodeContext, ntManagerProvider.getNamePathResolver(), ntManagerProvider.getEffectiveNodeTypeProvider(),
+        MatcherAssert.assertThat(node.addProperty(nodeContext, ntManagerProvider.getNamePathResolver(), ntManagerProvider.getEffectiveNodeTypeProvider(),
                 ntManagerProvider.getNodeTypeDefinitionProvider(), ntManagerProvider.getItemDefinitionProvider(),
                 ValidationMessageSeverity.ERROR,
-                "my:constrainedStringProperty", false, ValueFactoryImpl.getInstance().createValue("prefix1foo"));
-        assertNoValidationErrors(root);
+                "my:constrainedStringProperty", false, ValueFactoryImpl.getInstance().createValue("prefix1foo")),
+                AnyValidationMessageMatcher.noValidationInCollection());
 
-        node.addProperty(nodeContext, ntManagerProvider.getNamePathResolver(), ntManagerProvider.getEffectiveNodeTypeProvider(),
+        MatcherAssert.assertThat(node.addProperty(nodeContext, ntManagerProvider.getNamePathResolver(), ntManagerProvider.getEffectiveNodeTypeProvider(),
                 ntManagerProvider.getNodeTypeDefinitionProvider(), ntManagerProvider.getItemDefinitionProvider(),
                 ValidationMessageSeverity.ERROR,
-                "my:constrainedStringProperty", false, ValueFactoryImpl.getInstance().createValue("foosuffix1"));
-        assertNoValidationErrors(root);
+                "my:constrainedStringProperty", false, ValueFactoryImpl.getInstance().createValue("foosuffix1")),
+                AnyValidationMessageMatcher.noValidationInCollection());
 
-        node.addProperty(nodeContext, ntManagerProvider.getNamePathResolver(), ntManagerProvider.getEffectiveNodeTypeProvider(),
+        ValidationExecutorTest.assertViolation( node.addProperty(nodeContext, ntManagerProvider.getNamePathResolver(), ntManagerProvider.getEffectiveNodeTypeProvider(),
                 ntManagerProvider.getNodeTypeDefinitionProvider(), ntManagerProvider.getItemDefinitionProvider(),
                 ValidationMessageSeverity.ERROR,
-                "my:constrainedStringProperty", false, ValueFactoryImpl.getInstance().createValue("foo"));
-        assertValidationMessage(node,
+                "my:constrainedStringProperty", false, ValueFactoryImpl.getInstance().createValue("foo")),
                 new ValidationMessage(ValidationMessageSeverity.ERROR, String.format(JcrNodeTypeMetaDataImpl.MESSAGE_PROPERTY_NOT_ALLOWED,
                         "my:constrainedStringProperty", "String", "my:nodeType3",
                         String.format(JcrNodeTypeMetaDataImpl.CONSTRAINT_PROPERTY_VALUE,
@@ -434,16 +428,4 @@ public class JcrNodeTypeMetaDataImplTest {
                         nodeContext));
     }
 
-    private static void assertNoValidationErrors(JcrNodeTypeMetaData node) {
-        Collection<ValidationMessage> messages = new ArrayList<>();
-        node.fetchAndClearValidationMessages(messages);
-        MatcherAssert.assertThat(messages, Matchers.empty());
-    }
-
-    private static void assertValidationMessage(JcrNodeTypeMetaData node, ValidationMessage... expectedMessages) {
-        Collection<ValidationMessage> actualMessages = new ArrayList<>();
-        node.fetchAndClearValidationMessages(actualMessages);
-        MatcherAssert.assertThat(actualMessages, Matchers.contains(expectedMessages));
-    }
-
 }
diff --git a/vault-validation/src/test/java/org/apache/jackrabbit/vault/validation/spi/impl/nodetype/NodeTypeValidatorTest.java b/vault-validation/src/test/java/org/apache/jackrabbit/vault/validation/spi/impl/nodetype/NodeTypeValidatorTest.java
index 3790f3c..382649e 100644
--- a/vault-validation/src/test/java/org/apache/jackrabbit/vault/validation/spi/impl/nodetype/NodeTypeValidatorTest.java
+++ b/vault-validation/src/test/java/org/apache/jackrabbit/vault/validation/spi/impl/nodetype/NodeTypeValidatorTest.java
@@ -48,9 +48,7 @@ import org.apache.jackrabbit.vault.validation.spi.ValidationMessageSeverity;
 import org.apache.jackrabbit.vault.validation.spi.util.NodeContextImpl;
 import org.apache.jackrabbit.vault.validation.spi.util.classloaderurl.URLFactory;
 import org.hamcrest.MatcherAssert;
-import org.junit.Assert;
 import org.junit.Before;
-import org.junit.Ignore;
 import org.junit.Test;
 
 public class NodeTypeValidatorTest {
@@ -85,8 +83,7 @@ public class NodeTypeValidatorTest {
     }
 
     @Test
-    @Ignore
-    public void testValidateComplexUnstructuredNodeTypes() throws IOException, RepositoryException, ParseException, ConfigurationException {
+    public void testValidateNotAllowedProperties() throws IOException, RepositoryException, ParseException, ConfigurationException {
         NodeContext nodeContext = new NodeContextImpl("/apps/test/node4", Paths.get("node4"), Paths.get(""));
 
         Map<String, DocViewProperty> props = new HashMap<>();
@@ -96,16 +93,16 @@ public class NodeTypeValidatorTest {
                 new String[] { "nt:unstructured" }, false, PropertyType.STRING));
 
         // no primary type
-        DocViewNode node = new DocViewNode("jcr:root", "jcr:root", null, props, null, "sling:Folder");
-        Assert.assertNull(validator.validate(node, nodeContext, false));
+        DocViewNode node = new DocViewNode("jcr:root", "jcr:root", null, props, null, "nt:unstructured");
+        MatcherAssert.assertThat(validator.validate(node, nodeContext, false), AnyValidationMessageMatcher.noValidationInCollection());
 
         props.put(NameConstants.JCR_PRIMARYTYPE.toString(),
                 new DocViewProperty(NameConstants.JCR_PRIMARYTYPE.toString(), new String[] { "value1" }, false, PropertyType.STRING));
         node = new DocViewNode("test", "test", null, props, null, "nt:folder");
         ValidationExecutorTest.assertViolation(validator.validate(node, nodeContext, false),
                 new ValidationMessage(ValidationMessageSeverity.ERROR,
-                        String.format(JcrNodeTypeMetaDataImpl.MESSAGE_PROPERTY_NOT_ALLOWED, property, "nt:folder",
-                                "No property definition found for name!")));
+                        String.format(JcrNodeTypeMetaDataImpl.MESSAGE_PROPERTY_NOT_ALLOWED, "prop1", "String", "nt:folder",
+                                "No applicable property definition found for name and type!"), nodeContext));
     }
 
     @Test
@@ -138,15 +135,59 @@ public class NodeTypeValidatorTest {
         
         // nt:unstructured below nt:folder is not allowed
         DocViewNode node = new DocViewNode("jcr:root", "jcr:root", null, props, null, JcrConstants.NT_UNSTRUCTURED);
-        ValidationExecutorTest.assertViolation(validator.validate(node, nodeContext, false),
+        MatcherAssert.assertThat(validator.validate(node, nodeContext, false), AnyValidationMessageMatcher.noValidationInCollection());
+        ValidationExecutorTest.assertViolation(validator.done(),
                 new ValidationMessage(ValidationMessageSeverity.ERROR,
                         String.format(JcrNodeTypeMetaDataImpl.MESSAGE_CHILD_NODE_OF_NOT_CONTAINED_PARENT_POTENTIALLY_NOT_ALLOWED,
                                 "test", "nt:unstructured", JcrConstants.NT_FOLDER,
                                 "Node type does not allow arbitrary child nodes and does not allow this specific name and node type either!"), nodeContext));
+    }
+
+    // https://issues.apache.org/jira/browse/JCRVLT-527
+    @Test
+    public void testChildFolderBelowTypeNotAllowingNtFolder() {
+        NodeContext nodeContext = new NodeContextImpl("/apps/test", Paths.get("apps","test", ".content.xml"), Paths.get(""));
+        Map<String, DocViewProperty> props = new HashMap<>();
+        props.put(NameConstants.JCR_PRIMARYTYPE.toString(), new DocViewProperty(NameConstants.JCR_PRIMARYTYPE.toString(),
+                new String[] { "rep:AuthorizableFolder" }, false, PropertyType.STRING));
+        DocViewNode node = new DocViewNode("jcr:root", "jcr:root", null, props, null, "rep:AuthorizableFolder");
+        
+        MatcherAssert.assertThat(validator.validate(node, nodeContext, true), AnyValidationMessageMatcher.noValidationInCollection());
+        
+        // add child as folder first
+        nodeContext = new NodeContextImpl("/apps/test/child", Paths.get("apps", "test", "child"), Paths.get(""));
+        MatcherAssert.assertThat(validator.validateJcrPath(nodeContext, true, false), AnyValidationMessageMatcher.noValidationInCollection());
+        
+        // now refine type via .content.xml
+        nodeContext = new NodeContextImpl("/apps/test/child", Paths.get("apps", "test", "child", ".content.xml"), Paths.get(""));
+        props.put(NameConstants.JCR_PRIMARYTYPE.toString(), new DocViewProperty(NameConstants.JCR_PRIMARYTYPE.toString(),
+                new String[] { "rep:SystemUser" }, false, PropertyType.STRING));
+        props.put(NameConstants.REP_PRINCIPAL_NAME.toString(), new DocViewProperty(NameConstants.REP_PRINCIPAL_NAME.toString(),
+                new String[] { "mySystemUser" }, false, PropertyType.STRING));
+        node = new DocViewNode("jcr:root", "jcr:root", null, props, null, "rep:SystemUser");
+        
+        MatcherAssert.assertThat(validator.validate(node, nodeContext, true), AnyValidationMessageMatcher.noValidationInCollection());
         MatcherAssert.assertThat(validator.done(), AnyValidationMessageMatcher.noValidationInCollection());
     }
 
     @Test
+    public void testMissingMandatoryProperty() {
+        // now refine type via .content.xml
+        NodeContext nodeContext = new NodeContextImpl("/apps/test/child", Paths.get("apps", "test", "child", ".content.xml"), Paths.get(""));
+        Map<String, DocViewProperty> props = new HashMap<>();
+        props.put(NameConstants.JCR_PRIMARYTYPE.toString(), new DocViewProperty(NameConstants.JCR_PRIMARYTYPE.toString(),
+                new String[] { "rep:SystemUser" }, false, PropertyType.STRING));
+        DocViewNode node = new DocViewNode("jcr:root", "jcr:root", null, props, null, "rep:SystemUser");
+        
+        MatcherAssert.assertThat(validator.validate(node, nodeContext, true), AnyValidationMessageMatcher.noValidationInCollection());
+        
+        ValidationExecutorTest.assertViolation(validator.done(),
+                new ValidationMessage(ValidationMessageSeverity.ERROR,
+                        String.format(JcrNodeTypeMetaDataImpl.MESSAGE_MANDATORY_PROPERTY_MISSING,
+                                "rep:principalName", "rep:SystemUser", nodeContext.getNodePath()), nodeContext));
+    }
+
+    @Test
     public void testMissingMandatoryChildNode() {
         NodeContext nodeContext = new NodeContextImpl("/apps/test/node4", Paths.get("node4"), Paths.get(""));
 
@@ -181,7 +222,7 @@ public class NodeTypeValidatorTest {
     }
 
     @Test
-    public void testPropertyWitInconvertibleValue() {
+    public void testPropertyWithInconvertibleValue() {
         NodeContext nodeContext = new NodeContextImpl("/apps/test/node4", Paths.get("node4"), Paths.get(""));
 
         Map<String, DocViewProperty> props = new HashMap<>();