Java tutorial
/* * 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.focus; 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 com.evolveum.midpoint.model.impl.lens.LensUtil; import org.apache.commons.collections4.CollectionUtils; import org.apache.commons.lang.BooleanUtils; import org.jetbrains.annotations.NotNull; 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.repo.common.expression.ObjectDeltaObject; import com.evolveum.midpoint.model.common.mapping.MappingImpl; 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.IvwoConsolidator; import com.evolveum.midpoint.model.impl.lens.LensContext; import com.evolveum.midpoint.model.impl.lens.LensFocusContext; import com.evolveum.midpoint.model.impl.lens.StrengthSelector; import com.evolveum.midpoint.model.impl.lens.projector.MappingEvaluator; 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.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.query.ObjectQuery; import com.evolveum.midpoint.prism.query.builder.QueryBuilder; 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.GetOperationOptions; import com.evolveum.midpoint.schema.ResultHandler; import com.evolveum.midpoint.schema.constants.SchemaConstants; 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.CommunicationException; import com.evolveum.midpoint.util.exception.ConfigurationException; 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.exception.SecurityViolationException; import com.evolveum.midpoint.util.logging.Trace; import com.evolveum.midpoint.util.logging.TraceManager; import com.evolveum.midpoint.xml.ns._public.common.common_3.AbstractRoleType; import com.evolveum.midpoint.xml.ns._public.common.common_3.AutoassignMappingType; import com.evolveum.midpoint.xml.ns._public.common.common_3.AutoassignSpecificationType; import com.evolveum.midpoint.xml.ns._public.common.common_3.FocalAutoassignSpecificationType; 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.MappingType; 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.RoleManagementConfigurationType; import com.evolveum.midpoint.xml.ns._public.common.common_3.SystemConfigurationType; import com.evolveum.midpoint.xml.ns._public.common.common_3.TriggerType; import com.evolveum.midpoint.xml.ns._public.common.common_3.VariableBindingDefinitionType; import com.evolveum.prism.xml.ns._public.types_3.ItemPathType; /** * 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, SecurityViolationException, ConfigurationException, CommunicationException { LensFocusContext<F> focusContext = context.getFocusContext(); if (focusContext.isDelete()) { LOGGER.trace("Skipping processing of object template: focus delete"); return; } ObjectTemplateType objectTemplate = context.getFocusTemplate(); String objectTemplateDesc = "(no template)"; if (objectTemplate != null) { objectTemplateDesc = objectTemplate.toString(); } int iteration = focusContext.getIteration(); String iterationToken = focusContext.getIterationToken(); ObjectDeltaObject<F> focusOdo = focusContext.getObjectDeltaObject(); PrismObjectDefinition<F> focusDefinition = getObjectDefinition(focusContext.getObjectTypeClass()); LOGGER.trace("Applying object template {} to {}, iteration {} ({}), phase {}", objectTemplate, focusContext.getObjectNew(), iteration, iterationToken, phase); Map<ItemPath, ObjectTemplateItemDefinitionType> itemDefinitionsMap = collectItemDefinitionsFromTemplate( objectTemplate, objectTemplateDesc, task, result); focusContext.setItemDefinitionsMap(itemDefinitionsMap); List<FocalMappingSpec> mappings = new ArrayList<>(); collectMappingsFromTemplate(context, mappings, objectTemplate, objectTemplateDesc, task, result); collectAutoassignMappings(context, mappings, task, result); Map<ItemPath, DeltaSetTriple<? extends ItemValueWithOrigin<?, ?>>> outputTripleMap = new HashMap<>(); XMLGregorianCalendar nextRecomputeTime = collectTripleFromMappings(context, mappings, phase, focusOdo, focusOdo.getNewObject(), outputTripleMap, iteration, iterationToken, now, task, result); if (LOGGER.isTraceEnabled()) { LOGGER.trace("outputTripleMap before item delta computation:\n{}", DebugUtil.debugDumpMapMultiLine(outputTripleMap)); } String contextDesc = "object template " + objectTemplateDesc + " 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, SecurityViolationException, ConfigurationException, CommunicationException { 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, ObjectTemplateItemDefinitionType> itemDefinitionsMap = collectItemDefinitionsFromTemplate( objectMappingType, objectMappingType.toString(), task, result); List<FocalMappingSpec> mappings = new ArrayList<>(); collectMappingsFromTemplate(context, mappings, objectMappingType, objectMappingType.toString(), task, result); collectAutoassignMappings(context, mappings, task, result); Map<ItemPath, DeltaSetTriple<? extends ItemValueWithOrigin<?, ?>>> outputTripleMap = new HashMap<>(); XMLGregorianCalendar nextRecomputeTime = collectTripleFromMappings(context, mappings, ObjectTemplateMappingEvaluationPhaseType.BEFORE_ASSIGNMENTS, focusOdo, target, outputTripleMap, iteration, iterationToken, now, 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; } @NotNull private Map<ItemPath, ObjectTemplateItemDefinitionType> collectItemDefinitionsFromTemplate( ObjectTemplateType objectTemplateType, String contextDesc, Task task, OperationResult result) throws SchemaException, ObjectNotFoundException { Map<ItemPath, ObjectTemplateItemDefinitionType> definitions = new HashMap<>(); if (objectTemplateType == null) { return definitions; } // 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); 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(); boolean isAssignment = SchemaConstants.PATH_ASSIGNMENT.equivalent(itemPath); 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); IvwoConsolidator consolidator = new IvwoConsolidator<>(); consolidator.setItemPath(itemPath); consolidator.setIvwoTriple(outputTriple); consolidator.setItemDefinition(focusDefinition.findItemDefinition(itemPath)); consolidator.setAprioriItemDelta(aprioriItemDelta); consolidator.setItemContainer(targetObject); consolidator.setValueMatcher(null); consolidator.setComparator(null); consolidator.setAddUnchangedValues(addUnchangedValues); consolidator.setFilterExistingValues(!isNonTolerant); // if non-tolerant, we want to gather ZERO & PLUS sets consolidator.setExclusiveStrong(false); consolidator.setContextDescription(contextDesc); consolidator.setStrengthSelector(StrengthSelector.ALL); @NotNull ItemDelta itemDelta = consolidator.consolidateToDelta(); // 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.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( LensUtil.cloneAndApplyMetadata(valueFromZeroSet, isAssignment, mapping)); } } 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) { // TODO aren't values added twice (regarding addValuesToAdd called ~10 lines above)? itemDelta.addValuesToAdd(LensUtil.cloneAndApplyMetadata(zeroSetIvwo.getItemValue(), isAssignment, zeroSetIvwo.getMapping())); } 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> void collectMappingsFromTemplate(LensContext<F> context, List<FocalMappingSpec> mappings, ObjectTemplateType objectTemplateType, String contextDesc, Task task, OperationResult result) throws SchemaException, ExpressionEvaluationException, ObjectNotFoundException, PolicyViolationException, SecurityViolationException, ConfigurationException, CommunicationException { if (objectTemplateType == null) { return; } LOGGER.trace("Collecting mappings from {}", objectTemplateType); // 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(); collectMappingsFromTemplate(context, mappings, includeObjectType, "include " + includeObject + " in " + objectTemplateType + " in " + contextDesc, task, result); } // Process own mappings collectMappings(mappings, objectTemplateType); } private void collectMappings(List<FocalMappingSpec> mappings, ObjectTemplateType objectTemplateType) { for (ObjectTemplateMappingType mapping : objectTemplateType.getMapping()) { mappings.add(new FocalMappingSpec(mapping, objectTemplateType)); } for (ObjectTemplateItemDefinitionType templateItemDefType : objectTemplateType.getItem()) { for (ObjectTemplateMappingType mapping : templateItemDefType.getMapping()) { setMappingTarget(mapping, templateItemDefType.getRef()); mappings.add(new FocalMappingSpec(mapping, objectTemplateType)); } } } private <F extends FocusType, T extends FocusType> void collectAutoassignMappings(LensContext<F> context, List<FocalMappingSpec> mappings, Task task, OperationResult result) throws SchemaException, ExpressionEvaluationException, ObjectNotFoundException, PolicyViolationException, SecurityViolationException, ConfigurationException, CommunicationException { if (!autoassignEnabled(context.getSystemConfiguration())) { return; } ObjectQuery query = QueryBuilder.queryFor(AbstractRoleType.class, prismContext) .item(SchemaConstants.PATH_AUTOASSIGN_ENABLED).eq(true).build(); ResultHandler<AbstractRoleType> handler = (role, objectResult) -> { AutoassignSpecificationType autoassign = role.asObjectable().getAutoassign(); if (autoassign == null) { return true; } if (!BooleanUtils.isTrue(autoassign.isEnabled())) { return true; } FocalAutoassignSpecificationType focalAutoassignSpec = autoassign.getFocus(); if (focalAutoassignSpec == null) { return true; } for (AutoassignMappingType autoMapping : focalAutoassignSpec.getMapping()) { AutoassignMappingType mapping = autoMapping.clone(); setMappingTarget(mapping, SchemaConstants.PATH_ASSIGNMENT.asItemPathType()); mappings.add(new FocalMappingSpec(mapping, role.asObjectable())); LOGGER.trace("Collected autoassign mapping {} from {}", mapping.getName(), role); } return true; }; cacheRepositoryService.searchObjectsIterative(AbstractRoleType.class, query, handler, GetOperationOptions.createReadOnlyCollection(), false, result); } private void setMappingTarget(MappingType mapping, ItemPathType path) { VariableBindingDefinitionType target = mapping.getTarget(); if (target == null) { target = new VariableBindingDefinitionType(); target.setPath(path); mapping.setTarget(target); } else if (target.getPath() == null) { target = target.clone(); target.setPath(path); mapping.setTarget(target); } } private boolean autoassignEnabled(PrismObject<SystemConfigurationType> systemConfiguration) { if (systemConfiguration == null) { return false; } RoleManagementConfigurationType roleManagement = systemConfiguration.asObjectable().getRoleManagement(); if (roleManagement == null) { return false; } return BooleanUtils.isTrue(roleManagement.isAutoassignEnabled()); } /** * 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<FocalMappingSpec> sortMappingsByDependencies(List<FocalMappingSpec> 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<FocalMappingSpec> 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(FocalMappingSpec mappingSpec1, FocalMappingSpec mappingSpec2) { MappingType mapping1 = mappingSpec1.getMappingType(); MappingType mapping2 = mappingSpec2.getMappingType(); 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 <V extends PrismValue, D extends ItemDefinition, F extends FocusType, T extends FocusType> XMLGregorianCalendar collectTripleFromMappings( LensContext<F> context, List<FocalMappingSpec> mappings, ObjectTemplateMappingEvaluationPhaseType phase, ObjectDeltaObject<F> focusOdo, PrismObject<T> target, Map<ItemPath, DeltaSetTriple<? extends ItemValueWithOrigin<?, ?>>> outputTripleMap, int iteration, String iterationToken, XMLGregorianCalendar now, Task task, OperationResult result) throws SchemaException, ExpressionEvaluationException, ObjectNotFoundException, PolicyViolationException, SecurityViolationException, ConfigurationException, CommunicationException { List<FocalMappingSpec> sortedMappings = sortMappingsByDependencies(mappings); XMLGregorianCalendar nextRecomputeTime = null; for (FocalMappingSpec mappingSpec : sortedMappings) { ObjectTemplateMappingEvaluationPhaseType mappingPhase = mappingSpec.getEvaluationPhase(); if (phase != null && mappingPhase != phase) { continue; } String mappingDesc = mappingSpec.shortDump(); LOGGER.trace("Starting evaluation of {}", mappingDesc); ObjectDeltaObject<F> updatedFocusOdo = getUpdatedFocusOdo(context, focusOdo, outputTripleMap, mappingSpec, mappingDesc); // for mapping chaining MappingImpl<V, D> mapping = mappingEvaluator.createFocusMapping(mappingFactory, context, mappingSpec.getMappingType(), mappingSpec.getOriginObject(), updatedFocusOdo, mappingSpec.getDefaultSource(focusOdo), target, null, iteration, iterationToken, context.getSystemConfiguration(), now, mappingDesc, 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, FocalMappingSpec mappingSpec, String contextDesc) throws ExpressionEvaluationException, PolicyViolationException, SchemaException { ObjectDeltaObject<F> focusOdoCloned = null; for (VariableBindingDefinitionType source : mappingSpec.getMappingType().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 IvwoConsolidator consolidator = new IvwoConsolidator<>(); consolidator.setItemPath(path); consolidator.setIvwoTriple(triple); consolidator.setItemDefinition(itemDefinition); consolidator.setAprioriItemDelta(getAprioriItemDelta(focusOdo.getObjectDelta(), path)); consolidator.setItemContainer(focusOdo.getNewObject()); consolidator.setValueMatcher(null); consolidator.setComparator(null); consolidator.setAddUnchangedValues(true); consolidator.setFilterExistingValues(true); consolidator.setExclusiveStrong(false); consolidator.setContextDescription(" updating chained source (" + path + ") in " + contextDesc); consolidator.setStrengthSelector(StrengthSelector.ALL); ItemDelta itemDelta = consolidator.consolidateToDelta(); 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); } }