diff --git a/config/alfresco/model/transferModel.xml b/config/alfresco/model/transferModel.xml index 4de1531a70..36f9c086e4 100644 --- a/config/alfresco/model/transferModel.xml +++ b/config/alfresco/model/transferModel.xml @@ -188,25 +188,41 @@ - Nodes with this aspect have been transferred from one repository to another - + Transferred Node + Nodes with this aspect have been transferred from one repository to another Source RepositoryId + The repository id that this node originates from. d:text true - The repository that this node was transferred from + From Repository Id + The id of the repository that transferred this node to this repository d:text true + + + + + Alien Node + Nodes with this aspect are either alien nodes or have been invaded by alien content + Alien Content d:boolean false false + + The repositories that have invaded this node + d:text + false + true + false + diff --git a/config/alfresco/transfer-service-context.xml b/config/alfresco/transfer-service-context.xml index 2242d9aaf5..4b06a4495a 100644 --- a/config/alfresco/transfer-service-context.xml +++ b/config/alfresco/transfer-service-context.xml @@ -53,7 +53,13 @@ - + + + + + + @@ -63,6 +69,8 @@ + + /${spaces.company_home.childname}/${spaces.dictionary.childname}/${spaces.transfers.childname} @@ -107,6 +115,7 @@ + + * Alien nodes cannot be deleted through the transfer service, instead they are + * "pruned" + * + *

