Fixed MOB-426: Refactor CopyService: Apply new pattern to existing policy handlers

Fallout:
1. Policy handling for 'onCopy' has been changed to 'getCopyCallback'
2. All existing policy usage was refactored to control behaviour more closely
3. The default child association behaviour has changed:
3.1 Types and aspects control their own child association behaviour
3.2 cm:folder recurses into primary children, but merely copies the secondary association
3.3 cm:rule recurses into primary children
3.4 unless behaviour is defined for a child association, there is no recursion or copying
4. Node association behavior has changed
4.1 There is no copying of node associations.  Each type and aspect must handle this by
    recording nodes and fixing up the required associations in the onCopyComplete.
4.2 If there is a requirement, this can be added to the callback later

See 'org.alfresco.repo.copy.AbstractCopyBehaviourCallback' and derived classes for examples.

Areas to test with particular attention:
1. Normal copy behaviour
2. Copy of documents with discussions
3. Check-in check-out
4. Check-in, check-out of documents where a discussion was added to working copy
5. Copies of documents with thumbnails
6. Copies of documents with rules
7. Copying of hierarchies that contain rules to copy to another location within the hierarchy
8. Copying into folders where named children already exist


git-svn-id: https://svn.alfresco.com/repos/alfresco-enterprise/alfresco/HEAD/root@13915 c4b6b30b-aa2e-2d43-bbcb-ca4b014f7261
This commit is contained in:
Derek Hulley
2009-04-09 14:48:02 +00:00
parent 0b57c06ad3
commit d6586351c1
29 changed files with 5920 additions and 4222 deletions

View File

