Skip to content

Commit

Permalink
Fixes related to handling of repository shadows in provisioning
Browse files Browse the repository at this point in the history
  • Loading branch information
semancik committed Mar 21, 2016
1 parent f09de09 commit 4ec815d
Show file tree
Hide file tree
Showing 5 changed files with 125 additions and 73 deletions.
Expand Up @@ -222,9 +222,8 @@ public ResourceType getResourceType() {
return structuralObjectClassDefinition.getResourceType();
}

public PrismObjectDefinition<ShadowType> getObjectDefinition() {
return structuralObjectClassDefinition.getObjectDefinition();
}
// Do NOT override getObjectDefinition(). It will work by itself.
// overriding it will just complicate things.

public ObjectClassComplexTypeDefinition getObjectClassDefinition() {
return structuralObjectClassDefinition.getObjectClassDefinition();
Expand Down
Expand Up @@ -283,7 +283,10 @@ public PrismObject<ShadowType> getShadow(String oid, PrismObject<ShadowType> rep
LOGGER.trace("Resource object fetched from resource:\n{}", resourceShadow.debugDump());
}

shadowManager.updateRepoShadow(shadowCtx, resourceShadow.asObjectable(), repositoryShadow.asObjectable(), parentResult);
repositoryShadow = shadowManager.updateShadow(shadowCtx, resourceShadow, repositoryShadow, parentResult);
if (LOGGER.isTraceEnabled()) {
LOGGER.trace("Repository shadow after update:\n{}", repositoryShadow.debugDump());
}
// Complete the shadow by adding attributes from the resource object
PrismObject<ShadowType> resultShadow = completeShadow(shadowCtx, resourceShadow, repositoryShadow, parentResult);

Expand Down Expand Up @@ -734,7 +737,7 @@ public boolean handle(PrismObject<ShadowType> resourceShadow) {
// This determines the definitions exactly. How the repo shadow should have proper kind/intent
ProvisioningContext shadowCtx = applyAttributesDefinition(ctx, repoShadow);

shadowManager.updateRepoShadow(shadowCtx, resourceShadow.asObjectable(), repoShadow.asObjectable(), parentResult);
repoShadow = shadowManager.updateShadow(shadowCtx, resourceShadow, repoShadow, parentResult);

resultShadow = completeShadow(shadowCtx, resourceShadow, repoShadow, parentResult);

Expand Down Expand Up @@ -1331,7 +1334,6 @@ void processChange(ProvisioningContext ctx, Change<ShadowType> change, PrismObje

if (oldShadow != null) {
applyAttributesDefinition(ctx, oldShadow);
ShadowType oldShadowType = oldShadow.asObjectable();

LOGGER.trace("Old shadow: {}", oldShadow);

Expand All @@ -1348,8 +1350,7 @@ void processChange(ProvisioningContext ctx, Change<ShadowType> change, PrismObje
PrismObject<ShadowType> currentShadow = completeShadow(ctx, change.getCurrentShadow(),
oldShadow, parentResult);
change.setCurrentShadow(currentShadow);
ShadowType currentShadowType = currentShadow.asObjectable();
shadowManager.updateRepoShadow(ctx, currentShadowType, oldShadowType, parentResult);
shadowManager.updateShadow(ctx, currentShadow, oldShadow, parentResult);
}

// FIXME: hack. the object delta must have oid specified.
Expand Down Expand Up @@ -1550,13 +1551,11 @@ private PrismObject<ShadowType> completeShadow(ProvisioningContext ctx, PrismObj
PrismProperty<QName> resultAuxOcProp = resultShadow.findOrCreateProperty(ShadowType.F_AUXILIARY_OBJECT_CLASS);
resultAuxOcProp.addAll(PrismPropertyValue.cloneCollection(resourceAuxOcProp.getValues()));
}


resultShadowType.setName(new PolyStringType(ShadowUtil.determineShadowName(resourceShadow)));
if (resultShadowType.getObjectClass() == null) {
resultShadowType.setObjectClass(resourceAttributesContainer.getDefinition().getTypeName());
}
if (resultShadowType.getName() == null) {
resultShadowType.setName(new PolyStringType(ShadowUtil.determineShadowName(resourceShadow)));
}
if (resultShadowType.getResource() == null) {
resultShadowType.setResourceRef(ObjectTypeUtil.createObjectRef(ctx.getResource()));
}
Expand Down
Expand Up @@ -69,7 +69,9 @@ public String afterAddOnResource(ProvisioningContext ctx, PrismObject<ShadowType
"Error while creating account shadow object to save in the reposiotory. AccountShadow is null.");
}

LOGGER.trace("Adding object with identifiers to the repository.");
if (LOGGER.isTraceEnabled()) {
LOGGER.trace("Adding repository shadow\n{}", shadow.debugDump());
}
String oid = null;
try {
ConstraintsChecker.onShadowAddOperation(shadow.asObjectable());
Expand Down
Expand Up @@ -816,64 +816,111 @@ public Collection<ItemDelta> updateShadow(ProvisioningContext ctx, PrismObject<S
/**
* Updates repository shadow based on shadow from resource. Handles rename cases,
* change of auxiliary object classes, etc.
* @returns repository shadow as it should look like after the update
*/
public void updateRepoShadow(ProvisioningContext ctx, ShadowType currentShadowType, ShadowType oldShadowType, OperationResult parentResult) throws SchemaException, ObjectNotFoundException, ObjectAlreadyExistsException, ConfigurationException, CommunicationException {
Collection<ResourceAttribute<?>> oldSecondaryIdentifiers = ShadowUtil.getSecondaryIdentifiers(oldShadowType);
if (oldSecondaryIdentifiers.isEmpty()){
return;
}
ResourceAttributeContainer newSecondaryIdentifiers = ShadowUtil.getAttributesContainer(currentShadowType);

//remember name before normalizing attributes
PolyString currentShadowName = ShadowUtil.determineShadowName(currentShadowType);
currentShadowType.setName(new PolyStringType(currentShadowName));
public PrismObject<ShadowType> updateShadow(ProvisioningContext ctx, PrismObject<ShadowType> currentResourceShadow,
PrismObject<ShadowType> oldRepoShadow, OperationResult parentResult) throws SchemaException, ObjectNotFoundException, ObjectAlreadyExistsException, ConfigurationException, CommunicationException {

Iterator<ResourceAttribute<?>> oldSecondaryIterator = oldSecondaryIdentifiers.iterator();
Collection<PropertyDelta> repoShadowDeltas = new ArrayList<PropertyDelta>();
while (oldSecondaryIterator.hasNext()){
ResourceAttribute<?> oldSecondaryIdentifier = oldSecondaryIterator.next();
ResourceAttribute newSecondaryIdentifier = newSecondaryIdentifiers.findAttribute(oldSecondaryIdentifier.getElementName());
Collection newValue = newSecondaryIdentifier.getRealValues();

if (!compareAttribute(ctx.getObjectClassDefinition(), newSecondaryIdentifier, oldSecondaryIdentifier)){
PropertyDelta<?> propertyDelta = PropertyDelta.createDelta(new ItemPath(ShadowType.F_ATTRIBUTES, oldSecondaryIdentifier.getElementName()), oldShadowType.asPrismObject().getDefinition());
propertyDelta.addValuesToDelete(PrismPropertyValue.cloneCollection((Collection)oldSecondaryIdentifier.getValues()));
propertyDelta.addValuesToAdd(PrismPropertyValue.cloneCollection((Collection)newSecondaryIdentifier.getValues()));
repoShadowDeltas.add(propertyDelta);
RefinedObjectClassDefinition ocDef = ctx.getObjectClassDefinition();
ObjectDelta<ShadowType> shadowDelta = oldRepoShadow.createModifyDelta();
PrismContainer<Containerable> currentResourceAttributesContainer = currentResourceShadow.findContainer(ShadowType.F_ATTRIBUTES);
PrismContainer<Containerable> oldRepoAttributesContainer = oldRepoShadow.findContainer(ShadowType.F_ATTRIBUTES);
for (Item<?, ?> currentResourceItem: currentResourceAttributesContainer.getValue().getItems()) {
if (currentResourceItem instanceof PrismProperty<?>) {
PrismProperty<?> currentResourceAttrProperty = (PrismProperty<?>)currentResourceItem;
RefinedAttributeDefinition<Object> attrDef = ocDef.findAttributeDefinition(currentResourceAttrProperty.getElementName());
if (ProvisioningUtil.shouldStoreAtributeInShadow(ocDef, attrDef.getName())) {
MatchingRule matchingRule = matchingRuleRegistry.getMatchingRule(attrDef.getMatchingRuleQName(), attrDef.getTypeName());
PrismProperty<Object> oldRepoAttributeProperty = oldRepoAttributesContainer.findProperty(currentResourceAttrProperty.getElementName());
if (oldRepoAttributeProperty == null ) {
PropertyDelta<?> attrAddDelta = currentResourceAttrProperty.createDelta();
for (PrismPropertyValue pval: currentResourceAttrProperty.getValues()) {
Object normalizedRealValue;
if (matchingRule == null) {
normalizedRealValue = pval.getValue();
} else {
normalizedRealValue = matchingRule.normalize(pval.getValue());
}
attrAddDelta.addValueToAdd(new PrismPropertyValue(normalizedRealValue));
}
shadowDelta.addModification(attrAddDelta);
} else {
if (attrDef.isSingleValue()) {
Object currentResourceRealValue = currentResourceAttrProperty.getRealValue();
Object currentResourceNormalizedRealValue;
if (matchingRule == null) {
currentResourceNormalizedRealValue = currentResourceRealValue;
} else {
currentResourceNormalizedRealValue = matchingRule.normalize(currentResourceRealValue);
}
if (!currentResourceNormalizedRealValue.equals(oldRepoAttributeProperty.getRealValue())) {
shadowDelta.addModificationReplaceProperty(currentResourceAttrProperty.getPath(), currentResourceNormalizedRealValue);
}
} else {
PrismProperty<Object> normalizedCurrentResourceAttrProperty = (PrismProperty<Object>) currentResourceAttrProperty.clone();
if (matchingRule != null) {
for (PrismPropertyValue pval: normalizedCurrentResourceAttrProperty.getValues()) {
Object normalizedRealValue = matchingRule.normalize(pval.getValue());
pval.setValue(normalizedRealValue);
}
}
PropertyDelta<Object> attrDiff = normalizedCurrentResourceAttrProperty.diff(oldRepoAttributeProperty);
if (attrDiff != null && !attrDiff.isEmpty()) {
shadowDelta.addModification(attrDiff);
}

}
}
}
}

}

if (!repoShadowDeltas.isEmpty()){

PropertyDelta<?> shadowNameDelta = PropertyDelta.createModificationReplaceProperty(ShadowType.F_NAME,
oldShadowType.asPrismObject().getDefinition(),currentShadowName);
repoShadowDeltas.add(shadowNameDelta);
} else {

if (!oldShadowType.getName().getOrig().equals(currentShadowType.getName().getOrig())){
PropertyDelta<?> shadowNameDelta = PropertyDelta.createModificationReplaceProperty(ShadowType.F_NAME,
oldShadowType.asPrismObject().getDefinition(), currentShadowName);
repoShadowDeltas.add(shadowNameDelta);

for (Item<?, ?> oldRepoItem: oldRepoAttributesContainer.getValue().getItems()) {
if (oldRepoItem instanceof PrismProperty<?>) {
PrismProperty<?> oldRepoAttrProperty = (PrismProperty<?>)oldRepoItem;
RefinedAttributeDefinition<Object> attrDef = ocDef.findAttributeDefinition(oldRepoAttrProperty.getElementName());
if (attrDef == null || !ProvisioningUtil.shouldStoreAtributeInShadow(ocDef, attrDef.getName())) {
// No definition for this property, it should not be in the shadow
PropertyDelta<?> oldRepoAttrPropDelta = oldRepoAttrProperty.createDelta();
oldRepoAttrPropDelta.addValuesToDelete((Collection)PrismPropertyValue.cloneCollection(oldRepoAttrProperty.getValues()));
shadowDelta.addModification(oldRepoAttrPropDelta);
}
}
}

PolyString currentShadowName = ShadowUtil.determineShadowName(currentResourceShadow);
PolyString oldRepoShadowName = oldRepoShadow.getName();
if (!currentShadowName.equalsOriginalValue(oldRepoShadowName)) {
PropertyDelta<?> shadowNameDelta = PropertyDelta.createModificationReplaceProperty(ShadowType.F_NAME,
oldRepoShadow.getDefinition(),currentShadowName);
shadowDelta.addModification(shadowNameDelta);
}

PropertyDelta<QName> auxOcDelta = PrismProperty.diff(
oldShadowType.asPrismObject().findProperty(ShadowType.F_AUXILIARY_OBJECT_CLASS),
currentShadowType.asPrismObject().findProperty(ShadowType.F_AUXILIARY_OBJECT_CLASS));
PropertyDelta<QName> auxOcDelta = (PropertyDelta)PrismProperty.diff(
oldRepoShadow.findProperty(ShadowType.F_AUXILIARY_OBJECT_CLASS),
currentResourceShadow.findProperty(ShadowType.F_AUXILIARY_OBJECT_CLASS));
if (auxOcDelta != null) {
repoShadowDeltas.add(auxOcDelta);
shadowDelta.addModification(auxOcDelta);
}

if (!repoShadowDeltas.isEmpty()){
normalizeDeltas((Collection)repoShadowDeltas, ctx.getObjectClassDefinition());
ConstraintsChecker.onShadowModifyOperation(repoShadowDeltas);
LOGGER.trace("Modifying repository shadow {}:\n{}", oldShadowType, DebugUtil.debugDump(repoShadowDeltas));
repositoryService.modifyObject(ShadowType.class, oldShadowType.getOid(), repoShadowDeltas, parentResult);
oldShadowType.setName(new PolyStringType(currentShadowName));
}

if (!shadowDelta.isEmpty()) {
if (LOGGER.isTraceEnabled()) {
LOGGER.trace("Updating shadow {} with delta:\n{}", oldRepoShadow, shadowDelta.debugDump());
}
ConstraintsChecker.onShadowModifyOperation(shadowDelta.getModifications());
try {
repositoryService.modifyObject(ShadowType.class, oldRepoShadow.getOid(), shadowDelta.getModifications(), parentResult);
} catch (ObjectAlreadyExistsException e) {
// This should not happen for shadows
throw new SystemException(e.getMessage(), e);
}
PrismObject<ShadowType> newRepoShadow = oldRepoShadow.clone();
shadowDelta.applyTo(newRepoShadow);
return newRepoShadow;

} else {
LOGGER.trace("No need to update shadow {} (empty delta)", oldRepoShadow);
return oldRepoShadow;
}
}

/**
Expand All @@ -882,19 +929,21 @@ public void updateRepoShadow(ProvisioningContext ctx, ShadowType currentShadowTy
*/
public PrismObject<ShadowType> fixShadow(ProvisioningContext ctx, PrismObject<ShadowType> origRepoShadow,
OperationResult parentResult) throws ObjectNotFoundException, SchemaException, ConfigurationException, CommunicationException {
PrismObject<ShadowType> repoShadow = repositoryService.getObject(ShadowType.class, origRepoShadow.getOid(), null, parentResult);
ProvisioningContext shadowCtx = ctx.spawn(repoShadow);
PrismObject<ShadowType> currentRepoShadow = repositoryService.getObject(ShadowType.class, origRepoShadow.getOid(), null, parentResult);
ProvisioningContext shadowCtx = ctx.spawn(currentRepoShadow);
RefinedObjectClassDefinition ocDef = shadowCtx.getObjectClassDefinition();
PrismContainer<Containerable> attributesContainer = repoShadow.findContainer(ShadowType.F_ATTRIBUTES);
PrismContainer<Containerable> attributesContainer = currentRepoShadow.findContainer(ShadowType.F_ATTRIBUTES);
if (attributesContainer != null) {
ObjectDelta<ShadowType> shadowDelta = repoShadow.createModifyDelta();
ObjectDelta<ShadowType> shadowDelta = currentRepoShadow.createModifyDelta();
for (Item<?, ?> item: attributesContainer.getValue().getItems()) {
if (item instanceof PrismProperty<?>) {
PrismProperty<?> attrProperty = (PrismProperty<?>)item;
RefinedAttributeDefinition<Object> attrDef = ocDef.findAttributeDefinition(attrProperty.getElementName());
if (attrDef == null) {
// No definition for this property, it should not be in the shadow
shadowDelta.addModificationDeleteProperty(attrProperty.getPath(), attrProperty.getRealValues());
PropertyDelta<?> oldRepoAttrPropDelta = attrProperty.createDelta();
oldRepoAttrPropDelta.addValuesToDelete((Collection)PrismPropertyValue.cloneCollection(attrProperty.getValues()));
shadowDelta.addModification(oldRepoAttrPropDelta);
} else {
MatchingRule matchingRule = matchingRuleRegistry.getMatchingRule(attrDef.getMatchingRuleQName(), attrDef.getTypeName());
List<PrismPropertyValue> valuesToAdd = null;
Expand Down Expand Up @@ -939,14 +988,14 @@ public PrismObject<ShadowType> fixShadow(ProvisioningContext ctx, PrismObject<Sh
// This should not happen for shadows
throw new SystemException(e.getMessage(), e);
}
shadowDelta.applyTo(repoShadow);
shadowDelta.applyTo(currentRepoShadow);
} else {
LOGGER.trace("No need to fixing shadow {} (empty delta)", origRepoShadow);
}
} else {
LOGGER.trace("No need to fixing shadow {} (no atttributes)", origRepoShadow);
}
return repoShadow;
return currentRepoShadow;
}

public void setKindIfNecessary(ShadowType repoShadowType, RefinedObjectClassDefinition objectClassDefinition) {
Expand Down
Expand Up @@ -1113,22 +1113,23 @@ protected void checkAccountShadowWill(PrismObject<ShadowType> accountRepo) {

@Test
public void test101AddAccountWithoutName() throws Exception {
TestUtil.displayTestTile("test101AddAccountWithoutName");
final String TEST_NAME = "test101AddAccountWithoutName";
TestUtil.displayTestTile(TEST_NAME);
// GIVEN
Task syncTask = taskManager.createTaskInstance(TestDummy.class.getName()
+ ".test101AddAccountWithoutName");
OperationResult result = new OperationResult(TestDummy.class.getName()
+ ".test101AddAccountWithoutName");
Task syncTask = taskManager.createTaskInstance(TestDummy.class.getName() + "." + TEST_NAME);
OperationResult result = new OperationResult(TestDummy.class.getName() + "." + TEST_NAME);
syncServiceMock.reset();

ShadowType account = parseObjectType(ACCOUNT_MORGAN_FILE, ShadowType.class);

display("Adding shadow", account.asPrismObject());

// WHEN
TestUtil.displayWhen(TEST_NAME);
String addedObjectOid = provisioningService.addObject(account.asPrismObject(), null, null, syncTask, result);

// THEN
TestUtil.displayThen(TEST_NAME);
result.computeStatus();
display("add object result", result);
TestUtil.assertSuccess("addObject has failed (result)", result);
Expand All @@ -1141,8 +1142,10 @@ public void test101AddAccountWithoutName() throws Exception {

syncServiceMock.assertNotifySuccessOnly();

TestUtil.displayWhen(TEST_NAME);
ShadowType provisioningAccountType = provisioningService.getObject(ShadowType.class,
ACCOUNT_MORGAN_OID, null, syncTask, result).asObjectable();
TestUtil.displayThen(TEST_NAME);
display("account from provisioning", provisioningAccountType);
PrismAsserts.assertEqualsPolyString("Account name was not generated (provisioning)", transformNameFromResource(ACCOUNT_MORGAN_NAME),
provisioningAccountType.getName());
Expand Down

0 comments on commit 4ec815d

Please sign in to comment.