[skip tests]Merge pull request #1407 from Alfresco/merge-3.2/MNT-22310

Merge 3.2/mnt 22310
This commit is contained in:
Alexandru-Eusebiu Epure
2021-04-09 16:50:12 +03:00
committed by GitHub
4 changed files with 109 additions and 82 deletions

View File

@@ -52,6 +52,11 @@ rm.autocompletesuggestion.nodeParameterSuggester.aspectsAndTypes=rma:record,cm:c
# #
rm.dispositionlifecycletrigger.cronexpression=0 0/5 * * * ? rm.dispositionlifecycletrigger.cronexpression=0 0/5 * * * ?
#
# Global RM retention lifecycle cron job execution batch size
#
rm.dispositionlifecycletrigger.batchsize=500
# #
# Global RM notify of records due for review cron job expression # Global RM notify of records due for review cron job expression
# #

View File

@@ -80,6 +80,7 @@
<property name="searchService" ref="searchService" /> <property name="searchService" ref="searchService" />
<property name="personService" ref="personService" /> <property name="personService" ref="personService" />
<property name="recordsManagementActionService" ref="recordsManagementActionService" /> <property name="recordsManagementActionService" ref="recordsManagementActionService" />
<property name="batchSize" value="${rm.dispositionlifecycletrigger.batchsize}"/>
</bean> </bean>
<bean id="scheduledDispositionLifecyceleSchedulerAccessor" class="org.alfresco.schedule.AlfrescoSchedulerAccessorBean"> <bean id="scheduledDispositionLifecyceleSchedulerAccessor" class="org.alfresco.schedule.AlfrescoSchedulerAccessorBean">

View File

@@ -60,6 +60,9 @@ public class DispositionLifecycleJobExecuter extends RecordsManagementJobExecute
/** logger */ /** logger */
private static Log logger = LogFactory.getLog(DispositionLifecycleJobExecuter.class); private static Log logger = LogFactory.getLog(DispositionLifecycleJobExecuter.class);
/** batching properties */
private int batchSize;
/** list of disposition actions to automatically execute */ /** list of disposition actions to automatically execute */
private List<String> dispositionActions; private List<String> dispositionActions;
@@ -88,6 +91,11 @@ public class DispositionLifecycleJobExecuter extends RecordsManagementJobExecute
this.dispositionActions = dispositionActions; this.dispositionActions = dispositionActions;
} }
public void setBatchSize(int batchSize)
{
this.batchSize = batchSize;
}
/** /**
* @param recordsManagementActionService records management action service * @param recordsManagementActionService records management action service
*/ */
@@ -167,13 +175,14 @@ public class DispositionLifecycleJobExecuter extends RecordsManagementJobExecute
{ {
boolean hasMore = true; boolean hasMore = true;
int skipCount = 0; int skipCount = 0;
while(hasMore) while (hasMore)
{ {
SearchParameters params = new SearchParameters(); SearchParameters params = new SearchParameters();
params.addStore(StoreRef.STORE_REF_WORKSPACE_SPACESSTORE); params.addStore(StoreRef.STORE_REF_WORKSPACE_SPACESSTORE);
params.setLanguage(SearchService.LANGUAGE_FTS_ALFRESCO); params.setLanguage(SearchService.LANGUAGE_FTS_ALFRESCO);
params.setQuery(getQuery()); params.setQuery(getQuery());
params.setSkipCount(skipCount); params.setSkipCount(skipCount);
params.setMaxItems(batchSize);
// execute search // execute search
ResultSet results = searchService.query(params); ResultSet results = searchService.query(params);
@@ -188,13 +197,12 @@ public class DispositionLifecycleJobExecuter extends RecordsManagementJobExecute
} }
// process search results // process search results
for (NodeRef node : resultNodes) if (!resultNodes.isEmpty())
{ {
executeAction(node); executeAction(resultNodes);
} }
} }
} }
logger.debug("Job Finished"); logger.debug("Job Finished");
} }
catch (AlfrescoRuntimeException exception) catch (AlfrescoRuntimeException exception)
@@ -209,57 +217,52 @@ public class DispositionLifecycleJobExecuter extends RecordsManagementJobExecute
/** /**
* Helper method that executes a disposition action * Helper method that executes a disposition action
* *
* @param actionNode - the disposition action to execute * @param actionNodes - the disposition actions to execute
*/ */
private void executeAction(final NodeRef actionNode) private void executeAction(final List<NodeRef> actionNodes)
{ {
RetryingTransactionCallback<Boolean> processTranCB = new RetryingTransactionCallback<Boolean>() RetryingTransactionCallback<Boolean> processTranCB = () -> {
{ for (NodeRef actionNode : actionNodes)
public Boolean execute()
{ {
final String dispAction = (String) nodeService.getProperty(actionNode, if (nodeService.exists(actionNode))
RecordsManagementModel.PROP_DISPOSITION_ACTION);
// Run disposition action
if (dispAction != null && dispositionActions.contains(dispAction))
{ {
ChildAssociationRef parent = nodeService.getPrimaryParent(actionNode); final String dispAction = (String) nodeService
if (parent.getTypeQName().equals(RecordsManagementModel.ASSOC_NEXT_DISPOSITION_ACTION)) .getProperty(actionNode, RecordsManagementModel.PROP_DISPOSITION_ACTION);
// Run disposition action
if (dispAction != null && dispositionActions.contains(dispAction))
{ {
Map<String, Serializable> props = new HashMap<>(1); ChildAssociationRef parent = nodeService.getPrimaryParent(actionNode);
props.put(RMDispositionActionExecuterAbstractBase.PARAM_NO_ERROR_CHECK, if (parent.getTypeQName().equals(RecordsManagementModel.ASSOC_NEXT_DISPOSITION_ACTION))
Boolean.FALSE);
try
{ {
// execute disposition action Map<String, Serializable> props = new HashMap<>(1);
recordsManagementActionService.executeRecordsManagementAction( props.put(RMDispositionActionExecuterAbstractBase.PARAM_NO_ERROR_CHECK, Boolean.FALSE);
parent.getParentRef(), dispAction, props);
if (logger.isDebugEnabled()) try
{ {
logger.debug("Processed action: " + dispAction + "on" + parent); // execute disposition action
recordsManagementActionService
.executeRecordsManagementAction(parent.getParentRef(), dispAction, props);
if (logger.isDebugEnabled())
{
logger.debug("Processed action: " + dispAction + "on" + parent);
}
} }
} catch (AlfrescoRuntimeException exception)
catch (AlfrescoRuntimeException exception)
{
if (logger.isDebugEnabled())
{ {
logger.debug(exception); if (logger.isDebugEnabled())
{
logger.debug(exception);
}
} }
} }
} }
} }
return Boolean.TRUE;
} }
return Boolean.TRUE;
}; };
retryingTransactionHelper.doInTransaction(processTranCB, false, true);
// if exists
if (nodeService.exists(actionNode))
{
retryingTransactionHelper.doInTransaction(processTranCB);
}
} }
public PersonService getPersonService() public PersonService getPersonService()

