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");
+        }
+        
+    }
+}


Reply via email to