/* * Copyright (c) 2010-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.HashMap; import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Map.Entry; import java.util.Set; import java.util.stream.Collectors; import java.util.stream.Stream; import javax.xml.datatype.DatatypeConstants; import javax.xml.datatype.XMLGregorianCalendar; import javax.xml.namespace.QName; import org.apache.commons.collections4.CollectionUtils; import org.jetbrains.annotations.Nullable; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Qualifier; import org.springframework.stereotype.Component; import com.evolveum.midpoint.model.common.expression.ObjectDeltaObject; import com.evolveum.midpoint.model.common.mapping.Mapping; import com.evolveum.midpoint.model.common.mapping.MappingFactory; import com.evolveum.midpoint.model.common.mapping.PrismValueDeltaSetTripleProducer; import com.evolveum.midpoint.model.impl.ModelObjectResolver; import com.evolveum.midpoint.model.impl.lens.ItemValueWithOrigin; import com.evolveum.midpoint.model.impl.lens.LensContext; import com.evolveum.midpoint.model.impl.lens.LensFocusContext; import com.evolveum.midpoint.model.impl.lens.LensUtil; import com.evolveum.midpoint.model.impl.trigger.RecomputeTriggerHandler; import com.evolveum.midpoint.prism.Item; import com.evolveum.midpoint.prism.ItemDefinition; import com.evolveum.midpoint.prism.Objectable; 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.PrismObjectDefinition; import com.evolveum.midpoint.prism.PrismValue; import com.evolveum.midpoint.prism.delta.ChangeType; 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.delta.PropertyDelta; import com.evolveum.midpoint.prism.match.MatchingRule; import com.evolveum.midpoint.prism.match.MatchingRuleRegistry; import com.evolveum.midpoint.prism.path.ItemPath; import com.evolveum.midpoint.prism.util.ItemPathUtil; import com.evolveum.midpoint.prism.util.PrismUtil; import com.evolveum.midpoint.repo.api.RepositoryService; import com.evolveum.midpoint.schema.result.OperationResult; import com.evolveum.midpoint.task.api.Task; import com.evolveum.midpoint.util.DebugUtil; import com.evolveum.midpoint.util.QNameUtil; import com.evolveum.midpoint.util.exception.ExpressionEvaluationException; import com.evolveum.midpoint.util.exception.ObjectAlreadyExistsException; 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.logging.Trace; import com.evolveum.midpoint.util.logging.TraceManager; import com.evolveum.midpoint.xml.ns._public.common.common_3.FocusType; import com.evolveum.midpoint.xml.ns._public.common.common_3.MappingStrengthType; import com.evolveum.midpoint.xml.ns._public.common.common_3.ObjectReferenceType; import com.evolveum.midpoint.xml.ns._public.common.common_3.ObjectTemplateItemDefinitionType; import com.evolveum.midpoint.xml.ns._public.common.common_3.ObjectTemplateMappingEvaluationPhaseType; import com.evolveum.midpoint.xml.ns._public.common.common_3.ObjectTemplateMappingType; import com.evolveum.midpoint.xml.ns._public.common.common_3.ObjectTemplateType; import com.evolveum.midpoint.xml.ns._public.common.common_3.ObjectType; import com.evolveum.midpoint.xml.ns._public.common.common_3.TriggerType; import com.evolveum.midpoint.xml.ns._public.common.common_3.VariableBindingDefinitionType; /** * Processor to handle object template. * * @author Radovan Semancik * */ @Component public class ObjectTemplateProcessor { private static final Trace LOGGER = TraceManager.getTrace(ObjectTemplateProcessor.class); @Autowired private MappingFactory mappingFactory; @Autowired private PrismContext prismContext; @Autowired private ModelObjectResolver modelObjectResolver; @Autowired @Qualifier("cacheRepositoryService") private transient RepositoryService cacheRepositoryService; @Autowired private MappingEvaluator mappingEvaluator; @Autowired private MatchingRuleRegistry matchingRuleRegistry; /** * Process focus template: application of object template where focus is both source and target. */ public <F extends FocusType> void processTemplate(LensContext<F> context, ObjectTemplateMappingEvaluationPhaseType phase, XMLGregorianCalendar now, Task task, OperationResult result) throws ExpressionEvaluationException, ObjectNotFoundException, SchemaException, PolicyViolationException, ObjectAlreadyExistsException { LensFocusContext<F> focusContext = context.getFocusContext(); if (focusContext.isDelete()) { LOGGER.trace("Skipping processing of object template: focus delete"); return; } ObjectTemplateType objectTemplate = context.getFocusTemplate(); if (objectTemplate == null) { // No applicable template LOGGER.trace("Skipping processing of object template: no object template"); return; } int iteration = focusContext.getIteration(); String iterationToken = focusContext.getIterationToken(); ObjectDeltaObject<F> focusOdo = focusContext.getObjectDeltaObject(); PrismObjectDefinition<F> focusDefinition = getObjectDefinition(focusContext.getObjectTypeClass()); LOGGER.trace("Applying {} to {}, iteration {} ({}), phase {}", objectTemplate, focusContext.getObjectNew(), iteration, iterationToken, phase); Map<ItemPath,DeltaSetTriple<? extends ItemValueWithOrigin<?,?>>> outputTripleMap = new HashMap<>(); Map<ItemPath,ObjectTemplateItemDefinitionType> itemDefinitionsMap = collectItemDefinitionsFromTemplate(objectTemplate, objectTemplate.toString(), task, result); XMLGregorianCalendar nextRecomputeTime = collectTripleFromTemplate(context, objectTemplate, phase, focusOdo, focusOdo.getNewObject(), outputTripleMap, iteration, iterationToken, now, objectTemplate.toString(), task, result); if (LOGGER.isTraceEnabled()) { LOGGER.trace("outputTripleMap before item delta computation:\n{}", DebugUtil.debugDumpMapMultiLine(outputTripleMap)); } String contextDesc = "object template "+objectTemplate+ " for focus "+focusOdo.getAnyObject(); Collection<ItemDelta<?,?>> itemDeltas = computeItemDeltas(outputTripleMap, itemDefinitionsMap, focusOdo.getObjectDelta(), focusOdo.getNewObject(), focusDefinition, contextDesc); focusContext.applyProjectionWaveSecondaryDeltas(itemDeltas); if (nextRecomputeTime != null) { boolean alreadyHasTrigger = false; PrismObject<F> objectCurrent = focusContext.getObjectCurrent(); if (objectCurrent != null) { for (TriggerType trigger: objectCurrent.asObjectable().getTrigger()) { if (RecomputeTriggerHandler.HANDLER_URI.equals(trigger.getHandlerUri()) && nextRecomputeTime.equals(trigger.getTimestamp())) { alreadyHasTrigger = true; break; } } } if (!alreadyHasTrigger) { PrismObjectDefinition<F> objectDefinition = focusContext.getObjectDefinition(); PrismContainerDefinition<TriggerType> triggerContDef = objectDefinition.findContainerDefinition(ObjectType.F_TRIGGER); ContainerDelta<TriggerType> triggerDelta = triggerContDef.createEmptyDelta(new ItemPath(ObjectType.F_TRIGGER)); PrismContainerValue<TriggerType> triggerCVal = triggerContDef.createValue(); triggerDelta.addValueToAdd(triggerCVal); TriggerType triggerType = triggerCVal.asContainerable(); triggerType.setTimestamp(nextRecomputeTime); triggerType.setHandlerUri(RecomputeTriggerHandler.HANDLER_URI); focusContext.swallowToProjectionWaveSecondaryDelta(triggerDelta); } } } /** * Processing object mapping: application of object template where focus is the source and another object is the target. * Used to map focus to personas. */ public <F extends FocusType, T extends FocusType> Collection<ItemDelta<?,?>> processObjectMapping(LensContext<F> context, ObjectTemplateType objectMappingType, ObjectDeltaObject<F> focusOdo, PrismObject<T> target, ObjectDelta<T> targetAPrioriDelta, String contextDesc, XMLGregorianCalendar now, Task task, OperationResult result) throws ExpressionEvaluationException, ObjectNotFoundException, SchemaException, PolicyViolationException, ObjectAlreadyExistsException { LensFocusContext<F> focusContext = context.getFocusContext(); int iteration = 0; String iterationToken = null; PrismObjectDefinition<F> focusDefinition = getObjectDefinition(focusContext.getObjectTypeClass()); LOGGER.trace("Applying object mapping {} from {} to {}, iteration {} ({})", objectMappingType, focusContext.getObjectNew(), target, iteration, iterationToken); Map<ItemPath,DeltaSetTriple<? extends ItemValueWithOrigin<?,?>>> outputTripleMap = new HashMap<>(); Map<ItemPath,ObjectTemplateItemDefinitionType> itemDefinitionsMap = collectItemDefinitionsFromTemplate(objectMappingType, objectMappingType.toString(), task, result); collectTripleFromTemplate(context, objectMappingType, null, focusOdo, target, outputTripleMap, iteration, iterationToken, now, objectMappingType.toString(), task, result); if (LOGGER.isTraceEnabled()) { LOGGER.trace("outputTripleMap before item delta computation:\n{}", DebugUtil.debugDumpMapMultiLine(outputTripleMap)); } Collection<ItemDelta<?,?>> itemDeltas = computeItemDeltas(outputTripleMap, itemDefinitionsMap, targetAPrioriDelta, target, focusDefinition, contextDesc); return itemDeltas; } private Map<ItemPath, ObjectTemplateItemDefinitionType> collectItemDefinitionsFromTemplate(ObjectTemplateType objectTemplateType, String contextDesc, Task task, OperationResult result) throws SchemaException, ObjectNotFoundException { Map<ItemPath, ObjectTemplateItemDefinitionType> definitions = new HashMap<>(); // Process includes (TODO refactor as a generic method) for (ObjectReferenceType includeRef: objectTemplateType.getIncludeRef()) { PrismObject<ObjectTemplateType> includeObject = includeRef.asReferenceValue().getObject(); if (includeObject == null) { ObjectTemplateType includeObjectType = modelObjectResolver.resolve(includeRef, ObjectTemplateType.class, null, "include reference in "+objectTemplateType + " in " + contextDesc, task, result); includeObject = includeObjectType.asPrismObject(); // Store resolved object for future use (e.g. next waves). includeRef.asReferenceValue().setObject(includeObject); } LOGGER.trace("Including template {}", includeObject); ObjectTemplateType includeObjectType = includeObject.asObjectable(); Map<ItemPath, ObjectTemplateItemDefinitionType> includedDefinitions = collectItemDefinitionsFromTemplate(includeObjectType, "include "+includeObject+" in "+objectTemplateType + " in " + contextDesc, task, result); if (includedDefinitions != null) { ItemPathUtil.putAllToMap(definitions, includedDefinitions); } } // Process own definitions for (ObjectTemplateItemDefinitionType def : objectTemplateType.getItem()) { if (def.getRef() == null) { throw new IllegalStateException("Item definition with null ref in " + contextDesc); } ItemPathUtil.putToMap(definitions, def.getRef().getItemPath(), def); // TODO check for incompatible overrides } return definitions; } <F extends FocusType, T extends FocusType> Collection<ItemDelta<?,?>> computeItemDeltas(Map<ItemPath, DeltaSetTriple<? extends ItemValueWithOrigin<?,?>>> outputTripleMap, @Nullable Map<ItemPath,ObjectTemplateItemDefinitionType> itemDefinitionsMap, ObjectDelta<T> targetObjectAPrioriDelta, PrismObject<T> targetObject, PrismObjectDefinition<F> focusDefinition, String contextDesc) throws ExpressionEvaluationException, PolicyViolationException, SchemaException { Collection<ItemDelta<?,?>> itemDeltas = new ArrayList<>(); LOGGER.trace("Computing deltas in {}, focusDelta:\n{}", contextDesc, targetObjectAPrioriDelta); boolean addUnchangedValues = false; if (targetObjectAPrioriDelta != null && targetObjectAPrioriDelta.isAdd()) { addUnchangedValues = true; } for (Entry<ItemPath, DeltaSetTriple<? extends ItemValueWithOrigin<?,?>>> entry: outputTripleMap.entrySet()) { ItemPath itemPath = entry.getKey(); DeltaSetTriple<? extends ItemValueWithOrigin<?,?>> outputTriple = entry.getValue(); if (LOGGER.isTraceEnabled()) { LOGGER.trace("Computed triple for {}:\n{}", itemPath, outputTriple.debugDump()); } final ObjectTemplateItemDefinitionType templateItemDefinition; if (itemDefinitionsMap != null) { templateItemDefinition = ItemPathUtil.getFromMap(itemDefinitionsMap, itemPath); } else { templateItemDefinition = null; } boolean isNonTolerant = templateItemDefinition != null && Boolean.FALSE.equals(templateItemDefinition.isTolerant()); ItemDelta aprioriItemDelta = getAprioriItemDelta(targetObjectAPrioriDelta, itemPath); boolean filterExistingValues = !isNonTolerant; // if non-tolerant, we want to gather ZERO & PLUS sets ItemDefinition itemDefinition = focusDefinition.findItemDefinition(itemPath); ItemDelta itemDelta = LensUtil.consolidateTripleToDelta(itemPath, (DeltaSetTriple)outputTriple, itemDefinition, aprioriItemDelta, targetObject, null, null, addUnchangedValues, filterExistingValues, false, contextDesc, true); // Do a quick version of reconciliation. There is not much to reconcile as both the source and the target // is focus. But there are few cases to handle, such as strong mappings, and sourceless normal mappings. Collection<? extends ItemValueWithOrigin<?,?>> zeroSet = outputTriple.getZeroSet(); Item<PrismValue, ItemDefinition> itemNew = null; if (targetObject != null) { itemNew = targetObject.findItem(itemPath); } for (ItemValueWithOrigin<?,?> zeroSetIvwo: zeroSet) { PrismValueDeltaSetTripleProducer<?, ?> mapping = zeroSetIvwo.getMapping(); if ((mapping.getStrength() == null || mapping.getStrength() == MappingStrengthType.NORMAL)) { if (aprioriItemDelta != null && !aprioriItemDelta.isEmpty()) { continue; } if (!mapping.isSourceless()) { continue; } LOGGER.trace("Adding zero values from normal mapping {}, a-priori delta: {}, isSourceless: {}", mapping, aprioriItemDelta, mapping.isSourceless()); } else if (mapping.getStrength() == MappingStrengthType.WEAK) { if ((itemNew != null && !itemNew.isEmpty()) || (itemDelta != null && itemDelta.addsAnyValue())) { continue; } LOGGER.trace("Adding zero values from weak mapping {}, itemNew: {}, itemDelta: {}", mapping, itemNew, itemDelta); } else { LOGGER.trace("Adding zero values from strong mapping {}", mapping); } PrismValue valueFromZeroSet = zeroSetIvwo.getItemValue(); if (itemNew == null || !itemNew.containsRealValue(valueFromZeroSet)) { LOGGER.trace("Reconciliation will add value {} for item {}. Existing item: {}", valueFromZeroSet, itemPath, itemNew); itemDelta.addValuesToAdd(valueFromZeroSet.clone()); } } if (isNonTolerant) { if (itemDelta.isDelete()) { LOGGER.trace("Non-tolerant item with values to DELETE => removing them"); itemDelta.resetValuesToDelete(); // these are useless now - we move everything to REPLACE } if (itemDelta.isReplace()) { LOGGER.trace("Non-tolerant item with resulting REPLACE delta => doing nothing"); } else { for (ItemValueWithOrigin<?,?> zeroSetIvwo: zeroSet) { itemDelta.addValuesToAdd(zeroSetIvwo.getItemValue().clone()); } itemDelta.addToReplaceDelta(); LOGGER.trace("Non-tolerant item with resulting ADD delta => converted ADD to REPLACE values: {}", itemDelta.getValuesToReplace()); } // To avoid phantom changes, compare with existing values (MID-2499). // TODO this should be maybe moved into LensUtil.consolidateTripleToDelta (along with the above code), e.g. // under a special option "createReplaceDelta", but for the time being, let's keep it here if (itemDelta instanceof PropertyDelta) { PropertyDelta propertyDelta = ((PropertyDelta) itemDelta); QName matchingRuleName = templateItemDefinition != null ? templateItemDefinition.getMatchingRule() : null; MatchingRule matchingRule = matchingRuleRegistry.getMatchingRule(matchingRuleName, null); if (propertyDelta.isRedundant(targetObject, matchingRule)) { LOGGER.trace("Computed property delta is redundant => skipping it. Delta = \n{}", propertyDelta.debugDump()); continue; } } else { if (itemDelta.isRedundant(targetObject)) { LOGGER.trace("Computed item delta is redundant => skipping it. Delta = \n{}", itemDelta.debugDump()); continue; } } PrismUtil.setDeltaOldValue(targetObject, itemDelta); } itemDelta.simplify(); itemDelta.validate(contextDesc); itemDeltas.add(itemDelta); if (LOGGER.isTraceEnabled()) { LOGGER.trace("Computed delta:\n{}", itemDelta.debugDump()); } } return itemDeltas; } private <F extends FocusType> ItemDelta getAprioriItemDelta(ObjectDelta<F> focusDelta, ItemPath itemPath) { return focusDelta != null ? focusDelta.findItemDelta(itemPath) : null; } private <F extends FocusType, T extends FocusType> XMLGregorianCalendar collectTripleFromTemplate(LensContext<F> context, ObjectTemplateType objectTemplateType, ObjectTemplateMappingEvaluationPhaseType phase, ObjectDeltaObject<F> focusOdo, PrismObject<T> target, Map<ItemPath, DeltaSetTriple<? extends ItemValueWithOrigin<?,?>>> outputTripleMap, int iteration, String iterationToken, XMLGregorianCalendar now, String contextDesc, Task task, OperationResult result) throws SchemaException, ExpressionEvaluationException, ObjectNotFoundException, PolicyViolationException { LOGGER.trace("Collecting triples from {}", objectTemplateType); XMLGregorianCalendar nextRecomputeTime = null; // Process includes for (ObjectReferenceType includeRef: objectTemplateType.getIncludeRef()) { PrismObject<ObjectTemplateType> includeObject = includeRef.asReferenceValue().getObject(); if (includeObject == null) { ObjectTemplateType includeObjectType = modelObjectResolver.resolve(includeRef, ObjectTemplateType.class, null, "include reference in "+objectTemplateType + " in " + contextDesc, task, result); includeObject = includeObjectType.asPrismObject(); // Store resolved object for future use (e.g. next waves). includeRef.asReferenceValue().setObject(includeObject); } LOGGER.trace("Including template {}", includeObject); ObjectTemplateType includeObjectType = includeObject.asObjectable(); XMLGregorianCalendar includeNextRecomputeTime = collectTripleFromTemplate(context, includeObjectType, phase, focusOdo, target, outputTripleMap, iteration, iterationToken, now, "include "+includeObject+" in "+objectTemplateType + " in " + contextDesc, task, result); if (includeNextRecomputeTime != null) { if (nextRecomputeTime == null || nextRecomputeTime.compare(includeNextRecomputeTime) == DatatypeConstants.GREATER) { nextRecomputeTime = includeNextRecomputeTime; } } } // Process own mappings List<ObjectTemplateMappingType> mappings = collectMappings(objectTemplateType); List<ObjectTemplateMappingType> sortedMappings = sortMappingsByDependencies(mappings); XMLGregorianCalendar templateNextRecomputeTime = collectTripleFromMappings(sortedMappings, phase, context, objectTemplateType, focusOdo, target, outputTripleMap, iteration, iterationToken, now, contextDesc, task, result); if (templateNextRecomputeTime != null) { if (nextRecomputeTime == null || nextRecomputeTime.compare(templateNextRecomputeTime) == DatatypeConstants.GREATER) { nextRecomputeTime = templateNextRecomputeTime; } } return nextRecomputeTime; } /** * If M2 has a source of X, and M1 has a target of X, then M1 must be placed before M2; we want also to detect cycles. * * So let's stratify mappings according to their dependencies. */ private List<ObjectTemplateMappingType> sortMappingsByDependencies(List<ObjectTemplateMappingType> mappings) { // map.get(X) = { Y1 ... Yn } means that mapping X depends on output of mappings Y1 ... Yn // using indices instead of actual mappings because of equality issues Map<Integer, Set<Integer>> dependencyMap = createDependencyMap(mappings); LOGGER.trace("sortMappingsByDependencies: dependencyMap: {}", dependencyMap); List<Integer> processed = new ArrayList<>(); List<Integer> toProcess = Stream.iterate(0, t -> t+1).limit(mappings.size()).collect(Collectors.toList()); // not a set: to preserve original order while (!toProcess.isEmpty()) { LOGGER.trace("sortMappingsByDependencies: toProcess: {}, processed: {}", toProcess, processed); Integer available = toProcess.stream() .filter(i -> CollectionUtils.isSubCollection(dependencyMap.get(i), processed)) // cannot depend on yet-unprocessed mappings .findFirst().orElse(null); if (available == null) { LOGGER.warn("Cannot sort mappings according to dependencies, there is a cycle. Processing in the original order: {}", mappings); return mappings; } processed.add(available); toProcess.remove(available); } LOGGER.trace("sortMappingsByDependencies: final ordering: {}", processed); return processed.stream().map(i -> mappings.get(i)).collect(Collectors.toList()); } private Map<Integer, Set<Integer>> createDependencyMap(List<ObjectTemplateMappingType> mappings) { Map<Integer, Set<Integer>> map = new HashMap<>(); for (int i = 0; i < mappings.size(); i++) { Set<Integer> dependsOn = new HashSet<>(); for (int j = 0; j < mappings.size(); j++) { if (i == j) { continue; } if (dependsOn(mappings.get(i), mappings.get(j))) { dependsOn.add(j); } } map.put(i, dependsOn); } return map; } // true if any source of mapping1 is equivalent to the target of mapping2 private boolean dependsOn(ObjectTemplateMappingType mapping1, ObjectTemplateMappingType mapping2) { if (mapping2.getTarget() == null || mapping2.getTarget().getPath() == null) { return false; } ItemPath targetPath = mapping2.getTarget().getPath().getItemPath().stripVariableSegment(); for (VariableBindingDefinitionType source : mapping1.getSource()) { ItemPath sourcePath = source.getPath() != null ? source.getPath().getItemPath() : null; if (sourcePath != null && stripFocusVariableSegment(sourcePath).equivalent(targetPath)) { return true; } } return false; } private <T extends Objectable> ItemPath stripFocusVariableSegment(ItemPath sourcePath) { if (sourcePath.startsWithVariable() && QNameUtil.matchAny(ItemPath.getFirstName(sourcePath), MappingEvaluator.FOCUS_VARIABLE_NAMES)) { return sourcePath.stripVariableSegment(); } else { return sourcePath; } } private List<ObjectTemplateMappingType> collectMappings(ObjectTemplateType objectTemplateType) { List<ObjectTemplateMappingType> mappings = new ArrayList<ObjectTemplateMappingType>(); mappings.addAll(objectTemplateType.getMapping()); for (ObjectTemplateItemDefinitionType templateItemDefType: objectTemplateType.getItem()) { for (ObjectTemplateMappingType mapping: templateItemDefType.getMapping()) { VariableBindingDefinitionType target = mapping.getTarget(); if (target == null) { target = new VariableBindingDefinitionType(); target.setPath(templateItemDefType.getRef()); mapping.setTarget(target); } else if (target.getPath() == null) { target = target.clone(); target.setPath(templateItemDefType.getRef()); mapping.setTarget(target); } mappings.add(mapping); } } return mappings; } private <V extends PrismValue, D extends ItemDefinition, F extends FocusType, T extends FocusType> XMLGregorianCalendar collectTripleFromMappings( Collection<ObjectTemplateMappingType> mappings, ObjectTemplateMappingEvaluationPhaseType phase, LensContext<F> context, ObjectTemplateType objectTemplateType, ObjectDeltaObject<F> focusOdo, PrismObject<T> target, Map<ItemPath, DeltaSetTriple<? extends ItemValueWithOrigin<?,?>>> outputTripleMap, int iteration, String iterationToken, XMLGregorianCalendar now, String contextDesc, Task task, OperationResult result) throws SchemaException, ExpressionEvaluationException, ObjectNotFoundException, PolicyViolationException { XMLGregorianCalendar nextRecomputeTime = null; for (ObjectTemplateMappingType mappingType : mappings) { ObjectTemplateMappingEvaluationPhaseType mappingPhase = mappingType.getEvaluationPhase(); if (mappingPhase == null) { mappingPhase = ObjectTemplateMappingEvaluationPhaseType.BEFORE_ASSIGNMENTS; } if (phase != null && mappingPhase != phase) { continue; } LOGGER.trace("Starting evaluation of mapping '{}' in {}", mappingType.getName(), contextDesc); ObjectDeltaObject<F> updatedFocusOdo = getUpdatedFocusOdo(context, focusOdo, outputTripleMap, mappingType, contextDesc); // for mapping chaining Mapping<V,D> mapping = mappingEvaluator.createFocusMapping(mappingFactory, context, mappingType, objectTemplateType, updatedFocusOdo, target, null, iteration, iterationToken, context.getSystemConfiguration(), now, contextDesc, task, result); if (mapping == null) { continue; } Boolean timeConstraintValid = mapping.evaluateTimeConstraintValid(task, result); if (timeConstraintValid != null && !timeConstraintValid) { // Delayed mapping. Just schedule recompute time XMLGregorianCalendar mappingNextRecomputeTime = mapping.getNextRecomputeTime(); LOGGER.trace("Evaluation of mapping {} delayed to {}", mapping, mappingNextRecomputeTime); if (mappingNextRecomputeTime != null) { if (nextRecomputeTime == null || nextRecomputeTime.compare(mappingNextRecomputeTime) == DatatypeConstants.GREATER) { nextRecomputeTime = mappingNextRecomputeTime; } } continue; } mappingEvaluator.evaluateMapping(mapping, context, task, result); ItemPath itemPath = mapping.getOutputPath(); if (itemPath == null) { continue; } DeltaSetTriple<ItemValueWithOrigin<V,D>> outputTriple = ItemValueWithOrigin.createOutputTriple(mapping); if (LOGGER.isTraceEnabled()) { LOGGER.trace("Output triple for {}:\n{}", mapping, DebugUtil.debugDump(outputTriple)); } if (outputTriple == null) { continue; } DeltaSetTriple<ItemValueWithOrigin<V,D>> mapTriple = (DeltaSetTriple<ItemValueWithOrigin<V,D>>) outputTripleMap.get(itemPath); if (mapTriple == null) { outputTripleMap.put(itemPath, outputTriple); } else { mapTriple.merge(outputTriple); } } return nextRecomputeTime; } private <F extends FocusType> ObjectDeltaObject<F> getUpdatedFocusOdo(LensContext<F> context, ObjectDeltaObject<F> focusOdo, Map<ItemPath, DeltaSetTriple<? extends ItemValueWithOrigin<?, ?>>> outputTripleMap, ObjectTemplateMappingType mappingType, String contextDesc) throws ExpressionEvaluationException, PolicyViolationException, SchemaException { ObjectDeltaObject<F> focusOdoCloned = null; for (VariableBindingDefinitionType source : mappingType.getSource()) { if (source.getPath() == null) { continue; } ItemPath path = stripFocusVariableSegment(source.getPath().getItemPath()); if (path.startsWithVariable()) { continue; } DeltaSetTriple<? extends ItemValueWithOrigin<?, ?>> triple = DeltaSetTriple.find(outputTripleMap, path); if (triple == null) { continue; } if (focusOdoCloned == null) { LOGGER.trace("Cloning and updating focusOdo because of chained mappings; chained source path: {}", path); focusOdoCloned = focusOdo.clone(); } else { LOGGER.trace("Updating focusOdo because of chained mappings; chained source path: {}", path); } Class<F> focusClass = context.getFocusContext().getObjectTypeClass(); ItemDefinition<?> itemDefinition = getObjectDefinition(focusClass).findItemDefinition(path); // TODO not much sure about the parameters ItemDelta itemDelta = LensUtil .consolidateTripleToDelta(path, (DeltaSetTriple) triple, itemDefinition, getAprioriItemDelta(focusOdo.getObjectDelta(), path), focusOdo.getNewObject(), null, null, true, true, false, " updating chained source (" + path + ") in " + contextDesc, true); LOGGER.trace("Updating focus ODO with delta:\n{}", itemDelta.debugDumpLazily()); focusOdoCloned.update(itemDelta); } return focusOdoCloned != null ? focusOdoCloned : focusOdo; } private <F extends ObjectType> PrismObjectDefinition<F> getObjectDefinition(Class<F> focusClass) { return prismContext.getSchemaRegistry().findObjectDefinitionByCompileTimeClass(focusClass); } }