Skip to content

Commit

Permalink
Consistency update: fixing tests, required improvements, stabilisatio…
Browse files Browse the repository at this point in the history
…n (MID-3891)
  • Loading branch information
semancik committed Jul 31, 2018
1 parent 2a46af9 commit 22a3eb3
Show file tree
Hide file tree
Showing 38 changed files with 1,359 additions and 280 deletions.
Expand Up @@ -135,8 +135,21 @@ public class GetOperationOptions extends AbstractOptions implements Serializable
* The default value is zero, which means that a fresh value must always be returned. This means that caches that do
* not guarantee fresh value cannot be used. If non-zero value is specified then such caches may be used. In case that
* Long.MAX_VALUE is specified then the caches are always used and fresh value is never retrieved.
*
* Null value is special in one more aspect: it allows to return partial cached data in case that the original is not
* accessible. E.g. in that case provisioning can return repository shadow in case that the resource is not reachable.
* Explicit specification of staleness=0 disables this behavior.
*/
private Long staleness;

/**
* Force refresh of object before the data are retrieved. This option is a guarantee that we get the freshest
* data that is possible. However, strange things may happen here. E.g. object that existed before this operation
* may get deleted during refresh because it has expired in the meantime. Or get operation may in fact attempt
* to create, modify and even delete of an account. This may happen in case that there are some unfinished
* operations in the shadow. Therefore when using this option you have to be really prepared for everything.
*/
private Boolean forceRefresh;

