Skip to content

Commit

Permalink
Capability definition per object type (MID-4650)
Browse files Browse the repository at this point in the history
  • Loading branch information
semancik committed Aug 24, 2018
1 parent d954be6 commit 59ddc5d
Show file tree
Hide file tree
Showing 14 changed files with 388 additions and 35 deletions.
Expand Up @@ -1033,7 +1033,10 @@ public void sync(ObjectClass objectClass, SyncToken token, SyncResultsHandler ha
if (deltaObjectClass == DummyAccount.class) {
DummyAccount account = resource.getAccountById(delta.getObjectId());
if (account == null) {
throw new IllegalStateException("We have delta for account '"+delta.getObjectId()+"' but such account does not exist");
// We have delta for object that does not exist any more. It was probably deleted in the meantime.
// Just skip the delta.
log.warn("We have delta for account '"+delta.getObjectId()+"' but such account does not exist, skipping delta");
continue;
}
ConnectorObject cobject = convertToConnectorObject(account, attributesToGet);
deltaBuilder.setObject(cobject);
Expand Down
Expand Up @@ -71,6 +71,9 @@ public static boolean isSelected(ErrorSelectorType selector, Throwable exception
if (exception instanceof ConfigurationException || exception instanceof ExpressionEvaluationException) {
return isSelected(selector.isConfiguration(), defaultValue);
}
if (exception instanceof UnsupportedOperationException) {
return isSelected(selector.isUnsupported(), defaultValue);
}
return isSelected(selector.isGeneric(), defaultValue);
}

Expand Down
Expand Up @@ -719,4 +719,12 @@ public static boolean isRefreshOnRead(ResourceType resource) {
}
return reshreshOnRead;
}

public static ErrorSelectorType getConnectorErrorCriticality(ResourceType resourceType) {
ResourceConsistencyType consistency = resourceType.getConsistency();
if (consistency == null) {
return null;
}
return consistency.getConnectorErrorCriticality();
}
}
Expand Up @@ -6314,6 +6314,19 @@
</xsd:documentation>
</xsd:annotation>
</xsd:element>
<xsd:element name="unsupported" type="xsd:boolean" minOccurs="0" default="false">
<xsd:annotation>
<xsd:documentation>
Selects "unsupported operation" issues. These errors are caused by
lack of functionality or support for the operation in connector
or any other part of midPoint. E.g. this may be missing (or disabled)
connector capability.
</xsd:documentation>
<xsd:appinfo>
<a:since>3.9</a:since>
</xsd:appinfo>
</xsd:annotation>
</xsd:element>
<xsd:element name="generic" type="xsd:boolean" minOccurs="0" default="false">
<xsd:annotation>
<xsd:documentation>
Expand Down
Expand Up @@ -137,7 +137,7 @@ private void locateDefinitions() {
public <O extends ObjectType> boolean executeChanges(LensContext<O> context, Task task,
OperationResult parentResult) throws ObjectAlreadyExistsException, ObjectNotFoundException,
SchemaException, CommunicationException, ConfigurationException,
SecurityViolationException, ExpressionEvaluationException, PreconditionViolationException {
SecurityViolationException, ExpressionEvaluationException, PreconditionViolationException, PolicyViolationException {

OperationResult result = parentResult.createSubresult(OPERATION_EXECUTE);

Expand Down Expand Up @@ -345,6 +345,9 @@ public <O extends ObjectType> boolean executeChanges(LensContext<O> context, Tas
if (focusContext != null) {
updateLinks(focusContext, projCtx, shadowAfterModification, task, subResult);
}

Utils.handleConnectorErrorCriticality(projCtx.getResource(), e);


} catch (ObjectAlreadyExistsException e) {

Expand Down
Expand Up @@ -38,6 +38,7 @@
import com.evolveum.midpoint.model.impl.lens.projector.credentials.ProjectionCredentialsProcessor;
import com.evolveum.midpoint.model.impl.lens.projector.focus.AssignmentProcessor;
import com.evolveum.midpoint.model.impl.lens.projector.focus.FocusProcessor;
import com.evolveum.midpoint.model.impl.util.Utils;
import com.evolveum.midpoint.prism.xml.XmlTypeConverter;
import com.evolveum.midpoint.repo.api.PreconditionViolationException;
import com.evolveum.midpoint.schema.ResourceShadowDiscriminator;
Expand All @@ -46,6 +47,7 @@
import com.evolveum.midpoint.schema.result.OperationResult;
import com.evolveum.midpoint.schema.result.OperationResultStatus;
import com.evolveum.midpoint.schema.util.ExceptionUtil;
import com.evolveum.midpoint.schema.util.ResourceTypeUtil;
import com.evolveum.midpoint.task.api.Task;
import com.evolveum.midpoint.util.exception.CommunicationException;
import com.evolveum.midpoint.util.exception.ConfigurationException;
Expand Down Expand Up @@ -412,31 +414,7 @@ private <F extends ObjectType> void projectProjection(LensContext<F> context, Le

result.recordFatalError(e);

ResourceType resourceType = projectionContext.getResource();
if (resourceType == null) {
throw e;
} else {
ErrorSelectorType errorSelector = null;
if (resourceType.getConsistency() != null) {
errorSelector = resourceType.getConsistency().getConnectorErrorCriticality();
}
if (errorSelector == null) {
if (e instanceof CommunicationException) {
// Just continue evaluation. The error is recorded in the result.
// The consistency mechanism has (most likely) already done the best.
// We cannot do any better.
} else {
throw e;
}
} else {
if (ExceptionUtil.isSelected(errorSelector, e, true)) {
throw e;
} else {
LOGGER.warn("Exception {} selected as non-critical in {}, continuing evaluation; exception message: {}", e.getClass().getSimpleName(), resourceType, e.getMessage());
// Just continue evaluation. The error is recorded in the result.
}
}
}
Utils.handleConnectorErrorCriticality(projectionContext.getResource(), e);
}


Expand Down
@@ -1,5 +1,5 @@
/*
* Copyright (c) 2010-2017 Evolveum
* Copyright (c) 2010-2018 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 @@ -35,6 +35,7 @@
import com.evolveum.midpoint.prism.delta.ObjectDelta;
import com.evolveum.midpoint.prism.marshaller.QueryConvertor;
import com.evolveum.midpoint.prism.query.*;
import com.evolveum.midpoint.repo.api.PreconditionViolationException;
import com.evolveum.midpoint.repo.api.RepositoryService;
import com.evolveum.midpoint.schema.ObjectDeltaOperation;
import com.evolveum.midpoint.schema.ResourceShadowDiscriminator;
Expand All @@ -43,11 +44,18 @@
import com.evolveum.midpoint.schema.constants.SchemaConstants;
import com.evolveum.midpoint.schema.processor.ObjectClassComplexTypeDefinition;
import com.evolveum.midpoint.schema.result.OperationResult;
import com.evolveum.midpoint.schema.util.ExceptionUtil;
import com.evolveum.midpoint.schema.util.ResourceTypeUtil;
import com.evolveum.midpoint.task.api.Task;
import com.evolveum.midpoint.util.Handler;
import com.evolveum.midpoint.util.exception.CommunicationException;
import com.evolveum.midpoint.util.exception.ConfigurationException;
import com.evolveum.midpoint.util.exception.ExpressionEvaluationException;
import com.evolveum.midpoint.util.exception.ObjectAlreadyExistsException;
import com.evolveum.midpoint.util.exception.ObjectNotFoundException;
import com.evolveum.midpoint.util.exception.PolicyViolationException;
import com.evolveum.midpoint.util.exception.SchemaException;
import com.evolveum.midpoint.util.exception.SecurityViolationException;
import com.evolveum.midpoint.util.exception.SystemException;
import com.evolveum.midpoint.util.logging.Trace;
import com.evolveum.midpoint.util.logging.TraceManager;
Expand Down Expand Up @@ -658,5 +666,62 @@ public static <V extends PrismValue, F extends ObjectType> List<V> evaluateScrip
// }
}
}

public static void handleConnectorErrorCriticality(ResourceType resourceType, Throwable e) throws ObjectNotFoundException, CommunicationException, SchemaException, ConfigurationException,
SecurityViolationException, PolicyViolationException, ExpressionEvaluationException, ObjectAlreadyExistsException, PreconditionViolationException {
if (resourceType == null) {
throwException(e);
} else {
ErrorSelectorType errorSelector = ResourceTypeUtil.getConnectorErrorCriticality(resourceType);
if (errorSelector == null) {
if (e instanceof CommunicationException) {
// Just continue evaluation. The error is recorded in the result.
// The consistency mechanism has (most likely) already done the best.
// We cannot do any better.
} else {
throwException(e);
}
} else {
if (ExceptionUtil.isSelected(errorSelector, e, true)) {
throwException(e);
} else {
LOGGER.warn("Exception {} selected as non-critical in {}, continuing evaluation; exception message: {}", e.getClass().getSimpleName(), resourceType, e.getMessage());
// Just continue evaluation. The error should be recorded in the result.
}
}
}
}

private static void throwException(Throwable e)
throws ObjectNotFoundException, CommunicationException, SchemaException, ConfigurationException,
SecurityViolationException, PolicyViolationException, ExpressionEvaluationException, ObjectAlreadyExistsException,
PreconditionViolationException {
if (e instanceof RuntimeException) {
throw (RuntimeException)e;
} else if (e instanceof Error) {
throw (Error)e;
} else if (e instanceof ObjectNotFoundException) {
throw (ObjectNotFoundException)e;
} else if (e instanceof ObjectNotFoundException) {
throw (CommunicationException)e;
} else if (e instanceof CommunicationException) {
throw (SchemaException)e;
} else if (e instanceof SchemaException) {
throw (ConfigurationException)e;
} else if (e instanceof ConfigurationException) {
throw (SecurityViolationException)e;
} else if (e instanceof SecurityViolationException) {
throw (PolicyViolationException)e;
} else if (e instanceof PolicyViolationException) {
throw (ExpressionEvaluationException)e;
} else if (e instanceof ExpressionEvaluationException) {
throw (ObjectAlreadyExistsException)e;
} else if (e instanceof ObjectAlreadyExistsException) {
throw (ObjectNotFoundException)e;
} else if (e instanceof PreconditionViolationException) {
throw (PreconditionViolationException)e;
} else {
throw new SystemException(e.getMessage(), e);
}
}
}
Expand Up @@ -1537,7 +1537,7 @@ protected PrismObject<UserType> getUser(String userOid) throws ObjectNotFoundExc
TestUtil.assertSuccess("getObject(User) result not success", result);
return user;
}

protected PrismObject<UserType> getUserFromRepo(String userOid) throws ObjectNotFoundException, SchemaException {
return repositoryService.getObject(UserType.class, userOid, null, new OperationResult("dummy"));
}
Expand Down Expand Up @@ -5416,6 +5416,15 @@ protected UserAsserter<Void> assertUserByUsername(String username, String messag
return asserter;
}

protected FocusAsserter<ServiceType,Void> assertService(String oid, String message) throws ObjectNotFoundException, SchemaException, SecurityViolationException, CommunicationException, ConfigurationException, ExpressionEvaluationException {
PrismObject<ServiceType> service = getObject(ServiceType.class, oid);
// TODO: change to ServiceAsserter later
FocusAsserter<ServiceType,Void> asserter = FocusAsserter.forFocus(service, message);
initializeAsserter(asserter);
asserter.assertOid(oid);
return asserter;
}

protected FocusAsserter<ServiceType,Void> assertServiceByName(String name, String message) throws ObjectNotFoundException, SchemaException, SecurityViolationException, CommunicationException, ConfigurationException, ExpressionEvaluationException {
PrismObject<ServiceType> service = findServiceByName(name);
// TODO: change to ServiceAsserter later
Expand All @@ -5429,6 +5438,18 @@ protected FocusAsserter<ServiceType,Void> assertServiceAfterByName(String name)
return assertServiceByName(name, "after");
}

protected FocusAsserter<ServiceType,Void> assertServiceBeforeByName(String name) throws ObjectNotFoundException, SchemaException, SecurityViolationException, CommunicationException, ConfigurationException, ExpressionEvaluationException {
return assertServiceByName(name, "before");
}

protected FocusAsserter<ServiceType,Void> assertServiceAfter(String oid) throws ObjectNotFoundException, SchemaException, SecurityViolationException, CommunicationException, ConfigurationException, ExpressionEvaluationException {
return assertService(oid, "after");
}

protected FocusAsserter<ServiceType,Void> assertServiceBefore(String oid) throws ObjectNotFoundException, SchemaException, SecurityViolationException, CommunicationException, ConfigurationException, ExpressionEvaluationException {
return assertService(oid, "before");
}

protected void assertNoServiceByName(String name) throws ObjectNotFoundException, SchemaException, SecurityViolationException, CommunicationException, ConfigurationException, ExpressionEvaluationException {
PrismObject<ServiceType> service = findServiceByName(name);
if (service != null) {
Expand Down
Expand Up @@ -60,6 +60,7 @@
import com.evolveum.midpoint.xml.ns._public.resource.capabilities_3.ActivationLockoutStatusCapabilityType;
import com.evolveum.midpoint.xml.ns._public.resource.capabilities_3.ActivationStatusCapabilityType;
import com.evolveum.midpoint.xml.ns._public.resource.capabilities_3.AddRemoveAttributeValuesCapabilityType;
import com.evolveum.midpoint.xml.ns._public.resource.capabilities_3.CapabilityType;
import com.evolveum.midpoint.xml.ns._public.resource.capabilities_3.CreateCapabilityType;
import com.evolveum.midpoint.xml.ns._public.resource.capabilities_3.DeleteCapabilityType;
import com.evolveum.midpoint.xml.ns._public.resource.capabilities_3.LiveSyncCapabilityType;
Expand Down Expand Up @@ -261,6 +262,8 @@ public AsynchronousOperationReturnValue<PrismObject<ShadowType>> addResourceObje
checkForAddConflicts(ctx, shadow, result);
}

checkForCapability(ctx, CreateCapabilityType.class, result);

Collection<Operation> additionalOperations = new ArrayList<>();
addExecuteScriptOperation(additionalOperations, ProvisioningOperationTypeType.ADD, scripts, resource,
result);
Expand All @@ -277,10 +280,6 @@ public AsynchronousOperationReturnValue<PrismObject<ShadowType>> addResourceObje
}
transformActivationAttributesAdd(ctx, shadowType, result);

if (!ResourceTypeUtil.isCreateCapabilityEnabled(resource)){
throw new UnsupportedOperationException("Resource does not support 'create' operation");
}

connectorAsyncOpRet = connector.addObject(shadowClone, additionalOperations, ctx, result);
resourceAttributesAfterAdd = connectorAsyncOpRet.getReturnValue();

Expand Down Expand Up @@ -384,6 +383,8 @@ public AsynchronousOperationResult deleteResourceObject(ProvisioningContext ctx,

LOGGER.trace("Deleting resource object {}", shadow);

checkForCapability(ctx, DeleteCapabilityType.class, result);

Collection<? extends ResourceAttribute<?>> identifiers = ShadowUtil
.getAllIdentifiers(shadow);

Expand Down Expand Up @@ -716,6 +717,8 @@ private AsynchronousOperationReturnValue<Collection<PropertyModificationOperatio
LOGGER.trace("Resource object modification operations: {}", operations);
}

checkForCapability(ctx, UpdateCapabilityType.class, parentResult);

if (!ShadowUtil.hasPrimaryIdentifier(identifiers, objectClassDefinition)) {
Collection<? extends ResourceAttribute<?>> primaryIdentifiers = resourceObjectReferenceResolver.resolvePrimaryIdentifier(ctx, identifiers, "modification of resource object "+identifiers, parentResult);
if (primaryIdentifiers == null || primaryIdentifiers.isEmpty()) {
Expand Down Expand Up @@ -2481,6 +2484,17 @@ private void computeResultStatus(OperationResult parentResult) {
parentResult.setStatus(status);
parentResult.setAsynchronousOperationReference(asyncRef);
}

private <C extends CapabilityType> void checkForCapability(ProvisioningContext ctx, Class<C> capabilityClass, OperationResult result) throws ObjectNotFoundException, SchemaException, CommunicationException, ConfigurationException, ExpressionEvaluationException {
C capability = ctx.getObjectClassDefinition().getEffectiveCapability(capabilityClass, ctx.getResource());
if (capability == null) {
UnsupportedOperationException e = new UnsupportedOperationException("Operation not supported "+ctx.getDesc()+" as "+capabilityClass.getSimpleName()+" is missing");
if (result != null) {
result.recordFatalError(e);
}
throw e;
}
}


}
Expand Up @@ -120,6 +120,11 @@ public ShadowAsserter<R> assertKind(ShadowKindType expected) {
return this;
}

public ShadowAsserter<R> assertIntent(String expected) {
assertEquals("Wrong intent in "+desc(), expected, getObject().asObjectable().getIntent());
return this;
}

public ShadowAsserter<R> assertIteration(Integer expected) {
assertEquals("Wrong iteration in "+desc(), expected, getObject().asObjectable().getIteration());
return this;
Expand Down

0 comments on commit 59ddc5d

Please sign in to comment.