You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@jackrabbit.apache.org by ju...@apache.org on 2011/04/06 15:48:24 UTC

svn commit: r1089453 - in /jackrabbit/branches/2.2: ./ jackrabbit-core/src/main/java/org/apache/jackrabbit/core/ jackrabbit-core/src/main/java/org/apache/jackrabbit/core/security/authorization/acl/ jackrabbit-core/src/main/java/org/apache/jackrabbit/co...

Author: jukka
Date: Wed Apr  6 13:48:24 2011
New Revision: 1089453

URL: http://svn.apache.org/viewvc?rev=1089453&view=rev
Log:
2.2: Merged revisions 1087304 and 1089436 (JCR-2890)

Modified:
    jackrabbit/branches/2.2/   (props changed)
    jackrabbit/branches/2.2/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/SystemSession.java
    jackrabbit/branches/2.2/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/security/authorization/acl/EntryCollector.java
    jackrabbit/branches/2.2/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/state/DefaultISMLocking.java

Propchange: jackrabbit/branches/2.2/
------------------------------------------------------------------------------
--- svn:mergeinfo (original)
+++ svn:mergeinfo Wed Apr  6 13:48:24 2011
@@ -2,4 +2,4 @@
 /jackrabbit/sandbox/JCR-1456:774917-886178
 /jackrabbit/sandbox/JCR-2170:812417-816332
 /jackrabbit/sandbox/tripod-JCR-2209:795441-795863
-/jackrabbit/trunk:1038201,1038203,1038205,1038657,1039064,1039347,1039408,1039422-1039423,1039888,1039946,1040033,1040090,1040459,1040601,1040606,1040661,1040958,1041379,1041439,1041761,1042643,1042647,1042978-1042982,1043084-1043086,1043088,1043343,1043357-1043358,1043430,1043554,1043616,1043618,1043637,1043656,1043893,1043897,1044239,1044312,1044451,1044613,1049473,1049491,1049514,1049518,1049520,1049859,1049870,1049874,1049878,1049880,1049883,1049889,1049891,1049894-1049895,1049899-1049901,1049909-1049911,1049915-1049916,1049919,1049923,1049925,1049931,1049936,1049939,1050212,1050298,1050346,1050551,1055068,1055070-1055071,1055116-1055117,1055127,1055134,1055164,1055498,1060431,1060434,1060753,1063756,1065599,1065622,1066059,1066071,1069831,1071562,1071573,1071680,1074140,1079314,1079317,1089032
+/jackrabbit/trunk:1038201,1038203,1038205,1038657,1039064,1039347,1039408,1039422-1039423,1039888,1039946,1040033,1040090,1040459,1040601,1040606,1040661,1040958,1041379,1041439,1041761,1042643,1042647,1042978-1042982,1043084-1043086,1043088,1043343,1043357-1043358,1043430,1043554,1043616,1043618,1043637,1043656,1043893,1043897,1044239,1044312,1044451,1044613,1049473,1049491,1049514,1049518,1049520,1049859,1049870,1049874,1049878,1049880,1049883,1049889,1049891,1049894-1049895,1049899-1049901,1049909-1049911,1049915-1049916,1049919,1049923,1049925,1049931,1049936,1049939,1050212,1050298,1050346,1050551,1055068,1055070-1055071,1055116-1055117,1055127,1055134,1055164,1055498,1060431,1060434,1060753,1063756,1065599,1065622,1066059,1066071,1069831,1071562,1071573,1071680,1074140,1079314,1079317,1087304,1089032,1089436

Modified: jackrabbit/branches/2.2/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/SystemSession.java
URL: http://svn.apache.org/viewvc/jackrabbit/branches/2.2/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/SystemSession.java?rev=1089453&r1=1089452&r2=1089453&view=diff
==============================================================================
--- jackrabbit/branches/2.2/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/SystemSession.java (original)
+++ jackrabbit/branches/2.2/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/SystemSession.java Wed Apr  6 13:48:24 2011
@@ -105,6 +105,27 @@ class SystemSession extends SessionImpl 
         return new SystemAccessManager();
     }
 
