You are viewing a plain text version of this content. The canonical link for it is here.
Posted to oak-commits@jackrabbit.apache.org by ro...@apache.org on 2017/07/31 10:02:07 UTC

svn commit: r1803501 - in /jackrabbit/oak/trunk: oak-it/src/test/java/org/apache/jackrabbit/oak/composite/ oak-store-composite/src/main/java/org/apache/jackrabbit/oak/composite/ oak-store-composite/src/main/java/org/apache/jackrabbit/oak/composite/chec...

Author: rombert
Date: Mon Jul 31 10:02:06 2017
New Revision: 1803501

URL: http://svn.apache.org/viewvc?rev=1803501&view=rev
Log:
OAK-6445 - Ensure mounted node stores don't contain versionable nodes

Added:
    jackrabbit/oak/trunk/oak-store-composite/src/main/java/org/apache/jackrabbit/oak/composite/checks/
    jackrabbit/oak/trunk/oak-store-composite/src/main/java/org/apache/jackrabbit/oak/composite/checks/ErrorHolder.java
    jackrabbit/oak/trunk/oak-store-composite/src/main/java/org/apache/jackrabbit/oak/composite/checks/MountedNodeStoreChecker.java
      - copied, changed from r1803272, jackrabbit/oak/trunk/oak-store-composite/src/main/java/org/apache/jackrabbit/oak/composite/MountedNodeStore.java
    jackrabbit/oak/trunk/oak-store-composite/src/main/java/org/apache/jackrabbit/oak/composite/checks/NodeStoreChecks.java
    jackrabbit/oak/trunk/oak-store-composite/src/main/java/org/apache/jackrabbit/oak/composite/checks/NodeStoreChecksService.java
    jackrabbit/oak/trunk/oak-store-composite/src/main/java/org/apache/jackrabbit/oak/composite/checks/VersionableNodesMountedNodeStoreChecker.java
    jackrabbit/oak/trunk/oak-store-composite/src/test/java/org/apache/jackrabbit/oak/composite/checks/
    jackrabbit/oak/trunk/oak-store-composite/src/test/java/org/apache/jackrabbit/oak/composite/checks/NodeStoreChecksServiceTest.java
Modified:
    jackrabbit/oak/trunk/oak-it/src/test/java/org/apache/jackrabbit/oak/composite/CompositeNodeStoreBuilderTest.java
    jackrabbit/oak/trunk/oak-store-composite/src/main/java/org/apache/jackrabbit/oak/composite/CompositeNodeStore.java
    jackrabbit/oak/trunk/oak-store-composite/src/main/java/org/apache/jackrabbit/oak/composite/CompositeNodeStoreService.java
    jackrabbit/oak/trunk/oak-store-composite/src/main/java/org/apache/jackrabbit/oak/composite/MountedNodeStore.java
    jackrabbit/oak/trunk/oak-store-composite/src/test/java/org/apache/jackrabbit/oak/composite/CompositeNodeStoreServiceTest.java

Modified: jackrabbit/oak/trunk/oak-it/src/test/java/org/apache/jackrabbit/oak/composite/CompositeNodeStoreBuilderTest.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-it/src/test/java/org/apache/jackrabbit/oak/composite/CompositeNodeStoreBuilderTest.java?rev=1803501&r1=1803500&r2=1803501&view=diff
==============================================================================
--- jackrabbit/oak/trunk/oak-it/src/test/java/org/apache/jackrabbit/oak/composite/CompositeNodeStoreBuilderTest.java (original)
+++ jackrabbit/oak/trunk/oak-it/src/test/java/org/apache/jackrabbit/oak/composite/CompositeNodeStoreBuilderTest.java Mon Jul 31 10:02:06 2017
@@ -18,9 +18,21 @@
  */
 package org.apache.jackrabbit.oak.composite;
 
+import java.util.Collections;
+
+import org.apache.jackrabbit.JcrConstants;
+import org.apache.jackrabbit.oak.api.CommitFailedException;
+import org.apache.jackrabbit.oak.api.IllegalRepositoryStateException;
+import org.apache.jackrabbit.oak.api.Type;
+import org.apache.jackrabbit.oak.composite.checks.NodeStoreChecksService;
+import org.apache.jackrabbit.oak.composite.checks.VersionableNodesMountedNodeStoreChecker;
 import org.apache.jackrabbit.oak.plugins.memory.MemoryNodeStore;
