mirror of
https://github.com/Alfresco/alfresco-community-repo.git
synced 2025-08-14 17:58:59 +00:00
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
This commit is contained in:
@@ -482,5 +482,38 @@
|
|||||||
</property>
|
</property>
|
||||||
|
|
||||||
</bean>
|
</bean>
|
||||||
|
|
||||||
|
<!-- Tagging Service Scope Updates -->
|
||||||
|
<!-- (Catches any updates queued but not executed around repo shutdown -->
|
||||||
|
|
||||||
|
<bean id="taggingStartupJobDetail" class="org.springframework.scheduling.quartz.JobDetailBean">
|
||||||
|
<property name="jobClass">
|
||||||
|
<value>org.alfresco.repo.tagging.UpdateTagScopesQuartzJob</value>
|
||||||
|
</property>
|
||||||
|
<property name="jobDataAsMap">
|
||||||
|
<map>
|
||||||
|
<entry key="updateTagsAction">
|
||||||
|
<ref bean="update-tagscope" />
|
||||||
|
</entry>
|
||||||
|
<entry key="actionService">
|
||||||
|
<ref bean="ActionService" />
|
||||||
|
</entry>
|
||||||
|
</map>
|
||||||
|
</property>
|
||||||
|
</bean>
|
||||||
|
<bean id="taggingStartupTrigger" class="org.alfresco.util.TriggerBean">
|
||||||
|
<property name="jobDetail">
|
||||||
|
<ref bean="taggingStartupJobDetail" />
|
||||||
|
</property>
|
||||||
|
<property name="scheduler">
|
||||||
|
<ref bean="schedulerFactory" />
|
||||||
|
</property>
|
||||||
|
<property name="startDelayMinutes">
|
||||||
|
<value>2</value>
|
||||||
|
</property>
|
||||||
|
<property name="repeatIntervalMinutes">
|
||||||
|
<value>60</value> <!-- Periodically catch any that get missed -->
|
||||||
|
</property>
|
||||||
|
</bean>
|
||||||
|
|
||||||
</beans>
|
</beans>
|
||||||
|
@@ -26,44 +26,55 @@ import java.util.Map;
|
|||||||
|
|
||||||
import javax.transaction.UserTransaction;
|
import javax.transaction.UserTransaction;
|
||||||
|
|
||||||
|
import junit.framework.TestCase;
|
||||||
|
|
||||||
import org.alfresco.model.ContentModel;
|
import org.alfresco.model.ContentModel;
|
||||||
import org.alfresco.repo.jscript.ClasspathScriptLocation;
|
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.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.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.coci.CheckOutCheckInService;
|
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.CopyService;
|
||||||
import org.alfresco.service.cmr.repository.NodeRef;
|
import org.alfresco.service.cmr.repository.NodeRef;
|
||||||
import org.alfresco.service.cmr.repository.NodeService;
|
import org.alfresco.service.cmr.repository.NodeService;
|
||||||
import org.alfresco.service.cmr.repository.ScriptLocation;
|
import org.alfresco.service.cmr.repository.ScriptLocation;
|
||||||
import org.alfresco.service.cmr.repository.ScriptService;
|
import org.alfresco.service.cmr.repository.ScriptService;
|
||||||
import org.alfresco.service.cmr.repository.StoreRef;
|
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.TagDetails;
|
||||||
import org.alfresco.service.cmr.tagging.TagScope;
|
import org.alfresco.service.cmr.tagging.TagScope;
|
||||||
import org.alfresco.service.cmr.tagging.TaggingService;
|
import org.alfresco.service.cmr.tagging.TaggingService;
|
||||||
import org.alfresco.service.namespace.NamespaceService;
|
import org.alfresco.service.namespace.NamespaceService;
|
||||||
import org.alfresco.service.namespace.QName;
|
import org.alfresco.service.namespace.QName;
|
||||||
import org.alfresco.service.transaction.TransactionService;
|
import org.alfresco.service.transaction.TransactionService;
|
||||||
import org.alfresco.util.BaseAlfrescoSpringTest;
|
import org.alfresco.util.ApplicationContextHelper;
|
||||||
import org.alfresco.util.GUID;
|
import org.alfresco.util.GUID;
|
||||||
|
import org.springframework.context.ConfigurableApplicationContext;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Tagging service implementation unit test
|
* Tagging service implementation unit test
|
||||||
*
|
*
|
||||||
* @author Roy Wetherall
|
* @author Roy Wetherall
|
||||||
|
* @author Nick Burch
|
||||||
*/
|
*/
|
||||||
public class TaggingServiceImplTest extends BaseAlfrescoSpringTest
|
public class TaggingServiceImplTest extends TestCase
|
||||||
{
|
{
|
||||||
|
private static ConfigurableApplicationContext ctx =
|
||||||
|
(ConfigurableApplicationContext)ApplicationContextHelper.getApplicationContext();
|
||||||
|
|
||||||
/** Services */
|
/** Services */
|
||||||
private TaggingService taggingService;
|
private TaggingService taggingService;
|
||||||
|
private NodeService nodeService;
|
||||||
private CopyService copyService;
|
private CopyService copyService;
|
||||||
private CheckOutCheckInService checkOutCheckInService;
|
private CheckOutCheckInService checkOutCheckInService;
|
||||||
private ScriptService scriptService;
|
private ScriptService scriptService;
|
||||||
private PolicyComponent policyComponent;
|
private AuditService auditService;
|
||||||
|
private ActionService actionService;
|
||||||
private ActionTrackingService actionTrackingService;
|
private ActionTrackingService actionTrackingService;
|
||||||
|
private TransactionService transactionService;
|
||||||
|
private AuthenticationComponent authenticationComponent;
|
||||||
|
|
||||||
private static StoreRef storeRef;
|
private static StoreRef storeRef;
|
||||||
private static NodeRef rootNode;
|
private static NodeRef rootNode;
|
||||||
@@ -84,32 +95,28 @@ public class TaggingServiceImplTest extends BaseAlfrescoSpringTest
|
|||||||
|
|
||||||
private static boolean init = false;
|
private static boolean init = false;
|
||||||
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void onSetUpBeforeTransaction() throws Exception
|
protected void setUp() throws Exception
|
||||||
{
|
{
|
||||||
super.onSetUpBeforeTransaction();
|
|
||||||
|
|
||||||
// Get services
|
// Get services
|
||||||
this.taggingService = (TaggingService)this.applicationContext.getBean("TaggingService");
|
this.taggingService = (TaggingService)ctx.getBean("TaggingService");
|
||||||
this.nodeService = (NodeService) this.applicationContext.getBean("NodeService");
|
this.nodeService = (NodeService) ctx.getBean("NodeService");
|
||||||
this.copyService = (CopyService) this.applicationContext.getBean("CopyService");
|
this.copyService = (CopyService) ctx.getBean("CopyService");
|
||||||
this.contentService = (ContentService) this.applicationContext.getBean("ContentService");
|
this.checkOutCheckInService = (CheckOutCheckInService) ctx.getBean("CheckoutCheckinService");
|
||||||
this.checkOutCheckInService = (CheckOutCheckInService) this.applicationContext.getBean("CheckoutCheckinService");
|
this.actionService = (ActionService)ctx.getBean("ActionService");
|
||||||
this.authenticationService = (MutableAuthenticationService) this.applicationContext.getBean("authenticationService");
|
this.transactionService = (TransactionService)ctx.getBean("transactionComponent");
|
||||||
this.actionService = (ActionService)this.applicationContext.getBean("ActionService");
|
this.auditService = (AuditService)ctx.getBean("auditService");
|
||||||
this.transactionService = (TransactionService)this.applicationContext.getBean("transactionComponent");
|
this.scriptService = (ScriptService)ctx.getBean("scriptService");
|
||||||
this.scriptService = (ScriptService)this.applicationContext.getBean("scriptService");
|
this.actionTrackingService = (ActionTrackingService)ctx.getBean("actionTrackingService");
|
||||||
this.policyComponent = (PolicyComponent)this.applicationContext.getBean("policyComponent");
|
this.authenticationComponent = (AuthenticationComponent)ctx.getBean("authenticationComponent");
|
||||||
this.actionTrackingService = (ActionTrackingService)this.applicationContext.getBean("actionTrackingService");
|
|
||||||
|
|
||||||
if (init == false)
|
if (init == false)
|
||||||
{
|
{
|
||||||
UserTransaction tx = this.transactionService.getUserTransaction();
|
UserTransaction tx = this.transactionService.getNonPropagatingUserTransaction();
|
||||||
tx.begin();
|
tx.begin();
|
||||||
|
|
||||||
// Authenticate as the system user
|
// Authenticate as the system user
|
||||||
AuthenticationComponent authenticationComponent = (AuthenticationComponent) this.applicationContext
|
|
||||||
.getBean("authenticationComponent");
|
|
||||||
authenticationComponent.setSystemUserAsCurrentUser();
|
authenticationComponent.setSystemUserAsCurrentUser();
|
||||||
|
|
||||||
// Create the store and get the root node
|
// Create the store and get the root node
|
||||||
@@ -133,14 +140,22 @@ public class TaggingServiceImplTest extends BaseAlfrescoSpringTest
|
|||||||
|
|
||||||
tx.commit();
|
tx.commit();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Create the folders and documents to be tagged
|
||||||
|
createTestDocumentsAndFolders();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@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
|
// Authenticate as the system user
|
||||||
AuthenticationComponent authenticationComponent = (AuthenticationComponent) this.applicationContext
|
|
||||||
.getBean("authenticationComponent");
|
|
||||||
authenticationComponent.setSystemUserAsCurrentUser();
|
authenticationComponent.setSystemUserAsCurrentUser();
|
||||||
|
|
||||||
String guid = GUID.generate();
|
String guid = GUID.generate();
|
||||||
@@ -183,9 +198,40 @@ public class TaggingServiceImplTest extends BaseAlfrescoSpringTest
|
|||||||
ContentModel.TYPE_CONTENT,
|
ContentModel.TYPE_CONTENT,
|
||||||
props).getChildRef();
|
props).getChildRef();
|
||||||
|
|
||||||
//tx.commit();
|
tx.commit();
|
||||||
setComplete();
|
}
|
||||||
endTransaction();
|
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()
|
public void testTagCRUD()
|
||||||
@@ -203,10 +249,7 @@ public class TaggingServiceImplTest extends BaseAlfrescoSpringTest
|
|||||||
this.taggingService.createTag(TaggingServiceImplTest.storeRef, TAG_1);
|
this.taggingService.createTag(TaggingServiceImplTest.storeRef, TAG_1);
|
||||||
this.taggingService.createTag(TaggingServiceImplTest.storeRef, UPPER_TAG);
|
this.taggingService.createTag(TaggingServiceImplTest.storeRef, UPPER_TAG);
|
||||||
|
|
||||||
//setComplete();
|
|
||||||
//endTransaction();
|
|
||||||
tx.commit();
|
tx.commit();
|
||||||
|
|
||||||
tx = this.transactionService.getUserTransaction();
|
tx = this.transactionService.getUserTransaction();
|
||||||
tx.begin();
|
tx.begin();
|
||||||
|
|
||||||
@@ -905,7 +948,7 @@ public class TaggingServiceImplTest extends BaseAlfrescoSpringTest
|
|||||||
UserTransaction tx = this.transactionService.getUserTransaction();
|
UserTransaction tx = this.transactionService.getUserTransaction();
|
||||||
tx.begin();
|
tx.begin();
|
||||||
|
|
||||||
Map 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);
|
||||||
model.put("subFolder", this.subFolder);
|
model.put("subFolder", this.subFolder);
|
||||||
model.put("document", this.document);
|
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");
|
ScriptLocation location = new ClasspathScriptLocation("org/alfresco/repo/tagging/script/test_taggingService.js");
|
||||||
this.scriptService.executeScript(location, model);
|
this.scriptService.executeScript(location, model);
|
||||||
|
|
||||||
|
// Let the script run
|
||||||
|
tx = waitForActionExecution(tx);
|
||||||
tx.commit();
|
tx.commit();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -950,9 +995,8 @@ public class TaggingServiceImplTest extends BaseAlfrescoSpringTest
|
|||||||
tx = waitForActionExecution(tx);
|
tx = waitForActionExecution(tx);
|
||||||
this.taggingService.addTag(this.folder, TAG_1);
|
this.taggingService.addTag(this.folder, TAG_1);
|
||||||
tx = waitForActionExecution(tx);
|
tx = waitForActionExecution(tx);
|
||||||
tx.commit();
|
|
||||||
|
|
||||||
Map 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);
|
||||||
model.put("subFolder", this.subFolder);
|
model.put("subFolder", this.subFolder);
|
||||||
model.put("document", this.document);
|
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");
|
ScriptLocation location = new ClasspathScriptLocation("org/alfresco/repo/tagging/script/test_taggingService.js");
|
||||||
this.scriptService.executeScript(location, model);
|
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<NodeRef> 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<Thread> threads = new ArrayList<Thread>();
|
||||||
|
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<Void>() {
|
||||||
|
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)
|
private UserTransaction waitForActionExecution(UserTransaction txn)
|
||||||
throws Exception
|
throws Exception
|
||||||
|
@@ -22,8 +22,10 @@ import java.io.Serializable;
|
|||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
|
import java.util.HashSet;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
import java.util.Set;
|
||||||
|
|
||||||
import org.alfresco.model.ContentModel;
|
import org.alfresco.model.ContentModel;
|
||||||
import org.alfresco.repo.action.ParameterDefinitionImpl;
|
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? */
|
/** What's the largest number of updates we should claim for a tag scope in one transaction? */
|
||||||
private static final int tagUpdateBatchSize = 100;
|
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
|
* Set the node service
|
||||||
*
|
*
|
||||||
@@ -174,10 +185,7 @@ public class UpdateTagScopesActionExecuter extends ActionExecuterAbstractBase
|
|||||||
// to worry as they'll handle the update for us!
|
// to worry as they'll handle the update for us!
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
QName lockQName = QName.createQName("TagScope_" + tagScope.toString());
|
String lock = lockTagScope(tagScope);
|
||||||
String lock = jobLockService.getLock(
|
|
||||||
lockQName, 2500, 0, 0
|
|
||||||
);
|
|
||||||
|
|
||||||
// If we got here, we're the only thread currently
|
// If we got here, we're the only thread currently
|
||||||
// processing this tag scope
|
// processing this tag scope
|
||||||
@@ -236,7 +244,7 @@ public class UpdateTagScopesActionExecuter extends ActionExecuterAbstractBase
|
|||||||
);
|
);
|
||||||
|
|
||||||
// We're done with this tag scope
|
// We're done with this tag scope
|
||||||
jobLockService.releaseLock(lock, lockQName);
|
unlockTagScope(tagScope, lock);
|
||||||
} catch(LockAcquisitionException e) {}
|
} catch(LockAcquisitionException e) {}
|
||||||
|
|
||||||
// Now proceed to the next tag scope
|
// Now proceed to the next tag scope
|
||||||
@@ -257,19 +265,13 @@ public class UpdateTagScopesActionExecuter extends ActionExecuterAbstractBase
|
|||||||
*/
|
*/
|
||||||
private List<Long> searchForUpdates(final NodeRef tagScopeNode, final Map<String,Integer> updates)
|
private List<Long> searchForUpdates(final NodeRef tagScopeNode, final Map<String,Integer> 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
|
// Build the query
|
||||||
AuditQueryParameters params = new AuditQueryParameters();
|
final AuditQueryParameters params = new AuditQueryParameters();
|
||||||
params.setApplicationName(TaggingServiceImpl.TAGGING_AUDIT_APPLICATION_NAME);
|
params.setApplicationName(TaggingServiceImpl.TAGGING_AUDIT_APPLICATION_NAME);
|
||||||
params.addSearchKey(noderefPath, tagScopeNode.toString());
|
params.addSearchKey(noderefPath, tagScopeNode.toString());
|
||||||
|
|
||||||
// Execute it
|
// Execute the query, in a new transaction
|
||||||
|
// (Avoid contention issues with repeated runs / updates)
|
||||||
final List<Long> ids = new ArrayList<Long>();
|
final List<Long> ids = new ArrayList<Long>();
|
||||||
auditService.auditQuery(new AuditQueryCallback() {
|
auditService.auditQuery(new AuditQueryCallback() {
|
||||||
@Override
|
@Override
|
||||||
@@ -412,6 +414,92 @@ public class UpdateTagScopesActionExecuter extends ActionExecuterAbstractBase
|
|||||||
contentWriter.putContent(tagContent);
|
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<NodeRef> searchForTagScopesPendingUpdates()
|
||||||
|
{
|
||||||
|
final Set<String> tagNodesStrs = new HashSet<String>();
|
||||||
|
|
||||||
|
// 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<Void>() {
|
||||||
|
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<String, Serializable> 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<NodeRef> tagNodes = new ArrayList<NodeRef>();
|
||||||
|
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)
|
* @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)));
|
paramList.add(new ParameterDefinitionImpl(PARAM_TAG_SCOPES, DataTypeDefinition.ANY, true, getParamDisplayLabel(PARAM_TAG_SCOPES)));
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@@ -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 <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
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<NodeRef> tagNodes = new ArrayList<NodeRef>();
|
||||||
|
while(true)
|
||||||
|
{
|
||||||
|
// Fetch the list of changes
|
||||||
|
AuthenticationUtil.runAs(new AuthenticationUtil.RunAsWork<Void>()
|
||||||
|
{
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
Reference in New Issue
Block a user