Skip to content

Commit

Permalink
Pending operations - work in progress
Browse files Browse the repository at this point in the history
  • Loading branch information
semancik committed Apr 13, 2017
1 parent 9185513 commit 7809449
Show file tree
Hide file tree
Showing 7 changed files with 164 additions and 44 deletions.
@@ -1,5 +1,5 @@
/*
* Copyright (c) 2010-2016 Evolveum
* Copyright (c) 2010-2017 Evolveum
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
Expand Down Expand Up @@ -597,4 +597,17 @@ public static String formatDateXml(Date date) {
return createXMLGregorianCalendar(date).toXMLFormat();
}

public static int compare(XMLGregorianCalendar o1, XMLGregorianCalendar o2) {
if (o1 == null && o2 == null) {
return 0;
}
if (o1 == null) {
return -1;
}
if (o2 == null) {
return 1;
}
return o1.compare(o2);
}

}
Expand Up @@ -691,7 +691,7 @@ private boolean scanForUnfinishedOperations(Task task, String resourceOid, Recon

ProvisioningOperationOptions options = ProvisioningOperationOptions.createCompletePostponed(false);
Utils.clearRequestee(task);
provisioningService.finishOperation(shadow, options, task, provisioningResult);
provisioningService.refreshShadow(shadow, options, task, provisioningResult);
// retryFailedOperation(shadow.asObjectable(), opResult);

task.recordIterativeOperationEnd(shadow.asObjectable(), started, null);
Expand Down
@@ -1,5 +1,5 @@
/*
* Copyright (c) 2010-2016 Evolveum
* Copyright (c) 2010-2017 Evolveum
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
Expand Down Expand Up @@ -432,7 +432,13 @@ List<PrismObject<? extends ShadowType>> listResourceObjects(String resourceOid,
Task task, OperationResult parentResult) throws SchemaException, ObjectNotFoundException, CommunicationException, ConfigurationException, SecurityViolationException;


<T extends ShadowType> void finishOperation(PrismObject<T> object, ProvisioningOperationOptions options, Task task, OperationResult parentResult)
/**
* Makes sure that the shadow is in accord with the reality. If there are any unfinished operations associated with the shadow
* then this method will try to finish them. If there are pending (async) operations then this method will update their status.
* And so on. However, this is NOT reconciliation function that will make sure that the resource object attributes are OK
* with all the policies. This is just a provisioning-level operation.
*/
void refreshShadow(PrismObject<ShadowType> shadow, ProvisioningOperationOptions options, Task task, OperationResult parentResult)
throws SchemaException, ObjectNotFoundException, CommunicationException, ConfigurationException,
ObjectAlreadyExistsException, SecurityViolationException;

