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/08/11 12:05:55 UTC

svn commit: r984356 - in /jackrabbit/trunk/jackrabbit-core/src: main/java/org/apache/jackrabbit/core/ main/java/org/apache/jackrabbit/core/security/user/ test/java/org/apache/jackrabbit/api/security/user/

Author: angela
Date: Wed Aug 11 10:05:55 2010
New Revision: 984356

URL: http://svn.apache.org/viewvc?rev=984356&view=rev
Log:
JCR-2703 : UserManagement: Add Membership Cache

Added:
    jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/security/user/MembershipCache.java   (with props)
Modified:
    jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/DefaultSecurityManager.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/security/user/AuthorizableImpl.java
    jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/security/user/GroupImpl.java
    jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/security/user/UserImpl.java
    jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/security/user/UserManagerImpl.java
    jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/security/user/UserPerWorkspaceUserManager.java
    jackrabbit/trunk/jackrabbit-core/src/test/java/org/apache/jackrabbit/api/security/user/GroupTest.java

Modified: jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/DefaultSecurityManager.java
URL: http://svn.apache.org/viewvc/jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/DefaultSecurityManager.java?rev=984356&r1=984355&r2=984356&view=diff
==============================================================================
--- jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/DefaultSecurityManager.java (original)
+++ jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/DefaultSecurityManager.java Wed Aug 11 10:05:55 2010
@@ -61,6 +61,7 @@ import org.apache.jackrabbit.core.securi
 import org.apache.jackrabbit.core.security.principal.PrincipalProvider;
 import org.apache.jackrabbit.core.security.principal.PrincipalProviderRegistry;
 import org.apache.jackrabbit.core.security.principal.ProviderRegistryImpl;
+import org.apache.jackrabbit.core.security.user.MembershipCache;
 import org.apache.jackrabbit.core.security.user.UserManagerImpl;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
