Merge branch 'hotfix-2.5/MNT-18793_EligibleActionsNotExecuted' into 'release/V2.5.0.x'

MNT-18793 - added paginated navigation in the disposition action executer

See merge request !699
This commit is contained in:
Ana Bozianu
2017-11-24 14:27:03 +00:00
2 changed files with 149 additions and 64 deletions

View File

@@ -42,6 +42,7 @@ 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.StoreRef; import org.alfresco.service.cmr.repository.StoreRef;
import org.alfresco.service.cmr.search.ResultSet; import org.alfresco.service.cmr.search.ResultSet;
import org.alfresco.service.cmr.search.SearchParameters;
import org.alfresco.service.cmr.search.SearchService; import org.alfresco.service.cmr.search.SearchService;
import org.alfresco.service.cmr.security.PersonService; import org.alfresco.service.cmr.security.PersonService;
import org.apache.commons.logging.Log; import org.apache.commons.logging.Log;
@@ -122,7 +123,7 @@ public class DispositionLifecycleJobExecuter extends RecordsManagementJobExecute
{ {
StringBuilder sb = new StringBuilder(); StringBuilder sb = new StringBuilder();
sb.append("TYPE:\"rma:dispositionAction\" + "); sb.append("TYPE:\"rma:dispositionAction\" AND ");
sb.append("(@rma\\:dispositionAction:("); sb.append("(@rma\\:dispositionAction:(");
boolean bFirst = true; boolean bFirst = true;
@@ -164,10 +165,21 @@ public class DispositionLifecycleJobExecuter extends RecordsManagementJobExecute
if (dispositionActions != null && !dispositionActions.isEmpty()) if (dispositionActions != null && !dispositionActions.isEmpty())
{ {
boolean hasMore = true;
int skipCount = 0;
while(hasMore)
{
SearchParameters params = new SearchParameters();
params.addStore(StoreRef.STORE_REF_WORKSPACE_SPACESSTORE);
params.setLanguage(SearchService.LANGUAGE_FTS_ALFRESCO);
params.setQuery(getQuery());
params.setSkipCount(skipCount);
// execute search // execute search
ResultSet results = searchService.query(StoreRef.STORE_REF_WORKSPACE_SPACESSTORE, ResultSet results = searchService.query(params);
SearchService.LANGUAGE_FTS_ALFRESCO, getQuery());
List<NodeRef> resultNodes = results.getNodeRefs(); List<NodeRef> resultNodes = results.getNodeRefs();
hasMore = results.hasMore();
skipCount += resultNodes.size(); // increase by page size
results.close(); results.close();
if (logger.isDebugEnabled()) if (logger.isDebugEnabled())
@@ -178,19 +190,40 @@ public class DispositionLifecycleJobExecuter extends RecordsManagementJobExecute
// process search results // process search results
for (NodeRef node : resultNodes) for (NodeRef node : resultNodes)
{ {
final NodeRef currentNode = node; executeAction(node);
}
}
}
logger.debug("Job Finished");
}
catch (AlfrescoRuntimeException exception)
{
if (logger.isDebugEnabled())
{
logger.debug(exception);
}
}
}
/**
* Helper method that executes a disposition action
*
* @param actionNode - the disposition action to execute
*/
private void executeAction(final NodeRef actionNode)
{
RetryingTransactionCallback<Boolean> processTranCB = new RetryingTransactionCallback<Boolean>() RetryingTransactionCallback<Boolean> processTranCB = new RetryingTransactionCallback<Boolean>()
{ {
public Boolean execute() public Boolean execute()
{ {
final String dispAction = (String) nodeService.getProperty(currentNode, final String dispAction = (String) nodeService.getProperty(actionNode,
RecordsManagementModel.PROP_DISPOSITION_ACTION); RecordsManagementModel.PROP_DISPOSITION_ACTION);
// Run disposition action // Run disposition action
if (dispAction != null && dispositionActions.contains(dispAction)) if (dispAction != null && dispositionActions.contains(dispAction))
{ {
ChildAssociationRef parent = nodeService.getPrimaryParent(currentNode); ChildAssociationRef parent = nodeService.getPrimaryParent(actionNode);
if (parent.getTypeQName().equals(RecordsManagementModel.ASSOC_NEXT_DISPOSITION_ACTION)) if (parent.getTypeQName().equals(RecordsManagementModel.ASSOC_NEXT_DISPOSITION_ACTION))
{ {
Map<String, Serializable> props = new HashMap<String, Serializable>(1); Map<String, Serializable> props = new HashMap<String, Serializable>(1);
@@ -223,23 +256,11 @@ public class DispositionLifecycleJobExecuter extends RecordsManagementJobExecute
}; };
// if exists // if exists
if (nodeService.exists(currentNode)) if (nodeService.exists(actionNode))
{ {
retryingTransactionHelper.doInTransaction(processTranCB); retryingTransactionHelper.doInTransaction(processTranCB);
} }
} }
}
logger.debug("Job Finished");
}
catch (AlfrescoRuntimeException exception)
{
if (logger.isDebugEnabled())
{
logger.debug(exception);
}
}
}
public PersonService getPersonService() public PersonService getPersonService()
{ {

View File

@@ -29,17 +29,19 @@ package org.alfresco.module.org_alfresco_module_rm.job;
import static org.alfresco.module.org_alfresco_module_rm.test.util.AlfMock.generateQName; import static org.alfresco.module.org_alfresco_module_rm.test.util.AlfMock.generateQName;
import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;
import static org.mockito.Matchers.any; import static org.mockito.Matchers.any;
import static org.mockito.Matchers.anyMap; import static org.mockito.Matchers.anyMap;
import static org.mockito.Matchers.anyString;
import static org.mockito.Matchers.contains;
import static org.mockito.Matchers.eq; import static org.mockito.Matchers.eq;
import static org.mockito.Mockito.doReturn; import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.times; import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify; import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.verifyNoMoreInteractions; import static org.mockito.Mockito.verifyNoMoreInteractions;
import static org.mockito.Mockito.verifyZeroInteractions; import static org.mockito.Mockito.verifyZeroInteractions;
import static org.mockito.Mockito.when;
import java.util.Arrays;
import java.util.Collections; import java.util.Collections;
import java.util.List; import java.util.List;
@@ -48,13 +50,15 @@ import org.alfresco.module.org_alfresco_module_rm.test.util.BaseUnitTest;
import org.alfresco.repo.transaction.RetryingTransactionHelper.RetryingTransactionCallback; import org.alfresco.repo.transaction.RetryingTransactionHelper.RetryingTransactionCallback;
import org.alfresco.service.cmr.repository.ChildAssociationRef; import org.alfresco.service.cmr.repository.ChildAssociationRef;
import org.alfresco.service.cmr.repository.NodeRef; import org.alfresco.service.cmr.repository.NodeRef;
import org.alfresco.service.cmr.repository.StoreRef;
import org.alfresco.service.cmr.search.ResultSet; import org.alfresco.service.cmr.search.ResultSet;
import org.alfresco.service.cmr.search.SearchService; import org.alfresco.service.cmr.search.SearchParameters;
import org.junit.Before; import org.junit.Before;
import org.junit.Test; import org.junit.Test;
import org.mockito.ArgumentCaptor;
import org.mockito.InjectMocks; import org.mockito.InjectMocks;
import org.mockito.Mock; import org.mockito.Mock;
import org.mockito.invocation.InvocationOnMock;
import org.mockito.stubbing.Answer;
/** /**
* Disposition lifecycle job execution unit test. * Disposition lifecycle job execution unit test.
@@ -92,7 +96,8 @@ public class DispositionLifecycleJobExecuterUnitTest extends BaseUnitTest
executer.setDispositionActions(dispositionActions); executer.setDispositionActions(dispositionActions);
// setup interactions // setup interactions
doReturn(mockedResultSet).when(mockedSearchService).query(eq(StoreRef.STORE_REF_WORKSPACE_SPACESSTORE), eq(SearchService.LANGUAGE_FTS_ALFRESCO), anyString()); doReturn(mockedResultSet).when(mockedSearchService).query(any(SearchParameters.class));
when(mockedResultSet.hasMore()).thenReturn(false);
} }
/** /**
@@ -100,7 +105,9 @@ public class DispositionLifecycleJobExecuterUnitTest extends BaseUnitTest
*/ */
private void verifyQuery() private void verifyQuery()
{ {
verify(mockedSearchService, times(1)).query(eq(StoreRef.STORE_REF_WORKSPACE_SPACESSTORE), eq(SearchService.LANGUAGE_FTS_ALFRESCO), contains(QUERY)); ArgumentCaptor<SearchParameters> paramsCaptor = ArgumentCaptor.forClass(SearchParameters.class);
verify(mockedSearchService, times(1)).query(paramsCaptor.capture());
assertTrue(paramsCaptor.getValue().getQuery().contains(QUERY));
verify(mockedResultSet, times(1)).getNodeRefs(); verify(mockedResultSet, times(1)).getNodeRefs();
verify(mockedResultSet, times(1)).close(); verify(mockedResultSet, times(1)).close();
} }
@@ -249,7 +256,64 @@ public class DispositionLifecycleJobExecuterUnitTest extends BaseUnitTest
{ {
String actual = executer.getQuery(); String actual = executer.getQuery();
String expected = "TYPE:\"rma:dispositionAction\" + (@rma\\:dispositionAction:(\"cutoff\" OR \"retain\")) AND ISUNSET:\"rma:dispositionActionCompletedAt\" AND ( @rma\\:dispositionEventsEligible:true OR @rma\\:dispositionAsOf:[MIN TO NOW] ) "; String expected = "TYPE:\"rma:dispositionAction\" AND " +
"(@rma\\:dispositionAction:(\"cutoff\" OR \"retain\")) " +
"AND ISUNSET:\"rma:dispositionActionCompletedAt\" " +
"AND ( @rma\\:dispositionEventsEligible:true OR @rma\\:dispositionAsOf:[MIN TO NOW] ) ";
assertEquals(expected, actual); assertEquals(expected, actual);
} }
/**
* Given the maximum page of elements for search service is 2
* and search service finds more than one page of elements
* When the job executer runs
* Then the executer retrieves both pages and iterates all elements
*/
@Test
public void testPagination()
{
final NodeRef node1 = generateNodeRef();
final NodeRef node2 = generateNodeRef();
final NodeRef node3 = generateNodeRef();
final NodeRef node4 = generateNodeRef();
// mock the search service to return the right page
when(mockedSearchService.query(any(SearchParameters.class))).thenAnswer(
new Answer<ResultSet>()
{
@Override
public ResultSet answer(InvocationOnMock invocation)
{
SearchParameters params = invocation.getArgumentAt(0, SearchParameters.class);
if (params.getSkipCount() == 0)
{
// mock first page
ResultSet result1 = mock(ResultSet.class);
when(result1.getNodeRefs()).thenReturn(Arrays.asList(node1, node2));
when(result1.hasMore()).thenReturn(true);
return result1;
}
else if (params.getSkipCount() == 2)
{
// mock second page
ResultSet result2 = mock(ResultSet.class);
when(result2.getNodeRefs()).thenReturn(Arrays.asList(node3, node4));
when(result2.hasMore()).thenReturn(false);
return result2;
}
throw new IndexOutOfBoundsException("Pagination did not stop after the second page!");
}
});
// call the service
executer.executeImpl();
// check the loop iterated trough all the elements
verify(mockedNodeService).exists(node1);
verify(mockedNodeService).exists(node2);
verify(mockedNodeService).exists(node3);
verify(mockedNodeService).exists(node4);
verify(mockedSearchService, times(2)).query(any(SearchParameters.class));
}
} }