RM performance enhancements

* serach improvements
  * in-place record browse improvements
  * saved search via file plan browse improvements



git-svn-id: https://svn.alfresco.com/repos/alfresco-enterprise/modules/recordsmanagement/BRANCHES/V2.1.0.x@76850 c4b6b30b-aa2e-2d43-bbcb-ca4b014f7261
This commit is contained in:
Roy Wetherall
2014-07-18 04:17:45 +00:00
parent 60ab1304bd
commit 0431f25865
10 changed files with 198 additions and 136 deletions

View File

@@ -18,6 +18,8 @@
*/
package org.alfresco.module.org_alfresco_module_rm.capability;
import java.util.Map;
import net.sf.acegisecurity.vote.AccessDecisionVoter;
import org.alfresco.error.AlfrescoRuntimeException;
@@ -27,6 +29,7 @@ import org.alfresco.module.org_alfresco_module_rm.fileplan.FilePlanService;
import org.alfresco.module.org_alfresco_module_rm.model.RecordsManagementModel;
import org.alfresco.repo.security.authentication.AuthenticationUtil;
import org.alfresco.repo.transaction.AlfrescoTransactionSupport;
import org.alfresco.repo.transaction.TransactionalResourceHelper;
import org.alfresco.service.cmr.repository.AssociationRef;
import org.alfresco.service.cmr.repository.ChildAssociationRef;
import org.alfresco.service.cmr.repository.NodeRef;
@@ -34,6 +37,7 @@ import org.alfresco.service.cmr.repository.NodeService;
import org.alfresco.service.cmr.repository.StoreRef;
import org.alfresco.service.cmr.security.AccessStatus;
import org.alfresco.service.cmr.security.PermissionService;
import org.alfresco.util.Pair;
import org.aopalliance.intercept.MethodInvocation;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
@@ -192,48 +196,82 @@ public class RMSecurityCommon
}
/**
* Core RM read check
*
* @param nodeRef
* @return
* @param nodeRef node reference
* @return int see {@link AccessDecisionVoter}
*/
public int checkRmRead(NodeRef nodeRef)
{
int result = getTransactionCache("checkRmRead", nodeRef);
if (result != NOSET_VALUE)
{
return result;
}
if (permissionService.hasPermission(nodeRef, RMPermissionModel.READ_RECORDS) == AccessStatus.DENIED)
{
if (logger.isDebugEnabled())
{
logger.debug("\t\tUser does not have read record permission on node, access denied. (nodeRef=" + nodeRef.toString() + ", user=" + AuthenticationUtil.getRunAsUser() + ")");
}
return setTransactionCache("checkRmRead", nodeRef, AccessDecisionVoter.ACCESS_DENIED);
}
// Get the file plan for the node
NodeRef filePlan = filePlanService.getFilePlan(nodeRef);
if (permissionService.hasPermission(filePlan, RMPermissionModel.VIEW_RECORDS) == AccessStatus.DENIED)
{
if (logger.isDebugEnabled())
{
logger.debug("\t\tUser does not have view records capability permission on node, access denied. (filePlan=" + filePlan.toString() + ", user=" + AuthenticationUtil.getRunAsUser() + ")");
}
return setTransactionCache("checkRmRead", nodeRef, AccessDecisionVoter.ACCESS_DENIED);
}
if (caveatConfigComponent.hasAccess(nodeRef))
{
return setTransactionCache("checkRmRead", nodeRef, AccessDecisionVoter.ACCESS_GRANTED);
}
else
{
return setTransactionCache("checkRmRead", nodeRef, AccessDecisionVoter.ACCESS_DENIED);
}
{
int result = AccessDecisionVoter.ACCESS_ABSTAIN;
Map<Pair<String, NodeRef>, Integer> transactionCache = TransactionalResourceHelper.getMap("rm.security.checkRMRead");
Pair<String, NodeRef> key = new Pair<String, NodeRef>(AuthenticationUtil.getRunAsUser(), nodeRef);
if (transactionCache.containsKey(key))
{
result = transactionCache.get(key);
}
else
{
if (permissionService.hasPermission(nodeRef, RMPermissionModel.READ_RECORDS) == AccessStatus.DENIED)
{
if (logger.isDebugEnabled())
{
logger.debug("\t\tUser does not have read record permission on node, access denied. (nodeRef=" + nodeRef.toString() + ", user=" + AuthenticationUtil.getRunAsUser() + ")");
}
result = AccessDecisionVoter.ACCESS_DENIED;
}
else
{
// Get the file plan for the node
NodeRef filePlan = filePlanService.getFilePlan(nodeRef);
if (hasViewCapability(filePlan) == AccessStatus.DENIED)
{
if (logger.isDebugEnabled())
{
logger.debug("\t\tUser does not have view records capability permission on node, access denied. (filePlan=" + filePlan.toString() + ", user=" + AuthenticationUtil.getRunAsUser() + ")");
}
result = AccessDecisionVoter.ACCESS_DENIED;
}
else if (!caveatConfigComponent.hasAccess(nodeRef))
{
result = AccessDecisionVoter.ACCESS_DENIED;
}
else
{
result = AccessDecisionVoter.ACCESS_GRANTED;
}
}
// cache result
transactionCache.put(key, result);
}
return result;
}
/**
* Helper method to determine whether the current user has view capability on the file plan
*
* @param filePlan file plan
* @return {@link AccessStatus}
*/
private AccessStatus hasViewCapability(NodeRef filePlan)
{
Map<Pair<String, NodeRef>, AccessStatus> transactionCache = TransactionalResourceHelper.getMap("rm.security.hasViewCapability");
Pair<String, NodeRef> key = new Pair<String, NodeRef>(AuthenticationUtil.getRunAsUser(), filePlan);
if (transactionCache.containsKey(key))
{
return transactionCache.get(key);
}
else
{
AccessStatus result = permissionService.hasPermission(filePlan, RMPermissionModel.VIEW_RECORDS);
transactionCache.put(key, result);
return result;
}
}
@SuppressWarnings("rawtypes")

View File

@@ -33,9 +33,7 @@ import org.alfresco.module.org_alfresco_module_rm.search.RecordsManagementSearch
import org.alfresco.module.org_alfresco_module_rm.search.RecordsManagementSearchService;
import org.alfresco.module.org_alfresco_module_rm.search.SavedSearchDetailsCompatibility;
import org.alfresco.service.cmr.dictionary.DictionaryService;
import org.alfresco.service.cmr.repository.ChildAssociationRef;
import org.alfresco.service.cmr.repository.ContentData;
import org.alfresco.service.cmr.repository.ContentReader;
import org.alfresco.service.cmr.repository.ContentService;
import org.alfresco.service.cmr.repository.NodeRef;
import org.alfresco.service.cmr.repository.NodeService;
@@ -44,6 +42,7 @@ import org.alfresco.service.cmr.security.PersonService;
import org.alfresco.service.cmr.site.SiteService;
import org.alfresco.service.namespace.NamespaceService;
import org.alfresco.service.namespace.QName;
import org.alfresco.util.Pair;
import org.apache.commons.lang.ArrayUtils;
import org.apache.commons.lang.StringUtils;
import org.springframework.extensions.webscripts.Cache;
@@ -199,20 +198,20 @@ public class RMSearchGet extends DeclarativeWebScript
}
// Execute search
List<NodeRef> results = recordsManagementSearchService.search(siteId, query, searchParameters);
List<Pair<NodeRef, NodeRef>> results = recordsManagementSearchService.search(siteId, query, searchParameters);
// Reset person data cache
personDataCache = new HashMap<String, String>(57);
// Process the result items
List<Item> items = new ArrayList<Item>(results.size());
for (NodeRef nodeRef : results)
for (Pair<NodeRef, NodeRef> pair : results)
{
// FIXME: This is a workaround for DOD Recert
// TC 3-3 Create User Groups
try
{
Item item = new Item(nodeRef);
Item item = new Item(pair.getFirst(), pair.getSecond());
items.add(item);
}
catch(Exception e) {}
@@ -245,7 +244,7 @@ public class RMSearchGet extends DeclarativeWebScript
private Map<QName, Serializable> nodeProperties;
private Map<String, Serializable> properties;
public Item(NodeRef nodeRef)
public Item(NodeRef parent, NodeRef nodeRef)
{
// Set node ref
this.nodeRef = nodeRef;
@@ -265,12 +264,12 @@ public class RMSearchGet extends DeclarativeWebScript
}
// Get parent node reference
NodeRef parent = null;
ChildAssociationRef assoc = nodeService.getPrimaryParent(nodeRef);
if (assoc != null)
{
parent = assoc.getParentRef();
}
// NodeRef parent = null;
// ChildAssociationRef assoc = nodeService.getPrimaryParent(nodeRef);
// if (assoc != null)
// {
// parent = assoc.getParentRef();
// }
if (isContainer == true)
{
@@ -334,16 +333,6 @@ public class RMSearchGet extends DeclarativeWebScript
if (NamespaceService.SYSTEM_MODEL_1_0_URI.equals(qName.getNamespaceURI()) == false)
{
String prefixName = qName.getPrefixString().replace(":", "_");
Serializable value = entry.getValue();
if (value instanceof NodeRef)
{
value = value.toString();
}
else if (value instanceof ContentData)
{
ContentReader contentReader = contentService.getReader(nodeRef, qName);
value = contentReader.getContentString();
}
properties.put(prefixName, entry.getValue());
}
}

View File

@@ -21,6 +21,7 @@ package org.alfresco.module.org_alfresco_module_rm.search;
import java.util.List;
import org.alfresco.service.cmr.repository.NodeRef;
import org.alfresco.util.Pair;
/**
* Records management search service.
@@ -33,10 +34,10 @@ public interface RecordsManagementSearchService
* Execute a records management search
* @param siteId the id of the rm site to query
* @param query search query string
* @param searchParameters search parameters
* @return {@link List}<{@link NodeRef}> search results
* @param searchParameters search parameters
* @return {@link List}<{@link Pair}<{@link NodeRef}, {@link NodeRef}> search results as pairs for parent and child nodes
*/
List<NodeRef> search(String siteId, String query, RecordsManagementSearchParameters searchParameters);
List<Pair<NodeRef, NodeRef>> search(String siteId, String query, RecordsManagementSearchParameters searchParameters);
/**
* Get all the searches saved on the given records management site.

View File

@@ -30,6 +30,7 @@ import org.alfresco.repo.security.authentication.AuthenticationUtil;
import org.alfresco.repo.security.authentication.AuthenticationUtil.RunAsWork;
import org.alfresco.service.cmr.model.FileFolderService;
import org.alfresco.service.cmr.model.FileInfo;
import org.alfresco.service.cmr.repository.ChildAssociationRef;
import org.alfresco.service.cmr.repository.ContentReader;
import org.alfresco.service.cmr.repository.ContentWriter;
import org.alfresco.service.cmr.repository.NodeRef;
@@ -41,6 +42,7 @@ import org.alfresco.service.cmr.site.SiteService;
import org.alfresco.service.namespace.NamespaceService;
import org.alfresco.service.namespace.QName;
import org.alfresco.util.ISO9075;
import org.alfresco.util.Pair;
import org.alfresco.util.ParameterCheck;
import org.json.JSONArray;
import org.json.JSONException;
@@ -173,7 +175,7 @@ public class RecordsManagementSearchServiceImpl implements RecordsManagementSear
* @see org.alfresco.module.org_alfresco_module_rm.search.RecordsManagementSearchService#search(java.lang.String, java.lang.String, org.alfresco.module.org_alfresco_module_rm.search.RecordsManagementSearchParameters)
*/
@Override
public List<NodeRef> search(String siteId, String query, RecordsManagementSearchParameters rmSearchParameters)
public List<Pair<NodeRef, NodeRef>> search(String siteId, String query, RecordsManagementSearchParameters rmSearchParameters)
{
// build the full RM query
StringBuilder fullQuery = new StringBuilder(1024);
@@ -206,9 +208,16 @@ public class RecordsManagementSearchServiceImpl implements RecordsManagementSear
// execute query
ResultSet resultSet = searchService.query(searchParameters);
// process results
List<Pair<NodeRef, NodeRef>> result = new ArrayList<Pair<NodeRef, NodeRef>>(resultSet.length());
for (ChildAssociationRef childAssoc : resultSet.getChildAssocRefs())
{
result.add(new Pair<NodeRef, NodeRef>(childAssoc.getParentRef(), childAssoc.getChildRef()));
}
// return results
return resultSet.getNodeRefs();
return result;
}
/**

View File

@@ -49,4 +49,13 @@ public class ExtendedReaderDynamicAuthority extends ExtendedSecurityBaseDynamicA
{
return getExtendedSecurityService().getExtendedReaders(nodeRef);
}
/**
* @see org.alfresco.module.org_alfresco_module_rm.security.ExtendedSecurityBaseDynamicAuthority#getTransactionCacheName()
*/
@Override
protected String getTransactionCacheName()
{
return "rm.extendedreaderdynamicauthority";
}
}

