use of com.evolveum.midpoint.model.common.mapping.MappingImpl in project midpoint by Evolveum.
the class ProjectionMappingSetEvaluator method evaluateMappingsToTriples.
public <V extends PrismValue, D extends ItemDefinition<?>, T extends ObjectType, F extends FocusType> Map<UniformItemPath, MappingOutputStruct<V>> evaluateMappingsToTriples(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<UniformItemPath, MappingOutputStruct<V>> outputTripleMap = new HashMap<>();
XMLGregorianCalendar nextRecomputeTime = null;
String triggerOriginDescription = null;
Collection<MappingType> mappingBeans = params.getMappingTypes();
Collection<MappingImpl<V, D>> mappings = new ArrayList<>(mappingBeans.size());
for (MappingType mappingBean : mappingBeans) {
MappingBuilder<V, D> mappingBuilder = mappingFactory.createMappingBuilder(mappingBean, mappingDesc);
String mappingName = mappingBean.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) {
mappingBuilder.defaultTargetDefinition(targetObjectDefinition.findItemDefinition(defaultTargetItemPath));
} else {
mappingBuilder.defaultTargetDefinition(params.getTargetItemDefinition());
}
mappingBuilder.defaultTargetPath(defaultTargetItemPath);
mappingBuilder.targetContext(targetObjectDefinition);
mappingBuilder.sourceContext(params.getSourceContext());
// Initialize mapping (using Inversion of Control)
MappingBuilder<V, D> initializedMappingBuilder = params.getInitializer().initialize(mappingBuilder);
MappingImpl<V, D> mapping = initializedMappingBuilder.build();
mapping.evaluateTimeValidity(task, result);
boolean timeConstraintValid = mapping.isTimeConstraintValid();
if (params.getEvaluateCurrent() == MappingTimeEval.CURRENT && !timeConstraintValid) {
LOGGER.trace("Mapping {} is non-current, but evaluating current mappings, skipping {}", mappingName, params.getContext().getChannel());
} else if (params.getEvaluateCurrent() == MappingTimeEval.FUTURE && timeConstraintValid) {
LOGGER.trace("Mapping {} is current, but evaluating non-current mappings, skipping {}", mappingName, params.getContext().getChannel());
} else {
mappings.add(mapping);
}
}
boolean hasFullTargetObject = params.hasFullTargetObject();
PrismObject<T> aPrioriTargetObject = params.getAPrioriTargetObject();
LOGGER.trace("Going to process {} mappings for {}", mappings.size(), mappingDesc);
for (MappingImpl<V, D> mapping : mappings) {
if (mapping.getStrength() == MappingStrengthType.WEAK) {
// Evaluate weak mappings in a second run.
continue;
}
UniformItemPath mappingOutputPathUniform = prismContext.toUniformPathKeepNull(mapping.getOutputPath());
if (params.isFixTarget() && mappingOutputPathUniform != null && defaultTargetItemPath != null && !mappingOutputPathUniform.equivalent(defaultTargetItemPath)) {
throw new ExpressionEvaluationException("Target cannot be overridden in " + mappingDesc);
}
if (params.getAPrioriTargetDelta() != null && mappingOutputPathUniform != null) {
ItemDelta<?, ?> aPrioriItemDelta = params.getAPrioriTargetDelta().findItemDelta(mappingOutputPathUniform);
if (mapping.getStrength() != MappingStrengthType.STRONG) {
if (aPrioriItemDelta != null && !aPrioriItemDelta.isEmpty()) {
continue;
}
}
}
mappingEvaluator.evaluateMapping(mapping, params.getContext(), task, result);
PrismValueDeltaSetTriple<V> mappingOutputTriple = mapping.getOutputTriple();
LOGGER.trace("Output triple of mapping {}\n{}", mapping.getContextDescription(), mappingOutputTriple == null ? null : mappingOutputTriple.debugDumpLazily(1));
if (isMeaningful(mappingOutputTriple)) {
MappingOutputStruct<V> mappingOutputStruct = outputTripleMap.get(mappingOutputPathUniform);
if (mappingOutputStruct == null) {
mappingOutputStruct = new MappingOutputStruct<>();
outputTripleMap.put(mappingOutputPathUniform, mappingOutputStruct);
}
if (mapping.getStrength() == MappingStrengthType.STRONG) {
mappingOutputStruct.setStrongMappingWasUsed(true);
if (!hasFullTargetObject && params.getTargetLoader() != null && aPrioriTargetObject != null && aPrioriTargetObject.getOid() != null) {
if (!params.getTargetLoader().isLoaded()) {
aPrioriTargetObject = params.getTargetLoader().load("strong mapping", task, result);
LOGGER.trace("Loaded object because of strong mapping: {}", aPrioriTargetObject);
hasFullTargetObject = true;
}
}
}
// experimental
if (mapping.isPushChanges()) {
mappingOutputStruct.setPushChanges(true);
// (but it looks like we have to!)
if (!hasFullTargetObject && params.getTargetLoader() != null && aPrioriTargetObject != null && aPrioriTargetObject.getOid() != null) {
if (!params.getTargetLoader().isLoaded()) {
aPrioriTargetObject = params.getTargetLoader().load("pushing changes", task, result);
LOGGER.trace("Loaded object because of pushing changes: {}", aPrioriTargetObject);
hasFullTargetObject = true;
}
}
}
PrismValueDeltaSetTriple<V> outputTriple = mappingOutputStruct.getOutputTriple();
if (outputTriple == null) {
mappingOutputStruct.setOutputTriple(mappingOutputTriple);
} else {
outputTriple.merge(mappingOutputTriple);
}
} else {
LOGGER.trace("Output triple of mapping {} is NOT meaningful", mapping.getContextDescription());
}
}
if (params.isEvaluateWeak()) {
// Second pass, evaluate only weak mappings
for (MappingImpl<V, D> mapping : mappings) {
if (mapping.getStrength() != MappingStrengthType.WEAK) {
continue;
}
UniformItemPath mappingOutputPath = prismContext.toUniformPathKeepNull(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 && !outputTriple.getNonNegativeValues().isEmpty()) {
// MID-3847
continue;
}
Item<V, D> aPrioriTargetItem = null;
if (aPrioriTargetObject != null && mappingOutputPath != null) {
aPrioriTargetItem = aPrioriTargetObject.findItem(mappingOutputPath);
}
if (hasNoValue(aPrioriTargetItem)) {
mappingOutputStruct.setWeakMappingWasUsed(true);
mappingEvaluator.evaluateMapping(mapping, params.getContext(), task, result);
PrismValueDeltaSetTriple<V> mappingOutputTriple = mapping.getOutputTriple();
if (mappingOutputTriple != null) {
// This is all not right. See MID-3847
if (!hasFullTargetObject && params.getTargetLoader() != null && aPrioriTargetObject != null && aPrioriTargetObject.getOid() != null) {
if (!params.getTargetLoader().isLoaded()) {
aPrioriTargetObject = params.getTargetLoader().load("weak mapping", 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) {
// this is currently always true (see above)
mappingOutputStruct.setOutputTriple(mappingOutputTriple);
} else {
outputTriple.merge(mappingOutputTriple);
}
}
}
}
}
MappingOutputProcessor<V> processor = params.getProcessor();
for (Map.Entry<UniformItemPath, MappingOutputStruct<V>> outputTripleMapEntry : outputTripleMap.entrySet()) {
UniformItemPath mappingOutputPath = outputTripleMapEntry.getKey();
MappingOutputStruct<V> mappingOutputStruct = outputTripleMapEntry.getValue();
PrismValueDeltaSetTriple<V> outputTriple = mappingOutputStruct.getOutputTriple();
boolean defaultProcessing;
if (processor != null) {
LOGGER.trace("Executing processor to process mapping evaluation results: {}", processor);
defaultProcessing = processor.process(mappingOutputPath, mappingOutputStruct);
} else {
defaultProcessing = true;
}
if (defaultProcessing) {
if (outputTriple == null) {
LOGGER.trace("{} expression resulted in null triple for {}, skipping", mappingDesc, targetContext);
continue;
}
ItemDefinition targetItemDefinition;
if (mappingOutputPath != null) {
targetItemDefinition = targetObjectDefinition.findItemDefinition(mappingOutputPath);
if (targetItemDefinition == null) {
throw new SchemaException("No definition for item " + mappingOutputPath + " in " + targetObjectDefinition);
}
} else {
targetItemDefinition = params.getTargetItemDefinition();
}
// noinspection unchecked
ItemDelta<V, D> targetItemDelta = targetItemDefinition.createEmptyDelta(mappingOutputPath);
Item<V, D> aPrioriTargetItem;
if (aPrioriTargetObject != null) {
aPrioriTargetItem = aPrioriTargetObject.findItem(mappingOutputPath);
} else {
aPrioriTargetItem = null;
}
if (targetContext.isAdd()) {
Collection<V> nonNegativeValues = outputTriple.getNonNegativeValues();
if (nonNegativeValues.isEmpty()) {
LOGGER.trace("{} resulted in null or empty value for {}, skipping", mappingDesc, targetContext);
continue;
}
targetItemDelta.setValuesToReplace(PrismValueCollectionsUtil.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() || mappingOutputStruct.isPushChanges())) {
valuesToReplace = outputTriple.getNonNegativeValues();
} else {
valuesToReplace = outputTriple.getPlusSet();
}
LOGGER.trace("{}: hasFullTargetObject={}, isStrongMappingWasUsed={}, pushingChange={}, valuesToReplace={}", mappingDesc, hasFullTargetObject, mappingOutputStruct.isStrongMappingWasUsed(), mappingOutputStruct.isPushChanges(), valuesToReplace);
if (!valuesToReplace.isEmpty()) {
if (hasFullTargetObject && targetContext.isFresh() && aPrioriTargetItem != null) {
Collection<V> valuesPresent = aPrioriTargetItem.getValues();
if (PrismValueCollectionsUtil.equalsRealValues(valuesPresent, valuesToReplace)) {
LOGGER.trace("{} resulted in existing values for {}, skipping creation of a delta", mappingDesc, targetContext);
continue;
}
}
targetItemDelta.setValuesToReplace(PrismValueCollectionsUtil.cloneCollection(valuesToReplace));
applyEstematedOldValueInReplaceCase(targetItemDelta, outputTriple);
} 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();
applyEstematedOldValueInReplaceCase(targetItemDelta, outputTriple);
} 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);
}
}
for (MappingImpl<V, D> mapping : mappings) {
XMLGregorianCalendar mappingNextRecomputeTime = mapping.getNextRecomputeTime();
if (mappingNextRecomputeTime != null) {
if (mapping.isConditionSatisfied() && (nextRecomputeTime == null || nextRecomputeTime.compare(mappingNextRecomputeTime) == DatatypeConstants.GREATER)) {
nextRecomputeTime = mappingNextRecomputeTime;
// TODO: maybe better description? But consider storage requirements. We do not want to store too much.
triggerOriginDescription = mapping.getIdentifier();
}
}
}
if (nextRecomputeTime != null) {
NextRecompute nextRecompute = new NextRecompute(nextRecomputeTime, triggerOriginDescription);
nextRecompute.createTrigger(params.getAPrioriTargetObject(), targetObjectDefinition, targetContext);
}
return outputTripleMap;
}
use of com.evolveum.midpoint.model.common.mapping.MappingImpl in project midpoint by Evolveum.
the class AbstractAssignmentEvaluatorTest method test120DirectExpressionReplaceDescription.
@Test
public void test120DirectExpressionReplaceDescription() throws Exception {
// GIVEN
Task task = getTestTask();
OperationResult result = task.getResult();
PrismObject<UserType> user = userTypeJack.asPrismObject().clone();
AssignmentType assignmentType = unmarshalValueFromFile(ASSIGNMENT_DIRECT_EXPRESSION_FILE);
user.asObjectable().getAssignment().add(assignmentType.clone());
ItemPath path = ItemPath.create(UserType.F_ASSIGNMENT, 123L, AssignmentType.F_DESCRIPTION);
ObjectDelta<UserType> userDelta = prismContext.deltaFactory().object().createModificationReplaceProperty(UserType.class, USER_JACK_OID, path, "captain");
ObjectDeltaObject<UserType> userOdo = createUserOdo(user, userDelta);
AssignmentEvaluator<UserType> assignmentEvaluator = createAssignmentEvaluator(userOdo);
display("Assignment old", assignmentType);
ItemDeltaItem<PrismContainerValue<AssignmentType>, PrismContainerDefinition<AssignmentType>> assignmentIdi = createAssignmentIdi(assignmentType);
assignmentIdi.setResolvePath(UserType.F_ASSIGNMENT);
assignmentIdi.setSubItemDeltas(userDelta.getModifications());
assignmentIdi.recompute();
displayDumpable("Assignment IDI", assignmentIdi);
// WHEN
when();
EvaluatedAssignmentImpl<UserType> evaluatedAssignment = assignmentEvaluator.evaluate(assignmentIdi, PlusMinusZero.ZERO, false, userTypeJack, "testDirect", AssignmentOrigin.createInObject(), task, result);
evaluateConstructions(evaluatedAssignment, userOdo, task, result);
// THEN
then();
assertSuccess(result);
assertNotNull(evaluatedAssignment);
displayDumpable("Evaluated assignment", evaluatedAssignment);
assertEquals(1, evaluatedAssignment.getConstructionTriple().size());
PrismAsserts.assertParentConsistency(user);
ResourceObjectConstruction<UserType, EvaluatedAssignedResourceObjectConstructionImpl<UserType>> construction = evaluatedAssignment.getConstructionTriple().getZeroSet().iterator().next();
assertNotNull("No object class definition in construction", construction.getResourceObjectDefinition());
DeltaSetTriple<EvaluatedAssignedResourceObjectConstructionImpl<UserType>> evaluatedConstructionTriple = construction.getEvaluatedConstructionTriple();
assertEquals(1, evaluatedConstructionTriple.size());
EvaluatedAssignedResourceObjectConstructionImpl<UserType> evaluatedConstruction = evaluatedConstructionTriple.getZeroSet().iterator().next();
assertEquals(1, evaluatedConstruction.getAttributeMappings().size());
MappingImpl<PrismPropertyValue<String>, PrismPropertyDefinition<String>> attributeMapping = (MappingImpl<PrismPropertyValue<String>, PrismPropertyDefinition<String>>) evaluatedConstruction.getAttributeMappings().iterator().next();
PrismValueDeltaSetTriple<PrismPropertyValue<String>> outputTriple = attributeMapping.getOutputTriple();
displayDumpable("output triple", outputTriple);
PrismAsserts.assertTripleNoZero(outputTriple);
PrismAsserts.assertTriplePlus(outputTriple, "The best captain the world has ever seen");
PrismAsserts.assertTripleMinus(outputTriple, "The best pirate the world has ever seen");
// the same using other words
assertConstruction(evaluatedAssignment, ZERO, "title", ZERO);
assertConstruction(evaluatedAssignment, ZERO, "title", PLUS, "The best captain the world has ever seen");
assertConstruction(evaluatedAssignment, ZERO, "title", MINUS, "The best pirate the world has ever seen");
assertNoConstruction(evaluatedAssignment, PLUS, "title");
assertNoConstruction(evaluatedAssignment, MINUS, "title");
assertEquals("Wrong number of admin GUI configs", 0, evaluatedAssignment.getAdminGuiConfigurations().size());
}
use of com.evolveum.midpoint.model.common.mapping.MappingImpl in project midpoint by Evolveum.
the class MappedItem method createMappings.
/**
* Creates the respective mapping(s).
*/
void createMappings(@NotNull PathKeyedMap<List<InboundMappingInContext<?, ?>>> mappingsMap) throws SchemaException, ExpressionEvaluationException, CommunicationException, SecurityViolationException, ConfigurationException, ObjectNotFoundException {
boolean fromAbsoluteState = processingMode == ProcessingMode.ABSOLUTE_STATE || processingMode == ProcessingMode.ABSOLUTE_STATE_IF_KNOWN;
if (fromAbsoluteState && !source.isAbsoluteStateAvailable()) {
LOGGER.trace("Skipping inbound mapping(s) for {} as they should be processed from absolute state, but we don't" + " have one", itemDescription);
return;
}
Item<V, D> currentProjectionItem = itemProvider.provide();
if (postProcessor != null) {
postProcessor.postProcess(itemAPrioriDelta, currentProjectionItem);
}
LOGGER.trace("Creating {} inbound mapping(s) for {} in {} ({}). Relevant values are:\n" + "- a priori item delta:\n{}\n" + "- current item:\n{}", mappingBeans.size(), itemDescription, source.getProjectionHumanReadableNameLazy(), fromAbsoluteState ? "absolute mode" : "relative mode", DebugUtil.debugDumpLazily(itemAPrioriDelta, 1), DebugUtil.debugDumpLazily(currentProjectionItem, 1));
if (currentProjectionItem != null && currentProjectionItem.hasRaw()) {
throw new SystemException("Property " + currentProjectionItem + " has raw parsing state," + " such property cannot be used in inbound expressions");
}
source.setValueMetadata(currentProjectionItem, itemAPrioriDelta);
ResourceType resource = source.getResource();
// Value for the $shadow ($projection, $account) variable.
// TODO Why do we use "object new" here? (We should perhaps go with ODO, shouldn't we?)
// Bear in mind that the value might not contain the full shadow (for example)
PrismObject<ShadowType> shadowVariableValue = source.getResourceObjectNew();
PrismObjectDefinition<ShadowType> shadowVariableDef = getShadowDefinition(shadowVariableValue);
Source<V, D> defaultSource = new Source<>(currentProjectionItem, itemAPrioriDelta, null, ExpressionConstants.VAR_INPUT_QNAME, itemDefinition);
defaultSource.recompute();
for (MappingType mappingBean : mappingBeans) {
String channel = source.getChannel();
if (!MappingImpl.isApplicableToChannel(mappingBean, channel)) {
LOGGER.trace("Mapping is not applicable to channel {}", channel);
continue;
}
MappingBuilder<V, D> builder = beans.mappingFactory.<V, D>createMappingBuilder().mappingBean(mappingBean).mappingKind(MappingKindType.INBOUND).implicitSourcePath(implicitSourcePath).contextDescription("inbound expression for " + itemDescription + " in " + resource).defaultSource(defaultSource).targetContext(target.focusDefinition).addVariableDefinition(ExpressionConstants.VAR_USER, target.focus, target.focusDefinition).addVariableDefinition(ExpressionConstants.VAR_FOCUS, target.focus, target.focusDefinition).addAliasRegistration(ExpressionConstants.VAR_USER, ExpressionConstants.VAR_FOCUS).addVariableDefinition(ExpressionConstants.VAR_ACCOUNT, shadowVariableValue, shadowVariableDef).addVariableDefinition(ExpressionConstants.VAR_SHADOW, shadowVariableValue, shadowVariableDef).addVariableDefinition(ExpressionConstants.VAR_PROJECTION, shadowVariableValue, shadowVariableDef).addAliasRegistration(ExpressionConstants.VAR_ACCOUNT, ExpressionConstants.VAR_PROJECTION).addAliasRegistration(ExpressionConstants.VAR_SHADOW, ExpressionConstants.VAR_PROJECTION).addVariableDefinition(ExpressionConstants.VAR_RESOURCE, resource, resource.asPrismObject().getDefinition()).addVariableDefinition(ExpressionConstants.VAR_CONFIGURATION, context.getSystemConfiguration(), getSystemConfigurationDefinition()).addVariableDefinition(ExpressionConstants.VAR_OPERATION, context.getOperation(), String.class).variableResolver(variableProducer).valuePolicySupplier(context.createValuePolicySupplier()).originType(OriginType.INBOUND).originObject(resource).now(context.env.now);
if (!target.isFocusBeingDeleted()) {
assert target.focus != null;
TypedValue<PrismObject<F>> targetContext = new TypedValue<>(target.focus);
builder.originalTargetValues(ExpressionUtil.computeTargetValues(mappingBean.getTarget(), targetContext, builder.getVariables(), beans.mappingFactory.getObjectResolver(), "resolving target values", beans.prismContext, context.env.task, context.result));
}
MappingImpl<V, D> mapping = builder.build();
if (checkWeakSkip(mapping)) {
LOGGER.trace("Skipping because of mapping is weak and focus property has already a value");
continue;
}
InboundMappingInContext<V, D> mappingStruct = source.createInboundMappingInContext(mapping);
ItemPath targetFocusItemPath = mapping.getOutputPath();
if (ItemPath.isEmpty(targetFocusItemPath)) {
throw new ConfigurationException("Empty target path in " + mapping.getContextDescription());
}
checkTargetItemDefinitionKnown(targetFocusItemPath);
mappingsMap.computeIfAbsent(targetFocusItemPath, k -> new ArrayList<>()).add(mappingStruct);
}
}
Aggregations