You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@jackrabbit.apache.org by an...@apache.org on 2010/06/02 11:00:46 UTC

svn commit: r950440 [1/3] - in /jackrabbit/trunk/jackrabbit-core/src: main/java/org/apache/jackrabbit/core/ main/java/org/apache/jackrabbit/core/observation/ main/java/org/apache/jackrabbit/core/query/lucene/ main/java/org/apache/jackrabbit/core/securi...

Author: angela
Date: Wed Jun  2 09:00:44 2010
New Revision: 950440

URL: http://svn.apache.org/viewvc?rev=950440&view=rev
Log:
JCR-2573 - Performance of AC Evaluation [work in progress]

Added:
    jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/security/authorization/AccessControlListener.java   (with props)
    jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/security/authorization/AccessControlModifications.java   (with props)
    jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/security/authorization/AccessControlObserver.java   (with props)
    jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/security/authorization/acl/CachingEntryCollector.java   (with props)
    jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/security/authorization/acl/EntryCollector.java   (with props)
    jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/security/authorization/acl/EntryFilter.java   (with props)
    jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/security/authorization/acl/EntryFilterImpl.java   (with props)
    jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/security/authorization/principalbased/EntriesCache.java   (with props)
    jackrabbit/trunk/jackrabbit-core/src/test/java/org/apache/jackrabbit/core/security/authorization/acl/EntryCollectorTest.java   (with props)
Modified:
    jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/BatchedItemOperations.java
    jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/ItemManager.java
    jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/NodeImpl.java
    jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/SystemSession.java
    jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/UserPerWorkspaceSecurityManager.java
    jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/observation/EventConsumer.java
    jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/query/lucene/QueryResultImpl.java
    jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/security/AccessManager.java
    jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/security/DefaultAccessManager.java
    jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/security/SimpleJBossAccessManager.java
    jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/security/authorization/AbstractACLTemplate.java
    jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/security/authorization/AbstractAccessControlProvider.java
    jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/security/authorization/AbstractCompiledPermissions.java
    jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/security/authorization/CompiledPermissions.java
    jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/security/authorization/UnmodifiableAccessControlList.java
    jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/security/authorization/acl/ACLEditor.java
    jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/security/authorization/acl/ACLProvider.java
    jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/security/authorization/acl/ACLTemplate.java
    jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/security/authorization/combined/CombinedProvider.java
    jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/security/authorization/principalbased/ACLProvider.java
    jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/security/authorization/principalbased/ACLTemplate.java
    jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/security/authorization/principalbased/GlobPattern.java
    jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/security/simple/SimpleAccessManager.java
    jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/security/simple/SimpleLoginModule.java
    jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/security/user/UserAccessControlProvider.java
    jackrabbit/trunk/jackrabbit-core/src/test/java/org/apache/jackrabbit/core/security/AccessManagerTest.java
    jackrabbit/trunk/jackrabbit-core/src/test/java/org/apache/jackrabbit/core/security/authorization/acl/EntryTest.java
    jackrabbit/trunk/jackrabbit-core/src/test/java/org/apache/jackrabbit/core/security/authorization/acl/TestAll.java
    jackrabbit/trunk/jackrabbit-core/src/test/java/org/apache/jackrabbit/core/security/authorization/combined/WriteTest.java