+    /**
+     * Creates and returns a new <em>system</em> session for the
+     * given workspace.
+     *
+     * @param workspaceName workspace name,
+     *                      or <code>null</code> for the default workspace
+     */
+    @Override
+    public SessionImpl createSession(String workspaceName)
+            throws RepositoryException {
+        if (workspaceName == null) {
+            WorkspaceManager wm = repositoryContext.getWorkspaceManager();
+            workspaceName = wm.getDefaultWorkspaceName();
+        }
+
+        RepositoryImpl repository = repositoryContext.getRepository();
+        WorkspaceConfig wspConfig =
+            repository.getWorkspaceInfo(workspaceName).getConfig();
+        return create(repositoryContext, wspConfig);
+    }
+
     //--------------------------------------------------------< inner classes >
     /**
      * An access manager that grants access to everything.

Modified: jackrabbit/branches/2.2/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/security/authorization/acl/EntryCollector.java
URL: http://svn.apache.org/viewvc/jackrabbit/branches/2.2/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/security/authorization/acl/EntryCollector.java?rev=1089453&r1=1089452&r2=1089453&view=diff
==============================================================================
--- jackrabbit/branches/2.2/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/security/authorization/acl/EntryCollector.java (original)
+++ jackrabbit/branches/2.2/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/security/authorization/acl/EntryCollector.java Wed Apr  6 13:48:24 2011
@@ -23,12 +23,14 @@ import org.apache.jackrabbit.core.securi
 import org.apache.jackrabbit.core.security.authorization.AccessControlModifications;
 import org.apache.jackrabbit.core.security.authorization.AccessControlObserver;
 import org.apache.jackrabbit.core.security.authorization.PrivilegeRegistry;
+import org.apache.jackrabbit.spi.Name;
 import org.apache.jackrabbit.util.Text;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
 import javax.jcr.Node;
 import javax.jcr.RepositoryException;
+import javax.jcr.Session;
 import javax.jcr.observation.Event;
 import javax.jcr.observation.EventIterator;
 import javax.jcr.observation.ObservationManager;
@@ -64,11 +66,6 @@ public class EntryCollector extends Acce
     protected final NodeId rootID;
 
     private final PrivilegeRegistry privilegeRegistry;
-    
-    /**
-     * Standard JCR name form of the {@link #N_POLICY} constant.
-     */
-    private final String repPolicyName;
 
     /**
      *
@@ -81,7 +78,6 @@ public class EntryCollector extends Acce
         this.rootID = rootID;
 
         privilegeRegistry = new PrivilegeRegistry(systemSession);
-        repPolicyName = systemSession.getJCRName(N_POLICY);
 
         ObservationManager observationMgr = systemSession.getWorkspace().getObservationManager();
         /*
@@ -205,102 +201,191 @@ public class EntryCollector extends Acce
         return ((NodeImpl) systemSession.getItemManager().getItem(nodeId));
     }
 
-    //------------------------------------------------------------< private >---
-
-    private static NodeId accessControlledIdFromAclNode(Node aclNode) throws RepositoryException {
-        return ((NodeImpl) aclNode.getParent()).getNodeId();
-    }
-
-    private static NodeId accessControlledIdFromAceNode(Node aceNode) throws RepositoryException {
-        return ((NodeImpl) aceNode.getParent().getParent()).getNodeId();
-    }
-
-    private static void addModification(NodeId accessControllNodeId, int modType,
-                                        Map<NodeId,Integer> modMap) {
-        if (modMap.containsKey(accessControllNodeId)) {
-            // update modMap
-            modMap.put(accessControllNodeId, modType | modMap.get(accessControllNodeId));
-        } else {
-            modMap.put(accessControllNodeId, modType);
-        }
-    }
-    
     //------------------------------------------------------< EventListener >---
+
     /**
-     * Collect is of access controlled nodes that are effected by access control
-     * modification together with the corresponding modification type and
-     * finally inform listeners about the modifications.
+     * Collects access controlled nodes that are effected by access control
+     * changes together with the corresponding modification types, and
+     * notifies access control listeners about the modifications.
      * 
      * @param events
      */
     public void onEvent(EventIterator events) {
-        /* map of access-controlled nodeId to type of ac modification */
-        Map<NodeId,Integer> modMap = new HashMap<NodeId,Integer>();
-
-        // collect the ids of all access controlled nodes that have been affected
-        // by the events and thus need their cache entries updated or cleared.
-        while (events.hasNext()) {
+        try {
+            // JCR-2890: We need to use a fresh new session here to avoid
+            // deadlocks caused by concurrent threads possibly using the
+            // systemSession instance for other purposes.
+            String workspaceName = systemSession.getWorkspace().getName();
+            Session session = systemSession.createSession(workspaceName);
             try {
-                Event ev = events.nextEvent();
-                String identifier = ev.getIdentifier();
-                String path = ev.getPath();
+                // Sift through the events to find access control modifications
+                ACLEventSieve sieve = new ACLEventSieve(session);
+                sieve.siftEvents(events);
+
+                // Notify listeners and eventually clean up internal caches
+                AccessControlModifications<NodeId> mods =
+                    sieve.getModifications();
+                if (!mods.getNodeIdentifiers().isEmpty()) {
+                    notifyListeners(mods);
+                }
+            } finally {
+                session.logout();
+            }
+        } catch (RepositoryException e) {
+            log.error("Failed to process access control modifications", e);
+        }
+    }
 
-                switch (ev.getType()) {
+    /**
+     * Private utility class for sifting through observation events on
+     * ACL, ACE and Policy nodes to find out the nodes whose access controls
+     * have changed. Used by the {@link EntryCollector#onEvent(EventIterator)}
+     * method.
+     */
+    private static class ACLEventSieve {
+
+        /** Session with system privileges. */
+        private final Session session;
+
+        /**
+         * Standard JCR name form of the
+         * {@link AccessControlConstants#N_POLICY} constant.
+         */
+        private final String repPolicyName;
+
+        /**
+         * Map of access-controlled nodeId to type of access control modification.
+         */
+        private final Map<NodeId, Integer> modMap =
+            new HashMap<NodeId,Integer>();
+
+        public ACLEventSieve(Session session) throws RepositoryException {
+            this.session = session;
+            Name repPolicy = AccessControlConstants.N_POLICY;
+            this.repPolicyName =
+                session.getNamespacePrefix(repPolicy.getNamespaceURI())
+                + ":" + repPolicy.getLocalName();
+        }
+
+        /**
+         * Collects the identifiers of all access controlled nodes that have
+         * been affected by the events, and thus need their cache entries
+         * updated or cleared.
+         *
+         * @param events access control modification events
+         */
+        public void siftEvents(EventIterator events) {
+            while (events.hasNext()) {
+                Event event = events.nextEvent();
+                try {
+                    switch (event.getType()) {
                     case Event.NODE_ADDED:
-                        NodeImpl n = (NodeImpl) systemSession.getNodeByIdentifier(identifier);
-                        if (n.isNodeType(NT_REP_ACL)) {
-                            // a new ACL was added -> use the added node to update
-                            // the cache.
-                            addModification(accessControlledIdFromAclNode(n), POLICY_ADDED, modMap);
-                        } else if (n.isNodeType(NT_REP_ACE)) {
-                            // a new ACE was added -> use the parent node (acl)
-                            // to update the cache.
-                            addModification(accessControlledIdFromAceNode(n), POLICY_MODIFIED, modMap);
-                        } /* else: some other node added below an access controlled
-                             parent node -> not interested. */
+                        siftNodeAdded(event.getIdentifier());
                         break;
                     case Event.NODE_REMOVED:
-                        String parentPath = Text.getRelativeParent(path, 1);
-                        if (systemSession.nodeExists(parentPath)) {
-                            NodeImpl parent = (NodeImpl) systemSession.getNode(parentPath);
-                            if (repPolicyName.equals(Text.getName(path))){
-                                // the complete acl was removed -> clear cache entry
-                                addModification(parent.getNodeId(), POLICY_REMOVED, modMap);
-                            } else if (parent.isNodeType(NT_REP_ACL)) {
-                                // an ace was removed -> refresh cache for the
-                                // containing access control list upon next access
-                                addModification(accessControlledIdFromAclNode(parent), POLICY_MODIFIED, modMap);
-                            } /* else:
-                             a) some other child node of an access controlled
-                                node -> not interested.
-                             b) a child node of an ACE. not relevant for this
-                                implementation -> ignore
-                           */
-                        } else {
-                            log.debug("Cannot process NODE_REMOVED event. Parent " + parentPath + " doesn't exist (anymore).");
-                        }
+                        siftNodeRemoved(event.getPath());
                         break;
                     case Event.PROPERTY_CHANGED:
-                        // test if the changed prop belongs to an ACE
-                        NodeImpl parent = (NodeImpl) systemSession.getNodeByIdentifier(identifier);
-                        if (parent.isNodeType(NT_REP_ACE)) {
-                            addModification(accessControlledIdFromAceNode(parent), POLICY_MODIFIED, modMap);
-                        } /* some other property below an access controlled node
-                             changed -> not interested. (NOTE: rep:ACL doesn't
-                             define any properties. */
+                        siftPropertyChanged(event.getIdentifier());
                         break;
                     default:
                         // illegal event-type: should never occur. ignore
+                    }
+                } catch (RepositoryException e) {
+                    // should not get here
+                    log.error("Failed to process ACL event: " + event, e);
                 }
-            } catch (RepositoryException e) {
-                // should not get here
-                log.error("Internal error: ", e);
             }
         }
 
-        if (!modMap.isEmpty()) {
-            // notify listeners and eventually clean up internal caches.
-            notifyListeners(new AccessControlModifications<NodeId>(modMap));
+        /**
+         * Returns the access control modifications collected from
+         * related observation events.
+         *
+         * @return access control modifications
+         */
+        public AccessControlModifications<NodeId> getModifications() {
+            return new AccessControlModifications<NodeId>(modMap);
+        }
+
+        private void siftNodeAdded(String identifier)
+                throws RepositoryException {
+            NodeImpl n = (NodeImpl) session.getNodeByIdentifier(identifier);
+            if (n.isNodeType(EntryCollector.NT_REP_ACL)) {
+                // a new ACL was added -> use the added node to update
+                // the cache.
+                addModification(
+                        accessControlledIdFromAclNode(n),
+                        AccessControlObserver.POLICY_ADDED);
+            } else if (n.isNodeType(EntryCollector.NT_REP_ACE)) {
+                // a new ACE was added -> use the parent node (acl)
+                // to update the cache.
+                addModification(
+                        accessControlledIdFromAceNode(n),
+                        AccessControlObserver.POLICY_MODIFIED);
+            } /* else: some other node added below an access controlled
+               parent node -> not interested. */
+        }
+
+        private void siftNodeRemoved(String path) throws RepositoryException {
+            String parentPath = Text.getRelativeParent(path, 1);
+            if (session.nodeExists(parentPath)) {
+                NodeImpl parent = (NodeImpl) session.getNode(parentPath);
+                if (repPolicyName.equals(Text.getName(path))){
+                    // the complete ACL was removed -> clear cache entry
+                    addModification(
+                            parent.getNodeId(),
+                            AccessControlObserver.POLICY_REMOVED);
+                } else if (parent.isNodeType(EntryCollector.NT_REP_ACL)) {
+                    // an ace was removed -> refresh cache for the
+                    // containing access control list upon next access
+                    addModification(
+                            accessControlledIdFromAclNode(parent),
+                            AccessControlObserver.POLICY_MODIFIED);
+                } /* else:
+                         a) some other child node of an access controlled
+                            node -> not interested.
+                         b) a child node of an ACE. not relevant for this
+                            implementation -> ignore
+                 */
+            } else {
+                log.debug("Cannot process NODE_REMOVED event."
+                        + " Parent {} doesn't exist (anymore).",
+                        parentPath);
+            }
+        }
+
+        private void siftPropertyChanged(String identifier)
+                throws RepositoryException {
+            // test if the changed prop belongs to an ACE
+            NodeImpl parent = (NodeImpl) session.getNodeByIdentifier(identifier);
+            if (parent.isNodeType(EntryCollector.NT_REP_ACE)) {
+                addModification(
+                        accessControlledIdFromAceNode(parent),
+                        AccessControlObserver.POLICY_MODIFIED);
+            } /* some other property below an access controlled node
+                 changed -> not interested. (NOTE: rep:ACL doesn't
+                 define any properties. */
         }
+
+        private NodeId accessControlledIdFromAclNode(Node aclNode)
+                throws RepositoryException {
+            return ((NodeImpl) aclNode.getParent()).getNodeId();
+        }
+
+        private NodeId accessControlledIdFromAceNode(Node aceNode)
+                throws RepositoryException {
+            return accessControlledIdFromAclNode(aceNode.getParent());
+        }
+
+        private void addModification(NodeId accessControllNodeId, int modType) {
+            if (modMap.containsKey(accessControllNodeId)) {
+                // update modMap
+                modType |= modMap.get(accessControllNodeId);
+            }
+            modMap.put(accessControllNodeId, modType);
+        }
+
     }
-}
\ No newline at end of file
+
+}

Modified: jackrabbit/branches/2.2/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/state/DefaultISMLocking.java
URL: http://svn.apache.org/viewvc/jackrabbit/branches/2.2/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/state/DefaultISMLocking.java?rev=1089453&r1=1089452&r2=1089453&view=diff
==============================================================================
--- jackrabbit/branches/2.2/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/state/DefaultISMLocking.java (original)
+++ jackrabbit/branches/2.2/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/state/DefaultISMLocking.java Wed Apr  6 13:48:24 2011
@@ -51,6 +51,14 @@ public class DefaultISMLocking implement
     };
 
     /**
+     * Flag for determining whether this locking strategy should give
+     * preference to writers or not. If writers are preferred (which
+     * is the default setting), then all readers will get blocked whenever
+     * there's a writer waiting for the lock.
+     */
+    private boolean writerPreference = true;
+
+    /**
      * Number of writer threads waiting. While greater than zero, no new
      * (unrelated) readers are allowed to proceed.
      */
@@ -79,6 +87,24 @@ public class DefaultISMLocking implement
     private int readerCount = 0;
 
     /**
+     * Returns the writer preference status of this locking strategy.
+     *
+     * @return writer preference
+     */
+    public boolean isWriterPreference() {
+        return writerPreference;
+    }
+
+    /**
+     * Sets the writer preference status of this locking strategy.
+     *
+     * @param preference writer preference
+     */
+    public void setWriterPreference(boolean preference) {
+        this.writerPreference = preference;
+    }
+
+    /**
      * Increments the reader count and returns the acquired read lock once
      * there are no more writers or the current writer shares the thread id
      * with this reader.
@@ -88,7 +114,7 @@ public class DefaultISMLocking implement
         Object currentId = getCurrentThreadId();
         while (writerId != null
                 ? (writerCount > 0 && !isSameThreadId(writerId, currentId))
-                : writersWaiting > 0) {
+                : (writerPreference && writersWaiting > 0)) {
             wait();
         }