Merged V3.2 to HEAD

16674: Fix for ETHREEOH-2867: Deadlock issues experienced with PermissionModel where CPU thrashing was observed addressed by changing ConcurrentHashMap and Collections.synchronizedMaps instead of Hashmaps
    16678: Build Fix (Remove Java 6 constructors for ConcurrentHashMap)


git-svn-id: https://svn.alfresco.com/repos/alfresco-enterprise/alfresco/HEAD/root@17008 c4b6b30b-aa2e-2d43-bbcb-ca4b014f7261
This commit is contained in:
Andrew Hind
2009-10-18 19:40:38 +00:00
parent e6cd7ddbcd
commit 5b78c27360
3 changed files with 190 additions and 60 deletions

View File

@@ -35,6 +35,7 @@ import org.alfresco.repo.security.authentication.AuthenticationUtil;
import org.alfresco.repo.security.authentication.MutableAuthenticationDao;
import org.alfresco.repo.security.permissions.PermissionReference;
import org.alfresco.repo.security.permissions.PermissionServiceSPI;
import org.alfresco.repo.transaction.RetryingTransactionHelper;
import org.alfresco.service.ServiceRegistry;
import org.alfresco.service.cmr.dictionary.DictionaryService;
import org.alfresco.service.cmr.repository.NodeRef;
@@ -86,6 +87,8 @@ public class AbstractPermissionTest extends BaseSpringTest
protected AclDaoComponent aclDaoComponent;
protected RetryingTransactionHelper retryingTransactionHelper;
public AbstractPermissionTest()
{
super();
@@ -112,6 +115,8 @@ public class AbstractPermissionTest extends BaseSpringTest
nodeDaoService = (NodeDaoService) applicationContext.getBean("nodeDaoService");
aclDaoComponent = (AclDaoComponent) applicationContext.getBean("aclDaoComponent");
retryingTransactionHelper = (RetryingTransactionHelper) applicationContext.getBean("retryingTransactionHelper");
StoreRef storeRef = nodeService.createStore(StoreRef.PROTOCOL_WORKSPACE, "Test_" + System.nanoTime());
rootNodeRef = nodeService.getRootNode(storeRef);

View File

@@ -29,12 +29,12 @@ import java.io.InputStream;
import java.util.Collection;
import java.util.Collections;
import java.util.EnumMap;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.LinkedHashSet;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.locks.ReadWriteLock;
import java.util.concurrent.locks.ReentrantReadWriteLock;
@@ -100,33 +100,35 @@ public class PermissionModel implements ModelDAO, InitializingBean
private String model;
// Aprrox 6 - default size OK
private Map<QName, PermissionSet> permissionSets = new HashMap<QName, PermissionSet>(128, 1.0f);
private ConcurrentHashMap<QName, PermissionSet> permissionSets = new ConcurrentHashMap<QName, PermissionSet>(128, 1.0f, 16);
// Global permissions - default size OK
private Set<GlobalPermissionEntry> globalPermissions = new HashSet<GlobalPermissionEntry>();
private Set<GlobalPermissionEntry> globalPermissions = Collections.synchronizedSet(new HashSet<GlobalPermissionEntry>());
private AccessStatus defaultPermission;
// Cache granting permissions
private HashMap<PermissionReference, Set<PermissionReference>> grantingPermissions = new HashMap<PermissionReference, Set<PermissionReference>>(256, 1.0f);
private ConcurrentHashMap<PermissionReference, Set<PermissionReference>> grantingPermissions = new ConcurrentHashMap<PermissionReference, Set<PermissionReference>>(256, 1.0f,
32);
// Cache grantees
private HashMap<PermissionReference, Set<PermissionReference>> granteePermissions = new HashMap<PermissionReference, Set<PermissionReference>>(256, 1.0f);
private ConcurrentHashMap<PermissionReference, Set<PermissionReference>> granteePermissions = new ConcurrentHashMap<PermissionReference, Set<PermissionReference>>(256, 1.0f,
32);
// Cache the mapping of extended groups to the base
private HashMap<PermissionGroup, PermissionGroup> groupsToBaseGroup = new HashMap<PermissionGroup, PermissionGroup>(256, 1.0f);
private ConcurrentHashMap<PermissionGroup, PermissionGroup> groupsToBaseGroup = new ConcurrentHashMap<PermissionGroup, PermissionGroup>(256, 1.0f, 32);
private HashMap<String, PermissionReference> uniqueMap;
private ConcurrentHashMap<String, PermissionReference> uniqueMap;
private HashMap<PermissionReference, Permission> permissionMap;
private ConcurrentHashMap<PermissionReference, Permission> permissionMap;
private HashMap<PermissionReference, PermissionGroup> permissionGroupMap;
private ConcurrentHashMap<PermissionReference, PermissionGroup> permissionGroupMap;
private HashMap<String, PermissionReference> permissionReferenceMap;
private ConcurrentHashMap<String, PermissionReference> permissionReferenceMap;
private Map<QName, Set<PermissionReference>> cachedTypePermissionsExposed = new HashMap<QName, Set<PermissionReference>>(256, 1.0f);
private ConcurrentHashMap<QName, Set<PermissionReference>> cachedTypePermissionsExposed = new ConcurrentHashMap<QName, Set<PermissionReference>>(256, 1.0f, 32);
private Map<QName, Set<PermissionReference>> cachedTypePermissionsUnexposed = new HashMap<QName, Set<PermissionReference>>(256, 1.0f);
private ConcurrentHashMap<QName, Set<PermissionReference>> cachedTypePermissionsUnexposed = new ConcurrentHashMap<QName, Set<PermissionReference>>(256, 1.0f, 32);
private Collection<QName> allAspects;
@@ -340,7 +342,7 @@ public class PermissionModel implements ModelDAO, InitializingBean
private Set<PermissionReference> getAllPermissionsImpl(QName type, boolean exposedOnly)
{
Map<QName, Set<PermissionReference>> cache;
ConcurrentHashMap<QName, Set<PermissionReference>> cache;
if (exposedOnly)
{
cache = this.cachedTypePermissionsExposed;
@@ -517,6 +519,10 @@ public class PermissionModel implements ModelDAO, InitializingBean
public synchronized Set<PermissionReference> getGrantingPermissions(PermissionReference permissionReference)
{
if(permissionReference == null)
{
return Collections.<PermissionReference>emptySet();
}
// Cache the results
Set<PermissionReference> granters = grantingPermissions.get(permissionReference);
if (granters == null)
@@ -595,6 +601,10 @@ public class PermissionModel implements ModelDAO, InitializingBean
public synchronized Set<PermissionReference> getGranteePermissions(PermissionReference permissionReference)
{
if(permissionReference == null)
{
return Collections.<PermissionReference>emptySet();
}
// Cache the results
Set<PermissionReference> grantees = granteePermissions.get(permissionReference);
if (grantees == null)
@@ -806,6 +816,10 @@ public class PermissionModel implements ModelDAO, InitializingBean
*/
private synchronized PermissionGroup getBasePermissionGroupOrNull(PermissionGroup pg)
{
if (pg == null)
{
return null;
}
PermissionGroup permissionGroup = groupsToBaseGroup.get(pg);
if (permissionGroup == null)
{
@@ -889,7 +903,7 @@ public class PermissionModel implements ModelDAO, InitializingBean
private static ReadWriteLock lock = new ReentrantReadWriteLock();
private static HashMap<PermissionReference, HashMap<QName, HashMap<Set<QName>, EnumMap<RequiredPermission.On, RequiredKey>>>> instances = new HashMap<PermissionReference, HashMap<QName, HashMap<Set<QName>, EnumMap<RequiredPermission.On, RequiredKey>>>>();
private static ConcurrentHashMap<PermissionReference, ConcurrentHashMap<QName, ConcurrentHashMap<Set<QName>, EnumMap<RequiredPermission.On, RequiredKey>>>> instances = new ConcurrentHashMap<PermissionReference, ConcurrentHashMap<QName, ConcurrentHashMap<Set<QName>, EnumMap<RequiredPermission.On, RequiredKey>>>>();
/**
* factory for the key
@@ -905,10 +919,10 @@ public class PermissionModel implements ModelDAO, InitializingBean
lock.readLock().lock();
try
{
HashMap<QName, HashMap<Set<QName>, EnumMap<RequiredPermission.On, RequiredKey>>> byPermRef = instances.get(required);
ConcurrentHashMap<QName, ConcurrentHashMap<Set<QName>, EnumMap<RequiredPermission.On, RequiredKey>>> byPermRef = instances.get(required);
if (byPermRef != null)
{
HashMap<Set<QName>, EnumMap<RequiredPermission.On, RequiredKey>> byType = byPermRef.get(qName);
ConcurrentHashMap<Set<QName>, EnumMap<RequiredPermission.On, RequiredKey>> byType = byPermRef.get(qName);
if (byType != null)
{
EnumMap<RequiredPermission.On, RequiredKey> byAspects = byType.get(aspectQNames);
@@ -931,16 +945,17 @@ public class PermissionModel implements ModelDAO, InitializingBean
lock.writeLock().lock();
try
{
HashMap<QName, HashMap<Set<QName>, EnumMap<RequiredPermission.On, RequiredKey>>> byPermRef = instances.get(required);
ConcurrentHashMap<QName, ConcurrentHashMap<Set<QName>, EnumMap<RequiredPermission.On, RequiredKey>>> byPermRef = instances.get(required);
if (byPermRef == null)
{
byPermRef = new HashMap<QName, HashMap<Set<QName>, EnumMap<RequiredPermission.On, RequiredKey>>>();
byPermRef = new ConcurrentHashMap<QName, ConcurrentHashMap<Set<QName>, EnumMap<RequiredPermission.On, RequiredKey>>>();
instances.put(required, byPermRef);
}
HashMap<Set<QName>, EnumMap<RequiredPermission.On, RequiredKey>> byType = byPermRef.get(qName);
ConcurrentHashMap<Set<QName>, EnumMap<RequiredPermission.On, RequiredKey>> byType = byPermRef.get(qName);
if (byType == null)
{
byType = new HashMap<Set<QName>, EnumMap<RequiredPermission.On, RequiredKey>>();
byType = new ConcurrentHashMap<Set<QName>, EnumMap<RequiredPermission.On, RequiredKey>>();
byPermRef.put(qName, byType);
}
EnumMap<RequiredPermission.On, RequiredKey> byAspects = byType.get(aspectQNames);
@@ -1036,12 +1051,16 @@ public class PermissionModel implements ModelDAO, InitializingBean
}
private HashMap<RequiredKey, Set<PermissionReference>> requiredPermissionsCache = new HashMap<RequiredKey, Set<PermissionReference>>(1024);
private ConcurrentHashMap<RequiredKey, Set<PermissionReference>> requiredPermissionsCache = new ConcurrentHashMap<RequiredKey, Set<PermissionReference>>(1024);
public Set<PermissionReference> getRequiredPermissions(PermissionReference required, QName qName, Set<QName> aspectQNames, RequiredPermission.On on)
{
// Cache lookup as this is static
if((required == null) || (qName == null))
{
return Collections.<PermissionReference>emptySet();
}
RequiredKey key = generateKey(required, qName, aspectQNames, on);
Set<PermissionReference> answer = requiredPermissionsCache.get(key);
@@ -1241,10 +1260,10 @@ public class PermissionModel implements ModelDAO, InitializingBean
private void buildUniquePermissionMap()
{
Set<String> excluded = new HashSet<String>(128, 1.0f);
uniqueMap = new HashMap<String, PermissionReference>(256, 1.0f);
permissionReferenceMap = new HashMap<String, PermissionReference>(256, 1.0f);
permissionGroupMap = new HashMap<PermissionReference, PermissionGroup>(128, 1.0f);
permissionMap = new HashMap<PermissionReference, Permission>(64, 1.0f);
uniqueMap = new ConcurrentHashMap<String, PermissionReference>(256, 1.0f, 16);
permissionReferenceMap = new ConcurrentHashMap<String, PermissionReference>(256, 1.0f, 16);
permissionGroupMap = new ConcurrentHashMap<PermissionReference, PermissionGroup>(128, 1.0f, 16);
permissionMap = new ConcurrentHashMap<PermissionReference, Permission>(64, 1.0f, 16);
for (PermissionSet ps : permissionSets.values())
{
for (PermissionGroup pg : ps.getPermissionGroups())

View File

@@ -25,18 +25,24 @@
package org.alfresco.repo.security.permissions.impl.model;
import java.util.Collections;
import java.util.HashSet;
import java.util.Random;
import java.util.Set;
import org.alfresco.model.ContentModel;
import org.alfresco.repo.domain.hibernate.SessionSizeResourceManager;
import org.alfresco.repo.security.permissions.PermissionEntry;
import org.alfresco.repo.security.permissions.PermissionReference;
import org.alfresco.repo.security.permissions.impl.AbstractPermissionTest;
import org.alfresco.repo.security.permissions.impl.SimplePermissionReference;
import org.alfresco.repo.security.permissions.impl.RequiredPermission.On;
import org.alfresco.repo.transaction.RetryingTransactionHelper.RetryingTransactionCallback;
import org.alfresco.service.cmr.repository.ChildAssociationRef;
import org.alfresco.service.namespace.QName;
public class PermissionModelTest extends AbstractPermissionTest
{
public PermissionModelTest()
{
super();
@@ -50,7 +56,7 @@ public class PermissionModelTest extends AbstractPermissionTest
Set<PermissionReference> answer = permissionModelDAO.getRequiredPermissions(ref, typeQname, aspectQNames, On.NODE);
assertEquals(1, answer.size());
}
public void testIncludePermissionGroups()
{
Set<PermissionReference> grantees = permissionModelDAO.getGranteePermissions(SimplePermissionReference.getPermissionReference(QName.createQName("cm", "cmobject",
@@ -58,7 +64,7 @@ public class PermissionModelTest extends AbstractPermissionTest
assertEquals(8, grantees.size());
}
public void testIncludePermissionGroups2()
{
Set<PermissionReference> grantees = permissionModelDAO.getGranteePermissions(SimplePermissionReference.getPermissionReference(QName.createQName("cm", "cmobject",
@@ -66,7 +72,7 @@ public class PermissionModelTest extends AbstractPermissionTest
assertEquals(16, grantees.size());
}
public void testIncludePermissionGroups3()
{
Set<PermissionReference> grantees = permissionModelDAO.getGranteePermissions(SimplePermissionReference.getPermissionReference(QName.createQName("cm", "cmobject",
@@ -74,7 +80,7 @@ public class PermissionModelTest extends AbstractPermissionTest
assertEquals(19, grantees.size());
}
public void testIncludePermissionGroups4()
{
Set<PermissionReference> grantees = permissionModelDAO.getGranteePermissions(SimplePermissionReference.getPermissionReference(QName.createQName("cm", "cmobject",
@@ -82,7 +88,7 @@ public class PermissionModelTest extends AbstractPermissionTest
assertEquals(26, grantees.size());
}
public void testIncludePermissionGroups5()
{
Set<PermissionReference> grantees = permissionModelDAO.getGranteePermissions(SimplePermissionReference.getPermissionReference(QName.createQName("cm", "cmobject",
@@ -92,7 +98,7 @@ public class PermissionModelTest extends AbstractPermissionTest
// 63-97 from AVM permission fix up
assertEquals(99, grantees.size());
}
public void testIncludePermissionGroups6()
{
Set<PermissionReference> grantees = permissionModelDAO.getGranteePermissions(SimplePermissionReference.getPermissionReference(QName.createQName("cm", "cmobject",
@@ -100,7 +106,7 @@ public class PermissionModelTest extends AbstractPermissionTest
assertEquals(19, grantees.size());
}
public void testGetGrantingPermissions()
{
Set<PermissionReference> granters = permissionModelDAO.getGrantingPermissions(SimplePermissionReference.getPermissionReference(QName.createQName("sys", "base",
@@ -108,52 +114,152 @@ public class PermissionModelTest extends AbstractPermissionTest
// NB This has gone from 10 to 14 because of the new WCM roles, I believe.
// 14-18 -> 4 site base roles added
assertEquals(18, granters.size());
granters = permissionModelDAO.getGrantingPermissions(SimplePermissionReference.getPermissionReference(QName.createQName("sys", "base",
namespacePrefixResolver), "_ReadProperties"));
granters = permissionModelDAO.getGrantingPermissions(SimplePermissionReference.getPermissionReference(QName.createQName("sys", "base", namespacePrefixResolver),
"_ReadProperties"));
// NB 11 to 15 as above.
// 5-19 site based roles added
assertEquals(19, granters.size());
}
public void testGlobalPermissions()
{
Set<? extends PermissionEntry> globalPermissions = permissionModelDAO.getGlobalPermissionEntries();
assertEquals(6, globalPermissions.size());
}
public void testRequiredPermissions()
{
Set<PermissionReference> required = permissionModelDAO.getRequiredPermissions(SimplePermissionReference.getPermissionReference(QName.createQName("sys", "base",
namespacePrefixResolver), "Read"), QName.createQName("sys", "base",
namespacePrefixResolver), Collections.<QName>emptySet(), On.NODE);
namespacePrefixResolver), "Read"), QName.createQName("sys", "base", namespacePrefixResolver), Collections.<QName> emptySet(), On.NODE);
assertEquals(3, required.size());
required = permissionModelDAO.getRequiredPermissions(SimplePermissionReference.getPermissionReference(QName.createQName("sys", "base",
namespacePrefixResolver), "ReadContent"), QName.createQName("sys", "base",
namespacePrefixResolver), Collections.<QName>emptySet(), On.NODE);
required = permissionModelDAO.getRequiredPermissions(SimplePermissionReference.getPermissionReference(QName.createQName("sys", "base", namespacePrefixResolver),
"ReadContent"), QName.createQName("sys", "base", namespacePrefixResolver), Collections.<QName> emptySet(), On.NODE);
assertEquals(1, required.size());
required = permissionModelDAO.getRequiredPermissions(SimplePermissionReference.getPermissionReference(QName.createQName("sys", "base",
namespacePrefixResolver), "_ReadContent"), QName.createQName("sys", "base",
namespacePrefixResolver), Collections.<QName>emptySet(), On.NODE);
required = permissionModelDAO.getRequiredPermissions(SimplePermissionReference.getPermissionReference(QName.createQName("sys", "base", namespacePrefixResolver),
"_ReadContent"), QName.createQName("sys", "base", namespacePrefixResolver), Collections.<QName> emptySet(), On.NODE);
assertEquals(0, required.size());
required = permissionModelDAO.getRequiredPermissions(SimplePermissionReference.getPermissionReference(QName.createQName("cm", "cmobject",
namespacePrefixResolver), "Coordinator"), QName.createQName("cm", "cmobject",
namespacePrefixResolver), Collections.<QName>emptySet(), On.NODE);
required = permissionModelDAO.getRequiredPermissions(SimplePermissionReference.getPermissionReference(QName.createQName("cm", "cmobject", namespacePrefixResolver),
"Coordinator"), QName.createQName("cm", "cmobject", namespacePrefixResolver), Collections.<QName> emptySet(), On.NODE);
assertEquals(18, required.size());
required = permissionModelDAO.getRequiredPermissions(SimplePermissionReference.getPermissionReference(QName.createQName("sys", "base",
namespacePrefixResolver), "FullControl"), QName.createQName("sys", "base",
namespacePrefixResolver), Collections.<QName>emptySet(), On.NODE);
required = permissionModelDAO.getRequiredPermissions(SimplePermissionReference.getPermissionReference(QName.createQName("sys", "base", namespacePrefixResolver),
"FullControl"), QName.createQName("sys", "base", namespacePrefixResolver), Collections.<QName> emptySet(), On.NODE);
assertEquals(18, required.size());
}
public void testMultiThreadedAccess()
{
Thread runner = null;
for (int i = 0; i < 20; i++)
{
runner = new Nester("Concurrent-" + i, runner);
}
if (runner != null)
{
runner.start();
try
{
runner.join();
}
catch (InterruptedException e)
{
e.printStackTrace();
}
}
}
class Nester extends Thread
{
Thread waiter;
Nester(String name, Thread waiter)
{
super(name);
this.setDaemon(true);
this.waiter = waiter;
}
public void run()
{
authenticationComponent.setSystemUserAsCurrentUser();
if (waiter != null)
{
waiter.start();
}
try
{
System.out.println("Start " + this.getName());
RetryingTransactionCallback<Void> queryPermissionModel = new RetryingTransactionCallback<Void>()
{
public Void execute() throws Throwable
{
Random random = new Random();
Set<PermissionReference> toTest = permissionModelDAO.getAllPermissions(QName.createQName("sys", "base", namespacePrefixResolver));
for (int i = 0; i < 10000; i++)
{
for (PermissionReference pr : toTest)
{
if (random.nextFloat() < 0.5f)
{
// permissionModelDAO.getGranteePermissions(pr);
// permissionModelDAO.getGrantingPermissions(pr);
permissionModelDAO.getRequiredPermissions(pr, QName.createQName("sys", "base", namespacePrefixResolver), Collections.<QName> emptySet(),
On.NODE);
}
}
}
return null;
}
};
retryingTransactionHelper.doInTransaction(queryPermissionModel);
System.out.println("End " + this.getName());
}
catch (Exception e)
{
System.out.println("End " + this.getName() + " with error " + e.getMessage());
e.printStackTrace();
}
finally
{
authenticationComponent.clearCurrentSecurityContext();
}
if (waiter != null)
{
try
{
waiter.join();
}
catch (InterruptedException e)
{
}
}
}
}
public void testNulls()
{
permissionModelDAO.getRequiredPermissions(null, QName.createQName("sys", "base", namespacePrefixResolver), Collections.<QName> emptySet(), On.NODE);
permissionModelDAO.getRequiredPermissions(SimplePermissionReference.getPermissionReference(QName.createQName("sys", "base",
namespacePrefixResolver), "Read"),null, Collections.<QName> emptySet(), On.NODE);
permissionModelDAO.getRequiredPermissions(null, null, Collections.<QName> emptySet(), On.NODE);
permissionModelDAO.getGranteePermissions(null);
Set<PermissionReference> granters = permissionModelDAO.getGrantingPermissions(null);
permissionModelDAO.getGlobalPermissionEntries().contains(null);
}
}