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
This commit is contained in:
Tuna Aksoy
2015-04-24 23:26:37 +00:00
parent bcd7cfe912
commit dc4d19240c
3 changed files with 213 additions and 14 deletions

View File

@@ -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) * @see org.alfresco.repo.action.executer.ActionExecuterAbstractBase#executeImpl(org.alfresco.service.cmr.action.Action, org.alfresco.service.cmr.repository.NodeRef)
*/ */
@Override @Override
protected void executeImpl(final Action action, final NodeRef actionedUponNodeRef) protected synchronized void executeImpl(final Action action, final NodeRef actionedUponNodeRef)
{ {
String actionName = action.getActionDefinitionName(); String actionName = action.getActionDefinitionName();
if (isOkToProceedWithAction(actionedUponNodeRef, actionName)) if (isOkToProceedWithAction(actionedUponNodeRef, actionName))
@@ -132,7 +132,7 @@ public abstract class CopyMoveLinkFileToBaseAction extends RMActionExecuterAbstr
{ {
targetIsUnfiledRecords = (dictionaryService.isSubClass(actionedUponType, ContentModel.TYPE_CONTENT) && !recordService.isFiled(actionedUponNodeRef)) targetIsUnfiledRecords = (dictionaryService.isSubClass(actionedUponType, ContentModel.TYPE_CONTENT) && !recordService.isFiled(actionedUponNodeRef))
|| TYPE_UNFILED_RECORD_FOLDER.equals(actionedUponType); || TYPE_UNFILED_RECORD_FOLDER.equals(actionedUponType);
} }
// first look to see if the destination record folder has been specified // first look to see if the destination record folder has been specified
NodeRef recordFolder = (NodeRef)action.getParameterValue(PARAM_DESTINATION_RECORD_FOLDER); NodeRef recordFolder = (NodeRef)action.getParameterValue(PARAM_DESTINATION_RECORD_FOLDER);
@@ -146,17 +146,14 @@ public abstract class CopyMoveLinkFileToBaseAction extends RMActionExecuterAbstr
NodeRef result = null; NodeRef result = null;
try 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) catch (DuplicateChildNodeNameException ex)
{ {
throw new ConcurrencyFailureException("Cannot create or resolve path.", ex); throw new ConcurrencyFailureException("Cannot create or resolve path.", ex);
} }
return result; return result;
} }
}, false, true); }, false, true);
@@ -397,12 +394,12 @@ public abstract class CopyMoveLinkFileToBaseAction extends RMActionExecuterAbstr
if(targetisUnfiledRecords) if(targetisUnfiledRecords)
{ {
// create unfiled folder // 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) else if(lastAsFolder)
{ {
// create record folder // create record folder
child = recordFolderService.createRecordFolder(parent, childName); child = recordFolderService.createRecordFolder(parent, childName);
} }
else 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."); throw new AlfrescoRuntimeException("Unable to execute " + action.getActionDefinitionName() + " action, because the destination path has a record category within a record folder.");
} }
// create record category // create record category
child = filePlanService.createRecordCategory(parent, childName); child = filePlanService.createRecordCategory(parent, childName);
} }
} }
return child; return child;

View File

@@ -42,7 +42,8 @@ import org.junit.runners.Suite.SuiteClasses;
RM804Test.class, RM804Test.class,
RM994Test.class, RM994Test.class,
RM1039Test.class, RM1039Test.class,
RM1799Test.class RM1799Test.class,
RM2190Test.class
}) })
public class IssueTestSuite public class IssueTestSuite
{ {

View File

@@ -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<Void>()
{
@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<Void>()
{
@Override
public Void run() throws FileNotFoundException, InterruptedException
{
Thread thread1 = new Thread()
{
public void run() {
List<NodeRef> files = addFilesToFolder(folder1);
waitForFilesToBeDeclared(files);
}
};
Thread thread2 = new Thread()
{
public void run() {
List<NodeRef> 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<NodeRef> addFilesToFolder(final NodeRef folder)
{
List<NodeRef> records = new ArrayList<NodeRef>(NUMBER_OF_BATCHES * NUMBER_IN_BATCH);
for (int i = 0; i < NUMBER_OF_BATCHES; i++)
{
final int finali = i;
records.addAll(doTestInTransaction(new Test<List<NodeRef>>()
{
@Override
public List<NodeRef> run() throws Exception
{
List<NodeRef> files = new ArrayList<NodeRef>(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<NodeRef> files)
{
while (!files.isEmpty())
{
final Iterator<NodeRef> temp = files.iterator();
doTestInTransaction(new Test<Void>()
{
@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;
}
});
}
}
}