Skip to content

Commit

Permalink
Merge branch 'master' of github.com:Evolveum/midpoint
Browse files Browse the repository at this point in the history
  • Loading branch information
1azyman committed Jun 25, 2015
2 parents 8257789 + 446822f commit 1baa7cb
Show file tree
Hide file tree
Showing 8 changed files with 168 additions and 34 deletions.
Expand Up @@ -446,5 +446,13 @@ public static String getHumanReadableName(ShadowType shadowType) {
}
return getHumanReadableName(shadowType.asPrismObject());
}

public static boolean isFullShadow(PrismObject<ShadowType> shadow) {
ShadowType shadowType = shadow.asObjectable();
if (shadowType.getCachingMetadata() == null) {
return false;
}
return shadowType.getCachingMetadata().getRetrievalTimestamp() != null;
}

}
15 changes: 15 additions & 0 deletions infra/schema/src/main/resources/xml/ns/public/common/common-3.xsd
Expand Up @@ -6319,6 +6319,21 @@
</xsd:documentation>
</xsd:annotation>
</xsd:element>
<xsd:element ref="tns:cachingMetadata" minOccurs="0">
<xsd:annotation>
<xsd:documentation>
The shadow is essentially a bunch of metadata and cached data
about the actual resource object. This caching metadata structure
describes when the cached data were retrieved - therefore how fresh
they are.
For now the cachingMetadata are not stored in the repository.
This is now a "virtual" and "transient" property filled in
when the full shadow is retrieved from the resource.
Later on this can get a more proper meaning when we implement
full attribute caching.
</xsd:documentation>
</xsd:annotation>
</xsd:element>
</xsd:sequence>
</xsd:extension>
</xsd:complexContent>
Expand Down
Expand Up @@ -330,14 +330,14 @@ public void processEntitlementsAdd(ProvisioningContext ctx, PrismObject<ShadowTy
}
}

