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 ju...@apache.org on 2012/07/25 15:39:46 UTC

svn commit: r1365575 - in /jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak: plugins/memory/MemoryNodeState.java plugins/memory/MemoryNodeStateBuilder.java spi/state/AbstractNodeState.java spi/state/NodeState.java

Author: jukka
Date: Wed Jul 25 13:39:45 2012
New Revision: 1365575

URL: http://svn.apache.org/viewvc?rev=1365575&view=rev
Log:
OAK-167: Caching NodeStore implementation

Better handling of the internal state in MemoryNodeStateBuilder

Modified:
    jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/memory/MemoryNodeState.java
    jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/memory/MemoryNodeStateBuilder.java
    jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/spi/state/AbstractNodeState.java
    jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/spi/state/NodeState.java

Modified: jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/memory/MemoryNodeState.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/memory/MemoryNodeState.java?rev=1365575&r1=1365574&r2=1365575&view=diff
==============================================================================
--- jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/memory/MemoryNodeState.java (original)
+++ jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/memory/MemoryNodeState.java Wed Jul 25 13:39:45 2012
@@ -76,6 +76,11 @@ public class MemoryNodeState extends Abs
     }
 
     @Override
+    public boolean hasChildNode(String name) {
+        return nodes.containsKey(name);
+    }
+
+    @Override
     public NodeState getChildNode(String name) {
         return nodes.get(name);
     }

Modified: jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/memory/MemoryNodeStateBuilder.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/memory/MemoryNodeStateBuilder.java?rev=1365575&r1=1365574&r2=1365575&view=diff
==============================================================================
--- jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/memory/MemoryNodeStateBuilder.java (original)
+++ jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/memory/MemoryNodeStateBuilder.java Wed Jul 25 13:39:45 2012
@@ -16,6 +16,8 @@
  */
 package org.apache.jackrabbit.oak.plugins.memory;
 
+import static com.google.common.base.Preconditions.checkNotNull;
+
 import java.util.Collection;
 import java.util.Iterator;
 import java.util.List;
@@ -24,11 +26,11 @@ import java.util.Set;
 
 import org.apache.jackrabbit.oak.api.CoreValue;
 import org.apache.jackrabbit.oak.api.PropertyState;
+import org.apache.jackrabbit.oak.spi.state.AbstractNodeState;
 import org.apache.jackrabbit.oak.spi.state.ChildNodeEntry;
 import org.apache.jackrabbit.oak.spi.state.NodeState;
 import org.apache.jackrabbit.oak.spi.state.NodeStateBuilder;
 
-import com.google.common.base.Function;
 import com.google.common.base.Predicate;
 import com.google.common.base.Predicates;
 import com.google.common.collect.Collections2;
@@ -59,19 +61,13 @@ public class MemoryNodeStateBuilder impl
      */
     private String name;
 
-    // TODO: (Atomic)Reference for use with connect?
-    private NodeState base;
-
-    /**
-     * Set of added, modified or removed ({@code null} value) property states.
-     */
-    private Map<String, PropertyState> properties;
-
     /**
-     * Set of builders for added, modified or removed ({@code null} value)
-     * child nodes.
+     * The current state of this builder. Initially set to the immutable
+     * base state until this builder gets <em>connected</em>, after which
+     * this reference will point to the {@link MutableNodeState} instance
+     * that records all the changes to this node.
      */
