/** * 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.Collection; import java.util.Map; import java.util.function.Function; import org.springframework.stereotype.Component; import com.evolveum.midpoint.model.impl.lens.AbstractConstruction; import com.evolveum.midpoint.model.impl.lens.ConstructionPack; import com.evolveum.midpoint.model.impl.lens.EvaluatedAssignmentImpl; import com.evolveum.midpoint.model.impl.lens.FailableLensFunction; import com.evolveum.midpoint.model.impl.lens.LensContext; import com.evolveum.midpoint.prism.PrismPropertyValue; import com.evolveum.midpoint.prism.delta.DeltaMapTriple; import com.evolveum.midpoint.prism.delta.DeltaSetTriple; import com.evolveum.midpoint.prism.delta.PlusMinusZero; import com.evolveum.midpoint.schema.result.OperationResult; import com.evolveum.midpoint.task.api.Task; import com.evolveum.midpoint.util.HumanReadableDescribable; import com.evolveum.midpoint.util.exception.CommunicationException; import com.evolveum.midpoint.util.exception.ConfigurationException; import com.evolveum.midpoint.util.exception.ObjectNotFoundException; 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.FocusType; /** * @author semancik * */ @Component public class ConstructionProcessor { private static final Trace LOGGER = TraceManager.getTrace(ConstructionProcessor.class); public <F extends FocusType, K extends HumanReadableDescribable, T extends AbstractConstruction> DeltaMapTriple<K, ConstructionPack<T>> processConstructions(LensContext<F> context, DeltaSetTriple<EvaluatedAssignmentImpl<F>> evaluatedAssignmentTriple, Function<EvaluatedAssignmentImpl<F>,DeltaSetTriple<T>> constructionTripleExtractor, FailableLensFunction<T,K> keyGenerator, ComplexConstructionConsumer<K,T> consumer, Task task, OperationResult result) throws ObjectNotFoundException, SchemaException, CommunicationException, ConfigurationException, SecurityViolationException { // We will be collecting the evaluated account constructions into these three maps. // It forms a kind of delta set triple for the account constructions. DeltaMapTriple<K, ConstructionPack<T>> constructionMapTriple = new DeltaMapTriple<>(); collectToConstructionMaps(context, evaluatedAssignmentTriple, constructionMapTriple, constructionTripleExtractor, keyGenerator, task, result); if (LOGGER.isTraceEnabled()) { // Dump the maps LOGGER.trace("constructionMapTriple:\n{}", constructionMapTriple.debugDump()); } // Now we are processing constructions from all the three sets once again. We will create projection contexts // for them if not yet created. Now we will do the usual routing for converting the delta triples to deltas. // I.e. zero means unchanged, plus means added, minus means deleted. That will be recorded in the SynchronizationPolicyDecision. // We will also collect all the construction triples to projection context. These will be used later for computing // actual attribute deltas (in consolidation processor). for (K key : constructionMapTriple.unionKeySets()) { boolean cont = consumer.before(key); if (!cont) { continue; } String desc = key.toHumanReadableDescription(); ConstructionPack<T> zeroConstructionPack = constructionMapTriple.getZeroMap().get(key); ConstructionPack<T> plusConstructionPack = constructionMapTriple.getPlusMap().get(key); if (LOGGER.isTraceEnabled()) { if (zeroConstructionPack == null) { LOGGER.trace("ZERO construction pack: null"); } else { LOGGER.trace("ZERO construction pack (hasValidAssignment={}, hasStrongConstruction={})\n{}", zeroConstructionPack.hasValidAssignment(), zeroConstructionPack.hasStrongConstruction(), zeroConstructionPack.debugDump(1)); } if (plusConstructionPack == null) { LOGGER.trace("PLUS construction pack: null"); } else { LOGGER.trace("PLUS construction pack (hasValidAssignment={}, hasStrongConstruction={})\n{}", plusConstructionPack.hasValidAssignment(), plusConstructionPack.hasStrongConstruction(), plusConstructionPack.debugDump(1)); } } // SITUATION: The construction is ASSIGNED if (plusConstructionPack != null && plusConstructionPack.hasStrongConstruction()) { if (plusConstructionPack.hasValidAssignment()) { LOGGER.trace("Construction {}: assigned (valid)", desc); consumer.onAssigned(key, desc); } else { // Just ignore it, do not even create projection context LOGGER.trace("Construction {} ignoring: assigned (invalid)", desc); } // SITUATION: The projection should exist (is valid), there is NO CHANGE in assignments } else if (zeroConstructionPack != null && zeroConstructionPack.hasValidAssignment() && zeroConstructionPack.hasStrongConstruction()) { LOGGER.trace("Construction {}: unchanged (valid)", desc); consumer.onUnchangedValid(key, desc); // SITUATION: The projection is both ASSIGNED and UNASSIGNED } else if (constructionMapTriple.getPlusMap().containsKey(key) && constructionMapTriple.getMinusMap().containsKey(key) && plusConstructionPack.hasStrongConstruction()) { // Account was removed and added in the same operation. This is the case if e.g. one role is // removed and another is added and they include the same account. // Keep original account state ConstructionPack<T> plusPack = constructionMapTriple.getPlusMap().get(key); ConstructionPack<T> minusPack = constructionMapTriple.getMinusMap().get(key); if (plusPack.hasValidAssignment() && minusPack.hasValidAssignment()) { LOGGER.trace("Construction {}: both assigned and unassigned (valid)", desc); consumer.onUnchangedValid(key, desc); } else if (!plusPack.hasValidAssignment() && !minusPack.hasValidAssignment()) { // Just ignore it, do not even create projection context LOGGER.trace("Construction {} ignoring: both assigned and unassigned (invalid)", desc); } else if (plusPack.hasValidAssignment() && !minusPack.hasValidAssignment()) { // Assignment became valid. Same as if it was assigned. LOGGER.trace("Construction {}: both assigned and unassigned (invalid->valid)", desc); consumer.onAssigned(key, desc); } else if (!plusPack.hasValidAssignment() && minusPack.hasValidAssignment()) { // Assignment became invalid. Same as if it was unassigned. LOGGER.trace("Construction {}: both assigned and unassigned (valid->invalid)", desc); consumer.onUnassigned(key, desc); } else { throw new IllegalStateException("Whoops!?!"); } // SITUATION: The projection is UNASSIGNED } else if (constructionMapTriple.getMinusMap().containsKey(key)) { LOGGER.trace("Construction {}: unassigned", desc); consumer.onUnassigned(key, desc); // SITUATION: The projection should exist (invalid), there is NO CHANGE in assignments } else if (constructionMapTriple.getZeroMap().containsKey(key) && !constructionMapTriple.getZeroMap().get(key).hasValidAssignment()) { LOGGER.trace("Construction {}: unchanged (invalid)", desc); consumer.onUnchangedInvalid(key, desc); // This is a legal state: projection was assigned, but it only has weak construction (no strong) // We do not need to do anything. But we want to log the message // and we do not want the "looney" error below. } else if (plusConstructionPack != null && !plusConstructionPack.hasStrongConstruction()) { // Just ignore it, do not even create projection context LOGGER.trace("Construction {} ignoring: assigned (weak only)", desc); // This is a legal state: projection is unchanged, but it only has weak construction (no strong) // We do not need to do anything. But we want to log the message // and we do not want the "looney" error below. } else if (zeroConstructionPack != null && !zeroConstructionPack.hasStrongConstruction()) { // Just ignore it, do not even create projection context LOGGER.trace("Construction {} ignoring: unchanged (weak only)", desc); } else { throw new IllegalStateException("Construction " + desc + " went looney"); } consumer.after(key, desc, constructionMapTriple); } return constructionMapTriple; } public <F extends FocusType, K, T extends AbstractConstruction> void collectToConstructionMaps(LensContext<F> context, DeltaSetTriple<EvaluatedAssignmentImpl<F>> evaluatedAssignmentTriple, DeltaMapTriple<K, ConstructionPack<T>> constructionMapTriple, Function<EvaluatedAssignmentImpl<F>,DeltaSetTriple<T>> constructionTripleExtractor, FailableLensFunction<T,K> keyGenerator, Task task, OperationResult result) throws ObjectNotFoundException, SchemaException, CommunicationException, ConfigurationException, SecurityViolationException { collectToConstructionMapFromEvaluatedAssignments(context, evaluatedAssignmentTriple.getZeroSet(), constructionMapTriple, constructionTripleExtractor, keyGenerator, PlusMinusZero.ZERO, task, result); collectToConstructionMapFromEvaluatedAssignments(context, evaluatedAssignmentTriple.getPlusSet(), constructionMapTriple, constructionTripleExtractor, keyGenerator, PlusMinusZero.PLUS, task, result); collectToConstructionMapFromEvaluatedAssignments(context, evaluatedAssignmentTriple.getMinusSet(), constructionMapTriple, constructionTripleExtractor, keyGenerator, PlusMinusZero.MINUS, task, result); } private <F extends FocusType, K, T extends AbstractConstruction> void collectToConstructionMapFromEvaluatedAssignments(LensContext<F> context, Collection<EvaluatedAssignmentImpl<F>> evaluatedAssignments, DeltaMapTriple<K, ConstructionPack<T>> constructionMapTriple, Function<EvaluatedAssignmentImpl<F>,DeltaSetTriple<T>> constructionTripleExtractor, FailableLensFunction<T,K> keyGenerator, PlusMinusZero mode, Task task, OperationResult result) throws ObjectNotFoundException, SchemaException, CommunicationException, ConfigurationException, SecurityViolationException { for (EvaluatedAssignmentImpl<F> evaluatedAssignment: evaluatedAssignments) { if (LOGGER.isTraceEnabled()) { LOGGER.trace("Collecting constructions from evaluated assignment:\n{}", evaluatedAssignment.debugDump()); } DeltaSetTriple<T> constructionTriple = constructionTripleExtractor.apply(evaluatedAssignment); collectToConstructionMapFromEvaluatedConstructions(context, evaluatedAssignment, constructionTriple.getZeroSet(), constructionMapTriple, keyGenerator, mode, PlusMinusZero.ZERO, task, result); collectToConstructionMapFromEvaluatedConstructions(context, evaluatedAssignment, constructionTriple.getPlusSet(), constructionMapTriple, keyGenerator, mode, PlusMinusZero.PLUS, task, result); collectToConstructionMapFromEvaluatedConstructions(context, evaluatedAssignment, constructionTriple.getMinusSet(), constructionMapTriple, keyGenerator, mode, PlusMinusZero.MINUS, task, result); } } private <F extends FocusType, K, T extends AbstractConstruction> void collectToConstructionMapFromEvaluatedConstructions(LensContext<F> context, EvaluatedAssignmentImpl<F> evaluatedAssignment, Collection<T> evaluatedConstructions, DeltaMapTriple<K, ConstructionPack<T>> constructionMapTriple, FailableLensFunction<T,K> keyGenerator, PlusMinusZero mode1, PlusMinusZero mode2, Task task, OperationResult result) throws ObjectNotFoundException, SchemaException, CommunicationException, ConfigurationException, SecurityViolationException { for (T construction : evaluatedConstructions) { PlusMinusZero mode = PlusMinusZero.compute(mode1, mode2); Map<K, ConstructionPack<T>> constructionMap = constructionMapTriple.getMap(mode); if (constructionMap == null) { continue; } K key = keyGenerator.apply(construction); ConstructionPack<T> constructionPack; if (constructionMap.containsKey(key)) { constructionPack = constructionMap.get(key); } else { constructionPack = new ConstructionPack<>(); constructionMap.put(key, constructionPack); } constructionPack.add(new PrismPropertyValue<>(construction)); if (evaluatedAssignment.isValid()) { constructionPack.setHasValidAssignment(true); } if (evaluatedAssignment.isForceRecon()) { constructionPack.setForceRecon(true); } } } }