use of com.evolveum.midpoint.provisioning.impl.ResourceObjectDiscriminator in project midpoint by Evolveum.
the class EntitlementConverter method collectEntitlementAsObjectOperation.
private <TV, TA> PrismObject<ShadowType> collectEntitlementAsObjectOperation(Map<ResourceObjectDiscriminator, ResourceObjectOperations> roMap, PrismContainerValue<ShadowAssociationType> associationValue, PrismObject<ShadowType> subjectShadowBefore, PrismObject<ShadowType> subjectShadowAfter, ModificationType modificationType, ProvisioningContext subjectCtx, OperationResult result) throws SchemaException, ObjectNotFoundException, CommunicationException, SecurityViolationException, ConfigurationException, ExpressionEvaluationException {
ResourceType resource = subjectCtx.getResource();
ShadowAssociationType associationBean = associationValue.asContainerable();
QName associationName = associationBean.getName();
if (associationName == null) {
throw new SchemaException("No name in entitlement association " + associationValue);
}
ResourceObjectDefinition subjectDef = subjectCtx.getObjectDefinitionRequired();
ResourceAssociationDefinition associationDef = subjectDef.findAssociationDefinitionRequired(associationName, () -> " in " + subjectCtx);
ResourceObjectAssociationDirectionType direction = associationDef.getDirection();
if (direction != ResourceObjectAssociationDirectionType.OBJECT_TO_SUBJECT) {
// Process just this one direction. The other direction was processed before
return subjectShadowAfter;
}
Collection<String> entitlementIntents = associationDef.getIntents();
if (entitlementIntents == null || entitlementIntents.isEmpty()) {
throw new SchemaException("No entitlement intent specified in association " + associationValue + " in " + resource);
}
ShadowKindType entitlementKind = associationDef.getKind();
for (String entitlementIntent : entitlementIntents) {
// TODO deduplicate the code
// todo error handling
ProvisioningContext entitlementCtx = subjectCtx.spawnForKindIntent(entitlementKind, entitlementIntent);
ResourceObjectDefinition entitlementOcDef = entitlementCtx.getObjectDefinitionRequired();
QName assocAttrName = associationDef.getDefinitionBean().getAssociationAttribute();
QName valueAttrName = associationDef.getDefinitionBean().getValueAttribute();
schemaCheck(assocAttrName != null, "No association attribute defined in entitlement association '%s' in %s", associationName, subjectCtx);
schemaCheck(valueAttrName != null, "No value attribute defined in entitlement association '%s' in %s", associationName, subjectCtx);
// noinspection unchecked
ResourceAttributeDefinition<TA> assocAttrDef = (ResourceAttributeDefinition<TA>) entitlementOcDef.findAttributeDefinition(assocAttrName);
if (assocAttrDef == null) {
throw new SchemaException("Association attribute '" + assocAttrName + "'defined in entitlement association " + "was not found in entitlement intent(s) '" + entitlementIntents + "' in schema for " + resource);
}
ResourceAttributeContainer identifiersContainer = getIdentifiersAttributeContainer(associationValue, entitlementOcDef);
Collection<ResourceAttribute<?>> entitlementIdentifiersFromAssociation = identifiersContainer.getAttributes();
ResourceObjectDiscriminator disc = new ResourceObjectDiscriminator(entitlementOcDef.getTypeName(), entitlementIdentifiersFromAssociation);
ResourceObjectOperations operations = roMap.get(disc);
if (operations == null) {
operations = new ResourceObjectOperations();
operations.setResourceObjectContext(entitlementCtx);
roMap.put(disc, operations);
}
// Which shadow would we use - shadowBefore or shadowAfter?
//
// If the operation is ADD or REPLACE, we use current version of the shadow (shadowAfter), because we want
// to ensure that we add most-recent data to the subject.
//
// If the operation is DELETE, we have two possibilities:
// - if the resource provides referential integrity, the subject has already
// new data (because the object operation was already carried out), so we use shadowAfter
// - if the resource does not provide referential integrity, the subject has OLD data
// so we use shadowBefore
PrismObject<ShadowType> subjectShadow;
if (modificationType != ModificationType.DELETE) {
subjectShadow = subjectShadowAfter;
} else {
if (associationDef.requiresExplicitReferentialIntegrity()) {
// we must ensure the referential integrity
subjectShadow = subjectShadowBefore;
} else {
// i.e. resource has ref integrity assured by itself
subjectShadow = subjectShadowAfter;
}
}
ResourceAttribute<TV> valueAttr = ShadowUtil.getAttribute(subjectShadow, valueAttrName);
if (valueAttr == null) {
if (!ShadowUtil.isFullShadow(subjectShadow)) {
Collection<ResourceAttribute<?>> subjectIdentifiers = ShadowUtil.getAllIdentifiers(subjectShadow);
LOGGER.trace("Fetching {} ({})", subjectShadow, subjectIdentifiers);
subjectShadow = resourceObjectReferenceResolver.fetchResourceObject(subjectCtx, subjectIdentifiers, null, subjectShadow, result);
subjectShadowAfter = subjectShadow;
valueAttr = ShadowUtil.getAttribute(subjectShadow, valueAttrName);
}
if (valueAttr == null) {
LOGGER.error("No value attribute {} in shadow\n{}", valueAttrName, subjectShadow.debugDump());
// TODO: check schema and try to fetch full shadow if necessary
throw new SchemaException("No value attribute " + valueAttrName + " in " + subjectShadow);
}
}
PropertyDelta<TA> attributeDelta = null;
for (Operation operation : operations.getOperations()) {
if (operation instanceof PropertyModificationOperation) {
PropertyModificationOperation<?> propOp = (PropertyModificationOperation<?>) operation;
if (propOp.getPropertyDelta().getElementName().equals(assocAttrName)) {
// noinspection unchecked
attributeDelta = (PropertyDelta<TA>) propOp.getPropertyDelta();
}
}
}
if (attributeDelta == null) {
attributeDelta = assocAttrDef.createEmptyDelta(ItemPath.create(ShadowType.F_ATTRIBUTES, assocAttrName));
}
PrismProperty<TA> changedAssocAttr = PrismUtil.convertProperty(valueAttr, assocAttrDef);
if (modificationType == ModificationType.ADD) {
attributeDelta.addValuesToAdd(changedAssocAttr.getClonedValues());
} else if (modificationType == ModificationType.DELETE) {
attributeDelta.addValuesToDelete(changedAssocAttr.getClonedValues());
} else if (modificationType == ModificationType.REPLACE) {
// TODO: check if already exists
attributeDelta.setValuesToReplace(changedAssocAttr.getClonedValues());
}
if (ResourceTypeUtil.isAvoidDuplicateValues(resource)) {
PrismObject<ShadowType> currentObjectShadow = operations.getCurrentShadow();
if (currentObjectShadow == null) {
LOGGER.trace("Fetching entitlement shadow {} to avoid value duplication (intent={})", entitlementIdentifiersFromAssociation, entitlementIntent);
currentObjectShadow = resourceObjectReferenceResolver.fetchResourceObject(entitlementCtx, entitlementIdentifiersFromAssociation, null, null, result);
operations.setCurrentShadow(currentObjectShadow);
}
// TODO It seems that duplicate values are checked twice: once here and the second time
// in ResourceObjectConverter.executeModify. Check that and fix if necessary.
PropertyDelta<TA> attributeDeltaAfterNarrow = ProvisioningUtil.narrowPropertyDelta(attributeDelta, currentObjectShadow, associationDef.getMatchingRule(), matchingRuleRegistry);
if (attributeDeltaAfterNarrow == null || attributeDeltaAfterNarrow.isEmpty()) {
LOGGER.trace("Not collecting entitlement object operations ({}) association {}: " + "attribute delta is empty after narrow, orig delta: {}", modificationType, associationName.getLocalPart(), attributeDelta);
}
attributeDelta = attributeDeltaAfterNarrow;
}
if (attributeDelta != null && !attributeDelta.isEmpty()) {
PropertyModificationOperation<?> attributeModification = new PropertyModificationOperation<>(attributeDelta);
attributeModification.setMatchingRuleQName(associationDef.getMatchingRule());
LOGGER.trace("Collecting entitlement object operations ({}) association {}: {}", modificationType, associationName.getLocalPart(), attributeModification);
operations.add(attributeModification);
}
}
return subjectShadowAfter;
}
use of com.evolveum.midpoint.provisioning.impl.ResourceObjectDiscriminator in project midpoint by Evolveum.
the class EntitlementConverter method collectEntitlementsAsObjectOperationDelete.
// endregion
// region Delete
// ///////
// DELETE
// ///////
/**
* This is somehow different that all the other methods. We are not following the content of a shadow or delta.
* We are following the definitions. This is to avoid the need to read the object that is going to be deleted.
* In fact, the object should not be there any more, but we still want to clean up entitlement membership
* based on the information from the shadow.
*
* @param <T> Type of the association (referencing) attribute
*/
<T> void collectEntitlementsAsObjectOperationDelete(Map<ResourceObjectDiscriminator, ResourceObjectOperations> roMap, PrismObject<ShadowType> subjectShadow, ProvisioningContext subjectCtx, OperationResult result) throws SchemaException, CommunicationException, ObjectNotFoundException, ConfigurationException, SecurityViolationException, ExpressionEvaluationException {
ResourceObjectDefinition subjectDef = subjectCtx.getObjectDefinitionRequired();
Collection<ResourceAssociationDefinition> associationDefs = subjectDef.getAssociationDefinitions();
if (associationDefs.isEmpty()) {
LOGGER.trace("No associations in deleted shadow, nothing to do");
return;
}
ResourceAttributeContainer subjectAttributesContainer = ShadowUtil.getAttributesContainer(subjectShadow);
for (ResourceAssociationDefinition associationDef : associationDefs) {
if (associationDef.getDirection() != ResourceObjectAssociationDirectionType.OBJECT_TO_SUBJECT) {
// We can ignore these. They will die together with the object. No need to explicitly delete them.
LOGGER.trace("Ignoring subject-to-object association in deleted shadow");
continue;
}
if (!associationDef.requiresExplicitReferentialIntegrity()) {
LOGGER.trace("Ignoring association in deleted shadow because it does not require explicit" + " referential integrity assurance");
continue;
}
if (associationDef.getAuxiliaryObjectClass() != null && !subjectDef.hasAuxiliaryObjectClass(associationDef.getAuxiliaryObjectClass())) {
LOGGER.trace("Ignoring association in deleted shadow because subject does not have {} auxiliary object class", associationDef.getAuxiliaryObjectClass());
continue;
}
QName associationName = associationDef.getName();
ShadowKindType entitlementKind = associationDef.getKind();
for (String entitlementIntent : associationDef.getIntents()) {
// TODO deduplicate the code
// TODO error handling
ProvisioningContext entitlementCtx = subjectCtx.spawnForKindIntent(entitlementKind, entitlementIntent);
ResourceObjectDefinition entitlementDef = entitlementCtx.getObjectDefinitionRequired();
QName assocAttrName = associationDef.getDefinitionBean().getAssociationAttribute();
QName valueAttrName = associationDef.getDefinitionBean().getValueAttribute();
schemaCheck(assocAttrName != null, "No association attribute defined in entitlement association '%s' in %s", associationName, subjectCtx);
schemaCheck(valueAttrName != null, "No value attribute defined in entitlement association '%s' in %s", associationName, subjectCtx);
// noinspection unchecked
ResourceAttributeDefinition<T> assocAttrDef = (ResourceAttributeDefinition<T>) entitlementDef.findAttributeDefinitionRequired(assocAttrName, () -> " in entitlement association '" + associationName + "' in " + entitlementCtx + " [association attribute]");
final ResourceAttribute<T> valueAttr = subjectAttributesContainer.findAttribute(valueAttrName);
if (valueAttr == null || valueAttr.isEmpty()) {
// Although we cannot really remedy the situation now, we at least throw an error so the problem is detected.
throw new SchemaException("Value attribute " + valueAttrName + " has no value; attribute defined in entitlement " + "association '" + associationName + "' in " + subjectCtx);
}
if (valueAttr.size() > 1) {
throw new SchemaException("Value attribute " + valueAttrName + " has no more than one value; attribute defined" + " in entitlement association '" + associationName + "' in " + subjectCtx);
}
ResourceAttributeDefinition<T> valueAttrDef = valueAttr.getDefinition();
PrismPropertyValue<T> valueAttrValue = valueAttr.getAnyValue();
ObjectQuery query = createEntitlementQuery(valueAttrValue, valueAttrDef, assocAttrDef, associationDef);
AttributesToReturn attributesToReturn = ProvisioningUtil.createAttributesToReturn(entitlementCtx);
SearchHierarchyConstraints searchHierarchyConstraints = determineSearchHierarchyConstraints(entitlementCtx, result);
ObjectHandler handler = (ucfObject, lResult) -> {
PrismObject<ShadowType> entitlementShadow = ucfObject.getResourceObject();
Collection<? extends ResourceAttribute<?>> primaryIdentifiers = ShadowUtil.getPrimaryIdentifiers(entitlementShadow);
ResourceObjectDiscriminator disc = new ResourceObjectDiscriminator(entitlementDef.getTypeName(), primaryIdentifiers);
ResourceObjectOperations operations = roMap.get(disc);
if (operations == null) {
operations = new ResourceObjectOperations();
roMap.put(disc, operations);
operations.setResourceObjectContext(entitlementCtx);
Collection<? extends ResourceAttribute<?>> allIdentifiers = ShadowUtil.getAllIdentifiers(entitlementShadow);
operations.setAllIdentifiers(allIdentifiers);
}
PropertyDelta<T> attributeDelta = null;
for (Operation operation : operations.getOperations()) {
if (operation instanceof PropertyModificationOperation) {
PropertyModificationOperation<?> propOp = (PropertyModificationOperation<?>) operation;
if (propOp.getPropertyDelta().getElementName().equals(assocAttrName)) {
// noinspection unchecked
attributeDelta = (PropertyDelta<T>) propOp.getPropertyDelta();
}
}
}
if (attributeDelta == null) {
attributeDelta = assocAttrDef.createEmptyDelta(ItemPath.create(ShadowType.F_ATTRIBUTES, assocAttrName));
PropertyModificationOperation<?> attributeModification = new PropertyModificationOperation<>(attributeDelta);
attributeModification.setMatchingRuleQName(associationDef.getMatchingRule());
operations.add(attributeModification);
}
attributeDelta.addValuesToDelete(valueAttr.getClonedValues());
LOGGER.trace("Association in deleted shadow delta:\n{}", attributeDelta.debugDumpLazily());
return true;
};
try {
LOGGER.trace("Searching for associations in deleted shadow, query: {}", query);
ConnectorInstance connector = subjectCtx.getConnector(ReadCapabilityType.class, result);
connector.search(entitlementDef, query, handler, attributesToReturn, null, searchHierarchyConstraints, UcfFetchErrorReportingMethod.EXCEPTION, subjectCtx.getUcfExecutionContext(), result);
} catch (TunnelException e) {
throw (SchemaException) e.getCause();
} catch (GenericFrameworkException e) {
throw new GenericConnectorException(e.getMessage(), e);
}
}
}
}
Aggregations