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 2010/02/24 15:49:13 UTC

svn commit: r915810 [1/2] - in /jackrabbit/trunk: jackrabbit-jcr-client/src/test/java/org/apache/jackrabbit/client/ jackrabbit-jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/ jackrabbit-jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/hierarchy...

Author: mduerig
Date: Wed Feb 24 14:49:12 2010
New Revision: 915810

URL: http://svn.apache.org/viewvc?rev=915810&view=rev
Log:
JCR-2498: Implement caching mechanism for ItemInfo batches

Added:
    jackrabbit/trunk/jackrabbit-spi-commons/src/main/java/org/apache/jackrabbit/spi/commons/ItemInfoCacheImpl.java   (with props)
    jackrabbit/trunk/jackrabbit-spi/src/main/java/org/apache/jackrabbit/spi/ItemInfoCache.java   (with props)
Modified:
    jackrabbit/trunk/jackrabbit-jcr-client/src/test/java/org/apache/jackrabbit/client/RepositoryFactoryImplTest.java
    jackrabbit/trunk/jackrabbit-jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/WorkspaceManager.java
    jackrabbit/trunk/jackrabbit-jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/hierarchy/ChildNodeAttic.java
    jackrabbit/trunk/jackrabbit-jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/hierarchy/EntryFactory.java
    jackrabbit/trunk/jackrabbit-jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/hierarchy/HierarchyEntry.java
    jackrabbit/trunk/jackrabbit-jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/hierarchy/HierarchyEntryImpl.java
    jackrabbit/trunk/jackrabbit-jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/hierarchy/NodeEntryImpl.java
    jackrabbit/trunk/jackrabbit-jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/hierarchy/PropertyEntryImpl.java
    jackrabbit/trunk/jackrabbit-jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/state/ChangeLog.java
    jackrabbit/trunk/jackrabbit-jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/state/WorkspaceItemStateFactory.java
    jackrabbit/trunk/jackrabbit-jcr2spi/src/test/java/org/apache/jackrabbit/jcr2spi/AbstractJCR2SPITest.java
    jackrabbit/trunk/jackrabbit-jcr2spi/src/test/java/org/apache/jackrabbit/jcr2spi/AbstractRepositoryConfig.java
    jackrabbit/trunk/jackrabbit-jcr2spi/src/test/java/org/apache/jackrabbit/jcr2spi/ExternalModificationTest.java
    jackrabbit/trunk/jackrabbit-jcr2spi/src/test/java/org/apache/jackrabbit/jcr2spi/GetPropertyTest.java
    jackrabbit/trunk/jackrabbit-jcr2spi/src/test/java/org/apache/jackrabbit/jcr2spi/MoveReferenceableTest.java
    jackrabbit/trunk/jackrabbit-jcr2spi/src/test/java/org/apache/jackrabbit/jcr2spi/MoveTest.java
    jackrabbit/trunk/jackrabbit-jcr2spi/src/test/java/org/apache/jackrabbit/jcr2spi/ReorderMoveTest.java
    jackrabbit/trunk/jackrabbit-jcr2spi/src/test/java/org/apache/jackrabbit/jcr2spi/benchmark/ReadPerformanceTest.java
    jackrabbit/trunk/jackrabbit-spi-commons/src/main/java/org/apache/jackrabbit/spi/commons/AbstractReadableRepositoryService.java
    jackrabbit/trunk/jackrabbit-spi-commons/src/main/java/org/apache/jackrabbit/spi/commons/logging/RepositoryServiceLogger.java
    jackrabbit/trunk/jackrabbit-spi/src/main/java/org/apache/jackrabbit/spi/RepositoryService.java
    jackrabbit/trunk/jackrabbit-spi2dav/src/main/java/org/apache/jackrabbit/spi2dav/RepositoryServiceImpl.java
    jackrabbit/trunk/jackrabbit-spi2dav/src/main/java/org/apache/jackrabbit/spi2dav/Spi2davRepositoryServiceFactory.java
    jackrabbit/trunk/jackrabbit-spi2dav/src/main/java/org/apache/jackrabbit/spi2davex/RepositoryServiceImpl.java
    jackrabbit/trunk/jackrabbit-spi2dav/src/main/java/org/apache/jackrabbit/spi2davex/Spi2davexRepositoryServiceFactory.java
    jackrabbit/trunk/jackrabbit-spi2jcr/src/main/java/org/apache/jackrabbit/spi2jcr/RepositoryServiceImpl.java
    jackrabbit/trunk/jackrabbit-spi2jcr/src/main/java/org/apache/jackrabbit/spi2jcr/Spi2jcrRepositoryServiceFactory.java

Modified: jackrabbit/trunk/jackrabbit-jcr-client/src/test/java/org/apache/jackrabbit/client/RepositoryFactoryImplTest.java
URL: http://svn.apache.org/viewvc/jackrabbit/trunk/jackrabbit-jcr-client/src/test/java/org/apache/jackrabbit/client/RepositoryFactoryImplTest.java?rev=915810&r1=915809&r2=915810&view=diff
==============================================================================
--- jackrabbit/trunk/jackrabbit-jcr-client/src/test/java/org/apache/jackrabbit/client/RepositoryFactoryImplTest.java (original)
+++ jackrabbit/trunk/jackrabbit-jcr-client/src/test/java/org/apache/jackrabbit/client/RepositoryFactoryImplTest.java Wed Feb 24 14:49:12 2010
@@ -60,6 +60,7 @@
 import org.apache.jackrabbit.spi.IdFactory;
 import org.apache.jackrabbit.spi.ItemId;
 import org.apache.jackrabbit.spi.ItemInfo;
+import org.apache.jackrabbit.spi.ItemInfoCache;
 import org.apache.jackrabbit.spi.LockInfo;
 import org.apache.jackrabbit.spi.Name;
 import org.apache.jackrabbit.spi.NameFactory;
