You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@jackrabbit.apache.org by md...@apache.org on 2012/03/10 12:54:22 UTC

svn commit: r1299183 - in /jackrabbit/sandbox/jackrabbit-microkernel/src/main/java/org/apache/jackrabbit: NodeImpl.java state/ChangeTree.java state/EmptyNodeState.java state/PersistentNodeState.java state/TransientNodeState.java

Author: mduerig
Date: Sat Mar 10 11:54:21 2012
New Revision: 1299183

URL: http://svn.apache.org/viewvc?rev=1299183&view=rev
Log:
Microkernel based prototype of JCR implementation (WIP)
- introduce NodeState interface

Added:
    jackrabbit/sandbox/jackrabbit-microkernel/src/main/java/org/apache/jackrabbit/state/EmptyNodeState.java
    jackrabbit/sandbox/jackrabbit-microkernel/src/main/java/org/apache/jackrabbit/state/PersistentNodeState.java
Modified:
    jackrabbit/sandbox/jackrabbit-microkernel/src/main/java/org/apache/jackrabbit/NodeImpl.java
    jackrabbit/sandbox/jackrabbit-microkernel/src/main/java/org/apache/jackrabbit/state/ChangeTree.java
    jackrabbit/sandbox/jackrabbit-microkernel/src/main/java/org/apache/jackrabbit/state/TransientNodeState.java

Modified: jackrabbit/sandbox/jackrabbit-microkernel/src/main/java/org/apache/jackrabbit/NodeImpl.java
URL: http://svn.apache.org/viewvc/jackrabbit/sandbox/jackrabbit-microkernel/src/main/java/org/apache/jackrabbit/NodeImpl.java?rev=1299183&r1=1299182&r2=1299183&view=diff
==============================================================================
--- jackrabbit/sandbox/jackrabbit-microkernel/src/main/java/org/apache/jackrabbit/NodeImpl.java (original)
+++ jackrabbit/sandbox/jackrabbit-microkernel/src/main/java/org/apache/jackrabbit/NodeImpl.java Sat Mar 10 11:54:21 2012
@@ -22,9 +22,11 @@ package org.apache.jackrabbit;
 import org.apache.jackrabbit.SessionImpl.Context;
 import org.apache.jackrabbit.json.JsonValue;
 import org.apache.jackrabbit.json.JsonValue.JsonAtom;
+import org.apache.jackrabbit.oak.model.PropertyState;
 import org.apache.jackrabbit.spi.commons.iterator.Iterators;
 import org.apache.jackrabbit.spi.commons.iterator.Predicate;
 import org.apache.jackrabbit.spi.commons.iterator.Transformer;
+import org.apache.jackrabbit.state.PersistentNodeState.PropertyStateImpl;
 import org.apache.jackrabbit.state.TransientNodeState;
 import org.apache.jackrabbit.utils.ItemNameMatcher;
 import org.apache.jackrabbit.utils.NodeIteratorAdapter;
@@ -54,7 +56,6 @@ import java.io.InputStream;
 import java.math.BigDecimal;
 import java.util.Calendar;
 import java.util.Iterator;
