/*
* 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.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.evolveum.midpoint.provisioning.impl;
import java.util.ArrayList;
import java.util.Collection;
import org.springframework.stereotype.Component;
import com.evolveum.midpoint.prism.PrismObject;
import com.evolveum.midpoint.prism.delta.ContainerDelta;
import com.evolveum.midpoint.prism.delta.ItemDelta;
import com.evolveum.midpoint.prism.delta.ObjectDelta;
import com.evolveum.midpoint.prism.delta.PropertyDelta;
import com.evolveum.midpoint.provisioning.api.ProvisioningOperationOptions;
import com.evolveum.midpoint.provisioning.util.ProvisioningUtil;
import com.evolveum.midpoint.schema.DeltaConvertor;
import com.evolveum.midpoint.schema.result.AsynchronousOperationReturnValue;
import com.evolveum.midpoint.schema.result.OperationResult;
import com.evolveum.midpoint.schema.util.ShadowUtil;
import com.evolveum.midpoint.util.exception.ObjectAlreadyExistsException;
import com.evolveum.midpoint.util.exception.ObjectNotFoundException;
import com.evolveum.midpoint.util.exception.SchemaException;
import com.evolveum.midpoint.util.exception.SystemException;
import com.evolveum.midpoint.util.logging.Trace;
import com.evolveum.midpoint.util.logging.TraceManager;
import com.evolveum.midpoint.xml.ns._public.common.common_3.ObjectType;
import com.evolveum.midpoint.xml.ns._public.common.common_3.ShadowAssociationType;
import com.evolveum.midpoint.xml.ns._public.common.common_3.ShadowType;
import com.evolveum.prism.xml.ns._public.types_3.ObjectDeltaType;
@Component
public class ShadowCacheReconciler extends ShadowCache {
private static final Trace LOGGER = TraceManager.getTrace(ShadowCacheReconciler.class);
@Override
public String afterAddOnResource(ProvisioningContext ctx, AsynchronousOperationReturnValue<PrismObject<ShadowType>> addResult, OperationResult parentResult)
throws SchemaException, ObjectAlreadyExistsException, ObjectNotFoundException {
PrismObject<ShadowType> shadow = addResult.getReturnValue();
cleanShadowInRepository(shadow, parentResult);
return shadow.getOid();
}
@Override
public void afterModifyOnResource(ProvisioningContext ctx, PrismObject<ShadowType> shadow, Collection<? extends ItemDelta> modifications,
OperationResult resourceOperationResult, OperationResult parentResult) throws SchemaException, ObjectNotFoundException {
LOGGER.trace("Modified shadow is reconciled. Start to clean up account after successful reconciliation.");
try {
cleanShadowInRepository(shadow, parentResult);
} catch (ObjectAlreadyExistsException ex) {
//should be never thrown
throw new SystemException("While modifying object in the repository got exception: " + ex.getMessage(), ex);
}
LOGGER.trace("Shadow cleaned up successfully.");
}
private void cleanShadowInRepository(PrismObject<ShadowType> shadow, OperationResult parentResult) throws SchemaException, ObjectAlreadyExistsException, ObjectNotFoundException{
PrismObject<ShadowType> normalizedShadow = shadow.clone();
ProvisioningUtil.normalizeShadow(normalizedShadow.asObjectable(), parentResult);
if (LOGGER.isTraceEnabled()) {
LOGGER.trace("normalized shadow {}", normalizedShadow.debugDump(1));
}
// FIXME: ugly hack, need to be fixed (problem with comparing operation
// result, because it was changed and in this call it is different as
// one in repo, therefore the following if)
PrismObject<ShadowType> oldShadow = shadow.clone();
ShadowUtil.getAttributesContainer(oldShadow).clear();
PrismObject<ShadowType> repoShadow = getRepositoryService().getObject(ShadowType.class,
shadow.getOid(), null, parentResult);
ShadowType repoShadowType = repoShadow.asObjectable();
if (repoShadowType.getResult() != null) {
if (!repoShadowType.getResult().equals(oldShadow.asObjectable().getResult())) {
oldShadow.asObjectable().setResult(repoShadowType.getResult());
}
}
// ShadowUtil.getAttributesContainer(repoShadow).clear();
if (LOGGER.isTraceEnabled()) {
LOGGER.trace("origin shadow with failure description {}", oldShadow.debugDump(1));
}
ObjectDelta delta = oldShadow.diff(normalizedShadow);
// TODO: remove this ugly hack (problem is in prism - objectChange does not get deleted by delta produced by diff method) - see MID-2174
PropertyDelta<ObjectDeltaType> clearObjectChange = PropertyDelta.createModificationReplaceProperty(
ShadowType.F_OBJECT_CHANGE, oldShadow.getDefinition());
delta.removePropertyModification(ShadowType.F_OBJECT_CHANGE);
delta.addModification(clearObjectChange);
if (LOGGER.isTraceEnabled()) {
LOGGER.trace("Normalizing shadow: change description: {}", delta.debugDump());
}
try {
ConstraintsChecker.onShadowModifyOperation(delta.getModifications());
getRepositoryService().modifyObject(ShadowType.class, oldShadow.getOid(), delta.getModifications(),
parentResult);
} catch (SchemaException ex) {
parentResult.recordFatalError("Couldn't modify shadow: schema violation: " + ex.getMessage(), ex);
throw ex;
} catch (ObjectAlreadyExistsException ex) {
parentResult.recordFatalError("Couldn't modify shadow: shadow already exists: " + ex.getMessage(), ex);
throw ex;
} catch (ObjectNotFoundException ex) {
parentResult.recordFatalError("Couldn't modify shadow: shadow not found: " + ex.getMessage(), ex);
throw ex;
}
}
@Override
public Collection<? extends ItemDelta> beforeModifyOnResource(PrismObject<ShadowType> shadow, ProvisioningOperationOptions options, Collection<? extends ItemDelta> modifications) throws SchemaException {
ObjectDeltaType shadowDelta = shadow.asObjectable().getObjectChange();
//TODO: error handling
if (shadowDelta != null){
modifications = DeltaConvertor.toModifications(
shadowDelta.getItemDelta(), shadow.getDefinition());
}
// for the older versions
ObjectDelta<? extends ObjectType> objectDelta = ObjectDelta.createModifyDelta(shadow.getOid(),
modifications, ShadowType.class, getPrismContext());
if (LOGGER.isTraceEnabled()) {
LOGGER.trace("Storing delta to shadow:\n{}", objectDelta.debugDump());
}
ContainerDelta<ShadowAssociationType> associationDelta = objectDelta.findContainerDelta(ShadowType.F_ASSOCIATION);
if (associationDelta != null) {
normalizeAssociationDeltasBeforeSave(associationDelta.getValuesToAdd());
normalizeAssociationDeltasBeforeSave(associationDelta.getValuesToReplace());
normalizeAssociationDeltasBeforeSave(associationDelta.getValuesToDelete());
}
if (modifications == null){
modifications = new ArrayList<ItemDelta>();
}
return modifications;
}
}