/*
* 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.common;
import com.evolveum.midpoint.model.common.expression.ExpressionVariables;
import com.evolveum.midpoint.model.impl.trigger.TriggerHandler;
import com.evolveum.midpoint.model.impl.trigger.TriggerHandlerRegistry;
import com.evolveum.midpoint.prism.PrismObject;
import com.evolveum.midpoint.prism.util.CloneUtil;
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.task.api.TaskManager;
import com.evolveum.midpoint.util.exception.*;
import com.evolveum.midpoint.util.logging.Trace;
import com.evolveum.midpoint.util.logging.TraceManager;
import com.evolveum.midpoint.wf.api.WorkItemAllocationChangeOperationInfo;
import com.evolveum.midpoint.wf.api.WorkItemOperationSourceInfo;
import com.evolveum.midpoint.wf.api.WorkflowConstants;
import com.evolveum.midpoint.wf.impl.activiti.dao.WorkItemManager;
import com.evolveum.midpoint.wf.impl.activiti.dao.WorkItemProvider;
import com.evolveum.midpoint.wf.impl.tasks.WfTaskController;
import com.evolveum.midpoint.wf.util.ApprovalUtils;
import com.evolveum.midpoint.xml.ns._public.common.common_3.*;
import org.apache.commons.lang.BooleanUtils;
import org.jetbrains.annotations.NotNull;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import javax.annotation.PostConstruct;
import javax.xml.datatype.Duration;
import java.util.ArrayList;
import java.util.List;
/**
* @author mederly
*/
@Component
public class WfTimedActionTriggerHandler implements TriggerHandler {
public static final String HANDLER_URI = WorkflowConstants.NS_WORKFLOW_TRIGGER_PREFIX + "/timed-action/handler-3";
private static final transient Trace LOGGER = TraceManager.getTrace(WfTimedActionTriggerHandler.class);
@Autowired private TriggerHandlerRegistry triggerHandlerRegistry;
@Autowired private WorkItemProvider workItemProvider;
@Autowired private WorkItemManager workItemManager;
@Autowired private WfTaskController wfTaskController;
@Autowired private TaskManager taskManager;
@Autowired private WfExpressionEvaluationHelper evaluationHelper;
@PostConstruct
private void initialize() {
triggerHandlerRegistry.register(HANDLER_URI, this);
}
@Override
public <O extends ObjectType> void handle(PrismObject<O> object, TriggerType trigger, Task triggerScannerTask, OperationResult parentResult) {
if (!(object.asObjectable() instanceof TaskType)) {
throw new IllegalArgumentException("Unexpected object type: should be TaskType: " + object);
}
TaskType wfTaskType = (TaskType) object.asObjectable();
WfContextType wfc = wfTaskType.getWorkflowContext();
if (wfc == null) {
LOGGER.warn("Task without workflow context; ignoring it: " + object);
return;
}
String workItemId = ObjectTypeUtil.getExtensionItemRealValue(trigger.getExtension(), SchemaConstants.MODEL_EXTENSION_WORK_ITEM_ID);
if (workItemId == null) {
LOGGER.warn("Trigger without workItemId; ignoring it: " + trigger);
return;
}
OperationResult result = parentResult.createSubresult(WfTimedActionTriggerHandler.class.getName() + ".handle");
try {
WorkItemType workItem = workItemProvider.getWorkItem(workItemId, result);
if (workItem == null) {
throw new ObjectNotFoundException("No work item with ID " + workItemId);
}
Task wfTask = taskManager.createTaskInstance(wfTaskType.asPrismObject(), result);
Duration timeBeforeAction = ObjectTypeUtil.getExtensionItemRealValue(trigger.getExtension(), SchemaConstants.MODEL_EXTENSION_TIME_BEFORE_ACTION);
if (timeBeforeAction != null) {
AbstractWorkItemActionType action = ObjectTypeUtil.getExtensionItemRealValue(trigger.getExtension(), SchemaConstants.MODEL_EXTENSION_WORK_ITEM_ACTION);
if (action == null) {
LOGGER.warn("Notification trigger without workItemAction; ignoring it: " + trigger);
return;
}
executeNotifications(timeBeforeAction, action, workItem, wfTask, result);
} else {
WorkItemActionsType actions = ObjectTypeUtil.getExtensionItemRealValue(trigger.getExtension(), SchemaConstants.MODEL_EXTENSION_WORK_ITEM_ACTIONS);
if (actions == null) {
LOGGER.warn("Trigger without workItemActions; ignoring it: " + trigger);
return;
}
executeActions(actions, workItem, wfTask, triggerScannerTask, result);
}
} catch (RuntimeException|ObjectNotFoundException|SchemaException|SecurityViolationException|ExpressionEvaluationException e) {
String message = "Exception while handling work item trigger for ID " + workItemId + ": " + e.getMessage();
result.recordFatalError(message, e);
throw new SystemException(message, e);
} finally {
result.computeStatusIfUnknown();
}
}
private void executeNotifications(Duration timeBeforeAction, AbstractWorkItemActionType action, WorkItemType workItem,
Task wfTask, OperationResult result) throws SchemaException {
WorkItemOperationKindType operationKind = WfContextUtil.getOperationKind(action);
WorkItemEventCauseInformationType cause = WfContextUtil.createCause(action);
WorkItemAllocationChangeOperationInfo operationInfo =
new WorkItemAllocationChangeOperationInfo(operationKind, workItem.getAssigneeRef(), null);
WorkItemOperationSourceInfo sourceInfo = new WorkItemOperationSourceInfo(null, cause, action);
wfTaskController.notifyWorkItemAllocationChangeCurrentActors(workItem, operationInfo, sourceInfo, timeBeforeAction, wfTask, result);
}
private void executeActions(WorkItemActionsType actions, WorkItemType workItem, Task wfTask, Task triggerScannerTask,
OperationResult result)
throws SchemaException, SecurityViolationException, ObjectNotFoundException, ExpressionEvaluationException {
for (WorkItemNotificationActionType notificationAction : actions.getNotify()) {
executeNotificationAction(workItem, notificationAction, wfTask, result);
}
if (actions.getDelegate() != null) {
executeDelegateAction(workItem, actions.getDelegate(), false, wfTask, triggerScannerTask, result);
}
if (actions.getEscalate() != null) {
executeDelegateAction(workItem, actions.getEscalate(), true, wfTask, triggerScannerTask, result);
}
if (actions.getComplete() != null) {
executeCompleteAction(workItem, actions.getComplete(), result);
}
}
private void executeCompleteAction(WorkItemType workItem, CompleteWorkItemActionType completeAction,
OperationResult result) throws SchemaException, SecurityViolationException {
WorkItemOutcomeType outcome = completeAction.getOutcome() != null ? ApprovalUtils.fromUri(completeAction.getOutcome()) : WorkItemOutcomeType.REJECT;
workItemManager.completeWorkItem(workItem.getExternalId(), ApprovalUtils.toUri(outcome),
null, null, WfContextUtil.createCause(completeAction), result);
}
private void executeDelegateAction(WorkItemType workItem, DelegateWorkItemActionType delegateAction, boolean escalate,
Task wfTask, Task triggerScannerTask, OperationResult result)
throws SecurityViolationException, ObjectNotFoundException, SchemaException, ExpressionEvaluationException {
WorkItemEscalationLevelType escLevel = escalate ? WfContextUtil.createEscalationLevelInformation(delegateAction) : null;
List<ObjectReferenceType> delegates = computeDelegateTo(delegateAction, workItem, wfTask, triggerScannerTask, result);
workItemManager.delegateWorkItem(workItem.getExternalId(), delegates,
delegateAction.getDelegationMethod(), escLevel,
delegateAction.getDuration(), WfContextUtil.createCause(delegateAction), result);
}
private List<ObjectReferenceType> computeDelegateTo(DelegateWorkItemActionType delegateAction, WorkItemType workItem,
Task wfTask, Task triggerScannerTask, OperationResult result)
throws SchemaException, ObjectNotFoundException, ExpressionEvaluationException {
List<ObjectReferenceType> rv = new ArrayList<>();
rv.addAll(CloneUtil.cloneCollectionMembers(delegateAction.getApproverRef()));
if (!delegateAction.getApproverExpression().isEmpty()) {
ExpressionVariables variables = evaluationHelper.getDefaultVariables(null, wfTask, result);
variables.addVariableDefinition(SchemaConstants.C_WORK_ITEM, workItem);
rv.addAll(evaluationHelper.evaluateRefExpressions(delegateAction.getApproverExpression(),
variables, "computing delegates", triggerScannerTask, result));
}
// if (!delegateAction.getApproverRelation().isEmpty()) {
// throw new UnsupportedOperationException("Approver relation in delegate/escalate action is not supported yet.");
// }
return rv;
}
private void executeNotificationAction(WorkItemType workItem, @NotNull WorkItemNotificationActionType notificationAction, Task wfTask,
OperationResult result) throws SchemaException {
WorkItemEventCauseInformationType cause = WfContextUtil.createCause(notificationAction);
if (BooleanUtils.isNotFalse(notificationAction.isPerAssignee())) {
for (ObjectReferenceType assignee : workItem.getAssigneeRef()) {
wfTaskController.notifyWorkItemCustom(assignee, workItem, cause, wfTask, notificationAction, result);
}
} else {
wfTaskController.notifyWorkItemCustom(null, workItem, cause, wfTask, notificationAction, result);
}
}
}