@@ -233,6 +234,7 @@
      * Dummy RepositoryService
      */
     private static final class RepositoryServiceImpl implements RepositoryService {
+
         public static final RepositoryService INSTANCE = new RepositoryServiceImpl();
 
         private RepositoryServiceImpl() {
@@ -255,6 +257,10 @@
             return null;
         }
 
+        public ItemInfoCache getItemInfoCache(SessionInfo sessionInfo) throws RepositoryException {
+            return null;
+        }
+
         public Map<String, QValue[]> getRepositoryDescriptors() throws RepositoryException {
             return Collections.emptyMap();
         }

Modified: jackrabbit/trunk/jackrabbit-jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/WorkspaceManager.java
URL: http://svn.apache.org/viewvc/jackrabbit/trunk/jackrabbit-jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/WorkspaceManager.java?rev=915810&r1=915809&r2=915810&view=diff
==============================================================================
--- jackrabbit/trunk/jackrabbit-jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/WorkspaceManager.java (original)
+++ jackrabbit/trunk/jackrabbit-jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/WorkspaceManager.java Wed Feb 24 14:49:12 2010
@@ -98,6 +98,7 @@
 import org.apache.jackrabbit.spi.EventFilter;
 import org.apache.jackrabbit.spi.IdFactory;
 import org.apache.jackrabbit.spi.ItemId;
+import org.apache.jackrabbit.spi.ItemInfoCache;
 import org.apache.jackrabbit.spi.LockInfo;
 import org.apache.jackrabbit.spi.Name;
 import org.apache.jackrabbit.spi.NameFactory;
@@ -166,6 +167,11 @@
      */
     private Subscription subscription;
 
+    /**
+     * A cache for item infos as supplied by {@link RepositoryService#getItemInfoCache(SessionInfo)}
+     */
+    private ItemInfoCache cache;
+
     public WorkspaceManager(RepositoryService service, SessionInfo sessionInfo,
                             CacheBehaviour cacheBehaviour, int pollTimeout,
                             boolean enableObservation)
@@ -450,8 +456,11 @@
     /**
      * @return a new instance of <code>TransientItemStateFactory</code>.
      */
-    private TransientItemStateFactory createItemStateFactory() {
-        WorkspaceItemStateFactory isf = new WorkspaceItemStateFactory(service, sessionInfo, getItemDefinitionProvider());
+    private TransientItemStateFactory createItemStateFactory() throws RepositoryException {
+        cache = service.getItemInfoCache(sessionInfo);
+        WorkspaceItemStateFactory isf = new WorkspaceItemStateFactory(service, sessionInfo,
+                getItemDefinitionProvider(), cache);
+
         TransientItemStateFactory tisf = new TransientISFactory(isf, getItemDefinitionProvider());
         return tisf;
     }
@@ -619,6 +628,7 @@
                 service.dispose(subscription);
             }
             service.dispose(sessionInfo);
+            cache.dispose();
         } catch (Exception e) {
             log.warn("Exception while disposing WorkspaceManager: " + e);
         } finally {

Modified: jackrabbit/trunk/jackrabbit-jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/hierarchy/ChildNodeAttic.java
URL: http://svn.apache.org/viewvc/jackrabbit/trunk/jackrabbit-jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/hierarchy/ChildNodeAttic.java?rev=915810&r1=915809&r2=915810&view=diff
==============================================================================
--- jackrabbit/trunk/jackrabbit-jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/hierarchy/ChildNodeAttic.java (original)
+++ jackrabbit/trunk/jackrabbit-jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/hierarchy/ChildNodeAttic.java Wed Feb 24 14:49:12 2010
@@ -98,7 +98,7 @@
         if (uniqueId == null) {
             throw new IllegalArgumentException();
         }
-        for (NodeEntryImpl ne : attic) {
+        for (NodeEntry ne : attic) {
             if (uniqueId.equals(ne.getUniqueID())) {
                 return ne;
             }
@@ -111,7 +111,7 @@
         attic.add(movedEntry);
     }
 
-    boolean remove(NodeEntryImpl movedEntry) {
+    boolean remove(NodeEntry movedEntry) {
         if (attic.contains(movedEntry)) {
             return attic.remove(movedEntry);
         }

Modified: jackrabbit/trunk/jackrabbit-jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/hierarchy/EntryFactory.java
URL: http://svn.apache.org/viewvc/jackrabbit/trunk/jackrabbit-jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/hierarchy/EntryFactory.java?rev=915810&r1=915809&r2=915810&view=diff
==============================================================================
--- jackrabbit/trunk/jackrabbit-jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/hierarchy/EntryFactory.java (original)
+++ jackrabbit/trunk/jackrabbit-jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/hierarchy/EntryFactory.java Wed Feb 24 14:49:12 2010
@@ -166,20 +166,20 @@
          * <code>entry</code>. Implementors may choose to delay the actual call to
          * {@link org.apache.jackrabbit.jcr2spi.state.ItemState#invalidate()} for this
          * <code>entry</code> and for any of its child entries. They need to ensure however that
-         * {@link #applyPending(NodeEntry)} properly invalidates the respective state when called.
+         * {@link #applyPending(HierarchyEntry)} properly invalidates the respective state when called.
          *
-         * @param entry The <code>NodeEntry</code> to invalidate.
+         * @param entry The <code>HierarchyEntry</code> to invalidate.
          * @param recursive Invalidate state of child entries if <code>true</code>.
          */
-        public void invalidate(NodeEntry entry, boolean recursive);
+        public void invalidate(HierarchyEntry entry, boolean recursive);
 
         /**
          * Apply any pending {@link org.apache.jackrabbit.jcr2spi.state.ItemState#invalidate()
-         * invalidation} of the underyling {@link org.apache.jackrabbit.jcr2spi.state.ItemState} of
+         * invalidation} of the underlying {@link org.apache.jackrabbit.jcr2spi.state.ItemState} of
          * this <code>entry</code>.
          *
          * @param entry The affected <code>NodeEntry</code>.
          */
-        public void applyPending(NodeEntry entry);
+        public void applyPending(HierarchyEntry entry);
     }
 }

Modified: jackrabbit/trunk/jackrabbit-jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/hierarchy/HierarchyEntry.java
URL: http://svn.apache.org/viewvc/jackrabbit/trunk/jackrabbit-jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/hierarchy/HierarchyEntry.java?rev=915810&r1=915809&r2=915810&view=diff
==============================================================================
--- jackrabbit/trunk/jackrabbit-jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/hierarchy/HierarchyEntry.java (original)
+++ jackrabbit/trunk/jackrabbit-jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/hierarchy/HierarchyEntry.java Wed Feb 24 14:49:12 2010
@@ -23,6 +23,7 @@
 import org.apache.jackrabbit.jcr2spi.operation.Operation;
 import org.apache.jackrabbit.jcr2spi.state.ItemState;
 import org.apache.jackrabbit.jcr2spi.state.Status;
+import org.apache.jackrabbit.spi.ItemInfoCache;
 import org.apache.jackrabbit.spi.Name;
 import org.apache.jackrabbit.spi.Path;
 
@@ -173,4 +174,11 @@
      */
     public void complete(Operation transientOperation) throws RepositoryException;
 
+    /**
+     * The required generation of this <code>HierarchyEntry</code> . This is used by the
+     * {@link ItemInfoCache} to determine wheter an item info in the cache is up to date or not.
+     * That is whether the generation of the item info in the cache is the same or more recent
+     * as the required generation of this entry.
+     */
+    public long getGeneration();
 }

Modified: jackrabbit/trunk/jackrabbit-jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/hierarchy/HierarchyEntryImpl.java
URL: http://svn.apache.org/viewvc/jackrabbit/trunk/jackrabbit-jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/hierarchy/HierarchyEntryImpl.java?rev=915810&r1=915809&r2=915810&view=diff
==============================================================================
--- jackrabbit/trunk/jackrabbit-jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/hierarchy/HierarchyEntryImpl.java (original)
+++ jackrabbit/trunk/jackrabbit-jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/hierarchy/HierarchyEntryImpl.java Wed Feb 24 14:49:12 2010
@@ -29,6 +29,7 @@
 import org.apache.jackrabbit.jcr2spi.state.TransientItemStateFactory;
 import org.apache.jackrabbit.jcr2spi.state.ItemState.MergeResult;
 import org.apache.jackrabbit.spi.IdFactory;
+import org.apache.jackrabbit.spi.ItemInfoCache;
 import org.apache.jackrabbit.spi.Name;
 import org.apache.jackrabbit.spi.Path;
 import org.apache.jackrabbit.spi.PathFactory;
@@ -44,6 +45,13 @@
     private static Logger log = LoggerFactory.getLogger(HierarchyEntryImpl.class);
 
     /**
+     * The required generation of this entry. This is used by the {@link ItemInfoCache} to determine
+     * wheter an item info in the cache is up to date or not. That is whether the generation of the
+     * item info in the cache is the same or more recent as the required generation of this entry.
+     */
+    public long generation;
+
+    /**
      * Cached soft reference to the target ItemState.
      */
     private Reference<ItemState> target;
@@ -170,6 +178,24 @@
         return state;
     }
 
+    protected EntryFactory.InvalidationStrategy getInvalidationStrategy() {
+        return factory.getInvalidationStrategy();
+    }
+
+    /**
+     * Invalidates the underlying {@link ItemState}. If <code>recursive</code> is
+     * true also invalidates the underlying item states of all child entries.
+     * @param recursive
+     */
+    protected void invalidateInternal(boolean recursive) {
+        ItemState state = internalGetItemState();
+        if (state == null) {
+            log.debug("Skip invalidation for unresolved HierarchyEntry " + name);
+        } else {
+            state.invalidate();
+        }
+    }
+
     //-----------------------------------------------------< HierarchyEntry >---
     /**
      * @see HierarchyEntry#getName()
@@ -260,12 +286,11 @@
      * @see HierarchyEntry#invalidate(boolean)
      */
     public void invalidate(boolean recursive) {
-        ItemState state = internalGetItemState();
-        if (state == null) {
-            log.debug("Skip invalidation for unresolved HierarchyEntry " + name);
-        } else {
-            state.invalidate();
-        }
+        getInvalidationStrategy().invalidate(this, recursive);
+    }
+
+    public void calculateStatus() {
+        getInvalidationStrategy().applyPending(this);
     }
 
     /**
@@ -396,7 +421,13 @@
         internalRemove(false);
     }
 
+    public long getGeneration() {
+        calculateStatus();
+        return generation;
+    }
+
     //--------------------------------------------------------------------------
+
     /**
      * @param staleParent
      */
@@ -421,4 +452,109 @@
             }
         }
     }
+
+    // ----------------------------------------------< InvalidationStrategy >---
+    /**
+     * An implementation of <code>InvalidationStrategy</code> which lazily invalidates
+     * the underlying {@link ItemState}s.
+     */
+    static class LazyInvalidation implements EntryFactory.InvalidationStrategy {
+
+        /**
+         * Marker for entries with a pending recursive invalidation.
+         */
+        private static long INVALIDATION_PENDING = -1;
+
+        /**
+         * Number of the current generation
+         */
+        private long currentGeneration;
+
+        /**
+         * Increment for obtaining the next generation from the current generation.
+         */
+        private int nextGeneration;
+
+        /**
+         * A recursive invalidation is being processed if <code>true</code>.
+         * This flag is for preventing re-entrance.
+         */
+        private boolean invalidating;
+
+        /**
+         * Records a pending recursive {@link ItemState#invalidate() invalidation} for
+         * <code>entry</code> if <code>recursive</code> is <code>true</code>. Otherwise
+         * invalidates the entry right away.
+         * {@inheritDoc}
+         */
+        public void invalidate(HierarchyEntry entry, boolean recursive) {
+            HierarchyEntryImpl he = (HierarchyEntryImpl) entry;
+            if (recursive) {
+                he.generation = INVALIDATION_PENDING;
+                if (!invalidating) {
+                    nextGeneration = 1;
+                }
+            } else {
+                if (!invalidating) {
+                    nextGeneration = 1;
+                }
+                he.invalidateInternal(false);
+            }
+        }
+
+        /**
+         * Checks whether <code>entry</code> itself has a invalidation pending.
+         * If so, the <code>entry</code> is invalidated. Otherwise check
+         * whether an invalidation occurred after the entry has last been
+         * invalidated. If so, search the path to the root for an originator of
+         * the pending invalidation.
+         * If such an originator is found, invalidate each entry on the path.
+         * Otherwise this method does nothing.
+         * {@inheritDoc}
+         */
+        public void applyPending(HierarchyEntry entry) {
+            if (!invalidating) {
+                invalidating = true;
+                currentGeneration += nextGeneration;
+                nextGeneration = 0;
+                try {
+                    HierarchyEntryImpl he = (HierarchyEntryImpl) entry;
+                    if (he.generation == INVALIDATION_PENDING) {
+                        he.invalidateInternal(true);
+                        he.generation = currentGeneration;
+                    } else if (he.generation < currentGeneration) {
+                        resolvePendingInvalidation(he);
+                    }
+                } finally {
+                    invalidating = false;
+                }
+            }
+        }
+
+        /**
+         * Search the path to the root for an originator of a pending invalidation of
+         * this <code>entry</code>. If such an originator is found, invalidate each
+         * entry on the path. Otherwise do nothing.
+         *
+         * @param entry
+         */
+        private void resolvePendingInvalidation(HierarchyEntryImpl entry) {
+            if (entry != null) {
+
+                // First recursively travel up to the first parent node
+                // which has invalidation pending or to the root node if
+                // no such node exists.
+                if (entry.generation != INVALIDATION_PENDING) {
+                    resolvePendingInvalidation(entry.parent);
+                }
+
+                // Then travel the path backwards invalidating as required
+                if (entry.generation == INVALIDATION_PENDING) {
+                    entry.invalidateInternal(true);
+                }
+                entry.generation = currentGeneration;
+            }
+        }
+    }
+
 }

