/*
* Copyright (c) 2013-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.*;
import java.util.Map.Entry;
import javax.xml.bind.JAXBElement;
import javax.xml.datatype.DatatypeConstants;
import javax.xml.datatype.XMLGregorianCalendar;
import javax.xml.namespace.QName;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import com.evolveum.midpoint.model.common.expression.ExpressionUtil;
import com.evolveum.midpoint.model.common.expression.ExpressionVariables;
import com.evolveum.midpoint.model.common.expression.ItemDeltaItem;
import com.evolveum.midpoint.model.common.expression.ObjectDeltaObject;
import com.evolveum.midpoint.model.common.expression.Source;
import com.evolveum.midpoint.model.common.expression.StringPolicyResolver;
import com.evolveum.midpoint.model.common.mapping.Mapping;
import com.evolveum.midpoint.model.common.mapping.MappingFactory;
import com.evolveum.midpoint.model.impl.expr.ExpressionEnvironment;
import com.evolveum.midpoint.model.impl.expr.ModelExpressionThreadLocalHolder;
import com.evolveum.midpoint.model.impl.lens.AssignmentPathVariables;
import com.evolveum.midpoint.model.impl.lens.LensContext;
import com.evolveum.midpoint.model.impl.lens.LensElementContext;
import com.evolveum.midpoint.model.impl.lens.LensProjectionContext;
import com.evolveum.midpoint.model.impl.lens.LensUtil;
import com.evolveum.midpoint.model.impl.lens.projector.credentials.CredentialPolicyEvaluator;
import com.evolveum.midpoint.model.impl.lens.projector.credentials.CredentialsProcessor;
import com.evolveum.midpoint.model.impl.trigger.RecomputeTriggerHandler;
import com.evolveum.midpoint.model.impl.util.Utils;
import com.evolveum.midpoint.prism.Item;
import com.evolveum.midpoint.prism.ItemDefinition;
import com.evolveum.midpoint.prism.OriginType;
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.PrismPropertyDefinition;
import com.evolveum.midpoint.prism.PrismPropertyValue;
import com.evolveum.midpoint.prism.PrismValue;
import com.evolveum.midpoint.prism.delta.ContainerDelta;
import com.evolveum.midpoint.prism.delta.ItemDelta;
import com.evolveum.midpoint.prism.delta.PrismValueDeltaSetTriple;
import com.evolveum.midpoint.prism.path.ItemPath;
import com.evolveum.midpoint.schema.CapabilityUtil;
import com.evolveum.midpoint.schema.constants.ExpressionConstants;
import com.evolveum.midpoint.schema.constants.SchemaConstants;
import com.evolveum.midpoint.schema.result.OperationResult;
import com.evolveum.midpoint.schema.util.ObjectResolver;
import com.evolveum.midpoint.task.api.Task;
import com.evolveum.midpoint.util.exception.CommonException;
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.ObjectNotFoundException;
import com.evolveum.midpoint.util.exception.SchemaException;
import com.evolveum.midpoint.util.exception.SecurityViolationException;
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.ActivationStatusType;
import com.evolveum.midpoint.xml.ns._public.common.common_3.FocusType;
import com.evolveum.midpoint.xml.ns._public.common.common_3.GenerateExpressionEvaluatorType;
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.ObjectType;
import com.evolveum.midpoint.xml.ns._public.common.common_3.PasswordType;
import com.evolveum.midpoint.xml.ns._public.common.common_3.ResourceBidirectionalMappingType;
import com.evolveum.midpoint.xml.ns._public.common.common_3.ShadowType;
import com.evolveum.midpoint.xml.ns._public.common.common_3.StringPolicyType;
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.ValuePolicyType;
import com.evolveum.midpoint.xml.ns._public.common.common_3.VariableBindingDefinitionType;
import com.evolveum.midpoint.xml.ns._public.resource.capabilities_3.ActivationCapabilityType;
import com.evolveum.midpoint.xml.ns._public.resource.capabilities_3.ActivationValidityCapabilityType;
import com.evolveum.prism.xml.ns._public.types_3.ItemPathType;
/**
* @author Radovan Semancik
*
*/
@Component
public class MappingEvaluator {
private static final Trace LOGGER = TraceManager.getTrace(MappingEvaluator.class);
@Autowired(required=true)
private MappingFactory mappingFactory;
@Autowired(required=true)
private CredentialsProcessor credentialsProcessor;
@Autowired(required=true)
private ContextLoader contextLoader;
public static final List<QName> FOCUS_VARIABLE_NAMES = Arrays.asList(ExpressionConstants.VAR_FOCUS, ExpressionConstants.VAR_USER);
public <V extends PrismValue, D extends ItemDefinition, F extends ObjectType> void evaluateMapping(
Mapping<V,D> mapping, LensContext<F> lensContext, Task task, OperationResult parentResult) throws ExpressionEvaluationException, ObjectNotFoundException, SchemaException {
evaluateMapping(mapping, lensContext, null, task, parentResult);
}
public <V extends PrismValue, D extends ItemDefinition, F extends ObjectType> void evaluateMapping(
Mapping<V,D> mapping, LensContext<F> lensContext, LensProjectionContext projContext, Task task, OperationResult parentResult) throws ExpressionEvaluationException, ObjectNotFoundException, SchemaException {
ExpressionEnvironment<F> env = new ExpressionEnvironment<>();
env.setLensContext(lensContext);
env.setProjectionContext(projContext);
env.setCurrentResult(parentResult);
env.setCurrentTask(task);
ModelExpressionThreadLocalHolder.pushExpressionEnvironment(env);
ObjectType originObject = mapping.getOriginObject();
String objectOid, objectName, objectTypeName;
if (originObject != null) {
objectOid = originObject.getOid();
objectName = String.valueOf(originObject.getName());
objectTypeName = originObject.getClass().getSimpleName();
} else {
objectOid = objectName = objectTypeName = null;
}
String mappingName = mapping.getItemName() != null ? mapping.getItemName().getLocalPart() : null;
long start = System.currentTimeMillis();
try {
task.recordState("Started evaluation of mapping " + mapping.getMappingContextDescription() + ".");
mapping.evaluate(task, parentResult);
task.recordState("Successfully finished evaluation of mapping " + mapping.getMappingContextDescription() + " in " + (System.currentTimeMillis()-start) + " ms.");
} catch (IllegalArgumentException e) {
task.recordState("Evaluation of mapping " + mapping.getMappingContextDescription() + " finished with error in " + (System.currentTimeMillis()-start) + " ms.");
throw new IllegalArgumentException(e.getMessage()+" in "+mapping.getContextDescription(), e);
} finally {
task.recordMappingOperation(objectOid, objectName, objectTypeName, mappingName, System.currentTimeMillis() - start);
ModelExpressionThreadLocalHolder.popExpressionEnvironment();
if (lensContext.getDebugListener() != null) {
lensContext.getDebugListener().afterMappingEvaluation(lensContext, mapping);
}
}
}
public <T, F extends FocusType> void evaluateOutboundMapping(final LensContext<F> context,
final LensProjectionContext projCtx, List<MappingType> outboundMappings,
final ItemPath focusPropertyPath, final ItemPath projectionPropertyPath,
final MappingInitializer<PrismPropertyValue<T>,PrismPropertyDefinition<T>> initializer, MappingOutputProcessor<PrismPropertyValue<T>> processor,
XMLGregorianCalendar now, final boolean evaluateCurrent, boolean evaluateWeak,
String desc, final Task task, final OperationResult result) throws ExpressionEvaluationException, ObjectNotFoundException, SchemaException, CommunicationException, ConfigurationException, SecurityViolationException {
String projCtxDesc = projCtx.toHumanReadableString();
PrismObject<ShadowType> shadowNew = projCtx.getObjectNew();
MappingInitializer<PrismPropertyValue<T>,PrismPropertyDefinition<T>> internalInitializer =
builder -> {
builder.addVariableDefinitions(Utils.getDefaultExpressionVariables(context, projCtx).getMap());
builder.originType(OriginType.OUTBOUND);
builder.originObject(projCtx.getResource());
initializer.initialize(builder);
return builder;
};
MappingLoader<ShadowType> loader = new ProjectionMappingLoader<>(context, projCtx, contextLoader);
MappingEvaluatorParams<PrismPropertyValue<T>, PrismPropertyDefinition<T>, ShadowType, F> params = new MappingEvaluatorParams<>();
params.setMappingTypes(outboundMappings);
params.setMappingDesc(desc + " in projection " + projCtxDesc);
params.setNow(now);
params.setInitializer(internalInitializer);
params.setProcessor(processor);
params.setTargetLoader(loader);
params.setAPrioriTargetObject(shadowNew);
params.setAPrioriTargetDelta(LensUtil.findAPrioriDelta(context, projCtx));
params.setTargetContext(projCtx);
params.setDefaultTargetItemPath(projectionPropertyPath);
if (context.getFocusContext() != null) {
params.setSourceContext(context.getFocusContext().getObjectDeltaObject());
}
params.setEvaluateCurrent(evaluateCurrent);
params.setEvaluateWeak(evaluateWeak);
params.setContext(context);
params.setHasFullTargetObject(projCtx.hasFullShadow());
evaluateMappingSetProjection(params, task, result);
}
public <V extends PrismValue, D extends ItemDefinition, T extends ObjectType, F extends FocusType> void evaluateMappingSetProjection(
MappingEvaluatorParams<V,D,T,F> params,
Task task, OperationResult result) throws ExpressionEvaluationException, ObjectNotFoundException, SchemaException, CommunicationException, ConfigurationException, SecurityViolationException {
String mappingDesc = params.getMappingDesc();
LensElementContext<T> targetContext = params.getTargetContext();
PrismObjectDefinition<T> targetObjectDefinition = targetContext.getObjectDefinition();
ItemPath defaultTargetItemPath = params.getDefaultTargetItemPath();
Map<ItemPath,MappingOutputStruct<V>> outputTripleMap = new HashMap<>();
XMLGregorianCalendar nextRecomputeTime = null;
Collection<MappingType> mappingTypes = params.getMappingTypes();
Collection<Mapping<V,D>> mappings = new ArrayList<>(mappingTypes.size());
for (MappingType mappingType: mappingTypes) {
Mapping.Builder<V,D> mappingBuilder = mappingFactory.createMappingBuilder(mappingType, mappingDesc);
String mappingName = null;
if (mappingType.getName() != null) {
mappingName = mappingType.getName();
}
if (!mappingBuilder.isApplicableToChannel(params.getContext().getChannel())) {
LOGGER.trace("Mapping {} not applicable to channel, skipping {}", mappingName, params.getContext().getChannel());
continue;
}
mappingBuilder.now(params.getNow());
if (defaultTargetItemPath != null && targetObjectDefinition != null) {
D defaultTargetItemDef = targetObjectDefinition.findItemDefinition(defaultTargetItemPath);
mappingBuilder.defaultTargetDefinition(defaultTargetItemDef);
mappingBuilder.defaultTargetPath(defaultTargetItemPath);
} else {
mappingBuilder.defaultTargetDefinition(params.getTargetItemDefinition());
mappingBuilder.defaultTargetPath(defaultTargetItemPath);
}
mappingBuilder.targetContext(targetObjectDefinition);
if (params.getSourceContext() != null) {
mappingBuilder.sourceContext(params.getSourceContext());
}
// Initialize mapping (using Inversion of Control)
mappingBuilder = params.getInitializer().initialize(mappingBuilder);
Mapping<V,D> mapping = mappingBuilder.build();
Boolean timeConstraintValid = mapping.evaluateTimeConstraintValid(task, result);
if (params.getEvaluateCurrent() != null) {
if (params.getEvaluateCurrent() && !timeConstraintValid) {
LOGGER.trace("Mapping {} is non-current, but evulating current mappings, skipping {}", mappingName, params.getContext().getChannel());
continue;
}
if (!params.getEvaluateCurrent() && timeConstraintValid) {
LOGGER.trace("Mapping {} is current, but evulating non-current mappings, skipping {}", mappingName, params.getContext().getChannel());
continue;
}
}
mappings.add(mapping);
}
boolean hasFullTargetObject = params.hasFullTargetObject();
PrismObject<T> aPrioriTargetObject = params.getAPrioriTargetObject();
LOGGER.trace("Going to process {} mappings for {}", mappings.size(), mappingDesc);
for (Mapping<V,D> mapping: mappings) {
if (mapping.getStrength() == MappingStrengthType.WEAK) {
// Evaluate weak mappings in a second run.
continue;
}
ItemPath mappingOutputPath = mapping.getOutputPath();
if (params.isFixTarget() && mappingOutputPath != null && defaultTargetItemPath != null && !mappingOutputPath.equivalent(defaultTargetItemPath)) {
throw new ExpressionEvaluationException("Target cannot be overridden in "+mappingDesc);
}
if (params.getAPrioriTargetDelta() != null && mappingOutputPath != null) {
ItemDelta<?,?> aPrioriItemDelta = params.getAPrioriTargetDelta().findItemDelta(mappingOutputPath);
if (mapping.getStrength() != MappingStrengthType.STRONG) {
if (aPrioriItemDelta != null && !aPrioriItemDelta.isEmpty()) {
continue;
}
}
}
evaluateMapping(mapping, params.getContext(), task, result);
PrismValueDeltaSetTriple<V> mappingOutputTriple = mapping.getOutputTriple();
if (LOGGER.isTraceEnabled()) {
LOGGER.trace("Output triple of mapping {}\n{}", mapping.getContextDescription(),
mappingOutputTriple==null?null:mappingOutputTriple.debugDump(1));
}
if (mappingOutputTriple != null) {
MappingOutputStruct<V> mappingOutputStruct = outputTripleMap.get(mappingOutputPath);
if (mappingOutputStruct == null) {
mappingOutputStruct = new MappingOutputStruct<>();
outputTripleMap.put(mappingOutputPath, mappingOutputStruct);
}
if (mapping.getStrength() == MappingStrengthType.STRONG) {
mappingOutputStruct.setStrongMappingWasUsed(true);
// if (!hasFullTargetObject && params.getTargetLoader() != null) {
// if (!params.getTargetLoader().isLoaded()) {
// aPrioriTargetObject = params.getTargetLoader().load(task, result);
// LOGGER.trace("Loaded object because of strong mapping: {}", aPrioriTargetObject);
// hasFullTargetObject = true;
// }
// }
}
PrismValueDeltaSetTriple<V> outputTriple = mappingOutputStruct.getOutputTriple();
if (outputTriple == null) {
mappingOutputStruct.setOutputTriple(mappingOutputTriple);
} else {
outputTriple.merge(mappingOutputTriple);
}
}
}
if (params.isEvaluateWeak()) {
// Second pass, evaluate only weak mappings
for (Mapping<V,D> mapping: mappings) {
if (mapping.getStrength() != MappingStrengthType.WEAK) {
continue;
}
ItemPath mappingOutputPath = mapping.getOutputPath();
if (params.isFixTarget() && mappingOutputPath != null && defaultTargetItemPath != null && !mappingOutputPath.equivalent(defaultTargetItemPath)) {
throw new ExpressionEvaluationException("Target cannot be overridden in "+mappingDesc);
}
MappingOutputStruct<V> mappingOutputStruct = outputTripleMap.get(mappingOutputPath);
if (mappingOutputStruct == null) {
mappingOutputStruct = new MappingOutputStruct<>();
outputTripleMap.put(mappingOutputPath, mappingOutputStruct);
}
PrismValueDeltaSetTriple<V> outputTriple = mappingOutputStruct.getOutputTriple();
if (outputTriple != null) {
// Previous mapping produced output. We do not need to evaluate weak mapping.
// TODO: this is not entirelly correct. Previous mapping might have deleted all
// values. Also we may need the output of the weak mapping to correctly process
// non-tolerant values (to avoid removing the value that weak mapping produces).
// MID-3847
continue;
}
Item<V,D> aPrioriTargetItem = null;
if (aPrioriTargetObject != null && mappingOutputPath != null) {
aPrioriTargetItem = aPrioriTargetObject.findItem(mappingOutputPath);
}
if (hasNoValue(aPrioriTargetItem)) {
mappingOutputStruct.setWeakMappingWasUsed(true);
evaluateMapping(mapping, params.getContext(), task, result);
PrismValueDeltaSetTriple<V> mappingOutputTriple = mapping.getOutputTriple();
if (mappingOutputTriple != null) {
// This may be counter-intuitive to load object after the mapping is executed
// But the mapping may not be activated (e.g. condition is false). And in that
// case we really do not want to trigger object loading.
// This is all not right. See MID-3847
if (!hasFullTargetObject && params.getTargetLoader() != null) {
if (!params.getTargetLoader().isLoaded()) {
aPrioriTargetObject = params.getTargetLoader().load(task, result);
LOGGER.trace("Loaded object because of weak mapping: {}", aPrioriTargetObject);
hasFullTargetObject = true;
}
}
if (aPrioriTargetObject != null && mappingOutputPath != null) {
aPrioriTargetItem = aPrioriTargetObject.findItem(mappingOutputPath);
}
if (!hasNoValue(aPrioriTargetItem)) {
continue;
}
if (outputTriple == null) {
mappingOutputStruct.setOutputTriple(mappingOutputTriple);
} else {
outputTriple.merge(mappingOutputTriple);
}
}
}
}
}
MappingOutputProcessor<V> processor = params.getProcessor();
for (Entry<ItemPath, MappingOutputStruct<V>> outputTripleMapEntry: outputTripleMap.entrySet()) {
ItemPath mappingOutputPath = outputTripleMapEntry.getKey();
MappingOutputStruct<V> mappingOutputStruct = outputTripleMapEntry.getValue();
PrismValueDeltaSetTriple<V> outputTriple = mappingOutputStruct.getOutputTriple();
boolean defaultProcessing = true;
if (processor != null) {
LOGGER.trace("Executing processor to process mapping evaluation results: {}", processor);
defaultProcessing = processor.process(mappingOutputPath, mappingOutputStruct);
}
if (defaultProcessing) {
if (outputTriple == null) {
LOGGER.trace("{} expression resulted in null triple for {}, skipping", mappingDesc, targetContext);
continue;
}
ItemDefinition targetItemDefinition = null;
if (mappingOutputPath != null) {
targetItemDefinition = targetObjectDefinition.findItemDefinition(mappingOutputPath);
if (targetItemDefinition == null) {
throw new SchemaException("No definition for item "+mappingOutputPath+" in "+targetObjectDefinition);
}
} else {
targetItemDefinition = params.getTargetItemDefinition();
}
ItemDelta<V,D> targetItemDelta = targetItemDefinition.createEmptyDelta(mappingOutputPath);
Item<V,D> aPrioriTargetItem = null;
if (aPrioriTargetObject != null) {
aPrioriTargetItem = aPrioriTargetObject.findItem(mappingOutputPath);
}
// WARNING
// Following code seems to be wrong. It is not very relativisic. It seems to always
// go for replace.
// It seems that it is only used for activation mappings (outbout and inbound). As
// these are quite special single-value properties then it seems to work fine
// (with the exception of MID-3418). Todo: make it more relativistic: MID-3419
if (targetContext.isAdd()) {
Collection<V> nonNegativeValues = outputTriple.getNonNegativeValues();
if (nonNegativeValues == null || nonNegativeValues.isEmpty()) {
LOGGER.trace("{} resulted in null or empty value for {}, skipping", mappingDesc, targetContext);
continue;
}
targetItemDelta.setValuesToReplace(PrismValue.cloneCollection(nonNegativeValues));
} else {
// if we have fresh information (full shadow) AND the mapping used to derive the information was strong,
// we will consider all values (zero & plus sets) -- otherwise, we take only the "plus" (i.e. changed) set
// the first case is necessary, because in some situations (e.g. when mapping is changed)
// the evaluator sees no differences w.r.t. real state, even if there is a difference
// - and we must have a way to push new information onto the resource
Collection<V> valuesToReplace;
if (hasFullTargetObject && mappingOutputStruct.isStrongMappingWasUsed()) {
valuesToReplace = outputTriple.getNonNegativeValues();
} else {
valuesToReplace = outputTriple.getPlusSet();
}
if (LOGGER.isTraceEnabled()) {
LOGGER.trace("{}: hasFullTargetObject={}, isStrongMappingWasUsed={}, valuesToReplace={}",
new Object[]{mappingDesc, hasFullTargetObject,
mappingOutputStruct.isStrongMappingWasUsed(), valuesToReplace});
}
if (valuesToReplace != null && !valuesToReplace.isEmpty()) {
// if what we want to set is the same as is already in the shadow, we skip that
// (we insist on having full shadow, to be sure we work with current data)
if (hasFullTargetObject && targetContext.isFresh() && aPrioriTargetItem != null) {
Collection<V> valuesPresent = aPrioriTargetItem.getValues();
if (PrismValue.equalsRealValues(valuesPresent, valuesToReplace)) {
LOGGER.trace("{} resulted in existing values for {}, skipping creation of a delta", mappingDesc, targetContext);
continue;
}
}
targetItemDelta.setValuesToReplace(PrismValue.cloneCollection(valuesToReplace));
} else if (outputTriple.hasMinusSet()) {
LOGGER.trace("{} resulted in null or empty value for {} and there is a minus set, resetting it (replace with empty)", mappingDesc, targetContext);
targetItemDelta.setValueToReplace();
} else {
LOGGER.trace("{} resulted in null or empty value for {}, skipping", mappingDesc, targetContext);
}
}
if (targetItemDelta.isEmpty()) {
continue;
}
LOGGER.trace("{} adding new delta for {}: {}", mappingDesc, targetContext, targetItemDelta);
targetContext.swallowToSecondaryDelta(targetItemDelta);
}
}
// Figure out recompute time
for (Mapping<V,D> mapping: mappings) {
XMLGregorianCalendar mappingNextRecomputeTime = mapping.getNextRecomputeTime();
if (mappingNextRecomputeTime != null) {
if (nextRecomputeTime == null || nextRecomputeTime.compare(mappingNextRecomputeTime) == DatatypeConstants.GREATER) {
nextRecomputeTime = mappingNextRecomputeTime;
}
}
}
if (nextRecomputeTime != null) {
boolean alreadyHasTrigger = false;
if (params.getAPrioriTargetObject() != null) {
for (TriggerType trigger: params.getAPrioriTargetObject().asObjectable().getTrigger()) {
if (RecomputeTriggerHandler.HANDLER_URI.equals(trigger.getHandlerUri()) &&
nextRecomputeTime.equals(trigger.getTimestamp())) {
alreadyHasTrigger = true;
break;
}
}
}
if (!alreadyHasTrigger) {
PrismContainerDefinition<TriggerType> triggerContDef = targetObjectDefinition.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);
targetContext.swallowToSecondaryDelta(triggerDelta);
}
}
}
private boolean hasNoValue(Item aPrioriTargetItem) {
return aPrioriTargetItem == null
|| (aPrioriTargetItem.isEmpty() && !aPrioriTargetItem.isIncomplete());
}
public <V extends PrismValue, D extends ItemDefinition , F extends FocusType> Mapping<V, D> createFocusMapping(final MappingFactory mappingFactory,
final LensContext<F> context, final MappingType mappingType, ObjectType originObject,
ObjectDeltaObject<F> focusOdo, AssignmentPathVariables assignmentPathVariables, PrismObject<SystemConfigurationType> configuration,
XMLGregorianCalendar now, String contextDesc, Task task, OperationResult result) throws SchemaException, ExpressionEvaluationException, ObjectNotFoundException {
Integer iteration = null;
String iterationToken = null;
if (focusOdo.getNewObject() != null) {
F focusNewType = focusOdo.getNewObject().asObjectable();
iteration = focusNewType.getIteration();
iterationToken = focusNewType.getIterationToken();
} else if (focusOdo.getOldObject() != null) {
F focusOldType = focusOdo.getOldObject().asObjectable();
iteration = focusOldType.getIteration();
iterationToken = focusOldType.getIterationToken();
}
return createFocusMapping(mappingFactory, context, mappingType, originObject, focusOdo, focusOdo.getAnyObject(), assignmentPathVariables,
iteration, iterationToken, configuration, now, contextDesc, task, result);
}
public <V extends PrismValue, D extends ItemDefinition, F extends FocusType, T extends FocusType> Mapping<V, D> createFocusMapping(
final MappingFactory mappingFactory, final LensContext<F> context, final MappingType mappingType, ObjectType originObject,
ObjectDeltaObject<F> focusOdo, PrismObject<T> defaultTargetObject, AssignmentPathVariables assignmentPathVariables,
Integer iteration, String iterationToken, PrismObject<SystemConfigurationType> configuration,
XMLGregorianCalendar now, String contextDesc, final Task task, OperationResult result) throws SchemaException, ExpressionEvaluationException, ObjectNotFoundException {
if (!Mapping.isApplicableToChannel(mappingType, context.getChannel())) {
LOGGER.trace("Mapping {} not applicable to channel {}, skipping.", mappingType, context.getChannel());
return null;
}
StringPolicyResolver stringPolicyResolver = new StringPolicyResolver() {
private ItemPath outputPath;
private ItemDefinition outputDefinition;
@Override
public void setOutputPath(ItemPath outputPath) {
this.outputPath = outputPath;
}
@Override
public void setOutputDefinition(ItemDefinition outputDefinition) {
this.outputDefinition = outputDefinition;
}
@Override
public StringPolicyType resolve() {
// TODO need to switch to ObjectValuePolicyEvaluator
if (outputDefinition.getName().equals(PasswordType.F_VALUE)) {
ValuePolicyType passwordPolicy = credentialsProcessor.determinePasswordPolicy(context.getFocusContext(), task, result);
if (passwordPolicy == null) {
return null;
}
return passwordPolicy.getStringPolicy();
}
if (mappingType.getExpression() != null){
List<JAXBElement<?>> evaluators = mappingType.getExpression().getExpressionEvaluator();
if (evaluators != null) {
for (JAXBElement jaxbEvaluator : evaluators) {
Object object = jaxbEvaluator.getValue();
if (object instanceof GenerateExpressionEvaluatorType && ((GenerateExpressionEvaluatorType) object).getValuePolicyRef() != null) {
ObjectReferenceType ref = ((GenerateExpressionEvaluatorType) object).getValuePolicyRef();
try {
ValuePolicyType valuePolicyType = mappingFactory.getObjectResolver().resolve(ref, ValuePolicyType.class,
null, "resolving value policy for generate attribute "+ outputDefinition.getName()+" value", task, new OperationResult("Resolving value policy"));
if (valuePolicyType != null) {
return valuePolicyType.getStringPolicy();
}
} catch (CommonException ex) {
throw new SystemException(ex.getMessage(), ex);
}
}
}
}
}
return null;
}
};
ExpressionVariables variables = new ExpressionVariables();
FOCUS_VARIABLE_NAMES.forEach(name -> variables.addVariableDefinition(name, focusOdo));
variables.addVariableDefinition(ExpressionConstants.VAR_ITERATION, iteration);
variables.addVariableDefinition(ExpressionConstants.VAR_ITERATION_TOKEN, iterationToken);
variables.addVariableDefinition(ExpressionConstants.VAR_CONFIGURATION, configuration);
Collection<V> targetValues = computeTargetValues(mappingType.getTarget(), defaultTargetObject, variables, mappingFactory.getObjectResolver(), contextDesc, task, result);
Mapping.Builder<V,D> mappingBuilder = mappingFactory.<V,D>createMappingBuilder(mappingType, contextDesc)
.sourceContext(focusOdo)
.targetContext(defaultTargetObject.getDefinition())
.variables(variables)
.originalTargetValues(targetValues)
.originType(OriginType.USER_POLICY)
.originObject(originObject)
.stringPolicyResolver(stringPolicyResolver)
.rootNode(focusOdo)
.now(now);
mappingBuilder = LensUtil.addAssignmentPathVariables(mappingBuilder, assignmentPathVariables);
Mapping<V,D> mapping = mappingBuilder.build();
ItemPath itemPath = mapping.getOutputPath();
if (itemPath == null) {
// no output element, i.e. this is a "validation mapping"
return mapping;
}
if (defaultTargetObject != null) {
Item<V,D> existingTargetItem = (Item<V,D>) defaultTargetObject.findItem(itemPath);
if (existingTargetItem != null && !existingTargetItem.isEmpty()
&& mapping.getStrength() == MappingStrengthType.WEAK) {
LOGGER.trace("Mapping {} is weak and target already has a value {}, skipping.", mapping, existingTargetItem);
return null;
}
}
return mapping;
}
private <V extends PrismValue, F extends FocusType> Collection<V> computeTargetValues(VariableBindingDefinitionType target,
Object defaultTargetContext, ExpressionVariables variables, ObjectResolver objectResolver, String contextDesc,
Task task, OperationResult result) throws SchemaException, ObjectNotFoundException {
if (target == null) {
// Is this correct? What about default targets?
return null;
}
ItemPathType itemPathType = target.getPath();
if (itemPathType == null) {
// Is this correct? What about default targets?
return null;
}
ItemPath path = itemPathType.getItemPath();
Object object = ExpressionUtil.resolvePath(path, variables, defaultTargetContext, objectResolver, contextDesc, task, result);
if (object == null) {
return new ArrayList<>();
} else if (object instanceof Item) {
return ((Item) object).getValues();
} else if (object instanceof PrismValue) {
return (List<V>) Collections.singletonList((PrismValue) object);
} else if (object instanceof ItemDeltaItem) {
ItemDeltaItem<V, ?> idi = (ItemDeltaItem<V, ?>) object;
PrismValueDeltaSetTriple<V> triple = idi.toDeltaSetTriple();
return triple != null ? triple.getNonNegativeValues() : new ArrayList<V>();
} else {
throw new IllegalStateException("Unsupported target value(s): " + object.getClass() + " (" + object + ")");
}
}
}