use of org.alfresco.repo.node.db.NodeHierarchyWalker.VisitedNode in project alfresco-repository by Alfresco.
the class DbNodeServiceImpl method archiveHierarchyImpl.
/**
* Archive (direct copy) a node hierarchy
*
* @param walker the node hierarchy to archive
* @param archiveStoreRef StoreRef
*/
private void archiveHierarchyImpl(NodeHierarchyWalker walker, StoreRef archiveStoreRef) {
// Start with the node we are archiving to
Pair<Long, NodeRef> archiveStoreRootNodePair = nodeDAO.getRootNode(archiveStoreRef);
// Work through the hierarchy from the top down and archive all the nodes
boolean firstNode = true;
Map<Long, Pair<Long, NodeRef>> archiveRecord = new HashMap<Long, Pair<Long, NodeRef>>(walker.getNodes(false).size() * 2);
for (VisitedNode node : walker.getNodes(false)) {
// Get node metadata
Map<QName, Serializable> archiveProperties = nodeDAO.getNodeProperties(node.id);
Set<QName> archiveAspects = nodeDAO.getNodeAspects(node.id);
// The first node gets special treatment as it contains the archival details
ChildAssociationRef archivePrimaryParentAssocRef = null;
final Pair<Long, NodeRef> archiveParentNodePair;
if (firstNode) {
// Attach top-level archival details
ChildAssociationRef primaryParentAssocRef = node.primaryParentAssocPair.getSecond();
archiveAspects.add(ContentModel.ASPECT_ARCHIVED);
archiveProperties.put(ContentModel.PROP_ARCHIVED_BY, AuthenticationUtil.getFullyAuthenticatedUser());
archiveProperties.put(ContentModel.PROP_ARCHIVED_DATE, new Date());
archiveProperties.put(ContentModel.PROP_ARCHIVED_ORIGINAL_PARENT_ASSOC, primaryParentAssocRef);
Serializable originalOwner = archiveProperties.get(ContentModel.PROP_OWNER);
archiveProperties.put(ContentModel.PROP_ARCHIVED_ORIGINAL_OWNER, originalOwner != null ? originalOwner : OwnableService.NO_OWNER);
// change the node ownership
archiveAspects.add(ContentModel.ASPECT_OWNABLE);
archiveProperties.put(ContentModel.PROP_OWNER, AuthenticationUtil.getFullyAuthenticatedUser());
// Create new primary association
archivePrimaryParentAssocRef = new ChildAssociationRef(ContentModel.ASSOC_CHILDREN, archiveStoreRootNodePair.getSecond(), NodeArchiveService.QNAME_ARCHIVED_ITEM, new NodeRef(archiveStoreRef, node.nodeRef.getId()), true, -1);
archiveParentNodePair = archiveStoreRootNodePair;
} else {
ChildAssociationRef primaryParentAssocRef = node.primaryParentAssocPair.getSecond();
NodeRef parentNodeRef = primaryParentAssocRef.getParentRef();
// Look it up
VisitedNode parentNode = walker.getNode(parentNodeRef);
if (parentNode == null) {
throw new IllegalStateException("Expected that a child has a visited primary parent: " + primaryParentAssocRef);
}
// This needs to have been mapped to a new parent
archiveParentNodePair = archiveRecord.get(parentNode.id);
if (archiveParentNodePair == null) {
throw new IllegalStateException("Expected to have archived primary parent: " + primaryParentAssocRef);
}
// Build the primary association details
archivePrimaryParentAssocRef = new ChildAssociationRef(primaryParentAssocRef.getTypeQName(), archiveParentNodePair.getSecond(), primaryParentAssocRef.getQName(), new NodeRef(archiveStoreRef, node.nodeRef.getId()), true, primaryParentAssocRef.getNthSibling());
}
// Invoke behaviours
invokeBeforeCreateNode(archivePrimaryParentAssocRef.getParentRef(), archivePrimaryParentAssocRef.getTypeQName(), archivePrimaryParentAssocRef.getQName(), node.nodeType);
// Create a new node
boolean attempted = false;
Node archiveNode = null;
while (true) {
try {
ChildAssocEntity archiveChildAssocEntity = nodeDAO.newNode(archiveParentNodePair.getFirst(), archivePrimaryParentAssocRef.getTypeQName(), archivePrimaryParentAssocRef.getQName(), archiveStoreRef, node.nodeRef.getId(), node.nodeType, (Locale) archiveProperties.get(ContentModel.PROP_LOCALE), (String) archiveProperties.get(ContentModel.PROP_NAME), archiveProperties);
archiveNode = archiveChildAssocEntity.getChildNode();
// Store the archive mapping for this node
archiveRecord.put(node.id, archiveNode.getNodePair());
break;
} catch (NodeExistsException e) {
if (!attempted) {
// There is a conflict, so delete the currently-archived node
NodeRef conflictingNodeRef = e.getNodePair().getSecond();
deleteNode(conflictingNodeRef);
attempted = true;
} else {
throw e;
}
}
}
// Carry any explicit permissions over to the new node
Set<AccessPermission> originalNodePermissions = permissionService.getAllSetPermissions(node.nodeRef);
for (AccessPermission originalPermission : originalNodePermissions) {
if (originalPermission.isInherited()) {
// Ignore inherited permissions
continue;
}
NodeRef archiveNodeRef = archiveNode.getNodeRef();
permissionService.setPermission(archiveNodeRef, originalPermission.getAuthority(), originalPermission.getPermission(), originalPermission.getAccessStatus() == AccessStatus.ALLOWED);
}
// Check if it inherits permissions or not
if (!permissionService.getInheritParentPermissions(node.nodeRef)) {
permissionService.setInheritParentPermissions(archiveNode.getNodeRef(), false);
}
// Add properties and aspects
Long archiveNodeId = archiveNode.getId();
NodeRef archiveNodeRef = archiveNode.getNodeRef();
nodeDAO.addNodeAspects(archiveNodeId, archiveAspects);
nodeDAO.addNodeProperties(archiveNodeId, archiveProperties);
// username is linked to the document
if (firstNode) {
// Attach archiveRoot aspect to root
// TODO: In time, this can be moved into a patch
Long archiveStoreRootNodeId = archiveStoreRootNodePair.getFirst();
NodeRef archiveStoreRootNodeRef = archiveStoreRootNodePair.getSecond();
if (!nodeDAO.hasNodeAspect(archiveStoreRootNodeId, ContentModel.ASPECT_ARCHIVE_ROOT)) {
addAspect(archiveStoreRootNodeRef, ContentModel.ASPECT_ARCHIVE_ROOT, null);
}
// Ensure that the user has a folder for archival
String username = AuthenticationUtil.getFullyAuthenticatedUser();
if (username == null) {
username = AuthenticationUtil.getAdminUserName();
}
Pair<Long, ChildAssociationRef> userArchiveAssocPair = nodeDAO.getChildAssoc(archiveStoreRootNodeId, ContentModel.ASSOC_ARCHIVE_USER_LINK, username);
NodeRef userArchiveNodeRef = null;
if (userArchiveAssocPair == null) {
// User has no node entry. Create a new one.
QName archiveUserAssocQName = QName.createQName(NamespaceService.CONTENT_MODEL_1_0_URI, QName.createValidLocalName(username));
Map<QName, Serializable> userArchiveNodeProps = Collections.singletonMap(ContentModel.PROP_NAME, (Serializable) username);
userArchiveNodeRef = createNode(archiveStoreRootNodeRef, ContentModel.ASSOC_ARCHIVE_USER_LINK, archiveUserAssocQName, ContentModel.TYPE_ARCHIVE_USER, userArchiveNodeProps).getChildRef();
} else {
userArchiveNodeRef = userArchiveAssocPair.getSecond().getChildRef();
}
// Link user node to archived item via secondary child association
String archiveNodeName = (String) archiveProperties.get(ContentModel.PROP_NAME);
if (archiveNodeName == null) {
archiveNodeName = archiveNodeRef.getId();
}
QName archiveAssocQName = QName.createQNameWithValidLocalName(NamespaceService.SYSTEM_MODEL_1_0_URI, archiveNodeName);
addChild(userArchiveNodeRef, archiveNodeRef, ContentModel.ASSOC_ARCHIVED_LINK, archiveAssocQName);
}
// Invoke behaviours
invokeOnCreateNode(archivePrimaryParentAssocRef);
firstNode = false;
}
}
use of org.alfresco.repo.node.db.NodeHierarchyWalker.VisitedNode in project alfresco-repository by Alfresco.
the class NodeServiceTest method testNodeHierarchyWalker.
/**
* @see NodeHierarchyWalker
*/
@Test
public void testNodeHierarchyWalker() throws Exception {
final NodeRef workspaceRootNodeRef = nodeService.getRootNode(StoreRef.STORE_REF_WORKSPACE_SPACESSTORE);
final NodeRef[] nodes = new NodeRef[6];
buildNodeHierarchy(workspaceRootNodeRef, nodes);
// Hook up some associations
nodeService.addAspect(nodes[1], ContentModel.ASPECT_COPIEDFROM, null);
// Peer n1-n0
nodeService.createAssociation(nodes[1], nodes[0], ContentModel.ASSOC_ORIGINAL);
// Secondary child n0-n2
nodeService.addChild(nodes[0], nodes[2], ContentModel.ASSOC_CONTAINS, QName.createQName(NamespaceService.ALFRESCO_URI, "testNodeHierarchyWalker"));
// Walk the hierarchy
NodeHierarchyWalker walker = txnService.getRetryingTransactionHelper().doInTransaction(new RetryingTransactionCallback<NodeHierarchyWalker>() {
@Override
public NodeHierarchyWalker execute() throws Throwable {
Pair<Long, NodeRef> parentNodePair = nodeDAO.getNodePair(nodes[0]);
Pair<Long, ChildAssociationRef> parentAssocPair = nodeDAO.getPrimaryParentAssoc(parentNodePair.getFirst());
NodeHierarchyWalker walker = new NodeHierarchyWalker(nodeDAO);
walker.walkHierarchy(parentNodePair, parentAssocPair);
return walker;
}
}, true);
List<VisitedNode> nodesLeafFirst = walker.getNodes(true);
assertEquals("Unexpected number of nodes visited", 6, nodesLeafFirst.size());
assertEquals("Incorrect order ", nodesLeafFirst.get(0).nodeRef, nodes[5]);
assertEquals("Incorrect order ", nodesLeafFirst.get(5).nodeRef, nodes[0]);
List<VisitedNode> nodesParentFirst = walker.getNodes(false);
assertEquals("Unexpected number of nodes visited", 6, nodesParentFirst.size());
assertEquals("Incorrect order ", nodesParentFirst.get(0).nodeRef, nodes[0]);
assertEquals("Incorrect order ", nodesParentFirst.get(5).nodeRef, nodes[5]);
// Check primary parent links
assertEquals(workspaceRootNodeRef, nodesParentFirst.get(0).primaryParentAssocPair.getSecond().getParentRef());
assertEquals(nodes[0], nodesParentFirst.get(1).primaryParentAssocPair.getSecond().getParentRef());
assertEquals(nodes[4], nodesParentFirst.get(5).primaryParentAssocPair.getSecond().getParentRef());
// Check secondary parent links
assertEquals(0, nodesParentFirst.get(0).secondaryParentAssocs.size());
assertEquals(nodes[0], nodesParentFirst.get(2).secondaryParentAssocs.get(0).getSecond().getParentRef());
assertEquals(0, nodesParentFirst.get(1).secondaryParentAssocs.size());
assertEquals(1, nodesParentFirst.get(2).secondaryParentAssocs.size());
assertEquals(0, nodesParentFirst.get(3).secondaryParentAssocs.size());
// Check secondary child links
assertEquals(1, nodesParentFirst.get(0).secondaryChildAssocs.size());
assertEquals(nodes[2], nodesParentFirst.get(0).secondaryChildAssocs.get(0).getSecond().getChildRef());
assertEquals(0, nodesParentFirst.get(1).secondaryChildAssocs.size());
// Check target assocs
assertEquals(0, nodesParentFirst.get(0).targetAssocs.size());
assertEquals(1, nodesParentFirst.get(1).targetAssocs.size());
assertEquals(nodes[0], nodesParentFirst.get(1).targetAssocs.get(0).getSecond().getTargetRef());
// Check source assocs
assertEquals(1, nodesParentFirst.get(0).sourceAssocs.size());
assertEquals(nodes[1], nodesParentFirst.get(0).sourceAssocs.get(0).getSecond().getSourceRef());
assertEquals(0, nodesParentFirst.get(1).sourceAssocs.size());
}
use of org.alfresco.repo.node.db.NodeHierarchyWalker.VisitedNode in project alfresco-repository by Alfresco.
the class DbNodeServiceImpl method deleteNode.
/**
* Delete a node
*
* @param nodeRef the node to delete
* @param allowArchival <tt>true</tt> if normal archival may occur or
* <tt>false</tt> if the node must be forcibly deleted
*/
private void deleteNode(NodeRef nodeRef, boolean allowArchival) {
// The node(s) involved may not be pending deletion
checkPendingDelete(nodeRef);
// Pair contains NodeId, NodeRef
Pair<Long, NodeRef> nodePair = getNodePairNotNull(nodeRef);
Long nodeId = nodePair.getFirst();
Boolean requiresDelete = null;
// get type and aspect QNames as they will be unavailable after the delete
QName nodeTypeQName = nodeDAO.getNodeType(nodeId);
Set<QName> nodeAspectQNames = nodeDAO.getNodeAspects(nodeId);
// Have we been asked to delete a store?
if (nodeTypeQName.equals(ContentModel.TYPE_STOREROOT)) {
throw new IllegalArgumentException("A store root node cannot be deleted: " + nodeRef);
}
// get the primary parent-child relationship before it is gone
Pair<Long, ChildAssociationRef> childAssocPair = nodeDAO.getPrimaryParentAssoc(nodeId);
ChildAssociationRef childAssocRef = childAssocPair.getSecond();
// Is this store
StoreRef storeRef = nodeRef.getStoreRef();
StoreRef archiveStoreRef = storeArchiveMap.get(storeRef);
// Gather information about the hierarchy
NodeHierarchyWalker walker = new NodeHierarchyWalker(nodeDAO);
walker.walkHierarchy(nodePair, childAssocPair);
// Protect the nodes from being link/unlinked for the remainder of the process
Set<NodeRef> nodesPendingDelete = new HashSet<NodeRef>(walker.getNodes(false).size());
for (VisitedNode visitedNode : walker.getNodes(true)) {
nodesPendingDelete.add(visitedNode.nodeRef);
}
Set<NodeRef> nodesPendingDeleteTxn = TransactionalResourceHelper.getSet(KEY_PENDING_DELETE_NODES);
// We need to remove these later, again
nodesPendingDeleteTxn.addAll(nodesPendingDelete);
// Work out whether we need to archive or delete the node.
if (!allowArchival) {
// No archival allowed
requiresDelete = true;
} else if (archiveStoreRef == null) {
// The store does not specify archiving
requiresDelete = true;
} else {
// get the type and check if we need archiving.
TypeDefinition typeDef = dictionaryService.getType(nodeTypeQName);
if (typeDef != null) {
Boolean requiresArchive = typeDef.getArchive();
if (requiresArchive != null) {
requiresDelete = !requiresArchive;
}
}
// If the type hasn't asked for deletion, check whether any applied aspects have
Iterator<QName> i = nodeAspectQNames.iterator();
while ((requiresDelete == null || !requiresDelete) && i.hasNext()) {
QName nodeAspectQName = i.next();
AspectDefinition aspectDef = dictionaryService.getAspect(nodeAspectQName);
if (aspectDef != null) {
Boolean requiresArchive = aspectDef.getArchive();
if (requiresArchive != null) {
requiresDelete = !requiresArchive;
}
}
}
}
// Propagate timestamps
propagateTimeStamps(childAssocRef);
// Archive, if necessary
boolean archive = requiresDelete != null && !requiresDelete.booleanValue();
// Fire pre-delete events
// Prevents duplicate firing
Set<Long> childAssocIds = new HashSet<Long>(23);
// Prevents duplicate firing
Set<Long> peerAssocIds = new HashSet<Long>(23);
List<VisitedNode> nodesToDelete = walker.getNodes(true);
for (VisitedNode nodeToDelete : nodesToDelete) {
// Target associations
for (Pair<Long, AssociationRef> targetAssocPair : nodeToDelete.targetAssocs) {
if (!peerAssocIds.add(targetAssocPair.getFirst())) {
// Already fired
continue;
}
invokeBeforeDeleteAssociation(targetAssocPair.getSecond());
}
// Source associations
for (Pair<Long, AssociationRef> sourceAssocPair : nodeToDelete.sourceAssocs) {
if (!peerAssocIds.add(sourceAssocPair.getFirst())) {
// Already fired
continue;
}
invokeBeforeDeleteAssociation(sourceAssocPair.getSecond());
}
// Secondary child associations
for (Pair<Long, ChildAssociationRef> secondaryChildAssocPair : nodeToDelete.secondaryChildAssocs) {
if (!childAssocIds.add(secondaryChildAssocPair.getFirst())) {
// Already fired
continue;
}
invokeBeforeDeleteChildAssociation(secondaryChildAssocPair.getSecond());
}
// Secondary parent associations
for (Pair<Long, ChildAssociationRef> secondaryParentAssocPair : nodeToDelete.secondaryParentAssocs) {
if (!childAssocIds.add(secondaryParentAssocPair.getFirst())) {
// Already fired
continue;
}
invokeBeforeDeleteChildAssociation(secondaryParentAssocPair.getSecond());
}
// Primary child associations
if (archive) {
invokeBeforeArchiveNode(nodeToDelete.nodeRef);
}
invokeBeforeDeleteNode(nodeToDelete.nodeRef);
}
// Archive, if necessary
if (archive) {
// Archive node
archiveHierarchy(walker, archiveStoreRef);
}
// Delete/Archive and fire post-delete events incl. updating indexes
// Prevents duplicate firing
childAssocIds.clear();
// Prevents duplicate firing
peerAssocIds.clear();
for (VisitedNode nodeToDelete : nodesToDelete) {
// Target associations
for (Pair<Long, AssociationRef> targetAssocPair : nodeToDelete.targetAssocs) {
if (!peerAssocIds.add(targetAssocPair.getFirst())) {
// Already fired
continue;
}
nodeDAO.removeNodeAssocs(Collections.singletonList(targetAssocPair.getFirst()));
invokeOnDeleteAssociation(targetAssocPair.getSecond());
}
// Source associations
for (Pair<Long, AssociationRef> sourceAssocPair : nodeToDelete.sourceAssocs) {
if (!peerAssocIds.add(sourceAssocPair.getFirst())) {
// Already fired
continue;
}
nodeDAO.removeNodeAssocs(Collections.singletonList(sourceAssocPair.getFirst()));
invokeOnDeleteAssociation(sourceAssocPair.getSecond());
}
// Secondary child associations
for (Pair<Long, ChildAssociationRef> secondaryChildAssocPair : nodeToDelete.secondaryChildAssocs) {
if (!childAssocIds.add(secondaryChildAssocPair.getFirst())) {
// Already fired
continue;
}
nodeDAO.deleteChildAssoc(secondaryChildAssocPair.getFirst());
invokeOnDeleteChildAssociation(secondaryChildAssocPair.getSecond());
}
// Secondary parent associations
for (Pair<Long, ChildAssociationRef> secondaryParentAssocPair : nodeToDelete.secondaryParentAssocs) {
if (!childAssocIds.add(secondaryParentAssocPair.getFirst())) {
// Already fired
continue;
}
nodeDAO.deleteChildAssoc(secondaryParentAssocPair.getFirst());
invokeOnDeleteChildAssociation(secondaryParentAssocPair.getSecond());
}
QName childNodeTypeQName = nodeDAO.getNodeType(nodeToDelete.id);
Set<QName> childAspectQnames = nodeDAO.getNodeAspects(nodeToDelete.id);
// Delete the node
nodeDAO.deleteChildAssoc(nodeToDelete.primaryParentAssocPair.getFirst());
nodeDAO.deleteNode(nodeToDelete.id);
invokeOnDeleteNode(nodeToDelete.primaryParentAssocPair.getSecond(), childNodeTypeQName, childAspectQnames, archive);
}
// Clear out the list of nodes pending delete
nodesPendingDeleteTxn = TransactionalResourceHelper.getSet(KEY_PENDING_DELETE_NODES);
nodesPendingDeleteTxn.removeAll(nodesPendingDelete);
}
Aggregations