View File

@@ -28,6 +28,7 @@ import org.alfresco.repo.transaction.TransactionalResourceHelper;
import org.alfresco.service.cmr.repository.NodeRef;
import org.alfresco.service.cmr.repository.NodeService;
import org.alfresco.service.cmr.security.AuthorityService;
import org.alfresco.util.Pair;
import org.springframework.beans.BeansException;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
@@ -42,9 +43,6 @@ public abstract class ExtendedSecurityBaseDynamicAuthority implements DynamicAut
RecordsManagementModel,
ApplicationContextAware
{
/** transaction cache key */
private static final String KEY_HAS_AUTHORITY_CACHE = "rm.transaction.hasAuthority";
/** Authority service */
private AuthorityService authorityService;
@@ -95,6 +93,11 @@ public abstract class ExtendedSecurityBaseDynamicAuthority implements DynamicAut
}
return nodeService;
}
/**
* @return String transaction cache name
*/
protected abstract String getTransactionCacheName();
/**
* @see org.springframework.context.ApplicationContextAware#setApplicationContext(org.springframework.context.ApplicationContext)
@@ -122,51 +125,38 @@ public abstract class ExtendedSecurityBaseDynamicAuthority implements DynamicAut
{
boolean result = false;
if (getNodeService().hasAspect(nodeRef, ASPECT_EXTENDED_SECURITY) == true)
Map<Pair<NodeRef, String>, Boolean> transactionCache = TransactionalResourceHelper.getMap(getTransactionCacheName());
Pair<NodeRef, String> key = new Pair<NodeRef, String>(nodeRef, userName);
if (transactionCache.containsKey(key))
{
Set<String> authorities = getAuthorites(nodeRef);
if (authorities != null)
{
for (String authority : authorities)
{
if ("GROUP_EVERYONE".equals(authority) == true)
{
// 'eveyone' is there so break
result = true;
break;
}
else if (authority.startsWith("GROUP_") == true)
{
Map<String, Boolean> transactionCache = TransactionalResourceHelper.getMap(KEY_HAS_AUTHORITY_CACHE);
String key = authority + "|" + userName;
if (transactionCache.containsKey(key))
{
result = transactionCache.get(key);
break;
}
else
{
Set<String> contained = getAuthorityService().getAuthoritiesForUser(userName);
if (contained.contains(authority))
{
result = true;
transactionCache.put(key, result);
break;
}
}
}
else
{
// presume we have a user
if (authority.equals(userName) == true)
{
result = true;
break;
}
}
}
}
result = transactionCache.get(key);
}
else
{
if (getNodeService().hasAspect(nodeRef, ASPECT_EXTENDED_SECURITY) == true)
{
Set<String> authorities = getAuthorites(nodeRef);
if (authorities != null)
{
// check for everyone or the user
if (authorities.contains("GROUP_EVEYONE") ||
authorities.contains(userName))
{
result = true;
}
else
{
// determine whether any of the users groups are in the extended security
Set<String> contained = getAuthorityService().getAuthoritiesForUser(userName);
authorities.retainAll(contained);
result = (authorities.size() != 0);
}
}
}
// cache result
transactionCache.put(key, result);
}
return result;

View File

@@ -48,5 +48,14 @@ public class ExtendedWriterDynamicAuthority extends ExtendedSecurityBaseDynamicA
protected Set<String> getAuthorites(NodeRef nodeRef)
{
return getExtendedSecurityService().getExtendedWriters(nodeRef);
}
}
/**
* @see org.alfresco.module.org_alfresco_module_rm.security.ExtendedSecurityBaseDynamicAuthority#getTransactionCacheName()
*/
@Override
protected String getTransactionCacheName()
{
return "rm.extendedwriterdynamicauthority";
}
}