+import org.apache.jackrabbit.oak.plugins.memory.PropertyStates;
+import org.apache.jackrabbit.oak.spi.commit.CommitInfo;
+import org.apache.jackrabbit.oak.spi.commit.EmptyHook;
 import org.apache.jackrabbit.oak.spi.mount.MountInfoProvider;
 import org.apache.jackrabbit.oak.spi.mount.Mounts;
+import org.apache.jackrabbit.oak.spi.state.NodeBuilder;
 import org.junit.Test;
 
 public class CompositeNodeStoreBuilderTest {
@@ -82,4 +94,29 @@ public class CompositeNodeStoreBuilderTe
             .addMount("not-temp", new MemoryNodeStore())
             .build();
     }
+    
+    @Test(expected = IllegalRepositoryStateException.class)
+    public void versionableNode() throws CommitFailedException {
+
+        MemoryNodeStore root = new MemoryNodeStore();
+        MemoryNodeStore mount = new MemoryNodeStore();
+        
+        // create a child node that is versionable
+        // note that we won't cover all checks here, we are only interested in seeing that at least one check is triggered
+        NodeBuilder rootBuilder = mount.getRoot().builder();
+        NodeBuilder childNode = rootBuilder.setChildNode("readOnly").setChildNode("second").setChildNode("third");
+        childNode.setProperty(JcrConstants.JCR_ISCHECKEDOUT, false);
+        childNode.setProperty(PropertyStates.createProperty(JcrConstants.JCR_MIXINTYPES , Collections.singletonList(JcrConstants.MIX_VERSIONABLE), Type.NAMES));
+        mount.merge(rootBuilder, EmptyHook.INSTANCE, CommitInfo.EMPTY);
+        
+        MountInfoProvider mip = Mounts.newBuilder()
+                .readOnlyMount("readOnly", "/readOnly")
+                .build();
+
+        new CompositeNodeStore.Builder(mip, root)
+            .addMount("readOnly", mount)
+            .with(new NodeStoreChecksService(Collections.singletonList(new VersionableNodesMountedNodeStoreChecker())))
+            .build();        
+        
+    }
 }
\ No newline at end of file

Modified: jackrabbit/oak/trunk/oak-store-composite/src/main/java/org/apache/jackrabbit/oak/composite/CompositeNodeStore.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-store-composite/src/main/java/org/apache/jackrabbit/oak/composite/CompositeNodeStore.java?rev=1803501&r1=1803500&r2=1803501&view=diff
==============================================================================
--- jackrabbit/oak/trunk/oak-store-composite/src/main/java/org/apache/jackrabbit/oak/composite/CompositeNodeStore.java (original)
+++ jackrabbit/oak/trunk/oak-store-composite/src/main/java/org/apache/jackrabbit/oak/composite/CompositeNodeStore.java Mon Jul 31 10:02:06 2017
@@ -25,6 +25,7 @@ import org.apache.jackrabbit.oak.api.Blo
 import org.apache.jackrabbit.oak.api.CommitFailedException;
 import org.apache.jackrabbit.oak.api.PropertyState;
 import org.apache.jackrabbit.oak.commons.PathUtils;
+import org.apache.jackrabbit.oak.composite.checks.NodeStoreChecks;
 import org.apache.jackrabbit.oak.spi.commit.CommitHook;
 import org.apache.jackrabbit.oak.spi.commit.CommitInfo;
 import org.apache.jackrabbit.oak.spi.commit.EmptyHook;
@@ -461,10 +462,17 @@ public class CompositeNodeStore implemen
 
         private boolean partialReadOnly = true;
 
+        private NodeStoreChecks checks;
+
         public Builder(MountInfoProvider mip, NodeStore globalStore) {
             this.mip = checkNotNull(mip, "mountInfoProvider");
             this.globalStore = checkNotNull(globalStore, "globalStore");
         }
+        
+        public Builder with(NodeStoreChecks checks) {
+            this.checks = checks;
+            return this;
+        }
 
         public Builder addMount(String mountName, NodeStore store) {
             checkNotNull(store, "store");
@@ -490,6 +498,9 @@ public class CompositeNodeStore implemen
             if (partialReadOnly) {
                 assertPartialMountsAreReadOnly();
             }
+            if ( checks != null ) {
+                nonDefaultStores.forEach( s -> checks.check(globalStore, s));
+            }
             return new CompositeNodeStore(mip, globalStore, nonDefaultStores, ignoreReadOnlyWritePaths);
         }
 

