use of com.evolveum.midpoint.model.common.mapping.Mapping in project midpoint by Evolveum.
the class Construction method evaluateAttribute.
private <T> Mapping<PrismPropertyValue<T>, ResourceAttributeDefinition<T>> evaluateAttribute(ResourceAttributeDefinitionType attribudeDefinitionType, Task task, OperationResult result) throws ExpressionEvaluationException, ObjectNotFoundException, SchemaException {
QName attrName = ItemPathUtil.getOnlySegmentQName(attribudeDefinitionType.getRef());
if (attrName == null) {
throw new SchemaException("Missing 'ref' in attribute construction in account construction in " + getSource());
}
if (!attribudeDefinitionType.getInbound().isEmpty()) {
throw new SchemaException("Cannot process inbound section in definition of attribute " + attrName + " in account construction in " + getSource());
}
MappingType outboundMappingType = attribudeDefinitionType.getOutbound();
if (outboundMappingType == null) {
throw new SchemaException("No outbound section in definition of attribute " + attrName + " in account construction in " + getSource());
}
ResourceAttributeDefinition<T> outputDefinition = findAttributeDefinition(attrName);
if (outputDefinition == null) {
throw new SchemaException("Attribute " + attrName + " not found in schema for account type " + getIntent() + ", " + getResource(task, result) + " as definied in " + getSource(), attrName);
}
Mapping.Builder<PrismPropertyValue<T>, ResourceAttributeDefinition<T>> builder = mappingFactory.createMappingBuilder(outboundMappingType, "for attribute " + PrettyPrinter.prettyPrint(attrName) + " in " + getSource());
Mapping<PrismPropertyValue<T>, ResourceAttributeDefinition<T>> evaluatedMapping = evaluateMapping(builder, attrName, outputDefinition, null, task, result);
LOGGER.trace("Evaluated mapping for attribute " + attrName + ": " + evaluatedMapping);
return evaluatedMapping;
}
use of com.evolveum.midpoint.model.common.mapping.Mapping in project midpoint by Evolveum.
the class LensUtil method consolidateTripleToDelta.
/**
* Consolidate the mappings of a single item to a delta. It takes the convenient structure of ItemValueWithOrigin triple.
* It produces the delta considering the mapping exclusion, authoritativeness and strength.
*
* filterExistingValues: if true, then values that already exist in the item are not added (and those that don't exist are not removed)
*/
@NotNull
public static <V extends PrismValue, D extends ItemDefinition, I extends ItemValueWithOrigin<V, D>> ItemDelta<V, D> consolidateTripleToDelta(ItemPath itemPath, DeltaSetTriple<I> triple, D itemDefinition, ItemDelta<V, D> aprioriItemDelta, PrismContainer<?> itemContainer, ValueMatcher<?> valueMatcher, Comparator<V> comparator, boolean addUnchangedValues, boolean filterExistingValues, boolean isExclusiveStrong, String contextDescription, boolean applyWeak) throws ExpressionEvaluationException, PolicyViolationException, SchemaException {
ItemDelta<V, D> itemDelta = itemDefinition.createEmptyDelta(itemPath);
Item<V, D> itemExisting = null;
if (itemContainer != null) {
itemExisting = itemContainer.findItem(itemPath);
}
if (LOGGER.isTraceEnabled()) {
LOGGER.trace("Consolidating {} triple:\n{}\nApriori Delta:\n{}\nExisting item:\n{}", itemPath, triple.debugDump(1), DebugUtil.debugDump(aprioriItemDelta, 1), DebugUtil.debugDump(itemExisting, 1));
}
Collection<V> allValues = collectAllValues(triple, valueMatcher);
final MutableBoolean itemHasStrongMutable = new MutableBoolean(false);
SimpleVisitor<I> visitor = pvwo -> {
if (pvwo.getMapping().getStrength() == MappingStrengthType.STRONG) {
itemHasStrongMutable.setValue(true);
}
};
triple.accept(visitor);
boolean ignoreNormalMappings = itemHasStrongMutable.booleanValue() && isExclusiveStrong;
// a single item (e.g. attribute). But this loop iterates over every potential value of that item.
for (V value : allValues) {
LOGGER.trace(" consolidating value: {}", value);
// Check what to do with the value using the usual "triple routine". It means that if a value is
// in zero set than we need no delta, plus set means add delta and minus set means delete delta.
// The first set that the value is present determines the result.
Collection<ItemValueWithOrigin<V, D>> zeroPvwos = collectPvwosFromSet(value, triple.getZeroSet(), valueMatcher);
Collection<ItemValueWithOrigin<V, D>> plusPvwos = collectPvwosFromSet(value, triple.getPlusSet(), valueMatcher);
Collection<ItemValueWithOrigin<V, D>> minusPvwos = collectPvwosFromSet(value, triple.getMinusSet(), valueMatcher);
if (LOGGER.isTraceEnabled()) {
LOGGER.trace("PVWOs for value {}:\nzero = {}\nplus = {}\nminus = {}", value, zeroPvwos, plusPvwos, minusPvwos);
}
boolean zeroHasStrong = false;
if (!zeroPvwos.isEmpty()) {
for (ItemValueWithOrigin<V, D> pvwo : zeroPvwos) {
PrismValueDeltaSetTripleProducer<V, D> mapping = pvwo.getMapping();
if (mapping.getStrength() == MappingStrengthType.STRONG) {
zeroHasStrong = true;
}
}
}
if (zeroHasStrong && aprioriItemDelta != null && aprioriItemDelta.isValueToDelete(value, true)) {
throw new PolicyViolationException("Attempt to delete value " + value + " from item " + itemPath + " but that value is mandated by a strong mapping (in " + contextDescription + ")");
}
if (!zeroPvwos.isEmpty() && !addUnchangedValues) {
// Value unchanged, nothing to do
LOGGER.trace("Value {} unchanged, doing nothing", value);
continue;
}
PrismValueDeltaSetTripleProducer<V, D> exclusiveMapping = null;
Collection<ItemValueWithOrigin<V, D>> pvwosToAdd = null;
if (addUnchangedValues) {
pvwosToAdd = MiscUtil.union(zeroPvwos, plusPvwos);
} else {
pvwosToAdd = plusPvwos;
}
if (!pvwosToAdd.isEmpty()) {
boolean weakOnly = true;
boolean hasStrong = false;
// exclusions and strength
for (ItemValueWithOrigin<V, D> pvwoToAdd : pvwosToAdd) {
PrismValueDeltaSetTripleProducer<V, D> mapping = pvwoToAdd.getMapping();
if (mapping.getStrength() != MappingStrengthType.WEAK) {
weakOnly = false;
}
if (mapping.getStrength() == MappingStrengthType.STRONG) {
hasStrong = true;
}
if (mapping.isExclusive()) {
if (exclusiveMapping == null) {
exclusiveMapping = mapping;
} else {
String message = "Exclusion conflict in " + contextDescription + ", item " + itemPath + ", conflicting constructions: " + exclusiveMapping + " and " + mapping;
LOGGER.error(message);
throw new ExpressionEvaluationException(message);
}
}
}
if (weakOnly) {
// Postpone processing of weak values until we process all other values
LOGGER.trace("Value {} mapping is weak in item {}, postponing processing in {}", value, itemPath, contextDescription);
continue;
}
if (!hasStrong && ignoreNormalMappings) {
LOGGER.trace("Value {} mapping is normal in item {} and we have exclusiveStrong, skipping processing in {}", value, itemPath, contextDescription);
continue;
}
if (hasStrong && aprioriItemDelta != null && aprioriItemDelta.isValueToDelete(value, true)) {
throw new PolicyViolationException("Attempt to delete value " + value + " from item " + itemPath + " but that value is mandated by a strong mapping (in " + contextDescription + ")");
}
if (!hasStrong && (aprioriItemDelta != null && !aprioriItemDelta.isEmpty())) {
// There is already a delta, skip this
LOGGER.trace("Value {} mapping is not strong and the item {} already has a delta that is more concrete, " + "skipping adding in {}", value, itemPath, contextDescription);
continue;
}
if (filterExistingValues && hasValue(itemExisting, value, valueMatcher, comparator)) {
LOGGER.trace("Value {} NOT added to delta for item {} because the item already has that value in {}", value, itemPath, contextDescription);
continue;
}
LOGGER.trace("Value {} added to delta as ADD for item {} in {}", value, itemPath, contextDescription);
itemDelta.addValueToAdd((V) value.clone());
continue;
}
// So check for that special case here to avoid removing them.
if (!minusPvwos.isEmpty() && plusPvwos.isEmpty()) {
boolean weakOnly = true;
boolean hasStrong = false;
boolean hasAuthoritative = false;
// exclusions and strength
for (ItemValueWithOrigin<V, D> pvwo : minusPvwos) {
PrismValueDeltaSetTripleProducer<V, D> mapping = pvwo.getMapping();
if (mapping.getStrength() != MappingStrengthType.WEAK) {
weakOnly = false;
}
if (mapping.getStrength() == MappingStrengthType.STRONG) {
hasStrong = true;
}
if (mapping.isAuthoritative()) {
hasAuthoritative = true;
}
}
if (!hasAuthoritative) {
LOGGER.trace("Value {} has no authoritative mapping for item {}, skipping deletion in {}", value, itemPath, contextDescription);
continue;
}
if (!hasStrong && (aprioriItemDelta != null && !aprioriItemDelta.isEmpty())) {
// There is already a delta, skip this
LOGGER.trace("Value {} mapping is not strong and the item {} already has a delta that is more concrete, skipping deletion in {}", value, itemPath, contextDescription);
continue;
}
if (weakOnly && (itemExisting != null && !itemExisting.isEmpty())) {
// There is already a value, skip this
LOGGER.trace("Value {} mapping is weak and the item {} already has a value, skipping deletion in {}", value, itemPath, contextDescription);
continue;
}
if (weakOnly && !applyWeak && (itemExisting == null || itemExisting.isEmpty())) {
// There is a weak mapping on a property, but we do not have full account available, so skipping deletion of the value is better way
LOGGER.trace("Value {} mapping is weak and the full account could not be fetched, skipping deletion in {}", value, itemPath, contextDescription);
continue;
}
if (filterExistingValues && !hasValue(itemExisting, value, valueMatcher, comparator)) {
LOGGER.trace("Value {} NOT add to delta as DELETE because item {} the item does not have that value in {} (matcher: {})", value, itemPath, contextDescription, valueMatcher);
continue;
}
LOGGER.trace("Value {} added to delta as DELETE for item {} in {}", value, itemPath, contextDescription);
itemDelta.addValueToDelete((V) value.clone());
}
if (!zeroPvwos.isEmpty()) {
boolean weakOnly = true;
boolean hasStrong = false;
boolean hasAuthoritative = false;
// exclusions and strength
for (ItemValueWithOrigin<V, D> pvwo : zeroPvwos) {
PrismValueDeltaSetTripleProducer<V, D> mapping = pvwo.getMapping();
if (mapping.getStrength() != MappingStrengthType.WEAK) {
weakOnly = false;
}
if (mapping.getStrength() == MappingStrengthType.STRONG) {
hasStrong = true;
}
if (mapping.isAuthoritative()) {
hasAuthoritative = true;
}
}
if (aprioriItemDelta != null && aprioriItemDelta.isReplace()) {
// Any strong mappings in the zero set needs to be re-applied as otherwise the replace will destroy it
if (hasStrong) {
LOGGER.trace("Value {} added to delta for item {} in {} because there is strong mapping in the zero set", value, itemPath, contextDescription);
itemDelta.addValueToAdd((V) value.clone());
continue;
}
}
}
}
Item<V, D> itemNew = null;
if (itemContainer != null) {
itemNew = itemContainer.findItem(itemPath);
}
if (!hasValue(itemNew, itemDelta)) {
// The application of computed delta results in no value, apply weak mappings
Collection<? extends ItemValueWithOrigin<V, D>> nonNegativePvwos = triple.getNonNegativeValues();
Collection<V> valuesToAdd = addWeakValues(nonNegativePvwos, OriginType.ASSIGNMENTS, applyWeak);
if (valuesToAdd.isEmpty()) {
valuesToAdd = addWeakValues(nonNegativePvwos, OriginType.OUTBOUND, applyWeak);
}
if (valuesToAdd.isEmpty()) {
valuesToAdd = addWeakValues(nonNegativePvwos, null, applyWeak);
}
LOGGER.trace("No value for item {} in {}, weak mapping processing yielded values: {}", itemPath, contextDescription, valuesToAdd);
itemDelta.addValuesToAdd(valuesToAdd);
} else {
LOGGER.trace("Existing values for item {} in {}, weak mapping processing skipped", new Object[] { itemPath, contextDescription });
}
if (itemExisting != null) {
List<V> existingValues = itemExisting.getValues();
if (existingValues != null) {
itemDelta.setEstimatedOldValues(PrismValue.cloneCollection(existingValues));
}
}
return itemDelta;
}
use of com.evolveum.midpoint.model.common.mapping.Mapping in project midpoint by Evolveum.
the class AssignmentEvaluator method evaluateFocusMappings.
private void evaluateFocusMappings(AssignmentPathSegmentImpl segment, EvaluationContext ctx) throws SchemaException, ExpressionEvaluationException, ObjectNotFoundException {
assertSourceNotNull(segment.source, ctx.evalAssignment);
// TODO why "assignmentTypeNew" when we consider also old values?
AssignmentType assignmentTypeNew = LensUtil.getAssignmentType(segment.getAssignmentIdi(), ctx.evaluateOld);
MappingsType mappingsType = assignmentTypeNew.getFocusMappings();
LOGGER.trace("Evaluate focus mappings '{}' in {} ({} mappings)", mappingsType.getDescription(), segment.source, mappingsType.getMapping().size());
AssignmentPathVariables assignmentPathVariables = LensUtil.computeAssignmentPathVariables(ctx.assignmentPath);
for (MappingType mappingType : mappingsType.getMapping()) {
Mapping mapping = mappingEvaluator.createFocusMapping(mappingFactory, lensContext, mappingType, segment.source, focusOdo, assignmentPathVariables, systemConfiguration, now, segment.sourceDescription, ctx.task, ctx.result);
if (mapping == null) {
continue;
}
// TODO: time constratins?
mappingEvaluator.evaluateMapping(mapping, lensContext, ctx.task, ctx.result);
ctx.evalAssignment.addFocusMapping(mapping);
}
}
use of com.evolveum.midpoint.model.common.mapping.Mapping in project midpoint by Evolveum.
the class ConsolidationProcessor method consolidateValuesToModifyDelta.
private <F extends FocusType> ObjectDelta<ShadowType> consolidateValuesToModifyDelta(LensContext<F> context, LensProjectionContext projCtx, boolean addUnchangedValues, Task task, OperationResult result) throws SchemaException, ExpressionEvaluationException, ObjectNotFoundException, CommunicationException, ConfigurationException, SecurityViolationException, PolicyViolationException {
// "Squeeze" all the relevant mappings into a data structure that we can process conveniently. We want to have all the
// (meta)data about relevant for a specific attribute in one data structure, not spread over several account constructions.
Map<QName, DeltaSetTriple<ItemValueWithOrigin<PrismPropertyValue<?>, PrismPropertyDefinition<?>>>> squeezedAttributes = sqeeze(projCtx, construction -> (Collection) construction.getAttributeMappings());
projCtx.setSqueezedAttributes(squeezedAttributes);
Map<QName, DeltaSetTriple<ItemValueWithOrigin<PrismContainerValue<ShadowAssociationType>, PrismContainerDefinition<ShadowAssociationType>>>> squeezedAssociations = sqeeze(projCtx, construction -> construction.getAssociationMappings());
projCtx.setSqueezedAssociations(squeezedAssociations);
// So, we do it here - once and for all.
if (!squeezedAssociations.isEmpty()) {
fillInAssociationNames(squeezedAssociations);
}
MappingExtractor<PrismPropertyValue<QName>, PrismPropertyDefinition<QName>, F> auxiliaryObjectClassExtractor = construction -> {
PrismValueDeltaSetTripleProducer<PrismPropertyValue<QName>, PrismPropertyDefinition<QName>> prod = new PrismValueDeltaSetTripleProducer<PrismPropertyValue<QName>, PrismPropertyDefinition<QName>>() {
@Override
public QName getMappingQName() {
return ShadowType.F_AUXILIARY_OBJECT_CLASS;
}
@Override
public PrismValueDeltaSetTriple<PrismPropertyValue<QName>> getOutputTriple() {
PrismValueDeltaSetTriple<PrismPropertyValue<QName>> triple = new PrismValueDeltaSetTriple<>();
if (construction.getAuxiliaryObjectClassDefinitions() != null) {
for (RefinedObjectClassDefinition auxiliaryObjectClassDefinition : construction.getAuxiliaryObjectClassDefinitions()) {
triple.addToZeroSet(new PrismPropertyValue<QName>(auxiliaryObjectClassDefinition.getTypeName()));
}
}
return triple;
}
@Override
public MappingStrengthType getStrength() {
return MappingStrengthType.STRONG;
}
@Override
public PrismValueDeltaSetTripleProducer<PrismPropertyValue<QName>, PrismPropertyDefinition<QName>> clone() {
return this;
}
@Override
public boolean isExclusive() {
return false;
}
@Override
public boolean isAuthoritative() {
return true;
}
@Override
public boolean isSourceless() {
return false;
}
};
Collection<PrismValueDeltaSetTripleProducer<PrismPropertyValue<QName>, PrismPropertyDefinition<QName>>> col = new ArrayList<>(1);
col.add(prod);
return col;
};
Map<QName, DeltaSetTriple<ItemValueWithOrigin<PrismPropertyValue<QName>, PrismPropertyDefinition<QName>>>> squeezedAuxiliaryObjectClasses = sqeeze(projCtx, auxiliaryObjectClassExtractor);
projCtx.setSqueezedAuxiliaryObjectClasses(squeezedAuxiliaryObjectClasses);
ResourceShadowDiscriminator discr = projCtx.getResourceShadowDiscriminator();
ObjectDelta<ShadowType> objectDelta = new ObjectDelta<ShadowType>(ShadowType.class, ChangeType.MODIFY, prismContext);
objectDelta.setOid(projCtx.getOid());
// Let's be very very lazy about fetching the account from the resource.
if (!projCtx.hasFullShadow() && (hasActiveWeakMapping(squeezedAttributes, projCtx) || hasActiveWeakMapping(squeezedAssociations, projCtx) || (hasActiveStrongMapping(squeezedAttributes, projCtx) || hasActiveStrongMapping(squeezedAssociations, projCtx)))) {
// Full account was not yet loaded. This will cause problems as
// the weak mapping may be applied even though it should not be
// applied
// and also same changes may be discarded because of unavailability
// of all
// account's attributes.Therefore load the account now, but with
// doNotDiscovery options..
// We also need to get account if there are strong mappings. Strong mappings
// should always be applied. So reading the account now will indirectly
// trigger reconciliation which makes sure that the strong mappings are
// applied.
// By getting accounts from provisioning, there might be a problem with
// resource availability. We need to know, if the account was read full
// or we have only the shadow from the repository. If we have only
// shadow, the weak mappings may applied even if they should not be.
contextLoader.loadFullShadow(context, projCtx, "weak or strong mapping", task, result);
if (projCtx.getSynchronizationPolicyDecision() == SynchronizationPolicyDecision.BROKEN) {
return null;
}
}
boolean completeAccount = projCtx.hasFullShadow();
ObjectDelta<ShadowType> existingDelta = projCtx.getDelta();
// AUXILIARY OBJECT CLASSES
ItemPath auxiliaryObjectClassItemPath = new ItemPath(ShadowType.F_AUXILIARY_OBJECT_CLASS);
PrismPropertyDefinition<QName> auxiliaryObjectClassPropertyDef = projCtx.getObjectDefinition().findPropertyDefinition(auxiliaryObjectClassItemPath);
PropertyDelta<QName> auxiliaryObjectClassAPrioriDelta = null;
RefinedResourceSchema refinedSchema = projCtx.getRefinedResourceSchema();
List<QName> auxOcNames = new ArrayList<>();
List<RefinedObjectClassDefinition> auxOcDefs = new ArrayList<>();
ObjectDelta<ShadowType> projDelta = projCtx.getDelta();
if (projDelta != null) {
auxiliaryObjectClassAPrioriDelta = projDelta.findPropertyDelta(auxiliaryObjectClassItemPath);
}
for (Entry<QName, DeltaSetTriple<ItemValueWithOrigin<PrismPropertyValue<QName>, PrismPropertyDefinition<QName>>>> entry : squeezedAuxiliaryObjectClasses.entrySet()) {
DeltaSetTriple<ItemValueWithOrigin<PrismPropertyValue<QName>, PrismPropertyDefinition<QName>>> ivwoTriple = entry.getValue();
LOGGER.trace("CONSOLIDATE auxiliary object classes ({})", new Object[] { discr });
if (LOGGER.isTraceEnabled()) {
LOGGER.trace("Auxiliary object class triple:\n{}", ivwoTriple.debugDump());
}
for (ItemValueWithOrigin<PrismPropertyValue<QName>, PrismPropertyDefinition<QName>> ivwo : ivwoTriple.getAllValues()) {
QName auxObjectClassName = ivwo.getItemValue().getValue();
if (auxOcNames.contains(auxObjectClassName)) {
continue;
}
auxOcNames.add(auxObjectClassName);
RefinedObjectClassDefinition auxOcDef = refinedSchema.getRefinedDefinition(auxObjectClassName);
if (auxOcDef == null) {
LOGGER.error("Auxiliary object class definition {} for {} not found in the schema, but it should be there, dumping context:\n{}", auxObjectClassName, discr, context.debugDump());
throw new IllegalStateException("Auxiliary object class definition " + auxObjectClassName + " for " + discr + " not found in the context, but it should be there");
}
auxOcDefs.add(auxOcDef);
}
ItemDelta<PrismPropertyValue<QName>, PrismPropertyDefinition<QName>> itemDelta = LensUtil.consolidateTripleToDelta(auxiliaryObjectClassItemPath, ivwoTriple, auxiliaryObjectClassPropertyDef, auxiliaryObjectClassAPrioriDelta, projCtx.getObjectNew(), null, null, addUnchangedValues, completeAccount, false, discr.toHumanReadableDescription(), false);
PropertyDelta<QName> propDelta = (PropertyDelta) itemDelta;
if (LOGGER.isTraceEnabled()) {
LOGGER.trace("Auxiliary object class delta:\n{}", propDelta.debugDump());
}
if (!propDelta.isEmpty()) {
objectDelta.addModification(propDelta);
}
}
RefinedObjectClassDefinition structuralObjectClassDefinition = projCtx.getStructuralObjectClassDefinition();
if (structuralObjectClassDefinition == null) {
LOGGER.error("Structural object class definition for {} not found in the context, but it should be there, dumping context:\n{}", discr, context.debugDump());
throw new IllegalStateException("Structural object class definition for " + discr + " not found in the context, but it should be there");
}
RefinedObjectClassDefinition rOcDef = new CompositeRefinedObjectClassDefinitionImpl(structuralObjectClassDefinition, auxOcDefs);
if (LOGGER.isTraceEnabled()) {
LOGGER.trace("Object class definition for {} consolidation:\n{}", discr, rOcDef.debugDump());
}
// with the data in ItemValueWithOrigin triples.
for (Map.Entry<QName, DeltaSetTriple<ItemValueWithOrigin<PrismPropertyValue<?>, PrismPropertyDefinition<?>>>> entry : squeezedAttributes.entrySet()) {
QName attributeName = entry.getKey();
DeltaSetTriple<ItemValueWithOrigin<PrismPropertyValue<?>, PrismPropertyDefinition<?>>> triple = entry.getValue();
PropertyDelta<?> propDelta = consolidateAttribute(rOcDef, discr, existingDelta, projCtx, addUnchangedValues, completeAccount, attributeName, (DeltaSetTriple) triple);
if (propDelta != null) {
objectDelta.addModification(propDelta);
}
}
// ASSOCIATIONS
for (Entry<QName, DeltaSetTriple<ItemValueWithOrigin<PrismContainerValue<ShadowAssociationType>, PrismContainerDefinition<ShadowAssociationType>>>> entry : squeezedAssociations.entrySet()) {
QName associationName = entry.getKey();
DeltaSetTriple<ItemValueWithOrigin<PrismContainerValue<ShadowAssociationType>, PrismContainerDefinition<ShadowAssociationType>>> triple = entry.getValue();
ContainerDelta<ShadowAssociationType> containerDelta = consolidateAssociation(rOcDef, discr, existingDelta, projCtx, addUnchangedValues, completeAccount, associationName, triple);
if (containerDelta != null) {
objectDelta.addModification(containerDelta);
}
}
return objectDelta;
}
use of com.evolveum.midpoint.model.common.mapping.Mapping in project midpoint by Evolveum.
the class MappingEvaluator method evaluateMappingSetProjection.
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) {
// 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 is all not right. See MID-3847
if (!hasFullTargetObject && params.getTargetLoader() != 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) {
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);
}
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 (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);
}
}
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);
}
}
}
Aggregations