View File

@@ -338,24 +338,35 @@ public class ServiceBaseImpl implements RecordsManagementModel, ApplicationConte
public NodeRef getFilePlan(final NodeRef nodeRef)
{
NodeRef result = null;
if (nodeRef != null)
if (nodeRef != null)
{
result = (NodeRef)getInternalNodeService().getProperty(nodeRef, PROP_ROOT_NODEREF);
if (result == null || !instanceOf(result, TYPE_FILE_PLAN))
{
if (instanceOf(nodeRef, TYPE_FILE_PLAN))
{
result = nodeRef;
}
else
{
ChildAssociationRef parentAssocRef = getInternalNodeService().getPrimaryParent(nodeRef);
if (parentAssocRef != null)
{
result = getFilePlan(parentAssocRef.getParentRef());
}
}
}
Map<NodeRef, NodeRef> transactionCache = TransactionalResourceHelper.getMap("rm.servicebase.getFilePlan");
if (transactionCache.containsKey(nodeRef))
{
result = transactionCache.get(nodeRef);
}
else
{
result = (NodeRef)getInternalNodeService().getProperty(nodeRef, PROP_ROOT_NODEREF);
if (result == null || !instanceOf(result, TYPE_FILE_PLAN))
{
if (instanceOf(nodeRef, TYPE_FILE_PLAN))
{
result = nodeRef;
}
else
{
ChildAssociationRef parentAssocRef = getInternalNodeService().getPrimaryParent(nodeRef);
if (parentAssocRef != null)
{
result = getFilePlan(parentAssocRef.getParentRef());
}
}
}
// cache result in transaction
transactionCache.put(nodeRef, result);
}
}
return result;