mirror of
https://github.com/Alfresco/alfresco-community-repo.git
synced 2025-08-07 17:49:17 +00:00
Merged V3.2 to HEAD
17294: Fix for ETHREEOH-3194 - It's impossible to find Blogs and Discussions by title. 17301: Activity Service fixes (ETHREEOH-1362 & ETHREEOH-1741) 17302: Fix for ETHREEOH-2849 JPG to GIF transformation fails when using imagemagick cmd line options listed in our wiki 17305: AVM - fix AVMStoreDescriptor ( creator/createDate) returned by getStores 17306: Fix for ETHREEOH-1578 - Incorrect behavior of Calendar in Month view ... 17318: Merged V3.1 to V3.2 17317: Fix for ETHREEOH-3236 It is impossible to change start location, everything is reverted to My Alfresco 17320: Merged V3.1 to V3.2 17287 - Fix for ETHREEOH-110- It is impossible to browse events by tags 17326: iBatis mapping fixes for AVM on Oracle (including ETHREEOH-3205) 17327: Merged V3.1 to V3.2 17324: Fix for ETHREEOH-2723 Script error appears when trying to edit Home Space Name for user 17329: Merged V3.1 to V3.2 17180: Merged V2.2 to V3.1 17164: Fixes for deletion of large hierarchies: (ETHREEOH-2161 and ETHREEOH-2650) 17179: (RECORD ONLY) Merged V3.1 to V2.2 ... 17330: Fix for SiteActivityTest failure (caused by earlier -ve test data) 17331: Merged V3.1 to V3.2 17190: Further fixes for ETHREEOH-2161: Delete process hangs when deleting large directory structure (with rules applied) 17207: Fix fallout from work on ETHREEOH-2161: Delete process hangs when deleting large directory structure (with rules applied) 17215: Added back firing of policies for archive stores 17351: Build fix check in to DBNodeService. Will be followed by full fix when available. 17353: Applied TransactionListenerAdapter ___________________________________________________________________ Modified: svn:mergeinfo Reverse-merged /alfresco/BRANCHES/V3.1:r13091 Merged /alfresco/BRANCHES/V2.2:r13089,13091,14190-14191,14199,14210,14216,14229,14655,14825,14869,17164,17179 Merged /alfresco/BRANCHES/V3.1:r17180,17190,17207,17215,17287,17317,17324 Merged /alfresco/BRANCHES/V3.2:r17294,17301-17302,17305-17306,17318,17320,17326-17327,17329-17331,17351,17353 git-svn-id: https://svn.alfresco.com/repos/alfresco-enterprise/alfresco/HEAD/root@18056 c4b6b30b-aa2e-2d43-bbcb-ca4b014f7261
This commit is contained in:
@@ -1008,54 +1008,92 @@ public abstract class BaseNodeServiceTest extends BaseSpringTest
|
||||
{
|
||||
private NodeService nodeService;
|
||||
private List<NodeRef> deletedNodeRefs;
|
||||
private List<NodeRef> beforeDeleteNodeRefs;
|
||||
|
||||
public BadOnDeleteNodePolicy(NodeService nodeService, List<NodeRef> deletedNodeRefs)
|
||||
private boolean onDeleteCreateChild = true;
|
||||
|
||||
public BadOnDeleteNodePolicy(NodeService nodeService,
|
||||
List<NodeRef> beforeDeleteNodeRefs,
|
||||
List<NodeRef> deletedNodeRefs)
|
||||
{
|
||||
|
||||
|
||||
this.nodeService = nodeService;
|
||||
this.beforeDeleteNodeRefs = beforeDeleteNodeRefs;
|
||||
this.deletedNodeRefs = deletedNodeRefs;
|
||||
}
|
||||
|
||||
public void beforeDeleteNode(NodeRef nodeRef)
|
||||
{
|
||||
// add a new child to the child, i.e. just before it is deleted
|
||||
ChildAssociationRef assocRef = nodeService.createNode(
|
||||
// add the child to the list
|
||||
beforeDeleteNodeRefs.add(nodeRef);
|
||||
|
||||
if(onDeleteCreateChild)
|
||||
{
|
||||
// add a new child to the child, i.e. just before it is deleted
|
||||
ChildAssociationRef assocRef = nodeService.createNode(
|
||||
nodeRef,
|
||||
ASSOC_TYPE_QNAME_TEST_CHILDREN,
|
||||
QName.createQName("pre-delete new child"),
|
||||
ContentModel.TYPE_CONTAINER);
|
||||
// set some child node properties
|
||||
nodeService.setProperty(nodeRef, PROP_QNAME_BOOLEAN_VALUE, "true");
|
||||
// add an aspect to the child
|
||||
nodeService.addAspect(nodeRef, ASPECT_QNAME_TEST_TITLED, null);
|
||||
// set some child node properties
|
||||
nodeService.setProperty(nodeRef, PROP_QNAME_BOOLEAN_VALUE, "true");
|
||||
// add an aspect to the child
|
||||
nodeService.addAspect(nodeRef, ASPECT_QNAME_TEST_TITLED, null);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public void onDeleteNode(ChildAssociationRef childAssocRef, boolean isArchivedNode)
|
||||
{
|
||||
// add the child to the list
|
||||
deletedNodeRefs.add(childAssocRef.getChildRef());
|
||||
// now perform some nasties on the node's parent, i.e. add a new child
|
||||
NodeRef parentRef = childAssocRef.getParentRef();
|
||||
NodeRef childRef = childAssocRef.getChildRef();
|
||||
ChildAssociationRef assocRef = nodeService.createNode(
|
||||
|
||||
if(onDeleteCreateChild)
|
||||
{
|
||||
// now perform some nasties on the node's parent, i.e. add a new child
|
||||
NodeRef parentRef = childAssocRef.getParentRef();
|
||||
NodeRef childRef = childAssocRef.getChildRef();
|
||||
ChildAssociationRef assocRef = nodeService.createNode(
|
||||
parentRef,
|
||||
ASSOC_TYPE_QNAME_TEST_CHILDREN,
|
||||
QName.createQName("post-delete new child"),
|
||||
ContentModel.TYPE_CONTAINER);
|
||||
}
|
||||
}
|
||||
|
||||
private void setOnDeleteCreateChild(boolean onDeleteCreateChild)
|
||||
{
|
||||
this.onDeleteCreateChild = onDeleteCreateChild;
|
||||
}
|
||||
|
||||
private boolean isOnDeleteCreateChild()
|
||||
{
|
||||
return onDeleteCreateChild;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public void testDelete() throws Exception
|
||||
{
|
||||
final List<NodeRef> beforeDeleteNodeRefs = new ArrayList<NodeRef>(5);
|
||||
final List<NodeRef> deletedNodeRefs = new ArrayList<NodeRef>(5);
|
||||
|
||||
NodeServicePolicies.OnDeleteNodePolicy policy = new BadOnDeleteNodePolicy(nodeService, deletedNodeRefs);
|
||||
BadOnDeleteNodePolicy nasty = new BadOnDeleteNodePolicy(nodeService, beforeDeleteNodeRefs, deletedNodeRefs);
|
||||
nasty.setOnDeleteCreateChild(false);
|
||||
NodeServicePolicies.OnDeleteNodePolicy policy = nasty;
|
||||
|
||||
// bind to listen to the deletion of a node
|
||||
policyComponent.bindClassBehaviour(
|
||||
QName.createQName(NamespaceService.ALFRESCO_URI, "onDeleteNode"),
|
||||
policy,
|
||||
new JavaBehaviour(policy, "onDeleteNode"));
|
||||
|
||||
policyComponent.bindClassBehaviour(
|
||||
QName.createQName(NamespaceService.ALFRESCO_URI, "beforeDeleteNode"),
|
||||
policy,
|
||||
new JavaBehaviour(policy, "beforeDeleteNode"));
|
||||
|
||||
// build the node and commit the node graph
|
||||
Map<QName, ChildAssociationRef> assocRefs = buildNodeGraph(nodeService, rootNodeRef);
|
||||
NodeRef n1Ref = assocRefs.get(QName.createQName(BaseNodeServiceTest.NAMESPACE, "root_p_n1")).getChildRef();
|
||||
@@ -1072,11 +1110,86 @@ public abstract class BaseNodeServiceTest extends BaseSpringTest
|
||||
assertEquals("Node not cascade deleted", 0, countNodesByReference(n6Ref));
|
||||
assertEquals("Node not cascade deleted", 0, countNodesByReference(n8Ref));
|
||||
|
||||
// check before delete delete policy has been called
|
||||
assertTrue("n1Ref before delete policy not called", beforeDeleteNodeRefs.contains(n1Ref));
|
||||
assertTrue("n3Ref before delete policy not called", beforeDeleteNodeRefs.contains(n3Ref));
|
||||
assertTrue("n6Ref before delete policy not called", beforeDeleteNodeRefs.contains(n6Ref));
|
||||
assertTrue("n8Ref before delete policy not called", beforeDeleteNodeRefs.contains(n8Ref));
|
||||
|
||||
// check delete policy has been called
|
||||
assertTrue("n1Ref delete policy not called", deletedNodeRefs.contains(n1Ref));
|
||||
assertTrue("n3Ref delete policy not called", deletedNodeRefs.contains(n3Ref));
|
||||
assertTrue("n6Ref delete policy not called", deletedNodeRefs.contains(n6Ref));
|
||||
assertTrue("n8Ref delete policy not called", deletedNodeRefs.contains(n8Ref));
|
||||
|
||||
// commit to check
|
||||
setComplete();
|
||||
endTransaction();
|
||||
}
|
||||
|
||||
// /**
|
||||
// * This test is similar to the test above but the delete policies do nasty stuff such as
|
||||
// * creating children of the soon to be deleted children.
|
||||
// *
|
||||
// * In particular, it verifies that we don't get stuck in an infinite loop.
|
||||
// * @throws Exception
|
||||
// */
|
||||
// public void testDeleteWithBadlyBehavedPolicies() throws Exception
|
||||
// {
|
||||
// try
|
||||
// {
|
||||
// final List<NodeRef> beforeDeleteNodeRefs = new ArrayList<NodeRef>(5);
|
||||
// final List<NodeRef> deletedNodeRefs = new ArrayList<NodeRef>(5);
|
||||
//
|
||||
// BadOnDeleteNodePolicy nasty = new BadOnDeleteNodePolicy(nodeService, beforeDeleteNodeRefs, deletedNodeRefs);
|
||||
// nasty.setOnDeleteCreateChild(true);
|
||||
// NodeServicePolicies.OnDeleteNodePolicy policy = nasty;
|
||||
//
|
||||
// // bind to listen to the deletion of a node
|
||||
// policyComponent.bindClassBehaviour(
|
||||
// QName.createQName(NamespaceService.ALFRESCO_URI, "onDeleteNode"),
|
||||
// policy,
|
||||
// new JavaBehaviour(policy, "onDeleteNode"));
|
||||
//
|
||||
// policyComponent.bindClassBehaviour(
|
||||
// QName.createQName(NamespaceService.ALFRESCO_URI, "beforeDeleteNode"),
|
||||
// policy,
|
||||
// new JavaBehaviour(policy, "beforeDeleteNode"));
|
||||
//
|
||||
// // build the node and commit the node graph
|
||||
// Map<QName, ChildAssociationRef> assocRefs = buildNodeGraph(nodeService, rootNodeRef);
|
||||
// NodeRef n1Ref = assocRefs.get(QName.createQName(BaseNodeServiceTest.NAMESPACE, "root_p_n1")).getChildRef();
|
||||
// NodeRef n3Ref = assocRefs.get(QName.createQName(BaseNodeServiceTest.NAMESPACE, "n1_p_n3")).getChildRef();
|
||||
// NodeRef n4Ref = assocRefs.get(QName.createQName(BaseNodeServiceTest.NAMESPACE, "n2_p_n4")).getChildRef();
|
||||
// NodeRef n6Ref = assocRefs.get(QName.createQName(BaseNodeServiceTest.NAMESPACE, "n3_p_n6")).getChildRef();
|
||||
// NodeRef n8Ref = assocRefs.get(QName.createQName(BaseNodeServiceTest.NAMESPACE, "n6_p_n8")).getChildRef();
|
||||
//
|
||||
// // delete n1
|
||||
// nodeService.deleteNode(n1Ref);
|
||||
//
|
||||
// // turn off nasty policy - may upset other tests
|
||||
// nasty.setOnDeleteCreateChild(false);
|
||||
//
|
||||
// // Just a cut down set of tests to validate that something has happened, the real point of the test is to see how
|
||||
// // the end of the transaction fails.
|
||||
//
|
||||
// assertEquals("Node not directly deleted", 0, countNodesByReference(n1Ref));
|
||||
// assertTrue("n1Ref before delete policy not called", beforeDeleteNodeRefs.contains(n1Ref));
|
||||
// assertTrue("n1Ref delete policy not called", deletedNodeRefs.contains(n1Ref));
|
||||
//
|
||||
// // commit to check
|
||||
// setComplete();
|
||||
// endTransaction();
|
||||
// fail("test has not detected orphan children");
|
||||
// }
|
||||
// catch (Exception e)
|
||||
// {
|
||||
// // We expect to get here with this test.
|
||||
// e.printStackTrace();
|
||||
// }
|
||||
// }
|
||||
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
private int countChildrenOfNode(NodeRef nodeRef)
|
||||
{
|
||||
|
@@ -42,12 +42,12 @@ import org.alfresco.model.ContentModel;
|
||||
import org.alfresco.repo.domain.Node;
|
||||
import org.alfresco.repo.node.AbstractNodeServiceImpl;
|
||||
import org.alfresco.repo.node.StoreArchiveMap;
|
||||
import org.alfresco.repo.node.cleanup.AbstractNodeCleanupWorker;
|
||||
import org.alfresco.repo.node.db.NodeDaoService.NodeRefQueryCallback;
|
||||
import org.alfresco.repo.node.index.NodeIndexer;
|
||||
import org.alfresco.repo.security.authentication.AuthenticationUtil;
|
||||
import org.alfresco.repo.transaction.RetryingTransactionHelper;
|
||||
import org.alfresco.repo.transaction.RetryingTransactionHelper.RetryingTransactionCallback;
|
||||
import org.alfresco.repo.transaction.AlfrescoTransactionSupport;
|
||||
import org.alfresco.repo.transaction.TransactionListener;
|
||||
import org.alfresco.repo.transaction.TransactionListenerAdapter;
|
||||
import org.alfresco.repo.transaction.TransactionalResourceHelper;
|
||||
import org.alfresco.service.cmr.dictionary.AspectDefinition;
|
||||
import org.alfresco.service.cmr.dictionary.AssociationDefinition;
|
||||
import org.alfresco.service.cmr.dictionary.ChildAssociationDefinition;
|
||||
@@ -94,13 +94,12 @@ public class DbNodeServiceImpl extends AbstractNodeServiceImpl
|
||||
private NodeDaoService nodeDaoService;
|
||||
private StoreArchiveMap storeArchiveMap;
|
||||
private NodeService avmNodeService;
|
||||
private NodeIndexer nodeIndexer;
|
||||
private boolean cascadeInTransaction;
|
||||
private NodeIndexer nodeIndexer;
|
||||
private final static String KEY_PRE_COMMIT_ADD_NODE = "DbNodeServiceImpl.PreCommitAddNode";
|
||||
|
||||
public DbNodeServiceImpl()
|
||||
{
|
||||
storeArchiveMap = new StoreArchiveMap(); // in case it is not set
|
||||
cascadeInTransaction = true;
|
||||
}
|
||||
|
||||
public void setNodeDaoService(NodeDaoService nodeDaoService)
|
||||
@@ -128,15 +127,11 @@ public class DbNodeServiceImpl extends AbstractNodeServiceImpl
|
||||
}
|
||||
|
||||
/**
|
||||
* Set whether store delete and archive operations must cascade to all children
|
||||
* in the same transaction.
|
||||
*
|
||||
* @param cascadeInTransaction <tt>true</tt> (default) to cascade during
|
||||
* delete and archive
|
||||
* @deprecated the functionality did not see wide enough usage to warrant the maintenance
|
||||
*/
|
||||
public void setCascadeInTransaction(boolean cascadeInTransaction)
|
||||
{
|
||||
this.cascadeInTransaction = cascadeInTransaction;
|
||||
logger.warn("NodeService property 'cascadeInTransaction' is no longer available.");
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -339,12 +334,81 @@ public class DbNodeServiceImpl extends AbstractNodeServiceImpl
|
||||
addMissingAspects(childNodePair, propertiesBefore, propertiesAfter);
|
||||
addMissingAspects(parentNodePair, assocTypeQName);
|
||||
|
||||
/**
|
||||
* track new node ref so we can validate its path.
|
||||
*
|
||||
* it may be valid now, but who knows what will happen between
|
||||
* now and commit!
|
||||
*/
|
||||
trackNewNodeRef(childAssocRef.getChildRef());
|
||||
|
||||
// Index
|
||||
nodeIndexer.indexCreateNode(childAssocRef);
|
||||
|
||||
// done
|
||||
return childAssocRef;
|
||||
}
|
||||
|
||||
/**
|
||||
* Track a new node ref so we can validate its path at commit time.
|
||||
*
|
||||
* It may have a valid path now, but who knows what will happen between
|
||||
* now and commit!
|
||||
*
|
||||
* @param newNodeRef the node to track
|
||||
*/
|
||||
private void trackNewNodeRef(NodeRef newNodeRef)
|
||||
{
|
||||
// // bind a pre-commit listener to validate any new node associations
|
||||
// Set<NodeRef> newNodes = TransactionalResourceHelper.getSet(KEY_PRE_COMMIT_ADD_NODE);
|
||||
// if (newNodes.size() == 0)
|
||||
// {
|
||||
// PreCommitNewNodeListener listener = new PreCommitNewNodeListener();
|
||||
// AlfrescoTransactionSupport.bindListener(listener);
|
||||
// }
|
||||
// newNodes.add(newNodeRef);
|
||||
}
|
||||
|
||||
/**
|
||||
* loose interest in tracking a node ref
|
||||
*
|
||||
* for example if its been deleted or moved
|
||||
* @param nodeRef the node ref to untrack
|
||||
*/
|
||||
private void untrackNodeRef(NodeRef nodeRef)
|
||||
{
|
||||
// Set<NodeRef> newNodes = TransactionalResourceHelper.getSet(KEY_PRE_COMMIT_ADD_NODE);
|
||||
// if (newNodes.size() > 0)
|
||||
// {
|
||||
// newNodes.remove(nodeRef);
|
||||
// }
|
||||
}
|
||||
|
||||
private class PreCommitNewNodeListener extends TransactionListenerAdapter
|
||||
{
|
||||
@Override
|
||||
public void beforeCommit(boolean readOnly)
|
||||
{
|
||||
if (readOnly)
|
||||
{
|
||||
return;
|
||||
}
|
||||
Set<NodeRef> nodeRefs = TransactionalResourceHelper.getSet(KEY_PRE_COMMIT_ADD_NODE);
|
||||
// for (NodeRef nodeRef : nodeRefs)
|
||||
// {
|
||||
// // Need to check for exists the node may be created
|
||||
// // and deleted within the same transaction
|
||||
// if(exists(nodeRef))
|
||||
// {
|
||||
// System.out.println("Checking bideRef " + nodeRef);
|
||||
// // Check that the primary path is valid for this node
|
||||
// getPaths(nodeRef, false);
|
||||
// }
|
||||
// }
|
||||
nodeRefs.clear();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Adds all the default aspects and properties required for the given type.
|
||||
@@ -798,10 +862,7 @@ public class DbNodeServiceImpl extends AbstractNodeServiceImpl
|
||||
invokeBeforeDeleteNode(nodeRef);
|
||||
|
||||
// Cascade delecte as required
|
||||
if (cascadeInTransaction)
|
||||
{
|
||||
deletePrimaryChildrenNotArchived(nodePair, true);
|
||||
}
|
||||
deletePrimaryChildrenNotArchived(nodePair);
|
||||
// perform a normal deletion
|
||||
nodeDaoService.deleteNode(nodeId);
|
||||
// Invoke policy behaviours
|
||||
@@ -820,17 +881,18 @@ public class DbNodeServiceImpl extends AbstractNodeServiceImpl
|
||||
*/
|
||||
archiveNode(nodeRef, archiveStoreRef);
|
||||
}
|
||||
|
||||
// remove the deleted node from the list of new nodes
|
||||
untrackNodeRef(nodeRef);
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* delete primary children - private method for deleteNode.
|
||||
*
|
||||
* recurses through children when deleting a node. Does not archive.
|
||||
*
|
||||
* @param nodePair
|
||||
* @param cascade
|
||||
*/
|
||||
private void deletePrimaryChildrenNotArchived(Pair<Long, NodeRef> nodePair, boolean cascade)
|
||||
private void deletePrimaryChildrenNotArchived(Pair<Long, NodeRef> nodePair)
|
||||
{
|
||||
Long nodeId = nodePair.getFirst();
|
||||
// Get the node's primary children
|
||||
@@ -859,10 +921,10 @@ public class DbNodeServiceImpl extends AbstractNodeServiceImpl
|
||||
};
|
||||
|
||||
// Get all the QNames to remove
|
||||
nodeDaoService.getPrimaryChildAssocs(nodeId, callback);
|
||||
// Each child must be deleted
|
||||
for (Pair<Long, NodeRef> childNodePair : childNodePairs)
|
||||
{
|
||||
nodeDaoService.getPrimaryChildAssocs(nodeId, callback);
|
||||
// Each child must be deleted
|
||||
for (Pair<Long, NodeRef> childNodePair : childNodePairs)
|
||||
{
|
||||
// Fire node policies. This ensures that each node in the hierarchy gets a notification fired.
|
||||
Long childNodeId = childNodePair.getFirst();
|
||||
NodeRef childNodeRef = childNodePair.getSecond();
|
||||
@@ -872,16 +934,16 @@ public class DbNodeServiceImpl extends AbstractNodeServiceImpl
|
||||
|
||||
invokeBeforeDeleteNode(childNodeRef);
|
||||
|
||||
// Cascade first, if required.
|
||||
// Cascade first
|
||||
// This ensures that the beforeDelete policy is fired for all nodes in the hierarchy before
|
||||
// the actual delete starts.
|
||||
if (cascade)
|
||||
{
|
||||
deletePrimaryChildrenNotArchived(childNodePair, true);
|
||||
}
|
||||
deletePrimaryChildrenNotArchived(childNodePair);
|
||||
// Delete the child
|
||||
nodeDaoService.deleteNode(childNodeId);
|
||||
invokeOnDeleteNode(childParentAssocRef, childNodeType, childNodeQNames, false);
|
||||
|
||||
// loose interest in tracking this node ref
|
||||
untrackNodeRef(childNodeRef);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2197,12 +2259,8 @@ public class DbNodeServiceImpl extends AbstractNodeServiceImpl
|
||||
invokeOnMoveNode(oldParentAssocRef, newParentAssocRef);
|
||||
}
|
||||
|
||||
// If we have to cascade in the transaction, then pull the children over to the new store
|
||||
if (cascadeInTransaction)
|
||||
{
|
||||
// Pull children to the new store
|
||||
pullNodeChildrenToSameStore(newNodeToMovePair, true, true);
|
||||
}
|
||||
// Pull children to the new store
|
||||
pullNodeChildrenToSameStore(newNodeToMovePair, true);
|
||||
|
||||
// Done
|
||||
return newParentAssocRef;
|
||||
@@ -2231,7 +2289,7 @@ public class DbNodeServiceImpl extends AbstractNodeServiceImpl
|
||||
* do not need to be remade. If the children are in the same store, only the <code>indexChildren</code>
|
||||
* value is needed.
|
||||
*/
|
||||
private void pullNodeChildrenToSameStore(Pair<Long, NodeRef> nodePair, boolean cascade, boolean indexChildren)
|
||||
private void pullNodeChildrenToSameStore(Pair<Long, NodeRef> nodePair, boolean indexChildren)
|
||||
{
|
||||
Long nodeId = nodePair.getFirst();
|
||||
NodeRef nodeRef = nodePair.getSecond();
|
||||
@@ -2291,7 +2349,6 @@ public class DbNodeServiceImpl extends AbstractNodeServiceImpl
|
||||
// Index
|
||||
if (indexChildren)
|
||||
{
|
||||
nodeIndexer.indexDeleteNode(oldParentAssocPair.getSecond());
|
||||
nodeIndexer.indexCreateNode(newParentAssocPair.getSecond());
|
||||
}
|
||||
else
|
||||
@@ -2302,11 +2359,8 @@ public class DbNodeServiceImpl extends AbstractNodeServiceImpl
|
||||
// Fire node policies. This ensures that each node in the hierarchy gets a notification fired.
|
||||
invokeOnDeleteNode(oldParentAssocPair.getSecond(), childNodeTypeQName, childNodeAspectQNames, true);
|
||||
invokeOnCreateNode(newParentAssocPair.getSecond());
|
||||
// Cascade, if required
|
||||
if (cascade)
|
||||
{
|
||||
pullNodeChildrenToSameStore(newChildNodePair, cascade, indexChildren);
|
||||
}
|
||||
// Cascade
|
||||
pullNodeChildrenToSameStore(newChildNodePair, indexChildren);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2418,107 +2472,4 @@ public class DbNodeServiceImpl extends AbstractNodeServiceImpl
|
||||
nodeDaoService.setChildNameUnique(assocId, newName);
|
||||
}
|
||||
}
|
||||
|
||||
public static class MoveChildrenToCorrectStore extends AbstractNodeCleanupWorker
|
||||
{
|
||||
@Override
|
||||
protected List<String> doCleanInternal() throws Throwable
|
||||
{
|
||||
return dbNodeService.moveChildrenToCorrectStore();
|
||||
}
|
||||
};
|
||||
|
||||
private List<String> moveChildrenToCorrectStore()
|
||||
{
|
||||
List<String> results = new ArrayList<String>(1000);
|
||||
// Repeat the process for each store
|
||||
List<Pair<Long, StoreRef>> storePairs = nodeDaoService.getStores();
|
||||
for (Pair<Long, StoreRef> storePair : storePairs)
|
||||
{
|
||||
List<String> storeResults = moveChildrenToCorrectStore(storePair.getFirst());
|
||||
results.addAll(storeResults);
|
||||
}
|
||||
return results;
|
||||
}
|
||||
|
||||
private List<String> moveChildrenToCorrectStore(final Long storeId)
|
||||
{
|
||||
final List<Pair<Long, NodeRef>> parentNodePairs = new ArrayList<Pair<Long, NodeRef>>(100);
|
||||
final NodeRefQueryCallback callback = new NodeRefQueryCallback()
|
||||
{
|
||||
public boolean handle(Pair<Long, NodeRef> nodePair)
|
||||
{
|
||||
parentNodePairs.add(nodePair);
|
||||
return true;
|
||||
}
|
||||
};
|
||||
RetryingTransactionCallback<Object> getNodesCallback = new RetryingTransactionCallback<Object>()
|
||||
{
|
||||
public Object execute() throws Throwable
|
||||
{
|
||||
nodeDaoService.getNodesWithChildrenInDifferentStore(storeId, Long.MIN_VALUE, 100, callback);
|
||||
// Done
|
||||
return null;
|
||||
}
|
||||
};
|
||||
transactionService.getRetryingTransactionHelper().doInTransaction(getNodesCallback, true, true);
|
||||
// Process the nodes in random order
|
||||
Collections.shuffle(parentNodePairs);
|
||||
// Iterate and operate
|
||||
List<String> results = new ArrayList<String>(100);
|
||||
for (final Pair<Long, NodeRef> parentNodePair : parentNodePairs)
|
||||
{
|
||||
RetryingTransactionCallback<String> fixNodesCallback = new RetryingTransactionCallback<String>()
|
||||
{
|
||||
public String execute() throws Throwable
|
||||
{
|
||||
// Pull the children to the same store with full indexing - but don't cascade.
|
||||
pullNodeChildrenToSameStore(parentNodePair, true, true);
|
||||
// Done
|
||||
return null;
|
||||
}
|
||||
};
|
||||
RetryingTransactionHelper txnHelper = transactionService.getRetryingTransactionHelper();
|
||||
txnHelper.setMaxRetries(1);
|
||||
try
|
||||
{
|
||||
txnHelper.doInTransaction(fixNodesCallback, false, true);
|
||||
String msg =
|
||||
"Moved child nodes to parent node's store: \n" +
|
||||
" Parent node: " + parentNodePair.getFirst();
|
||||
results.add(msg);
|
||||
}
|
||||
catch (Throwable e)
|
||||
{
|
||||
String msg =
|
||||
"Failed to move child nodes to parent node's store." +
|
||||
" Set log level to WARN for this class to get exception log: \n" +
|
||||
" Parent node: " + parentNodePair.getFirst() + "\n" +
|
||||
" Error: " + e.getMessage();
|
||||
// It failed; do a full log in WARN mode
|
||||
if (logger.isWarnEnabled())
|
||||
{
|
||||
logger.warn(msg, e);
|
||||
}
|
||||
else
|
||||
{
|
||||
logger.error(msg);
|
||||
}
|
||||
results.add(msg);
|
||||
}
|
||||
}
|
||||
// Done
|
||||
if (logger.isDebugEnabled())
|
||||
{
|
||||
StringBuilder sb = new StringBuilder(256);
|
||||
sb.append("Moved children to correct stores: \n")
|
||||
.append(" Results:\n");
|
||||
for (String msg : results)
|
||||
{
|
||||
sb.append(" ").append(msg).append("\n");
|
||||
}
|
||||
logger.debug(sb.toString());
|
||||
}
|
||||
return results;
|
||||
}
|
||||
}
|
||||
|
@@ -38,8 +38,6 @@ import org.springframework.extensions.surf.util.I18NUtil;
|
||||
import org.alfresco.model.ContentModel;
|
||||
import org.alfresco.repo.content.MimetypeMap;
|
||||
import org.alfresco.repo.node.BaseNodeServiceTest;
|
||||
import org.alfresco.repo.node.StoreArchiveMap;
|
||||
import org.alfresco.repo.node.cleanup.NodeCleanupRegistry;
|
||||
import org.alfresco.repo.node.db.NodeDaoService.NodePropertyHandler;
|
||||
import org.alfresco.repo.transaction.AlfrescoTransactionSupport;
|
||||
import org.alfresco.repo.transaction.RetryingTransactionHelper.RetryingTransactionCallback;
|
||||
@@ -50,7 +48,6 @@ import org.alfresco.service.cmr.repository.ContentData;
|
||||
import org.alfresco.service.cmr.repository.MLText;
|
||||
import org.alfresco.service.cmr.repository.NodeRef;
|
||||
import org.alfresco.service.cmr.repository.NodeService;
|
||||
import org.alfresco.service.cmr.repository.StoreRef;
|
||||
import org.alfresco.service.namespace.QName;
|
||||
import org.alfresco.service.namespace.RegexQNamePattern;
|
||||
import org.alfresco.service.transaction.TransactionService;
|
||||
@@ -73,7 +70,6 @@ public class DbNodeServiceImplTest extends BaseNodeServiceTest
|
||||
{
|
||||
// Force cascading
|
||||
DbNodeServiceImpl dbNodeServiceImpl = (DbNodeServiceImpl) applicationContext.getBean("dbNodeServiceImpl");
|
||||
dbNodeServiceImpl.setCascadeInTransaction(true);
|
||||
|
||||
return (NodeService) applicationContext.getBean("dbNodeService");
|
||||
}
|
||||
@@ -402,49 +398,6 @@ public class DbNodeServiceImplTest extends BaseNodeServiceTest
|
||||
nodeService.deleteNode(nodeRef);
|
||||
}
|
||||
|
||||
public void testCleanup() throws Exception
|
||||
{
|
||||
@SuppressWarnings("unchecked")
|
||||
StoreArchiveMap storeArchiveMap = (StoreArchiveMap) applicationContext.getBean("storeArchiveMap");
|
||||
DbNodeServiceImpl ns = (DbNodeServiceImpl) applicationContext.getBean("dbNodeServiceImpl");
|
||||
ns.setCascadeInTransaction(false);
|
||||
|
||||
NodeRef parentNodeRef = nodeService.createNode(
|
||||
rootNodeRef,
|
||||
ASSOC_TYPE_QNAME_TEST_CHILDREN,
|
||||
QName.createQName(NAMESPACE, this.getName()),
|
||||
ContentModel.TYPE_FOLDER).getChildRef();
|
||||
NodeRef childNodeRef = nodeService.createNode(
|
||||
parentNodeRef,
|
||||
ContentModel.ASSOC_CONTAINS,
|
||||
QName.createQName(NAMESPACE, this.getName()),
|
||||
ContentModel.TYPE_FOLDER).getChildRef();
|
||||
|
||||
// Ensure that the archive feature is enabled
|
||||
StoreRef archiveStoreRef = ns.createStore("test", getName() + "-" + System.currentTimeMillis());
|
||||
storeArchiveMap.put(parentNodeRef.getStoreRef(), archiveStoreRef);
|
||||
|
||||
// Delete parent. Cascade is OFF, so children should be left in their current store.
|
||||
ns.deleteNode(parentNodeRef);
|
||||
// Check that the node n1 is in the archive store
|
||||
assertFalse("Parent should be deleted", ns.exists(parentNodeRef));
|
||||
NodeRef parentArchiveRef = new NodeRef(archiveStoreRef, parentNodeRef.getId());
|
||||
assertTrue("Parent should be in the archive store", ns.exists(parentArchiveRef));
|
||||
|
||||
// Force a commit here
|
||||
setComplete();
|
||||
endTransaction();
|
||||
|
||||
NodeCleanupRegistry nodeCleanupRegistry = new NodeCleanupRegistry();
|
||||
DbNodeServiceImpl.MoveChildrenToCorrectStore worker = new DbNodeServiceImpl.MoveChildrenToCorrectStore();
|
||||
worker.setTransactionService(transactionService);
|
||||
worker.setDbNodeService(ns);
|
||||
worker.setNodeDaoService(nodeDaoService);
|
||||
|
||||
// Run cleanup
|
||||
worker.doClean();
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a property to a node and checks that it can be found using the low-level DB query
|
||||
*/
|
||||
|
@@ -357,6 +357,8 @@ public interface NodeDaoService
|
||||
* @param minNodeId the min node ID to return
|
||||
* @param count the maximum number of results
|
||||
* @param resultsCallback the node callback
|
||||
*
|
||||
* @deprecated Since 2.2SP6, 3.1SP2, 3.2: not performant and not relevant to any use-cases
|
||||
*/
|
||||
@DirtySessionAnnotation(markDirty=false)
|
||||
public void getNodesWithChildrenInDifferentStore(
|
||||
|
@@ -3232,6 +3232,9 @@ public class HibernateNodeDaoServiceImpl
|
||||
// Done
|
||||
}
|
||||
|
||||
/**
|
||||
* @deprecated Not performant. Do not use.
|
||||
*/
|
||||
public void getNodesWithChildrenInDifferentStore(
|
||||
final Long storeId,
|
||||
final Long minNodeId,
|
||||
|
Reference in New Issue
Block a user