diff --git a/config/alfresco/cache-context.xml b/config/alfresco/cache-context.xml index 34c08f4eca..1e3d51054f 100644 --- a/config/alfresco/cache-context.xml +++ b/config/alfresco/cache-context.xml @@ -13,7 +13,7 @@ - + @@ -25,6 +25,12 @@ nullPermissionCache + + 10000 + + + false + @@ -43,7 +49,7 @@ nullPermissionTransactionalCache - 20000 + 5000 @@ -51,7 +57,7 @@ - + @@ -60,11 +66,17 @@ userToAuthorityCache + + 10000 + + + false + - + @@ -78,7 +90,89 @@ userToAuthorityTransactionalCache - 20000 + 5000 + + + + + + + + + + + + + + + permissionsAccessCache + + + 10000 + + + false + + + + + + + + + + + + + + + + + permissionsAccessTransactionalCache + + + 5000 + + + + + + + + + + + + + + + nodeOwnerCache + + + 10000 + + + false + + + + + + + + + + + + + + + + + nodeOwnerTransactionalCache + + + 5000 diff --git a/config/alfresco/ownable-services-context.xml b/config/alfresco/ownable-services-context.xml index 89106a7ef9..a2bbab152b 100644 --- a/config/alfresco/ownable-services-context.xml +++ b/config/alfresco/ownable-services-context.xml @@ -9,5 +9,8 @@ + + + \ No newline at end of file diff --git a/config/alfresco/public-services-security-context.xml b/config/alfresco/public-services-security-context.xml index 70628de797..3c08eca55c 100644 --- a/config/alfresco/public-services-security-context.xml +++ b/config/alfresco/public-services-security-context.xml @@ -63,6 +63,9 @@ + + + diff --git a/config/ehcache.xml b/config/ehcache.xml index 6dc991d364..95ebd4e911 100644 --- a/config/ehcache.xml +++ b/config/ehcache.xml @@ -3,7 +3,7 @@ path="java.io.tmpdir"/> - * If the threshold value is not reached, the items are not removed unless specifically requested with - * a call to remove() or clear(). - *