-    private Map<String, MemoryNodeStateBuilder> builders;
+    private NodeState state;
 
     /**
      * Creates a new in-memory node state builder.
@@ -82,11 +78,9 @@ public class MemoryNodeStateBuilder impl
      */
     protected MemoryNodeStateBuilder(
             MemoryNodeStateBuilder parent, String name, NodeState base) {
-        this.parent = parent;
-        this.name = name;
-        this.properties = ImmutableMap.of();
-        this.builders = ImmutableMap.of();
-        this.base = base;
+        this.parent = checkNotNull(parent);
+        this.name = checkNotNull(name);
+        this.state = checkNotNull(base);
     }
 
     /**
@@ -97,42 +91,54 @@ public class MemoryNodeStateBuilder impl
     public MemoryNodeStateBuilder(NodeState base) {
         this.parent = null;
         this.name = null;
-        this.properties = Maps.newHashMap();
-        this.builders = Maps.newHashMap();
-        this.base = base;
+        this.state = new MutableNodeState(checkNotNull(base));
     }
 
-    private void connect(boolean modify) {
+    private NodeState read() {
         if (parent != null) {
-            parent.connect(modify);
+            NodeState pstate = parent.read();
+            if (pstate instanceof MutableNodeState) {
+                MutableNodeState mstate = (MutableNodeState) pstate;
+                MemoryNodeStateBuilder existing = mstate.builders.get(name);
+                if (existing != null) {
+                    state = existing.state;
+                    parent = null;
+                    name = null;
+                }
+            }
+        }
+        return state;
+    }
 
-            MemoryNodeStateBuilder existing = parent.builders.get(name);
+    private MutableNodeState write() {
+        if (parent != null) {
+            MutableNodeState mstate = parent.write();
+            MemoryNodeStateBuilder existing = mstate.builders.get(name);
             if (existing != null) {
-                properties = existing.properties;
-                builders = existing.builders;
-                parent = null;
-                name = null;
-            } else if (modify) {
-                parent.builders.put(name, this);
-                properties = Maps.newHashMap();
-                builders = Maps.newHashMap();
-                parent = null;
-                name = null;
+                state = existing.state;
+            } else {
+                state = new MutableNodeState(state);
+                mstate.builders.put(name, this);
             }
+            parent = null;
+            name = null;
         }
+        return (MutableNodeState) state;
     }
 
     private void reset(NodeState newBase) {
-        base = newBase;
+        MutableNodeState mstate = write();
+
+        mstate.base = newBase;
 
-        properties.clear();
+        mstate.properties.clear();
 
         Iterator<Map.Entry<String, MemoryNodeStateBuilder>> iterator =
-                builders.entrySet().iterator();
+                mstate.builders.entrySet().iterator();
         while (iterator.hasNext()) {
             Map.Entry<String, MemoryNodeStateBuilder> entry = iterator.next();
             MemoryNodeStateBuilder childBuilder = entry.getValue();
-            NodeState childBase = base.getChildNode(entry.getKey());
+            NodeState childBase = newBase.getChildNode(entry.getKey());
             if (childBase == null || childBuilder == null) {
                 iterator.remove();
             } else {
@@ -165,99 +171,48 @@ public class MemoryNodeStateBuilder impl
     }
 
     protected NodeState getBaseState() {
-        return base;
+        NodeState state = read();
+        if (state instanceof MutableNodeState) { 
+            return ((MutableNodeState) state).base;
+        } else {
+            return state;
+        }
     }
 
     @Override
     public NodeState getNodeState() {
-        connect(false);
-
-        if (parent != null) {
-            return base; // shortcut
-        }
-
-        Map<String, PropertyState> props = Maps.newHashMap(properties);
-        Map<String, NodeState> nodes = Maps.newHashMap();
-        for (Map.Entry<String, MemoryNodeStateBuilder> entry
-                : builders.entrySet()) {
-            NodeStateBuilder builder = entry.getValue();
-            if (builder != null) {
-                nodes.put(entry.getKey(), builder.getNodeState());
-            } else {
-                nodes.put(entry.getKey(), null);
-            }
+        NodeState state = read();
+        if (state instanceof MutableNodeState) {
+            return ((MutableNodeState) state).snapshot();
+        } else {
+            return state;
         }
-
-        return new ModifiedNodeState(base, props, nodes);
     }
 
     @Override
     public long getChildNodeCount() {
-        connect(false);
-
-        long count = base.getChildNodeCount();
-
-        for (Map.Entry<String, MemoryNodeStateBuilder> entry
-                : builders.entrySet()) {
-            if (base.getChildNode(entry.getKey()) != null) {
-                count--;
-            }
-            if (entry.getValue() != null) {
-                count++;
-            }
-        }
-
-        return count;
+        return read().getChildNodeCount();
     }
 
     @Override
     public boolean hasChildNode(String name) {
-        connect(false);
-
-        NodeStateBuilder builder = builders.get(name);
-        if (builder != null) {
-            return true;
-        } else if (builders.containsKey(name)) {
-            return false;
-        }
-
-        return base.getChildNode(name) != null;
+        return read().hasChildNode(name);
     }
 
     @Override
     public Iterable<String> getChildNodeNames() {
-        connect(false);
-
-        Iterable<String> unmodified = Iterables.transform(
-                base.getChildNodeEntries(),
-                new Function<ChildNodeEntry, String>() {
-                    @Override
-                    public String apply(ChildNodeEntry input) {
-                        return input.getName();
-                    }
-                });
-        if (parent != null) {
-            return unmodified; // shortcut
-        }
-
-        Predicate<String> unmodifiedFilter = Predicates.not(Predicates.in(
-                ImmutableSet.copyOf(builders.keySet())));
-        Set<String> modified = ImmutableSet.copyOf(
-                Maps.filterValues(builders, Predicates.notNull()).keySet());
-        return Iterables.concat(
-                Iterables.filter(unmodified, unmodifiedFilter),
-                modified);
+        return read().getChildNodeNames();
     }
 
     @Override
     public void setNode(String name, NodeState state) {
-        connect(true);
+        MutableNodeState mstate = write();
 
-        MemoryNodeStateBuilder builder = builders.get(name);
+        MemoryNodeStateBuilder builder = mstate.builders.get(name);
         if (builder != null) {
             builder.reset(state);
         } else {
-            createChildBuilder(name, state).connect(true);
+            createChildBuilder(name, state).write();
         }
 
         updated();
@@ -265,12 +220,12 @@ public class MemoryNodeStateBuilder impl
 
     @Override
     public void removeNode(String name) {
-        connect(true);
+        MutableNodeState mstate = write();
 
-        if (base.getChildNode(name) != null) {
-            builders.put(name, null);
+        if (mstate.base.getChildNode(name) != null) {
+            mstate.builders.put(name, null);
         } else {
-            builders.remove(name);
+            mstate.builders.remove(name);
         }
 
         updated();
@@ -278,75 +233,37 @@ public class MemoryNodeStateBuilder impl
 
     @Override
     public long getPropertyCount() {
-        connect(false);
-
-        long count = base.getPropertyCount();
-
-        for (Map.Entry<String, PropertyState> entry : properties.entrySet()) {
-            if (base.getProperty(entry.getKey()) != null) {
-                count--;
-            }
-            if (entry.getValue() != null) {
-                count++;
-            }
-        }
-
-        return count;
+        return read().getPropertyCount();
     }
 
     @Override
     public Iterable<? extends PropertyState> getProperties() {
-        connect(false);
-
-        Iterable<? extends PropertyState> unmodified = base.getProperties();
-        if (parent != null) {
-            return unmodified; // shortcut
-        }
-
-        final Set<String> names = ImmutableSet.copyOf(properties.keySet());
-        Predicate<PropertyState> filter = new Predicate<PropertyState>() {
-            @Override
-            public boolean apply(PropertyState input) {
-                return !names.contains(input.getName());
-            }
-        };
-        Collection<PropertyState> modified = ImmutableList.copyOf(
-                Collections2.filter(properties.values(), Predicates.notNull()));
-        return Iterables.concat(
-                Iterables.filter(unmodified, filter),
-                modified);
+        return read().getProperties();
     }
 
 
     @Override
     public PropertyState getProperty(String name) {
-        connect(false);
-
-        PropertyState property = properties.get(name);
-        if (property != null || properties.containsKey(name)) {
-            return property;
-        }
-
-        return base.getProperty(name);
+        return read().getProperty(name);
     }
 
     @Override
     public void setProperty(String name, CoreValue value) {
-        connect(true);
+        MutableNodeState mstate = write();
 
-        properties.put(name, new SinglePropertyState(name, value));
+        mstate.properties.put(name, new SinglePropertyState(name, value));
 
         updated();
     }
 
     @Override
     public void setProperty(String name, List<CoreValue> values) {
-        connect(true);
+        MutableNodeState mstate = write();
 
         if (values.isEmpty()) {
-            properties.put(name, new EmptyPropertyState(name));
+            mstate.properties.put(name, new EmptyPropertyState(name));
         } else {
-            properties.put(name, new MultiPropertyState(name, values));
+            mstate.properties.put(name, new MultiPropertyState(name, values));
         }
 
         updated();
@@ -354,12 +271,12 @@ public class MemoryNodeStateBuilder impl
 
     @Override
     public void removeProperty(String name) {
-        connect(true);
+        MutableNodeState mstate = write();
 
-        if (base.getProperty(name) != null) {
-            properties.put(name, null);
+        if (mstate.base.getProperty(name) != null) {
+            mstate.properties.put(name, null);
         } else {
-            properties.remove(name);
+            mstate.properties.remove(name);
         }
 
         updated();
@@ -367,20 +284,171 @@ public class MemoryNodeStateBuilder impl
 
     @Override
     public NodeStateBuilder getChildBuilder(String name) {
-        connect(true);
+        NodeState state = read();
 
-        MemoryNodeStateBuilder builder = builders.get(name);
-        if (builder == null) {
-            NodeState baseState = base.getChildNode(name);
-            if (baseState == null) {
-                baseState = NULL_STATE;
+        if (!(state instanceof MutableNodeState)) {
+            NodeState base = state.getChildNode(name);
+            if (base != null) {
+                return createChildBuilder(name, base); // shortcut
             }
-            builder = createChildBuilder(name, baseState);
-            builder.connect(true);
-            builders.put(name, builder);
         }
 
+        MutableNodeState mstate = write();
+        MemoryNodeStateBuilder builder = mstate.builders.get(name);
+        if (builder == null) {
+            if (mstate.builders.containsKey(name)) {
+                builder = createChildBuilder(name, NULL_STATE);
+                builder.write();
+            } else {
+                NodeState base = mstate.base.getChildNode(name);
+                if (base == null) {
+                    base = NULL_STATE;
+                }
+                builder = createChildBuilder(name, base);
+            }
+        }
         return builder;
     }
 
+    /**
+     * The <em>mutable</em> state being built. Instances of this class
+     * are never passed beyond the containing MemoryNodeStateBuilder,
+     * so it's not a problem that we intentionally break the immutability
+     * assumption of the {@link NodeState} interface.
+     */
+    private static class MutableNodeState extends AbstractNodeState {
+
+        /**
+         * The immutable base state.
+         */
+        private NodeState base;
+
+        /**
+         * Set of added, modified or removed ({@code null} value)
+         * property states.
+         */
+        private final Map<String, PropertyState> properties =
+                Maps.newHashMap();
+
+        /**
+         * Set of builders for added, modified or removed
+         * ({@code null} value) child nodes.
+         */
+        private final Map<String, MemoryNodeStateBuilder> builders =
+                Maps.newHashMap();
+
+        public MutableNodeState(NodeState base) {
+            this.base = base;
+        }
+
+        public NodeState snapshot() {
+            Map<String, PropertyState> props = Maps.newHashMap(properties);
+            Map<String, NodeState> nodes = Maps.newHashMap();
+            for (Map.Entry<String, MemoryNodeStateBuilder> entry
+                    : builders.entrySet()) {
+                NodeStateBuilder builder = entry.getValue();
+                if (builder != null) {
+                    nodes.put(entry.getKey(), builder.getNodeState());
+                } else {
+                    nodes.put(entry.getKey(), null);
+                }
+            }
+            return new ModifiedNodeState(base, props, nodes);
+        }
+
+        //-----------------------------------------------------< NodeState >--
+
+        @Override
+        public long getPropertyCount() {
+            long count = base.getPropertyCount();
+
+            for (Map.Entry<String, PropertyState> entry
+                    : properties.entrySet()) {
+                if (base.getProperty(entry.getKey()) != null) {
+                    count--;
+                }
+                if (entry.getValue() != null) {
+                    count++;
+                }
+            }
+
+            return count;
+        }
+
+        @Override
+        public PropertyState getProperty(String name) {
+            PropertyState property = properties.get(name);
+            if (property != null || properties.containsKey(name)) {
+                return property;
+            }
+
+            return base.getProperty(name);
+        }
+
+        @Override
+        public Iterable<? extends PropertyState> getProperties() {
+            final Set<String> names = ImmutableSet.copyOf(properties.keySet());
+            Predicate<PropertyState> filter = new Predicate<PropertyState>() {
+                @Override
+                public boolean apply(PropertyState input) {
+                    return !names.contains(input.getName());
+                }
+            };
+            Collection<PropertyState> modified = Collections2.filter(
+                    properties.values(), Predicates.notNull());
+            return Iterables.concat(
+                    Iterables.filter(base.getProperties(), filter),
+                    ImmutableList.copyOf(modified));
+        }
+
+        @Override
+        public long getChildNodeCount() {
+            long count = base.getChildNodeCount();
+
+            for (Map.Entry<String, MemoryNodeStateBuilder> entry
+                    : builders.entrySet()) {
+                if (base.getChildNode(entry.getKey()) != null) {
+                    count--;
+                }
+                if (entry.getValue() != null) {
+                    count++;
+                }
+            }
+
+            return count;
+        }
+
+        @Override
+        public boolean hasChildNode(String name) {
+            NodeStateBuilder builder = builders.get(name);
+            if (builder != null) {
+                return true;
+            } else if (builders.containsKey(name)) {
+                return false;
+            }
+
+            return base.getChildNode(name) != null;
+        }
+
+        @Override
+        public Iterable<String> getChildNodeNames() {
+            Iterable<String> unmodified = base.getChildNodeNames();
+            Predicate<String> unmodifiedFilter = Predicates.not(Predicates.in(
+                    ImmutableSet.copyOf(builders.keySet())));
+            Set<String> modified = ImmutableSet.copyOf(
+                    Maps.filterValues(builders, Predicates.notNull()).keySet());
+            return Iterables.concat(
+                    Iterables.filter(unmodified, unmodifiedFilter),
+                    modified);
+        }
+
+        @Override
+        public Iterable<? extends ChildNodeEntry> getChildNodeEntries() {
+            // TODO Auto-generated method stub
+            return null;
+        }
+
+
+    }
+
 }