View File

@@ -33,6 +33,7 @@ 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.eq; import static org.mockito.Matchers.eq;
import static org.mockito.Mockito.doAnswer;
import static org.mockito.Mockito.doReturn; import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.mock; import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.times; import static org.mockito.Mockito.times;
@@ -56,8 +57,8 @@ import org.junit.Before;
import org.junit.Test; import org.junit.Test;
import org.mockito.ArgumentCaptor; import org.mockito.ArgumentCaptor;
import org.mockito.InjectMocks; import org.mockito.InjectMocks;
import org.mockito.Matchers;
import org.mockito.Mock; import org.mockito.Mock;
import org.mockito.invocation.InvocationOnMock;
import org.mockito.stubbing.Answer; import org.mockito.stubbing.Answer;
/** /**
@@ -73,7 +74,7 @@ public class DispositionLifecycleJobExecuterUnitTest extends BaseUnitTest
private static final String RETAIN = "retain"; private static final String RETAIN = "retain";
private static final String DESTROY = "destroy"; private static final String DESTROY = "destroy";
/** test query snipit */ /** test query snippet */
private static final String QUERY = "\"" + CUTOFF + "\" OR \"" + RETAIN + "\""; private static final String QUERY = "\"" + CUTOFF + "\" OR \"" + RETAIN + "\"";
/** mocked result set */ /** mocked result set */
@@ -91,9 +92,18 @@ public class DispositionLifecycleJobExecuterUnitTest extends BaseUnitTest
{ {
super.before(); super.before();
// Because of the fix implemented in MNT-22310, a new setup for retrying transaction helper is required.
Answer<Object> doInTransactionAnswer = invocation -> {
RetryingTransactionCallback callback = (RetryingTransactionCallback)invocation.getArguments()[0];
return callback.execute();
};
doAnswer(doInTransactionAnswer).when(mockedRetryingTransactionHelper).doInTransaction(any(RetryingTransactionCallback.class),
Matchers.anyBoolean(), Matchers.anyBoolean());
// setup data // setup data
List<String> dispositionActions = buildList(CUTOFF, RETAIN); List<String> dispositionActions = buildList(CUTOFF, RETAIN);
executer.setDispositionActions(dispositionActions); executer.setDispositionActions(dispositionActions);
executer.setBatchSize(1);
// setup interactions // setup interactions
doReturn(mockedResultSet).when(mockedSearchService).query(any(SearchParameters.class)); doReturn(mockedResultSet).when(mockedSearchService).query(any(SearchParameters.class));
@@ -102,14 +112,15 @@ public class DispositionLifecycleJobExecuterUnitTest extends BaseUnitTest
/** /**
* Helper method to verify that the query has been executed and closed * Helper method to verify that the query has been executed and closed
* @param numberOfInvocation number of times the query has been executed and closed
*/ */
private void verifyQuery() private void verifyQueryTimes(int numberOfInvocation)
{ {
ArgumentCaptor<SearchParameters> paramsCaptor = ArgumentCaptor.forClass(SearchParameters.class); ArgumentCaptor<SearchParameters> paramsCaptor = ArgumentCaptor.forClass(SearchParameters.class);
verify(mockedSearchService, times(1)).query(paramsCaptor.capture()); verify(mockedSearchService, times(numberOfInvocation)).query(paramsCaptor.capture());
assertTrue(paramsCaptor.getValue().getQuery().contains(QUERY)); assertTrue(paramsCaptor.getValue().getQuery().contains(QUERY));
verify(mockedResultSet, times(1)).getNodeRefs(); verify(mockedResultSet, times(numberOfInvocation)).getNodeRefs();
verify(mockedResultSet, times(1)).close(); verify(mockedResultSet, times(numberOfInvocation)).close();
} }
/** /**
@@ -127,7 +138,7 @@ public class DispositionLifecycleJobExecuterUnitTest extends BaseUnitTest
// then // then
// ensure the query is executed and closed // ensure the query is executed and closed
verifyQuery(); verifyQueryTimes(1);
// ensure nothing else happens becuase we have no results // ensure nothing else happens becuase we have no results
verifyZeroInteractions(mockedNodeService, mockedRecordFolderService, mockedRetryingTransactionHelper); verifyZeroInteractions(mockedNodeService, mockedRecordFolderService, mockedRetryingTransactionHelper);
@@ -143,24 +154,31 @@ public class DispositionLifecycleJobExecuterUnitTest extends BaseUnitTest
// test data // test data
NodeRef node1 = generateNodeRef(); NodeRef node1 = generateNodeRef();
NodeRef node2 = generateNodeRef(); NodeRef node2 = generateNodeRef();
List<NodeRef> nodeRefs = buildList(node1, node2);
// given // given
doReturn(nodeRefs).when(mockedResultSet).getNodeRefs();
doReturn(DESTROY).when(mockedNodeService).getProperty(node1, RecordsManagementModel.PROP_DISPOSITION_ACTION); doReturn(DESTROY).when(mockedNodeService).getProperty(node1, RecordsManagementModel.PROP_DISPOSITION_ACTION);
doReturn(DESTROY).when(mockedNodeService).getProperty(node2, RecordsManagementModel.PROP_DISPOSITION_ACTION); doReturn(DESTROY).when(mockedNodeService).getProperty(node2, RecordsManagementModel.PROP_DISPOSITION_ACTION);
when(mockedResultSet.getNodeRefs())
.thenReturn(buildList(node1))
.thenReturn(buildList(node2));
when(mockedResultSet.hasMore())
.thenReturn(true)
.thenReturn(false);
// when // when
executer.executeImpl(); executer.executeImpl();
// then // then
// ensure the query is executed and closed // ensure the query is executed and closed
verifyQuery(); verifyQueryTimes(2);
// ensure work is executed in transaction for each node processed // ensure work is executed in transaction for each node processed
verify(mockedNodeService, times(2)).exists(any(NodeRef.class)); verify(mockedNodeService, times(2)).exists(any(NodeRef.class));
verify(mockedRetryingTransactionHelper, times(2)).<Object>doInTransaction(any(RetryingTransactionCallback.class)); verify(mockedRetryingTransactionHelper, times(2)).doInTransaction(any(RetryingTransactionCallback.class),
Matchers.anyBoolean(), Matchers.anyBoolean());
// ensure each node is process correctly // ensure each node is process correctly
verify(mockedNodeService, times(1)).getProperty(node1, RecordsManagementModel.PROP_DISPOSITION_ACTION); verify(mockedNodeService, times(1)).getProperty(node1, RecordsManagementModel.PROP_DISPOSITION_ACTION);
@@ -191,14 +209,14 @@ public class DispositionLifecycleJobExecuterUnitTest extends BaseUnitTest
// then // then
// ensure the query is executed and closed // ensure the query is executed and closed
verifyQuery(); verifyQueryTimes(1);
// ensure the node exist check is made for the node // ensure the node exist check is made for the node
verify(mockedNodeService, times(1)).exists(any(NodeRef.class)); verify(mockedNodeService, times(1)).exists(any(NodeRef.class));
// ensure no more interactions // ensure no more interactions
verifyNoMoreInteractions(mockedNodeService); verifyNoMoreInteractions(mockedNodeService);
verifyZeroInteractions(mockedRecordsManagementActionService, mockedRetryingTransactionHelper); verifyZeroInteractions(mockedRecordsManagementActionService);
} }
/** /**
@@ -211,27 +229,33 @@ public class DispositionLifecycleJobExecuterUnitTest extends BaseUnitTest
// test data // test data
NodeRef node1 = generateNodeRef(); NodeRef node1 = generateNodeRef();
NodeRef node2 = generateNodeRef(); NodeRef node2 = generateNodeRef();
List<NodeRef> nodeRefs = buildList(node1, node2);
NodeRef parent = generateNodeRef(); NodeRef parent = generateNodeRef();
ChildAssociationRef parentAssoc = new ChildAssociationRef(ASSOC_NEXT_DISPOSITION_ACTION, parent, generateQName(), generateNodeRef()); ChildAssociationRef parentAssoc = new ChildAssociationRef(ASSOC_NEXT_DISPOSITION_ACTION, parent, generateQName(), generateNodeRef());
// given
doReturn(nodeRefs).when(mockedResultSet).getNodeRefs();
doReturn(CUTOFF).when(mockedNodeService).getProperty(node1, RecordsManagementModel.PROP_DISPOSITION_ACTION); doReturn(CUTOFF).when(mockedNodeService).getProperty(node1, RecordsManagementModel.PROP_DISPOSITION_ACTION);
doReturn(RETAIN).when(mockedNodeService).getProperty(node2, RecordsManagementModel.PROP_DISPOSITION_ACTION); doReturn(RETAIN).when(mockedNodeService).getProperty(node2, RecordsManagementModel.PROP_DISPOSITION_ACTION);
doReturn(parentAssoc).when(mockedNodeService).getPrimaryParent(any(NodeRef.class)); doReturn(parentAssoc).when(mockedNodeService).getPrimaryParent(any(NodeRef.class));
when(mockedResultSet.getNodeRefs())
.thenReturn(buildList(node1))
.thenReturn(buildList(node2));
when(mockedResultSet.hasMore())
.thenReturn(true)
.thenReturn(false);
// when // when
executer.executeImpl(); executer.executeImpl();
// then // then
// ensure the query is executed and closed // ensure the query is executed and closed
verifyQuery(); verifyQueryTimes(2);
// ensure work is executed in transaction for each node processed // ensure work is executed in transaction for each node processed
verify(mockedNodeService, times(2)).exists(any(NodeRef.class)); verify(mockedNodeService, times(2)).exists(any(NodeRef.class));
verify(mockedRetryingTransactionHelper, times(2)).<Object>doInTransaction(any(RetryingTransactionCallback.class)); verify(mockedRetryingTransactionHelper, times(2)).doInTransaction(any(RetryingTransactionCallback.class),
Matchers.anyBoolean(), Matchers.anyBoolean());
// ensure each node is process correctly // ensure each node is process correctly
// node1 // node1
@@ -279,32 +303,26 @@ public class DispositionLifecycleJobExecuterUnitTest extends BaseUnitTest
final NodeRef node4 = generateNodeRef(); final NodeRef node4 = generateNodeRef();
// mock the search service to return the right page // mock the search service to return the right page
when(mockedSearchService.query(any(SearchParameters.class))).thenAnswer( when(mockedSearchService.query(any(SearchParameters.class))).thenAnswer((Answer<ResultSet>) invocation -> {
new Answer<ResultSet>() SearchParameters params = invocation.getArgumentAt(0, SearchParameters.class);
if (params.getSkipCount() == 0)
{ {
@Override // mock first page
public ResultSet answer(InvocationOnMock invocation) ResultSet result1 = mock(ResultSet.class);
{ when(result1.getNodeRefs()).thenReturn(Arrays.asList(node1, node2));
SearchParameters params = invocation.getArgumentAt(0, SearchParameters.class); when(result1.hasMore()).thenReturn(true);
if (params.getSkipCount() == 0) return result1;
{ }
// mock first page else if (params.getSkipCount() == 2)
ResultSet result1 = mock(ResultSet.class); {
when(result1.getNodeRefs()).thenReturn(Arrays.asList(node1, node2)); // mock second page
when(result1.hasMore()).thenReturn(true); ResultSet result2 = mock(ResultSet.class);
return result1; when(result2.getNodeRefs()).thenReturn(Arrays.asList(node3, node4));
} when(result2.hasMore()).thenReturn(false);
else if (params.getSkipCount() == 2) return result2;
{ }
// mock second page throw new IndexOutOfBoundsException("Pagination did not stop after the 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 // call the service
executer.executeImpl(); executer.executeImpl();