/**
* Copyright (c) 2017 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.model.impl.lens.projector;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Iterator;
import javax.xml.datatype.XMLGregorianCalendar;
import com.evolveum.midpoint.common.ActivationComputer;
import com.evolveum.midpoint.model.api.ModelExecuteOptions;
import com.evolveum.midpoint.model.api.context.SynchronizationPolicyDecision;
import com.evolveum.midpoint.model.common.expression.ItemDeltaItem;
import com.evolveum.midpoint.model.impl.controller.ModelUtils;
import com.evolveum.midpoint.model.impl.lens.AssignmentEvaluator;
import com.evolveum.midpoint.model.impl.lens.EvaluatedAssignmentImpl;
import com.evolveum.midpoint.model.impl.lens.LensContext;
import com.evolveum.midpoint.model.impl.lens.LensFocusContext;
import com.evolveum.midpoint.model.impl.lens.LensProjectionContext;
import com.evolveum.midpoint.model.impl.lens.LensUtil;
import com.evolveum.midpoint.prism.PrismContainer;
import com.evolveum.midpoint.prism.PrismContainerDefinition;
import com.evolveum.midpoint.prism.PrismContainerValue;
import com.evolveum.midpoint.prism.PrismContext;
import com.evolveum.midpoint.prism.PrismObject;
import com.evolveum.midpoint.prism.delta.ContainerDelta;
import com.evolveum.midpoint.prism.delta.DeltaSetTriple;
import com.evolveum.midpoint.prism.delta.ItemDelta;
import com.evolveum.midpoint.prism.delta.ObjectDelta;
import com.evolveum.midpoint.prism.path.IdItemPathSegment;
import com.evolveum.midpoint.prism.path.ItemPath;
import com.evolveum.midpoint.prism.path.NameItemPathSegment;
import com.evolveum.midpoint.schema.ResourceShadowDiscriminator;
import com.evolveum.midpoint.schema.result.OperationResult;
import com.evolveum.midpoint.schema.util.FocusTypeUtil;
import com.evolveum.midpoint.schema.util.SchemaDebugUtil;
import com.evolveum.midpoint.task.api.Task;
import com.evolveum.midpoint.util.exception.ExpressionEvaluationException;
import com.evolveum.midpoint.util.exception.ObjectNotFoundException;
import com.evolveum.midpoint.util.exception.PolicyViolationException;
import com.evolveum.midpoint.util.exception.SchemaException;
import com.evolveum.midpoint.util.exception.SystemException;
import com.evolveum.midpoint.util.logging.Trace;
import com.evolveum.midpoint.util.logging.TraceManager;
import com.evolveum.midpoint.xml.ns._public.common.common_3.AssignmentType;
import com.evolveum.midpoint.xml.ns._public.common.common_3.FocusType;
import com.evolveum.midpoint.xml.ns._public.common.common_3.ObjectType;
/**
* Evaluates all assignments and sorts them to triple: added, removed and untouched assignments.
*
* @author semancik
*
*/
public class AssignmentTripleEvaluator<F extends FocusType> {
private static final Trace LOGGER = TraceManager.getTrace(AssignmentTripleEvaluator.class);
private LensContext<F> context;
private ObjectType source;
private AssignmentEvaluator<F> assignmentEvaluator;
private ActivationComputer activationComputer;
private PrismContext prismContext;
private XMLGregorianCalendar now;
private Task task;
private OperationResult result;
public LensContext<F> getContext() {
return context;
}
public void setContext(LensContext<F> context) {
this.context = context;
}
public ObjectType getSource() {
return source;
}
public void setSource(ObjectType source) {
this.source = source;
}
public AssignmentEvaluator<F> getAssignmentEvaluator() {
return assignmentEvaluator;
}
public void setAssignmentEvaluator(AssignmentEvaluator<F> assignmentEvaluator) {
this.assignmentEvaluator = assignmentEvaluator;
}
public ActivationComputer getActivationComputer() {
return activationComputer;
}
public void setActivationComputer(ActivationComputer activationComputer) {
this.activationComputer = activationComputer;
}
public PrismContext getPrismContext() {
return prismContext;
}
public void setPrismContext(PrismContext prismContext) {
this.prismContext = prismContext;
}
public XMLGregorianCalendar getNow() {
return now;
}
public void setNow(XMLGregorianCalendar now) {
this.now = now;
}
public Task getTask() {
return task;
}
public void setTask(Task task) {
this.task = task;
}
public OperationResult getResult() {
return result;
}
public void setResult(OperationResult result) {
this.result = result;
}
public DeltaSetTriple<EvaluatedAssignmentImpl<F>> processAllAssignments() throws SchemaException, ExpressionEvaluationException, PolicyViolationException {
LensFocusContext<F> focusContext = context.getFocusContext();
ObjectDelta<F> focusDelta = focusContext.getDelta();
ContainerDelta<AssignmentType> assignmentDelta = getExecutionWaveAssignmentDelta(focusContext);
assignmentDelta.expand(focusContext.getObjectCurrent());
LOGGER.trace("Assignment delta:\n{}", assignmentDelta.debugDump());
// This information is used for various reasons. We specifically distinguish between assignments in objectCurrent and objectOld
// to be able to reliably detect phantom adds: a phantom add is an assignment that is both in OLD and CURRENT objects. This is
// important in waves greater than 0, where objectCurrent is already updated with existing assignments. (See MID-2422.)
Collection<PrismContainerValue<AssignmentType>> assignmentsCurrent = getAssignmentsFromObject(focusContext.getObjectCurrent());
Collection<PrismContainerValue<AssignmentType>> assignmentsOld = getAssignmentsFromObject(focusContext.getObjectOld());
Collection<PrismContainerValue<AssignmentType>> changedAssignments = computeChangedAssignments(assignmentDelta, assignmentsCurrent);
if (LOGGER.isTraceEnabled()) {
LOGGER.trace("Current assignments {}", SchemaDebugUtil.prettyPrint(assignmentsCurrent));
LOGGER.trace("Changed assignments {}", SchemaDebugUtil.prettyPrint(changedAssignments));
}
// Iterate over all the assignments. I mean really all. This is a union of the existing and changed assignments
// therefore it contains all three types of assignments (plus, minus and zero). As it is an union each assignment
// will be processed only once. Inside the loop we determine whether it was added, deleted or remains unchanged.
// This is a first step of the processing. It takes all the account constructions regardless of the resource and
// account type (intent). Therefore several constructions for the same resource and intent may appear in the resulting
// sets. This is not good as we want only a single account for each resource/intent combination. But that will be
// sorted out later.
Collection<PrismContainerValue<AssignmentType>> allAssignments = mergeAssignments(assignmentsCurrent, changedAssignments);
DeltaSetTriple<EvaluatedAssignmentImpl<F>> evaluatedAssignmentTriple = new DeltaSetTriple<>();
for (PrismContainerValue<AssignmentType> assignmentCVal : allAssignments) {
processAssignment(evaluatedAssignmentTriple, focusDelta, assignmentDelta,
assignmentsCurrent, assignmentsOld, changedAssignments,
assignmentCVal);
}
return evaluatedAssignmentTriple;
}
private void processAssignment(DeltaSetTriple<EvaluatedAssignmentImpl<F>> evaluatedAssignmentTriple,
ObjectDelta<F> focusDelta, ContainerDelta<AssignmentType> assignmentDelta,
Collection<PrismContainerValue<AssignmentType>> assignmentsCurrent,
Collection<PrismContainerValue<AssignmentType>> assignmentsOld,
Collection<PrismContainerValue<AssignmentType>> changedAssignments,
PrismContainerValue<AssignmentType> assignmentCVal)
throws SchemaException, ExpressionEvaluationException, PolicyViolationException {
LensFocusContext<F> focusContext = context.getFocusContext();
AssignmentType assignmentType = assignmentCVal.asContainerable();
PrismContainerValue<AssignmentType> assignmentCValOld = assignmentCVal;
PrismContainerValue<AssignmentType> assignmentCValNew = assignmentCVal;
ItemDeltaItem<PrismContainerValue<AssignmentType>,PrismContainerDefinition<AssignmentType>> assignmentIdi = new ItemDeltaItem<>();
assignmentIdi.setItemOld(LensUtil.createAssignmentSingleValueContainerClone(assignmentType));
boolean presentInCurrent = PrismContainerValue.containsRealValue(assignmentsCurrent, assignmentCVal);
boolean presentInOld = PrismContainerValue.containsRealValue(assignmentsOld, assignmentCVal);
boolean forceRecon = false;
// This really means whether the WHOLE assignment was changed (e.g. added/delted/replaced). It tells nothing
// about "micro-changes" inside assignment, these will be processed later.
boolean isAssignmentChanged = PrismContainerValue.containsRealValue(changedAssignments, assignmentCVal);
String assignmentPlacementDesc;
if (isAssignmentChanged) {
// Whole assignment added or deleted
assignmentPlacementDesc = "delta for "+source;
} else {
assignmentPlacementDesc = source.toString();
Collection<? extends ItemDelta<?,?>> assignmentItemDeltas = getExecutionWaveAssignmentItemDeltas(focusContext, assignmentCVal.getId());
if (assignmentItemDeltas != null && !assignmentItemDeltas.isEmpty()) {
// Small changes inside assignment, but otherwise the assignment stays as it is (not added or deleted)
assignmentIdi.setSubItemDeltas(assignmentItemDeltas);
// The subItemDeltas above will handle some changes. But not other.
// E.g. a replace of the whole construction will not be handled properly.
// Therefore we force recon to sort it out.
forceRecon = true;
isAssignmentChanged = true;
PrismContainer<AssignmentType> assContNew = focusContext.getObjectNew().findContainer(FocusType.F_ASSIGNMENT);
assignmentCValNew = assContNew.getValue(assignmentCVal.getId());
}
}
assignmentIdi.recompute();
// The following code is using collectToAccountMap() to collect the account constructions to one of the three "delta"
// sets (zero, plus, minus). It is handling several situations that needs to be handled specially.
// It is also collecting assignments to evaluatedAssignmentTriple.
if (focusDelta != null && focusDelta.isDelete()) {
// USER DELETE
// If focus (user) is being deleted that all the assignments are to be gone. Including those that
// were not changed explicitly.
if (LOGGER.isTraceEnabled()) {
LOGGER.trace("Processing focus delete for: {}", SchemaDebugUtil.prettyPrint(assignmentCVal));
}
EvaluatedAssignmentImpl<F> evaluatedAssignment = evaluateAssignment(assignmentIdi, false, context, source, assignmentEvaluator, assignmentPlacementDesc, task, result);
if (evaluatedAssignment == null) {
return;
}
evaluatedAssignment.setPresentInCurrentObject(presentInCurrent);
evaluatedAssignment.setPresentInOldObject(presentInOld);
collectToMinus(evaluatedAssignmentTriple, evaluatedAssignment, forceRecon);
} else {
if (assignmentDelta.isReplace()) {
if (LOGGER.isTraceEnabled()) {
LOGGER.trace("Processing replace of all assignments for: {}", SchemaDebugUtil.prettyPrint(assignmentCVal));
}
// ASSIGNMENT REPLACE
// Handling assignment replace delta. This needs to be handled specially as all the "old"
// assignments should be considered deleted - except those that are part of the new value set
// (remain after replace). As account delete and add are costly operations (and potentially dangerous)
// we optimize here are consider the assignments that were there before replace and still are there
// after it as unchanged.
boolean hadValue = presentInCurrent;
boolean willHaveValue = assignmentDelta.isValueToReplace(assignmentCVal, true);
if (hadValue && willHaveValue) {
// No change
EvaluatedAssignmentImpl<F> evaluatedAssignment = evaluateAssignment(assignmentIdi, false, context, source, assignmentEvaluator, assignmentPlacementDesc, task, result);
if (evaluatedAssignment == null) {
return;
}
evaluatedAssignment.setPresentInCurrentObject(presentInCurrent);
evaluatedAssignment.setPresentInOldObject(presentInOld);
collectToZero(evaluatedAssignmentTriple, evaluatedAssignment, forceRecon);
} else if (willHaveValue) {
// add
EvaluatedAssignmentImpl<F> evaluatedAssignment = evaluateAssignment(assignmentIdi, false, context, source, assignmentEvaluator, assignmentPlacementDesc, task, result);
if (evaluatedAssignment == null) {
return;
}
evaluatedAssignment.setPresentInCurrentObject(presentInCurrent);
evaluatedAssignment.setPresentInOldObject(presentInOld);
collectToPlus(evaluatedAssignmentTriple, evaluatedAssignment, forceRecon);
} else if (hadValue) {
// delete
EvaluatedAssignmentImpl<F> evaluatedAssignment = evaluateAssignment(assignmentIdi, true, context, source, assignmentEvaluator, assignmentPlacementDesc, task, result);
if (evaluatedAssignment == null) {
return;
}
evaluatedAssignment.setPresentInCurrentObject(presentInCurrent);
evaluatedAssignment.setPresentInOldObject(presentInOld);
collectToMinus(evaluatedAssignmentTriple, evaluatedAssignment, forceRecon);
} else {
throw new SystemException("Whoops. Unexpected things happen. Assignment is not old nor new (replace delta)");
}
} else {
// ADD/DELETE of entire assignment or small changes inside existing assignments
// This is the usual situation.
// Just sort assignments to sets: unchanged (zero), added (plus), removed (minus)
if (isAssignmentChanged) {
// There was some change
boolean isAdd = assignmentDelta.isValueToAdd(assignmentCVal, true);
boolean isDelete = assignmentDelta.isValueToDelete(assignmentCVal, true);
if (isAdd & !isDelete) {
// Entirely new assignment is added
if (presentInCurrent && presentInOld) {
// Phantom add: adding assignment that is already there
if (LOGGER.isTraceEnabled()) {
LOGGER.trace("Processing changed assignment, phantom add: {}", SchemaDebugUtil.prettyPrint(assignmentCVal));
}
EvaluatedAssignmentImpl<F> evaluatedAssignment = evaluateAssignment(assignmentIdi, false, context, source, assignmentEvaluator, assignmentPlacementDesc, task, result);
if (evaluatedAssignment == null) {
return;
}
evaluatedAssignment.setPresentInCurrentObject(presentInCurrent);
evaluatedAssignment.setPresentInOldObject(presentInOld);
collectToZero(evaluatedAssignmentTriple, evaluatedAssignment, forceRecon);
} else {
if (LOGGER.isTraceEnabled()) {
LOGGER.trace("Processing changed assignment, add: {}", SchemaDebugUtil.prettyPrint(assignmentCVal));
}
EvaluatedAssignmentImpl<F> evaluatedAssignment = evaluateAssignment(assignmentIdi, false, context, source, assignmentEvaluator, assignmentPlacementDesc, task, result);
if (evaluatedAssignment == null) {
return;
}
evaluatedAssignment.setPresentInCurrentObject(presentInCurrent);
evaluatedAssignment.setPresentInOldObject(presentInOld);
collectToPlus(evaluatedAssignmentTriple, evaluatedAssignment, forceRecon);
}
} else if (isDelete && !isAdd) {
// Existing assignment is removed
if (LOGGER.isTraceEnabled()) {
LOGGER.trace("Processing changed assignment, delete: {}", SchemaDebugUtil.prettyPrint(assignmentCVal));
}
EvaluatedAssignmentImpl<F> evaluatedAssignment = evaluateAssignment(assignmentIdi, true, context, source, assignmentEvaluator, assignmentPlacementDesc, task, result);
if (evaluatedAssignment == null) {
return;
}
evaluatedAssignment.setPresentInCurrentObject(presentInCurrent);
evaluatedAssignment.setPresentInOldObject(presentInOld);
collectToMinus(evaluatedAssignmentTriple, evaluatedAssignment, forceRecon);
} else {
// Small change inside an assignment
// The only thing that we need to worry about is assignment validity change. That is a cause
// of provisioning/deprovisioning of the projections. So check that explicitly. Other changes are
// not significant, i.e. reconciliation can handle them.
boolean isValidOld = LensUtil.isAssignmentValid(focusContext.getObjectOld().asObjectable(),
assignmentCValOld.asContainerable(), now, activationComputer);
boolean isValid = LensUtil.isAssignmentValid(focusContext.getObjectNew().asObjectable(),
assignmentCValNew.asContainerable(), now, activationComputer);
if (isValid == isValidOld) {
// No change in validity -> right to the zero set
// The change is not significant for assignment applicability. Recon will sort out the details.
if (LOGGER.isTraceEnabled()) {
LOGGER.trace("Processing changed assignment, minor change (add={}, delete={}, valid={}): {}",
new Object[]{isAdd, isDelete, isValid, SchemaDebugUtil.prettyPrint(assignmentCVal)});
}
EvaluatedAssignmentImpl<F> evaluatedAssignment = evaluateAssignment(assignmentIdi, false, context, source, assignmentEvaluator, assignmentPlacementDesc, task, result);
if (evaluatedAssignment == null) {
return;
}
evaluatedAssignment.setPresentInCurrentObject(presentInCurrent);
evaluatedAssignment.setPresentInOldObject(presentInOld);
collectToZero(evaluatedAssignmentTriple, evaluatedAssignment, true);
} else if (isValid) {
// Assignment became valid. We need to place it in plus set to initiate provisioning
if (LOGGER.isTraceEnabled()) {
LOGGER.trace("Processing changed assignment, assignment becomes valid (add={}, delete={}): {}",
new Object[]{isAdd, isDelete, SchemaDebugUtil.prettyPrint(assignmentCVal)});
}
EvaluatedAssignmentImpl<F> evaluatedAssignment = evaluateAssignment(assignmentIdi, false, context, source, assignmentEvaluator, assignmentPlacementDesc, task, result);
if (evaluatedAssignment == null) {
return;
}
evaluatedAssignment.setPresentInCurrentObject(presentInCurrent);
evaluatedAssignment.setPresentInOldObject(presentInOld);
collectToPlus(evaluatedAssignmentTriple, evaluatedAssignment, true);
} else {
// Assignment became invalid. We need to place is in minus set to initiate deprovisioning
if (LOGGER.isTraceEnabled()) {
LOGGER.trace("Processing changed assignment, assignment becomes invalid (add={}, delete={}): {}",
new Object[]{isAdd, isDelete, SchemaDebugUtil.prettyPrint(assignmentCVal)});
}
EvaluatedAssignmentImpl<F> evaluatedAssignment = evaluateAssignment(assignmentIdi, false, context, source, assignmentEvaluator, assignmentPlacementDesc, task, result);
if (evaluatedAssignment == null) {
return;
}
evaluatedAssignment.setPresentInCurrentObject(presentInCurrent);
evaluatedAssignment.setPresentInOldObject(presentInOld);
collectToMinus(evaluatedAssignmentTriple, evaluatedAssignment, true);
}
}
} else {
// No change in assignment
if (LOGGER.isTraceEnabled()) {
LOGGER.trace("Processing unchanged assignment {}", new Object[]{SchemaDebugUtil.prettyPrint(assignmentCVal)});
}
EvaluatedAssignmentImpl<F> evaluatedAssignment = evaluateAssignment(assignmentIdi, false, context, source, assignmentEvaluator, assignmentPlacementDesc, task, result);
if (evaluatedAssignment == null) {
return;
}
evaluatedAssignment.setPresentInCurrentObject(presentInCurrent);
evaluatedAssignment.setPresentInOldObject(presentInOld);
collectToZero(evaluatedAssignmentTriple, evaluatedAssignment, forceRecon);
}
}
}
}
private <F extends FocusType> Collection<? extends ItemDelta<?,?>> getExecutionWaveAssignmentItemDeltas(LensFocusContext<F> focusContext, Long id) throws SchemaException {
ObjectDelta<? extends FocusType> focusDelta = focusContext.getWaveDelta(focusContext.getLensContext().getExecutionWave());
if (focusDelta == null) {
return null;
}
return focusDelta.findItemDeltasSubPath(new ItemPath(new NameItemPathSegment(FocusType.F_ASSIGNMENT),
new IdItemPathSegment(id)));
}
private <F extends FocusType> void collectToZero(DeltaSetTriple<EvaluatedAssignmentImpl<F>> evaluatedAssignmentTriple,
EvaluatedAssignmentImpl<F> evaluatedAssignment, boolean forceRecon) {
if (forceRecon) {
evaluatedAssignment.setForceRecon(true);
}
evaluatedAssignmentTriple.addToZeroSet(evaluatedAssignment);
}
private <F extends FocusType> void collectToPlus(DeltaSetTriple<EvaluatedAssignmentImpl<F>> evaluatedAssignmentTriple,
EvaluatedAssignmentImpl<F> evaluatedAssignment, boolean forceRecon) {
if (forceRecon) {
evaluatedAssignment.setForceRecon(true);
}
evaluatedAssignmentTriple.addToPlusSet(evaluatedAssignment);
}
private <F extends FocusType> void collectToMinus(DeltaSetTriple<EvaluatedAssignmentImpl<F>> evaluatedAssignmentTriple,
EvaluatedAssignmentImpl<F> evaluatedAssignment, boolean forceRecon) {
if (forceRecon) {
evaluatedAssignment.setForceRecon(true);
}
evaluatedAssignmentTriple.addToMinusSet(evaluatedAssignment);
}
private <F extends FocusType> EvaluatedAssignmentImpl<F> evaluateAssignment(ItemDeltaItem<PrismContainerValue<AssignmentType>,PrismContainerDefinition<AssignmentType>> assignmentIdi,
boolean evaluateOld, LensContext<F> context, ObjectType source, AssignmentEvaluator<F> assignmentEvaluator,
String assignmentPlacementDesc, Task task, OperationResult parentResult) throws SchemaException, ExpressionEvaluationException, PolicyViolationException {
OperationResult result = parentResult.createMinorSubresult(AssignmentProcessor.class.getSimpleName()+".evaluateAssignment");
result.addParam("assignmentDescription", assignmentPlacementDesc);
try{
// Evaluate assignment. This follows to the assignment targets, follows to the inducements,
// evaluates all the expressions, etc.
EvaluatedAssignmentImpl<F> evaluatedAssignment = assignmentEvaluator.evaluate(assignmentIdi, evaluateOld, source, assignmentPlacementDesc, task, result);
context.rememberResources(evaluatedAssignment.getResources(task, result));
result.recordSuccess();
return evaluatedAssignment;
} catch (ObjectNotFoundException ex) {
if (LOGGER.isTraceEnabled()) {
LOGGER.trace("Processing of assignment resulted in error {}: {}", ex, SchemaDebugUtil.prettyPrint(LensUtil.getAssignmentType(assignmentIdi, evaluateOld)));
}
if (ModelExecuteOptions.isForce(context.getOptions())) {
result.recordHandledError(ex);
return null;
}
ModelUtils.recordFatalError(result, ex);
return null;
} catch (SchemaException ex) {
AssignmentType assignmentType = LensUtil.getAssignmentType(assignmentIdi, evaluateOld);
if (LOGGER.isTraceEnabled()) {
LOGGER.trace("Processing of assignment resulted in error {}: {}", ex, SchemaDebugUtil.prettyPrint(assignmentType));
}
ModelUtils.recordFatalError(result, ex);
String resourceOid = FocusTypeUtil.determineConstructionResource(assignmentType);
if (resourceOid == null) {
// This is a role assignment or something like that. Just throw the original exception for now.
throw ex;
}
ResourceShadowDiscriminator rad = new ResourceShadowDiscriminator(resourceOid,
FocusTypeUtil.determineConstructionKind(assignmentType),
FocusTypeUtil.determineConstructionIntent(assignmentType));
LensProjectionContext accCtx = context.findProjectionContext(rad);
if (accCtx != null) {
accCtx.setSynchronizationPolicyDecision(SynchronizationPolicyDecision.BROKEN);
}
return null;
} catch (ExpressionEvaluationException | PolicyViolationException e) {
result.recordFatalError(e);
throw e;
}
}
private Collection<PrismContainerValue<AssignmentType>> mergeAssignments(
Collection<PrismContainerValue<AssignmentType>> currentAssignments,
Collection<PrismContainerValue<AssignmentType>> changedAssignments) {
Collection<PrismContainerValue<AssignmentType>> all = new ArrayList<>(currentAssignments.size() + changedAssignments.size());
all.addAll(currentAssignments);
for (PrismContainerValue<AssignmentType> changedAssignment: changedAssignments) {
boolean skip = false;
for (PrismContainerValue<AssignmentType> currentAssignment: currentAssignments) {
if (currentAssignment.match(changedAssignment)) {
skip = true;
break;
}
}
if (!skip) {
all.add(changedAssignment);
}
}
return all;
}
private Collection<PrismContainerValue<AssignmentType>> computeChangedAssignments(
ContainerDelta<AssignmentType> assignmentDelta,
Collection<PrismContainerValue<AssignmentType>> assignmentsCurrent) {
Collection<PrismContainerValue<AssignmentType>> changedAssignments = assignmentDelta.getValues(AssignmentType.class);
// Changes assignments may be "light", i.e. they may contain just the identifier. Make sure that we always have
// the full assignment data.
Iterator<PrismContainerValue<AssignmentType>> iterator = changedAssignments.iterator();
while (iterator.hasNext()) {
PrismContainerValue<AssignmentType> changedAssignment = iterator.next();
if (changedAssignment.getItems().isEmpty()) {
if (changedAssignment.getId() != null) {
for (PrismContainerValue<AssignmentType> assignmentCurrent: assignmentsCurrent) {
if (changedAssignment.getId().equals(assignmentCurrent.getId())) {
iterator.remove();
changedAssignments.add(assignmentCurrent.clone());
}
}
}
}
}
return changedAssignments;
}
private <F extends FocusType> Collection<PrismContainerValue<AssignmentType>> getAssignmentsFromObject(PrismObject<F> object) {
Collection<PrismContainerValue<AssignmentType>> assignments = new ArrayList<PrismContainerValue<AssignmentType>>();
if (object != null) {
PrismContainer<AssignmentType> assignmentContainer = object.findContainer(FocusType.F_ASSIGNMENT);
if (assignmentContainer != null) {
assignments.addAll(assignmentContainer.getValues());
}
}
return assignments;
}
/**
* Returns delta of user assignments, both primary and secondary (merged together).
* The returned object is (kind of) immutable. Changing it may do strange things (but most likely the changes will be lost).
*
* Originally we took only the delta related to current execution wave, to avoid re-processing of already executed assignments.
* But MID-2422 shows that we need to take deltas from waves 0..N (N=current execution wave) [that effectively means all the secondary deltas]
*/
private <F extends FocusType> ContainerDelta<AssignmentType> getExecutionWaveAssignmentDelta(LensFocusContext<F> focusContext) throws SchemaException {
ObjectDelta<? extends FocusType> focusDelta = focusContext.getAggregatedWaveDelta(focusContext.getLensContext().getExecutionWave());
if (focusDelta == null) {
return createEmptyAssignmentDelta(focusContext);
}
ContainerDelta<AssignmentType> assignmentDelta = focusDelta.findContainerDelta(new ItemPath(FocusType.F_ASSIGNMENT));
if (assignmentDelta == null) {
return createEmptyAssignmentDelta(focusContext);
}
return assignmentDelta;
}
private <F extends FocusType> ContainerDelta<AssignmentType> createEmptyAssignmentDelta(LensFocusContext<F> focusContext) {
return new ContainerDelta<>(getAssignmentContainerDefinition(focusContext), prismContext);
}
private <F extends FocusType> PrismContainerDefinition<AssignmentType> getAssignmentContainerDefinition(LensFocusContext<F> focusContext) {
return focusContext.getObjectDefinition().findContainerDefinition(FocusType.F_ASSIGNMENT);
}
}