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:
@@ -259,7 +259,7 @@
|
|||||||
<bean id="destroy" class="org.alfresco.module.org_alfresco_module_rm.action.impl.DestroyAction" parent="rmAction"
|
<bean id="destroy" class="org.alfresco.module.org_alfresco_module_rm.action.impl.DestroyAction" parent="rmAction"
|
||||||
depends-on="rmDestroyRecordsScheduledForDestructionCapability">
|
depends-on="rmDestroyRecordsScheduledForDestructionCapability">
|
||||||
<property name="capabilityService" ref="CapabilityService" />
|
<property name="capabilityService" ref="CapabilityService" />
|
||||||
<property name="eagerContentStoreCleaner" ref="eagerContentStoreCleaner"/>
|
<property name="contentDestructionComponent" ref="contentDestructionComponent"/>
|
||||||
<property name="ghostingEnabled">
|
<property name="ghostingEnabled">
|
||||||
<value>${rm.ghosting.enabled}</value>
|
<value>${rm.ghosting.enabled}</value>
|
||||||
</property>
|
</property>
|
||||||
|
@@ -452,6 +452,12 @@
|
|||||||
<classifier>tests</classifier>
|
<classifier>tests</classifier>
|
||||||
<scope>test</scope>
|
<scope>test</scope>
|
||||||
</dependency>
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.alfresco.test</groupId>
|
||||||
|
<artifactId>alfresco-testng</artifactId>
|
||||||
|
<version>1.1</version>
|
||||||
|
<scope>test</scope>
|
||||||
|
</dependency>
|
||||||
<!-- Database drivers -->
|
<!-- Database drivers -->
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>postgresql</groupId>
|
<groupId>postgresql</groupId>
|
||||||
|
@@ -21,19 +21,13 @@ package org.alfresco.module.org_alfresco_module_rm.action.impl;
|
|||||||
import java.io.Serializable;
|
import java.io.Serializable;
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
import java.util.List;
|
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.action.RMDispositionActionExecuterAbstractBase;
|
||||||
import org.alfresco.module.org_alfresco_module_rm.capability.CapabilityService;
|
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.DispositionActionDefinition;
|
||||||
import org.alfresco.module.org_alfresco_module_rm.disposition.DispositionSchedule;
|
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.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.repository.NodeRef;
|
||||||
import org.alfresco.service.cmr.security.AccessStatus;
|
import org.alfresco.service.cmr.security.AccessStatus;
|
||||||
import org.alfresco.service.namespace.QName;
|
import org.alfresco.service.namespace.QName;
|
||||||
@@ -49,9 +43,9 @@ public class DestroyAction extends RMDispositionActionExecuterAbstractBase
|
|||||||
/** Action name */
|
/** Action name */
|
||||||
public static final String NAME = "destroy";
|
public static final String NAME = "destroy";
|
||||||
|
|
||||||
/** Eager content store cleaner */
|
/** content destruction component */
|
||||||
private EagerContentStoreCleaner eagerContentStoreCleaner;
|
private ContentDestructionComponent contentDestructionComponent;
|
||||||
|
|
||||||
/** Capability service */
|
/** Capability service */
|
||||||
private CapabilityService capabilityService;
|
private CapabilityService capabilityService;
|
||||||
|
|
||||||
@@ -59,13 +53,13 @@ public class DestroyAction extends RMDispositionActionExecuterAbstractBase
|
|||||||
private boolean ghostingEnabled = true;
|
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
|
* @param capabilityService capability service
|
||||||
*/
|
*/
|
||||||
@@ -128,10 +122,12 @@ public class DestroyAction extends RMDispositionActionExecuterAbstractBase
|
|||||||
}
|
}
|
||||||
if (isGhostOnDestroySetForAction(action, recordFolder))
|
if (isGhostOnDestroySetForAction(action, recordFolder))
|
||||||
{
|
{
|
||||||
|
// add aspect
|
||||||
getNodeService().addAspect(recordFolder, ASPECT_GHOSTED, Collections.<QName, Serializable> emptyMap());
|
getNodeService().addAspect(recordFolder, ASPECT_GHOSTED, Collections.<QName, Serializable> emptyMap());
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
// just delete the node
|
||||||
getNodeService().deleteNode(recordFolder);
|
getNodeService().deleteNode(recordFolder);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -141,95 +137,22 @@ public class DestroyAction extends RMDispositionActionExecuterAbstractBase
|
|||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
protected void executeRecordLevelDisposition(Action action, NodeRef record)
|
protected void executeRecordLevelDisposition(Action action, NodeRef record)
|
||||||
{
|
{
|
||||||
// Clear the content
|
|
||||||
clearAllContent(record);
|
|
||||||
|
|
||||||
// Clear thumbnail content
|
|
||||||
clearThumbnails(record);
|
|
||||||
|
|
||||||
if (isGhostOnDestroySetForAction(action, record))
|
if (isGhostOnDestroySetForAction(action, record))
|
||||||
{
|
{
|
||||||
// Add the ghosted aspect
|
// Add the ghosted aspect
|
||||||
getNodeService().addAspect(record, ASPECT_GHOSTED, null);
|
getNodeService().addAspect(record, ASPECT_GHOSTED, null);
|
||||||
|
|
||||||
|
// destroy content
|
||||||
|
contentDestructionComponent.destroyContent(record);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
// If ghosting is not enabled, delete the node
|
// just delete the node
|
||||||
getNodeService().deleteNode(record);
|
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
|
* Return true if the ghost on destroy property is set against the
|
||||||
* definition for the passed action on the specified node
|
* definition for the passed action on the specified node
|
||||||
|
@@ -18,8 +18,12 @@
|
|||||||
*/
|
*/
|
||||||
package org.alfresco.module.org_alfresco_module_rm.content;
|
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.classification.ContentClassificationService;
|
||||||
import org.alfresco.module.org_alfresco_module_rm.record.RecordService;
|
import org.alfresco.module.org_alfresco_module_rm.record.RecordService;
|
||||||
import org.alfresco.module.org_alfresco_module_rm.util.AuthenticationUtil;
|
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.BehaviourBean;
|
||||||
import org.alfresco.repo.policy.annotation.BehaviourKind;
|
import org.alfresco.repo.policy.annotation.BehaviourKind;
|
||||||
import org.alfresco.repo.security.authentication.AuthenticationUtil.RunAsWork;
|
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.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.ContentData;
|
||||||
import org.alfresco.service.cmr.repository.NodeRef;
|
import org.alfresco.service.cmr.repository.NodeRef;
|
||||||
import org.alfresco.service.cmr.repository.NodeService;
|
import org.alfresco.service.cmr.repository.NodeService;
|
||||||
@@ -162,36 +165,75 @@ public class ContentDestructionComponent implements NodeServicePolicies.BeforeDe
|
|||||||
recordService.isRecord(nodeRef))
|
recordService.isRecord(nodeRef))
|
||||||
{
|
{
|
||||||
// then register all content for destruction
|
// then register all content for destruction
|
||||||
registerAllContentForDestruction(nodeRef);
|
registerAllContentForDestruction(nodeRef, false);
|
||||||
}
|
}
|
||||||
return null;
|
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.
|
* Registers all content on the given node for destruction.
|
||||||
*
|
*
|
||||||
* @param nodeRef node reference
|
* @param nodeRef node reference
|
||||||
*/
|
*/
|
||||||
private void registerAllContentForDestruction(NodeRef nodeRef)
|
private void registerAllContentForDestruction(NodeRef nodeRef, boolean clearContentProperty)
|
||||||
{
|
{
|
||||||
// get node type
|
Map<QName, Serializable> properties = nodeService.getProperties(nodeRef);
|
||||||
QName nodeType = nodeService.getType(nodeRef);
|
|
||||||
|
|
||||||
// get type properties
|
for (Map.Entry<QName, Serializable> entry : properties.entrySet())
|
||||||
Collection<QName> nodeProperties = dictionaryService.getAllProperties(nodeType);
|
|
||||||
for (QName nodeProperty : nodeProperties)
|
|
||||||
{
|
{
|
||||||
// get property definition
|
if (entry.getValue() instanceof ContentData)
|
||||||
PropertyDefinition propertyDefinition = dictionaryService.getProperty(nodeProperty);
|
|
||||||
|
|
||||||
// if content property
|
|
||||||
if (propertyDefinition != null &&
|
|
||||||
DataTypeDefinition.CONTENT.equals(propertyDefinition.getDataType().getName()))
|
|
||||||
{
|
{
|
||||||
// get content data
|
// get content data
|
||||||
ContentData dataContent = (ContentData)nodeService.getProperty(nodeRef, nodeProperty);
|
ContentData dataContent = (ContentData)entry.getValue();
|
||||||
|
|
||||||
// if enabled cleanse content
|
// if enabled cleanse content
|
||||||
if (isCleansingEnabled())
|
if (isCleansingEnabled())
|
||||||
@@ -204,6 +246,12 @@ public class ContentDestructionComponent implements NodeServicePolicies.BeforeDe
|
|||||||
// register for immediate destruction
|
// register for immediate destruction
|
||||||
eagerContentStoreCleaner.registerOrphanedContentUrl(dataContent.getContentUrl(), true);
|
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);
|
contentCleanser.cleanse(file);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
catch (Throwable e)
|
catch (Exception e)
|
||||||
{
|
{
|
||||||
logger.error(
|
logger.error(
|
||||||
"Content cleansing failed: \n" +
|
"Content cleansing failed: \n" +
|
||||||
|
@@ -53,8 +53,7 @@ public abstract class ContentCleanser
|
|||||||
try
|
try
|
||||||
{
|
{
|
||||||
// get an output stream
|
// get an output stream
|
||||||
OutputStream os = new BufferedOutputStream(new FileOutputStream(file));
|
try (OutputStream os = new BufferedOutputStream(new FileOutputStream(file)))
|
||||||
try
|
|
||||||
{
|
{
|
||||||
for (int i = 0; i < bytes; i++)
|
for (int i = 0; i < bytes; i++)
|
||||||
{
|
{
|
||||||
@@ -62,11 +61,6 @@ public abstract class ContentCleanser
|
|||||||
overwriteOperation.operation(os);
|
overwriteOperation.operation(os);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
finally
|
|
||||||
{
|
|
||||||
// close ouput stream
|
|
||||||
try {os.close(); } catch (Throwable e) {}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
catch (IOException ioException)
|
catch (IOException ioException)
|
||||||
{
|
{
|
||||||
@@ -101,7 +95,7 @@ public abstract class ContentCleanser
|
|||||||
{
|
{
|
||||||
public void operation(OutputStream os) throws IOException
|
public void operation(OutputStream os) throws IOException
|
||||||
{
|
{
|
||||||
os.write(1);
|
os.write(0xff);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@@ -0,0 +1,552 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2005-2015 Alfresco Software Limited.
|
||||||
|
*
|
||||||
|
* This file is part of Alfresco
|
||||||
|
*
|
||||||
|
* Alfresco is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU Lesser General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* Alfresco is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU Lesser General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU Lesser General Public License
|
||||||
|
* along with Alfresco. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
package org.alfresco.module.org_alfresco_module_rm.test.integration.destroy;
|
||||||
|
|
||||||
|
import java.util.Collections;
|
||||||
|
|
||||||
|
import org.alfresco.module.org_alfresco_module_rm.action.impl.CutOffAction;
|
||||||
|
import org.alfresco.module.org_alfresco_module_rm.action.impl.DestroyAction;
|
||||||
|
import org.alfresco.module.org_alfresco_module_rm.classification.ClassificationAspectProperties;
|
||||||
|
import org.alfresco.module.org_alfresco_module_rm.content.ContentDestructionComponent;
|
||||||
|
import org.alfresco.module.org_alfresco_module_rm.content.EagerContentStoreCleaner;
|
||||||
|
import org.alfresco.module.org_alfresco_module_rm.test.util.BaseRMTestCase;
|
||||||
|
import org.alfresco.module.org_alfresco_module_rm.test.util.CommonRMTestUtils;
|
||||||
|
import org.alfresco.module.org_alfresco_module_rm.test.util.TestContentCleanser;
|
||||||
|
import org.alfresco.repo.content.ContentStore;
|
||||||
|
import org.alfresco.repo.content.MimetypeMap;
|
||||||
|
import org.alfresco.service.cmr.repository.ContentData;
|
||||||
|
import org.alfresco.service.cmr.repository.ContentReader;
|
||||||
|
import org.alfresco.service.cmr.repository.ContentWriter;
|
||||||
|
import org.alfresco.service.cmr.repository.NodeRef;
|
||||||
|
import org.alfresco.test.AlfrescoTest;
|
||||||
|
import org.alfresco.util.GUID;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Acceptance criteria for content destruction and content cleansing.
|
||||||
|
*
|
||||||
|
* @author Roy Wetherall
|
||||||
|
* @Author 3.0.a
|
||||||
|
*/
|
||||||
|
public class DestroyContentTest extends BaseRMTestCase
|
||||||
|
{
|
||||||
|
private static final String BEAN_NAME_CONTENT_CLEANSER = "contentCleanser.test";
|
||||||
|
|
||||||
|
private ContentStore contentStore;
|
||||||
|
private TestContentCleanser contentCleanser;
|
||||||
|
private EagerContentStoreCleaner eagerContentStoreCleaner;
|
||||||
|
private ContentDestructionComponent contentDestructionComponent;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void initServices()
|
||||||
|
{
|
||||||
|
super.initServices();
|
||||||
|
contentStore = (ContentStore)applicationContext.getBean("fileContentStore");
|
||||||
|
contentCleanser = (TestContentCleanser)applicationContext.getBean(BEAN_NAME_CONTENT_CLEANSER);
|
||||||
|
eagerContentStoreCleaner = (EagerContentStoreCleaner)applicationContext.getBean("eagerContentStoreCleaner");
|
||||||
|
contentDestructionComponent = (ContentDestructionComponent)applicationContext.getBean("contentDestructionComponent");
|
||||||
|
|
||||||
|
// set the test content store cleaner
|
||||||
|
eagerContentStoreCleaner.setContentCleanser(contentCleanser);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Given that a record folder is eligible for destruction
|
||||||
|
* And record ghosting is applied
|
||||||
|
* When the record folder is destroyed
|
||||||
|
* Then the record folder and records are ghosted
|
||||||
|
* And the content is destroyed
|
||||||
|
*/
|
||||||
|
@AlfrescoTest (jira="RM-2506")
|
||||||
|
public void testRecordFolderDestroy() throws Exception
|
||||||
|
{
|
||||||
|
doBehaviourDrivenTest(new BehaviourDrivenTest()
|
||||||
|
{
|
||||||
|
private NodeRef recordCategoryFolderLevel;
|
||||||
|
private NodeRef destroyableFolder;
|
||||||
|
private NodeRef subRecord;
|
||||||
|
|
||||||
|
public void given() throws Exception
|
||||||
|
{
|
||||||
|
// create destroyable record folder that contains a record
|
||||||
|
recordCategoryFolderLevel = filePlanService.createRecordCategory(filePlan, GUID.generate());
|
||||||
|
utils.createBasicDispositionSchedule(
|
||||||
|
recordCategoryFolderLevel,
|
||||||
|
CommonRMTestUtils.DEFAULT_DISPOSITION_INSTRUCTIONS,
|
||||||
|
CommonRMTestUtils.DEFAULT_DISPOSITION_AUTHORITY,
|
||||||
|
false,
|
||||||
|
true);
|
||||||
|
destroyableFolder = recordFolderService.createRecordFolder(recordCategoryFolderLevel, GUID.generate());
|
||||||
|
subRecord = utils.createRecord(destroyableFolder, GUID.generate(), GUID.generate());
|
||||||
|
utils.completeRecord(subRecord);
|
||||||
|
utils.completeEvent(destroyableFolder, CommonRMTestUtils.DEFAULT_EVENT_NAME);
|
||||||
|
rmActionService.executeRecordsManagementAction(destroyableFolder, CutOffAction.NAME);
|
||||||
|
|
||||||
|
// assert things are as we expect
|
||||||
|
assertEquals(DestroyAction.NAME, dispositionService.getNextDispositionAction(destroyableFolder).getName());
|
||||||
|
assertTrue(dispositionService.isNextDispositionActionEligible(destroyableFolder));
|
||||||
|
|
||||||
|
// reset test content cleanser
|
||||||
|
contentCleanser.reset();
|
||||||
|
assertFalse(contentDestructionComponent.isCleansingEnabled());
|
||||||
|
}
|
||||||
|
|
||||||
|
public void when() throws Exception
|
||||||
|
{
|
||||||
|
// destroy the folder
|
||||||
|
rmActionService.executeRecordsManagementAction(destroyableFolder, DestroyAction.NAME);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void then() throws Exception
|
||||||
|
{
|
||||||
|
// folder and record exist and are ghosted
|
||||||
|
assertTrue(nodeService.exists(destroyableFolder));
|
||||||
|
assertTrue(nodeService.hasAspect(destroyableFolder, ASPECT_GHOSTED));
|
||||||
|
assertTrue(nodeService.exists(subRecord));
|
||||||
|
assertTrue(nodeService.hasAspect(subRecord, ASPECT_GHOSTED));
|
||||||
|
|
||||||
|
// record content is destroyed
|
||||||
|
ContentReader reader = contentService.getReader(subRecord, PROP_CONTENT);
|
||||||
|
assertNull(reader);
|
||||||
|
|
||||||
|
// content cleansing hasn't taken place
|
||||||
|
assertFalse(contentCleanser.hasCleansed());
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Given that a record is eligible for destruction
|
||||||
|
* And record ghosting is applied
|
||||||
|
* When the record is destroyed
|
||||||
|
* Then the record is ghosted
|
||||||
|
* And the content is destroyed
|
||||||
|
*/
|
||||||
|
@AlfrescoTest (jira="RM-2506")
|
||||||
|
public void testRecordDestroy() throws Exception
|
||||||
|
{
|
||||||
|
doBehaviourDrivenTest(new BehaviourDrivenTest()
|
||||||
|
{
|
||||||
|
private NodeRef recordCategoryRecordLevel;
|
||||||
|
private NodeRef recordFolder;
|
||||||
|
private NodeRef destroyableRecord;
|
||||||
|
|
||||||
|
public void given() throws Exception
|
||||||
|
{
|
||||||
|
// create destroyable record
|
||||||
|
recordCategoryRecordLevel = filePlanService.createRecordCategory(filePlan, GUID.generate());
|
||||||
|
utils.createBasicDispositionSchedule(
|
||||||
|
recordCategoryRecordLevel,
|
||||||
|
CommonRMTestUtils.DEFAULT_DISPOSITION_INSTRUCTIONS,
|
||||||
|
CommonRMTestUtils.DEFAULT_DISPOSITION_AUTHORITY,
|
||||||
|
true,
|
||||||
|
true);
|
||||||
|
recordFolder = recordFolderService.createRecordFolder(recordCategoryRecordLevel, GUID.generate());
|
||||||
|
destroyableRecord = utils.createRecord(recordFolder, GUID.generate(), GUID.generate());
|
||||||
|
utils.completeRecord(destroyableRecord);
|
||||||
|
utils.completeEvent(destroyableRecord, CommonRMTestUtils.DEFAULT_EVENT_NAME);
|
||||||
|
rmActionService.executeRecordsManagementAction(destroyableRecord, CutOffAction.NAME);
|
||||||
|
|
||||||
|
// assert things are as we expect
|
||||||
|
assertEquals(DestroyAction.NAME, dispositionService.getNextDispositionAction(destroyableRecord).getName());
|
||||||
|
assertTrue(dispositionService.isNextDispositionActionEligible(destroyableRecord));
|
||||||
|
|
||||||
|
// reset test content cleanser
|
||||||
|
contentCleanser.reset();
|
||||||
|
assertFalse(contentDestructionComponent.isCleansingEnabled());
|
||||||
|
}
|
||||||
|
|
||||||
|
public void when() throws Exception
|
||||||
|
{
|
||||||
|
// destroy the folder
|
||||||
|
rmActionService.executeRecordsManagementAction(destroyableRecord, DestroyAction.NAME);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void then() throws Exception
|
||||||
|
{
|
||||||
|
// show that record still exists and has the ghosted aspect applied
|
||||||
|
assertTrue(nodeService.exists(destroyableRecord));
|
||||||
|
assertTrue(nodeService.hasAspect(destroyableRecord, ASPECT_GHOSTED));
|
||||||
|
|
||||||
|
// record content is destroyed
|
||||||
|
ContentReader reader = contentService.getReader(destroyableRecord, PROP_CONTENT);
|
||||||
|
assertNull(reader);
|
||||||
|
|
||||||
|
// content cleansing hasn't taken place
|
||||||
|
assertFalse(contentCleanser.hasCleansed());
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Given that a record is eligible for destruction
|
||||||
|
* And record ghosting is applied
|
||||||
|
* And cleansing is configured on
|
||||||
|
* When the record is destroyed
|
||||||
|
* Then the record is ghosted
|
||||||
|
* And the content is cleansed
|
||||||
|
* And then content is destroyed
|
||||||
|
*/
|
||||||
|
@AlfrescoTest (jira="RM-2505")
|
||||||
|
public void testRecordDestroyAndCleanse() throws Exception
|
||||||
|
{
|
||||||
|
doBehaviourDrivenTest(new BehaviourDrivenTest()
|
||||||
|
{
|
||||||
|
private NodeRef recordCategoryRecordLevel;
|
||||||
|
private NodeRef recordFolder;
|
||||||
|
private NodeRef destroyableRecord;
|
||||||
|
|
||||||
|
public void given() throws Exception
|
||||||
|
{
|
||||||
|
// create destroyable record
|
||||||
|
recordCategoryRecordLevel = filePlanService.createRecordCategory(filePlan, GUID.generate());
|
||||||
|
utils.createBasicDispositionSchedule(
|
||||||
|
recordCategoryRecordLevel,
|
||||||
|
CommonRMTestUtils.DEFAULT_DISPOSITION_INSTRUCTIONS,
|
||||||
|
CommonRMTestUtils.DEFAULT_DISPOSITION_AUTHORITY,
|
||||||
|
true,
|
||||||
|
true);
|
||||||
|
recordFolder = recordFolderService.createRecordFolder(recordCategoryRecordLevel, GUID.generate());
|
||||||
|
destroyableRecord = utils.createRecord(recordFolder, GUID.generate(), GUID.generate());
|
||||||
|
utils.completeRecord(destroyableRecord);
|
||||||
|
utils.completeEvent(destroyableRecord, CommonRMTestUtils.DEFAULT_EVENT_NAME);
|
||||||
|
rmActionService.executeRecordsManagementAction(destroyableRecord, CutOffAction.NAME);
|
||||||
|
|
||||||
|
// assert things are as we expect
|
||||||
|
assertEquals(DestroyAction.NAME, dispositionService.getNextDispositionAction(destroyableRecord).getName());
|
||||||
|
assertTrue(dispositionService.isNextDispositionActionEligible(destroyableRecord));
|
||||||
|
|
||||||
|
// reset test content cleanser and configure on
|
||||||
|
contentCleanser.reset();
|
||||||
|
contentDestructionComponent.setCleansingEnabled(true);
|
||||||
|
assertTrue(contentDestructionComponent.isCleansingEnabled());
|
||||||
|
}
|
||||||
|
|
||||||
|
public void when() throws Exception
|
||||||
|
{
|
||||||
|
// destroy the folder
|
||||||
|
rmActionService.executeRecordsManagementAction(destroyableRecord, DestroyAction.NAME);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void then() throws Exception
|
||||||
|
{
|
||||||
|
// show that record still exists and has the ghosted aspect applied
|
||||||
|
assertTrue(nodeService.exists(destroyableRecord));
|
||||||
|
assertTrue(nodeService.hasAspect(destroyableRecord, ASPECT_GHOSTED));
|
||||||
|
|
||||||
|
// record content is destroyed
|
||||||
|
ContentReader reader = contentService.getReader(destroyableRecord, PROP_CONTENT);
|
||||||
|
assertNull(reader);
|
||||||
|
|
||||||
|
// content cleansing has taken place
|
||||||
|
assertTrue(contentCleanser.hasCleansed());
|
||||||
|
}
|
||||||
|
|
||||||
|
public void after() throws Exception
|
||||||
|
{
|
||||||
|
// reset cleansing to default
|
||||||
|
contentDestructionComponent.setCleansingEnabled(false);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* When the a record is deleted
|
||||||
|
* Then the content is destroyed
|
||||||
|
*/
|
||||||
|
@AlfrescoTest (jira="RM-2461")
|
||||||
|
public void testRecordDelete() throws Exception
|
||||||
|
{
|
||||||
|
doBehaviourDrivenTest(new BehaviourDrivenTest()
|
||||||
|
{
|
||||||
|
private NodeRef recordCategoryRecordLevel;
|
||||||
|
private NodeRef recordFolder;
|
||||||
|
private NodeRef deleteableRecord;
|
||||||
|
private ContentData contentData;
|
||||||
|
|
||||||
|
public void given() throws Exception
|
||||||
|
{
|
||||||
|
// create destroyable record
|
||||||
|
recordCategoryRecordLevel = filePlanService.createRecordCategory(filePlan, GUID.generate());
|
||||||
|
recordFolder = recordFolderService.createRecordFolder(recordCategoryRecordLevel, GUID.generate());
|
||||||
|
deleteableRecord = utils.createRecord(recordFolder, GUID.generate(), GUID.generate());
|
||||||
|
contentData = (ContentData)nodeService.getProperty(deleteableRecord, PROP_CONTENT);
|
||||||
|
|
||||||
|
// assert things are as we expect
|
||||||
|
assertNotNull(contentData);
|
||||||
|
assertTrue(contentStore.exists(contentData.getContentUrl()));
|
||||||
|
|
||||||
|
// reset test content cleanser
|
||||||
|
contentCleanser.reset();
|
||||||
|
assertFalse(contentDestructionComponent.isCleansingEnabled());
|
||||||
|
}
|
||||||
|
|
||||||
|
public void when() throws Exception
|
||||||
|
{
|
||||||
|
// delete the record
|
||||||
|
nodeService.deleteNode(deleteableRecord);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void then() throws Exception
|
||||||
|
{
|
||||||
|
// record destroyed
|
||||||
|
assertFalse(nodeService.exists(deleteableRecord));
|
||||||
|
assertFalse(contentStore.exists(contentData.getContentUrl()));
|
||||||
|
|
||||||
|
// content cleansing hasn't taken place
|
||||||
|
assertFalse(contentCleanser.hasCleansed());
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Given cleansing is configured on
|
||||||
|
* When the a record is deleted
|
||||||
|
* Then the content is cleansed
|
||||||
|
* And then the content is destroyed
|
||||||
|
*/
|
||||||
|
@AlfrescoTest (jira="RM-2460")
|
||||||
|
public void testRecordDeleteAndCleanse() throws Exception
|
||||||
|
{
|
||||||
|
doBehaviourDrivenTest(new BehaviourDrivenTest()
|
||||||
|
{
|
||||||
|
private NodeRef recordCategoryRecordLevel;
|
||||||
|
private NodeRef recordFolder;
|
||||||
|
private NodeRef deleteableRecord;
|
||||||
|
private ContentData contentData;
|
||||||
|
|
||||||
|
public void given() throws Exception
|
||||||
|
{
|
||||||
|
// create destroyable record
|
||||||
|
recordCategoryRecordLevel = filePlanService.createRecordCategory(filePlan, GUID.generate());
|
||||||
|
recordFolder = recordFolderService.createRecordFolder(recordCategoryRecordLevel, GUID.generate());
|
||||||
|
deleteableRecord = utils.createRecord(recordFolder, GUID.generate(), GUID.generate());
|
||||||
|
contentData = (ContentData)nodeService.getProperty(deleteableRecord, PROP_CONTENT);
|
||||||
|
|
||||||
|
// assert things are as we expect
|
||||||
|
assertNotNull(contentData);
|
||||||
|
assertTrue(contentStore.exists(contentData.getContentUrl()));
|
||||||
|
|
||||||
|
// reset test content cleanser and configure on
|
||||||
|
contentCleanser.reset();
|
||||||
|
contentDestructionComponent.setCleansingEnabled(true);
|
||||||
|
assertTrue(contentDestructionComponent.isCleansingEnabled());
|
||||||
|
}
|
||||||
|
|
||||||
|
public void when() throws Exception
|
||||||
|
{
|
||||||
|
// delete the record
|
||||||
|
nodeService.deleteNode(deleteableRecord);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void then() throws Exception
|
||||||
|
{
|
||||||
|
// record destroyed
|
||||||
|
assertFalse(nodeService.exists(deleteableRecord));
|
||||||
|
assertFalse(contentStore.exists(contentData.getContentUrl()));
|
||||||
|
|
||||||
|
// content cleansing has taken place
|
||||||
|
assertTrue(contentCleanser.hasCleansed());
|
||||||
|
}
|
||||||
|
|
||||||
|
public void after() throws Exception
|
||||||
|
{
|
||||||
|
// reset cleansing to default
|
||||||
|
contentDestructionComponent.setCleansingEnabled(false);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* When classified content (non-record) is deleted
|
||||||
|
* Then it is destroyed
|
||||||
|
*/
|
||||||
|
@AlfrescoTest (jira="RM-2461")
|
||||||
|
public void testClassifiedContentDelete() throws Exception
|
||||||
|
{
|
||||||
|
doBehaviourDrivenTest(new BehaviourDrivenTest()
|
||||||
|
{
|
||||||
|
private NodeRef deleteableContent;
|
||||||
|
private ContentData contentData;
|
||||||
|
|
||||||
|
public void given() throws Exception
|
||||||
|
{
|
||||||
|
// create deletable classified content
|
||||||
|
assertTrue(nodeService.exists(folder));
|
||||||
|
deleteableContent = fileFolderService.create(folder, "myDocument.txt", TYPE_CONTENT).getNodeRef();
|
||||||
|
ContentWriter writer = fileFolderService.getWriter(deleteableContent);
|
||||||
|
writer.setEncoding("UTF-8");
|
||||||
|
writer.setMimetype(MimetypeMap.MIMETYPE_TEXT_PLAIN);
|
||||||
|
writer.putContent(GUID.generate());
|
||||||
|
|
||||||
|
// classify the content
|
||||||
|
ClassificationAspectProperties properties = new ClassificationAspectProperties();
|
||||||
|
properties.setClassificationLevelId("level1");
|
||||||
|
properties.setClassifiedBy("me");
|
||||||
|
properties.setClassificationReasonIds(Collections.singleton("Test Reason 1"));
|
||||||
|
contentClassificationService.classifyContent(properties, deleteableContent);
|
||||||
|
|
||||||
|
// grab the content data
|
||||||
|
contentData = (ContentData)nodeService.getProperty(deleteableContent, PROP_CONTENT);
|
||||||
|
|
||||||
|
// assert things are as we expect
|
||||||
|
assertNotNull(contentData);
|
||||||
|
assertTrue(contentStore.exists(contentData.getContentUrl()));
|
||||||
|
|
||||||
|
// reset test content cleanser
|
||||||
|
contentCleanser.reset();
|
||||||
|
assertFalse(contentDestructionComponent.isCleansingEnabled());
|
||||||
|
}
|
||||||
|
|
||||||
|
public void when() throws Exception
|
||||||
|
{
|
||||||
|
// delete the content
|
||||||
|
nodeService.deleteNode(deleteableContent);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void then() throws Exception
|
||||||
|
{
|
||||||
|
// content destroyed
|
||||||
|
assertFalse(nodeService.exists(deleteableContent));
|
||||||
|
assertFalse(contentStore.exists(contentData.getContentUrl()));
|
||||||
|
|
||||||
|
// content cleansing hasn't taken place
|
||||||
|
assertFalse(contentCleanser.hasCleansed());
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Given data cleansing is configured on
|
||||||
|
* When classified content (non-record) is deleted
|
||||||
|
* Then it is cleansed
|
||||||
|
* And then it is destroyed
|
||||||
|
*/
|
||||||
|
@AlfrescoTest (jira="RM-2460")
|
||||||
|
public void testClassifiedContentDeleteAndCleanse() throws Exception
|
||||||
|
{
|
||||||
|
doBehaviourDrivenTest(new BehaviourDrivenTest()
|
||||||
|
{
|
||||||
|
private NodeRef deleteableContent;
|
||||||
|
private ContentData contentData;
|
||||||
|
|
||||||
|
public void given() throws Exception
|
||||||
|
{
|
||||||
|
// create deletable classified content
|
||||||
|
assertTrue(nodeService.exists(folder));
|
||||||
|
deleteableContent = fileFolderService.create(folder, "myDocument.txt", TYPE_CONTENT).getNodeRef();
|
||||||
|
ContentWriter writer = fileFolderService.getWriter(deleteableContent);
|
||||||
|
writer.setEncoding("UTF-8");
|
||||||
|
writer.setMimetype(MimetypeMap.MIMETYPE_TEXT_PLAIN);
|
||||||
|
writer.putContent(GUID.generate());
|
||||||
|
|
||||||
|
// classify the content
|
||||||
|
ClassificationAspectProperties properties = new ClassificationAspectProperties();
|
||||||
|
properties.setClassificationLevelId("level1");
|
||||||
|
properties.setClassifiedBy("me");
|
||||||
|
properties.setClassificationReasonIds(Collections.singleton("Test Reason 1"));
|
||||||
|
contentClassificationService.classifyContent(properties, deleteableContent);
|
||||||
|
|
||||||
|
// grab the content data
|
||||||
|
contentData = (ContentData)nodeService.getProperty(deleteableContent, PROP_CONTENT);
|
||||||
|
|
||||||
|
// assert things are as we expect
|
||||||
|
assertNotNull(contentData);
|
||||||
|
assertTrue(contentStore.exists(contentData.getContentUrl()));
|
||||||
|
|
||||||
|
// reset test content cleanser and configure on
|
||||||
|
contentCleanser.reset();
|
||||||
|
contentDestructionComponent.setCleansingEnabled(true);
|
||||||
|
assertTrue(contentDestructionComponent.isCleansingEnabled());
|
||||||
|
}
|
||||||
|
|
||||||
|
public void when() throws Exception
|
||||||
|
{
|
||||||
|
// delete the content
|
||||||
|
nodeService.deleteNode(deleteableContent);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void then() throws Exception
|
||||||
|
{
|
||||||
|
// content destroyed
|
||||||
|
assertFalse(nodeService.exists(deleteableContent));
|
||||||
|
assertFalse(contentStore.exists(contentData.getContentUrl()));
|
||||||
|
|
||||||
|
// content cleansing has taken place
|
||||||
|
assertTrue(contentCleanser.hasCleansed());
|
||||||
|
}
|
||||||
|
|
||||||
|
public void after() throws Exception
|
||||||
|
{
|
||||||
|
// reset cleansing to default
|
||||||
|
contentDestructionComponent.setCleansingEnabled(false);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* When a unclassified document (non-record) is deleted
|
||||||
|
* Then it is deleted but the the content is not immediately destroyed
|
||||||
|
*/
|
||||||
|
@AlfrescoTest (jira="RM-2507")
|
||||||
|
public void testContentDelete() throws Exception
|
||||||
|
{
|
||||||
|
doBehaviourDrivenTest(new BehaviourDrivenTest()
|
||||||
|
{
|
||||||
|
private NodeRef deleteableContent;
|
||||||
|
private ContentData contentData;
|
||||||
|
|
||||||
|
public void given() throws Exception
|
||||||
|
{
|
||||||
|
// create deletable content
|
||||||
|
assertTrue(nodeService.exists(folder));
|
||||||
|
deleteableContent = fileFolderService.create(folder, "myDocument.txt", TYPE_CONTENT).getNodeRef();
|
||||||
|
ContentWriter writer = fileFolderService.getWriter(deleteableContent);
|
||||||
|
writer.setEncoding("UTF-8");
|
||||||
|
writer.setMimetype(MimetypeMap.MIMETYPE_TEXT_PLAIN);
|
||||||
|
writer.putContent(GUID.generate());
|
||||||
|
|
||||||
|
contentData = (ContentData)nodeService.getProperty(deleteableContent, PROP_CONTENT);
|
||||||
|
|
||||||
|
// assert things are as we expect
|
||||||
|
assertNotNull(contentData);
|
||||||
|
assertTrue(contentStore.exists(contentData.getContentUrl()));
|
||||||
|
|
||||||
|
// reset test content cleanser
|
||||||
|
contentCleanser.reset();
|
||||||
|
assertFalse(contentDestructionComponent.isCleansingEnabled());
|
||||||
|
}
|
||||||
|
|
||||||
|
public void when() throws Exception
|
||||||
|
{
|
||||||
|
// delete the content
|
||||||
|
nodeService.deleteNode(deleteableContent);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void then() throws Exception
|
||||||
|
{
|
||||||
|
// content deleted but not destroyed
|
||||||
|
assertFalse(nodeService.exists(deleteableContent));
|
||||||
|
assertTrue(contentStore.exists(contentData.getContentUrl()));
|
||||||
|
|
||||||
|
// content cleansing hasn't taken place
|
||||||
|
assertFalse(contentCleanser.hasCleansed());
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
@@ -30,6 +30,7 @@ import java.util.Set;
|
|||||||
import org.alfresco.error.AlfrescoRuntimeException;
|
import org.alfresco.error.AlfrescoRuntimeException;
|
||||||
import org.alfresco.model.ContentModel;
|
import org.alfresco.model.ContentModel;
|
||||||
import org.alfresco.module.org_alfresco_module_rm.action.RecordsManagementActionService;
|
import org.alfresco.module.org_alfresco_module_rm.action.RecordsManagementActionService;
|
||||||
|
import org.alfresco.module.org_alfresco_module_rm.action.impl.CompleteEventAction;
|
||||||
import org.alfresco.module.org_alfresco_module_rm.action.impl.CutOffAction;
|
import org.alfresco.module.org_alfresco_module_rm.action.impl.CutOffAction;
|
||||||
import org.alfresco.module.org_alfresco_module_rm.action.impl.DestroyAction;
|
import org.alfresco.module.org_alfresco_module_rm.action.impl.DestroyAction;
|
||||||
import org.alfresco.module.org_alfresco_module_rm.action.impl.TransferAction;
|
import org.alfresco.module.org_alfresco_module_rm.action.impl.TransferAction;
|
||||||
@@ -292,4 +293,20 @@ public class CommonRMTestUtils implements RecordsManagementModel
|
|||||||
|
|
||||||
return filePlanRoleService.createRole(filePlan, roleName, roleName, capabilities);
|
return filePlanRoleService.createRole(filePlan, roleName, roleName, capabilities);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Helper method to complete event on disposable item
|
||||||
|
*
|
||||||
|
* @param disposableItem disposable item (record or record folder)
|
||||||
|
* @param eventName event name
|
||||||
|
*/
|
||||||
|
public void completeEvent(NodeRef disposableItem, String eventName)
|
||||||
|
{
|
||||||
|
// build action properties
|
||||||
|
Map<String, Serializable> params = new HashMap<String, Serializable>(1);
|
||||||
|
params.put(CompleteEventAction.PARAM_EVENT_NAME, eventName);
|
||||||
|
|
||||||
|
// complete event
|
||||||
|
actionService.executeRecordsManagementAction(disposableItem, CompleteEventAction.NAME, params);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@@ -0,0 +1,56 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2005-2015 Alfresco Software Limited.
|
||||||
|
*
|
||||||
|
* This file is part of Alfresco
|
||||||
|
*
|
||||||
|
* Alfresco is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU Lesser General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* Alfresco is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU Lesser General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU Lesser General Public License
|
||||||
|
* along with Alfresco. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
package org.alfresco.module.org_alfresco_module_rm.test.util;
|
||||||
|
|
||||||
|
import java.io.File;
|
||||||
|
|
||||||
|
import org.alfresco.module.org_alfresco_module_rm.content.cleanser.ContentCleanser522022M;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Test Content Cleanser
|
||||||
|
*
|
||||||
|
* @author Roy Wetherall
|
||||||
|
* @since 3.0.a
|
||||||
|
*/
|
||||||
|
public class TestContentCleanser extends ContentCleanser522022M
|
||||||
|
{
|
||||||
|
private boolean hasCleansed = false;
|
||||||
|
|
||||||
|
public void reset()
|
||||||
|
{
|
||||||
|
hasCleansed = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean hasCleansed()
|
||||||
|
{
|
||||||
|
return hasCleansed;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @see org.alfresco.module.org_alfresco_module_rm.content.cleanser.ContentCleanser#cleanse(java.io.File)
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public void cleanse(File file)
|
||||||
|
{
|
||||||
|
hasCleansed = false;
|
||||||
|
super.cleanse(file);
|
||||||
|
hasCleansed = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@@ -234,4 +234,9 @@
|
|||||||
</list>
|
</list>
|
||||||
</property>
|
</property>
|
||||||
</bean>
|
</bean>
|
||||||
|
|
||||||
|
<!-- Test content cleanser -->
|
||||||
|
|
||||||
|
<bean id="contentCleanser.test" class="org.alfresco.module.org_alfresco_module_rm.test.util.TestContentCleanser"/>
|
||||||
|
|
||||||
</beans>
|
</beans>
|
@@ -18,24 +18,20 @@
|
|||||||
*/
|
*/
|
||||||
package org.alfresco.module.org_alfresco_module_rm.content;
|
package org.alfresco.module.org_alfresco_module_rm.content;
|
||||||
|
|
||||||
import static org.mockito.Matchers.any;
|
|
||||||
import static org.mockito.Matchers.eq;
|
|
||||||
import static org.mockito.Mockito.mock;
|
import static org.mockito.Mockito.mock;
|
||||||
import static org.mockito.Mockito.times;
|
import static org.mockito.Mockito.times;
|
||||||
import static org.mockito.Mockito.verify;
|
import static org.mockito.Mockito.verify;
|
||||||
import static org.mockito.Mockito.verifyZeroInteractions;
|
import static org.mockito.Mockito.verifyZeroInteractions;
|
||||||
import static org.mockito.Mockito.when;
|
import static org.mockito.Mockito.when;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.io.Serializable;
|
||||||
import java.util.List;
|
import java.util.HashMap;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
import org.alfresco.model.ContentModel;
|
|
||||||
import org.alfresco.module.org_alfresco_module_rm.classification.ContentClassificationService;
|
import org.alfresco.module.org_alfresco_module_rm.classification.ContentClassificationService;
|
||||||
import org.alfresco.module.org_alfresco_module_rm.content.cleanser.ContentCleanser;
|
import org.alfresco.module.org_alfresco_module_rm.content.cleanser.ContentCleanser;
|
||||||
import org.alfresco.module.org_alfresco_module_rm.test.util.AlfMock;
|
import org.alfresco.module.org_alfresco_module_rm.test.util.AlfMock;
|
||||||
import org.alfresco.module.org_alfresco_module_rm.test.util.BaseUnitTest;
|
import org.alfresco.module.org_alfresco_module_rm.test.util.BaseUnitTest;
|
||||||
import org.alfresco.service.cmr.dictionary.DataTypeDefinition;
|
|
||||||
import org.alfresco.service.cmr.dictionary.PropertyDefinition;
|
|
||||||
import org.alfresco.service.cmr.repository.ContentData;
|
import org.alfresco.service.cmr.repository.ContentData;
|
||||||
import org.alfresco.service.cmr.repository.NodeRef;
|
import org.alfresco.service.cmr.repository.NodeRef;
|
||||||
import org.alfresco.service.namespace.QName;
|
import org.alfresco.service.namespace.QName;
|
||||||
@@ -79,8 +75,10 @@ public class ContentDestructionComponentUnitTest extends BaseUnitTest
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Given a record
|
* Given a record
|
||||||
|
* And that by default cleansing is off
|
||||||
* When it is deleted
|
* When it is deleted
|
||||||
* Then it is sent for immediate destruction
|
* Then it is sent for immediate destruction
|
||||||
|
* And not cleansed
|
||||||
*/
|
*/
|
||||||
@Test
|
@Test
|
||||||
public void deleteRecord()
|
public void deleteRecord()
|
||||||
@@ -100,8 +98,10 @@ public class ContentDestructionComponentUnitTest extends BaseUnitTest
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Given classified content
|
* Given classified content
|
||||||
|
* And that by default cleansing is off
|
||||||
* When it is deleted
|
* When it is deleted
|
||||||
* Then it is send for immediate destruction
|
* Then it is send for immediate destruction
|
||||||
|
* And not cleansed
|
||||||
*/
|
*/
|
||||||
@Test
|
@Test
|
||||||
public void deleteClassifiedContent()
|
public void deleteClassifiedContent()
|
||||||
@@ -122,7 +122,7 @@ public class ContentDestructionComponentUnitTest extends BaseUnitTest
|
|||||||
/**
|
/**
|
||||||
* Given that content cleansing is turned on
|
* Given that content cleansing is turned on
|
||||||
* When a sensitive node is deleted
|
* When a sensitive node is deleted
|
||||||
* Then it is not scheduled for cleansing before destruction
|
* Then it is scheduled for cleansing before destruction
|
||||||
*/
|
*/
|
||||||
@Test
|
@Test
|
||||||
public void contentCleansingOn()
|
public void contentCleansingOn()
|
||||||
@@ -144,7 +144,7 @@ public class ContentDestructionComponentUnitTest extends BaseUnitTest
|
|||||||
/**
|
/**
|
||||||
* Given that content cleansing is turned off
|
* Given that content cleansing is turned off
|
||||||
* When a sensitive node is deleted
|
* When a sensitive node is deleted
|
||||||
* Then it is scheduled for cleansing before destruction
|
* Then it is not scheduled for cleansing before destruction
|
||||||
*/
|
*/
|
||||||
@Test
|
@Test
|
||||||
public void contentCleansingOff()
|
public void contentCleansingOff()
|
||||||
@@ -202,31 +202,18 @@ public class ContentDestructionComponentUnitTest extends BaseUnitTest
|
|||||||
{
|
{
|
||||||
NodeRef nodeRef = generateCmContent("myContent.txt");
|
NodeRef nodeRef = generateCmContent("myContent.txt");
|
||||||
|
|
||||||
List<QName> contentProperties = new ArrayList<QName>(contentPropertiesCount);
|
ContentData mockedContentData = mock(ContentData.class);
|
||||||
for (int i = 0; i < contentPropertiesCount; i++)
|
when(mockedContentData.getContentUrl())
|
||||||
{
|
|
||||||
contentProperties.add(AlfMock.generateQName());
|
|
||||||
}
|
|
||||||
|
|
||||||
when(mockedDictionaryService.getAllProperties(ContentModel.TYPE_CONTENT))
|
|
||||||
.thenReturn(contentProperties);
|
|
||||||
|
|
||||||
DataTypeDefinition mockedDataTypeDefinition = mock(DataTypeDefinition.class);
|
|
||||||
when(mockedDataTypeDefinition.getName())
|
|
||||||
.thenReturn(DataTypeDefinition.CONTENT);
|
|
||||||
|
|
||||||
PropertyDefinition mockedPropertyDefinition = mock(PropertyDefinition.class);
|
|
||||||
when(mockedPropertyDefinition.getDataType())
|
|
||||||
.thenReturn(mockedDataTypeDefinition);
|
|
||||||
|
|
||||||
when(mockedDictionaryService.getProperty(any(QName.class)))
|
|
||||||
.thenReturn(mockedPropertyDefinition);
|
|
||||||
|
|
||||||
ContentData mockedDataContent = mock(ContentData.class);
|
|
||||||
when(mockedDataContent.getContentUrl())
|
|
||||||
.thenReturn(contentURL);
|
.thenReturn(contentURL);
|
||||||
when(mockedNodeService.getProperty(eq(nodeRef), any(QName.class)))
|
|
||||||
.thenReturn(mockedDataContent);
|
Map<QName, Serializable> propertiesMap = new HashMap<QName, Serializable>(contentPropertiesCount);
|
||||||
|
for(int i = 0; i < contentPropertiesCount; i++)
|
||||||
|
{
|
||||||
|
propertiesMap.put(AlfMock.generateQName(), mockedContentData);
|
||||||
|
}
|
||||||
|
|
||||||
|
when(mockedNodeService.getProperties(nodeRef))
|
||||||
|
.thenReturn(propertiesMap);
|
||||||
|
|
||||||
return nodeRef;
|
return nodeRef;
|
||||||
}
|
}
|
||||||
|
@@ -18,7 +18,7 @@
|
|||||||
*/
|
*/
|
||||||
package org.alfresco.module.org_alfresco_module_rm.content.cleanser;
|
package org.alfresco.module.org_alfresco_module_rm.content.cleanser;
|
||||||
|
|
||||||
import static org.mockito.Mockito.verify;
|
import static org.mockito.Mockito.inOrder;
|
||||||
import static org.mockito.Mockito.when;
|
import static org.mockito.Mockito.when;
|
||||||
|
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
@@ -26,6 +26,7 @@ import java.io.File;
|
|||||||
import org.alfresco.module.org_alfresco_module_rm.test.util.BaseUnitTest;
|
import org.alfresco.module.org_alfresco_module_rm.test.util.BaseUnitTest;
|
||||||
import org.alfresco.service.cmr.repository.ContentIOException;
|
import org.alfresco.service.cmr.repository.ContentIOException;
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
|
import org.mockito.InOrder;
|
||||||
import org.mockito.InjectMocks;
|
import org.mockito.InjectMocks;
|
||||||
import org.mockito.Mock;
|
import org.mockito.Mock;
|
||||||
import org.mockito.Spy;
|
import org.mockito.Spy;
|
||||||
@@ -61,12 +62,14 @@ public class ContentCleanser522022MUnitTest extends BaseUnitTest
|
|||||||
|
|
||||||
contentCleanser522022M.cleanse(mockedFile);
|
contentCleanser522022M.cleanse(mockedFile);
|
||||||
|
|
||||||
verify(contentCleanser522022M)
|
InOrder inOrder = inOrder(contentCleanser522022M);
|
||||||
|
|
||||||
|
inOrder.verify(contentCleanser522022M)
|
||||||
.overwrite(mockedFile, contentCleanser522022M.overwriteOnes);
|
.overwrite(mockedFile, contentCleanser522022M.overwriteOnes);
|
||||||
verify(contentCleanser522022M)
|
inOrder.verify(contentCleanser522022M)
|
||||||
.overwrite(mockedFile, contentCleanser522022M.overwriteZeros);
|
.overwrite(mockedFile, contentCleanser522022M.overwriteZeros);
|
||||||
verify(contentCleanser522022M)
|
inOrder.verify(contentCleanser522022M)
|
||||||
.overwrite(mockedFile, contentCleanser522022M.overwriteOnes);
|
.overwrite(mockedFile, contentCleanser522022M.overwriteRandom);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
Reference in New Issue
Block a user