ALF-4128 : F99 transfer service (alien invader)

implementation check point - some testing complete.

git-svn-id: https://svn.alfresco.com/repos/alfresco-enterprise/alfresco/HEAD/root@21637 c4b6b30b-aa2e-2d43-bbcb-ca4b014f7261
This commit is contained in:
Mark Rogers
2010-08-05 13:17:56 +00:00
parent 2c97a0e38b
commit ff2b31480d
11 changed files with 2384 additions and 796 deletions

View File

@@ -188,25 +188,41 @@
</aspect> </aspect>
<aspect name="trx:transferred"> <aspect name="trx:transferred">
<title>Nodes with this aspect have been transferred from one repository to another <title>Transferred Node</title>
</title> <description>Nodes with this aspect have been transferred from one repository to another</description>
<properties> <properties>
<property name="trx:repositoryId"> <property name="trx:repositoryId">
<title>Source RepositoryId</title> <title>Source RepositoryId</title>
<description>The repository id that this node originates from.</description>
<type>d:text</type> <type>d:text</type>
<mandatory enforced="true">true</mandatory> <mandatory enforced="true">true</mandatory>
</property> </property>
<property name="trx:fromRepositoryId"> <property name="trx:fromRepositoryId">
<title>The repository that this node was transferred from</title> <title>From Repository Id</title>
<description>The id of the repository that transferred this node to this repository</description>
<type>d:text</type> <type>d:text</type>
<mandatory enforced="true">true</mandatory> <mandatory enforced="true">true</mandatory>
</property> </property>
</properties>
</aspect>
<aspect name="trx:alien">
<title>Alien Node</title>
<description>Nodes with this aspect are either alien nodes or have been invaded by alien content</description>
<properties>
<property name="trx:alien"> <property name="trx:alien">
<title>Alien Content</title> <title>Alien Content</title>
<type>d:boolean</type> <type>d:boolean</type>
<mandatory enforced="false">false</mandatory> <mandatory enforced="false">false</mandatory>
<default>false</default> <default>false</default>
</property> </property>
<property name="trx:invadedBy">
<title>The repositories that have invaded this node</title>
<type>d:text</type>
<mandatory enforced="false">false</mandatory>
<multiple>true</multiple>
<default>false</default>
</property>
</properties> </properties>
</aspect> </aspect>

View File

@@ -54,6 +54,12 @@
<property name="permissionService" ref="PermissionService" /> <property name="permissionService" ref="PermissionService" />
</bean> </bean>
<bean id="alienProcessor" class="org.alfresco.repo.transfer.AlienProcessorImpl"
init-method="init">
<property name="nodeService" ref="NodeService" />
<property name="dictionaryService" ref="DictionaryService" />
<property name="behaviourFilter" ref="policyBehaviourFilter" />
</bean>
<bean id="transferReceiver" class="org.alfresco.repo.transfer.RepoTransferReceiverImpl" <bean id="transferReceiver" class="org.alfresco.repo.transfer.RepoTransferReceiverImpl"
init-method="init"> init-method="init">
@@ -63,6 +69,8 @@
<property name="actionService" ref="ActionService" /> <property name="actionService" ref="ActionService" />
<property name="tenantService" ref="tenantService" /> <property name="tenantService" ref="tenantService" />
<property name="ruleService" ref="RuleService" /> <property name="ruleService" ref="RuleService" />
<property name="descriptorService" ref="DescriptorService" />
<property name="alienProcessor" ref="alienProcessor" />
<property name="transferLockFolderPath"> <property name="transferLockFolderPath">
<value>/${spaces.company_home.childname}/${spaces.dictionary.childname}/${spaces.transfers.childname}</value> <value>/${spaces.company_home.childname}/${spaces.dictionary.childname}/${spaces.transfers.childname}</value>
@@ -107,6 +115,7 @@
<property name="dictionaryService" ref="DictionaryService" /> <property name="dictionaryService" ref="DictionaryService" />
<property name="permissionService" ref="PermissionService" /> <property name="permissionService" ref="PermissionService" />
<property name="nodeResolverFactory" ref="transferNodeResolverFactory" /> <property name="nodeResolverFactory" ref="transferNodeResolverFactory" />
<property name="alienProcessor" ref="alienProcessor" />
</bean> </bean>
<bean id="transferNodeResolverFactory" <bean id="transferNodeResolverFactory"

View File

