Index: jackrabbit-core/src/main/java/org/apache/jackrabbit/core/security/authorization/acl/CachingEntryCollector.java
===================================================================
--- jackrabbit-core/src/main/java/org/apache/jackrabbit/core/security/authorization/acl/CachingEntryCollector.java	(revision 1351635)
+++ jackrabbit-core/src/main/java/org/apache/jackrabbit/core/security/authorization/acl/CachingEntryCollector.java	(working copy)
@@ -16,6 +16,13 @@
  */
 package org.apache.jackrabbit.core.security.authorization.acl;
 
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Map;
+import java.util.concurrent.ConcurrentHashMap;
+
+import javax.jcr.RepositoryException;
+
 import org.apache.jackrabbit.core.NodeImpl;
 import org.apache.jackrabbit.core.SessionImpl;
 import org.apache.jackrabbit.core.cache.GrowingLRUMap;
@@ -24,9 +31,6 @@
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
-import javax.jcr.RepositoryException;
-import java.util.Map;
-
 /**
  * <code>CachingEntryCollector</code> extends <code>EntryCollector</code> by
  * keeping a cache of ACEs per access controlled nodeId.
@@ -73,7 +77,7 @@
         Entries entries = cache.get(nodeId);
         if (entries == null) {
             // fetch entries and update the cache
-            entries = updateCache(node);
+            entries = weaklySynchronizedUpdateCache(node);
         }
         return entries;
     }
@@ -87,7 +91,7 @@
         if (entries == null) {
             // fetch entries and update the cache
             NodeImpl n = getNodeById(nodeId);
-            entries = updateCache(n);
+            entries = weaklySynchronizedUpdateCache(n);
         }
         return entries;
     }
@@ -111,6 +115,101 @@
         return entries;
     }
 
+    // waits for a result to become ready 
+    private class FutureEntries {
+
+        private boolean ready = false;
+        private Entries result = null;
+        private Throwable problem = null;
+
+        synchronized public Entries get() throws RepositoryException {
+            while (!ready) {
+                try {
+                    wait();
+                } catch (InterruptedException e) {
+                }
+            }
+            if (problem != null) {
+                if (problem instanceof RepositoryException) {
+                    throw new RepositoryException(problem);
+                }
+                else {
+                    throw new RuntimeException(problem);
+                }
+            }
+            return result;
+        }
+
+        synchronized public void setResult(Entries e) {
+            result = e;
+            ready = true;
+            notifyAll();
+        }
+
+        synchronized public void setProblem(Throwable t) {
+            problem = t;
+            ready = true;
+            notifyAll();
+        }
+    }
+
+    private Map<NodeId, List<FutureEntries>> sems = new ConcurrentHashMap<NodeId, List<FutureEntries>>();
+
+    private Entries weaklySynchronizedUpdateCache(NodeImpl node) throws RepositoryException {
+        NodeId id = node.getNodeId();
+        List<FutureEntries> l = null;
+        FutureEntries fe = null;
+
+        synchronized(sems) {
+            // obtain a list of future entries from the map
+            l = sems.get(id);
+            if (l == null) {
+                // found -- create empty list for other callers to add to
+                l = new LinkedList<FutureEntries>();
+                sems.put(id, l);
+            }
+            else {
+                // found -- add a new entry
+                fe = new FutureEntries();
+                l.add(fe);
+            }
+        }
+
+        if (fe != null) {
+            // we have created a FutureEntries object, so use it
+            return fe.get();
+        }
+        else {
+            // otherwise obtain result and when done notify waiting FutureEntries
+
+            try {
+                Entries e = updateCache(node);
+                synchronized(sems) {
+                    for (FutureEntries f : l) {
+                        f.setResult(e);
+                    }
+                    sems.remove(id);
+                }
+
+                return e;
+            }
+            catch (Throwable problem) {
+                synchronized(sems) {
+                    for (FutureEntries f : l) {
+                        f.setProblem(problem);
+                    }
+                    sems.remove(id);
+                }
+                if (problem instanceof RepositoryException) {
+                    throw (RepositoryException)problem;
+                }
+                else {
+                    throw new RuntimeException(problem);
+                }
+            }
+        }
+    }
+
     /**
      * Find the next access control ancestor in the hierarchy 'null' indicates
      * that there is no ac-controlled ancestor.
