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/08/02 12:19:07 UTC

svn commit: r1803812 - 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/checks/ oak-store-composite/src/test/java/org/apache/jackrabbit/oak/composi...

Author: rombert
Date: Wed Aug  2 12:19:07 2017
New Revision: 1803812

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

Create an  OSGi service that performs checks based on node types for a
certain tree. The service allow skipping certain node types from the
check, to allow transitioning existing instances in a more streamlined
way.

Added:
    jackrabbit/oak/trunk/oak-store-composite/src/main/java/org/apache/jackrabbit/oak/composite/checks/NodeTypeMountedNodeStoreChecker.java
    jackrabbit/oak/trunk/oak-store-composite/src/test/java/org/apache/jackrabbit/oak/composite/checks/NodeTypeMountedNodeStoreCheckerTest.java
Removed:
    jackrabbit/oak/trunk/oak-store-composite/src/main/java/org/apache/jackrabbit/oak/composite/checks/VersionableNodesMountedNodeStoreChecker.java
Modified:
    jackrabbit/oak/trunk/oak-it/src/test/java/org/apache/jackrabbit/oak/composite/CompositeNodeStoreBuilderTest.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=1803812&r1=1803811&r2=1803812&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 Wed Aug  2 12:19:07 2017
@@ -25,7 +25,7 @@ import org.apache.jackrabbit.oak.api.Com
 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.composite.checks.NodeTypeMountedNodeStoreChecker;
 import org.apache.jackrabbit.oak.plugins.memory.MemoryNodeStore;
 import org.apache.jackrabbit.oak.plugins.memory.PropertyStates;
 import org.apache.jackrabbit.oak.spi.commit.CommitInfo;
@@ -115,7 +115,7 @@ public class CompositeNodeStoreBuilderTe
 
         new CompositeNodeStore.Builder(mip, root)
             .addMount("readOnly", mount)
-            .with(new NodeStoreChecksService(Collections.singletonList(new VersionableNodesMountedNodeStoreChecker())))
+            .with(new NodeStoreChecksService(Collections.singletonList(new NodeTypeMountedNodeStoreChecker(JcrConstants.MIX_VERSIONABLE, "test error"))))
             .build();        
         
     }

