mirror of
https://github.com/Alfresco/alfresco-community-repo.git
synced 2025-08-07 17:49:17 +00:00
RM-1461: CLONE - RM slower then standard repo/sites when rendering document details when folder contains 15k documents
* general browse experience better * viewing record category list (with one folder) down from seconds to almost immediately * viewing of record details page down from 20 plus seconds to under a second * although view of 15k records is still slow to render (around 10/15 seconds) this is still much better then previously and over 80% of that time is spent in the repo getting the children .. could look at getting a reduced result set? * cached 'hasHeldChildren' state on record folders .. this was a significant overhead previously and is now a simple property lookup, maintained by behaviours * unit tests for above to ensure nothing is broken * TODO more caching of frequently asked for state, optimisation of more node service intensive capability conditions git-svn-id: https://svn.alfresco.com/repos/alfresco-enterprise/modules/recordsmanagement/HEAD@74932 c4b6b30b-aa2e-2d43-bbcb-ca4b014f7261
This commit is contained in:
@@ -21,8 +21,10 @@ package org.alfresco.module.org_alfresco_module_rm.freeze;
|
||||
import java.io.Serializable;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Date;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
import org.alfresco.model.ContentModel;
|
||||
@@ -30,9 +32,14 @@ import org.alfresco.module.org_alfresco_module_rm.fileplan.FilePlanService;
|
||||
import org.alfresco.module.org_alfresco_module_rm.hold.HoldService;
|
||||
import org.alfresco.module.org_alfresco_module_rm.model.RecordsManagementModel;
|
||||
import org.alfresco.module.org_alfresco_module_rm.util.ServiceBaseImpl;
|
||||
import org.alfresco.repo.security.authentication.AuthenticationUtil;
|
||||
import org.alfresco.repo.security.authentication.AuthenticationUtil.RunAsWork;
|
||||
import org.alfresco.repo.transaction.RetryingTransactionHelper.RetryingTransactionCallback;
|
||||
import org.alfresco.service.cmr.repository.ChildAssociationRef;
|
||||
import org.alfresco.service.cmr.repository.NodeRef;
|
||||
import org.alfresco.service.namespace.QName;
|
||||
import org.alfresco.service.namespace.RegexQNamePattern;
|
||||
import org.alfresco.service.transaction.TransactionService;
|
||||
import org.alfresco.util.ParameterCheck;
|
||||
import org.apache.commons.lang.StringUtils;
|
||||
import org.springframework.extensions.surf.util.I18NUtil;
|
||||
@@ -104,21 +111,69 @@ public class FreezeServiceImpl extends ServiceBaseImpl
|
||||
* @see org.alfresco.module.org_alfresco_module_rm.freeze.FreezeService#hasFrozenChildren(org.alfresco.service.cmr.repository.NodeRef)
|
||||
*/
|
||||
@Override
|
||||
public boolean hasFrozenChildren(NodeRef nodeRef)
|
||||
public boolean hasFrozenChildren(final NodeRef nodeRef)
|
||||
{
|
||||
ParameterCheck.mandatory("nodeRef", nodeRef);
|
||||
|
||||
List<ChildAssociationRef> childAssocs = nodeService.getChildAssocs(nodeRef, ContentModel.ASSOC_CONTAINS,
|
||||
RegexQNamePattern.MATCH_ALL);
|
||||
if (childAssocs != null && !childAssocs.isEmpty())
|
||||
{
|
||||
for (ChildAssociationRef childAssociationRef : childAssocs)
|
||||
boolean result = false;
|
||||
|
||||
// check that we are dealing with a record folder
|
||||
if (isRecordFolder(nodeRef))
|
||||
{
|
||||
int heldCount = 0;
|
||||
|
||||
if (nodeService.hasAspect(nodeRef, ASPECT_HELD_CHILDREN))
|
||||
{
|
||||
if (isFrozen(childAssociationRef.getChildRef())) { return true; }
|
||||
heldCount = (Integer)getInternalNodeService().getProperty(nodeRef, PROP_HELD_CHILDREN_COUNT);
|
||||
}
|
||||
else
|
||||
{
|
||||
final TransactionService transactionService = (TransactionService)applicationContext.getBean("transactionService");
|
||||
|
||||
heldCount = AuthenticationUtil.runAsSystem(new RunAsWork<Integer>()
|
||||
{
|
||||
@Override
|
||||
public Integer doWork()
|
||||
{
|
||||
return transactionService.getRetryingTransactionHelper().doInTransaction(new RetryingTransactionCallback<Integer>()
|
||||
{
|
||||
public Integer execute() throws Throwable
|
||||
{
|
||||
int heldCount = 0;
|
||||
|
||||
// NOTE: this process remains to 'patch' older systems to improve performance next time around
|
||||
List<ChildAssociationRef> childAssocs = getInternalNodeService().getChildAssocs(nodeRef, ContentModel.ASSOC_CONTAINS,
|
||||
RegexQNamePattern.MATCH_ALL);
|
||||
if (childAssocs != null && !childAssocs.isEmpty())
|
||||
{
|
||||
for (ChildAssociationRef childAssociationRef : childAssocs)
|
||||
{
|
||||
NodeRef record = childAssociationRef.getChildRef();
|
||||
if (childAssociationRef.isPrimary() && isRecord(record) && isFrozen(record))
|
||||
{
|
||||
heldCount ++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// add aspect and set count
|
||||
Map<QName, Serializable> props = new HashMap<QName, Serializable>(1);
|
||||
props.put(PROP_HELD_CHILDREN_COUNT, heldCount);
|
||||
getInternalNodeService().addAspect(nodeRef, ASPECT_HELD_CHILDREN, props);
|
||||
|
||||
return heldCount;
|
||||
}
|
||||
},
|
||||
false, true);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// true if more than one child held
|
||||
result = (heldCount > 0);
|
||||
}
|
||||
|
||||
return false;
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@@ -286,6 +286,23 @@ public class JSONConversionComponent extends org.alfresco.repo.jscript.app.JSONC
|
||||
rmNodeValues.put("actions", jsonActions);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @see org.alfresco.repo.jscript.app.JSONConversionComponent#permissionsToJSON(org.alfresco.service.cmr.repository.NodeRef)
|
||||
*/
|
||||
protected JSONObject permissionsToJSON(final NodeRef nodeRef)
|
||||
{
|
||||
JSONObject permissionsJSON = null;
|
||||
if (!filePlanService.isFilePlanComponent(nodeRef))
|
||||
{
|
||||
permissionsJSON = super.permissionsToJSON(nodeRef);
|
||||
}
|
||||
else
|
||||
{
|
||||
permissionsJSON = new JSONObject();
|
||||
}
|
||||
return permissionsJSON;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the rm 'type' used as a UI convenience and compatibility flag.
|
||||
|
@@ -258,6 +258,11 @@ public interface RecordsManagementModel extends RecordsManagementCustomModel
|
||||
QName PROP_RECORD_REJECTION_DATE = QName.createQName(RM_URI, "recordRejectionDate");
|
||||
QName PROP_RECORD_REJECTION_REASON = QName.createQName(RM_URI, "recordRejectionReason");
|
||||
|
||||
// Held children aspect
|
||||
// @since 2.2
|
||||
QName ASPECT_HELD_CHILDREN = QName.createQName(RM_URI, "heldChildren");
|
||||
QName PROP_HELD_CHILDREN_COUNT = QName.createQName(RM_URI, "heldChildrenCount");
|
||||
|
||||
// Countable aspect
|
||||
QName ASPECT_COUNTABLE = QName.createQName(RM_URI, "countable");
|
||||
QName PROP_COUNT = QName.createQName(RM_URI, "count");
|
||||
|
@@ -33,6 +33,7 @@ import org.alfresco.repo.security.authentication.AuthenticationUtil.RunAsWork;
|
||||
import org.alfresco.repo.security.permissions.AccessDeniedException;
|
||||
import org.alfresco.service.cmr.repository.ChildAssociationRef;
|
||||
import org.alfresco.service.cmr.repository.NodeRef;
|
||||
import org.alfresco.service.namespace.QName;
|
||||
|
||||
/**
|
||||
* rma:frozen behaviour bean
|
||||
@@ -45,7 +46,9 @@ import org.alfresco.service.cmr.repository.NodeRef;
|
||||
defaultType = "rma:frozen"
|
||||
)
|
||||
public class FrozenAspect extends BaseBehaviourBean
|
||||
implements NodeServicePolicies.BeforeDeleteNodePolicy
|
||||
implements NodeServicePolicies.BeforeDeleteNodePolicy,
|
||||
NodeServicePolicies.OnAddAspectPolicy,
|
||||
NodeServicePolicies.OnRemoveAspectPolicy
|
||||
{
|
||||
/** file plan service */
|
||||
protected FilePlanService filePlanService;
|
||||
@@ -129,5 +132,74 @@ public class FrozenAspect extends BaseBehaviourBean
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
@Behaviour
|
||||
(
|
||||
kind = BehaviourKind.CLASS,
|
||||
notificationFrequency = NotificationFrequency.TRANSACTION_COMMIT
|
||||
)
|
||||
public void onAddAspect(final NodeRef record, final QName aspectTypeQName)
|
||||
{
|
||||
AuthenticationUtil.runAsSystem(new RunAsWork<Void>()
|
||||
{
|
||||
@Override
|
||||
public Void doWork()
|
||||
{
|
||||
if (nodeService.exists(record) &&
|
||||
isRecord(record))
|
||||
{
|
||||
// get the owning record folder
|
||||
NodeRef recordFolder = nodeService.getPrimaryParent(record).getParentRef();
|
||||
// check that the aspect has been added
|
||||
if (nodeService.hasAspect(recordFolder, ASPECT_HELD_CHILDREN))
|
||||
{
|
||||
// increment current count
|
||||
int currentCount = (Integer)nodeService.getProperty(recordFolder, PROP_HELD_CHILDREN_COUNT);
|
||||
currentCount = currentCount + 1;
|
||||
nodeService.setProperty(recordFolder, PROP_HELD_CHILDREN_COUNT, currentCount);
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
@Behaviour
|
||||
(
|
||||
kind = BehaviourKind.CLASS,
|
||||
notificationFrequency = NotificationFrequency.TRANSACTION_COMMIT
|
||||
)
|
||||
public void onRemoveAspect(final NodeRef record, QName aspectTypeQName)
|
||||
{
|
||||
AuthenticationUtil.runAsSystem(new RunAsWork<Void>()
|
||||
{
|
||||
@Override
|
||||
public Void doWork()
|
||||
{
|
||||
if (nodeService.exists(record) &&
|
||||
isRecord(record))
|
||||
{
|
||||
// get the owning record folder
|
||||
NodeRef recordFolder = nodeService.getPrimaryParent(record).getParentRef();
|
||||
|
||||
// check that the aspect has been added
|
||||
if (nodeService.hasAspect(recordFolder, ASPECT_HELD_CHILDREN))
|
||||
{
|
||||
// decrement current count
|
||||
int currentCount = (Integer)nodeService.getProperty(recordFolder, PROP_HELD_CHILDREN_COUNT);
|
||||
if (currentCount > 0)
|
||||
{
|
||||
currentCount = currentCount - 1;
|
||||
nodeService.setProperty(recordFolder, PROP_HELD_CHILDREN_COUNT, currentCount);
|
||||
}
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
@@ -18,6 +18,8 @@
|
||||
*/
|
||||
package org.alfresco.module.org_alfresco_module_rm.util;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
import org.alfresco.module.org_alfresco_module_rm.fileplan.FilePlanComponentKind;
|
||||
@@ -86,7 +88,7 @@ public class ServiceBaseImpl implements RecordsManagementModel, ApplicationConte
|
||||
* <p>
|
||||
* Used for performance reasons.
|
||||
*/
|
||||
private NodeService getInternalNodeService()
|
||||
protected NodeService getInternalNodeService()
|
||||
{
|
||||
if (internalNodeService == null)
|
||||
{
|
||||
@@ -371,6 +373,8 @@ public class ServiceBaseImpl implements RecordsManagementModel, ApplicationConte
|
||||
QName className = getInternalNodeService().getType(nodeRef);
|
||||
return instanceOf(className, ofClassName);
|
||||
}
|
||||
|
||||
private static Map<String, Boolean> instanceOfCache = new HashMap<String, Boolean>();
|
||||
|
||||
/**
|
||||
* Utility method to quickly determine whether one class is equal to or sub of another.
|
||||
@@ -383,12 +387,25 @@ public class ServiceBaseImpl implements RecordsManagementModel, ApplicationConte
|
||||
{
|
||||
ParameterCheck.mandatory("className", className);
|
||||
ParameterCheck.mandatory("ofClassName", ofClassName);
|
||||
|
||||
boolean result = false;
|
||||
if (ofClassName.equals(className) ||
|
||||
dictionaryService.isSubClass(className, ofClassName))
|
||||
|
||||
String key = className.toString() + "|" + ofClassName.toString();
|
||||
if (instanceOfCache.containsKey(key))
|
||||
{
|
||||
result = true;
|
||||
result = instanceOfCache.get(key);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (ofClassName.equals(className) ||
|
||||
dictionaryService.isSubClass(className, ofClassName))
|
||||
{
|
||||
result = true;
|
||||
}
|
||||
|
||||
instanceOfCache.put(key, result);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
|
Reference in New Issue
Block a user