Index: src/main/java/org/apache/jackrabbit/core/security/user/MembershipCache.java =================================================================== --- src/main/java/org/apache/jackrabbit/core/security/user/MembershipCache.java (revision 1518513) +++ src/main/java/org/apache/jackrabbit/core/security/user/MembershipCache.java (working copy) @@ -203,6 +203,10 @@ return cache.size(); } + synchronized void clear() { + cache.clear(); + } + /** * Collects the declared memberships for the specified identifier of an * authorizable using the specified session. Index: src/test/java/org/apache/jackrabbit/core/security/user/MembershipCacheTest.java =================================================================== --- src/test/java/org/apache/jackrabbit/core/security/user/MembershipCacheTest.java (revision 0) +++ src/test/java/org/apache/jackrabbit/core/security/user/MembershipCacheTest.java (revision 0) @@ -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; + +/** + * MembershipCacheTest... + */ +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 users = new ArrayList(); + 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 exceptions = Collections.synchronizedList(new ArrayList()); + List readers = new ArrayList(); + 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 knownUsers = new ArrayList(); + private final Random random = new Random(); + private final List exceptions; + + public Reader(JackrabbitSession s, + Stats stats, + List 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(); + } + } + + +} Property changes on: src\test\java\org\apache\jackrabbit\core\security\user\MembershipCacheTest.java ___________________________________________________________________ Added: svn:keywords + Author Date Id Revision Rev URL Added: svn:eol-style + native