@@ -0,0 +1,53 @@
package org.alfresco.repo.transfer;
import org.alfresco.service.cmr.repository.ChildAssociationRef;
import org.alfresco.service.cmr.repository.NodeRef;
/**
* This class groups together the business logic for alien nodes which are
* transferred nodes that contain children from another repository.
* <p>
* Alien nodes cannot be deleted through the transfer service, instead they are
* "pruned"
*
* <p>
* 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.
* <p>
* 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);
}

View File

@@ -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<QName> 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<Void> actionRunAs = new RunAsWork<Void>()
{
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");
List<String>stuff = (List<String>)nodeService.getProperty(deletedNodeRef, TransferModel.PROP_INVADED_BY);
Vector<String> exInvaders = new Vector<String>(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<String> 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<ChildAssociationRef> refs = nodeService.getChildAssocs(parentNodeRef);
for(ChildAssociationRef ref : refs)
{
NodeRef childRef = ref.getChildRef();
List<String>invadedBy = (List<String>)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<String> parentInvaders = (List<String>)nodeService.getProperty(parentNodeRef, TransferModel.PROP_INVADED_BY);
final List<String> newInvaders = new ArrayList<String>(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<Void> actionRunAs = new RunAsWork<Void>()
{
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<NodeRef> nodesToPrune = new Stack<NodeRef>();
Stack<NodeRef> foldersToRecalculate = new Stack<NodeRef>();
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
List<String>invadedBy = (List<String>)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<ChildAssociationRef> 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<ChildAssociationRef> 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);
List<String>folderInvadedBy = (List<String>)getNodeService().getProperty(folderNodeRef, TransferModel.PROP_INVADED_BY);
boolean stillInvaded = false;
//TODO need a more efficient query here
List<ChildAssociationRef> refs = getNodeService().getChildAssocs(folderNodeRef);
for(ChildAssociationRef ref : refs)
{
NodeRef childNode = ref.getChildRef();
List<String>childInvadedBy = (List<String>)getNodeService().getProperty(childNode, TransferModel.PROP_INVADED_BY);
if(childInvadedBy.contains(fromRepositoryId))
{
log.debug("folder is still invaded");
stillInvaded = true;
break;
}
}
if(!stillInvaded)
{
List<String> newInvadedBy = new ArrayList<String>(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)
{
List<String>invadedBy = (List<String>)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<String> invadedBy = (List<String>)nodeService.getProperty(newAlien,
TransferModel.PROP_INVADED_BY);
if(invadedBy == null)
{
nodeService.setProperty(newAlien, TransferModel.PROP_ALIEN, Boolean.TRUE);
invadedBy = new ArrayList<String>(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;
}
}

View File

@@ -40,6 +40,7 @@ public class DefaultManifestProcessorFactoryImpl implements ManifestProcessorFac
private DictionaryService dictionaryService; private DictionaryService dictionaryService;
private PermissionService permissionService; private PermissionService permissionService;
private CorrespondingNodeResolverFactory nodeResolverFactory; private CorrespondingNodeResolverFactory nodeResolverFactory;
private AlienProcessor alienProcessor;
/* /*
* (non-Javadoc) * (non-Javadoc)
@@ -57,6 +58,7 @@ public class DefaultManifestProcessorFactoryImpl implements ManifestProcessorFac
primaryProcessor.setNodeService(nodeService); primaryProcessor.setNodeService(nodeService);
primaryProcessor.setDictionaryService(dictionaryService); primaryProcessor.setDictionaryService(dictionaryService);
primaryProcessor.setPermissionService(getPermissionService()); primaryProcessor.setPermissionService(getPermissionService());
primaryProcessor.setAlienProcessor(getAlienProcessor());
processors.add(primaryProcessor); processors.add(primaryProcessor);
RepoSecondaryManifestProcessorImpl secondaryProcessor = new RepoSecondaryManifestProcessorImpl(receiver, transferId); RepoSecondaryManifestProcessorImpl secondaryProcessor = new RepoSecondaryManifestProcessorImpl(receiver, transferId);
@@ -65,8 +67,8 @@ public class DefaultManifestProcessorFactoryImpl implements ManifestProcessorFac
processors.add(secondaryProcessor); processors.add(secondaryProcessor);
RepoTertiaryManifestProcessorImpl tertiaryProcessor = new RepoTertiaryManifestProcessorImpl(receiver, transferId); RepoTertiaryManifestProcessorImpl tertiaryProcessor = new RepoTertiaryManifestProcessorImpl(receiver, transferId);
tertiaryProcessor.setNodeResolver(nodeResolver);
tertiaryProcessor.setNodeService(nodeService); tertiaryProcessor.setNodeService(nodeService);
tertiaryProcessor.setAlienProcessor(getAlienProcessor());
processors.add(tertiaryProcessor); processors.add(tertiaryProcessor);
return processors; return processors;
@@ -114,7 +116,6 @@ public class DefaultManifestProcessorFactoryImpl implements ManifestProcessorFac
RepoRequsiteManifestProcessorImpl processor = new RepoRequsiteManifestProcessorImpl(receiver, transferId, out); RepoRequsiteManifestProcessorImpl processor = new RepoRequsiteManifestProcessorImpl(receiver, transferId, out);
CorrespondingNodeResolver nodeResolver = nodeResolverFactory.getResolver(); CorrespondingNodeResolver nodeResolver = nodeResolverFactory.getResolver();
processor.setNodeResolver(nodeResolver); processor.setNodeResolver(nodeResolver);
processor.setNodeService(nodeService); processor.setNodeService(nodeService);
@@ -131,4 +132,14 @@ public class DefaultManifestProcessorFactoryImpl implements ManifestProcessorFac
return permissionService; return permissionService;
} }
public void setAlienProcessor(AlienProcessor alienProcessor)
{
this.alienProcessor = alienProcessor;
}
public AlienProcessor getAlienProcessor()
{
return alienProcessor;
}
} }

View File

@@ -89,6 +89,7 @@ public class RepoPrimaryManifestProcessorImpl extends AbstractManifestProcessorB
private ContentService contentService; private ContentService contentService;
private DictionaryService dictionaryService; private DictionaryService dictionaryService;
private CorrespondingNodeResolver nodeResolver; private CorrespondingNodeResolver nodeResolver;
private AlienProcessor alienProcessor;
// State within this class // State within this class
/** /**
@@ -148,18 +149,35 @@ public class RepoPrimaryManifestProcessorImpl extends AbstractManifestProcessorB
// Does a corresponding node exist in this repo? // Does a corresponding node exist in this repo?
if (resolvedNodes.resolvedChild != null) if (resolvedNodes.resolvedChild != null)
{ {
NodeRef exNode = resolvedNodes.resolvedChild;
// Yes, it does. Delete it. // Yes, it does. Delete it.
if (log.isDebugEnabled()) if (log.isDebugEnabled())
{ {
log.debug("Incoming deleted noderef " + node.getNodeRef() 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"); + " - deleting");
} }
logProgress("Deleting local node: " + resolvedNodes.resolvedChild);
nodeService.deleteNode(resolvedNodes.resolvedChild); //TODO : do we have a business rule that only the "from" repo can delete a node? Yes we do.
if(alienProcessor.isAlien(exNode))
{
logProgress("Pruning local node: " + exNode);
if (log.isDebugEnabled()) if (log.isDebugEnabled())
{ {
log.debug("Deleted local node: " + resolvedNodes.resolvedChild); 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 else
@@ -204,7 +222,7 @@ public class RepoPrimaryManifestProcessorImpl extends AbstractManifestProcessorB
// Does a corresponding node exist in this repo? // Does a corresponding node exist in this repo?
if (resolvedNodes.resolvedChild != null) if (resolvedNodes.resolvedChild != null)
{ {
// Yes, it does. Update it. // Yes, the corresponding node does exist. Update it.
if (log.isDebugEnabled()) if (log.isDebugEnabled())
{ {
log.debug("Incoming noderef " + node.getNodeRef() + " has been resolved to existing local noderef " log.debug("Incoming noderef " + node.getNodeRef() + " has been resolved to existing local noderef "
@@ -214,8 +232,7 @@ public class RepoPrimaryManifestProcessorImpl extends AbstractManifestProcessorB
} }
else else
{ {
// No, there is no corresponding node. Worth just quickly checking // No, there is no corresponding node.
// the archive store...
NodeRef archiveNodeRef = new NodeRef(StoreRef.STORE_REF_ARCHIVE_SPACESSTORE, node.getNodeRef().getId()); NodeRef archiveNodeRef = new NodeRef(StoreRef.STORE_REF_ARCHIVE_SPACESSTORE, node.getNodeRef().getId());
if (nodeService.exists(archiveNodeRef)) if (nodeService.exists(archiveNodeRef))
{ {
@@ -240,6 +257,7 @@ public class RepoPrimaryManifestProcessorImpl extends AbstractManifestProcessorB
} }
/** /**
* Create new node.
* *
* @param node * @param node
* @param resolvedNodes * @param resolvedNodes
@@ -285,13 +303,11 @@ public class RepoPrimaryManifestProcessorImpl extends AbstractManifestProcessorB
// Split out the content properties and sanitise the others // Split out the content properties and sanitise the others
Map<QName, Serializable> contentProps = processProperties(null, props, null); Map<QName, Serializable> contentProps = processProperties(null, props, null);
// inject transferred property here injectTransferred(props);
if(!contentProps.containsKey(TransferModel.PROP_REPOSITORY_ID))
{ // Remove the invadedBy property since that is used by the transfer service
log.debug("injecting repositoryId property"); // and is local to this repository.
props.put(TransferModel.PROP_REPOSITORY_ID, header.getRepositoryId()); props.remove(TransferModel.PROP_INVADED_BY);
}
props.put(TransferModel.PROP_FROM_REPOSITORY_ID, header.getRepositoryId());
// Do we need to worry about locking this new node ? // Do we need to worry about locking this new node ?
if(header.isReadOnly()) if(header.isReadOnly())
@@ -323,7 +339,6 @@ public class RepoPrimaryManifestProcessorImpl extends AbstractManifestProcessorB
nodeService.addAspect(newNode.getChildRef(), aspect, null); nodeService.addAspect(newNode.getChildRef(), aspect, null);
} }
ManifestAccessControl acl = node.getAccessControl(); ManifestAccessControl acl = node.getAccessControl();
// Apply new ACL to this node // Apply new ACL to this node
if(acl != null) if(acl != null)
@@ -345,6 +360,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 // Is the node that we've just added the parent of any orphans that
// we've found earlier? // we've found earlier?
checkOrphans(newNode.getChildRef()); checkOrphans(newNode.getChildRef());
@@ -359,8 +383,16 @@ public class RepoPrimaryManifestProcessorImpl extends AbstractManifestProcessorB
for (ChildAssociationRef orphan : orphansToClaim) for (ChildAssociationRef orphan : orphansToClaim)
{ {
logProgress("Re-parenting previously orphaned node (" + orphan.getChildRef() + ") with found parent " + orphan.getParentRef()); 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()); .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 // We can now remove the record of these orphans, as their parent
// has been found // has been found
@@ -402,9 +434,25 @@ public class RepoPrimaryManifestProcessorImpl extends AbstractManifestProcessorB
|| !currentParent.getTypeQName().equals(parentAssocType) || !currentParent.getTypeQName().equals(parentAssocType)
|| !currentParent.getQName().equals(parentAssocName)) || !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 // 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); 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); log.info("Resolved parent node to " + parentNodeRef);
@@ -418,12 +466,18 @@ public class RepoPrimaryManifestProcessorImpl extends AbstractManifestProcessorB
Map<QName, Serializable> existingProps = nodeService.getProperties(nodeToUpdate); Map<QName, Serializable> existingProps = nodeService.getProperties(nodeToUpdate);
// inject transferred property here // inject transferred property here
if(!props.containsKey(TransferModel.PROP_REPOSITORY_ID)) injectTransferred(props);
{
log.debug("injecting repositoryId property"); // if(!props.containsKey(TransferModel.PROP_REPOSITORY_ID))
props.put(TransferModel.PROP_REPOSITORY_ID, header.getRepositoryId()); // {
} // log.debug("injecting repositoryId property");
props.put(TransferModel.PROP_FROM_REPOSITORY_ID, header.getRepositoryId()); // 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 ? // Do we need to worry about locking this updated ?
if(header.isReadOnly()) if(header.isReadOnly())
@@ -437,6 +491,12 @@ public class RepoPrimaryManifestProcessorImpl extends AbstractManifestProcessorB
// Split out the content properties and sanitise the others // Split out the content properties and sanitise the others
Map<QName, Serializable> contentProps = processProperties(nodeToUpdate, props, existingProps); Map<QName, Serializable> 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 // Update the non-content properties
nodeService.setProperties(nodeToUpdate, props); nodeService.setProperties(nodeToUpdate, props);
@@ -461,7 +521,13 @@ public class RepoPrimaryManifestProcessorImpl extends AbstractManifestProcessorB
} }
aspectsToRemove.removeAll(suppliedAspects); aspectsToRemove.removeAll(suppliedAspects);
/**
* Don't remove the aspects that the transfer service uses itself.
*/
aspectsToRemove.remove(TransferModel.ASPECT_TRANSFERRED); aspectsToRemove.remove(TransferModel.ASPECT_TRANSFERRED);
aspectsToRemove.remove(TransferModel.ASPECT_ALIEN);
suppliedAspects.removeAll(existingAspects); suppliedAspects.removeAll(existingAspects);
// Now aspectsToRemove contains the set of aspects to remove // Now aspectsToRemove contains the set of aspects to remove
@@ -542,6 +608,7 @@ public class RepoPrimaryManifestProcessorImpl extends AbstractManifestProcessorB
} }
} }
/** /**
* This method takes all the received properties and separates them into two parts. The content properties are * This method takes all the received properties and separates them into two parts. The content properties are
* removed from the non-content properties such that the non-content properties remain in the "props" map and the * removed from the non-content properties such that the non-content properties remain in the "props" map and the
@@ -798,4 +865,27 @@ public class RepoPrimaryManifestProcessorImpl extends AbstractManifestProcessorB
return permissionService; return permissionService;
} }
/**
* inject transferred
*/
private void injectTransferred(Map<QName, Serializable> 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;
}
} }

