Search in sources :

Example 1 with VisitedNode

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;
    }
}
Also used : Serializable(java.io.Serializable) HashMap(java.util.HashMap) QName(org.alfresco.service.namespace.QName) NodeExistsException(org.alfresco.repo.domain.node.NodeExistsException) VisitedNode(org.alfresco.repo.node.db.NodeHierarchyWalker.VisitedNode) Node(org.alfresco.repo.domain.node.Node) AccessPermission(org.alfresco.service.cmr.security.AccessPermission) ChildAssociationRef(org.alfresco.service.cmr.repository.ChildAssociationRef) Date(java.util.Date) NodeRef(org.alfresco.service.cmr.repository.NodeRef) ChildAssocEntity(org.alfresco.repo.domain.node.ChildAssocEntity) VisitedNode(org.alfresco.repo.node.db.NodeHierarchyWalker.VisitedNode) Pair(org.alfresco.util.Pair)

Example 2 with VisitedNode

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());
}
Also used : NodeRef(org.alfresco.service.cmr.repository.NodeRef) NodeHierarchyWalker(org.alfresco.repo.node.db.NodeHierarchyWalker) VisitedNode(org.alfresco.repo.node.db.NodeHierarchyWalker.VisitedNode) Pair(org.alfresco.util.Pair) CannedQueryDAOTest(org.alfresco.repo.domain.query.CannedQueryDAOTest) Test(org.junit.Test)

Example 3 with VisitedNode

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);
}
Also used : StoreRef(org.alfresco.service.cmr.repository.StoreRef) QName(org.alfresco.service.namespace.QName) AspectDefinition(org.alfresco.service.cmr.dictionary.AspectDefinition) ChildAssociationRef(org.alfresco.service.cmr.repository.ChildAssociationRef) ChildAssociationRef(org.alfresco.service.cmr.repository.ChildAssociationRef) AssociationRef(org.alfresco.service.cmr.repository.AssociationRef) TypeDefinition(org.alfresco.service.cmr.dictionary.TypeDefinition) NodeRef(org.alfresco.service.cmr.repository.NodeRef) VisitedNode(org.alfresco.repo.node.db.NodeHierarchyWalker.VisitedNode) Iterator(java.util.Iterator) HashSet(java.util.HashSet)

Aggregations

VisitedNode (org.alfresco.repo.node.db.NodeHierarchyWalker.VisitedNode)3 NodeRef (org.alfresco.service.cmr.repository.NodeRef)3 ChildAssociationRef (org.alfresco.service.cmr.repository.ChildAssociationRef)2 QName (org.alfresco.service.namespace.QName)2 Pair (org.alfresco.util.Pair)2 Serializable (java.io.Serializable)1 Date (java.util.Date)1 HashMap (java.util.HashMap)1 HashSet (java.util.HashSet)1 Iterator (java.util.Iterator)1 ChildAssocEntity (org.alfresco.repo.domain.node.ChildAssocEntity)1 Node (org.alfresco.repo.domain.node.Node)1 NodeExistsException (org.alfresco.repo.domain.node.NodeExistsException)1 CannedQueryDAOTest (org.alfresco.repo.domain.query.CannedQueryDAOTest)1 NodeHierarchyWalker (org.alfresco.repo.node.db.NodeHierarchyWalker)1 AspectDefinition (org.alfresco.service.cmr.dictionary.AspectDefinition)1 TypeDefinition (org.alfresco.service.cmr.dictionary.TypeDefinition)1 AssociationRef (org.alfresco.service.cmr.repository.AssociationRef)1 StoreRef (org.alfresco.service.cmr.repository.StoreRef)1 AccessPermission (org.alfresco.service.cmr.security.AccessPermission)1