public <T> void collectEntitlementsAsObjectOperationInShadowAdd(ProvisioningContext ctx, Map<ResourceObjectDiscriminator, ResourceObjectOperations> roMap,
public <T> PrismObject<ShadowType> collectEntitlementsAsObjectOperationInShadowAdd(ProvisioningContext ctx, Map<ResourceObjectDiscriminator, ResourceObjectOperations> roMap,
PrismObject<ShadowType> shadow, OperationResult result)
throws SchemaException, ObjectNotFoundException, CommunicationException, SecurityViolationException, ConfigurationException {
PrismContainer<ShadowAssociationType> associationContainer = shadow.findContainer(ShadowType.F_ASSOCIATION);
if (associationContainer == null || associationContainer.isEmpty()) {
return;
return shadow;
}
collectEntitlementsAsObjectOperation(ctx, roMap, associationContainer.getValues(), null, shadow,
return collectEntitlementsAsObjectOperation(ctx, roMap, associationContainer.getValues(), null, shadow,
ModificationType.ADD, result);
}

Expand All @@ -361,17 +361,18 @@ public void collectEntitlementChange(ProvisioningContext ctx, ContainerDelta<Sha
operations.addAll(operationsMap.values());
}

public <T> void collectEntitlementsAsObjectOperation(ProvisioningContext ctx, Map<ResourceObjectDiscriminator, ResourceObjectOperations> roMap,
public <T> PrismObject<ShadowType> collectEntitlementsAsObjectOperation(ProvisioningContext ctx, Map<ResourceObjectDiscriminator, ResourceObjectOperations> roMap,
ContainerDelta<ShadowAssociationType> containerDelta,
PrismObject<ShadowType> subjectShadowBefore, PrismObject<ShadowType> subjectShadowAfter,
OperationResult result)
throws SchemaException, ObjectNotFoundException, CommunicationException, SecurityViolationException, ConfigurationException {
collectEntitlementsAsObjectOperation(ctx, roMap, containerDelta.getValuesToAdd(),
subjectShadowAfter = collectEntitlementsAsObjectOperation(ctx, roMap, containerDelta.getValuesToAdd(),
subjectShadowBefore, subjectShadowAfter, ModificationType.ADD, result);
collectEntitlementsAsObjectOperation(ctx, roMap, containerDelta.getValuesToDelete(),
subjectShadowAfter = collectEntitlementsAsObjectOperation(ctx, roMap, containerDelta.getValuesToDelete(),
subjectShadowBefore, subjectShadowAfter, ModificationType.DELETE, result);
collectEntitlementsAsObjectOperation(ctx, roMap, containerDelta.getValuesToReplace(),
subjectShadowAfter = collectEntitlementsAsObjectOperation(ctx, roMap, containerDelta.getValuesToReplace(),
subjectShadowBefore, subjectShadowAfter, ModificationType.REPLACE, result);
return subjectShadowAfter;
}

/////////
Expand Down Expand Up @@ -586,21 +587,22 @@ private <T> void collectEntitlementToAttrDelta(ProvisioningContext ctx, Map<QNam
}
}

private <T> void collectEntitlementsAsObjectOperation(ProvisioningContext ctx, Map<ResourceObjectDiscriminator, ResourceObjectOperations> roMap,
private <T> PrismObject<ShadowType> collectEntitlementsAsObjectOperation(ProvisioningContext ctx, Map<ResourceObjectDiscriminator, ResourceObjectOperations> roMap,
Collection<PrismContainerValue<ShadowAssociationType>> set,
PrismObject<ShadowType> subjectShadowBefore, PrismObject<ShadowType> subjectShadowAfter,
ModificationType modificationType, OperationResult result)
throws SchemaException, ObjectNotFoundException, CommunicationException, SecurityViolationException, ConfigurationException {
if (set == null) {
return;
return subjectShadowAfter;
}
for (PrismContainerValue<ShadowAssociationType> associationCVal: set) {
collectEntitlementAsObjectOperation(ctx, roMap, associationCVal, subjectShadowBefore, subjectShadowAfter,
subjectShadowAfter = collectEntitlementAsObjectOperation(ctx, roMap, associationCVal, subjectShadowBefore, subjectShadowAfter,
modificationType, result);
}
return subjectShadowAfter;
}

private <TV,TA> void collectEntitlementAsObjectOperation(ProvisioningContext subjectCtx, Map<ResourceObjectDiscriminator, ResourceObjectOperations> roMap,
private <TV,TA> PrismObject<ShadowType> collectEntitlementAsObjectOperation(ProvisioningContext subjectCtx, Map<ResourceObjectDiscriminator, ResourceObjectOperations> roMap,
PrismContainerValue<ShadowAssociationType> associationCVal,
PrismObject<ShadowType> subjectShadowBefore, PrismObject<ShadowType> subjectShadowAfter,
ModificationType modificationType, OperationResult result)
Expand All @@ -619,7 +621,7 @@ private <TV,TA> void collectEntitlementAsObjectOperation(ProvisioningContext sub
ResourceObjectAssociationDirectionType direction = assocDefType.getResourceObjectAssociationType().getDirection();
if (direction != ResourceObjectAssociationDirectionType.OBJECT_TO_SUBJECT) {
// Process just this one direction. The other direction was processed before
return;
return subjectShadowAfter;
}

Collection<String> entitlementIntents = assocDefType.getIntents();
Expand All @@ -645,9 +647,9 @@ private <TV,TA> void collectEntitlementAsObjectOperation(ProvisioningContext sub

ResourceAttributeContainer identifiersContainer =
ShadowUtil.getAttributesContainer(associationCVal, ShadowAssociationType.F_IDENTIFIERS);
Collection<ResourceAttribute<?>> identifiers = identifiersContainer.getAttributes();
Collection<ResourceAttribute<?>> entitlementIdentifiers = identifiersContainer.getAttributes();

ResourceObjectDiscriminator disc = new ResourceObjectDiscriminator(entitlementOcDef.getTypeName(), identifiers);
ResourceObjectDiscriminator disc = new ResourceObjectDiscriminator(entitlementOcDef.getTypeName(), entitlementIdentifiers);
ResourceObjectOperations operations = roMap.get(disc);
if (operations == null) {
operations = new ResourceObjectOperations();
Expand All @@ -670,23 +672,33 @@ private <TV,TA> void collectEntitlementAsObjectOperation(ProvisioningContext sub
// 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> shadow;
PrismObject<ShadowType> subjectShadow;
if (modificationType != ModificationType.DELETE) {
shadow = subjectShadowAfter;
subjectShadow = subjectShadowAfter;
} else {
if (assocDefType.requiresExplicitReferentialIntegrity()) {
// we must ensure the referential integrity
shadow = subjectShadowBefore;
subjectShadow = subjectShadowBefore;
} else {
// i.e. resource has ref integrity assured by itself
shadow = subjectShadowAfter;
subjectShadow = subjectShadowAfter;
}
}

ResourceAttribute<TV> valueAttr = ShadowUtil.getAttribute(shadow, valueAttrName);
ResourceAttribute<TV> valueAttr = ShadowUtil.getAttribute(subjectShadow, valueAttrName);
if (valueAttr == null) {
// TODO: check schema and try to fetch full shadow if necessary
throw new SchemaException("No value attribute "+valueAttrName+" in shadow");
if (!ShadowUtil.isFullShadow(subjectShadow)) {
Collection<ResourceAttribute<?>> subjectIdentifiers = ShadowUtil.getIdentifiers(subjectShadow);
LOGGER.trace("Fetching {} ({})", subjectShadow, subjectIdentifiers);
subjectShadow = resourceObjectReferenceResolver.fetchResourceObject(subjectCtx, subjectIdentifiers, null, 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;
Expand Down Expand Up @@ -716,7 +728,7 @@ private <TV,TA> void collectEntitlementAsObjectOperation(ProvisioningContext sub
if (ResourceTypeUtil.isAvoidDuplicateValues(resource)) {
PrismObject<ShadowType> currentObjectShadow = operations.getCurrentShadow();
if (currentObjectShadow == null) {
currentObjectShadow = resourceObjectReferenceResolver.fetchResourceObject(entitlementCtx, identifiers, null, result);
currentObjectShadow = resourceObjectReferenceResolver.fetchResourceObject(entitlementCtx, entitlementIdentifiers, null, result);
operations.setCurrentShadow(currentObjectShadow);
}
// TODO it seems that duplicate values are checked twice: once here and the second time in ResourceObjectConverter.executeModify
Expand All @@ -729,7 +741,9 @@ private <TV,TA> void collectEntitlementAsObjectOperation(ProvisioningContext sub
attributeModification.setMatchingRuleQName(assocDefType.getMatchingRule());
operations.getOperations().add(attributeModification);
}

}
return subjectShadowAfter;
}


Expand Down
Expand Up @@ -41,6 +41,7 @@
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import com.evolveum.midpoint.common.Clock;
import com.evolveum.midpoint.common.ResourceObjectPattern;
import com.evolveum.midpoint.common.refinery.RefinedAttributeDefinition;
import com.evolveum.midpoint.common.refinery.RefinedObjectClassDefinition;
Expand Down Expand Up @@ -102,6 +103,7 @@
import com.evolveum.midpoint.util.logging.TraceManager;
import com.evolveum.midpoint.xml.ns._public.common.common_3.ActivationStatusType;
import com.evolveum.midpoint.xml.ns._public.common.common_3.ActivationType;
import com.evolveum.midpoint.xml.ns._public.common.common_3.CachingMetadataType;
import com.evolveum.midpoint.xml.ns._public.common.common_3.LockoutStatusType;
import com.evolveum.midpoint.xml.ns._public.common.common_3.OperationProvisioningScriptType;
import com.evolveum.midpoint.xml.ns._public.common.common_3.OperationProvisioningScriptsType;
Expand Down Expand Up @@ -143,6 +145,9 @@ public class ResourceObjectConverter {

@Autowired(required=true)
private ResourceObjectReferenceResolver resourceObjectReferenceResolver;

@Autowired(required=true)
private Clock clock;

@Autowired(required=true)
private PrismContext prismContext;
Expand Down Expand Up @@ -488,7 +493,7 @@ public Collection<PropertyModificationOperation> modifyResourceObject(
}

// Execute entitlement modification on other objects (if needed)
executeEntitlementChangesModify(ctx, shadowBefore, shadowAfter, scripts, itemDeltas, parentResult);
shadowAfter = executeEntitlementChangesModify(ctx, shadowBefore, shadowAfter, scripts, itemDeltas, parentResult);

parentResult.recordSuccess();
return sideEffectChanges;
Expand Down Expand Up @@ -872,18 +877,19 @@ private Collection<PropertyModificationOperation> distillRenameDeltas(Collection
return deltas;
}

private void executeEntitlementChangesAdd(ProvisioningContext ctx, PrismObject<ShadowType> shadow, OperationProvisioningScriptsType scripts,
private PrismObject<ShadowType> executeEntitlementChangesAdd(ProvisioningContext ctx, PrismObject<ShadowType> shadow, OperationProvisioningScriptsType scripts,
OperationResult parentResult) throws SchemaException, ObjectNotFoundException, CommunicationException, SecurityViolationException, ConfigurationException, ObjectAlreadyExistsException {

Map<ResourceObjectDiscriminator, ResourceObjectOperations> roMap = new HashMap<>();

entitlementConverter.collectEntitlementsAsObjectOperationInShadowAdd(ctx, roMap, shadow, parentResult);
shadow = entitlementConverter.collectEntitlementsAsObjectOperationInShadowAdd(ctx, roMap, shadow, parentResult);

executeEntitlements(ctx, roMap, parentResult);

return shadow;
}

private void executeEntitlementChangesModify(ProvisioningContext ctx, PrismObject<ShadowType> subjectShadowBefore,
private PrismObject<ShadowType> executeEntitlementChangesModify(ProvisioningContext ctx, PrismObject<ShadowType> subjectShadowBefore,
PrismObject<ShadowType> subjectShadowAfter,
OperationProvisioningScriptsType scripts, Collection<? extends ItemDelta> objectDeltas, OperationResult parentResult) throws SchemaException, ObjectNotFoundException, CommunicationException, SecurityViolationException, ConfigurationException, ObjectAlreadyExistsException {

Expand All @@ -892,7 +898,7 @@ private void executeEntitlementChangesModify(ProvisioningContext ctx, PrismObjec
for (ItemDelta itemDelta : objectDeltas) {
if (new ItemPath(ShadowType.F_ASSOCIATION).equivalent(itemDelta.getPath())) {
ContainerDelta<ShadowAssociationType> containerDelta = (ContainerDelta<ShadowAssociationType>)itemDelta;
entitlementConverter.collectEntitlementsAsObjectOperation(ctx, roMap, containerDelta,
subjectShadowAfter = entitlementConverter.collectEntitlementsAsObjectOperation(ctx, roMap, containerDelta,
subjectShadowBefore, subjectShadowAfter, parentResult);

} else if (isRename(itemDelta)) {
Expand Down Expand Up @@ -931,10 +937,9 @@ private void executeEntitlementChangesModify(ProvisioningContext ctx, PrismObjec
}
}



executeEntitlements(ctx, roMap, parentResult);

return subjectShadowAfter;
}

private void executeEntitlementChangesDelete(ProvisioningContext ctx, PrismObject<ShadowType> shadow,
Expand Down Expand Up @@ -1517,6 +1522,7 @@ private PrismObject<ShadowType> postProcessResourceObjectRead(ProvisioningContex
ConnectorInstance connector = ctx.getConnector(parentResult);

ShadowType resourceObjectType = resourceObject.asObjectable();
setCachingMetadata(ctx, resourceObject);
setProtectedFlag(ctx, resourceObject);

// Simulated Activation
Expand Down Expand Up @@ -1549,6 +1555,12 @@ public void setProtectedFlag(ProvisioningContext ctx, PrismObject<ShadowType> re
}
}

public void setCachingMetadata(ProvisioningContext ctx, PrismObject<ShadowType> resourceObject) throws SchemaException, ConfigurationException, ObjectNotFoundException, CommunicationException {
CachingMetadataType cachingMetadata = new CachingMetadataType();
cachingMetadata.setRetrievalTimestamp(clock.currentTimeXMLGregorianCalendar());
resourceObject.asObjectable().setCachingMetadata(cachingMetadata);
}

/**
* Completes activation state by determinig simulated activation if
* necessary.
Expand Down
Expand Up @@ -1609,6 +1609,8 @@ private PrismObject<ShadowType> completeShadow(ProvisioningContext ctx, PrismObj
}
}

resultShadowType.setCachingMetadata(resourceShadowType.getCachingMetadata());

// Sanity asserts to catch some exotic bugs
PolyStringType resultName = resultShadow.asObjectable().getName();
assert resultName != null : "No name generated in "+resultShadow;
Expand Down
Expand Up @@ -631,6 +631,8 @@ public PrismObject<ShadowType> createRepositoryShadow(ProvisioningContext ctx, P
}

normalizeAttributes(repoShadow, ctx.getObjectClassDefinition());

repoShadowType.setCachingMetadata(null);

return repoShadow;
}
Expand Down
Expand Up @@ -40,6 +40,7 @@
import java.util.Set;

import javax.xml.bind.JAXBException;
import javax.xml.datatype.XMLGregorianCalendar;
import javax.xml.namespace.QName;

import com.evolveum.midpoint.prism.PrismContext;
Expand Down Expand Up @@ -1108,6 +1109,8 @@ public void test102GetAccount() throws Exception {
OperationResult result = new OperationResult(TestDummy.class.getName()
+ "." + TEST_NAME);
rememberShadowFetchOperationCount();

XMLGregorianCalendar startTs = clock.currentTimeXMLGregorianCalendar();

// WHEN
ShadowType shadow = provisioningService.getObject(ShadowType.class, ACCOUNT_WILL_OID, null, null,
Expand All @@ -1118,6 +1121,8 @@ public void test102GetAccount() throws Exception {
display("getObject result", result);
TestUtil.assertSuccess(result);
assertShadowFetchOperationCountIncrement(1);

XMLGregorianCalendar endTs = clock.currentTimeXMLGregorianCalendar();

display("Retrieved account shadow", shadow);

Expand All @@ -1129,9 +1134,23 @@ public void test102GetAccount() throws Exception {

checkConsistency(shadow.asPrismObject());

checkCachingMetadata(shadow, startTs, endTs);
assertNoCachingMetadata(shadowRepo);

assertSteadyResource();
}

private void assertNoCachingMetadata(PrismObject<ShadowType> shadowRepo) {
assertNull("Unexpected caching metadata in "+shadowRepo, shadowRepo.asObjectable().getCachingMetadata());
}

private void checkCachingMetadata(ShadowType shadow, XMLGregorianCalendar startTs,
XMLGregorianCalendar endTs) {
CachingMetadataType cachingMetadata = shadow.getCachingMetadata();
assertNotNull("No caching metadata in "+shadow, cachingMetadata);
TestUtil.assertBetween("Wrong retrievalTimestamp in caching metadata in "+shadow, startTs, endTs, cachingMetadata.getRetrievalTimestamp());
}

protected void checkAccountWill(ShadowType shadow, OperationResult result) throws SchemaException, EncryptionException {
checkAccountShadow(shadow, result);
Collection<ResourceAttribute<?>> attributes = ShadowUtil.getAttributes(shadow);
Expand Down

0 comments on commit 1baa7cb

Please sign in to comment.