From ff776d954abda93c0e28a12761dfd1b804ba96de Mon Sep 17 00:00:00 2001 From: Radovan Semancik Date: Mon, 13 Aug 2018 15:56:54 +0200 Subject: [PATCH] Consistency update: fixing model-impl tests --- .../schema/ResourceShadowDiscriminator.java | 38 ++++++------- .../model/impl/lens/ChangeExecutor.java | 14 ++++- .../impl/lens/LensProjectionContext.java | 26 +++++++-- .../impl/lens/projector/ContextLoader.java | 32 +++++++---- .../lens/projector/DependencyProcessor.java | 4 +- .../projector/ShadowConstraintsChecker.java | 4 +- .../impl/sync/SynchronizationContext.java | 25 ++++++++- .../impl/sync/SynchronizationServiceImpl.java | 2 + .../AbstractInternalModelIntegrationTest.java | 4 ++ .../model/impl/lens/TestReconScript.java | 4 +- .../impl/sync/TestSynchronizationService.java | 54 ++++++++++++++----- .../src/test/resources/logback-test.xml | 3 +- .../provisioning/impl/ShadowManager.java | 3 ++ .../test/AbstractIntegrationTest.java | 11 ++++ 14 files changed, 167 insertions(+), 57 deletions(-) diff --git a/infra/schema/src/main/java/com/evolveum/midpoint/schema/ResourceShadowDiscriminator.java b/infra/schema/src/main/java/com/evolveum/midpoint/schema/ResourceShadowDiscriminator.java index 141d6a107d1..3890e5225c2 100644 --- a/infra/schema/src/main/java/com/evolveum/midpoint/schema/ResourceShadowDiscriminator.java +++ b/infra/schema/src/main/java/com/evolveum/midpoint/schema/ResourceShadowDiscriminator.java @@ -30,7 +30,7 @@ import com.evolveum.midpoint.xml.ns._public.common.common_3.ShadowKindType; /** - * Aggregate bean containing resource OID, intent and thombstone flag. + * Aggregate bean containing resource OID, intent and tombstone flag. * It uniquely identifies an shadow projection (usually account) for a specific user regardless whether it has OID, does not have * OID yet, it exists of was deleted. * @@ -38,7 +38,7 @@ * * TODO: split to two objects: * 1: ResourceShadowCoordinates which will stay in common - * 2: ResourceShadowDiscriminator (subclass) which will go to model. This will contains thombstone and order. + * 2: ResourceShadowDiscriminator (subclass) which will go to model. This will contains tombstone and order. * * @author Radovan Semancik */ @@ -49,12 +49,12 @@ public class ResourceShadowDiscriminator implements Serializable, DebugDumpable, private ShadowKindType kind = ShadowKindType.ACCOUNT; private String intent; private QName objectClass; - private boolean thombstone; + private boolean tombstone; private int order = 0; - public ResourceShadowDiscriminator(String resourceOid, ShadowKindType kind, String intent, boolean thombstone) { + public ResourceShadowDiscriminator(String resourceOid, ShadowKindType kind, String intent, boolean tombstone) { this.resourceOid = resourceOid; - this.thombstone = thombstone; + this.tombstone = tombstone; setIntent(intent); setKind(kind); } @@ -78,7 +78,7 @@ public ResourceShadowDiscriminator(ShadowDiscriminatorType accRefType, String de } else { this.resourceOid = accRefType.getResourceRef().getOid(); } - this.thombstone = false; + this.tombstone = false; setIntent(accRefType.getIntent()); setKind(kind); } @@ -133,18 +133,18 @@ public void setOrder(int order) { } /** - * Thumbstone flag is true: the account no longer exists. The data we have are the latest metadata we were able to get. - * The projection will be marked as thombstone if we discover that the associated resource object is gone. Or the shadow - * is gone and we can no longer associate the resource object. In any way the thombstoned projection is marked for removal. + * Tombstone flag is true: the account no longer exists. The data we have are the latest metadata we were able to get. + * The projection will be marked as tombstone if we discover that the associated resource object is gone. Or the shadow + * is gone and we can no longer associate the resource object. In any way the tombstoned projection is marked for removal. * It will be eventually unlinked and the shadow will be deleted. The shadow may stay around in the "dead" state for * some time for reporting purposes. */ - public boolean isThombstone() { - return thombstone; + public boolean isTombstone() { + return tombstone; } - public void setThombstone(boolean thombstone) { - this.thombstone = thombstone; + public void setTombstone(boolean tombstone) { + this.tombstone = tombstone; } public boolean isWildcard() { @@ -189,7 +189,7 @@ public int hashCode() { result = prime * result + order; result = prime * result + ((resourceOid == null) ? 0 : resourceOid.hashCode()); - result = prime * result + (thombstone ? 1231 : 1237); + result = prime * result + (tombstone ? 1231 : 1237); return result; } @@ -216,7 +216,7 @@ public boolean equals(Object obj) { return false; } else if (!resourceOid.equals(other.resourceOid)) return false; - if (thombstone != other.thombstone) + if (tombstone != other.tombstone) return false; return true; } @@ -242,7 +242,7 @@ public boolean equivalent(Object obj) { return false; } else if (!resourceOid.equals(other.resourceOid)) return false; - if (thombstone != other.thombstone) + if (tombstone != other.tombstone) return false; return true; } @@ -273,8 +273,8 @@ public String toHumanReadableDescription() { sb.append(" order="); sb.append(order); } - if (thombstone) { - sb.append(" THOMBSTONE"); + if (tombstone) { + sb.append(" TOMBSTONE"); } sb.append(")"); return sb.toString(); @@ -289,7 +289,7 @@ public String debugDump(int indent) { DebugUtil.debugDumpWithLabelToStringLn(sb, "kind", kind, indent + 1); DebugUtil.debugDumpWithLabelLn(sb, "intent", indent, indent + 1); DebugUtil.debugDumpWithLabelLn(sb, "objectClass", objectClass, indent + 1); - DebugUtil.debugDumpWithLabelLn(sb, "thombstone", thombstone, indent + 1); + DebugUtil.debugDumpWithLabelLn(sb, "tombstone", tombstone, indent + 1); DebugUtil.debugDumpWithLabel(sb, "order", order, indent + 1); return sb.toString(); } diff --git a/model/model-impl/src/main/java/com/evolveum/midpoint/model/impl/lens/ChangeExecutor.java b/model/model-impl/src/main/java/com/evolveum/midpoint/model/impl/lens/ChangeExecutor.java index 551f7cbcbe4..14ad29932f9 100644 --- a/model/model-impl/src/main/java/com/evolveum/midpoint/model/impl/lens/ChangeExecutor.java +++ b/model/model-impl/src/main/java/com/evolveum/midpoint/model/impl/lens/ChangeExecutor.java @@ -674,6 +674,10 @@ private void updateLinks( } private boolean linkShouldExist(LensProjectionContext projCtx, PrismObject shadowAfterModification, OperationResult result) { + if (!projCtx.isShadowExistsInRepo()) { + // Nothing to link to + return false; + } if (projCtx.getSynchronizationPolicyDecision() == SynchronizationPolicyDecision.UNLINK) { return false; } @@ -696,7 +700,7 @@ private boolean linkShouldExist(LensProjectionContext projCtx, PrismObject void updateSituationInShadow(Task task, return; } + SynchronizationSituationType currentSynchronizationSituation = currentShadow.asObjectable().getSynchronizationSituation(); + if (SynchronizationSituationType.DELETED.equals(currentSynchronizationSituation) && ShadowUtil.isDead(currentShadow.asObjectable())) { + LOGGER.trace("Skipping update of synchronization situation for deleted dead shadow"); + result.recordSuccess(); + return; + } + // TODO: can we skip the update or cannot we? Do we always need to update sync timestamp? -// SynchronizationSituationType currentSynchronizationSituation = currentShadow.asObjectable().getSynchronizationSituation(); // if (newSituation.equals(currentSynchronizationSituation)) { // LOGGER.trace("Skipping update of synchronization situation because there is no change ({})", currentSynchronizationSituation); // result.recordSuccess(); diff --git a/model/model-impl/src/main/java/com/evolveum/midpoint/model/impl/lens/LensProjectionContext.java b/model/model-impl/src/main/java/com/evolveum/midpoint/model/impl/lens/LensProjectionContext.java index 2a1fe937e6d..0822db288c4 100644 --- a/model/model-impl/src/main/java/com/evolveum/midpoint/model/impl/lens/LensProjectionContext.java +++ b/model/model-impl/src/main/java/com/evolveum/midpoint/model/impl/lens/LensProjectionContext.java @@ -119,6 +119,13 @@ public class LensProjectionContext extends LensElementContext implem * isExists will in fact be true. */ private boolean isExists; + + /** + * True if shadow exists in the repo. It is set to false after projector discovers that a shadow is gone. + * This is a corner case, but it may happen: if shadow is unintentionally deleted, if the shadow is + * cleaned up by another thread and so on. + */ + private transient boolean shadowExistsInRepo = true; /** * Decision regarding the account. It indicated what the engine has DECIDED TO DO with the context. @@ -340,7 +347,7 @@ public boolean compareResourceShadowDiscriminator(ResourceShadowDiscriminator rs if (!rsd.getKind().equals(resourceShadowDiscriminator.getKind())) { return false; } - if (rsd.isThombstone() != resourceShadowDiscriminator.isThombstone()) { + if (rsd.isTombstone() != resourceShadowDiscriminator.isTombstone()) { return false; } if (rsd.getIntent() == null) { @@ -366,7 +373,7 @@ public boolean isThombstone() { if (resourceShadowDiscriminator == null) { return false; } - return resourceShadowDiscriminator.isThombstone(); + return resourceShadowDiscriminator.isTombstone(); } public void addAccountSyncDelta(ObjectDelta delta) throws SchemaException { @@ -483,6 +490,14 @@ public boolean isExists() { public void setExists(boolean exists) { this.isExists = exists; } + + public boolean isShadowExistsInRepo() { + return shadowExistsInRepo; + } + + public void setShadowExistsInRepo(boolean shadowExistsInRepo) { + this.shadowExistsInRepo = shadowExistsInRepo; + } public SynchronizationPolicyDecision getSynchronizationPolicyDecision() { return synchronizationPolicyDecision; @@ -931,7 +946,7 @@ public void checkConsistence(String contextDesc, boolean fresh, boolean force) { if (synchronizationPolicyDecision == SynchronizationPolicyDecision.BROKEN) { return; } - if (fresh && !force && resourceShadowDiscriminator != null && !resourceShadowDiscriminator.isThombstone()) { + if (fresh && !force && resourceShadowDiscriminator != null && !resourceShadowDiscriminator.isTombstone()) { if (resource == null) { throw new IllegalStateException("Null resource in "+this + (contextDesc == null ? "" : " in " +contextDesc)); } @@ -1220,6 +1235,9 @@ public String debugDump(int indent, boolean showTriples) { sb.append(", shadow"); } sb.append(", exists=").append(isExists); + if (!shadowExistsInRepo) { + sb.append(" (shadow not in repo)"); + } sb.append(", assigned=").append(isAssignedOld).append("->").append(isAssigned); sb.append(", active=").append(isActive); sb.append(", legal=").append(isLegalOld).append("->").append(isLegal); @@ -1229,7 +1247,7 @@ public String debugDump(int indent, boolean showTriples) { if (!isFresh()) { sb.append(", NOT FRESH"); } - if (resourceShadowDiscriminator != null && resourceShadowDiscriminator.isThombstone()) { + if (resourceShadowDiscriminator != null && resourceShadowDiscriminator.isTombstone()) { sb.append(", THOMBSTONE"); } if (syncAbsoluteTrigger) { diff --git a/model/model-impl/src/main/java/com/evolveum/midpoint/model/impl/lens/projector/ContextLoader.java b/model/model-impl/src/main/java/com/evolveum/midpoint/model/impl/lens/projector/ContextLoader.java index 6c370e9a2a6..1719d3ade8e 100644 --- a/model/model-impl/src/main/java/com/evolveum/midpoint/model/impl/lens/projector/ContextLoader.java +++ b/model/model-impl/src/main/java/com/evolveum/midpoint/model/impl/lens/projector/ContextLoader.java @@ -272,7 +272,7 @@ private void preprocessProjectionContext(LensContext c ResourceShadowDiscriminator rsd = projectionContext.getResourceShadowDiscriminator(); if (rsd != null) { resourceOid = rsd.getResourceOid(); - isThombstone = rsd.isThombstone(); + isThombstone = rsd.isTombstone(); kind = rsd.getKind(); intent = rsd.getIntent(); order = rsd.getOrder(); @@ -532,9 +532,10 @@ private void loadLinkRefsFromFocus(LensContext context, shadow = provisioningService.getObject(ShadowType.class, oid, options, task, result); } catch (ObjectNotFoundException e) { // Broken accountRef. We need to mark it for deletion - LensProjectionContext accountContext = getOrCreateEmptyThombstoneProjectionContext(context, oid); - accountContext.setFresh(true); - accountContext.setExists(false); + LensProjectionContext projectionContext = getOrCreateEmptyThombstoneProjectionContext(context, oid); + projectionContext.setFresh(true); + projectionContext.setExists(false); + projectionContext.setShadowExistsInRepo(false); OperationResult getObjectSubresult = result.getLastSubresult(); getObjectSubresult.setErrorsHandled(); continue; @@ -675,6 +676,7 @@ private void loadLinkRefsFromDelta(LensContext context, projectionContext.setPrimaryDelta(accountPrimaryDelta); projectionContext.setFullShadow(true); projectionContext.setExists(false); + projectionContext.setShadowExistsInRepo(false); isCombinedAdd = true; } } @@ -711,6 +713,7 @@ private void loadLinkRefsFromDelta(LensContext context, projectionContext = getOrCreateEmptyThombstoneProjectionContext(context, oid); projectionContext.setFresh(true); projectionContext.setExists(false); + projectionContext.setShadowExistsInRepo(false); OperationResult getObjectSubresult = result.getLastSubresult(); getObjectSubresult.setErrorsHandled(); } catch (ObjectNotFoundException ex){ @@ -795,6 +798,7 @@ private void loadProjectionContextsSync(LensContext co LOGGER.trace("Loading shadow {} from sync delta failed: not found", oid); projCtx.setExists(false); projCtx.setObjectCurrent(null); + projCtx.setShadowExistsInRepo(false); } // We will not set old account if the delta is delete. The @@ -803,7 +807,7 @@ private void loadProjectionContextsSync(LensContext co // shadow) if (syncDelta.getChangeType() == ChangeType.DELETE) { projCtx.setExists(false); - projCtx.getResourceShadowDiscriminator().setThombstone(true); + projCtx.getResourceShadowDiscriminator().setTombstone(true); } else if (shadow != null) { syncDelta.applyTo(shadow); projCtx.setLoadedObject(shadow); @@ -885,17 +889,19 @@ private LensProjectionContext getOrCreateAccountContext(Le // This is somehow expected, fix it and we can go on result.muteLastSubresultError(); // We have to create new context in this case, but it has to have thumbstone set - rsd.setThombstone(true); + rsd.setTombstone(true); projectionContext = LensUtil.getOrCreateProjectionContext(context, rsd); // We have to mark it as dead right now, otherwise the uniqueness check may fail markShadowDead(projection.getOid(), result); + projectionContext.setShadowExistsInRepo(false); } } catch (ObjectNotFoundException e) { // This is somehow expected, fix it and we can go on result.muteLastSubresultError(); String shadowOid = projectionContext.getOid(); - projectionContext.getResourceShadowDiscriminator().setThombstone(true); + projectionContext.getResourceShadowDiscriminator().setTombstone(true); projectionContext = LensUtil.getOrCreateProjectionContext(context, rsd); + projectionContext.setShadowExistsInRepo(false); // We have to mark it as dead right now, otherwise the uniqueness check may fail markShadowDead(shadowOid, result); } @@ -968,7 +974,7 @@ private LensProjectionContext getOrCreateEmptyThombstoneP if (projContext.getResourceShadowDiscriminator() == null) { projContext.setResourceShadowDiscriminator(new ResourceShadowDiscriminator(null, null, null, true)); } else { - projContext.getResourceShadowDiscriminator().setThombstone(true); + projContext.getResourceShadowDiscriminator().setTombstone(true); } projContext.setFullShadow(false); @@ -1077,6 +1083,7 @@ private void finishLoadOfProjectionContext(LensContext // Consistency mechanism might have kicked in and fixed the shadow. // What we really want here is a thombstone projection or a refreshed projection. result.muteLastSubresultError(); + projContext.setShadowExistsInRepo(false); refreshContextAfterShadowNotFound(context, projContext, options, task, result); } catch (CommunicationException | SchemaException | ConfigurationException | SecurityViolationException @@ -1155,7 +1162,7 @@ private void finishLoadOfProjectionContext(LensContext } else { if (thombstone) { // We do not want to reset thombstone flag if it was set before - discr.setThombstone(thombstone); + discr.setTombstone(thombstone); } } @@ -1285,6 +1292,7 @@ public void loadFullShadow(LensContext context, LensPr } catch (ObjectNotFoundException ex) { LOGGER.debug("Load of full resource object {} ended with ObjectNotFoundException (options={})", projCtx, getOptions); result.muteLastSubresultError(); + projCtx.setShadowExistsInRepo(false); refreshContextAfterShadowNotFound(context, projCtx, options, task, result); } @@ -1355,8 +1363,10 @@ public void refreshContextAfterShadowNotFound(LensContext } if (!compensated) { - LOGGER.trace("ObjectNotFound error is not compensated, setting context to thombstone"); - projCtx.getResourceShadowDiscriminator().setThombstone(true); + LOGGER.trace("ObjectNotFound error is not compensated, setting context to tombstone"); + if (projCtx.getResourceShadowDiscriminator() != null) { + projCtx.getResourceShadowDiscriminator().setTombstone(true); + } projCtx.setExists(false); projCtx.setFullShadow(false); } diff --git a/model/model-impl/src/main/java/com/evolveum/midpoint/model/impl/lens/projector/DependencyProcessor.java b/model/model-impl/src/main/java/com/evolveum/midpoint/model/impl/lens/projector/DependencyProcessor.java index 94861a0a655..223feaf8ad9 100644 --- a/model/model-impl/src/main/java/com/evolveum/midpoint/model/impl/lens/projector/DependencyProcessor.java +++ b/model/model-impl/src/main/java/com/evolveum/midpoint/model/impl/lens/projector/DependencyProcessor.java @@ -190,7 +190,7 @@ private LensProjectionContext determineProjectionWaveProv // started, we checked at the beginning). Therefore this context must have been visited again. // therefore there is a circular dependency. Therefore we need to create another context to split it. ResourceShadowDiscriminator origDiscr = projectionContext.getResourceShadowDiscriminator(); - ResourceShadowDiscriminator discr = new ResourceShadowDiscriminator(origDiscr.getResourceOid(), origDiscr.getKind(), origDiscr.getIntent(), origDiscr.isThombstone()); + ResourceShadowDiscriminator discr = new ResourceShadowDiscriminator(origDiscr.getResourceOid(), origDiscr.getKind(), origDiscr.getIntent(), origDiscr.isTombstone()); discr.setOrder(determinedOrder); if (!projectionContext.compareResourceShadowDiscriminator(discr, true)){ resultAccountContext = createAnotherContext(context, projectionContext, discr); @@ -384,7 +384,7 @@ private LensProjectionContext createAnotherContext(LensCo private LensProjectionContext createAnotherContext(LensContext context, LensProjectionContext origProjectionContext, int determinedOrder) throws PolicyViolationException { ResourceShadowDiscriminator origDiscr = origProjectionContext.getResourceShadowDiscriminator(); - ResourceShadowDiscriminator discr = new ResourceShadowDiscriminator(origDiscr.getResourceOid(), origDiscr.getKind(), origDiscr.getIntent(), origDiscr.isThombstone()); + ResourceShadowDiscriminator discr = new ResourceShadowDiscriminator(origDiscr.getResourceOid(), origDiscr.getKind(), origDiscr.getIntent(), origDiscr.isTombstone()); discr.setOrder(determinedOrder); LensProjectionContext otherCtx = createAnotherContext(context, origProjectionContext, discr); return otherCtx; diff --git a/model/model-impl/src/main/java/com/evolveum/midpoint/model/impl/lens/projector/ShadowConstraintsChecker.java b/model/model-impl/src/main/java/com/evolveum/midpoint/model/impl/lens/projector/ShadowConstraintsChecker.java index 93f4419c6eb..c030187e316 100644 --- a/model/model-impl/src/main/java/com/evolveum/midpoint/model/impl/lens/projector/ShadowConstraintsChecker.java +++ b/model/model-impl/src/main/java/com/evolveum/midpoint/model/impl/lens/projector/ShadowConstraintsChecker.java @@ -129,7 +129,7 @@ public void check(Task task, OperationResult result) throws SchemaException, Obj LensProjectionContext foundContext = context.findProjectionContextByOid(conflictingShadowCandidate.getOid()); if (foundContext != null) { if (foundContext.getResourceShadowDiscriminator() != null) { - if (foundContext.getResourceShadowDiscriminator().isThombstone()) { + if (foundContext.getResourceShadowDiscriminator().isTombstone()) { violation = false; } LOGGER.trace("Comparing with account in other context resulted to violation confirmation of {}", violation); @@ -154,7 +154,7 @@ public void check(Task task, OperationResult result) throws SchemaException, Obj } } } - if (projectionContext.getResourceShadowDiscriminator() != null && projectionContext.getResourceShadowDiscriminator().isThombstone()) { + if (projectionContext.getResourceShadowDiscriminator() != null && projectionContext.getResourceShadowDiscriminator().isTombstone()) { satisfiesConstraints = true; } else { satisfiesConstraints = false; diff --git a/model/model-impl/src/main/java/com/evolveum/midpoint/model/impl/sync/SynchronizationContext.java b/model/model-impl/src/main/java/com/evolveum/midpoint/model/impl/sync/SynchronizationContext.java index efc67b74c74..d15a32ac581 100644 --- a/model/model-impl/src/main/java/com/evolveum/midpoint/model/impl/sync/SynchronizationContext.java +++ b/model/model-impl/src/main/java/com/evolveum/midpoint/model/impl/sync/SynchronizationContext.java @@ -1,3 +1,18 @@ +/* + * Copyright (c) 2010-2018 Evolveum + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ package com.evolveum.midpoint.model.impl.sync; import javax.xml.namespace.QName; @@ -36,6 +51,8 @@ public class SynchronizationContext { private SynchronizationReactionType reaction; + private boolean shadowExistsInRepo = true; + public SynchronizationContext(PrismObject applicableShadow, PrismObject currentShadow, PrismObject resource, String chanel, Task task, OperationResult result) { this.applicableShadow = applicableShadow; this.currentShadow = currentShadow; @@ -160,5 +177,11 @@ public void setResult(OperationResult result) { this.result = result; } - + public boolean isShadowExistsInRepo() { + return shadowExistsInRepo; + } + + public void setShadowExistsInRepo(boolean shadowExistsInRepo) { + this.shadowExistsInRepo = shadowExistsInRepo; + } } diff --git a/model/model-impl/src/main/java/com/evolveum/midpoint/model/impl/sync/SynchronizationServiceImpl.java b/model/model-impl/src/main/java/com/evolveum/midpoint/model/impl/sync/SynchronizationServiceImpl.java index 12641b429f9..6161713addd 100644 --- a/model/model-impl/src/main/java/com/evolveum/midpoint/model/impl/sync/SynchronizationServiceImpl.java +++ b/model/model-impl/src/main/java/com/evolveum/midpoint/model/impl/sync/SynchronizationServiceImpl.java @@ -1012,6 +1012,7 @@ private LensContext createLensContext(SynchronizationCo projectionContext.setResource(resource); projectionContext.setOid(getOidFromChange(change)); projectionContext.setSynchronizationSituationDetected(syncCtx.getSituation()); + projectionContext.setShadowExistsInRepo(syncCtx.isShadowExistsInRepo()); // insert object delta if available in change ObjectDelta delta = change.getObjectDelta(); @@ -1195,6 +1196,7 @@ private PrismObject saveSyncMetadata(Synchroni LOGGER.debug( "Could not update situation in account, because shadow {} does not exist any more (this may be harmless)", shadow.getOid()); + syncCtx.setShadowExistsInRepo(false); parentResult.getLastSubresult().setStatus(OperationResultStatus.HANDLED_ERROR); } catch (ObjectAlreadyExistsException | SchemaException ex) { task.recordObjectActionExecuted(shadow, ChangeType.MODIFY, ex); diff --git a/model/model-impl/src/test/java/com/evolveum/midpoint/model/impl/AbstractInternalModelIntegrationTest.java b/model/model-impl/src/test/java/com/evolveum/midpoint/model/impl/AbstractInternalModelIntegrationTest.java index 0cd24f52c82..2ecc4aa4d6a 100644 --- a/model/model-impl/src/test/java/com/evolveum/midpoint/model/impl/AbstractInternalModelIntegrationTest.java +++ b/model/model-impl/src/test/java/com/evolveum/midpoint/model/impl/AbstractInternalModelIntegrationTest.java @@ -19,6 +19,9 @@ import javax.xml.namespace.QName; +import org.springframework.test.annotation.DirtiesContext; +import org.springframework.test.annotation.DirtiesContext.ClassMode; + import com.evolveum.icf.dummy.resource.DummyAccount; import com.evolveum.midpoint.model.impl.util.mock.MockClockworkHook; import com.evolveum.midpoint.model.test.AbstractModelIntegrationTest; @@ -38,6 +41,7 @@ * @author semancik * */ +@DirtiesContext(classMode = ClassMode.AFTER_CLASS) public class AbstractInternalModelIntegrationTest extends AbstractModelImplementationIntegrationTest { public static final File SYSTEM_CONFIGURATION_FILE = new File(COMMON_DIR, "system-configuration.xml"); diff --git a/model/model-impl/src/test/java/com/evolveum/midpoint/model/impl/lens/TestReconScript.java b/model/model-impl/src/test/java/com/evolveum/midpoint/model/impl/lens/TestReconScript.java index 1d0e7fabbf6..5433c471dcc 100644 --- a/model/model-impl/src/test/java/com/evolveum/midpoint/model/impl/lens/TestReconScript.java +++ b/model/model-impl/src/test/java/com/evolveum/midpoint/model/impl/lens/TestReconScript.java @@ -249,8 +249,8 @@ public void test006TestReconDelete() throws Exception{ .assertIsNotExists(); PrismObject user = repositoryService.searchShadowOwner(ACCOUNT_BEFORE_SCRIPT_OID, null, parentResult); - display("Unexpected owner", user); - AssertJUnit.assertNull("Owner for account " + ACCOUNT_BEFORE_SCRIPT_OID + " was found, but it should be not.", user); + display("Account owner", user); + AssertJUnit.assertNotNull("Owner for account " + ACCOUNT_BEFORE_SCRIPT_OID + " was not found", user); } diff --git a/model/model-impl/src/test/java/com/evolveum/midpoint/model/impl/sync/TestSynchronizationService.java b/model/model-impl/src/test/java/com/evolveum/midpoint/model/impl/sync/TestSynchronizationService.java index fd62a9cd6a5..2dd9784bf5a 100644 --- a/model/model-impl/src/test/java/com/evolveum/midpoint/model/impl/sync/TestSynchronizationService.java +++ b/model/model-impl/src/test/java/com/evolveum/midpoint/model/impl/sync/TestSynchronizationService.java @@ -21,6 +21,7 @@ import static org.testng.AssertJUnit.assertFalse; import java.io.File; +import java.util.List; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.test.annotation.DirtiesContext; @@ -40,6 +41,7 @@ import com.evolveum.midpoint.model.impl.sync.action.UnlinkAction; import com.evolveum.midpoint.model.impl.util.mock.MockLensDebugListener; import com.evolveum.midpoint.prism.PrismObject; +import com.evolveum.midpoint.prism.delta.ItemDelta; import com.evolveum.midpoint.prism.delta.ObjectDelta; import com.evolveum.midpoint.prism.path.ItemPath; import com.evolveum.midpoint.prism.util.PrismAsserts; @@ -53,6 +55,7 @@ import com.evolveum.midpoint.schema.util.DiagnosticContextHolder; import com.evolveum.midpoint.task.api.Task; import com.evolveum.midpoint.test.DummyResourceContoller; +import com.evolveum.midpoint.test.asserter.ShadowAsserter; import com.evolveum.midpoint.test.asserter.UserAsserter; import com.evolveum.midpoint.test.util.TestUtil; import com.evolveum.midpoint.xml.ns._public.common.common_3.ActivationStatusType; @@ -337,8 +340,12 @@ public void test039DeletedAccountJack() throws Exception { OperationResult result = task.getResult(); PrismObject shadowRepo = repositoryService.getObject(ShadowType.class, accountShadowJackDummyOid, null, result); - assertIteration(shadowRepo, 0, ""); - assertSituation(shadowRepo, SynchronizationSituationType.LINKED); + ShadowAsserter.forShadow(shadowRepo, "repo shadow before") + .assertLife() + .assertIteration(0) + .assertIterationToken("") + .assertSynchronizationSituation(SynchronizationSituationType.LINKED); + setDebugListener(); getDummyResource().deleteAccountByName(ACCOUNT_JACK_DUMMY_USERNAME); @@ -346,8 +353,32 @@ public void test039DeletedAccountJack() throws Exception { PrismObject shadow = getShadowModelNoFetch(accountShadowJackDummyOid); shadowRepo = repositoryService.getObject(ShadowType.class, accountShadowJackDummyOid, null, result); - assertIteration(shadowRepo, 0, ""); - assertSituation(shadowRepo, SynchronizationSituationType.LINKED); + ShadowAsserter.forShadow(shadowRepo, "repo shadow after noFetch") + // This is noFetch. Provisioning won't figure out that the shadow is dead (yet). + .assertLife() + .assertIteration(0) + .assertIterationToken("") + .assertSynchronizationSituation(SynchronizationSituationType.LINKED); + + // In fact, it is responsibility of provisioning to mark shadow dead before invoking + // sync service. This is unit test, therefore we have to simulate behavior of provisioning + // here. + markShadowTombstone(accountShadowJackDummyOid); + + shadowRepo = repositoryService.getObject(ShadowType.class, accountShadowJackDummyOid, null, result); + ShadowAsserter.forShadow(shadowRepo, "repo shadow before synchronization") + .assertTombstone() + .assertIteration(0) + .assertIterationToken("") + .assertSynchronizationSituation(SynchronizationSituationType.LINKED); + + // Once again, to have fresh data + shadow = getShadowModelNoFetch(accountShadowJackDummyOid); + ShadowAsserter.forShadow(shadowRepo, "repo shadow before synchronization (noFetch)") + .assertTombstone() + .assertIteration(0) + .assertIterationToken("") + .assertSynchronizationSituation(SynchronizationSituationType.LINKED); ResourceObjectShadowChangeDescription change = new ResourceObjectShadowChangeDescription(); change.setCurrentShadow(shadow); @@ -397,7 +428,7 @@ public void test039DeletedAccountJack() throws Exception { unlinkUser(USER_JACK_OID, accountShadowJackDummyOid); repositoryService.deleteObject(ShadowType.class, accountShadowJackDummyOid, result); } - + /** * Calypso is protected, no reaction should be applied. */ @@ -594,9 +625,12 @@ public void test199DeletedAccountJackTotal() throws Exception { repositoryService.deleteObject(ShadowType.class, accountShadowJackDummyOid, result); // WHEN + displayWhen(TEST_NAME); synchronizationService.notifyChange(change, task, result); // THEN + displayThen(TEST_NAME); + assertSuccess(result, 1); LensContext context = cleanDebugListener(); display("Resulting context (as seen by debug listener)", context); @@ -614,14 +648,8 @@ public void test199DeletedAccountJackTotal() throws Exception { PrismAsserts.assertNoDelta("Unexpected account primary delta", accCtx.getPrimaryDelta()); - assertNotLinked(context.getFocusContext().getObjectOld().getOid(), accountShadowJackDummyOid); - - PrismObject userAfter = getUser(USER_JACK_OID); - assertLinks(userAfter, 0); - - result.computeStatus(); - display("Final result", result); - TestUtil.assertSuccess(result,1); + assertUserAfter(USER_JACK_OID) + .assertLinks(0); assertNoObject(ShadowType.class, accountShadowJackDummyOid, task, result); } diff --git a/model/model-impl/src/test/resources/logback-test.xml b/model/model-impl/src/test/resources/logback-test.xml index 5a5b0d08c64..7a4e86896f5 100644 --- a/model/model-impl/src/test/resources/logback-test.xml +++ b/model/model-impl/src/test/resources/logback-test.xml @@ -34,7 +34,8 @@ - + + diff --git a/provisioning/provisioning-impl/src/main/java/com/evolveum/midpoint/provisioning/impl/ShadowManager.java b/provisioning/provisioning-impl/src/main/java/com/evolveum/midpoint/provisioning/impl/ShadowManager.java index c246d498a71..d5f4f6e3679 100644 --- a/provisioning/provisioning-impl/src/main/java/com/evolveum/midpoint/provisioning/impl/ShadowManager.java +++ b/provisioning/provisioning-impl/src/main/java/com/evolveum/midpoint/provisioning/impl/ShadowManager.java @@ -2197,6 +2197,9 @@ private void addModifyMetadataDeltas(PrismObject repoShadow, Collect if (modifyTimestampDelta != null) { return; } + if (LOGGER.isDebugEnabled()) { + LOGGER.debug("Metadata not found, adding minimal metadata. Modifications:\n{}", DebugUtil.debugDump(shadowChanges, 1)); + } PrismPropertyDefinition def = repoShadow.getDefinition().findPropertyDefinition(SchemaConstants.PATH_METADATA_MODIFY_TIMESTAMP); modifyTimestampDelta = def.createEmptyDelta(SchemaConstants.PATH_METADATA_MODIFY_TIMESTAMP); modifyTimestampDelta.setValuesToReplace(new PrismPropertyValue<>(clock.currentTimeXMLGregorianCalendar())); diff --git a/repo/repo-test-util/src/main/java/com/evolveum/midpoint/test/AbstractIntegrationTest.java b/repo/repo-test-util/src/main/java/com/evolveum/midpoint/test/AbstractIntegrationTest.java index 4cf53da99f1..876ef09dc01 100644 --- a/repo/repo-test-util/src/main/java/com/evolveum/midpoint/test/AbstractIntegrationTest.java +++ b/repo/repo-test-util/src/main/java/com/evolveum/midpoint/test/AbstractIntegrationTest.java @@ -2441,4 +2441,15 @@ protected RawType rawize(QName attrName, T value) { return new RawType(new PrismPropertyValue(value), attrName, prismContext); } + protected void markShadowTombstone(String oid) throws ObjectNotFoundException, SchemaException, ObjectAlreadyExistsException { + Task task = createTask("markShadowTombstone"); + OperationResult result = task.getResult(); + List> deadModifications = deltaFor(ShadowType.class) + .item(ShadowType.F_DEAD).replace(true) + .item(ShadowType.F_EXISTS).replace(false) + .asItemDeltas(); + repositoryService.modifyObject(ShadowType.class, oid, deadModifications, result); + assertSuccess(result); + } + }