Skip to content

Commit

Permalink
Add time support to standard outbound mappings
Browse files Browse the repository at this point in the history
These mappings did not support timeFrom/timeTo, now they do.
Some code was deduplicated and cleaned up.

(This is an implementation of MID-5874.)
  • Loading branch information
mederly committed Feb 14, 2020
1 parent 2541f7e commit d306d96
Show file tree
Hide file tree
Showing 12 changed files with 260 additions and 127 deletions.
12 changes: 12 additions & 0 deletions infra/util/src/main/java/com/evolveum/midpoint/util/DebugUtil.java
Original file line number Diff line number Diff line change
Expand Up @@ -672,6 +672,18 @@ public String toString() {
};
}

public static Object debugDumpLazily(Map<?, ?> dumpables, int indent) {
if (dumpables == null || dumpables.isEmpty()) {
return dumpables;
}
return new Object() {
@Override
public String toString() {
return debugDump(dumpables, indent);
}
};
}

public static String shortDump(ShortDumpable sd) {
if (sd == null) {
return null;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -99,12 +99,12 @@ public class MappingImpl<V extends PrismValue,D extends ItemDefinition> implemen

private PrismValueDeltaSetTriple<V> outputTriple;
private PrismValueDeltaSetTriple<PrismPropertyValue<Boolean>> conditionOutputTriple;
private Boolean timeConstraintValid = null;
private XMLGregorianCalendar nextRecomputeTime = null;
private Long evaluationStartTime = null;
private Long evaluationEndTime = null;
private Boolean timeConstraintValid;
private XMLGregorianCalendar nextRecomputeTime;
private Long evaluationStartTime;
private Long evaluationEndTime;

private String mappingContextDescription = null;
private String mappingContextDescription;

private VariableProducer variableProducer;

Expand Down Expand Up @@ -475,7 +475,7 @@ public boolean isActivated() {
}

// TODO: rename to evaluate -- or evaluatePrepared?
public void evaluateBody(Task task, OperationResult parentResult) throws ExpressionEvaluationException, ObjectNotFoundException, SchemaException, SecurityViolationException, ConfigurationException, CommunicationException {
private void evaluateBody(Task task, OperationResult parentResult) throws ExpressionEvaluationException, ObjectNotFoundException, SchemaException, SecurityViolationException, ConfigurationException, CommunicationException {

assertState(MappingEvaluationState.PREPARED);

Expand Down Expand Up @@ -793,19 +793,19 @@ private boolean computeConditionResult(Collection<PrismPropertyValue<Boolean>> b
return ExpressionUtil.computeConditionResult(booleanPropertyValues);
}

public Boolean evaluateTimeConstraintValid(Task task, OperationResult result) throws SchemaException, ObjectNotFoundException, CommunicationException, ConfigurationException, SecurityViolationException, ExpressionEvaluationException {
public boolean evaluateTimeConstraintValid(Task task, OperationResult result) throws SchemaException, ObjectNotFoundException, CommunicationException, ConfigurationException, SecurityViolationException, ExpressionEvaluationException {
if (timeConstraintValid == null) {
parseTimeConstraints(task, result);
timeConstraintValid = parseTimeConstraints(task, result);
}
return timeConstraintValid;
}

private void parseTimeConstraints(Task task, OperationResult result) throws SchemaException, ObjectNotFoundException, CommunicationException, ConfigurationException, SecurityViolationException, ExpressionEvaluationException {
// Sets nextRecomputeTime as a side effect.
private boolean parseTimeConstraints(Task task, OperationResult result) throws SchemaException, ObjectNotFoundException, CommunicationException, ConfigurationException, SecurityViolationException, ExpressionEvaluationException {
MappingTimeDeclarationType timeFromType = mappingType.getTimeFrom();
MappingTimeDeclarationType timeToType = mappingType.getTimeTo();
if (timeFromType == null && timeToType == null) {
timeConstraintValid = true;
return;
return true;
}

XMLGregorianCalendar timeFrom = parseTime(timeFromType, task, result);
Expand All @@ -816,8 +816,7 @@ private void parseTimeConstraints(Task task, OperationResult result) throws Sche
// Time is specified but there is no value for it.
// This means that event that should start validity haven't happened yet
// therefore the mapping is not yet valid.
timeConstraintValid = false;
return;
return false;
}
XMLGregorianCalendar timeTo = parseTime(timeToType, task, result);
if (trace != null) {
Expand All @@ -827,35 +826,26 @@ private void parseTimeConstraints(Task task, OperationResult result) throws Sche
if (timeFrom != null && timeFrom.compare(now) == DatatypeConstants.GREATER) {
// before timeFrom
nextRecomputeTime = timeFrom;
timeConstraintValid = false;
return;
return false;
}

if (timeTo == null && timeToType != null) {
// Time is specified but there is no value for it.
// This means that event that should stop validity haven't happened yet
// therefore the mapping is still valid.
timeConstraintValid = true;
return;
return true;
}

if (timeTo != null && timeTo.compare(now) == DatatypeConstants.GREATER) {
// between timeFrom and timeTo (also no timeFrom and before timeTo)
nextRecomputeTime = timeTo;
timeConstraintValid = true;
return;
return true;
}

//noinspection RedundantIfStatement
if (timeTo == null) {
// after timeFrom and no timeTo
// no nextRecomputeTime set, there is nothing to recompute in the future
timeConstraintValid = true;
} else {
// after timeTo
// no nextRecomputeTime set, there is nothing to recompute in the future
timeConstraintValid = false;
}
// If timeTo is null, we are "in range"
// Otherwise it is less than now (so we are after it), i.e. we are "out of range"
// In both cases there is nothing to recompute in the future
return timeTo == null;
}

private XMLGregorianCalendar parseTime(MappingTimeDeclarationType timeType, Task task, OperationResult result) throws SchemaException, ObjectNotFoundException, CommunicationException, ConfigurationException, SecurityViolationException, ExpressionEvaluationException {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -691,9 +691,8 @@ private <T, F extends FocusType> void evaluateOutboundMapping(final LensContext<

Map<UniformItemPath, MappingOutputStruct<PrismPropertyValue<T>>> outputTripleMap = mappingEvaluator.evaluateMappingSetProjection(params, task, result);

if (LOGGER.isTraceEnabled()) {
LOGGER.trace("Mapping processing output after {} ({}):\n{}", desc, evaluateCurrent, DebugUtil.debugDump(outputTripleMap, 1));
}
LOGGER.trace("Mapping processing output after {} ({}):\n{}", desc, evaluateCurrent,
DebugUtil.debugDumpLazily(outputTripleMap, 1));

if (projCtx.isDoReconciliation()) {
reconcileOutboundValue(context, projCtx, outputTripleMap, desc);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,10 @@
import javax.xml.bind.JAXBElement;
import javax.xml.namespace.QName;

import com.evolveum.midpoint.common.Clock;
import com.evolveum.midpoint.model.impl.lens.projector.mappings.MappingEvaluator;
import com.evolveum.midpoint.model.impl.lens.projector.mappings.NextRecompute;
import com.evolveum.midpoint.prism.*;
import com.evolveum.midpoint.prism.path.ItemPath;
import com.evolveum.midpoint.util.exception.*;

Expand All @@ -30,16 +33,6 @@
import com.evolveum.midpoint.model.impl.lens.LensContext;
import com.evolveum.midpoint.model.impl.lens.LensProjectionContext;
import com.evolveum.midpoint.model.impl.lens.LensUtil;
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.PrismProperty;
import com.evolveum.midpoint.prism.PrismPropertyValue;
import com.evolveum.midpoint.prism.PrismValue;
import com.evolveum.midpoint.prism.delta.ChangeType;
import com.evolveum.midpoint.prism.delta.ObjectDelta;
import com.evolveum.midpoint.prism.util.ObjectDeltaObject;
Expand Down Expand Up @@ -79,8 +72,9 @@ public class OutboundProcessor {
@Autowired private MappingFactory mappingFactory;
@Autowired private MappingEvaluator mappingEvaluator;
@Autowired private ContextLoader contextLoader;
@Autowired private Clock clock;

public <F extends FocusType> void processOutbound(LensContext<F> context, LensProjectionContext projCtx, Task task, OperationResult result) throws SchemaException,
<F extends FocusType> void processOutbound(LensContext<F> context, LensProjectionContext projCtx, Task task, OperationResult result) throws SchemaException,
ExpressionEvaluationException, ObjectNotFoundException, CommunicationException, ConfigurationException, SecurityViolationException {

ResourceShadowDiscriminator discr = projCtx.getResourceShadowDiscriminator();
Expand Down Expand Up @@ -113,15 +107,20 @@ public <F extends FocusType> void processOutbound(LensContext<F> context, LensPr

String operation = projCtx.getOperation().getValue();

NextRecompute nextRecompute = null;

for (QName attributeName : rOcDef.getNamesOfAttributesWithOutboundExpressions()) {
RefinedAttributeDefinition<?> refinedAttributeDefinition = rOcDef.findAttributeDefinition(attributeName);
if (refinedAttributeDefinition == null) {
throw new IllegalStateException("No definition for " + attributeName);
}

final MappingType outboundMappingType = refinedAttributeDefinition.getOutboundMappingType();
if (outboundMappingType == null) {
continue;
}

if (refinedAttributeDefinition.isIgnored(LayerType.MODEL)) {
if (refinedAttributeDefinition.getProcessing(LayerType.MODEL) == ItemProcessing.IGNORE) {
LOGGER.trace("Skipping processing outbound mapping for attribute {} because it is ignored", attributeName);
continue;
}
Expand All @@ -132,16 +131,19 @@ public <F extends FocusType> void processOutbound(LensContext<F> context, LensPr
projectionOdo = projCtx.getObjectDeltaObject();
}

MappingImpl.Builder<PrismPropertyValue<?>,RefinedAttributeDefinition<?>> builder = mappingFactory.createMappingBuilder(outboundMappingType,
"outbound mapping for " + PrettyPrinter.prettyPrint(refinedAttributeDefinition.getItemName())
+ " in " + projCtx.getResource());
String mappingShortDesc = "outbound mapping for " +
PrettyPrinter.prettyPrint(refinedAttributeDefinition.getItemName()) + " in " + projCtx.getResource();
MappingImpl.Builder<PrismPropertyValue<?>, RefinedAttributeDefinition<?>> builder =
mappingFactory.createMappingBuilder(outboundMappingType, mappingShortDesc);
//noinspection ConstantConditions
builder = builder.originObject(projCtx.getResource())
.originType(OriginType.OUTBOUND);
MappingImpl<PrismPropertyValue<?>,RefinedAttributeDefinition<?>> evaluatedMapping = evaluateMapping(builder, attributeName, refinedAttributeDefinition,
focusOdo, projectionOdo, operation, rOcDef, null, context, projCtx, task, result);

if (evaluatedMapping != null) {
outboundConstruction.addAttributeMapping(evaluatedMapping);
nextRecompute = NextRecompute.update(evaluatedMapping, nextRecompute);
}
}

Expand All @@ -163,16 +165,20 @@ public <F extends FocusType> void processOutbound(LensContext<F> context, LensPr
+ " in " + projCtx.getResource());

PrismContainerDefinition<ShadowAssociationType> outputDefinition = getAssociationContainerDefinition();
MappingImpl<PrismContainerValue<ShadowAssociationType>,PrismContainerDefinition<ShadowAssociationType>> evaluatedMapping = (MappingImpl) evaluateMapping(mappingBuilder,
MappingImpl<PrismContainerValue<ShadowAssociationType>,PrismContainerDefinition<ShadowAssociationType>> evaluatedMapping = evaluateMapping(mappingBuilder,
assocName, outputDefinition, focusOdo, projectionOdo, operation, rOcDef,
associationDefinition.getAssociationTarget(), context, projCtx, task, result);

if (evaluatedMapping != null) {
outboundConstruction.addAssociationMapping(evaluatedMapping);
nextRecompute = NextRecompute.update(evaluatedMapping, nextRecompute);
}
}

projCtx.setOutboundConstruction(outboundConstruction);
if (nextRecompute != null) {
nextRecompute.createTrigger(context.getFocusContext());
}
}

// TODO: unify with MappingEvaluator.evaluateOutboundMapping(...)
Expand Down Expand Up @@ -222,6 +228,7 @@ private <F extends FocusType, V extends PrismValue, D extends ItemDefinition> Ma

mappingBuilder.addVariableDefinition(ExpressionConstants.VAR_LEGAL, projCtx.isLegal());
mappingBuilder.addVariableDefinition(ExpressionConstants.VAR_ASSIGNED, projCtx.isAssigned());
mappingBuilder.now(clock.currentTimeXMLGregorianCalendar());

if (assocTargetObjectClassDefinition != null) {
mappingBuilder.addVariableDefinition(ExpressionConstants.VAR_ASSOCIATION_TARGET_OBJECT_CLASS_DEFINITION,
Expand All @@ -232,33 +239,33 @@ private <F extends FocusType, V extends PrismValue, D extends ItemDefinition> Ma
mappingBuilder.refinedObjectClassDefinition(rOcDef);

if (projCtx.isDelete()) {
mappingBuilder.originalTargetValues(Collections.EMPTY_LIST);
mappingBuilder.originalTargetValues(Collections.emptyList());
} else if (projCtx.isAdd()) {
mappingBuilder.originalTargetValues(Collections.EMPTY_LIST);
mappingBuilder.originalTargetValues(Collections.emptyList());
} else {
PrismObject<ShadowType> oldObject = projectionOdo.getOldObject();
if (oldObject != null) {
PrismProperty<Object> attributeOld = oldObject.findProperty(targetPath);
if (attributeOld == null) {
if (projCtx.hasFullShadow()) {
// We know that the attribute has no values
mappingBuilder.originalTargetValues(Collections.EMPTY_LIST);
mappingBuilder.originalTargetValues(Collections.emptyList());
} else {
// We do not have full shadow. Therefore we know nothing about attribute values.
// We cannot set originalTargetValues here.
}
} else {
//noinspection unchecked
mappingBuilder.originalTargetValues((Collection<V>) attributeOld.getValues());
}
}
}

ValuePolicyResolver stringPolicyResolver = new ValuePolicyResolver() {
private ItemPath outputPath;
private ItemDefinition outputDefinition;

@Override
public void setOutputPath(ItemPath outputPath) {
this.outputPath = outputPath;
}

@Override
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -35,11 +35,8 @@
import com.evolveum.midpoint.model.impl.lens.LensContext;
import com.evolveum.midpoint.model.impl.lens.LensFocusContext;
import com.evolveum.midpoint.model.impl.lens.StrengthSelector;
import com.evolveum.midpoint.model.impl.trigger.RecomputeTriggerHandler;
import com.evolveum.midpoint.prism.Item;
import com.evolveum.midpoint.prism.ItemDefinition;
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;
Expand Down Expand Up @@ -82,7 +79,6 @@
import com.evolveum.midpoint.xml.ns._public.common.common_3.ObjectType;
import com.evolveum.midpoint.xml.ns._public.common.common_3.RoleManagementConfigurationType;
import com.evolveum.midpoint.xml.ns._public.common.common_3.SystemConfigurationType;
import com.evolveum.midpoint.xml.ns._public.common.common_3.TriggerType;
import com.evolveum.midpoint.xml.ns._public.common.common_3.VariableBindingDefinitionType;
import com.evolveum.prism.xml.ns._public.types_3.ItemPathType;

Expand Down Expand Up @@ -165,32 +161,7 @@ <AH extends AssignmentHolderType> void processTemplate(LensContext<AH> context,
focusContext.applyProjectionWaveSecondaryDeltas(itemDeltas);

if (nextRecompute != null) {

boolean alreadyHasTrigger = false;
PrismObject<AH> objectCurrent = focusContext.getObjectCurrent();
if (objectCurrent != null) {
for (TriggerType trigger: objectCurrent.asObjectable().getTrigger()) {
if (RecomputeTriggerHandler.HANDLER_URI.equals(trigger.getHandlerUri()) &&
nextRecompute.nextRecomputeTime.equals(trigger.getTimestamp())) {
alreadyHasTrigger = true;
break;
}
}
}

if (!alreadyHasTrigger) {
PrismObjectDefinition<AH> objectDefinition = focusContext.getObjectDefinition();
PrismContainerDefinition<TriggerType> triggerContDef = objectDefinition.findContainerDefinition(ObjectType.F_TRIGGER);
ContainerDelta<TriggerType> triggerDelta = triggerContDef.createEmptyDelta(ObjectType.F_TRIGGER);
PrismContainerValue<TriggerType> triggerCVal = triggerContDef.createValue();
triggerDelta.addValueToAdd(triggerCVal);
TriggerType triggerType = triggerCVal.asContainerable();
triggerType.setTimestamp(nextRecompute.nextRecomputeTime);
triggerType.setHandlerUri(RecomputeTriggerHandler.HANDLER_URI);
triggerType.setOriginDescription(nextRecompute.triggerOriginDescription);

focusContext.swallowToProjectionWaveSecondaryDelta(triggerDelta);
}
nextRecompute.createTrigger(focusContext);
}
}

Expand Down
Loading

0 comments on commit d306d96

Please sign in to comment.