Wire in the asynchronous action service OnAsyncActionExecute policy (policy was previously defined but not active)

Update the tagging service tests, and some of the action tracking service tests to wait on the policy firing instead of using Thread.sleep


git-svn-id: https://svn.alfresco.com/repos/alfresco-enterprise/alfresco/HEAD/root@23188 c4b6b30b-aa2e-2d43-bbcb-ca4b014f7261
This commit is contained in:
Nick Burch
2010-10-18 05:24:03 +00:00
parent 7442465407
commit 8bfc88b8d6
3 changed files with 292 additions and 125 deletions

View File

@@ -32,11 +32,17 @@ import junit.framework.TestCase;
import org.alfresco.model.ContentModel; import org.alfresco.model.ContentModel;
import org.alfresco.repo.action.ActionServiceImplTest.CancellableSleepAction; import org.alfresco.repo.action.ActionServiceImplTest.CancellableSleepAction;
import org.alfresco.repo.action.ActionServiceImplTest.SleepActionExecuter; import org.alfresco.repo.action.ActionServiceImplTest.SleepActionExecuter;
import org.alfresco.repo.action.AsynchronousActionExecutionQueuePolicies.OnAsyncActionExecute;
import org.alfresco.repo.action.executer.MoveActionExecuter; import org.alfresco.repo.action.executer.MoveActionExecuter;
import org.alfresco.repo.cache.SimpleCache; import org.alfresco.repo.cache.SimpleCache;
import org.alfresco.repo.content.MimetypeMap; import org.alfresco.repo.content.MimetypeMap;
import org.alfresco.repo.jscript.ClasspathScriptLocation; import org.alfresco.repo.jscript.ClasspathScriptLocation;
import org.alfresco.repo.policy.JavaBehaviour;
import org.alfresco.repo.policy.PolicyComponent;
import org.alfresco.repo.policy.Behaviour.NotificationFrequency;
import org.alfresco.repo.security.authentication.AuthenticationUtil; import org.alfresco.repo.security.authentication.AuthenticationUtil;
import org.alfresco.repo.transaction.AlfrescoTransactionSupport;
import org.alfresco.repo.transaction.AlfrescoTransactionSupport.TxnReadState;
import org.alfresco.service.cmr.action.Action; import org.alfresco.service.cmr.action.Action;
import org.alfresco.service.cmr.action.ActionService; import org.alfresco.service.cmr.action.ActionService;
import org.alfresco.service.cmr.action.ActionStatus; import org.alfresco.service.cmr.action.ActionStatus;
@@ -58,9 +64,6 @@ import org.springframework.context.ConfigurableApplicationContext;
* Action tracking service tests. These mostly need * Action tracking service tests. These mostly need
* careful control over the transactions they use. * careful control over the transactions they use.
* *
* TODO Replace various sleep statements in here with a wait on the async action executor.
* Needs a way to be notified of async actions completing, which isn't currently available.
*
* @author Nick Burch * @author Nick Burch
*/ */
public class ActionTrackingServiceImplTest extends TestCase public class ActionTrackingServiceImplTest extends TestCase
@@ -82,9 +85,21 @@ public class ActionTrackingServiceImplTest extends TestCase
private ActionTrackingService actionTrackingService; private ActionTrackingService actionTrackingService;
private SimpleCache<String, ExecutionDetails> executingActionsCache; private SimpleCache<String, ExecutionDetails> executingActionsCache;
private AsyncOccurs asyncOccurs;
@Override @Override
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
protected void setUp() throws Exception { protected void setUp() throws Exception {
// Detect any dangling transactions as there is a lot of direct UserTransaction manipulation
if (AlfrescoTransactionSupport.getTransactionReadState() != TxnReadState.TXN_NONE)
{
throw new IllegalStateException(
"There should not be any transactions when starting test: " +
AlfrescoTransactionSupport.getTransactionId() + " started at " +
new Date(AlfrescoTransactionSupport.getTransactionStartTime()));
}
// Grab our beans
this.nodeService = (NodeService)ctx.getBean("nodeService"); this.nodeService = (NodeService)ctx.getBean("nodeService");
this.scriptService = (ScriptService)ctx.getBean("scriptService"); this.scriptService = (ScriptService)ctx.getBean("scriptService");
this.actionService = (ActionService)ctx.getBean("actionService"); this.actionService = (ActionService)ctx.getBean("actionService");
@@ -129,6 +144,14 @@ public class ActionTrackingServiceImplTest extends TestCase
// Register the test executor, if needed // Register the test executor, if needed
SleepActionExecuter.registerIfNeeded(ctx); SleepActionExecuter.registerIfNeeded(ctx);
// We want to know when async actions occur
asyncOccurs = new AsyncOccurs();
((PolicyComponent)ctx.getBean("policyComponent")).bindClassBehaviour(
AsynchronousActionExecutionQueuePolicies.OnAsyncActionExecute.QNAME,
ActionModel.TYPE_ACTION,
new JavaBehaviour(asyncOccurs, "onAsyncActionExecute", NotificationFrequency.EVERY_EVENT)
);
} }
/** Creating cache keys */ /** Creating cache keys */
@@ -329,12 +352,12 @@ public class ActionTrackingServiceImplTest extends TestCase
// End the transaction. Should allow the async action // End the transaction. Should allow the async action
// to be started // to start up, and begin sleeping
txn.commit(); txn.commit();
Thread.sleep(150); Thread.sleep(150);
// The action should now be running
// Will get an execution instance id, so a new key // It will have got an execution instance id, so a new key
key = ActionTrackingServiceImpl.generateCacheKey(action); key = ActionTrackingServiceImpl.generateCacheKey(action);
@@ -353,8 +376,8 @@ public class ActionTrackingServiceImplTest extends TestCase
// Tell it to stop sleeping // Tell it to stop sleeping
sleepActionExec.getExecutingThread().interrupt(); // Then wait for it to finish
Thread.sleep(100); asyncOccurs.awaitExecution(null, sleepActionExec.getExecutingThread(), action.getActionDefinitionName());
// Ensure it went away again // Ensure it went away again
@@ -388,7 +411,7 @@ public class ActionTrackingServiceImplTest extends TestCase
// End the transaction. Should allow the async action // End the transaction. Should allow the async action
// to be started // to be started, and move into its sleeping phase
txn.commit(); txn.commit();
Thread.sleep(150); Thread.sleep(150);
@@ -412,8 +435,11 @@ public class ActionTrackingServiceImplTest extends TestCase
// Tell it to stop sleeping // Tell it to stop sleeping
// Then wait for it to finish and go bang
// (Need to do it by hand, as it won't fire the complete policy
// as the action has failed)
sleepActionExec.getExecutingThread().interrupt(); sleepActionExec.getExecutingThread().interrupt();
Thread.sleep(100); Thread.sleep(150);
// Ensure it went away again // Ensure it went away again
@@ -774,6 +800,7 @@ public class ActionTrackingServiceImplTest extends TestCase
assertNotNull(executingActionsCache.get(key3)); assertNotNull(executingActionsCache.get(key3));
// Have it finish sleeping, will have been cancelled // Have it finish sleeping, will have been cancelled
// (Can't use the policy, as cancel is counted as a failure)
sleepActionExec.getExecutingThread().interrupt(); sleepActionExec.getExecutingThread().interrupt();
Thread.sleep(150); Thread.sleep(150);
@@ -874,10 +901,11 @@ public class ActionTrackingServiceImplTest extends TestCase
assertEquals(ActionStatus.Completed, action.getExecutionStatus()); assertEquals(ActionStatus.Completed, action.getExecutionStatus());
// Let the update change the stored node // Let the update change the stored node
// (Can't use policy as the action has already finished!)
txn.commit(); txn.commit();
Thread.sleep(150);
txn = transactionService.getUserTransaction(); txn = transactionService.getUserTransaction();
txn.begin(); txn.begin();
Thread.sleep(50);
// Now re-load and check the stored one // Now re-load and check the stored one
action = runtimeActionService.createAction(actionNode); action = runtimeActionService.createAction(actionNode);
@@ -962,8 +990,7 @@ public class ActionTrackingServiceImplTest extends TestCase
// End the transaction. Should allow the async action // End the transaction. Should allow the async action
// to be executed // to be executed
txn.commit(); asyncOccurs.awaitExecution(txn, null, action.getActionDefinitionName());
Thread.sleep(150);
assertNotNull(action.getExecutionStartDate()); assertNotNull(action.getExecutionStartDate());
assertNotNull(action.getExecutionEndDate()); assertNotNull(action.getExecutionEndDate());
@@ -992,8 +1019,8 @@ public class ActionTrackingServiceImplTest extends TestCase
assertNull(action.getExecutionFailureMessage()); assertNull(action.getExecutionFailureMessage());
assertEquals(ActionStatus.Pending, action.getExecutionStatus()); assertEquals(ActionStatus.Pending, action.getExecutionStatus());
// End the transaction. Should allow the async action // End the transaction, and await the failure
// to be executed // (Can't use the policy as fails not suceeds)
txn.commit(); txn.commit();
Thread.sleep(150); Thread.sleep(150);
@@ -1030,8 +1057,8 @@ public class ActionTrackingServiceImplTest extends TestCase
// End the transaction. Should allow the async action // End the transaction. Should allow the async action
// to be executed // to be executed
txn.commit(); asyncOccurs.awaitExecution(txn, null, action.getActionDefinitionName());
Thread.sleep(450); Thread.sleep(250); // Need to allow the post-commit update to the stored node
txn = transactionService.getUserTransaction(); txn = transactionService.getUserTransaction();
txn.begin(); txn.begin();
@@ -1072,12 +1099,12 @@ public class ActionTrackingServiceImplTest extends TestCase
assertNull(action.getExecutionFailureMessage()); assertNull(action.getExecutionFailureMessage());
assertEquals(ActionStatus.Pending, action.getExecutionStatus()); assertEquals(ActionStatus.Pending, action.getExecutionStatus());
// End the transaction. Should allow the async action // End the transaction, and await the failure
// to be executed // (Can't use the policy as fails not suceeds)
// Need to wait longer, as we have two async actions
// that need to occur - action + record
txn.commit(); txn.commit();
Thread.sleep(350); // Now also wait for the on-rollback to kick in and update
// the persisted copy of the action node too
Thread.sleep(400);
txn = transactionService.getUserTransaction(); txn = transactionService.getUserTransaction();
txn.begin(); txn.begin();
@@ -1097,6 +1124,8 @@ public class ActionTrackingServiceImplTest extends TestCase
assertBefore(action.getExecutionEndDate(), new Date()); assertBefore(action.getExecutionEndDate(), new Date());
assertNotNull(action.getExecutionFailureMessage()); assertNotNull(action.getExecutionFailureMessage());
assertEquals(ActionStatus.Failed, action.getExecutionStatus()); assertEquals(ActionStatus.Failed, action.getExecutionStatus());
txn.commit();
} }
public void testJavascriptAPI() throws Exception public void testJavascriptAPI() throws Exception
@@ -1148,6 +1177,54 @@ public class ActionTrackingServiceImplTest extends TestCase
// =================================================================== // // =================================================================== //
public class AsyncOccurs implements OnAsyncActionExecute {
private Object waitForExecutionLock = new Object();
private String wantedType = null;
private static final long waitTime = 3500;
@Override
public void onAsyncActionExecute(Action action, NodeRef actionedUponNodeRef)
{
if(wantedType == null || action.getActionDefinitionName().equals(wantedType))
{
synchronized (waitForExecutionLock) {
waitForExecutionLock.notify();
}
}
else
{
System.out.println("Ignoring unexpected async action:" + action);
}
}
public void awaitExecution(UserTransaction tx, Thread toWake, String type) throws Exception
{
this.wantedType = type;
synchronized (waitForExecutionLock) {
// Have things begin working
if(tx != null)
{
tx.commit();
}
if(toWake != null)
{
toWake.interrupt();
}
// Now wait for them to finish
try {
long now = System.currentTimeMillis();
waitForExecutionLock.wait(waitTime);
if(System.currentTimeMillis() - now >= waitTime)
{
System.err.println("Warning - trigger wasn't received");
}
} catch(InterruptedException e) {}
}
}
}
private Action createFailingMoveAction() { private Action createFailingMoveAction() {
Action failingAction = this.actionService.createAction(MoveActionExecuter.NAME); Action failingAction = this.actionService.createAction(MoveActionExecuter.NAME);

View File

@@ -139,10 +139,9 @@ public class AsynchronousActionExecutionQueueImpl implements AsynchronousActionE
private void invokeOnAsyncActionExecutePolicy(Action action, NodeRef actionedUponNodeRef) private void invokeOnAsyncActionExecutePolicy(Action action, NodeRef actionedUponNodeRef)
{ {
// get qnames to invoke against // Execute the policy, passing it all details, firing as a general action case
Set<QName> qnames = getTypeAndAspectQNames(actionedUponNodeRef); AsynchronousActionExecutionQueuePolicies.OnAsyncActionExecute policy =
// execute policy for node type and aspects onAsyncActionExecuteDelegate.get(actionedUponNodeRef, ActionModel.TYPE_ACTION);
AsynchronousActionExecutionQueuePolicies.OnAsyncActionExecute policy = onAsyncActionExecuteDelegate.get(actionedUponNodeRef, qnames);
policy.onAsyncActionExecute(action, actionedUponNodeRef); policy.onAsyncActionExecute(action, actionedUponNodeRef);
} }
@@ -428,11 +427,19 @@ public class AsynchronousActionExecutionQueueImpl implements AsynchronousActionE
{ {
public Object execute() public Object execute()
{ {
// If we have rules, apply them
if (ActionExecutionWrapper.this.executedRules != null) if (ActionExecutionWrapper.this.executedRules != null)
{ {
AlfrescoTransactionSupport.bindResource("RuleServiceImpl.ExecutedRules", ActionExecutionWrapper.this.executedRules); AlfrescoTransactionSupport.bindResource("RuleServiceImpl.ExecutedRules", ActionExecutionWrapper.this.executedRules);
} }
// Allow other classes to know when this action completes
AlfrescoTransactionSupport.bindListener(new CallbackTransactionListener(
ActionExecutionWrapper.this.action,
ActionExecutionWrapper.this.actionedUponNodeRef
));
// Have the action run
ActionExecutionWrapper.this.actionService.executeActionImpl( ActionExecutionWrapper.this.actionService.executeActionImpl(
ActionExecutionWrapper.this.action, ActionExecutionWrapper.this.action,
ActionExecutionWrapper.this.actionedUponNodeRef, ActionExecutionWrapper.this.actionedUponNodeRef,

View File

@@ -30,12 +30,19 @@ import javax.transaction.UserTransaction;
import junit.framework.TestCase; import junit.framework.TestCase;
import org.alfresco.model.ContentModel; import org.alfresco.model.ContentModel;
import org.alfresco.repo.action.ActionModel;
import org.alfresco.repo.action.AsynchronousActionExecutionQueuePolicies;
import org.alfresco.repo.action.AsynchronousActionExecutionQueuePolicies.OnAsyncActionExecute;
import org.alfresco.repo.jscript.ClasspathScriptLocation; import org.alfresco.repo.jscript.ClasspathScriptLocation;
import org.alfresco.repo.policy.JavaBehaviour;
import org.alfresco.repo.policy.PolicyComponent;
import org.alfresco.repo.policy.Behaviour.NotificationFrequency;
import org.alfresco.repo.security.authentication.AuthenticationComponent; import org.alfresco.repo.security.authentication.AuthenticationComponent;
import org.alfresco.repo.security.authentication.AuthenticationUtil; import org.alfresco.repo.security.authentication.AuthenticationUtil;
import org.alfresco.repo.transaction.AlfrescoTransactionSupport; import org.alfresco.repo.transaction.AlfrescoTransactionSupport;
import org.alfresco.repo.transaction.AlfrescoTransactionSupport.TxnReadState; import org.alfresco.repo.transaction.AlfrescoTransactionSupport.TxnReadState;
import org.alfresco.repo.transaction.RetryingTransactionHelper.RetryingTransactionCallback; import org.alfresco.repo.transaction.RetryingTransactionHelper.RetryingTransactionCallback;
import org.alfresco.service.cmr.action.Action;
import org.alfresco.service.cmr.action.ActionService; import org.alfresco.service.cmr.action.ActionService;
import org.alfresco.service.cmr.action.ActionTrackingService; import org.alfresco.service.cmr.action.ActionTrackingService;
import org.alfresco.service.cmr.audit.AuditService; import org.alfresco.service.cmr.audit.AuditService;
@@ -78,6 +85,7 @@ public class TaggingServiceImplTest extends TestCase
private ActionTrackingService actionTrackingService; private ActionTrackingService actionTrackingService;
private TransactionService transactionService; private TransactionService transactionService;
private AuthenticationComponent authenticationComponent; private AuthenticationComponent authenticationComponent;
private AsyncOccurs asyncOccurs;
private static StoreRef storeRef; private static StoreRef storeRef;
private static NodeRef rootNode; private static NodeRef rootNode;
@@ -153,6 +161,14 @@ public class TaggingServiceImplTest extends TestCase
tx.commit(); tx.commit();
} }
// We want to know when tagging actions have finished running
asyncOccurs = new AsyncOccurs();
((PolicyComponent)ctx.getBean("policyComponent")).bindClassBehaviour(
AsynchronousActionExecutionQueuePolicies.OnAsyncActionExecute.QNAME,
ActionModel.TYPE_ACTION,
new JavaBehaviour(asyncOccurs, "onAsyncActionExecute", NotificationFrequency.EVERY_EVENT)
);
// Create the folders and documents to be tagged // Create the folders and documents to be tagged
createTestDocumentsAndFolders(); createTestDocumentsAndFolders();
} }
@@ -436,17 +452,18 @@ public class TaggingServiceImplTest extends TestCase
// Add some more tags after the scopes have been added // Add some more tags after the scopes have been added
this.taggingService.addTag(this.subDocument, TAG_1); // folder+subfolder this.taggingService.addTag(this.subDocument, TAG_1); // folder+subfolder
tx = waitForActionExecution(tx); tx = asyncOccurs.awaitExecution(tx);
this.taggingService.addTag(this.subDocument, TAG_2); // folder+subfolder this.taggingService.addTag(this.subDocument, TAG_2); // folder+subfolder
tx = waitForActionExecution(tx); tx = asyncOccurs.awaitExecution(tx);
this.taggingService.addTag(this.subDocument, TAG_3); // folder+subfolder this.taggingService.addTag(this.subDocument, TAG_3); // folder+subfolder
tx = waitForActionExecution(tx); tx = asyncOccurs.awaitExecution(tx);
this.taggingService.addTag(this.subFolder, TAG_1); // folder+subfolder this.taggingService.addTag(this.subFolder, TAG_1); // folder+subfolder
tx = waitForActionExecution(tx); tx = asyncOccurs.awaitExecution(tx);
this.taggingService.addTag(this.subFolder, TAG_2); // folder+subfolder this.taggingService.addTag(this.subFolder, TAG_2); // folder+subfolder
tx = waitForActionExecution(tx); tx = asyncOccurs.awaitExecution(tx);
this.taggingService.addTag(this.folder, TAG_2); // folder only this.taggingService.addTag(this.folder, TAG_2); // folder only
tx = waitForActionExecution(tx);
tx = asyncOccurs.awaitExecution(tx);
// re get the tag scopes // re get the tag scopes
TagScope ts1 = this.taggingService.findTagScope(this.subDocument); TagScope ts1 = this.taggingService.findTagScope(this.subDocument);
@@ -480,16 +497,17 @@ public class TaggingServiceImplTest extends TestCase
// Take some off again // Take some off again
this.taggingService.removeTag(this.folder, TAG_2); this.taggingService.removeTag(this.folder, TAG_2);
tx = waitForActionExecution(tx); tx = asyncOccurs.awaitExecution(tx);
this.taggingService.removeTag(this.subFolder, TAG_2); this.taggingService.removeTag(this.subFolder, TAG_2);
tx = waitForActionExecution(tx); tx = asyncOccurs.awaitExecution(tx);
this.taggingService.removeTag(this.subFolder, TAG_1); this.taggingService.removeTag(this.subFolder, TAG_1);
tx = waitForActionExecution(tx); tx = asyncOccurs.awaitExecution(tx);
this.taggingService.removeTag(this.subDocument, TAG_1); this.taggingService.removeTag(this.subDocument, TAG_1);
tx = waitForActionExecution(tx); tx = asyncOccurs.awaitExecution(tx);
// And add one more // And add one more
this.taggingService.addTag(this.folder, TAG_3); this.taggingService.addTag(this.folder, TAG_3);
tx = waitForActionExecution(tx);
tx = asyncOccurs.awaitExecution(tx);
// re get the tag scopes // re get the tag scopes
ts1 = this.taggingService.findTagScope(this.subDocument); ts1 = this.taggingService.findTagScope(this.subDocument);
@@ -533,21 +551,27 @@ public class TaggingServiceImplTest extends TestCase
// tag1 = 2 // tag1 = 2
// tag3 = 1 // tag3 = 1
this.taggingService.addTag(this.subDocument, TAG_1); this.taggingService.addTag(this.subDocument, TAG_1);
tx = waitForActionExecution(tx);
this.taggingService.addTag(this.subDocument, TAG_2); this.taggingService.addTag(this.subDocument, TAG_2);
tx = waitForActionExecution(tx);
this.taggingService.addTag(this.subDocument, TAG_3); this.taggingService.addTag(this.subDocument, TAG_3);
tx = waitForActionExecution(tx);
this.taggingService.addTag(this.subFolder, TAG_1); this.taggingService.addTag(this.subFolder, TAG_1);
tx = waitForActionExecution(tx);
this.taggingService.addTag(this.subFolder, TAG_2); this.taggingService.addTag(this.subFolder, TAG_2);
tx = waitForActionExecution(tx);
this.taggingService.addTag(this.folder, TAG_2); this.taggingService.addTag(this.folder, TAG_2);
tx = waitForActionExecution(tx);
// Commit the changes. No action will fire, as there
// aren't currently any tag scopes to be updated!
tx.commit();
tx = this.transactionService.getUserTransaction();
tx.begin();
// Add the tag scope // Add the tag scope
this.taggingService.addTagScope(this.folder); this.taggingService.addTagScope(this.folder);
// The updates in this case are synchronous, not
// async, so we don't need to wait for any actions
tx.commit();
tx = this.transactionService.getUserTransaction();
tx.begin();
// Get the tag scope and check that all the values have been set correctly // Get the tag scope and check that all the values have been set correctly
TagScope tagScope = this.taggingService.findTagScope(this.folder); TagScope tagScope = this.taggingService.findTagScope(this.folder);
assertNotNull(tagScope); assertNotNull(tagScope);
@@ -571,19 +595,19 @@ public class TaggingServiceImplTest extends TestCase
// Add some tags // Add some tags
this.taggingService.addTag(this.folder, TAG_1); this.taggingService.addTag(this.folder, TAG_1);
tx = waitForActionExecution(tx); tx = asyncOccurs.awaitExecution(tx);
this.taggingService.addTag(this.document, TAG_1); this.taggingService.addTag(this.document, TAG_1);
tx = waitForActionExecution(tx); tx = asyncOccurs.awaitExecution(tx);
this.taggingService.addTag(this.document, TAG_2); this.taggingService.addTag(this.document, TAG_2);
tx = waitForActionExecution(tx); tx = asyncOccurs.awaitExecution(tx);
this.taggingService.addTag(this.subDocument, TAG_1); this.taggingService.addTag(this.subDocument, TAG_1);
tx = waitForActionExecution(tx); tx = asyncOccurs.awaitExecution(tx);
this.taggingService.addTag(this.subDocument, TAG_2); this.taggingService.addTag(this.subDocument, TAG_2);
tx = waitForActionExecution(tx); tx = asyncOccurs.awaitExecution(tx);
this.taggingService.addTag(this.subDocument, TAG_3); this.taggingService.addTag(this.subDocument, TAG_3);
tx = waitForActionExecution(tx); tx = asyncOccurs.awaitExecution(tx);
this.taggingService.addTag(this.subFolder, TAG_1); this.taggingService.addTag(this.subFolder, TAG_1);
tx = waitForActionExecution(tx); tx = asyncOccurs.awaitExecution(tx);
// Check that tag scope // Check that tag scope
TagScope ts1 = this.taggingService.findTagScope(this.folder); TagScope ts1 = this.taggingService.findTagScope(this.folder);
@@ -602,7 +626,7 @@ public class TaggingServiceImplTest extends TestCase
tags.add(TAG_4); tags.add(TAG_4);
this.taggingService.setTags(this.subDocument, tags); this.taggingService.setTags(this.subDocument, tags);
tx = waitForActionExecution(tx); tx = asyncOccurs.awaitExecution(tx);
// Check that the tagscope has been updated correctly // Check that the tagscope has been updated correctly
ts1 = this.taggingService.findTagScope(this.folder); ts1 = this.taggingService.findTagScope(this.folder);
@@ -625,7 +649,7 @@ public class TaggingServiceImplTest extends TestCase
this.taggingService.addTagScope(this.folder); this.taggingService.addTagScope(this.folder);
this.taggingService.addTag(this.folder, TAG_I18N); this.taggingService.addTag(this.folder, TAG_I18N);
tx = waitForActionExecution(tx); tx = asyncOccurs.awaitExecution(tx);
// Get the tag from the node // Get the tag from the node
List<String> tags = this.taggingService.getTags(this.folder); List<String> tags = this.taggingService.getTags(this.folder);
@@ -689,7 +713,7 @@ public class TaggingServiceImplTest extends TestCase
// Check that the tag scopes are empty // Check that the tag scopes are empty
taggingService.addTagScope(container1); taggingService.addTagScope(container1);
taggingService.addTagScope(container2); taggingService.addTagScope(container2);
tx = waitForActionExecution(tx); tx = asyncOccurs.awaitExecution(tx);
assertTrue( taggingService.isTagScope(container1) ); assertTrue( taggingService.isTagScope(container1) );
assertTrue( taggingService.isTagScope(container2) ); assertTrue( taggingService.isTagScope(container2) );
assertEquals(0, taggingService.findTagScope(container1).getTags().size()); assertEquals(0, taggingService.findTagScope(container1).getTags().size());
@@ -706,7 +730,10 @@ public class TaggingServiceImplTest extends TestCase
ContentModel.TYPE_FOLDER, ContentModel.TYPE_FOLDER,
taggedFolderProps).getChildRef(); taggedFolderProps).getChildRef();
tx = waitForActionExecution(tx); // No tags, so no changes should have occured, and no actions
tx.commit();
tx = transactionService.getUserTransaction();
tx.begin();
assertEquals(0, taggingService.findTagScope(container1).getTags().size()); assertEquals(0, taggingService.findTagScope(container1).getTags().size());
assertEquals(0, taggingService.findTagScope(container2).getTags().size()); assertEquals(0, taggingService.findTagScope(container2).getTags().size());
assertEquals(1, nodeService.getChildAssocs(container1).size()); assertEquals(1, nodeService.getChildAssocs(container1).size());
@@ -721,9 +748,15 @@ public class TaggingServiceImplTest extends TestCase
taggedFolderProps.put(ContentModel.ASPECT_TAGGABLE, (Serializable)tagsList); taggedFolderProps.put(ContentModel.ASPECT_TAGGABLE, (Serializable)tagsList);
this.nodeService.addProperties(taggedFolder, taggedFolderProps); this.nodeService.addProperties(taggedFolder, taggedFolderProps);
tx = waitForActionExecution(tx); tx = asyncOccurs.awaitExecution(tx);
assertEquals(2, taggingService.findTagScope(container1).getTags().size()); assertEquals(
assertEquals(0, taggingService.findTagScope(container2).getTags().size()); "Unexpected tags " + taggingService.findTagScope(container1).getTags(),
2, taggingService.findTagScope(container1).getTags().size()
);
assertEquals(
"Unexpected tags " + taggingService.findTagScope(container2).getTags(),
0, taggingService.findTagScope(container2).getTags().size()
);
assertEquals(1, taggingService.findTagScope(container1).getTag("foo1").getCount()); assertEquals(1, taggingService.findTagScope(container1).getTag("foo1").getCount());
assertEquals(1, taggingService.findTagScope(container1).getTag("foo3").getCount()); assertEquals(1, taggingService.findTagScope(container1).getTag("foo3").getCount());
@@ -744,7 +777,7 @@ public class TaggingServiceImplTest extends TestCase
ContentModel.TYPE_CONTENT, ContentModel.TYPE_CONTENT,
taggedDocProps).getChildRef(); taggedDocProps).getChildRef();
tx = waitForActionExecution(tx); tx = asyncOccurs.awaitExecution(tx);
assertEquals(3, taggingService.findTagScope(container1).getTags().size()); assertEquals(3, taggingService.findTagScope(container1).getTags().size());
assertEquals(0, taggingService.findTagScope(container2).getTags().size()); assertEquals(0, taggingService.findTagScope(container2).getTags().size());
@@ -764,7 +797,7 @@ public class TaggingServiceImplTest extends TestCase
// because it isn't applied at suitable times to take not of) // because it isn't applied at suitable times to take not of)
NodeRef checkedOutDoc = checkOutCheckInService.checkout(taggedDoc); NodeRef checkedOutDoc = checkOutCheckInService.checkout(taggedDoc);
tx = waitForActionExecution(tx); tx = asyncOccurs.awaitExecution(tx);
assertEquals(3, taggingService.findTagScope(container1).getTags().size()); assertEquals(3, taggingService.findTagScope(container1).getTags().size());
assertEquals(0, taggingService.findTagScope(container2).getTags().size()); assertEquals(0, taggingService.findTagScope(container2).getTags().size());
@@ -780,7 +813,7 @@ public class TaggingServiceImplTest extends TestCase
// Tags should go back to how they were // Tags should go back to how they were
checkOutCheckInService.checkin(checkedOutDoc, null); checkOutCheckInService.checkin(checkedOutDoc, null);
tx = waitForActionExecution(tx); tx = asyncOccurs.awaitExecution(tx);
assertEquals(3, taggingService.findTagScope(container1).getTags().size()); assertEquals(3, taggingService.findTagScope(container1).getTags().size());
assertEquals(0, taggingService.findTagScope(container2).getTags().size()); assertEquals(0, taggingService.findTagScope(container2).getTags().size());
@@ -803,14 +836,14 @@ public class TaggingServiceImplTest extends TestCase
ContentModel.TYPE_CONTENT, ContentModel.TYPE_CONTENT,
taggedDocProps).getChildRef(); taggedDocProps).getChildRef();
tx = waitForActionExecution(tx); tx = asyncOccurs.awaitExecution(tx);
assertEquals(3, taggingService.findTagScope(container1).getTags().size()); assertEquals(3, taggingService.findTagScope(container1).getTags().size());
assertEquals(0, taggingService.findTagScope(container2).getTags().size()); assertEquals(0, taggingService.findTagScope(container2).getTags().size());
assertEquals(1, nodeService.getChildAssocs(container2).size()); assertEquals(1, nodeService.getChildAssocs(container2).size());
copyService.copy(taggedDoc, taggedDoc2); copyService.copy(taggedDoc, taggedDoc2);
tx = waitForActionExecution(tx); tx = asyncOccurs.awaitExecution(tx);
assertEquals(3, taggingService.findTagScope(container1).getTags().size()); assertEquals(3, taggingService.findTagScope(container1).getTags().size());
assertEquals(2, taggingService.findTagScope(container2).getTags().size()); assertEquals(2, taggingService.findTagScope(container2).getTags().size());
@@ -835,7 +868,7 @@ public class TaggingServiceImplTest extends TestCase
ContentModel.ASSOC_CHILDREN, ContentModel.ASSOC_CHILDREN,
true); true);
tx = waitForActionExecution(tx); tx = asyncOccurs.awaitExecution(tx);
assertEquals(3, taggingService.findTagScope(container1).getTags().size()); assertEquals(3, taggingService.findTagScope(container1).getTags().size());
assertEquals(3, taggingService.findTagScope(container2).getTags().size()); assertEquals(3, taggingService.findTagScope(container2).getTags().size());
assertEquals(2, nodeService.getChildAssocs(container2).size()); assertEquals(2, nodeService.getChildAssocs(container2).size());
@@ -858,7 +891,7 @@ public class TaggingServiceImplTest extends TestCase
taggedDocProps.put(ContentModel.PROP_NAME, "UpdatedDocument"); taggedDocProps.put(ContentModel.PROP_NAME, "UpdatedDocument");
this.nodeService.addProperties(taggedDoc, taggedDocProps); this.nodeService.addProperties(taggedDoc, taggedDocProps);
tx = waitForActionExecution(tx); tx = asyncOccurs.awaitExecution(tx);
assertEquals(3, taggingService.findTagScope(container1).getTags().size()); assertEquals(3, taggingService.findTagScope(container1).getTags().size());
assertEquals(3, taggingService.findTagScope(container2).getTags().size()); assertEquals(3, taggingService.findTagScope(container2).getTags().size());
@@ -875,7 +908,7 @@ public class TaggingServiceImplTest extends TestCase
taggedDoc = nodeService.moveNode(taggedDoc, container2, taggedDoc = nodeService.moveNode(taggedDoc, container2,
ContentModel.ASSOC_CONTAINS, ContentModel.ASSOC_CONTAINS,
ContentModel.ASPECT_TAGGABLE).getChildRef(); ContentModel.ASPECT_TAGGABLE).getChildRef();
tx = waitForActionExecution(tx); tx = asyncOccurs.awaitExecution(tx);
assertEquals(2, taggingService.findTagScope(container1).getTags().size()); assertEquals(2, taggingService.findTagScope(container1).getTags().size());
assertEquals(4, taggingService.findTagScope(container2).getTags().size()); assertEquals(4, taggingService.findTagScope(container2).getTags().size());
@@ -901,7 +934,7 @@ public class TaggingServiceImplTest extends TestCase
// Delete the documents and folder one at a time // Delete the documents and folder one at a time
nodeService.deleteNode(taggedDoc); // container 2, "bar" nodeService.deleteNode(taggedDoc); // container 2, "bar"
tx = waitForActionExecution(tx); tx = asyncOccurs.awaitExecution(tx);
assertEquals(2, taggingService.findTagScope(container1).getTags().size()); assertEquals(2, taggingService.findTagScope(container1).getTags().size());
assertEquals(3, taggingService.findTagScope(container2).getTags().size()); assertEquals(3, taggingService.findTagScope(container2).getTags().size());
assertEquals(1, nodeService.getChildAssocs(container1).size()); assertEquals(1, nodeService.getChildAssocs(container1).size());
@@ -918,7 +951,7 @@ public class TaggingServiceImplTest extends TestCase
nodeService.deleteNode(taggedDoc2); // container 2, "foo1", "foo2" nodeService.deleteNode(taggedDoc2); // container 2, "foo1", "foo2"
tx = waitForActionExecution(tx); tx = asyncOccurs.awaitExecution(tx);
assertEquals(2, taggingService.findTagScope(container1).getTags().size()); assertEquals(2, taggingService.findTagScope(container1).getTags().size());
assertEquals(3, taggingService.findTagScope(container2).getTags().size()); assertEquals(3, taggingService.findTagScope(container2).getTags().size());
assertEquals(1, nodeService.getChildAssocs(container1).size()); assertEquals(1, nodeService.getChildAssocs(container1).size());
@@ -935,7 +968,7 @@ public class TaggingServiceImplTest extends TestCase
nodeService.deleteNode(taggedFolder); // container 1, "foo1", "foo3" nodeService.deleteNode(taggedFolder); // container 1, "foo1", "foo3"
tx = waitForActionExecution(tx); tx = asyncOccurs.awaitExecution(tx);
assertEquals(0, taggingService.findTagScope(container1).getTags().size()); assertEquals(0, taggingService.findTagScope(container1).getTags().size());
assertEquals(3, taggingService.findTagScope(container2).getTags().size()); assertEquals(3, taggingService.findTagScope(container2).getTags().size());
assertEquals(0, nodeService.getChildAssocs(container1).size()); assertEquals(0, nodeService.getChildAssocs(container1).size());
@@ -948,7 +981,7 @@ public class TaggingServiceImplTest extends TestCase
nodeService.deleteNode(taggedFolder2); // container 2, has a child also nodeService.deleteNode(taggedFolder2); // container 2, has a child also
tx = waitForActionExecution(tx); tx = asyncOccurs.awaitExecution(tx);
assertEquals(0, taggingService.findTagScope(container1).getTags().size()); assertEquals(0, taggingService.findTagScope(container1).getTags().size());
assertEquals(0, taggingService.findTagScope(container2).getTags().size()); assertEquals(0, taggingService.findTagScope(container2).getTags().size());
assertEquals(0, nodeService.getChildAssocs(container1).size()); assertEquals(0, nodeService.getChildAssocs(container1).size());
@@ -976,7 +1009,7 @@ public class TaggingServiceImplTest extends TestCase
this.scriptService.executeScript(location, model); this.scriptService.executeScript(location, model);
// Let the script run // Let the script run
tx = waitForActionExecution(tx); tx = asyncOccurs.awaitExecution(tx);
tx.commit(); tx.commit();
} }
@@ -997,21 +1030,21 @@ public class TaggingServiceImplTest extends TestCase
this.taggingService.addTagScope(this.subFolder); this.taggingService.addTagScope(this.subFolder);
this.taggingService.addTag(this.subDocument, TAG_1); this.taggingService.addTag(this.subDocument, TAG_1);
tx = waitForActionExecution(tx); tx = asyncOccurs.awaitExecution(tx);
this.taggingService.addTag(this.subDocument, TAG_2); this.taggingService.addTag(this.subDocument, TAG_2);
tx = waitForActionExecution(tx); tx = asyncOccurs.awaitExecution(tx);
this.taggingService.addTag(this.subDocument, TAG_3); this.taggingService.addTag(this.subDocument, TAG_3);
tx = waitForActionExecution(tx); tx = asyncOccurs.awaitExecution(tx);
this.taggingService.addTag(this.subFolder, TAG_1); this.taggingService.addTag(this.subFolder, TAG_1);
tx = waitForActionExecution(tx); tx = asyncOccurs.awaitExecution(tx);
this.taggingService.addTag(this.subFolder, TAG_2); this.taggingService.addTag(this.subFolder, TAG_2);
tx = waitForActionExecution(tx); tx = asyncOccurs.awaitExecution(tx);
this.taggingService.addTag(this.document, TAG_1); this.taggingService.addTag(this.document, TAG_1);
tx = waitForActionExecution(tx); tx = asyncOccurs.awaitExecution(tx);
this.taggingService.addTag(this.document, TAG_2); this.taggingService.addTag(this.document, TAG_2);
tx = waitForActionExecution(tx); tx = asyncOccurs.awaitExecution(tx);
this.taggingService.addTag(this.folder, TAG_1); this.taggingService.addTag(this.folder, TAG_1);
tx = waitForActionExecution(tx); tx = asyncOccurs.awaitExecution(tx);
Map<String, Object> model = new HashMap<String, Object>(0); Map<String, Object> model = new HashMap<String, Object>(0);
model.put("folder", this.folder); model.put("folder", this.folder);
@@ -1025,7 +1058,7 @@ public class TaggingServiceImplTest extends TestCase
this.scriptService.executeScript(location, model); this.scriptService.executeScript(location, model);
// Let the script run // Let the script run
tx = waitForActionExecution(tx); tx = asyncOccurs.awaitExecution(tx);
tx.commit(); tx.commit();
} }
@@ -1058,7 +1091,7 @@ public class TaggingServiceImplTest extends TestCase
this.taggingService.addTag(this.document, TAG_1); this.taggingService.addTag(this.document, TAG_1);
this.taggingService.addTag(this.folder, TAG_1); this.taggingService.addTag(this.folder, TAG_1);
this.taggingService.addTag(this.folder, TAG_3); this.taggingService.addTag(this.folder, TAG_3);
tx = waitForActionExecution(tx); tx = asyncOccurs.awaitExecution(tx);
// Tag scope updates shouldn't have happened yet, // Tag scope updates shouldn't have happened yet,
@@ -1086,7 +1119,7 @@ public class TaggingServiceImplTest extends TestCase
// It won't be able to do anything, as the locks are taken // It won't be able to do anything, as the locks are taken
UpdateTagScopesQuartzJob job = new UpdateTagScopesQuartzJob(); UpdateTagScopesQuartzJob job = new UpdateTagScopesQuartzJob();
job.execute(actionService, updateTagsAction); job.execute(actionService, updateTagsAction);
tx = waitForActionExecution(tx); tx = asyncOccurs.awaitExecution(tx);
// Check that things are still pending despite the quartz run // Check that things are still pending despite the quartz run
assertEquals(2, updateTagsAction.searchForTagScopesPendingUpdates().size()); assertEquals(2, updateTagsAction.searchForTagScopesPendingUpdates().size());
@@ -1103,7 +1136,7 @@ public class TaggingServiceImplTest extends TestCase
// Fire off the quartz bean, this time it can really work // Fire off the quartz bean, this time it can really work
job = new UpdateTagScopesQuartzJob(); job = new UpdateTagScopesQuartzJob();
job.execute(actionService, updateTagsAction); job.execute(actionService, updateTagsAction);
tx = waitForActionExecution(tx); tx = asyncOccurs.awaitExecution(tx);
// Now check again - nothing should be pending // Now check again - nothing should be pending
@@ -1135,7 +1168,7 @@ public class TaggingServiceImplTest extends TestCase
* Test that when multiple threads do tag updates, the right thing still * Test that when multiple threads do tag updates, the right thing still
* happens * happens
*/ */
public void DEACTIVATEDtestMultiThreaded() throws Exception public void testMultiThreaded() throws Exception
{ {
UserTransaction tx = this.transactionService.getNonPropagatingUserTransaction(); UserTransaction tx = this.transactionService.getNonPropagatingUserTransaction();
tx.begin(); tx.begin();
@@ -1199,25 +1232,36 @@ public class TaggingServiceImplTest extends TestCase
t.join(); t.join();
} }
// Have a brief pause, while we wait for their related
// async actions to kick off
Thread.sleep(150);
// At this point, the action tracking service should see
// anything that will run as running, so we can use that
// to wait for everything to finish as needed
// Now we wait for the asynchronous tag execution to finish // Now we wait for the asynchronous tag execution to finish
try // Wait a maximum of 60 seconds
for (int i = 0; i < 600; i++)
{ {
// Wait for a maximum of 60 seconds if (actionTrackingService.getAllExecutingActions().size() > 0)
for (int i = 0; i < 60; i++)
{ {
if (actionTrackingService.getAllExecutingActions().size() > 0) try
{ {
Thread.sleep(1000); Thread.sleep(100);
} }
else catch (InterruptedException e)
{ {
break;
} }
} }
else
{
break;
}
} }
catch (InterruptedException e)
{ // Extra sleep just to be sure things are quiet before we continue
} Thread.sleep(150);
// Now check that things ended up as planned // Now check that things ended up as planned
tx = this.transactionService.getUserTransaction(); tx = this.transactionService.getUserTransaction();
@@ -1225,8 +1269,14 @@ public class TaggingServiceImplTest extends TestCase
TagScope ts1 = this.taggingService.findTagScope(this.folder); TagScope ts1 = this.taggingService.findTagScope(this.folder);
TagScope ts2 = this.taggingService.findTagScope(this.subFolder); TagScope ts2 = this.taggingService.findTagScope(this.subFolder);
assertEquals("Wrong tags on folder tagscope: " + ts1.getTags(), 5, ts1.getTags().size()); assertEquals(
assertEquals("Wrong tags on folder tagscope: " + ts1.getTags(), 5, ts2.getTags().size()); "Wrong tags on folder tagscope: " + ts1.getTags(),
5, ts1.getTags().size()
);
assertEquals(
"Wrong tags on subfolder tagscope: " + ts2.getTags(),
5, ts2.getTags().size()
);
// Each tag should crop up 3 times on the folder // Each tag should crop up 3 times on the folder
// and twice for the subfolder // and twice for the subfolder
@@ -1240,37 +1290,70 @@ public class TaggingServiceImplTest extends TestCase
tx.commit(); tx.commit();
} }
private UserTransaction waitForActionExecution(UserTransaction txn)
throws Exception
{
int size = actionTrackingService.getAllExecutingActions().size();
if(size == 0)
{
System.err.println("No actions pending, is your action really async?");
}
// Finish the transaction, so that any background public class AsyncOccurs implements OnAsyncActionExecute {
// actions can kick off private Object waitForExecutionLock = new Object();
txn.commit(); private static final long waitTime = 3500;
private static final String ACTION_TYPE = UpdateTagScopesActionExecuter.NAME;
// Wait for it to run @Override
try { public void onAsyncActionExecute(Action action, NodeRef actionedUponNodeRef)
// Wait for a maximum of 10 seconds {
for(int i=0; i<1000; i++) if(action.getActionDefinitionName().equals(ACTION_TYPE))
{
synchronized (waitForExecutionLock) {
try {
waitForExecutionLock.notify();
} catch(IllegalMonitorStateException e) {}
}
}
else
{
System.out.println("Ignoring unexpected async action:" + action);
}
}
public UserTransaction awaitExecution(UserTransaction tx) throws Exception
{
synchronized (waitForExecutionLock) {
// Have things begin working
tx.commit();
// Always wait 25ms
waitForExecutionLock.wait(25);
// If there are actions if the required type
// currently running, keep waiting longer for
// them to finish
if(actionTrackingService.getExecutingActions(ACTION_TYPE).size() > 0)
{ {
if(actionTrackingService.getAllExecutingActions().size() > 0) long now = System.currentTimeMillis();
waitForExecutionLock.wait(waitTime);
if(System.currentTimeMillis() - now >= waitTime)
{ {
Thread.sleep(10); System.err.println("Warning - trigger wasn't received");
}
else {
break;
} }
} }
} catch(InterruptedException e) {} }
// Start a new transaction for the next one // If there are any more actions of the same type,
txn = transactionService.getUserTransaction(); // then wait for them to finish too
txn.begin(); for(int i=0; i<50; i++)
return txn; {
if( actionTrackingService.getExecutingActions(ACTION_TYPE).size() == 0 )
{
break;
}
try {
Thread.sleep(10);
} catch(InterruptedException e) {}
}
// Now create a new transaction for them
tx = transactionService.getUserTransaction();
tx.begin();
return tx;
}
} }
} }