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;
}
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() + ")");
}
return setTransactionCache("checkRmRead", nodeRef, AccessDecisionVoter.ACCESS_DENIED);
result = AccessDecisionVoter.ACCESS_DENIED;
}
else
{
// Get the file plan for the node
NodeRef filePlan = filePlanService.getFilePlan(nodeRef);
if (permissionService.hasPermission(filePlan, RMPermissionModel.VIEW_RECORDS) == AccessStatus.DENIED)
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() + ")");
}
return setTransactionCache("checkRmRead", nodeRef, AccessDecisionVoter.ACCESS_DENIED);
result = AccessDecisionVoter.ACCESS_DENIED;
}
if (caveatConfigComponent.hasAccess(nodeRef))
else if (!caveatConfigComponent.hasAccess(nodeRef))
{
return setTransactionCache("checkRmRead", nodeRef, AccessDecisionVoter.ACCESS_GRANTED);
result = AccessDecisionVoter.ACCESS_DENIED;
}
else
{
return setTransactionCache("checkRmRead", nodeRef, AccessDecisionVoter.ACCESS_DENIED);
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.
@@ -34,9 +35,9 @@ public interface RecordsManagementSearchService
* @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
* @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);
@@ -207,8 +209,15 @@ 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;
@@ -96,6 +94,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;
Map<Pair<NodeRef, String>, Boolean> transactionCache = TransactionalResourceHelper.getMap(getTransactionCacheName());
Pair<NodeRef, String> key = new Pair<NodeRef, String>(nodeRef, userName);
if (transactionCache.containsKey(key))
{
result = transactionCache.get(key);
}
else
{
if (getNodeService().hasAspect(nodeRef, ASPECT_EXTENDED_SECURITY) == true)
{
Set<String> authorities = getAuthorites(nodeRef);
if (authorities != null)
{
for (String authority : authorities)
// check for everyone or the user
if (authorities.contains("GROUP_EVEYONE") ||
authorities.contains(userName))
{
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
{
// determine whether any of the users groups are in the extended security
Set<String> contained = getAuthorityService().getAuthoritiesForUser(userName);
if (contained.contains(authority))
{
result = true;
authorities.retainAll(contained);
result = (authorities.size() != 0);
}
}
}
// cache result
transactionCache.put(key, result);
break;
}
}
}
else
{
// presume we have a user
if (authority.equals(userName) == true)
{
result = true;
break;
}
}
}
}
}
return result;

View File

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

View File

@@ -339,6 +339,13 @@ public class ServiceBaseImpl implements RecordsManagementModel, ApplicationConte
{
NodeRef result = null;
if (nodeRef != null)
{
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))
@@ -356,6 +363,10 @@ public class ServiceBaseImpl implements RecordsManagementModel, ApplicationConte
}
}
}
// cache result in transaction
transactionCache.put(nodeRef, result);
}
}
return result;

View File

@@ -294,10 +294,12 @@ public class RecordServiceImplTest extends BaseRMTestCase
// create record from document
doTestInTransaction(new Test<Void>()
{
private NodeRef originalLocation;
@Override
public Void run()
{
NodeRef originalLocation = nodeService.getPrimaryParent(dmDocument).getParentRef();
originalLocation = nodeService.getPrimaryParent(dmDocument).getParentRef();
assertFalse(recordService.isRecord(dmDocument));
assertFalse(extendedSecurityService.hasExtendedSecurity(dmDocument));
@@ -319,6 +321,11 @@ public class RecordServiceImplTest extends BaseRMTestCase
recordService.createRecord(filePlan, dmDocument);
return null;
}
public void test(Void result)
{
checkPermissions(READ_RECORDS, AccessStatus.ALLOWED, // file
// plan
AccessStatus.ALLOWED, // unfiled container
@@ -367,8 +374,6 @@ public class RecordServiceImplTest extends BaseRMTestCase
Capability updateProperties = capabilityService.getCapability("UpdateProperties");
assertEquals(AccessStatus.ALLOWED, updateProperties.hasPermission(dmDocument));
return null;
}
}, dmCollaborator);

View File

@@ -26,6 +26,7 @@ import org.alfresco.module.org_alfresco_module_rm.test.util.BaseRMTestCase;
import org.alfresco.repo.security.authentication.AuthenticationUtil;
import org.alfresco.service.cmr.repository.NodeRef;
import org.alfresco.service.cmr.security.MutableAuthenticationService;
import org.alfresco.util.Pair;
import org.alfresco.util.TestWithUserUtils;
/**
@@ -151,7 +152,7 @@ public class RecordsManagementSearchServiceImplTest extends BaseRMTestCase
String query = "keywords:\"elephant\"";
RecordsManagementSearchParameters params = new RecordsManagementSearchParameters();
params.setIncludeUndeclaredRecords(true);
List<NodeRef> results = rmSearchService.search(siteId, query, params);
List<Pair<NodeRef, NodeRef>> results = rmSearchService.search(siteId, query, params);
assertNotNull(results);
assertEquals(2, results.size());