@@ -436,6 +437,20 @@ public class DefaultSecurityManager impl
     }
 
     /**
+     * @param session
+     * @return
+     * @throws RepositoryException
+     */
+    protected MembershipCache getMembershipCache(SessionImpl session) throws RepositoryException {
+        if (session == systemSession || session instanceof SystemSession) {
+            // force creation of the membership cache within the corresponding uMgr
+            return null;
+        } else {
+            return ((UserManagerImpl) getSystemUserManager(session.getWorkspace().getName())).getMembershipCache();
+        }
+    }
+
+    /**
      * Creates a {@link UserManagerImpl} for the given session. May be overridden
      * to return a custom implementation.
      *
@@ -449,17 +464,23 @@ public class DefaultSecurityManager impl
 
         // since users are stored in and retrieved from a dedicated workspace
         // only the system session assigned with that workspace will get the
-        // system user manager (special implementation that asserts the existance
+        // system user manager (special implementation that asserts the existence
         // of the admin user).
         UserManagerImpl um;
         if (umc != null) {
-            Class<?>[] paramTypes = new Class[] { SessionImpl.class, String.class, Properties.class };
-            um = (UserManagerImpl) umc.getUserManager(UserManagerImpl.class, paramTypes, (SessionImpl) session, adminId, params);
+            Class<?>[] paramTypes = new Class[] {
+                    SessionImpl.class,
+                    String.class,
+                    Properties.class,
+                    MembershipCache.class};
+            um = (UserManagerImpl) umc.getUserManager(UserManagerImpl.class,
+                    paramTypes, (SessionImpl) session, adminId, params,
+                    getMembershipCache(session));
             // TODO: should we make sure the implementation doesn't allow
             // TODO: to change the autosave behavior? since the user manager
             // TODO: writes to a separate workspace this would cause troubles.
         } else {
-            um = new UserManagerImpl(session, adminId, params);
+            um = new UserManagerImpl(session, adminId, params, getMembershipCache(session));
         }
         return um;
     }

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=984356&r1=984355&r2=984356&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 Aug 11 10:05:55 2010
@@ -26,6 +26,7 @@ import org.apache.jackrabbit.core.securi
 import org.apache.jackrabbit.core.security.principal.PrincipalProvider;
 import org.apache.jackrabbit.core.security.principal.PrincipalProviderRegistry;
 import org.apache.jackrabbit.core.security.simple.SimpleWorkspaceAccessManager;
+import org.apache.jackrabbit.core.security.user.MembershipCache;
 import org.apache.jackrabbit.core.security.user.UserPerWorkspaceUserManager;
 import org.apache.jackrabbit.core.security.user.UserManagerImpl;
 
@@ -223,10 +224,15 @@ public class UserPerWorkspaceSecurityMan
         // from a dedicated workspace: the system session of each workspace must
         // get a system user manager that asserts the existence of the admin user.
         if (umc != null) {
-            Class<?>[] paramTypes = new Class[] { SessionImpl.class, String.class, Properties.class };
-            return (UserPerWorkspaceUserManager) umc.getUserManager(UserPerWorkspaceUserManager.class, paramTypes, (SessionImpl) session, adminId, params);
+            Class<?>[] paramTypes = new Class[] {
+                    SessionImpl.class,
+                    String.class,
+                    Properties.class,
+                    MembershipCache.class};
+            return (UserPerWorkspaceUserManager) umc.getUserManager(UserPerWorkspaceUserManager.class,
+                    paramTypes, (SessionImpl) session, adminId, params, getMembershipCache(session));
         } else {
-            return new UserPerWorkspaceUserManager(session, adminId, params);
+            return new UserPerWorkspaceUserManager(session, adminId, params, getMembershipCache(session));
         }
     }
 

Modified: jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/security/user/AuthorizableImpl.java
URL: http://svn.apache.org/viewvc/jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/security/user/AuthorizableImpl.java?rev=984356&r1=984355&r2=984356&view=diff
==============================================================================
--- jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/security/user/AuthorizableImpl.java (original)
+++ jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/security/user/AuthorizableImpl.java Wed Aug 11 10:05:55 2010
@@ -22,7 +22,6 @@ import org.apache.jackrabbit.api.securit
 import org.apache.jackrabbit.api.security.user.User;
 import org.apache.jackrabbit.core.NodeImpl;
 import org.apache.jackrabbit.core.SessionImpl;
-import org.apache.jackrabbit.core.PropertyImpl;
 import org.apache.jackrabbit.core.id.NodeId;
 import org.apache.jackrabbit.core.nodetype.NodeTypeImpl;
 import org.apache.jackrabbit.core.security.principal.PrincipalImpl;
@@ -34,20 +33,16 @@ import org.slf4j.LoggerFactory;
 import javax.jcr.Property;
 import javax.jcr.PropertyIterator;
 import javax.jcr.RepositoryException;
-import javax.jcr.Value;
 import javax.jcr.Session;
-import javax.jcr.ItemNotFoundException;
-import javax.jcr.AccessDeniedException;
-import javax.jcr.ItemVisitor;
-import javax.jcr.Node;
-import javax.jcr.util.TraversingItemVisitor;
+import javax.jcr.Value;
 import javax.jcr.nodetype.ConstraintViolationException;
 import javax.jcr.nodetype.PropertyDefinition;
 import java.util.ArrayList;
+import java.util.Collection;
+import java.util.HashSet;
 import java.util.Iterator;
 import java.util.List;
 import java.util.Set;
-import java.util.HashSet;
 
 /**
  * AuthorizableImpl
@@ -67,8 +62,7 @@ abstract class AuthorizableImpl implemen
      * {@link #NT_REP_AUTHORIZABLE}.
      * @throws RepositoryException If an error occurs.
      */
