use of javax.xml.datatype.Duration in project midpoint by Evolveum.
the class ShadowCaretaker method findPreviousPendingLifecycleOperationInGracePeriod.
public ChangeTypeType findPreviousPendingLifecycleOperationInGracePeriod(@Nullable ProvisioningContext ctx, @NotNull PrismObject<ShadowType> shadow, @NotNull XMLGregorianCalendar now) throws ObjectNotFoundException, SchemaException, CommunicationException, ConfigurationException, ExpressionEvaluationException {
List<PendingOperationType> pendingOperations = shadow.asObjectable().getPendingOperation();
if (pendingOperations.isEmpty()) {
return null;
}
Duration gracePeriod = ctx != null ? ProvisioningUtil.getGracePeriod(ctx) : null;
ChangeTypeType found = null;
for (PendingOperationType pendingOperation : pendingOperations) {
ObjectDeltaType delta = pendingOperation.getDelta();
if (delta == null) {
continue;
}
ChangeTypeType changeType = delta.getChangeType();
if (ChangeTypeType.MODIFY.equals(changeType)) {
continue;
}
if (ProvisioningUtil.isOverPeriod(now, gracePeriod, pendingOperation)) {
continue;
}
if (changeType == ChangeTypeType.DELETE) {
// DELETE always wins
return changeType;
} else {
// If there is an ADD then let's check for delete.
found = changeType;
}
}
return found;
}
use of javax.xml.datatype.Duration in project midpoint by Evolveum.
the class ShadowCaretaker method applyPendingOperations.
public PrismObject<ShadowType> applyPendingOperations(ProvisioningContext ctx, PrismObject<ShadowType> repoShadow, PrismObject<ShadowType> resourceShadow, boolean skipExecutionPendingOperations, XMLGregorianCalendar now) throws SchemaException, ConfigurationException, ObjectNotFoundException, CommunicationException, ExpressionEvaluationException {
if (repoShadow == null) {
return resourceShadow;
}
PrismObject<ShadowType> resultShadow;
if (resourceShadow == null) {
resultShadow = repoShadow;
} else {
resultShadow = resourceShadow;
}
if (ShadowUtil.isDead(resultShadow)) {
return resultShadow;
}
List<PendingOperationType> pendingOperations = repoShadow.asObjectable().getPendingOperation();
if (pendingOperations.isEmpty()) {
return resultShadow;
}
ShadowType resultShadowBean = resultShadow.asObjectable();
List<PendingOperationType> sortedOperations = sortPendingOperations(pendingOperations);
Duration gracePeriod = ProvisioningUtil.getGracePeriod(ctx);
boolean resourceReadIsCachingOnly = ProvisioningUtil.resourceReadIsCachingOnly(ctx.getResource());
for (PendingOperationType pendingOperation : sortedOperations) {
OperationResultStatusType resultStatus = pendingOperation.getResultStatus();
PendingOperationExecutionStatusType executionStatus = pendingOperation.getExecutionStatus();
if (OperationResultStatusType.NOT_APPLICABLE.equals(resultStatus)) {
// Not applicable means: "no point trying this, will not retry". Therefore it will not change future state.
continue;
}
if (PendingOperationExecutionStatusType.COMPLETED.equals(executionStatus) && ProvisioningUtil.isOverPeriod(now, gracePeriod, pendingOperation)) {
// Completed operations over grace period. They have already affected current state. They are already "applied".
continue;
}
// are going to be retried and they still may influence future state
if (skipExecutionPendingOperations && executionStatus == PendingOperationExecutionStatusType.EXECUTION_PENDING) {
continue;
}
if (resourceReadIsCachingOnly) {
// Re-applying them will mean additional risk of corrupting the data.
if (resultStatus != null && resultStatus != OperationResultStatusType.IN_PROGRESS && resultStatus != OperationResultStatusType.UNKNOWN) {
continue;
}
} else {
// We want to apply all the deltas, even those that are already completed. They might not be reflected on the resource yet.
// E.g. they may be not be present in the CSV export until the next export cycle is scheduled
}
ObjectDeltaType pendingDeltaType = pendingOperation.getDelta();
ObjectDelta<ShadowType> pendingDelta = DeltaConvertor.createObjectDelta(pendingDeltaType, prismContext);
if (pendingDelta.isAdd()) {
// resource are going to be more precise than the pending ADD delta (which might not have been applied completely)
if (resourceShadow == null) {
ShadowType repoShadowBean = repoShadow.asObjectable();
resultShadow = pendingDelta.getObjectToAdd().clone();
resultShadow.setOid(repoShadow.getOid());
resultShadowBean = resultShadow.asObjectable();
resultShadowBean.setExists(true);
resultShadowBean.setName(repoShadowBean.getName());
resultShadowBean.setShadowLifecycleState(repoShadowBean.getShadowLifecycleState());
List<PendingOperationType> newPendingOperations = resultShadowBean.getPendingOperation();
for (PendingOperationType pendingOperation2 : repoShadowBean.getPendingOperation()) {
newPendingOperations.add(pendingOperation2.clone());
}
applyAttributesDefinition(ctx, resultShadow);
}
}
if (pendingDelta.isModify()) {
pendingDelta.applyTo(resultShadow);
}
if (pendingDelta.isDelete()) {
resultShadowBean.setDead(true);
resultShadowBean.setExists(false);
resultShadowBean.setPrimaryIdentifierValue(null);
}
}
// }
return resultShadow;
}
use of javax.xml.datatype.Duration in project midpoint by Evolveum.
the class TriggerAsserter method assertTimestampFuture.
public TriggerAsserter<R> assertTimestampFuture(XMLGregorianCalendar now, String durationOffset, long tolerance) {
Duration offsetDuration = XmlTypeConverter.createDuration(durationOffset);
XMLGregorianCalendar mid = XmlTypeConverter.addDuration(now, offsetDuration);
XMLGregorianCalendar start = XmlTypeConverter.addMillis(mid, -tolerance);
XMLGregorianCalendar end = XmlTypeConverter.addMillis(mid, tolerance);
TestUtil.assertBetween("Wrong timestamp in " + desc(), start, end, trigger.getTimestamp());
return this;
}
use of javax.xml.datatype.Duration in project midpoint by Evolveum.
the class RefreshHelper method deleteDeadShadowIfPossible.
private PrismObject<ShadowType> deleteDeadShadowIfPossible(ProvisioningContext ctx, PrismObject<ShadowType> repoShadow, XMLGregorianCalendar now, Task task, OperationResult parentResult) throws ObjectNotFoundException, SchemaException, CommunicationException, ConfigurationException, ExpressionEvaluationException {
ShadowType shadowType = repoShadow.asObjectable();
if (!ShadowUtil.isDead(shadowType)) {
return repoShadow;
}
Duration gracePeriod = ProvisioningUtil.getGracePeriod(ctx);
Duration deadRetentionPeriod = ProvisioningUtil.getDeadShadowRetentionPeriod(ctx);
Duration expirationPeriod = XmlTypeConverter.longerDuration(gracePeriod, deadRetentionPeriod);
XMLGregorianCalendar lastActivityTimestamp = null;
for (PendingOperationType pendingOperation : shadowType.getPendingOperation()) {
lastActivityTimestamp = XmlTypeConverter.laterTimestamp(lastActivityTimestamp, pendingOperation.getRequestTimestamp());
lastActivityTimestamp = XmlTypeConverter.laterTimestamp(lastActivityTimestamp, pendingOperation.getLastAttemptTimestamp());
lastActivityTimestamp = XmlTypeConverter.laterTimestamp(lastActivityTimestamp, pendingOperation.getCompletionTimestamp());
}
if (lastActivityTimestamp == null) {
MetadataType metadata = shadowType.getMetadata();
if (metadata != null) {
lastActivityTimestamp = metadata.getModifyTimestamp();
if (lastActivityTimestamp == null) {
lastActivityTimestamp = metadata.getCreateTimestamp();
}
}
}
// If we have zero deadRetentionPeriod, we should get rid of all dead shadows immediately.
if (XmlTypeConverter.isZero(deadRetentionPeriod) || expirationPeriod == null || lastActivityTimestamp == null || XmlTypeConverter.isAfterInterval(lastActivityTimestamp, expirationPeriod, now)) {
// Perish you stinking corpse!
LOGGER.debug("Deleting dead {} because it is expired", repoShadow);
shadowManager.deleteShadow(repoShadow, task, parentResult);
definitionsHelper.applyDefinition(repoShadow, task, parentResult);
ResourceOperationDescription operationDescription = createSuccessOperationDescription(ctx, repoShadow, repoShadow.createDeleteDelta(), parentResult);
operationListener.notifySuccess(operationDescription, task, parentResult);
return null;
}
LOGGER.trace("Keeping dead {} because it is not expired yet, last activity={}, expiration period={}", repoShadow, lastActivityTimestamp, expirationPeriod);
return repoShadow;
}
use of javax.xml.datatype.Duration in project midpoint by Evolveum.
the class RefreshHelper method refreshShadowRetryOperations.
private RefreshShadowOperation refreshShadowRetryOperations(ProvisioningContext ctx, PrismObject<ShadowType> repoShadow, List<PendingOperationType> sortedOperations, ProvisioningOperationOptions options, Task task, OperationResult parentResult) throws ObjectNotFoundException, SchemaException, CommunicationException, ConfigurationException, ExpressionEvaluationException, EncryptionException {
ShadowType shadowType = repoShadow.asObjectable();
OperationResult retryResult = new OperationResult(OP_REFRESH_RETRY);
if (ShadowUtil.isDead(shadowType)) {
RefreshShadowOperation rso = new RefreshShadowOperation();
retryResult.recordSuccess();
rso.setRefreshedShadow(repoShadow);
rso.setRefreshResult(retryResult);
return rso;
}
Duration retryPeriod = ProvisioningUtil.getRetryPeriod(ctx);
Collection<ObjectDeltaOperation<ShadowType>> executedDeltas = new ArrayList<>();
for (PendingOperationType pendingOperation : sortedOperations) {
if (!needsRetry(pendingOperation)) {
continue;
}
// We really want to get "now" here. Retrying operation may take some time. We want good timestamps that do not lie.
XMLGregorianCalendar now = clock.currentTimeXMLGregorianCalendar();
if (!isAfterRetryPeriod(pendingOperation, retryPeriod, now)) {
if (PendingOperationTypeType.RETRY != pendingOperation.getType()) {
continue;
}
if (!ProvisioningOperationOptions.isForceRetry(options)) {
continue;
}
}
LOGGER.trace("Going to retry operation {} on {}", pendingOperation, repoShadow);
// Record attempt number and timestamp before the operation
// TODO: later use this as an optimistic lock to make sure that two threads won't retry the operation at the same time
// TODO: move to a better place
ObjectDelta<ShadowType> shadowDelta = repoShadow.createModifyDelta();
ItemPath containerPath = pendingOperation.asPrismContainerValue().getPath();
int attemptNumber = pendingOperation.getAttemptNumber() + 1;
PropertyDelta<Integer> attemptNumberDelta = shadowDelta.createPropertyModification(containerPath.append(PendingOperationType.F_ATTEMPT_NUMBER));
attemptNumberDelta.setRealValuesToReplace(attemptNumber);
shadowDelta.addModification(attemptNumberDelta);
PropertyDelta<XMLGregorianCalendar> lastAttemptTimestampDelta = shadowDelta.createPropertyModification(containerPath.append(PendingOperationType.F_LAST_ATTEMPT_TIMESTAMP));
lastAttemptTimestampDelta.setRealValuesToReplace(now);
shadowDelta.addModification(lastAttemptTimestampDelta);
PropertyDelta<OperationResultStatusType> resultStatusDelta = shadowDelta.createPropertyModification(containerPath.append(PendingOperationType.F_RESULT_STATUS));
resultStatusDelta.setRealValuesToReplace(OperationResultStatusType.IN_PROGRESS);
shadowDelta.addModification(resultStatusDelta);
shadowManager.modifyShadowAttributes(ctx, repoShadow, shadowDelta.getModifications(), parentResult);
shadowDelta.applyTo(repoShadow);
ObjectDeltaType pendingDeltaType = pendingOperation.getDelta();
ObjectDelta<ShadowType> pendingDelta = DeltaConvertor.createObjectDelta(pendingDeltaType, prismContext);
ProvisioningOperationState<? extends AsynchronousOperationResult> opState = ProvisioningOperationState.fromPendingOperation(repoShadow, pendingOperation);
LOGGER.debug("Retrying operation {} on {}, attempt #{}", pendingDelta, repoShadow, attemptNumber);
ObjectDeltaOperation<ShadowType> objectDeltaOperation = new ObjectDeltaOperation<>(pendingDelta);
OperationResult result = parentResult.createSubresult(OP_OPERATION_RETRY);
try {
retryOperation(ctx, pendingDelta, opState, task, result);
repoShadow = opState.getRepoShadow();
result.computeStatus();
if (result.isError()) {
retryResult.setStatus(result.getStatus());
}
// TODO maybe add whole "result" as subresult to the retryResult?
result.muteError();
} catch (CommunicationException | GenericFrameworkException | ObjectAlreadyExistsException | SchemaException | ObjectNotFoundException | ConfigurationException | SecurityViolationException e) {
// This is final failure: the error is not handled.
// Therefore the operation is now completed - finished with an error.
// But we do not want to stop the task. Just log the error.
LOGGER.error("Operation {} on {} ended up with an error after {} retries: {}", pendingDelta, repoShadow, attemptNumber, e.getMessage(), e);
// The retry itself was a success. Operation that was retried might have failed.
// And that is recorded in the shadow. But we have successfully retried the operation.
result.recordHandledError(e);
retryResult.recordFatalError("Operation " + pendingDelta + " on " + repoShadow + " ended with an error after " + attemptNumber + " retries: " + e.getMessage());
} catch (Throwable e) {
// This is unexpected error during retry. This means that there was other
// failure that we did not expected. This is likely to be bug - or maybe wrong
// error handling. This means that the retry was a failure.
result.recordFatalError(e);
retryResult.recordFatalError(e);
} finally {
// Status should be set by now, we just want to close the result
result.computeStatusIfUnknown();
}
objectDeltaOperation.setExecutionResult(result);
executedDeltas.add(objectDeltaOperation);
}
RefreshShadowOperation rso = new RefreshShadowOperation();
rso.setExecutedDeltas(executedDeltas);
rso.setRefreshedShadow(repoShadow);
parentResult.computeStatus();
rso.setRefreshResult(retryResult);
LOGGER.trace("refreshShadowOperation {}", rso.debugDumpLazily());
return rso;
}
Aggregations