Index: jackrabbit-jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/hierarchy/NodeEntryImpl.java
===================================================================
--- jackrabbit-jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/hierarchy/NodeEntryImpl.java	(revision 679098)
+++ jackrabbit-jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/hierarchy/NodeEntryImpl.java	(working copy)
@@ -16,46 +16,47 @@
  */
 package org.apache.jackrabbit.jcr2spi.hierarchy;
 
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.LinkedHashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+import javax.jcr.InvalidItemStateException;
+import javax.jcr.ItemExistsException;
+import javax.jcr.ItemNotFoundException;
+import javax.jcr.PathNotFoundException;
+import javax.jcr.RepositoryException;
+
+import org.apache.commons.collections.iterators.IteratorChain;
+import org.apache.jackrabbit.commons.iterator.RangeIteratorAdapter;
+import org.apache.jackrabbit.jcr2spi.state.ChangeLog;
+import org.apache.jackrabbit.jcr2spi.state.ItemState;
+import org.apache.jackrabbit.jcr2spi.state.ItemStateLifeCycleListener;
+import org.apache.jackrabbit.jcr2spi.state.NodeState;
+import org.apache.jackrabbit.jcr2spi.state.PropertyState;
+import org.apache.jackrabbit.jcr2spi.state.Status;
+import org.apache.jackrabbit.jcr2spi.util.StateUtility;
+import org.apache.jackrabbit.spi.Event;
+import org.apache.jackrabbit.spi.IdFactory;
+import org.apache.jackrabbit.spi.ItemId;
 import org.apache.jackrabbit.spi.Name;
+import org.apache.jackrabbit.spi.NodeId;
 import org.apache.jackrabbit.spi.Path;
-import org.apache.jackrabbit.spi.NodeId;
-import org.apache.jackrabbit.spi.Event;
-import org.apache.jackrabbit.spi.ItemId;
+import org.apache.jackrabbit.spi.PathFactory;
+import org.apache.jackrabbit.spi.PropertyId;
 import org.apache.jackrabbit.spi.QNodeDefinition;
 import org.apache.jackrabbit.spi.QPropertyDefinition;
-import org.apache.jackrabbit.spi.IdFactory;
-import org.apache.jackrabbit.spi.PropertyId;
-import org.apache.jackrabbit.spi.PathFactory;
-import org.apache.jackrabbit.jcr2spi.state.NodeState;
-import org.apache.jackrabbit.jcr2spi.state.ItemState;
-import org.apache.jackrabbit.jcr2spi.state.ChangeLog;
-import org.apache.jackrabbit.jcr2spi.state.Status;
-import org.apache.jackrabbit.jcr2spi.state.PropertyState;
-import org.apache.jackrabbit.jcr2spi.state.ItemStateLifeCycleListener;
-import org.apache.jackrabbit.jcr2spi.util.StateUtility;
 import org.apache.jackrabbit.spi.commons.name.NameConstants;
 import org.apache.jackrabbit.spi.commons.name.PathBuilder;
-import org.apache.jackrabbit.commons.iterator.RangeIteratorAdapter;
-import org.apache.commons.collections.iterators.IteratorChain;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
-import javax.jcr.ItemExistsException;
-import javax.jcr.RepositoryException;
-import javax.jcr.PathNotFoundException;
-import javax.jcr.ItemNotFoundException;
-import javax.jcr.InvalidItemStateException;
-import java.util.Iterator;
-import java.util.List;
-import java.util.ArrayList;
-import java.util.Collection;
-import java.util.Collections;
-import java.util.HashMap;
-import java.util.Set;
-import java.util.HashSet;
-import java.util.Map;
-import java.util.LinkedHashMap;
-
 /**
  * <code>NodeEntryImpl</code> implements common functionality for child
  * node entry implementations.
@@ -80,7 +81,7 @@
      * Map used to remember transiently removed or moved childNodeEntries, that
      * must not be retrieved from the persistent storage.
      */
