/**
* Copyright (c) 2016 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.model.impl.lens;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import javax.xml.datatype.XMLGregorianCalendar;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import com.evolveum.midpoint.common.ActivationComputer;
import com.evolveum.midpoint.prism.PrismContainer;
import com.evolveum.midpoint.prism.PrismContainerValue;
import com.evolveum.midpoint.prism.PrismContext;
import com.evolveum.midpoint.prism.PrismObject;
import com.evolveum.midpoint.prism.PrismObjectDefinition;
import com.evolveum.midpoint.prism.PrismReferenceValue;
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.prism.delta.ReferenceDelta;
import com.evolveum.midpoint.prism.path.ItemPath;
import com.evolveum.midpoint.schema.constants.SchemaConstants;
import com.evolveum.midpoint.schema.result.OperationResult;
import com.evolveum.midpoint.schema.util.ObjectTypeUtil;
import com.evolveum.midpoint.task.api.Task;
import com.evolveum.midpoint.util.exception.SchemaException;
import com.evolveum.midpoint.util.logging.Trace;
import com.evolveum.midpoint.util.logging.TraceManager;
import com.evolveum.midpoint.wf.api.WorkflowManager;
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.AssignmentType;
import com.evolveum.midpoint.xml.ns._public.common.common_3.FocusType;
import com.evolveum.midpoint.xml.ns._public.common.common_3.MetadataType;
import com.evolveum.midpoint.xml.ns._public.common.common_3.ObjectReferenceType;
import com.evolveum.midpoint.xml.ns._public.common.common_3.ObjectType;
/**
* @author semancik
*
*/
@Component
public class OperationalDataManager {
private static final Trace LOGGER = TraceManager.getTrace(OperationalDataManager.class);
@Autowired(required = false)
private ActivationComputer activationComputer;
// for inserting workflow-related metadata to changed object
@Autowired(required = false)
private WorkflowManager workflowManager;
@Autowired(required = true)
private PrismContext prismContext;
public <F extends ObjectType> void applyRequestMetadata(LensContext<F> context,
XMLGregorianCalendar now, Task task, OperationResult result) throws SchemaException {
MetadataType requestMetadata = new MetadataType();
applyRequestMetadata(context, requestMetadata, now, task);
context.setRequestMetadata(requestMetadata);
}
public <T extends ObjectType, F extends ObjectType> void applyMetadataAdd(LensContext<F> context,
PrismObject<T> objectToAdd, XMLGregorianCalendar now, Task task, OperationResult result)
throws SchemaException {
T objectType = objectToAdd.asObjectable();
MetadataType metadataType = objectType.getMetadata();
if (metadataType == null) {
metadataType = new MetadataType();
objectType.setMetadata(metadataType);
}
transplantRequestMetadata(context, metadataType);
applyCreateMetadata(context, metadataType, now, task);
if (workflowManager != null) {
metadataType.getCreateApproverRef().addAll(workflowManager.getApprovedBy(task, result));
}
if (objectToAdd.canRepresent(FocusType.class)) {
applyAssignmentMetadataObject((LensContext<? extends FocusType>) context, objectToAdd, now, task, result);
}
}
public <T extends ObjectType, F extends ObjectType> void applyMetadataModify(ObjectDelta<T> objectDelta,
LensElementContext<T> objectContext, Class objectTypeClass,
XMLGregorianCalendar now, Task task, LensContext<F> context,
OperationResult result) throws SchemaException {
String channel = LensUtil.getChannel(context, task);
PrismObjectDefinition<T> def = prismContext.getSchemaRegistry()
.findObjectDefinitionByCompileTimeClass(objectTypeClass);
ItemDelta.mergeAll(objectDelta.getModifications(), createModifyMetadataDeltas(context,
new ItemPath(ObjectType.F_METADATA), def, now, task));
List<PrismReferenceValue> approverReferenceValues = new ArrayList<PrismReferenceValue>();
if (workflowManager != null) {
for (ObjectReferenceType approverRef : workflowManager.getApprovedBy(task, result)) {
approverReferenceValues.add(new PrismReferenceValue(approverRef.getOid()));
}
}
if (!approverReferenceValues.isEmpty()) {
ReferenceDelta refDelta = ReferenceDelta.createModificationReplace(
(new ItemPath(ObjectType.F_METADATA, MetadataType.F_MODIFY_APPROVER_REF)), def,
approverReferenceValues);
((Collection) objectDelta.getModifications()).add(refDelta);
} else {
// a bit of hack - we want to replace all existing values with empty
// set of values;
// however, it is not possible to do this using REPLACE, so we have
// to explicitly remove all existing values
if (objectContext != null && objectContext.getObjectOld() != null) {
// a null value of objectOld means that we execute MODIFY delta
// that is a part of primary ADD operation (in a wave greater
// than 0)
// i.e. there are NO modifyApprovers set (theoretically they
// could be set in previous waves, but because in these waves
// the data
// are taken from the same source as in this step - so there are
// none modify approvers).
if (objectContext.getObjectOld().asObjectable().getMetadata() != null) {
List<ObjectReferenceType> existingModifyApproverRefs = objectContext.getObjectOld()
.asObjectable().getMetadata().getModifyApproverRef();
LOGGER.trace("Original values of MODIFY_APPROVER_REF: {}", existingModifyApproverRefs);
if (!existingModifyApproverRefs.isEmpty()) {
List<PrismReferenceValue> valuesToDelete = new ArrayList<PrismReferenceValue>();
for (ObjectReferenceType approverRef : objectContext.getObjectOld().asObjectable()
.getMetadata().getModifyApproverRef()) {
valuesToDelete.add(approverRef.asReferenceValue().clone());
}
ReferenceDelta refDelta = ReferenceDelta.createModificationDelete(
(new ItemPath(ObjectType.F_METADATA, MetadataType.F_MODIFY_APPROVER_REF)),
def, valuesToDelete);
((Collection) objectDelta.getModifications()).add(refDelta);
}
}
}
}
if (FocusType.class.isAssignableFrom(objectTypeClass)) {
applyAssignmentMetadataDelta((LensContext) context,
(ObjectDelta)objectDelta, now, task, result);
}
}
private <F extends FocusType, T extends ObjectType> void applyAssignmentMetadataObject(LensContext<F> context,
PrismObject<T> objectToAdd,
XMLGregorianCalendar now, Task task, OperationResult result) throws SchemaException {
PrismContainer<AssignmentType> assignmentContainer = objectToAdd.findContainer(FocusType.F_ASSIGNMENT);
if (assignmentContainer != null) {
for (PrismContainerValue<AssignmentType> assignmentContainerValue: assignmentContainer.getValues()) {
applyAssignmentValueMetadataAdd(context, assignmentContainerValue, "ADD", now, task, result);
}
}
}
private <F extends FocusType> void applyAssignmentMetadataDelta(LensContext<F> context, ObjectDelta<F> objectDelta,
XMLGregorianCalendar now, Task task, OperationResult result) throws SchemaException {
if (objectDelta == null || objectDelta.isDelete()) {
return;
}
if (objectDelta.isAdd()) {
applyAssignmentMetadataObject(context, objectDelta.getObjectToAdd(), now, task, result);
} else {
for (ItemDelta<?,?> itemDelta: objectDelta.getModifications()) {
if (itemDelta.getPath().equivalent(SchemaConstants.PATH_ASSIGNMENT)) {
ContainerDelta<AssignmentType> assignmentDelta = (ContainerDelta<AssignmentType>)itemDelta;
if (assignmentDelta.getValuesToAdd() != null) {
for (PrismContainerValue<AssignmentType> assignmentContainerValue: assignmentDelta.getValuesToAdd()) {
applyAssignmentValueMetadataAdd(context, assignmentContainerValue, "MOD/add", now, task, result);
}
}
if (assignmentDelta.getValuesToReplace() != null) {
for (PrismContainerValue<AssignmentType> assignmentContainerValue: assignmentDelta.getValuesToReplace()) {
applyAssignmentValueMetadataAdd(context, assignmentContainerValue, "MOD/replace", now, task, result);
}
}
}
// TODO: assignment modification
}
}
}
private <F extends FocusType> void applyAssignmentValueMetadataAdd(LensContext<F> context,
PrismContainerValue<AssignmentType> assignmentContainerValue, String desc,
XMLGregorianCalendar now, Task task, OperationResult result) throws SchemaException {
AssignmentType assignmentType = assignmentContainerValue.asContainerable();
MetadataType metadataType = assignmentType.getMetadata();
if (metadataType == null) {
metadataType = new MetadataType();
assignmentType.setMetadata(metadataType);
}
transplantRequestMetadata(context, metadataType);
ActivationType activationType = assignmentType.getActivation();
ActivationStatusType effectiveStatus = activationComputer.getEffectiveStatus(assignmentType.getLifecycleState(), activationType);
if (activationType == null) {
activationType = new ActivationType();
assignmentType.setActivation(activationType);
}
activationType.setEffectiveStatus(effectiveStatus);
applyCreateMetadata(context, metadataType, now, task);
if (LOGGER.isTraceEnabled()) {
LOGGER.trace("Adding operational data {} to assignment cval ({}):\nMETADATA:\n{}\nACTIVATION:\n{}",
metadataType, desc, assignmentContainerValue.debugDump(1), activationType.asPrismContainerValue().debugDump(1));
}
}
private <F extends ObjectType> void applyRequestMetadata(LensContext<F> context, MetadataType metaData, XMLGregorianCalendar now, Task task) {
String channel = LensUtil.getChannel(context, task);
metaData.setCreateChannel(channel);
metaData.setRequestTimestamp(now);
if (task.getOwner() != null) {
metaData.setRequestorRef(ObjectTypeUtil.createObjectRef(task.getOwner()));
}
}
private <F extends ObjectType> void transplantRequestMetadata(LensContext<F> context, MetadataType metaData) {
MetadataType requestMetadata = context.getRequestMetadata();
if (requestMetadata == null) {
return;
}
metaData.setRequestTimestamp(requestMetadata.getRequestTimestamp());
metaData.setRequestorRef(requestMetadata.getRequestorRef());
}
public <F extends ObjectType> MetadataType createCreateMetadata(LensContext<F> context, XMLGregorianCalendar now, Task task) {
MetadataType metaData = new MetadataType();
applyCreateMetadata(context, metaData, now, task);
return metaData;
}
private <F extends ObjectType> void applyCreateMetadata(LensContext<F> context, MetadataType metaData, XMLGregorianCalendar now, Task task) {
String channel = LensUtil.getChannel(context, task);
metaData.setCreateChannel(channel);
metaData.setCreateTimestamp(now);
if (task.getOwner() != null) {
metaData.setCreatorRef(ObjectTypeUtil.createObjectRef(task.getOwner()));
}
}
public <F extends ObjectType, T extends ObjectType> Collection<? extends ItemDelta<?,?>> createModifyMetadataDeltas(LensContext<F> context,
ItemPath metadataPath, PrismObjectDefinition<T> def, XMLGregorianCalendar now, Task task) {
Collection<? extends ItemDelta<?,?>> deltas = new ArrayList<>();
String channel = LensUtil.getChannel(context, task);
if (channel != null) {
PropertyDelta<String> delta = PropertyDelta.createModificationReplaceProperty(metadataPath.subPath(MetadataType.F_MODIFY_CHANNEL), def, channel);
((Collection)deltas).add(delta);
}
PropertyDelta<XMLGregorianCalendar> delta = PropertyDelta.createModificationReplaceProperty(metadataPath.subPath(MetadataType.F_MODIFY_TIMESTAMP), def, now);
((Collection)deltas).add(delta);
if (task.getOwner() != null) {
ReferenceDelta refDelta = ReferenceDelta.createModificationReplace(
metadataPath.subPath(MetadataType.F_MODIFIER_REF), def, task.getOwner().getOid());
((Collection)deltas).add(refDelta);
}
return deltas;
}
}