Modified: jackrabbit/trunk/jackrabbit-jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/hierarchy/NodeEntryImpl.java
URL: http://svn.apache.org/viewvc/jackrabbit/trunk/jackrabbit-jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/hierarchy/NodeEntryImpl.java?rev=915810&r1=915809&r2=915810&view=diff
==============================================================================
--- jackrabbit/trunk/jackrabbit-jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/hierarchy/NodeEntryImpl.java (original)
+++ jackrabbit/trunk/jackrabbit-jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/hierarchy/NodeEntryImpl.java Wed Feb 24 14:49:12 2010
@@ -116,12 +116,6 @@
     private RevertInfo revertInfo;
 
     /**
-     * Information regarding the invalidation status of the underlying {@link ItemState}
-     * of this entry. The semantics depend on the {@link EntryFactory.InvalidationStrategy}.
-     */
-    private long invalidationStatus;
-
-    /**
      * Creates a new <code>NodeEntryImpl</code>
      *
      * @param parent    the <code>NodeEntry</code> that owns this child item
@@ -171,15 +165,6 @@
         return true;
     }
 
-    @Override
-    public void invalidate(boolean recursive) {
-        getInvalidationStrategy().invalidate(this, recursive);
-    }
-
-    public void calculateStatus() {
-        getInvalidationStrategy().applyPending(this);
-    }
-
     /**
      * If 'recursive' is true, the complete hierarchy below this entry is
      * traversed and reloaded. Otherwise only this entry and the direct
@@ -205,7 +190,7 @@
 
     /**
      * Calls {@link HierarchyEntryImpl#revert()} and moves all properties from the
-     * attic back into th properties map. If this HierarchyEntry has been
+     * attic back into the properties map. If this HierarchyEntry has been
      * transiently moved, it is in addition moved back to its old parent.
      * Similarly reordering of child node entries is reverted.
      *
@@ -885,7 +870,7 @@
            }
        }
 
-       NodeEntryImpl entry = (NodeEntryImpl) parent.childNodeEntries.remove(this);
+       NodeEntry entry = parent.childNodeEntries.remove(this);
        if (entry != this) {
            // should never occur
            String msg = "Internal error. Attempt to move NodeEntry (" + getName() + ") which is not connected to its parent.";
@@ -1083,7 +1068,7 @@
     void internalRemoveChildEntry(HierarchyEntry childEntry) {
         if (childEntry.denotesNode()) {
             if (childNodeEntries.remove((NodeEntry) childEntry) == null) {
-                childNodeAttic.remove((NodeEntryImpl) childEntry);
+                childNodeAttic.remove((NodeEntry) childEntry);
             }
         } else {
             Name propName = childEntry.getName();
@@ -1101,16 +1086,8 @@
         }
     }
 
-    private EntryFactory.InvalidationStrategy getInvalidationStrategy() {
-        return factory.getInvalidationStrategy();
-    }
-
-    /**
-     * Invalidates the underlying {@link ItemState}. If <code>recursive</code> is
-     * true also invalidates the underlying item states of all child entries.
-     * @param recursive
-     */
-    private void invalidateInternal(boolean recursive) {
+    @Override
+    protected void invalidateInternal(boolean recursive) {
         if (recursive) {
             // invalidate all child entries including properties present in the
             // attic (removed props shadowed by a new property with the same name).
@@ -1119,7 +1096,7 @@
                 ce.invalidate(true);
             }
         }
-        super.invalidate(true);
+        super.invalidateInternal(true);
     }
 
     /**
@@ -1651,15 +1628,15 @@
         private final NodeEntryImpl oldParent;
         private final Name oldName;
         private final int oldIndex;
-        private final NodeEntryImpl oldSuccessor;
-        private final NodeEntryImpl oldPredecessor;
+        private final NodeEntry oldSuccessor;
+        private final NodeEntry oldPredecessor;
 
         private RevertInfo() throws InvalidItemStateException, RepositoryException {
             this.oldParent = parent;
             this.oldName = name;
             this.oldIndex = getIndex();
-            this.oldSuccessor = (NodeEntryImpl) ((ChildNodeEntriesImpl) parent.childNodeEntries).getNext(NodeEntryImpl.this);
-            this.oldPredecessor = (NodeEntryImpl) ((ChildNodeEntriesImpl) parent.childNodeEntries).getPrevious(NodeEntryImpl.this);
+            this.oldSuccessor = ((ChildNodeEntriesImpl) parent.childNodeEntries).getNext(NodeEntryImpl.this);
+            this.oldPredecessor = ((ChildNodeEntriesImpl) parent.childNodeEntries).getPrevious(NodeEntryImpl.this);
         }
 
         private boolean isMoved() {
@@ -1668,7 +1645,7 @@
 
         private void dispose(boolean persisted) {
             if (!persisted) {
-                NodeEntryImpl ne = NodeEntryImpl.this;
+                NodeEntry ne = NodeEntryImpl.this;
                 ChildNodeEntriesImpl parentCNEs = (ChildNodeEntriesImpl) parent.childNodeEntries;
                 parentCNEs.reorderAfter(ne, revertInfo.oldPredecessor);
                 try {
@@ -1684,133 +1661,4 @@
         }
     }
 
-    // ----------------------------------------------< InvalidationStrategy >---
-    /**
-     * An implementation of <code>InvalidationStrategy</code> which lazily invalidates
-     * the underlying {@link ItemState}s.
-     */
-    static class LazyInvalidation implements EntryFactory.InvalidationStrategy {
-
-        /**
-         * Marker for entries with a pending recursive invalidation.
-         */
-        private static long INVALIDATION_PENDING = -1;
-
-        /**
-         * Time stamp of the last time a recursive invalidation occurred.
-         */
-        private long lastInvalidation;
-
-        /**
-         * A recursive invalidation is being processed if <code>true</code>.
-         * This flag is for preventing re-entrance.
-         */
-        private boolean invalidating;
-
-        /**
-         * Actual time stamp
-         */
-        private long timeStamp;
-
-        /**
-         * @return  time stamp used to mark entries
-         */
-        private long getTimeStamp() {
-            return timeStamp++;
-        }
-
-        /**
-         * Records a pending recursive {@link ItemState#invalidate() invalidation} for
-         * <code>entry</code> if <code>recursive</code> is <code>true</code>. Otherwise
-         * invalidates the entry right away.
-         * {@inheritDoc}
-         */
-        public void invalidate(NodeEntry entry, boolean recursive) {
-            if (recursive) {
-                ((NodeEntryImpl)entry).invalidationStatus = INVALIDATION_PENDING;
-                if (!invalidating) {
-                    lastInvalidation = getTimeStamp();
-                }
-            } else {
-                ((NodeEntryImpl)entry).invalidateInternal(false);
-            }
-        }
-
-        /**
-         * Checks whether <code>entry</code> itself has a invalidation pending.
-         * If so, the <code>entry</code> is invalidated. Otherwise check
-         * whether an invalidation occurred after the entry has last been
-         * invalidated. If so, search the path to the root for an originator of
-         * the pending invalidation.
-         * If such an originator is found, invalidate each entry on the path.
-         * Otherwise this method does nothing.
-         * {@inheritDoc}
-         */
-        public void applyPending(NodeEntry entry) {
-            if (!invalidating) {
-                invalidating = true;
-                try {
-                    NodeEntryImpl ne = (NodeEntryImpl) entry;
-                    if (ne.invalidationStatus == INVALIDATION_PENDING) {
-                        ne.invalidateInternal(true);
-                        ne.invalidationStatus = getTimeStamp();
-                    } else if (ne.invalidationStatus <= lastInvalidation) {
-                        resolvePendingInvalidation(ne);
-                    }
-                } finally {
-                    invalidating = false;
-                }
-            }
-        }
-
-        /**
-         * Search the path to the root for an originator of a pending invalidation of
-         * this <code>entry</code>. If such an originator is found, invalidate each
-         * entry on the path. Otherwise do nothing.
-         *
-         * @param entry
-         */
-        private void resolvePendingInvalidation(NodeEntryImpl entry) {
-            if (entry != null) {
-
-                // First recursively travel up to the first parent node
-                // which has invalidation pending or to the root node if
-                // no such node exists.
-                if (entry.invalidationStatus != INVALIDATION_PENDING) {
-                    resolvePendingInvalidation(entry.parent);
-                }
-
-                // Then travel the path backwards invalidating as required
-                if (entry.invalidationStatus == INVALIDATION_PENDING) {
-                    entry.invalidateInternal(true);
-                }
-                entry.invalidationStatus = getTimeStamp();
-            }
-        }
-    }
-
-    /**
-     * An implementation of <code>InvalidationStrategy</code> which eagerly invalidates
-     * the underlying {@link ItemState}s.
-     */
-    static class EagerInvalidation implements EntryFactory.InvalidationStrategy {
-
-        /**
-         * Calls {@link ItemState#invalidate()} for the underlying item state of this
-         * <code>entry</code> and - if <code>recursive</code> is <code>true</code> -
-         * recursively for all item states of all child entries
-         * {@inheritDoc}
-         */
-        public void invalidate(NodeEntry entry, boolean recursive) {
-            ((NodeEntryImpl) entry).invalidateInternal(recursive);
-        }
-
-        /**
-         * Does nothing since invalidation has occurred already.
-         * {@inheritDoc}
-         */
-        public void applyPending(NodeEntry entry) {
-            // Empty
-        }
-    }
 }

Modified: jackrabbit/trunk/jackrabbit-jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/hierarchy/PropertyEntryImpl.java
URL: http://svn.apache.org/viewvc/jackrabbit/trunk/jackrabbit-jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/hierarchy/PropertyEntryImpl.java?rev=915810&r1=915809&r2=915810&view=diff
==============================================================================
--- jackrabbit/trunk/jackrabbit-jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/hierarchy/PropertyEntryImpl.java (original)
+++ jackrabbit/trunk/jackrabbit-jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/hierarchy/PropertyEntryImpl.java Wed Feb 24 14:49:12 2010
@@ -141,7 +141,4 @@
         }
     }
 
-    public void calculateStatus() {
-        parent.calculateStatus();
-    }
 }

Modified: jackrabbit/trunk/jackrabbit-jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/state/ChangeLog.java
URL: http://svn.apache.org/viewvc/jackrabbit/trunk/jackrabbit-jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/state/ChangeLog.java?rev=915810&r1=915809&r2=915810&view=diff
==============================================================================
--- jackrabbit/trunk/jackrabbit-jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/state/ChangeLog.java (original)
+++ jackrabbit/trunk/jackrabbit-jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/state/ChangeLog.java Wed Feb 24 14:49:12 2010
@@ -137,7 +137,7 @@
                     break;
                 case Status.INVALIDATED:
                 case Status.REMOVED:
-                    // ignore. operations already have been completed
+                    he.invalidate(false);
                     break;
             }
         }

Modified: jackrabbit/trunk/jackrabbit-jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/state/WorkspaceItemStateFactory.java
URL: http://svn.apache.org/viewvc/jackrabbit/trunk/jackrabbit-jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/state/WorkspaceItemStateFactory.java?rev=915810&r1=915809&r2=915810&view=diff
==============================================================================
--- jackrabbit/trunk/jackrabbit-jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/state/WorkspaceItemStateFactory.java (original)
+++ jackrabbit/trunk/jackrabbit-jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/state/WorkspaceItemStateFactory.java Wed Feb 24 14:49:12 2010
@@ -18,11 +18,8 @@
 
 import java.util.ArrayList;
 import java.util.Collections;
-import java.util.HashMap;
 import java.util.Iterator;
 import java.util.List;
-import java.util.Map;
-import java.util.NoSuchElementException;
 import java.util.Set;
 
 import javax.jcr.ItemExistsException;
@@ -37,6 +34,7 @@
 import org.apache.jackrabbit.spi.ChildInfo;
 import org.apache.jackrabbit.spi.IdFactory;
 import org.apache.jackrabbit.spi.ItemInfo;
+import org.apache.jackrabbit.spi.ItemInfoCache;
 import org.apache.jackrabbit.spi.Name;
 import org.apache.jackrabbit.spi.NodeId;
 import org.apache.jackrabbit.spi.NodeInfo;
@@ -46,6 +44,7 @@
 import org.apache.jackrabbit.spi.PropertyInfo;
 import org.apache.jackrabbit.spi.RepositoryService;
 import org.apache.jackrabbit.spi.SessionInfo;
+import org.apache.jackrabbit.spi.ItemInfoCache.Entry;
 import org.apache.jackrabbit.spi.commons.name.NameConstants;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
@@ -53,24 +52,24 @@
 /**
  * <code>WorkspaceItemStateFactory</code>...
  */