+ * This class owns the aspect trx:alien (TransferModel.ASPECT_ALIEN) + */ +public interface AlienProcessor +{ + /** + * Prune the given node of aliens from the specified repositoryId + * @param parentNodeRef the root to prune + * @param fromRepositoryId the repositoryId to prune. + */ + public void pruneNode(NodeRef parentNodeRef, String fromRepositoryId); + + /** + * Has the node been invaded by aliens ? + * @param nodeRef the node to check + * @return true the node has been invaded by aliens. + */ + public boolean isAlien(NodeRef nodeRef); + + /** + * Called before deleting an alien node. + * + * @param nodeBeingDeleted node about to be deleted + */ + public void beforeDeleteAlien(NodeRef deletedNodeRef); + + /** + * Called before creating a child of a transferred node. + * + * When a new node is created as a child of a Transferred or Alien node then + * the new node needs to be marked as an alien. + *

+ * Then the tree needs to be walked upwards to mark all parent + * transferred nodes as alien. + * + * @param childAssocRef the association ref to the new node + * @param repositoryId - the repositoryId of the system who owns the new node. + */ + public void onCreateChild(ChildAssociationRef childAssocRef, String repositoryId); + +} diff --git a/source/java/org/alfresco/repo/transfer/AlienProcessorImpl.java b/source/java/org/alfresco/repo/transfer/AlienProcessorImpl.java new file mode 100644 index 0000000000..cd1c7d3534 --- /dev/null +++ b/source/java/org/alfresco/repo/transfer/AlienProcessorImpl.java @@ -0,0 +1,497 @@ +package org.alfresco.repo.transfer; + +import java.io.Serializable; +import java.util.ArrayList; +import java.util.Collection; +import java.util.Iterator; +import java.util.List; +import java.util.Stack; +import java.util.Vector; + +import org.alfresco.model.ContentModel; +import org.alfresco.repo.node.NodeServicePolicies; +import org.alfresco.repo.policy.BehaviourFilter; +import org.alfresco.repo.policy.JavaBehaviour; +import org.alfresco.repo.policy.Behaviour.NotificationFrequency; +import org.alfresco.repo.security.authentication.AuthenticationUtil; +import org.alfresco.repo.security.authentication.AuthenticationUtil.RunAsWork; +import org.alfresco.service.cmr.dictionary.DictionaryService; +import org.alfresco.service.cmr.repository.ChildAssociationRef; +import org.alfresco.service.cmr.repository.NodeRef; +import org.alfresco.service.cmr.repository.NodeService; +import org.alfresco.service.namespace.QName; +import org.alfresco.util.PropertyCheck; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; + +/** + * Class to encapsulate the behaviour of "Alien" nodes. + */ +public class AlienProcessorImpl implements AlienProcessor +{ + private NodeService nodeService; + private BehaviourFilter behaviourFilter; + private DictionaryService dictionaryService; + + private static final Log log = LogFactory.getLog(AlienProcessorImpl.class); + + public void init() + { + PropertyCheck.mandatory(this, "nodeService", nodeService); + PropertyCheck.mandatory(this, "behaviourFilter", behaviourFilter); + PropertyCheck.mandatory(this, "dictionaryService", getDictionaryService()); + } + + public void onCreateChild(ChildAssociationRef childAssocRef, final String repositoryId) + { + log.debug("on create child association to transferred node"); + + ChildAssociationRef currentAssoc = childAssocRef; + + if(!childAssocRef.isPrimary()) + { + log.debug("not a primary assoc - do nothing"); + return; + } + + // TODO Needs to check assoc is a cm:contains or subtype of cm:contains + if(!childAssocRef.getTypeQName().equals(ContentModel.ASSOC_CONTAINS)) + { + Collection subAspects = dictionaryService.getSubAspects(ContentModel.ASSOC_CONTAINS, true); + if(!subAspects.contains(childAssocRef.getTypeQName())) + { + log.debug("not a subtype of cm:contains - do nothing"); + return; + } + } + + NodeRef parentNodeRef = currentAssoc.getParentRef(); + NodeRef childNodeRef = currentAssoc.getChildRef(); + + if(!nodeService.hasAspect(parentNodeRef, TransferModel.ASPECT_TRANSFERRED)) + { + log.debug("parent was not transferred - do nothing"); + return; + } + + if(!nodeService.hasAspect(parentNodeRef, TransferModel.ASPECT_ALIEN)) + { + // parent is not yet an alien invader ... + String parentFromRepo = (String)nodeService.getProperty(parentNodeRef, TransferModel.PROP_FROM_REPOSITORY_ID); + { + if(repositoryId.equalsIgnoreCase(parentFromRepo)) + { + log.debug("parent was not alien and this node is from the same repo - do nothing"); + return; + } + } + } + + /** + * If we get this far then we are going to Make the new child node + * an alien node + */ + setAlien(childNodeRef, repositoryId); + + /** + * Now deal with the parents of this alien node + */ + while(currentAssoc != null) + { + parentNodeRef = currentAssoc.getParentRef(); + childNodeRef = currentAssoc.getChildRef(); + + if(nodeService.hasAspect(parentNodeRef, TransferModel.ASPECT_TRANSFERRED) || nodeService.hasAspect(parentNodeRef, TransferModel.ASPECT_ALIEN)) + { + if (!isInvaded(parentNodeRef, repositoryId)) + { + if(log.isDebugEnabled()) + { + log.debug("alien invades parent node:" + parentNodeRef + ", repositoryId:" + repositoryId); + } + + final NodeRef newAlien = parentNodeRef; + + /** + * Parent may be locked or not be editable by the current user + * turn off auditing and lock service for this transaction and + * run as admin. + */ + RunAsWork actionRunAs = new RunAsWork() + { + public Void doWork() throws Exception + { + getBehaviourFilter().disableBehaviour(newAlien, ContentModel.ASPECT_AUDITABLE); + getBehaviourFilter().disableBehaviour(newAlien, ContentModel.ASPECT_LOCKABLE); + setAlien(newAlien, repositoryId); + return null; + } + }; + AuthenticationUtil.runAs(actionRunAs, AuthenticationUtil.getSystemUserName()); + + // Yes the parent has been invaded so step up to the parent's parent + currentAssoc = nodeService.getPrimaryParent(parentNodeRef); + } + else + { + log.debug("parent node is already invaded"); + currentAssoc = null; + } + } + else + { + log.debug("parent is not a transferred node"); + currentAssoc = null; + } + } + } + + public void beforeDeleteAlien(NodeRef deletedNodeRef) + { + log.debug("on delete node - need to check for transferred node"); + + Liststuff = (List)nodeService.getProperty(deletedNodeRef, TransferModel.PROP_INVADED_BY); + + Vector exInvaders = new Vector(stuff); + + ChildAssociationRef currentAssoc = nodeService.getPrimaryParent(deletedNodeRef); + + while(currentAssoc != null && exInvaders != null && exInvaders.size() > 0) + { + NodeRef parentNodeRef = currentAssoc.getParentRef(); + NodeRef currentNodeRef = currentAssoc.getChildRef(); + + /** + * Does the parent have alien invaders ? + */ + if(nodeService.hasAspect(parentNodeRef, TransferModel.ASPECT_ALIEN)) + { + log.debug("parent node is alien - check siblings"); + + /** + * For each invader of the deletedNode + */ + Iterator i = exInvaders.listIterator(); + while(i.hasNext()) + { + String exInvader = i.next(); + log.debug("Checking exInvader:" + exInvader); + + /** + * Check the siblings of this node to see whether there are any other alien nodes for this invader. + */ + //TODO replace with a more efficient query + List refs = nodeService.getChildAssocs(parentNodeRef); + + for(ChildAssociationRef ref : refs) + { + NodeRef childRef = ref.getChildRef(); + ListinvadedBy = (List)nodeService.getProperty(childRef, TransferModel.PROP_INVADED_BY); + + if(childRef.equals(currentNodeRef)) + { + // do nothing - this is the node we are working with. + } + else + { + if(invadedBy != null && invadedBy.contains(exInvader)) + { + // There is a sibling so remove this from the list of ex invaders. + log.debug("yes there is a sibling so it remains an invader"); + i.remove(); + break; + } + } + } // for each child assoc + + } // for each invader + + log.debug("end of checking siblings"); + + if(exInvaders.size() > 0) + { + log.debug("removing invaders from parent node:" + parentNodeRef); + List parentInvaders = (List)nodeService.getProperty(parentNodeRef, TransferModel.PROP_INVADED_BY); + + final List newInvaders = new ArrayList(10); + for(String invader : parentInvaders) + { + if(exInvaders.contains(invader)) + { + log.debug("removing invader:" + invader); + } + else + { + newInvaders.add(invader); + } + } + + final NodeRef oldAlien = parentNodeRef; + + /** + * Parent may be locked or not be editable by the current user + * turn off auditing and lock service for this transaction and + * run as admin. + */ + RunAsWork actionRunAs = new RunAsWork() + { + public Void doWork() throws Exception + { + behaviourFilter.disableBehaviour(oldAlien, ContentModel.ASPECT_AUDITABLE); + behaviourFilter.disableBehaviour(oldAlien, ContentModel.ASPECT_LOCKABLE); + if(newInvaders.size() > 0) + { + nodeService.setProperty(oldAlien, TransferModel.PROP_INVADED_BY, (Serializable)newInvaders); + } + else + { + log.debug("parent node is no longer alien nodeRef" + oldAlien); + nodeService.removeAspect(oldAlien, TransferModel.ASPECT_ALIEN); + } + return null; + } + }; + AuthenticationUtil.runAs(actionRunAs, AuthenticationUtil.getSystemUserName()); + } + + /** + * Now step up to the parent's parent + */ + currentAssoc = nodeService.getPrimaryParent(parentNodeRef); + } + else + { + log.debug("parent is not an alien node"); + currentAssoc = null; + } + } // end of while + } + + public boolean isAlien(NodeRef nodeRef) + { + return nodeService.hasAspect(nodeRef, TransferModel.ASPECT_ALIEN); + } + + public void pruneNode(NodeRef parentNodeRef, String fromRepositoryId) + { + Stack nodesToPrune = new Stack(); + Stack foldersToRecalculate = new Stack(); + nodesToPrune.add(parentNodeRef); + + while(!nodesToPrune.isEmpty()) + { + /** + * for all alien children + * + * if from the repo with no (other) aliens - delete + * + * if from the repo with multiple alien invasions - leave alone but process children + */ + NodeRef currentNodeRef = nodesToPrune.pop(); + + log.debug("pruneNode:" + currentNodeRef); + + if(getNodeService().hasAspect(currentNodeRef, TransferModel.ASPECT_ALIEN)) + { + // Yes this is an alien node + ListinvadedBy = (List)getNodeService().getProperty(currentNodeRef, TransferModel.PROP_INVADED_BY); + if(invadedBy.contains(fromRepositoryId)) + { + if(invadedBy.size() == 1) + { + // we are invaded by a single repository which must be fromRepositoryId + log.debug("pruned - deleted node:" + currentNodeRef); + getNodeService().deleteNode(currentNodeRef); + } + else + { + log.debug("folder has multiple invaders"); + // multiple invasion - so it must be a folder + //TODO replace with a more efficient query + List refs = getNodeService().getChildAssocs(parentNodeRef); + for(ChildAssociationRef ref : refs) + { + if(log.isDebugEnabled()) + { + log.debug("will need to check child:" + ref); + } + nodesToPrune.push(ref.getChildRef()); + + /** + * This folder can't be deleted so its invaded flag needs to be re-calculated + */ + if(!foldersToRecalculate.contains(ref.getParentRef())) + { + foldersToRecalculate.push(ref.getParentRef()); + } + } + } + } + else + { + /** + * Current node has been invaded by another repository + * + * Need to check fromRepositoryId since its children may need to be pruned + */ + getNodeService().hasAspect(currentNodeRef, TransferModel.ASPECT_TRANSFERRED); + { + String fromRepoId = (String)getNodeService().getProperty(currentNodeRef, TransferModel.PROP_FROM_REPOSITORY_ID); + if(fromRepositoryId.equalsIgnoreCase(fromRepoId)) + { + log.debug("folder is from the transferring repository"); + // invaded from somewhere else - so it must be a folder + List refs = getNodeService().getChildAssocs(currentNodeRef); + for(ChildAssociationRef ref : refs) + { + if(log.isDebugEnabled()) + { + log.debug("will need to check child:" + ref); + } + nodesToPrune.push(ref.getChildRef()); + + /** + * This folder can't be deleted so its invaded flag needs to be re-calculated + */ + if(!foldersToRecalculate.contains(ref.getParentRef())) + { + foldersToRecalculate.push(ref.getParentRef()); + } + } + } + } + } + } + else + { + // Current node does not contain alien nodes so it can be deleted. + getNodeService().hasAspect(currentNodeRef, TransferModel.ASPECT_TRANSFERRED); + { + String fromRepoId = (String)getNodeService().getProperty(currentNodeRef, TransferModel.PROP_FROM_REPOSITORY_ID); + if(fromRepositoryId.equalsIgnoreCase(fromRepoId)) + { + // we are invaded by a single repository + log.debug("pruned - deleted non alien node:" + currentNodeRef); + getNodeService().deleteNode(currentNodeRef); + } + } + } + } + + /** + * Now ripple the "invadedBy" flag upwards. + */ + + while(!foldersToRecalculate.isEmpty()) + { + NodeRef folderNodeRef = foldersToRecalculate.pop(); + + log.debug("recalculate invadedBy :" + folderNodeRef); + + ListfolderInvadedBy = (List)getNodeService().getProperty(folderNodeRef, TransferModel.PROP_INVADED_BY); + + boolean stillInvaded = false; + //TODO need a more efficient query here + List refs = getNodeService().getChildAssocs(folderNodeRef); + for(ChildAssociationRef ref : refs) + { + NodeRef childNode = ref.getChildRef(); + ListchildInvadedBy = (List)getNodeService().getProperty(childNode, TransferModel.PROP_INVADED_BY); + + if(childInvadedBy.contains(fromRepositoryId)) + { + log.debug("folder is still invaded"); + stillInvaded = true; + break; + } + } + + if(!stillInvaded) + { + List newInvadedBy = new ArrayList(folderInvadedBy); + folderInvadedBy.remove(fromRepositoryId); + getNodeService().setProperty(folderNodeRef, TransferModel.PROP_INVADED_BY, (Serializable)newInvadedBy); + } + } + log.debug("pruneNode: end"); + } + + + /** + * Is this node invaded ? + * @param nodeRef + * @param invader + * @return true, this node has been invaded by the invader + */ + private boolean isInvaded(NodeRef nodeRef, String invader) + { + ListinvadedBy = (List)nodeService.getProperty(nodeRef, TransferModel.PROP_INVADED_BY); + + if(invadedBy == null) + { + return false; + } + + return invadedBy.contains(invader); + } + + /** + * Mark the specified node as an alien node, invadedby the invader. + * @param newAlien + * @param invader + */ + private void setAlien(NodeRef newAlien, String invader) + { + // Introduce a Multi-valued property + List invadedBy = (List)nodeService.getProperty(newAlien, + TransferModel.PROP_INVADED_BY); + + if(invadedBy == null) + { + nodeService.setProperty(newAlien, TransferModel.PROP_ALIEN, Boolean.TRUE); + invadedBy = new ArrayList(1); + } + invadedBy.add(invader); + + /** + * Set the invaded by property + */ + nodeService.setProperty(newAlien, TransferModel.PROP_INVADED_BY, (Serializable) invadedBy); + +// /** +// * Experiment with a residual property +// */ +// nodeService.setProperty(newAlien, QName.createQName(TransferModel.TRANSFER_MODEL_1_0_URI, +// "invader" + invader), Boolean.TRUE); + + } + public void setNodeService(NodeService nodeService) + { + this.nodeService = nodeService; + } + + public NodeService getNodeService() + { + return nodeService; + } + + public void setBehaviourFilter(BehaviourFilter behaviourFilter) + { + this.behaviourFilter = behaviourFilter; + } + + public BehaviourFilter getBehaviourFilter() + { + return behaviourFilter; + } + + public void setDictionaryService(DictionaryService dictionaryService) + { + this.dictionaryService = dictionaryService; + } + + public DictionaryService getDictionaryService() + { + return dictionaryService; + } +} diff --git a/source/java/org/alfresco/repo/transfer/DefaultManifestProcessorFactoryImpl.java b/source/java/org/alfresco/repo/transfer/DefaultManifestProcessorFactoryImpl.java index c58d2cc81a..17734500c7 100644 --- a/source/java/org/alfresco/repo/transfer/DefaultManifestProcessorFactoryImpl.java +++ b/source/java/org/alfresco/repo/transfer/DefaultManifestProcessorFactoryImpl.java @@ -40,6 +40,7 @@ public class DefaultManifestProcessorFactoryImpl implements ManifestProcessorFac private DictionaryService dictionaryService; private PermissionService permissionService; private CorrespondingNodeResolverFactory nodeResolverFactory; + private AlienProcessor alienProcessor; /* * (non-Javadoc) @@ -57,6 +58,7 @@ public class DefaultManifestProcessorFactoryImpl implements ManifestProcessorFac primaryProcessor.setNodeService(nodeService); primaryProcessor.setDictionaryService(dictionaryService); primaryProcessor.setPermissionService(getPermissionService()); + primaryProcessor.setAlienProcessor(getAlienProcessor()); processors.add(primaryProcessor); RepoSecondaryManifestProcessorImpl secondaryProcessor = new RepoSecondaryManifestProcessorImpl(receiver, transferId); @@ -65,8 +67,8 @@ public class DefaultManifestProcessorFactoryImpl implements ManifestProcessorFac processors.add(secondaryProcessor); RepoTertiaryManifestProcessorImpl tertiaryProcessor = new RepoTertiaryManifestProcessorImpl(receiver, transferId); - tertiaryProcessor.setNodeResolver(nodeResolver); tertiaryProcessor.setNodeService(nodeService); + tertiaryProcessor.setAlienProcessor(getAlienProcessor()); processors.add(tertiaryProcessor); return processors; @@ -113,8 +115,7 @@ public class DefaultManifestProcessorFactoryImpl implements ManifestProcessorFac { RepoRequsiteManifestProcessorImpl processor = new RepoRequsiteManifestProcessorImpl(receiver, transferId, out); - CorrespondingNodeResolver nodeResolver = nodeResolverFactory.getResolver(); - + CorrespondingNodeResolver nodeResolver = nodeResolverFactory.getResolver(); processor.setNodeResolver(nodeResolver); processor.setNodeService(nodeService); @@ -131,4 +132,14 @@ public class DefaultManifestProcessorFactoryImpl implements ManifestProcessorFac return permissionService; } + public void setAlienProcessor(AlienProcessor alienProcessor) + { + this.alienProcessor = alienProcessor; + } + + public AlienProcessor getAlienProcessor() + { + return alienProcessor; + } + } diff --git a/source/java/org/alfresco/repo/transfer/RepoPrimaryManifestProcessorImpl.java b/source/java/org/alfresco/repo/transfer/RepoPrimaryManifestProcessorImpl.java index d3410d9272..fb2f6ae148 100644 --- a/source/java/org/alfresco/repo/transfer/RepoPrimaryManifestProcessorImpl.java +++ b/source/java/org/alfresco/repo/transfer/RepoPrimaryManifestProcessorImpl.java @@ -89,6 +89,7 @@ public class RepoPrimaryManifestProcessorImpl extends AbstractManifestProcessorB private ContentService contentService; private DictionaryService dictionaryService; private CorrespondingNodeResolver nodeResolver; + private AlienProcessor alienProcessor; // State within this class /** @@ -148,18 +149,35 @@ public class RepoPrimaryManifestProcessorImpl extends AbstractManifestProcessorB // Does a corresponding node exist in this repo? if (resolvedNodes.resolvedChild != null) { + NodeRef exNode = resolvedNodes.resolvedChild; // Yes, it does. Delete it. if (log.isDebugEnabled()) { log.debug("Incoming deleted noderef " + node.getNodeRef() - + " has been resolved to existing local noderef " + resolvedNodes.resolvedChild + + " has been resolved to existing local noderef " + exNode + " - deleting"); } - logProgress("Deleting local node: " + resolvedNodes.resolvedChild); - nodeService.deleteNode(resolvedNodes.resolvedChild); - if (log.isDebugEnabled()) + + //TODO : do we have a business rule that only the "from" repo can delete a node? Yes we do. + if(alienProcessor.isAlien(exNode)) { - log.debug("Deleted local node: " + resolvedNodes.resolvedChild); + logProgress("Pruning local node: " + exNode); + if (log.isDebugEnabled()) + { + log.debug("Node to be deleted is alien prune rather than delete: " + exNode); + } + alienProcessor.pruneNode(exNode, header.getRepositoryId()); + } + else + { + // TODO Need to restrict to the "from" repo Id. + // Not alien - delete it. + logProgress("Deleting local node: " + exNode); + nodeService.deleteNode(exNode); + if (log.isDebugEnabled()) + { + log.debug("Deleted local node: " + exNode); + } } } else @@ -204,7 +222,7 @@ public class RepoPrimaryManifestProcessorImpl extends AbstractManifestProcessorB // Does a corresponding node exist in this repo? if (resolvedNodes.resolvedChild != null) { - // Yes, it does. Update it. + // Yes, the corresponding node does exist. Update it. if (log.isDebugEnabled()) { log.debug("Incoming noderef " + node.getNodeRef() + " has been resolved to existing local noderef " @@ -214,8 +232,7 @@ public class RepoPrimaryManifestProcessorImpl extends AbstractManifestProcessorB } else { - // No, there is no corresponding node. Worth just quickly checking - // the archive store... + // No, there is no corresponding node. NodeRef archiveNodeRef = new NodeRef(StoreRef.STORE_REF_ARCHIVE_SPACESSTORE, node.getNodeRef().getId()); if (nodeService.exists(archiveNodeRef)) { @@ -240,6 +257,7 @@ public class RepoPrimaryManifestProcessorImpl extends AbstractManifestProcessorB } /** + * Create new node. * * @param node * @param resolvedNodes @@ -285,14 +303,12 @@ public class RepoPrimaryManifestProcessorImpl extends AbstractManifestProcessorB // Split out the content properties and sanitise the others Map contentProps = processProperties(null, props, null); - // inject transferred property here - if(!contentProps.containsKey(TransferModel.PROP_REPOSITORY_ID)) - { - log.debug("injecting repositoryId property"); - props.put(TransferModel.PROP_REPOSITORY_ID, header.getRepositoryId()); - } - props.put(TransferModel.PROP_FROM_REPOSITORY_ID, header.getRepositoryId()); - + injectTransferred(props); + + // Remove the invadedBy property since that is used by the transfer service + // and is local to this repository. + props.remove(TransferModel.PROP_INVADED_BY); + // Do we need to worry about locking this new node ? if(header.isReadOnly()) { @@ -305,12 +321,12 @@ public class RepoPrimaryManifestProcessorImpl extends AbstractManifestProcessorB // Create the corresponding node... ChildAssociationRef newNode = nodeService.createNode(parentNodeRef, parentAssocType, parentAssocName, node .getType(), props); - + if (log.isDebugEnabled()) { log.debug("Created new node (" + newNode.getChildRef() + ") parented by node " + newNode.getParentRef()); } - + // Deal with the content properties writeContent(newNode.getChildRef(), contentProps); @@ -323,7 +339,6 @@ public class RepoPrimaryManifestProcessorImpl extends AbstractManifestProcessorB nodeService.addAspect(newNode.getChildRef(), aspect, null); } - ManifestAccessControl acl = node.getAccessControl(); // Apply new ACL to this node if(acl != null) @@ -344,6 +359,15 @@ public class RepoPrimaryManifestProcessorImpl extends AbstractManifestProcessorB } } } + + /** + * are we adding an alien node here? The transfer service has policies disabled + * so have to call the consequence of the policy directly. + */ + if(nodeService.hasAspect(parentNodeRef, TransferModel.ASPECT_TRANSFERRED)) + { + alienProcessor.onCreateChild(newNode, header.getRepositoryId()); + } // Is the node that we've just added the parent of any orphans that // we've found earlier? @@ -359,8 +383,16 @@ public class RepoPrimaryManifestProcessorImpl extends AbstractManifestProcessorB for (ChildAssociationRef orphan : orphansToClaim) { logProgress("Re-parenting previously orphaned node (" + orphan.getChildRef() + ") with found parent " + orphan.getParentRef()); - nodeService.moveNode(orphan.getChildRef(), orphan.getParentRef(), orphan.getTypeQName(), orphan + ChildAssociationRef newRef = nodeService.moveNode(orphan.getChildRef(), orphan.getParentRef(), orphan.getTypeQName(), orphan .getQName()); + + /** + * We may be creating an alien node here and the policies are turned off. + */ + if(nodeService.hasAspect(newRef.getParentRef(), TransferModel.ASPECT_TRANSFERRED)) + { + alienProcessor.onCreateChild(newRef, header.getRepositoryId()); + } } // We can now remove the record of these orphans, as their parent // has been found @@ -402,9 +434,25 @@ public class RepoPrimaryManifestProcessorImpl extends AbstractManifestProcessorB || !currentParent.getTypeQName().equals(parentAssocType) || !currentParent.getQName().equals(parentAssocName)) { + + /** + * Yes, the parent assoc has changed so we need to move the node + */ + // the parent node may no longer be an alien + if(nodeService.hasAspect(parentNodeRef, TransferModel.ASPECT_ALIEN)) + { + alienProcessor.beforeDeleteAlien(node.getNodeRef()); + } + // Yes, we need to move the node - nodeService.moveNode(nodeToUpdate, parentNodeRef, parentAssocType, parentAssocName); + ChildAssociationRef newNode = nodeService.moveNode(nodeToUpdate, parentNodeRef, parentAssocType, parentAssocName); logProgress("Moved node " + nodeToUpdate + " to be under parent node " + parentNodeRef); + + // We may have created a new alien. + if(nodeService.hasAspect(parentNodeRef, TransferModel.ASPECT_TRANSFERRED)) + { + alienProcessor.onCreateChild(newNode, header.getRepositoryId()); + } } log.info("Resolved parent node to " + parentNodeRef); @@ -418,12 +466,18 @@ public class RepoPrimaryManifestProcessorImpl extends AbstractManifestProcessorB Map existingProps = nodeService.getProperties(nodeToUpdate); // inject transferred property here - if(!props.containsKey(TransferModel.PROP_REPOSITORY_ID)) - { - log.debug("injecting repositoryId property"); - props.put(TransferModel.PROP_REPOSITORY_ID, header.getRepositoryId()); - } - props.put(TransferModel.PROP_FROM_REPOSITORY_ID, header.getRepositoryId()); + injectTransferred(props); + +// if(!props.containsKey(TransferModel.PROP_REPOSITORY_ID)) +// { +// log.debug("injecting repositoryId property"); +// props.put(TransferModel.PROP_REPOSITORY_ID, header.getRepositoryId()); +// } +// props.put(TransferModel.PROP_FROM_REPOSITORY_ID, header.getRepositoryId()); + + // Remove the invadedBy property since that is used by the transfer service + // and is local to this repository. + props.remove(TransferModel.PROP_INVADED_BY); // Do we need to worry about locking this updated ? if(header.isReadOnly()) @@ -436,6 +490,12 @@ public class RepoPrimaryManifestProcessorImpl extends AbstractManifestProcessorB // Split out the content properties and sanitise the others Map contentProps = processProperties(nodeToUpdate, props, existingProps); + + // If there was already a value for invadedBy then leave it alone rather than replacing it. + if(existingProps.containsKey(TransferModel.PROP_INVADED_BY)) + { + props.put(TransferModel.PROP_INVADED_BY, existingProps.get(TransferModel.PROP_INVADED_BY)); + } // Update the non-content properties nodeService.setProperties(nodeToUpdate, props); @@ -461,7 +521,13 @@ public class RepoPrimaryManifestProcessorImpl extends AbstractManifestProcessorB } aspectsToRemove.removeAll(suppliedAspects); + + /** + * Don't remove the aspects that the transfer service uses itself. + */ aspectsToRemove.remove(TransferModel.ASPECT_TRANSFERRED); + aspectsToRemove.remove(TransferModel.ASPECT_ALIEN); + suppliedAspects.removeAll(existingAspects); // Now aspectsToRemove contains the set of aspects to remove @@ -541,6 +607,7 @@ public class RepoPrimaryManifestProcessorImpl extends AbstractManifestProcessorB } } } + /** * This method takes all the received properties and separates them into two parts. The content properties are @@ -798,4 +865,27 @@ public class RepoPrimaryManifestProcessorImpl extends AbstractManifestProcessorB return permissionService; } + /** + * inject transferred + */ + private void injectTransferred(Map props) + { + if(!props.containsKey(TransferModel.PROP_REPOSITORY_ID)) + { + log.debug("injecting repositoryId property"); + props.put(TransferModel.PROP_REPOSITORY_ID, header.getRepositoryId()); + } + props.put(TransferModel.PROP_FROM_REPOSITORY_ID, header.getRepositoryId()); + } + + public void setAlienProcessor(AlienProcessor alienProcessor) + { + this.alienProcessor = alienProcessor; + } + + public AlienProcessor getAlienProcessor() + { + return alienProcessor; + } + } diff --git a/source/java/org/alfresco/repo/transfer/RepoSecondaryManifestProcessorImpl.java b/source/java/org/alfresco/repo/transfer/RepoSecondaryManifestProcessorImpl.java index 2022c10f3c..aed60e2b9b 100644 --- a/source/java/org/alfresco/repo/transfer/RepoSecondaryManifestProcessorImpl.java +++ b/source/java/org/alfresco/repo/transfer/RepoSecondaryManifestProcessorImpl.java @@ -40,10 +40,10 @@ import org.alfresco.service.namespace.RegexQNamePattern; * * The secondary manifest processor performs a second parse of the snapshot file. * - * It is responsible for linking nodes together, moving them out of the temporary space - * into their final position in the repository. At the point that this processor runs both - * ends (source and target) of the nodes' associations should be available in the receiving - * repository. + * It is responsible for linking nodes together. + * + * At the point that this processor runs both ends (source and target) of the nodes' associations should be + * available in the receiving repository. * */ public class RepoSecondaryManifestProcessorImpl extends AbstractManifestProcessorBase diff --git a/source/java/org/alfresco/repo/transfer/RepoTertiaryManifestProcessorImpl.java b/source/java/org/alfresco/repo/transfer/RepoTertiaryManifestProcessorImpl.java index 3bfb02c08b..19686dbb18 100644 --- a/source/java/org/alfresco/repo/transfer/RepoTertiaryManifestProcessorImpl.java +++ b/source/java/org/alfresco/repo/transfer/RepoTertiaryManifestProcessorImpl.java @@ -19,10 +19,12 @@ package org.alfresco.repo.transfer; +import java.io.Serializable; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; +import java.util.Stack; import org.alfresco.repo.transfer.manifest.TransferManifestDeletedNode; import org.alfresco.repo.transfer.manifest.TransferManifestHeader; @@ -46,12 +48,11 @@ import org.apache.commons.logging.LogFactory; * which exist in the target repository that do not exist in the source repository. * * If the transfer is not "sync" then this processor does nothing. - * */ public class RepoTertiaryManifestProcessorImpl extends AbstractManifestProcessorBase { private NodeService nodeService; - private CorrespondingNodeResolver nodeResolver; + private AlienProcessor alienProcessor; private static final Log log = LogFactory.getLog(RepoTertiaryManifestProcessorImpl.class); @@ -83,7 +84,7 @@ public class RepoTertiaryManifestProcessorImpl extends AbstractManifestProcessor protected void processNode(TransferManifestNormalNode node) { NodeRef nodeRef = node.getNodeRef(); - log.debug("processNode " + nodeRef); + log.debug("processNode : " + nodeRef); if(isSync) { @@ -94,7 +95,10 @@ public class RepoTertiaryManifestProcessorImpl extends AbstractManifestProcessor for(ChildAssociationRef ref : expectedChildren) { - log.debug("expecting child" + ref); + if(log.isDebugEnabled()) + { + log.debug("expecting child" + ref); + } expectedChildNodeRefs.add(ref.getChildRef()); } @@ -103,6 +107,7 @@ public class RepoTertiaryManifestProcessorImpl extends AbstractManifestProcessor if(nodeService.exists(nodeRef)) { log.debug("destination node exists"); + /** * yes this node exists in the destination. */ @@ -126,8 +131,10 @@ public class RepoTertiaryManifestProcessorImpl extends AbstractManifestProcessor { /** * An unexpected child - if this node has been transferred then - * it needs to be deleted. If it is a local node then we don't - * touch it. + * it needs to be deleted. + * + * another repository then we have to prune the alien children + * rather than deleting it. */ log.debug("an unexpected child node:" + child); if(nodeService.hasAspect(childNodeRef, TransferModel.ASPECT_TRANSFERRED)) @@ -141,10 +148,22 @@ public class RepoTertiaryManifestProcessorImpl extends AbstractManifestProcessor if(manifestRepositoryId.equalsIgnoreCase(fromRepositoryId)) { // Yes the manifest repository Id and the from repository Id match. - - // Destination node needs to be deleted. - nodeService.deleteNode(childNodeRef); - log.debug("deleted node:" + childNodeRef); + if(nodeService.hasAspect(childNodeRef, TransferModel.ASPECT_ALIEN)) + { + /** + * This node can't be deleted since it contains alien content + * it needs to be "pruned" of the transferring repo's content instead. + */ + log.debug("node to be deleted contains alien content so needs to be pruned." + childNodeRef); + alienProcessor.pruneNode(childNodeRef, fromRepositoryId); + //pruneNode(childNodeRef, fromRepositoryId); + } + else + { + // Destination node needs to be deleted. + nodeService.deleteNode(childNodeRef); + log.debug("deleted node:" + childNodeRef); + } } } } @@ -182,13 +201,169 @@ public class RepoTertiaryManifestProcessorImpl extends AbstractManifestProcessor { this.nodeService = nodeService; } + +// /** +// * Prune out the non aliens from the specified repository +// * +// * Need to walk the tree downwards pruning any aliens for this repository +// * +// * Also any folders remaining need to have their invaded by field rippled upwards since they may no +// * longer be invaded by the specified repository if all the alien children have been pruned. +// * +// * @param nodeRef the node to prune +// * @param fromRepositoryId the repository id of the nodes to prune. +// */ +// private void pruneNode(NodeRef parentNodeRef, String fromRepositoryId) +// { +// Stack nodesToPrune = new Stack(); +// Stack foldersToRecalculate = new Stack(); +// nodesToPrune.add(parentNodeRef); +// +// while(!nodesToPrune.isEmpty()) +// { +// /** +// * for all alien children +// * +// * if from the repo with no (other) aliens - delete +// * +// * if from the repo with multiple alien invasions - leave alone but process children +// */ +// NodeRef currentNodeRef = nodesToPrune.pop(); +// +// log.debug("pruneNode:" + currentNodeRef); +// +// if(nodeService.hasAspect(currentNodeRef, TransferModel.ASPECT_ALIEN)) +// { +// // Yes this is an alien node +// ListinvadedBy = (List)nodeService.getProperty(currentNodeRef, TransferModel.PROP_INVADED_BY); +// if(invadedBy.contains(fromRepositoryId)) +// { +// if(invadedBy.size() == 1) +// { +// // we are invaded by a single repository which must be fromRepositoryId +// log.debug("pruned - deleted node:" + currentNodeRef); +// nodeService.deleteNode(currentNodeRef); +// } +// else +// { +// log.debug("folder has multiple invaders"); +// // multiple invasion - so it must be a folder +// //TODO replace with a more efficient query +// List refs = nodeService.getChildAssocs(parentNodeRef); +// for(ChildAssociationRef ref : refs) +// { +// if(log.isDebugEnabled()) +// { +// log.debug("will need to check child:" + ref); +// } +// nodesToPrune.push(ref.getChildRef()); +// +// /** +// * This folder can't be deleted so its invaded flag needs to be re-calculated +// */ +// if(!foldersToRecalculate.contains(ref.getParentRef())) +// { +// foldersToRecalculate.push(ref.getParentRef()); +// } +// } +// } +// } +// else +// { +// /** +// * Current node has been invaded by another repository +// * +// * Need to check fromRepositoryId since its children may need to be pruned +// */ +// nodeService.hasAspect(currentNodeRef, TransferModel.ASPECT_TRANSFERRED); +// { +// String fromRepoId = (String)nodeService.getProperty(currentNodeRef, TransferModel.PROP_FROM_REPOSITORY_ID); +// if(fromRepositoryId.equalsIgnoreCase(fromRepoId)) +// { +// log.debug("folder is from the transferring repository"); +// // invaded from somewhere else - so it must be a folder +// List refs = nodeService.getChildAssocs(currentNodeRef); +// for(ChildAssociationRef ref : refs) +// { +// if(log.isDebugEnabled()) +// { +// log.debug("will need to check child:" + ref); +// } +// nodesToPrune.push(ref.getChildRef()); +// +// /** +// * This folder can't be deleted so its invaded flag needs to be re-calculated +// */ +// if(!foldersToRecalculate.contains(ref.getParentRef())) +// { +// foldersToRecalculate.push(ref.getParentRef()); +// } +// } +// } +// } +// } +// } +// else +// { +// // Current node does not contain alien nodes so it can be deleted. +// nodeService.hasAspect(currentNodeRef, TransferModel.ASPECT_TRANSFERRED); +// { +// String fromRepoId = (String)nodeService.getProperty(currentNodeRef, TransferModel.PROP_FROM_REPOSITORY_ID); +// if(fromRepositoryId.equalsIgnoreCase(fromRepoId)) +// { +// // we are invaded by a single repository +// log.debug("pruned - deleted non alien node:" + currentNodeRef); +// nodeService.deleteNode(currentNodeRef); +// } +// } +// } +// } +// +// /** +// * Now ripple the "invadedBy" flag upwards. +// */ +// +// while(!foldersToRecalculate.isEmpty()) +// { +// NodeRef folderNodeRef = foldersToRecalculate.pop(); +// +// log.debug("recalculate invadedBy :" + folderNodeRef); +// +// ListfolderInvadedBy = (List)nodeService.getProperty(folderNodeRef, TransferModel.PROP_INVADED_BY); +// +// boolean stillInvaded = false; +// //TODO need a more efficient query here +// List refs = nodeService.getChildAssocs(folderNodeRef); +// for(ChildAssociationRef ref : refs) +// { +// NodeRef childNode = ref.getChildRef(); +// ListchildInvadedBy = (List)nodeService.getProperty(childNode, TransferModel.PROP_INVADED_BY); +// +// if(childInvadedBy.contains(fromRepositoryId)) +// { +// log.debug("folder is still invaded"); +// stillInvaded = true; +// break; +// } +// } +// +// if(!stillInvaded) +// { +// List newInvadedBy = new ArrayList(folderInvadedBy); +// folderInvadedBy.remove(fromRepositoryId); +// nodeService.setProperty(folderNodeRef, TransferModel.PROP_INVADED_BY, (Serializable)newInvadedBy); +// } +// } +// log.debug("pruneNode: end"); +// } - /** - * @param nodeResolver - * the nodeResolver to set - */ - public void setNodeResolver(CorrespondingNodeResolver nodeResolver) + public void setAlienProcessor(AlienProcessor alienProcessor) { - this.nodeResolver = nodeResolver; + this.alienProcessor = alienProcessor; + } + + public AlienProcessor getAlienProcessor() + { + return alienProcessor; } } diff --git a/source/java/org/alfresco/repo/transfer/RepoTransferReceiverImpl.java b/source/java/org/alfresco/repo/transfer/RepoTransferReceiverImpl.java index 994dff19e6..edcf1ec898 100644 --- a/source/java/org/alfresco/repo/transfer/RepoTransferReceiverImpl.java +++ b/source/java/org/alfresco/repo/transfer/RepoTransferReceiverImpl.java @@ -28,10 +28,13 @@ import java.io.OutputStreamWriter; import java.io.Serializable; import java.io.Writer; import java.text.SimpleDateFormat; +import java.util.ArrayList; import java.util.Date; import java.util.HashMap; +import java.util.Iterator; import java.util.List; import java.util.Map; +import java.util.Vector; import java.util.concurrent.ConcurrentHashMap; import javax.xml.parsers.SAXParser; @@ -67,6 +70,7 @@ import org.alfresco.service.cmr.transfer.TransferException; import org.alfresco.service.cmr.transfer.TransferProgress; import org.alfresco.service.cmr.transfer.TransferReceiver; import org.alfresco.service.cmr.transfer.TransferProgress.Status; +import org.alfresco.service.descriptor.DescriptorService; import org.alfresco.service.namespace.NamespaceService; import org.alfresco.service.namespace.QName; import org.alfresco.service.namespace.RegexQNamePattern; @@ -158,6 +162,10 @@ public class RepoTransferReceiverImpl implements TransferReceiver, private TenantService tenantService; private RuleService ruleService; private PolicyComponent policyComponent; + private DescriptorService descriptorService; + private AlienProcessor alienProcessor; + + //private String localRepositoryId = descriptorService.getCurrentRepositoryDescriptor().getId(); private Map transferLockFolderMap = new ConcurrentHashMap(); private Map transferTempFolderMap = new ConcurrentHashMap(); @@ -176,17 +184,24 @@ public class RepoTransferReceiverImpl implements TransferReceiver, PropertyCheck.mandatory(this, "inboundTransferRecordsPath", inboundTransferRecordsPath); PropertyCheck.mandatory(this, "rootStagingDirectory", rootStagingDirectory); PropertyCheck.mandatory(this, "policyComponent", policyComponent); + PropertyCheck.mandatory(this, "descriptorService", descriptorService); + PropertyCheck.mandatory(this, "alienProcessor", alienProcessor); -// // Register policy behaviours -// this.getPolicyComponent().bindAssociationBehaviour( -// NodeServicePolicies.OnCreateChildAssociationPolicy.QNAME, -// TransferModel.ASPECT_TRANSFERRED, -// new JavaBehaviour(this, "onCreateChildAssociation", NotificationFrequency.EVERY_EVENT)); -// -// this.getPolicyComponent().bindClassBehaviour( -// NodeServicePolicies.BeforeDeleteNodePolicy.QNAME, -// this, -// new JavaBehaviour(this, "beforeDeleteNode", NotificationFrequency.EVERY_EVENT)); + /** + * For every new child of a node with the trx:transferred aspect run this.onCreateChildAssociation + */ + this.getPolicyComponent().bindAssociationBehaviour( + NodeServicePolicies.OnCreateChildAssociationPolicy.QNAME, + TransferModel.ASPECT_TRANSFERRED, + new JavaBehaviour(this, "onCreateChildAssociation", NotificationFrequency.EVERY_EVENT)); + + /** + * For every node with the trx:alien aspect run this.beforeDeleteNode + */ + this.getPolicyComponent().bindClassBehaviour( + NodeServicePolicies.BeforeDeleteNodePolicy.QNAME, + TransferModel.ASPECT_ALIEN, + new JavaBehaviour(this, "beforeDeleteNode", NotificationFrequency.EVERY_EVENT)); } @@ -859,14 +874,19 @@ public class RepoTransferReceiverImpl implements TransferReceiver, } /** - * @param progressMonitor - * the progressMonitor to set + * Set the ruleService + * @param ruleService + * the ruleService to set */ public void setRuleService(RuleService ruleService) { this.ruleService = ruleService; } + /** + * Get the rule service + * @return the rule service + */ public RuleService getRuleService() { return this.ruleService; @@ -935,10 +955,11 @@ public class RepoTransferReceiverImpl implements TransferReceiver, } /** - * When a new node is created as a child of a Transferred node then - * the transferred nodes need to be marked as Alien nodes. - * - * The tree needs to be walked upwards to mark all parent transferred nodes as alien. + * When a new node is created as a child of a Transferred or Alien node then + * the new node needs to be marked as an alien. + *

+ * Then the tree needs to be walked upwards to mark all parent + * transferred nodes as alien. */ public void onCreateChildAssociation(ChildAssociationRef childAssocRef, boolean isNewNode) @@ -946,75 +967,273 @@ public class RepoTransferReceiverImpl implements TransferReceiver, log.debug("on create child association to transferred node"); - ChildAssociationRef ref = childAssocRef; - - if(childAssocRef.isPrimary()) - { - while(ref != null) - { - NodeRef parentNodeRef = ref.getParentRef(); - NodeRef childNodeRef = ref.getChildRef(); - - if(nodeService.hasAspect(parentNodeRef, TransferModel.ASPECT_TRANSFERRED)) - { - Boolean isAlien = (Boolean)nodeService.getProperty(parentNodeRef, TransferModel.PROP_ALIEN); - - if (!isAlien) - { - log.debug("setting node as alien:" + parentNodeRef); - nodeService.setProperty(parentNodeRef, TransferModel.PROP_ALIEN, Boolean.TRUE); - ref = nodeService.getPrimaryParent(parentNodeRef); - } - else - { - log.debug("parent node is already alien"); - ref = null; - } - } - else - { - log.debug("parent is not a transferred node"); - ref = null; - } - } - } + final String localRepositoryId = descriptorService.getCurrentRepositoryDescriptor().getId(); + alienProcessor.onCreateChild(childAssocRef, localRepositoryId); +// +// ChildAssociationRef currentAssoc = childAssocRef; +// +// final String localRepositoryId = descriptorService.getCurrentRepositoryDescriptor().getId(); +// +// // TODO Needs to check assoc is a cm:contains or subtype +// if(childAssocRef.isPrimary()) +// { +// NodeRef parentNodeRef = currentAssoc.getParentRef(); +// NodeRef childNodeRef = currentAssoc.getChildRef(); +// +// /** +// * Make the new child node ref an alien node +// */ +// setAlien(childNodeRef, localRepositoryId); +// +// /** +// * Now deal with the parents of this alien node +// */ +// while(currentAssoc != null) +// { +// parentNodeRef = currentAssoc.getParentRef(); +// childNodeRef = currentAssoc.getChildRef(); +// +// if(nodeService.hasAspect(parentNodeRef, TransferModel.ASPECT_TRANSFERRED) || nodeService.hasAspect(parentNodeRef, TransferModel.ASPECT_ALIEN)) +// { +// if (!isInvaded(parentNodeRef, localRepositoryId)) +// { +// if(log.isDebugEnabled()) +// { +// log.debug("alien invades parent node:" + parentNodeRef + ", repositoryId:" + localRepositoryId); +// } +// +// final NodeRef newAlien = parentNodeRef; +// +// /** +// * Parent may be locked or not be editable by the current user +// * turn off auditing and lock service for this transaction and +// * run as admin. +// */ +// RunAsWork actionRunAs = new RunAsWork() +// { +// public Void doWork() throws Exception +// { +// behaviourFilter.disableBehaviour(newAlien, ContentModel.ASPECT_AUDITABLE); +// behaviourFilter.disableBehaviour(newAlien, ContentModel.ASPECT_LOCKABLE); +// setAlien(newAlien, localRepositoryId); +// return null; +// } +// }; +// AuthenticationUtil.runAs(actionRunAs, AuthenticationUtil.getSystemUserName()); +// +// // Yes the parent has been invaded so step up to the parent's parent +// currentAssoc = nodeService.getPrimaryParent(parentNodeRef); +// } +// else +// { +// log.debug("parent node is already invaded"); +// currentAssoc = null; +// } +// } +// else +// { +// log.debug("parent is not a transferred node"); +// currentAssoc = null; +// } +// } +// } } /** - * When an old node is deleted that is a child of a transferred node the tree may need to be walked to - * mark parent folder as non alien. + * When an alien node is deleted the it may be the last alien invader + *

+ * Walk the tree checking the invasion status! */ - public void beforeDeleteNode(NodeRef nodeRef) + public void beforeDeleteNode(NodeRef deletedNodeRef) { log.debug("on delete node - need to check for transferred node"); - - ChildAssociationRef ref = nodeService.getPrimaryParent(nodeRef); - - while(ref != null) - { - NodeRef parentNodeRef = ref.getParentRef(); - - /** - * Was the parent node transferred ? - */ - if(nodeService.hasAspect(parentNodeRef, TransferModel.ASPECT_TRANSFERRED)) - { - log.debug("parent node was transferred - check siblings"); - - /** - * Check the siblings of this node to see whether there are any other alien nodes. - */ - // nodeService.getChildAssocs(parentNodeRef); - // BUGBUG Code not complete - ref = null; - - - } - else - { - log.debug("parent is not a transferred node"); - ref = null; - } - } + alienProcessor.beforeDeleteAlien(deletedNodeRef); +// +// Liststuff = (List)nodeService.getProperty(deletedNodeRef, TransferModel.PROP_INVADED_BY); +// +// Vector exInvaders = new Vector(stuff); +// +// ChildAssociationRef currentAssoc = nodeService.getPrimaryParent(deletedNodeRef); +// +// while(currentAssoc != null && exInvaders != null && exInvaders.size() > 0) +// { +// NodeRef parentNodeRef = currentAssoc.getParentRef(); +// NodeRef currentNodeRef = currentAssoc.getChildRef(); +// +// /** +// * Does the parent have alien invaders ? +// */ +// if(nodeService.hasAspect(parentNodeRef, TransferModel.ASPECT_ALIEN)) +// { +// log.debug("parent node is alien - check siblings"); +// +// /** +// * For each invader of the deletedNode +// */ +// Iterator i = exInvaders.listIterator(); +// while(i.hasNext()) +// { +// String exInvader = i.next(); +// log.debug("Checking exInvader:" + exInvader); +// +// /** +// * Check the siblings of this node to see whether there are any other alien nodes for this invader. +// */ +// //TODO replace with a more efficient query +// List refs = nodeService.getChildAssocs(parentNodeRef); +// +// for(ChildAssociationRef ref : refs) +// { +// NodeRef childRef = ref.getChildRef(); +// ListinvadedBy = (List)nodeService.getProperty(childRef, TransferModel.PROP_INVADED_BY); +// +// if(childRef.equals(currentNodeRef)) +// { +// // do nothing - this is the node we are working with. +// } +// else +// { +// if(invadedBy != null && invadedBy.contains(exInvader)) +// { +// // There is a sibling so remove this from the list of ex invaders. +// log.debug("yes there is a sibling so it remains an invader"); +// i.remove(); +// break; +// } +// } +// } // for each child assoc +// +// } // for each invader +// +// log.debug("end of checking siblings"); +// +// if(exInvaders.size() > 0) +// { +// log.debug("removing invaders from parent node:" + parentNodeRef); +// List parentInvaders = (List)nodeService.getProperty(parentNodeRef, TransferModel.PROP_INVADED_BY); +// +// final List newInvaders = new ArrayList(10); +// for(String invader : parentInvaders) +// { +// if(exInvaders.contains(invader)) +// { +// log.debug("removing invader:" + invader); +// } +// else +// { +// newInvaders.add(invader); +// } +// } +// +// final NodeRef oldAlien = parentNodeRef; +// +// /** +// * Parent may be locked or not be editable by the current user +// * turn off auditing and lock service for this transaction and +// * run as admin. +// */ +// RunAsWork actionRunAs = new RunAsWork() +// { +// public Void doWork() throws Exception +// { +// behaviourFilter.disableBehaviour(oldAlien, ContentModel.ASPECT_AUDITABLE); +// behaviourFilter.disableBehaviour(oldAlien, ContentModel.ASPECT_LOCKABLE); +// if(newInvaders.size() > 0) +// { +// nodeService.setProperty(oldAlien, TransferModel.PROP_INVADED_BY, (Serializable)newInvaders); +// } +// else +// { +// log.debug("parent node no is no longer alien"); +// nodeService.removeAspect(oldAlien, TransferModel.ASPECT_ALIEN); +// } +// return null; +// } +// }; +// AuthenticationUtil.runAs(actionRunAs, AuthenticationUtil.getSystemUserName()); +// } +// +// /** +// * Now step up to the parent's parent +// */ +// currentAssoc = nodeService.getPrimaryParent(parentNodeRef); +// } +// else +// { +// log.debug("parent is not an alien node"); +// currentAssoc = null; +// } +// } // end of while + } + +// /** +// * Is this node invaded ? +// * @param nodeRef +// * @param invader +// * @return true, this node has been invaded by the invader +// */ +// private boolean isInvaded(NodeRef nodeRef, String invader) +// { +// ListinvadedBy = (List)nodeService.getProperty(nodeRef, TransferModel.PROP_INVADED_BY); +// +// if(invadedBy == null) +// { +// return false; +// } +// +// return invadedBy.contains(invader); +// } +// +// /** +// * Mark the specified node as an alien node, invadedby the invader. +// * @param newAlien +// * @param invader +// */ +// private void setAlien(NodeRef newAlien, String invader) +// { +// // Introduce a Multi-valued property +// List invadedBy = (List)nodeService.getProperty(newAlien, +// TransferModel.PROP_INVADED_BY); +// +// if(invadedBy == null) +// { +// nodeService.setProperty(newAlien, TransferModel.PROP_ALIEN, Boolean.TRUE); +// invadedBy = new ArrayList(1); +// } +// invadedBy.add(invader); +// +// /** +// * Set the invaded by property +// */ +// nodeService.setProperty(newAlien, TransferModel.PROP_INVADED_BY, (Serializable) invadedBy); +// +// /** +// * Experiment with a residual property +// */ +// nodeService.setProperty(newAlien, QName.createQName(TransferModel.TRANSFER_MODEL_1_0_URI, +// "invader" + invader), Boolean.TRUE); +// +// } + + + + public void setDescriptorService(DescriptorService descriptorService) + { + this.descriptorService = descriptorService; + } + + public DescriptorService getDescriptorService() + { + return descriptorService; + } + + public void setAlienProcessor(AlienProcessor alienProcessor) + { + this.alienProcessor = alienProcessor; + } + + public AlienProcessor getAlienProcessor() + { + return alienProcessor; } } diff --git a/source/java/org/alfresco/repo/transfer/TransferModel.java b/source/java/org/alfresco/repo/transfer/TransferModel.java index c2137786fb..f1e55163be 100644 --- a/source/java/org/alfresco/repo/transfer/TransferModel.java +++ b/source/java/org/alfresco/repo/transfer/TransferModel.java @@ -39,7 +39,14 @@ public interface TransferModel static final QName ASPECT_TRANSFERRED = QName.createQName(TRANSFER_MODEL_1_0_URI, "transferred"); static final QName PROP_REPOSITORY_ID = QName.createQName(TRANSFER_MODEL_1_0_URI, "repositoryId"); static final QName PROP_FROM_REPOSITORY_ID = QName.createQName(TRANSFER_MODEL_1_0_URI, "fromRepositoryId"); + + /** + * Aspect : alien + */ + static final QName ASPECT_ALIEN = QName.createQName(TRANSFER_MODEL_1_0_URI, "alien"); + static final QName PROP_INVADED_BY = QName.createQName(TRANSFER_MODEL_1_0_URI, "invadedBy"); static final QName PROP_ALIEN = QName.createQName(TRANSFER_MODEL_1_0_URI, "alien"); + /* * Type : Transfer Group */ @@ -72,8 +79,7 @@ public interface TransferModel static final QName PROP_PROGRESS_ENDPOINT = QName.createQName(TRANSFER_MODEL_1_0_URI, "progressEndpoint"); static final QName PROP_TRANSFER_STATUS = QName.createQName(TRANSFER_MODEL_1_0_URI, "transferStatus"); static final QName PROP_TRANSFER_ERROR = QName.createQName(TRANSFER_MODEL_1_0_URI, "transferError"); - - + /* * Type : Transfer report */ diff --git a/source/java/org/alfresco/repo/transfer/TransferServiceImplTest.java b/source/java/org/alfresco/repo/transfer/TransferServiceImplTest.java index bc823a8cb6..923af3f49a 100644 --- a/source/java/org/alfresco/repo/transfer/TransferServiceImplTest.java +++ b/source/java/org/alfresco/repo/transfer/TransferServiceImplTest.java @@ -18,9 +18,11 @@ */ package org.alfresco.repo.transfer; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + import java.util.ArrayList; import java.util.Date; -import java.util.HashMap; import java.util.HashSet; import java.util.List; import java.util.Locale; @@ -35,12 +37,9 @@ import javax.xml.validation.Schema; import javax.xml.validation.SchemaFactory; import javax.xml.validation.Validator; -import org.alfresco.error.AlfrescoRuntimeException; import org.alfresco.model.ContentModel; -import org.alfresco.repo.dictionary.DictionaryDAOImpl.DictionaryRegistry; import org.alfresco.repo.security.authentication.AuthenticationComponent; import org.alfresco.repo.security.authentication.AuthenticationUtil; -import org.alfresco.repo.security.authentication.AuthenticationUtil.RunAsWork; import org.alfresco.repo.transfer.manifest.TransferManifestNodeFactory; import org.alfresco.service.cmr.action.ActionService; import org.alfresco.service.cmr.lock.LockService; @@ -76,8 +75,6 @@ import org.alfresco.util.BaseAlfrescoSpringTest; import org.alfresco.util.GUID; import org.alfresco.util.Pair; import org.alfresco.util.PropertyMap; -import org.apache.commons.logging.Log; -import org.apache.commons.logging.LogFactory; import org.springframework.transaction.TransactionDefinition; import org.springframework.transaction.support.DefaultTransactionDefinition; import org.springframework.util.ResourceUtils; @@ -109,7 +106,9 @@ public class TransferServiceImplTest extends BaseAlfrescoSpringTest String COMPANY_HOME_XPATH_QUERY = "/{http://www.alfresco.org/model/application/1.0}company_home"; String GUEST_HOME_XPATH_QUERY = "/{http://www.alfresco.org/model/application/1.0}company_home/{http://www.alfresco.org/model/application/1.0}guest_home"; - + String REPO_ID_A = "RepoIdA"; + String REPO_ID_B = "RepoIdB"; + String REPO_ID_C = "RepoIdC"; @Override public void runBare() throws Throwable @@ -145,10 +144,7 @@ public class TransferServiceImplTest extends BaseAlfrescoSpringTest authenticationComponent.setSystemUserAsCurrentUser(); setTransactionDefinition(new DefaultTransactionDefinition(TransactionDefinition.PROPAGATION_REQUIRES_NEW)); - assertNotNull("receiver is null", this.receiver); - - - + assertNotNull("receiver is null", this.receiver); } /** @@ -345,7 +341,7 @@ public class TransferServiceImplTest extends BaseAlfrescoSpringTest } /** - * Test of Get All Trabsfer Targets By Group + * Test of Get All Transfer Targets By Group */ //TODO Test not complete - can't yet put targets in different groups public void testGetAllTransferTargetsByGroup() throws Exception @@ -605,6 +601,21 @@ public class TransferServiceImplTest extends BaseAlfrescoSpringTest /** * Test the transfer method by sending one node. * + * Step 1: Create a new node (No content) + * transfer + * + * Step 2: Update Node title property + * transfer + * + * Step 3: Update Content property + * transfer + * + * Step 4: Transfer again + * transfer (Should transfer but not request the content item) + * + * Step 5: Negative test : transfer no nodes + * transfer (should throw exception) + * * This is a unit test so it does some shenanigans to send to the same instance of alfresco. */ public void testTransferOneNode() throws Exception @@ -620,6 +631,8 @@ public class TransferServiceImplTest extends BaseAlfrescoSpringTest * For unit test * - replace the HTTP transport with the in-process transport * - replace the node factory with one that will map node refs, paths etc. + * + * Fake Repository Id */ TransferTransmitter transmitter = new UnitTestInProcessTransmitterImpl(receiver, contentService, transactionService); transferServiceImpl.setTransmitter(transmitter); @@ -628,7 +641,10 @@ public class TransferServiceImplTest extends BaseAlfrescoSpringTest List> pathMap = testNodeFactory.getPathMap(); // Map company_home/guest_home to company_home so tranferred nodes and moved "up" one level. pathMap.add(new Pair(PathHelper.stringToPath(GUEST_HOME_XPATH_QUERY), PathHelper.stringToPath(COMPANY_HOME_XPATH_QUERY))); - + + DescriptorService mockedDescriptorService = getMockDescriptorService(REPO_ID_A); + transferServiceImpl.setDescriptorService(mockedDescriptorService); + /** * Now go ahead and create our first transfer target */ @@ -657,11 +673,7 @@ public class TransferServiceImplTest extends BaseAlfrescoSpringTest contentNodeRef = child.getChildRef(); nodeService.setProperty(contentNodeRef, ContentModel.PROP_TITLE, CONTENT_TITLE); nodeService.setProperty(contentNodeRef, ContentModel.PROP_NAME, name); - - ContentWriter writer = contentService.getWriter(contentNodeRef, ContentModel.PROP_CONTENT, true); - writer.setLocale(CONTENT_LOCALE); - writer.putContent(CONTENT_STRING); - + if(!transferService.targetExists(targetName)) { transferMe = createTransferTarget(targetName); @@ -681,7 +693,7 @@ public class TransferServiceImplTest extends BaseAlfrescoSpringTest try { /** - * Transfer our transfer target node which has no content + * Step 1: Transfer our node which has no content */ { TransferDefinition definition = new TransferDefinition(); @@ -718,8 +730,6 @@ public class TransferServiceImplTest extends BaseAlfrescoSpringTest logger.debug("srcCreatedDate : " + srcCreatedDate + " destCreatedDate : " + destCreatedDate); assertTrue("dest created date is not correct", destCreatedDate.compareTo(srcCreatedDate)== 0); - - // Check injected transferred aspect. assertNotNull("transferredAspect", (String)nodeService.getProperty(destNodeRef, TransferModel.PROP_REPOSITORY_ID)); @@ -737,6 +747,7 @@ public class TransferServiceImplTest extends BaseAlfrescoSpringTest try { /** + * Step 2: * Transfer our node again - so this is an update of the title property */ { @@ -777,17 +788,22 @@ public class TransferServiceImplTest extends BaseAlfrescoSpringTest // Check injected transferred aspect. assertNotNull("transferredAspect", (String)nodeService.getProperty(destNodeRef, TransferModel.PROP_REPOSITORY_ID)); + ContentWriter writer = contentService.getWriter(contentNodeRef, ContentModel.PROP_CONTENT, true); + writer.setLocale(CONTENT_LOCALE); + writer.putContent(CONTENT_STRING); + } finally { endTransaction(); } - logger.debug("Transfer again - this is an update"); + logger.debug("Transfer again - this is an update with new content"); startNewTransaction(); try { /** + * Step 3: * Transfer our node again - so this is an update */ { @@ -804,6 +820,7 @@ public class TransferServiceImplTest extends BaseAlfrescoSpringTest } /** + * Step 4: * Now transfer nothing - content items do not need to be transferred since its already on * the destination. */ @@ -841,6 +858,7 @@ public class TransferServiceImplTest extends BaseAlfrescoSpringTest } /** + * Step 5 * Negative test transfer nothing */ logger.debug("Transfer again - with no content - should throw exception"); @@ -897,6 +915,9 @@ public class TransferServiceImplTest extends BaseAlfrescoSpringTest // Map company_home/guest_home to company_home so tranferred nodes and moved "up" one level. pathMap.add(new Pair(PathHelper.stringToPath(GUEST_HOME_XPATH_QUERY), PathHelper.stringToPath(COMPANY_HOME_XPATH_QUERY))); + DescriptorService mockedDescriptorService = getMockDescriptorService(REPO_ID_A); + transferServiceImpl.setDescriptorService(mockedDescriptorService); + TransferTarget transferMe; startNewTransaction(); @@ -1159,7 +1180,11 @@ public class TransferServiceImplTest extends BaseAlfrescoSpringTest List> pathMap = testNodeFactory.getPathMap(); // Map company_home/guest_home to company_home so tranferred nodes and moved "up" one level. pathMap.add(new Pair(PathHelper.stringToPath(GUEST_HOME_XPATH_QUERY), PathHelper.stringToPath(COMPANY_HOME_XPATH_QUERY))); - + + DescriptorService mockedDescriptorService = getMockDescriptorService(REPO_ID_A); + transferServiceImpl.setDescriptorService(mockedDescriptorService); + + String CONTENT_TITLE = "ContentTitle"; String CONTENT_TITLE_UPDATED = "ContentTitleUpdated"; String CONTENT_NAME = GUID.generate(); @@ -1316,7 +1341,10 @@ public class TransferServiceImplTest extends BaseAlfrescoSpringTest List> pathMap = testNodeFactory.getPathMap(); // Map company_home/guest_home to company_home so tranferred nodes and moved "up" one level. pathMap.add(new Pair(PathHelper.stringToPath(GUEST_HOME_XPATH_QUERY), PathHelper.stringToPath(COMPANY_HOME_XPATH_QUERY))); - + + DescriptorService mockedDescriptorService = getMockDescriptorService(REPO_ID_A); + transferServiceImpl.setDescriptorService(mockedDescriptorService); + /** * Now go ahead and create our first transfer target * This needs to be committed before we can call transfer asycnc. @@ -1524,7 +1552,10 @@ public class TransferServiceImplTest extends BaseAlfrescoSpringTest List> pathMap = testNodeFactory.getPathMap(); // Map company_home/guest_home to company_home so tranferred nodes and moved "up" one level. pathMap.add(new Pair(PathHelper.stringToPath(GUEST_HOME_XPATH_QUERY), PathHelper.stringToPath(COMPANY_HOME_XPATH_QUERY))); - + + DescriptorService mockedDescriptorService = getMockDescriptorService(REPO_ID_A); + transferServiceImpl.setDescriptorService(mockedDescriptorService); + /** * Now go ahead and create our first transfer target * This needs to be committed before we can call transfer asycnc. @@ -1756,7 +1787,10 @@ public class TransferServiceImplTest extends BaseAlfrescoSpringTest List> pathMap = testNodeFactory.getPathMap(); // Map company_home/guest_home to company_home so tranferred nodes and moved "up" one level. pathMap.add(new Pair(PathHelper.stringToPath(GUEST_HOME_XPATH_QUERY), PathHelper.stringToPath(COMPANY_HOME_XPATH_QUERY))); - + + DescriptorService mockedDescriptorService = getMockDescriptorService(REPO_ID_A); + transferServiceImpl.setDescriptorService(mockedDescriptorService); + /** * Now go ahead and create our first transfer target * This needs to be committed before we can call transfer asycnc. @@ -2031,7 +2065,10 @@ public class TransferServiceImplTest extends BaseAlfrescoSpringTest List> pathMap = testNodeFactory.getPathMap(); // Map company_home/guest_home to company_home so tranferred nodes and moved "up" one level. pathMap.add(new Pair(PathHelper.stringToPath(GUEST_HOME_XPATH_QUERY), PathHelper.stringToPath(COMPANY_HOME_XPATH_QUERY))); - + + DescriptorService mockedDescriptorService = getMockDescriptorService(REPO_ID_A); + transferServiceImpl.setDescriptorService(mockedDescriptorService); + /** * Now go ahead and create our first transfer target */ @@ -2695,637 +2732,749 @@ public class TransferServiceImplTest extends BaseAlfrescoSpringTest } -// /** -// * Test the transfer method behaviour with respect to sync with alien nodes. -// * -// * In general an alien node will prevent deletion of the parent folders -// * -// * This is a unit test so it does some shenanigans to send to the same instance of alfresco. -// * -// * Tree of nodes -// * -// * A1 -// * | | | -// * A2 A3 (Content Node) B9 Alien Content Node -// * | -// * A4 A5 B10 (Alien Content Node) A6 -// * | | -// * A7 B11 (Alien Content Node) A8 B12 B13 Alien Contact Nodes -// * -// * Test steps - -// * 1 add A1, A2, A3, A4, A5, A6, A7, A8 -// * transfer(sync) -// * 2 add Alien node B9. A1 becomes Alien. -// * 3 remove alien node B9. A1 becomes non Alien. -// * 4 add Alien node B10. A1 and A2 become Alien -// * 5 remove Alien node B10. A1 and A2 become non Alien -// * 6 add B12 A6, A2, A1 becomes Alien -// * 7 add B13 A6, A2, A1 remains Alien -// * 8 remove B13 A6, A2, A1 remains Alien -// * 9 remove B12 A6, A2, A1 becomes non Alien. -// * 10 add B9 and B10 A1 and A2 become Alien -// * 11 remove B10 A2 becomes non alien A1 remains alien. -// * 12 delete A2 containing alien node B11 -// * transfer -// * (A5, A6, A7 and A8 should be deleted A2 and A4 remain since they contain alien content.) -// * -// */ -// public void testSyncWithAlienNodes() throws Exception -// { -// setDefaultRollback(false); -// -// String CONTENT_TITLE = "ContentTitle"; -// String CONTENT_TITLE_UPDATED = "ContentTitleUpdated"; -// Locale CONTENT_LOCALE = Locale.JAPAN; -// String CONTENT_STRING = "Hello"; -// -// /** -// * For unit test -// * - replace the HTTP transport with the in-process transport -// * - replace the node factory with one that will map node refs, paths etc. -// */ -// TransferTransmitter transmitter = new UnitTestInProcessTransmitterImpl(receiver, contentService, transactionService); -// transferServiceImpl.setTransmitter(transmitter); -// UnitTestTransferManifestNodeFactory testNodeFactory = new UnitTestTransferManifestNodeFactory(this.transferManifestNodeFactory); -// transferServiceImpl.setTransferManifestNodeFactory(testNodeFactory); -// List> pathMap = testNodeFactory.getPathMap(); -// // Map company_home/guest_home to company_home so tranferred nodes and moved "up" one level. -// pathMap.add(new Pair(PathHelper.stringToPath(GUEST_HOME_XPATH_QUERY), PathHelper.stringToPath(COMPANY_HOME_XPATH_QUERY))); -// -// /** -// * Now go ahead and create our first transfer target -// */ -// String targetName = "testSyncWithAlienNodes"; -// TransferTarget transferMe; -// -// NodeRef A1NodeRef; -// NodeRef A2NodeRef; -// NodeRef A3NodeRef; -// NodeRef A4NodeRef; -// NodeRef A5NodeRef; -// NodeRef A6NodeRef; -// NodeRef A7NodeRef; -// NodeRef A8NodeRef; -// NodeRef B9NodeRef; -// NodeRef B10NodeRef; -// NodeRef B11NodeRef; -// NodeRef B12NodeRef; -// NodeRef B13NodeRef; -// -// NodeRef destNodeRef; -// -// startNewTransaction(); -// try -// { -// /** -// * Get guest home -// */ -// String guestHomeQuery = "/app:company_home/app:guest_home"; -// ResultSet guestHomeResult = searchService.query(StoreRef.STORE_REF_WORKSPACE_SPACESSTORE, SearchService.LANGUAGE_XPATH, guestHomeQuery); -// assertEquals("", 1, guestHomeResult.length()); -// NodeRef guestHome = guestHomeResult.getNodeRef(0); -// -// /** -// * Create a test nodes A1 through A8 that we will read and write -// */ -// { -// // Node A1 -// String name = GUID.generate(); -// ChildAssociationRef child = nodeService.createNode(guestHome, ContentModel.ASSOC_CONTAINS, QName.createQName(name), ContentModel.TYPE_FOLDER); -// A1NodeRef = child.getChildRef(); -// nodeService.setProperty(A1NodeRef, ContentModel.PROP_TITLE, CONTENT_TITLE); -// nodeService.setProperty(A1NodeRef, ContentModel.PROP_NAME, name); -// } -// -// { -// // Node A2 -// ChildAssociationRef child = nodeService.createNode(A1NodeRef, ContentModel.ASSOC_CONTAINS, QName.createQName("A2"), ContentModel.TYPE_FOLDER); -// A2NodeRef = child.getChildRef(); -// nodeService.setProperty(A2NodeRef, ContentModel.PROP_TITLE, CONTENT_TITLE); -// nodeService.setProperty(A2NodeRef, ContentModel.PROP_NAME, "A2"); -// } -// -// { -// // Node A3 -// ChildAssociationRef child = nodeService.createNode(A1NodeRef, ContentModel.ASSOC_CONTAINS, QName.createQName("A3"), ContentModel.TYPE_CONTENT); -// A3NodeRef = child.getChildRef(); -// nodeService.setProperty(A3NodeRef, ContentModel.PROP_TITLE, CONTENT_TITLE); -// nodeService.setProperty(A3NodeRef, ContentModel.PROP_NAME, "A3"); -// -// ContentWriter writer = contentService.getWriter(A3NodeRef, ContentModel.PROP_CONTENT, true); -// writer.setLocale(CONTENT_LOCALE); -// writer.putContent(CONTENT_STRING); -// } -// { -// // Node A4 -// ChildAssociationRef child = nodeService.createNode(A2NodeRef, ContentModel.ASSOC_CONTAINS, QName.createQName("A4"), ContentModel.TYPE_FOLDER); -// A4NodeRef = child.getChildRef(); -// nodeService.setProperty(A4NodeRef, ContentModel.PROP_TITLE, CONTENT_TITLE); -// nodeService.setProperty(A4NodeRef, ContentModel.PROP_NAME, "A4"); -// } -// { -// // Node A5 -// ChildAssociationRef child = nodeService.createNode(A2NodeRef, ContentModel.ASSOC_CONTAINS, QName.createQName("A5"), ContentModel.TYPE_CONTENT); -// A5NodeRef = child.getChildRef(); -// nodeService.setProperty(A5NodeRef, ContentModel.PROP_TITLE, CONTENT_TITLE); -// nodeService.setProperty(A5NodeRef, ContentModel.PROP_NAME, "A5"); -// -// ContentWriter writer = contentService.getWriter(A5NodeRef, ContentModel.PROP_CONTENT, true); -// writer.setLocale(CONTENT_LOCALE); -// writer.putContent(CONTENT_STRING); -// } -// -// { -// // Node A6 -// ChildAssociationRef child = nodeService.createNode(A2NodeRef, ContentModel.ASSOC_CONTAINS, QName.createQName("A6"), ContentModel.TYPE_FOLDER); -// A6NodeRef = child.getChildRef(); -// nodeService.setProperty(A6NodeRef, ContentModel.PROP_TITLE, CONTENT_TITLE); -// nodeService.setProperty(A6NodeRef, ContentModel.PROP_NAME, "A6"); -// } -// { -// // Node A7 -// ChildAssociationRef child = nodeService.createNode(A4NodeRef, ContentModel.ASSOC_CONTAINS, QName.createQName("A7"), ContentModel.TYPE_CONTENT); -// A7NodeRef = child.getChildRef(); -// nodeService.setProperty(A7NodeRef, ContentModel.PROP_TITLE, CONTENT_TITLE); -// nodeService.setProperty(A7NodeRef, ContentModel.PROP_NAME, "A7"); -// -// ContentWriter writer = contentService.getWriter(A7NodeRef, ContentModel.PROP_CONTENT, true); -// writer.setLocale(CONTENT_LOCALE); -// writer.putContent(CONTENT_STRING); -// } -// { -// // Node A8 -// ChildAssociationRef child = nodeService.createNode(A6NodeRef, ContentModel.ASSOC_CONTAINS, QName.createQName("A8"), ContentModel.TYPE_CONTENT); -// A8NodeRef = child.getChildRef(); -// nodeService.setProperty(A8NodeRef, ContentModel.PROP_TITLE, CONTENT_TITLE); -// nodeService.setProperty(A8NodeRef, ContentModel.PROP_NAME, "A8"); -// -// ContentWriter writer = contentService.getWriter(A8NodeRef, ContentModel.PROP_CONTENT, true); -// writer.setLocale(CONTENT_LOCALE); -// writer.putContent(CONTENT_STRING); -// } -// -// // Create the transfer target if it does not already exist -// if(!transferService.targetExists(targetName)) -// { -// transferMe = createTransferTarget(targetName); -// } else -// { -// transferMe = transferService.getTransferTarget(targetName); -// } -// } -// finally -// { -// endTransaction(); -// } -// -// -// /** -// * Step 1. add A1, A2, A3, A4, A5, A6, A7, A8 -// * transfer(sync) -// */ -// startNewTransaction(); -// try -// { -// /** -// * Transfer Nodes A1 through A8 -// */ -// { -// TransferDefinition definition = new TransferDefinition(); -// Setnodes = new HashSet(); -// nodes.add(A1NodeRef); -// nodes.add(A2NodeRef); -// nodes.add(A3NodeRef); -// nodes.add(A4NodeRef); -// nodes.add(A5NodeRef); -// nodes.add(A6NodeRef); -// nodes.add(A7NodeRef); -// nodes.add(A8NodeRef); -// definition.setNodes(nodes); -// definition.setSync(true); -// transferService.transfer(targetName, definition); -// } -// } -// finally -// { -// endTransaction(); -// } -// -// startNewTransaction(); -// try -// { -// // Now validate that the target node exists and has similar properties to the source -// destNodeRef = testNodeFactory.getMappedNodeRef(A1NodeRef); -// assertFalse("unit test stuffed up - comparing with self", destNodeRef.equals(transferMe.getNodeRef())); -// assertTrue("dest node ref does not exist", nodeService.exists(destNodeRef)); -// assertEquals("title is wrong", (String)nodeService.getProperty(destNodeRef, ContentModel.PROP_TITLE), CONTENT_TITLE); -// assertEquals("type is wrong", nodeService.getType(A1NodeRef), nodeService.getType(destNodeRef)); -// -// // Check injected transferred aspect. -// assertNotNull("transferredAspect", (String)nodeService.getProperty(destNodeRef, TransferModel.PROP_REPOSITORY_ID)); -// } -// finally -// { -// endTransaction(); -// } -// -// /** -// * Step 2 add Alien node B9 child of A1(dest). A1(dest) becomes Alien because it contains an alien child. -// */ -// startNewTransaction(); -// try -// { -// destNodeRef = testNodeFactory.getMappedNodeRef(A1NodeRef); -// ChildAssociationRef child = nodeService.createNode(destNodeRef, ContentModel.ASSOC_CONTAINS, QName.createQName("B9"), ContentModel.TYPE_CONTENT); -// B9NodeRef = child.getChildRef(); -// nodeService.setProperty(B9NodeRef, ContentModel.PROP_TITLE, CONTENT_TITLE); -// nodeService.setProperty(B9NodeRef, ContentModel.PROP_NAME, "B9"); -// -// ContentWriter writer = contentService.getWriter(B9NodeRef, ContentModel.PROP_CONTENT, true); -// writer.setLocale(CONTENT_LOCALE); -// writer.putContent(CONTENT_STRING); -// } -// finally -// { -// endTransaction(); -// } -// -// startNewTransaction(); -// try -// { -// // Now validate that the target node exists and has similar properties to the source -// destNodeRef = testNodeFactory.getMappedNodeRef(A1NodeRef); -// assertFalse("unit test stuffed up - comparing with self", destNodeRef.equals(transferMe.getNodeRef())); -// assertTrue("dest node ref does not exist", nodeService.exists(destNodeRef)); -// // Check injected transferred aspect. -// assertTrue("node A1 is not alien", (Boolean)nodeService.getProperty(destNodeRef, TransferModel.PROP_ALIEN)); -// assertNotNull("repository id is null", (String)nodeService.getProperty(destNodeRef, TransferModel.PROP_REPOSITORY_ID)); -// assertNotNull("from repository id is null", (String)nodeService.getProperty(destNodeRef, TransferModel.PROP_FROM_REPOSITORY_ID)); -// } -// finally -// { -// endTransaction(); -// } -// -// /** -// * Step 3 remove alien node B9. A1 becomes non Alien. -// */ -// startNewTransaction(); -// try -// { -// logger.debug("delete node B9"); -// nodeService.deleteNode(B9NodeRef); -// } -// finally -// { -// endTransaction(); -// } -// -// startNewTransaction(); -// try -// { -// // Now validate that the target node exists and has similar properties to the source -// destNodeRef = testNodeFactory.getMappedNodeRef(A1NodeRef); -// assertFalse("unit test stuffed up - comparing with self", destNodeRef.equals(transferMe.getNodeRef())); -// assertTrue("dest node ref does not exist", nodeService.exists(destNodeRef)); -// -// assertFalse("node A1 is still alien", (Boolean)nodeService.getProperty(destNodeRef, TransferModel.PROP_ALIEN)); -// assertNotNull("repository id is null", (String)nodeService.getProperty(destNodeRef, TransferModel.PROP_REPOSITORY_ID)); -// assertNotNull("from repository id is null", (String)nodeService.getProperty(destNodeRef, TransferModel.PROP_FROM_REPOSITORY_ID)); -// } -// finally -// { -// endTransaction(); -// } -// -// /** -// * 4 add Alien node B10 child of A2. A1 and A2 become Alien -// */ -// startNewTransaction(); -// try -// { -// destNodeRef = testNodeFactory.getMappedNodeRef(A2NodeRef); -// ChildAssociationRef child = nodeService.createNode(destNodeRef, ContentModel.ASSOC_CONTAINS, QName.createQName("B10"), ContentModel.TYPE_CONTENT); -// B10NodeRef = child.getChildRef(); -// nodeService.setProperty(B10NodeRef, ContentModel.PROP_TITLE, CONTENT_TITLE); -// nodeService.setProperty(B10NodeRef, ContentModel.PROP_NAME, "B10"); -// -// ContentWriter writer = contentService.getWriter(B10NodeRef, ContentModel.PROP_CONTENT, true); -// writer.setLocale(CONTENT_LOCALE); -// writer.putContent(CONTENT_STRING); -// } -// finally -// { -// endTransaction(); -// } -// -// startNewTransaction(); -// try -// { -// // Now validate that the target node exists and has similar properties to the source -// NodeRef A1destNodeRef = testNodeFactory.getMappedNodeRef(A1NodeRef); -// NodeRef A2destNodeRef = testNodeFactory.getMappedNodeRef(A2NodeRef); -// -// assertTrue("node A1 is not alien", (Boolean)nodeService.getProperty(A1destNodeRef, TransferModel.PROP_ALIEN)); -// assertTrue("node A2 is not alien", (Boolean)nodeService.getProperty(A2destNodeRef, TransferModel.PROP_ALIEN)); -// } -// -// finally -// { -// endTransaction(); -// } -// -// /** -// * 5 remove Alien node B10. A1 and A2 become non Alien -// */ -// /** -// * Step 9 remove B12 A6, A2, A1 becomes non Alien. -// */ -// startNewTransaction(); -// try -// { -// nodeService.deleteNode(B10NodeRef); -// } -// finally -// { -// endTransaction(); -// } -// -// startNewTransaction(); -// try -// { -// // Now validate that the target node exists and has similar properties to the source -// NodeRef A1destNodeRef = testNodeFactory.getMappedNodeRef(A1NodeRef); -// NodeRef A2destNodeRef = testNodeFactory.getMappedNodeRef(A2NodeRef); -// -// // BUGBUG -// //assertFalse("node A1 is still alien", (Boolean)nodeService.getProperty(A1destNodeRef, TransferModel.PROP_ALIEN)); -// //assertFalse("node A2 is still alien", (Boolean)nodeService.getProperty(A2destNodeRef, TransferModel.PROP_ALIEN)); -// } -// -// finally -// { -// endTransaction(); -// } -// -// -// /** -// * Step 6 -// * add B12 (child of A6) A6, A2, A1 becomes Alien -// */ -// startNewTransaction(); -// try -// { -// destNodeRef = testNodeFactory.getMappedNodeRef(A6NodeRef); -// ChildAssociationRef child = nodeService.createNode(destNodeRef, ContentModel.ASSOC_CONTAINS, QName.createQName("B12"), ContentModel.TYPE_CONTENT); -// B12NodeRef = child.getChildRef(); -// nodeService.setProperty(B12NodeRef, ContentModel.PROP_TITLE, CONTENT_TITLE); -// nodeService.setProperty(B12NodeRef, ContentModel.PROP_NAME, "B12"); -// -// ContentWriter writer = contentService.getWriter(B12NodeRef, ContentModel.PROP_CONTENT, true); -// writer.setLocale(CONTENT_LOCALE); -// writer.putContent(CONTENT_STRING); -// } -// finally -// { -// endTransaction(); -// } -// -// startNewTransaction(); -// try -// { -// // Now validate that the target node exists and has similar properties to the source -// NodeRef A1destNodeRef = testNodeFactory.getMappedNodeRef(A1NodeRef); -// NodeRef A2destNodeRef = testNodeFactory.getMappedNodeRef(A2NodeRef); -// NodeRef A6destNodeRef = testNodeFactory.getMappedNodeRef(A6NodeRef); -// -// assertTrue("node A1 is not alien", (Boolean)nodeService.getProperty(A1destNodeRef, TransferModel.PROP_ALIEN)); -// assertTrue("node A2 is not alien", (Boolean)nodeService.getProperty(A2destNodeRef, TransferModel.PROP_ALIEN)); -// assertTrue("node A6 is not alien", (Boolean)nodeService.getProperty(A6destNodeRef, TransferModel.PROP_ALIEN)); -// } -// -// finally -// { -// endTransaction(); -// } -// -// -// /** -// * Step 7 -// * add B13 A6, A2, A1 remains Alien -// */ -// startNewTransaction(); -// try -// { -// destNodeRef = testNodeFactory.getMappedNodeRef(A6NodeRef); -// ChildAssociationRef child = nodeService.createNode(destNodeRef, ContentModel.ASSOC_CONTAINS, QName.createQName("B13"), ContentModel.TYPE_CONTENT); -// B13NodeRef = child.getChildRef(); -// nodeService.setProperty(B13NodeRef, ContentModel.PROP_TITLE, CONTENT_TITLE); -// nodeService.setProperty(B13NodeRef, ContentModel.PROP_NAME, "B13"); -// -// ContentWriter writer = contentService.getWriter(B13NodeRef, ContentModel.PROP_CONTENT, true); -// writer.setLocale(CONTENT_LOCALE); -// writer.putContent(CONTENT_STRING); -// } -// finally -// { -// endTransaction(); -// } -// -// startNewTransaction(); -// try -// { -// // Now validate that the target node exists and has similar properties to the source -// NodeRef A1destNodeRef = testNodeFactory.getMappedNodeRef(A1NodeRef); -// NodeRef A2destNodeRef = testNodeFactory.getMappedNodeRef(A2NodeRef); -// NodeRef A6destNodeRef = testNodeFactory.getMappedNodeRef(A6NodeRef); -// -// assertTrue("node A1 is not alien", (Boolean)nodeService.getProperty(A1destNodeRef, TransferModel.PROP_ALIEN)); -// assertTrue("node A2 is not alien", (Boolean)nodeService.getProperty(A2destNodeRef, TransferModel.PROP_ALIEN)); -// assertTrue("node A6 is not alien", (Boolean)nodeService.getProperty(A6destNodeRef, TransferModel.PROP_ALIEN)); -// } -// -// finally -// { -// endTransaction(); -// } -// -// /** -// * Step 8 remove B13 A6, A2, A1 remains Alien Due to B12 -// */ -// startNewTransaction(); -// try -// { -// nodeService.deleteNode(B13NodeRef); -// } -// finally -// { -// endTransaction(); -// } -// -// startNewTransaction(); -// try -// { -// // Now validate that the target node exists and has similar properties to the source -// NodeRef A1destNodeRef = testNodeFactory.getMappedNodeRef(A1NodeRef); -// NodeRef A2destNodeRef = testNodeFactory.getMappedNodeRef(A2NodeRef); -// NodeRef A6destNodeRef = testNodeFactory.getMappedNodeRef(A6NodeRef); -// -// assertTrue("node A1 is not alien", (Boolean)nodeService.getProperty(A1destNodeRef, TransferModel.PROP_ALIEN)); -// assertTrue("node A2 is not alien", (Boolean)nodeService.getProperty(A2destNodeRef, TransferModel.PROP_ALIEN)); -// assertTrue("node A6 is not alien", (Boolean)nodeService.getProperty(A6destNodeRef, TransferModel.PROP_ALIEN)); -// } -// -// finally -// { -// endTransaction(); -// } -// -// -// /** -// * Step 9 remove B12 A6, A2, A1 becomes non Alien. -// */ -// startNewTransaction(); -// try -// { -// nodeService.deleteNode(B12NodeRef); -// } -// finally -// { -// endTransaction(); -// } -// -// startNewTransaction(); -// try -// { -// // Now validate that the target node exists and has similar properties to the source -// NodeRef A1destNodeRef = testNodeFactory.getMappedNodeRef(A1NodeRef); -// NodeRef A2destNodeRef = testNodeFactory.getMappedNodeRef(A2NodeRef); -// NodeRef A6destNodeRef = testNodeFactory.getMappedNodeRef(A6NodeRef); -// -// // BUGBUG -// //assertFalse("node A1 is still alien", (Boolean)nodeService.getProperty(A1destNodeRef, TransferModel.PROP_ALIEN)); -// //assertFalse("node A2 is still alien", (Boolean)nodeService.getProperty(A2destNodeRef, TransferModel.PROP_ALIEN)); -// //assertFalse("node A6 is still alien", (Boolean)nodeService.getProperty(A6destNodeRef, TransferModel.PROP_ALIEN)); -// } -// -// finally -// { -// endTransaction(); -// } -// -// /** -// * Step 10 add B9 and B10 A1 and A2 become Alien -// */ -// startNewTransaction(); -// try -// { -// destNodeRef = testNodeFactory.getMappedNodeRef(A1NodeRef); -// ChildAssociationRef child = nodeService.createNode(destNodeRef, ContentModel.ASSOC_CONTAINS, QName.createQName("B9"), ContentModel.TYPE_CONTENT); -// B9NodeRef = child.getChildRef(); -// nodeService.setProperty(B9NodeRef, ContentModel.PROP_TITLE, CONTENT_TITLE); -// nodeService.setProperty(B9NodeRef, ContentModel.PROP_NAME, "B9"); -// -// ContentWriter writer = contentService.getWriter(B9NodeRef, ContentModel.PROP_CONTENT, true); -// writer.setLocale(CONTENT_LOCALE); -// writer.putContent(CONTENT_STRING); -// -// destNodeRef = testNodeFactory.getMappedNodeRef(A2NodeRef); -// child = nodeService.createNode(destNodeRef, ContentModel.ASSOC_CONTAINS, QName.createQName("B10"), ContentModel.TYPE_CONTENT); -// B10NodeRef = child.getChildRef(); -// nodeService.setProperty(B10NodeRef, ContentModel.PROP_TITLE, CONTENT_TITLE); -// nodeService.setProperty(B10NodeRef, ContentModel.PROP_NAME, "B10"); -// -// writer = contentService.getWriter(B10NodeRef, ContentModel.PROP_CONTENT, true); -// writer.setLocale(CONTENT_LOCALE); -// writer.putContent(CONTENT_STRING); -// } -// finally -// { -// endTransaction(); -// } -// -// startNewTransaction(); -// try -// { -// // Now validate that the target node exists and has similar properties to the source -// NodeRef A1destNodeRef = testNodeFactory.getMappedNodeRef(A1NodeRef); -// NodeRef A2destNodeRef = testNodeFactory.getMappedNodeRef(A2NodeRef); -// -// assertTrue("node A1 is not alien", (Boolean)nodeService.getProperty(A1destNodeRef, TransferModel.PROP_ALIEN)); -// assertTrue("node A2 is not alien", (Boolean)nodeService.getProperty(A2destNodeRef, TransferModel.PROP_ALIEN)); -// } -// finally -// { -// endTransaction(); -// } -// -// -// /** -// * Step 11 remove B10 A2 becomes non alien A1 remains alien. -// */ -// startNewTransaction(); -// try -// { -// nodeService.deleteNode(B10NodeRef); -// } -// finally -// { -// endTransaction(); -// } -// -// startNewTransaction(); -// try -// { -// // Now validate that the target node exists and has similar properties to the source -// NodeRef A1destNodeRef = testNodeFactory.getMappedNodeRef(A1NodeRef); -// NodeRef A2destNodeRef = testNodeFactory.getMappedNodeRef(A2NodeRef); -// NodeRef A6destNodeRef = testNodeFactory.getMappedNodeRef(A6NodeRef); -// -// // BUGBUG -// assertTrue("node A1 is still alien", (Boolean)nodeService.getProperty(A1destNodeRef, TransferModel.PROP_ALIEN)); -// //assertFalse("node A2 is still alien", (Boolean)nodeService.getProperty(A2destNodeRef, TransferModel.PROP_ALIEN)); -// -// } -// -// finally -// { -// endTransaction(); -// } -// -// -// /** -// * 12 delete A2 containing alien node B11 -// * transfer sync -// * (A5, A6, A7 and A8 should be deleted A2 and A4 remain since they contain alien content.) -// */ -// -// } -// + /** + * Test the transfer method behaviour with respect to sync with (local) alien nodes. + * + * So we have Repository A transferring content and Repository B is the local repo that we + * add and delete alien nodes. + * + * In general an alien node will prevent deletion of the parent folders + * + *

+     * Tree of nodes
+     * 
+     *      A1
+     *      |      |                      | 
+     *      A2     A3 (Content Node)      B9 Alien Content Node
+     *      |
+     *   A4 A5 B10 (Alien Content Node)   A6
+     *   |                                |
+     *   A7 B11 (Alien Content Node)      A8 B12 B13 Alien Contact Nodes
+     * 
+ * Test steps - + *
    + *
  1. add A1, A2, A3, A4, A5, A6, A7, A8 + * transfer(sync)
  2. + *
  3. add Alien node B9. A1 becomes Alien.
  4. + *
  5. remove alien node B9. A1 becomes non Alien.
  6. + *
  7. add Alien node B10. A1 and A2 become Alien
  8. + *
  9. remove Alien node B10. A1 and A2 become non Alien
  10. + *
  11. add B12 A6, A2, A1 becomes Alien
  12. + *
  13. add B13 A6, A2, A1 remains Alien
  14. + *
  15. remove B13 A6, A2, A1 remains Alien
  16. + *
  17. remove B12 A6, A2, A1 becomes non Alien.
  18. + *
  19. add B9 and B10 A1 and A2 become Alien
  20. + *
  21. remove B10 A2 becomes non alien A1 remains alien.
  22. + *
  23. Add B11, delete A2 + * transfer sync
  24. + * (A5, A6, A7 and A8 should be deleted A2 and A4 remain since they contain alien content.) + *
+ * + * TODO test move and alien + * + * TODO test restore and alien + * + */ + public void testTransferInvadedByLocalAlienNodes() throws Exception + { + setDefaultRollback(false); + + String CONTENT_TITLE = "ContentTitle"; + String CONTENT_TITLE_UPDATED = "ContentTitleUpdated"; + Locale CONTENT_LOCALE = Locale.JAPAN; + String CONTENT_STRING = "Hello"; + + /** + * For unit test + * - replace the HTTP transport with the in-process transport + * - replace the node factory with one that will map node refs, paths etc. + * + * Mock the transfer service to be from Repo A + */ + TransferTransmitter transmitter = new UnitTestInProcessTransmitterImpl(receiver, contentService, transactionService); + transferServiceImpl.setTransmitter(transmitter); + UnitTestTransferManifestNodeFactory testNodeFactory = new UnitTestTransferManifestNodeFactory(this.transferManifestNodeFactory); + transferServiceImpl.setTransferManifestNodeFactory(testNodeFactory); + List> pathMap = testNodeFactory.getPathMap(); + // Map company_home/guest_home to company_home so tranferred nodes and moved "up" one level. + pathMap.add(new Pair(PathHelper.stringToPath(GUEST_HOME_XPATH_QUERY), PathHelper.stringToPath(COMPANY_HOME_XPATH_QUERY))); + + final String localRepositoryId = descriptorService.getCurrentRepositoryDescriptor().getId(); + + DescriptorService mockedDescriptorService = getMockDescriptorService(REPO_ID_A); + transferServiceImpl.setDescriptorService(mockedDescriptorService); + + /** + * Now go ahead and create our first transfer target + */ + String targetName = "testSyncWithAlienNodes"; + TransferTarget transferMe; + + NodeRef A1NodeRef; + NodeRef A2NodeRef; + NodeRef A3NodeRef; + NodeRef A4NodeRef; + NodeRef A5NodeRef; + NodeRef A6NodeRef; + NodeRef A7NodeRef; + NodeRef A8NodeRef; + NodeRef B9NodeRef; + NodeRef B10NodeRef; + NodeRef B11NodeRef; + NodeRef B12NodeRef; + NodeRef B13NodeRef; + + NodeRef destNodeRef; + + startNewTransaction(); + try + { + /** + * Get guest home + */ + String guestHomeQuery = "/app:company_home/app:guest_home"; + ResultSet guestHomeResult = searchService.query(StoreRef.STORE_REF_WORKSPACE_SPACESSTORE, SearchService.LANGUAGE_XPATH, guestHomeQuery); + assertEquals("", 1, guestHomeResult.length()); + NodeRef guestHome = guestHomeResult.getNodeRef(0); + + /** + * Create a test nodes A1 through A8 that we will read and write + */ + { + // Node A1 + String name = GUID.generate(); + ChildAssociationRef child = nodeService.createNode(guestHome, ContentModel.ASSOC_CONTAINS, QName.createQName(name), ContentModel.TYPE_FOLDER); + A1NodeRef = child.getChildRef(); + nodeService.setProperty(A1NodeRef, ContentModel.PROP_TITLE, CONTENT_TITLE); + nodeService.setProperty(A1NodeRef, ContentModel.PROP_NAME, name); + } + + { + // Node A2 + ChildAssociationRef child = nodeService.createNode(A1NodeRef, ContentModel.ASSOC_CONTAINS, QName.createQName("A2"), ContentModel.TYPE_FOLDER); + A2NodeRef = child.getChildRef(); + nodeService.setProperty(A2NodeRef, ContentModel.PROP_TITLE, CONTENT_TITLE); + nodeService.setProperty(A2NodeRef, ContentModel.PROP_NAME, "A2"); + } + + { + // Node A3 + ChildAssociationRef child = nodeService.createNode(A1NodeRef, ContentModel.ASSOC_CONTAINS, QName.createQName("A3"), ContentModel.TYPE_CONTENT); + A3NodeRef = child.getChildRef(); + nodeService.setProperty(A3NodeRef, ContentModel.PROP_TITLE, CONTENT_TITLE); + nodeService.setProperty(A3NodeRef, ContentModel.PROP_NAME, "A3"); + + ContentWriter writer = contentService.getWriter(A3NodeRef, ContentModel.PROP_CONTENT, true); + writer.setLocale(CONTENT_LOCALE); + writer.putContent(CONTENT_STRING); + } + { + // Node A4 + ChildAssociationRef child = nodeService.createNode(A2NodeRef, ContentModel.ASSOC_CONTAINS, QName.createQName("A4"), ContentModel.TYPE_FOLDER); + A4NodeRef = child.getChildRef(); + nodeService.setProperty(A4NodeRef, ContentModel.PROP_TITLE, CONTENT_TITLE); + nodeService.setProperty(A4NodeRef, ContentModel.PROP_NAME, "A4"); + } + { + // Node A5 + ChildAssociationRef child = nodeService.createNode(A2NodeRef, ContentModel.ASSOC_CONTAINS, QName.createQName("A5"), ContentModel.TYPE_CONTENT); + A5NodeRef = child.getChildRef(); + nodeService.setProperty(A5NodeRef, ContentModel.PROP_TITLE, CONTENT_TITLE); + nodeService.setProperty(A5NodeRef, ContentModel.PROP_NAME, "A5"); + + ContentWriter writer = contentService.getWriter(A5NodeRef, ContentModel.PROP_CONTENT, true); + writer.setLocale(CONTENT_LOCALE); + writer.putContent(CONTENT_STRING); + } + + { + // Node A6 + ChildAssociationRef child = nodeService.createNode(A2NodeRef, ContentModel.ASSOC_CONTAINS, QName.createQName("A6"), ContentModel.TYPE_FOLDER); + A6NodeRef = child.getChildRef(); + nodeService.setProperty(A6NodeRef, ContentModel.PROP_TITLE, CONTENT_TITLE); + nodeService.setProperty(A6NodeRef, ContentModel.PROP_NAME, "A6"); + } + { + // Node A7 + ChildAssociationRef child = nodeService.createNode(A4NodeRef, ContentModel.ASSOC_CONTAINS, QName.createQName("A7"), ContentModel.TYPE_CONTENT); + A7NodeRef = child.getChildRef(); + nodeService.setProperty(A7NodeRef, ContentModel.PROP_TITLE, CONTENT_TITLE); + nodeService.setProperty(A7NodeRef, ContentModel.PROP_NAME, "A7"); + + ContentWriter writer = contentService.getWriter(A7NodeRef, ContentModel.PROP_CONTENT, true); + writer.setLocale(CONTENT_LOCALE); + writer.putContent(CONTENT_STRING); + } + { + // Node A8 + ChildAssociationRef child = nodeService.createNode(A6NodeRef, ContentModel.ASSOC_CONTAINS, QName.createQName("A8"), ContentModel.TYPE_CONTENT); + A8NodeRef = child.getChildRef(); + nodeService.setProperty(A8NodeRef, ContentModel.PROP_TITLE, CONTENT_TITLE); + nodeService.setProperty(A8NodeRef, ContentModel.PROP_NAME, "A8"); + + ContentWriter writer = contentService.getWriter(A8NodeRef, ContentModel.PROP_CONTENT, true); + writer.setLocale(CONTENT_LOCALE); + writer.putContent(CONTENT_STRING); + } + + // Create the transfer target if it does not already exist + if(!transferService.targetExists(targetName)) + { + transferMe = createTransferTarget(targetName); + } else + { + transferMe = transferService.getTransferTarget(targetName); + } + } + finally + { + endTransaction(); + } + + + /** + * Step 1. add A1, A2, A3, A4, A5, A6, A7, A8 + * transfer(sync) + */ + startNewTransaction(); + try + { + /** + * Transfer Nodes A1 through A8 + */ + { + TransferDefinition definition = new TransferDefinition(); + Setnodes = new HashSet(); + nodes.add(A1NodeRef); + nodes.add(A2NodeRef); + nodes.add(A3NodeRef); + nodes.add(A4NodeRef); + nodes.add(A5NodeRef); + nodes.add(A6NodeRef); + nodes.add(A7NodeRef); + nodes.add(A8NodeRef); + definition.setNodes(nodes); + definition.setSync(true); + transferService.transfer(targetName, definition); + } + } + finally + { + endTransaction(); + } + + startNewTransaction(); + try + { + // Now validate that the target node exists and has similar properties to the source + NodeRef A1destNodeRef = testNodeFactory.getMappedNodeRef(A1NodeRef); + assertFalse("unit test stuffed up - comparing with self", A1destNodeRef.equals(transferMe.getNodeRef())); + assertTrue("dest node ref does not exist", nodeService.exists(A1destNodeRef)); + assertEquals("title is wrong", (String)nodeService.getProperty(A1destNodeRef, ContentModel.PROP_TITLE), CONTENT_TITLE); + assertEquals("type is wrong", nodeService.getType(A1NodeRef), nodeService.getType(A1destNodeRef)); + assertFalse("A1 is alien", nodeService.hasAspect(A1destNodeRef, TransferModel.ASPECT_ALIEN)); + + // Check injected transferred aspect. + assertNotNull("transferredAspect", (String)nodeService.getProperty(A1destNodeRef, TransferModel.PROP_REPOSITORY_ID)); + } + finally + { + endTransaction(); + } + + /** + * Step 2 add Alien node B9 child of A1(dest). A1(dest) becomes Alien because it contains an alien child. + */ + startNewTransaction(); + try + { + destNodeRef = testNodeFactory.getMappedNodeRef(A1NodeRef); + ChildAssociationRef child = nodeService.createNode(destNodeRef, ContentModel.ASSOC_CONTAINS, QName.createQName("B9"), ContentModel.TYPE_CONTENT); + B9NodeRef = child.getChildRef(); + nodeService.setProperty(B9NodeRef, ContentModel.PROP_TITLE, CONTENT_TITLE); + nodeService.setProperty(B9NodeRef, ContentModel.PROP_NAME, "B9"); + + ContentWriter writer = contentService.getWriter(B9NodeRef, ContentModel.PROP_CONTENT, true); + writer.setLocale(CONTENT_LOCALE); + writer.putContent(CONTENT_STRING); + } + finally + { + endTransaction(); + } + + startNewTransaction(); + try + { + // Now validate that the target node exists and has similar properties to the source + NodeRef A1destNodeRef = testNodeFactory.getMappedNodeRef(A1NodeRef); + + assertTrue("dest node ref does not exist", nodeService.exists(A1destNodeRef)); + // Check injected transferred aspect. + assertTrue("node A1 is not alien prop", (Boolean)nodeService.getProperty(A1destNodeRef, TransferModel.PROP_ALIEN)); + assertTrue("node A1 is not alien aspect", (Boolean)nodeService.hasAspect(A1destNodeRef, TransferModel.ASPECT_ALIEN)); + assertNotNull("repository id is null", (String)nodeService.getProperty(A1destNodeRef, TransferModel.PROP_REPOSITORY_ID)); + assertNotNull("from repository id is null", (String)nodeService.getProperty(A1destNodeRef, TransferModel.PROP_FROM_REPOSITORY_ID)); + assertTrue("node B9 is not alien", (Boolean)nodeService.hasAspect(B9NodeRef, TransferModel.ASPECT_ALIEN)); + + // Temp code + List invaders = (List) nodeService.getProperty(A1destNodeRef, TransferModel.PROP_INVADED_BY); + assertTrue("invaders contains local repository Id", invaders.contains(localRepositoryId)); + assertFalse("invaders contains REPO_ID_A", invaders.contains(REPO_ID_A)); + logger.debug("MER WOZ ERE" + invaders); + + } + finally + { + endTransaction(); + } + + /** + * Step 3 remove alien node B9. A1 becomes non Alien. + */ + startNewTransaction(); + try + { + logger.debug("delete node B9"); + nodeService.deleteNode(B9NodeRef); + } + finally + { + endTransaction(); + } + + startNewTransaction(); + try + { + NodeRef A1destNodeRef = testNodeFactory.getMappedNodeRef(A1NodeRef); + NodeRef A3destNodeRef = testNodeFactory.getMappedNodeRef(A3NodeRef); + + // Temp code + List invaders = (List) nodeService.getProperty(A1destNodeRef, TransferModel.PROP_INVADED_BY); + logger.debug("MER WOZ ERE AFTER B9 deleted" + invaders); + + assertTrue("dest node ref does not exist", nodeService.exists(A1destNodeRef)); + assertFalse("node A1 is still alien", (Boolean)nodeService.hasAspect(A1destNodeRef, TransferModel.ASPECT_ALIEN)); + assertFalse("node A3 is alien", (Boolean)nodeService.hasAspect(A3destNodeRef, TransferModel.ASPECT_ALIEN)); + assertNotNull("repository id is null", (String)nodeService.getProperty(A1destNodeRef, TransferModel.PROP_REPOSITORY_ID)); + assertNotNull("from repository id is null", (String)nodeService.getProperty(A1destNodeRef, TransferModel.PROP_FROM_REPOSITORY_ID)); + } + finally + { + endTransaction(); + } + + /** + * 4 add Alien node B10 child of A2. A1 and A2 become Alien + */ + startNewTransaction(); + try + { + destNodeRef = testNodeFactory.getMappedNodeRef(A2NodeRef); + ChildAssociationRef child = nodeService.createNode(destNodeRef, ContentModel.ASSOC_CONTAINS, QName.createQName("B10"), ContentModel.TYPE_CONTENT); + B10NodeRef = child.getChildRef(); + nodeService.setProperty(B10NodeRef, ContentModel.PROP_TITLE, CONTENT_TITLE); + nodeService.setProperty(B10NodeRef, ContentModel.PROP_NAME, "B10"); + + ContentWriter writer = contentService.getWriter(B10NodeRef, ContentModel.PROP_CONTENT, true); + writer.setLocale(CONTENT_LOCALE); + writer.putContent(CONTENT_STRING); + } + finally + { + endTransaction(); + } + + startNewTransaction(); + try + { + // Now validate that the target node exists and has similar properties to the source + NodeRef A1destNodeRef = testNodeFactory.getMappedNodeRef(A1NodeRef); + NodeRef A2destNodeRef = testNodeFactory.getMappedNodeRef(A2NodeRef); + + assertTrue("node A1 is not alien", (Boolean)nodeService.getProperty(A1destNodeRef, TransferModel.PROP_ALIEN)); + assertTrue("node A2 is not alien", (Boolean)nodeService.getProperty(A2destNodeRef, TransferModel.PROP_ALIEN)); + + assertTrue("node A1 is not alien aspect", (Boolean)nodeService.hasAspect(A1destNodeRef, TransferModel.ASPECT_ALIEN)); + assertTrue("node A2 is not alien aspect", (Boolean)nodeService.hasAspect(A2destNodeRef, TransferModel.ASPECT_ALIEN)); + } + + finally + { + endTransaction(); + } + + /** + * 5 remove Alien node B10. A1 and A2 become non Alien + */ + startNewTransaction(); + try + { + logger.debug("delete node B10"); + nodeService.deleteNode(B10NodeRef); + } + finally + { + endTransaction(); + } + + startNewTransaction(); + try + { + // Now validate that the target node exists and has similar properties to the source + NodeRef A1destNodeRef = testNodeFactory.getMappedNodeRef(A1NodeRef); + NodeRef A2destNodeRef = testNodeFactory.getMappedNodeRef(A2NodeRef); + + assertFalse("node A1 is still alien aspect", (Boolean)nodeService.hasAspect(A1destNodeRef, TransferModel.ASPECT_ALIEN)); + assertFalse("node A2 is still alien aspect", (Boolean)nodeService.hasAspect(A2destNodeRef, TransferModel.ASPECT_ALIEN)); + } + + finally + { + endTransaction(); + } + + /** + * Step 6 + * add B12 (child of A6) A6, A2, A1 becomes Alien + */ + startNewTransaction(); + try + { + destNodeRef = testNodeFactory.getMappedNodeRef(A6NodeRef); + ChildAssociationRef child = nodeService.createNode(destNodeRef, ContentModel.ASSOC_CONTAINS, QName.createQName("B12"), ContentModel.TYPE_CONTENT); + B12NodeRef = child.getChildRef(); + nodeService.setProperty(B12NodeRef, ContentModel.PROP_TITLE, CONTENT_TITLE); + nodeService.setProperty(B12NodeRef, ContentModel.PROP_NAME, "B12"); + + ContentWriter writer = contentService.getWriter(B12NodeRef, ContentModel.PROP_CONTENT, true); + writer.setLocale(CONTENT_LOCALE); + writer.putContent(CONTENT_STRING); + } + finally + { + endTransaction(); + } + + startNewTransaction(); + try + { + // Now validate that the target node exists and has similar properties to the source + NodeRef A1destNodeRef = testNodeFactory.getMappedNodeRef(A1NodeRef); + NodeRef A2destNodeRef = testNodeFactory.getMappedNodeRef(A2NodeRef); + NodeRef A6destNodeRef = testNodeFactory.getMappedNodeRef(A6NodeRef); + + assertTrue("node A1 is not alien", (Boolean)nodeService.hasAspect(A1destNodeRef, TransferModel.ASPECT_ALIEN)); + assertTrue("node A2 is not alien", (Boolean)nodeService.hasAspect(A2destNodeRef, TransferModel.ASPECT_ALIEN)); + assertTrue("node A6 is not alien", (Boolean)nodeService.hasAspect(A6destNodeRef, TransferModel.ASPECT_ALIEN)); + } + + finally + { + endTransaction(); + } + + /** + * Step 7 + * add B13 A6, A2, A1 remains Alien + */ + startNewTransaction(); + try + { + destNodeRef = testNodeFactory.getMappedNodeRef(A6NodeRef); + ChildAssociationRef child = nodeService.createNode(destNodeRef, ContentModel.ASSOC_CONTAINS, QName.createQName("B13"), ContentModel.TYPE_CONTENT); + B13NodeRef = child.getChildRef(); + nodeService.setProperty(B13NodeRef, ContentModel.PROP_TITLE, CONTENT_TITLE); + nodeService.setProperty(B13NodeRef, ContentModel.PROP_NAME, "B13"); + + ContentWriter writer = contentService.getWriter(B13NodeRef, ContentModel.PROP_CONTENT, true); + writer.setLocale(CONTENT_LOCALE); + writer.putContent(CONTENT_STRING); + } + finally + { + endTransaction(); + } + + startNewTransaction(); + try + { + // Now validate that the target node exists and has similar properties to the source + NodeRef A1destNodeRef = testNodeFactory.getMappedNodeRef(A1NodeRef); + NodeRef A2destNodeRef = testNodeFactory.getMappedNodeRef(A2NodeRef); + NodeRef A6destNodeRef = testNodeFactory.getMappedNodeRef(A6NodeRef); + + assertTrue("node A1 is not alien", (Boolean)nodeService.hasAspect(A1destNodeRef, TransferModel.ASPECT_ALIEN)); + assertTrue("node A2 is not alien", (Boolean)nodeService.hasAspect(A2destNodeRef, TransferModel.ASPECT_ALIEN)); + assertTrue("node A6 is not alien", (Boolean)nodeService.hasAspect(A6destNodeRef, TransferModel.ASPECT_ALIEN)); + } + + finally + { + endTransaction(); + } + + /** + * Step 8 remove B13 A6, A2, A1 remains Alien Due to B12 + */ + startNewTransaction(); + try + { + nodeService.deleteNode(B13NodeRef); + } + finally + { + endTransaction(); + } + + startNewTransaction(); + try + { + // Now validate that the target node exists and has similar properties to the source + NodeRef A1destNodeRef = testNodeFactory.getMappedNodeRef(A1NodeRef); + NodeRef A2destNodeRef = testNodeFactory.getMappedNodeRef(A2NodeRef); + NodeRef A6destNodeRef = testNodeFactory.getMappedNodeRef(A6NodeRef); + + assertTrue("node A1 is not alien", (Boolean)nodeService.hasAspect(A1destNodeRef, TransferModel.ASPECT_ALIEN)); + assertTrue("node A2 is not alien", (Boolean)nodeService.hasAspect(A2destNodeRef, TransferModel.ASPECT_ALIEN)); + assertTrue("node A6 is not alien", (Boolean)nodeService.hasAspect(A6destNodeRef, TransferModel.ASPECT_ALIEN)); + } + + finally + { + endTransaction(); + } + + /** + * Step 9 remove B12 A6, A2, A1 becomes non Alien. + */ + startNewTransaction(); + try + { + nodeService.deleteNode(B12NodeRef); + } + finally + { + endTransaction(); + } + + startNewTransaction(); + try + { + // Now validate that the target node exists and has similar properties to the source + NodeRef A1destNodeRef = testNodeFactory.getMappedNodeRef(A1NodeRef); + NodeRef A2destNodeRef = testNodeFactory.getMappedNodeRef(A2NodeRef); + NodeRef A6destNodeRef = testNodeFactory.getMappedNodeRef(A6NodeRef); + + assertFalse("node A1 is still alien", (Boolean)nodeService.hasAspect(A1destNodeRef, TransferModel.ASPECT_ALIEN)); + assertFalse("node A2 is still alien", (Boolean)nodeService.hasAspect(A2destNodeRef, TransferModel.ASPECT_ALIEN)); + assertFalse("node A6 is still alien", (Boolean)nodeService.hasAspect(A6destNodeRef, TransferModel.ASPECT_ALIEN)); + } + + finally + { + endTransaction(); + } + + /** + * Step 10 add B9 and B10 A1 and A2 become Alien + */ + startNewTransaction(); + try + { + destNodeRef = testNodeFactory.getMappedNodeRef(A1NodeRef); + ChildAssociationRef child = nodeService.createNode(destNodeRef, ContentModel.ASSOC_CONTAINS, QName.createQName("B9"), ContentModel.TYPE_CONTENT); + B9NodeRef = child.getChildRef(); + nodeService.setProperty(B9NodeRef, ContentModel.PROP_TITLE, CONTENT_TITLE); + nodeService.setProperty(B9NodeRef, ContentModel.PROP_NAME, "B9"); + + ContentWriter writer = contentService.getWriter(B9NodeRef, ContentModel.PROP_CONTENT, true); + writer.setLocale(CONTENT_LOCALE); + writer.putContent(CONTENT_STRING); + + destNodeRef = testNodeFactory.getMappedNodeRef(A2NodeRef); + child = nodeService.createNode(destNodeRef, ContentModel.ASSOC_CONTAINS, QName.createQName("B10"), ContentModel.TYPE_CONTENT); + B10NodeRef = child.getChildRef(); + nodeService.setProperty(B10NodeRef, ContentModel.PROP_TITLE, CONTENT_TITLE); + nodeService.setProperty(B10NodeRef, ContentModel.PROP_NAME, "B10"); + + writer = contentService.getWriter(B10NodeRef, ContentModel.PROP_CONTENT, true); + writer.setLocale(CONTENT_LOCALE); + writer.putContent(CONTENT_STRING); + } + finally + { + endTransaction(); + } + + startNewTransaction(); + try + { + // Now validate that the target node exists and has similar properties to the source + NodeRef A1destNodeRef = testNodeFactory.getMappedNodeRef(A1NodeRef); + NodeRef A2destNodeRef = testNodeFactory.getMappedNodeRef(A2NodeRef); + + assertTrue("node A1 is not alien", (Boolean)nodeService.hasAspect(A1destNodeRef, TransferModel.ASPECT_ALIEN)); + assertTrue("node A2 is not alien", (Boolean)nodeService.hasAspect(A2destNodeRef, TransferModel.ASPECT_ALIEN)); + } + finally + { + endTransaction(); + } + + + /** + * Step 11 remove B10 A2 becomes non alien A1 remains alien. + */ + startNewTransaction(); + try + { + nodeService.deleteNode(B10NodeRef); + } + finally + { + endTransaction(); + } + + startNewTransaction(); + try + { + // Now validate that the target node exists and has similar properties to the source + NodeRef A1destNodeRef = testNodeFactory.getMappedNodeRef(A1NodeRef); + NodeRef A2destNodeRef = testNodeFactory.getMappedNodeRef(A2NodeRef); + + // BUGBUG + assertTrue("node A1 is still alien", (Boolean)nodeService.hasAspect(A1destNodeRef, TransferModel.ASPECT_ALIEN)); + assertFalse("node A2 is still alien", (Boolean)nodeService.hasAspect(A2destNodeRef, TransferModel.ASPECT_ALIEN)); + } + + finally + { + endTransaction(); + } + + + /** + * 12 Add Alien node B11. + * delete A2 (will cascade delete A4, A5, A6, A7, A8 + * transfer sync + * (A5, A6, A7, A8 and should be deleted A2 and A4 remain since they contain alien content.) + */ + logger.debug("Step 12 Add Node B11, Delete A2 and sync"); + startNewTransaction(); + try + { + destNodeRef = testNodeFactory.getMappedNodeRef(A4NodeRef); + ChildAssociationRef child = nodeService.createNode(destNodeRef, ContentModel.ASSOC_CONTAINS, QName.createQName("B11"), ContentModel.TYPE_CONTENT); + B11NodeRef = child.getChildRef(); + nodeService.setProperty(B11NodeRef, ContentModel.PROP_TITLE, CONTENT_TITLE); + nodeService.setProperty(B11NodeRef, ContentModel.PROP_NAME, "B11"); + + ContentWriter writer = contentService.getWriter(B11NodeRef, ContentModel.PROP_CONTENT, true); + writer.setLocale(CONTENT_LOCALE); + writer.putContent(CONTENT_STRING); + + nodeService.deleteNode(A2NodeRef); + } + finally + { + endTransaction(); + } + + startNewTransaction(); + try + { + // Now validate A1, A2 and A4 are alien + NodeRef A1destNodeRef = testNodeFactory.getMappedNodeRef(A1NodeRef); + NodeRef A2destNodeRef = testNodeFactory.getMappedNodeRef(A2NodeRef); + NodeRef A4destNodeRef = testNodeFactory.getMappedNodeRef(A4NodeRef); + + + assertTrue("node A1 is not alien", (Boolean)nodeService.hasAspect(A1destNodeRef, TransferModel.ASPECT_ALIEN)); + assertTrue("node A2 is not alien", (Boolean)nodeService.hasAspect(A2destNodeRef, TransferModel.ASPECT_ALIEN)); + assertTrue("node A4 is not alien", (Boolean)nodeService.hasAspect(A4destNodeRef, TransferModel.ASPECT_ALIEN)); + + assertFalse("test error: node A2 not deleted", nodeService.exists(A2NodeRef)); + assertFalse("test error: node A4 not deleted", nodeService.exists(A4NodeRef)); + assertFalse("test error: node A5 not deleted", nodeService.exists(A5NodeRef)); + assertFalse("test error: node A6 not deleted", nodeService.exists(A6NodeRef)); + assertFalse("test error: node A7 not deleted", nodeService.exists(A7NodeRef)); + assertFalse("test error: node A8 not deleted", nodeService.exists(A8NodeRef)); + + /** + * Transfer Nodes A1 through A8 + */ + { + TransferDefinition definition = new TransferDefinition(); + Setnodes = new HashSet(); + nodes.add(A1NodeRef); + nodes.add(A3NodeRef); + definition.setNodes(nodes); + definition.setSync(true); + transferService.transfer(targetName, definition); + } + } + finally + { + endTransaction(); + } + + startNewTransaction(); + try + { + // Now validate that the target node exists and has similar properties to the source + NodeRef A1destNodeRef = testNodeFactory.getMappedNodeRef(A1NodeRef); + NodeRef A2destNodeRef = testNodeFactory.getMappedNodeRef(A2NodeRef); + NodeRef A3destNodeRef = testNodeFactory.getMappedNodeRef(A3NodeRef); + NodeRef A4destNodeRef = testNodeFactory.getMappedNodeRef(A4NodeRef); + NodeRef A5destNodeRef = testNodeFactory.getMappedNodeRef(A5NodeRef); + NodeRef A6destNodeRef = testNodeFactory.getMappedNodeRef(A6NodeRef); + NodeRef A7destNodeRef = testNodeFactory.getMappedNodeRef(A7NodeRef); + NodeRef A8destNodeRef = testNodeFactory.getMappedNodeRef(A8NodeRef); + + assertTrue("node A1 not alien", (Boolean)nodeService.hasAspect(A1destNodeRef, TransferModel.ASPECT_ALIEN)); + assertTrue("node A2 not alien", (Boolean)nodeService.hasAspect(A2destNodeRef, TransferModel.ASPECT_ALIEN)); + assertTrue("node A4 not alien", (Boolean)nodeService.hasAspect(A4destNodeRef, TransferModel.ASPECT_ALIEN)); + assertTrue("node B11 does not exist", nodeService.exists(B11NodeRef)); + + assertTrue("node A3 deleted", nodeService.exists(A3destNodeRef)); + + assertFalse("node A5 not deleted", nodeService.exists(A5destNodeRef)); + assertFalse("node A6 not deleted", nodeService.exists(A6destNodeRef)); + assertFalse("node A7 not deleted", nodeService.exists(A7destNodeRef)); + assertFalse("node A8 not deleted", nodeService.exists(A8destNodeRef)); + } + + finally + { + endTransaction(); + } + } + /** * Test the transfer method with regard to permissions on a node. - * + *

* Step 1: * Create a node with a single permission * Inherit:false * Read, Admin, Allow * Transfer - * + *

* Step 2: * Update it to have several permissions * Inherit:false * Read, Everyone, DENY * Read, Admin, Allow - * + *

* Step 3: * Remove a permission * Inherit:false * Read, Admin, Allow - * + *

* Step 4: * Revert to inherit all permissions * Inherit:true - * + *

* This is a unit test so it does some shenanigans to send to the same instance of alfresco. */ public void testTransferWithPermissions() throws Exception @@ -3349,7 +3498,10 @@ public class TransferServiceImplTest extends BaseAlfrescoSpringTest List> pathMap = testNodeFactory.getPathMap(); // Map company_home/guest_home to company_home so tranferred nodes and moved "up" one level. pathMap.add(new Pair(PathHelper.stringToPath(GUEST_HOME_XPATH_QUERY), PathHelper.stringToPath(COMPANY_HOME_XPATH_QUERY))); - + + DescriptorService mockedDescriptorService = getMockDescriptorService(REPO_ID_A); + transferServiceImpl.setDescriptorService(mockedDescriptorService); + /** * Now go ahead and create our transfer target */ @@ -3712,7 +3864,10 @@ public class TransferServiceImplTest extends BaseAlfrescoSpringTest List> pathMap = testNodeFactory.getPathMap(); // Map company_home/guest_home to company_home so tranferred nodes and moved "up" one level. pathMap.add(new Pair(PathHelper.stringToPath(GUEST_HOME_XPATH_QUERY), PathHelper.stringToPath(COMPANY_HOME_XPATH_QUERY))); - + + DescriptorService mockedDescriptorService = getMockDescriptorService(REPO_ID_A); + transferServiceImpl.setDescriptorService(mockedDescriptorService); + TransferTarget transferMe; startNewTransaction(); @@ -4062,15 +4217,12 @@ public class TransferServiceImplTest extends BaseAlfrescoSpringTest * id and the from repository id * b) to support hub and spoke - when syncing don't imply delete nodes that are not "from" the transferring system * - * - * Q. What about non sync transfer? Assume that this will update. - * * * Tree of nodes * A1 * | | * A2 A3 (Content Node) B6 (Content Node) * | - * A4 A5 A7 + * A4 A5 B7 * * Step 1 * Hub and Spoke Sync @@ -4099,12 +4251,8 @@ public class TransferServiceImplTest extends BaseAlfrescoSpringTest * //Update A5 * //Transfer A5 (normal) - should update */ - public void testMultiRepoSync() throws Exception + public void testTwoRepoSync() throws Exception { - - Descriptor descriptor = descriptorService.getCurrentRepositoryDescriptor(); - String repositoryId = descriptor.getId(); - /** * Step 1 * create Tree A1...A6 @@ -4120,8 +4268,6 @@ public class TransferServiceImplTest extends BaseAlfrescoSpringTest Locale CONTENT_LOCALE = Locale.GERMAN; String CONTENT_STRING = "Hello"; - String REPO_ID_B = "RepoB"; - /** * For unit test * - replace the HTTP transport with the in-process transport @@ -4134,7 +4280,12 @@ public class TransferServiceImplTest extends BaseAlfrescoSpringTest List> pathMap = testNodeFactory.getPathMap(); // Map company_home/guest_home to company_home so tranferred nodes and moved "up" one level. pathMap.add(new Pair(PathHelper.stringToPath(GUEST_HOME_XPATH_QUERY), PathHelper.stringToPath(COMPANY_HOME_XPATH_QUERY))); - + + DescriptorService mockedDescriptorService = getMockDescriptorService(REPO_ID_A); + transferServiceImpl.setDescriptorService(mockedDescriptorService); + + String repositoryId = REPO_ID_A; + /** * Now go ahead and create our first transfer target */ @@ -4276,10 +4427,15 @@ public class TransferServiceImplTest extends BaseAlfrescoSpringTest ChildAssociationRef child = nodeService.createNode(a1Dest, ContentModel.ASSOC_CONTAINS, QName.createQName("B6"), ContentModel.TYPE_CONTENT); B6NodeRef = child.getChildRef(); nodeService.setProperty(B6NodeRef, ContentModel.PROP_TITLE, CONTENT_TITLE); - nodeService.setProperty(B6NodeRef, ContentModel.PROP_NAME, "B4"); - nodeService.setProperty(B6NodeRef, TransferModel.PROP_ALIEN, Boolean.FALSE); - nodeService.setProperty(B6NodeRef, TransferModel.PROP_FROM_REPOSITORY_ID, REPO_ID_B); - nodeService.setProperty(B6NodeRef, TransferModel.PROP_REPOSITORY_ID, REPO_ID_B); + nodeService.setProperty(B6NodeRef, ContentModel.PROP_NAME, "B6"); + + /** + * The first tranfer was mocked to repository A - this is repository B. + */ + + // This is repository B so there's no need to fake it +// nodeService.setProperty(B6NodeRef, TransferModel.PROP_FROM_REPOSITORY_ID, REPO_ID_B); +// nodeService.setProperty(B6NodeRef, TransferModel.PROP_REPOSITORY_ID, REPO_ID_B); ContentWriter writer = contentService.getWriter(B6NodeRef, ContentModel.PROP_CONTENT, true); writer.setLocale(CONTENT_LOCALE); @@ -4309,6 +4465,7 @@ public class TransferServiceImplTest extends BaseAlfrescoSpringTest { // Does node B6 still exist ? assertTrue("dest node B6 does not exist", nodeService.exists(B6NodeRef)); + assertTrue("B6 not alien", nodeService.hasAspect(B6NodeRef, TransferModel.ASPECT_ALIEN)); } finally { @@ -4342,7 +4499,9 @@ public class TransferServiceImplTest extends BaseAlfrescoSpringTest nodeService.setProperty(A3NodeRef, TransferModel.PROP_FROM_REPOSITORY_ID, REPO_ID_B); nodeService.setProperty(A3NodeRef, TransferModel.PROP_REPOSITORY_ID, REPO_ID_B); - // Fake Node A7 + /** + * The repository was mocked to repoistory A. This is repository B + */ ChildAssociationRef child = nodeService.createNode(A2NodeRef, ContentModel.ASSOC_CONTAINS, QName.createQName("A7"), ContentModel.TYPE_CONTENT); A7NodeRef = child.getChildRef(); nodeService.setProperty(A7NodeRef, ContentModel.PROP_TITLE, NEW_TITLE); @@ -4395,8 +4554,342 @@ public class TransferServiceImplTest extends BaseAlfrescoSpringTest { endTransaction(); } - } + } // test two repo sync + /** + * Transfer sync from multiple repos. + * + * This is a unit test so does lots of shenanigans to fake transfer from three repositories on a single repo. + * + * Trees of nodes + * + * A1 + * | + * images + * | + * A2 + * + * B + * | + * images + * | + * B2 + * + * C1 + * | + * images + * | + * C2 + * + * + */ + public void testMultiRepoSync() throws Exception + { + /** + * Step 1 + * create DesTree A1...A6 + * transfer (sync) + * check the transfered aspect + * create node B6. Fake its transfered aspect to be from Repo B, Non Alien. + * transfer (sync) + */ + setDefaultRollback(false); + + String CONTENT_TITLE = "ContentTitle"; + String CONTENT_TITLE_UPDATED = "ContentTitleUpdated"; + Locale CONTENT_LOCALE = Locale.GERMAN; + String CONTENT_STRING = "Hello"; + + /** + * For unit test + * - replace the HTTP transport with the in-process transport + * - replace the node factory with one that will map node refs, paths etc. + */ + TransferTransmitter transmitter = new UnitTestInProcessTransmitterImpl(receiver, contentService, transactionService); + transferServiceImpl.setTransmitter(transmitter); + UnitTestTransferManifestNodeFactory testNodeFactory = new UnitTestTransferManifestNodeFactory(this.transferManifestNodeFactory); + transferServiceImpl.setTransferManifestNodeFactory(testNodeFactory); + List> pathMap = testNodeFactory.getPathMap(); + // Map company_home/guest_home to company_home so tranferred nodes and moved "up" one level. + pathMap.add(new Pair(PathHelper.stringToPath(GUEST_HOME_XPATH_QUERY), PathHelper.stringToPath(COMPANY_HOME_XPATH_QUERY))); + + DescriptorService mockedDescriptorService = getMockDescriptorService(REPO_ID_A); + transferServiceImpl.setDescriptorService(mockedDescriptorService); + + String repositoryId = REPO_ID_A; + + /** + * Now go ahead and create our first transfer target + */ + String targetName = "testTransferSyncNodes"; + TransferTarget transferMe; + NodeRef A1NodeRef; + NodeRef A2NodeRef; + NodeRef A3NodeRef; + NodeRef A4NodeRef; + NodeRef A5NodeRef; + NodeRef B6NodeRef; + NodeRef A7NodeRef; + + startNewTransaction(); + try + { + /** + * Get guest home + */ + String guestHomeQuery = "/app:company_home/app:guest_home"; + ResultSet guestHomeResult = searchService.query(StoreRef.STORE_REF_WORKSPACE_SPACESSTORE, SearchService.LANGUAGE_XPATH, guestHomeQuery); + assertEquals("", 1, guestHomeResult.length()); + NodeRef guestHome = guestHomeResult.getNodeRef(0); + + /** + * Create a test nodes A1 through A5 that we will read and write + */ + { + // Node A1 + String name = GUID.generate(); + ChildAssociationRef child = nodeService.createNode(guestHome, ContentModel.ASSOC_CONTAINS, QName.createQName(name), ContentModel.TYPE_FOLDER); + A1NodeRef = child.getChildRef(); + nodeService.setProperty(A1NodeRef, ContentModel.PROP_TITLE, CONTENT_TITLE); + nodeService.setProperty(A1NodeRef, ContentModel.PROP_NAME, name); + } + + { + // Node A2 + ChildAssociationRef child = nodeService.createNode(A1NodeRef, ContentModel.ASSOC_CONTAINS, QName.createQName("A2"), ContentModel.TYPE_FOLDER); + A2NodeRef = child.getChildRef(); + nodeService.setProperty(A2NodeRef, ContentModel.PROP_TITLE, CONTENT_TITLE); + nodeService.setProperty(A2NodeRef, ContentModel.PROP_NAME, "A2"); + } + + { + // Node A3 + ChildAssociationRef child = nodeService.createNode(A1NodeRef, ContentModel.ASSOC_CONTAINS, QName.createQName("A3"), ContentModel.TYPE_CONTENT); + A3NodeRef = child.getChildRef(); + nodeService.setProperty(A3NodeRef, ContentModel.PROP_TITLE, CONTENT_TITLE); + nodeService.setProperty(A3NodeRef, ContentModel.PROP_NAME, "A3"); + + ContentWriter writer = contentService.getWriter(A3NodeRef, ContentModel.PROP_CONTENT, true); + writer.setLocale(CONTENT_LOCALE); + writer.putContent(CONTENT_STRING); + } + { + // Node A4 + ChildAssociationRef child = nodeService.createNode(A2NodeRef, ContentModel.ASSOC_CONTAINS, QName.createQName("A4"), ContentModel.TYPE_CONTENT); + A4NodeRef = child.getChildRef(); + nodeService.setProperty(A4NodeRef, ContentModel.PROP_TITLE, CONTENT_TITLE); + nodeService.setProperty(A4NodeRef, ContentModel.PROP_NAME, "A4"); + + ContentWriter writer = contentService.getWriter(A4NodeRef, ContentModel.PROP_CONTENT, true); + writer.setLocale(CONTENT_LOCALE); + writer.putContent(CONTENT_STRING); + } + { + // Node A5 + ChildAssociationRef child = nodeService.createNode(A2NodeRef, ContentModel.ASSOC_CONTAINS, QName.createQName("A5"), ContentModel.TYPE_CONTENT); + A5NodeRef = child.getChildRef(); + nodeService.setProperty(A5NodeRef, ContentModel.PROP_TITLE, CONTENT_TITLE); + nodeService.setProperty(A5NodeRef, ContentModel.PROP_NAME, "A5"); + + ContentWriter writer = contentService.getWriter(A5NodeRef, ContentModel.PROP_CONTENT, true); + writer.setLocale(CONTENT_LOCALE); + writer.putContent(CONTENT_STRING); + } + + // Create the transfer target if it does not already exist + if(!transferService.targetExists(targetName)) + { + transferMe = createTransferTarget(targetName); + } + else + { + transferMe = transferService.getTransferTarget(targetName); + } + } + finally + { + endTransaction(); + } + + Setnodes = new HashSet(); + nodes.add(A1NodeRef); + nodes.add(A2NodeRef); + nodes.add(A3NodeRef); + nodes.add(A4NodeRef); + nodes.add(A5NodeRef); + + /** + * transfer (sync) + * check the transfered aspect + * create node B6. Fake its transfered aspect to be from Repo B, Non Alien. + * transfer (sync) + */ + startNewTransaction(); + try + { + { + TransferDefinition definition = new TransferDefinition(); + definition.setNodes(nodes); + definition.setSync(true); + transferService.transfer(targetName, definition); + } + } + finally + { + endTransaction(); + } + startNewTransaction(); + try + { + // Node B6 - faked transfer from repository B. Child of Destination node A1 + NodeRef a1Dest = testNodeFactory.getMappedNodeRef(A1NodeRef); + + assertTrue("dest node A does not exist", nodeService.exists(testNodeFactory.getMappedNodeRef(A1NodeRef))); + assertEquals("dest node A1 From RepositoryId", nodeService.getProperty(testNodeFactory.getMappedNodeRef(A1NodeRef), TransferModel.PROP_FROM_REPOSITORY_ID), repositoryId); + assertEquals("dest node A1 Repository Id", nodeService.getProperty(testNodeFactory.getMappedNodeRef(A1NodeRef), TransferModel.PROP_REPOSITORY_ID), repositoryId); + assertEquals("dest node A2 From RepositoryId", nodeService.getProperty(testNodeFactory.getMappedNodeRef(A2NodeRef), TransferModel.PROP_FROM_REPOSITORY_ID), repositoryId); + assertEquals("dest node A2 Repository Id", nodeService.getProperty(testNodeFactory.getMappedNodeRef(A2NodeRef), TransferModel.PROP_REPOSITORY_ID), repositoryId); + assertEquals("dest node A3 From RepositoryId", nodeService.getProperty(testNodeFactory.getMappedNodeRef(A3NodeRef), TransferModel.PROP_FROM_REPOSITORY_ID), repositoryId); + assertEquals("dest node A3 Repository Id", nodeService.getProperty(testNodeFactory.getMappedNodeRef(A3NodeRef), TransferModel.PROP_REPOSITORY_ID), repositoryId); + assertEquals("dest node A4 From RepositoryId", nodeService.getProperty(testNodeFactory.getMappedNodeRef(A4NodeRef), TransferModel.PROP_FROM_REPOSITORY_ID), repositoryId); + assertEquals("dest node A4 Repository Id", nodeService.getProperty(testNodeFactory.getMappedNodeRef(A4NodeRef), TransferModel.PROP_REPOSITORY_ID), repositoryId); + assertEquals("dest node A5 From RepositoryId", nodeService.getProperty(testNodeFactory.getMappedNodeRef(A5NodeRef), TransferModel.PROP_FROM_REPOSITORY_ID), repositoryId); + assertEquals("dest node A5 Repository Id", nodeService.getProperty(testNodeFactory.getMappedNodeRef(A5NodeRef), TransferModel.PROP_REPOSITORY_ID), repositoryId); + + ChildAssociationRef child = nodeService.createNode(a1Dest, ContentModel.ASSOC_CONTAINS, QName.createQName("B6"), ContentModel.TYPE_CONTENT); + B6NodeRef = child.getChildRef(); + nodeService.setProperty(B6NodeRef, ContentModel.PROP_TITLE, CONTENT_TITLE); + nodeService.setProperty(B6NodeRef, ContentModel.PROP_NAME, "B6"); + + /** + * The first tranfer was mocked to repository A - this is repository B. + */ + + // This is repository B so there's no need to fake it +// nodeService.setProperty(B6NodeRef, TransferModel.PROP_FROM_REPOSITORY_ID, REPO_ID_B); +// nodeService.setProperty(B6NodeRef, TransferModel.PROP_REPOSITORY_ID, REPO_ID_B); + + ContentWriter writer = contentService.getWriter(B6NodeRef, ContentModel.PROP_CONTENT, true); + writer.setLocale(CONTENT_LOCALE); + writer.putContent(CONTENT_STRING); + + } + finally + { + endTransaction(); + } + + startNewTransaction(); + try + { + TransferDefinition definition = new TransferDefinition(); + definition.setNodes(nodes); + definition.setSync(true); + transferService.transfer(targetName, definition); + } + finally + { + endTransaction(); + } + + startNewTransaction(); + try + { + // Does node B6 still exist ? + assertTrue("dest node B6 does not exist", nodeService.exists(B6NodeRef)); + assertTrue("B6 not alien", nodeService.hasAspect(B6NodeRef, TransferModel.ASPECT_ALIEN)); + } + finally + { + endTransaction(); + } + + /** Step 2 + * Chain Sync + * Change Nodes A1 ... A5 source to be received "from repo B" + * Create Node A7 - Fake it to be received "from repo B" + * transfer + */ + String NEW_TITLE="Chain sync"; + + + startNewTransaction(); + try + { + nodeService.setProperty(A1NodeRef, ContentModel.PROP_TITLE, NEW_TITLE); + nodeService.setProperty(A1NodeRef, TransferModel.PROP_ALIEN, Boolean.FALSE); + nodeService.setProperty(A1NodeRef, TransferModel.PROP_FROM_REPOSITORY_ID, REPO_ID_B); + nodeService.setProperty(A1NodeRef, TransferModel.PROP_REPOSITORY_ID, REPO_ID_B); + + nodeService.setProperty(A2NodeRef, ContentModel.PROP_TITLE, NEW_TITLE); + nodeService.setProperty(A2NodeRef, TransferModel.PROP_ALIEN, Boolean.FALSE); + nodeService.setProperty(A2NodeRef, TransferModel.PROP_FROM_REPOSITORY_ID, REPO_ID_B); + nodeService.setProperty(A2NodeRef, TransferModel.PROP_REPOSITORY_ID, REPO_ID_B); + + nodeService.setProperty(A3NodeRef, ContentModel.PROP_TITLE, NEW_TITLE); + nodeService.setProperty(A3NodeRef, TransferModel.PROP_ALIEN, Boolean.FALSE); + nodeService.setProperty(A3NodeRef, TransferModel.PROP_FROM_REPOSITORY_ID, REPO_ID_B); + nodeService.setProperty(A3NodeRef, TransferModel.PROP_REPOSITORY_ID, REPO_ID_B); + + /** + * The repository was mocked to repoistory A. This is repository B + */ + ChildAssociationRef child = nodeService.createNode(A2NodeRef, ContentModel.ASSOC_CONTAINS, QName.createQName("A7"), ContentModel.TYPE_CONTENT); + A7NodeRef = child.getChildRef(); + nodeService.setProperty(A7NodeRef, ContentModel.PROP_TITLE, NEW_TITLE); + nodeService.setProperty(A7NodeRef, ContentModel.PROP_NAME, "A7"); + nodeService.setProperty(A7NodeRef, ContentModel.PROP_TITLE, NEW_TITLE); + nodeService.setProperty(A7NodeRef, TransferModel.PROP_ALIEN, Boolean.FALSE); + nodeService.setProperty(A7NodeRef, TransferModel.PROP_FROM_REPOSITORY_ID, REPO_ID_B); + nodeService.setProperty(A7NodeRef, TransferModel.PROP_REPOSITORY_ID, REPO_ID_B); + + ContentWriter writer = contentService.getWriter(A3NodeRef, ContentModel.PROP_CONTENT, true); + writer.setLocale(CONTENT_LOCALE); + writer.putContent(CONTENT_STRING); + } + finally + { + endTransaction(); + } + nodes.add(A7NodeRef); + + startNewTransaction(); + try + { + TransferDefinition definition = new TransferDefinition(); + definition.setNodes(nodes); + definition.setSync(true); + transferService.transfer(targetName, definition); + } + finally + { + endTransaction(); + } + + try + { + assertTrue("dest node A7 does not exist", nodeService.exists(testNodeFactory.getMappedNodeRef(A7NodeRef))); + + assertEquals("dest node A1 Title", nodeService.getProperty(testNodeFactory.getMappedNodeRef(A1NodeRef), ContentModel.PROP_TITLE), NEW_TITLE); + assertEquals("dest node A1 Repository Id", nodeService.getProperty(testNodeFactory.getMappedNodeRef(A1NodeRef), TransferModel.PROP_REPOSITORY_ID), REPO_ID_B); + assertEquals("dest node A1 Repository Id", nodeService.getProperty(testNodeFactory.getMappedNodeRef(A1NodeRef), TransferModel.PROP_FROM_REPOSITORY_ID), repositoryId); + + assertEquals("dest node A2 Title", nodeService.getProperty(testNodeFactory.getMappedNodeRef(A2NodeRef), ContentModel.PROP_TITLE), NEW_TITLE); + assertEquals("dest node A2 Repository Id", nodeService.getProperty(testNodeFactory.getMappedNodeRef(A2NodeRef), TransferModel.PROP_REPOSITORY_ID), REPO_ID_B); + assertEquals("dest node A2 Repository Id", nodeService.getProperty(testNodeFactory.getMappedNodeRef(A2NodeRef), TransferModel.PROP_FROM_REPOSITORY_ID), repositoryId); + + assertEquals("dest node A3 Title", nodeService.getProperty(testNodeFactory.getMappedNodeRef(A3NodeRef), ContentModel.PROP_TITLE), NEW_TITLE); + assertEquals("dest node A3 Repository Id", nodeService.getProperty(testNodeFactory.getMappedNodeRef(A3NodeRef), TransferModel.PROP_REPOSITORY_ID), REPO_ID_B); + assertEquals("dest node A3 Repository Id", nodeService.getProperty(testNodeFactory.getMappedNodeRef(A3NodeRef), TransferModel.PROP_FROM_REPOSITORY_ID), repositoryId); + } + finally + { + endTransaction(); + } + } // test multi repo sync + + + // Utility methods below. private TransferTarget createTransferTarget(String name) { @@ -4416,7 +4909,15 @@ public class TransferServiceImplTest extends BaseAlfrescoSpringTest return target; } - +// /** +// * transfer should only be able to update and delete nodes that are "from" the transferring system +// * +// * not yet implemented. +// */ +// public void testFromRepo() +// { +// assertTrue("not yet implemented", false); +// } private void createUser(String userName, String password) { @@ -4434,4 +4935,15 @@ public class TransferServiceImplTest extends BaseAlfrescoSpringTest this.personService.createPerson(ppOne); } } + + private DescriptorService getMockDescriptorService(String repositoryId) + { + DescriptorService descriptorService = mock(DescriptorService.class); + Descriptor descriptor = mock(Descriptor.class); + + when(descriptor.getId()).thenReturn(repositoryId); + when(descriptorService.getCurrentRepositoryDescriptor()).thenReturn(descriptor); + + return descriptorService; + } }