From 4ff69f599a4b6581148e21632a5fd6b79fd1d6d7 Mon Sep 17 00:00:00 2001 From: Nick Burch Date: Fri, 8 Oct 2010 15:47:36 +0000 Subject: [PATCH] ALF-5028 - More tag scope updates and unit tests. Shortly after the system is started, check for un-applied tag scope updates, and apply them. git-svn-id: https://svn.alfresco.com/repos/alfresco-enterprise/alfresco/HEAD/root@22997 c4b6b30b-aa2e-2d43-bbcb-ca4b014f7261 --- config/alfresco/scheduled-jobs-context.xml | 33 ++ .../repo/tagging/TaggingServiceImplTest.java | 304 +++++++++++++++--- .../UpdateTagScopesActionExecuter.java | 119 ++++++- .../tagging/UpdateTagScopesQuartzJob.java | 112 +++++++ 4 files changed, 516 insertions(+), 52 deletions(-) create mode 100644 source/java/org/alfresco/repo/tagging/UpdateTagScopesQuartzJob.java 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); + } + } +}