Skip to content

Commit

Permalink
Add dead links to dead shadows
Browse files Browse the repository at this point in the history
BEHAVIOR CHANGE: Links to dead shadows are kept in focal objects
as long as these shadows physically exist in repo. Such links are
considered "dead" and are distinguished from live ones by setting
relation to org:related (instead of org:default).

These inactive links are treated in a special way during context
loading:
1. when present in deltas, they are ignored;
2. when present in focus, they are almost ignored. No projection
contexts are created, but noFetch get is issued (in order to assure
fast shadow refresh).

Related changes:
- Methods for linkRef assertions in tests were adapted.
- Link maintenance was factored out to LinkUpdater class.
- Cleaned up SynchronizationServiceImpl code a bit.
- Refactored and cleaned ChangeExecutor code significantly.

Unrelated changes:
- Fixed treatment of conflicting shadow with the same owner
in ProjectionValuesProcessor.

Work in progress. Some tests still fail, e.g. because the dead links
vs. dead shadow correspondence is not 100% yet.

Related to MID-6862.
  • Loading branch information
mederly committed Mar 12, 2021
1 parent f1e4891 commit 3a0f17a
Show file tree
Hide file tree
Showing 163 changed files with 4,299 additions and 3,725 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -186,24 +186,21 @@ public void setAbortRequested(boolean value) {
this.abortRequested = value;
}

