Index: jackrabbit-core/src/test/java/org/apache/jackrabbit/api/security/user/ConcurrentCreateUserTest.java =================================================================== --- jackrabbit-core/src/test/java/org/apache/jackrabbit/api/security/user/ConcurrentCreateUserTest.java (revision 0) +++ jackrabbit-core/src/test/java/org/apache/jackrabbit/api/security/user/ConcurrentCreateUserTest.java (revision 0) @@ -0,0 +1,84 @@ +/* + * 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.api.security.user; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import java.util.UUID; + +import javax.jcr.Node; +import javax.jcr.RepositoryException; +import javax.jcr.Session; + +import org.apache.jackrabbit.api.JackrabbitSession; +import org.apache.jackrabbit.core.AbstractConcurrencyTest; +import org.apache.jackrabbit.core.security.TestPrincipal; + +public class ConcurrentCreateUserTest extends AbstractConcurrencyTest { + + /** + * The number of threads. + */ + private static final int CONCURRENCY = 5; + + List userIDs = Collections.synchronizedList(new ArrayList()); + public static final String INTERMEDIATE_PATH = UUID.randomUUID().toString(); + + @Override + protected void tearDown() throws Exception { + + try { + for (String id : userIDs) { + Authorizable a = ((JackrabbitSession)superuser).getUserManager().getAuthorizable(id); + a.remove(); + superuser.save(); + } + } + catch (Exception e) { + // this is best effort + } + + super.tearDown(); + } + + public void testCreateUsers() throws Exception { + + log.println("ConcurrentCreateUserTest.testCreateUsers: c=" + + CONCURRENCY); + log.flush(); + + runTask(new Task() { + public void execute(Session session, Node test) + throws RepositoryException { + JackrabbitSession s = null; + try { + s = (JackrabbitSession) getHelper().getSuperuserSession(); + String name = "newname-" + UUID.randomUUID(); + Authorizable authorizable = s.getUserManager().createUser( + name, "password1", new TestPrincipal(name), + INTERMEDIATE_PATH); + s.save(); + userIDs.add(authorizable.getID()); + log.println(authorizable + " created"); + } finally { + s.logout(); + } + } + }, CONCURRENCY, "/" + testPath); + } +} Property changes on: jackrabbit-core/src/test/java/org/apache/jackrabbit/api/security/user/ConcurrentCreateUserTest.java ___________________________________________________________________ Added: svn:executable + * Index: jackrabbit-core/src/test/java/org/apache/jackrabbit/api/security/user/TestAll.java =================================================================== --- jackrabbit-core/src/test/java/org/apache/jackrabbit/api/security/user/TestAll.java (revision 1176331) +++ jackrabbit-core/src/test/java/org/apache/jackrabbit/api/security/user/TestAll.java (working copy) @@ -42,6 +42,7 @@ suite.addTestSuite(NestedGroupTest.class); suite.addTestSuite(ImpersonationTest.class); suite.addTestSuite(UserManagerSearchTest.class); + suite.addTestSuite(ConcurrentCreateUserTest.class); return suite; } Index: jackrabbit-core/src/main/java/org/apache/jackrabbit/core/security/user/UserManagerImpl.java =================================================================== --- jackrabbit-core/src/main/java/org/apache/jackrabbit/core/security/user/UserManagerImpl.java (revision 1176331) +++ jackrabbit-core/src/main/java/org/apache/jackrabbit/core/security/user/UserManagerImpl.java (working copy) @@ -57,6 +57,7 @@ import java.util.Properties; import java.util.Set; import java.util.UUID; +import java.util.concurrent.Semaphore; /** * Default implementation of the UserManager interface with the @@ -258,6 +259,12 @@ private final MembershipCache membershipCache; + // allow only one user creation operation at a time in order to avoid + // race conditions when creating intermediate paths + private static Semaphore USERCREATIONSEM = new Semaphore(1); + // see above + private static Semaphore GROUPCREATIONSEM = new Semaphore(1); + /** * Create a new UserManager with the default configuration. * @@ -530,6 +537,7 @@ // NOTE: principal validation during setPrincipal call. try { + USERCREATIONSEM.acquireUninterruptibly(); NodeImpl userNode = (NodeImpl) nodeCreator.createUserNode(userID, intermediatePath); setPrincipal(userNode, principal); setProperty(userNode, P_PASSWORD, getValue(UserImpl.buildPasswordValue(password)), true); @@ -547,6 +555,10 @@ log.debug("Failed to create new User, reverting changes."); throw e; } + finally { + USERCREATIONSEM.release(); + } + } /** @@ -609,6 +621,7 @@ checkValidID(groupID); // NOTE: principal validation during setPrincipal call. try { + GROUPCREATIONSEM.acquireUninterruptibly(); NodeImpl groupNode = (NodeImpl) nodeCreator.createGroupNode(groupID, intermediatePath); if (principal != null) { @@ -627,6 +640,9 @@ log.debug("newInstance new Group failed, revert changes on parent"); throw e; } + finally { + GROUPCREATIONSEM.release(); + } } /**