-import java.util.Map.Entry;
 
 public class NodeImpl extends ItemImpl implements Node {
     private final TransientNodeState state;
@@ -342,17 +343,17 @@ public class NodeImpl extends ItemImpl i
 
     @Override
     public PropertyIterator getProperties() throws RepositoryException {
-        Iterator<Entry<String,JsonValue>> properties = state.getProperties();
+        Iterator<PropertyState> properties = state.getProperties();
         return new PropertyIteratorAdapter(propertyIterator(properties));
     }
 
     @Override
     public PropertyIterator getProperties(final String namePattern) throws RepositoryException {
-        Iterator<Entry<String, JsonValue>> properties = state.getProperties(
-            new Predicate<Entry<String, JsonValue>>() {
+        Iterator<PropertyState> properties = state.getProperties(
+            new Predicate<PropertyState>() {
                 @Override
-                public boolean evaluate(Entry<String, JsonValue> entry) {
-                    return ItemNameMatcher.matches(entry.getKey(), namePattern);
+                public boolean evaluate(PropertyState entry) {
+                    return ItemNameMatcher.matches(entry.getName(), namePattern);
                 }
         });
 
@@ -361,11 +362,11 @@ public class NodeImpl extends ItemImpl i
 
     @Override
     public PropertyIterator getProperties(final String[] nameGlobs) throws RepositoryException {
-        Iterator<Entry<String, JsonValue>> propertyNames = state.getProperties(
-                new Predicate<Entry<String, JsonValue>>() {
+        Iterator<PropertyState> propertyNames = state.getProperties(
+                new Predicate<PropertyState>() {
                     @Override
-                    public boolean evaluate(Entry<String, JsonValue> entry) {
-                        return ItemNameMatcher.matches(entry.getKey(), nameGlobs);
+                    public boolean evaluate(PropertyState entry) {
+                        return ItemNameMatcher.matches(entry.getName(), nameGlobs);
                     }
                 });
 
@@ -599,12 +600,14 @@ public class NodeImpl extends ItemImpl i
         });
     }
 
-    private Iterator<Property> propertyIterator(Iterator<Entry<String, JsonValue>> properties) {
-        return Iterators.transformIterator(properties, new Transformer<Entry<String, JsonValue>, Property>() {
+    private Iterator<Property> propertyIterator(Iterator<PropertyState> properties) {
+        return Iterators.transformIterator(properties, new Transformer<PropertyState, Property>() {
             @Override
-            public Property transform(Entry<String, JsonValue> entry) {
-                return PropertyImpl.create(sessionContext, state, entry.getKey(), entry.getValue());
+            public Property transform(PropertyState state) { // fixme don't cast
+                JsonValue value = ((PropertyStateImpl) state).getValue();
+                return PropertyImpl.create(sessionContext, NodeImpl.this.state, state.getName(), value);
             }
         });
     }
+
 }

Modified: jackrabbit/sandbox/jackrabbit-microkernel/src/main/java/org/apache/jackrabbit/state/ChangeTree.java
URL: http://svn.apache.org/viewvc/jackrabbit/sandbox/jackrabbit-microkernel/src/main/java/org/apache/jackrabbit/state/ChangeTree.java?rev=1299183&r1=1299182&r2=1299183&view=diff
==============================================================================
--- jackrabbit/sandbox/jackrabbit-microkernel/src/main/java/org/apache/jackrabbit/state/ChangeTree.java (original)
+++ jackrabbit/sandbox/jackrabbit-microkernel/src/main/java/org/apache/jackrabbit/state/ChangeTree.java Sat Mar 10 11:54:21 2012
@@ -23,8 +23,11 @@ import org.apache.commons.collections.ma
 import org.apache.jackrabbit.Path;
 import org.apache.jackrabbit.json.JsonValue;
 import org.apache.jackrabbit.json.JsonValue.JsonAtom;
+import org.apache.jackrabbit.oak.model.PropertyState;
 import org.apache.jackrabbit.spi.commons.iterator.Iterators;
 import org.apache.jackrabbit.spi.commons.iterator.Predicate;
+import org.apache.jackrabbit.spi.commons.iterator.Transformer;
+import org.apache.jackrabbit.state.PersistentNodeState.PropertyStateImpl;
 
 import javax.jcr.ItemExistsException;
 import javax.jcr.ItemNotFoundException;
@@ -257,13 +260,24 @@ public class ChangeTree {
         /**
          * @return  an iterator of all added and modified properties.
          */
-        public Iterator<Entry<String, JsonValue>> getProperties() {
-            return Iterators.filterIterator(properties.entrySet().iterator(), new Predicate<Entry<String, JsonValue>>() {
-                @Override
-                public boolean evaluate(Entry<String, JsonValue> e) {
-                    return e.getValue() != JsonAtom.NULL;
-                }
-            });
+        public Iterator<PropertyState> getPropertyStates() {
+            Iterator<Entry<String, JsonValue>> entries =
+                Iterators.filterIterator(properties.entrySet().iterator(),
+                    new Predicate<Entry<String, JsonValue>>() {
+                        @Override
+                        public boolean evaluate(Entry<String, JsonValue> entry) {
+                            return entry.getValue() != JsonAtom.NULL;
+                        }
+                    });
+
+            // fixme: refactor property value encoding
+            return Iterators.transformIterator(entries,
+                new Transformer<Entry<String, JsonValue>, PropertyState>() {
+                    @Override
+                    public PropertyState transform(final Entry<String, JsonValue> entry) {
+                        return new PropertyStateImpl(entry.getKey(), entry.getValue());
+                    }
+                });
         }
 
         /**

Added: jackrabbit/sandbox/jackrabbit-microkernel/src/main/java/org/apache/jackrabbit/state/EmptyNodeState.java
URL: http://svn.apache.org/viewvc/jackrabbit/sandbox/jackrabbit-microkernel/src/main/java/org/apache/jackrabbit/state/EmptyNodeState.java?rev=1299183&view=auto
==============================================================================
--- jackrabbit/sandbox/jackrabbit-microkernel/src/main/java/org/apache/jackrabbit/state/EmptyNodeState.java (added)
+++ jackrabbit/sandbox/jackrabbit-microkernel/src/main/java/org/apache/jackrabbit/state/EmptyNodeState.java Sat Mar 10 11:54:21 2012
@@ -0,0 +1,34 @@
+package org.apache.jackrabbit.state;
+
+import org.apache.jackrabbit.oak.model.AbstractNodeState;
+import org.apache.jackrabbit.oak.model.ChildNodeEntry;
+import org.apache.jackrabbit.oak.model.PropertyState;
+import org.apache.jackrabbit.spi.commons.iterator.Iterators;
+
+import java.util.Iterator;
+
+public final class EmptyNodeState extends AbstractNodeState {
+    public static final EmptyNodeState INSTANCE = new EmptyNodeState();
+    
+    private EmptyNodeState() { }
+
+    @Override
+    public Iterable<PropertyState> getProperties() {
+        return new Iterable<PropertyState>() {
+            @Override
+            public Iterator<PropertyState> iterator() {
+                return Iterators.empty();
+            }
+        };
+    }
+
+    @Override
+    public Iterable<ChildNodeEntry> getChildNodeEntries(long offset, long length) {
+        return new Iterable<ChildNodeEntry>() {
+            @Override
+            public Iterator<ChildNodeEntry> iterator() {
+                return Iterators.empty();
+            }
+        };
+    }
+}

Added: jackrabbit/sandbox/jackrabbit-microkernel/src/main/java/org/apache/jackrabbit/state/PersistentNodeState.java
URL: http://svn.apache.org/viewvc/jackrabbit/sandbox/jackrabbit-microkernel/src/main/java/org/apache/jackrabbit/state/PersistentNodeState.java?rev=1299183&view=auto
==============================================================================
--- jackrabbit/sandbox/jackrabbit-microkernel/src/main/java/org/apache/jackrabbit/state/PersistentNodeState.java (added)
+++ jackrabbit/sandbox/jackrabbit-microkernel/src/main/java/org/apache/jackrabbit/state/PersistentNodeState.java Sat Mar 10 11:54:21 2012
@@ -0,0 +1,186 @@
+package org.apache.jackrabbit.state;
+
+import org.apache.jackrabbit.Path;
+import org.apache.jackrabbit.json.FullJsonParser;
+import org.apache.jackrabbit.json.JsonValue;
+import org.apache.jackrabbit.json.JsonValue.JsonObject;
+import org.apache.jackrabbit.json.UnescapingJsonTokenizer;
+import org.apache.jackrabbit.mk.api.MicroKernel;
+import org.apache.jackrabbit.oak.model.AbstractChildNodeEntry;
+import org.apache.jackrabbit.oak.model.AbstractNodeState;
+import org.apache.jackrabbit.oak.model.AbstractPropertyState;
+import org.apache.jackrabbit.oak.model.ChildNodeEntry;
+import org.apache.jackrabbit.oak.model.NodeState;
+import org.apache.jackrabbit.oak.model.PropertyState;
+import org.apache.jackrabbit.spi.commons.iterator.Iterators;
+import org.apache.jackrabbit.spi.commons.iterator.Predicate;
+import org.apache.jackrabbit.spi.commons.iterator.Transformer;
+
+import java.util.Iterator;
+import java.util.Map.Entry;
+
+public class PersistentNodeState extends AbstractNodeState {
+    private MicroKernel microkernel;
+    private String revision;
+    private Path path;
+
+    public PersistentNodeState(MicroKernel microkernel, String revision, Path path) {
+        this.microkernel = microkernel;
+        this.revision = revision;
+        this.path = path;
+    }
+    
+    @Override
+    public PropertyState getProperty(String name) {
+        JsonObject jsonObject = getPropertiesAsJson();
+        return createPropertyState(name, jsonObject.get(name));
+    }
+
+    @Override
+    public Iterable<PropertyState> getProperties() {
+        return new Iterable<PropertyState>() {
+            private final JsonObject jsonObject = getPropertiesAsJson();
+
+            @Override
+            public Iterator<PropertyState> iterator() {
+                return Iterators.transformIterator(jsonObject.value().entrySet().iterator(), 
+                    new Transformer<Entry<String, JsonValue>, PropertyState>() {
+                        @Override
+                        public PropertyState transform(Entry<String, JsonValue> entry) {
+                            return createPropertyState(entry.getKey(), entry.getValue());   
+                        }
+                });   
+            }
+        };
+    }
+
+    @Override
+    public NodeState getChildNode(String name) {
+        if (microkernel.nodeExists(name, revision)) {
+            return new PersistentNodeState(microkernel, revision, path.concat(name));
+        }
+        else {
+            return null;
+        }
+    }
+
+    @Override
+    public long getChildNodeCount() {
+        JsonObject jsonObject = getPropertiesAsJson();
+        JsonValue count = jsonObject.get(":childNodeCount");
+        long c = toLong(count);
+        if (c < 0) {
+            return super.getChildNodeCount();
+        }
+        else {
+            return c;
+        }
+    }
+
+    @Override
+    public Iterable<ChildNodeEntry> getChildNodeEntries(final long offset, final long length) {
+        return new Iterable<ChildNodeEntry>() {
+            private final JsonObject jsonObject = getNodesAsJson(offset, length);
+
+            Iterator<Entry<String, JsonValue>> childObjects =
+                Iterators.filterIterator(jsonObject.value().entrySet().iterator(),
+                    new Predicate<Entry<String, JsonValue>>() {
+                        @Override
+                        public boolean evaluate(Entry<String, JsonValue> entry) {
+                            return entry.getValue().isObject();
+                        }
+                    });
+
+            Iterator<ChildNodeEntry> childNodeEntry = Iterators.transformIterator(childObjects,
+                new Transformer<Entry<String, JsonValue>, ChildNodeEntry>() {
+                    @Override
+                    public ChildNodeEntry transform(Entry<String, JsonValue> entry) {
+                        return createChildNodeEntry(entry.getKey());
+                    }
+                });
+
+            @Override
+            public Iterator<ChildNodeEntry> iterator() {
+                return childNodeEntry;
+            }
+        };
+    }
+
+    //------------------------------------------< private >---
+
+    private JsonObject getPropertiesAsJson() {
+        String json = microkernel.getNodes(path.toMkPath(), revision, 0, 0, -1, null);
+        return FullJsonParser.parseObject(new UnescapingJsonTokenizer(json));
+    }
+
+    private JsonObject getNodesAsJson(long offset, long length) {
+        // fixme: microkernel should also use long for length
+        String json = microkernel.getNodes(path.toMkPath(), revision, 1, offset, (int) length, null);
+        return FullJsonParser.parseObject(new UnescapingJsonTokenizer(json));
+    }
+
+    private static PropertyState createPropertyState(String name, JsonValue jsonObject) {
+        return new PropertyStateImpl(name, jsonObject);
+    }
+
+    private ChildNodeEntry createChildNodeEntry(String name) {
+        return new ChildNodeEntryImpl(name);
+    }
+
+    private static long toLong(JsonValue count) {
+        if (count == null || !count.isAtom()) {
+            return -1;
+        }
+        else {
+            try {
+                return Long.parseLong(count.asAtom().value());
+            }
+            catch (NumberFormatException e) {
+                return -1;
+            }
+        }
+    }
+
+    public static class PropertyStateImpl extends AbstractPropertyState {
+        private final String name;
+        private final JsonValue value;
+
+        public PropertyStateImpl(String name, JsonValue value) {
+            this.name = name;
+            this.value = value;
+        }
+
+        public JsonValue getValue() {
+            return value;
+        }
+
+        @Override
+        public String getName() {
+            return name;
+        }
+
+        @Override
+        public String getEncodedValue() {
+            return value.toJson();
+        }
+    }
+
+    private class ChildNodeEntryImpl extends AbstractChildNodeEntry {
+        private final String name;
+
+        public ChildNodeEntryImpl(String name) {
+            this.name = name;
+        }
+
+        @Override
+        public String getName() {
+            return name;
+        }
+
+        @Override
+        public NodeState getNode() {
+            return new PersistentNodeState(microkernel, revision, path.concat(name));
+        }
+    }
+
+}

Modified: jackrabbit/sandbox/jackrabbit-microkernel/src/main/java/org/apache/jackrabbit/state/TransientNodeState.java
URL: http://svn.apache.org/viewvc/jackrabbit/sandbox/jackrabbit-microkernel/src/main/java/org/apache/jackrabbit/state/TransientNodeState.java?rev=1299183&r1=1299182&r2=1299183&view=diff
==============================================================================
--- jackrabbit/sandbox/jackrabbit-microkernel/src/main/java/org/apache/jackrabbit/state/TransientNodeState.java (original)
+++ jackrabbit/sandbox/jackrabbit-microkernel/src/main/java/org/apache/jackrabbit/state/TransientNodeState.java Sat Mar 10 11:54:21 2012
@@ -21,29 +21,28 @@ package org.apache.jackrabbit.state;
 
 import org.apache.jackrabbit.Path;
 import org.apache.jackrabbit.SessionImpl.Context;
-import org.apache.jackrabbit.json.FullJsonParser;
 import org.apache.jackrabbit.json.JsonValue;
 import org.apache.jackrabbit.json.JsonValue.JsonAtom;
-import org.apache.jackrabbit.json.JsonValue.JsonObject;
-import org.apache.jackrabbit.json.UnescapingJsonTokenizer;
 import org.apache.jackrabbit.mk.api.MicroKernel;
+import org.apache.jackrabbit.oak.model.ChildNodeEntry;
+import org.apache.jackrabbit.oak.model.NodeState;
+import org.apache.jackrabbit.oak.model.PropertyState;
 import org.apache.jackrabbit.spi.commons.iterator.Iterators;
 import org.apache.jackrabbit.spi.commons.iterator.Predicate;
 import org.apache.jackrabbit.spi.commons.iterator.Transformer;
 import org.apache.jackrabbit.state.ChangeTree.NodeDelta;
+import org.apache.jackrabbit.state.PersistentNodeState.PropertyStateImpl;
 
 import javax.jcr.ItemExistsException;
 import javax.jcr.ItemNotFoundException;
 import javax.jcr.PathNotFoundException;
 import java.util.Iterator;
-import java.util.Map;
-import java.util.Map.Entry;
 
 public class TransientNodeState {
     private final Context sessionContext;
 
     private String revision;
-    private JsonObject jsonObject;
+    private NodeState persistentNodeState;
     private NodeDelta nodeDelta;
 
     private TransientNodeState(Context sessionContext, NodeDelta nodeDelta) {
@@ -97,36 +96,35 @@ public class TransientNodeState {
     }
 
     public Iterator<TransientNodeState> getChildNodeStates() {
-        Map<String, JsonValue> childEntries = getJsonObject().value();
+        Iterable<ChildNodeEntry> persistedEntries = getPersistentNodeState().getChildNodeEntries(0, Long.MAX_VALUE);
         final NodeDelta delta = getNodeDelta();
 
-        // fixme: this is not safe against concurrent modifications. Either cope delta or make NodeDelta immutable
-        Iterator<Entry<String, JsonValue>> childNodeEntries =
-            Iterators.filterIterator(childEntries.entrySet().iterator(),
-                    new Predicate<Entry<String, JsonValue>>() {
-                        @Override
-                        public boolean evaluate(Entry<String, JsonValue> entry) {
-                            return isNode(entry.getValue()) && !delta.isNodeModified(entry.getKey());
-                        }
-                    });
-
-        Iterator<TransientNodeState> childNodeStates = Iterators.transformIterator(childNodeEntries,
-            new Transformer<Entry<String, JsonValue>, TransientNodeState>() {
+        // fixme: this is not safe against concurrent modifications. Either copy delta or make NodeDelta immutable
+        Iterator<ChildNodeEntry> unmodifiedEntries = Iterators.filterIterator(persistedEntries.iterator(),
+            new Predicate<ChildNodeEntry>() {
+                @Override
+                public boolean evaluate(ChildNodeEntry entry) {
+                    return !delta.isNodeModified(entry.getName());
+                }
+            });
+        
+        Iterator<TransientNodeState> unmodifiedStates = Iterators.transformIterator(unmodifiedEntries, 
+            new Transformer<ChildNodeEntry, TransientNodeState>() {
                 @Override
-                public TransientNodeState transform(Entry<String, JsonValue> entry) {
-                    return getNodeState(sessionContext, delta.getNode(entry.getKey()));
+                public TransientNodeState transform(ChildNodeEntry entry) {
+                    return getNodeState(sessionContext, delta.getNode(entry.getName()));
                 }
         });
 
-        Iterator<TransientNodeState> modifiedNodeStates = Iterators.transformIterator(delta.getNodes(),
-                new Transformer<NodeDelta, TransientNodeState>() {
-                    @Override
-                    public TransientNodeState transform(NodeDelta delta) {
-                        return getNodeState(sessionContext, delta);
-                    }
-                });
+        Iterator<TransientNodeState> modifiedStates = Iterators.transformIterator(delta.getNodes(),
+            new Transformer<NodeDelta, TransientNodeState>() {
+                @Override
+                public TransientNodeState transform(NodeDelta delta) {
+                    return getNodeState(sessionContext, delta);
+                }
+            });
 
-        return Iterators.iteratorChain(childNodeStates, modifiedNodeStates);
+        return Iterators.iteratorChain(unmodifiedStates, modifiedStates);
     }
 
     public Iterator<TransientNodeState> getChildNodeStates(Predicate<TransientNodeState> condition) {
@@ -137,25 +135,24 @@ public class TransientNodeState {
         return getProperties().hasNext();
     }
 
-    public Iterator<Entry<String, JsonValue>> getProperties() {
-        Map<String, JsonValue> childEntries = getJsonObject().value();
+    public Iterator<PropertyState> getProperties() {
+        Iterable<PropertyState> propertyStates = getPersistentNodeState().getProperties();
         final NodeDelta delta = getNodeDelta();
 
-        // fixme: this is not safe against concurrent modifications. Either cope delta or make NodeDelta immutable
-        Iterator<Entry<String, JsonValue>> propertyEntries =
-            Iterators.filterIterator(childEntries.entrySet().iterator(),
-                    new Predicate<Entry<String, JsonValue>>() {
-                        @Override
-                        public boolean evaluate(Entry<String, JsonValue> entry) {
-                            return !entry.getKey().startsWith(":") && !isNode(entry.getValue())
-                                    && !delta.hasProperty(entry.getKey());
-                        }
-                    });
+        // fixme: this is not safe against concurrent modifications. Either copy delta or make NodeDelta immutable
+        Iterator<PropertyState> propertyEntries =
+                Iterators.filterIterator(propertyStates.iterator(),
+                        new Predicate<PropertyState>() {
+                            @Override
+                            public boolean evaluate(PropertyState state) {
+                                return !state.getName().startsWith(":") && !delta.hasProperty(state.getName());
+                            }
+                        });
 
-        return Iterators.iteratorChain(propertyEntries, delta.getProperties());
+        return Iterators.iteratorChain(propertyEntries, delta.getPropertyStates());
     }
 
-    public Iterator<Entry<String, JsonValue>> getProperties(Predicate<Entry<String, JsonValue>> condition) {
+    public Iterator<PropertyState> getProperties(Predicate<PropertyState> condition) {
         return Iterators.filterIterator(getProperties(), condition);
     }
 
@@ -244,30 +241,29 @@ public class TransientNodeState {
     }
 
     private JsonValue getPersistedPropertyValue(String name) {
-        JsonValue value = getJsonObject().get(name);
-        if (value == null) {
+        PropertyState state = getPersistentNodeState().getProperty(name);
+        if (state == null) {
             return null;
         }
         else {
-            return isNode(value) ? null : value;
+            return ((PropertyStateImpl) state).getValue();  // fixme: don't cast
         }
     }
 
-    private synchronized JsonObject getJsonObject() {
+    private synchronized NodeState getPersistentNodeState() {
         Path path = getNodeDelta().getPersistentPath();
         String baseRevision = getBaseRevision();
-        if (jsonObject == null || !revision.equals(baseRevision)) {
+        if (persistentNodeState == null || !revision.equals(baseRevision)) {
             revision =  baseRevision;
             if (path == null) {
-                jsonObject = JsonObject.EMPTY;
+                persistentNodeState = EmptyNodeState.INSTANCE;
             }
             else {
-                String json = getMicrokernel().getNodes(path.toMkPath(), revision);
-                jsonObject = FullJsonParser.parseObject(new UnescapingJsonTokenizer(json));
+                persistentNodeState = new PersistentNodeState(getMicrokernel(), revision, path);
             }
         }
 
-        return jsonObject;
+        return persistentNodeState;
     }
 
     private NodeDelta getNodeDelta() {