-    private ChildNodeAttic childNodeAttic;
+    private final ChildNodeAttic childNodeAttic;
 
     /**
      * Map of properties.<br>
@@ -380,7 +381,7 @@
         NodeEntryImpl entry = this;
         Path.Element[] elems = path.getElements();
         for (int i = 0; i < elems.length; i++) {
-            Path.Element elem = (Path.Element) elems[i];
+            Path.Element elem = elems[i];
             // check for root element
             if (elem.denotesRoot()) {
                 if (getParent() != null) {
@@ -491,7 +492,7 @@
      */
     public synchronized boolean hasNodeEntry(Name nodeName) {
         try {
-            List namedEntries = childNodeEntries().get(nodeName);
+            List namedEntries = childNodeEntries(true).get(nodeName);
             if (namedEntries.isEmpty()) {
                 return false;
             } else {
@@ -529,8 +530,12 @@
      * @see NodeEntry#getNodeEntry(Name, int, boolean)
      */
     public NodeEntry getNodeEntry(Name nodeName, int index, boolean loadIfNotFound) throws RepositoryException {
-        // TODO: avoid loading the child-infos if childNodeEntries == null
-        List entries = childNodeEntries().get(nodeName);
+        ChildNodeEntries childNodeEntries = childNodeEntries(loadIfNotFound);
+        if (childNodeEntries == null) {
+            return null;
+        }
+
+        List entries = childNodeEntries.get(nodeName);
         NodeEntry cne = null;
         if (entries.size() >= index) {
             // position of entry might differ from index-1 if a SNS with lower
@@ -558,7 +563,7 @@
      */
     public synchronized Iterator getNodeEntries() throws RepositoryException {
         Collection entries = new ArrayList();
-        for (Iterator it = childNodeEntries().iterator(); it.hasNext();) {
+        for (Iterator it = childNodeEntries(true).iterator(); it.hasNext();) {
             NodeEntry entry = (NodeEntry) it.next();
             if (EntryValidation.isValidNodeEntry(entry)) {
                 entries.add(entry);
@@ -571,7 +576,7 @@
      * @see NodeEntry#getNodeEntries(Name)
      */
     public synchronized List getNodeEntries(Name nodeName) throws RepositoryException {
-        List namedEntries = childNodeEntries().get(nodeName);
+        List namedEntries = childNodeEntries(true).get(nodeName);
         if (namedEntries.isEmpty()) {
             return Collections.EMPTY_LIST;
         } else {
@@ -607,7 +612,7 @@
      * @see NodeEntry#addNodeEntry(Name, String, int)
      */
     public NodeEntry addNodeEntry(Name nodeName, String uniqueID, int index) throws RepositoryException {
-        return internalAddNodeEntry(nodeName, uniqueID, index, childNodeEntries());
+        return internalAddNodeEntry(nodeName, uniqueID, index, childNodeEntries(true));
     }
 
     /**
@@ -616,7 +621,7 @@
      */
     public NodeState addNewNodeEntry(Name nodeName, String uniqueID,
                                      Name primaryNodeType, QNodeDefinition definition) throws RepositoryException {
-        NodeEntry entry = internalAddNodeEntry(nodeName, uniqueID, Path.INDEX_UNDEFINED, childNodeEntries());
+        NodeEntry entry = internalAddNodeEntry(nodeName, uniqueID, Path.INDEX_UNDEFINED, childNodeEntries(true));
         NodeState state = factory.getItemStateFactory().createNewNodeState(entry, primaryNodeType, definition);
         if (!entry.isAvailable()) {
             entry.setItemState(state);
@@ -831,12 +836,12 @@
     public void orderBefore(NodeEntry beforeEntry) throws RepositoryException {
         if (Status.NEW == getStatus()) {
             // new states get remove upon revert
-            parent.childNodeEntries().reorder(this, beforeEntry);
+            parent.childNodeEntries(true).reorder(this, beforeEntry);
         } else {
             createSiblingRevertInfos();
             parent.createRevertInfo();
             // now reorder child entries on parent
-            NodeEntry previousBefore = parent.childNodeEntries().reorder(this, beforeEntry);
+            NodeEntry previousBefore = parent.childNodeEntries(true).reorder(this, beforeEntry);
             parent.revertInfo.reordered(this, previousBefore);
         }
     }
@@ -858,7 +863,7 @@
            parent.childNodeAttic.add(this);
        }
 
-       NodeEntryImpl entry = (NodeEntryImpl) parent.childNodeEntries().remove(this);
+       NodeEntryImpl entry = (NodeEntryImpl) parent.childNodeEntries(true).remove(this);
        if (entry != this) {
            // should never occur
            String msg = "Internal error. Attempt to move NodeEntry (" + getName() + ") which is not connected to its parent.";
@@ -869,7 +874,7 @@
        parent = (NodeEntryImpl) newParent;
        name = newName;
        // register entry with its new parent
-       parent.childNodeEntries().add(this);
+       parent.childNodeEntries(true).add(this);
        return this;
    }
 
@@ -1194,12 +1199,17 @@
     }
 
     /**
+     * @param loadIfNotFound
      * @return The <code>ChildNodeEntries</code> defined for this
-     * <code>NodeEntry</code>. Please note, that this method never returns
-     * <code>null</code>, since the child node entries are loaded/reloaded
+     * <code>NodeEntry</code>. This method never returns <code>null</code>
+     * if loadIfNotFound is true, since the child node entries are loaded/reloaded
      * in case they have not been loaded yet.
      */
-    private ChildNodeEntries childNodeEntries() throws InvalidItemStateException, RepositoryException {
+    private ChildNodeEntries childNodeEntries(boolean loadIfNotFound) throws InvalidItemStateException, RepositoryException {
+        if (childNodeEntries == null && !loadIfNotFound) {
+            return null;
+        }
+
         try {
             if (childNodeEntries == null) {
                 childNodeEntries = new ChildNodeEntriesImpl(this, factory);
@@ -1250,7 +1260,7 @@
      * this <code>NodeEntry</code>.
      */
     private int getChildIndex(NodeEntry cne) throws ItemNotFoundException, RepositoryException {
-        List sns = childNodeEntries().get(cne.getName());
+        List sns = childNodeEntries(true).get(cne.getName());
         // index is one based
         int index = Path.INDEX_DEFAULT;
         for (Iterator it = sns.iterator(); it.hasNext(); ) {
@@ -1314,7 +1324,7 @@
         }
         // for SNSs without UniqueID remember original index in order to
         // be able to build the workspaceID TODO: improve
-        List sns = parent.childNodeEntries().get(name);
+        List sns = parent.childNodeEntries(true).get(name);
         if (sns.size() > 1) {
             for (Iterator it = sns.iterator(); it.hasNext();) {
                 NodeEntryImpl sibling = (NodeEntryImpl) it.next();
@@ -1336,7 +1346,7 @@
         if (isTransientlyMoved())  {
             // move NodeEntry back to its original parent
             // TODO improve for simple renaming
-            parent.childNodeEntries().remove(this);
+            parent.childNodeEntries(true).remove(this);
             revertInfo.oldParent.childNodeAttic.remove(this);
 
             // now restore moved entry with the old name and index and re-add
@@ -1345,7 +1355,7 @@
             name = revertInfo.oldName;
             ItemState state = internalGetItemState();
             if (state != null && !Status.isTerminal(state.getStatus())) {
-                parent.childNodeEntries().add(this, revertInfo.oldIndex);
+                parent.childNodeEntries(true).add(this, revertInfo.oldIndex);
             }
         }
         // revert reordering of child-node-entries
@@ -1378,9 +1388,9 @@
      */
     private class RevertInfo implements ItemStateLifeCycleListener {
 
-        private NodeEntryImpl oldParent;
-        private Name oldName;
-        private int oldIndex;
+        private final NodeEntryImpl oldParent;
+        private final Name oldName;
+        private final int oldIndex;
 
         private Map reorderedChildren;
 
Index: jackrabbit-jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/state/WorkspaceItemStateFactory.java
===================================================================
--- jackrabbit-jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/state/WorkspaceItemStateFactory.java	(revision 679098)
+++ jackrabbit-jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/state/WorkspaceItemStateFactory.java	(working copy)
@@ -18,8 +18,12 @@
 
 import java.util.ArrayList;
 import java.util.Collections;
+import java.util.HashMap;
 import java.util.Iterator;
+import java.util.LinkedList;
 import java.util.List;
+import java.util.Map;
+import java.util.NoSuchElementException;
 
 import javax.jcr.ItemExistsException;
 import javax.jcr.ItemNotFoundException;
@@ -142,7 +146,7 @@
     public PropertyState createDeepPropertyState(PropertyId propertyId, NodeEntry anyParent) throws ItemNotFoundException, RepositoryException {
         try {
             PropertyInfo info = service.getPropertyInfo(sessionInfo, propertyId);
-            return createDeepPropertyState(info, anyParent);
+            return createDeepPropertyState(info, anyParent, null);
         } catch (PathNotFoundException e) {
             throw new ItemNotFoundException(e.getMessage());
         }
@@ -187,7 +191,7 @@
     /**
      *
      * @param nodeId
-     * @param itemInfos
+     * @param infos
      * @param entry
      * @return
      * @throws ItemNotFoundException
@@ -200,13 +204,14 @@
             throws ItemNotFoundException, RepositoryException {
         NodeState nodeState;
         // first entry in the iterator is the originally requested Node.
-        if (itemInfos.hasNext()) {
-            NodeInfo first = (NodeInfo) itemInfos.next();
+        ItemInfos infos = new ItemInfos(itemInfos);
+        if (infos.hasNext()) {
+            NodeInfo first = (NodeInfo) infos.next();
             if (isDeep) {
                 // for a deep state, the hierarchy entry does not correspond to
                 // the given NodeEntry -> retrieve NodeState before executing
                 // validation check.
-                nodeState = createDeepNodeState(first, entry);
+                nodeState = createDeepNodeState(first, entry, infos);
                 assertMatchingPath(first, nodeState.getNodeEntry());
             } else {
                 // 'isDeep' == false -> the given NodeEntry must match to the
@@ -222,12 +227,12 @@
         // deal with all additional ItemInfos that may be present.
         NodeEntry parentEntry = nodeState.getNodeEntry();
         if (parentEntry.getStatus() != Status.INVALIDATED) {
-            while (itemInfos.hasNext()) {
-                ItemInfo info = (ItemInfo) itemInfos.next();
+            while (infos.hasNext()) {
+                ItemInfo info = (ItemInfo) infos.next();
                 if (info.denotesNode()) {
-                    createDeepNodeState((NodeInfo) info, parentEntry);
+                    createDeepNodeState((NodeInfo) info, parentEntry, infos);
                 } else {
-                    createDeepPropertyState((PropertyInfo) info, parentEntry);
+                    createDeepPropertyState((PropertyInfo) info, parentEntry, infos);
                 }
             }
         }
@@ -245,7 +250,7 @@
      */
     private NodeState createNodeState(NodeInfo info, NodeEntry entry) throws ItemNotFoundException, RepositoryException {
         // make sure the entry has the correct ItemId
-        // this make not be the case, if the hierachy has not been completely
+        // this make not be the case, if the hierarchy has not been completely
         // resolved yet -> if uniqueID is present, set it on this entry or on
         // the appropriate parent entry
         String uniqueID = info.getId().getUniqueID();
@@ -315,10 +320,13 @@
      *
      * @param info
      * @param anyParent
+     * @param infos
      * @return
      * @throws RepositoryException
      */
-    private NodeState createDeepNodeState(NodeInfo info, NodeEntry anyParent) throws RepositoryException {
+    private NodeState createDeepNodeState(NodeInfo info, NodeEntry anyParent, ItemInfos infos)
+            throws RepositoryException {
+
         try {
             // node for nodeId exists -> build missing entries in hierarchy
             // Note, that the path contained in NodeId does not reveal which
@@ -331,7 +339,7 @@
             for (int i = 0; i < missingElems.length; i++) {
                 Name name = missingElems[i].getName();
                 int index = missingElems[i].getNormalizedIndex();
-                entry = createIntermediateNodeEntry(entry, name, index);
+                entry = createIntermediateNodeEntry(entry, name, index, infos);
             }
             if (entry == anyParent) {
                 throw new RepositoryException("Internal error while getting deep itemState");
@@ -346,10 +354,13 @@
      *
      * @param info
      * @param anyParent
+     * @param infos
      * @return
      * @throws RepositoryException
      */
-    private PropertyState createDeepPropertyState(PropertyInfo info, NodeEntry anyParent) throws RepositoryException {
+    private PropertyState createDeepPropertyState(PropertyInfo info, NodeEntry anyParent,
+            ItemInfos infos) throws RepositoryException {
+
         try {
             // prop for propertyId exists -> build missing entries in hierarchy
             // Note, that the path contained in PropertyId does not reveal which
@@ -358,12 +369,13 @@
             Path relPath = anyParentPath.computeRelativePath(info.getPath());
             Path.Element[] missingElems = relPath.getElements();
             NodeEntry entry = anyParent;
+
             int i = 0;
             // NodeEntries except for the very last 'missingElem'
             while (i < missingElems.length - 1) {
                 Name name = missingElems[i].getName();
                 int index = missingElems[i].getNormalizedIndex();
-                entry = createIntermediateNodeEntry(entry, name, index);
+                entry = createIntermediateNodeEntry(entry, name, index, infos);
                 i++;
             }
             // create PropertyEntry for the last element if not existing yet
@@ -383,10 +395,34 @@
      * @param parentEntry
      * @param name
      * @param index
+     * @param infos
      * @return
      * @throws RepositoryException
      */
-    private static NodeEntry createIntermediateNodeEntry(NodeEntry parentEntry, Name name, int index) throws RepositoryException {
+    private static NodeEntry createIntermediateNodeEntry(NodeEntry parentEntry, Name name,
+            int index, ItemInfos infos) throws RepositoryException {
+
+        if (infos != null && !parentEntry.hasNodeEntry(name, index)) {
+            Iterator childInfos = infos.getChildInfos(parentEntry.getWorkspaceId());
+            if (childInfos != null) {
+                parentEntry.setNodeEntries(childInfos);
+            }
+        }
+
+        NodeEntry entry = parentEntry.getNodeEntry(name, index, true);
+        if (entry == null) {
+            entry = parentEntry.addNodeEntry(name, null, index);
+        }
+        return entry;
+
+        /*
+        if ((infos != null) && ((NodeEntryImpl) parentEntry).childNodeEntries == null) {
+            Iterator childInfos = infos.getChildInfos(parentEntry.getWorkspaceId());
+            if (childInfos != null) {
+                parentEntry.setNodeEntries(childInfos);
+            }
+        }
+
         NodeEntry entry;
         if (parentEntry.hasNodeEntry(name, index)) {
             entry = parentEntry.getNodeEntry(name, index);
@@ -394,6 +430,7 @@
             entry = parentEntry.addNodeEntry(name, null, index);
         }
         return entry;
+        */
     }
 
     /**
@@ -434,4 +471,66 @@
         return parent;
     }
 
+
+    private class ItemInfos implements Iterator {
+        private final List prefetchQueue = new LinkedList();
+        private final Map nodeInfos = new HashMap();
+        private final Iterator infos;
+
+        public ItemInfos(Iterator infos) {
+            super();
+            this.infos = infos;
+        }
+
+        public Iterator getChildInfos(NodeId nodeId) {
+            NodeInfo nodeInfo = (NodeInfo) nodeInfos.get(nodeId);
+            while (nodeInfo == null && prefetch()) {
+                nodeInfo = (NodeInfo) nodeInfos.get(nodeId);
+            }
+            return nodeInfo == null? null : nodeInfo.getChildInfos();
+        }
+
+        // -----------------------------------------------------< Iterator >---
+
+        public boolean hasNext() {
+            if (!prefetchQueue.isEmpty()) {
+                return true;
+            }
+            else {
+                return prefetch();
+            }
+        }
+
+        public Object next() {
+            if (prefetchQueue.isEmpty()) {
+                throw new NoSuchElementException();
+            }
+            else {
+                return prefetchQueue.remove(0);
+            }
+        }
+
+        public void remove() {
+            throw new UnsupportedOperationException();
+        }
+
+        // -----------------------------------------------------< private >---
+
+        private boolean prefetch() {
+            if (!infos.hasNext()) {
+                return false;
+            }
+            else {
+                ItemInfo info = (ItemInfo) infos.next();
+                prefetchQueue.add(info);
+                if (info.denotesNode()) {
+                    NodeInfo nodeInfo = (NodeInfo) info;
+                    nodeInfos.put(nodeInfo.getId(), nodeInfo);
+                }
+                return true;
+            }
+        }
+
+    }
+
 }
