diff --git a/source/java/org/alfresco/repo/security/permissions/impl/AbstractPermissionTest.java b/source/java/org/alfresco/repo/security/permissions/impl/AbstractPermissionTest.java index bfcdbb4a7b..eca199d213 100644 --- a/source/java/org/alfresco/repo/security/permissions/impl/AbstractPermissionTest.java +++ b/source/java/org/alfresco/repo/security/permissions/impl/AbstractPermissionTest.java @@ -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); diff --git a/source/java/org/alfresco/repo/security/permissions/impl/model/PermissionModel.java b/source/java/org/alfresco/repo/security/permissions/impl/model/PermissionModel.java index 69662dd865..20539dda4c 100644 --- a/source/java/org/alfresco/repo/security/permissions/impl/model/PermissionModel.java +++ b/source/java/org/alfresco/repo/security/permissions/impl/model/PermissionModel.java @@ -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 permissionSets = new HashMap(128, 1.0f); + private ConcurrentHashMap permissionSets = new ConcurrentHashMap(128, 1.0f, 16); // Global permissions - default size OK - private Set globalPermissions = new HashSet(); + private Set globalPermissions = Collections.synchronizedSet(new HashSet()); private AccessStatus defaultPermission; // Cache granting permissions - private HashMap> grantingPermissions = new HashMap>(256, 1.0f); + private ConcurrentHashMap> grantingPermissions = new ConcurrentHashMap>(256, 1.0f, + 32); // Cache grantees - private HashMap> granteePermissions = new HashMap>(256, 1.0f); + private ConcurrentHashMap> granteePermissions = new ConcurrentHashMap>(256, 1.0f, + 32); // Cache the mapping of extended groups to the base - private HashMap groupsToBaseGroup = new HashMap(256, 1.0f); + private ConcurrentHashMap groupsToBaseGroup = new ConcurrentHashMap(256, 1.0f, 32); - private HashMap uniqueMap; + private ConcurrentHashMap uniqueMap; - private HashMap permissionMap; + private ConcurrentHashMap permissionMap; - private HashMap permissionGroupMap; + private ConcurrentHashMap permissionGroupMap; - private HashMap permissionReferenceMap; + private ConcurrentHashMap permissionReferenceMap; - private Map> cachedTypePermissionsExposed = new HashMap>(256, 1.0f); + private ConcurrentHashMap> cachedTypePermissionsExposed = new ConcurrentHashMap>(256, 1.0f, 32); - private Map> cachedTypePermissionsUnexposed = new HashMap>(256, 1.0f); + private ConcurrentHashMap> cachedTypePermissionsUnexposed = new ConcurrentHashMap>(256, 1.0f, 32); private Collection allAspects; @@ -340,7 +342,7 @@ public class PermissionModel implements ModelDAO, InitializingBean private Set getAllPermissionsImpl(QName type, boolean exposedOnly) { - Map> cache; + ConcurrentHashMap> cache; if (exposedOnly) { cache = this.cachedTypePermissionsExposed; @@ -517,6 +519,10 @@ public class PermissionModel implements ModelDAO, InitializingBean public synchronized Set getGrantingPermissions(PermissionReference permissionReference) { + if(permissionReference == null) + { + return Collections.emptySet(); + } // Cache the results Set granters = grantingPermissions.get(permissionReference); if (granters == null) @@ -595,6 +601,10 @@ public class PermissionModel implements ModelDAO, InitializingBean public synchronized Set getGranteePermissions(PermissionReference permissionReference) { + if(permissionReference == null) + { + return Collections.emptySet(); + } // Cache the results Set 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, EnumMap>>> instances = new HashMap, EnumMap>>>(); + private static ConcurrentHashMap, EnumMap>>> instances = new ConcurrentHashMap, EnumMap>>>(); /** * factory for the key @@ -905,10 +919,10 @@ public class PermissionModel implements ModelDAO, InitializingBean lock.readLock().lock(); try { - HashMap, EnumMap>> byPermRef = instances.get(required); + ConcurrentHashMap, EnumMap>> byPermRef = instances.get(required); if (byPermRef != null) { - HashMap, EnumMap> byType = byPermRef.get(qName); + ConcurrentHashMap, EnumMap> byType = byPermRef.get(qName); if (byType != null) { EnumMap byAspects = byType.get(aspectQNames); @@ -931,16 +945,17 @@ public class PermissionModel implements ModelDAO, InitializingBean lock.writeLock().lock(); try { - HashMap, EnumMap>> byPermRef = instances.get(required); + ConcurrentHashMap, EnumMap>> byPermRef = instances.get(required); if (byPermRef == null) { - byPermRef = new HashMap, EnumMap>>(); + byPermRef = new ConcurrentHashMap, EnumMap>>(); instances.put(required, byPermRef); } - HashMap, EnumMap> byType = byPermRef.get(qName); + + ConcurrentHashMap, EnumMap> byType = byPermRef.get(qName); if (byType == null) { - byType = new HashMap, EnumMap>(); + byType = new ConcurrentHashMap, EnumMap>(); byPermRef.put(qName, byType); } EnumMap byAspects = byType.get(aspectQNames); @@ -1036,12 +1051,16 @@ public class PermissionModel implements ModelDAO, InitializingBean } - private HashMap> requiredPermissionsCache = new HashMap>(1024); + private ConcurrentHashMap> requiredPermissionsCache = new ConcurrentHashMap>(1024); public Set getRequiredPermissions(PermissionReference required, QName qName, Set aspectQNames, RequiredPermission.On on) { // Cache lookup as this is static - + if((required == null) || (qName == null)) + { + return Collections.emptySet(); + } + RequiredKey key = generateKey(required, qName, aspectQNames, on); Set answer = requiredPermissionsCache.get(key); @@ -1241,10 +1260,10 @@ public class PermissionModel implements ModelDAO, InitializingBean private void buildUniquePermissionMap() { Set excluded = new HashSet(128, 1.0f); - uniqueMap = new HashMap(256, 1.0f); - permissionReferenceMap = new HashMap(256, 1.0f); - permissionGroupMap = new HashMap(128, 1.0f); - permissionMap = new HashMap(64, 1.0f); + uniqueMap = new ConcurrentHashMap(256, 1.0f, 16); + permissionReferenceMap = new ConcurrentHashMap(256, 1.0f, 16); + permissionGroupMap = new ConcurrentHashMap(128, 1.0f, 16); + permissionMap = new ConcurrentHashMap(64, 1.0f, 16); for (PermissionSet ps : permissionSets.values()) { for (PermissionGroup pg : ps.getPermissionGroups()) diff --git a/source/java/org/alfresco/repo/security/permissions/impl/model/PermissionModelTest.java b/source/java/org/alfresco/repo/security/permissions/impl/model/PermissionModelTest.java index d41dbd84e8..cb06a34f7c 100644 --- a/source/java/org/alfresco/repo/security/permissions/impl/model/PermissionModelTest.java +++ b/source/java/org/alfresco/repo/security/permissions/impl/model/PermissionModelTest.java @@ -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 answer = permissionModelDAO.getRequiredPermissions(ref, typeQname, aspectQNames, On.NODE); assertEquals(1, answer.size()); } - + public void testIncludePermissionGroups() { Set 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 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 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 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 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 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 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 globalPermissions = permissionModelDAO.getGlobalPermissionEntries(); assertEquals(6, globalPermissions.size()); } - + public void testRequiredPermissions() { Set required = permissionModelDAO.getRequiredPermissions(SimplePermissionReference.getPermissionReference(QName.createQName("sys", "base", - namespacePrefixResolver), "Read"), QName.createQName("sys", "base", - namespacePrefixResolver), Collections.emptySet(), On.NODE); + namespacePrefixResolver), "Read"), QName.createQName("sys", "base", namespacePrefixResolver), Collections. emptySet(), On.NODE); assertEquals(3, required.size()); - - required = permissionModelDAO.getRequiredPermissions(SimplePermissionReference.getPermissionReference(QName.createQName("sys", "base", - namespacePrefixResolver), "ReadContent"), QName.createQName("sys", "base", - namespacePrefixResolver), Collections.emptySet(), On.NODE); + + required = permissionModelDAO.getRequiredPermissions(SimplePermissionReference.getPermissionReference(QName.createQName("sys", "base", namespacePrefixResolver), + "ReadContent"), QName.createQName("sys", "base", namespacePrefixResolver), Collections. emptySet(), On.NODE); assertEquals(1, required.size()); - - required = permissionModelDAO.getRequiredPermissions(SimplePermissionReference.getPermissionReference(QName.createQName("sys", "base", - namespacePrefixResolver), "_ReadContent"), QName.createQName("sys", "base", - namespacePrefixResolver), Collections.emptySet(), On.NODE); + + required = permissionModelDAO.getRequiredPermissions(SimplePermissionReference.getPermissionReference(QName.createQName("sys", "base", namespacePrefixResolver), + "_ReadContent"), QName.createQName("sys", "base", namespacePrefixResolver), Collections. emptySet(), On.NODE); assertEquals(0, required.size()); - - - - required = permissionModelDAO.getRequiredPermissions(SimplePermissionReference.getPermissionReference(QName.createQName("cm", "cmobject", - namespacePrefixResolver), "Coordinator"), QName.createQName("cm", "cmobject", - namespacePrefixResolver), Collections.emptySet(), On.NODE); + + required = permissionModelDAO.getRequiredPermissions(SimplePermissionReference.getPermissionReference(QName.createQName("cm", "cmobject", namespacePrefixResolver), + "Coordinator"), QName.createQName("cm", "cmobject", namespacePrefixResolver), Collections. emptySet(), On.NODE); assertEquals(18, required.size()); - - - required = permissionModelDAO.getRequiredPermissions(SimplePermissionReference.getPermissionReference(QName.createQName("sys", "base", - namespacePrefixResolver), "FullControl"), QName.createQName("sys", "base", - namespacePrefixResolver), Collections.emptySet(), On.NODE); + + required = permissionModelDAO.getRequiredPermissions(SimplePermissionReference.getPermissionReference(QName.createQName("sys", "base", namespacePrefixResolver), + "FullControl"), QName.createQName("sys", "base", namespacePrefixResolver), Collections. 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 queryPermissionModel = new RetryingTransactionCallback() + { + public Void execute() throws Throwable + { + Random random = new Random(); + Set 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. 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. emptySet(), On.NODE); + permissionModelDAO.getRequiredPermissions(SimplePermissionReference.getPermissionReference(QName.createQName("sys", "base", + namespacePrefixResolver), "Read"),null, Collections. emptySet(), On.NODE); + permissionModelDAO.getRequiredPermissions(null, null, Collections. emptySet(), On.NODE); + permissionModelDAO.getGranteePermissions(null); + + Set granters = permissionModelDAO.getGrantingPermissions(null); + permissionModelDAO.getGlobalPermissionEntries().contains(null); + } }