Modified: jackrabbit/oak/trunk/oak-store-composite/src/main/java/org/apache/jackrabbit/oak/composite/CompositeNodeStoreService.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-store-composite/src/main/java/org/apache/jackrabbit/oak/composite/CompositeNodeStoreService.java?rev=1803501&r1=1803500&r2=1803501&view=diff
==============================================================================
--- jackrabbit/oak/trunk/oak-store-composite/src/main/java/org/apache/jackrabbit/oak/composite/CompositeNodeStoreService.java (original)
+++ jackrabbit/oak/trunk/oak-store-composite/src/main/java/org/apache/jackrabbit/oak/composite/CompositeNodeStoreService.java Mon Jul 31 10:02:06 2017
@@ -27,6 +27,7 @@ import org.apache.felix.scr.annotations.
 import org.apache.felix.scr.annotations.ReferencePolicy;
 import org.apache.jackrabbit.oak.api.jmx.CheckpointMBean;
 import org.apache.jackrabbit.oak.commons.PropertiesUtil;
+import org.apache.jackrabbit.oak.composite.checks.NodeStoreChecks;
 import org.apache.jackrabbit.oak.osgi.OsgiWhiteboard;
 import org.apache.jackrabbit.oak.spi.commit.ObserverTracker;
 import org.apache.jackrabbit.oak.spi.mount.Mount;