private void addExpectedStatusItems(List<ProgressReportActivityDto> progressReportActivities, ModelContext modelContext) {
private void addExpectedStatusItems(List<ProgressReportActivityDto> progressReportActivities, ModelContext<?> modelContext) {
if (modelContext.getFocusContext() != null) {
ModelElementContext fc = modelContext.getFocusContext();
ModelElementContext<?> fc = modelContext.getFocusContext();
if (isNotEmpty(fc.getPrimaryDelta()) || isNotEmpty(fc.getSecondaryDelta())) {
ProgressInformation modelStatus = new ProgressInformation(FOCUS_OPERATION, (ProgressInformation.StateType) null);
if (findRelevantStatusItem(progressReportActivities, modelStatus) == null) {
progressReportActivities.add(createStatusItem(modelStatus, modelContext));
}
}
}
if (modelContext.getProjectionContexts() != null) {
Collection<ModelProjectionContext> projectionContexts = modelContext.getProjectionContexts();
for (ModelProjectionContext mpc : projectionContexts) {
ProgressInformation projectionStatus = new ProgressInformation(RESOURCE_OBJECT_OPERATION,
mpc.getResourceShadowDiscriminator(), (ProgressInformation.StateType) null);
if (findRelevantStatusItem(progressReportActivities, projectionStatus) == null) {
progressReportActivities.add(createStatusItem(projectionStatus, modelContext));
}
for (ModelProjectionContext mpc : modelContext.getProjectionContexts()) {
ProgressInformation projectionStatus = new ProgressInformation(RESOURCE_OBJECT_OPERATION,
mpc.getResourceShadowDiscriminator(), (ProgressInformation.StateType) null);
if (findRelevantStatusItem(progressReportActivities, projectionStatus) == null) {
progressReportActivities.add(createStatusItem(projectionStatus, modelContext));
}
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,8 @@
import java.util.stream.Collectors;
import javax.xml.namespace.QName;

import com.evolveum.midpoint.util.annotation.Experimental;

import org.jetbrains.annotations.NotNull;

import com.evolveum.midpoint.prism.delta.ChangeType;
Expand Down Expand Up @@ -185,4 +187,10 @@ default boolean isOfType(@NotNull Class<?> type) {
Class<O> compileTimeClass = getCompileTimeClass();
return compileTimeClass != null && type.isAssignableFrom(compileTimeClass);
}

@Experimental
static <T extends Objectable> PrismObject<T> cast(PrismObject<?> object, Class<T> type) {
//noinspection unchecked
return (PrismObject<T>) object;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,10 @@ public interface S_ConditionEntry {
* Creates filter matching oid and/or targetTypeName, any of them optional.
* If both are null the result is the same like {@link #isNull()} (null ref OID matches).
*/
S_AtomicFilterExit ref(@Nullable String oid, @Nullable QName targetTypeName);
default S_AtomicFilterExit ref(@Nullable String oid, @Nullable QName targetTypeName) {
return ref(oid, targetTypeName, null);
}

S_AtomicFilterExit ref(@Nullable String oid, @Nullable QName targetTypeName, @Nullable QName relation);
S_AtomicFilterExit isNull();
}
Original file line number Diff line number Diff line change
Expand Up @@ -295,11 +295,13 @@ public S_AtomicFilterExit ref(String... oids) {
}

@Override
public S_AtomicFilterExit ref(@Nullable String oid, @Nullable QName targetTypeName) {
if (oid == null && targetTypeName == null) {
public S_AtomicFilterExit ref(@Nullable String oid, @Nullable QName targetTypeName, @Nullable QName relation) {
if (oid == null && targetTypeName == null && relation == null) {
return isNull();
} else {
return ref(new PrismReferenceValueImpl(oid, targetTypeName));
PrismReferenceValueImpl value = new PrismReferenceValueImpl(oid, targetTypeName);
value.setRelation(relation);
return ref(value);
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,25 +8,16 @@

import java.util.ArrayList;
import java.util.List;
import java.util.stream.Collectors;

import javax.xml.namespace.QName;

import com.evolveum.midpoint.prism.PrismContext;
import com.evolveum.midpoint.prism.PrismObject;
import com.evolveum.midpoint.schema.RelationRegistry;
import com.evolveum.midpoint.schema.SchemaService;
import com.evolveum.midpoint.schema.constants.SchemaConstants;
import com.evolveum.midpoint.xml.ns._public.common.common_3.AssignmentSelectorType;
import com.evolveum.midpoint.xml.ns._public.common.common_3.AssignmentType;
import com.evolveum.midpoint.xml.ns._public.common.common_3.ConstructionType;
import com.evolveum.midpoint.xml.ns._public.common.common_3.CredentialsType;
import com.evolveum.midpoint.xml.ns._public.common.common_3.ObjectReferenceType;
import com.evolveum.midpoint.xml.ns._public.common.common_3.ObjectType;
import com.evolveum.midpoint.xml.ns._public.common.common_3.OrderConstraintsType;
import com.evolveum.midpoint.xml.ns._public.common.common_3.OrgType;
import com.evolveum.midpoint.xml.ns._public.common.common_3.PasswordType;
import com.evolveum.midpoint.xml.ns._public.common.common_3.RoleType;
import com.evolveum.midpoint.xml.ns._public.common.common_3.ServiceType;
import com.evolveum.midpoint.xml.ns._public.common.common_3.ShadowKindType;
import com.evolveum.midpoint.xml.ns._public.common.common_3.UserType;
import com.evolveum.midpoint.xml.ns._public.common.common_3.*;
import com.evolveum.prism.xml.ns._public.types_3.ProtectedStringType;
import org.jetbrains.annotations.NotNull;

Expand Down Expand Up @@ -207,11 +198,8 @@ public static <O extends ObjectType> List<String> determineSubTypes(PrismObject<
}

public static <O extends ObjectType> boolean hasSubtype(PrismObject<O> object, String subtype) {
List<String> objectSubtypes = determineSubTypes(object);
if (objectSubtypes == null) {
return false;
}
return objectSubtypes.contains(subtype);
return determineSubTypes(object)
.contains(subtype);
}

public static <O extends ObjectType> void setSubtype(PrismObject<O> object, List<String> subtypes) {
Expand All @@ -224,4 +212,12 @@ public static <O extends ObjectType> void setSubtype(PrismObject<O> object, Lis
objSubtypes.addAll(subtypes);
}
}

@NotNull
public static <F extends FocusType> List<ObjectReferenceType> getLiveLinkRefs(F focus) {
RelationRegistry relationRegistry = SchemaService.get().relationRegistry();
return focus.getLinkRef().stream()
.filter(ref -> relationRegistry.isMember(ref.getRelation()))
.collect(Collectors.toList());
}
}
17 changes: 15 additions & 2 deletions infra/util/src/main/java/com/evolveum/midpoint/util/MiscUtil.java
Original file line number Diff line number Diff line change
Expand Up @@ -87,8 +87,8 @@ public static <T> Collection<? extends T> unionExtends(Collection<? extends T>..
return resultSet;
}

public static <T> boolean unorderedCollectionEquals(Collection<T> a, Collection<T> b) {
return unorderedCollectionEquals(a, b, (xa, xb) -> xa.equals(xb));
public static <T> boolean unorderedCollectionEquals(Collection<? extends T> a, Collection<? extends T> b) {
return unorderedCollectionEquals(a, b, Object::equals);
}

/**
Expand Down Expand Up @@ -963,6 +963,19 @@ public static <T> T requireNonNull(T value, Supplier<String> messageSupplier) th
}
}

@FunctionalInterface
public interface ExceptionSupplier<E> {
E get();
}

public static <T, E extends Exception> T requireNonNull(T value, ExceptionSupplier<E> exceptionSupplier) throws E {
if (value != null) {
return value;
} else {
throw exceptionSupplier.get();
}
}

public static void checkCollectionImmutable(Collection<?> collection) {
try {
collection.add(null);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@ public interface ModelContext<F extends ObjectType> extends Serializable, DebugD
@NotNull
ModelElementContext<F> getFocusContextRequired();

@NotNull
Collection<? extends ModelProjectionContext> getProjectionContexts();

ModelProjectionContext findProjectionContext(ResourceShadowDiscriminator rat);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,9 @@ public enum SynchronizationIntent {
KEEP,

/**
* Existing account that should be unlinked (but NOT deleted)
* Existing account that should be unlinked (but NOT deleted). By unlinking we mean either physically removing
* a value from `linkRef` (if shadow does not exist any more), or changing the relation from `org:default` to
* `org:related`.
*/
UNLINK,

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,9 @@ public enum SynchronizationPolicyDecision {
KEEP,

/**
* Existing account that is going to be unlinked (but NOT deleted)
* Existing account that should be unlinked (but NOT deleted). By unlinking we mean either physically removing
* a value from `linkRef` (if shadow does not exist any more), or changing the relation from `org:default` to
* `org:related`.
*/
UNLINK,

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1116,8 +1116,8 @@ public ModelElementContext<UserType> getFocusContext() {
}

@Override
public Collection<? extends ModelProjectionContext> getProjectionContexts() {
return null;
public @NotNull Collection<? extends ModelProjectionContext> getProjectionContexts() {
return Collections.emptyList();
}

@Override
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,15 +8,28 @@
package com.evolveum.midpoint.model.impl;

import com.evolveum.midpoint.common.ActivationComputer;
import com.evolveum.midpoint.common.Clock;
import com.evolveum.midpoint.model.common.ModelCommonBeans;

import com.evolveum.midpoint.model.impl.lens.ClockworkConflictResolver;
import com.evolveum.midpoint.model.impl.lens.ClockworkMedic;
import com.evolveum.midpoint.model.impl.lens.OperationalDataManager;
import com.evolveum.midpoint.model.impl.lens.projector.ContextLoader;
import com.evolveum.midpoint.model.impl.lens.projector.credentials.CredentialsProcessor;
import com.evolveum.midpoint.model.impl.lens.projector.focus.ProjectionValueMetadataCreator;
import com.evolveum.midpoint.prism.crypto.Protector;
import com.evolveum.midpoint.provisioning.api.ProvisioningService;

import com.evolveum.midpoint.repo.common.expression.ExpressionFactory;
import com.evolveum.midpoint.schema.SchemaService;

import com.evolveum.midpoint.security.api.SecurityContextManager;
import com.evolveum.midpoint.security.enforcer.api.SecurityEnforcer;

import com.evolveum.midpoint.task.api.TaskManager;

import com.evolveum.midpoint.wf.api.WorkflowManager;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.stereotype.Component;
Expand Down Expand Up @@ -54,6 +67,7 @@ public static ModelBeans get() {
}

@Autowired public PrismContext prismContext;
@Autowired public SchemaService schemaService;
@Autowired public ModelObjectResolver modelObjectResolver;
@Autowired @Qualifier("cacheRepositoryService") public RepositoryService cacheRepositoryService;
@Autowired public MatchingRuleRegistry matchingRuleRegistry;
Expand All @@ -68,4 +82,12 @@ public static ModelBeans get() {
@Autowired public ProvisioningService provisioningService;
@Autowired public ProjectionValueMetadataCreator projectionValueMetadataCreator;
@Autowired public ActivationComputer activationComputer;
@Autowired public Clock clock;
@Autowired public SecurityEnforcer securityEnforcer;
@Autowired public SecurityContextManager securityContextManager;
@Autowired public OperationalDataManager metadataManager;
@Autowired public TaskManager taskManager;
@Autowired public ExpressionFactory expressionFactory;
@Autowired(required = false) public WorkflowManager workflowManager; // not available e.g. during tests
@Autowired public ClockworkConflictResolver clockworkConflictResolver;
}
Original file line number Diff line number Diff line change
Expand Up @@ -307,8 +307,8 @@ public <F extends ObjectType> boolean hasLinkedAccount(String resourceOid) {
for (ResourceShadowDiscriminator deletedOne : ctx.getHistoricResourceObjects()) {
if (resourceOid.equals(deletedOne.getResourceOid()) && deletedOne.getKind() == ShadowKindType.ACCOUNT
&& deletedOne.getIntent() == null || "default"
.equals(deletedOne.getIntent())) { // TODO implement this seriously
LOGGER.trace("Found deleted one: {}", deletedOne); // TODO remove
.equals(deletedOne.getIntent())) { // TODO implement this seriously
LOGGER.trace("Found deleted one: {}", deletedOne); // TODO remove
return true;
}
}
Expand Down Expand Up @@ -1830,8 +1830,9 @@ public <F extends FocusType> List<F> getFocusesByCorrelationRule(Class<F> type,
discriminator.setKind(kind);
discriminator.setIntent(intent);

SynchronizationContext<F> syncCtx = new SynchronizationContext<>(shadow.asPrismObject(), shadow.asPrismObject(),
null, resource.asPrismObject(), getCurrentTask().getChannel(), getPrismContext(), expressionFactory, getCurrentTask(), null);
SynchronizationContext<F> syncCtx = new SynchronizationContext<>(shadow.asPrismObject(), null,
resource.asPrismObject(), getCurrentTask().getChannel(), getPrismContext(), expressionFactory,
getCurrentTask(), null);

ObjectSynchronizationType applicablePolicy = null;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -323,13 +323,16 @@ private PrismObject<ShadowType> fetchShadow(ShadowCheckResult checkResult, Prism
return taskHandler.getProvisioningService().getObject(ShadowType.class, shadow.getOid(),
SelectorOptions.createCollection(GetOperationOptions.createDoNotDiscovery()),
task, result);
} catch (ObjectNotFoundException | CommunicationException | SchemaException | ConfigurationException | SecurityViolationException | ExpressionEvaluationException | RuntimeException | Error e) {
checkResult.recordError(ShadowStatistics.CANNOT_FETCH_RESOURCE_OBJECT, new SystemException("The resource object couldn't be fetched", e));
} catch (ObjectNotFoundException | CommunicationException | SchemaException | ConfigurationException |
SecurityViolationException | ExpressionEvaluationException | RuntimeException | Error e) {
checkResult.recordError(ShadowStatistics.CANNOT_FETCH_RESOURCE_OBJECT,
new SystemException("The resource object couldn't be fetched", e));
return null;
}
}

private void doFixIntent(ShadowCheckResult checkResult, PrismObject<ShadowType> fetchedShadow, PrismObject<ShadowType> shadow, PrismObject<ResourceType> resource, Task task, OperationResult result) throws SchemaException {
private void doFixIntent(ShadowCheckResult checkResult, PrismObject<ShadowType> fetchedShadow, PrismObject<ShadowType> shadow,
PrismObject<ResourceType> resource, Task task, OperationResult result) throws SchemaException {
PrismObject<ShadowType> fullShadow;

if (!getConfiguration().checkFetch) {
Expand All @@ -345,8 +348,8 @@ private void doFixIntent(ShadowCheckResult checkResult, PrismObject<ShadowType>
SynchronizationContext<? extends FocusType> syncCtx;
try {
syncCtx = taskHandler.getSynchronizationService()
.loadSynchronizationContext(fullShadow, fullShadow, null, resource, task.getChannel(),
null, taskHandler.getSystemObjectsCache().getSystemConfiguration(result), task, result);
.loadSynchronizationContext(fullShadow, null, resource, task.getChannel(),
null, null, task, result);
} catch (SchemaException | ObjectNotFoundException | ExpressionEvaluationException | RuntimeException | CommunicationException | ConfigurationException | SecurityViolationException e) {
checkResult.recordError(ShadowStatistics.CANNOT_APPLY_FIX, new SystemException("Couldn't prepare fix for missing intent, because the synchronization policy couldn't be determined", e));
return;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
import com.evolveum.midpoint.provisioning.api.ResourceOperationListener;
import com.evolveum.midpoint.schema.result.OperationResult;
import com.evolveum.midpoint.task.api.Task;
import org.jetbrains.annotations.NotNull;

/**
* @author Pavol Mederly
Expand All @@ -27,7 +28,7 @@ public AssociationSearchExpressionCacheInvalidator(AssociationSearchExpressionEv
}

@Override
public void notifyChange(ResourceObjectShadowChangeDescription change, Task task, OperationResult parentResult) {
public void notifyChange(@NotNull ResourceObjectShadowChangeDescription change, Task task, OperationResult parentResult) {
cache.invalidate(change.getResource(), change.getShadowedResourceObject());
}

Expand Down

0 comments on commit 3a0f17a

Please sign in to comment.