From a50e39259b9ee95605b80f58a586bddeb058bdaa Mon Sep 17 00:00:00 2001 From: Pavol Mederly Date: Tue, 15 May 2018 17:46:23 +0200 Subject: [PATCH] Tentatively fix db lockup by big certifications 2 More elaborate batching of campaign modifications; now when opening, closing and escalating of every stage, as well as when closing the whole campaign. --- .../impl/AccCertCaseOperationsHelper.java | 45 ++++---- .../impl/AccCertUpdateHelper.java | 105 ++++++++++-------- .../impl/CertificationManagerImpl.java | 16 +-- .../impl/ModificationsToExecute.java | 75 +++++++++++++ 4 files changed, 159 insertions(+), 82 deletions(-) create mode 100644 model/certification-impl/src/main/java/com/evolveum/midpoint/certification/impl/ModificationsToExecute.java diff --git a/model/certification-impl/src/main/java/com/evolveum/midpoint/certification/impl/AccCertCaseOperationsHelper.java b/model/certification-impl/src/main/java/com/evolveum/midpoint/certification/impl/AccCertCaseOperationsHelper.java index 6316d4b0610..66629daa037 100644 --- a/model/certification-impl/src/main/java/com/evolveum/midpoint/certification/impl/AccCertCaseOperationsHelper.java +++ b/model/certification-impl/src/main/java/com/evolveum/midpoint/certification/impl/AccCertCaseOperationsHelper.java @@ -128,11 +128,10 @@ void recordDecision(String campaignOid, long caseId, long workItemId, updateHelper.modifyObjectViaModel(AccessCertificationCampaignType.class, campaignOid, deltaList, task, result); } - List> getDeltasToCreateCases( - final AccessCertificationCampaignType campaign, AccessCertificationStageType stage, - final CertificationHandler handler, final Task task, final OperationResult result) throws SchemaException, ObjectNotFoundException { - - final List> rv = new ArrayList<>(); + void getDeltasToCreateCases( + final AccessCertificationCampaignType campaign, AccessCertificationStageType stage, + final CertificationHandler handler, ModificationsToExecute modifications, + final Task task, final OperationResult result) throws SchemaException, ObjectNotFoundException { final String campaignShortName = toShortString(campaign); @@ -197,9 +196,9 @@ List> getDeltasToCreateCases( AccessCertificationReviewerSpecificationType reviewerSpec = reviewersHelper.findReviewersSpecification(campaign, 1, task, result); - ContainerDelta caseDelta = ContainerDelta.createDelta(F_CASE, - AccessCertificationCampaignType.class, prismContext); for (AccessCertificationCaseType _case : caseList) { + ContainerDelta caseDelta = ContainerDelta.createDelta(F_CASE, + AccessCertificationCampaignType.class, prismContext); _case.setStageNumber(1); _case.setCurrentStageCreateTimestamp(stage.getStartTimestamp()); _case.setCurrentStageDeadline(stage.getDeadline()); @@ -215,11 +214,11 @@ List> getDeltasToCreateCases( PrismContainerValue caseCVal = _case.asPrismContainerValue(); caseDelta.addValueToAdd(caseCVal); LOGGER.trace("Adding certification case:\n{}", caseCVal.debugDumpLazily()); + modifications.add(caseDelta); } - rv.add(caseDelta); - LOGGER.trace("Created {} deltas to create {} cases for campaign {}", rv.size(), caseList.size(), campaignShortName); - return rv; + LOGGER.trace("Created {} deltas (in {} batches) to create {} cases for campaign {}", modifications.getTotalDeltasCount(), + modifications.batches.size(), caseList.size(), campaignShortName); } private List createWorkItems(List forReviewers, int forStage) { @@ -234,12 +233,12 @@ private List createWorkItems(List> getDeltasToAdvanceCases(AccessCertificationCampaignType campaign, AccessCertificationStageType stage, Task task, OperationResult result) - throws SchemaException, ObjectAlreadyExistsException, ObjectNotFoundException { + void getDeltasToAdvanceCases(AccessCertificationCampaignType campaign, AccessCertificationStageType stage, + ModificationsToExecute modifications, Task task, OperationResult result) + throws SchemaException, ObjectNotFoundException { LOGGER.trace("Advancing reviewers and timestamps for cases in {}", toShortString(campaign)); List caseList = queryHelper.searchCases(campaign.getOid(), null, null, result); - List> rv = new ArrayList<>(caseList.size()); int stageToBe = campaign.getStageNumber() + 1; @@ -259,7 +258,7 @@ List> getDeltasToAdvanceCases(AccessCertificationCampaignType cam _case.getWorkItem().addAll(CloneUtil.cloneCollectionMembers(workItems)); AccessCertificationResponseType currentOutcome = computationHelper.computeOutcomeForStage(_case, campaign, stageToBe); AccessCertificationResponseType overallOutcome = computationHelper.computeOverallOutcome(_case, campaign, currentOutcome); - rv.addAll(DeltaBuilder.deltaFor(AccessCertificationCampaignType.class, prismContext) + modifications.add(DeltaBuilder.deltaFor(AccessCertificationCampaignType.class, prismContext) .item(F_CASE, caseId, F_WORK_ITEM).add(PrismContainerValue.toPcvList(workItems)) .item(F_CASE, caseId, F_CURRENT_STAGE_CREATE_TIMESTAMP).replace(stage.getStartTimestamp()) .item(F_CASE, caseId, F_CURRENT_STAGE_DEADLINE).replace(stage.getDeadline()) @@ -269,14 +268,13 @@ List> getDeltasToAdvanceCases(AccessCertificationCampaignType cam .asItemDeltas()); } - LOGGER.debug("Created {} deltas to advance {} cases for campaign {}", rv.size(), caseList.size(), toShortString(campaign)); - return rv; + LOGGER.debug("Created {} deltas (in {} batches) to advance {} cases for campaign {}", modifications.getTotalDeltasCount(), + modifications.batches.size(), caseList.size(), toShortString(campaign)); } // computes outcomes at stage close (stage-level and overall) and creates appropriate deltas - List> createOutcomeDeltas(AccessCertificationCampaignType campaign, OperationResult result) throws ObjectNotFoundException, SchemaException { - List> rv = new ArrayList<>(); - + void createOutcomeDeltas(AccessCertificationCampaignType campaign, ModificationsToExecute modifications, + OperationResult result) throws SchemaException { if (LOGGER.isTraceEnabled()) { LOGGER.trace("Updating current outcome for cases in {}", toShortString(campaign)); } @@ -286,13 +284,14 @@ List> createOutcomeDeltas(AccessCertificationCampaignType campaig if (_case.getStageNumber() != campaign.getStageNumber()) { continue; } + List> deltas = new ArrayList<>(); String newStageOutcome = OutcomeUtils.toUri(computationHelper.computeOutcomeForStage(_case, campaign, campaign.getStageNumber())); if (!Objects.equals(newStageOutcome, _case.getCurrentStageOutcome())) { - rv.add(DeltaBuilder.deltaFor(AccessCertificationCampaignType.class, prismContext) + deltas.add(DeltaBuilder.deltaFor(AccessCertificationCampaignType.class, prismContext) .item(F_CASE, _case.asPrismContainerValue().getId(), F_CURRENT_STAGE_OUTCOME).replace(newStageOutcome) .asItemDelta()); } - rv.add(DeltaBuilder.deltaFor(AccessCertificationCampaignType.class, prismContext) + deltas.add(DeltaBuilder.deltaFor(AccessCertificationCampaignType.class, prismContext) .item(F_CASE, _case.asPrismContainerValue().getId(), F_EVENT).add(new StageCompletionEventType() .timestamp(clock.currentTimeXMLGregorianCalendar()) .stageNumber(campaign.getStageNumber()) @@ -301,12 +300,12 @@ List> createOutcomeDeltas(AccessCertificationCampaignType campaig String newOverallOutcome = OutcomeUtils.toUri(computationHelper.computeOverallOutcome(_case, campaign, newStageOutcome)); if (!Objects.equals(newOverallOutcome, _case.getOutcome())) { - rv.add(DeltaBuilder.deltaFor(AccessCertificationCampaignType.class, prismContext) + deltas.add(DeltaBuilder.deltaFor(AccessCertificationCampaignType.class, prismContext) .item(F_CASE, _case.asPrismContainerValue().getId(), F_OUTCOME).replace(newOverallOutcome) .asItemDelta()); } + modifications.add(deltas); } - return rv; } // TODO temporary implementation - should be done somehow in batches in order to improve performance diff --git a/model/certification-impl/src/main/java/com/evolveum/midpoint/certification/impl/AccCertUpdateHelper.java b/model/certification-impl/src/main/java/com/evolveum/midpoint/certification/impl/AccCertUpdateHelper.java index 19f37867162..155008354dd 100644 --- a/model/certification-impl/src/main/java/com/evolveum/midpoint/certification/impl/AccCertUpdateHelper.java +++ b/model/certification-impl/src/main/java/com/evolveum/midpoint/certification/impl/AccCertUpdateHelper.java @@ -112,7 +112,7 @@ public class AccCertUpdateHelper { AccessCertificationCampaignType createCampaignObject(AccessCertificationDefinitionType definition, Task task, OperationResult result) - throws SchemaException, ObjectNotFoundException, SecurityViolationException { + throws SchemaException, SecurityViolationException { AccessCertificationCampaignType newCampaign = new AccessCertificationCampaignType(prismContext); if (definition.getName() != null) { @@ -169,7 +169,7 @@ AccessCertificationCampaignType createAdHocCampaignObject return campaign; } - private PolyStringType generateCampaignName(AccessCertificationDefinitionType definition, Task task, OperationResult result) throws SchemaException, ObjectNotFoundException { + private PolyStringType generateCampaignName(AccessCertificationDefinitionType definition, Task task, OperationResult result) throws SchemaException { String prefix = definition.getName().getOrig(); Integer lastCampaignIdUsed = definition.getLastCampaignIdUsed() != null ? definition.getLastCampaignIdUsed() : 0; for (int i = lastCampaignIdUsed+1;; i++) { @@ -207,13 +207,9 @@ public void recordLastCampaignIdUsed(String definitionOid, int lastIdUsed, Task //region ================================ Stage open ================================ - public static class StageOpenDeltas { - public final List> casesDeltas = new ArrayList<>(); - public final List> campaignDeltas = new ArrayList<>(); - } - - public StageOpenDeltas getDeltasForStageOpen(AccessCertificationCampaignType campaign, AccessCertificationStageType stage, CertificationHandler handler, final Task task, OperationResult result) - throws SchemaException, ObjectNotFoundException, ObjectAlreadyExistsException { + public ModificationsToExecute getDeltasForStageOpen(AccessCertificationCampaignType campaign, AccessCertificationStageType stage, + CertificationHandler handler, final Task task, OperationResult result) + throws SchemaException, ObjectNotFoundException { Validate.notNull(campaign, "certificationCampaign"); Validate.notNull(campaign.getOid(), "certificationCampaign.oid"); @@ -225,28 +221,30 @@ public StageOpenDeltas getDeltasForStageOpen(AccessCertificationCampaignType cam ObjectTypeUtil.toShortString(campaign), stageNumber); } - StageOpenDeltas rv = new StageOpenDeltas(); + ModificationsToExecute rv = new ModificationsToExecute(); if (stageNumber == 0) { - rv.casesDeltas.addAll(caseHelper.getDeltasToCreateCases(campaign, stage, handler, task, result)); + caseHelper.getDeltasToCreateCases(campaign, stage, handler, rv, task, result); } else { - rv.casesDeltas.addAll(caseHelper.getDeltasToAdvanceCases(campaign, stage, task, result)); + caseHelper.getDeltasToAdvanceCases(campaign, stage, rv, task, result); } - - rv.campaignDeltas.add(createStageAddDelta(stage)); - rv.campaignDeltas.addAll(createDeltasToRecordStageOpen(campaign, stage)); - rv.campaignDeltas.addAll(createTriggersForTimedActions(campaign.getOid(), 0, + rv.createNewBatch(); + rv.add(createStageAddDelta(stage)); + rv.add(createDeltasToRecordStageOpen(campaign, stage)); + rv.add(createTriggersForTimedActions(campaign.getOid(), 0, XmlTypeConverter.toDate(stage.getStartTimestamp()), XmlTypeConverter.toDate(stage.getDeadline()), CertCampaignTypeUtil.findStageDefinition(campaign, newStageNumber).getTimedActions())); - LOGGER.trace("getDeltasForStageOpen finishing, returning {} cases deltas and {} campaign deltas:\n{}\n{}", - rv.casesDeltas.size(), rv.campaignDeltas.size(), DebugUtil.debugDumpLazily(rv.casesDeltas), - DebugUtil.debugDumpLazily(rv.campaignDeltas)); + if (LOGGER.isTraceEnabled()) { + List> allDeltas = rv.getAllDeltas(); + LOGGER.trace("getDeltasForStageOpen finishing, returning {} deltas (in {} batches):\n{}", + allDeltas.size(), rv.batches.size(), DebugUtil.debugDump(allDeltas)); + } return rv; } // some bureaucracy... stage#, state, start time, triggers private List> createDeltasToRecordStageOpen(AccessCertificationCampaignType campaign, - AccessCertificationStageType newStage) throws ObjectNotFoundException, SchemaException, ObjectAlreadyExistsException { + AccessCertificationStageType newStage) throws SchemaException { final List> itemDeltaList = new ArrayList<>(); @@ -383,8 +381,8 @@ public List getReviewerAndDeputies(ObjectReferenceType actu public void delegateWorkItems(String campaignOid, List workItems, DelegateWorkItemActionType delegateAction, Task task, OperationResult result) - throws SchemaException, ObjectNotFoundException, ObjectAlreadyExistsException, ExpressionEvaluationException, - SecurityViolationException { + throws SchemaException, ObjectNotFoundException, ObjectAlreadyExistsException, + SecurityViolationException { LOGGER.info("Going to delegate {} work item(s) in campaign {}", workItems.size(), campaignOid); MidPointPrincipal principal = securityContextManager.getPrincipal(); @@ -468,7 +466,7 @@ public void escalateCampaign(String campaignOid, EscalateWorkItemActionType esca LOGGER.info("Going to escalate the campaign {}: {} work item(s)", campaignOid, workItems.size()); XMLGregorianCalendar now = clock.currentTimeXMLGregorianCalendar(); - List> deltas = new ArrayList<>(); + ModificationsToExecute modifications = new ModificationsToExecute(); // Currently we expect all open certification work items for a given campaign to have the same escalation level. // Because of consistence with other parts of midPoint we store the escalation level within work item itself. // But we enforce it to be the same for all the open work items. @@ -518,25 +516,26 @@ public void escalateCampaign(String campaignOid, EscalateWorkItemActionType esca event.setWorkItemId(workItem.getId()); event.setEscalationLevel(workItem.getEscalationLevel()); + List> deltas = new ArrayList<>(); addDeltasForAssigneesAndEvent(deltas, workItem, aCase, newAssignees, event); deltas.add(DeltaBuilder.deltaFor(AccessCertificationCampaignType.class, prismContext) .item(F_CASE, aCase.getId(), F_WORK_ITEM, workItem.getId(), F_ESCALATION_LEVEL).replace(newEscalationLevel) .asItemDelta()); - + modifications.add(deltas); // notification (after modifications) } AccessCertificationStageType stage = CertCampaignTypeUtil.getCurrentStage(campaign); assert stage != null; Long stageId = stage.asPrismContainerValue().getId(); assert stageId != null; - deltas.add(DeltaBuilder.deltaFor(AccessCertificationCampaignType.class, prismContext) + modifications.add(DeltaBuilder.deltaFor(AccessCertificationCampaignType.class, prismContext) .item(F_STAGE, stageId, AccessCertificationStageType.F_ESCALATION_LEVEL).replace(newEscalationLevel) .asItemDelta()); AccessCertificationStageDefinitionType stageDefinition = CertCampaignTypeUtil.getCurrentStageDefinition(campaign); - deltas.addAll(createTriggersForTimedActions(campaignOid, newStageEscalationLevelNumber, + modifications.add(createTriggersForTimedActions(campaignOid, newStageEscalationLevelNumber, XmlTypeConverter.toDate(stage.getStartTimestamp()), XmlTypeConverter.toDate(stage.getDeadline()), stageDefinition.getTimedActions())); - modifyObjectViaModel(AccessCertificationCampaignType.class, campaignOid, deltas, task, result); + modifyCampaignViaModel(campaignOid, modifications, task, result); campaign = generalHelper.getCampaign(campaignOid, null, task, result); // TODO differentiate between "old" and "new" reviewers @@ -562,11 +561,9 @@ private void addDeltasForAssigneesAndEvent(List> deltas, AccessC private List computeDelegateTo(DelegateWorkItemActionType delegateAction, AccessCertificationWorkItemType workItem, AccessCertificationCaseType aCase, - AccessCertificationCampaignType campaign, Task task, OperationResult result) - throws SchemaException, ObjectNotFoundException, ExpressionEvaluationException { + AccessCertificationCampaignType campaign, Task task, OperationResult result) { - List rv = new ArrayList<>(); - rv.addAll(CloneUtil.cloneCollectionMembers(delegateAction.getApproverRef())); + List rv = CloneUtil.cloneCollectionMembers(delegateAction.getApproverRef()); if (!delegateAction.getApproverExpression().isEmpty()) { ExpressionVariables variables = new ExpressionVariables(); variables.addVariableDefinition(ExpressionConstants.VAR_WORK_ITEM, workItem); @@ -608,18 +605,19 @@ private List computeDelegateTo(DelegateWorkItemActionType d //region ================================ Campaign and stage close ================================ - void closeCampaign(AccessCertificationCampaignType campaign, Task task, OperationResult result) throws ObjectAlreadyExistsException, ObjectNotFoundException, SchemaException, SecurityViolationException { + void closeCampaign(AccessCertificationCampaignType campaign, Task task, OperationResult result) throws ObjectAlreadyExistsException, ObjectNotFoundException, SchemaException { LOGGER.info("Closing campaign {}", ObjectTypeUtil.toShortString(campaign)); XMLGregorianCalendar now = XmlTypeConverter.createXMLGregorianCalendar(new Date()); int lastStageNumber = CertCampaignTypeUtil.getNumberOfStages(campaign); // TODO issue a warning if we are not in a correct state - List> deltas = new ArrayList<>(); - deltas.add(createStageNumberDelta(lastStageNumber + 1)); - deltas.add(createStateDelta(CLOSED)); - deltas.add(createTriggerDeleteDelta()); - deltas.add(createEndTimeDelta(now)); - deltas.addAll(createWorkItemsCloseDeltas(campaign, now, result)); - modifyObjectViaModel(AccessCertificationCampaignType.class, campaign.getOid(), deltas, task, result); + ModificationsToExecute modifications = new ModificationsToExecute(); + modifications.add(createStageNumberDelta(lastStageNumber + 1)); + modifications.add(createStateDelta(CLOSED)); + modifications.add(createTriggerDeleteDelta()); + modifications.add(createEndTimeDelta(now)); + createWorkItemsCloseDeltas(campaign, modifications, now, result); + + modifyCampaignViaModel(campaign.getOid(), modifications, task, result); AccessCertificationCampaignType updatedCampaign = refreshCampaign(campaign, result); LOGGER.info("Updated campaign state: {}", updatedCampaign.getState()); @@ -633,30 +631,31 @@ void closeCampaign(AccessCertificationCampaignType campaign, Task task, Operatio } } - private Collection> createWorkItemsCloseDeltas(AccessCertificationCampaignType campaign, + private void createWorkItemsCloseDeltas(AccessCertificationCampaignType campaign, ModificationsToExecute modifications, XMLGregorianCalendar now, OperationResult result) throws SchemaException, ObjectNotFoundException { ObjectQuery query = CertCampaignTypeUtil.createWorkItemsForCampaignQuery(campaign.getOid(), prismContext); List openWorkItems = queryHelper.searchOpenWorkItems(query, null, false, null, result); LOGGER.debug("There are {} open work items for {}", openWorkItems.size(), ObjectTypeUtil.toShortString(campaign)); - Collection> deltas = new ArrayList<>(); for (AccessCertificationWorkItemType workItem : openWorkItems) { AccessCertificationCaseType aCase = CertCampaignTypeUtil.getCaseChecked(workItem); - deltas.add( + modifications.add( DeltaBuilder.deltaFor(AccessCertificationCampaignType.class, prismContext) .item(F_CASE, aCase.getId(), F_WORK_ITEM, workItem.getId(), F_CLOSE_TIMESTAMP) .replace(now) .asItemDelta()); } - return deltas; } - List> getDeltasForStageClose(AccessCertificationCampaignType campaign, OperationResult result) throws ObjectNotFoundException, SchemaException, ObjectAlreadyExistsException { + ModificationsToExecute getDeltasForStageClose(AccessCertificationCampaignType campaign, OperationResult result) throws ObjectNotFoundException, SchemaException { XMLGregorianCalendar now = XmlTypeConverter.createXMLGregorianCalendar(new Date()); - List> rv = caseHelper.createOutcomeDeltas(campaign, result); + ModificationsToExecute rv = new ModificationsToExecute(); + caseHelper.createOutcomeDeltas(campaign, rv, result); + rv.createNewBatch(); + createWorkItemsCloseDeltas(campaign, rv, now, result); + rv.createNewBatch(); rv.add(createStateDelta(REVIEW_STAGE_DONE)); rv.add(createStageEndTimeDelta(campaign, now)); rv.add(createTriggerDeleteDelta()); - rv.addAll(createWorkItemsCloseDeltas(campaign, now, result)); return rv; } @@ -835,7 +834,17 @@ void addObject(ObjectType objectType, Task task, OperationResult result) throws */ } - void modifyObjectViaModel(Class objectClass, String oid, Collection> itemDeltas, Task task, OperationResult result) throws ObjectAlreadyExistsException, SchemaException, ObjectNotFoundException { + void modifyCampaignViaModel(String campaignOid, ModificationsToExecute modifications, Task task, OperationResult result) + throws ObjectAlreadyExistsException, SchemaException, ObjectNotFoundException { + for (List> batch : modifications.batches) { + if (!batch.isEmpty()) { + LOGGER.trace("Applying {} changes to campaign {}", batch.size(), campaignOid); + modifyObjectViaModel(AccessCertificationCampaignType.class, campaignOid, batch, task, result); + } + } + } + + void modifyObjectViaModel(Class objectClass, String oid, Collection> itemDeltas, Task task, OperationResult result) throws ObjectAlreadyExistsException, SchemaException, ObjectNotFoundException { ObjectDelta objectDelta = ObjectDelta.createModifyDelta(oid, itemDeltas, objectClass, prismContext); try { ModelExecuteOptions options = ModelExecuteOptions.createRaw().setPreAuthorized(); @@ -851,7 +860,7 @@ void modifyObjectViaModel(Class objectClass, String oi // TODO implement more efficiently public AccessCertificationCampaignType refreshCampaign(AccessCertificationCampaignType campaign, - OperationResult result) throws ObjectNotFoundException, SchemaException, SecurityViolationException { + OperationResult result) throws ObjectNotFoundException, SchemaException { return repositoryService.getObject(AccessCertificationCampaignType.class, campaign.getOid(), null, result).asObjectable(); } diff --git a/model/certification-impl/src/main/java/com/evolveum/midpoint/certification/impl/CertificationManagerImpl.java b/model/certification-impl/src/main/java/com/evolveum/midpoint/certification/impl/CertificationManagerImpl.java index d9eb0ff831a..6f6ed4dc5fa 100644 --- a/model/certification-impl/src/main/java/com/evolveum/midpoint/certification/impl/CertificationManagerImpl.java +++ b/model/certification-impl/src/main/java/com/evolveum/midpoint/certification/impl/CertificationManagerImpl.java @@ -42,7 +42,6 @@ import com.evolveum.midpoint.util.logging.Trace; import com.evolveum.midpoint.util.logging.TraceManager; import com.evolveum.midpoint.xml.ns._public.common.common_3.*; -import org.apache.commons.collections4.ListUtils; import org.apache.commons.lang3.StringUtils; import org.apache.commons.lang3.Validate; import org.jetbrains.annotations.NotNull; @@ -259,13 +258,8 @@ public void openNextStage(String campaignOid, int requestedStageNumber, Task tas } else { final CertificationHandler handler = findCertificationHandler(campaign); final AccessCertificationStageType stage = updateHelper.createStage(campaign, currentStageNumber+1); - final AccCertUpdateHelper.StageOpenDeltas deltas = updateHelper.getDeltasForStageOpen(campaign, stage, handler, task, result); - // TODO rollback in case of error - List>> batches = ListUtils.partition(deltas.casesDeltas, CASES_DELTAS_BATCH_SIZE); - for (List> batch : batches) { - updateHelper.modifyObjectViaModel(AccessCertificationCampaignType.class, campaignOid, batch, task, result); - } - updateHelper.modifyObjectViaModel(AccessCertificationCampaignType.class, campaignOid, deltas.campaignDeltas, task, result); + final ModificationsToExecute modifications = updateHelper.getDeltasForStageOpen(campaign, stage, handler, task, result); + updateHelper.modifyCampaignViaModel(campaignOid, modifications, task, result); updateHelper.afterStageOpen(campaignOid, stage, task, result); } } catch (RuntimeException e) { @@ -307,8 +301,8 @@ public void closeCurrentStage(String campaignOid, int stageNumberToClose, Task t } else if (!IN_REVIEW_STAGE.equals(state)) { result.recordFatalError("Couldn't close review stage " + stageNumberToClose + " as it is currently not open"); } else { - List> deltas = updateHelper.getDeltasForStageClose(campaign, result); - updateHelper.modifyObjectViaModel(AccessCertificationCampaignType.class, campaignOid, deltas, task, result); + ModificationsToExecute modifications = updateHelper.getDeltasForStageClose(campaign, result); + updateHelper.modifyCampaignViaModel(campaignOid, modifications, task, result); updateHelper.afterStageClose(campaignOid, task, result); } } catch (RuntimeException e) { @@ -319,7 +313,7 @@ public void closeCurrentStage(String campaignOid, int stageNumberToClose, Task t } } - @Override + @Override public void startRemediation(String campaignOid, Task task, OperationResult parentResult) throws ObjectNotFoundException, SchemaException, SecurityViolationException, ObjectAlreadyExistsException, ExpressionEvaluationException, CommunicationException, ConfigurationException { Validate.notNull(campaignOid, "campaignOid"); Validate.notNull(task, "task"); diff --git a/model/certification-impl/src/main/java/com/evolveum/midpoint/certification/impl/ModificationsToExecute.java b/model/certification-impl/src/main/java/com/evolveum/midpoint/certification/impl/ModificationsToExecute.java new file mode 100644 index 00000000000..3d97cfb6d71 --- /dev/null +++ b/model/certification-impl/src/main/java/com/evolveum/midpoint/certification/impl/ModificationsToExecute.java @@ -0,0 +1,75 @@ +/* + * Copyright (c) 2010-2018 Evolveum + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.evolveum.midpoint.certification.impl; + +import com.evolveum.midpoint.prism.delta.ItemDelta; + +import java.util.*; +import java.util.stream.Collectors; + +/** + * Modifications to execute on an object (mostly on a campaign). + * + * Because there can be lots of modifications that could take literally hours to execute (sometimes blocking DB as described + * e.g. in MID-4611), they are divided into smaller batches. + * + * @author mederly + */ +public class ModificationsToExecute { + + public static final int BATCH_SIZE = 50; + + public final List>> batches = new ArrayList<>(); + + public List> getLastBatch() { + return batches.get(batches.size() - 1); + } + + public boolean isEmpty() { + return batches.isEmpty(); + } + + public void add(ItemDelta... deltas) { + add(Arrays.asList(deltas)); + } + + public void add(Collection> deltas) { + if (deltas.isEmpty()) { + return; + } + if (isEmpty() || getLastBatch().size() + deltas.size() > BATCH_SIZE) { + createNewBatch(); + } + getLastBatch().addAll(deltas); + } + + public void createNewBatch() { + batches.add(new ArrayList<>()); + } + + public int getTotalDeltasCount() { + int rv = 0; + for (List> batch : batches) { + rv += batch.size(); + } + return rv; + } + + public List> getAllDeltas() { + return batches.stream().flatMap(Collection::stream).collect(Collectors.toList()); + } +}