Expand Down
Expand Up @@ -111,7 +111,7 @@ public <T extends ShadowType> T handleError(T shadow, FailedOperation op, Except
}
try {
//ProvisioningOperationOptions.createCompletePostponed(false);
provisioningService.finishOperation(shadow.asPrismObject(), null, task, result);
provisioningService.refreshShadow(shadow.asPrismObject(), null, task, result);
result.computeStatus();
if (result.isSuccess()) {
LOGGER.trace("Postponed operation was finished successfully while getting shadow. Getting new object.");
Expand Down Expand Up @@ -151,7 +151,7 @@ public <T extends ShadowType> T handleError(T shadow, FailedOperation op, Except
.asPrismObject().getDefinition());
}
PropertyDelta.applyTo(modifications, shadow.asPrismObject());
provisioningService.finishOperation(shadow.asPrismObject(), null, task, result);
provisioningService.refreshShadow(shadow.asPrismObject(), null, task, result);
result.computeStatus();

if (!result.isSuccess()) {
Expand Down
Expand Up @@ -991,54 +991,53 @@ public boolean handle(ShadowType shadow) {
return objectList;
}

public <T extends ShadowType> void finishOperation(PrismObject<T> object, ProvisioningOperationOptions options, Task task, OperationResult parentResult)
@Override
public void refreshShadow(PrismObject<ShadowType> shadow, ProvisioningOperationOptions options, Task task, OperationResult parentResult)
throws SchemaException, ObjectNotFoundException, CommunicationException, ConfigurationException,
ObjectAlreadyExistsException, SecurityViolationException {
Validate.notNull(object, "Object for finishing operation must not be null.");
Validate.notNull(shadow, "Shadow for refresh must not be null.");
OperationResult result = parentResult.createSubresult(ProvisioningServiceImpl.class.getName() +".finishOperation");
PrismObject<ShadowType> shadow = (PrismObject<ShadowType>)object;
ShadowType shadowType = shadow.asObjectable();

LOGGER.debug("Finishing operation {} on {}", shadowType.getFailedOperationType(), object);
LOGGER.debug("Refreshing shadow {}", shadow);

try {
if (FailedOperationTypeType.ADD == shadowType.getFailedOperationType()) {
getShadowCache(Mode.RECON).addShadow(shadow, null, null, options, task, result);
} else if (FailedOperationTypeType.MODIFY == shadowType.getFailedOperationType()) {
getShadowCache(Mode.RECON).modifyShadow(shadow, shadow.getOid(), new ArrayList<ItemDelta>(), null, options, task, result);
} else if (FailedOperationTypeType.DELETE == shadowType.getFailedOperationType()) {
getShadowCache(Mode.RECON).deleteShadow(shadow, options, null, task, result);
} else {
result.recordWarning("Missing or unknown type of operation to finish: " + shadowType.getFailedOperationType());
}
} catch (CommunicationException e) {
ProvisioningUtil.recordFatalError(LOGGER, result, "Couldn't finish operation: communication problem: " + e.getMessage(), e);
throw e;

getShadowCache(Mode.RECON).refreshShadow(shadow, options, task, result);

refreshShadowLegacy(shadow, options, task, result);

} catch (GenericFrameworkException e) {
ProvisioningUtil.recordFatalError(LOGGER, result, "Couldn't finish operation: generic error in the connector: " + e.getMessage(),
e);
ProvisioningUtil.recordFatalError(LOGGER, result, "Couldn't refresh shadow: " + e.getClass().getSimpleName() + ": "+ e.getMessage(), e);
throw new CommunicationException(e.getMessage(), e);
} catch (SchemaException e) {
ProvisioningUtil.recordFatalError(LOGGER, result, "Couldn't finish operation: schema problem: " + e.getMessage(), e);
throw e;
} catch (ObjectNotFoundException e) {
ProvisioningUtil.recordFatalError(LOGGER, result, "Couldn't finish operation: object doesn't exist: " + e.getMessage(), e);
throw e;
} catch (RuntimeException e) {
ProvisioningUtil.recordFatalError(LOGGER, result, "Couldn't finish operation: unexpected problem: " + e.getMessage(), e);
throw new SystemException("Internal error: " + e.getMessage(), e);
} catch (ConfigurationException e) {
ProvisioningUtil.recordFatalError(LOGGER, result, "Couldn't finish operation: configuration problem: " + e.getMessage(), e);
throw e;
} catch (SecurityViolationException e) {
ProvisioningUtil.recordFatalError(LOGGER, result, "Couldn't finish operation: security violation: " + e.getMessage(), e);
throw e;
} catch (ObjectAlreadyExistsException e) {
ProvisioningUtil.recordFatalError(LOGGER, result, "Couldn't finish operation: object after modification would conflict with another existing object: " + e.getMessage(), e);

} catch (CommunicationException | SchemaException | ObjectNotFoundException | ConfigurationException
| SecurityViolationException | ObjectAlreadyExistsException | RuntimeException | Error e) {
ProvisioningUtil.recordFatalError(LOGGER, result, "Couldn't refresh shadow: " + e.getClass().getSimpleName() + ": "+ e.getMessage(), e);
throw e;
}

result.computeStatus();
result.cleanupResult();
LOGGER.debug("Finished operation {} on {}: ", shadowType.getFailedOperationType(), object, result);

LOGGER.debug("Finished refreshing shadow {}: ", shadow, result);
}

private void refreshShadowLegacy(PrismObject<ShadowType> shadow, ProvisioningOperationOptions options, Task task, OperationResult result)
throws SchemaException, ObjectNotFoundException, CommunicationException, ConfigurationException,
ObjectAlreadyExistsException, SecurityViolationException, GenericFrameworkException {

ShadowType shadowType = shadow.asObjectable();

if (FailedOperationTypeType.ADD == shadowType.getFailedOperationType()) {
getShadowCache(Mode.RECON).addShadow(shadow, null, null, options, task, result);
} else if (FailedOperationTypeType.MODIFY == shadowType.getFailedOperationType()) {
getShadowCache(Mode.RECON).modifyShadow(shadow, shadow.getOid(), new ArrayList<ItemDelta>(), null, options, task, result);
} else if (FailedOperationTypeType.DELETE == shadowType.getFailedOperationType()) {
getShadowCache(Mode.RECON).deleteShadow(shadow, options, null, task, result);
} else {
result.recordWarning("Missing or unknown type of operation to finish: " + shadowType.getFailedOperationType());
}

}

private <T extends ObjectType> boolean handleRepoObject(final Class<T> type, PrismObject<T> object,
Expand Down
Expand Up @@ -40,6 +40,7 @@
import com.evolveum.midpoint.schema.constants.SchemaConstants;
import com.evolveum.midpoint.schema.internals.InternalsConfig;
import com.evolveum.midpoint.schema.processor.*;
import com.evolveum.midpoint.schema.result.AsynchronousOperationQueryable;
import com.evolveum.midpoint.schema.result.AsynchronousOperationReturnValue;
import com.evolveum.midpoint.schema.result.OperationResult;
import com.evolveum.midpoint.schema.result.OperationResultStatus;
Expand Down Expand Up @@ -89,7 +90,8 @@ public class ResourceObjectConverter {

private static final String DOT_CLASS = ResourceObjectConverter.class.getName() + ".";
public static final String OPERATION_MODIFY_ENTITLEMENT = DOT_CLASS + "modifyEntitlement";
private static final String OPERATION_ADD_RESOURCE_OBJECT = DOT_CLASS + "addResourceObject";;
private static final String OPERATION_ADD_RESOURCE_OBJECT = DOT_CLASS + "addResourceObject";
private static final String OPERATION_REFRESH_OPERATION_STATUS = DOT_CLASS + "refreshOperationStatus";

@Autowired
private EntitlementConverter entitlementConverter;
Expand All @@ -110,6 +112,7 @@ public class ResourceObjectConverter {

static final String FULL_SHADOW_KEY = ResourceObjectConverter.class.getName()+".fullShadow";


public PrismObject<ShadowType> getResourceObject(ProvisioningContext ctx,
Collection<? extends ResourceAttribute<?>> identifiers, boolean fetchAssociations, OperationResult parentResult)
throws ObjectNotFoundException, CommunicationException, SchemaException, ConfigurationException,
Expand Down Expand Up @@ -2195,6 +2198,54 @@ private void addExecuteScriptOperation(Collection<Operation> operations, Provisi
}
}

public OperationResultStatus refreshOperationStatus(ProvisioningContext ctx,
PrismObject<ShadowType> shadow, String asyncRef, OperationResult parentResult)
throws ObjectNotFoundException, SchemaException, CommunicationException, ConfigurationException {

OperationResult result = parentResult.createSubresult(OPERATION_REFRESH_OPERATION_STATUS);

ResourceType resource;
ConnectorInstance connector;
try {
resource = ctx.getResource();
connector = ctx.getConnector(result);
} catch (ObjectNotFoundException | SchemaException | CommunicationException
| ConfigurationException e) {
result.recordFatalError(e);
throw e;
}

OperationResultStatus status = null;
if (connector instanceof AsynchronousOperationQueryable) {

if (LOGGER.isDebugEnabled()) {
LOGGER.debug("PROVISIONING REFRESH operation on {}, object: {}",
resource, shadow);
}

try {

status = ((AsynchronousOperationQueryable)connector).queryOperationStatus(asyncRef, result);

} catch (ObjectNotFoundException | SchemaException e) {
result.recordFatalError(e);
throw e;
}

result.recordSuccess();

if (LOGGER.isDebugEnabled()) {
LOGGER.debug("PROVISIONING REFRESH successful, returned status: {}", status);
}

} else {
LOGGER.trace("Ignoring refresh of shadow {}, because the connector is not async");
result.recordNotApplicableIfUnknown();
}

return status;
}

private void computeResultStatus(OperationResult parentResult) {
OperationResultStatus status = OperationResultStatus.SUCCESS;
String asyncRef = null;
Expand Down
Expand Up @@ -74,6 +74,8 @@
import javax.xml.namespace.QName;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;

/**
Expand Down Expand Up @@ -610,6 +612,55 @@ public void deleteShadow(PrismObject<ShadowType> shadow, ProvisioningOperationOp
AvailabilityStatusType.UP, parentResult);
}

public void refreshShadow(PrismObject<ShadowType> shadow, ProvisioningOperationOptions options, Task task, OperationResult parentResult) throws ObjectNotFoundException, SchemaException, CommunicationException, ConfigurationException {
ShadowType shadowType = shadow.asObjectable();
List<PendingOperationType> pendingOperations = shadowType.getPendingOperation();
if (pendingOperations.isEmpty()) {
return;
}
sortOperations(pendingOperations);

ProvisioningContext ctx = ctxFactory.create(shadow, task, parentResult);
ctx.assertDefinition();

ObjectDelta<ShadowType> shadowDelta = shadow.createModifyDelta();
for (PendingOperationType pendingOperation: pendingOperations) {

String asyncRef = pendingOperation.getAsynchronousOperationReference();
if (asyncRef != null) {

OperationResultStatus status = resouceObjectConverter.refreshOperationStatus(ctx, shadow, asyncRef, parentResult);
if (status != null) {
OperationResultStatusType statusType = status.createStatusType();
if (!statusType.equals(pendingOperation.getResultStatus())) {
// TODO: check for grace period

ItemPath containerPath = pendingOperation.asPrismContainerValue().getPath();
PropertyDelta<OperationResultStatusType> statusDelta = shadowDelta.createPropertyModification(containerPath.subPath(PendingOperationType.F_RESULT_STATUS));
statusDelta.setValuesToReplace(new PrismPropertyValue<>(statusType));
shadowDelta.addModification(statusDelta);
if (status != OperationResultStatus.IN_PROGRESS || status != OperationResultStatus.UNKNOWN && pendingOperation.getCompletionTimestamp() == null) {
PropertyDelta<Object> timestampDelta = shadowDelta.createPropertyModification(containerPath.subPath(PendingOperationType.F_COMPLETION_TIMESTAMP));
timestampDelta.setValuesToReplace(new PrismPropertyValue<>(clock.currentTimeXMLGregorianCalendar()));
shadowDelta.addModification(timestampDelta);
}

// TODO: update cached attributes (delta)

// TODO: notify
}
}
}


// TODO: check for expiration (grace period)
}
}

private void sortOperations(List<PendingOperationType> pendingOperations) {
Collections.sort(pendingOperations, (o1,o2) -> XmlTypeConverter.compare(o1.getRequestTimestamp(), o2.getRequestTimestamp()) );
}

public void applyDefinition(ObjectDelta<ShadowType> delta, ShadowType shadowTypeWhenNoOid,
OperationResult parentResult) throws SchemaException, ObjectNotFoundException,
CommunicationException, ConfigurationException {
Expand Down

0 comments on commit 7809449

Please sign in to comment.