diff --git a/provisioning/provisioning-impl/src/main/java/com/evolveum/midpoint/provisioning/impl/ShadowCache.java b/provisioning/provisioning-impl/src/main/java/com/evolveum/midpoint/provisioning/impl/ShadowCache.java index f6b5b51c4f9..4cf41881b95 100644 --- a/provisioning/provisioning-impl/src/main/java/com/evolveum/midpoint/provisioning/impl/ShadowCache.java +++ b/provisioning/provisioning-impl/src/main/java/com/evolveum/midpoint/provisioning/impl/ShadowCache.java @@ -949,7 +949,7 @@ public String modifyShadow(PrismObject repoShadow, options.setForceRetry(Boolean.TRUE); } - return modifyShadowAttempt(ctx, modifications, scripts, options, opState, task, parentResult); + return modifyShadowAttempt(ctx, modifications, scripts, options, opState, false, task, parentResult); } private String modifyShadowAttempt(ProvisioningContext ctx, @@ -957,6 +957,7 @@ private String modifyShadowAttempt(ProvisioningContext ctx, OperationProvisioningScriptsType scripts, ProvisioningOperationOptions options, ProvisioningOperationState>>> opState, + boolean inRefresh, Task task, OperationResult parentResult) throws CommunicationException, GenericFrameworkException, ObjectNotFoundException, SchemaException, ConfigurationException, SecurityViolationException, PolicyViolationException, ExpressionEvaluationException, EncryptionException, ObjectAlreadyExistsException { @@ -987,7 +988,7 @@ private String modifyShadowAttempt(ProvisioningContext ctx, LOGGER.trace("MODIFY {}: resource modification, execution starting\n{}", repoShadow, DebugUtil.debugDumpLazily(modifications)); RefreshShadowOperation refreshShadowOperation = null; - if (shouldRefresh(repoShadow)) { + if (!inRefresh && shouldRefresh(repoShadow)) { refreshShadowOperation = refreshShadow(repoShadow, options, task, parentResult); } @@ -1751,7 +1752,7 @@ private void retryOperation(ProvisioningContext ctx, if (pendingDelta.isModify()) { modifyShadowAttempt(ctx, pendingDelta.getModifications(), scripts, options, (ProvisioningOperationState>>>) opState, - task, result); + true, task, result); } if (pendingDelta.isDelete()) { diff --git a/provisioning/provisioning-impl/src/test/java/com/evolveum/midpoint/provisioning/impl/dummy/TestDummyConsistency.java b/provisioning/provisioning-impl/src/test/java/com/evolveum/midpoint/provisioning/impl/dummy/TestDummyConsistency.java index 2690988538c..341758fba17 100644 --- a/provisioning/provisioning-impl/src/test/java/com/evolveum/midpoint/provisioning/impl/dummy/TestDummyConsistency.java +++ b/provisioning/provisioning-impl/src/test/java/com/evolveum/midpoint/provisioning/impl/dummy/TestDummyConsistency.java @@ -592,6 +592,10 @@ public void test116RefreshAccountMorganRetrySuccess() throws Exception { assertSteadyResources(); } + /** + * Tries to change the name to "Henry Morgan". + * Attempt will not succeed because of the communication failure. + */ @Test public void test120ModifyMorganFullNameCommunicationFailure() throws Exception { Task task = getTestTask(); @@ -2067,6 +2071,73 @@ public void test900GetAccountMurrayPending() throws Exception { assertSteadyResources(); } + + /** + * Tries to change the name to "Henry Morgan" and then to "Captain Henry Morgan". + * Attempts will not succeed because of the communication failure. + * + * Then we make resource available and refresh the shadow. + * We verify that the operations are executed in the correct order. + * + * MID-6795 + */ + @Test + public void test910ModifyMorganFullNameTwiceCommunicationFailure() throws Exception { + Task task = getTestTask(); + OperationResult result = task.getResult(); + syncServiceMock.reset(); + + clock.resetOverride(); + + dummyResource.setBreakMode(BreakMode.NONE); + + PrismObject account = prismContext.parseObject(ACCOUNT_MORGAN_FILE); + // Reset morgan OID. We cannot use the same OID, as there is a dead shadow with the original OID. + account.setOid(null); + account.checkConsistence(); + display("Adding shadow", account); + + shadowMorganOid = provisioningService.addObject(account, null, null, task, result); + + dummyResource.setBreakMode(BreakMode.NETWORK); + + ObjectDelta delta1 = prismContext.deltaFactory().object().createModificationReplaceProperty(ShadowType.class, + shadowMorganOid, dummyResourceCtl.getAttributeFullnamePath(), ACCOUNT_MORGAN_FULLNAME_HM); + displayDumpable("ObjectDelta", delta1); + + ObjectDelta delta2 = prismContext.deltaFactory().object().createModificationReplaceProperty(ShadowType.class, + shadowMorganOid, dummyResourceCtl.getAttributeFullnamePath(), ACCOUNT_MORGAN_FULLNAME_CHM); + displayDumpable("ObjectDelta", delta2); + + when("no connection"); + provisioningService.modifyObject(ShadowType.class, delta1.getOid(), delta1.getModifications(), + null, null, task, result); + provisioningService.modifyObject(ShadowType.class, delta2.getOid(), delta2.getModifications(), + null, null, task, result); + + when("no connection"); + assertInProgress(result); + + ShadowAsserter.forShadow(getShadowRepo(shadowMorganOid), "repository") + .display() + .pendingOperations() + .assertOperations(2); + + when("connection reestablished, shadow refresh"); + dummyResource.setBreakMode(BreakMode.NONE); + + clockForward("PT1H"); + + PrismObject shadow = provisioningService.getObject(ShadowType.class, shadowMorganOid, null, task, result); + provisioningService.refreshShadow(shadow, null, task, result); + + then("connection reestablished, shadow refresh"); + assertShadowProvisioning(shadowMorganOid) + .display() + .attributes() + .assertValue(dummyResourceCtl.getAttributeFullnameQName(), ACCOUNT_MORGAN_FULLNAME_CHM); + } + private void assertUncreatedMorgan(int expectedAttemptNumber) throws Exception { // @formatter:off