-public class WorkspaceItemStateFactory extends AbstractItemStateFactory implements ItemStateFactory {
-
+public class WorkspaceItemStateFactory extends AbstractItemStateFactory {
     private static Logger log = LoggerFactory.getLogger(WorkspaceItemStateFactory.class);
 
     private final RepositoryService service;
     private final SessionInfo sessionInfo;
     private final ItemDefinitionProvider definitionProvider;
 
+    public final ItemInfoCache cache;
+
     public WorkspaceItemStateFactory(RepositoryService service, SessionInfo sessionInfo,
-                                     ItemDefinitionProvider definitionProvider) {
+            ItemDefinitionProvider definitionProvider, ItemInfoCache cache) throws RepositoryException {
+
         this.service = service;
         this.sessionInfo = sessionInfo;
         this.definitionProvider = definitionProvider;
+        this.cache = cache;
     }
 
-    /**
-     * @see ItemStateFactory#createRootState(NodeEntry)
-     */
     public NodeState createRootState(NodeEntry entry) throws ItemNotFoundException, RepositoryException {
         IdFactory idFactory = service.getIdFactory();
         PathFactory pf = service.getPathFactory();
@@ -79,98 +78,182 @@
     }
 
     /**
-     * Creates the node with information retrieved from the
-     * <code>RepositoryService</code>.
-     *
-     * @see ItemStateFactory#createNodeState(NodeId,NodeEntry)
+     * Creates the node with information retrieved from the <code>RepositoryService</code>.
      */
-    public NodeState createNodeState(NodeId nodeId, NodeEntry entry)
-            throws ItemNotFoundException, RepositoryException {
-        // build new node state from server information
-        try {
-            Iterator<? extends ItemInfo> infos = service.getItemInfos(sessionInfo, nodeId);
-            NodeState nodeState = createItemStates(nodeId, infos, entry, false);
+    public NodeState createNodeState(NodeId nodeId, NodeEntry entry) throws ItemNotFoundException,
+            RepositoryException {
 
-            if (nodeState == null) {
-                throw new ItemNotFoundException("HierarchyEntry does not belong to any existing ItemInfo.");
+        try {
+            // Get item info from cache and use it if up to date
+            long generation = entry.getGeneration();
+            Entry<NodeInfo> cached = cache.getNodeInfo(nodeId);
+            NodeInfo info;
+            if (isUpToDate(cached, generation)) {
+                info = cached.info;
+            }
+            else {
+                // otherwise retreive item info from service and cache the whole batch
+                Iterator<? extends ItemInfo> infos = service.getItemInfos(sessionInfo, nodeId);
+                info = first(infos, cache, generation);
+                if (info == null) {
+                    throw new ItemNotFoundException("NodeId: " + nodeId);
+                }
             }
-            return nodeState;
-        } catch (PathNotFoundException e) {
-            throw new ItemNotFoundException(e.getMessage());
+
+            assertMatchingPath(info, entry);
+            return createNodeState(info, entry);
+        }
+        catch (PathNotFoundException e) {
+            throw new ItemNotFoundException(e);
         }
     }
 
     /**
-     * @see ItemStateFactory#createDeepNodeState(NodeId,NodeEntry)
+     * Creates the node with information retrieved from the <code>RepositoryService</code>.
+     * Intermediate entries are created as needed.
      */
-    public NodeState createDeepNodeState(NodeId nodeId, NodeEntry anyParent) throws ItemNotFoundException, RepositoryException {
+    public NodeState createDeepNodeState(NodeId nodeId, NodeEntry anyParent) throws ItemNotFoundException,
+            RepositoryException {
+
         try {
-            Iterator<? extends ItemInfo> infos = service.getItemInfos(sessionInfo, nodeId);
-            return createItemStates(nodeId, infos, anyParent, true);
-        } catch (PathNotFoundException e) {
-            throw new ItemNotFoundException(e.getMessage());
+            // Get item info from cache
+            Iterator<? extends ItemInfo> infos = null;
+            Entry<NodeInfo> cached = cache.getNodeInfo(nodeId);
+            NodeInfo info;
+            if (cached == null) {
+                // or from service if not in cache
+                infos = service.getItemInfos(sessionInfo, nodeId);
+                info = first(infos, null, 0);
+                if (info == null) {
+                    throw new ItemNotFoundException("NodeId: " + nodeId);
+                }
+            }
+            else {
+                info = cached.info;
+            }
+
+            // Build the hierarchy entry for the item info
+            HierarchyEntry entry = createHierarchyEntries(info, anyParent);
+            if (entry == null || !entry.denotesNode()) {
+                throw new ItemNotFoundException(
+                        "HierarchyEntry does not belong to any existing ItemInfo. No ItemState was created.");
+            }
+            else {
+                // Now we can check wheter the item info from the cache is up to date
+                long generation = entry.getGeneration();
+                if (isOutdated(cached, generation)) {
+                    // if not, retreive the item info from the service and put the whole batch into the cache
+                    infos = service.getItemInfos(sessionInfo, nodeId);
+                    info = first(infos, cache, generation);
+                }
+                else if (infos != null) {
+                    // Otherwise put the whole batch retreived from the service earlier into the cache
+                    cache.put(info, generation);
+                    first(infos, cache, generation);
+                }
+
+                assertMatchingPath(info, entry);
+                return createNodeState(info, (NodeEntry) entry);
+            }
+        }
+        catch (PathNotFoundException e) {
+            throw new ItemNotFoundException(e);
         }
     }
 
     /**
-     * Creates the PropertyState with information retrieved from the
-     * <code>RepositoryService</code>.
-     *
-     * @see ItemStateFactory#createPropertyState(PropertyId,PropertyEntry)
+     * Creates the PropertyState with information retrieved from the <code>RepositoryService</code>.
      */
-    public PropertyState createPropertyState(PropertyId propertyId,
-                                             PropertyEntry entry)
+    public PropertyState createPropertyState(PropertyId propertyId, PropertyEntry entry)
             throws ItemNotFoundException, RepositoryException {
+
         try {
-            PropertyInfo info = service.getPropertyInfo(sessionInfo, propertyId);
+            // Get item info from cache and use it if up to date
+            Entry<PropertyInfo> cached = cache.getPropertyInfo(propertyId);
+            PropertyInfo info;
+            if (isUpToDate(cached, entry.getGeneration())) {
+                info = cached.info;
+            }
+            else {
+                // otherwise retreive item info from service and cache the whole batch
+                info = service.getPropertyInfo(sessionInfo, propertyId);
+                cache.put(info, entry.getGeneration());
+            }
+
             assertMatchingPath(info, entry);
             return createPropertyState(info, entry);
-        } catch (PathNotFoundException e) {
-            throw new ItemNotFoundException(e.getMessage());
+        }
+        catch (PathNotFoundException e) {
+            throw new ItemNotFoundException(e);
         }
     }
 
     /**
-     * @see ItemStateFactory#createDeepPropertyState(PropertyId,NodeEntry)
+     * Creates the PropertyState with information retrieved from the <code>RepositoryService</code>.
+     * Intermediate entries are created as needed.
      */
-    public PropertyState createDeepPropertyState(PropertyId propertyId, NodeEntry anyParent) throws ItemNotFoundException, RepositoryException {
+    public PropertyState createDeepPropertyState(PropertyId propertyId, NodeEntry anyParent)
+            throws RepositoryException {
+
         try {
-            PropertyInfo info = service.getPropertyInfo(sessionInfo, propertyId);
-            PropertyState propState = createDeepPropertyState(info, anyParent, null);
-            assertValidState(propState, info);
-            return propState;
-        } catch (PathNotFoundException e) {
-            throw new ItemNotFoundException(e.getMessage());
+            // Get item info from cache
+            Entry<PropertyInfo> cached = cache.getPropertyInfo(propertyId);
+            PropertyInfo info;
+            if (cached == null) {
+                // or from service if not in cache
+                info = service.getPropertyInfo(sessionInfo, propertyId);
+            }
+            else {
+                info = cached.info;
+            }
+
+            // Build the hierarchy entry for the item info
+            HierarchyEntry entry = createHierarchyEntries(info, anyParent);
+
+            if (entry == null || entry.denotesNode()) {
+                throw new ItemNotFoundException(
+                        "HierarchyEntry does not belong to any existing ItemInfo. No ItemState was created.");
+            }
+            else {
+                // Now we can check wheter the item info from the cache is up to date
+                long generation = entry.getGeneration();
+                if (isOutdated(cached, generation)) {
+                    // if not, retreive the item info from the service and put the whole batch into the cache
+                    info = service.getPropertyInfo(sessionInfo, propertyId);
+                    cache.put(info, generation);
+                }
+
+                assertMatchingPath(info, entry);
+                return createPropertyState(info, (PropertyEntry) entry);
+            }
+
+        }
+        catch (PathNotFoundException e) {
+            throw new ItemNotFoundException(e);
         }
     }
 
-    /**
-     * @see ItemStateFactory#getChildNodeInfos(NodeId)
-     * @param nodeId
-     */
-    public Iterator<ChildInfo> getChildNodeInfos(NodeId nodeId)
-            throws ItemNotFoundException, RepositoryException {
+    public Iterator<ChildInfo> getChildNodeInfos(NodeId nodeId) throws ItemNotFoundException,
+            RepositoryException {
+
         return service.getChildInfos(sessionInfo, nodeId);
     }
 
-    /**
-     * @see ItemStateFactory#getNodeReferences(NodeState,org.apache.jackrabbit.spi.Name,boolean)
-     */
     public Iterator<PropertyId> getNodeReferences(NodeState nodeState, Name propertyName, boolean weak) {
         NodeEntry entry = nodeState.getNodeEntry();
-        // shortcut
-        if (entry.getUniqueID() == null
-                || !entry.hasPropertyEntry(NameConstants.JCR_UUID)) {
+
+        // Shortcut
+        if (entry.getUniqueID() == null || !entry.hasPropertyEntry(NameConstants.JCR_UUID)) {
             // for sure not referenceable
             Set<PropertyId> t = Collections.emptySet();
             return t.iterator();
         }
 
-        // nodestate has a unique ID and is potentially mix:referenceable
-        // => try to retrieve references
+        // Has a unique ID and is potentially mix:referenceable. Try to retrieve references
         try {
             return service.getReferences(sessionInfo, entry.getWorkspaceId(), propertyName, weak);
-        } catch (RepositoryException e) {
+        }
+        catch (RepositoryException e) {
             log.debug("Unable to determine references to {}", nodeState);
             Set<PropertyId> t = Collections.emptySet();
             return t.iterator();
@@ -178,80 +261,48 @@
     }
 
     //------------------------------------------------------------< private >---
+
     /**
-     *
-     * @param nodeId
-     * @param itemInfos
-     * @param entry
-     * @param isDeep
-     * @return
-     * @throws ItemNotFoundException
-     * @throws RepositoryException
+     * Returns the first item in the iterator if it exists and denotes a node.
+     * Otherwise returns <code>null</code>. If <code>cache</code> is not
+     * <code>null</code>, caches all items by the given <code>generation</code>.
+     * @param generation
      */
-    private synchronized NodeState createItemStates(NodeId nodeId, Iterator<? extends ItemInfo> itemInfos,
-                                                    NodeEntry entry, boolean isDeep)
-            throws ItemNotFoundException, RepositoryException {
-        NodeState nodeState;
-        ItemInfos infos = new ItemInfos(itemInfos);
-
-        // first entry in the iterator is the originally requested Node.
-        NodeInfo first = first(infos);
-        if (first == null) {
-            throw new ItemNotFoundException("Node with id " + nodeId + " could not be found.");
-        }
-        else {
-            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, infos);
-                assertValidState(nodeState, first);
-            } else {
-                // 'isDeep' == false -> the given NodeEntry must match to the
-                // first ItemInfo retrieved from the iterator.
-                assertMatchingPath(first, entry);
-                nodeState = createNodeState(first, entry);
+    private static NodeInfo first(Iterator<? extends ItemInfo> infos, ItemInfoCache cache, long generation) {
+        ItemInfo first = null;
+        if (infos.hasNext()) {
+            first = infos.next();
+            if (cache != null) {
+                cache.put(first, generation);
             }
-        }
 
-        // deal with all additional ItemInfos that may be present.
-        // Assuming locality of the itemInfos, we keep an estimate of a parent entry.
-        // This reduces the part of the hierarchy to traverse. For large batches this
-        // optimization results in about 25% speed up.
-        NodeEntry approxParentEntry = nodeState.getNodeEntry();
-        while (infos.hasNext()) {
-            ItemInfo info = infos.next();
-            if (info.denotesNode()) {
-                approxParentEntry = createDeepNodeState((NodeInfo) info, approxParentEntry, infos).getNodeEntry();
-            } else {
-                createDeepPropertyState((PropertyInfo) info, approxParentEntry, infos);
+            if (!first.denotesNode()) {
+                first = null;
             }
         }
-        return nodeState;
-    }
 
-    private static NodeInfo first(ItemInfos infos) {
-        if (infos.hasNext()) {
-            ItemInfo first = infos.next();
-            if (first.denotesNode()) {
-                return (NodeInfo) first;
+        if (cache != null) {
+            while (infos.hasNext()) {
+                cache.put(infos.next(), generation);
             }
         }
 
-        return null;
+        return (NodeInfo) first;
     }
 
     /**
-     * Creates the node with information retrieved from <code>info</code>.
+     * Create the node state with the information from <code>info</code>.
      *
      * @param info the <code>NodeInfo</code> to use to create the <code>NodeState</code>.
-     * @param entry
+     * @param entry  the hierarchy entry for of this state
      * @return the new <code>NodeState</code>.
      * @throws ItemNotFoundException
      * @throws RepositoryException
      */
-    private NodeState createNodeState(NodeInfo info, NodeEntry entry) throws ItemNotFoundException, RepositoryException {
-        // make sure the entry has the correct ItemId
+    private NodeState createNodeState(NodeInfo info, NodeEntry entry) throws ItemNotFoundException,
+            RepositoryException {
+
+        // Make sure the entry has the correct ItemId
         // this may 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
@@ -282,7 +333,7 @@
             entry.setPropertyEntries(propNames);
         } catch (ItemExistsException e) {
             // should not get here
-            log.warn("Internal error", e);
+            log.error("Internal error", e);
         }
 
         // unless the child-info are omitted by the SPI impl -> make sure
@@ -309,15 +360,16 @@
     }
 
     /**
-     * Creates the property with information retrieved from <code>info</code>.
+     * Create the property state with the information from <code>info</code>.
      *
-     * @param info   the <code>PropertyInfo</code> to use to create the
-     *               <code>PropertyState</code>.
-     * @param entry
+     * @param info the <code>PropertyInfo</code> to use to create the <code>PropertyState</code>.
+     * @param entry  the hierarchy entry for of this state
      * @return the new <code>PropertyState</code>.
+     * @throws RepositoryException
      */
     private PropertyState createPropertyState(PropertyInfo info, PropertyEntry entry)
             throws RepositoryException {
+
         // make sure uuid part of id is correct
         String uniqueID = info.getId().getUniqueID();
         if (uniqueID != null) {
@@ -349,136 +401,78 @@
     }
 
     /**
+     * Create missing hierarchy entries on the path from <code>anyParent</code> to the path
+     * of the <code>itemInfo</code>.
      *
      * @param info
      * @param anyParent
-     * @return
+     * @return the hierarchy entry for <code>info</code>
      * @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
-            // entries are missing -> calculate relative path.
-            Path anyParentPath = anyParent.getWorkspacePath();
-            Path relPath = anyParentPath.computeRelativePath(info.getPath());
-            Path.Element[] missingElems = relPath.getElements();
-
-            if (startsWithIllegalElement(missingElems)) {
-                log.error("Relative path to NodeEntry starts with illegal element -> ignore NodeInfo with path " + info.getPath());
-                return null;
-            }
-
-            NodeEntry entry = anyParent;
-            for (int i = 0; i < missingElems.length; i++) {
-                if (missingElems[i].denotesParent()) {
-                    // Walk up the hierarchy for 'negative' paths
-                    // until the smallest common root is found
-                    entry = entry.getParent();
+    private HierarchyEntry createHierarchyEntries(ItemInfo info, NodeEntry anyParent)
+            throws RepositoryException {
+
+        // Calculate relative path of missing entries
+        Path anyParentPath = anyParent.getWorkspacePath();
+        Path relPath = anyParentPath.computeRelativePath(info.getPath());
+        Path.Element[] missingElems = relPath.getElements();
+
+        NodeEntry entry = anyParent;
+        int last = missingElems.length - 1;
+        for (int i = 0; i <= last; i++) {
+            if (missingElems[i].denotesParent()) {
+                // Walk up the hierarchy for 'negative' paths
+                // until the smallest common root is found
+                entry = entry.getParent();
+            }
+            else if (missingElems[i].denotesName()) {
+                // Add missing elements starting from the smallest common root
+                Name name = missingElems[i].getName();
+                int index = missingElems[i].getNormalizedIndex();
+
+                if (i == last && !info.denotesNode()) {
+                    return entry.getOrAddPropertyEntry(name);
                 }
-                else if (missingElems[i].denotesName()) {
-                    // Add missing elements starting from the smallest common root
-                    Name name = missingElems[i].getName();
-                    int index = missingElems[i].getNormalizedIndex();
-                    entry = createIntermediateNodeEntry(entry, name, index, infos);
+                else {
+                    entry = createNodeEntry(entry, name, index);
                 }
-                // else denotesCurrent -> ignore
             }
-
-            return createNodeState(info, entry);
-        } catch (PathNotFoundException e) {
-            throw new ItemNotFoundException(e.getMessage());
         }
+        return entry;
     }
 
-    /**
-     *
-     * @param info
-     * @param anyParent
-     * @return
-     * @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
-            // entries are missing -> calculate relative path.
-            Path anyParentPath = anyParent.getWorkspacePath();
-            Path relPath = anyParentPath.computeRelativePath(info.getPath());
-            Path.Element[] missingElems = relPath.getElements();
-
-            // make sure the missing elements don't start with . or .. in which
-            // case the info is not within the tree as it is expected
-            // (see also JCR-1797)
-            if (startsWithIllegalElement(missingElems)) {
-                log.error("Relative path to PropertyEntry starts with illegal element -> ignore PropertyInfo with path " + info.getPath());
-                return null;
-            }
-
-            NodeEntry entry = anyParent;
-            int i = 0;
-            // NodeEntries except for the very last 'missingElem'
-            while (i < missingElems.length - 1) {
-                if (missingElems[i].denotesParent()) {
-                    // Walk up the hierarchy for 'negative' paths
-                    // until the smallest common root is found
-                    entry = entry.getParent();
-                }
-                else if (missingElems[i].denotesName()) {
-                    // Add missing elements starting from the smallest common root
-                    Name name = missingElems[i].getName();
-                    int index = missingElems[i].getNormalizedIndex();
-                    entry = createIntermediateNodeEntry(entry, name, index, infos);
-                }
-                // else denotesCurrent -> ignore
-                i++;
+    private NodeEntry createNodeEntry(NodeEntry parentEntry, Name name, int index) throws RepositoryException {
+        Entry<NodeInfo> cached = cache.getNodeInfo(parentEntry.getWorkspaceId());
+        if (isUpToDate(cached, parentEntry.getGeneration())) {
+            Iterator<ChildInfo> childInfos = cached.info.getChildInfos();
+            if (childInfos != null) {
+                parentEntry.setNodeEntries(childInfos);
             }
-            // create PropertyEntry for the last element if not existing yet
-            Name propName = missingElems[i].getName();
-            PropertyEntry propEntry = entry.getOrAddPropertyEntry(propName);
-
-            return createPropertyState(info, propEntry);
-        } catch (PathNotFoundException e) {
-            throw new ItemNotFoundException(e.getMessage());
         }
+
+        return parentEntry.getOrAddNodeEntry(name, index, null);
     }
 
     /**
-     *
-     * @param parentEntry
-     * @param name
-     * @param index
+     * Returns true iff <code>cache</code> is not <code>null</code> and
+     * the cached entry is up to date.
+     * @param cacheEntry
+     * @param generation
      * @return
-     * @throws RepositoryException
      */
-    private static NodeEntry createIntermediateNodeEntry(NodeEntry parentEntry,
-                                                         Name name, int index,
-                                                         ItemInfos infos) throws RepositoryException {
-        if (infos != null) {
-            Iterator<ChildInfo> childInfos = infos.getChildInfos(parentEntry.getWorkspaceId());
-            if (childInfos != null) {
-                parentEntry.setNodeEntries(childInfos);
-            }
-        }
-        NodeEntry entry = parentEntry.getOrAddNodeEntry(name, index, null);
-        return entry;
+    private static boolean isUpToDate(Entry<?> cacheEntry, long generation) {
+        return cacheEntry != null && cacheEntry.generation >= generation;
     }
 
     /**
-     * Validation check: make sure the state is not null (was really created)
-     * and matches with the specified ItemInfo (path).
-     *
-     * @param state
-     * @param info
-     * @throws ItemNotFoundException
-     * @throws RepositoryException
+     * Returns true iff <code>cache</code> is not <code>null</code> and
+     * the cached entry is not up to date.
+     * @param cacheEntry
+     * @param generation
+     * @return
      */
-    private static void assertValidState(ItemState state, ItemInfo info)
-            throws ItemNotFoundException, RepositoryException {
-        if (state == null) {
-            throw new ItemNotFoundException("HierarchyEntry does not belong to any existing ItemInfo. No ItemState was created.");
-        }
-        assertMatchingPath(info, state.getHierarchyEntry());
+    private static boolean isOutdated(Entry<?> cacheEntry, long generation) {
+        return cacheEntry != null && cacheEntry.generation < generation;
     }
 
     /**
@@ -488,35 +482,18 @@
      *
      * @param info
      * @param entry
-     * @throws ItemNotFoundException
      * @throws RepositoryException
      */
-    private static void assertMatchingPath(ItemInfo info, HierarchyEntry entry)
-            throws ItemNotFoundException, RepositoryException {
+    private static void assertMatchingPath(ItemInfo info, HierarchyEntry entry) throws RepositoryException {
         Path infoPath = info.getPath();
-        if (!infoPath.equals(entry.getWorkspacePath())) {
+        Path wspPath = entry.getWorkspacePath();
+        if (!infoPath.equals(wspPath)) {
             // TODO: handle external move of nodes (parents) identified by uniqueID
-            throw new ItemNotFoundException("HierarchyEntry does not belong the given ItemInfo.");
+            throw new ItemNotFoundException("HierarchyEntry " + infoPath + " does not match ItemInfo " + wspPath);
         }
     }
 
     /**
-     * Returns true if the given <code>missingElems</code> start with
-     * the root element, in which case the info is not within
-     * the tree as it is expected.
-     * See also #JCR-1797 for the corresponding enhancement request.
-     *
-     * @param missingElems
-     * @return true if the first element doesn't denote a named element.
-     */
-    private static boolean startsWithIllegalElement(Path.Element[] missingElems) {
-        if (missingElems.length > 0) {
-            return missingElems[0].denotesRoot();
-        }
-        return false;
-    }
-
-    /**
      * @param entry
      * @param degree
      * @return the ancestor entry at the specified degree.
@@ -529,91 +506,9 @@
             degree--;
         }
         if (degree != 0) {
+            log.error("Parent of degree {} does not exist.", degree);
             throw new IllegalArgumentException();
         }
         return parent;
     }
-
-    //--------------------------------------------------------------------------
-    /**
-     * Iterator
-     */
-    private class ItemInfos implements Iterator<ItemInfo> {
-
-        private final List<ItemInfo> prefetchQueue = new ArrayList<ItemInfo>();
-        private final Map<NodeId, NodeInfo> nodeInfos = new HashMap<NodeId, NodeInfo>();
-        private final Iterator<? extends ItemInfo> infos;
-
-        private ItemInfos(Iterator<? extends ItemInfo> infos) {
-            super();
-            this.infos = infos;
-        }
-
-        // ------------------------------------------------------< Iterator >---
-        /**
-         * @see Iterator#hasNext()
-         */
-        public boolean hasNext() {
-            if (!prefetchQueue.isEmpty()) {
-                return true;
-            } else {
-                return prefetch();
-            }
-        }
-
-        /**
-         * @see Iterator#next()
-         */
-        public ItemInfo next() {
-            if (prefetchQueue.isEmpty()) {
-                throw new NoSuchElementException();
-            } else {
-                ItemInfo next = prefetchQueue.remove(0);
-                if (next instanceof NodeInfo) {
-                    nodeInfos.remove(((NodeInfo) next).getId());
-                }
-                return next;
-            }
-        }
-
-        /**
-         * @see Iterator#remove()
-         */
-        public void remove() {
-            throw new UnsupportedOperationException();
-        }
-
-        // -------------------------------------------------------< private >---
-        /**
-         * @param parentId
-         * @return The children <code>NodeInfo</code>s for the parent identified
-         * by the given <code>parentId</code> or <code>null</code> if the parent
-         * has not been read yet, has already been processed (childInfo is up
-         * to date) or does not provide child infos.
-         */
-        private Iterator<ChildInfo> getChildInfos(NodeId parentId) {
-            NodeInfo nodeInfo = nodeInfos.get(parentId);
-            while (nodeInfo == null && prefetch()) {
-                nodeInfo = nodeInfos.get(parentId);
-            }
-            return nodeInfo == null? null : nodeInfo.getChildInfos();
-        }
-
-        /**
-         * @return <code>true</code> if the next info could be retrieved.
-         */
-        private boolean prefetch() {
-            if (infos.hasNext()) {
-                ItemInfo info = infos.next();
-                prefetchQueue.add(info);
-                if (info.denotesNode()) {
-                    NodeInfo nodeInfo = (NodeInfo) info;
-                    nodeInfos.put(nodeInfo.getId(), nodeInfo);
-                }
-                return true;
-            } else {
-                return false;
-            }
-        }
-    }
 }

Modified: jackrabbit/trunk/jackrabbit-jcr2spi/src/test/java/org/apache/jackrabbit/jcr2spi/AbstractJCR2SPITest.java
URL: http://svn.apache.org/viewvc/jackrabbit/trunk/jackrabbit-jcr2spi/src/test/java/org/apache/jackrabbit/jcr2spi/AbstractJCR2SPITest.java?rev=915810&r1=915809&r2=915810&view=diff
==============================================================================
--- jackrabbit/trunk/jackrabbit-jcr2spi/src/test/java/org/apache/jackrabbit/jcr2spi/AbstractJCR2SPITest.java (original)
+++ jackrabbit/trunk/jackrabbit-jcr2spi/src/test/java/org/apache/jackrabbit/jcr2spi/AbstractJCR2SPITest.java Wed Feb 24 14:49:12 2010
@@ -46,6 +46,7 @@
 import org.apache.jackrabbit.spi.IdFactory;
 import org.apache.jackrabbit.spi.ItemId;
 import org.apache.jackrabbit.spi.ItemInfo;
+import org.apache.jackrabbit.spi.ItemInfoCache;
 import org.apache.jackrabbit.spi.LockInfo;
 import org.apache.jackrabbit.spi.Name;
 import org.apache.jackrabbit.spi.NameFactory;
@@ -78,8 +79,9 @@
 public abstract class AbstractJCR2SPITest extends TestCase implements RepositoryService {
     private static final String DEFAULT_WSP = "default";
 
+    private RepositoryService repositoryService;
+
     protected ItemInfoStore itemInfoStore;
-    protected RepositoryService repositoryService;
     protected RepositoryConfig config;
     protected Repository repository;
 
@@ -223,7 +225,7 @@
     protected RepositoryConfig getRepositoryConfig() {
         return new AbstractRepositoryConfig() {
             public RepositoryService getRepositoryService() throws RepositoryException {
-                return repositoryService;
+                return AbstractJCR2SPITest.this;
             }
         };
     }
@@ -258,6 +260,9 @@
         return repositoryService.getRepositoryDescriptors();
     }
 
+    public ItemInfoCache getItemInfoCache(SessionInfo sessionInfo) throws RepositoryException {
+        return repositoryService.getItemInfoCache(sessionInfo);
+    }
 
     //-----------------------------------< SessionInfo creation and release >---
 

Modified: jackrabbit/trunk/jackrabbit-jcr2spi/src/test/java/org/apache/jackrabbit/jcr2spi/AbstractRepositoryConfig.java
URL: http://svn.apache.org/viewvc/jackrabbit/trunk/jackrabbit-jcr2spi/src/test/java/org/apache/jackrabbit/jcr2spi/AbstractRepositoryConfig.java?rev=915810&r1=915809&r2=915810&view=diff
==============================================================================
--- jackrabbit/trunk/jackrabbit-jcr2spi/src/test/java/org/apache/jackrabbit/jcr2spi/AbstractRepositoryConfig.java (original)
+++ jackrabbit/trunk/jackrabbit-jcr2spi/src/test/java/org/apache/jackrabbit/jcr2spi/AbstractRepositoryConfig.java Wed Feb 24 14:49:12 2010
@@ -16,10 +16,10 @@
  */
 package org.apache.jackrabbit.jcr2spi;
 
+import org.apache.jackrabbit.jcr2spi.config.CacheBehaviour;
+import org.apache.jackrabbit.jcr2spi.config.RepositoryConfig;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
-import org.apache.jackrabbit.jcr2spi.config.RepositoryConfig;
-import org.apache.jackrabbit.jcr2spi.config.CacheBehaviour;
 
 /**
  * <code>AbstractRepositoryConfig</code>...
@@ -29,6 +29,7 @@
     private static Logger log = LoggerFactory.getLogger(AbstractRepositoryConfig.class);
 
     private static final int DEFAULT_ITEM_CACHE_SIZE = 5000;
+    private static final int DEFAULT_INFO_CACHE_SIZE = 5000;
     private static final int DEFAULT_POLL_TIMEOUT = 3000; // 3 seconds
 
     public CacheBehaviour getCacheBehaviour() {
@@ -43,4 +44,8 @@
         return DEFAULT_POLL_TIMEOUT;
     }
 
+    public int getInfoCacheSize() {
+        return DEFAULT_INFO_CACHE_SIZE;
+    }
+
 }

Modified: jackrabbit/trunk/jackrabbit-jcr2spi/src/test/java/org/apache/jackrabbit/jcr2spi/ExternalModificationTest.java
URL: http://svn.apache.org/viewvc/jackrabbit/trunk/jackrabbit-jcr2spi/src/test/java/org/apache/jackrabbit/jcr2spi/ExternalModificationTest.java?rev=915810&r1=915809&r2=915810&view=diff
==============================================================================
--- jackrabbit/trunk/jackrabbit-jcr2spi/src/test/java/org/apache/jackrabbit/jcr2spi/ExternalModificationTest.java (original)
+++ jackrabbit/trunk/jackrabbit-jcr2spi/src/test/java/org/apache/jackrabbit/jcr2spi/ExternalModificationTest.java Wed Feb 24 14:49:12 2010
@@ -187,8 +187,7 @@
         String uuid = refNode.getUUID();
 
         Node refNode2 = (Node) testSession.getItem(refNode.getPath());
-        // TODO: for generic jsr 170 test isSame must be replace by 'Item.isSame'
-        assertSame(refNode2, testSession.getNodeByUUID(uuid));
+        assertTrue(refNode2.isSame(testSession.getNodeByUUID(uuid)));
         // add some modification
         refNode2.addMixin(mixLockable);
 
@@ -202,9 +201,9 @@
         assertItemStatus(refNode2, Status.STALE_DESTROYED);
         // the uuid must be transferred to the 'moved' node
         Node n = testSession.getNodeByUUID(uuid);
-        // TODO: for generic jsr 170 test assertSame must be replace by 'Item.isSame'
-        assertSame(n, testSession.getItem(destPath));
-        assertSame(refNode2, testSession.getItem(srcPath));
+        assertTrue(n.isSame(testSession.getItem(destPath)));
+        // assertSame(refNode2, testSession.getItem(srcPath));
+        assertTrue(refNode2.isSame(testSession.getItem(srcPath)));
     }
 
     public void testExternalRemoval() throws RepositoryException, NotExecutableException {
@@ -225,8 +224,7 @@
         assertItemStatus(refNode2, Status.REMOVED);
         // the uuid must be transferred to the 'moved' node
         Node n = testSession.getNodeByUUID(uuid);
-        // TODO: for generic jsr 170 test assertSame must be replace by 'Item.isSame'
-        assertSame(n, testSession.getItem(destPath));
+        assertTrue(n.isSame(testSession.getItem(destPath)));
     }
 
     public void testExternalRemoval2() throws RepositoryException, NotExecutableException {
@@ -311,10 +309,10 @@
         assertItemStatus(childN.getProperty(jcrPrimaryType), Status.NEW);
 
         assertTrue(testSession.itemExists(childNPath));
-        assertSame(childN, testSession.getItem(childNPath));
+        assertTrue(childN.isSame(testSession.getItem(childNPath)));
 
         assertTrue(testSession.itemExists(childPPath));
-        assertSame(childP, testSession.getItem(childPPath));
+        assertTrue(childP.isSame(testSession.getItem(childPPath)));
 
         testSession.refresh(false);
 

Modified: jackrabbit/trunk/jackrabbit-jcr2spi/src/test/java/org/apache/jackrabbit/jcr2spi/GetPropertyTest.java
URL: http://svn.apache.org/viewvc/jackrabbit/trunk/jackrabbit-jcr2spi/src/test/java/org/apache/jackrabbit/jcr2spi/GetPropertyTest.java?rev=915810&r1=915809&r2=915810&view=diff
==============================================================================
--- jackrabbit/trunk/jackrabbit-jcr2spi/src/test/java/org/apache/jackrabbit/jcr2spi/GetPropertyTest.java (original)
+++ jackrabbit/trunk/jackrabbit-jcr2spi/src/test/java/org/apache/jackrabbit/jcr2spi/GetPropertyTest.java Wed Feb 24 14:49:12 2010
@@ -265,7 +265,7 @@
             Property pAgain = (Property) rw.getItem(prop1Path);
 
             // TODO: for generic jsr 170 test: change assert to p.isSame(pAgain)
-            assertSame(p, pAgain);
+            assertTrue(p.isSame(pAgain));
             assertEquals("string1", p.getString());
         } finally {
             rw.logout();

Modified: jackrabbit/trunk/jackrabbit-jcr2spi/src/test/java/org/apache/jackrabbit/jcr2spi/MoveReferenceableTest.java
URL: http://svn.apache.org/viewvc/jackrabbit/trunk/jackrabbit-jcr2spi/src/test/java/org/apache/jackrabbit/jcr2spi/MoveReferenceableTest.java?rev=915810&r1=915809&r2=915810&view=diff
==============================================================================
--- jackrabbit/trunk/jackrabbit-jcr2spi/src/test/java/org/apache/jackrabbit/jcr2spi/MoveReferenceableTest.java (original)
+++ jackrabbit/trunk/jackrabbit-jcr2spi/src/test/java/org/apache/jackrabbit/jcr2spi/MoveReferenceableTest.java Wed Feb 24 14:49:12 2010
@@ -83,8 +83,6 @@
 
         Node n = superuser.getNodeByUUID(uuid);
         assertTrue("After successful moving a referenceable node node, accessing the node by uuid must return the same node.", n.isSame(moveNode));
-        // NOTE: implementation specific test
-        assertTrue("After successful moving a referenceable node node, accessing the node by uuid be the identical node.", n == moveNode);
     }
 
     /**
@@ -100,7 +98,5 @@
 
         Node n = superuser.getNodeByUUID(uuid);
         assertTrue("After successful moving a referenceable node node, accessing the node by uuid must return the same node.", n.isSame(moveNode));
-        // NOTE: implementation specific test
-        assertTrue("After successful moving a referenceable node node, accessing the node by uuid be the identical node.", n == moveNode);
     }
 }
\ No newline at end of file

Modified: jackrabbit/trunk/jackrabbit-jcr2spi/src/test/java/org/apache/jackrabbit/jcr2spi/MoveTest.java
URL: http://svn.apache.org/viewvc/jackrabbit/trunk/jackrabbit-jcr2spi/src/test/java/org/apache/jackrabbit/jcr2spi/MoveTest.java?rev=915810&r1=915809&r2=915810&view=diff
==============================================================================
--- jackrabbit/trunk/jackrabbit-jcr2spi/src/test/java/org/apache/jackrabbit/jcr2spi/MoveTest.java (original)
+++ jackrabbit/trunk/jackrabbit-jcr2spi/src/test/java/org/apache/jackrabbit/jcr2spi/MoveTest.java Wed Feb 24 14:49:12 2010
@@ -178,8 +178,6 @@
         doMove(oldPath, destinationPath);
         Item movedItem = superuser.getItem(newPath);
         assertTrue("Moved Node must be the same after the move.", movedItem.isSame(moveNode));
-        // NOTE: implementation specific test
-        assertTrue("After successful moving a referenceable node node, accessing the node by uuid be the identical node.", movedItem == moveNode);
     }
 
     /**
@@ -202,8 +200,6 @@
 
         Item movedItem = superuser.getItem(destinationPath);
         assertTrue("Moved Node must be the same after the move.", movedItem.isSame(moveNode));
-        // NOTE: implementation specific test
-        assertTrue("After successful moving a referenceable node node, accessing the node by uuid be the identical node.", movedItem == moveNode);
     }
 
     /**
@@ -216,8 +212,6 @@
         //move the node
         doMove(moveNode.getPath(), destinationPath);
         assertTrue("Parent of moved node must be the destination parent node.", moveNode.getParent().isSame(destParentNode));
-        // NOTE: implementation specific test
-        assertTrue("After successful moving a referenceable node node, accessing the node by uuid be the identical node.", moveNode.getParent() == destParentNode);
     }
 
     /**
@@ -232,8 +226,6 @@
         superuser.save();
 
         assertTrue("Parent of moved node must be the destination parent node.", moveNode.getParent().isSame(destParentNode));
-        // NOTE: implementation specific test
-        assertTrue("After successful moving a referenceable node node, accessing the node by uuid be the identical node.", moveNode.getParent() == destParentNode);
     }
 
     /**

Modified: jackrabbit/trunk/jackrabbit-jcr2spi/src/test/java/org/apache/jackrabbit/jcr2spi/ReorderMoveTest.java
URL: http://svn.apache.org/viewvc/jackrabbit/trunk/jackrabbit-jcr2spi/src/test/java/org/apache/jackrabbit/jcr2spi/ReorderMoveTest.java?rev=915810&r1=915809&r2=915810&view=diff
==============================================================================
--- jackrabbit/trunk/jackrabbit-jcr2spi/src/test/java/org/apache/jackrabbit/jcr2spi/ReorderMoveTest.java (original)
+++ jackrabbit/trunk/jackrabbit-jcr2spi/src/test/java/org/apache/jackrabbit/jcr2spi/ReorderMoveTest.java Wed Feb 24 14:49:12 2010
@@ -93,9 +93,7 @@
         int i = 0;
         while (it.hasNext()) {
             Node child = it.nextNode();
-            // TODO: check for sameNess must be replaced by 'isSame' for generic JCR impl
-            // assertTrue(child.isSame(children[i]));
-            assertSame(child, children[i]);
+            assertTrue(child.isSame(children[i]));
             i++;
         }
     }

Modified: jackrabbit/trunk/jackrabbit-jcr2spi/src/test/java/org/apache/jackrabbit/jcr2spi/benchmark/ReadPerformanceTest.java
URL: http://svn.apache.org/viewvc/jackrabbit/trunk/jackrabbit-jcr2spi/src/test/java/org/apache/jackrabbit/jcr2spi/benchmark/ReadPerformanceTest.java?rev=915810&r1=915809&r2=915810&view=diff
==============================================================================
--- jackrabbit/trunk/jackrabbit-jcr2spi/src/test/java/org/apache/jackrabbit/jcr2spi/benchmark/ReadPerformanceTest.java (original)
+++ jackrabbit/trunk/jackrabbit-jcr2spi/src/test/java/org/apache/jackrabbit/jcr2spi/benchmark/ReadPerformanceTest.java Wed Feb 24 14:49:12 2010
@@ -37,6 +37,7 @@
 import org.apache.jackrabbit.jcr2spi.AbstractJCR2SPITest;
 import org.apache.jackrabbit.spi.ChildInfo;
 import org.apache.jackrabbit.spi.ItemInfo;
+import org.apache.jackrabbit.spi.ItemInfoCache;
 import org.apache.jackrabbit.spi.NodeId;
 import org.apache.jackrabbit.spi.NodeInfo;
 import org.apache.jackrabbit.spi.PropertyId;
@@ -44,6 +45,7 @@
 import org.apache.jackrabbit.spi.QNodeDefinition;
 import org.apache.jackrabbit.spi.RepositoryService;
 import org.apache.jackrabbit.spi.SessionInfo;
+import org.apache.jackrabbit.spi.commons.ItemInfoCacheImpl;
 import org.apache.jackrabbit.spi.commons.ItemInfoBuilder.NodeInfoBuilder;
 import org.apache.jackrabbit.spi.commons.ItemInfoBuilder.PropertyInfoBuilder;
 
@@ -73,6 +75,12 @@
     private static int OP_COUNT = 500;
 
     /**
+     * Size of the item info cache.
+     * @see ItemInfoCache
+     */
+    private static int ITEM_INFO_CACHE_SIZE = 50000;
+
+    /**
      * Ratios of the number of items in the whole content tree compared to the number of items
      * in a batch of a {@link RepositoryService#getItemInfos(SessionInfo, NodeId)} call.
      * The array contains one ratio per run
@@ -92,6 +100,15 @@
     private final Random rnd = new Random(12345);
 
     /**
+     * This implementation ovverides the default cache size with the value of
+     * {@value #ITEM_INFO_CACHE_SIZE}
+     */
+    @Override
+    public ItemInfoCache getItemInfoCache(SessionInfo sessionInfo) throws RepositoryException {
+        return new ItemInfoCacheImpl(ITEM_INFO_CACHE_SIZE);
+    }
+
+    /**
      * This implementation adds a tree of nodes and properties up to certain {@link #TREE_DEPTH}.
      * Each node has {@link #NODE_COUNT} child nodes and {@link #PROPERTY_COUNT} properties.
      * {@inheritDoc}

Modified: jackrabbit/trunk/jackrabbit-spi-commons/src/main/java/org/apache/jackrabbit/spi/commons/AbstractReadableRepositoryService.java
URL: http://svn.apache.org/viewvc/jackrabbit/trunk/jackrabbit-spi-commons/src/main/java/org/apache/jackrabbit/spi/commons/AbstractReadableRepositoryService.java?rev=915810&r1=915809&r2=915810&view=diff
==============================================================================
--- jackrabbit/trunk/jackrabbit-spi-commons/src/main/java/org/apache/jackrabbit/spi/commons/AbstractReadableRepositoryService.java (original)
+++ jackrabbit/trunk/jackrabbit-spi-commons/src/main/java/org/apache/jackrabbit/spi/commons/AbstractReadableRepositoryService.java Wed Feb 24 14:49:12 2010
@@ -35,6 +35,7 @@
 import org.apache.jackrabbit.commons.cnd.ParseException;
 import org.apache.jackrabbit.spi.ItemId;
 import org.apache.jackrabbit.spi.ItemInfo;
+import org.apache.jackrabbit.spi.ItemInfoCache;
 import org.apache.jackrabbit.spi.NodeId;
 import org.apache.jackrabbit.spi.NodeInfo;
 import org.apache.jackrabbit.spi.QValue;
@@ -95,6 +96,7 @@
 
     //---------------------< may be overwritten by subclasses>------------------
 
+
     /**
      * Checks whether the <code>workspaceName</code> is valid.
      * @param workspaceName  name of the workspace to check
@@ -115,6 +117,12 @@
         return super.createSessionInfo(credentials, workspaceName == null? defaulWsp : workspaceName);
     }
 
+    // -----------------------------------------------------< cache >---
+
+    public ItemInfoCache getItemInfoCache(SessionInfo sessionInfo) {
+        return new ItemInfoCacheImpl();
+    }
+
     //-----------------------------< reading >----------------------------------
 
     /**

Added: jackrabbit/trunk/jackrabbit-spi-commons/src/main/java/org/apache/jackrabbit/spi/commons/ItemInfoCacheImpl.java
URL: http://svn.apache.org/viewvc/jackrabbit/trunk/jackrabbit-spi-commons/src/main/java/org/apache/jackrabbit/spi/commons/ItemInfoCacheImpl.java?rev=915810&view=auto
==============================================================================
--- jackrabbit/trunk/jackrabbit-spi-commons/src/main/java/org/apache/jackrabbit/spi/commons/ItemInfoCacheImpl.java (added)
+++ jackrabbit/trunk/jackrabbit-spi-commons/src/main/java/org/apache/jackrabbit/spi/commons/ItemInfoCacheImpl.java Wed Feb 24 14:49:12 2010
@@ -0,0 +1,148 @@
+/*
+ * 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.spi.commons;
+
+import org.apache.commons.collections.map.LinkedMap;
+import org.apache.jackrabbit.spi.ItemId;
+import org.apache.jackrabbit.spi.ItemInfo;
+import org.apache.jackrabbit.spi.ItemInfoCache;
+import org.apache.jackrabbit.spi.NodeId;
+import org.apache.jackrabbit.spi.NodeInfo;
+import org.apache.jackrabbit.spi.PropertyId;
+import org.apache.jackrabbit.spi.PropertyInfo;
+import org.apache.jackrabbit.spi.RepositoryService;
+
+/**
+ * This implementation of {@link ItemInfoCache} has a default size of 5000 items.
+ * Item infos are put into the cache after they have been read from the {@link RepositoryService}.
+ * If the cache is full, the oldest item is discared. Reading items removes the from the cache.
+ *
+ * The undrlying idea here is, that {@link ItemInfo}s which are supplied by the
+ * <code>RepositoryService</code> but not immediatly needed are put into the cache to avoid further
+ * round trips to <code>RepositoryService</code>. When they are needed later, they are read
+ * from the cache. There is no need to keep them in this cache after that point since they are
+ * present in the hierarchy from then on.
+ */
+public class ItemInfoCacheImpl implements ItemInfoCache {
+
+    /**
+     * Default size of the cache.
+     */
+    public static final int DEFAULT_CACHE_SIZE = 5000;
+
+    private final int cacheSize;
+    private final LinkedMap entries;
+
+    /**
+     * Create a new instance with the default cache size.
+     * @see #DEFAULT_CACHE_SIZE
+     */
+    public ItemInfoCacheImpl() {
+        this(DEFAULT_CACHE_SIZE);
+    }
+
+    /**
+     * Create a new instance with a given cache size.
+     * @param cacheSize
+     */
+    public ItemInfoCacheImpl(int cacheSize) {
+        super();
+        this.cacheSize = cacheSize;
+        entries = new LinkedMap(cacheSize);
+    }
+
+    /**
+     * This implementation removes the item from the cache
+     * if it is present. Furthermore if the <code>nodeId</code>
+     * id uuid based, and no item is found by the <code>nodeId</code>
+     * a second lookup is done by the path.
+     */
+    public Entry<NodeInfo> getNodeInfo(NodeId nodeId) {
+        Object entry = entries.remove(nodeId);
+        if (entry == null) {
+            entry = entries.remove(nodeId.getPath());
+        }
+
+        return node(entry);
+    }
+
+    /**
+     * This implementation removes the item from the cache
+     * if it is present. Furthermore if the <code>propertyId</code>
+     * id uuid based, and no item is found by the <code>propertyId</code>
+     * a second lookup is done by the path.
+     */
+    public Entry<PropertyInfo> getPropertyInfo(PropertyId propertyId) {
+        Object entry = entries.remove(propertyId);
+        if (entry == null) {
+            entry = entries.remove(propertyId.getPath());
+        }
+
+        return property(entry);
+    }
+
+    /**
+     * This implementation cached the item by its id and if the id
+     * id uuid based but has no path, also by its path.
+     */
+    public void put(ItemInfo info, long generation) {
+        ItemId id = info.getId();
+        Entry<? extends ItemInfo> entry = info.denotesNode()
+            ? new Entry<NodeInfo>((NodeInfo) info, generation)
+            : new Entry<PropertyInfo>((PropertyInfo) info, generation);
+
+        put(id, entry);
+        if (id.getUniqueID() != null && id.getPath() == null) {
+            put(info.getPath(), entry);
+        }
+    }
+
+    public void dispose() {
+        entries.clear();
+    }
+
+    // -----------------------------------------------------< private >---
+
+    @SuppressWarnings("unchecked")
+    private void put(Object key, Entry<? extends ItemInfo> entry) {
+        entries.remove(key);
+        if (entries.size() >= cacheSize) {
+            entries.remove(entries.firstKey());  // xxx AbstractLinkedMap#firstKey() Javadoc is wrong. See COLLECTIONS-353
+        }
+        entries.put(key, entry);
+    }
+
+    @SuppressWarnings("unchecked")
+    private static Entry<NodeInfo> node(Object entry) {
+        if (entry != null && ((Entry<? extends ItemInfo>) entry).info.denotesNode()) {
+            return (Entry<NodeInfo>) entry;
+        }
+        else {
+            return null;
+        }
+    }
+
+    @SuppressWarnings("unchecked")
+    private static Entry<PropertyInfo> property(Object entry) {
+        if (entry != null && !((Entry<? extends ItemInfo>) entry).info.denotesNode()) {
+            return (Entry<PropertyInfo>) entry;
+        }
+        else {
+            return null;
+        }
+    }
+}
\ No newline at end of file

Propchange: jackrabbit/trunk/jackrabbit-spi-commons/src/main/java/org/apache/jackrabbit/spi/commons/ItemInfoCacheImpl.java
------------------------------------------------------------------------------
    svn:eol-style = native

Modified: jackrabbit/trunk/jackrabbit-spi-commons/src/main/java/org/apache/jackrabbit/spi/commons/logging/RepositoryServiceLogger.java
URL: http://svn.apache.org/viewvc/jackrabbit/trunk/jackrabbit-spi-commons/src/main/java/org/apache/jackrabbit/spi/commons/logging/RepositoryServiceLogger.java?rev=915810&r1=915809&r2=915810&view=diff
==============================================================================
--- jackrabbit/trunk/jackrabbit-spi-commons/src/main/java/org/apache/jackrabbit/spi/commons/logging/RepositoryServiceLogger.java (original)
+++ jackrabbit/trunk/jackrabbit-spi-commons/src/main/java/org/apache/jackrabbit/spi/commons/logging/RepositoryServiceLogger.java Wed Feb 24 14:49:12 2010
@@ -24,14 +24,17 @@
 import javax.jcr.RepositoryException;
 import javax.jcr.UnsupportedRepositoryOperationException;
 import javax.jcr.nodetype.InvalidNodeTypeDefinitionException;
-import javax.jcr.nodetype.NodeTypeExistsException;
 import javax.jcr.nodetype.NoSuchNodeTypeException;
+import javax.jcr.nodetype.NodeTypeExistsException;
 
 import org.apache.jackrabbit.spi.Batch;
+import org.apache.jackrabbit.spi.ChildInfo;
 import org.apache.jackrabbit.spi.EventBundle;
 import org.apache.jackrabbit.spi.EventFilter;
 import org.apache.jackrabbit.spi.IdFactory;
 import org.apache.jackrabbit.spi.ItemId;
+import org.apache.jackrabbit.spi.ItemInfo;
+import org.apache.jackrabbit.spi.ItemInfoCache;
 import org.apache.jackrabbit.spi.LockInfo;
 import org.apache.jackrabbit.spi.Name;
 import org.apache.jackrabbit.spi.NameFactory;
@@ -42,16 +45,14 @@
 import org.apache.jackrabbit.spi.PropertyId;
 import org.apache.jackrabbit.spi.PropertyInfo;
 import org.apache.jackrabbit.spi.QNodeDefinition;
+import org.apache.jackrabbit.spi.QNodeTypeDefinition;
 import org.apache.jackrabbit.spi.QPropertyDefinition;
+import org.apache.jackrabbit.spi.QValue;
 import org.apache.jackrabbit.spi.QValueFactory;
 import org.apache.jackrabbit.spi.QueryInfo;
 import org.apache.jackrabbit.spi.RepositoryService;
 import org.apache.jackrabbit.spi.SessionInfo;
 import org.apache.jackrabbit.spi.Subscription;
-import org.apache.jackrabbit.spi.QNodeTypeDefinition;
-import org.apache.jackrabbit.spi.QValue;
-import org.apache.jackrabbit.spi.ChildInfo;
-import org.apache.jackrabbit.spi.ItemInfo;
 
 /**
  * Log wrapper for a {@link RepositoryService}.
@@ -117,6 +118,14 @@
         }, "getRepositoryDescriptors()", new Object[]{});
     }
 
+    public ItemInfoCache getItemInfoCache(final SessionInfo sessionInfo) throws RepositoryException {
+        return (ItemInfoCache) execute(new Callable() {
+            public Object call() throws RepositoryException {
+                return service.getItemInfoCache(sessionInfo);
+            }
+        }, "getItemInfoCache(SessionInfo)", new Object[]{sessionInfo});
+    }
+
     public SessionInfo obtain(final Credentials credentials, final String workspaceName)
             throws RepositoryException {