diff --git a/rm-server/config/alfresco/module/org_alfresco_module_rm/rm-service-context.xml b/rm-server/config/alfresco/module/org_alfresco_module_rm/rm-service-context.xml index 06f21ffa6f..629a9ee404 100644 --- a/rm-server/config/alfresco/module/org_alfresco_module_rm/rm-service-context.xml +++ b/rm-server/config/alfresco/module/org_alfresco_module_rm/rm-service-context.xml @@ -892,9 +892,44 @@ + + + + + + + + + + + + + + + + org.alfresco.cache.caveatConfigCache + + + + + + + + + + + + + org.alfresco.caveatConfigTransactionalCache + + + + + + diff --git a/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/caveat/RMCaveatConfigComponentImpl.java b/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/caveat/RMCaveatConfigComponentImpl.java index 09a94de97a..60781a2f83 100644 --- a/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/caveat/RMCaveatConfigComponentImpl.java +++ b/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/caveat/RMCaveatConfigComponentImpl.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2005-2011 Alfresco Software Limited. + * Copyright (C) 2005-2013 Alfresco Software Limited. * * This file is part of Alfresco * @@ -22,18 +22,22 @@ import java.io.File; import java.io.InputStream; import java.io.Serializable; import java.util.ArrayList; +import java.util.Collection; import java.util.Collections; import java.util.HashMap; import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Set; -import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.locks.Lock; +import java.util.concurrent.locks.ReadWriteLock; +import java.util.concurrent.locks.ReentrantReadWriteLock; import org.alfresco.error.AlfrescoRuntimeException; import org.alfresco.model.ContentModel; import org.alfresco.module.org_alfresco_module_rm.caveat.RMListOfValuesConstraint.MatchLogic; import org.alfresco.module.org_alfresco_module_rm.model.RecordsManagementModel; +import org.alfresco.repo.cache.SimpleCache; import org.alfresco.repo.content.ContentServicePolicies; import org.alfresco.repo.content.MimetypeMap; import org.alfresco.repo.node.NodeServicePolicies; @@ -97,16 +101,25 @@ public class RMCaveatConfigComponentImpl implements ContentServicePolicies.OnCon private static final QName DATATYPE_TEXT = DataTypeDefinition.TEXT; + /** + * Lock objects + */ + private ReadWriteLock lock = new ReentrantReadWriteLock(); + private Lock readLock = lock.readLock(); + private Lock writeLock = lock.writeLock(); /* - * Caveat Config + * Caveat Config (Shared) config * first string is property name * second string is authority name (user or group full name) * third string is list of values of property - */ + */ + private SimpleCache>> caveatConfig; - // TODO - convert to SimpleCache to be cluster-aware (for dynamic changes to caveat config across a cluster) - private Map>> caveatConfig = new ConcurrentHashMap>>(2); + public void setCaveatConfig(SimpleCache>> caveatConfig) + { + this.caveatConfig = caveatConfig; + } public void setPolicyComponent(PolicyComponent policyComponent) { @@ -252,6 +265,12 @@ public class RMCaveatConfigComponentImpl implements ContentServicePolicies.OnCon validateAndReset(childAssocRef.getChildRef()); } + /** + * Validate the caveat config and optionally update the cache. + * + * @param nodeRef The nodeRef of the config + * @param updateCache Set to true to update the cache + */ @SuppressWarnings("unchecked") protected void validateAndReset(NodeRef nodeRef) { @@ -409,15 +428,29 @@ public class RMCaveatConfigComponentImpl implements ContentServicePolicies.OnCon } } - // Valid, so update - caveatConfig.clear(); - - for (Map.Entry conEntry : caveatConfigMap.entrySet()) + try { - String conStr = conEntry.getKey(); - Map> caveatMap = (Map>)conEntry.getValue(); + writeLock.lock(); + // we can't just clear the cache, as all puts to the cache afterwards in this transaction will be ignored + // first delete all keys that are now not in the config + caveatConfig.getKeys().retainAll(caveatConfigMap.keySet()); - caveatConfig.put(conStr, caveatMap); + for (Map.Entry conEntry : caveatConfigMap.entrySet()) + { + String conStr = conEntry.getKey(); + Map> caveatMap = (Map>)conEntry.getValue(); + + Map> cacheValue = caveatConfig.get(conStr); + if (cacheValue == null || !cacheValue.equals(caveatMap)) + { + // update the cache + caveatConfig.put(conStr, caveatMap); + } + } + } + finally + { + writeLock.unlock(); } } catch (JSONException e) @@ -495,9 +528,19 @@ public class RMCaveatConfigComponentImpl implements ContentServicePolicies.OnCon } // Get list of all caveat qualified names - public Set getRMConstraintNames() + public Collection getRMConstraintNames() { - return caveatConfig.keySet(); + Collection rmConstraintNames = Collections.emptySet(); + try + { + readLock.lock(); + rmConstraintNames = caveatConfig.getKeys(); + } + finally + { + readLock.unlock(); + } + return Collections.unmodifiableCollection(rmConstraintNames); } // Get allowed values for given caveat (for current user) @@ -510,8 +553,7 @@ public class RMCaveatConfigComponentImpl implements ContentServicePolicies.OnCon { if (! (AuthenticationUtil.isMtEnabled() && AuthenticationUtil.isRunAsUserTheSystemUser())) { - // note: userName and userGroupNames must not be null - Map> caveatConstraintDef = caveatConfig.get(constraintName); + // note: userName and userGroupNames must not be null Set userGroupFullNames = authorityService.getAuthoritiesForUser(userName); allowedValues = getRMAllowedValues(userName, userGroupFullNames, constraintName); } @@ -525,7 +567,16 @@ public class RMCaveatConfigComponentImpl implements ContentServicePolicies.OnCon SetallowedValues = new HashSet(); // note: userName and userGroupNames must not be null - Map> caveatConstraintDef = caveatConfig.get(constraintName); + Map> caveatConstraintDef = null; + try + { + readLock.lock(); + caveatConstraintDef = caveatConfig.get(constraintName); + } + finally + { + readLock.unlock(); + } if (caveatConstraintDef != null) { @@ -547,7 +598,7 @@ public class RMCaveatConfigComponentImpl implements ContentServicePolicies.OnCon Listret = new ArrayList(); ret.addAll(allowedValues); - return ret; + return Collections.unmodifiableList(ret); } /** @@ -692,23 +743,53 @@ public class RMCaveatConfigComponentImpl implements ContentServicePolicies.OnCon * * @param listName the name of the RMConstraintList * @param authorityName - * @param values + * @param value * @throws AlfrescoRuntimeException if either the list or the authority do not already exist. */ public void addRMConstraintListValue(String listName, String authorityName, String value) { - Map> members = caveatConfig.get(listName); - if(members == null) + Map> members = null; + try { - throw new AlfrescoRuntimeException("unable to add to list, list not defined:"+ listName); + readLock.lock(); + members = caveatConfig.get(listName); + if(members == null) + { + throw new AlfrescoRuntimeException("unable to add to list, list not defined:"+ listName); + } + + try + { + readLock.unlock(); + writeLock.lock(); + // check again + members = caveatConfig.get(listName); + if(members == null) + { + throw new AlfrescoRuntimeException("unable to add to list, list not defined:"+ listName); + } + + List values = members.get(authorityName); + if(values == null) + { + throw new AlfrescoRuntimeException("Unable to add to authority in list. Authority not member listName: "+ listName + " authorityName:" +authorityName); + } + values.add(value); + + caveatConfig.put(listName, members); + updateOrCreateCaveatConfig(convertToJSONString(caveatConfig)); + } + finally + { + readLock.lock(); + writeLock.unlock(); + } + } - List values = members.get(authorityName); - if(values == null) + finally { - throw new AlfrescoRuntimeException("Unable to add to authority in list. Authority not member listName: "+ listName + " authorityName:" +authorityName); + readLock.unlock(); } - values.add(value); - updateOrCreateCaveatConfig(convertToJSONString(caveatConfig)); } /** @@ -718,7 +799,24 @@ public class RMCaveatConfigComponentImpl implements ContentServicePolicies.OnCon */ public Map> getListDetails(String listName) { - return caveatConfig.get(listName); + Map> listDetails = null; + try + { + readLock.lock(); + listDetails = caveatConfig.get(listName); + } + finally + { + readLock.unlock(); + } + if (listDetails == null) + { + return Collections.emptyMap(); + } + else + { + return Collections.unmodifiableMap(listDetails); + } } public List getRMCaveatModels() @@ -738,20 +836,30 @@ public class RMCaveatConfigComponentImpl implements ContentServicePolicies.OnCon */ public void updateRMConstraintListAuthority(String listName, String authorityName, Listvalues) { - Map> members = caveatConfig.get(listName); - if(members == null) + Map> members = null; + try { - // Create the new list, with the authority name - Map> constraint = new HashMap>(0); - constraint.put(authorityName, values); - caveatConfig.put(listName, constraint); - } - else - { - members.put(authorityName, values); - } + writeLock.lock(); + members = caveatConfig.get(listName); + if(members == null) + { + // Create the new list, with the authority name + Map> constraint = new HashMap>(0); + constraint.put(authorityName, new ArrayList(values)); + members = constraint; + } + else + { + members.put(authorityName, new ArrayList(values)); + } - updateOrCreateCaveatConfig(convertToJSONString(caveatConfig)); + caveatConfig.put(listName, members); + updateOrCreateCaveatConfig(convertToJSONString(caveatConfig)); + } + finally + { + writeLock.unlock(); + } } /** @@ -764,57 +872,19 @@ public class RMCaveatConfigComponentImpl implements ContentServicePolicies.OnCon public void updateRMConstraintListValue(String listName, String valueName, Listauthorities) { - // members contains member, values[] - Map> members = caveatConfig.get(listName); - - if(members == null) + Map> members = null; + try { - // Members List does not exist - Map> emptyConstraint = new HashMap>(0); - caveatConfig.put(listName, emptyConstraint); - members = emptyConstraint; - - } - // authorities contains authority, values[] - // pivot contains value, members[] - Map> pivot = PivotUtil.getPivot(members); + writeLock.lock(); - // remove all authorities which have this value - List existingAuthorities = pivot.get(valueName); - if(existingAuthorities != null) - { - for(String authority : existingAuthorities) + if(members == null) { - List vals = members.get(authority); - vals.remove(valueName); + // Members List does not exist + Map> emptyConstraint = new HashMap>(0); + caveatConfig.put(listName, emptyConstraint); + members = emptyConstraint; + } - } - // add the new authorities for this value - for(String authority : authorities) - { - List vals = members.get(authority); - if(vals == null) - { - vals= new ArrayList(); - members.put(authority, vals); - } - vals.add(valueName); - } - - updateOrCreateCaveatConfig(convertToJSONString(caveatConfig)); - } - - public void removeRMConstraintListValue(String listName, String valueName) - { - // members contains member, values[] - Map> members = caveatConfig.get(listName); - - if(members == null) - { - // list does not exist - } - else - { // authorities contains authority, values[] // pivot contains value, members[] Map> pivot = PivotUtil.getPivot(members); @@ -829,8 +899,82 @@ public class RMCaveatConfigComponentImpl implements ContentServicePolicies.OnCon vals.remove(valueName); } } - - updateOrCreateCaveatConfig(convertToJSONString(caveatConfig)); + // add the new authorities for this value + for(String authority : authorities) + { + List vals = members.get(authority); + if(vals == null) + { + vals= new ArrayList(); + members.put(authority, vals); + } + vals.add(valueName); + } + caveatConfig.put(listName, members); + updateOrCreateCaveatConfig(convertToJSONString(caveatConfig)); + } + finally + { + writeLock.unlock(); + } + } + + public void removeRMConstraintListValue(String listName, String valueName) + { + Map> members = null; + try + { + readLock.lock(); + + members = caveatConfig.get(listName); + if(members == null) + { + // list does not exist + } + else + { + try + { + readLock.unlock(); + writeLock.lock(); + // check again + members = caveatConfig.get(listName); + if(members == null) + { + // list does not exist + } + else + { + // authorities contains authority, values[] + // pivot contains value, members[] + Map> pivot = PivotUtil.getPivot(members); + + // remove all authorities which have this value + List existingAuthorities = pivot.get(valueName); + if(existingAuthorities != null) + { + for(String authority : existingAuthorities) + { + List vals = members.get(authority); + vals.remove(valueName); + } + caveatConfig.put(listName, members); + } + } + + updateOrCreateCaveatConfig(convertToJSONString(caveatConfig)); + } + finally + { + readLock.lock(); + writeLock.unlock(); + } + + } + } + finally + { + readLock.unlock(); } } @@ -843,26 +987,37 @@ public class RMCaveatConfigComponentImpl implements ContentServicePolicies.OnCon */ public void removeRMConstraintListAuthority(String listName, String authorityName) { - Map> members = caveatConfig.get(listName); - if(members != null) + Map> members = null; + try { - members.remove(listName); + writeLock.lock(); + members = caveatConfig.get(listName); + if(members != null) + { + members.remove(listName); + } + + caveatConfig.put(listName, members); + updateOrCreateCaveatConfig(convertToJSONString(caveatConfig)); + } - - updateOrCreateCaveatConfig(convertToJSONString(caveatConfig)); - } + finally + { + writeLock.unlock(); + } +} /** * @param config the configuration to convert * @return a String containing the JSON representation of the configuration. */ - private String convertToJSONString(Map>> config) + private String convertToJSONString(SimpleCache>> config) { JSONObject obj = new JSONObject(); try { - Set listNames = config.keySet(); + Collection listNames = config.getKeys(); for(String listName : listNames) { Map> members = config.get(listName); @@ -932,14 +1087,30 @@ public class RMCaveatConfigComponentImpl implements ContentServicePolicies.OnCon public void deleteRMConstraint(String listName) { - caveatConfig.remove(listName); - updateOrCreateCaveatConfig(convertToJSONString(caveatConfig)); + try + { + writeLock.lock(); + caveatConfig.remove(listName); + updateOrCreateCaveatConfig(convertToJSONString(caveatConfig)); + } + finally + { + writeLock.unlock(); + } } public void addRMConstraint(String listName) { - Map> emptyConstraint = new HashMap>(0); - caveatConfig.put(listName, emptyConstraint); - updateOrCreateCaveatConfig(convertToJSONString(caveatConfig)); + try + { + writeLock.lock(); + Map> emptyConstraint = new HashMap>(0); + caveatConfig.put(listName, emptyConstraint); + updateOrCreateCaveatConfig(convertToJSONString(caveatConfig)); + } + finally + { + writeLock.unlock(); + } } }