Skip to content

Commit

Permalink
Document expression evaluators
Browse files Browse the repository at this point in the history
Expression evaluators are now a bit more documented.
Some of them were slightly refactored to improve understandability.

This is a preparation for MID-6275 implementation.
  • Loading branch information
mederly committed Jun 9, 2020
1 parent defc3fe commit ec25c0a
Show file tree
Hide file tree
Showing 27 changed files with 433 additions and 349 deletions.
Expand Up @@ -147,19 +147,13 @@ protected List<V> transformSingleValue(ExpressionVariables variables, PlusMinusZ
throw new SchemaException("No filter in "+shortDebugDump());
}
query = prismContext.getQueryConverter().createObjectQuery(targetTypeClass, filterType);
if (LOGGER.isTraceEnabled()){
LOGGER.trace("XML query converted to: {}", query.debugDump());
}
LOGGER.trace("XML query converted to: {}", query.debugDumpLazily());

query = ExpressionUtil.evaluateQueryExpressions(query, variables, context.getExpressionProfile(), context.getExpressionFactory(),
prismContext, context.getContextDescription(), task, result);
if (LOGGER.isTraceEnabled()){
LOGGER.trace("Expression in query evaluated to: {}", query.debugDump());
}
LOGGER.trace("Expression in query evaluated to: {}", query.debugDumpLazily());
query = extendQuery(query, context);

if (LOGGER.isTraceEnabled()){
LOGGER.trace("Query after extension: {}", query.debugDump());
}
LOGGER.trace("Query after extension: {}", query.debugDumpLazily());

resultValues = executeSearchUsingCache(targetTypeClass, targetTypeQName, query, additionalAttributeDeltas, context,
contextDescription, task, result);
Expand Down Expand Up @@ -344,10 +338,8 @@ private <O extends ObjectType> List<V> executeSearchAttempt(List<PrismObject> ra
}
}

if (LOGGER.isTraceEnabled()) {
LOGGER.trace("Assignment expression resulted in {} objects, using query:\n{}",
valueResults.size(), query.debugDump());
}
LOGGER.trace("Assignment expression resulted in {} objects, using query:\n{}",
valueResults.size(), query.debugDumpLazily());

return valueResults;
}
Expand Down
Expand Up @@ -30,6 +30,9 @@
import static java.util.Collections.emptyList;

/**
* Creates an assignment (or assignments) based on specified conditions for the assignment target.
* Can create target objects on demand.
*
* @author Radovan Semancik
*/
public class AssignmentTargetSearchExpressionEvaluator
Expand Down
Expand Up @@ -9,6 +9,7 @@
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.Objects;

import javax.xml.namespace.QName;

Expand Down Expand Up @@ -51,15 +52,24 @@
import com.evolveum.midpoint.xml.ns._public.common.common_3.ShadowKindType;
import com.evolveum.midpoint.xml.ns._public.common.common_3.ShadowType;
import org.apache.commons.collections4.CollectionUtils;
import org.jetbrains.annotations.NotNull;

import static com.evolveum.midpoint.schema.GetOperationOptions.createNoFetchCollection;