Modified: jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/spi/state/AbstractNodeState.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/spi/state/AbstractNodeState.java?rev=1365575&r1=1365574&r2=1365575&view=diff
==============================================================================
--- jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/spi/state/AbstractNodeState.java (original)
+++ jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/spi/state/AbstractNodeState.java Wed Jul 25 13:39:45 2012
@@ -18,6 +18,9 @@ package org.apache.jackrabbit.oak.spi.st
 
 import org.apache.jackrabbit.oak.api.PropertyState;
 
+import com.google.common.base.Function;
+import com.google.common.collect.Iterables;
+
 import java.util.HashSet;
 import java.util.Iterator;
 import java.util.Set;
@@ -54,6 +57,11 @@ public abstract class AbstractNodeState 
     }
 
     @Override
+    public boolean hasChildNode(String name) {
+        return getChildNode(name) != null;
+    }
+
+    @Override
     public NodeState getChildNode(String name) {
         for (ChildNodeEntry entry : getChildNodeEntries()) {
             if (name.equals(entry.getName())) {
@@ -68,6 +76,18 @@ public abstract class AbstractNodeState 
         return count(getChildNodeEntries());
     }
 
+    @Override
+    public Iterable<String> getChildNodeNames() {
+        return Iterables.transform(
+                getChildNodeEntries(),
+                new Function<ChildNodeEntry, String>() {
+                    @Override
+                    public String apply(ChildNodeEntry input) {
+                        return input.getName();
+                    }
+                });
+    }
+
     /**
      * Generic default comparison algorithm that simply walks through the
      * property and child node lists of the given base state and compares

Modified: jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/spi/state/NodeState.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/spi/state/NodeState.java?rev=1365575&r1=1365574&r2=1365575&view=diff
==============================================================================
--- jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/spi/state/NodeState.java (original)
+++ jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/spi/state/NodeState.java Wed Jul 25 13:39:45 2012
@@ -128,6 +128,15 @@ public interface NodeState {
     Iterable<? extends PropertyState> getProperties();
 
     /**
+     * Checks whether the named child node exists.
+     *
+     * @param name name of the child node
+     * @return {@code true} if the named child node exists,
+     *         {@code false} otherwise
+     */
+    boolean hasChildNode(String name);
+
+    /**
      * Returns the named child node. The name is an opaque string and
      * is not parsed or otherwise interpreted by this method.
      * <p>
@@ -150,6 +159,13 @@ public interface NodeState {
     long getChildNodeCount();
 
     /**
+     * Returns the names of all child nodes.
+     *
+     * @return child node names in some stable order
+     */
+    Iterable<String> getChildNodeNames();
+
+    /**
      * Returns an iterable of the child node entries of this instance.
      * Multiple iterations are guaranteed to return the child nodes in
      * the same order, but the specific order used is implementation