mirror of
https://github.com/Alfresco/alfresco-community-repo.git
synced 2025-07-31 17:39:05 +00:00
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:
@@ -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
|
||||
|
@@ -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());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -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" +
|
||||
|
@@ -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);
|
||||
}
|
||||
};
|
||||
|
||||
|
Reference in New Issue
Block a user