Modified: jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/BatchedItemOperations.java
URL: http://svn.apache.org/viewvc/jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/BatchedItemOperations.java?rev=950440&r1=950439&r2=950440&view=diff
==============================================================================
--- jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/BatchedItemOperations.java (original)
+++ jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/BatchedItemOperations.java Wed Jun  2 09:00:44 2010
@@ -1757,11 +1757,11 @@ public class BatchedItemOperations exten
             }
             // copy properties
             for (Name propName : srcState.getPropertyNames()) {
-                Path propPath = PathFactoryImpl.getInstance().create(srcPath, propName, true);
-                if (!srcAccessMgr.canRead(propPath)) {
+                Path propPath = PathFactoryImpl.getInstance().create(srcPath, propName, true);                
+                PropertyId propId = new PropertyId(srcState.getNodeId(), propName);
+                if (!srcAccessMgr.canRead(propPath, propId)) {
                     continue;
                 }
-                PropertyId propId = new PropertyId(srcState.getNodeId(), propName);
                 PropertyState srcChildState =
                         (PropertyState) srcStateMgr.getItemState(propId);
 

Modified: jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/ItemManager.java
URL: http://svn.apache.org/viewvc/jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/ItemManager.java?rev=950440&r1=950439&r2=950440&view=diff
==============================================================================
--- jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/ItemManager.java (original)
+++ jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/ItemManager.java Wed Jun  2 09:00:44 2010
@@ -39,7 +39,7 @@ import org.apache.jackrabbit.core.id.Pro
 import org.apache.jackrabbit.core.nodetype.NodeTypeRegistry;
 import org.apache.jackrabbit.core.nodetype.EffectiveNodeType;
 import org.apache.jackrabbit.core.nodetype.NodeTypeConflictException;
-import org.apache.jackrabbit.core.security.AccessManager;
+import org.apache.jackrabbit.core.security.authorization.Permission;
 import org.apache.jackrabbit.core.state.ChildNodeEntry;
 import org.apache.jackrabbit.core.state.ItemState;
 import org.apache.jackrabbit.core.state.ItemStateException;
@@ -51,6 +51,7 @@ import org.apache.jackrabbit.core.state.
 import org.apache.jackrabbit.core.util.Dumpable;
 import org.apache.jackrabbit.core.version.VersionHistoryImpl;
 import org.apache.jackrabbit.core.version.VersionImpl;
+import org.apache.jackrabbit.core.security.AccessManager;
 import org.apache.jackrabbit.spi.Name;
 import org.apache.jackrabbit.spi.Path;
 import org.apache.jackrabbit.spi.QPropertyDefinition;
@@ -437,26 +438,48 @@ public class ItemManager implements Dump
         if (state == null) {
             throw new InvalidItemStateException(data.getId() + ": the item does not exist anymore");
         }
-        if (state.getStatus() == ItemState.STATUS_NEW &&
-                !data.getDefinition().isProtected()) {
-            // NEW items can always be read as long they have been added
-            // through the API and NOT by the system (i.e. protected props).
-            return true;
+        if (state.getStatus() == ItemState.STATUS_NEW) {
+            if (!data.getDefinition().isProtected()) {
+                /*
+                NEW items can always be read as long they have been added through
+                the API and NOT by the system (i.e. protected items).
+                */
+                return true;
+            } else {
+                /*
+                NEW protected (system) item:
+                need use the path to evaluate the effective permissions.
+                */
+                return (path == null) ?
+                        session.getAccessManager().isGranted(data.getId(), AccessManager.READ) :
+                        session.getAccessManager().isGranted(path, Permission.READ);
+            }
         } else {
-            return (path == null) ?
-                    canRead(data.getId()) :
-                    session.getAccessManager().canRead(path);
+            /* item is not NEW -> save to call acMgr.canRead(Path,ItemId) */
+            return session.getAccessManager().canRead(path, data.getId());
         }
     }
 
     /**
-     * @param id
-     * @return true if the item with the given <code>id</code> can be read;
+     * @param parent The item data of the parent node.
+     * @param childId
+     * @return true if the item with the given <code>childId</code> can be read;
      * <code>false</code> otherwise.
      * @throws RepositoryException
      */
-    private boolean canRead(ItemId id) throws RepositoryException {
-        return session.getAccessManager().isGranted(id, AccessManager.READ);
+    private boolean canRead(ItemData parent, ItemId childId) throws RepositoryException {
+        if (parent.getStatus() == ItemState.STATUS_EXISTING) {
+            /*
+             child item is for sure not NEW (because then the parent was modified).
+             safe to use AccessManager#canRead(Path, ItemId).
+             */
+            return session.getAccessManager().canRead(null, childId);
+        } else {
+            /*
+             child could be NEW -> don't use AccessManager#canRead(Path, ItemId)
+             */
+            return session.getAccessManager().isGranted(childId, AccessManager.READ);
+        }
     }
 
     //--------------------------------------------------< item access methods >
@@ -697,7 +720,7 @@ public class ItemManager implements Dump
         NodeState state = (NodeState) data.getState();
         for (ChildNodeEntry entry : state.getChildNodeEntries()) {
             // make sure any of the properties can be read.
-            if (canRead(entry.getId())) {
+            if (canRead(data, entry.getId())) {
                 return true;
             }
         }
@@ -758,7 +781,7 @@ public class ItemManager implements Dump
         while (iter.hasNext()) {
             Name propName = iter.next();
             // make sure any of the properties can be read.
-            if (canRead(new PropertyId(parentId, propName))) {
+            if (canRead(data, new PropertyId(parentId, propName))) {
                 return true;
             }
         }

Modified: jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/NodeImpl.java
URL: http://svn.apache.org/viewvc/jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/NodeImpl.java?rev=950440&r1=950439&r2=950440&view=diff
==============================================================================
--- jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/NodeImpl.java (original)
+++ jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/NodeImpl.java Wed Jun  2 09:00:44 2010
@@ -3026,7 +3026,7 @@ public class NodeImpl extends ItemImpl i
      *
      * @return parent id
      */
-    NodeId getParentId() {
+    public NodeId getParentId() {
         return data.getParentId();
     }
 

Modified: jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/SystemSession.java
URL: http://svn.apache.org/viewvc/jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/SystemSession.java?rev=950440&r1=950439&r2=950440&view=diff
==============================================================================
--- jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/SystemSession.java (original)
+++ jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/SystemSession.java Wed Jun  2 09:00:44 2010
@@ -48,10 +48,10 @@ class SystemSession extends SessionImpl 
     /**
      * Package private factory method
      *
-     * @param rep
-     * @param wspConfig
-     * @return
-     * @throws RepositoryException
+     * @param repositoryContext The repository context
+     * @param wspConfig The workspace configuration
+     * @return A new instance of <code>SystemSession</code>
+     * @throws RepositoryException If an error occurs
      */
     static SystemSession create(
             RepositoryContext repositoryContext, WorkspaceConfig wspConfig)
@@ -69,7 +69,9 @@ class SystemSession extends SessionImpl 
      * private constructor
      *
      * @param repositoryContext repository context
-     * @param wspConfig
+     * @param subject The subject
+     * @param wspConfig The workspace configuration
+     * @throws javax.jcr.RepositoryException If an error occurs.
      */
     private SystemSession(
             RepositoryContext repositoryContext, Subject subject,
@@ -193,10 +195,9 @@ class SystemSession extends SessionImpl 
         /**
          * Always returns true.
          *
-         * @see AccessManager#canRead(Path)
-         * @param itemPath
+         * @see AccessManager#canRead(org.apache.jackrabbit.spi.Path,org.apache.jackrabbit.core.id.ItemId)
          */
-        public boolean canRead(Path itemPath) throws RepositoryException {
+        public boolean canRead(Path itemPath, ItemId itemId) throws RepositoryException {
             return true;
         }
 

Modified: jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/UserPerWorkspaceSecurityManager.java
URL: http://svn.apache.org/viewvc/jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/UserPerWorkspaceSecurityManager.java?rev=950440&r1=950439&r2=950440&view=diff
==============================================================================
--- jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/UserPerWorkspaceSecurityManager.java (original)
+++ jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/UserPerWorkspaceSecurityManager.java Wed Jun  2 09:00:44 2010
@@ -63,6 +63,7 @@ import java.util.Set;
 public class UserPerWorkspaceSecurityManager extends DefaultSecurityManager {
 
     private final Map<String, PrincipalProviderRegistry> ppRegistries = new HashMap<String, PrincipalProviderRegistry>();
+    private final Object monitor = new Object();
 
     /**
      * List of workspace names for which {@link #createSystemUsers} has already
@@ -72,25 +73,27 @@ public class UserPerWorkspaceSecurityMan
 
     private PrincipalProviderRegistry getPrincipalProviderRegistry(SessionImpl s) throws RepositoryException {
         String wspName = s.getWorkspace().getName();
-        PrincipalProviderRegistry p = ppRegistries.get(wspName);
-        if (p == null) {
-            SystemSession systemSession;
-            if (s instanceof SystemSession) {
-                systemSession = (SystemSession) s;
-            } else {
-                RepositoryImpl repo = (RepositoryImpl) getRepository();
-                systemSession = repo.getSystemSession(wspName);
-                // TODO: review again... this workaround is used in several places.
-                repo.onSessionCreated(systemSession);
-            }
+        synchronized (monitor) {
+            PrincipalProviderRegistry p = ppRegistries.get(wspName);
+            if (p == null) {
+                SystemSession systemSession;
+                if (s instanceof SystemSession) {
+                    systemSession = (SystemSession) s;
+                } else {
+                    RepositoryImpl repo = (RepositoryImpl) getRepository();
+                    systemSession = repo.getSystemSession(wspName);
+                    // TODO: review again... this workaround is used in several places.
+                    repo.onSessionCreated(systemSession);
+                }
 
-            PrincipalProvider defaultPP = new DefaultPrincipalProvider(systemSession, (UserManagerImpl) getUserManager(systemSession));
-            defaultPP.init(new Properties());
-            
-            p = new WorkspaceBasedPrincipalProviderRegistry(defaultPP);
-            ppRegistries.put(wspName, p);
+                PrincipalProvider defaultPP = new DefaultPrincipalProvider(systemSession, (UserManagerImpl) getUserManager(systemSession));
+                defaultPP.init(new Properties());
+
+                p = new WorkspaceBasedPrincipalProviderRegistry(defaultPP);
+                ppRegistries.put(wspName, p);
+            }
+            return p;
         }
-        return p;
     }
 
     //------------------------------------------< JackrabbitSecurityManager >---
@@ -110,7 +113,7 @@ public class UserPerWorkspaceSecurityMan
     @Override
     public void dispose(String workspaceName) {
         super.dispose(workspaceName);
-        synchronized (ppRegistries) {
+        synchronized (monitor) {
             PrincipalProviderRegistry reg = ppRegistries.remove(workspaceName);
             if (reg != null) {
                 reg.getDefault().close();
@@ -124,7 +127,7 @@ public class UserPerWorkspaceSecurityMan
     @Override
     public void close() {
         super.close();
-        synchronized (ppRegistries) {
+        synchronized (monitor) {
             for (PrincipalProviderRegistry registry : ppRegistries.values()) {
                 registry.getDefault().close();
             }

Modified: jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/observation/EventConsumer.java
URL: http://svn.apache.org/viewvc/jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/observation/EventConsumer.java?rev=950440&r1=950439&r2=950440&view=diff
==============================================================================
--- jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/observation/EventConsumer.java (original)
+++ jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/observation/EventConsumer.java Wed Jun  2 09:00:44 2010
@@ -301,6 +301,6 @@ class EventConsumer {
      */
     private boolean canRead(EventState eventState) throws RepositoryException {
         Path targetPath = pathFactory.create(eventState.getParentPath(), eventState.getChildRelPath().getName(), eventState.getChildRelPath().getNormalizedIndex(), true);
-        return session.getAccessManager().canRead(targetPath);
+        return session.getAccessManager().canRead(targetPath, null);
     }
 }

Modified: jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/query/lucene/QueryResultImpl.java
URL: http://svn.apache.org/viewvc/jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/query/lucene/QueryResultImpl.java?rev=950440&r1=950439&r2=950440&view=diff
==============================================================================
--- jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/query/lucene/QueryResultImpl.java (original)
+++ jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/query/lucene/QueryResultImpl.java Wed Jun  2 09:00:44 2010
@@ -371,8 +371,7 @@ public abstract class QueryResultImpl im
             throws RepositoryException {
         for (ScoreNode node : nodes) {
             try {
-                // TODO: rather use AccessManager.canRead(Path)
-                if (node != null && !accessMgr.isGranted(node.getNodeId(), AccessManager.READ)) {
+                if (node != null && !accessMgr.canRead(null, node.getNodeId())) {
                     return false;
                 }
             } catch (ItemNotFoundException e) {

Modified: jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/security/AccessManager.java
URL: http://svn.apache.org/viewvc/jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/security/AccessManager.java?rev=950440&r1=950439&r2=950440&view=diff
==============================================================================
--- jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/security/AccessManager.java (original)
+++ jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/security/AccessManager.java Wed Jun  2 09:00:44 2010
@@ -171,13 +171,24 @@ public interface AccessManager {
     boolean isGranted(Path parentPath, Name childName, int permissions) throws RepositoryException;
 
     /**
-     * Determines whether the item at the specified absolute path can be read.
+     * Determines whether the item with the specified <code>itemPath</code>
+     * or <code>itemId</code> can be read. Either of the two parameters
+     * may be <code>null</code>.<br>
+     * Note, that this method should only be called for persisted items as NEW
+     * items may not be visible to the permission evaluation.
+     * For new items {@link #isGranted(Path, int)} should be used instead.<p/>
+     * If this method is called with both Path and ItemId it is left to the
+     * evaluation, which parameter is used.
      *
-     * @param itemPath Path to the item to be tested.s
+     * @param itemPath The path to the item or <code>null</code> if itemId
+     * should be used to determine the READ permission.
+     * @param itemId Id of the item to be tested or <code>null</code> if the
+     * itemPath should be used to determine the permission.
      * @return <code>true</code> if the item can be read; otherwise <code>false</code>.
-     * @throws RepositoryException if an error occurs.
+     * @throws RepositoryException if the item is NEW and only an itemId is
+     * specified or if another error occurs.
      */
-    boolean canRead(Path itemPath) throws RepositoryException;
+    boolean canRead(Path itemPath, ItemId itemId) throws RepositoryException;
 
     /**
      * Determines whether the subject of the current context is granted access

Modified: jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/security/DefaultAccessManager.java
URL: http://svn.apache.org/viewvc/jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/security/DefaultAccessManager.java?rev=950440&r1=950439&r2=950440&view=diff
==============================================================================
--- jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/security/DefaultAccessManager.java (original)
+++ jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/security/DefaultAccessManager.java Wed Jun  2 09:00:44 2010
@@ -215,6 +215,7 @@ public class DefaultAccessManager extend
             if ((actions & REMOVE) == REMOVE) {
                 perm |= (id.denotesNode()) ? Permission.REMOVE_NODE : Permission.REMOVE_PROPERTY;
             }
+            
             Path path = hierMgr.getPath(id);
             return isGranted(path, perm);
         }
@@ -240,13 +241,14 @@ public class DefaultAccessManager extend
     }
 
     /**
-     * @see AccessManager#canRead(Path)
+     * @see AccessManager#canRead(org.apache.jackrabbit.spi.Path,org.apache.jackrabbit.core.id.ItemId)
      */
-    public boolean canRead(Path itemPath) throws RepositoryException {
+    public boolean canRead(Path itemPath, ItemId itemId) throws RepositoryException {
+        checkInitialized();
         if (compiledPermissions.canReadAll()) {
             return true;
         } else {
-            return isGranted(itemPath, Permission.READ);
+            return compiledPermissions.canRead(itemPath, itemId);
         }
     }
 

Modified: jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/security/SimpleJBossAccessManager.java
URL: http://svn.apache.org/viewvc/jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/security/SimpleJBossAccessManager.java?rev=950440&r1=950439&r2=950440&view=diff
==============================================================================
--- jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/security/SimpleJBossAccessManager.java (original)
+++ jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/security/SimpleJBossAccessManager.java Wed Jun  2 09:00:44 2010
@@ -121,7 +121,7 @@ public class SimpleJBossAccessManager im
         return internalIsGranted(permissions);
     }
 
-    public boolean canRead(Path itemPath) throws RepositoryException {
+    public boolean canRead(Path itemPath, ItemId itemId) throws RepositoryException {
         return true;
     }
 

Modified: jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/security/authorization/AbstractACLTemplate.java
URL: http://svn.apache.org/viewvc/jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/security/authorization/AbstractACLTemplate.java?rev=950440&r1=950439&r2=950440&view=diff
==============================================================================
--- jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/security/authorization/AbstractACLTemplate.java (original)
+++ jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/security/authorization/AbstractACLTemplate.java Wed Jun  2 09:00:44 2010
@@ -76,12 +76,9 @@ public abstract class AbstractACLTemplat
      * Return the list of entries, if they are held in a orderable list.
      *
      * @return the list of entries.
-     * @throws UnsupportedRepositoryOperationException If the implementation
-     * does not held the entries in a list.
-     * @throws RepositoryException
      * @see #orderBefore(AccessControlEntry, AccessControlEntry)
      */
-    protected abstract List<? extends AccessControlEntry> getEntries() throws UnsupportedRepositoryOperationException, RepositoryException;
+    protected abstract List<? extends AccessControlEntry> getEntries();
 
     //--------------------------------------< JackrabbitAccessControlPolicy >---
     /**
@@ -101,6 +98,20 @@ public abstract class AbstractACLTemplat
     }
 
     /**
+     * @see org.apache.jackrabbit.api.security.JackrabbitAccessControlList#size()
+     */
+    public int size() {
+        return getEntries().size();
+    }
+
+    /**
+     * @see org.apache.jackrabbit.api.security.JackrabbitAccessControlList#isEmpty()
+     */
+    public boolean isEmpty() {
+        return getEntries().isEmpty();
+    }
+
+    /**
      *
      * @param srcEntry The access control entry to be moved within the list.
      * @param destEntry The entry before which the <code>srcEntry</code> will be moved.
@@ -131,6 +142,14 @@ public abstract class AbstractACLTemplat
 
     //--------------------------------------------------< AccessControlList >---
     /**
+     * @see javax.jcr.security.AccessControlList#getAccessControlEntries()
+     */
+    public AccessControlEntry[] getAccessControlEntries() throws RepositoryException {       
+        List<? extends AccessControlEntry> l = getEntries();
+        return l.toArray(new AccessControlEntry[l.size()]);
+    }
+
+    /**
      * @see javax.jcr.security.AccessControlList#addAccessControlEntry(java.security.Principal , javax.jcr.security.Privilege[])
      */
     public boolean addAccessControlEntry(Principal principal, Privilege[] privileges)

Modified: jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/security/authorization/AbstractAccessControlProvider.java
URL: http://svn.apache.org/viewvc/jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/security/authorization/AbstractAccessControlProvider.java?rev=950440&r1=950439&r2=950440&view=diff
==============================================================================
--- jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/security/authorization/AbstractAccessControlProvider.java (original)
+++ jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/security/authorization/AbstractAccessControlProvider.java Wed Jun  2 09:00:44 2010
@@ -25,9 +25,14 @@ import javax.jcr.Session;
 import javax.jcr.observation.ObservationManager;
 import javax.jcr.security.Privilege;
 
+import org.apache.jackrabbit.core.ItemImpl;
+import org.apache.jackrabbit.core.NodeImpl;
 import org.apache.jackrabbit.core.SessionImpl;
+import org.apache.jackrabbit.core.id.ItemId;
+import org.apache.jackrabbit.core.nodetype.NodeTypeImpl;
 import org.apache.jackrabbit.core.security.SystemPrincipal;
 import org.apache.jackrabbit.core.security.principal.AdminPrincipal;
+import org.apache.jackrabbit.spi.Name;
 import org.apache.jackrabbit.spi.Path;
 import org.apache.jackrabbit.spi.commons.conversion.NamePathResolver;
 
@@ -35,7 +40,7 @@ import org.apache.jackrabbit.spi.commons
  * <code>AbstractAccessControlProvider</code>...
  */
 public abstract class AbstractAccessControlProvider implements AccessControlProvider,
-        AccessControlUtils {
+        AccessControlUtils, AccessControlConstants {
 
     /**
      * Constant for the name of the configuration option "omit-default-permission".
@@ -98,6 +103,9 @@ public abstract class AbstractAccessCont
             public boolean canReadAll() {
                 return true;
             }
+            public boolean canRead(Path itemPath, ItemId itemId) throws RepositoryException {
+                return true;
+            }
         };
     }
 
@@ -131,11 +139,48 @@ public abstract class AbstractAccessCont
             public boolean canReadAll() {
                 return false;
             }
+            public boolean canRead(Path itemPath, ItemId itemId) throws RepositoryException {
+                if (itemPath != null) {
+                    return !isAcItem(itemPath);
+                } else {
+                    return !isAcItem(session.getItemManager().getItem(itemId));
+                }
+            }
         };
     }
 
     //-------------------------------------------------< AccessControlUtils >---
     /**
+     * @see org.apache.jackrabbit.core.security.authorization.AccessControlUtils#isAcItem(Path)
+     */
+    public boolean isAcItem(Path absPath) throws RepositoryException {
+        Path.Element[] elems = absPath.getElements();
+        // start looking for a rep:policy name starting from the last element.
+        // NOTE: with the current content structure max. 3 levels must be looked
+        // at as the rep:policy node may only have ACE nodes with a properties.
+        if (elems.length > 1) {
+            for (int index = elems.length-1, j = 1; index >= 0 && j <= 3; index--, j++) {
+                if (N_POLICY.equals(elems[index].getName())) {
+                    return true;
+                }
+            }
+        }
+        return false;
+    }
+
+    /**
+     * Test if the given node is itself a rep:ACL or a rep:ACE node.
+     * @see org.apache.jackrabbit.core.security.authorization.AccessControlUtils#isAcItem(org.apache.jackrabbit.core.ItemImpl)
+     */
+    public boolean isAcItem(ItemImpl item) throws RepositoryException {
+        NodeImpl n = ((item.isNode()) ? (NodeImpl) item : (NodeImpl) item.getParent());
+        Name ntName = ((NodeTypeImpl) n.getPrimaryNodeType()).getQName();
+        return ntName.equals(NT_REP_ACL) ||
+                ntName.equals(NT_REP_GRANT_ACE) ||
+                ntName.equals(NT_REP_DENY_ACE);
+    }
+
+    /**
      * @see AccessControlUtils#isAdminOrSystem(Set)
      */
     public boolean isAdminOrSystem(Set<Principal> principals) {

Modified: jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/security/authorization/AbstractCompiledPermissions.java
URL: http://svn.apache.org/viewvc/jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/security/authorization/AbstractCompiledPermissions.java?rev=950440&r1=950439&r2=950440&view=diff
==============================================================================
--- jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/security/authorization/AbstractCompiledPermissions.java (original)
+++ jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/security/authorization/AbstractCompiledPermissions.java Wed Jun  2 09:00:44 2010
@@ -16,12 +16,11 @@
  */
 package org.apache.jackrabbit.core.security.authorization;
 
-import java.util.Map;
-
 import org.apache.commons.collections.map.LRUMap;
 import org.apache.jackrabbit.spi.Path;
 
 import javax.jcr.RepositoryException;
+import java.util.Map;
 
 /**
  * <code>AbstractCompiledPermissions</code>...
@@ -30,6 +29,7 @@ public abstract class AbstractCompiledPe
 
     // cache mapping a Path to a 'Result' containing permissions and privileges.
     private final Map<Path, Result> cache;
+    private final Object monitor = new Object();
 
     @SuppressWarnings("unchecked")
     protected AbstractCompiledPermissions() {
@@ -44,7 +44,7 @@ public abstract class AbstractCompiledPe
      */
     public Result getResult(Path absPath) throws RepositoryException {
         Result result;
-        synchronized (cache) {
+        synchronized (monitor) {
             result = cache.get(absPath);
             if (result == null) {
                 result = buildResult(absPath);
@@ -66,7 +66,7 @@ public abstract class AbstractCompiledPe
      * Removes all entries from the cache.
      */
     protected void clearCache() {
-        synchronized (cache) {
+        synchronized (monitor) {
             cache.clear();
         }
     }
@@ -113,20 +113,13 @@ public abstract class AbstractCompiledPe
         private final int allowPrivileges;
         private final int denyPrivileges;
 
-        private final int hashCode;
+        private int hashCode = -1;
 
         public Result(int allows, int denies, int allowPrivileges, int denyPrivileges) {
             this.allows = allows;
             this.denies = denies;
             this.allowPrivileges = allowPrivileges;
             this.denyPrivileges = denyPrivileges;
-
-            int h = 17;
-            h = 37 * h + allows;
-            h = 37 * h + denies;
-            h = 37 * h + allowPrivileges;
-            h = 37 * h + denyPrivileges;
-            hashCode = h;
         }
 
         public boolean grants(int permissions) {
@@ -148,13 +141,23 @@ public abstract class AbstractCompiledPe
         /**
          * @see Object#hashCode()
          */
+        @Override
         public int hashCode() {
+            if (hashCode == -1) {
+                int h = 17;
+                h = 37 * h + allows;
+                h = 37 * h + denies;
+                h = 37 * h + allowPrivileges;
+                h = 37 * h + denyPrivileges;
+                hashCode = h;
+            }
             return hashCode;
         }
 
         /**
          * @see Object#equals(Object)
          */
+        @Override
         public boolean equals(Object object) {
             if (object == this) {
                 return true;

Added: jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/security/authorization/AccessControlListener.java
URL: http://svn.apache.org/viewvc/jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/security/authorization/AccessControlListener.java?rev=950440&view=auto
==============================================================================
--- jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/security/authorization/AccessControlListener.java (added)
+++ jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/security/authorization/AccessControlListener.java Wed Jun  2 09:00:44 2010
@@ -0,0 +1,30 @@
+/*
+ * 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.core.security.authorization;
+
+/**
+ * 
+ */
+public interface AccessControlListener {
+
+    /**
+     * Informs this listener about changes made to access control content.
+     * 
+     * @param modifications Information about access control modifications.
+     */
+    void acModified(AccessControlModifications modifications);
+}

Propchange: jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/security/authorization/AccessControlListener.java
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/security/authorization/AccessControlListener.java
------------------------------------------------------------------------------
    svn:keywords = Author Date Id Revision Rev URL

Added: jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/security/authorization/AccessControlModifications.java
URL: http://svn.apache.org/viewvc/jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/security/authorization/AccessControlModifications.java?rev=950440&view=auto
==============================================================================
--- jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/security/authorization/AccessControlModifications.java (added)
+++ jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/security/authorization/AccessControlModifications.java Wed Jun  2 09:00:44 2010
@@ -0,0 +1,70 @@
+/*
+ * 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.core.security.authorization;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.util.Collections;
+import java.util.Map;
+import java.util.Set;
+
+/**
+ * <code>AccessControlModifications</code> is an unmodifiable collection of
+ * modifications made to access control content allowing the
+ * {@link AccessControlListener modification listeners} to keep caches up to date.
+ */
+public class AccessControlModifications<K> {
+
+    /**
+     * logger instance
+     */
+    private static final Logger log = LoggerFactory.getLogger(AccessControlModifications.class);
+
+    private final Map<K, Integer> modificationMap;
+
+    /**
+     * @param modificationMap Map specifying the access control modifications.
+     * The keys allows to identify the <code>Node</code> that was modified by
+     * the policy modifications. The values specifies the modification type,
+     * which may be any of
+     * <ul>
+     * <li>{@link AccessControlObserver#POLICY_ADDED}</li>
+     * <li>{@link AccessControlObserver#POLICY_MODIFIED}</li>
+     * <li>{@link AccessControlObserver#POLICY_REMOVED}</li>
+     * </ul>
+     */
+    public AccessControlModifications(Map<K, Integer> modificationMap) {
+        this.modificationMap = Collections.unmodifiableMap(modificationMap);
+    }
+
+    /**
+     * @return Set of <code>Node</code> identifiers or paths.
+     */
+    public Set<K> getNodeIdentifiers() {
+        return modificationMap.keySet();
+    }
+
+    /**
+     * @param identifier
+     * @return The modification type for the specified "identifier". Note that
+     * the object type of the identifier is independent specific. 
+     */
+    public Integer getType(K identifier) {
+        return modificationMap.get(identifier);
+    }
+}
\ No newline at end of file

Propchange: jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/security/authorization/AccessControlModifications.java
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/security/authorization/AccessControlModifications.java
------------------------------------------------------------------------------
    svn:keywords = Author Date Id Revision Rev URL

Added: jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/security/authorization/AccessControlObserver.java
URL: http://svn.apache.org/viewvc/jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/security/authorization/AccessControlObserver.java?rev=950440&view=auto
==============================================================================
--- jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/security/authorization/AccessControlObserver.java (added)
+++ jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/security/authorization/AccessControlObserver.java Wed Jun  2 09:00:44 2010
@@ -0,0 +1,79 @@
+/*
+ * 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.core.security.authorization;
+
+import org.apache.jackrabbit.core.observation.SynchronousEventListener;
+
+import java.util.HashSet;
+import java.util.Set;
+
+/**
+ * <code>AccessControlObserver</code>...
+ */
+public abstract class AccessControlObserver implements SynchronousEventListener {
+    
+    public static final int POLICY_ADDED = 1;
+    public static final int POLICY_REMOVED = 2;
+    public static final int POLICY_MODIFIED = 4;
+
+    private final Set<AccessControlListener> listeners = new HashSet<AccessControlListener>();
+    private final Object listenerMonitor = new Object();
+    
+    protected void close() {
+        synchronized (listenerMonitor) {
+            listeners.clear();
+        }
+    }
+
+    /**
+     * Add a listener that needs to be informed about changes made to access
+     * control.
+     *
+     * @param listener <code>EntryListener</code> to be added.
+     */
+    public void addListener(AccessControlListener listener) {
+        synchronized (listenerMonitor) {
+            listeners.add(listener);
+        }
+    }
+
+    /**
+     * Remove a listener added before.
+     *
+     * @param listener <code>EntryListener</code> to be removed.
+     */
+    public void removeListener(AccessControlListener listener) {
+        synchronized (listenerMonitor) {
+            listeners.remove(listener);
+        }
+    }
+
+    /**
+     * Notifies the listeners about AC modifications.
+     *
+     * @param modifications
+     */
+    protected void notifyListeners(AccessControlModifications modifications) {
+        AccessControlListener[] lstnrs;
+        synchronized (listenerMonitor) {
+            lstnrs = listeners.toArray(new AccessControlListener[listeners.size()]);
+        }
+        for (AccessControlListener lstnr : lstnrs) {
+            lstnr.acModified(modifications);
+        }
+    }
+}
\ No newline at end of file

Propchange: jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/security/authorization/AccessControlObserver.java
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/security/authorization/AccessControlObserver.java
------------------------------------------------------------------------------
    svn:keywords = Author Date Id Revision Rev URL

Modified: jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/security/authorization/CompiledPermissions.java
URL: http://svn.apache.org/viewvc/jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/security/authorization/CompiledPermissions.java?rev=950440&r1=950439&r2=950440&view=diff
==============================================================================
--- jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/security/authorization/CompiledPermissions.java (original)
+++ jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/security/authorization/CompiledPermissions.java Wed Jun  2 09:00:44 2010
@@ -17,6 +17,7 @@
 package org.apache.jackrabbit.core.security.authorization;
 
 import org.apache.jackrabbit.spi.Path;
+import org.apache.jackrabbit.core.id.ItemId;
 
 import javax.jcr.RepositoryException;
 
@@ -74,6 +75,27 @@ public interface CompiledPermissions {
     boolean canReadAll() throws RepositoryException;
 
     /**
+     * Returns <code>true</code> if READ permission is granted for the
+     * <i>existing</i> item with the given <code>Path</code> and/or
+     * <code>ItemId</code>.
+     * This method acts as shortcut for {@link #grants(Path, int)} where
+     * permissions is {@link Permission#READ} and allows to shorten the
+     * evaluation time given the fact that a check for READ permissions is
+     * considered to be the most frequent test.<br>
+     * If both Path and ItemId are not <code>null</code> it is left to the
+     * implementation which parameter to use.n
+     *
+     * @param itemPath The path to the item or <code>null</code> if the ID
+     * should be used to determine the READ permission.
+     * @param itemId The itemId or <code>null</code> if the path should be
+     * used to determine the READ permission.
+     * @return <code>true</code> if the READ permission is granted.
+     * @throws RepositoryException If no item exists with the specified path or
+     * itemId or if some other error occurs.
+     */
+    boolean canRead(Path itemPath, ItemId itemId) throws RepositoryException;
+
+    /**
      * Static implementation of a <code>CompiledPermissions</code> that doesn't
      * grant any permissions at all.
      */
@@ -91,5 +113,8 @@ public interface CompiledPermissions {
         public boolean canReadAll() {
             return false;
         }
+        public boolean canRead(Path itemPath, ItemId itemId) throws RepositoryException {
+            return false;
+        }
     };
 }

Modified: jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/security/authorization/UnmodifiableAccessControlList.java
URL: http://svn.apache.org/viewvc/jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/security/authorization/UnmodifiableAccessControlList.java?rev=950440&r1=950439&r2=950440&view=diff
==============================================================================
--- jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/security/authorization/UnmodifiableAccessControlList.java (original)
+++ jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/security/authorization/UnmodifiableAccessControlList.java Wed Jun  2 09:00:44 2010
@@ -26,6 +26,7 @@ import javax.jcr.Value;
 import javax.jcr.PropertyType;
 
 import java.security.Principal;
+import java.util.Arrays;
 import java.util.List;
 import java.util.Map;
 import java.util.Collections;
@@ -48,6 +49,8 @@ public class UnmodifiableAccessControlLi
 
     private final String path;
 
+    private int hashCode = 0;
+
     /**
      * Construct a new <code>UnmodifiableAccessControlList</code>
      *
@@ -78,10 +81,21 @@ public class UnmodifiableAccessControlLi
      *
      * @param accessControlEntries A list of {@link AccessControlEntry access control entries}.
      */
-    public UnmodifiableAccessControlList(List<AccessControlEntry> accessControlEntries) {
+    public UnmodifiableAccessControlList(List<? extends AccessControlEntry> accessControlEntries) {
+        this(accessControlEntries, null, Collections.<String, Integer>emptyMap());
+    }
+
+    /**
+     * Construct a new <code>UnmodifiableAccessControlList</code>
+     *
+     * @param accessControlEntries
+     * @param path
+     * @param restrictions
+     */
+    public UnmodifiableAccessControlList(List<? extends AccessControlEntry> accessControlEntries, String path, Map<String, Integer>restrictions) {
         this.accessControlEntries = accessControlEntries.toArray(new AccessControlEntry[accessControlEntries.size()]);
-        path = null;
-        restrictions = Collections.emptyMap();
+        this.path = path;
+        this.restrictions = restrictions;
     }
 
     //--------------------------------------------------< AccessControlList >---
@@ -110,10 +124,17 @@ public class UnmodifiableAccessControlLi
         throw new AccessControlException("Unmodifiable ACL. Use AccessControlManager#getApplicablePolicies in order to obtain an modifiable ACL.");
     }
 
+    //----------------------------------------< JackrabbitAccessControlList >---
+    /**
+     * @see org.apache.jackrabbit.api.security.JackrabbitAccessControlList#getRestrictionNames()
+     */
     public String[] getRestrictionNames() {
         return restrictions.keySet().toArray(new String[restrictions.size()]);
     }
 
+    /**
+     * @see org.apache.jackrabbit.api.security.JackrabbitAccessControlList#getRestrictionType(String)
+     */
     public int getRestrictionType(String restrictionName) {
         if (restrictions.containsKey(restrictionName)) {
             return restrictions.get(restrictionName);
@@ -122,27 +143,83 @@ public class UnmodifiableAccessControlLi
         }
     }
 
+    /**
+     * @see org.apache.jackrabbit.api.security.JackrabbitAccessControlList#isEmpty()
+     */
     public boolean isEmpty() {
         return accessControlEntries.length == 0;
     }
 
+    /**
+     * @see org.apache.jackrabbit.api.security.JackrabbitAccessControlList#size()
+     */
     public int size() {
         return accessControlEntries.length;
     }
 
+    /**
+     * @see org.apache.jackrabbit.api.security.JackrabbitAccessControlList#addEntry(Principal, Privilege[], boolean)
+     */
     public boolean addEntry(Principal principal, Privilege[] privileges, boolean isAllow) throws AccessControlException {
         throw new AccessControlException("Unmodifiable ACL. Use AccessControlManager#getPolicy or #getApplicablePolicies in order to obtain an modifiable ACL.");
     }
 
+    /**
+     * @see org.apache.jackrabbit.api.security.JackrabbitAccessControlList#addEntry(Principal, Privilege[], boolean, Map)
+     */
     public boolean addEntry(Principal principal, Privilege[] privileges, boolean isAllow, Map<String, Value> restrictions) throws AccessControlException {
         throw new AccessControlException("Unmodifiable ACL. Use AccessControlManager#getPolicy or #getApplicablePolicies in order to obtain an modifiable ACL.");
     }
 
+    /**
+     * @see org.apache.jackrabbit.api.security.JackrabbitAccessControlList#orderBefore(AccessControlEntry, AccessControlEntry)
+     */
     public void orderBefore(AccessControlEntry srcEntry, AccessControlEntry destEntry) throws AccessControlException {
         throw new AccessControlException("Unmodifiable ACL. Use AccessControlManager#getPolicy or #getApplicablePolicy in order to obtain a modifiable ACL.");
     }
 
+    /**
+     * @see org.apache.jackrabbit.api.security.JackrabbitAccessControlList#getPath()
+     */
     public String getPath() {
         return path;
     }
+
+    //-------------------------------------------------------------< Object >---
+    /**
+     * @see Object#hashCode()
+     */
+    @Override
+    public int hashCode() {
+        if (hashCode == 0) {
+            int result = 17;
+            result = 37 * result + (path != null ? path.hashCode() : 0);
+            for (AccessControlEntry entry : accessControlEntries) {
+                result = 37 * result + entry.hashCode();
+            }
+            for (String restrictionName : restrictions.keySet()) {
+                result = 37 * (restrictionName + "." + restrictions.get(restrictionName)).hashCode();
+            }
+            hashCode = result;
+        }
+        return hashCode;
+    }
+
+    /**
+     * @see Object#equals(Object)
+     */
+    @Override
+    public boolean equals(Object obj) {
+        if (obj == this) {
+            return true;
+        }
+
+        if (obj instanceof UnmodifiableAccessControlList) {
+            UnmodifiableAccessControlList acl = (UnmodifiableAccessControlList) obj;
+            return ((path == null) ? acl.path == null : path.equals(acl.path)) &&
+                    Arrays.equals(accessControlEntries, acl.accessControlEntries) &&
+                    restrictions.equals(acl.restrictions);
+        }
+        return false;
+    }
 }
\ No newline at end of file

Modified: jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/security/authorization/acl/ACLEditor.java
URL: http://svn.apache.org/viewvc/jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/security/authorization/acl/ACLEditor.java?rev=950440&r1=950439&r2=950440&view=diff
==============================================================================
--- jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/security/authorization/acl/ACLEditor.java (original)
+++ jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/security/authorization/acl/ACLEditor.java Wed Jun  2 09:00:44 2010
@@ -44,7 +44,6 @@ import javax.jcr.ValueFormatException;
 import javax.jcr.NodeIterator;
 import javax.jcr.security.AccessControlEntry;
 import javax.jcr.security.AccessControlException;
-import javax.jcr.security.AccessControlList;
 import javax.jcr.security.AccessControlPolicy;
 import javax.jcr.security.Privilege;
 import java.security.Principal;
@@ -87,7 +86,7 @@ public class ACLEditor extends Protected
      * @return the control list
      * @throws RepositoryException if an error occurs
      */
-    AccessControlList getACL(NodeImpl aclNode) throws RepositoryException {
+    ACLTemplate getACL(NodeImpl aclNode) throws RepositoryException {
         return new ACLTemplate(aclNode, privilegeRegistry);
     }
 

Modified: jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/security/authorization/acl/ACLProvider.java
URL: http://svn.apache.org/viewvc/jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/security/authorization/acl/ACLProvider.java?rev=950440&r1=950439&r2=950440&view=diff
==============================================================================
--- jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/security/authorization/acl/ACLProvider.java (original)
+++ jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/security/authorization/acl/ACLProvider.java Wed Jun  2 09:00:44 2010
@@ -16,49 +16,48 @@
  */
 package org.apache.jackrabbit.core.security.authorization.acl;
 
-import java.security.Principal;
-import java.security.acl.Group;
-import java.util.ArrayList;
-import java.util.Collection;
-import java.util.List;
-import java.util.Map;
-import java.util.Set;
-import java.util.Iterator;
-
-import javax.jcr.ItemNotFoundException;
-import javax.jcr.RepositoryException;
-import javax.jcr.Session;
-import javax.jcr.observation.Event;
-import javax.jcr.observation.EventIterator;
-import javax.jcr.security.AccessControlEntry;
-import javax.jcr.security.AccessControlList;
-import javax.jcr.security.AccessControlManager;
-import javax.jcr.security.AccessControlPolicy;
-import javax.jcr.security.Privilege;
-
+import org.apache.commons.collections.map.LRUMap;
 import org.apache.jackrabbit.api.security.principal.PrincipalManager;
 import org.apache.jackrabbit.core.ItemImpl;
+import org.apache.jackrabbit.core.ItemManager;
 import org.apache.jackrabbit.core.NodeImpl;
-import org.apache.jackrabbit.core.PropertyImpl;
 import org.apache.jackrabbit.core.SessionImpl;
+import org.apache.jackrabbit.core.id.ItemId;
 import org.apache.jackrabbit.core.id.NodeId;
-import org.apache.jackrabbit.core.observation.SynchronousEventListener;
+import org.apache.jackrabbit.core.id.PropertyId;
+import org.apache.jackrabbit.core.nodetype.NodeTypeImpl;
 import org.apache.jackrabbit.core.security.SecurityConstants;
+import org.apache.jackrabbit.core.security.authorization.AccessControlListener;
 import org.apache.jackrabbit.core.security.authorization.AbstractAccessControlProvider;
 import org.apache.jackrabbit.core.security.authorization.AbstractCompiledPermissions;
 import org.apache.jackrabbit.core.security.authorization.AccessControlConstants;
 import org.apache.jackrabbit.core.security.authorization.AccessControlEditor;
+import org.apache.jackrabbit.core.security.authorization.AccessControlModifications;
 import org.apache.jackrabbit.core.security.authorization.CompiledPermissions;
 import org.apache.jackrabbit.core.security.authorization.Permission;
 import org.apache.jackrabbit.core.security.authorization.PrivilegeRegistry;
 import org.apache.jackrabbit.core.security.authorization.UnmodifiableAccessControlList;
+import org.apache.jackrabbit.spi.Name;
 import org.apache.jackrabbit.spi.Path;
-import org.apache.jackrabbit.spi.commons.name.PathFactoryImpl;
-import org.apache.jackrabbit.util.Text;
-import org.apache.commons.collections.iterators.IteratorChain;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
+import javax.jcr.ItemNotFoundException;
+import javax.jcr.RepositoryException;
+import javax.jcr.Session;
+import javax.jcr.security.AccessControlEntry;
+import javax.jcr.security.AccessControlList;
+import javax.jcr.security.AccessControlManager;
+import javax.jcr.security.AccessControlPolicy;
+import javax.jcr.security.Privilege;
+import java.security.Principal;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
 /**
  * The ACLProvider generates access control policies out of the items stored
  * in the workspace applying the following rules:
@@ -97,28 +96,12 @@ public class ACLProvider extends Abstrac
      */
     private NodeId rootNodeId;
 
-    //-------------------------------------------------< AccessControlUtils >---
     /**
-     * @see org.apache.jackrabbit.core.security.authorization.AccessControlUtils#isAcItem(Path)
+     * Cache to ease the retrieval of ACEs defined for a given node. This cache
+     * is used by the ACLPermissions created individually for each Session
+     * instance.
      */
-    public boolean isAcItem(Path absPath) throws RepositoryException {
-        Path.Element[] elems = absPath.getElements();
-        for (Path.Element elem : elems) {
-            if (N_POLICY.equals(elem.getName())) {
-                return true;
-            }
-        }
-        return false;
-    }
-
-    /**
-     * Test if the given node is itself a rep:ACL or a rep:ACE node.
-     * @see org.apache.jackrabbit.core.security.authorization.AccessControlUtils#isAcItem(ItemImpl)
-     */
-    public boolean isAcItem(ItemImpl item) throws RepositoryException {
-        NodeImpl n = ((item.isNode()) ? (NodeImpl) item : (NodeImpl) item.getParent());
-        return n.isNodeType(NT_REP_ACL) || n.isNodeType(NT_REP_ACE);
-    }
+    private EntryCollector entryCollector;
 
     //----------------------------------------------< AccessControlProvider >---
     /**
@@ -133,11 +116,20 @@ public class ACLProvider extends Abstrac
         NodeImpl root = (NodeImpl) session.getRootNode();
         rootNodeId = root.getNodeId();
         systemEditor = new ACLEditor(systemSession, this);
+
         // TODO: replace by configurable default policy (see JCR-2331)
         boolean initializedWithDefaults = !configuration.containsKey(PARAM_OMIT_DEFAULT_PERMISSIONS);
         if (initializedWithDefaults && !isAccessControlled(root)) {
             initRootACL(session, systemEditor);
         }
+
+        entryCollector = createEntryCollector((SessionImpl) systemSession);
+    }
+
+    @Override
+    public void close() {
+        super.close();        
+        entryCollector.close();
     }
 
     /**
@@ -148,7 +140,7 @@ public class ACLProvider extends Abstrac
         checkInitialized();
 
         NodeImpl targetNode = (NodeImpl) session.getNode(session.getJCRPath(absPath));
-        NodeImpl node = getNode(targetNode);
+        NodeImpl node = getNode(targetNode, isAcItem(targetNode));
         List<AccessControlList> acls = new ArrayList<AccessControlList>();
 
         // collect all ACLs effective at node
@@ -194,22 +186,37 @@ public class ACLProvider extends Abstrac
             return true;
         } else {
             CompiledPermissions cp = new AclPermissions(principals, false);
-            return cp.grants(PathFactoryImpl.getInstance().getRootPath(), Permission.READ);
+            return cp.canRead(null, rootNodeId);
         }
     }
 
     //----------------------------------------------------------< protected >---
     /**
+     * Create the <code>EntryCollector</code> instance that is used by this
+     * provider to gather the effective ACEs for a given list of principals at a
+     * given node during AC evaluation.
+     *
+     * @param systemSession The system session to create the entry collector for.
+     * @return A new instance of <code>CachingEntryCollector</code>.
+     * @throws RepositoryException If an error occurs.
+     * @see #retrieveResultEntries(NodeImpl, EntryFilter)
+     */
+    protected EntryCollector createEntryCollector(SessionImpl systemSession) throws RepositoryException {
+        return new CachingEntryCollector(systemSession, systemEditor, rootNodeId);
+    }
+
+    /**
      * Retrieve an iterator of <code>AccessControlEntry</code> to be evaluated
      * upon {@link AbstractCompiledPermissions#buildResult}.
      *
      * @param node Target node.
-     * @param principalNames List of principal names.
+     * @param filter The entry filter used to collect the access control entries.
      * @return an iterator of <code>AccessControlEntry</code>.
      * @throws RepositoryException If an error occurs.
      */
-    protected Iterator<AccessControlEntry> retrieveResultEntries(NodeImpl node, List<String> principalNames) throws RepositoryException {
-        return new Entries(node, principalNames).iterator();
+    protected Iterator<AccessControlEntry> retrieveResultEntries(NodeImpl node, EntryFilter filter) throws RepositoryException {
+        Iterator<AccessControlEntry> itr = entryCollector.collectEntries(node, filter).iterator();
+        return itr;
     }
 
     //------------------------------------------------------------< private >---
@@ -219,13 +226,17 @@ public class ACLProvider extends Abstrac
      * searched and returned.
      *
      * @param targetNode The node for which AC information needs to be retrieved.
-     * @return the node
+     * @param isAcItem true if the specified target node defines access control
+     * content; false otherwise.
+     * @return the given <code>targetNode</code> or the nearest non-ac-parent
+     * in case the <code>targetNode</code> itself defines access control content.
      * @throws RepositoryException if an error occurs
      */
-    private NodeImpl getNode(NodeImpl targetNode) throws RepositoryException {
+    private NodeImpl getNode(NodeImpl targetNode, boolean isAcItem) throws RepositoryException {
         NodeImpl node;
-        if (isAcItem(targetNode)) {
-            if (targetNode.isNodeType(NT_REP_ACL)) {
+        if (isAcItem) {
+            Name ntName = ((NodeTypeImpl) targetNode.getPrimaryNodeType()).getQName();
+            if (ntName.equals(NT_REP_ACL)) {
                 node = (NodeImpl) targetNode.getParent();
             } else {
                 node = (NodeImpl) targetNode.getParent().getParent();
@@ -248,10 +259,8 @@ public class ACLProvider extends Abstrac
         // if the given node is access-controlled, construct a new ACL and add
         // it to the list
         if (isAccessControlled(node)) {
-            // build acl for the access controlled node
-            NodeImpl aclNode = node.getNode(N_POLICY);
-            AccessControlList acl = systemEditor.getACL(aclNode);
-            acls.add(new UnmodifiableAccessControlList(acl));
+            // retrieve the entries for the access controlled node
+            acls.add(new UnmodifiableAccessControlList(entryCollector.getEntries(node), node.getPath(), Collections.<String, Integer>emptyMap()));
         }
         // then, recursively look for access controlled parents up the hierarchy.
         if (!rootNodeId.equals(node.getId())) {
@@ -316,22 +325,24 @@ public class ACLProvider extends Abstrac
      * and if it has a child node named
      * {@link AccessControlConstants#N_POLICY "rep:ACL"}.
      *
-     * @param node hte node
-     * @return <code>true</code> if the node is access controlled;
-     *         <code>false</code> otherwise.
+     * @param node the node to be tested
+     * @return <code>true</code> if the node is access controlled and has a
+     * rep:policy child; <code>false</code> otherwise.
      * @throws RepositoryException if an error occurs
      */
     static boolean isAccessControlled(NodeImpl node) throws RepositoryException {
-        return node.isNodeType(NT_REP_ACCESS_CONTROLLABLE) && node.hasNode(N_POLICY);
+        return node.hasNode(N_POLICY) && node.isNodeType(NT_REP_ACCESS_CONTROLLABLE);
     }
 
     //------------------------------------------------< CompiledPermissions >---
     /**
      *
      */
-    private class AclPermissions extends AbstractCompiledPermissions implements SynchronousEventListener {
+    private class AclPermissions extends AbstractCompiledPermissions implements AccessControlListener {
 
         private final List<String> principalNames;
+        private final Map<NodeId, Boolean> readCache = new LRUMap(1000);
+        private final Object monitor = new Object();
 
         private AclPermissions(Set<Principal> principals) throws RepositoryException {
             this(principals, true);
@@ -346,58 +357,17 @@ public class ACLProvider extends Abstrac
             if (listenToEvents) {
                 /*
                  Make sure this AclPermission recalculates the permissions if
-                 any ACL concerning it is modified. interesting events are:
-                 - new ACE-entry for any of the principals (NODE_ADDED)
-                 - changing ACE-entry for any of the principals (PROPERTY_CHANGED)
-                   > new permissions granted/denied
-                   >
-                 - removed ACE-entry for any of the principals (NODE_REMOVED)
-                */
-                int events = Event.PROPERTY_CHANGED | Event.NODE_ADDED | Event.NODE_REMOVED;
-                String[] ntNames = new String[] {
-                        resolver.getJCRName(NT_REP_ACE),
-                        resolver.getJCRName(NT_REP_ACL)
-                };
-                observationMgr.addEventListener(this, events, session.getRootNode().getPath(), true, null, ntNames, true);
+                 any ACL concerning it is modified.
+                 */
+                 entryCollector.addListener(this);
             }
         }
 
-        //------------------------------------< AbstractCompiledPermissions >---
-        /**
-         * @see AbstractCompiledPermissions#buildResult(Path)
-         */
-        @Override
-        protected Result buildResult(Path absPath) throws RepositoryException {
-            boolean existingNode = false;
-            NodeImpl node = null;
-            String jcrPath = resolver.getJCRPath(absPath);
-
-            if (session.nodeExists(jcrPath)) {
-                node = (NodeImpl) session.getNode(jcrPath);
-                existingNode = true;
-            } else {
-                // path points to existing prop or non-existing item (node or prop).
-                // -> find the nearest persisted node
-                String parentPath = Text.getRelativeParent(jcrPath, 1);
-                while (parentPath.length() > 0) {
-                    if (session.nodeExists(parentPath)) {
-                        node = (NodeImpl) session.getNode(parentPath);
-                        break;
-                    }
-                    parentPath = Text.getRelativeParent(parentPath, 1);
-                }
-            }
-
-            if (node == null) {
-                // should never get here
-                throw new ItemNotFoundException("Item out of hierarchy.");
-            }
-
-            boolean isAcItem = isAcItem(absPath);
-
+        private Result buildResult(NodeImpl node, boolean existingNode, boolean isAcItem, EntryFilter filter) throws RepositoryException {
             // retrieve all ACEs at path or at the direct ancestor of path that
             // apply for the principal names.
-            Iterator<AccessControlEntry> entries = retrieveResultEntries(getNode(node), principalNames);
+            Iterator<AccessControlEntry> entries = retrieveResultEntries(getNode(node, isAcItem), filter);
+
             /*
              Calculate privileges and permissions:
              Since the ACEs only define privileges on a node and do not allow
@@ -421,7 +391,7 @@ public class ACLProvider extends Abstrac
                  defined locally must be treated different than inherited entries.
                  */
                 int entryBits = ace.getPrivilegeBits();
-                boolean isLocal = existingNode && ace.isLocal(jcrPath);
+                boolean isLocal = existingNode && ace.isLocal(node.getNodeId());
                 if (!isLocal) {
                     if (ace.isAllow()) {
                         parentAllows |= Permission.diff(entryBits, parentDenies);
@@ -442,163 +412,115 @@ public class ACLProvider extends Abstrac
             return new Result(allows, denies, allowPrivileges, denyPrivileges);
         }
 
-        //--------------------------------------------< CompiledPermissions >---
+        //------------------------------------< AbstractCompiledPermissions >---
         /**
-         * @see CompiledPermissions#close()
+         * @see AbstractCompiledPermissions#buildResult(Path)
          */
         @Override
-        public void close() {
+        protected Result buildResult(Path absPath) throws RepositoryException {
+            boolean existingNode = false;
+            NodeImpl node;
+
+            ItemManager itemMgr = session.getItemManager();
             try {
-                observationMgr.removeEventListener(this);
+                ItemImpl item = itemMgr.getItem(absPath);
+                if (item.isNode()) {
+                    node = (NodeImpl) item;
+                    existingNode = true;
+                } else {
+                    node = (NodeImpl) item.getParent();
+                }
             } catch (RepositoryException e) {
-                log.debug("Unable to unregister listener: ", e.getMessage());
+                // path points to a non-persisted item.
+                // -> find the nearest persisted node starting from the root.
+                Path.Element[] elems = absPath.getElements();
+                NodeImpl parent = (NodeImpl) session.getRootNode();
+                for (int i = 1; i < elems.length - 1; i++) {
+                    Name name = elems[i].getName();
+                    int index = elems[i].getIndex();
+                    if (!parent.hasNode(name, index)) {
+                        // last persisted node reached
+                        break;
+                    }
+                    parent = parent.getNode(name, index);
+
+                }
+                node = parent;
             }
-            super.close();
+
+            if (node == null) {
+                // should never get here
+                throw new ItemNotFoundException("Item out of hierarchy.");
+            }
+
+            boolean isAcItem = isAcItem(absPath);
+            return buildResult(node, existingNode, isAcItem, new EntryFilterImpl(principalNames));
         }
 
-        //--------------------------------------------------< EventListener >---
         /**
-         * @see javax.jcr.observation.EventListener#onEvent(EventIterator)
+         * @see AbstractCompiledPermissions#clearCache()
          */
-        public synchronized void onEvent(EventIterator events) {
-            // only invalidate cache if any of the events affects the
-            // nodes defining permissions for principals compiled here.
-            boolean clearCache = false;
-            while (events.hasNext() && !clearCache) {
-                try {
-                    Event ev = events.nextEvent();
-                    String path = ev.getPath();
-                    switch (ev.getType()) {
-                        case Event.NODE_ADDED:
-                            // test if the new node is an ACE node that affects
-                            // the permission of any of the principals listed in
-                            // principalNames.
-                            NodeImpl n = (NodeImpl) session.getNode(path);
-                            if (n.isNodeType(NT_REP_ACE) &&
-                                    principalNames.contains(n.getProperty(P_PRINCIPAL_NAME).getString())) {
-                                clearCache = true;
-                            }
-                            break;
-                        case Event.PROPERTY_REMOVED:
-                        case Event.NODE_REMOVED:
-                            // can't find out if the removed ACL/ACE node was
-                            // relevant for the principals
-                            clearCache = true;
-                            break;
-                        case Event.PROPERTY_ADDED:
-                        case Event.PROPERTY_CHANGED:
-                            // test if the added/changed prop belongs to an ACe
-                            // node and affects the permission of any of the
-                            // principals listed in principalNames.
-                            PropertyImpl p = (PropertyImpl) session.getProperty(path);
-                            NodeImpl parent = (NodeImpl) p.getParent();
-                            if (parent.isNodeType(NT_REP_ACE)) {
-                                String principalName = null;
-                                if (P_PRIVILEGES.equals(p.getQName())) {
-                                    // test if principal-name sibling-prop matches
-                                    principalName = parent.getProperty(P_PRINCIPAL_NAME).getString();
-                                } else if (P_PRINCIPAL_NAME.equals(p.getQName())) {
-                                    // a new ace or an ace change its principal-name.
-                                    principalName = p.getString();
-                                }
-                                if (principalName != null &&
-                                        principalNames.contains(principalName)) {
-                                    clearCache = true;
-                                }
-                            }
-                            break;
-                        case Event.NODE_MOVED:
-                            // protected ac nodes cannot be moved around
-                            // -> nothing to do TODO check again
-                            break;
-                        default:
-                            // illegal event-type: should never occur. ignore
-                    }
-                } catch (RepositoryException e) {
-                    // should not get here
-                    log.warn("Internal error: ", e.getMessage());
-                }
-            }
-            if (clearCache) {
-                clearCache();
+        @Override
+        protected void clearCache() {
+            synchronized (monitor) {
+                readCache.clear();
             }
+            super.clearCache();
         }
-    }
-
-    //--------------------------------------------------------------------------
-    /**
-     * Inner class used to collect ACEs for a given set of principals throughout
-     * the node hierarchy.
-     */
-    private class Entries {
 
-        private final Collection<String> principalNames;
-        private final List<AccessControlEntry> userAces = new ArrayList();
-        private final List<AccessControlEntry> groupAces = new ArrayList();
-
-        private Entries(NodeImpl node, Collection<String> principalNames) throws RepositoryException {
-            this.principalNames = principalNames;
-            collectEntries(node);
-        }
-
-        private void collectEntries(NodeImpl node) throws RepositoryException {
-            // if the given node is access-controlled, construct a new ACL and add
-            // it to the list
-            if (isAccessControlled(node)) {
-                // build acl for the access controlled node
-                NodeImpl aclNode = node.getNode(N_POLICY);
-                //collectEntries(aclNode, principalNamesToEntries);
-                collectEntriesFromAcl(aclNode);
-            }
-            // recursively look for access controlled parents up the hierarchy.
-            if (!rootNodeId.equals(node.getId())) {
-                NodeImpl parentNode = (NodeImpl) node.getParent();
-                collectEntries(parentNode);
-            }
+        //--------------------------------------------< CompiledPermissions >---
+        /**
+         * @see CompiledPermissions#close()
+         */
+        @Override
+        public void close() {
+            entryCollector.removeListener(this);
+            super.close();
         }
 
         /**
-         * Separately collect the entries defined for the user and group
-         * principals.
-         *
-         * @param aclNode acl node
-         * @throws RepositoryException if an error occurs
+         * @see CompiledPermissions#canRead(Path, ItemId)
          */
-        private void collectEntriesFromAcl(NodeImpl aclNode) throws RepositoryException {
-            // first collect aces present on the given aclNode.
-            List<AccessControlEntry> gaces = new ArrayList<AccessControlEntry>();
-            List<AccessControlEntry> uaces = new ArrayList<AccessControlEntry>();
-
-            ACLTemplate tmpl = (ACLTemplate) systemEditor.getACL(aclNode);
-            for (AccessControlEntry ace : tmpl.getAccessControlEntries()) {
-                Principal principal = ace.getPrincipal();
-                // only process aceNode if 'principalName' is contained in the given set
-                if (principalNames.contains(principal.getName())) {
-                    // add it to the proper list (e.g. separated by principals)
-                    /**
-                     * NOTE: access control entries must be collected in reverse
-                     * order in order to assert proper evaluation.
-                     */
-                    if (principal instanceof Group) {
-                        gaces.add(0, ace);
-                    } else {
-                        uaces.add(0, ace);
-                    }
-                }
+        public boolean canRead(Path path, ItemId itemId) throws RepositoryException {
+            ItemId id = (itemId == null) ? session.getHierarchyManager().resolvePath(path) : itemId;
+            /* currently READ access cannot be denied to individual properties.
+               if the parent node is readable the properties are as well.
+               this simplifies the canRead test as well as the caching.
+             */
+            boolean existingNode = false;
+            NodeId nodeId;
+            if (id.denotesNode()) {
+                nodeId = (NodeId) id;
+                // since method may only be called for existing nodes the
+                // flag be set to true if the id identifies a node.
+                existingNode = true;
+            } else {
+                nodeId = ((PropertyId) id).getParentId();
             }
 
-            // add the lists of aces to the overall lists that contain the entries
-            // throughout the hierarchy.
-            if (!gaces.isEmpty()) {
-                groupAces.addAll(gaces);
-            }
-            if (!uaces.isEmpty()) {
-                userAces.addAll(uaces);
+            boolean canRead;
+            synchronized (monitor) {
+                if (readCache.containsKey(nodeId)) {
+                    canRead = readCache.get(nodeId);
+                } else {
+                    ItemManager itemMgr = session.getItemManager();
+                    NodeImpl node = (NodeImpl) itemMgr.getItem(nodeId);
+                    Result result = buildResult(node, existingNode, isAcItem(node), new EntryFilterImpl(principalNames));
+
+                    canRead = result.grants(Permission.READ);
+                    readCache.put(nodeId, canRead);
+                }
             }
+            return canRead;
         }
 
-        private Iterator<AccessControlEntry> iterator() {
-            return new IteratorChain(userAces.iterator(), groupAces.iterator());
+        //----------------------------------------< ACLModificationListener >---
+        /**
+         * @see org.apache.jackrabbit.core.security.authorization.AccessControlListener#acModified(AccessControlModifications)
+         */
+        public void acModified(AccessControlModifications modifications) {
+            // ignore the details of the modifications and clear all caches.
+            clearCache();
         }
     }
 }

Modified: jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/security/authorization/acl/ACLTemplate.java
URL: http://svn.apache.org/viewvc/jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/security/authorization/acl/ACLTemplate.java?rev=950440&r1=950439&r2=950440&view=diff
==============================================================================
--- jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/security/authorization/acl/ACLTemplate.java (original)
+++ jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/security/authorization/acl/ACLTemplate.java Wed Jun  2 09:00:44 2010
@@ -36,6 +36,8 @@ import org.apache.jackrabbit.api.securit
 import org.apache.jackrabbit.api.security.principal.PrincipalManager;
 import org.apache.jackrabbit.core.NodeImpl;
 import org.apache.jackrabbit.core.SessionImpl;
+import org.apache.jackrabbit.core.id.NodeId;
+import org.apache.jackrabbit.core.nodetype.NodeTypeImpl;
 import org.apache.jackrabbit.core.security.authorization.AbstractACLTemplate;
 import org.apache.jackrabbit.core.security.authorization.AccessControlConstants;
 import org.apache.jackrabbit.core.security.authorization.AccessControlEntryImpl;
@@ -76,6 +78,14 @@ class ACLTemplate extends AbstractACLTem
     private final PrivilegeRegistry privilegeRegistry;
 
     /**
+     * The id of the access controlled node or <code>null</code> if this
+     * ACLTemplate isn't created for an existing access controlled node.
+     * Used for the Entry#isLocal(NodeId) call only in order to avoid calls
+     * to {@link javax.jcr.Node#getPath()}.
+     */
+    private final NodeId id;
+
+    /**
      * Construct a new empty {@link ACLTemplate}.
      *
      * @param path path
@@ -88,6 +98,7 @@ class ACLTemplate extends AbstractACLTem
         super(path, valueFactory);
         this.principalMgr = principalMgr;
         this.privilegeRegistry = privilegeRegistry;
+        this.id = null;
     }
 
     /**
@@ -100,13 +111,14 @@ class ACLTemplate extends AbstractACLTem
      */
     ACLTemplate(NodeImpl aclNode, PrivilegeRegistry privilegeRegistry) throws RepositoryException {
         super((aclNode != null) ? aclNode.getParent().getPath() : null, (aclNode != null) ? aclNode.getSession().getValueFactory() : null);
-        if (aclNode == null || !aclNode.isNodeType(AccessControlConstants.NT_REP_ACL)) {
+        if (aclNode == null || !AccessControlConstants.NT_REP_ACL.equals(((NodeTypeImpl)aclNode.getPrimaryNodeType()).getQName())) {
             throw new IllegalArgumentException("Node must be of type 'rep:ACL'");
         }
         SessionImpl sImpl = (SessionImpl) aclNode.getSession();
         principalMgr = sImpl.getPrincipalManager();
-        
+
         this.privilegeRegistry = privilegeRegistry;
+        this.id = aclNode.getParentId();
 
         // load the entries:
         AccessControlManager acMgr = sImpl.getAccessControlManager();
@@ -130,7 +142,7 @@ class ACLTemplate extends AbstractACLTem
                 Entry ace = createEntry(
                         princ,
                         privs,
-                        aceNode.isNodeType(AccessControlConstants.NT_REP_GRANT_ACE));
+                        AccessControlConstants.NT_REP_GRANT_ACE.equals(((NodeTypeImpl) aceNode.getPrimaryNodeType()).getQName()));
                 // add the entry
                 internalAdd(ace);
             } catch (RepositoryException e) {
@@ -141,7 +153,7 @@ class ACLTemplate extends AbstractACLTem
 
     /**
      * Create a new entry omitting any validation checks.
-     * 
+     *
      * @param principal
      * @param privileges
      * @param isAllow
@@ -267,14 +279,6 @@ class ACLTemplate extends AbstractACLTem
 
     //--------------------------------------------------< AccessControlList >---
     /**
-     * @see javax.jcr.security.AccessControlList#getAccessControlEntries()
-     */
-    public AccessControlEntry[] getAccessControlEntries() throws RepositoryException {
-        List<? extends AccessControlEntry> l = getEntries();
-        return l.toArray(new AccessControlEntry[l.size()]);
-    }
-
-    /**
      * @see javax.jcr.security.AccessControlList#removeAccessControlEntry(AccessControlEntry)
      */
     public synchronized void removeAccessControlEntry(AccessControlEntry ace)
@@ -310,20 +314,6 @@ class ACLTemplate extends AbstractACLTem
     }
 
     /**
-     * @see org.apache.jackrabbit.api.security.JackrabbitAccessControlList#isEmpty()
-     */
-    public boolean isEmpty() {
-        return entries.isEmpty();
-    }
-
-    /**
-     * @see org.apache.jackrabbit.api.security.JackrabbitAccessControlList#size()
-     */
-    public int size() {
-        return getEntries().size();
-    }
-
-    /**
      * @see org.apache.jackrabbit.api.security.JackrabbitAccessControlList#addEntry(Principal, Privilege[], boolean, Map)
      */
     public boolean addEntry(Principal principal, Privilege[] privileges,
@@ -342,6 +332,7 @@ class ACLTemplate extends AbstractACLTem
      * @return always zero
      * @see Object#hashCode()
      */
+    @Override
     public int hashCode() {
         return 0;
     }
@@ -353,6 +344,7 @@ class ACLTemplate extends AbstractACLTem
      * @return true if the path and the entries are equal; false otherwise.
      * @see Object#equals(Object)
      */
+    @Override
     public boolean equals(Object obj) {
         if (obj == this) {
             return true;
@@ -377,12 +369,12 @@ class ACLTemplate extends AbstractACLTem
         }
 
         /**
-         * @param nodePath
+         * @param nodeId
          * @return <code>true</code> if this entry is defined on the node
-         * at <code>nodePath</code>
+         * at <code>nodeId</code>
          */
-        boolean isLocal(String nodePath) {
-            return path != null && path.equals(nodePath);
+        boolean isLocal(NodeId nodeId) {
+            return id != null && id.equals(nodeId);
         }
     }
 }