From dc4d19240c3176357e9ba91a9dea164e33c40778 Mon Sep 17 00:00:00 2001 From: Tuna Aksoy Date: Fri, 24 Apr 2015 23:26:37 +0000 Subject: [PATCH] RM-2190 (Concurrency exception when upload document to several folders with rules configured to file records) git-svn-id: https://svn.alfresco.com/repos/alfresco-enterprise/modules/recordsmanagement/BRANCHES/V2.2.1.x@102675 c4b6b30b-aa2e-2d43-bbcb-ca4b014f7261 --- .../impl/CopyMoveLinkFileToBaseAction.java | 23 +- .../integration/issue/IssueTestSuite.java | 3 +- .../test/integration/issue/RM2190Test.java | 201 ++++++++++++++++++ 3 files changed, 213 insertions(+), 14 deletions(-) create mode 100644 rm-server/test/java/org/alfresco/module/org_alfresco_module_rm/test/integration/issue/RM2190Test.java diff --git a/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/action/impl/CopyMoveLinkFileToBaseAction.java b/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/action/impl/CopyMoveLinkFileToBaseAction.java index 7ca8943f27..1c1325b416 100644 --- a/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/action/impl/CopyMoveLinkFileToBaseAction.java +++ b/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/action/impl/CopyMoveLinkFileToBaseAction.java @@ -116,7 +116,7 @@ public abstract class CopyMoveLinkFileToBaseAction extends RMActionExecuterAbstr * @see org.alfresco.repo.action.executer.ActionExecuterAbstractBase#executeImpl(org.alfresco.service.cmr.action.Action, org.alfresco.service.cmr.repository.NodeRef) */ @Override - protected void executeImpl(final Action action, final NodeRef actionedUponNodeRef) + protected synchronized void executeImpl(final Action action, final NodeRef actionedUponNodeRef) { String actionName = action.getActionDefinitionName(); if (isOkToProceedWithAction(actionedUponNodeRef, actionName)) @@ -132,7 +132,7 @@ public abstract class CopyMoveLinkFileToBaseAction extends RMActionExecuterAbstr { targetIsUnfiledRecords = (dictionaryService.isSubClass(actionedUponType, ContentModel.TYPE_CONTENT) && !recordService.isFiled(actionedUponNodeRef)) || TYPE_UNFILED_RECORD_FOLDER.equals(actionedUponType); - } + } // first look to see if the destination record folder has been specified NodeRef recordFolder = (NodeRef)action.getParameterValue(PARAM_DESTINATION_RECORD_FOLDER); @@ -146,17 +146,14 @@ public abstract class CopyMoveLinkFileToBaseAction extends RMActionExecuterAbstr NodeRef result = null; try { - synchronized (this) - { - // get the reference to the record folder based on the relative path - result = createOrResolvePath(action, actionedUponNodeRef, finaltargetIsUnfiledRecords); - } + // get the reference to the record folder based on the relative path + result = createOrResolvePath(action, actionedUponNodeRef, finaltargetIsUnfiledRecords); } catch (DuplicateChildNodeNameException ex) - { + { throw new ConcurrencyFailureException("Cannot create or resolve path.", ex); } - + return result; } }, false, true); @@ -397,12 +394,12 @@ public abstract class CopyMoveLinkFileToBaseAction extends RMActionExecuterAbstr if(targetisUnfiledRecords) { // create unfiled folder - child = fileFolderService.create(parent, childName, RecordsManagementModel.TYPE_UNFILED_RECORD_FOLDER).getNodeRef(); + child = fileFolderService.create(parent, childName, RecordsManagementModel.TYPE_UNFILED_RECORD_FOLDER).getNodeRef(); } else if(lastAsFolder) { // create record folder - child = recordFolderService.createRecordFolder(parent, childName); + child = recordFolderService.createRecordFolder(parent, childName); } else { @@ -411,9 +408,9 @@ public abstract class CopyMoveLinkFileToBaseAction extends RMActionExecuterAbstr { throw new AlfrescoRuntimeException("Unable to execute " + action.getActionDefinitionName() + " action, because the destination path has a record category within a record folder."); } - + // create record category - child = filePlanService.createRecordCategory(parent, childName); + child = filePlanService.createRecordCategory(parent, childName); } } return child; diff --git a/rm-server/test/java/org/alfresco/module/org_alfresco_module_rm/test/integration/issue/IssueTestSuite.java b/rm-server/test/java/org/alfresco/module/org_alfresco_module_rm/test/integration/issue/IssueTestSuite.java index 2c11e9689e..f5d15465cf 100755 --- a/rm-server/test/java/org/alfresco/module/org_alfresco_module_rm/test/integration/issue/IssueTestSuite.java +++ b/rm-server/test/java/org/alfresco/module/org_alfresco_module_rm/test/integration/issue/IssueTestSuite.java @@ -42,7 +42,8 @@ import org.junit.runners.Suite.SuiteClasses; RM804Test.class, RM994Test.class, RM1039Test.class, - RM1799Test.class + RM1799Test.class, + RM2190Test.class }) public class IssueTestSuite { diff --git a/rm-server/test/java/org/alfresco/module/org_alfresco_module_rm/test/integration/issue/RM2190Test.java b/rm-server/test/java/org/alfresco/module/org_alfresco_module_rm/test/integration/issue/RM2190Test.java new file mode 100644 index 0000000000..a44735ad5d --- /dev/null +++ b/rm-server/test/java/org/alfresco/module/org_alfresco_module_rm/test/integration/issue/RM2190Test.java @@ -0,0 +1,201 @@ +package org.alfresco.module.org_alfresco_module_rm.test.integration.issue; + +import static java.util.Arrays.asList; +import static org.alfresco.service.cmr.rule.RuleType.INBOUND; +import static org.alfresco.util.GUID.generate; +import static org.springframework.util.StringUtils.tokenizeToStringArray; + +import java.util.ArrayList; +import java.util.Iterator; +import java.util.List; + +import org.alfresco.module.org_alfresco_module_rm.action.dm.CreateRecordAction; +import org.alfresco.module.org_alfresco_module_rm.action.impl.FileToAction; +import org.alfresco.module.org_alfresco_module_rm.test.util.BaseRMTestCase; +import org.alfresco.service.cmr.action.Action; +import org.alfresco.service.cmr.model.FileInfo; +import org.alfresco.service.cmr.model.FileNotFoundException; +import org.alfresco.service.cmr.repository.NodeRef; +import org.alfresco.service.cmr.rule.Rule; +import org.alfresco.service.cmr.rule.RuleService; + +/** + * System test for RM-2190: Concurrency exception when upload document to several folders with rules configured to file records + * + * @author Tuna Aksoy + * @since 2.2.1.1 + */ +public class RM2190Test extends BaseRMTestCase +{ + private static final int NUMBER_OF_BATCHES = 1; + private static final int NUMBER_IN_BATCH = 10; + + private static final String PATH = "/111/222/333"; + + private RuleService ruleService; + + private NodeRef rootFolder; + private NodeRef folder1; + private NodeRef folder2; + + @Override + protected void initServices() + { + super.initServices(); + + ruleService = (RuleService) applicationContext.getBean("RuleService"); + } + + @Override + protected boolean isCollaborationSiteTest() + { + return true; + } + + @Override + protected boolean isRecordTest() + { + return true; + } + + public void testUploadDocumentsSimultaneouslyWithRules() + { + doTestInTransaction(new Test() + { + @Override + public Void run() + { + rootFolder = fileFolderService.create(documentLibrary, generate(), TYPE_FOLDER).getNodeRef(); + + Action createAction = actionService.createAction(CreateRecordAction.NAME); + createAction.setParameterValue(CreateRecordAction.PARAM_FILE_PLAN, filePlan); + + Rule declareRule = new Rule(); + declareRule.setRuleType(INBOUND); + declareRule.setTitle(generate()); + declareRule.setAction(createAction); + declareRule.setExecuteAsynchronously(true); + declareRule.applyToChildren(true); + ruleService.saveRule(rootFolder, declareRule); + + folder1 = fileFolderService.create(rootFolder, generate(), TYPE_FOLDER).getNodeRef(); + folder2 = fileFolderService.create(rootFolder, generate(), TYPE_FOLDER).getNodeRef(); + + Action fileAction = actionService.createAction(FileToAction.NAME); + fileAction.setParameterValue(FileToAction.PARAM_PATH, PATH); + fileAction.setParameterValue(FileToAction.PARAM_CREATE_RECORD_PATH, true); + + Rule fileRule = new Rule(); + fileRule.setRuleType(INBOUND); + fileRule.setTitle(generate()); + fileRule.setAction(fileAction); + fileRule.setExecuteAsynchronously(true); + ruleService.saveRule(unfiledContainer, fileRule); + + return null; + } + + @Override + public void test(Void result) throws Exception + { + assertFalse(ruleService.getRules(rootFolder).isEmpty()); + assertFalse(ruleService.getRules(unfiledContainer).isEmpty()); + } + }); + + doTestInTransaction(new Test() + { + @Override + public Void run() throws FileNotFoundException, InterruptedException + { + Thread thread1 = new Thread() + { + public void run() { + List files = addFilesToFolder(folder1); + waitForFilesToBeDeclared(files); + } + }; + + Thread thread2 = new Thread() + { + public void run() { + List files = addFilesToFolder(folder2); + waitForFilesToBeDeclared(files); + } + }; + + thread1.start(); + thread2.start(); + + thread1.join(120000); + thread2.join(120000); + + return null; + } + + @Override + public void test(Void result) throws Exception + { + FileInfo category = fileFolderService.resolveNamePath(filePlan, asList(tokenizeToStringArray(PATH, "/", false, true))); + assertEquals(NUMBER_IN_BATCH * 2, nodeService.getChildAssocs(category.getNodeRef()).size()); + } + }); + } + + private List addFilesToFolder(final NodeRef folder) + { + List records = new ArrayList(NUMBER_OF_BATCHES * NUMBER_IN_BATCH); + + for (int i = 0; i < NUMBER_OF_BATCHES; i++) + { + final int finali = i; + records.addAll(doTestInTransaction(new Test>() + { + @Override + public List run() throws Exception + { + List files = new ArrayList(NUMBER_IN_BATCH); + for (int j = 0; j < NUMBER_IN_BATCH; j++) + { + int count = (finali+1)*(j+1); + String name = folder.getId() + " - content" + count + ".txt"; + System.out.println(name + " - creating"); + + NodeRef file = fileFolderService.create(folder, name, TYPE_CONTENT).getNodeRef(); + files.add(file); + } + return files; + } + })); + } + + return records; + } + + private void waitForFilesToBeDeclared(List files) + { + while (!files.isEmpty()) + { + final Iterator temp = files.iterator(); + doTestInTransaction(new Test() + { + @Override + public Void run() throws Exception + { + while (temp.hasNext()) + { + NodeRef record = temp.next(); + if (nodeService.hasAspect(record, ASPECT_RECORD) && recordService.isFiled(record)) + { + String name = (String) nodeService.getProperty(record, PROP_NAME); + System.out.println(name + " - complete"); + temp.remove(); + } + } + + return null; + } + }); + } + } +}