- * If the max size value is reached then no more items are added to the cache until some are removed - * either explicitly or automically via timed-out values. - * - * @author Kevin Roast - */ -public class AutoExpireCache implements SimpleCache -{ - // TODO: configure these values via Spring - private final long TIMEDIFF = 1000000L * 1000L * 60L * 5L; // 5 mins in nano-seconds - private final int MAXSIZE = 4096; // maximum size of the cache - private final float THRESHOLD = 0.75f; // before we start removing items - private int maxsize = MAXSIZE; - private float threshold = THRESHOLD; - private Map> cache = new HashMap>(maxsize, 1.0f); - - /** - * Default constructor - */ - public AutoExpireCache() - { - } - - /** - * Constructor - */ - public AutoExpireCache(int maxsize, float threshold) - { - maxsize = maxsize; - threshold = threshold; - } - - /** - * @see org.alfresco.repo.cache.SimpleCache#get(K) - */ - public synchronized V get(K key) - { - CacheItem wrapper = cache.get(key); - if (wrapper != null) - { - // we cache values till a specific timeout then remove them - // this also gives other values a chance to get added if we are nearing the max size - if (cache.size() > (maxsize * threshold) && - System.nanoTime() > (wrapper.Timestamp + TIMEDIFF)) - { - //if (log.isDebugEnabled()) - // log.debug("*****Removing timedout key: " + key); - cache.remove(key); - wrapper = null; - } - } - return wrapper != null ? wrapper.Value : null; - } - - /** - * @see org.alfresco.repo.cache.SimpleCache#put(K, V) - */ - public synchronized void put(K key, V value) - { - if (cache.size() < maxsize) - { - //if (log.isDebugEnabled()) - // log.debug("***Adding new key: " + key + " size: " + cache.size()); - cache.put(key, new CacheItem(key, value)); - } - } - - /** - * @see org.alfresco.repo.cache.SimpleCache#remove(K) - */ - public synchronized void remove(K key) - { - cache.remove(key); - } - - /** - * @see org.alfresco.repo.cache.SimpleCache#clear() - */ - public void clear() - { - // better to let the GC deal with removing the old map in one shot - // rather than try to clear each slot slowly using clear() - cache = new HashMap>(maxsize, 1.0f); - } - - /** - * @see org.alfresco.repo.cache.SimpleCache#contains(K) - */ - public synchronized boolean contains(K key) - { - return false; - } - - - /** - * Simple wrapper class for a cache item. - * Stores a timestamp of when the item was added so it can be purged from the cache later. - */ - private static class CacheItem - { - CacheItem(K key, V value) - { - this.key = key; - Value = value; - Timestamp = System.nanoTime(); - } - - public int hashCode() - { - return key.hashCode(); - } - - public boolean equals(Object o) - { - if (o instanceof CacheItem == false) return false; - return key.equals( ((CacheItem)o).key ); - } - - private K key; - long Timestamp; - V Value; - } -} diff --git a/source/java/org/alfresco/repo/ownable/impl/OwnableServiceImpl.java b/source/java/org/alfresco/repo/ownable/impl/OwnableServiceImpl.java index 781368e0e1..86f3b3afe5 100644 --- a/source/java/org/alfresco/repo/ownable/impl/OwnableServiceImpl.java +++ b/source/java/org/alfresco/repo/ownable/impl/OwnableServiceImpl.java @@ -20,7 +20,7 @@ import java.io.Serializable; import java.util.HashMap; import org.alfresco.model.ContentModel; -import org.alfresco.repo.cache.AutoExpireCache; +import org.alfresco.repo.cache.SimpleCache; import org.alfresco.service.cmr.repository.NodeRef; import org.alfresco.service.cmr.repository.NodeService; import org.alfresco.service.cmr.repository.datatype.DefaultTypeConverter; @@ -40,7 +40,7 @@ public class OwnableServiceImpl implements OwnableService, InitializingBean private AuthenticationService authenticationService; - private static AutoExpireCache ownerCache = new AutoExpireCache(1024, 0.75f); + private SimpleCache nodeOwnerCache; public OwnableServiceImpl() { @@ -59,16 +59,27 @@ public class OwnableServiceImpl implements OwnableService, InitializingBean this.authenticationService = authenticationService; } + /** + * @param ownerCache a transactionally-safe cache of node owners + */ + public void setNodeOwnerCache(SimpleCache ownerCache) + { + this.nodeOwnerCache = ownerCache; + } public void afterPropertiesSet() throws Exception { if (nodeService == null) { - throw new IllegalArgumentException("A node service must be set"); + throw new IllegalArgumentException("Property 'nodeService' has not been set"); } if (authenticationService == null) { - throw new IllegalArgumentException("An authentication service must be set"); + throw new IllegalArgumentException("Property 'authenticationService' has not been set"); + } + if (nodeOwnerCache == null) + { + throw new IllegalArgumentException("Property 'nodeOwnerCache' has not been set"); } } @@ -76,7 +87,7 @@ public class OwnableServiceImpl implements OwnableService, InitializingBean public String getOwner(NodeRef nodeRef) { - String userName = ownerCache.get(nodeRef); + String userName = nodeOwnerCache.get(nodeRef); if (userName == null) { @@ -89,7 +100,7 @@ public class OwnableServiceImpl implements OwnableService, InitializingBean { userName = DefaultTypeConverter.INSTANCE.convert(String.class, nodeService.getProperty(nodeRef, ContentModel.PROP_CREATOR)); } - ownerCache.put(nodeRef, userName); + nodeOwnerCache.put(nodeRef, userName); } return userName; @@ -97,8 +108,6 @@ public class OwnableServiceImpl implements OwnableService, InitializingBean public void setOwner(NodeRef nodeRef, String userName) { - ownerCache.remove(nodeRef); - if (!nodeService.hasAspect(nodeRef, ContentModel.ASPECT_OWNABLE)) { HashMap properties = new HashMap(1, 1.0f); @@ -109,6 +118,7 @@ public class OwnableServiceImpl implements OwnableService, InitializingBean { nodeService.setProperty(nodeRef, ContentModel.PROP_OWNER, userName); } + nodeOwnerCache.put(nodeRef, userName); } public void takeOwnership(NodeRef nodeRef) diff --git a/source/java/org/alfresco/repo/security/permissions/impl/PermissionServiceImpl.java b/source/java/org/alfresco/repo/security/permissions/impl/PermissionServiceImpl.java index 75a5411474..c216e88b60 100644 --- a/source/java/org/alfresco/repo/security/permissions/impl/PermissionServiceImpl.java +++ b/source/java/org/alfresco/repo/security/permissions/impl/PermissionServiceImpl.java @@ -25,7 +25,7 @@ import net.sf.acegisecurity.Authentication; import net.sf.acegisecurity.GrantedAuthority; import net.sf.acegisecurity.providers.dao.User; -import org.alfresco.repo.cache.AutoExpireCache; +import org.alfresco.repo.cache.SimpleCache; import org.alfresco.repo.security.authentication.AuthenticationComponent; import org.alfresco.repo.security.permissions.DynamicAuthority; import org.alfresco.repo.security.permissions.NodePermissionEntry; @@ -62,8 +62,8 @@ public class PermissionServiceImpl implements PermissionServiceSPI, Initializing private static Log log = LogFactory.getLog(PermissionServiceImpl.class); - private static AutoExpireCache accessCache = new AutoExpireCache(4096, 0.75f); - + /** a transactionally-safe cache to be injected */ + private SimpleCache accessCache; /* * Access to the model @@ -147,33 +147,46 @@ public class PermissionServiceImpl implements PermissionServiceSPI, Initializing this.dynamicAuthorities = dynamicAuthorities; } + /** + * Set the permissions access cache. + * + * @param accessCache a transactionally safe cache + */ + public void setAccessCache(SimpleCache accessCache) + { + this.accessCache = accessCache; + } + public void afterPropertiesSet() throws Exception { if (dictionaryService == null) { - throw new IllegalArgumentException("There must be a dictionary service"); + throw new IllegalArgumentException("Property 'dictionaryService' has not been set"); } if (modelDAO == null) { - throw new IllegalArgumentException("There must be a permission model service"); + throw new IllegalArgumentException("Property 'modelDAO' has not been set"); } if (nodeService == null) { - throw new IllegalArgumentException("There must be a node service"); + throw new IllegalArgumentException("Property 'nodeService' has not been set"); } if (permissionsDAO == null) { - throw new IllegalArgumentException("There must be a permission dao"); + throw new IllegalArgumentException("Property 'permissionsDAO' has not been set"); } if (authenticationComponent == null) { - throw new IllegalArgumentException("There must be an authentication component"); + throw new IllegalArgumentException("Property 'authenticationComponent' has not been set"); } if(authorityService == null) { - throw new IllegalArgumentException("There must be an authority service"); + throw new IllegalArgumentException("Property 'authorityService' has not been set"); + } + if (accessCache == null) + { + throw new IllegalArgumentException("Property 'accessCache' has not been set"); } - } // @@ -405,9 +418,9 @@ public class PermissionServiceImpl implements PermissionServiceSPI, Initializing * dynamically so they must all be used) the NodeRef ID and the permission reference itself. * This gives a unique key for each permission test. */ - static Serializable generateKey(Set auths, NodeRef ref, PermissionReference perm) + static Serializable generateKey(Set auths, NodeRef ref, PermissionReference perm) { - HashSet key = new HashSet(auths); + HashSet key = new HashSet(auths); key.add(ref.getId()); key.add(perm.toString()); return key;