From 191b50faedd5ae0e0f956e98ae5df3c361fbecc7 Mon Sep 17 00:00:00 2001 From: Tuna Aksoy Date: Fri, 9 Nov 2012 22:02:42 +0000 Subject: [PATCH] Implementing FreezeService git-svn-id: https://svn.alfresco.com/repos/alfresco-enterprise/modules/recordsmanagement/HEAD@43521 c4b6b30b-aa2e-2d43-bbcb-ca4b014f7261 --- .../messages/action-service.properties | 3 +- .../rm-action-context.xml | 18 +- .../rm-service-context.xml | 1 + .../action/RMActionExecuterAbstractBase.java | 5 +- .../action/impl/EditHoldReasonAction.java | 162 +++-- .../action/impl/FileAction.java | 3 +- .../action/impl/FreezeAction.java | 265 +++---- .../action/impl/RelinquishHoldAction.java | 216 ++---- .../action/impl/TransferAction.java | 3 +- .../action/impl/UnfreezeAction.java | 154 +--- .../freeze/FreezeService.java | 33 +- .../freeze/FreezeServiceImpl.java | 676 +++++++++++++++--- 12 files changed, 899 insertions(+), 640 deletions(-) diff --git a/rm-server/config/alfresco/module/org_alfresco_module_rm/messages/action-service.properties b/rm-server/config/alfresco/module/org_alfresco_module_rm/messages/action-service.properties index c55856199e..ab66f43877 100644 --- a/rm-server/config/alfresco/module/org_alfresco_module_rm/messages/action-service.properties +++ b/rm-server/config/alfresco/module/org_alfresco_module_rm/messages/action-service.properties @@ -34,4 +34,5 @@ rm.action.node-already-transfer=Node is already being transfered. rm.action.node-not-transfer=Node is not a transfer object. rm.action.undo-not-last=Can not undo cut off, because last disposition action was not cut off. rm.action.records_only_undeclared=Only records can be undeclared. -rm.action.event-not-undone=The event {0} can not be undone, because it is not defined on the disposition lifecycle. \ No newline at end of file +rm.action.event-not-undone=The event {0} can not be undone, because it is not defined on the disposition lifecycle. +rm.action.empty-set-of-noderefs=The set of nodeRefs is empty. \ No newline at end of file diff --git a/rm-server/config/alfresco/module/org_alfresco_module_rm/rm-action-context.xml b/rm-server/config/alfresco/module/org_alfresco_module_rm/rm-action-context.xml index 7ff02d14d0..2731aefaaa 100644 --- a/rm-server/config/alfresco/module/org_alfresco_module_rm/rm-action-context.xml +++ b/rm-server/config/alfresco/module/org_alfresco_module_rm/rm-action-context.xml @@ -398,8 +398,9 @@ - - + + + @@ -425,8 +426,9 @@ - - + + + @@ -442,8 +444,9 @@ - - + + + @@ -469,7 +472,8 @@ - + + 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 eb95219e78..8d6aa004ec 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 @@ -722,6 +722,7 @@ + diff --git a/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/action/RMActionExecuterAbstractBase.java b/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/action/RMActionExecuterAbstractBase.java index 6623bf9dc0..2ce37fad33 100644 --- a/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/action/RMActionExecuterAbstractBase.java +++ b/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/action/RMActionExecuterAbstractBase.java @@ -365,8 +365,11 @@ public abstract class RMActionExecuterAbstractBase extends ActionExecuterAbstra * @param s String to pad with leading zero '0' characters * @param len Length to pad to * - * @return padded string or the original if already at >=len characters + * @return padded string or the original if already at >=len characters + * + * @deprecated As of 2.1, replaced by {@link org.apache.commons.lang.StringUtils.leftPad} */ + @Deprecated protected String padString(String s, int len) { String result = s; diff --git a/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/action/impl/EditHoldReasonAction.java b/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/action/impl/EditHoldReasonAction.java index f840f8e42f..3308e06ff0 100644 --- a/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/action/impl/EditHoldReasonAction.java +++ b/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/action/impl/EditHoldReasonAction.java @@ -25,9 +25,11 @@ import java.util.Set; import org.alfresco.error.AlfrescoRuntimeException; import org.alfresco.module.org_alfresco_module_rm.action.RMActionExecuterAbstractBase; +import org.alfresco.module.org_alfresco_module_rm.freeze.FreezeService; import org.alfresco.service.cmr.action.Action; import org.alfresco.service.cmr.repository.NodeRef; import org.alfresco.service.namespace.QName; +import org.apache.commons.lang.StringUtils; import org.springframework.extensions.surf.util.I18NUtil; /** @@ -37,83 +39,93 @@ import org.springframework.extensions.surf.util.I18NUtil; */ public class EditHoldReasonAction extends RMActionExecuterAbstractBase { - private static final String MSG_HOLD_EDIT_REASON_NONE = "rm.action.hold-edit-reason-none"; - private static final String MSG_HOLD_EDIT_TYPE = "rm.action.hold-edit-type"; - - /** Parameter names */ - public static final String PARAM_REASON = "reason"; - - /** - * @see org.alfresco.repo.action.executer.ActionExecuterAbstractBase#executeImpl(org.alfresco.service.cmr.action.Action, org.alfresco.service.cmr.repository.NodeRef) - */ - @Override - protected void executeImpl(Action action, NodeRef actionedUponNodeRef) - { - QName nodeType = this.nodeService.getType(actionedUponNodeRef); - if (this.dictionaryService.isSubClass(nodeType, TYPE_HOLD) == true) - { - // Get the property values - String reason = (String)action.getParameterValue(PARAM_REASON); - if (reason == null || reason.length() == 0) - { - throw new AlfrescoRuntimeException(I18NUtil.getMessage(MSG_HOLD_EDIT_REASON_NONE)); - } - - // Set the hold reason - nodeService.setProperty(actionedUponNodeRef, PROP_HOLD_REASON, reason); + private static final String MSG_HOLD_EDIT_REASON_NONE = "rm.action.hold-edit-reason-none"; + private static final String MSG_HOLD_EDIT_TYPE = "rm.action.hold-edit-type"; - } - else - { - throw new AlfrescoRuntimeException(I18NUtil.getMessage(MSG_HOLD_EDIT_TYPE, TYPE_HOLD.toString(), actionedUponNodeRef.toString())); - } - } - - /** - * @see org.alfresco.module.org_alfresco_module_rm.action.RMActionExecuterAbstractBase#getProtectedAspects() - */ - @Override - public Set getProtectedAspects() - { - HashSet qnames = new HashSet(); - qnames.add(ASPECT_FROZEN); - return qnames; - } + /** Parameter names */ + public static final String PARAM_REASON = "reason"; - /** - * @see org.alfresco.module.org_alfresco_module_rm.action.RMActionExecuterAbstractBase#getProtectedProperties() - */ - @Override - public Set getProtectedProperties() - { - HashSet qnames = new HashSet(); - qnames.add(PROP_HOLD_REASON); - return qnames; - } + /** Freeze Service */ + private FreezeService freezeService; - /** - * @see org.alfresco.module.org_alfresco_module_rm.action.RMActionExecuterAbstractBase#isExecutableImpl(org.alfresco.service.cmr.repository.NodeRef, java.util.Map, boolean) - */ - @Override - protected boolean isExecutableImpl(NodeRef filePlanComponent, Map parameters, boolean throwException) - { - QName nodeType = this.nodeService.getType(filePlanComponent); - if (this.dictionaryService.isSubClass(nodeType, TYPE_HOLD) == true) - { - return true; - } - else - { - if(throwException) - { - throw new AlfrescoRuntimeException(I18NUtil.getMessage(MSG_HOLD_EDIT_TYPE, TYPE_HOLD.toString(), filePlanComponent.toString())); - } - else - { - return false; - } - } - } + /** + * Set freeze service + * + * @param freezeService freeze service + */ + public void setFreezeService(FreezeService freezeService) + { + this.freezeService = freezeService; + } + + /** + * @see org.alfresco.repo.action.executer.ActionExecuterAbstractBase#executeImpl(org.alfresco.service.cmr.action.Action, org.alfresco.service.cmr.repository.NodeRef) + */ + @Override + protected void executeImpl(Action action, NodeRef actionedUponNodeRef) + { + if (freezeService.isHold(actionedUponNodeRef)) + { + // Get the property values + String reason = (String) action.getParameterValue(PARAM_REASON); + if (StringUtils.isBlank(reason)) + { + throw new AlfrescoRuntimeException(I18NUtil.getMessage(MSG_HOLD_EDIT_REASON_NONE)); + } + + // Update hold reason + freezeService.updateReason(actionedUponNodeRef, reason); + } + else + { + throw new AlfrescoRuntimeException(I18NUtil.getMessage(MSG_HOLD_EDIT_TYPE, TYPE_HOLD.toString(), actionedUponNodeRef.toString())); + } + } + + /** + * @see org.alfresco.module.org_alfresco_module_rm.action.RMActionExecuterAbstractBase#getProtectedAspects() + */ + @Override + public Set getProtectedAspects() + { + HashSet qnames = new HashSet(); + qnames.add(ASPECT_FROZEN); + return qnames; + } + + /** + * @see org.alfresco.module.org_alfresco_module_rm.action.RMActionExecuterAbstractBase#getProtectedProperties() + */ + @Override + public Set getProtectedProperties() + { + HashSet qnames = new HashSet(); + qnames.add(PROP_HOLD_REASON); + return qnames; + } + + /** + * @see org.alfresco.module.org_alfresco_module_rm.action.RMActionExecuterAbstractBase#isExecutableImpl(org.alfresco.service.cmr.repository.NodeRef, java.util.Map, boolean) + */ + @Override + protected boolean isExecutableImpl(NodeRef filePlanComponent, Map parameters, boolean throwException) + { + QName nodeType = this.nodeService.getType(filePlanComponent); + if (this.dictionaryService.isSubClass(nodeType, TYPE_HOLD) == true) + { + return true; + } + else + { + if(throwException) + { + throw new AlfrescoRuntimeException(I18NUtil.getMessage(MSG_HOLD_EDIT_TYPE, TYPE_HOLD.toString(), filePlanComponent.toString())); + } + else + { + return false; + } + } + } - } \ No newline at end of file diff --git a/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/action/impl/FileAction.java b/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/action/impl/FileAction.java index 5a0e0cc058..754eb1fe2a 100644 --- a/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/action/impl/FileAction.java +++ b/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/action/impl/FileAction.java @@ -38,6 +38,7 @@ import org.alfresco.service.cmr.dictionary.DataTypeDefinition; import org.alfresco.service.cmr.repository.NodeRef; import org.alfresco.service.namespace.NamespaceService; import org.alfresco.service.namespace.QName; +import org.apache.commons.lang.StringUtils; /** * Files a record into a particular record folder @@ -79,7 +80,7 @@ public class FileAction extends RMActionExecuterAbstractBase // Calculate the filed date and record identifier String year = Integer.toString(fileCalendar.get(Calendar.YEAR)); QName nodeDbid = QName.createQName(NamespaceService.SYSTEM_MODEL_1_0_URI, "node-dbid"); - String recordId = year + "-" + padString(recordProperties.get(nodeDbid).toString(), 10); + String recordId = year + "-" + StringUtils.leftPad(recordProperties.get(nodeDbid).toString(), 10, "0"); recordProperties.put(RecordsManagementModel.PROP_IDENTIFIER, recordId); } diff --git a/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/action/impl/FreezeAction.java b/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/action/impl/FreezeAction.java index 2ceb3da826..0455bdea9d 100644 --- a/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/action/impl/FreezeAction.java +++ b/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/action/impl/FreezeAction.java @@ -19,24 +19,16 @@ package org.alfresco.module.org_alfresco_module_rm.action.impl; import java.io.Serializable; -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.error.AlfrescoRuntimeException; -import org.alfresco.model.ContentModel; import org.alfresco.module.org_alfresco_module_rm.action.RMActionExecuterAbstractBase; -import org.alfresco.repo.security.authentication.AuthenticationUtil; -import org.alfresco.repo.transaction.AlfrescoTransactionSupport; +import org.alfresco.module.org_alfresco_module_rm.freeze.FreezeService; import org.alfresco.service.cmr.action.Action; import org.alfresco.service.cmr.repository.NodeRef; -import org.alfresco.service.namespace.NamespaceService; import org.alfresco.service.namespace.QName; -import org.apache.commons.logging.Log; -import org.apache.commons.logging.LogFactory; import org.springframework.extensions.surf.util.I18NUtil; /** @@ -46,180 +38,95 @@ import org.springframework.extensions.surf.util.I18NUtil; */ public class FreezeAction extends RMActionExecuterAbstractBase { - private static final String MSG_FREEZE_NO_REASON = "rm.action.freeze-no-reason"; - private static final String MSG_FREEZE_ONLY_RECORDS_FOLDERS = "rm.action.freeze-only-records-folders"; - - /** Logger */ - private static Log logger = LogFactory.getLog(FreezeAction.class); + private static final String MSG_FREEZE_NO_REASON = "rm.action.freeze-no-reason"; + private static final String MSG_FREEZE_ONLY_RECORDS_FOLDERS = "rm.action.freeze-only-records-folders"; - /** Parameter names */ - public static final String PARAM_REASON = "reason"; - - /** Hold node reference key */ - private static final String KEY_HOLD_NODEREF = "holdNodeRef"; - - /** - * @see org.alfresco.repo.action.executer.ActionExecuterAbstractBase#executeImpl(org.alfresco.service.cmr.action.Action, org.alfresco.service.cmr.repository.NodeRef) - */ - @Override - protected void executeImpl(Action action, NodeRef actionedUponNodeRef) - { - final boolean isRecord = recordsManagementService.isRecord(actionedUponNodeRef); - final boolean isFolder = this.recordsManagementService.isRecordFolder(actionedUponNodeRef); - - if (isRecord || isFolder) - { - // Get the property values - String reason = (String)action.getParameterValue(PARAM_REASON); + /** Parameter names */ + public static final String PARAM_REASON = "reason"; + + /** Freeze Service */ + private FreezeService freezeService; + + /** + * Set freeze service + * + * @param freezeService freeze service + */ + public void setFreezeService(FreezeService freezeService) + { + this.freezeService = freezeService; + } + + /** + * @see org.alfresco.repo.action.executer.ActionExecuterAbstractBase#executeImpl(org.alfresco.service.cmr.action.Action, org.alfresco.service.cmr.repository.NodeRef) + */ + @Override + protected void executeImpl(Action action, NodeRef actionedUponNodeRef) + { + freezeService.freeze((String) action.getParameterValue(PARAM_REASON), actionedUponNodeRef); + } + + /** + * @see org.alfresco.module.org_alfresco_module_rm.action.RMActionExecuterAbstractBase#getProtectedAspects() + */ + @Override + public Set getProtectedAspects() + { + HashSet qnames = new HashSet(); + qnames.add(ASPECT_FROZEN); + return qnames; + } + + /** + * @see org.alfresco.module.org_alfresco_module_rm.action.RMActionExecuterAbstractBase#getProtectedProperties() + */ + @Override + public Set getProtectedProperties() + { + HashSet qnames = new HashSet(); + qnames.add(PROP_HOLD_REASON); + //TODO Add prop frozen at/by? + return qnames; + } + + /** + * @see org.alfresco.module.org_alfresco_module_rm.action.RMActionExecuterAbstractBase#isExecutableImpl(org.alfresco.service.cmr.repository.NodeRef, java.util.Map, boolean) + */ + @Override + protected boolean isExecutableImpl(NodeRef filePlanComponent, Map parameters, boolean throwException) + { + if (this.recordsManagementService.isRecord(filePlanComponent) == true || + this.recordsManagementService.isRecordFolder(filePlanComponent) == true) + { + // Get the property values + if(parameters != null) + { + String reason = (String)parameters.get(PARAM_REASON); if (reason == null || reason.length() == 0) { - throw new AlfrescoRuntimeException(I18NUtil.getMessage(MSG_FREEZE_NO_REASON)); + if(throwException) + { + throw new AlfrescoRuntimeException(I18NUtil.getMessage(MSG_FREEZE_NO_REASON)); + } + else + { + return false; + } } - - if (logger.isDebugEnabled()) - { - StringBuilder msg = new StringBuilder(); - msg.append("Freezing node ").append(actionedUponNodeRef); - if (isFolder) - { - msg.append(" (folder)"); - } - msg.append(" with reason '").append(reason).append("'"); - logger.debug(msg.toString()); - } - - // Get the root rm node - NodeRef root = this.recordsManagementService.getFilePlan(actionedUponNodeRef); - - // Get the hold object - NodeRef holdNodeRef = (NodeRef)AlfrescoTransactionSupport.getResource(KEY_HOLD_NODEREF); - - if (holdNodeRef == null) - { - // Calculate a transfer name - QName nodeDbid = QName.createQName(NamespaceService.SYSTEM_MODEL_1_0_URI, "node-dbid"); - Long dbId = (Long)this.nodeService.getProperty(actionedUponNodeRef, nodeDbid); - String transferName = padString(dbId.toString(), 10); - - // Create the hold object - Map holdProps = new HashMap(2); - holdProps.put(ContentModel.PROP_NAME, transferName); - holdProps.put(PROP_HOLD_REASON, reason); - final QName transferQName = QName.createQName(RM_URI, transferName); - holdNodeRef = this.nodeService.createNode(root, - ASSOC_HOLDS, - transferQName, - TYPE_HOLD, - holdProps).getChildRef(); - - if (logger.isDebugEnabled()) - { - StringBuilder msg = new StringBuilder(); - msg.append("Created hold object ").append(holdNodeRef) - .append(" with transfer name ").append(transferQName); - logger.debug(msg.toString()); - } - - // Bind the hold node reference to the transaction - AlfrescoTransactionSupport.bindResource(KEY_HOLD_NODEREF, holdNodeRef); - } - - // Link the record to the hold - this.nodeService.addChild( holdNodeRef, - actionedUponNodeRef, - ASSOC_FROZEN_RECORDS, - ASSOC_FROZEN_RECORDS); - - // Apply the freeze aspect - Map props = new HashMap(2); - props.put(PROP_FROZEN_AT, new Date()); - props.put(PROP_FROZEN_BY, AuthenticationUtil.getFullyAuthenticatedUser()); - this.nodeService.addAspect(actionedUponNodeRef, ASPECT_FROZEN, props); - - if (logger.isDebugEnabled()) - { - StringBuilder msg = new StringBuilder(); - msg.append("Frozen aspect applied to ").append(actionedUponNodeRef); - logger.debug(msg.toString()); - } - - - // Mark all the folders contents as frozen - if (isFolder) - { - List records = this.recordsManagementService.getRecords(actionedUponNodeRef); - for (NodeRef record : records) - { - this.nodeService.addAspect(record, ASPECT_FROZEN, props); - - if (logger.isDebugEnabled()) - { - StringBuilder msg = new StringBuilder(); - msg.append("Frozen aspect applied to ").append(record); - logger.debug(msg.toString()); - } - } - } - } - else - { + } + return true; + } + else + { + if(throwException) + { throw new AlfrescoRuntimeException(I18NUtil.getMessage(MSG_FREEZE_ONLY_RECORDS_FOLDERS)); - } - } - - @Override - public Set getProtectedAspects() - { - HashSet qnames = new HashSet(); - qnames.add(ASPECT_FROZEN); - return qnames; - } + } + else + { + return false; + } + } + } - @Override - public Set getProtectedProperties() - { - HashSet qnames = new HashSet(); - qnames.add(PROP_HOLD_REASON); - //TODO Add prop frozen at/by? - return qnames; - } - - @Override - protected boolean isExecutableImpl(NodeRef filePlanComponent, Map parameters, boolean throwException) - { - if (this.recordsManagementService.isRecord(filePlanComponent) == true || - this.recordsManagementService.isRecordFolder(filePlanComponent) == true) - { - // Get the property values - if(parameters != null) - { - String reason = (String)parameters.get(PARAM_REASON); - if (reason == null || reason.length() == 0) - { - if(throwException) - { - throw new AlfrescoRuntimeException(I18NUtil.getMessage(MSG_FREEZE_NO_REASON)); - } - else - { - return false; - } - } - } - return true; - } - else - { - if(throwException) - { - throw new AlfrescoRuntimeException(I18NUtil.getMessage(MSG_FREEZE_ONLY_RECORDS_FOLDERS)); - } - else - { - return false; - } - } - } - - } \ No newline at end of file diff --git a/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/action/impl/RelinquishHoldAction.java b/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/action/impl/RelinquishHoldAction.java index 5fdb6a9676..f0e205d088 100644 --- a/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/action/impl/RelinquishHoldAction.java +++ b/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/action/impl/RelinquishHoldAction.java @@ -20,20 +20,15 @@ package org.alfresco.module.org_alfresco_module_rm.action.impl; import java.io.Serializable; import java.util.HashSet; -import java.util.List; import java.util.Map; import java.util.Set; import org.alfresco.error.AlfrescoRuntimeException; -import org.alfresco.model.ContentModel; import org.alfresco.module.org_alfresco_module_rm.action.RMActionExecuterAbstractBase; +import org.alfresco.module.org_alfresco_module_rm.freeze.FreezeService; import org.alfresco.service.cmr.action.Action; -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.apache.commons.logging.Log; -import org.apache.commons.logging.LogFactory; import org.springframework.extensions.surf.util.I18NUtil; /** @@ -43,158 +38,71 @@ import org.springframework.extensions.surf.util.I18NUtil; */ public class RelinquishHoldAction extends RMActionExecuterAbstractBase { - /** Logger */ - private static Log logger = LogFactory.getLog(RelinquishHoldAction.class); - - /** I18N */ - private static final String MSG_NOT_HOLD_TYPE = "rm.action.not-hold-type"; + /** I18N */ + private static final String MSG_NOT_HOLD_TYPE = "rm.action.not-hold-type"; - /** - * @see org.alfresco.repo.action.executer.ActionExecuterAbstractBase#executeImpl(org.alfresco.service.cmr.action.Action, org.alfresco.service.cmr.repository.NodeRef) - */ - @Override - protected void executeImpl(Action action, NodeRef actionedUponNodeRef) - { - QName nodeType = this.nodeService.getType(actionedUponNodeRef); - if (this.dictionaryService.isSubClass(nodeType, TYPE_HOLD) == true) - { - final NodeRef holdBeingRelinquished = actionedUponNodeRef; - List frozenNodeAssocs = nodeService.getChildAssocs(holdBeingRelinquished, ASSOC_FROZEN_RECORDS, RegexQNamePattern.MATCH_ALL); - - if (logger.isDebugEnabled()) - { - StringBuilder msg = new StringBuilder(); - msg.append("Relinquishing hold ").append(holdBeingRelinquished) - .append(" which has ").append(frozenNodeAssocs.size()).append(" frozen node(s)."); - logger.debug(msg.toString()); - } - - for (ChildAssociationRef assoc : frozenNodeAssocs) - { - final NodeRef nextFrozenNode = assoc.getChildRef(); - - // Remove the freeze if this is the only hold that references the node - removeFreeze(nextFrozenNode, holdBeingRelinquished); - } - - if (logger.isDebugEnabled()) - { - StringBuilder msg = new StringBuilder(); - msg.append("Deleting hold object ").append(holdBeingRelinquished) - .append(" with name ").append(nodeService.getProperty(holdBeingRelinquished, ContentModel.PROP_NAME)); - logger.debug(msg.toString()); - } - - // Delete the hold node - this.nodeService.deleteNode(holdBeingRelinquished); - } - else - { - throw new AlfrescoRuntimeException(I18NUtil.getMessage(MSG_NOT_HOLD_TYPE, TYPE_HOLD.toString(), actionedUponNodeRef.toString())); - } - } - - /** - * Removes a freeze from a node - * - * @param nodeRef node reference - */ - private void removeFreeze(NodeRef nodeRef, NodeRef holdBeingRelinquished) - { - // We should only remove the frozen aspect if there are no other 'holds' in effect for this node. - // One complication to consider is that holds can be placed on records or on folders. - // Therefore if the nodeRef here is a record, we need to go up the containment hierarchy looking - // for holds at each level. + /** Freeze Service */ + private FreezeService freezeService; - // Get all the holds and remove this node from them. - List parentAssocs = this.nodeService.getParentAssocs(nodeRef, ASSOC_FROZEN_RECORDS, RegexQNamePattern.MATCH_ALL); - // If the nodeRef is a record, there could also be applicable holds as parents of the folder(s). - if (recordsManagementService.isRecord(nodeRef)) - { - List parentFolders = recordsManagementService.getRecordFolders(nodeRef); - for (NodeRef folder : parentFolders) - { - List moreAssocs = nodeService.getParentAssocs(folder, ASSOC_FROZEN_RECORDS, RegexQNamePattern.MATCH_ALL); - parentAssocs.addAll(moreAssocs); - } - } - - if (logger.isDebugEnabled()) - { - StringBuilder msg = new StringBuilder(); - msg.append("Removing freeze from ").append(nodeRef).append(" which has ") - .append(parentAssocs.size()).append(" holds"); - logger.debug(msg.toString()); - } + /** + * Set freeze service + * + * @param freezeService freeze service + */ + public void setFreezeService(FreezeService freezeService) + { + this.freezeService = freezeService; + } - boolean otherHoldsAreInEffect = false; - for (ChildAssociationRef chAssRef : parentAssocs) - { - if (!chAssRef.getParentRef().equals(holdBeingRelinquished)) - { - otherHoldsAreInEffect = true; - break; - } - } - - if (!otherHoldsAreInEffect) - { - if (logger.isDebugEnabled()) - { - StringBuilder msg = new StringBuilder(); - msg.append("Removing frozen aspect from ").append(nodeRef); - logger.debug(msg.toString()); - } + /** + * @see org.alfresco.repo.action.executer.ActionExecuterAbstractBase#executeImpl(org.alfresco.service.cmr.action.Action, org.alfresco.service.cmr.repository.NodeRef) + */ + @Override + protected void executeImpl(Action action, NodeRef actionedUponNodeRef) + { + if (freezeService.isHold(actionedUponNodeRef)) + { + freezeService.relinquish(actionedUponNodeRef); + } + else + { + throw new AlfrescoRuntimeException(I18NUtil.getMessage(MSG_NOT_HOLD_TYPE, TYPE_HOLD.toString(), actionedUponNodeRef.toString())); + } + } - // Remove the aspect - this.nodeService.removeAspect(nodeRef, ASPECT_FROZEN); - } - - // Remove the freezes on the child records as long as there is no other hold referencing them - if (this.recordsManagementService.isRecordFolder(nodeRef) == true) - { - if (logger.isDebugEnabled()) - { - StringBuilder msg = new StringBuilder(); - msg.append(nodeRef).append(" is a record folder"); - logger.debug(msg.toString()); - } - for (NodeRef record : recordsManagementService.getRecords(nodeRef)) - { - removeFreeze(record, holdBeingRelinquished); - } - } + /** + * @see org.alfresco.module.org_alfresco_module_rm.action.RMActionExecuterAbstractBase#getProtectedAspects() + */ + @Override + public Set getProtectedAspects() + { + HashSet qnames = new HashSet(); + qnames.add(ASPECT_FROZEN); + return qnames; + } - } - - @Override - public Set getProtectedAspects() - { - HashSet qnames = new HashSet(); - qnames.add(ASPECT_FROZEN); - return qnames; - } + /** + * @see org.alfresco.module.org_alfresco_module_rm.action.RMActionExecuterAbstractBase#isExecutableImpl(org.alfresco.service.cmr.repository.NodeRef, java.util.Map, boolean) + */ + @Override + protected boolean isExecutableImpl(NodeRef filePlanComponent, Map parameters, boolean throwException) + { + QName nodeType = this.nodeService.getType(filePlanComponent); + if (this.dictionaryService.isSubClass(nodeType, TYPE_HOLD) == true) + { + return true; + } + else + { + if(throwException) + { + throw new AlfrescoRuntimeException(I18NUtil.getMessage(MSG_NOT_HOLD_TYPE, TYPE_HOLD.toString(), filePlanComponent.toString())); + } + else + { + return false; + } + } + } - @Override - protected boolean isExecutableImpl(NodeRef filePlanComponent, Map parameters, boolean throwException) - { - QName nodeType = this.nodeService.getType(filePlanComponent); - if (this.dictionaryService.isSubClass(nodeType, TYPE_HOLD) == true) - { - return true; - } - else - { - if(throwException) - { - throw new AlfrescoRuntimeException(I18NUtil.getMessage(MSG_NOT_HOLD_TYPE, TYPE_HOLD.toString(), filePlanComponent.toString())); - } - else - { - return false; - } - } - } - - } \ No newline at end of file diff --git a/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/action/impl/TransferAction.java b/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/action/impl/TransferAction.java index ba051b93b6..709fb3dfdc 100644 --- a/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/action/impl/TransferAction.java +++ b/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/action/impl/TransferAction.java @@ -37,6 +37,7 @@ import org.alfresco.service.cmr.repository.ContentData; import org.alfresco.service.cmr.repository.NodeRef; import org.alfresco.service.namespace.NamespaceService; import org.alfresco.service.namespace.QName; +import org.apache.commons.lang.StringUtils; import org.springframework.extensions.surf.util.I18NUtil; /** @@ -111,7 +112,7 @@ public class TransferAction extends RMDispositionActionExecuterAbstractBase // Calculate a transfer name QName nodeDbid = QName.createQName(NamespaceService.SYSTEM_MODEL_1_0_URI, "node-dbid"); Long dbId = (Long)this.nodeService.getProperty(dispositionLifeCycleNodeRef, nodeDbid); - String transferName = padString(dbId.toString(), 10); + String transferName = StringUtils.leftPad(dbId.toString(), 10, "0"); // Create the transfer object Map transferProps = new HashMap(2); diff --git a/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/action/impl/UnfreezeAction.java b/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/action/impl/UnfreezeAction.java index 9189184f75..a3b7c71582 100644 --- a/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/action/impl/UnfreezeAction.java +++ b/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/action/impl/UnfreezeAction.java @@ -20,19 +20,14 @@ package org.alfresco.module.org_alfresco_module_rm.action.impl; import java.io.Serializable; import java.util.HashSet; -import java.util.List; import java.util.Map; import java.util.Set; -import org.alfresco.model.ContentModel; import org.alfresco.module.org_alfresco_module_rm.action.RMActionExecuterAbstractBase; +import org.alfresco.module.org_alfresco_module_rm.freeze.FreezeService; import org.alfresco.service.cmr.action.Action; -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.apache.commons.logging.Log; -import org.apache.commons.logging.LogFactory; /** * Unfreeze Action @@ -41,119 +36,46 @@ import org.apache.commons.logging.LogFactory; */ public class UnfreezeAction extends RMActionExecuterAbstractBase { - /** Logger */ - private static Log logger = LogFactory.getLog(UnfreezeAction.class); + /** Freeze Service */ + private FreezeService freezeService; - /** - * @see org.alfresco.repo.action.executer.ActionExecuterAbstractBase#executeImpl(org.alfresco.service.cmr.action.Action, - * org.alfresco.service.cmr.repository.NodeRef) - */ - @Override - protected void executeImpl(Action action, NodeRef actionedUponNodeRef) - { - if (this.nodeService.hasAspect(actionedUponNodeRef, ASPECT_FROZEN) == true) - { - final boolean isFolder = this.recordsManagementService.isRecordFolder(actionedUponNodeRef); + /** + * Set freeze service + * + * @param freezeService freeze service + */ + public void setFreezeService(FreezeService freezeService) + { + this.freezeService = freezeService; + } - if (logger.isDebugEnabled()) - { - StringBuilder msg = new StringBuilder(); - msg.append("Unfreezing node ").append(actionedUponNodeRef); - if (isFolder) - { - msg.append(" (folder)"); - } - logger.debug(msg.toString()); - } + /** + * @see org.alfresco.repo.action.executer.ActionExecuterAbstractBase#executeImpl(org.alfresco.service.cmr.action.Action, org.alfresco.service.cmr.repository.NodeRef) + */ + @Override + protected void executeImpl(Action action, NodeRef actionedUponNodeRef) + { + freezeService.unFreeze(actionedUponNodeRef); + } - // Remove freeze from node - removeFreeze(actionedUponNodeRef); + /** + * @see org.alfresco.module.org_alfresco_module_rm.action.RMActionExecuterAbstractBase#getProtectedAspects() + */ + @Override + public Set getProtectedAspects() + { + HashSet qnames = new HashSet(); + qnames.add(ASPECT_FROZEN); + return qnames; + } - // Remove freeze from records if a record folder - if (isFolder) - { - List records = this.recordsManagementService.getRecords(actionedUponNodeRef); - for (NodeRef record : records) - { - removeFreeze(record); - } - } - } - } - - /** - * Removes a freeze from a node - * - * @param nodeRef - * node reference - */ - private void removeFreeze(NodeRef nodeRef) - { - // Get all the holds and remove this node from them - List assocs = this.nodeService.getParentAssocs(nodeRef, ASSOC_FROZEN_RECORDS, RegexQNamePattern.MATCH_ALL); - - if (logger.isDebugEnabled()) - { - StringBuilder msg = new StringBuilder(); - msg.append("Removing freeze from node ").append(nodeRef) - .append("which has ").append(assocs.size()).append(" holds"); - logger.debug(msg.toString()); - } - - for (ChildAssociationRef assoc : assocs) - { - // Remove the frozen node as a child - NodeRef holdNodeRef = assoc.getParentRef(); - this.nodeService.removeChild(holdNodeRef, nodeRef); - - if (logger.isDebugEnabled()) - { - StringBuilder msg = new StringBuilder(); - msg.append("Removed frozen node from hold ").append(holdNodeRef); - logger.debug(msg.toString()); - } - - // Check to see if we should delete the hold - List holdAssocs = this.nodeService.getChildAssocs(holdNodeRef, ASSOC_FROZEN_RECORDS, RegexQNamePattern.MATCH_ALL); - if (holdAssocs.size() == 0) - { - if (logger.isDebugEnabled()) - { - StringBuilder msg = new StringBuilder(); - msg.append("Hold node ").append(holdNodeRef) - .append(" with name ").append(nodeService.getProperty(holdNodeRef, ContentModel.PROP_NAME)) - .append(" has no frozen nodes. Hence deleting it."); - logger.debug(msg.toString()); - } - - // Delete the hold object - this.nodeService.deleteNode(holdNodeRef); - } - } - - // Remove the aspect - this.nodeService.removeAspect(nodeRef, ASPECT_FROZEN); - - if (logger.isDebugEnabled()) - { - StringBuilder msg = new StringBuilder(); - msg.append("Removed frozen aspect from ").append(nodeRef); - logger.debug(msg.toString()); - } - } - - @Override - public Set getProtectedAspects() - { - HashSet qnames = new HashSet(); - qnames.add(ASPECT_FROZEN); - return qnames; - } - - @Override - protected boolean isExecutableImpl(NodeRef filePlanComponent, Map parameters, boolean throwException) - { - return this.nodeService.hasAspect(filePlanComponent, ASPECT_FROZEN); - } + /** + * @see org.alfresco.module.org_alfresco_module_rm.action.RMActionExecuterAbstractBase#isExecutableImpl(org.alfresco.service.cmr.repository.NodeRef, java.util.Map, boolean) + */ + @Override + protected boolean isExecutableImpl(NodeRef filePlanComponent, Map parameters, boolean throwException) + { + return this.nodeService.hasAspect(filePlanComponent, ASPECT_FROZEN); + } } \ No newline at end of file diff --git a/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/freeze/FreezeService.java b/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/freeze/FreezeService.java index 66d372eb64..9c8c79f86b 100644 --- a/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/freeze/FreezeService.java +++ b/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/freeze/FreezeService.java @@ -18,6 +18,9 @@ */ package org.alfresco.module.org_alfresco_module_rm.freeze; +import java.util.Set; + +import org.alfresco.service.cmr.repository.NodeRef; /** * Freeze Service Interface @@ -39,7 +42,7 @@ public interface FreezeService * @param nodeRef hold node reference * @return boolean true if hold, false otherwise */ - // TODO boolean isHold(NodeRef nodeRef); + boolean isHold(NodeRef nodeRef); /** * Indicates whether the passed node reference is frozen. @@ -47,7 +50,7 @@ public interface FreezeService * @param nodeRef node reference * @return boolean true if frozen, false otherwise */ - // TODO boolean isFrozen(NodeRef nodeRef); + boolean isFrozen(NodeRef nodeRef); /** * Get the 'root' frozen node references in a hold. @@ -55,7 +58,7 @@ public interface FreezeService * @param hold hold node reference * @return Set frozen node references */ - // TODO Set getFrozen(NodeRef hold); + Set getFrozen(NodeRef hold); /** * Freezes a node with the provided reason, creating a hold node reference. @@ -64,7 +67,7 @@ public interface FreezeService * @param nodeRef node reference * @return NodeRef hold node reference */ - // TODO NodeRef freeze(String reason, NodeRef nodeRef); + NodeRef freeze(String reason, NodeRef nodeRef); /** * Freezes a node, adding it an existing hold. @@ -72,24 +75,24 @@ public interface FreezeService * @param hold hold node reference * @param nodeRef node reference */ - // TODO void freeze(NodeRef hold, NodeRef nodeRef); + void freeze(NodeRef hold, NodeRef nodeRef); /** * Freezes a collection of nodes with the given reason, creating a hold. * * @param reason freeze reason - * @param Set set of nodes to freeze + * @param nodeRefs set of nodes to freeze * @return NodeRef hold node reference */ - // TODO NodeRef freeze(String reason, Set nodeRef); + NodeRef freeze(String reason, Set nodeRefs); /** * Freeze a collection of nodes, adding them to an existing hold. * * @param hold hold node reference - * @param nodeRef set of nodes to freeze + * @param nodeRefs set of nodes to freeze */ - // TODO void freeze(NodeRef hold, Set nodeRef); + void freeze(NodeRef hold, Set nodeRefs); /** * Unfreeze a frozen node. @@ -99,7 +102,7 @@ public interface FreezeService * * @param nodeRef node reference */ - // TODO void unFreeze(NodeRef nodeRef); + void unFreeze(NodeRef nodeRef); /** * Unfreeze a collection of nodes. @@ -107,16 +110,16 @@ public interface FreezeService * The unfrozen nodes are automatically removed from the hold(s) the are in. If the hold(s) is * subsequently empty, the hold is automatically deleted. * - * @param Set set of nodes to unfreeze + * @param nodeRefs set of nodes to unfreeze */ - // TODO void unFreeze(Set nodeRef); + void unFreeze(Set nodeRefs); /** * Unfreezes all nodes within a hold and deletes the hold. * * @param hold hold node reference */ - // TODO void relinquish(NodeRef hold); + void relinquish(NodeRef hold); /** * Gets the freeze reason for a hold. @@ -124,7 +127,7 @@ public interface FreezeService * @param hold hold node reference * @return String freeze reason */ - // TODO String getReason(NodeRef hold); + String getReason(NodeRef hold); /** * Updates the freeze reason for a given hold. @@ -132,5 +135,5 @@ public interface FreezeService * @param hold hold node reference * @param reason updated reason */ - // TODO void updateReason(NodeRef hold, String reason); + void updateReason(NodeRef hold, String reason); } diff --git a/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/freeze/FreezeServiceImpl.java b/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/freeze/FreezeServiceImpl.java index f28a7bd57b..ea8feb9920 100644 --- a/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/freeze/FreezeServiceImpl.java +++ b/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/freeze/FreezeServiceImpl.java @@ -18,20 +18,38 @@ */ package org.alfresco.module.org_alfresco_module_rm.freeze; +import java.io.Serializable; +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.error.AlfrescoRuntimeException; +import org.alfresco.model.ContentModel; import org.alfresco.module.org_alfresco_module_rm.RecordsManagementService; import org.alfresco.module.org_alfresco_module_rm.model.RecordsManagementModel; import org.alfresco.repo.node.NodeServicePolicies; +import org.alfresco.repo.policy.Behaviour.NotificationFrequency; import org.alfresco.repo.policy.JavaBehaviour; import org.alfresco.repo.policy.PolicyComponent; -import org.alfresco.repo.policy.Behaviour.NotificationFrequency; import org.alfresco.repo.security.authentication.AuthenticationUtil; import org.alfresco.repo.security.authentication.AuthenticationUtil.RunAsWork; import org.alfresco.repo.security.permissions.AccessDeniedException; +import org.alfresco.repo.transaction.AlfrescoTransactionSupport; +import org.alfresco.service.cmr.dictionary.DictionaryService; import org.alfresco.service.cmr.repository.ChildAssociationRef; import org.alfresco.service.cmr.repository.NodeRef; import org.alfresco.service.cmr.repository.NodeService; +import org.alfresco.service.namespace.NamespaceService; +import org.alfresco.service.namespace.QName; +import org.alfresco.service.namespace.RegexQNamePattern; +import org.alfresco.util.ParameterCheck; +import org.apache.commons.lang.StringUtils; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.springframework.extensions.surf.util.I18NUtil; /** * Freeze Service Implementation @@ -42,101 +60,579 @@ public class FreezeServiceImpl implements FreezeService, RecordsManagementModel, NodeServicePolicies.BeforeDeleteNodePolicy { - /** Policy Component */ - private PolicyComponent policyComponent; - - /** Node Service */ - private NodeService nodeService; + /** Logger */ + private static Log logger = LogFactory.getLog(FreezeServiceImpl.class); - /** Records Management Service */ - private RecordsManagementService recordsManagementService; + /** I18N */ + private static final String MSG_FREEZE_ONLY_RECORDS_FOLDERS = "rm.action.freeze-only-records-folders"; + private static final String MSG_EMPTY_SET_OF_NODEREFS = "rm.action.empty-set-of-noderefs"; - /** - * @param policyComponent policy component - */ - public void setPolicyComponent(PolicyComponent policyComponent) - { - this.policyComponent = policyComponent; - } - - /** - * @param nodeService node service - */ - public void setNodeService(NodeService nodeService) - { - this.nodeService = nodeService; - } - - /** - * @param recordsManagementService records management service - */ - public void setRecordsManagementService(RecordsManagementService recordsManagementService) - { - this.recordsManagementService = recordsManagementService; - } - - /** - * Init service - */ - public void init() - { - policyComponent.bindClassBehaviour( - NodeServicePolicies.BeforeDeleteNodePolicy.QNAME, - this, - new JavaBehaviour(this, "beforeDeleteNode", NotificationFrequency.FIRST_EVENT)); + /** Hold node reference key */ + private static final String KEY_HOLD_NODEREF = "holdNodeRef"; + + /** Policy Component */ + private PolicyComponent policyComponent; + + /** Node Service */ + private NodeService nodeService; + + /** Records Management Service */ + private RecordsManagementService recordsManagementService; + + /** Dictionary Service */ + private DictionaryService dictionaryService; + + /** + * @param policyComponent policy component + */ + public void setPolicyComponent(PolicyComponent policyComponent) + { + this.policyComponent = policyComponent; + } + + /** + * @param nodeService node service + */ + public void setNodeService(NodeService nodeService) + { + this.nodeService = nodeService; + } + + /** + * @param recordsManagementService records management service + */ + public void setRecordsManagementService(RecordsManagementService recordsManagementService) + { + this.recordsManagementService = recordsManagementService; + } + + /** + * @param dictionaryService dictionary service + */ + public void setDictionaryService(DictionaryService dictionaryService) + { + this.dictionaryService = dictionaryService; + } + + /** + * Init service + */ + public void init() + { + policyComponent.bindClassBehaviour( + NodeServicePolicies.BeforeDeleteNodePolicy.QNAME, + this, + new JavaBehaviour(this, "beforeDeleteNode", NotificationFrequency.FIRST_EVENT)); } - /** - * @see org.alfresco.repo.node.NodeServicePolicies.BeforeDeleteNodePolicy#beforeDeleteNode(org.alfresco.service.cmr.repository.NodeRef) - */ - @Override - public void beforeDeleteNode(final NodeRef nodeRef) - { - AuthenticationUtil.runAsSystem(new RunAsWork(){ + /** + * @see org.alfresco.repo.node.NodeServicePolicies.BeforeDeleteNodePolicy#beforeDeleteNode(org.alfresco.service.cmr.repository.NodeRef) + */ + @Override + public void beforeDeleteNode(final NodeRef nodeRef) + { + AuthenticationUtil.runAsSystem(new RunAsWork(){ - @Override - public Void doWork() throws Exception + @Override + public Void doWork() throws Exception + { + if (nodeService.exists(nodeRef) == true && + recordsManagementService.isFilePlanComponent(nodeRef) == true) { - if (nodeService.exists(nodeRef) == true && - recordsManagementService.isFilePlanComponent(nodeRef) == true) - { - if (recordsManagementService.isFrozen(nodeRef) == true) - { - // never allowed to delete a frozen node - throw new AccessDeniedException("Frozen nodes can not be deleted."); - } - - // check children - checkChildren(nodeService.getChildAssocs(nodeRef)); - } - - return null; - }}); - - } - - /** - * Checks the children for frozen nodes. Throws security error if any are found. - * - * @param assocs - */ - private void checkChildren(List assocs) - { - for (ChildAssociationRef assoc : assocs) - { - // we only care about primary children - if (assoc.isPrimary() == true) - { - NodeRef nodeRef = assoc.getChildRef(); - if (recordsManagementService.isFrozen(nodeRef) == true) - { - // never allowed to delete a node with a frozen child - throw new AccessDeniedException("Can not delete node, because it contains a frozen child node."); - } - - // check children - checkChildren(nodeService.getChildAssocs(nodeRef)); + if (recordsManagementService.isFrozen(nodeRef) == true) + { + // never allowed to delete a frozen node + throw new AccessDeniedException("Frozen nodes can not be deleted."); + } + + // check children + checkChildren(nodeService.getChildAssocs(nodeRef)); } - } + return null; + } + }); } + + /** + * Checks the children for frozen nodes. Throws security error if any are found. + * + * @param assocs + */ + private void checkChildren(List assocs) + { + for (ChildAssociationRef assoc : assocs) + { + // we only care about primary children + if (assoc.isPrimary() == true) + { + NodeRef nodeRef = assoc.getChildRef(); + if (recordsManagementService.isFrozen(nodeRef) == true) + { + // never allowed to delete a node with a frozen child + throw new AccessDeniedException("Can not delete node, because it contains a frozen child node."); + } + + // check children + checkChildren(nodeService.getChildAssocs(nodeRef)); + } + } + } + + /** + * @see org.alfresco.module.org_alfresco_module_rm.freeze.FreezeService#isHold(org.alfresco.service.cmr.repository.NodeRef) + */ + @Override + public boolean isHold(NodeRef nodeRef) + { + ParameterCheck.mandatory("nodeRef", nodeRef); + + return dictionaryService.isSubClass(nodeService.getType(nodeRef), TYPE_HOLD); + } + + /** + * @see org.alfresco.module.org_alfresco_module_rm.freeze.FreezeService#isFrozen(org.alfresco.service.cmr.repository.NodeRef) + */ + @Override + public boolean isFrozen(NodeRef nodeRef) + { + ParameterCheck.mandatory("nodeRef", nodeRef); + + return nodeService.hasAspect(nodeRef, ASPECT_FROZEN); + } + + /** + * @see org.alfresco.module.org_alfresco_module_rm.freeze.FreezeService#getFrozen(org.alfresco.service.cmr.repository.NodeRef) + */ + @Override + public Set getFrozen(NodeRef hold) + { + ParameterCheck.mandatory("hold", hold); + + Set frozenNodes = new HashSet(); + List childAssocs = nodeService.getChildAssocs(hold, ASSOC_FROZEN_RECORDS, RegexQNamePattern.MATCH_ALL); + if (childAssocs != null && !childAssocs.isEmpty()) + { + for (ChildAssociationRef childAssociationRef : childAssocs) + { + frozenNodes.add(childAssociationRef.getChildRef()); + } + } + return frozenNodes; + } + + /** + * @see org.alfresco.module.org_alfresco_module_rm.freeze.FreezeService#freeze(java.lang.String, org.alfresco.service.cmr.repository.NodeRef) + */ + @Override + public NodeRef freeze(String reason, NodeRef nodeRef) + { + ParameterCheck.mandatoryString("reason", reason); + ParameterCheck.mandatory("nodeRef", nodeRef); + + // Check if the actionedUponNodeRef is a valid file plan component + boolean isRecord = recordsManagementService.isRecord(nodeRef); + boolean isFolder = recordsManagementService.isRecordFolder(nodeRef); + + if (!(isRecord || isFolder)) + { + throw new AlfrescoRuntimeException(I18NUtil.getMessage(MSG_FREEZE_ONLY_RECORDS_FOLDERS)); + } + + // Log a message about freezing the node with the reason + if (logger.isDebugEnabled()) + { + StringBuilder msg = new StringBuilder(); + msg.append("Freezing node '").append(nodeRef).append("'"); + if (isFolder) + { + msg.append(" (folder)"); + } + msg.append(" with reason '").append(reason).append("'."); + logger.debug(msg.toString()); + } + + // Create the hold object + NodeRef holdNodeRef = createHold(nodeRef, reason); + + // Freeze the node and add it to the hold + freeze(holdNodeRef, nodeRef); + + return holdNodeRef; + } + + /** + * @see org.alfresco.module.org_alfresco_module_rm.freeze.FreezeService#freeze(org.alfresco.service.cmr.repository.NodeRef, org.alfresco.service.cmr.repository.NodeRef) + */ + @Override + public void freeze(NodeRef hold, NodeRef nodeRef) + { + ParameterCheck.mandatory("hold", hold); + ParameterCheck.mandatory("nodeRef", nodeRef); + + // Link the record to the hold + nodeService.addChild(hold, nodeRef, ASSOC_FROZEN_RECORDS, ASSOC_FROZEN_RECORDS); + + // Apply the freeze aspect + Map props = new HashMap(2); + props.put(PROP_FROZEN_AT, new Date()); + props.put(PROP_FROZEN_BY, AuthenticationUtil.getFullyAuthenticatedUser()); + nodeService.addAspect(nodeRef, ASPECT_FROZEN, props); + + // Log a message about applying the the frozen aspect + if (logger.isDebugEnabled()) + { + StringBuilder msg = new StringBuilder(); + msg.append("Frozen aspect applied to '").append(nodeRef).append("'."); + logger.debug(msg.toString()); + } + + // Mark all the folders contents as frozen + if (recordsManagementService.isRecordFolder(nodeRef)) + { + List records = recordsManagementService.getRecords(nodeRef); + for (NodeRef record : records) + { + nodeService.addAspect(record, ASPECT_FROZEN, props); + + if (logger.isDebugEnabled()) + { + StringBuilder msg = new StringBuilder(); + msg.append("Frozen aspect applied to '").append(record).append("'."); + logger.debug(msg.toString()); + } + } + } + } + + /** + * @see org.alfresco.module.org_alfresco_module_rm.freeze.FreezeService#freeze(java.lang.String, java.util.Set) + */ + @Override + public NodeRef freeze(String reason, Set nodeRefs) + { + ParameterCheck.mandatoryString("reason", reason); + ParameterCheck.mandatory("nodeRefs", nodeRefs); + + if (nodeRefs.isEmpty()) + { + throw new AlfrescoRuntimeException(MSG_EMPTY_SET_OF_NODEREFS); + } + + // FIXME: Can we assume that the nodeRefs are in the same filePlan??? + NodeRef nodeRef = nodeRefs.iterator().next(); + NodeRef hold = createHold(nodeRef, reason); + + freeze(hold, nodeRefs); + + return hold; + } + + /** + * @see org.alfresco.module.org_alfresco_module_rm.freeze.FreezeService#freeze(org.alfresco.service.cmr.repository.NodeRef, java.util.Set) + */ + @Override + public void freeze(NodeRef hold, Set nodeRefs) + { + ParameterCheck.mandatory("hold", hold); + ParameterCheck.mandatory("nodeRefs", nodeRefs); + + for (NodeRef nodeRef : nodeRefs) + { + freeze(hold, nodeRef); + } + } + + /** + * @see org.alfresco.module.org_alfresco_module_rm.freeze.FreezeService#unFreeze(org.alfresco.service.cmr.repository.NodeRef) + */ + @Override + public void unFreeze(NodeRef nodeRef) + { + ParameterCheck.mandatory("nodeRef", nodeRef); + + if (nodeService.hasAspect(nodeRef, ASPECT_FROZEN)) + { + boolean isRecordFolder = recordsManagementService.isRecordFolder(nodeRef); + + if (logger.isDebugEnabled()) + { + StringBuilder msg = new StringBuilder(); + msg.append("Unfreezing node '").append(nodeRef).append("'"); + if (isRecordFolder) + { + msg.append(" (folder)"); + } + msg.append("."); + logger.debug(msg.toString()); + } + + // Remove freeze from node + removeFreeze(nodeRef); + + // Remove freeze from records if a record folder + if (isRecordFolder) + { + List records = recordsManagementService.getRecords(nodeRef); + for (NodeRef record : records) + { + removeFreeze(record); + } + } + } + else + { + StringBuilder msg = new StringBuilder(); + msg.append("The node '").append(nodeRef).append("' was not frozen. So it cannot be unfrozen!"); + logger.info(msg.toString()); + } + } + + /** + * @see org.alfresco.module.org_alfresco_module_rm.freeze.FreezeService#unFreeze(java.util.Set) + */ + @Override + public void unFreeze(Set nodeRefs) + { + ParameterCheck.mandatory("nodeRefs", nodeRefs); + + for (NodeRef nodeRef : nodeRefs) + { + unFreeze(nodeRef); + } + } + + /** + * @see org.alfresco.module.org_alfresco_module_rm.freeze.FreezeService#relinquish(org.alfresco.service.cmr.repository.NodeRef) + */ + @Override + public void relinquish(NodeRef hold) + { + ParameterCheck.mandatory("hold", hold); + + final NodeRef holdBeingRelinquished = hold; + List frozenNodeAssocs = nodeService.getChildAssocs(holdBeingRelinquished, ASSOC_FROZEN_RECORDS, RegexQNamePattern.MATCH_ALL); + + if (logger.isDebugEnabled()) + { + StringBuilder msg = new StringBuilder(); + msg.append("Relinquishing hold ").append(holdBeingRelinquished) + .append(" which has ").append(frozenNodeAssocs.size()).append(" frozen node(s)."); + logger.debug(msg.toString()); + } + + for (ChildAssociationRef assoc : frozenNodeAssocs) + { + final NodeRef nextFrozenNode = assoc.getChildRef(); + + // Remove the freeze if this is the only hold that references the node + removeFreeze(nextFrozenNode, holdBeingRelinquished); + } + + if (logger.isDebugEnabled()) + { + StringBuilder msg = new StringBuilder(); + msg.append("Deleting hold object ").append(holdBeingRelinquished) + .append(" with name ").append(nodeService.getProperty(holdBeingRelinquished, ContentModel.PROP_NAME)); + logger.debug(msg.toString()); + } + + // Delete the hold node + this.nodeService.deleteNode(holdBeingRelinquished); + } + + /** + * Removes a freeze from a node + * + * @param nodeRef node reference + */ + private void removeFreeze(NodeRef nodeRef, NodeRef holdBeingRelinquished) + { + // We should only remove the frozen aspect if there are no other 'holds' in effect for this node. + // One complication to consider is that holds can be placed on records or on folders. + // Therefore if the nodeRef here is a record, we need to go up the containment hierarchy looking + // for holds at each level. + + // Get all the holds and remove this node from them. + List parentAssocs = this.nodeService.getParentAssocs(nodeRef, ASSOC_FROZEN_RECORDS, RegexQNamePattern.MATCH_ALL); + // If the nodeRef is a record, there could also be applicable holds as parents of the folder(s). + if (recordsManagementService.isRecord(nodeRef)) + { + List parentFolders = recordsManagementService.getRecordFolders(nodeRef); + for (NodeRef folder : parentFolders) + { + List moreAssocs = nodeService.getParentAssocs(folder, ASSOC_FROZEN_RECORDS, RegexQNamePattern.MATCH_ALL); + parentAssocs.addAll(moreAssocs); + } + } + + if (logger.isDebugEnabled()) + { + StringBuilder msg = new StringBuilder(); + msg.append("Removing freeze from ").append(nodeRef).append(" which has ") + .append(parentAssocs.size()).append(" holds"); + logger.debug(msg.toString()); + } + + boolean otherHoldsAreInEffect = false; + for (ChildAssociationRef chAssRef : parentAssocs) + { + if (!chAssRef.getParentRef().equals(holdBeingRelinquished)) + { + otherHoldsAreInEffect = true; + break; + } + } + + if (!otherHoldsAreInEffect) + { + if (logger.isDebugEnabled()) + { + StringBuilder msg = new StringBuilder(); + msg.append("Removing frozen aspect from ").append(nodeRef); + logger.debug(msg.toString()); + } + + // Remove the aspect + this.nodeService.removeAspect(nodeRef, ASPECT_FROZEN); + } + + // Remove the freezes on the child records as long as there is no other hold referencing them + if (this.recordsManagementService.isRecordFolder(nodeRef) == true) + { + if (logger.isDebugEnabled()) + { + StringBuilder msg = new StringBuilder(); + msg.append(nodeRef).append(" is a record folder"); + logger.debug(msg.toString()); + } + for (NodeRef record : recordsManagementService.getRecords(nodeRef)) + { + removeFreeze(record, holdBeingRelinquished); + } + } + } + + /** + * @see org.alfresco.module.org_alfresco_module_rm.freeze.FreezeService#getReason(org.alfresco.service.cmr.repository.NodeRef) + */ + @Override + public String getReason(NodeRef hold) + { + ParameterCheck.mandatory("hold", hold); + + return (String) nodeService.getProperty(hold, PROP_HOLD_REASON); + } + + /** + * @see org.alfresco.module.org_alfresco_module_rm.freeze.FreezeService#updateReason(org.alfresco.service.cmr.repository.NodeRef, java.lang.String) + */ + @Override + public void updateReason(NodeRef hold, String reason) + { + ParameterCheck.mandatory("hold", hold); + ParameterCheck.mandatoryString("reason", reason); + + nodeService.setProperty(hold, PROP_HOLD_REASON, reason); + } + + /** + * Creates a hold using the given nodeRef and reason + * + * @param nodeRef the nodeRef which will be frozen + * @param reason the reason why the record will be frozen + * @return NodeRef of the created hold + */ + private NodeRef createHold(NodeRef nodeRef, String reason) + { + NodeRef holdNodeRef = null; + + // Calculate a transfer name + QName nodeDbid = QName.createQName(NamespaceService.SYSTEM_MODEL_1_0_URI, "node-dbid"); + Long dbId = (Long) nodeService.getProperty(nodeRef, nodeDbid); + String transferName = StringUtils.leftPad(dbId.toString(), 10, "0"); + + // Create the hold object + Map holdProps = new HashMap(2); + holdProps.put(ContentModel.PROP_NAME, transferName); + holdProps.put(PROP_HOLD_REASON, reason); + + // Get the root rm node + NodeRef root = recordsManagementService.getFilePlan(nodeRef); + final QName transferQName = QName.createQName(RM_URI, transferName); + holdNodeRef = nodeService.createNode(root, ASSOC_HOLDS, transferQName, TYPE_HOLD, holdProps).getChildRef(); + + if (logger.isDebugEnabled()) + { + StringBuilder msg = new StringBuilder(); + msg.append("Created hold object ").append(holdNodeRef) + .append(" with transfer name ").append(transferQName); + logger.debug(msg.toString()); + } + + // Bind the hold node reference to the transaction + AlfrescoTransactionSupport.bindResource(KEY_HOLD_NODEREF, holdNodeRef); + + return holdNodeRef; + } + + /** + * Removes a freeze from a node. The unfrozen node is automatically removed from the hold(s) it is in. + * If the hold is subsequently empty, the hold is automatically deleted. + * + * @param nodeRef node reference + */ + private void removeFreeze(NodeRef nodeRef) + { + // Get all the holds and remove this node from them + List assocs = nodeService.getParentAssocs(nodeRef, ASSOC_FROZEN_RECORDS, RegexQNamePattern.MATCH_ALL); + + if (logger.isDebugEnabled()) + { + StringBuilder msg = new StringBuilder(); + msg.append("Removing freeze from node '").append(nodeRef) + .append("' which has '").append(assocs.size()).append("' holds."); + logger.debug(msg.toString()); + } + + for (ChildAssociationRef assoc : assocs) + { + // Remove the frozen node as a child + NodeRef holdNodeRef = assoc.getParentRef(); + nodeService.removeChild(holdNodeRef, nodeRef); + + if (logger.isDebugEnabled()) + { + StringBuilder msg = new StringBuilder(); + msg.append("Removed frozen node from hold '").append(holdNodeRef).append("'."); + logger.debug(msg.toString()); + } + + // Check to see if we should delete the hold + List holdAssocs = nodeService.getChildAssocs(holdNodeRef, ASSOC_FROZEN_RECORDS, RegexQNamePattern.MATCH_ALL); + if (holdAssocs != null && holdAssocs.isEmpty()) + { + if (logger.isDebugEnabled()) + { + StringBuilder msg = new StringBuilder(); + msg.append("Hold node '").append(holdNodeRef) + .append("' with name '").append(nodeService.getProperty(holdNodeRef, ContentModel.PROP_NAME)) + .append("' has no frozen nodes. Hence deleting it."); + logger.debug(msg.toString()); + } + + // Delete the hold object + nodeService.deleteNode(holdNodeRef); + } + } + + // Remove the aspect + this.nodeService.removeAspect(nodeRef, ASPECT_FROZEN); + + if (logger.isDebugEnabled()) + { + StringBuilder msg = new StringBuilder(); + msg.append("Removed frozen aspect from ").append(nodeRef); + logger.debug(msg.toString()); + } + } }