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 md...@apache.org on 2013/10/28 12:22:54 UTC

svn commit: r1536315 - in /jackrabbit/oak/trunk/oak-core/src: main/java/org/apache/jackrabbit/oak/plugins/observation/ main/java/org/apache/jackrabbit/oak/spi/state/ test/java/org/apache/jackrabbit/oak/

Author: mduerig
Date: Mon Oct 28 11:22:54 2013
New Revision: 1536315

URL: http://svn.apache.org/r1536315
Log:
OAK-144 Implement observation
Refactor to use Validator instead of RecursingNodeStateDiff

Added:
    jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/observation/SecurableValidator.java
    jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/observation/SecureValidator.java
      - copied, changed from r1536314, jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/observation/SecureNodeStateDiff.java
Removed:
    jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/observation/SecurableNodeStateDiff.java
    jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/observation/SecureNodeStateDiff.java
    jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/spi/state/RecursingNodeStateDiff.java
    jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/spi/state/VisibleDiff.java
Modified:
    jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/observation/ChangeProcessor.java
    jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/SecureNodeStateDiffTest.java

Modified: jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/observation/ChangeProcessor.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/observation/ChangeProcessor.java?rev=1536315&r1=1536314&r2=1536315&view=diff
==============================================================================
--- jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/observation/ChangeProcessor.java (original)
+++ jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/observation/ChangeProcessor.java Mon Oct 28 11:22:54 2013
@@ -35,13 +35,11 @@ import java.util.Iterator;
 import java.util.List;
 import java.util.concurrent.atomic.AtomicReference;
 
-import javax.annotation.Nonnull;
 import javax.jcr.observation.Event;
 import javax.jcr.observation.EventListener;
 
 import com.google.common.base.Function;
 import com.google.common.collect.Iterators;
-
 import org.apache.jackrabbit.api.jmx.EventListenerMBean;
 import org.apache.jackrabbit.commons.iterator.EventIteratorAdapter;
 import org.apache.jackrabbit.commons.observation.ListenerTracker;
@@ -55,9 +53,10 @@ import org.apache.jackrabbit.oak.namepat
 import org.apache.jackrabbit.oak.plugins.observation.ChangeDispatcher.ChangeSet;
 import org.apache.jackrabbit.oak.plugins.observation.ChangeDispatcher.Listener;
 import org.apache.jackrabbit.oak.spi.commit.CommitInfo;
+import org.apache.jackrabbit.oak.spi.commit.DefaultValidator;
+import org.apache.jackrabbit.oak.spi.commit.Validator;
+import org.apache.jackrabbit.oak.spi.commit.VisibleValidator;
 import org.apache.jackrabbit.oak.spi.state.NodeState;
-import org.apache.jackrabbit.oak.spi.state.RecursingNodeStateDiff;
-import org.apache.jackrabbit.oak.spi.state.VisibleDiff;
 import org.apache.jackrabbit.oak.spi.whiteboard.Registration;
 import org.apache.jackrabbit.oak.spi.whiteboard.Whiteboard;
 import org.apache.jackrabbit.oak.spi.whiteboard.WhiteboardUtils;