@@ -25,22 +25,25 @@
package org.alfresco.repo.version;
import java.io.Serializable;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
import org.alfresco.i18n.I18NUtil;
import org.alfresco.model.ContentModel;
import org.alfresco.repo.content.ContentServicePolicies;
import org.alfresco.repo.copy.CopyBehaviourCallback;
import org.alfresco.repo.copy.CopyDetails;
import org.alfresco.repo.copy.CopyServicePolicies;
import org.alfresco.repo.copy.DefaultCopyBehaviourCallback;
import org.alfresco.repo.node.NodeServicePolicies;
import org.alfresco.repo.policy.Behaviour;
import org.alfresco.repo.policy.JavaBehaviour;
import org.alfresco.repo.policy.PolicyComponent;
import org.alfresco.repo.policy.PolicyScope;
import org.alfresco.repo.transaction.AlfrescoTransactionSupport;
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.cmr.repository.StoreRef;
import org.alfresco.service.cmr.version.Version;
import org.alfresco.service.cmr.version.VersionService;
import org.alfresco.service.namespace.NamespaceService;
@@ -52,20 +55,21 @@ import org.alfresco.service.namespace.QName;
* @author Roy Wetherall
*/
public class VersionableAspect implements ContentServicePolicies.OnContentUpdatePolicy,
NodeServicePolicies.OnAddAspectPolicy,
NodeServicePolicies.OnRemoveAspectPolicy,
NodeServicePolicies.OnDeleteNodePolicy,
VersionServicePolicies.AfterCreateVersionPolicy
NodeServicePolicies.OnAddAspectPolicy,
NodeServicePolicies.OnRemoveAspectPolicy,
NodeServicePolicies.OnDeleteNodePolicy,
VersionServicePolicies.AfterCreateVersionPolicy,
CopyServicePolicies.OnCopyNodePolicy
{
/** The i18n'ized messages */
private static final String MSG_INITIAL_VERSION = "create_version.initial_version";
private static final String MSG_AUTO_VERSION = "create_version.auto_version";
/** Transaction resource key */
private static final String KEY_VERSIONED_NODEREFS = "versioned_noderefs";
/** The i18n'ized messages */
private static final String MSG_INITIAL_VERSION = "create_version.initial_version";
private static final String MSG_AUTO_VERSION = "create_version.auto_version";
/** Transaction resource key */
private static final String KEY_VERSIONED_NODEREFS = "versioned_noderefs";
/** The policy component */
private PolicyComponent policyComponent;
private PolicyComponent policyComponent;
/** The node service */
private NodeService nodeService;
@@ -75,26 +79,26 @@ public class VersionableAspect implements ContentServicePolicies.OnContentUpdate
/** Auto version behaviour */
private Behaviour autoVersionBehaviour;
/**
* Set the policy component
*
* @param policyComponent the policy component
*/
public void setPolicyComponent(PolicyComponent policyComponent)
{
this.policyComponent = policyComponent;
}
public void setPolicyComponent(PolicyComponent policyComponent)
{
this.policyComponent = policyComponent;
}
/**
* Set the version service
*
* @param versionService the version service
*/
/**
* Set the version service
*
* @param versionService the version service
*/
public void setVersionService(VersionService versionService)
{
this.versionService = versionService;
}
this.versionService = versionService;
}
/**
* Set the node service
@@ -109,25 +113,25 @@ public class VersionableAspect implements ContentServicePolicies.OnContentUpdate
/**
* Initialise the versionable aspect policies
*/
public void init()
{
this.policyComponent.bindClassBehaviour(
QName.createQName(NamespaceService.ALFRESCO_URI, "onAddAspect"),
ContentModel.ASPECT_VERSIONABLE,
public void init()
{
this.policyComponent.bindClassBehaviour(
QName.createQName(NamespaceService.ALFRESCO_URI, "onAddAspect"),
ContentModel.ASPECT_VERSIONABLE,
new JavaBehaviour(this, "onAddAspect", Behaviour.NotificationFrequency.TRANSACTION_COMMIT));
this.policyComponent.bindClassBehaviour(
QName.createQName(NamespaceService.ALFRESCO_URI, "onRemoveAspect"),
ContentModel.ASPECT_VERSIONABLE,
this.policyComponent.bindClassBehaviour(
QName.createQName(NamespaceService.ALFRESCO_URI, "onRemoveAspect"),
ContentModel.ASPECT_VERSIONABLE,
new JavaBehaviour(this, "onRemoveAspect", Behaviour.NotificationFrequency.TRANSACTION_COMMIT));
this.policyComponent.bindClassBehaviour(
QName.createQName(NamespaceService.ALFRESCO_URI, "onDeleteNode"),
ContentModel.ASPECT_VERSIONABLE,
this.policyComponent.bindClassBehaviour(
QName.createQName(NamespaceService.ALFRESCO_URI, "onDeleteNode"),
ContentModel.ASPECT_VERSIONABLE,
new JavaBehaviour(this, "onDeleteNode", Behaviour.NotificationFrequency.TRANSACTION_COMMIT));
this.policyComponent.bindClassBehaviour(
QName.createQName(NamespaceService.ALFRESCO_URI, "afterCreateVersion"),
ContentModel.ASPECT_VERSIONABLE,
this.policyComponent.bindClassBehaviour(
QName.createQName(NamespaceService.ALFRESCO_URI, "afterCreateVersion"),
ContentModel.ASPECT_VERSIONABLE,
new JavaBehaviour(this, "afterCreateVersion", Behaviour.NotificationFrequency.EVERY_EVENT));
autoVersionBehaviour = new JavaBehaviour(this, "onContentUpdate", Behaviour.NotificationFrequency.TRANSACTION_COMMIT);
this.policyComponent.bindClassBehaviour(
ContentServicePolicies.ON_CONTENT_UPDATE,
@@ -136,57 +140,72 @@ public class VersionableAspect implements ContentServicePolicies.OnContentUpdate
// Register the copy behaviour
this.policyComponent.bindClassBehaviour(
QName.createQName(NamespaceService.ALFRESCO_URI, "onCopyNode"),
QName.createQName(NamespaceService.ALFRESCO_URI, "getCopyCallback"),
ContentModel.ASPECT_VERSIONABLE,
new JavaBehaviour(this, "onCopy"));
}
new JavaBehaviour(this, "getCopyCallback"));
}
/**
* @see org.alfresco.repo.node.NodeServicePolicies.OnDeleteNodePolicy#onDeleteNode(org.alfresco.service.cmr.repository.ChildAssociationRef, boolean)
*/
public void onDeleteNode(ChildAssociationRef childAssocRef, boolean isNodeArchived)
{
if (isNodeArchived == false)
{
// If we are perminantly deleting the node then we need to remove the associated version history
this.versionService.deleteVersionHistory(childAssocRef.getChildRef());
}
// otherwise we do nothing since we need to hold onto the version history in case the node is restored later
}
/**
* OnCopy behaviour implementation for the version aspect.
* <p>
* Ensures that the propety values of the version aspect are not copied onto
* the destination node.
*
* @see org.alfresco.repo.copy.CopyServicePolicies.OnCopyNodePolicy#onCopyNode(QName, NodeRef, StoreRef, boolean, PolicyScope)
* @see org.alfresco.repo.node.NodeServicePolicies.OnDeleteNodePolicy#onDeleteNode(org.alfresco.service.cmr.repository.ChildAssociationRef, boolean)
*/
public void onCopy(
QName sourceClassRef,
NodeRef sourceNodeRef,
StoreRef destinationStoreRef,
boolean copyToNewNode,
PolicyScope copyDetails)
public void onDeleteNode(ChildAssociationRef childAssocRef, boolean isNodeArchived)
{
// Add the version aspect, but do not copy the version label
copyDetails.addAspect(ContentModel.ASPECT_VERSIONABLE);
copyDetails.addProperty(
ContentModel.ASPECT_VERSIONABLE,
ContentModel.PROP_AUTO_VERSION,
this.nodeService.getProperty(sourceNodeRef, ContentModel.PROP_AUTO_VERSION));
}
/**
* On add aspect policy behaviour
if (isNodeArchived == false)
{
// If we are perminantly deleting the node then we need to remove the associated version history
this.versionService.deleteVersionHistory(childAssocRef.getChildRef());
}
// otherwise we do nothing since we need to hold onto the version history in case the node is restored later
}
/**
* @return Returns the {@link VersionableAspectCopyBehaviourCallback}
*/
public CopyBehaviourCallback getCopyCallback(QName classRef, CopyDetails copyDetails)
{
return VersionableAspectCopyBehaviourCallback.INSTANCE;
}
/**
* Copy behaviour for the <b>cm:versionable</b> aspect
*
* @param nodeRef
* @param aspectTypeQName
*/
* @author Derek Hulley
* @since 3.2
*/
private static class VersionableAspectCopyBehaviourCallback extends DefaultCopyBehaviourCallback
{
private static final CopyBehaviourCallback INSTANCE = new VersionableAspectCopyBehaviourCallback();
/**
* Copy the aspect, but only the {@link ContentModel#PROP_AUTO_VERSION} property
*/
@Override
public Map<QName, Serializable> getCopyProperties(
QName classQName,
CopyDetails copyDetails,
Map<QName, Serializable> properties)
{
Serializable value = properties.get(ContentModel.PROP_AUTO_VERSION);
if (value != null)
{
return Collections.singletonMap(ContentModel.PROP_AUTO_VERSION, value);
}
else
{
return Collections.emptyMap();
}
}
}
/**
* On add aspect policy behaviour
*
* @param nodeRef
* @param aspectTypeQName
*/
public void onAddAspect(NodeRef nodeRef, QName aspectTypeQName)
{
if (this.nodeService.exists(nodeRef) == true && aspectTypeQName.equals(ContentModel.ASPECT_VERSIONABLE) == true)
{
if (this.nodeService.exists(nodeRef) == true && aspectTypeQName.equals(ContentModel.ASPECT_VERSIONABLE) == true)
{
boolean initialVersion = true;
Boolean value = (Boolean)this.nodeService.getProperty(nodeRef, ContentModel.PROP_INITIAL_VERSION);
@@ -210,16 +229,16 @@ public class VersionableAspect implements ContentServicePolicies.OnContentUpdate
}
}
}
}
}
/**
* @see org.alfresco.repo.node.NodeServicePolicies.OnRemoveAspectPolicy#onRemoveAspect(org.alfresco.service.cmr.repository.NodeRef, org.alfresco.service.namespace.QName)
*/
public void onRemoveAspect(NodeRef nodeRef, QName aspectTypeQName)
{
// When the versionable aspect is removed from a node, then delete the associatied verison history
this.versionService.deleteVersionHistory(nodeRef);
}
public void onRemoveAspect(NodeRef nodeRef, QName aspectTypeQName)
{
// When the versionable aspect is removed from a node, then delete the associatied verison history
this.versionService.deleteVersionHistory(nodeRef);
}
/**
* On content update policy bahaviour
@@ -227,47 +246,47 @@ public class VersionableAspect implements ContentServicePolicies.OnContentUpdate
* @param nodeRef the node reference
*/
@SuppressWarnings("unchecked")
public void onContentUpdate(NodeRef nodeRef, boolean newContent)
public void onContentUpdate(NodeRef nodeRef, boolean newContent)
{
if (this.nodeService.exists(nodeRef) == true && this.nodeService.hasAspect(nodeRef, ContentModel.ASPECT_VERSIONABLE) == true
&& this.nodeService.hasAspect(nodeRef, ContentModel.ASPECT_TEMPORARY) == false)
&& this.nodeService.hasAspect(nodeRef, ContentModel.ASPECT_TEMPORARY) == false)
{
Map<NodeRef, NodeRef> versionedNodeRefs = (Map)AlfrescoTransactionSupport.getResource(KEY_VERSIONED_NODEREFS);
if (versionedNodeRefs == null || versionedNodeRefs.containsKey(nodeRef) == false)
{
// Determine whether the node is auto versionable or not
boolean autoVersion = false;
Boolean value = (Boolean)this.nodeService.getProperty(nodeRef, ContentModel.PROP_AUTO_VERSION);
if (value != null)
{
// If the value is not null then
autoVersion = value.booleanValue();
}
// else this means that the default value has not been set and the versionable aspect was applied pre-1.1
if (autoVersion == true)
{
// Create the auto-version
Map<String, Serializable> versionProperties = new HashMap<String, Serializable>(1);
versionProperties.put(Version.PROP_DESCRIPTION, I18NUtil.getMessage(MSG_AUTO_VERSION));
this.versionService.createVersion(nodeRef, versionProperties);
}
}
Map<NodeRef, NodeRef> versionedNodeRefs = (Map)AlfrescoTransactionSupport.getResource(KEY_VERSIONED_NODEREFS);
if (versionedNodeRefs == null || versionedNodeRefs.containsKey(nodeRef) == false)
{
// Determine whether the node is auto versionable or not
boolean autoVersion = false;
Boolean value = (Boolean)this.nodeService.getProperty(nodeRef, ContentModel.PROP_AUTO_VERSION);
if (value != null)
{
// If the value is not null then
autoVersion = value.booleanValue();
}
// else this means that the default value has not been set and the versionable aspect was applied pre-1.1
if (autoVersion == true)
{
// Create the auto-version
Map<String, Serializable> versionProperties = new HashMap<String, Serializable>(1);
versionProperties.put(Version.PROP_DESCRIPTION, I18NUtil.getMessage(MSG_AUTO_VERSION));
this.versionService.createVersion(nodeRef, versionProperties);
}
}
}
}
/**
* @see org.alfresco.repo.version.VersionServicePolicies.OnCreateVersionPolicy#onCreateVersion(org.alfresco.service.namespace.QName, org.alfresco.service.cmr.repository.NodeRef, java.util.Map, org.alfresco.repo.policy.PolicyScope)
*/
@SuppressWarnings("unchecked")
public void afterCreateVersion(NodeRef versionableNode, Version version)
{
Map<NodeRef, NodeRef> versionedNodeRefs = (Map<NodeRef, NodeRef>)AlfrescoTransactionSupport.getResource(KEY_VERSIONED_NODEREFS);
if (versionedNodeRefs == null)
{
versionedNodeRefs = new HashMap<NodeRef, NodeRef>();
AlfrescoTransactionSupport.bindResource(KEY_VERSIONED_NODEREFS, versionedNodeRefs);
}
versionedNodeRefs.put(versionableNode, versionableNode);
}
@SuppressWarnings("unchecked")
public void afterCreateVersion(NodeRef versionableNode, Version version)
{
Map<NodeRef, NodeRef> versionedNodeRefs = (Map<NodeRef, NodeRef>)AlfrescoTransactionSupport.getResource(KEY_VERSIONED_NODEREFS);
if (versionedNodeRefs == null)
{
versionedNodeRefs = new HashMap<NodeRef, NodeRef>();
AlfrescoTransactionSupport.bindResource(KEY_VERSIONED_NODEREFS, versionedNodeRefs);
}
versionedNodeRefs.put(versionableNode, versionableNode);
}
}