jackrabbit-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From mreut...@apache.org
Subject svn commit: r1519376 - 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/core/security/user/ test/resources/org/apache/jackrabbit/core/sec...
Date Mon, 02 Sep 2013 09:29:14 GMT
Author: mreutegg
Date: Mon Sep  2 09:29:14 2013
New Revision: 1519376

URL: http://svn.apache.org/r1519376
Log:
JCR-3658: MembershipCache not consistently synchronized

Added:
    jackrabbit/trunk/jackrabbit-core/src/test/java/org/apache/jackrabbit/core/security/user/MembershipCacheTest.java
  (with props)
    jackrabbit/trunk/jackrabbit-core/src/test/resources/org/apache/jackrabbit/core/security/
    jackrabbit/trunk/jackrabbit-core/src/test/resources/org/apache/jackrabbit/core/security/user/
    jackrabbit/trunk/jackrabbit-core/src/test/resources/org/apache/jackrabbit/core/security/user/repository.xml
  (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/security/user/MembershipCache.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=1519376&r1=1519375&r2=1519376&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
Mon Sep  2 09:29:14 2013
@@ -49,6 +49,7 @@ import org.apache.jackrabbit.core.securi
 import org.apache.jackrabbit.core.security.DefaultAccessManager;
 import org.apache.jackrabbit.core.security.JackrabbitSecurityManager;
 import org.apache.jackrabbit.core.security.SecurityConstants;
+import org.apache.jackrabbit.core.security.SystemPrincipal;
 import org.apache.jackrabbit.core.security.authentication.AuthContext;
 import org.apache.jackrabbit.core.security.authentication.AuthContextProvider;
 import org.apache.jackrabbit.core.security.authorization.AccessControlProvider;
@@ -336,11 +337,15 @@ public class DefaultSecurityManager impl
     public String getUserID(Subject subject, String workspaceName) throws RepositoryException
{
         checkInitialized();
 
-        /* shortcut if the subject contains the AdminPrincipal in which case
-           the userID is already known. */
+        // shortcut if the subject contains the AdminPrincipal or
+        // SystemPrincipal in which cases the userID is already known.
         if (!subject.getPrincipals(AdminPrincipal.class).isEmpty()) {
             return adminId;
+        } else if (!subject.getPrincipals(SystemPrincipal.class).isEmpty()) {
+            // system session does not have a userId
+            return null;
         }
+
         /* if there is a configure principal class that should be used to
            determine the UserID -> try this one. */
         Class cl = getConfig().getUserIdClass();

Modified: 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=1519376&r1=1519375&r2=1519376&view=diff
==============================================================================
--- jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/security/user/MembershipCache.java
(original)
+++ jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/security/user/MembershipCache.java
Mon Sep  2 09:29:14 2013
@@ -19,7 +19,6 @@ package org.apache.jackrabbit.core.secur
 import java.util.Collection;
 import java.util.Collections;
 import java.util.HashSet;
-import java.util.Map;
 import java.util.Set;
 
 import javax.jcr.AccessDeniedException;
@@ -39,7 +38,7 @@ import org.apache.jackrabbit.core.NodeIm
 import org.apache.jackrabbit.core.PropertyImpl;
 import org.apache.jackrabbit.core.SessionImpl;
 import org.apache.jackrabbit.core.SessionListener;
-import org.apache.jackrabbit.core.cache.GrowingLRUMap;
+import org.apache.jackrabbit.core.cache.ConcurrentCache;
 import org.apache.jackrabbit.core.nodetype.NodeTypeImpl;
 import org.apache.jackrabbit.core.observation.SynchronousEventListener;
 import org.apache.jackrabbit.spi.Name;
@@ -58,11 +57,6 @@ public class MembershipCache implements 
     private static final Logger log = LoggerFactory.getLogger(MembershipCache.class);
 
     /**
-     * The initial size of this cache (TODO: make configurable)
-     */
-    private static final int MAX_INITIAL_CACHE_SIZE = 1024;
-
-    /**
      * The maximum size of this cache (TODO: make configurable)
      */
     private static final int MAX_CACHE_SIZE = 5000;
@@ -71,7 +65,7 @@ public class MembershipCache implements 
     private final String groupsPath;
     private final boolean useMembersNode;
     private final String pMembers;
-    private final Map<String, Collection<String>> cache;
+    private final ConcurrentCache<String, Collection<String>> cache;
 
     @SuppressWarnings("unchecked")
     MembershipCache(SessionImpl systemSession, String groupsPath, boolean useMembersNode)
throws RepositoryException {
@@ -80,7 +74,8 @@ public class MembershipCache implements 
         this.useMembersNode = useMembersNode;
 
         pMembers = systemSession.getJCRName(UserManagerImpl.P_MEMBERS);
-        cache = new GrowingLRUMap(MAX_INITIAL_CACHE_SIZE, MAX_CACHE_SIZE);
+        cache = new ConcurrentCache<String, Collection<String>>("MembershipCache",
16);
+        cache.setMaxMemorySize(MAX_CACHE_SIZE);
 
         String[] ntNames = new String[] {
                 systemSession.getJCRName(UserConstants.NT_REP_GROUP),
@@ -143,9 +138,7 @@ public class MembershipCache implements 
         }
 
         if (clear) {
-            synchronized (cache) {
-                cache.clear();
-            }
+            cache.clear();
             log.debug("Membership cache cleared because of observation event.");
         }
     }
@@ -178,7 +171,7 @@ public class MembershipCache implements 
      * authorizable in question is declared member of.
      * @throws RepositoryException If an error occurs.
      */
-    synchronized Collection<String> getDeclaredMemberOf(String authorizableNodeIdentifier)
throws RepositoryException {
+    Collection<String> getDeclaredMemberOf(String authorizableNodeIdentifier) throws
RepositoryException {
         return declaredMemberOf(authorizableNodeIdentifier);
     }
 
@@ -189,7 +182,7 @@ public class MembershipCache implements 
      * authorizable in question is a direct or indirect member of.
      * @throws RepositoryException If an error occurs.
      */
-    synchronized Collection<String> getMemberOf(String authorizableNodeIdentifier)
throws RepositoryException {
+    Collection<String> getMemberOf(String authorizableNodeIdentifier) throws RepositoryException
{
         Set<String> groupNodeIds = new HashSet<String>();
         memberOf(authorizableNodeIdentifier, groupNodeIds);
         return Collections.unmodifiableCollection(groupNodeIds);
@@ -199,8 +192,15 @@ public class MembershipCache implements 
      * Returns the size of the membership cache
      * @return the size
      */
-    synchronized int getSize() {
-        return cache.size();
+    int getSize() {
+        return (int) cache.getElementCount();
+    }
+
+    /**
+     * For testing purposes only.
+     */
+    void clear() {
+        cache.clear();
     }
 
     /**
@@ -282,7 +282,7 @@ public class MembershipCache implements 
             Session session = getSession();
             try {
                 groupNodeIds = collectDeclaredMembership(authorizableNodeIdentifier, session);
-                cache.put(authorizableNodeIdentifier, Collections.unmodifiableCollection(groupNodeIds));
+                cache.put(authorizableNodeIdentifier, Collections.unmodifiableCollection(groupNodeIds),
1);
             }
             finally {
                 // release session if it isn't the original system session
@@ -299,7 +299,7 @@ public class MembershipCache implements 
                     groupNodeIds.size(),
                     authorizableNodeIdentifier,
                     (t1-t0) / 1000,
-                    cache.size()
+                    cache.getElementCount()
             });
         }
         return groupNodeIds;

Added: jackrabbit/trunk/jackrabbit-core/src/test/java/org/apache/jackrabbit/core/security/user/MembershipCacheTest.java
URL: http://svn.apache.org/viewvc/jackrabbit/trunk/jackrabbit-core/src/test/java/org/apache/jackrabbit/core/security/user/MembershipCacheTest.java?rev=1519376&view=auto
==============================================================================
--- jackrabbit/trunk/jackrabbit-core/src/test/java/org/apache/jackrabbit/core/security/user/MembershipCacheTest.java
(added)
+++ jackrabbit/trunk/jackrabbit-core/src/test/java/org/apache/jackrabbit/core/security/user/MembershipCacheTest.java
Mon Sep  2 09:29:14 2013
@@ -0,0 +1,235 @@
+/*
+ * 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 java.io.File;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+import java.util.Random;
+import java.util.concurrent.atomic.AtomicLong;
+
+import javax.jcr.Node;
+import javax.jcr.RepositoryException;
+import javax.jcr.SimpleCredentials;
+
+import org.apache.commons.io.FileUtils;
+import org.apache.jackrabbit.api.JackrabbitSession;
+import org.apache.jackrabbit.api.security.user.Group;
+import org.apache.jackrabbit.api.security.user.User;
+import org.apache.jackrabbit.api.security.user.UserManager;
+import org.apache.jackrabbit.core.RepositoryImpl;
+import org.apache.jackrabbit.core.config.RepositoryConfig;
+import org.apache.jackrabbit.test.JUnitTest;
+
+/**
+ * Performance test for JCR-3658.
+ */
+public class MembershipCacheTest extends JUnitTest {
+
+    private static final String TEST_USER_PREFIX = "MembershipCacheTestUser-";
+    private static final String REPO_HOME = new File("target",
+            MembershipCacheTest.class.getSimpleName()).getPath();
+    private static final int NUM_USERS = 100;
+    private static final int NUM_GROUPS = 8;
+    private static final int NUM_READERS = 8;
+    private RepositoryImpl repo;
+    private JackrabbitSession session;
+    private UserManager userMgr;
+    private MembershipCache cache;
+
+    @Override
+    protected void setUp() throws Exception {
+        super.setUp();
+        FileUtils.deleteDirectory(new File(REPO_HOME));
+        RepositoryConfig config = RepositoryConfig.create(
+                getClass().getResourceAsStream("repository.xml"), REPO_HOME);
+        repo = RepositoryImpl.create(config);
+        session = createSession();
+        userMgr = session.getUserManager();
+        cache = ((UserManagerImpl) userMgr).getMembershipCache();
+        boolean autoSave = userMgr.isAutoSave();
+        userMgr.autoSave(false);
+        // create test users and groups
+        List<User> users = new ArrayList<User>();
+        for (int i = 0; i < NUM_USERS; i++) {
+            users.add(userMgr.createUser(TEST_USER_PREFIX + i, "secret"));
+        }
+        for (int i = 0; i < NUM_GROUPS; i++) {
+            Group g = userMgr.createGroup("MembershipCacheTestGroup-" + i);
+            for (User u : users) {
+                g.addMember(u);
+            }
+        }
+        session.save();
+        userMgr.autoSave(autoSave);
+        logger.info("Initial cache size: " + cache.getSize());
+    }
+
+    @Override
+    protected void tearDown() throws Exception {
+        boolean autoSave = userMgr.isAutoSave();
+        userMgr.autoSave(false);
+        for (int i = 0; i < NUM_USERS; i++) {
+            userMgr.getAuthorizable(TEST_USER_PREFIX + i).remove();
+        }
+        for (int i = 0; i < NUM_GROUPS; i++) {
+            userMgr.getAuthorizable("MembershipCacheTestGroup-" + i).remove();
+        }
+        session.save();
+        userMgr.autoSave(autoSave);
+        userMgr = null;
+        cache = null;
+        session.logout();
+        repo.shutdown();
+        repo = null;
+        FileUtils.deleteDirectory(new File(REPO_HOME));
+        super.tearDown();
+    }
+
+    public void testConcurrency() throws Exception {
+        Stats stats = new Stats();
+        List<Exception> exceptions = Collections.synchronizedList(new ArrayList<Exception>());
+        List<Reader> readers = new ArrayList<Reader>();
+        for (int i = 0; i < NUM_READERS; i++) {
+            Reader r = new Reader(createSession(), stats, exceptions);
+            r.addUser(TEST_USER_PREFIX + 0);
+            readers.add(r);
+        }
+        Node test = session.getRootNode().addNode("test", "nt:unstructured");
+        session.save();
+        for (Reader r : readers) {
+            r.start();
+        }
+        for (int i = 1; i < NUM_USERS; i++) {
+            test.addNode("node-" + i);
+            session.save();
+            for (Reader r : readers) {
+                r.addUser(TEST_USER_PREFIX + i);
+            }
+        }
+        for (Reader r : readers) {
+            r.join();
+        }
+        test.remove();
+        session.save();
+        System.out.println(stats);
+        for (Exception e : exceptions) {
+            throw e;
+        }
+    }
+
+    public void testRun75() throws Exception {
+        for (int i = 0; i < 75; i++) {
+            testConcurrency();
+            cache.clear();
+        }
+    }
+
+    private JackrabbitSession createSession() throws RepositoryException {
+        return (JackrabbitSession) repo.login(
+                new SimpleCredentials("admin", "admin".toCharArray()));
+    }
+
+    private static final class Reader extends Thread {
+
+        private final JackrabbitSession session;
+        private final UserManager userMgr;
+        private final Stats stats;
+        private final List<Object> knownUsers = new ArrayList<Object>();
+        private final Random random = new Random();
+        private final List<Exception> exceptions;
+
+        public Reader(JackrabbitSession s,
+                      Stats stats,
+                      List<Exception> exceptions)
+                throws RepositoryException {
+            this.session = s;
+            this.userMgr = s.getUserManager();
+            this.stats = stats;
+            this.exceptions = exceptions;
+        }
+
+        void addUser(String user) {
+            synchronized (knownUsers) {
+                knownUsers.add(user);
+            }
+        }
+
+        public void run() {
+            try {
+                while (knownUsers.size() < NUM_USERS) {
+                    Object idOrUser;
+                    int idx;
+                    synchronized (knownUsers) {
+                        idx = random.nextInt(knownUsers.size());
+                        idOrUser = knownUsers.get(idx);
+                    }
+                    User user;
+                    if (idOrUser instanceof String) {
+                        user = (User) userMgr.getAuthorizable((String) idOrUser);
+                        synchronized (knownUsers) {
+                            knownUsers.set(idx, user);
+                        }
+                    } else {
+                        user = (User) idOrUser;
+                    }
+                    long time = System.nanoTime();
+                    user.memberOf();
+                    stats.logTime(System.nanoTime() - time);
+                }
+            } catch (RepositoryException e) {
+                exceptions.add(e);
+            } finally {
+                session.logout();
+            }
+        }
+    }
+
+    private static final class Stats {
+
+        private AtomicLong[] buckets = new AtomicLong[20];
+
+        public Stats() {
+            for (int i = 0; i < buckets.length; i++) {
+                buckets[i] = new AtomicLong();
+            }
+        }
+
+        void logTime(long nanos) {
+            if (nanos == 0) {
+                buckets[0].incrementAndGet();
+            } else {
+                buckets[(int) Math.log10(nanos)].incrementAndGet();
+            }
+        }
+
+        @Override
+        public String toString() {
+            StringBuilder sb = new StringBuilder();
+            String separator = "";
+            for (AtomicLong bucket : buckets) {
+                sb.append(separator);
+                sb.append(bucket.get());
+                separator = ",";
+            }
+            return sb.toString();
+        }
+    }
+
+
+}

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

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

Added: jackrabbit/trunk/jackrabbit-core/src/test/resources/org/apache/jackrabbit/core/security/user/repository.xml
URL: http://svn.apache.org/viewvc/jackrabbit/trunk/jackrabbit-core/src/test/resources/org/apache/jackrabbit/core/security/user/repository.xml?rev=1519376&view=auto
==============================================================================
--- jackrabbit/trunk/jackrabbit-core/src/test/resources/org/apache/jackrabbit/core/security/user/repository.xml
(added)
+++ jackrabbit/trunk/jackrabbit-core/src/test/resources/org/apache/jackrabbit/core/security/user/repository.xml
Mon Sep  2 09:29:14 2013
@@ -0,0 +1,90 @@
+<?xml version="1.0"?>
+<!--
+   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.
+-->
+<!DOCTYPE Repository PUBLIC "-//The Apache Software Foundation//DTD Jackrabbit 2.4//EN"
+                            "http://jackrabbit.apache.org/dtd/repository-2.4.dtd">
+<Repository>
+    <FileSystem class="org.apache.jackrabbit.core.fs.local.LocalFileSystem">
+        <param name="path" value="${rep.home}/repository"/>
+    </FileSystem>
+
+    <Security appName="Jackrabbit">
+        <SecurityManager class="org.apache.jackrabbit.core.UserPerWorkspaceSecurityManager">
+            <UserManager class="org.apache.jackrabbit.core.security.user.UserPerWorkspaceUserManager">
+                <param name="usersPath" value="/home/users"/>
+                <param name="groupsPath" value="/home/groups"/>
+                <param name="defaultDepth" value="1"/>
+                <param name="autoExpandTree" value="true"/>
+                <AuthorizableAction class="org.apache.jackrabbit.core.security.user.action.AccessControlAction">
+                  <param name="groupPrivilegeNames" value="jcr:read"/>
+                  <param name="userPrivilegeNames" value="jcr:all"/>
+                </AuthorizableAction>
+            </UserManager>
+        </SecurityManager>
+
+        <AccessManager class="org.apache.jackrabbit.core.security.DefaultAccessManager">
+        </AccessManager>
+
+        <LoginModule class="org.apache.jackrabbit.core.security.authentication.DefaultLoginModule">
+           <param name="anonymousId" value="anonymous"/>
+           <param name="adminId" value="admin"/>
+        </LoginModule>
+    </Security>
+
+    <!--
+        location of workspaces root directory and name of default workspace
+    -->
+    <Workspaces rootPath="${rep.home}/workspaces" defaultWorkspace="default"/>
+    <!--
+        workspace configuration template:
+        used to create the initial workspace if there's no workspace yet
+    -->
+    <Workspace name="${wsp.name}">
+        <FileSystem class="org.apache.jackrabbit.core.fs.local.LocalFileSystem">
+            <param name="path" value="${wsp.home}"/>
+        </FileSystem>
+        <PersistenceManager class="org.apache.jackrabbit.core.persistence.pool.DerbyPersistenceManager">
+          <param name="url" value="jdbc:derby:${wsp.home}/db;create=true"/>
+          <param name="schemaObjectPrefix" value="${wsp.name}_"/>
+        </PersistenceManager>
+        <SearchIndex class="org.apache.jackrabbit.core.query.lucene.SearchIndex">
+            <param name="path" value="${wsp.home}/index"/>
+        </SearchIndex>
+    </Workspace>
+
+    <!--
+        Configures the versioning
+    -->
+    <Versioning rootPath="${rep.home}/version">
+        <FileSystem class="org.apache.jackrabbit.core.fs.local.LocalFileSystem">
+            <param name="path" value="${rep.home}/version" />
+        </FileSystem>
+        <PersistenceManager class="org.apache.jackrabbit.core.persistence.pool.DerbyPersistenceManager">
+          <param name="url" value="jdbc:derby:${rep.home}/version/db;create=true"/>
+          <param name="schemaObjectPrefix" value="version_"/>
+        </PersistenceManager>
+    </Versioning>
+
+    <!--
+        Search index for content that is shared repository wide
+        (/jcr:system tree, contains mainly versions)
+    -->
+    <SearchIndex class="org.apache.jackrabbit.core.query.lucene.SearchIndex">
+        <param name="path" value="${rep.home}/repository/index"/>
+    </SearchIndex>
+    
+</Repository>

Propchange: jackrabbit/trunk/jackrabbit-core/src/test/resources/org/apache/jackrabbit/core/security/user/repository.xml
------------------------------------------------------------------------------
    svn:eol-style = native



Mime
View raw message