-    protected AuthorizableImpl(NodeImpl node, UserManagerImpl userManager)
-            throws RepositoryException {
+    protected AuthorizableImpl(NodeImpl node, UserManagerImpl userManager) {
         this.node = node;
         this.userManager = userManager;
     }
@@ -88,18 +82,14 @@ abstract class AuthorizableImpl implemen
      * @see Authorizable#declaredMemberOf()
      */
     public Iterator<Group> declaredMemberOf() throws RepositoryException {
-        Set<Group> memberShip = new HashSet<Group>();
-        collectMembership(memberShip, false);
-        return memberShip.iterator();
+        return collectMembership(false);
     }
 
     /**
      * @see Authorizable#memberOf()
      */
     public Iterator<Group> memberOf() throws RepositoryException {
-        Set<Group> memberShip = new HashSet<Group>();
-        collectMembership(memberShip, true);
-        return memberShip.iterator();
+        return collectMembership(true);
     }
 
     /**
@@ -242,7 +232,8 @@ abstract class AuthorizableImpl implemen
     public synchronized void remove() throws RepositoryException {
         // don't allow for removal of the administrator even if the executing
         // session has all permissions.
-        if (!isGroup() && ((User) this).isAdmin()) {
+        boolean isGroup = isGroup();
+        if (!isGroup && ((User) this).isAdmin()) {
             throw new RepositoryException("The administrator cannot be removed.");
         }
         Session s = getSession();
@@ -250,6 +241,11 @@ abstract class AuthorizableImpl implemen
         if (userManager.isAutoSave()) {
             s.save();
         }
+
+        // upon successful removal of a Group -> clear the membership cache
+        if (isGroup) {
+            userManager.getMembershipCache().clear();
+        }
     }
 
     //-------------------------------------------------------------< Object >---
@@ -309,69 +305,25 @@ abstract class AuthorizableImpl implemen
         return node.getProperty(P_PRINCIPAL_NAME).getString();
     }
 
-    private void collectMembership(final Set<Group> groups, boolean includeIndirect) throws RepositoryException {
-        PropertyIterator refs = getMembershipReferences();
-        if (refs != null) {
-            while (refs.hasNext()) {
-                try {
-                    NodeImpl n = (NodeImpl) refs.nextProperty().getParent();
-                    if (n.isNodeType(NT_REP_GROUP)) {
-                        Group group = userManager.createGroup(n);
-                        // only retrieve indirect membership if the group is not
-                        // yet present (detected eventual circular membership).
-                        if (groups.add(group) && includeIndirect) {
-                            ((AuthorizableImpl) group).collectMembership(groups, true);
-                        }
-                    } else {
-                        // weak-ref property 'rep:members' that doesn't reside under an
-                        // group node -> doesn't represent a valid group member.
-                        log.debug("Invalid member reference to '" + this + "' -> Not included in membership set.");
-                    }
-                } catch (ItemNotFoundException e) {
-                    // group node doesn't exist  -> -> ignore exception
-                    // and skip this reference from membership list.
-                } catch (AccessDeniedException e) {
-                    // not allowed to see the group node -> ignore exception
-                    // and skip this reference from membership list.
-                }
-            }
+    private Iterator<Group> collectMembership(boolean includeIndirect) throws RepositoryException {
+        Collection<String> groupNodeIds;
+        if (includeIndirect) {
+            groupNodeIds = userManager.getMembershipCache().getMemberOf(node.getIdentifier());
         } else {
-            // workaround for failure of Node#getWeakReferences
-            // traverse the tree below groups-path and collect membership manually.
-            log.info("Traversing groups tree to collect membership.");
-            ItemVisitor visitor = new TraversingItemVisitor.Default() {
-                @Override
-                protected void entering(Property property, int level) throws RepositoryException {
-                    PropertyImpl pImpl = (PropertyImpl) property;
-                    NodeImpl n = (NodeImpl) pImpl.getParent();
-                    if (P_MEMBERS.equals(pImpl.getQName()) && n.isNodeType(NT_REP_GROUP)) {
-                        for (Value value : property.getValues()) {
-                            if (value.getString().equals(node.getIdentifier())) {
-                                Group gr = (Group) userManager.getAuthorizable(n);
-                                groups.add(gr);
-                            }
-                        }
-                    }
-                }
-            };
-            Node groupsNode = getSession().getNode(userManager.getGroupsPath());
-            visitor.visit(groupsNode);
+            groupNodeIds = userManager.getMembershipCache().getDeclaredMemberOf(node.getIdentifier());
         }
-    }
 
-    /**
-     * @return the iterator returned by {@link Node#getWeakReferences(String)}
-     * or <code>null</code> if the method call fails with <code>RepositoryException</code>.
-     * See fallback scenario above.
-     */
-    private PropertyIterator getMembershipReferences() {
-        PropertyIterator refs = null;
-        try {
-            refs = node.getWeakReferences(getSession().getJCRName(P_MEMBERS));
-        } catch (RepositoryException e) {
-            log.error("Failed to retrieve membership references of " + this + ".", e);
+        Set<Group> groups = new HashSet<Group>(groupNodeIds.size());
+        for (String identifier : groupNodeIds) {
+            try {
+                NodeImpl n = (NodeImpl) getSession().getNodeByIdentifier(identifier);
+                Group group = userManager.createGroup(n);
+                groups.add(group);
+            } catch (RepositoryException e) {
+                // group node doesn't exist or cannot be read -> ignore.
+            }
         }
-        return refs;
+        return groups.iterator();
     }
 
     /**

Modified: jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/security/user/GroupImpl.java
URL: http://svn.apache.org/viewvc/jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/security/user/GroupImpl.java?rev=984356&r1=984355&r2=984356&view=diff
==============================================================================
--- jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/security/user/GroupImpl.java (original)
+++ jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/security/user/GroupImpl.java Wed Aug 11 10:05:55 2010
@@ -52,7 +52,7 @@ class GroupImpl extends AuthorizableImpl
 
     private Principal principal;
 
-    protected GroupImpl(NodeImpl node, UserManagerImpl userManager) throws RepositoryException {
+    protected GroupImpl(NodeImpl node, UserManagerImpl userManager) {
         super(node, userManager);
     }
 
@@ -149,6 +149,7 @@ class GroupImpl extends AuthorizableImpl
         values[values.length - 1] = toAdd;
 
         userManager.setProtectedProperty(node, P_MEMBERS, values, PropertyType.WEAKREFERENCE);
+        userManager.getMembershipCache().clear();
         return true;
     }
 
@@ -178,6 +179,7 @@ class GroupImpl extends AuthorizableImpl
                     Value[] values = valList.toArray(new Value[valList.size()]);
                     userManager.setProtectedProperty(node, P_MEMBERS, values);
                 }
+                userManager.getMembershipCache().clear();
                 return true;
             } catch (RepositoryException e) {
                 // modification failed -> revert all pending changes.

Added: jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/security/user/MembershipCache.java
URL: http://svn.apache.org/viewvc/jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/security/user/MembershipCache.java?rev=984356&view=auto
==============================================================================
--- jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/security/user/MembershipCache.java (added)
+++ jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/security/user/MembershipCache.java Wed Aug 11 10:05:55 2010
@@ -0,0 +1,190 @@
+/*
+ * 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.user;
+
+import org.apache.commons.collections.map.LRUMap;
+import org.apache.jackrabbit.core.NodeImpl;
+import org.apache.jackrabbit.core.PropertyImpl;
+import org.apache.jackrabbit.core.SessionImpl;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import javax.jcr.AccessDeniedException;
+import javax.jcr.ItemNotFoundException;
+import javax.jcr.ItemVisitor;
+import javax.jcr.Node;
+import javax.jcr.Property;
+import javax.jcr.PropertyIterator;
+import javax.jcr.RepositoryException;
+import javax.jcr.Value;
+import javax.jcr.util.TraversingItemVisitor;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.Map;
+import java.util.Set;
+
+/**
+ * <code>MembershipCache</code>...
+ */
+public class MembershipCache implements UserConstants {
+
+    /**
+     * logger instance
+     */
+    private static final Logger log = LoggerFactory.getLogger(MembershipCache.class);
+
+    private final SessionImpl systemSession;
+    private final String groupsPath;
+    private final Map<String, Collection<String>> cache;
+
+    MembershipCache(SessionImpl systemSession, String groupsPath) {
+        this.systemSession = systemSession;
+        this.groupsPath = (groupsPath == null) ? UserConstants.GROUPS_PATH : groupsPath;
+        cache = new LRUMap();
+    }
+
+    synchronized void clear() {
+        cache.clear();
+    }
+
+    synchronized Collection<String> getDeclaredMemberOf(String authorizableNodeIdentifier) throws RepositoryException {
+        return declaredMemberOf(authorizableNodeIdentifier);
+    }
+
+    synchronized Collection<String> getMemberOf(String authorizableNodeIdentifier) throws RepositoryException {
+        Set<String> groupNodeIds = new HashSet<String>();
+        memberOf(authorizableNodeIdentifier, groupNodeIds);
+        return Collections.unmodifiableCollection(groupNodeIds);
+    }
+
+    //------------------------------------------------------------< private >---
+    /**
+     *
+     * @param authorizableNodeIdentifier
+     * @return
+     * @throws RepositoryException
+     */
+    private Collection<String> declaredMemberOf(String authorizableNodeIdentifier) throws RepositoryException {
+        Collection<String> groupNodeIds = cache.get(authorizableNodeIdentifier);
+        if (groupNodeIds == null) {
+            groupNodeIds = collectDeclaredMembership(authorizableNodeIdentifier);
+            cache.put(authorizableNodeIdentifier, Collections.unmodifiableCollection(groupNodeIds));
+        }
+        return groupNodeIds;
+    }
+
+    private void memberOf(String authorizableNodeIdentifier, Collection<String> groupNodeIds) throws RepositoryException {
+        Collection<String> declared = declaredMemberOf(authorizableNodeIdentifier);
+        for (String identifier : declared) {
+            if (groupNodeIds.add(identifier)) {
+                memberOf(identifier, groupNodeIds);
+            }
+        }
+    }
+
+    private Collection<String> collectDeclaredMembership(final String authorizableNodeIdentifier) throws RepositoryException {
+        // retrieve a new session with system-subject in order to avoid
+        // concurrent read operations using the system session of this workspace.
+        final SessionImpl session = getSession();
+        try {
+            final Set<String> groupNodeIdentifiers = new HashSet<String>();
+
+            // try to retrieve the membership references using JCR API.
+            PropertyIterator refs = getMembershipReferences(authorizableNodeIdentifier, session);
+            if (refs != null) {
+                while (refs.hasNext()) {
+                    try {
+                        NodeImpl n = (NodeImpl) refs.nextProperty().getParent();
+                        if (n.isNodeType(NT_REP_GROUP)) {
+                            String identifier = n.getIdentifier();
+                            if (!groupNodeIdentifiers.contains(identifier)) {
+                                groupNodeIdentifiers.add(identifier);
+                            }
+                        } else {
+                            // weak-ref property 'rep:members' that doesn't reside under an
+                            // group node -> doesn't represent a valid group member.
+                            log.debug("Invalid member reference to '" + this + "' -> Not included in membership set.");
+                        }
+                    } catch (ItemNotFoundException e) {
+                        // group node doesn't exist  -> -> ignore exception
+                        // and skip this reference from membership list.
+                    } catch (AccessDeniedException e) {
+                        // not allowed to see the group node -> ignore exception
+                        // and skip this reference from membership list.
+                    }
+                }
+            } else {
+                // workaround for failure of Node#getWeakReferences
+                // traverse the tree below groups-path and collect membership manually.
+                log.info("Traversing groups tree to collect membership.");
+                ItemVisitor visitor = new TraversingItemVisitor.Default() {
+                    @Override
+                    protected void entering(Property property, int level) throws RepositoryException {
+                        PropertyImpl pImpl = (PropertyImpl) property;
+                        NodeImpl n = (NodeImpl) pImpl.getParent();
+                        if (P_MEMBERS.equals(pImpl.getQName()) && n.isNodeType(NT_REP_GROUP)) {
+                            for (Value value : property.getValues()) {
+                                String v = value.getString();
+                                if (v.equals(authorizableNodeIdentifier)) {
+                                    groupNodeIdentifiers.add(v);
+                                }
+                            }
+                        }
+                    }
+                };
+
+                if (session.nodeExists(groupsPath)) {
+                    Node groupsNode = session.getNode(groupsPath);
+                    visitor.visit(groupsNode);
+                } // else: no groups exist -> nothing to do.
+            }
+            return groupNodeIdentifiers;
+
+        } finally {
+            // release session if it isn't the original system session but has
+            // been created for this method call only.
+            if (session != systemSession) {
+                session.logout();
+            }
+        }
+    }
+   
+    /**
+     * @return a new Session that needs to be properly released after usage.
+     * @throws RepositoryException
+     * @throws AccessDeniedException
+     */
+    private SessionImpl getSession() {
+        try {
+            return (SessionImpl) systemSession.createSession(systemSession.getWorkspace().getName());
+        } catch (RepositoryException e) {
+            // fallback
+            return systemSession;
+        }
+    }
+
+    private static PropertyIterator getMembershipReferences(String authorizableNodeIdentifier, SessionImpl session) {
+        PropertyIterator refs = null;
+        try {
+            refs = session.getNodeByIdentifier(authorizableNodeIdentifier).getWeakReferences(session.getJCRName(P_MEMBERS));
+        } catch (RepositoryException e) {
+            log.error("Failed to retrieve membership references of " + authorizableNodeIdentifier + ".", e);
+        }
+        return refs;
+    }
+}
\ No newline at end of file

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

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

Modified: jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/security/user/UserImpl.java
URL: http://svn.apache.org/viewvc/jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/security/user/UserImpl.java?rev=984356&r1=984355&r2=984356&view=diff
==============================================================================
--- jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/security/user/UserImpl.java (original)
+++ jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/security/user/UserImpl.java Wed Aug 11 10:05:55 2010
@@ -38,7 +38,7 @@ public class UserImpl extends Authorizab
     private Principal principal;
     private Impersonation impersonation;
 
-    protected UserImpl(NodeImpl node, UserManagerImpl userManager) throws RepositoryException {
+    protected UserImpl(NodeImpl node, UserManagerImpl userManager) {
         super(node, userManager);
     }
 

Modified: jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/security/user/UserManagerImpl.java
URL: http://svn.apache.org/viewvc/jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/security/user/UserManagerImpl.java?rev=984356&r1=984355&r2=984356&view=diff
==============================================================================
--- jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/security/user/UserManagerImpl.java (original)
+++ jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/security/user/UserManagerImpl.java Wed Aug 11 10:05:55 2010
@@ -236,6 +236,8 @@ public class UserManagerImpl extends Pro
      */
     private final boolean isSystemUserManager;
 
+    private final MembershipCache membershipCache;
+
     /**
      * Create a new <code>UserManager</code> with the default configuration.
      *
@@ -243,7 +245,18 @@ public class UserManagerImpl extends Pro
      * @param adminId The user ID of the administrator.
      */
     public UserManagerImpl(SessionImpl session, String adminId) {
-        this(session, adminId, null);
+        this(session, adminId, null, null);
+    }
+
+    /**
+     * Create a new <code>UserManager</code>
+     *
+     * @param session The editing/reading session.
+     * @param adminId The user ID of the administrator.
+     * @param config The configuration parameters.
+     */
+    public UserManagerImpl(SessionImpl session, String adminId, Properties config) {
+        this(session, adminId, config, null);
     }
 
     /**
@@ -263,8 +276,10 @@ public class UserManagerImpl extends Pro
      * @param session The editing/reading session.
      * @param adminId The user ID of the administrator.
      * @param config The configuration parameters.
+     * @param mCache Shared membership cache.
      */
-    public UserManagerImpl(SessionImpl session, String adminId, Properties config) {
+    public UserManagerImpl(SessionImpl session, String adminId, Properties config,
+                           MembershipCache mCache) {
         this.session = session;
         this.adminId = adminId;
 
@@ -279,6 +294,12 @@ public class UserManagerImpl extends Pro
         param = (config != null) ? config.get(PARAM_COMPATIBILE_JR16) : null;
         compatibleJR16 = (param != null) && Boolean.parseBoolean(param.toString());
 
+        if (mCache != null) {
+            membershipCache = mCache;
+        } else {
+            membershipCache = new MembershipCache(session, groupsPath);
+        }
+
         NodeResolver nr;
         try {
             nr = new IndexNodeResolver(session, session);
@@ -323,6 +344,13 @@ public class UserManagerImpl extends Pro
         return groupsPath;
     }
 
+    /**
+     * @return The membership cache present with this user manager instance.
+     */
+    public MembershipCache getMembershipCache() {
+        return membershipCache;
+    }
+
     //--------------------------------------------------------< UserManager >---
     /**
      * @see UserManager#getAuthorizable(String)
@@ -333,7 +361,7 @@ public class UserManagerImpl extends Pro
         }
         Authorizable a = internalGetAuthorizable(id);
         /**
-         * Extra check for the existance of the administrator user that must
+         * Extra check for the existence of the administrator user that must
          * always exist.
          * In case it got removed if must be recreated using a system session.
          * Since a regular session may lack read permission on the admin-user's
@@ -596,7 +624,7 @@ public class UserManagerImpl extends Pro
      * <pre>
      * - the passed node is <code>null</code>,
      * - doesn't have the correct node type or
-     * - isn't placed underneith the configured user/group tree.
+     * - isn't placed underneath the configured user/group tree.
      * </pre>
      *
      * @param n A user/group node.
@@ -625,7 +653,7 @@ public class UserManagerImpl extends Pro
      * which might happen if userID != principal-name.
      * In this case: generate another ID for the group to be created.
      *
-     * @param principalName to be used as hint for the groupid.
+     * @param principalName to be used as hint for the group id.
      * @return a group id.
      * @throws RepositoryException If an error occurs.
      */
@@ -753,7 +781,7 @@ public class UserManagerImpl extends Pro
      * <li>The <code>usersPath</code> has been modified in the user manager
      * configuration after a successful repository start that already created
      * the administrator user.</li>
-     * <li>The NodeId created by {@link #buildNodeId(String)} by conincidence
+     * <li>The NodeId created by {@link #buildNodeId(String)} by coincidence
      * collides with another NodeId created during the regular node creation
      * process.</li>
      * </ul>

Modified: jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/security/user/UserPerWorkspaceUserManager.java
URL: http://svn.apache.org/viewvc/jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/security/user/UserPerWorkspaceUserManager.java?rev=984356&r1=984355&r2=984356&view=diff
==============================================================================
--- jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/security/user/UserPerWorkspaceUserManager.java (original)
+++ jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/security/user/UserPerWorkspaceUserManager.java Wed Aug 11 10:05:55 2010
@@ -38,14 +38,14 @@ public class UserPerWorkspaceUserManager
     private boolean autoSave = true;
 
     /**
-     * Same as <code>TransientChangeUserManagerImpl(session, adminID, null)</code>.
+     * Same as <code>UserPerWorkspaceUserManager(session, adminID, null, null)</code>.
      *
      * @param session
      * @param adminId
      * @throws RepositoryException
      */
-    public UserPerWorkspaceUserManager(SessionImpl session, String adminId) throws RepositoryException {
-        this(session, adminId, null);
+    public UserPerWorkspaceUserManager(SessionImpl session, String adminId) {
+        super(session, adminId);
     }
 
     /**
@@ -57,9 +57,23 @@ public class UserPerWorkspaceUserManager
      * @param config
      * @throws javax.jcr.RepositoryException
      */
-    public UserPerWorkspaceUserManager(SessionImpl session, String adminId, Properties config) throws RepositoryException {
+    public UserPerWorkspaceUserManager(SessionImpl session, String adminId, Properties config) {
         super(session, adminId, config);
     }
+        
+    /**
+     * Creates a UserManager that doesn't implicitly save changes but requires
+     * an explicit call to {@link javax.jcr.Session#save()}.
+     *
+     * @param session
+     * @param adminId
+     * @param config
+     * @throws javax.jcr.RepositoryException
+     */
+    public UserPerWorkspaceUserManager(SessionImpl session, String adminId,
+                                       Properties config, MembershipCache mCache) {
+        super(session, adminId, config, mCache);
+    }
 
     //--------------------------------------------------------< UserManager >---
     /**

Modified: jackrabbit/trunk/jackrabbit-core/src/test/java/org/apache/jackrabbit/api/security/user/GroupTest.java
URL: http://svn.apache.org/viewvc/jackrabbit/trunk/jackrabbit-core/src/test/java/org/apache/jackrabbit/api/security/user/GroupTest.java?rev=984356&r1=984355&r2=984356&view=diff
==============================================================================
--- jackrabbit/trunk/jackrabbit-core/src/test/java/org/apache/jackrabbit/api/security/user/GroupTest.java (original)
+++ jackrabbit/trunk/jackrabbit-core/src/test/java/org/apache/jackrabbit/api/security/user/GroupTest.java Wed Aug 11 10:05:55 2010
@@ -387,4 +387,41 @@ public class GroupTest extends AbstractU
             }
         }
     }
+       
+    public void testRemoveGroupClearsMembership() throws NotExecutableException, RepositoryException {
+        User auth = getTestUser(superuser);
+        Group newGroup = null;
+        String groupId;
+        try {
+            newGroup = userMgr.createGroup(getTestPrincipal());
+            groupId = newGroup.getID();
+            save(superuser);
+
+            assertTrue(newGroup.addMember(auth));
+            save(superuser);
+
+            boolean isMember = false;
+            Iterator<Group> it = auth.declaredMemberOf();
+            while (it.hasNext() && !isMember) {
+                isMember = groupId.equals(it.next().getID());
+            }
+            assertTrue(isMember);
+
+        } finally {
+            if (newGroup != null) {
+                newGroup.remove();
+                save(superuser);
+            }
+        }
+
+        Iterator<Group> it = auth.declaredMemberOf();
+        while (it.hasNext()) {
+            assertFalse(groupId.equals(it.next().getID()));
+        }
+
+        it = auth.memberOf();
+        while (it.hasNext()) {
+            assertFalse(groupId.equals(it.next().getID()));
+        }
+    }
 }
\ No newline at end of file