Skip to content

Commit

Permalink
Simplify CorrelationService and its implementation
Browse files Browse the repository at this point in the history
Here we also slightly improved the documentation and correlation unit
tests.
  • Loading branch information
mederly committed Aug 15, 2022
1 parent a28e7d1 commit 60b1528
Show file tree
Hide file tree
Showing 17 changed files with 227 additions and 342 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,18 @@
*
* Need not be connected to actual {@link CaseType} object. The term "case" is used more figuratively here, to describe
* a correlation situation that is going to be resolved.
*
* Contains the object being correlated (currently called {@link #preFocus}) and the correlation candidates ({@link #candidates}).
*
* The correlation data are represented as a set of {@link #correlationProperties}, whose values on the source are to be fetched
* directly from {@link #preFocus}, but the valued for candidates are processed into the form of
* {@link CorrelationPropertyValuesDescription}:
*
* - sorted out into "primary" and "secondary" values (corresponding to the main identity and alternative ones),
* - and providing a {@link Match} value that shows the degree of match between the particular candidate and the pre-focus
* on this particular property.
*
* Optionally, there may be a {@link CorrelationExplanation} object for each correlation candidate. (If requested.)
*/
public class CorrelationCaseDescription<F extends FocusType> implements DebugDumpable, Serializable {

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,29 +7,34 @@

package com.evolveum.midpoint.model.api.correlation;

import java.io.Serializable;

import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.jetbrains.annotations.VisibleForTesting;

import com.evolveum.midpoint.model.api.correlation.CorrelationCaseDescription.CandidateDescription;
import com.evolveum.midpoint.model.api.correlator.Correlator;
import com.evolveum.midpoint.model.api.correlator.CorrelatorContext;
import com.evolveum.midpoint.model.api.expr.MidpointFunctions;
import com.evolveum.midpoint.schema.processor.SynchronizationPolicy;
import com.evolveum.midpoint.schema.result.OperationResult;
import com.evolveum.midpoint.task.api.Task;
import com.evolveum.midpoint.util.annotation.Experimental;
import com.evolveum.midpoint.util.exception.*;
import com.evolveum.midpoint.xml.ns._public.common.common_3.*;
import com.evolveum.midpoint.xml.ns._public.common.common_3.CaseType;

/**
* Supports correlation.
* Contains correlation-related methods that should be accessible from the outside of `model` module.
*/
@Experimental
public interface CorrelationService {

/**
* TODO
* Describes the provided correlation case by providing {@link CorrelationCaseDescription} object.
*
* Currently, it
*
* . takes the shadow stored in the correlation case (i.e. does NOT fetch it anew),
* . recomputes inbound mappings (i.e. ignores stored pre-focus),
* . and processes candidate owners stored in the correlation case (i.e. does NOT search for them again).
*
* The {@link CorrelationCaseDescriptionOptions} parameter signals if the client wishes to provide also
* the correlation explanation, or not. (In the future, we may provide options also for behavior in points 1-3
* mentioned above.)
*/
@NotNull CorrelationCaseDescription<?> describeCorrelationCase(
@NotNull CaseType aCase,
Expand All @@ -39,16 +44,6 @@ public interface CorrelationService {
throws SchemaException, ConfigurationException, ExpressionEvaluationException, CommunicationException,
SecurityViolationException, ObjectNotFoundException;

/**
* Instantiates the correlator for given correlation case. TODO remove this method - hide the logic in the service impl
*/
Correlator instantiateCorrelator(
@NotNull CaseType aCase,
@NotNull Task task,
@NotNull OperationResult result)
throws SchemaException, ConfigurationException, ExpressionEvaluationException, CommunicationException,
SecurityViolationException, ObjectNotFoundException;

/**
* Completes given correlation case.
*
Expand All @@ -65,103 +60,19 @@ void completeCorrelationCase(
throws SchemaException, ExpressionEvaluationException, CommunicationException, SecurityViolationException,
ConfigurationException, ObjectNotFoundException;

/**
* Creates the root correlator context for given configuration.
*/
@NotNull CorrelatorContext<?> createRootCorrelatorContext(
@NotNull SynchronizationPolicy synchronizationPolicy,
@Nullable ObjectTemplateType objectTemplate,
@Nullable SystemConfigurationType systemConfiguration) throws ConfigurationException, SchemaException;

/**
* Clears the correlation state of a shadow.
*
* Does not do unlinking (if the shadow is linked)!
*/
void clearCorrelationState(@NotNull String shadowOid, @NotNull OperationResult result) throws ObjectNotFoundException;

/**
* Executes the correlation in the standard way.
*/
@NotNull CompleteCorrelationResult correlate(
@NotNull CorrelatorContext<?> rootCorrelatorContext,
@NotNull CorrelationContext correlationContext,
@NotNull OperationResult result)
throws SchemaException, ExpressionEvaluationException, CommunicationException, SecurityViolationException,
ConfigurationException, ObjectNotFoundException;

/**
* Executes the correlation for a given shadow + pre-focus.
*
* This is _not_ the standard use of the correlation, though.
* (Note that it lacks e.g. the resource object delta information.)
*/
@VisibleForTesting
@NotNull CompleteCorrelationResult correlate(
@NotNull ShadowType shadow,
@Nullable FocusType preFocus,
@NotNull Task task,
@NotNull OperationResult result)
throws SchemaException, ExpressionEvaluationException, CommunicationException, SecurityViolationException,
ConfigurationException, ObjectNotFoundException;

/**
* Executes the correlation for a given shadow. (By computing pre-focus first.)
*
* This is _not_ the standard use. It lacks e.g. the resource object delta information. It is used in special cases like
* {@link MidpointFunctions#findCandidateOwners(Class, ShadowType, String, ShadowKindType, String)}.
*/
@NotNull CompleteCorrelationResult correlate(
@NotNull ShadowType shadowedResourceObject,
@NotNull ResourceType resource,
@NotNull SynchronizationPolicy synchronizationPolicy,
@NotNull Class<? extends FocusType> focusType,
@NotNull Task task,
@NotNull OperationResult result)
throws SchemaException, ExpressionEvaluationException, SecurityViolationException, CommunicationException,
ConfigurationException, ObjectNotFoundException;

/**
* Checks whether the supplied candidate owner would be the correlation result (if real correlation would take place).
* Used for opportunistic synchronization.
*
* Why not doing the actual correlation? Because the owner may not exist in repository yet.
*/
boolean checkCandidateOwner(
@NotNull ShadowType shadowedResourceObject,
@NotNull ResourceType resource,
@NotNull SynchronizationPolicy synchronizationPolicy,
@NotNull FocusType candidateOwner,
@NotNull Task task,
@NotNull OperationResult result)
throws SchemaException, ExpressionEvaluationException, SecurityViolationException, CommunicationException,
ConfigurationException, ObjectNotFoundException;

/** TEMPORARY!!! */
ObjectTemplateType determineObjectTemplate(
@NotNull SynchronizationPolicy synchronizationPolicy,
@NotNull FocusType preFocus,
@NotNull OperationResult result)
throws SchemaException, ConfigurationException, ObjectNotFoundException;

/** TODO Maybe temporary. Maybe visible for testing? */
@NotNull <F extends FocusType> F computePreFocus(
@NotNull ShadowType shadowedResourceObject,
@NotNull ResourceType resource,
@NotNull SynchronizationPolicy synchronizationPolicy,
@NotNull Class<F> focusClass,
@NotNull Task task,
@NotNull OperationResult result)
throws SchemaException, ExpressionEvaluationException, SecurityViolationException, CommunicationException,
ConfigurationException, ObjectNotFoundException;

@FunctionalInterface
interface CaseCloser {
/** Closes the case in repository. */
void closeCaseInRepository(OperationResult result) throws ObjectNotFoundException;
}

class CorrelationCaseDescriptionOptions {
/**
* Options for {@link #describeCorrelationCase(CaseType, CorrelationCaseDescriptionOptions, Task, OperationResult)} method.
*
* TODO Make also other parts of the method behavior configurable.
*/
class CorrelationCaseDescriptionOptions implements Serializable {

/** Whether to explain the correlation. See {@link CandidateDescription#explanation}. */
private boolean explain;

Expand All @@ -178,10 +89,15 @@ public CorrelationCaseDescriptionOptions explain(boolean value) {
return this;
}

// TODO: whether to do the correlation anew

public static boolean isExplain(CorrelationCaseDescriptionOptions options) {
return options != null && options.explain;
}

@Override
public String toString() {
return "CorrelationCaseDescriptionOptions{" +
"explain=" + explain +
'}';
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,8 @@

/**
* Describes how the correlator (could) came to a given candidate owner, and the specific confidence value of it.
*
* There are subtypes of this class for particular correlators, with the special cases for generic and unsupported correlators.
*/
public abstract class CorrelationExplanation implements Serializable, DebugDumpable {

Expand Down Expand Up @@ -45,7 +47,10 @@ public double getConfidence() {
return confidence;
}

/** For correlators that support candidate check but not the specific explanation. */
/**
* This is for correlators that support candidate check (i.e., determining confidence for the provided candidate)
* but do not provide any the specific explanations of their decisions.
*/
public static class GenericCorrelationExplanation extends CorrelationExplanation {

public GenericCorrelationExplanation(@NotNull CorrelatorConfiguration correlatorConfiguration, double confidence) {
Expand All @@ -57,7 +62,10 @@ void doSpecificDebugDump(StringBuilder sb, int indent) {
}
}

/** For correlators that do not support neither explanation nor candidate check. */
/**
* This is for correlators that do not support neither explanation nor candidate check (i.e., determining confidence
* for the provided candidate).
*/
public static class UnsupportedCorrelationExplanation extends CorrelationExplanation {

public UnsupportedCorrelationExplanation(@NotNull CorrelatorConfiguration correlatorConfiguration) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,21 +7,14 @@

package com.evolveum.midpoint.model.api.correlator;

import com.evolveum.midpoint.model.api.correlation.CompleteCorrelationResult;
import com.evolveum.midpoint.model.api.correlation.CorrelationContext;
import com.evolveum.midpoint.model.api.correlation.CorrelationService;
import com.evolveum.midpoint.schema.processor.SynchronizationPolicy;
import com.evolveum.midpoint.task.api.Task;
import com.evolveum.midpoint.util.DebugDumpable;
import com.evolveum.midpoint.util.DebugUtil;
import com.evolveum.midpoint.xml.ns._public.common.common_3.ResourceType;
import com.evolveum.midpoint.xml.ns._public.common.common_3.ShadowType;
import java.io.Serializable;

import org.jetbrains.annotations.NotNull;

import com.evolveum.midpoint.model.api.correlation.CorrelationContext;
import com.evolveum.midpoint.schema.result.OperationResult;

import java.io.Serializable;
import com.evolveum.midpoint.util.DebugDumpable;
import com.evolveum.midpoint.util.DebugUtil;

/**
* Result of the correlation at the level of {@link Correlator}, i.e. the return value of
Expand All @@ -30,9 +23,6 @@
* *Does not* deal with the question "who is the owner".
* It simply provides a list of candidates with appropriate confidence values.
*
* The full result of correlation (as returned from {@link CorrelationService#correlate(ShadowType, ResourceType,
* SynchronizationPolicy, Class, Task, OperationResult)}) is provided by {@link CompleteCorrelationResult} class.
*
* TODO better name?
*/
public class CorrelationResult implements Serializable, DebugDumpable {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -153,7 +153,9 @@ private void setupCandidates(OperationResult result)
CorrelationExplanation explanation;
if (explain) {
explanation =
beans.correlationServiceImpl.explain(candidate, correlatorContext, correlationContext, task, result);
beans.correlatorFactoryRegistry
.instantiateCorrelator(correlatorContext, task, result)
.explain(correlationContext, candidate, result);
confidence = explanation.getConfidence();
} else {
explanation = null;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -66,15 +66,11 @@ public class CorrelationCaseManager {

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

private static final String OP_PERFORM_COMPLETION_IN_CORRELATOR =
CorrelationCaseManager.class.getName() + ".performCompletionInCorrelator";

@Autowired @Qualifier("cacheRepositoryService") private RepositoryService repositoryService;
@Autowired private ModelService modelService;
@Autowired private PrismContext prismContext;
@Autowired private Clock clock;
@Autowired private CorrelationService correlationService;
@Autowired private ProvisioningService provisioningService;
@Autowired private CorrelationServiceImpl correlationService;
@Autowired private CaseEventDispatcher caseEventDispatcher;
@Autowired private SystemObjectCache systemObjectCache;
@Autowired(required = false) private CaseManager caseManager;
Expand Down Expand Up @@ -278,7 +274,7 @@ public void closeCaseIfStillOpen(
* - case is freshly fetched,
* - case is a correlation one
*/
public void completeCorrelationCase(
void completeCorrelationCase(
@NotNull CaseType aCase,
@NotNull CorrelationService.CaseCloser caseCloser,
@NotNull Task task,
Expand All @@ -292,13 +288,9 @@ public void completeCorrelationCase(
return;
}

Correlator correlator = correlationService.instantiateCorrelator(aCase, task, result);

recordCaseCompletionInShadow(aCase, task, result);

caseCloser.closeCaseInRepository(result);

performCompletionInCorrelator(aCase, outcomeUri, correlator, task, result);
correlationService.resolve(aCase, task, result);

// As a convenience, we try to re-import the object. Technically this is not a part of the correlation case processing.
// Whether we do this should be made configurable (in the future).
Expand Down Expand Up @@ -391,39 +383,4 @@ private boolean hasOutcomeUri(@NotNull CaseWorkItemType workItem, @NotNull Strin
return workItem.getOutput() != null
&& outcomeUri.equals(workItem.getOutput().getOutcome());
}

private void performCompletionInCorrelator(
@NotNull CaseType aCase,
@NotNull String outcomeUri,
@NotNull Correlator correlator,
@NotNull Task task,
@NotNull OperationResult parentResult)
throws SchemaException, ConfigurationException, ExpressionEvaluationException, CommunicationException,
SecurityViolationException, ObjectNotFoundException {
OperationResult result = parentResult.createSubresult(OP_PERFORM_COMPLETION_IN_CORRELATOR);
try {
applyShadowDefinition(aCase, task, result);
correlator.resolve(aCase, outcomeUri, task, result);
} catch (Throwable t) {
result.recordFatalError(t);
throw t;
} finally {
result.close();
}
}

/**
* Applies the correct definition to the shadow embedded in the case.targetRef.
*/
private void applyShadowDefinition(CaseType aCase, Task task, OperationResult result)
throws SchemaException, ExpressionEvaluationException, CommunicationException, ConfigurationException,
ObjectNotFoundException {
ShadowType shadow =
MiscUtil.requireNonNull(
MiscUtil.castSafely(
ObjectTypeUtil.getObjectFromReference(aCase.getTargetRef()),
ShadowType.class),
() -> "No embedded shadow in " + aCase);
provisioningService.applyDefinition(shadow.asPrismObject(), task, result);
}
}

0 comments on commit 60b1528

Please sign in to comment.