View File

@@ -40,10 +40,10 @@ import org.alfresco.service.namespace.RegexQNamePattern;
* *
* The secondary manifest processor performs a second parse of the snapshot file. * 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 * It is responsible for linking nodes together.
* 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 * At the point that this processor runs both ends (source and target) of the nodes' associations should be
* repository. * available in the receiving repository.
* *
*/ */
public class RepoSecondaryManifestProcessorImpl extends AbstractManifestProcessorBase public class RepoSecondaryManifestProcessorImpl extends AbstractManifestProcessorBase

View File

@@ -19,10 +19,12 @@
package org.alfresco.repo.transfer; package org.alfresco.repo.transfer;
import java.io.Serializable;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.HashMap; import java.util.HashMap;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.Stack;
import org.alfresco.repo.transfer.manifest.TransferManifestDeletedNode; import org.alfresco.repo.transfer.manifest.TransferManifestDeletedNode;
import org.alfresco.repo.transfer.manifest.TransferManifestHeader; 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. * 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. * If the transfer is not "sync" then this processor does nothing.
*
*/ */
public class RepoTertiaryManifestProcessorImpl extends AbstractManifestProcessorBase public class RepoTertiaryManifestProcessorImpl extends AbstractManifestProcessorBase
{ {
private NodeService nodeService; private NodeService nodeService;
private CorrespondingNodeResolver nodeResolver; private AlienProcessor alienProcessor;
private static final Log log = LogFactory.getLog(RepoTertiaryManifestProcessorImpl.class); private static final Log log = LogFactory.getLog(RepoTertiaryManifestProcessorImpl.class);
@@ -83,7 +84,7 @@ public class RepoTertiaryManifestProcessorImpl extends AbstractManifestProcessor
protected void processNode(TransferManifestNormalNode node) protected void processNode(TransferManifestNormalNode node)
{ {
NodeRef nodeRef = node.getNodeRef(); NodeRef nodeRef = node.getNodeRef();
log.debug("processNode " + nodeRef); log.debug("processNode : " + nodeRef);
if(isSync) if(isSync)
{ {
@@ -93,8 +94,11 @@ public class RepoTertiaryManifestProcessorImpl extends AbstractManifestProcessor
List<NodeRef> expectedChildNodeRefs = new ArrayList<NodeRef>(); List<NodeRef> expectedChildNodeRefs = new ArrayList<NodeRef>();
for(ChildAssociationRef ref : expectedChildren) for(ChildAssociationRef ref : expectedChildren)
{
if(log.isDebugEnabled())
{ {
log.debug("expecting child" + ref); log.debug("expecting child" + ref);
}
expectedChildNodeRefs.add(ref.getChildRef()); expectedChildNodeRefs.add(ref.getChildRef());
} }
@@ -103,6 +107,7 @@ public class RepoTertiaryManifestProcessorImpl extends AbstractManifestProcessor
if(nodeService.exists(nodeRef)) if(nodeService.exists(nodeRef))
{ {
log.debug("destination node exists"); log.debug("destination node exists");
/** /**
* yes this node exists in the destination. * 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 * 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 * it needs to be deleted.
* touch it. *
* another repository then we have to prune the alien children
* rather than deleting it.
*/ */
log.debug("an unexpected child node:" + child); log.debug("an unexpected child node:" + child);
if(nodeService.hasAspect(childNodeRef, TransferModel.ASPECT_TRANSFERRED)) if(nodeService.hasAspect(childNodeRef, TransferModel.ASPECT_TRANSFERRED))
@@ -141,7 +148,18 @@ public class RepoTertiaryManifestProcessorImpl extends AbstractManifestProcessor
if(manifestRepositoryId.equalsIgnoreCase(fromRepositoryId)) if(manifestRepositoryId.equalsIgnoreCase(fromRepositoryId))
{ {
// Yes the manifest repository Id and the from repository Id match. // Yes the manifest repository Id and the from repository Id match.
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. // Destination node needs to be deleted.
nodeService.deleteNode(childNodeRef); nodeService.deleteNode(childNodeRef);
log.debug("deleted node:" + childNodeRef); log.debug("deleted node:" + childNodeRef);
@@ -154,6 +172,7 @@ public class RepoTertiaryManifestProcessorImpl extends AbstractManifestProcessor
} }
} }
} }
}
protected void processHeader(TransferManifestHeader header) protected void processHeader(TransferManifestHeader header)
{ {
@@ -183,12 +202,168 @@ public class RepoTertiaryManifestProcessorImpl extends AbstractManifestProcessor
this.nodeService = nodeService; this.nodeService = nodeService;
} }
/** // /**
* @param nodeResolver // * Prune out the non aliens from the specified repository
* the nodeResolver to set // *
*/ // * Need to walk the tree downwards pruning any aliens for this repository
public void setNodeResolver(CorrespondingNodeResolver nodeResolver) // *
// * 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<NodeRef> nodesToPrune = new Stack<NodeRef>();
// Stack<NodeRef> foldersToRecalculate = new Stack<NodeRef>();
// 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
// List<String>invadedBy = (List<String>)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<ChildAssociationRef> 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<ChildAssociationRef> 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);
//
// List<String>folderInvadedBy = (List<String>)nodeService.getProperty(folderNodeRef, TransferModel.PROP_INVADED_BY);
//
// boolean stillInvaded = false;
// //TODO need a more efficient query here
// List<ChildAssociationRef> refs = nodeService.getChildAssocs(folderNodeRef);
// for(ChildAssociationRef ref : refs)
// {
// NodeRef childNode = ref.getChildRef();
// List<String>childInvadedBy = (List<String>)nodeService.getProperty(childNode, TransferModel.PROP_INVADED_BY);
//
// if(childInvadedBy.contains(fromRepositoryId))
// {
// log.debug("folder is still invaded");
// stillInvaded = true;
// break;
// }
// }
//
// if(!stillInvaded)
// {
// List<String> newInvadedBy = new ArrayList<String>(folderInvadedBy);
// folderInvadedBy.remove(fromRepositoryId);
// nodeService.setProperty(folderNodeRef, TransferModel.PROP_INVADED_BY, (Serializable)newInvadedBy);
// }
// }
// log.debug("pruneNode: end");
// }
public void setAlienProcessor(AlienProcessor alienProcessor)
{ {
this.nodeResolver = nodeResolver; this.alienProcessor = alienProcessor;
}
public AlienProcessor getAlienProcessor()
{
return alienProcessor;
} }
} }

View File

@@ -28,10 +28,13 @@ import java.io.OutputStreamWriter;
import java.io.Serializable; import java.io.Serializable;
import java.io.Writer; import java.io.Writer;
import java.text.SimpleDateFormat; import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date; import java.util.Date;
import java.util.HashMap; import java.util.HashMap;
import java.util.Iterator;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.Vector;
import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentHashMap;
import javax.xml.parsers.SAXParser; 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.TransferProgress;
import org.alfresco.service.cmr.transfer.TransferReceiver; import org.alfresco.service.cmr.transfer.TransferReceiver;
import org.alfresco.service.cmr.transfer.TransferProgress.Status; 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.NamespaceService;
import org.alfresco.service.namespace.QName; import org.alfresco.service.namespace.QName;
import org.alfresco.service.namespace.RegexQNamePattern; import org.alfresco.service.namespace.RegexQNamePattern;
@@ -158,6 +162,10 @@ public class RepoTransferReceiverImpl implements TransferReceiver,
private TenantService tenantService; private TenantService tenantService;
private RuleService ruleService; private RuleService ruleService;
private PolicyComponent policyComponent; private PolicyComponent policyComponent;
private DescriptorService descriptorService;
private AlienProcessor alienProcessor;
//private String localRepositoryId = descriptorService.getCurrentRepositoryDescriptor().getId();
private Map<String,NodeRef> transferLockFolderMap = new ConcurrentHashMap<String, NodeRef>(); private Map<String,NodeRef> transferLockFolderMap = new ConcurrentHashMap<String, NodeRef>();
private Map<String,NodeRef> transferTempFolderMap = new ConcurrentHashMap<String, NodeRef>(); private Map<String,NodeRef> transferTempFolderMap = new ConcurrentHashMap<String, NodeRef>();
@@ -176,17 +184,24 @@ public class RepoTransferReceiverImpl implements TransferReceiver,
PropertyCheck.mandatory(this, "inboundTransferRecordsPath", inboundTransferRecordsPath); PropertyCheck.mandatory(this, "inboundTransferRecordsPath", inboundTransferRecordsPath);
PropertyCheck.mandatory(this, "rootStagingDirectory", rootStagingDirectory); PropertyCheck.mandatory(this, "rootStagingDirectory", rootStagingDirectory);
PropertyCheck.mandatory(this, "policyComponent", policyComponent); PropertyCheck.mandatory(this, "policyComponent", policyComponent);
PropertyCheck.mandatory(this, "descriptorService", descriptorService);
PropertyCheck.mandatory(this, "alienProcessor", alienProcessor);
// // Register policy behaviours /**
// this.getPolicyComponent().bindAssociationBehaviour( * For every new child of a node with the trx:transferred aspect run this.onCreateChildAssociation
// NodeServicePolicies.OnCreateChildAssociationPolicy.QNAME, */
// TransferModel.ASPECT_TRANSFERRED, this.getPolicyComponent().bindAssociationBehaviour(
// new JavaBehaviour(this, "onCreateChildAssociation", NotificationFrequency.EVERY_EVENT)); NodeServicePolicies.OnCreateChildAssociationPolicy.QNAME,
// TransferModel.ASPECT_TRANSFERRED,
// this.getPolicyComponent().bindClassBehaviour( new JavaBehaviour(this, "onCreateChildAssociation", NotificationFrequency.EVERY_EVENT));
// NodeServicePolicies.BeforeDeleteNodePolicy.QNAME,
// this, /**
// new JavaBehaviour(this, "beforeDeleteNode", 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 * Set the ruleService
* the progressMonitor to set * @param ruleService
* the ruleService to set
*/ */
public void setRuleService(RuleService ruleService) public void setRuleService(RuleService ruleService)
{ {
this.ruleService = ruleService; this.ruleService = ruleService;
} }
/**
* Get the rule service
* @return the rule service
*/
public RuleService getRuleService() public RuleService getRuleService()
{ {
return this.ruleService; 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 * When a new node is created as a child of a Transferred or Alien node then
* the transferred nodes need to be marked as Alien nodes. * the new node needs to be marked as an alien.
* * <p>
* The tree needs to be walked upwards to mark all parent transferred nodes as alien. * Then the tree needs to be walked upwards to mark all parent
* transferred nodes as alien.
*/ */
public void onCreateChildAssociation(ChildAssociationRef childAssocRef, public void onCreateChildAssociation(ChildAssociationRef childAssocRef,
boolean isNewNode) boolean isNewNode)
@@ -946,75 +967,273 @@ public class RepoTransferReceiverImpl implements TransferReceiver,
log.debug("on create child association to transferred node"); log.debug("on create child association to transferred node");
ChildAssociationRef ref = childAssocRef; final String localRepositoryId = descriptorService.getCurrentRepositoryDescriptor().getId();
alienProcessor.onCreateChild(childAssocRef, localRepositoryId);
if(childAssocRef.isPrimary()) //
{ // ChildAssociationRef currentAssoc = childAssocRef;
while(ref != null) //
{ // final String localRepositoryId = descriptorService.getCurrentRepositoryDescriptor().getId();
NodeRef parentNodeRef = ref.getParentRef(); //
NodeRef childNodeRef = ref.getChildRef(); // // TODO Needs to check assoc is a cm:contains or subtype
// if(childAssocRef.isPrimary())
if(nodeService.hasAspect(parentNodeRef, TransferModel.ASPECT_TRANSFERRED)) // {
{ // NodeRef parentNodeRef = currentAssoc.getParentRef();
Boolean isAlien = (Boolean)nodeService.getProperty(parentNodeRef, TransferModel.PROP_ALIEN); // NodeRef childNodeRef = currentAssoc.getChildRef();
//
if (!isAlien) // /**
{ // * Make the new child node ref an alien node
log.debug("setting node as alien:" + parentNodeRef); // */
nodeService.setProperty(parentNodeRef, TransferModel.PROP_ALIEN, Boolean.TRUE); // setAlien(childNodeRef, localRepositoryId);
ref = nodeService.getPrimaryParent(parentNodeRef); //
} // /**
else // * Now deal with the parents of this alien node
{ // */
log.debug("parent node is already alien"); // while(currentAssoc != null)
ref = null; // {
} // parentNodeRef = currentAssoc.getParentRef();
} // childNodeRef = currentAssoc.getChildRef();
else //
{ // if(nodeService.hasAspect(parentNodeRef, TransferModel.ASPECT_TRANSFERRED) || nodeService.hasAspect(parentNodeRef, TransferModel.ASPECT_ALIEN))
log.debug("parent is not a transferred node"); // {
ref = null; // 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<Void> actionRunAs = new RunAsWork<Void>()
// {
// 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 * When an alien node is deleted the it may be the last alien invader
* mark parent folder as non alien. * <p>
* 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"); log.debug("on delete node - need to check for transferred node");
alienProcessor.beforeDeleteAlien(deletedNodeRef);
ChildAssociationRef ref = nodeService.getPrimaryParent(nodeRef); //
// List<String>stuff = (List<String>)nodeService.getProperty(deletedNodeRef, TransferModel.PROP_INVADED_BY);
while(ref != null) //
{ // Vector<String> exInvaders = new Vector<String>(stuff);
NodeRef parentNodeRef = ref.getParentRef(); //
// ChildAssociationRef currentAssoc = nodeService.getPrimaryParent(deletedNodeRef);
/** //
* Was the parent node transferred ? // while(currentAssoc != null && exInvaders != null && exInvaders.size() > 0)
*/ // {
if(nodeService.hasAspect(parentNodeRef, TransferModel.ASPECT_TRANSFERRED)) // NodeRef parentNodeRef = currentAssoc.getParentRef();
{ // NodeRef currentNodeRef = currentAssoc.getChildRef();
log.debug("parent node was transferred - check siblings"); //
// /**
/** // * Does the parent have alien invaders ?
* Check the siblings of this node to see whether there are any other alien nodes. // */
*/ // if(nodeService.hasAspect(parentNodeRef, TransferModel.ASPECT_ALIEN))
// nodeService.getChildAssocs(parentNodeRef); // {
// BUGBUG Code not complete // log.debug("parent node is alien - check siblings");
ref = null; //
// /**
// * For each invader of the deletedNode
// */
// Iterator<String> 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<ChildAssociationRef> refs = nodeService.getChildAssocs(parentNodeRef);
//
// for(ChildAssociationRef ref : refs)
// {
// NodeRef childRef = ref.getChildRef();
// List<String>invadedBy = (List<String>)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<String> parentInvaders = (List<String>)nodeService.getProperty(parentNodeRef, TransferModel.PROP_INVADED_BY);
//
// final List<String> newInvaders = new ArrayList<String>(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<Void> actionRunAs = new RunAsWork<Void>()
// {
// 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
} }
else
// /**
// * 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)
// {
// List<String>invadedBy = (List<String>)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<String> invadedBy = (List<String>)nodeService.getProperty(newAlien,
// TransferModel.PROP_INVADED_BY);
//
// if(invadedBy == null)
// {
// nodeService.setProperty(newAlien, TransferModel.PROP_ALIEN, Boolean.TRUE);
// invadedBy = new ArrayList<String>(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)
{ {
log.debug("parent is not a transferred node"); this.descriptorService = descriptorService;
ref = null;
} }
public DescriptorService getDescriptorService()
{
return descriptorService;
} }
public void setAlienProcessor(AlienProcessor alienProcessor)
{
this.alienProcessor = alienProcessor;
}
public AlienProcessor getAlienProcessor()
{
return alienProcessor;
} }
} }

View File

@@ -39,7 +39,14 @@ public interface TransferModel
static final QName ASPECT_TRANSFERRED = QName.createQName(TRANSFER_MODEL_1_0_URI, "transferred"); 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_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"); 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"); static final QName PROP_ALIEN = QName.createQName(TRANSFER_MODEL_1_0_URI, "alien");
/* /*
* Type : Transfer Group * Type : Transfer Group
*/ */
@@ -73,7 +80,6 @@ public interface TransferModel
static final QName PROP_TRANSFER_STATUS = QName.createQName(TRANSFER_MODEL_1_0_URI, "transferStatus"); 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"); static final QName PROP_TRANSFER_ERROR = QName.createQName(TRANSFER_MODEL_1_0_URI, "transferError");
/* /*
* Type : Transfer report * Type : Transfer report
*/ */