Added: jackrabbit/oak/trunk/oak-store-composite/src/main/java/org/apache/jackrabbit/oak/composite/checks/NodeTypeMountedNodeStoreChecker.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-store-composite/src/main/java/org/apache/jackrabbit/oak/composite/checks/NodeTypeMountedNodeStoreChecker.java?rev=1803812&view=auto
==============================================================================
--- jackrabbit/oak/trunk/oak-store-composite/src/main/java/org/apache/jackrabbit/oak/composite/checks/NodeTypeMountedNodeStoreChecker.java (added)
+++ jackrabbit/oak/trunk/oak-store-composite/src/main/java/org/apache/jackrabbit/oak/composite/checks/NodeTypeMountedNodeStoreChecker.java Wed Aug  2 12:19:07 2017
@@ -0,0 +1,129 @@
+/*
+ * 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.Set;
+
+import org.apache.felix.scr.annotations.Component;
+import org.apache.felix.scr.annotations.ConfigurationPolicy;
+import org.apache.felix.scr.annotations.Property;
+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.commons.PropertiesUtil;
+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.spi.state.NodeStore;
+import org.osgi.service.component.ComponentContext;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import com.google.common.collect.ImmutableSet;
+
+@Component(configurationFactory=true, 
+    policy = ConfigurationPolicy.REQUIRE)
+@Service(MountedNodeStoreChecker.class)
+public class NodeTypeMountedNodeStoreChecker implements 
+        MountedNodeStoreChecker<NodeTypeMountedNodeStoreChecker.Context>  {
+    
+    private final Logger log = LoggerFactory.getLogger(getClass());
+    
+    @Property(label = "The name of a node type that is invalid and will be rejected when found")
+    private static final String INVALID_NODE_TYPE = "invalidNodeType";
+    @Property(label = "The error label to use when rejecting an invalid node type")
+    private static final String ERROR_LABEL = "errorLabel";
+    
+    @Property(label="Node types that will cause the check to succeeed, even in the invalid node type is also found.",
+            cardinality = Integer.MAX_VALUE)
+    private static final String EXCLUDED_NODE_TYPES = "excludedNodeTypes";
+
+    private String invalidNodeType;
+    private String errorLabel;
+    private Set<String> excludedNodeTypes;
+    
+    // used by SCR
+    public NodeTypeMountedNodeStoreChecker() {
+
+    }
+    
+    // visible for testing
+    public NodeTypeMountedNodeStoreChecker(String invalidNodeType, String errorLabel, String... excludedNodeTypes) {
+        this.invalidNodeType = invalidNodeType;
+        this.errorLabel = errorLabel;
+        this.excludedNodeTypes = ImmutableSet.copyOf(excludedNodeTypes);
+    }
+
+    protected void activate(ComponentContext ctx) {
+        invalidNodeType = checkNotNull(PropertiesUtil.toString(ctx.getProperties().get(INVALID_NODE_TYPE), null), INVALID_NODE_TYPE);
+        errorLabel = checkNotNull(PropertiesUtil.toString(ctx.getProperties().get(ERROR_LABEL), null), ERROR_LABEL);
+        excludedNodeTypes = ImmutableSet.copyOf(PropertiesUtil.toStringArray(ctx.getProperties().get(EXCLUDED_NODE_TYPES), new String[0]));
+    }
+
+    @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, invalidNodeType) &&
+                !isExcluded(mountedStore, tree, context) ) {
+            errorHolder.report(mountedStore, tree.getPath(), errorLabel);
+        }
+    }
+
+    private boolean isExcluded(MountedNodeStore mountedStore, Tree tree, Context context) {
+
+        for ( String excludedNodeType : excludedNodeTypes ) {
+            if ( context.getTypeManager().isNodeType(tree, excludedNodeType ) ) {
+                log.warn("Not failing check for tree at path {}, mount {} due to matching excluded node type {}", 
+                        tree.getPath(), mountedStore.getMount().getName(), excludedNodeType);
+                return true;
+            }
+        }
+        return false;
+    }
+    
+    @Override
+    public String toString() {
+        return getClass().getName()+ ": [ invalidNodeType: " + invalidNodeType + 
+                ", excludedNodeTypes: " + excludedNodeTypes + " ]";
+    }
+
+    protected static class Context {
+
+        private final ReadOnlyNodeTypeManager typeManager;
+
+        Context(ReadOnlyNodeTypeManager typeManager) {
+            this.typeManager = typeManager;
+        }
+
+        public ReadOnlyNodeTypeManager getTypeManager() {
+            return typeManager;
+        }
+    }
+
+}
\ No newline at end of file

Added: jackrabbit/oak/trunk/oak-store-composite/src/test/java/org/apache/jackrabbit/oak/composite/checks/NodeTypeMountedNodeStoreCheckerTest.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-store-composite/src/test/java/org/apache/jackrabbit/oak/composite/checks/NodeTypeMountedNodeStoreCheckerTest.java?rev=1803812&view=auto
==============================================================================
--- jackrabbit/oak/trunk/oak-store-composite/src/test/java/org/apache/jackrabbit/oak/composite/checks/NodeTypeMountedNodeStoreCheckerTest.java (added)
+++ jackrabbit/oak/trunk/oak-store-composite/src/test/java/org/apache/jackrabbit/oak/composite/checks/NodeTypeMountedNodeStoreCheckerTest.java Wed Aug  2 12:19:07 2017
@@ -0,0 +1,80 @@
+package org.apache.jackrabbit.oak.composite.checks;
+
+import java.util.Collections;
+import java.util.UUID;
+
+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.MountedNodeStore;
+import org.apache.jackrabbit.oak.composite.checks.NodeTypeMountedNodeStoreChecker.Context;
+import org.apache.jackrabbit.oak.plugins.memory.MemoryNodeStore;
+import org.apache.jackrabbit.oak.plugins.memory.PropertyStates;
+import org.apache.jackrabbit.oak.plugins.tree.TreeFactory;
+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 NodeTypeMountedNodeStoreCheckerTest {
+
+    @Test(expected = IllegalRepositoryStateException.class)
+    public void referenceableNodeIsDetected() throws CommitFailedException {
+        
+        MemoryNodeStore root = new MemoryNodeStore();
+        MemoryNodeStore mount = new MemoryNodeStore();
+        
+        NodeBuilder builder = mount.getRoot().builder();
+        builder.child("first")
+            .setProperty(PropertyStates.createProperty(JcrConstants.JCR_MIXINTYPES, 
+                Collections.singletonList(JcrConstants.MIX_REFERENCEABLE), Type.NAMES))
+            .setProperty(JcrConstants.JCR_UUID, UUID.randomUUID().toString());
+        
+        mount.merge(builder, EmptyHook.INSTANCE, CommitInfo.EMPTY);
+        
+        MountInfoProvider mip = Mounts.newBuilder()
+                .readOnlyMount("first", "/first")
+                .build();
+        
+        NodeTypeMountedNodeStoreChecker checker = new NodeTypeMountedNodeStoreChecker(JcrConstants.MIX_REFERENCEABLE, "test error");
+        Context context = checker.createContext(root);
+        ErrorHolder errorHolder = new ErrorHolder();
+        
+        checker.check(new MountedNodeStore(mip.getMountByName("first"), mount), TreeFactory.createReadOnlyTree(mount.getRoot()).getChild("first"), errorHolder, context);
+        
+        errorHolder.end();
+    }
+    
+    @Test
+    public void referenceableNodeInWhitelistIsSkipped() throws CommitFailedException {
+        
+        MemoryNodeStore root = new MemoryNodeStore();
+        MemoryNodeStore mount = new MemoryNodeStore();
+        
+        NodeBuilder builder = mount.getRoot().builder();
+        builder.child("first")
+        .setProperty(PropertyStates.createProperty(JcrConstants.JCR_PRIMARYTYPE, 
+                JcrConstants.NT_RESOURCE, Type.NAME))
+        .setProperty(PropertyStates.createProperty(JcrConstants.JCR_MIXINTYPES, 
+                Collections.singletonList(JcrConstants.MIX_REFERENCEABLE), Type.NAMES))
+        .setProperty(JcrConstants.JCR_UUID, UUID.randomUUID().toString());
+        
+        mount.merge(builder, EmptyHook.INSTANCE, CommitInfo.EMPTY);
+        
+        MountInfoProvider mip = Mounts.newBuilder()
+                .readOnlyMount("first", "/first")
+                .build();
+        
+        NodeTypeMountedNodeStoreChecker checker = new NodeTypeMountedNodeStoreChecker(JcrConstants.MIX_REFERENCEABLE, "test error", 
+                JcrConstants.NT_RESOURCE);
+        Context context = checker.createContext(root);
+        ErrorHolder errorHolder = new ErrorHolder();
+        
+        checker.check(new MountedNodeStore(mip.getMountByName("first"), mount), TreeFactory.createReadOnlyTree(mount.getRoot()).getChild("first"), errorHolder, context);
+        
+        errorHolder.end();
+    }
+}