/*
* 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.wf.impl.processes.itemApproval;
import com.evolveum.midpoint.prism.*;
import com.evolveum.midpoint.prism.delta.ItemDelta;
import com.evolveum.midpoint.prism.delta.builder.DeltaBuilder;
import com.evolveum.midpoint.prism.delta.builder.S_ItemEntry;
import com.evolveum.midpoint.prism.path.ItemPath;
import com.evolveum.midpoint.prism.query.ObjectQuery;
import com.evolveum.midpoint.prism.query.builder.QueryBuilder;
import com.evolveum.midpoint.prism.util.PrismUtil;
import com.evolveum.midpoint.prism.xml.XmlTypeConverter;
import com.evolveum.midpoint.repo.api.RepositoryService;
import com.evolveum.midpoint.schema.ObjectTreeDeltas;
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.schema.util.WfContextUtil;
import com.evolveum.midpoint.task.api.Task;
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.LoggingUtils;
import com.evolveum.midpoint.util.logging.Trace;
import com.evolveum.midpoint.util.logging.TraceManager;
import com.evolveum.midpoint.wf.impl.processes.common.WfTimedActionTriggerHandler;
import com.evolveum.midpoint.xml.ns._public.common.common_3.*;
import com.evolveum.prism.xml.ns._public.types_3.ObjectDeltaType;
import org.activiti.engine.delegate.DelegateTask;
import javax.xml.datatype.Duration;
import javax.xml.datatype.XMLGregorianCalendar;
import java.util.*;
import java.util.stream.Collectors;
import static com.evolveum.midpoint.wf.impl.processes.common.SpringApplicationContextHolder.getCacheRepositoryService;
import static com.evolveum.midpoint.wf.impl.processes.common.SpringApplicationContextHolder.getPrismContext;
import static com.evolveum.midpoint.xml.ns._public.common.common_3.TaskType.F_WORKFLOW_CONTEXT;
import static com.evolveum.midpoint.xml.ns._public.common.common_3.WfContextType.F_EVENT;
import static com.evolveum.midpoint.xml.ns._public.common.common_3.WfContextType.F_PROCESSOR_SPECIFIC_STATE;
/**
* @author mederly
*/
public class MidpointUtil {
private static final Trace LOGGER = TraceManager.getTrace(MidpointUtil.class);
public static ApprovalStageDefinitionType getApprovalStageDefinition(String taskOid) {
RepositoryService cacheRepositoryService = getCacheRepositoryService();
OperationResult result = new OperationResult(MidpointUtil.class.getName() + ".getApprovalStageDefinition");
try {
PrismObject<TaskType> task = cacheRepositoryService.getObject(TaskType.class, taskOid, null, result);
return WfContextUtil.getCurrentStageDefinition(task.asObjectable().getWorkflowContext());
} catch (Exception e) {
throw new SystemException("Couldn't retrieve approval stage for task " + taskOid + ": " + e.getMessage(), e);
}
}
// additional delta is a bit hack ... TODO refactor (but without splitting the modify operation!)
public static void recordEventInTask(CaseEventType event, ObjectDeltaType additionalDelta, String taskOid, OperationResult result) {
RepositoryService cacheRepositoryService = getCacheRepositoryService();
PrismContext prismContext = getPrismContext();
try {
S_ItemEntry deltaBuilder = DeltaBuilder.deltaFor(TaskType.class, getPrismContext())
.item(F_WORKFLOW_CONTEXT, F_EVENT).add(event);
if (additionalDelta != null) {
PrismObject<TaskType> task = cacheRepositoryService.getObject(TaskType.class, taskOid, null, result);
WfPrimaryChangeProcessorStateType state = WfContextUtil
.getPrimaryChangeProcessorState(task.asObjectable().getWorkflowContext());
ObjectTreeDeltasType updatedDelta = ObjectTreeDeltas.mergeDeltas(state.getDeltasToProcess(),
additionalDelta, prismContext);
ItemPath deltasToProcessPath = new ItemPath(F_WORKFLOW_CONTEXT, F_PROCESSOR_SPECIFIC_STATE, WfPrimaryChangeProcessorStateType.F_DELTAS_TO_PROCESS); // assuming it already exists!
ItemDefinition<?> deltasToProcessDefinition = getPrismContext().getSchemaRegistry()
.findContainerDefinitionByCompileTimeClass(WfPrimaryChangeProcessorStateType.class)
.findItemDefinition(WfPrimaryChangeProcessorStateType.F_DELTAS_TO_PROCESS);
deltaBuilder = deltaBuilder.item(deltasToProcessPath, deltasToProcessDefinition)
.replace(updatedDelta);
}
cacheRepositoryService.modifyObject(TaskType.class, taskOid, deltaBuilder.asItemDeltas(), result);
} catch (ObjectNotFoundException | SchemaException | ObjectAlreadyExistsException e) {
throw new SystemException("Couldn't record decision to the task " + taskOid + ": " + e.getMessage(), e);
}
}
public static Set<ObjectReferenceType> expandGroups(Set<ObjectReferenceType> approverRefs) {
PrismContext prismContext = getPrismContext();
Set<ObjectReferenceType> rv = new HashSet<>();
for (ObjectReferenceType approverRef : approverRefs) {
@SuppressWarnings({ "unchecked", "raw" })
Class<? extends Containerable> clazz = (Class<? extends Containerable>)
prismContext.getSchemaRegistry().getCompileTimeClassForObjectType(approverRef.getType());
if (clazz == null) {
throw new IllegalStateException("Unknown object type " + approverRef.getType());
}
if (UserType.class.isAssignableFrom(clazz)) {
rv.add(approverRef.clone());
} else if (AbstractRoleType.class.isAssignableFrom(clazz)) {
rv.addAll(expandAbstractRole(approverRef, prismContext));
} else {
LOGGER.warn("Unexpected type {} for approver: {}", clazz, approverRef);
rv.add(approverRef.clone());
}
}
return rv;
}
private static Collection<ObjectReferenceType> expandAbstractRole(ObjectReferenceType approverRef, PrismContext prismContext) {
ObjectQuery query = QueryBuilder.queryFor(UserType.class, prismContext)
.item(FocusType.F_ROLE_MEMBERSHIP_REF).ref(approverRef.asReferenceValue())
.build();
try {
return getCacheRepositoryService()
.searchObjects(UserType.class, query, null, new OperationResult("dummy"))
.stream()
.map(o -> ObjectTypeUtil.createObjectRef(o))
.collect(Collectors.toList());
} catch (SchemaException e) {
throw new SystemException("Couldn't resolve " + approverRef + ": " + e.getMessage(), e);
}
}
static void setTaskDeadline(DelegateTask delegateTask, Duration duration) {
XMLGregorianCalendar deadline = XmlTypeConverter.createXMLGregorianCalendar(new Date());
deadline.add(duration);
delegateTask.setDueDate(XmlTypeConverter.toDate(deadline));
}
public static void createTriggersForTimedActions(String workItemId, int escalationLevel, Date workItemCreateTime,
Date workItemDeadline, Task wfTask, List<WorkItemTimedActionsType> timedActionsList, OperationResult result) {
LOGGER.trace("Creating triggers for timed actions for work item {}, escalation level {}, create time {}, deadline {}, {} timed action(s)",
workItemId, escalationLevel, workItemCreateTime, workItemDeadline, timedActionsList.size());
try {
PrismContext prismContext = getPrismContext();
List<TriggerType> triggers = WfContextUtil.createTriggers(escalationLevel, workItemCreateTime, workItemDeadline,
timedActionsList, prismContext, LOGGER, workItemId, WfTimedActionTriggerHandler.HANDLER_URI);
LOGGER.trace("Adding {} triggers to {}:\n{}", triggers.size(), wfTask,
PrismUtil.serializeQuietlyLazily(prismContext, triggers));
if (!triggers.isEmpty()) {
List<ItemDelta<?, ?>> itemDeltas = DeltaBuilder.deltaFor(TaskType.class, prismContext)
.item(TaskType.F_TRIGGER).add(PrismContainerValue.toPcvList(triggers))
.asItemDeltas();
getCacheRepositoryService().modifyObject(TaskType.class, wfTask.getOid(), itemDeltas, result);
}
} catch (ObjectNotFoundException | SchemaException | ObjectAlreadyExistsException | RuntimeException e) {
throw new SystemException("Couldn't add trigger(s) to " + wfTask + ": " + e.getMessage(), e);
}
}
public static void removeTriggersForWorkItem(Task wfTask, String workItemId, OperationResult result) {
List<PrismContainerValue<TriggerType>> toDelete = new ArrayList<>();
for (TriggerType triggerType : wfTask.getTaskPrismObject().asObjectable().getTrigger()) {
if (WfTimedActionTriggerHandler.HANDLER_URI.equals(triggerType.getHandlerUri())) {
PrismProperty workItemIdProperty = triggerType.getExtension().asPrismContainerValue()
.findProperty(SchemaConstants.MODEL_EXTENSION_WORK_ITEM_ID);
if (workItemIdProperty != null && workItemId.equals(workItemIdProperty.getRealValue())) {
toDelete.add(triggerType.clone().asPrismContainerValue());
}
}
}
removeSelectedTriggers(wfTask, toDelete, result);
}
// not necessary any more, as work item triggers are deleted when the work item (task) is deleted
// (and there are currently no triggers other than work-item-related)
public static void removeAllStageTriggersForWorkItem(Task wfTask, OperationResult result) {
List<PrismContainerValue<TriggerType>> toDelete = new ArrayList<>();
for (TriggerType triggerType : wfTask.getTaskPrismObject().asObjectable().getTrigger()) {
if (WfTimedActionTriggerHandler.HANDLER_URI.equals(triggerType.getHandlerUri())) {
toDelete.add(triggerType.clone().asPrismContainerValue());
}
}
removeSelectedTriggers(wfTask, toDelete, result);
}
private static void removeSelectedTriggers(Task wfTask, List<PrismContainerValue<TriggerType>> toDelete, OperationResult result) {
try {
LOGGER.trace("About to delete {} triggers from {}: {}", toDelete.size(), wfTask, toDelete);
List<ItemDelta<?, ?>> itemDeltas = DeltaBuilder.deltaFor(TaskType.class, getPrismContext())
.item(TaskType.F_TRIGGER).delete(toDelete)
.asItemDeltas();
getCacheRepositoryService().modifyObject(TaskType.class, wfTask.getOid(), itemDeltas, result);
} catch (SchemaException|ObjectNotFoundException|ObjectAlreadyExistsException|RuntimeException e) {
LoggingUtils.logUnexpectedException(LOGGER, "Couldn't remove triggers from task {}", e, wfTask);
}
}
}