diff --git a/config/alfresco/scheduled-jobs-context.xml b/config/alfresco/scheduled-jobs-context.xml
index a99fbf944a..69a3578fa0 100644
--- a/config/alfresco/scheduled-jobs-context.xml
+++ b/config/alfresco/scheduled-jobs-context.xml
@@ -482,5 +482,38 @@
+
+
+
+
+
+
+ org.alfresco.repo.tagging.UpdateTagScopesQuartzJob
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 2
+
+
+ 60
+
+
diff --git a/source/java/org/alfresco/repo/tagging/TaggingServiceImplTest.java b/source/java/org/alfresco/repo/tagging/TaggingServiceImplTest.java
index 21a7579f25..949ff64682 100644
--- a/source/java/org/alfresco/repo/tagging/TaggingServiceImplTest.java
+++ b/source/java/org/alfresco/repo/tagging/TaggingServiceImplTest.java
@@ -26,44 +26,55 @@ import java.util.Map;
import javax.transaction.UserTransaction;
+import junit.framework.TestCase;
+
import org.alfresco.model.ContentModel;
import org.alfresco.repo.jscript.ClasspathScriptLocation;
-import org.alfresco.repo.policy.PolicyComponent;
import org.alfresco.repo.security.authentication.AuthenticationComponent;
+import org.alfresco.repo.security.authentication.AuthenticationUtil;
+import org.alfresco.repo.transaction.RetryingTransactionHelper.RetryingTransactionCallback;
import org.alfresco.service.cmr.action.ActionService;
import org.alfresco.service.cmr.action.ActionTrackingService;
+import org.alfresco.service.cmr.audit.AuditService;
import org.alfresco.service.cmr.coci.CheckOutCheckInService;
-import org.alfresco.service.cmr.repository.ContentService;
import org.alfresco.service.cmr.repository.CopyService;
import org.alfresco.service.cmr.repository.NodeRef;
import org.alfresco.service.cmr.repository.NodeService;
import org.alfresco.service.cmr.repository.ScriptLocation;
import org.alfresco.service.cmr.repository.ScriptService;
import org.alfresco.service.cmr.repository.StoreRef;
-import org.alfresco.service.cmr.security.MutableAuthenticationService;
import org.alfresco.service.cmr.tagging.TagDetails;
import org.alfresco.service.cmr.tagging.TagScope;
import org.alfresco.service.cmr.tagging.TaggingService;
import org.alfresco.service.namespace.NamespaceService;
import org.alfresco.service.namespace.QName;
import org.alfresco.service.transaction.TransactionService;
-import org.alfresco.util.BaseAlfrescoSpringTest;
+import org.alfresco.util.ApplicationContextHelper;
import org.alfresco.util.GUID;
+import org.springframework.context.ConfigurableApplicationContext;
/**
* Tagging service implementation unit test
*
* @author Roy Wetherall
+ * @author Nick Burch
*/
-public class TaggingServiceImplTest extends BaseAlfrescoSpringTest
+public class TaggingServiceImplTest extends TestCase
{
+ private static ConfigurableApplicationContext ctx =
+ (ConfigurableApplicationContext)ApplicationContextHelper.getApplicationContext();
+
/** Services */
private TaggingService taggingService;
+ private NodeService nodeService;
private CopyService copyService;
private CheckOutCheckInService checkOutCheckInService;
private ScriptService scriptService;
- private PolicyComponent policyComponent;
+ private AuditService auditService;
+ private ActionService actionService;
private ActionTrackingService actionTrackingService;
+ private TransactionService transactionService;
+ private AuthenticationComponent authenticationComponent;
private static StoreRef storeRef;
private static NodeRef rootNode;
@@ -84,32 +95,28 @@ public class TaggingServiceImplTest extends BaseAlfrescoSpringTest
private static boolean init = false;
+
@Override
- protected void onSetUpBeforeTransaction() throws Exception
+ protected void setUp() throws Exception
{
- super.onSetUpBeforeTransaction();
-
// Get services
- this.taggingService = (TaggingService)this.applicationContext.getBean("TaggingService");
- this.nodeService = (NodeService) this.applicationContext.getBean("NodeService");
- this.copyService = (CopyService) this.applicationContext.getBean("CopyService");
- this.contentService = (ContentService) this.applicationContext.getBean("ContentService");
- this.checkOutCheckInService = (CheckOutCheckInService) this.applicationContext.getBean("CheckoutCheckinService");
- this.authenticationService = (MutableAuthenticationService) this.applicationContext.getBean("authenticationService");
- this.actionService = (ActionService)this.applicationContext.getBean("ActionService");
- this.transactionService = (TransactionService)this.applicationContext.getBean("transactionComponent");
- this.scriptService = (ScriptService)this.applicationContext.getBean("scriptService");
- this.policyComponent = (PolicyComponent)this.applicationContext.getBean("policyComponent");
- this.actionTrackingService = (ActionTrackingService)this.applicationContext.getBean("actionTrackingService");
+ this.taggingService = (TaggingService)ctx.getBean("TaggingService");
+ this.nodeService = (NodeService) ctx.getBean("NodeService");
+ this.copyService = (CopyService) ctx.getBean("CopyService");
+ this.checkOutCheckInService = (CheckOutCheckInService) ctx.getBean("CheckoutCheckinService");
+ this.actionService = (ActionService)ctx.getBean("ActionService");
+ this.transactionService = (TransactionService)ctx.getBean("transactionComponent");
+ this.auditService = (AuditService)ctx.getBean("auditService");
+ this.scriptService = (ScriptService)ctx.getBean("scriptService");
+ this.actionTrackingService = (ActionTrackingService)ctx.getBean("actionTrackingService");
+ this.authenticationComponent = (AuthenticationComponent)ctx.getBean("authenticationComponent");
if (init == false)
{
- UserTransaction tx = this.transactionService.getUserTransaction();
+ UserTransaction tx = this.transactionService.getNonPropagatingUserTransaction();
tx.begin();
// Authenticate as the system user
- AuthenticationComponent authenticationComponent = (AuthenticationComponent) this.applicationContext
- .getBean("authenticationComponent");
authenticationComponent.setSystemUserAsCurrentUser();
// Create the store and get the root node
@@ -133,14 +140,22 @@ public class TaggingServiceImplTest extends BaseAlfrescoSpringTest
tx.commit();
}
+
+ // Create the folders and documents to be tagged
+ createTestDocumentsAndFolders();
}
@Override
- protected void onSetUpInTransaction() throws Exception
+ protected void tearDown() throws Exception {
+ removeTestDocumentsAndFolders();
+ }
+
+ private void createTestDocumentsAndFolders() throws Exception
{
+ UserTransaction tx = this.transactionService.getNonPropagatingUserTransaction();
+ tx.begin();
+
// Authenticate as the system user
- AuthenticationComponent authenticationComponent = (AuthenticationComponent) this.applicationContext
- .getBean("authenticationComponent");
authenticationComponent.setSystemUserAsCurrentUser();
String guid = GUID.generate();
@@ -183,9 +198,40 @@ public class TaggingServiceImplTest extends BaseAlfrescoSpringTest
ContentModel.TYPE_CONTENT,
props).getChildRef();
- //tx.commit();
- setComplete();
- endTransaction();
+ tx.commit();
+ }
+ private void removeTestDocumentsAndFolders() throws Exception
+ {
+ UserTransaction tx = this.transactionService.getNonPropagatingUserTransaction();
+ tx.begin();
+
+ // Authenticate as the system user
+ authenticationComponent.setSystemUserAsCurrentUser();
+
+ // If anything is a tag scope, stop it being
+ NodeRef[] nodes = new NodeRef[] { subDocument, subFolder, document, folder };
+ for(NodeRef nodeRef : nodes)
+ {
+ if(taggingService.isTagScope(nodeRef))
+ {
+ taggingService.removeTagScope(nodeRef);
+ }
+ }
+
+ // Remove the sample nodes
+ for(NodeRef nodeRef : nodes)
+ {
+ nodeService.deleteNode(nodeRef);
+ }
+
+ // Tidy up the audit component, now all the nodes have gone
+ auditService.clearAudit(
+ TaggingServiceImpl.TAGGING_AUDIT_APPLICATION_NAME,
+ 0l, System.currentTimeMillis()+1
+ );
+
+ // All done
+ tx.commit();
}
public void testTagCRUD()
@@ -203,10 +249,7 @@ public class TaggingServiceImplTest extends BaseAlfrescoSpringTest
this.taggingService.createTag(TaggingServiceImplTest.storeRef, TAG_1);
this.taggingService.createTag(TaggingServiceImplTest.storeRef, UPPER_TAG);
- //setComplete();
- //endTransaction();
tx.commit();
-
tx = this.transactionService.getUserTransaction();
tx.begin();
@@ -905,7 +948,7 @@ public class TaggingServiceImplTest extends BaseAlfrescoSpringTest
UserTransaction tx = this.transactionService.getUserTransaction();
tx.begin();
- Map model = new HashMap(0);
+ Map model = new HashMap(0);
model.put("folder", this.folder);
model.put("subFolder", this.subFolder);
model.put("document", this.document);
@@ -915,6 +958,8 @@ public class TaggingServiceImplTest extends BaseAlfrescoSpringTest
ScriptLocation location = new ClasspathScriptLocation("org/alfresco/repo/tagging/script/test_taggingService.js");
this.scriptService.executeScript(location, model);
+ // Let the script run
+ tx = waitForActionExecution(tx);
tx.commit();
}
@@ -950,9 +995,8 @@ public class TaggingServiceImplTest extends BaseAlfrescoSpringTest
tx = waitForActionExecution(tx);
this.taggingService.addTag(this.folder, TAG_1);
tx = waitForActionExecution(tx);
- tx.commit();
- Map model = new HashMap(0);
+ Map model = new HashMap(0);
model.put("folder", this.folder);
model.put("subFolder", this.subFolder);
model.put("document", this.document);
@@ -962,9 +1006,197 @@ public class TaggingServiceImplTest extends BaseAlfrescoSpringTest
ScriptLocation location = new ClasspathScriptLocation("org/alfresco/repo/tagging/script/test_taggingService.js");
this.scriptService.executeScript(location, model);
+
+ // Let the script run
+ tx = waitForActionExecution(tx);
+ tx.commit();
}
- private static Object mutex = new Object();
+ /**
+ * Test that the scheduled task will do the right thing
+ * when it runs.
+ */
+ public void testOnStartupJob() throws Exception
+ {
+ UserTransaction tx = this.transactionService.getUserTransaction();
+ tx.begin();
+
+ // Nothing is pending to start with
+ UpdateTagScopesActionExecuter updateTagsAction = (UpdateTagScopesActionExecuter)
+ ctx.getBean("update-tagscope");
+ assertEquals(0, updateTagsAction.searchForTagScopesPendingUpdates().size());
+
+
+ // Take the tag scope lock, so that no real updates will happen
+ String lockF = updateTagsAction.lockTagScope(this.folder);
+ String lockSF = updateTagsAction.lockTagScope(this.subFolder);
+
+ // Do some tagging
+ this.taggingService.addTagScope(this.folder);
+ this.taggingService.addTagScope(this.subFolder);
+
+ this.taggingService.addTag(this.subDocument, TAG_1);
+ this.taggingService.addTag(this.subDocument, TAG_2);
+ this.taggingService.addTag(this.subFolder, TAG_1);
+ this.taggingService.addTag(this.document, TAG_1);
+ this.taggingService.addTag(this.folder, TAG_1);
+ this.taggingService.addTag(this.folder, TAG_3);
+ tx = waitForActionExecution(tx);
+
+
+ // Tag scope updates shouldn't have happened yet,
+ // as the scopes are locked
+ TagScope ts1 = this.taggingService.findTagScope(this.folder);
+ TagScope ts2 = this.taggingService.findTagScope(this.subFolder);
+ assertEquals(
+ "Wrong tags on folder tagscope: " + ts1.getTags(),
+ 0, ts1.getTags().size()
+ );
+ assertEquals(
+ "Wrong tags on folder tagscope: " + ts1.getTags(),
+ 0, ts2.getTags().size()
+ );
+
+
+ // Check the pending list now
+ assertEquals(2, updateTagsAction.searchForTagScopesPendingUpdates().size());
+ List pendingScopes = updateTagsAction.searchForTagScopesPendingUpdates();
+ assertTrue("Not found in " + pendingScopes, pendingScopes.contains(this.folder));
+ assertTrue("Not found in " + pendingScopes, pendingScopes.contains(this.subFolder));
+
+
+ // Give back our locks, so we can proceed
+ updateTagsAction.unlockTagScope(this.folder, lockF);
+ updateTagsAction.unlockTagScope(this.subFolder, lockSF);
+
+
+ // Fire off the quartz bean
+ UpdateTagScopesQuartzJob job = new UpdateTagScopesQuartzJob();
+ job.execute(actionService, updateTagsAction);
+
+
+ // Now check again
+ assertEquals(0, updateTagsAction.searchForTagScopesPendingUpdates().size());
+
+ ts1 = this.taggingService.findTagScope(this.folder);
+ ts2 = this.taggingService.findTagScope(this.subFolder);
+ assertEquals(
+ "Wrong tags on folder tagscope: " + ts1.getTags(),
+ 3, ts1.getTags().size()
+ );
+ assertEquals(
+ "Wrong tags on folder tagscope: " + ts1.getTags(),
+ 2, ts2.getTags().size()
+ );
+
+ assertEquals(4, ts1.getTag(TAG_1).getCount());
+ assertEquals(1, ts1.getTag(TAG_2).getCount());
+ assertEquals(1, ts1.getTag(TAG_3.toLowerCase()).getCount());
+
+ assertEquals(2, ts2.getTag(TAG_1).getCount());
+ assertEquals(1, ts2.getTag(TAG_2).getCount());
+ }
+
+ /**
+ * Test that when multiple threads do tag updates, the right
+ * thing still happens
+ */
+ public void DISABLEDtestMultiThreaded() throws Exception
+ {
+ UserTransaction tx = this.transactionService.getNonPropagatingUserTransaction();
+ tx.begin();
+ this.taggingService.addTagScope(this.folder);
+ this.taggingService.addTagScope(this.subFolder);
+ tx.commit();
+
+ // Prepare a bunch of threads to do tagging
+ final List threads = new ArrayList();
+ String[] tags = new String[] { TAG_1, TAG_2, TAG_3, TAG_4, TAG_5 };
+ for(String tmpTag : tags)
+ {
+ final String tag = tmpTag;
+ Thread t = new Thread(new Runnable() {
+ @Override
+ public void run() {
+ // Let everything catch up
+ try {
+ Thread.sleep(250);
+ } catch(InterruptedException e) {}
+System.out.println(Thread.currentThread() + " - About to start tagging");
+
+ // Do the updates
+ AuthenticationUtil.setFullyAuthenticatedUser(AuthenticationUtil.getSystemUserName());
+ transactionService.getRetryingTransactionHelper().doInTransaction(
+ new RetryingTransactionCallback() {
+ public Void execute() throws Throwable {
+ taggingService.addTag(folder, tag);
+ taggingService.addTag(subFolder, tag);
+ taggingService.addTag(subDocument, tag);
+System.out.println(Thread.currentThread() + " - Tagging");
+ return null;
+ }
+ }, false, true
+ );
+System.out.println(Thread.currentThread() + " - Done tagging");
+ }
+ });
+ threads.add(t);
+ t.start();
+ }
+
+
+ // Release the threads
+ Thread.sleep(50);
+ for(Thread t : threads) {
+ t.interrupt();
+ }
+ Thread.sleep(100);
+ System.out.println("Done waiting, proceeding with multi-threaded test");
+
+
+ // Wait for all the threads to finish working
+ try {
+ // Wait for a maximum of 10 seconds
+ for(int i=0; i<1000; i++)
+ {
+ if(actionTrackingService.getAllExecutingActions().size() > 0)
+ {
+ Thread.sleep(10);
+ }
+ else {
+ break;
+ }
+ }
+ } catch(InterruptedException e) {}
+
+
+ // Now check that things ended up as planned
+ tx = this.transactionService.getUserTransaction();
+ tx.begin();
+
+ TagScope ts1 = this.taggingService.findTagScope(this.folder);
+ TagScope ts2 = this.taggingService.findTagScope(this.subFolder);
+ assertEquals(
+ "Wrong tags on folder tagscope: " + ts1.getTags(),
+ 5, ts1.getTags().size()
+ );
+ assertEquals(
+ "Wrong tags on folder tagscope: " + ts1.getTags(),
+ 5, ts2.getTags().size()
+ );
+
+ // Each tag should crop up 3 times on the folder
+ // and twice for the subfolder
+ for(String tag : tags)
+ {
+ assertEquals(3, ts1.getTag(tag.toLowerCase()).getCount());
+ assertEquals(2, ts2.getTag(tag.toLowerCase()).getCount());
+ }
+
+ // All done
+ tx.commit();
+ }
+
private UserTransaction waitForActionExecution(UserTransaction txn)
throws Exception
diff --git a/source/java/org/alfresco/repo/tagging/UpdateTagScopesActionExecuter.java b/source/java/org/alfresco/repo/tagging/UpdateTagScopesActionExecuter.java
index a8b9437dde..416c42fcea 100644
--- a/source/java/org/alfresco/repo/tagging/UpdateTagScopesActionExecuter.java
+++ b/source/java/org/alfresco/repo/tagging/UpdateTagScopesActionExecuter.java
@@ -22,8 +22,10 @@ import java.io.Serializable;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
+import java.util.HashSet;
import java.util.List;
import java.util.Map;
+import java.util.Set;
import org.alfresco.model.ContentModel;
import org.alfresco.repo.action.ParameterDefinitionImpl;
@@ -87,7 +89,16 @@ public class UpdateTagScopesActionExecuter extends ActionExecuterAbstractBase
/** What's the largest number of updates we should claim for a tag scope in one transaction? */
private static final int tagUpdateBatchSize = 100;
-
+
+ // For searching
+ private static final String noderefPath =
+ TaggingServiceImpl.TAGGING_AUDIT_ROOT_PATH + "/" +
+ TaggingServiceImpl.TAGGING_AUDIT_KEY_NODEREF + "/value";
+ private static final String tagsPath =
+ TaggingServiceImpl.TAGGING_AUDIT_ROOT_PATH + "/" +
+ TaggingServiceImpl.TAGGING_AUDIT_KEY_TAGS + "/value";
+
+
/**
* Set the node service
*
@@ -174,10 +185,7 @@ public class UpdateTagScopesActionExecuter extends ActionExecuterAbstractBase
// to worry as they'll handle the update for us!
try
{
- QName lockQName = QName.createQName("TagScope_" + tagScope.toString());
- String lock = jobLockService.getLock(
- lockQName, 2500, 0, 0
- );
+ String lock = lockTagScope(tagScope);
// If we got here, we're the only thread currently
// processing this tag scope
@@ -236,7 +244,7 @@ public class UpdateTagScopesActionExecuter extends ActionExecuterAbstractBase
);
// We're done with this tag scope
- jobLockService.releaseLock(lock, lockQName);
+ unlockTagScope(tagScope, lock);
} catch(LockAcquisitionException e) {}
// Now proceed to the next tag scope
@@ -257,19 +265,13 @@ public class UpdateTagScopesActionExecuter extends ActionExecuterAbstractBase
*/
private List searchForUpdates(final NodeRef tagScopeNode, final Map updates)
{
- final String noderefPath =
- TaggingServiceImpl.TAGGING_AUDIT_ROOT_PATH + "/" +
- TaggingServiceImpl.TAGGING_AUDIT_KEY_NODEREF + "/value";
- final String tagsPath =
- TaggingServiceImpl.TAGGING_AUDIT_ROOT_PATH + "/" +
- TaggingServiceImpl.TAGGING_AUDIT_KEY_TAGS + "/value";
-
// Build the query
- AuditQueryParameters params = new AuditQueryParameters();
+ final AuditQueryParameters params = new AuditQueryParameters();
params.setApplicationName(TaggingServiceImpl.TAGGING_AUDIT_APPLICATION_NAME);
params.addSearchKey(noderefPath, tagScopeNode.toString());
- // Execute it
+ // Execute the query, in a new transaction
+ // (Avoid contention issues with repeated runs / updates)
final List ids = new ArrayList();
auditService.auditQuery(new AuditQueryCallback() {
@Override
@@ -412,6 +414,92 @@ public class UpdateTagScopesActionExecuter extends ActionExecuterAbstractBase
contentWriter.putContent(tagContent);
}
}
+
+ /**
+ * Checks several batches of updates in the Audit event log,
+ * and returns the list of Tag Scope Node References found there.
+ * You should generally call this action to process the list of
+ * tag nodes before re-calling this method.
+ * You may need to call this method several times if lots of work
+ * is backed up, when an empty list is returned then you know
+ * that all work is handled.
+ */
+ public List searchForTagScopesPendingUpdates()
+ {
+ final Set tagNodesStrs = new HashSet();
+
+ // We want all entries for tagging
+ final AuditQueryParameters params = new AuditQueryParameters();
+ params.setApplicationName(TaggingServiceImpl.TAGGING_AUDIT_APPLICATION_NAME);
+
+ // Execute the query, in a new transaction
+ // (Avoid contention issues with repeated runs / updates)
+ transactionService.getRetryingTransactionHelper().doInTransaction(
+ new RetryingTransactionCallback() {
+ public Void execute() throws Throwable {
+ auditService.auditQuery(new AuditQueryCallback() {
+ @Override
+ public boolean valuesRequired() {
+ return true;
+ }
+
+ @Override
+ public boolean handleAuditEntryError(Long entryId, String errorMsg,
+ Throwable error) {
+ logger.warn("Error fetching tagging update entry - " + errorMsg, error);
+ // Keep trying
+ return true;
+ }
+
+ @Override
+ public boolean handleAuditEntry(Long entryId, String applicationName,
+ String user, long time, Map values) {
+ // Save the NodeRef
+ if(values.containsKey(noderefPath))
+ {
+ String nodeRefStr = (String)values.get(noderefPath);
+ if(! tagNodesStrs.contains(nodeRefStr))
+ tagNodesStrs.add(nodeRefStr);
+ }
+ else
+ {
+ logger.warn("Unexpected Tag Scope update entry of " + values);
+ }
+
+ // Next entry please!
+ return true;
+ }
+ }, params, 4*tagUpdateBatchSize);
+ return null;
+ }
+ }, false, true
+ );
+
+ // Turn it into NodeRefs
+ List tagNodes = new ArrayList();
+ for(String nodeRefStr : tagNodesStrs)
+ {
+ tagNodes.add( new NodeRef(nodeRefStr) );
+ }
+ return tagNodes;
+ }
+
+ private QName tagScopeToLockQName(NodeRef tagScope)
+ {
+ QName lockQName = QName.createQName("TagScope_" + tagScope.toString());
+ return lockQName;
+ }
+ protected String lockTagScope(NodeRef tagScope)
+ {
+ String lock = jobLockService.getLock(
+ tagScopeToLockQName(tagScope), 2500, 0, 0
+ );
+ return lock;
+ }
+ protected void unlockTagScope(NodeRef tagScope, String lockToken)
+ {
+ jobLockService.releaseLock(lockToken, tagScopeToLockQName(tagScope));
+ }
/**
* @see org.alfresco.repo.action.ParameterizedItemAbstractBase#addParameterDefinitions(java.util.List)
@@ -421,5 +509,4 @@ public class UpdateTagScopesActionExecuter extends ActionExecuterAbstractBase
{
paramList.add(new ParameterDefinitionImpl(PARAM_TAG_SCOPES, DataTypeDefinition.ANY, true, getParamDisplayLabel(PARAM_TAG_SCOPES)));
}
-
}
diff --git a/source/java/org/alfresco/repo/tagging/UpdateTagScopesQuartzJob.java b/source/java/org/alfresco/repo/tagging/UpdateTagScopesQuartzJob.java
new file mode 100644
index 0000000000..22239b1b47
--- /dev/null
+++ b/source/java/org/alfresco/repo/tagging/UpdateTagScopesQuartzJob.java
@@ -0,0 +1,112 @@
+/*
+ * Copyright (C) 2005-2010 Alfresco Software Limited.
+ *
+ * This file is part of Alfresco
+ *
+ * Alfresco is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * Alfresco is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with Alfresco. If not, see .
+ */
+package org.alfresco.repo.tagging;
+import java.io.Serializable;
+import java.util.ArrayList;
+
+import org.alfresco.error.AlfrescoRuntimeException;
+import org.alfresco.repo.security.authentication.AuthenticationUtil;
+import org.alfresco.service.cmr.action.Action;
+import org.alfresco.service.cmr.action.ActionService;
+import org.alfresco.service.cmr.repository.NodeRef;
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.quartz.Job;
+import org.quartz.JobDataMap;
+import org.quartz.JobExecutionContext;
+import org.quartz.JobExecutionException;
+
+/**
+ * Finds Tag Scope updates that haven't been applied, and triggers
+ * then to be run.
+ * Works with the {@link UpdateTagScopesActionExecuter}, and is typically
+ * run after server restart.
+ * Also refer to scheduled-jobs-context.xml for more information
+ */
+public class UpdateTagScopesQuartzJob implements Job {
+ private static Log logger = LogFactory.getLog(UpdateTagScopesQuartzJob.class);
+
+ public UpdateTagScopesQuartzJob() {}
+
+ /**
+ * Finds the tag scopes to be updated, and has them worked on
+ */
+ public void execute(JobExecutionContext context) throws JobExecutionException
+ {
+ JobDataMap jobData = context.getJobDetail().getJobDataMap();
+
+ // Extract out our beans
+ Object actionServiceO = jobData.get("actionService");
+ if(actionServiceO == null || !(actionServiceO instanceof ActionService))
+ {
+ throw new AlfrescoRuntimeException(
+ "UpdateTagScopesQuartzJob data must contain a valid 'actionService' reference");
+ }
+
+ Object updateTagsActionO = jobData.get("updateTagsAction");
+ if(updateTagsActionO == null || !(updateTagsActionO instanceof UpdateTagScopesActionExecuter))
+ {
+ throw new AlfrescoRuntimeException(
+ "UpdateTagScopesQuartzJob data must contain a valid 'updateTagsAction' reference");
+ }
+
+ ActionService actionService = (ActionService)actionServiceO;
+ UpdateTagScopesActionExecuter updateTagsAction = (UpdateTagScopesActionExecuter)updateTagsActionO;
+
+ // Do the work
+ execute(actionService, updateTagsAction);
+ }
+
+ protected void execute(final ActionService actionService, final UpdateTagScopesActionExecuter updateTagsAction)
+ {
+ // Process
+ final ArrayList tagNodes = new ArrayList();
+ while(true)
+ {
+ // Fetch the list of changes
+ AuthenticationUtil.runAs(new AuthenticationUtil.RunAsWork()
+ {
+ public Void doWork() throws Exception
+ {
+ tagNodes.clear();
+ tagNodes.addAll(
+ updateTagsAction.searchForTagScopesPendingUpdates()
+ );
+ return null;
+ }
+ }, AuthenticationUtil.getSystemUserName()
+ );
+
+ // Log what we found
+ if(logger.isDebugEnabled())
+ {
+ logger.debug("Checked for tag scopes with pending tag updates, found " + tagNodes);
+ }
+
+ if(tagNodes.size() == 0)
+ break;
+
+ // Have the action run for these tag scope nodes
+ // Needs to run synchronously
+ Action action = actionService.createAction(UpdateTagScopesActionExecuter.NAME);
+ action.setParameterValue(UpdateTagScopesActionExecuter.PARAM_TAG_SCOPES, (Serializable)tagNodes);
+ actionService.executeAction(action, null, false, false);
+ }
+ }
+}