package com.evolveum.midpoint.wf.impl.tasks;
import com.evolveum.midpoint.model.api.context.ModelContext;
import com.evolveum.midpoint.prism.PrismContext;
import com.evolveum.midpoint.prism.PrismObject;
import com.evolveum.midpoint.prism.delta.builder.DeltaBuilder;
import com.evolveum.midpoint.prism.xml.XmlTypeConverter;
import com.evolveum.midpoint.schema.result.OperationResult;
import com.evolveum.midpoint.schema.util.WfContextUtil;
import com.evolveum.midpoint.task.api.Task;
import com.evolveum.midpoint.task.api.TaskExecutionStatus;
import com.evolveum.midpoint.util.exception.CommunicationException;
import com.evolveum.midpoint.util.exception.ConfigurationException;
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.wf.impl.processors.ChangeProcessor;
import com.evolveum.midpoint.schema.ObjectTreeDeltas;
import com.evolveum.midpoint.wf.util.ApprovalUtils;
import com.evolveum.midpoint.xml.ns._public.common.common_3.ObjectReferenceType;
import com.evolveum.midpoint.xml.ns._public.common.common_3.TaskType;
import com.evolveum.midpoint.xml.ns._public.common.common_3.UserType;
import org.apache.commons.lang3.Validate;
import javax.xml.datatype.XMLGregorianCalendar;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import static com.evolveum.midpoint.task.api.TaskExecutionStatus.WAITING;
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.*;
/**
* A class that describes wf-enabled tasks (plus tasks that do not carry wf process, like "task0" executing changes that do not need approvals)
*
* It points to the activiti process instance information as well as to the corresponding midPoint task.
*
* @author mederly
*/
public class WfTask {
private WfTaskController wfTaskController;
private Task task; // must be non-null
private String processInstanceId; // must be non-null for Activiti-related jobs (and may be filled-in later, when activiti process is started)
private ChangeProcessor changeProcessor; // must be non-null
//region Constructors and basic getters
WfTask(WfTaskController wfTaskController, Task task, ChangeProcessor changeProcessor) {
this(wfTaskController, task, null, changeProcessor);
}
protected WfTask(WfTaskController wfTaskController, Task task, String processInstanceId, ChangeProcessor changeProcessor) {
Validate.notNull(task, "Task");
Validate.notNull(changeProcessor, "Change processor");
this.wfTaskController = wfTaskController;
this.task = task;
this.processInstanceId = processInstanceId;
this.changeProcessor = changeProcessor;
}
protected WfTask(WfTask original) {
this.wfTaskController = original.wfTaskController;
this.task = original.task;
this.processInstanceId = original.processInstanceId;
this.changeProcessor = original.changeProcessor;
}
public String getProcessInstanceId() {
return processInstanceId;
}
public Task getTask() {
return task;
}
public ChangeProcessor getChangeProcessor() {
return changeProcessor;
}
//endregion
@Override
public String toString() {
return "WfTask{" +
"task=" + task +
", processInstanceId='" + processInstanceId + '\'' +
", changeProcessor=" + changeProcessor +
'}';
}
public void addDependent(WfTask wfTask) {
wfTaskController.addDependency(this, wfTask);
}
public void commitChanges(OperationResult result) throws SchemaException, ObjectNotFoundException, ObjectAlreadyExistsException {
task.savePendingModifications(result);
}
public void resumeTask(OperationResult result) throws SchemaException, ObjectNotFoundException {
wfTaskController.resumeTask(this, result);
}
public void startWaitingForSubtasks(OperationResult result) throws SchemaException, ObjectNotFoundException {
task.startWaitingForTasksImmediate(result);
}
public void setWfProcessId(String pid) throws SchemaException {
processInstanceId = pid;
task.addModification(
DeltaBuilder.deltaFor(TaskType.class, getPrismContext())
.item(F_WORKFLOW_CONTEXT, F_PROCESS_INSTANCE_ID).replace(pid)
.asItemDelta());
}
public void setProcessInstanceEndTimestamp() throws SchemaException, ObjectNotFoundException, ObjectAlreadyExistsException {
XMLGregorianCalendar now = XmlTypeConverter.createXMLGregorianCalendar(new Date());
task.addModification(
DeltaBuilder.deltaFor(TaskType.class, getPrismContext())
.item(F_WORKFLOW_CONTEXT, F_END_TIMESTAMP).replace(now)
.asItemDelta());
}
public TaskExecutionStatus getTaskExecutionStatus() {
return task.getExecutionStatus();
}
public void removeCurrentTaskHandlerAndUnpause(OperationResult result) throws SchemaException, ObjectNotFoundException {
boolean wasWaiting = getTaskExecutionStatus() == WAITING;
task.finishHandler(result);
boolean isWaiting = getTaskExecutionStatus() == WAITING;
if (wasWaiting && isWaiting) { // if the task was not closed ... (i.e. if there are other handler(s) on the stack)
wfTaskController.unpauseTask(this, result);
}
}
public void computeTaskResultIfUnknown(OperationResult result) throws SchemaException, ObjectNotFoundException {
OperationResult taskResult = task.getResult();
if (result.isUnknown()) {
result.computeStatus();
}
taskResult.recordStatus(result.getStatus(), result.getMessage(), result.getCause());
task.setResultImmediate(taskResult, result);
}
public boolean hasModelContext() {
return getWfTaskUtil().hasModelContext(task);
}
private WfTaskUtil getWfTaskUtil() {
return wfTaskController.getWfTaskUtil();
}
public ModelContext retrieveModelContext(OperationResult result) throws SchemaException, ObjectNotFoundException, CommunicationException, ConfigurationException {
return getWfTaskUtil().getModelContext(task, result);
}
public List<WfTask> listChildren(OperationResult result) throws SchemaException {
List<WfTask> wfTasks = new ArrayList<>();
for (Task subtask : task.listSubtasks(result)) {
wfTasks.add(wfTaskController.recreateChildWfTask(subtask, this));
}
return wfTasks;
}
public ObjectTreeDeltas retrieveResultingDeltas() throws SchemaException {
return getWfTaskUtil().retrieveResultingDeltas(task);
}
public void deleteModelOperationContext() throws SchemaException, ObjectNotFoundException {
getWfTaskUtil().deleteModelOperationContext(task);
}
public void storeModelContext(ModelContext modelContext) throws SchemaException {
getWfTaskUtil().storeModelContext(task, modelContext);
}
public List<WfTask> listDependents(OperationResult result) throws SchemaException, ObjectNotFoundException {
List<WfTask> wfTasks = new ArrayList<>();
for (Task subtask : task.listDependents(result)) {
wfTasks.add(wfTaskController.recreateWfTask(subtask));
}
return wfTasks;
}
public WfTask getParentJob(OperationResult result) throws SchemaException, ObjectNotFoundException {
Task parentTask = task.getParentTask(result);
return wfTaskController.recreateWfTask(parentTask);
}
private PrismContext getPrismContext() {
return wfTaskController.getPrismContext();
}
public String getProcessInstanceName() {
return task.getWorkflowContext() != null ? task.getWorkflowContext().getProcessInstanceName() : null;
}
public String getOutcome() {
return task.getWorkflowContext() != null ? task.getWorkflowContext().getOutcome() : null;
}
public String getAnswerNice() {
return ApprovalUtils.makeNiceFromUri(getOutcome());
}
public String getCompleteStageInfo() {
return WfContextUtil.getCompleteStageInfo(task.getWorkflowContext());
}
@SuppressWarnings("unchecked")
public PrismObject<UserType> getRequesterIfExists(OperationResult result) {
if (task.getWorkflowContext() == null || task.getWorkflowContext().getRequesterRef() == null) {
return null;
}
ObjectReferenceType requesterRef = task.getWorkflowContext().getRequesterRef();
return (PrismObject<UserType>) wfTaskController.getMiscDataUtil().resolveAndStoreObjectReference(requesterRef, result);
}
public void setOutcome(String outcome) throws SchemaException {
task.addModifications(DeltaBuilder.deltaFor(TaskType.class, getPrismContext())
.item(F_WORKFLOW_CONTEXT, F_OUTCOME).replace(outcome)
.asItemDeltas());
}
public void setProcessInstanceStageInformation(Integer stageNumber, Integer stageCount, String stageName, String stageDisplayName)
throws SchemaException {
task.addModifications(DeltaBuilder.deltaFor(TaskType.class, getPrismContext())
.item(F_WORKFLOW_CONTEXT, F_STAGE_NUMBER).replace(stageNumber)
.asItemDeltas());
}
}