@@ -171,7 +170,8 @@ public class ChangeProcessor {
                         ImmutableTree afterTree = getTree(changes.getAfterState(), path);
                         EventGeneratingNodeStateDiff diff = new EventGeneratingNodeStateDiff(
                                 changes.getCommitInfo(), beforeTree, afterTree);
-                        SecureNodeStateDiff.compare(VisibleDiff.wrap(diff), beforeTree, afterTree);
+                        VisibleValidator visibleDiff = new VisibleValidator(diff, true, true);
+                        SecureValidator.compare(beforeTree, afterTree, visibleDiff);
                         if (!stopping) {
                             diff.sendEvents();
                         }
@@ -188,7 +188,7 @@ public class ChangeProcessor {
 
     }
 
-    private class EventGeneratingNodeStateDiff extends RecursingNodeStateDiff {
+    private class EventGeneratingNodeStateDiff extends DefaultValidator {
         public static final int EVENT_LIMIT = 8192;
 
         private final String userId;
@@ -252,34 +252,31 @@ public class ChangeProcessor {
         }
 
         @Override
-        public boolean propertyAdded(PropertyState after) {
+        public void propertyAdded(PropertyState after) {
             if (filterRef.get().include(PROPERTY_ADDED, afterTree)) {
                 Event event = generatePropertyEvent(PROPERTY_ADDED, afterTree, after);
                 events.add(singletonIterator(event));
             }
-            return !stopping;
         }
 
         @Override
-        public boolean propertyChanged(PropertyState before, PropertyState after) {
+        public void propertyChanged(PropertyState before, PropertyState after) {
             if (filterRef.get().include(Event.PROPERTY_CHANGED, afterTree)) {
                 Event event = generatePropertyEvent(Event.PROPERTY_CHANGED, afterTree, after);
                 events.add(singletonIterator(event));
             }
-            return !stopping;
         }
 
         @Override
-        public boolean propertyDeleted(PropertyState before) {
+        public void propertyDeleted(PropertyState before) {
             if (filterRef.get().include(PROPERTY_REMOVED, afterTree)) {
                 Event event = generatePropertyEvent(PROPERTY_REMOVED, beforeTree, before);
                 events.add(singletonIterator(event));
             }
-            return !stopping;
         }
 
         @Override
-        public boolean childNodeAdded(String name, NodeState after) {
+        public Validator childNodeAdded(String name, NodeState after) {
             if (filterRef.get().includeChildren(afterTree.getPath())) {
                 Iterator<Event> events = generateNodeEvents(
                         NODE_ADDED, afterTree.getChild(name));
@@ -288,33 +285,25 @@ public class ChangeProcessor {
                     sendEvents();
                 }
             }
-            return !stopping;
+            return null;
         }
 
         @Override
-        public boolean childNodeDeleted(String name, NodeState before) {
+        public Validator childNodeDeleted(String name, NodeState before) {
             if (filterRef.get().includeChildren(beforeTree.getPath())) {
                 Iterator<Event> events = generateNodeEvents(
                         NODE_REMOVED, beforeTree.getChild(name));
                 this.events.add(events);
             }
-            return !stopping;
+            return null;
         }
 
         @Override
-        public boolean childNodeChanged(String name, NodeState before, NodeState after) {
-            return !stopping;
-        }
-
-        @Nonnull
-        @Override
-        public RecursingNodeStateDiff createChildDiff(String name, NodeState before, NodeState after) {
-            if (filterRef.get().includeChildren(afterTree.getPath())) {
-                EventGeneratingNodeStateDiff diff =
-                        new EventGeneratingNodeStateDiff(this, name);
-                return VisibleDiff.wrap(diff);
+        public Validator childNodeChanged(String name, NodeState before, NodeState after) {
+            if (stopping || !filterRef.get().includeChildren(afterTree.getPath())) {
+                return null;
             } else {
-                return RecursingNodeStateDiff.EMPTY;
+                return new EventGeneratingNodeStateDiff(this, name);
             }
         }
 

Added: jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/observation/SecurableValidator.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/observation/SecurableValidator.java?rev=1536315&view=auto
==============================================================================
--- jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/observation/SecurableValidator.java (added)
+++ jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/observation/SecurableValidator.java Mon Oct 28 11:22:54 2013
@@ -0,0 +1,195 @@
+/*
+ * 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.plugins.observation;
+
+import javax.annotation.CheckForNull;
+import javax.annotation.Nonnull;
+
+import org.apache.jackrabbit.oak.api.CommitFailedException;
+import org.apache.jackrabbit.oak.api.PropertyState;
+import org.apache.jackrabbit.oak.api.Tree;
+import org.apache.jackrabbit.oak.spi.commit.Validator;
+import org.apache.jackrabbit.oak.spi.state.NodeState;
+
+/**
+ * Base class for {@code Validator} implementations that can be secured.
+ * That is, with exception of {@link Validator#childNodeChanged(String, NodeState, NodeState)},
+ * the call back methods of the wrapped validator are only called when its receiver has sufficient
+ * rights to access the respective items.
+ * <p>
+ * Implementors must implement the {@link #create(Tree, Tree, Validator)} factory method for
+ * creating {@code SecurableValidator} instances. Further implementors should override
+ * {@link #canRead(Tree, PropertyState, Tree, PropertyState)} and {@link #canRead(Tree, Tree)}
+ * to determine whether the passed states are accessible. Finally implementors should override,
+ * {@link #secureBefore(String, NodeState)}, and {@link #secureAfter(String, NodeState)}} wrapping
+ * the passed node state into a node state that restricts access to accessible child nodes and
+ * properties.
+ */
+public abstract class SecurableValidator implements Validator {
+
+    /**
+     * Base tree against which the validation is performed
+     */
+    private final Tree beforeTree;
+
+    /**
+     * Changed tree against which the validation is performed
+     */
+    private final Tree afterTree;
+
+    /**
+     * Validator receiving call backs for accessible items
+     */
+    private final Validator validator;
+
+    /**
+     * Create a new instance wrapping the passed {@code validator}.
+     * @param beforeTree base tree against which the validation is performed
+     * @param afterTree  changed tree against which the validation is performed
+     * @param validator  the wrapped validator
+     */
+    protected SecurableValidator(Tree beforeTree, Tree afterTree, Validator validator) {
+        this.beforeTree = beforeTree;
+        this.afterTree = afterTree;
+        this.validator = validator;
+    }
+
+    /**
+     * Factory method for creating {@code SecurableValidator} instances of the concrete sub type.
+     * @return  a new {@code SecurableValidator}
+     */
+    @CheckForNull
+    protected abstract SecurableValidator create(Tree beforeTree, Tree afterTree,
+            Validator secureValidator);
+
+    /**
+     * Determine whether a property is accessible
+     * @param beforeParent parent before the changes
+     * @param before  before state of the property
+     * @param afterParent parent after the changes
+     * @param after   after state of the property
+     * @return  {@code true} if accessible, {@code false} otherwise.
+     */
+    protected boolean canRead(Tree beforeParent, PropertyState before, Tree afterParent,
+            PropertyState after) {
+        return true;
+    }
+
+    /**
+     * Determine whether a node is accessible
+     * @param before  before state of the node
+     * @param after   after state of the node
+     * @return  {@code true} if accessible, {@code false} otherwise.
+     */
+    protected boolean canRead(Tree before, Tree after) {
+        return true;
+    }
+
+    /**
+     * Secure the before state of a child node such that it only provides
+     * accessible child nodes and properties.
+     * @param name       name of the child node
+     * @param nodeState  before state of the child node
+     * @return  secured before state
+     */
+    @Nonnull
+    protected NodeState secureBefore(String name, NodeState nodeState) {
+        return nodeState;
+    }
+
+    /**
+     * Secure the after state of a child node such that it only provides
+     * accessible child nodes and properties.
+     * @param name       name of the child node
+     * @param nodeState  after state of the child node
+     * @return  secured after state
+     */
+    @Nonnull
+    protected NodeState secureAfter(String name, NodeState nodeState) {
+        return nodeState;
+    }
+
+    //------------------------------------------------------------< Validator >---
+
+    @Override
+    public void enter(NodeState before, NodeState after) throws CommitFailedException {
+    }
+
+    @Override
+    public void leave(NodeState before, NodeState after) throws CommitFailedException {
+    }
+
+    @Override
+    public void propertyAdded(PropertyState after) throws CommitFailedException {
+        if (canRead(beforeTree, null, afterTree, after)) {
+            validator.propertyAdded(after);
+        }
+    }
+
+    @Override
+    public void propertyChanged(PropertyState before, PropertyState after)
+            throws CommitFailedException {
+        if (canRead(beforeTree, before, afterTree, after)) {
+            validator.propertyChanged(before, after);
+        }
+    }
+
+    @Override
+    public void propertyDeleted(PropertyState before) throws CommitFailedException {
+        if (canRead(beforeTree, before, afterTree, null)) {
+            validator.propertyDeleted(before);
+        }
+    }
+
+    @Override
+    public Validator childNodeAdded(String name, NodeState after) throws CommitFailedException {
+        if (canRead(beforeTree.getChild(name), afterTree.getChild(name))) {
+            Validator childValidator = validator.childNodeAdded(name, secureAfter(name, after));
+            return childValidator == null
+                ? null
+                : create(beforeTree.getChild(name), afterTree.getChild(name), childValidator);
+        } else {
+            return null;
+        }
+    }
+
+    @Override
+    public Validator childNodeChanged(String name, NodeState before, NodeState after)
+            throws CommitFailedException {
+        Validator childValidator = validator.childNodeChanged(
+                name, secureBefore(name, before), secureAfter(name, after));
+        return childValidator == null
+            ? null
+            : create(beforeTree.getChild(name), afterTree.getChild(name), childValidator);
+    }
+
+    @Override
+    public Validator childNodeDeleted(String name, NodeState before) throws CommitFailedException {
+        if (canRead(beforeTree.getChild(name), afterTree.getChild(name))) {
+            Validator childValidator = validator.childNodeDeleted(name, secureBefore(name, before));
+            return childValidator == null
+                ? null
+                : create(beforeTree.getChild(name), afterTree.getChild(name), childValidator);
+        } else {
+            return null;
+        }
+    }
+
+}

Copied: jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/observation/SecureValidator.java (from r1536314, jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/observation/SecureNodeStateDiff.java)
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/observation/SecureValidator.java?p2=jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/observation/SecureValidator.java&p1=jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/observation/SecureNodeStateDiff.java&r1=1536314&r2=1536315&rev=1536315&view=diff
==============================================================================
--- jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/observation/SecureNodeStateDiff.java (original)
+++ jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/observation/SecureValidator.java Mon Oct 28 11:22:54 2013
@@ -19,37 +19,37 @@
 
 package org.apache.jackrabbit.oak.plugins.observation;
 
-import static org.apache.jackrabbit.oak.spi.state.NodeStateUtils.isHidden;
-
+import org.apache.jackrabbit.oak.api.CommitFailedException;
 import org.apache.jackrabbit.oak.api.PropertyState;
 import org.apache.jackrabbit.oak.api.Tree;
 import org.apache.jackrabbit.oak.core.ImmutableTree;
+import org.apache.jackrabbit.oak.spi.commit.EditorDiff;
+import org.apache.jackrabbit.oak.spi.commit.Validator;
 import org.apache.jackrabbit.oak.spi.state.NodeState;
-import org.apache.jackrabbit.oak.spi.state.RecursingNodeStateDiff;
-
-public class SecureNodeStateDiff extends SecurableNodeStateDiff {
-    private SecureNodeStateDiff(RecursingNodeStateDiff diff, Tree before, Tree after) {
-        super(diff, before, after);
-    }
 
-    private SecureNodeStateDiff(SecurableNodeStateDiff parent, Tree beforeParent, Tree afterParent, String name) {
-        super(parent, beforeParent, afterParent, name);
+public class SecureValidator extends SecurableValidator {
+    private SecureValidator(Tree before, Tree after, Validator diff) {
+        super(before, after, diff);
     }
 
-    public static void compare(RecursingNodeStateDiff diff, ImmutableTree before, ImmutableTree after) {
-        SecureNodeStateDiff secureDiff = new SecureNodeStateDiff(diff, before, after);
-        after.getNodeState().compareAgainstBaseState(before.getNodeState(), secureDiff);
+    public static void compare(ImmutableTree before, ImmutableTree after, Validator diff)
+            throws CommitFailedException {
+        SecureValidator secureValidator = new SecureValidator(before, after, diff);
+        CommitFailedException exception = EditorDiff.process(
+                secureValidator, before.getNodeState(), after.getNodeState());
+        if (exception != null) {
+            throw exception;
+        }
     }
 
     @Override
-    protected SecurableNodeStateDiff create(SecurableNodeStateDiff parent,
-            String name, NodeState before, NodeState after) {
-
-        return isHidden(name) ? null : new SecureNodeStateDiff(parent, beforeTree, afterTree, name);
+    protected SecurableValidator create(Tree beforeTree, Tree afterTree, Validator secureValidator) {
+        return new SecureValidator(beforeTree, afterTree, secureValidator);
     }
 
     @Override
-    protected boolean canRead(Tree beforeParent, PropertyState before, Tree afterParent, PropertyState after) {
+    protected boolean canRead(Tree beforeParent, PropertyState before, Tree afterParent,
+            PropertyState after) {
         // TODO implement canRead
         return true;
     }

Modified: jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/SecureNodeStateDiffTest.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/SecureNodeStateDiffTest.java?rev=1536315&r1=1536314&r2=1536315&view=diff
==============================================================================
--- jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/SecureNodeStateDiffTest.java (original)
+++ jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/SecureNodeStateDiffTest.java Mon Oct 28 11:22:54 2013
@@ -22,14 +22,16 @@ package org.apache.jackrabbit.oak;
 import static org.apache.jackrabbit.oak.plugins.memory.EmptyNodeState.EMPTY_NODE;
 import static org.junit.Assert.assertEquals;
 
+import org.apache.jackrabbit.oak.api.CommitFailedException;
 import org.apache.jackrabbit.oak.api.PropertyState;
 import org.apache.jackrabbit.oak.api.Tree;
 import org.apache.jackrabbit.oak.core.ImmutableTree;
-import org.apache.jackrabbit.oak.plugins.observation.SecurableNodeStateDiff;
+import org.apache.jackrabbit.oak.plugins.observation.SecurableValidator;
+import org.apache.jackrabbit.oak.spi.commit.DefaultValidator;
+import org.apache.jackrabbit.oak.spi.commit.EditorDiff;
+import org.apache.jackrabbit.oak.spi.commit.Validator;
 import org.apache.jackrabbit.oak.spi.state.NodeBuilder;
 import org.apache.jackrabbit.oak.spi.state.NodeState;
-import org.apache.jackrabbit.oak.spi.state.NodeStateDiff;
-import org.apache.jackrabbit.oak.spi.state.RecursingNodeStateDiff;
 import org.junit.Before;
 import org.junit.Test;
 
@@ -52,89 +54,86 @@ public class SecureNodeStateDiffTest {
     }
 
     @Test
-    public void testRemoveNode() {
+    public void testRemoveNode() throws CommitFailedException {
         NodeBuilder builder = base.builder();
         builder.child("x").remove();
         new AssertingNodeStateDiff(base, builder.getNodeState()).expect("-x");
     }
 
     @Test
-    public void testAddNode() {
+    public void testAddNode() throws CommitFailedException {
         NodeBuilder builder = base.builder();
         builder.child("v");
         new AssertingNodeStateDiff(base, builder.getNodeState()).expect("+v");
     }
 
     @Test
-    public void testRemoveProperty() {
+    public void testRemoveProperty() throws CommitFailedException {
         NodeBuilder builder = base.builder();
         builder.removeProperty("a");
         new AssertingNodeStateDiff(base, builder.getNodeState()).expect("-a");
     }
 
     @Test
-    public void testAddProperty() {
+    public void testAddProperty() throws CommitFailedException {
         NodeBuilder builder = base.builder();
         builder.setProperty("d", "d");
         new AssertingNodeStateDiff(base, builder.getNodeState()).expect("+d");
     }
 
     @Test
-    public void testChangeProperty() {
+    public void testChangeProperty() throws CommitFailedException {
         NodeBuilder builder = base.builder();
         builder.setProperty("c", 42);
         new AssertingNodeStateDiff(base, builder.getNodeState()).expect("^c");
     }
 
     @Test
-    public void testChangeNode() {
+    public void testChangeNode() throws CommitFailedException {
         NodeBuilder builder = base.builder();
         builder.child("NA1").setProperty("p", "p");
-        new AssertingNodeStateDiff(base, builder.getNodeState()).expect("^NA1+p");
+        new AssertingNodeStateDiff(base, builder.getNodeState()).expect("+p");
     }
 
     @Test
-    public void testAddInaccessibleChild() {
+    public void testAddInaccessibleChild() throws CommitFailedException {
         NodeBuilder builder = base.builder();
         builder.child("NA3").child("x3").child("NA3a").child("y3").child("NA3a");
         new AssertingNodeStateDiff(base, builder.getNodeState()).expect("");
     }
 
     @Test
-    public void testChangeInaccessibleChild() {
+    public void testChangeInaccessibleChild() throws CommitFailedException {
         NodeBuilder builder = base.builder();
         builder.child("NA3").child("x3").child("NA3a").child("y3").remove();
-        new AssertingNodeStateDiff(base, builder.getNodeState()).expect("^NA3^x3^NA3a-y3");
+        new AssertingNodeStateDiff(base, builder.getNodeState()).expect("-y3");
     }
 
     @Test
-    public void testRemoveInaccessibleChild() {
+    public void testRemoveInaccessibleChild() throws CommitFailedException {
         NodeBuilder builder = base.builder();
         builder.child("NA3").child("x3").child("NA3a").remove();
         new AssertingNodeStateDiff(base, builder.getNodeState()).expect("");
     }
 
-    private static class SecureNodeStateDiff extends SecurableNodeStateDiff {
-        protected SecureNodeStateDiff(SecurableNodeStateDiff parent, Tree beforeParent, Tree afterParent, String name) {
-            super(parent, beforeParent, afterParent, name);
+    private static class SecureValidator extends SecurableValidator {
+        public static SecureValidator wrap(Tree before, Tree after, Validator secureValidator) {
+            return new SecureValidator(before, after, secureValidator);
         }
 
-        public static NodeStateDiff wrap(RecursingNodeStateDiff diff, Tree before, Tree after) {
-            return new SecureNodeStateDiff(diff, before, after);
-        }
-
-        private SecureNodeStateDiff(RecursingNodeStateDiff diff, Tree before, Tree after) {
-            super(diff, before, after);
+        private SecureValidator(Tree before, Tree after, Validator secureValidator) {
+            super(before, after, secureValidator);
         }
 
         @Override
-        protected SecurableNodeStateDiff create(SecurableNodeStateDiff parent,
-                String name, NodeState before, NodeState after) {
-            return new SecureNodeStateDiff(parent, beforeTree, afterTree, name);
+        protected SecurableValidator create(Tree beforeTree, Tree afterTree,
+                Validator secureValidator) {
+            return wrap(beforeTree, afterTree, secureValidator);
         }
 
         @Override
-        protected boolean canRead(Tree beforeParent, PropertyState before, Tree afterParent, PropertyState after) {
+        protected boolean canRead(Tree beforeParent, PropertyState before, Tree afterParent,
+                PropertyState after) {
             return canRead(before) && canRead(after);
         }
 
@@ -152,7 +151,7 @@ public class SecureNodeStateDiffTest {
         }
     }
 
-    private static class AssertingNodeStateDiff extends RecursingNodeStateDiff {
+    private static class AssertingNodeStateDiff extends DefaultValidator {
         private final StringBuilder actual = new StringBuilder();
         private final NodeState before;
         private final NodeState after;
@@ -162,46 +161,46 @@ public class SecureNodeStateDiffTest {
             this.after = after;
         }
 
-        public void expect(String expected) {
-            NodeStateDiff secureDiff = SecureNodeStateDiff.wrap(this, new ImmutableTree(before), new ImmutableTree(after));
-            after.compareAgainstBaseState(before, secureDiff);
+        public void expect(String expected) throws CommitFailedException {
+            SecureValidator secureDiff = SecureValidator.wrap(
+                    new ImmutableTree(before), new ImmutableTree(after), this);
+            CommitFailedException exception = EditorDiff.process(secureDiff, before, after);
+            if (exception != null) {
+                throw exception;
+            }
             assertEquals(expected, actual.toString());
         }
 
         @Override
-        public boolean propertyAdded(PropertyState after) {
+        public void propertyAdded(PropertyState after) {
             actual.append('+').append(after.getName());
-            return true;
         }
 
         @Override
-        public boolean propertyChanged(PropertyState before, PropertyState after) {
+        public void propertyChanged(PropertyState before, PropertyState after) {
             actual.append('^').append(after.getName());
-            return true;
         }
 
         @Override
-        public boolean propertyDeleted(PropertyState before) {
+        public void propertyDeleted(PropertyState before) {
             actual.append('-').append(before.getName());
-            return true;
         }
 
         @Override
-        public boolean childNodeAdded(String name, NodeState after) {
+        public Validator childNodeAdded(String name, NodeState after) {
             actual.append('+').append(name);
-            return true;
+            return this;
         }
 
         @Override
-        public boolean childNodeChanged(String name, NodeState before, NodeState after) {
-            actual.append('^').append(name);
-            return true;
+        public Validator childNodeChanged(String name, NodeState before, NodeState after) {
+            return this;
         }
 
         @Override
-        public boolean childNodeDeleted(String name, NodeState before) {
+        public Validator childNodeDeleted(String name, NodeState before) {
             actual.append('-').append(name);
-            return true;
+            return this;
         }
     }
 }