/**
* Should the results be made distinct.
Expand Down Expand Up @@ -650,6 +663,30 @@ public static long getStaleness(GetOperationOptions options) {
public static boolean isMaxStaleness(GetOperationOptions options) {
return GetOperationOptions.getStaleness(options) == Long.MAX_VALUE;
}

public Boolean getForceRefresh() {
return forceRefresh;
}

public void setForceRefresh(Boolean forceRefresh) {
this.forceRefresh = forceRefresh;
}

public static boolean isForceRefresh(GetOperationOptions options) {
if (options == null) {
return false;
}
if (options.forceRefresh == null) {
return false;
}
return options.forceRefresh;
}

public static GetOperationOptions createForceRefresh() {
GetOperationOptions opts = new GetOperationOptions();
opts.setForceRefresh(true);
return opts;
}

public Boolean getDistinct() {
return distinct;
Expand Down
Expand Up @@ -31,6 +31,8 @@
import com.evolveum.midpoint.util.logging.Trace;
import com.evolveum.midpoint.util.logging.TraceManager;
import com.evolveum.midpoint.xml.ns._public.common.common_3.*;
import com.evolveum.prism.xml.ns._public.types_3.ChangeTypeType;
import com.evolveum.prism.xml.ns._public.types_3.ObjectDeltaType;
import com.evolveum.prism.xml.ns._public.types_3.ProtectedStringType;

import javax.xml.datatype.XMLGregorianCalendar;
Expand Down
Expand Up @@ -38,6 +38,7 @@
import com.evolveum.midpoint.util.logging.TraceManager;
import com.evolveum.midpoint.xml.ns._public.common.common_3.*;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.stereotype.Component;

import javax.xml.namespace.QName;
Expand All @@ -54,7 +55,7 @@ public abstract class BaseCertificationHandler implements CertificationHandler {

@Autowired protected PrismContext prismContext;
@Autowired protected ModelService modelService;
@Autowired protected ObjectResolver objectResolver;
@Autowired @Qualifier("modelObjectResolver") protected ObjectResolver objectResolver;
@Autowired protected CertificationManagerImpl certificationManager;
@Autowired protected AccCertGeneralHelper helper;
@Autowired protected AccCertResponseComputationHelper computationHelper;
Expand Down
Expand Up @@ -251,6 +251,7 @@ public <O extends ObjectType> boolean executeChanges(LensContext<O> context, Tas
subResult.addParam("resource", projCtx.getResource());
}

PrismObject<ShadowType> shadowAfterModification = null;
try {
LOGGER.trace("Executing projection context {}", projCtx.toHumanReadableString());

Expand Down Expand Up @@ -281,7 +282,7 @@ public <O extends ObjectType> boolean executeChanges(LensContext<O> context, Tas
}
if (projDelta != null && projDelta.isDelete()) {

executeDelta(projDelta, projCtx, context, null, null, projCtx.getResource(), task,
shadowAfterModification = executeDelta(projDelta, projCtx, context, null, null, projCtx.getResource(), task,
subResult);

}
Expand All @@ -291,8 +292,9 @@ public <O extends ObjectType> boolean executeChanges(LensContext<O> context, Tas
if (LOGGER.isTraceEnabled()) {
LOGGER.trace("No change for " + projCtx.getResourceShadowDiscriminator());
}
shadowAfterModification = projCtx.getObjectCurrent();
if (focusContext != null) {
updateLinks(focusContext, projCtx, task, subResult);
updateLinks(focusContext, projCtx, shadowAfterModification, task, subResult);
}

// Make sure post-reconcile delta is always executed,
Expand All @@ -317,13 +319,13 @@ public <O extends ObjectType> boolean executeChanges(LensContext<O> context, Tas
}
}

executeDelta(projDelta, projCtx, context, null, null, projCtx.getResource(), task, subResult);
shadowAfterModification = executeDelta(projDelta, projCtx, context, null, null, projCtx.getResource(), task, subResult);

}

subResult.computeStatus();
if (focusContext != null) {
updateLinks(focusContext, projCtx, task, subResult);
updateLinks(focusContext, projCtx, shadowAfterModification, task, subResult);
}

executeReconciliationScript(projCtx, context, BeforeAfterType.AFTER, task, subResult);
Expand All @@ -338,7 +340,7 @@ public <O extends ObjectType> boolean executeChanges(LensContext<O> context, Tas
// We still want to update the links here. E.g. this may be live sync case where we discovered new account
// try to reconcile, but the reconciliation fails. We still want this shadow linked to user.
if (focusContext != null) {
updateLinks(focusContext, projCtx, task, subResult);
updateLinks(focusContext, projCtx, shadowAfterModification, task, subResult);
}

} catch (ObjectAlreadyExistsException e) {
Expand Down Expand Up @@ -590,8 +592,9 @@ private void recordFatalError(OperationResult subResult, OperationResult result,
* Make sure that the account is linked (or unlinked) as needed.
*/
private <O extends ObjectType, F extends FocusType> void updateLinks(
LensFocusContext<O> focusObjectContext, LensProjectionContext projCtx, Task task,
OperationResult result) throws ObjectNotFoundException, SchemaException {
LensFocusContext<O> focusObjectContext, LensProjectionContext projCtx,
PrismObject<ShadowType> shadowAfterModification,
Task task, OperationResult result) throws ObjectNotFoundException, SchemaException {
if (focusObjectContext == null) {
return;
}
Expand Down Expand Up @@ -619,7 +622,7 @@ private <O extends ObjectType, F extends FocusType> void updateLinks(
throw new IllegalStateException("Projection "+projCtx.toHumanReadableString()+" has null OID, this should not happen");
}

if (linkShouldExist(projCtx, result)) {
if (linkShouldExist(projCtx, shadowAfterModification, result)) {
// Link should exist

PrismObject<F> objectCurrent = focusContext.getObjectCurrent();
Expand Down Expand Up @@ -686,24 +689,19 @@ private <O extends ObjectType, F extends FocusType> void updateLinks(
}
}

private boolean linkShouldExist(LensProjectionContext projCtx, OperationResult result) {
private boolean linkShouldExist(LensProjectionContext projCtx, PrismObject<ShadowType> shadowAfterModification, OperationResult result) {
if (projCtx.getSynchronizationPolicyDecision() == SynchronizationPolicyDecision.UNLINK) {
return false;
}
if (isEmptyThombstone(projCtx)) {
return false;
}
if (projCtx.hasPendingOperations()) {
return true;
}
if (projCtx.getSynchronizationPolicyDecision() == SynchronizationPolicyDecision.DELETE
|| projCtx.isDelete()) {
if (result.isInProgress()) {
// Keep the link until operation is finished
return true;
} else {
return false;
}
return shadowAfterModification != null;
}
if (projCtx.hasPendingOperations()) {
return true;
}
return true;
}
Expand Down Expand Up @@ -866,7 +864,11 @@ private <F extends ObjectType> void updateSituationInShadow(Task task,

}

private <T extends ObjectType, F extends ObjectType> void executeDelta(ObjectDelta<T> objectDelta,
/**
* @return Returns estimate of the object after modification. Or null if the object was deleted.
* NOTE: this is only partially implemented (only for shadow delete).
*/
private <T extends ObjectType, F extends ObjectType> PrismObject<T> executeDelta(ObjectDelta<T> objectDelta,
LensElementContext<T> objectContext, LensContext<F> context, ModelExecuteOptions options,
ConflictResolutionType conflictResolution, ResourceType resource, Task task, OperationResult parentResult)
throws ObjectAlreadyExistsException, ObjectNotFoundException, SchemaException,
Expand All @@ -889,7 +891,7 @@ private <T extends ObjectType, F extends ObjectType> void executeDelta(ObjectDel

if (objectDelta == null || objectDelta.isEmpty()) {
LOGGER.debug("Skipping execution of delta because it was already executed: {}", objectContext);
return;
return objectContext.getObjectCurrent();
}

if (InternalsConfig.consistencyChecks) {
Expand All @@ -909,6 +911,7 @@ private <T extends ObjectType, F extends ObjectType> void executeDelta(ObjectDel
}

OperationResult result = parentResult.createSubresult(OPERATION_EXECUTE_DELTA);
PrismObject<T> objectAfterModification = null;

try {

Expand All @@ -917,7 +920,7 @@ private <T extends ObjectType, F extends ObjectType> void executeDelta(ObjectDel
} else if (objectDelta.getChangeType() == ChangeType.MODIFY) {
executeModification(objectDelta, context, objectContext, options, conflictResolution, resource, task, result);
} else if (objectDelta.getChangeType() == ChangeType.DELETE) {
executeDeletion(objectDelta, context, objectContext, options, resource, task, result);
objectAfterModification = executeDeletion(objectDelta, context, objectContext, options, resource, task, result);
}

// To make sure that the OID is set (e.g. after ADD operation)
Expand Down Expand Up @@ -947,6 +950,8 @@ private <T extends ObjectType, F extends ObjectType> void executeDelta(ObjectDel
}
}
}

return objectAfterModification;
}

private <T extends ObjectType, F extends FocusType> void removeExecutedItemDeltas(
Expand Down Expand Up @@ -1274,7 +1279,7 @@ private <T extends ObjectType, F extends ObjectType> void executeAddition(Object
}
}

private <T extends ObjectType, F extends ObjectType> void executeDeletion(ObjectDelta<T> change,
private <T extends ObjectType, F extends ObjectType> PrismObject<T> executeDeletion(ObjectDelta<T> change,
LensContext<F> context, LensElementContext<T> objectContext, ModelExecuteOptions options,
ResourceType resource, Task task, OperationResult result) throws ObjectNotFoundException,
ObjectAlreadyExistsException, SchemaException, CommunicationException,
Expand All @@ -1285,6 +1290,7 @@ private <T extends ObjectType, F extends ObjectType> void executeDeletion(Object

PrismObject<T> objectOld = objectContext.getObjectOld();
OwnerResolver ownerResolver = createOwnerResolver(context, task, result);
PrismObject<T> objectAfterModification = null;
try {
securityEnforcer.authorize(ModelAuthorizationAction.DELETE.getUrl(),
AuthorizationPhaseType.EXECUTION, AuthorizationParameters.Builder.buildObject(objectOld), ownerResolver, task, result);
Expand All @@ -1296,7 +1302,7 @@ private <T extends ObjectType, F extends ObjectType> void executeDeletion(Object
} else if (ObjectTypes.isClassManagedByProvisioning(objectTypeClass)) {
ProvisioningOperationOptions provisioningOptions = getProvisioningOptions(context, options);
try {
deleteProvisioningObject(objectTypeClass, oid, context, objectContext,
objectAfterModification = deleteProvisioningObject(objectTypeClass, oid, context, objectContext,
provisioningOptions, resource, task, result);
} catch (ObjectNotFoundException e) {
// Object that we wanted to delete is already gone. This can
Expand Down Expand Up @@ -1327,6 +1333,8 @@ private <T extends ObjectType, F extends ObjectType> void executeDeletion(Object
context.getChannel(), t);
throw t;
}

return objectAfterModification;
}

private <T extends ObjectType, F extends ObjectType> void executeModification(ObjectDelta<T> delta,
Expand Down Expand Up @@ -1428,7 +1436,7 @@ private <F extends ObjectType, T extends ObjectType> String addProvisioningObjec
return oid;
}

private <F extends ObjectType, T extends ObjectType> void deleteProvisioningObject(
private <F extends ObjectType, T extends ObjectType> PrismObject<T> deleteProvisioningObject(
Class<T> objectTypeClass, String oid, LensContext<F> context, LensElementContext<T> objectContext,
ProvisioningOperationOptions options, ResourceType resource, Task task, OperationResult result)
throws ObjectNotFoundException, ObjectAlreadyExistsException, SchemaException,
Expand All @@ -1452,8 +1460,9 @@ private <F extends ObjectType, T extends ObjectType> void deleteProvisioningObje
ProvisioningOperationTypeType.DELETE, resource, task, result);
}
Utils.setRequestee(task, context);
provisioning.deleteObject(objectTypeClass, oid, options, scripts, task, result);
PrismObject<T> objectAfterModification = provisioning.deleteObject(objectTypeClass, oid, options, scripts, task, result);
Utils.clearRequestee(task);
return objectAfterModification;
}

private <F extends ObjectType, T extends ObjectType> String modifyProvisioningObject(
Expand Down
Expand Up @@ -1283,6 +1283,9 @@ private <F extends ObjectType> void logFinalReadable(LensContext<F> context, Tas
for (LensProjectionContext projectionContext: context.getProjectionContexts()) {
DebugUtil.indentDebugDump(sb, 1);
sb.append(projectionContext.getHumanReadableName());
if (projectionContext.isThombstone()) {
sb.append(" THOMBSTONE");
}
sb.append(": ");
sb.append(projectionContext.getSynchronizationPolicyDecision());
sb.append("\n");
Expand Down
Expand Up @@ -112,6 +112,12 @@ public class LensProjectionContext extends LensElementContext<ShadowType> implem
private Boolean isLegal = null;
private Boolean isLegalOld = null;

/**
* True if the projection exists (or will exist) on resource. False if it does not exist.
* NOTE: entire projection is loaded with pointInTime=future. Therefore this does NOT
* reflect actual situation. If there is a pending operation to create the object then
* isExists will in fact be true.
*/
private boolean isExists;

/**
Expand Down
Expand Up @@ -87,6 +87,7 @@ public class PersonaProcessor {
private ObjectTemplateProcessor objectTemplateProcessor;

@Autowired(required=true)
@Qualifier("modelObjectResolver")
private ObjectResolver objectResolver;

@Autowired
Expand Down

0 comments on commit 22a3eb3

Please sign in to comment.