/**
* @author Radovan Semancik
* Creates an association (or associations) based on projections of given role.
*
* I.e. a role has projection (e.g. group) and it also induces a construction of a user account. Using this expression
* evaluator the account can obtain groups that are projections of that particular role.
*
* To be used in induced constructions only i.e. not in mappings!
*
* @author Radovan Semancik
*/
public class AssociationFromLinkExpressionEvaluator
extends AbstractExpressionEvaluator<PrismContainerValue<ShadowAssociationType>,PrismContainerDefinition<ShadowAssociationType>, AssociationFromLinkExpressionEvaluatorType> {
extends AbstractExpressionEvaluator<PrismContainerValue<ShadowAssociationType>,
PrismContainerDefinition<ShadowAssociationType>,
AssociationFromLinkExpressionEvaluatorType> {

private static final Trace LOGGER = TraceManager.getTrace(AssociationFromLinkExpressionEvaluator.class);

Expand All @@ -71,65 +81,22 @@ public class AssociationFromLinkExpressionEvaluator
this.objectResolver = objectResolver;
}

/* (non-Javadoc)
* @see com.evolveum.midpoint.common.expression.ExpressionEvaluator#evaluate(java.util.Collection, java.util.Map, boolean, java.lang.String, com.evolveum.midpoint.schema.result.OperationResult)
*/
@Override
public PrismValueDeltaSetTriple<PrismContainerValue<ShadowAssociationType>> evaluate(ExpressionEvaluationContext context,
OperationResult result)
throws SchemaException, ExpressionEvaluationException, ObjectNotFoundException, CommunicationException, ConfigurationException, SecurityViolationException {
throws SchemaException, ExpressionEvaluationException, ObjectNotFoundException, CommunicationException,
ConfigurationException, SecurityViolationException {

checkEvaluatorProfile(context);

String desc = context.getContextDescription();

AbstractRoleType thisRole;

Integer assignmentPathIndex = expressionEvaluatorBean.getAssignmentPathIndex();
if (assignmentPathIndex == null) {
// Legacy ... or default in simple cases
@SuppressWarnings("unchecked")
TypedValue<AbstractRoleType> orderOneObjectTypedValue = context.getVariables().get(ExpressionConstants.VAR_ORDER_ONE_OBJECT);
if (orderOneObjectTypedValue == null || orderOneObjectTypedValue.getValue() == null) {
throw new ExpressionEvaluationException("No order one object variable in "+desc+"; the expression may be used in a wrong place. It is only supposed to work in a role.");
}
Object orderOneObject = orderOneObjectTypedValue.getValue();
if (!(orderOneObject instanceof AbstractRoleType)) {
throw new ExpressionEvaluationException("Order one object variable in "+desc+" is not a role, it is "+orderOneObject.getClass().getName()
+"; the expression may be used in a wrong place. It is only supposed to work in a role.");
}

thisRole = (AbstractRoleType)orderOneObject;

} else {

@SuppressWarnings("unchecked")
TypedValue<AssignmentPath> assignmentPathTypedValue = context.getVariables().get(ExpressionConstants.VAR_ASSIGNMENT_PATH);
if (assignmentPathTypedValue == null || assignmentPathTypedValue.getValue() == null) {
throw new ExpressionEvaluationException("No assignment path variable in "+desc+"; the expression may be used in a wrong place. It is only supposed to work in a role.");
}

AssignmentPath assignmentPath = (AssignmentPath) assignmentPathTypedValue.getValue();
if (assignmentPath.isEmpty()) {
throw new ExpressionEvaluationException("Empty assignment path variable in "+desc+"; the expression may be used in a wrong place. It is only supposed to work in a role.");
}

LOGGER.trace("assignmentPath {}:\n{}", expressionEvaluatorBean.getDescription(), assignmentPath.debugDumpLazily(1));

AssignmentPathSegment segment;
try {
segment = assignmentPath.getSegment(assignmentPathIndex);
} catch (IndexOutOfBoundsException e) {
throw new ExpressionEvaluationException("Wrong assignment path index in "+desc+"; Index "+assignmentPathIndex+" cannot be applied to a path of length "+assignmentPath.size(), e);
}

thisRole = (AbstractRoleType) segment.getSource();
}

LOGGER.trace("thisRole {}: {}", expressionEvaluatorBean.getDescription(), thisRole);

LOGGER.trace("Evaluating association from link on: {}", thisRole);
AbstractRoleType thisRole = getRelevantRole(context);
LOGGER.trace("Evaluating association from link {} on: {}", expressionEvaluatorBean.getDescription(), thisRole);

TypedValue<RefinedObjectClassDefinition> rAssocTargetDefTypedValue = context.getVariables().get(ExpressionConstants.VAR_ASSOCIATION_TARGET_OBJECT_CLASS_DEFINITION);
//noinspection unchecked
TypedValue<RefinedObjectClassDefinition> rAssocTargetDefTypedValue = context.getVariables()
.get(ExpressionConstants.VAR_ASSOCIATION_TARGET_OBJECT_CLASS_DEFINITION);
if (rAssocTargetDefTypedValue == null || rAssocTargetDefTypedValue.getValue() == null) {
throw new ExpressionEvaluationException("No association target object class definition variable in "+desc+"; the expression may be used in a wrong place. It is only supposed to create an association.");
}
Expand All @@ -149,19 +116,76 @@ public PrismValueDeltaSetTriple<PrismContainerValue<ShadowAssociationType>> eval

QName assocName = context.getMappingQName();
String resourceOid = rAssocTargetDef.getResourceOid();
Collection<SelectorOptions<GetOperationOptions>> options = null;
List<String> candidateShadowOidList = new ArrayList<>();
// Always process the first role (myself) regardless of recursion setting
gatherCandidateShadowsFromAbstractRole(thisRole, candidateShadowOidList);
if (thisRole instanceof OrgType && matchesForRecursion((OrgType)thisRole)) {
gatherCandidateShadowsFromAbstractRoleRecurse((OrgType)thisRole, candidateShadowOidList, options, desc, context, result);
gatherCandidateShadowsFromAbstractRoleRecurse((OrgType)thisRole, candidateShadowOidList, null, desc, context, result);
}
LOGGER.trace("Candidate shadow OIDs: {}", candidateShadowOidList);

selectMatchingShadows(candidateShadowOidList, output, resourceOid, kind, intent, assocName, context, result);
return ItemDeltaUtil.toDeltaSetTriple(output, null, prismContext);
}

private AbstractRoleType getRelevantRole(ExpressionEvaluationContext context) throws ExpressionEvaluationException {
AbstractRoleType thisRole;
Integer assignmentPathIndex = expressionEvaluatorBean.getAssignmentPathIndex();
if (assignmentPathIndex == null) {
// Legacy ... or default in simple cases
thisRole = getOrderOneObject(context);
} else {
AssignmentPathSegment segment = getSpecifiedAssignmentPathSegment(context, assignmentPathIndex);
thisRole = (AbstractRoleType) segment.getSource();
}
return thisRole;
}

@NotNull
private AbstractRoleType getOrderOneObject(ExpressionEvaluationContext context)
throws ExpressionEvaluationException {
@SuppressWarnings("unchecked")
TypedValue<AbstractRoleType> orderOneObjectTypedValue = context.getVariables().get(ExpressionConstants.VAR_ORDER_ONE_OBJECT);
if (orderOneObjectTypedValue == null || orderOneObjectTypedValue.getValue() == null) {
throw new ExpressionEvaluationException("No order one object variable in " + context.getContextDescription() +
"; the expression may be used in a wrong place. It is only supposed to work in a role.");
}
Object orderOneObject = orderOneObjectTypedValue.getValue();
if (orderOneObject instanceof AbstractRoleType) {
return (AbstractRoleType) orderOneObject;
} else {
throw new ExpressionEvaluationException("Order one object variable in " + context.getContextDescription() +
" is not a role, it is "+orderOneObject.getClass().getName() +
"; the expression may be used in a wrong place. It is only supposed to work in a role.");
}
}

private AssignmentPathSegment getSpecifiedAssignmentPathSegment(ExpressionEvaluationContext context, Integer assignmentPathIndex)
throws ExpressionEvaluationException {

@SuppressWarnings("unchecked")
TypedValue<AssignmentPath> assignmentPathTypedValue = context.getVariables().get(ExpressionConstants.VAR_ASSIGNMENT_PATH);
if (assignmentPathTypedValue == null || assignmentPathTypedValue.getValue() == null) {
throw new ExpressionEvaluationException("No assignment path variable in " + context.getContextDescription() +
"; the expression may be used in a wrong place. It is only supposed to work in a role.");
}

AssignmentPath assignmentPath = (AssignmentPath) assignmentPathTypedValue.getValue();
if (assignmentPath.isEmpty()) {
throw new ExpressionEvaluationException("Empty assignment path variable in " + context.getContextDescription() +
"; the expression may be used in a wrong place. It is only supposed to work in a role.");
}

LOGGER.trace("assignmentPath {}:\n{}", expressionEvaluatorBean.getDescription(), assignmentPath.debugDumpLazily(1));

try {
return assignmentPath.getSegment(assignmentPathIndex);
} catch (IndexOutOfBoundsException e) {
throw new ExpressionEvaluationException("Wrong assignment path index in " + context.getContextDescription() +
"; Index "+assignmentPathIndex+" cannot be applied to a path of length "+assignmentPath.size(), e);
}
}

private void selectMatchingShadows(List<String> candidateShadowsOidList,
PrismContainer<ShadowAssociationType> output, String resourceOid, ShadowKindType kind,
String intent, QName assocName, ExpressionEvaluationContext context, OperationResult result) {
Expand Down Expand Up @@ -199,7 +223,8 @@ private void toAssociation(PrismObject<ShadowType> shadow, ShadowAssociationType
ResourceAttributeContainer identifiersContainer = ObjectFactory.createResourceAttributeContainer(
ShadowAssociationType.F_IDENTIFIERS, shadowAttributesContainer.getDefinition(), prismContext);
shadowAssociationType.asPrismContainerValue().add(identifiersContainer);
Collection<ResourceAttribute<?>> shadowIdentifiers = ShadowUtil.getAllIdentifiers(shadow);
Collection<ResourceAttribute<?>> shadowIdentifiers =
Objects.requireNonNull(ShadowUtil.getAllIdentifiers(shadow), "no shadow identifiers");
for (ResourceAttribute<?> shadowIdentifier : shadowIdentifiers) {
identifiersContainer.add(shadowIdentifier.clone());
}
Expand All @@ -208,7 +233,6 @@ private void toAssociation(PrismObject<ShadowType> shadow, ShadowAssociationType
// Should not happen
throw new SystemException(e.getMessage(), e);
}

}

private void gatherCandidateShadowsFromAbstractRole(AbstractRoleType thisRole, List<String> candidateShadowsOidList) {
Expand Down Expand Up @@ -240,12 +264,8 @@ private boolean matchesForRecursion(OrgType thisOrg) {
return false;
}

/* (non-Javadoc)
* @see com.evolveum.midpoint.common.expression.ExpressionEvaluator#shortDebugDump()
*/
@Override
public String shortDebugDump() {
return "associationFromLink";
}

}
Expand Up @@ -43,6 +43,8 @@
import com.evolveum.midpoint.xml.ns._public.common.common_3.ShadowType;

/**
* Creates an association (or associations) based on specified condition for the associated object.
*
* @author Radovan Semancik
*/
public class AssociationTargetSearchExpressionEvaluator
Expand Down
Expand Up @@ -26,10 +26,13 @@
import org.jetbrains.annotations.NotNull;

/**
* @author semancik
* Returns zero set with a single value obtained by resolving given <a href="https://wiki.evolveum.com/display/midPoint/Constants">constant</a>.
* Currently limited to single-valued string constants.
*
* @author semancik
*/
public class ConstExpressionEvaluator<V extends PrismValue, D extends ItemDefinition> extends AbstractExpressionEvaluator<V, D, ConstExpressionEvaluatorType> {
public class ConstExpressionEvaluator<V extends PrismValue, D extends ItemDefinition>
extends AbstractExpressionEvaluator<V, D, ConstExpressionEvaluatorType> {

private final ConstantsManager constantsManager;

Expand All @@ -47,6 +50,7 @@ public PrismValueDeltaSetTriple<V> evaluate(ExpressionEvaluationContext context,
String constName = expressionEvaluatorBean.getValue();
String stringValue = constantsManager.getConstantValue(constName);

//noinspection unchecked
Item<V, D> output = outputDefinition.instantiate();

Object value = ExpressionUtil.convertToOutputValue(stringValue, outputDefinition, protector);
Expand All @@ -55,18 +59,12 @@ public PrismValueDeltaSetTriple<V> evaluate(ExpressionEvaluationContext context,
((PrismProperty<Object>) output).addRealValue(value);
} else {
throw new UnsupportedOperationException(
"Can only generate values of property, not " + output.getClass());
"Can only provide values of property, not " + output.getClass());
}

return ItemDeltaUtil.toDeltaSetTriple(output, null, prismContext);
}

/*
* (non-Javadoc)
*
* @see com.evolveum.midpoint.common.expression.ExpressionEvaluator#
* shortDebugDump()
*/
@Override
public String shortDebugDump() {
return "const:"+ expressionEvaluatorBean.getValue();
Expand Down

0 comments on commit ec25c0a

Please sign in to comment.