From 75359375b8dc3cc940805853bd06a415562d8e7f Mon Sep 17 00:00:00 2001 From: Derek Hulley Date: Tue, 2 Jul 2013 16:21:08 +0000 Subject: [PATCH] Merged DEV to HEAD 51998: Deleting a store is now functionally the same as deleting a node but without any option of archival - ALF-19153: CLOUD-1685, CLOUD-1827 and CLOUD-1828 - While cleaning Cloud test data tenants are deleted, which leads to their stores being deleted. - NodeService.deleteStore was not firing policies for every node in the store; several bits of pre- and post-delete code were therefore not being called, which lead to secondary data (caches, attributes, etc) being left lying around. - A side effect of this fix was that the sys:undeletable aspect has to be disabled during account deletion git-svn-id: https://svn.alfresco.com/repos/alfresco-enterprise/alfresco/HEAD/root@52007 c4b6b30b-aa2e-2d43-bbcb-ca4b014f7261 --- .../node-common-SqlMap.xml | 8 ++++ .../repo/domain/node/AbstractNodeDAOImpl.java | 6 +++ .../repo/domain/node/ibatis/NodeDAOImpl.java | 10 ++++ .../repo/node/BaseNodeServiceTest.java | 7 +++ .../repo/node/db/DbNodeServiceImpl.java | 46 +++++++++++++++++-- 5 files changed, 72 insertions(+), 5 deletions(-) diff --git a/config/alfresco/ibatis/org.hibernate.dialect.Dialect/node-common-SqlMap.xml b/config/alfresco/ibatis/org.hibernate.dialect.Dialect/node-common-SqlMap.xml index 3a80648bb6..29789aca16 100644 --- a/config/alfresco/ibatis/org.hibernate.dialect.Dialect/node-common-SqlMap.xml +++ b/config/alfresco/ibatis/org.hibernate.dialect.Dialect/node-common-SqlMap.xml @@ -384,6 +384,14 @@ id = #{id} + + update alf_node set + version = version + 1, + transaction_id = #{idOne} + where + store_id = #{idTwo} + + update alf_node set version = #{version} diff --git a/source/java/org/alfresco/repo/domain/node/AbstractNodeDAOImpl.java b/source/java/org/alfresco/repo/domain/node/AbstractNodeDAOImpl.java index c2b58f982f..e1b807d0a3 100644 --- a/source/java/org/alfresco/repo/domain/node/AbstractNodeDAOImpl.java +++ b/source/java/org/alfresco/repo/domain/node/AbstractNodeDAOImpl.java @@ -850,6 +850,11 @@ public abstract class AbstractNodeDAOImpl implements NodeDAO, BatchingDAO { throw new ConcurrencyFailureException("Store not updated: " + oldStoreRef); } + // Bring all the associated nodes into the current transaction + Long txnId = getCurrentTransaction().getId(); + Long storeId = store.getId(); + updateNodesInStore(txnId, storeId); + // All the NodeRef-based caches are invalid. ID-based caches are fine. rootNodesCache.removeByKey(oldStoreRef); allRootNodesCache.remove(oldStoreRef); @@ -4827,6 +4832,7 @@ public abstract class AbstractNodeDAOImpl implements NodeDAO, BatchingDAO protected abstract Long insertStore(StoreEntity store); protected abstract int updateStoreRoot(StoreEntity store); protected abstract int updateStore(StoreEntity store); + protected abstract int updateNodesInStore(Long txnId, Long storeId); protected abstract Long insertNode(NodeEntity node); protected abstract int updateNode(NodeUpdateEntity nodeUpdate); protected abstract int updateNodes(Long txnId, List nodeIds); diff --git a/source/java/org/alfresco/repo/domain/node/ibatis/NodeDAOImpl.java b/source/java/org/alfresco/repo/domain/node/ibatis/NodeDAOImpl.java index 5f2e55db1d..6efc1c4d25 100644 --- a/source/java/org/alfresco/repo/domain/node/ibatis/NodeDAOImpl.java +++ b/source/java/org/alfresco/repo/domain/node/ibatis/NodeDAOImpl.java @@ -81,6 +81,7 @@ public class NodeDAOImpl extends AbstractNodeDAOImpl private static final String INSERT_STORE = "alfresco.node.insert.insert_Store"; private static final String UPDATE_STORE_ROOT = "alfresco.node.update_StoreRoot"; private static final String UPDATE_STORE = "alfresco.node.update_Store"; + private static final String UPDATE_NODES_IN_STORE = "alfresco.node.update_NodesInStore"; private static final String SELECT_STORE_ALL = "alfresco.node.select_StoreAll"; private static final String SELECT_STORE_BY_REF = "alfresco.node.select_StoreByRef"; private static final String SELECT_STORE_ROOT_NODE_BY_REF = "alfresco.node.select_StoreRootNodeByRef"; @@ -310,6 +311,15 @@ public class NodeDAOImpl extends AbstractNodeDAOImpl return template.update(UPDATE_STORE, store); } + @Override + protected int updateNodesInStore(Long txnId, Long storeId) + { + IdsEntity ids = new IdsEntity(); + ids.setIdOne(txnId); + ids.setIdTwo(storeId); + return template.update(UPDATE_NODES_IN_STORE, ids); + } + @Override protected Long insertNode(NodeEntity node) { diff --git a/source/java/org/alfresco/repo/node/BaseNodeServiceTest.java b/source/java/org/alfresco/repo/node/BaseNodeServiceTest.java index 7ef5240790..60dce4d853 100644 --- a/source/java/org/alfresco/repo/node/BaseNodeServiceTest.java +++ b/source/java/org/alfresco/repo/node/BaseNodeServiceTest.java @@ -485,6 +485,10 @@ public abstract class BaseNodeServiceTest extends BaseSpringTest List storeRefs = nodeService.getStores(); // check that the store ref is present assertTrue("New store not present in list of stores", storeRefs.contains(storeRef)); + // Get the root node + NodeRef rootNodeRef = nodeService.getRootNode(storeRef); + assertTrue("Store should still exist", nodeService.exists(storeRef)); + assertTrue("Node should still exist", nodeService.exists(rootNodeRef)); // Delete it nodeService.deleteStore(storeRef); storeRefs = nodeService.getStores(); @@ -497,6 +501,9 @@ public abstract class BaseNodeServiceTest extends BaseSpringTest fail("NodeService should not have returned 'deleted' stores." + storeRefs); } } + // They should not exist as far as external code is concerned + assertFalse("Store should still exist", nodeService.exists(storeRef)); + assertFalse("Node should still exist", nodeService.exists(rootNodeRef)); // Commit to ensure all is well setComplete(); diff --git a/source/java/org/alfresco/repo/node/db/DbNodeServiceImpl.java b/source/java/org/alfresco/repo/node/db/DbNodeServiceImpl.java index 0eb04c2aa9..109419668d 100644 --- a/source/java/org/alfresco/repo/node/db/DbNodeServiceImpl.java +++ b/source/java/org/alfresco/repo/node/db/DbNodeServiceImpl.java @@ -263,7 +263,16 @@ public class DbNodeServiceImpl extends AbstractNodeServiceImpl { // Delete the index nodeIndexer.indexDeleteStore(storeRef); - // Rename the store + // Cannot delete the root node but we can delete, without archive, all immediate children + NodeRef rootNodeRef = nodeDAO.getRootNode(storeRef).getSecond(); + List childAssocRefs = getChildAssocs(rootNodeRef); + for (ChildAssociationRef childAssocRef : childAssocRefs) + { + NodeRef childNodeRef = childAssocRef.getChildRef(); + // We do NOT want to archive these, so mark them as temporary + deleteNode(childNodeRef, false); + } + // Rename the store. This takes all the nodes with it. StoreRef deletedStoreRef = new StoreRef(StoreRef.PROTOCOL_DELETED, GUID.generate()); nodeDAO.moveStore(storeRef, deletedStoreRef); @@ -1044,7 +1053,20 @@ public class DbNodeServiceImpl extends AbstractNodeServiceImpl /** * Delete Node */ + @Override public void deleteNode(NodeRef nodeRef) + { + deleteNode(nodeRef, true); + } + + /** + * Delete a node + * + * @param nodeRef the node to delete + * @param allowArchival true if normal archival may occur or + * false 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); @@ -1055,13 +1077,21 @@ public class DbNodeServiceImpl extends AbstractNodeServiceImpl Boolean requiresDelete = null; - // get the primary parent-child relationship before it is gone - Pair childAssocPair = nodeDAO.getPrimaryParentAssoc(nodeId); - ChildAssociationRef childAssocRef = childAssocPair.getSecond(); // get type and aspect QNames as they will be unavailable after the delete QName nodeTypeQName = nodeDAO.getNodeType(nodeId); Set 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 childAssocPair = nodeDAO.getPrimaryParentAssoc(nodeId); + ChildAssociationRef childAssocRef = childAssocPair.getSecond(); + + // Is this store StoreRef storeRef = nodeRef.getStoreRef(); StoreRef archiveStoreRef = storeArchiveMap.get(storeRef); @@ -1079,7 +1109,12 @@ public class DbNodeServiceImpl extends AbstractNodeServiceImpl nodesPendingDeleteTxn.addAll(nodesPendingDelete); // We need to remove these later, again // Work out whether we need to archive or delete the node. - if (archiveStoreRef == null) + if (!allowArchival) + { + // No archival allowed + requiresDelete = true; + } + else if (archiveStoreRef == null) { // The store does not specify archiving requiresDelete = true; @@ -1162,6 +1197,7 @@ public class DbNodeServiceImpl extends AbstractNodeServiceImpl } invokeBeforeDeleteChildAssociation(secondaryParentAssocPair.getSecond()); } + // Primary child associations if (archive) {