@@ -67,6 +68,9 @@ public class CompositeNodeStoreService {
 
     @Reference(cardinality = ReferenceCardinality.MANDATORY_MULTIPLE, policy = ReferencePolicy.DYNAMIC, bind = "bindNodeStore", unbind = "unbindNodeStore", referenceInterface = NodeStoreProvider.class, target="(!(service.pid=org.apache.jackrabbit.oak.composite.CompositeNodeStore))")
     private List<NodeStoreWithProps> nodeStores = new ArrayList<>();
+    
+    @Reference
+    private NodeStoreChecks checks;
 
     @Property(label = "Ignore read only writes",
             unbounded = PropertyUnbounded.ARRAY,
@@ -126,7 +130,7 @@ public class CompositeNodeStoreService {
             LOG.info("Composite node store registration is deferred until there's a global node store registered in OSGi");
             return;
         } else {
-            LOG.info("Found global node store: {}", getDescription(globalNs));
+            LOG.info("Found global node store: {}", globalNs.getDescription());
         }
 
         for (Mount m : mountInfoProvider.getNonDefaultMounts()) {
@@ -140,6 +144,7 @@ public class CompositeNodeStoreService {
         CompositeNodeStore.Builder builder = new CompositeNodeStore.Builder(mountInfoProvider, globalNs.getNodeStoreProvider().getNodeStore());
         nodeStoresInUse.add(globalNs.getNodeStoreProvider());
 
+        builder.with(checks);
         builder.setPartialReadOnly(partialReadOnly);
         for (String p : ignoreReadOnlyWritePaths) {
             builder.addIgnoredReadOnlyWritePath(p);
@@ -152,7 +157,7 @@ public class CompositeNodeStoreService {
             String mountName = getMountName(ns);
             if (mountName != null) {
                 builder.addMount(mountName, ns.getNodeStoreProvider().getNodeStore());
-                LOG.info("Mounting {} as {}", getDescription(ns), mountName);
+                LOG.info("Mounting {} as {}", ns.getDescription(), mountName);
                 nodeStoresInUse.add(ns.getNodeStoreProvider());
             }
         }
@@ -197,10 +202,6 @@ public class CompositeNodeStoreService {
         return role.substring(MOUNT_ROLE_PREFIX.length());
     }
 
-    private String getDescription(NodeStoreWithProps ns) {
-        return PropertiesUtil.toString(ns.getProps().get("oak.nodestore.description"), ns.getNodeStoreProvider().getClass().toString());
-    }
-
     private void unregisterCompositeNodeStore() {
         if (nsReg != null) {
             LOG.info("Unregistering the composite node store");
@@ -272,5 +273,10 @@ public class CompositeNodeStoreService {
         public String getRole() {
             return PropertiesUtil.toString(props.get(NodeStoreProvider.ROLE), null);
         }
+
+        public String getDescription() {
+            return PropertiesUtil.toString(getProps().get("oak.nodestore.description"),
+                    getNodeStoreProvider().getClass().toString());
+        }
     }
 }
\ No newline at end of file

Modified: jackrabbit/oak/trunk/oak-store-composite/src/main/java/org/apache/jackrabbit/oak/composite/MountedNodeStore.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-store-composite/src/main/java/org/apache/jackrabbit/oak/composite/MountedNodeStore.java?rev=1803501&r1=1803500&r2=1803501&view=diff
==============================================================================
--- jackrabbit/oak/trunk/oak-store-composite/src/main/java/org/apache/jackrabbit/oak/composite/MountedNodeStore.java (original)
+++ jackrabbit/oak/trunk/oak-store-composite/src/main/java/org/apache/jackrabbit/oak/composite/MountedNodeStore.java Mon Jul 31 10:02:06 2017
@@ -21,7 +21,7 @@ package org.apache.jackrabbit.oak.compos
 import org.apache.jackrabbit.oak.spi.mount.Mount;
 import org.apache.jackrabbit.oak.spi.state.NodeStore;
 
-class MountedNodeStore {
+public class MountedNodeStore {
 
     private final Mount mount;
 

Added: jackrabbit/oak/trunk/oak-store-composite/src/main/java/org/apache/jackrabbit/oak/composite/checks/ErrorHolder.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-store-composite/src/main/java/org/apache/jackrabbit/oak/composite/checks/ErrorHolder.java?rev=1803501&view=auto
==============================================================================
--- jackrabbit/oak/trunk/oak-store-composite/src/main/java/org/apache/jackrabbit/oak/composite/checks/ErrorHolder.java (added)
+++ jackrabbit/oak/trunk/oak-store-composite/src/main/java/org/apache/jackrabbit/oak/composite/checks/ErrorHolder.java Mon Jul 31 10:02:06 2017
@@ -0,0 +1,47 @@
+/*
+ * 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.oak.composite.checks;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import org.apache.jackrabbit.oak.api.IllegalRepositoryStateException;
+import org.apache.jackrabbit.oak.composite.MountedNodeStore;
+
+class ErrorHolder {
+    
+    private static final int FAIL_IMMEDIATELY_THRESHOLD = 100;
+    private final List<String> errors = new ArrayList<>();
+    
+    public void report(MountedNodeStore mountedStore, String path, String error) {
+        errors.add(String.format("For NodeStore mount %s, path %s, encountered the following problem: '%s'", mountedStore.getMount().getName(), path, error));
+        if ( errors.size() == FAIL_IMMEDIATELY_THRESHOLD ) { 
+            end();
+        }
+    }
+    
+    public void end() {
+        if ( errors.isEmpty() ) {
+            return;
+        }
+        StringBuilder out = new StringBuilder();
+        out.append(errors.size()).append(" errors were found: \n");
+        errors.forEach( e -> out.append(e).append('\n'));
+        
+        throw new IllegalRepositoryStateException(out.toString());
+    }
+}
\ No newline at end of file

Copied: jackrabbit/oak/trunk/oak-store-composite/src/main/java/org/apache/jackrabbit/oak/composite/checks/MountedNodeStoreChecker.java (from r1803272, jackrabbit/oak/trunk/oak-store-composite/src/main/java/org/apache/jackrabbit/oak/composite/MountedNodeStore.java)
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-store-composite/src/main/java/org/apache/jackrabbit/oak/composite/checks/MountedNodeStoreChecker.java?p2=jackrabbit/oak/trunk/oak-store-composite/src/main/java/org/apache/jackrabbit/oak/composite/checks/MountedNodeStoreChecker.java&p1=jackrabbit/oak/trunk/oak-store-composite/src/main/java/org/apache/jackrabbit/oak/composite/MountedNodeStore.java&r1=1803272&r2=1803501&rev=1803501&view=diff
==============================================================================
--- jackrabbit/oak/trunk/oak-store-composite/src/main/java/org/apache/jackrabbit/oak/composite/MountedNodeStore.java (original)
+++ jackrabbit/oak/trunk/oak-store-composite/src/main/java/org/apache/jackrabbit/oak/composite/checks/MountedNodeStoreChecker.java Mon Jul 31 10:02:06 2017
@@ -16,40 +16,25 @@
  * specific language governing permissions and limitations
  * under the License.
  */
-package org.apache.jackrabbit.oak.composite;
 
-import org.apache.jackrabbit.oak.spi.mount.Mount;
-import org.apache.jackrabbit.oak.spi.state.NodeStore;
-
-class MountedNodeStore {
-
-    private final Mount mount;
-
-    private final NodeStore nodeStore;
+package org.apache.jackrabbit.oak.composite.checks;
 
-    public MountedNodeStore(Mount mount, NodeStore nodeStore) {
-        this.mount = mount;
-        this.nodeStore = nodeStore;
-    }
-
-    public Mount getMount() {
-        return mount;
-    }
+import org.apache.jackrabbit.oak.api.Tree;
+import org.apache.jackrabbit.oak.composite.MountedNodeStore;
+import org.apache.jackrabbit.oak.spi.state.NodeStore;
 
-    public NodeStore getNodeStore() {
-        return nodeStore;
-    }
+/**
+ * Applies a category of consistence checks specific to <tt>NodeStore</tt> mounts
+ * 
+ * <p>Checks are only performed on non-default mounts.</p>
+ * 
+ * <p>Named 'Checker' to clarify that it is not a Validator in the Oak sense.</p> 
+ *
+ */
+public interface MountedNodeStoreChecker<T> {
+    
+    public T createContext(NodeStore globalStore);
+    
+    void check(MountedNodeStore mountedStore, Tree tree, ErrorHolder errorHolder, T context);
 
-    @Override
-    public String toString() {
-        StringBuilder result = new StringBuilder(super.toString());
-        result.append('[');
-        if (mount.isDefault()) {
-            result.append("default");
-        } else {
-            result.append(mount.getName());
-        }
-        result.append(']');
-        return result.toString();
-    }
-}
\ No newline at end of file
+}

Added: jackrabbit/oak/trunk/oak-store-composite/src/main/java/org/apache/jackrabbit/oak/composite/checks/NodeStoreChecks.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-store-composite/src/main/java/org/apache/jackrabbit/oak/composite/checks/NodeStoreChecks.java?rev=1803501&view=auto
==============================================================================
--- jackrabbit/oak/trunk/oak-store-composite/src/main/java/org/apache/jackrabbit/oak/composite/checks/NodeStoreChecks.java (added)
+++ jackrabbit/oak/trunk/oak-store-composite/src/main/java/org/apache/jackrabbit/oak/composite/checks/NodeStoreChecks.java Mon Jul 31 10:02:06 2017
@@ -0,0 +1,26 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.jackrabbit.oak.composite.checks;
+
+import org.apache.jackrabbit.oak.composite.MountedNodeStore;
+import org.apache.jackrabbit.oak.spi.state.NodeStore;
+
+public interface NodeStoreChecks {
+
+    void check(NodeStore globalStore, MountedNodeStore mountedStore);
+
+}

Added: jackrabbit/oak/trunk/oak-store-composite/src/main/java/org/apache/jackrabbit/oak/composite/checks/NodeStoreChecksService.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-store-composite/src/main/java/org/apache/jackrabbit/oak/composite/checks/NodeStoreChecksService.java?rev=1803501&view=auto
==============================================================================
--- jackrabbit/oak/trunk/oak-store-composite/src/main/java/org/apache/jackrabbit/oak/composite/checks/NodeStoreChecksService.java (added)
+++ jackrabbit/oak/trunk/oak-store-composite/src/main/java/org/apache/jackrabbit/oak/composite/checks/NodeStoreChecksService.java Mon Jul 31 10:02:06 2017
@@ -0,0 +1,108 @@
+/*
+ * 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.oak.composite.checks;
+
+import java.util.List;
+import java.util.concurrent.CopyOnWriteArrayList;
+
+import org.apache.felix.scr.annotations.Component;
+import org.apache.felix.scr.annotations.Reference;
+import org.apache.felix.scr.annotations.ReferenceCardinality;
+import org.apache.felix.scr.annotations.Service;
+import org.apache.jackrabbit.oak.api.Tree;
+import org.apache.jackrabbit.oak.composite.MountedNodeStore;
+import org.apache.jackrabbit.oak.plugins.tree.TreeFactory;
+import org.apache.jackrabbit.oak.spi.mount.Mount;
+import org.apache.jackrabbit.oak.spi.state.NodeStore;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+@Component
+@Service(NodeStoreChecks.class)
+public class NodeStoreChecksService implements NodeStoreChecks {
+    
+    private final Logger log = LoggerFactory.getLogger(getClass());
+    
+    @Reference(cardinality = ReferenceCardinality.OPTIONAL_MULTIPLE, 
+            bind = "bindChecker", 
+            unbind = "unbindChecker",
+            referenceInterface = MountedNodeStoreChecker.class)
+    private List<MountedNodeStoreChecker<?>> checkers = new CopyOnWriteArrayList<>();
+
+    // used by SCR
+    public NodeStoreChecksService() {
+        
+    }
+    
+    // visible for testing
+    public NodeStoreChecksService(List<MountedNodeStoreChecker<?>> checkers) {
+        this.checkers = checkers;
+    }
+
+    @Override
+    public void check(NodeStore globalStore, MountedNodeStore mountedStore) {
+        
+        ErrorHolder errorHolder = new ErrorHolder();
+        
+        checkers.forEach( c -> {
+            log.info("Checking NodeStore from mount {} with {}", mountedStore.getMount().getName(), c );
+            
+            check(mountedStore, errorHolder, globalStore, c);
+
+            log.info("Check complete");
+        });
+        
+        errorHolder.end();
+    }
+    
+    private <T> void check(MountedNodeStore mountedStore, ErrorHolder errorHolder, NodeStore globalStore,
+            MountedNodeStoreChecker<T> c) {
+        
+        T context = c.createContext(globalStore);
+        Tree mountRoot = TreeFactory.createReadOnlyTree(mountedStore.getNodeStore().getRoot());
+        
+        visit(mountRoot, mountedStore,  errorHolder, context, c);
+    }
+
+    private <T> void visit(Tree tree, MountedNodeStore mountedStore, ErrorHolder errorHolder, T context, MountedNodeStoreChecker<T> c) {
+        
+        
+        // a mounted NodeStore may contain more paths than what it owns, but since these are not accessible
+        // through the CompositeNodeStore skip them
+        Mount mount = mountedStore.getMount();
+        
+        boolean under = mount.isUnder(tree.getPath());
+        boolean mounted = mount.isMounted(tree.getPath());
+        
+        
+        if ( mounted ) {
+            c.check(mountedStore, tree, errorHolder, context);
+        }
+
+        if ( mounted || under ) {
+            tree.getChildren().forEach( child -> visit(child, mountedStore, errorHolder, context, c));
+        }
+    }    
+    
+    protected void bindChecker(MountedNodeStoreChecker<?> checker) {
+        checkers.add(checker);
+    }
+    
+    protected void unbindChecker(MountedNodeStoreChecker<?> checker) {
+        checkers.remove(checker);
+    }
+}

Added: jackrabbit/oak/trunk/oak-store-composite/src/main/java/org/apache/jackrabbit/oak/composite/checks/VersionableNodesMountedNodeStoreChecker.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-store-composite/src/main/java/org/apache/jackrabbit/oak/composite/checks/VersionableNodesMountedNodeStoreChecker.java?rev=1803501&view=auto
==============================================================================
--- jackrabbit/oak/trunk/oak-store-composite/src/main/java/org/apache/jackrabbit/oak/composite/checks/VersionableNodesMountedNodeStoreChecker.java (added)
+++ jackrabbit/oak/trunk/oak-store-composite/src/main/java/org/apache/jackrabbit/oak/composite/checks/VersionableNodesMountedNodeStoreChecker.java Mon Jul 31 10:02:06 2017
@@ -0,0 +1,67 @@
+/*
+ * 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.oak.composite.checks;
+
+import org.apache.felix.scr.annotations.Component;
+import org.apache.felix.scr.annotations.Service;
+import org.apache.jackrabbit.oak.api.Root;
+import org.apache.jackrabbit.oak.api.Tree;
+import org.apache.jackrabbit.oak.composite.MountedNodeStore;
+import org.apache.jackrabbit.oak.namepath.NamePathMapper;
+import org.apache.jackrabbit.oak.plugins.nodetype.ReadOnlyNodeTypeManager;
+import org.apache.jackrabbit.oak.plugins.tree.RootFactory;
+import org.apache.jackrabbit.oak.plugins.version.VersionConstants;
+import org.apache.jackrabbit.oak.spi.state.NodeStore;
+
+/**
+ * Checks that no <tt>versionable</tt> nodes are present in a non-default <tt>NodeStore</tt>
+ */
+@Component
+@Service(MountedNodeStoreChecker.class)
+public class VersionableNodesMountedNodeStoreChecker implements MountedNodeStoreChecker<VersionableNodesMountedNodeStoreChecker.Context> {
+
+    @Override
+    public Context createContext(NodeStore globalStore) {
+        
+        Root globalRoot = RootFactory.createReadOnlyRoot(globalStore.getRoot());
+        ReadOnlyNodeTypeManager typeManager = ReadOnlyNodeTypeManager.getInstance(globalRoot, NamePathMapper.DEFAULT);
+
+        return new Context(typeManager);
+    }
+    
+    @Override
+    public void check(MountedNodeStore mountedStore, Tree tree, ErrorHolder errorHolder, Context context) {
+        
+        if ( context.getTypeManager().isNodeType(tree, VersionConstants.MIX_VERSIONABLE) ) {
+            errorHolder.report(mountedStore, tree.getPath(), "versionable node");
+        }
+    }
+    
+    static class Context {
+        
+        private final ReadOnlyNodeTypeManager typeManager;
+        
+        Context(ReadOnlyNodeTypeManager typeManager) {
+            this.typeManager = typeManager;
+        }
+        
+        public ReadOnlyNodeTypeManager getTypeManager() {
+            return typeManager;
+        }
+    }
+
+}

Modified: jackrabbit/oak/trunk/oak-store-composite/src/test/java/org/apache/jackrabbit/oak/composite/CompositeNodeStoreServiceTest.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-store-composite/src/test/java/org/apache/jackrabbit/oak/composite/CompositeNodeStoreServiceTest.java?rev=1803501&r1=1803500&r2=1803501&view=diff
==============================================================================
--- jackrabbit/oak/trunk/oak-store-composite/src/test/java/org/apache/jackrabbit/oak/composite/CompositeNodeStoreServiceTest.java (original)
+++ jackrabbit/oak/trunk/oak-store-composite/src/test/java/org/apache/jackrabbit/oak/composite/CompositeNodeStoreServiceTest.java Mon Jul 31 10:02:06 2017
@@ -20,6 +20,7 @@ import static org.hamcrest.Matchers.notN
 import static org.hamcrest.Matchers.nullValue;
 import static org.junit.Assert.assertThat;
 
+import org.apache.jackrabbit.oak.composite.checks.NodeStoreChecksService;
 import org.apache.jackrabbit.oak.plugins.memory.MemoryNodeStore;
 import org.apache.jackrabbit.oak.spi.mount.MountInfoProvider;
 import org.apache.jackrabbit.oak.spi.mount.Mounts;
@@ -50,9 +51,11 @@ public class CompositeNodeStoreServiceTe
 		ctx.registerService(MountInfoProvider.class, mip);
 		ctx.registerService(NodeStoreProvider.class, new SimpleNodeStoreProvider(global), ImmutableMap.of("role", "composite:global", "registerDescriptors", Boolean.TRUE));
 		ctx.registerService(NodeStoreProvider.class, new SimpleNodeStoreProvider(mount), ImmutableMap.of("role", "composite:mount:libs"));
+		ctx.registerInjectActivateService(new NodeStoreChecksService());
 		
 		ctx.registerInjectActivateService(new CompositeNodeStoreService());
-		
+
+
 		assertThat("No NodeStore registered", ctx.getService(NodeStore.class), notNullValue());
 	}
 	
@@ -70,6 +73,7 @@ public class CompositeNodeStoreServiceTe
 		ctx.registerService(MountInfoProvider.class, mip);
 		ctx.registerService(NodeStoreProvider.class, new SimpleNodeStoreProvider(global), ImmutableMap.of("role", "composite:global", "registerDescriptors", Boolean.TRUE));
 		ctx.registerService(NodeStoreProvider.class, new SimpleNodeStoreProvider(mount), ImmutableMap.of("role", "composite:mount:libs"));
+		ctx.registerInjectActivateService(new NodeStoreChecksService());
 		
 		ctx.registerInjectActivateService(new CompositeNodeStoreService());
 		
@@ -88,6 +92,7 @@ public class CompositeNodeStoreServiceTe
 		
 		ctx.registerService(MountInfoProvider.class, mip);
 		ctx.registerService(NodeStoreProvider.class, new SimpleNodeStoreProvider(mount), ImmutableMap.of("role", "composite:mount:libs"));
+		ctx.registerInjectActivateService(new NodeStoreChecksService());
 		
 		ctx.registerInjectActivateService(new CompositeNodeStoreService());
 		

Added: jackrabbit/oak/trunk/oak-store-composite/src/test/java/org/apache/jackrabbit/oak/composite/checks/NodeStoreChecksServiceTest.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-store-composite/src/test/java/org/apache/jackrabbit/oak/composite/checks/NodeStoreChecksServiceTest.java?rev=1803501&view=auto
==============================================================================
--- jackrabbit/oak/trunk/oak-store-composite/src/test/java/org/apache/jackrabbit/oak/composite/checks/NodeStoreChecksServiceTest.java (added)
+++ jackrabbit/oak/trunk/oak-store-composite/src/test/java/org/apache/jackrabbit/oak/composite/checks/NodeStoreChecksServiceTest.java Mon Jul 31 10:02:06 2017
@@ -0,0 +1,106 @@
+/*
+ * 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.oak.composite.checks;
+
+import static com.google.common.base.Preconditions.checkNotNull;
+
+import java.util.Arrays;
+
+import org.apache.jackrabbit.oak.api.CommitFailedException;
+import org.apache.jackrabbit.oak.api.IllegalRepositoryStateException;
+import org.apache.jackrabbit.oak.api.Tree;
+import org.apache.jackrabbit.oak.composite.MountedNodeStore;
+import org.apache.jackrabbit.oak.plugins.memory.MemoryNodeStore;
+import org.apache.jackrabbit.oak.spi.commit.CommitInfo;
+import org.apache.jackrabbit.oak.spi.commit.EmptyHook;
+import org.apache.jackrabbit.oak.spi.mount.Mount;
+import org.apache.jackrabbit.oak.spi.mount.MountInfoProvider;
+import org.apache.jackrabbit.oak.spi.mount.Mounts;
+import org.apache.jackrabbit.oak.spi.state.NodeBuilder;
+import org.apache.jackrabbit.oak.spi.state.NodeStore;
+import org.junit.Before;
+import org.junit.Test;
+
+public class NodeStoreChecksServiceTest {
+
+    private MemoryNodeStore globalStore;
+    private MemoryNodeStore mountedStore;
+    private MountInfoProvider mip;
+    private Mount mount;
+
+    @Before
+    public void createFixture() throws CommitFailedException {
+        globalStore = new MemoryNodeStore();
+        mountedStore = new MemoryNodeStore();        
+
+        NodeBuilder rootBuilder = mountedStore.getRoot().builder();
+        rootBuilder.setChildNode("first").setChildNode("second").setChildNode("third");
+        rootBuilder.setChildNode("not-covered");
+        mountedStore.merge(rootBuilder, EmptyHook.INSTANCE, CommitInfo.EMPTY);
+        
+        mip = Mounts.newBuilder()
+                .readOnlyMount("first", "/first")
+                .build();
+        
+        mount = mip.getMountByName("first");
+    }
+    
+    @Test
+    public void noCheckers() throws CommitFailedException {
+
+        NodeStoreChecksService checks = new NodeStoreChecksService();
+        
+        checks.check(globalStore, new MountedNodeStore(mount, mountedStore));
+    }
+    
+    @Test(expected = IllegalRepositoryStateException.class)
+    public void failOnNodeCoveredByMount() {
+
+        NodeStoreChecksService checks = new NodeStoreChecksService(Arrays.asList(new FailOnTreeNameChecker("third")));
+        
+        checks.check(globalStore, new MountedNodeStore(mount, mountedStore));
+    }
+
+    @Test
+    public void doNotFailOnNodeNotCoveredByMount() {
+        
+        NodeStoreChecksService checks = new NodeStoreChecksService(Arrays.asList(new FailOnTreeNameChecker("not-covered")));
+        
+        checks.check(globalStore, new MountedNodeStore(mount, mountedStore));
+    }
+    
+    static class FailOnTreeNameChecker implements MountedNodeStoreChecker<Void> {
+        
+        private final String name;
+
+        private FailOnTreeNameChecker(String name) {
+            this.name = checkNotNull(name, "name shold not be null");
+        }
+
+        @Override
+        public Void createContext(NodeStore globalStore) {
+            return null;
+        }
+
+        @Override
+        public void check(MountedNodeStore mountedStore, Tree tree, ErrorHolder errorHolder, Void context) {
+            if ( name.equals(tree.getName()))
+                errorHolder.report(mountedStore, tree.getPath(), "test failure");
+        }
+        
+    }
+}