Acceptance Criteria Automation for RM-1997: Content store data cleansing

* added @AlfrescoTest annotation dependency to help track AC's back to JIRA
 * feedback from previous review
 * see RM-2460, RM-2461, RM-2462, RM-2505, RM-2506, RM-2507

+review RM 



git-svn-id: https://svn.alfresco.com/repos/alfresco-enterprise/modules/recordsmanagement/HEAD@109733 c4b6b30b-aa2e-2d43-bbcb-ca4b014f7261
This commit is contained in:
Roy Wetherall
2015-08-07 02:04:51 +00:00
parent 7e61856833
commit 8f2f5258d1
12 changed files with 748 additions and 157 deletions

View File

@@ -21,19 +21,13 @@ package org.alfresco.module.org_alfresco_module_rm.action.impl;
import java.io.Serializable;
import java.util.Collections;
import java.util.List;
import java.util.Set;
import org.alfresco.model.ContentModel;
import org.alfresco.model.RenditionModel;
import org.alfresco.module.org_alfresco_module_rm.action.RMDispositionActionExecuterAbstractBase;
import org.alfresco.module.org_alfresco_module_rm.capability.CapabilityService;
import org.alfresco.module.org_alfresco_module_rm.content.ContentDestructionComponent;
import org.alfresco.module.org_alfresco_module_rm.disposition.DispositionActionDefinition;
import org.alfresco.module.org_alfresco_module_rm.disposition.DispositionSchedule;
import org.alfresco.repo.content.cleanup.EagerContentStoreCleaner;
import org.alfresco.service.cmr.action.Action;
import org.alfresco.service.cmr.dictionary.DataTypeDefinition;
import org.alfresco.service.cmr.repository.ChildAssociationRef;
import org.alfresco.service.cmr.repository.ContentData;
import org.alfresco.service.cmr.repository.NodeRef;
import org.alfresco.service.cmr.security.AccessStatus;
import org.alfresco.service.namespace.QName;
@@ -49,9 +43,9 @@ public class DestroyAction extends RMDispositionActionExecuterAbstractBase
/** Action name */
public static final String NAME = "destroy";
/** Eager content store cleaner */
private EagerContentStoreCleaner eagerContentStoreCleaner;
/** content destruction component */
private ContentDestructionComponent contentDestructionComponent;
/** Capability service */
private CapabilityService capabilityService;
@@ -59,13 +53,13 @@ public class DestroyAction extends RMDispositionActionExecuterAbstractBase
private boolean ghostingEnabled = true;
/**
* @param eagerContentStoreCleaner eager content store cleaner
* @param contentDestructionComponent content destruction component
*/
public void setEagerContentStoreCleaner(EagerContentStoreCleaner eagerContentStoreCleaner)
public void setContentDestructionComponent(ContentDestructionComponent contentDestructionComponent)
{
this.eagerContentStoreCleaner = eagerContentStoreCleaner;
this.contentDestructionComponent = contentDestructionComponent;
}
/**
* @param capabilityService capability service
*/
@@ -128,10 +122,12 @@ public class DestroyAction extends RMDispositionActionExecuterAbstractBase
}
if (isGhostOnDestroySetForAction(action, recordFolder))
{
// add aspect
getNodeService().addAspect(recordFolder, ASPECT_GHOSTED, Collections.<QName, Serializable> emptyMap());
}
else
{
// just delete the node
getNodeService().deleteNode(recordFolder);
}
}
@@ -141,95 +137,22 @@ public class DestroyAction extends RMDispositionActionExecuterAbstractBase
*/
@Override
protected void executeRecordLevelDisposition(Action action, NodeRef record)
{
// Clear the content
clearAllContent(record);
// Clear thumbnail content
clearThumbnails(record);
{
if (isGhostOnDestroySetForAction(action, record))
{
// Add the ghosted aspect
getNodeService().addAspect(record, ASPECT_GHOSTED, null);
// destroy content
contentDestructionComponent.destroyContent(record);
}
else
{
// If ghosting is not enabled, delete the node
// just delete the node
getNodeService().deleteNode(record);
}
}
/**
* Clear all the content properties
*
* @param nodeRef
*/
private void clearAllContent(NodeRef nodeRef)
{
Set<QName> props = this.getNodeService().getProperties(nodeRef).keySet();
props.retainAll(this.getDictionaryService().getAllProperties(DataTypeDefinition.CONTENT));
for (QName prop : props)
{
// Clear the content
clearContent(nodeRef, prop);
// Remove the property
this.getNodeService().removeProperty(nodeRef, prop);
}
}
/**
* Clear all the thumbnail information
*
* @param nodeRef
*/
@SuppressWarnings("deprecation")
private void clearThumbnails(NodeRef nodeRef)
{
// Remove the renditioned aspect (and its properties and associations) if it is present.
//
// From Alfresco 3.3 it is the rn:renditioned aspect which defines the
// child-association being considered in this method.
// Note also that the cm:thumbnailed aspect extends the rn:renditioned aspect.
//
// We want to remove the rn:renditioned aspect, but due to the possibility
// that there is Alfresco 3.2-era data with the cm:thumbnailed aspect
// applied, we must consider removing it too.
if (getNodeService().hasAspect(nodeRef, RenditionModel.ASPECT_RENDITIONED) ||
getNodeService().hasAspect(nodeRef, ContentModel.ASPECT_THUMBNAILED))
{
// Add the ghosted aspect to all the renditioned children, so that they will not be archived when the
// renditioned aspect is removed
Set<QName> childAssocTypes = getDictionaryService().getAspect(RenditionModel.ASPECT_RENDITIONED).getChildAssociations().keySet();
for (ChildAssociationRef child : getNodeService().getChildAssocs(nodeRef))
{
if (childAssocTypes.contains(child.getTypeQName()))
{
// Clear the content and delete the rendition
clearAllContent(child.getChildRef());
getNodeService().deleteNode(child.getChildRef());
}
}
}
}
/**
* Clear a content property
*
* @param nodeRef
* @param contentProperty
*/
private void clearContent(NodeRef nodeRef, QName contentProperty)
{
// Ensure the content is cleaned at the end of the transaction
ContentData contentData = (ContentData)getNodeService().getProperty(nodeRef, contentProperty);
if (contentData != null && contentData.getContentUrl() != null)
{
eagerContentStoreCleaner.registerOrphanedContentUrl(contentData.getContentUrl(), true);
}
}
/**
* Return true if the ghost on destroy property is set against the
* definition for the passed action on the specified node

View File

@@ -18,8 +18,12 @@
*/
package org.alfresco.module.org_alfresco_module_rm.content;
import java.util.Collection;
import java.io.Serializable;
import java.util.Map;
import java.util.Set;
import org.alfresco.model.ContentModel;
import org.alfresco.model.RenditionModel;
import org.alfresco.module.org_alfresco_module_rm.classification.ContentClassificationService;
import org.alfresco.module.org_alfresco_module_rm.record.RecordService;
import org.alfresco.module.org_alfresco_module_rm.util.AuthenticationUtil;
@@ -28,9 +32,8 @@ import org.alfresco.repo.policy.annotation.Behaviour;
import org.alfresco.repo.policy.annotation.BehaviourBean;
import org.alfresco.repo.policy.annotation.BehaviourKind;
import org.alfresco.repo.security.authentication.AuthenticationUtil.RunAsWork;
import org.alfresco.service.cmr.dictionary.DataTypeDefinition;
import org.alfresco.service.cmr.dictionary.DictionaryService;
import org.alfresco.service.cmr.dictionary.PropertyDefinition;
import org.alfresco.service.cmr.repository.ChildAssociationRef;
import org.alfresco.service.cmr.repository.ContentData;
import org.alfresco.service.cmr.repository.NodeRef;
import org.alfresco.service.cmr.repository.NodeService;
@@ -162,36 +165,75 @@ public class ContentDestructionComponent implements NodeServicePolicies.BeforeDe
recordService.isRecord(nodeRef))
{
// then register all content for destruction
registerAllContentForDestruction(nodeRef);
registerAllContentForDestruction(nodeRef, false);
}
return null;
}
});
}
/**
* Destroy content
*
* @param nodeRef
*/
public void destroyContent(NodeRef nodeRef)
{
destroyContent(nodeRef, true);
}
/**
* Destroy content
*
* @param nodeRef
* @param includeRenditions
*/
@SuppressWarnings("deprecation")
public void destroyContent(NodeRef nodeRef, boolean includeRenditions)
{
// destroy the nodes content properties
registerAllContentForDestruction(nodeRef, true);
// Remove the renditioned aspect (and its properties and associations) if it is present.
//
// From Alfresco 3.3 it is the rn:renditioned aspect which defines the
// child-association being considered in this method.
// Note also that the cm:thumbnailed aspect extends the rn:renditioned aspect.
//
// We want to remove the rn:renditioned aspect, but due to the possibility
// that there is Alfresco 3.2-era data with the cm:thumbnailed aspect
// applied, we must consider removing it too.
if (nodeService.hasAspect(nodeRef, RenditionModel.ASPECT_RENDITIONED) ||
nodeService.hasAspect(nodeRef, ContentModel.ASPECT_THUMBNAILED))
{
// get the rendition assoc types
Set<QName> childAssocTypes = dictionaryService.getAspect(RenditionModel.ASPECT_RENDITIONED).getChildAssociations().keySet();
for (ChildAssociationRef child : nodeService.getChildAssocs(nodeRef))
{
if (childAssocTypes.contains(child.getTypeQName()))
{
// destroy renditions content
destroyContent(nodeRef, false);
}
}
}
}
/**
* Registers all content on the given node for destruction.
*
* @param nodeRef node reference
*/
private void registerAllContentForDestruction(NodeRef nodeRef)
private void registerAllContentForDestruction(NodeRef nodeRef, boolean clearContentProperty)
{
// get node type
QName nodeType = nodeService.getType(nodeRef);
Map<QName, Serializable> properties = nodeService.getProperties(nodeRef);
// get type properties
Collection<QName> nodeProperties = dictionaryService.getAllProperties(nodeType);
for (QName nodeProperty : nodeProperties)
for (Map.Entry<QName, Serializable> entry : properties.entrySet())
{
// get property definition
PropertyDefinition propertyDefinition = dictionaryService.getProperty(nodeProperty);
// if content property
if (propertyDefinition != null &&
DataTypeDefinition.CONTENT.equals(propertyDefinition.getDataType().getName()))
if (entry.getValue() instanceof ContentData)
{
// get content data
ContentData dataContent = (ContentData)nodeService.getProperty(nodeRef, nodeProperty);
ContentData dataContent = (ContentData)entry.getValue();
// if enabled cleanse content
if (isCleansingEnabled())
@@ -204,6 +246,12 @@ public class ContentDestructionComponent implements NodeServicePolicies.BeforeDe
// register for immediate destruction
eagerContentStoreCleaner.registerOrphanedContentUrl(dataContent.getContentUrl(), true);
}
// clear the property
if (clearContentProperty)
{
nodeService.removeProperty(nodeRef, entry.getKey());
}
}
}
}

View File

@@ -140,7 +140,7 @@ public class EagerContentStoreCleaner extends org.alfresco.repo.content.cleanup.
contentCleanser.cleanse(file);
}
}
catch (Throwable e)
catch (Exception e)
{
logger.error(
"Content cleansing failed: \n" +

View File

@@ -53,8 +53,7 @@ public abstract class ContentCleanser
try
{
// get an output stream
OutputStream os = new BufferedOutputStream(new FileOutputStream(file));
try
try (OutputStream os = new BufferedOutputStream(new FileOutputStream(file)))
{
for (int i = 0; i < bytes; i++)
{
@@ -62,11 +61,6 @@ public abstract class ContentCleanser
overwriteOperation.operation(os);
}
}
finally
{
// close ouput stream
try {os.close(); } catch (Throwable e) {}
}
}
catch (IOException ioException)
{
@@ -101,7 +95,7 @@ public abstract class ContentCleanser
{
public void operation(OutputStream os) throws IOException
{
os.write(1);
os.write(0xff);
}
};