Skip to content

Commit

Permalink
More tests and fixes in the time machine (MID-3471)
Browse files Browse the repository at this point in the history
  • Loading branch information
semancik committed Nov 2, 2016
1 parent 925a34f commit c57ff84
Show file tree
Hide file tree
Showing 4 changed files with 386 additions and 32 deletions.
Expand Up @@ -1758,6 +1758,13 @@ public ItemDelta<V,D> createReverseDelta() {
reverseDelta.valuesToDelete = cloneValuesToAdd;
if (cloneValuesToReplace != null) {
reverseDelta.valuesToReplace = cloneEstimatedOldValues;
if (reverseDelta.valuesToReplace == null) {
// We want to force replace delta here. Otherwise the reverse delta
// may look like empty. We do not explicitly have old values here,
// so this is a bit tricky and not entirely correct. But we can live
// with that for now.
reverseDelta.valuesToReplace = new ArrayList<>(0);
}
reverseDelta.estimatedOldValues = cloneValuesToReplace;
} else {
// TODO: what about estimatedOldValues here?
Expand Down
Expand Up @@ -115,11 +115,16 @@ public <O extends ObjectType> PrismObject<O> reconstructObject(Class<O> type, St

LOGGER.info("TRAIL:\n{}", DebugUtil.debugDump(changeTrail, 1));

PrismObject<O> objectFromLastEvent = getObjectFromLastEvent(currentObject, changeTrail, eventIdentifier);
if (objectFromLastEvent != null) {
return objectFromLastEvent;
}

PrismObject<O> reconstructedObject = rollBackTime(currentObject.clone(), changeTrail);

return reconstructedObject;
}

private List<AuditEventRecord> getChangeTrail(String targetOid, String finalEventIdentifier) throws ObjectNotFoundException {
AuditEventRecord finalEvent = findEvent(finalEventIdentifier);
if (finalEvent == null) {
Expand All @@ -140,7 +145,6 @@ private List<AuditEventRecord> getChangeTrail(String targetOid, String finalEven
iterator.remove();
} else if (finalEventIdentifier.equals(event.getEventIdentifier())) {
foundFinalEvent = true;
iterator.remove();
}
}

Expand Down Expand Up @@ -170,26 +174,44 @@ private AuditEventRecord findEvent(String eventIdentifier) {
return listRecords.get(0);
}

private <O extends ObjectType> PrismObject<O> rollBackTime(PrismObject<O> object, List<AuditEventRecord> changeTrail) throws SchemaException {

private <O extends ObjectType> PrismObject<O> getObjectFromLastEvent(PrismObject<O> object, List<AuditEventRecord> changeTrail, String eventIdentifier) {
if (changeTrail.isEmpty()) {
return object;
}
AuditEventRecord lastEvent = changeTrail.remove(changeTrail.size() - 1);
if (!eventIdentifier.equals(lastEvent.getEventIdentifier())) {
throw new IllegalStateException("Wrong last event identifier, expected " + eventIdentifier+" but was " + lastEvent.getEventIdentifier());
}
Collection<ObjectDeltaOperation<? extends ObjectType>> lastEventDeltasOperations = lastEvent.getDeltas();
for (ObjectDeltaOperation<? extends ObjectType> lastEventDeltasOperation: lastEventDeltasOperations) {
ObjectDelta<O> objectDelta = (ObjectDelta<O>) lastEventDeltasOperation.getObjectDelta();
if (!isApplicable(lastEventDeltasOperation, object, lastEvent)) {
continue;
}
if (objectDelta.isAdd()) {
// We are lucky. This is object add, so we have complete object there. No need to roll back
// the operations.
PrismObject<O> objectToAdd = objectDelta.getObjectToAdd();
if (LOGGER.isTraceEnabled()) {
LOGGER.trace("Taking object from add delta in last event {}:\n{}", lastEvent.getEventIdentifier(), objectToAdd.debugDump(1));
}
return objectToAdd;
}
}
return null;
}

private <O extends ObjectType> PrismObject<O> rollBackTime(PrismObject<O> object, List<AuditEventRecord> changeTrail) throws SchemaException {
for (AuditEventRecord event: changeTrail) {
if (LOGGER.isTraceEnabled()) {
LOGGER.trace("Applying event {} ({})", event.getEventIdentifier(), XmlTypeConverter.createXMLGregorianCalendar(event.getTimestamp()));
}
Collection<ObjectDeltaOperation<? extends ObjectType>> deltaOperations = event.getDeltas();
if (deltaOperations != null) {
for (ObjectDeltaOperation<? extends ObjectType> deltaOperation: deltaOperations) {
OperationResult executionResult = deltaOperation.getExecutionResult();
ObjectDelta<O> objectDelta = (ObjectDelta<O>) deltaOperation.getObjectDelta();
if (executionResult.getStatus() == OperationResultStatus.FATAL_ERROR) {
LOGGER.trace("Skipping delta {} in event {} because it is {}", objectDelta, event.getEventIdentifier(),
executionResult.getStatus());
continue;
}
if (!object.getOid().equals(objectDelta.getOid())) {
if (LOGGER.isTraceEnabled()) {
LOGGER.trace("Skipping delta {} in event {} because OID does not match ({} vs {})", objectDelta, event.getEventIdentifier(),
object.getOid(), objectDelta.getOid());
}
if (!isApplicable(deltaOperation, object, event)) {
continue;
}
if (objectDelta.isDelete()) {
Expand All @@ -213,4 +235,23 @@ private <O extends ObjectType> PrismObject<O> rollBackTime(PrismObject<O> object
return object;
}

private <O extends ObjectType> boolean isApplicable(ObjectDeltaOperation<? extends ObjectType> lastEventDeltasOperation,
PrismObject<O> object, AuditEventRecord lastEvent) {
OperationResult executionResult = lastEventDeltasOperation.getExecutionResult();
ObjectDelta<O> objectDelta = (ObjectDelta<O>) lastEventDeltasOperation.getObjectDelta();
if (executionResult.getStatus() == OperationResultStatus.FATAL_ERROR) {
LOGGER.trace("Skipping delta {} in event {} because it is {}", objectDelta, lastEvent.getEventIdentifier(),
executionResult.getStatus());
return false;
}
if (!object.getOid().equals(objectDelta.getOid())) {
if (LOGGER.isTraceEnabled()) {
LOGGER.trace("Skipping delta {} in event {} because OID does not match ({} vs {})", objectDelta, lastEvent.getEventIdentifier(),
object.getOid(), objectDelta.getOid());
}
return false;
}
return true;
}

}
Expand Up @@ -630,6 +630,19 @@ private <F extends ObjectType> void recordEffectiveStatusDelta(LensFocusContext<
throws SchemaException {
PrismContainerDefinition<ActivationType> activationDefinition = getActivationDefinition();

// It is not enough to check alreadyHasDelta(). The change may happen in previous waves
// and the secondary delta may no longer be here. When it comes to disableTimestamp we even
// cannot rely on natural filtering of already executed deltas as the timestamp here may
// be off by several milliseconds. So explicitly check for the change here.
PrismObject<F> objectCurrent = focusContext.getObjectCurrent();
if (objectCurrent != null) {
PrismProperty<ActivationStatusType> effectiveStatusPropCurrent = objectCurrent.findProperty(SchemaConstants.PATH_ACTIVATION_EFFECTIVE_STATUS);
if (effectiveStatusPropCurrent != null && effectiveStatusNew.equals(effectiveStatusPropCurrent.getRealValue())) {
LOGGER.trace("Skipping setting of effective status and disableTimestamp because there was no change");
return;
}
}

PrismPropertyDefinition<ActivationStatusType> effectiveStatusDef = activationDefinition.findPropertyDefinition(ActivationType.F_EFFECTIVE_STATUS);
PropertyDelta<ActivationStatusType> effectiveStatusDelta
= effectiveStatusDef.createEmptyDelta(new ItemPath(UserType.F_ACTIVATION, ActivationType.F_EFFECTIVE_STATUS));
Expand All @@ -639,7 +652,7 @@ private <F extends ObjectType> void recordEffectiveStatusDelta(LensFocusContext<
}

PropertyDelta<XMLGregorianCalendar> timestampDelta = LensUtil.createActivationTimestampDelta(effectiveStatusNew, now, activationDefinition, OriginType.USER_POLICY);
if (!focusContext.alreadyHasDelta(timestampDelta)){
if (!focusContext.alreadyHasDelta(timestampDelta)) {
focusContext.swallowToProjectionWaveSecondaryDelta(timestampDelta);
}
}
Expand Down

0 comments on commit c57ff84

Please sign in to comment.