org.ow2.petals.activitibpmn.incoming.operation.CompleteUserTaskOperation Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of petals-se-activiti Show documentation
Show all versions of petals-se-activiti Show documentation
BPMN Service Engine based on Activiti
/**
* Copyright (c) 2015-2016 Linagora
*
* This program/library is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 2.1 of the License, or (at your
* option) any later version.
*
* This program/library is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
* for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program/library; If not, see http://www.gnu.org/licenses/
* for the GNU Lesser General Public License version 2.1.
*/
package org.ow2.petals.activitibpmn.incoming.operation;
import static org.ow2.petals.activitibpmn.ActivitiSEConstants.Activiti.VAR_PETALS_CORRELATED_FLOW_INSTANCE_ID;
import static org.ow2.petals.activitibpmn.ActivitiSEConstants.Activiti.VAR_PETALS_CORRELATED_FLOW_STEP_ID;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.logging.Logger;
import javax.xml.namespace.QName;
import javax.xml.xpath.XPathExpressionException;
import org.activiti.engine.HistoryService;
import org.activiti.engine.IdentityService;
import org.activiti.engine.RuntimeService;
import org.activiti.engine.TaskService;
import org.activiti.engine.history.HistoricTaskInstance;
import org.activiti.engine.task.Task;
import org.ow2.petals.activitibpmn.incoming.operation.annotated.AnnotatedOperation;
import org.ow2.petals.activitibpmn.incoming.operation.annotated.CompleteUserTaskAnnotatedOperation;
import org.ow2.petals.activitibpmn.incoming.operation.exception.NoProcessInstanceIdValueException;
import org.ow2.petals.activitibpmn.incoming.operation.exception.OperationProcessingException;
import org.ow2.petals.activitibpmn.incoming.operation.exception.OperationProcessingFault;
import org.ow2.petals.activitibpmn.incoming.operation.exception.ProcessInstanceNotFoundException;
import org.ow2.petals.activitibpmn.incoming.operation.exception.TaskCompletedException;
import org.ow2.petals.activitibpmn.incoming.operation.exception.UnexpectedUserException;
import org.ow2.petals.activitibpmn.utils.XslUtils;
import org.ow2.petals.commons.log.FlowAttributes;
import org.ow2.petals.commons.log.Level;
import org.ow2.petals.commons.log.PetalsExecutionContext;
import org.ow2.petals.component.framework.api.message.Exchange;
import org.w3c.dom.Document;
/**
* The operation to complete the user task of process instance
*
* @author Bertrand ESCUDIE - Linagora
* @author Christophe DENEUX - Linagora
*
*/
public class CompleteUserTaskOperation extends ActivitiOperation {
/**
* The identity service of the BPMN engine
*/
private final IdentityService identityService;
/**
* The runtime service of the BPMN engine
*/
private final RuntimeService runtimeService;
/**
* The task service of the BPMN engine
*/
private final TaskService taskService;
/**
* The history service of the BPMN engine
*/
private final HistoryService historyService;
/**
* @param annotatedOperation
* Annotations of the operation to create
* @param logger
*/
public CompleteUserTaskOperation(final AnnotatedOperation annotatedOperation, final TaskService taskService,
final IdentityService identityService, final HistoryService historyService,
final RuntimeService runtimeService, final Logger logger) {
super(annotatedOperation, logger);
this.identityService = identityService;
this.taskService = taskService;
this.historyService = historyService;
this.runtimeService = runtimeService;
}
@Override
protected void doExecute(final Document incomingPayload, final String bpmnUserId,
final Map taskVars, final Map outputNamedValues, final Exchange exchange)
throws OperationProcessingException {
// Get the process instance identifier
final String processInstanceId;
try {
processInstanceId = this.proccesInstanceIdXPathExpr.evaluate(incomingPayload);
if (processInstanceId == null || processInstanceId.trim().isEmpty()) {
throw new NoProcessInstanceIdValueException(this.wsdlOperation);
}
if (this.logger.isLoggable(Level.FINE)) {
this.logger.fine("Process instance identifier value: " + processInstanceId);
}
} catch (final XPathExpressionException e) {
throw new OperationProcessingException(this.wsdlOperation, e);
}
// Get the task
final List taskList = this.taskService.createTaskQuery().processInstanceId(processInstanceId)
.taskDefinitionKey(this.actionId).taskCandidateUser(bpmnUserId).list();
if ((taskList == null) || (taskList.isEmpty())) {
throw this.investigateMissingTask(processInstanceId, bpmnUserId);
}
final Task taskToComplete = taskList.get(0);
final String taskId = taskToComplete.getId();
// Set flow attributes as task local variables that will be used by Activiti event listener to generate a MONIT
// trace
final FlowAttributes exchangeFlowAttibutes = PetalsExecutionContext.getFlowAttributes();
final Map taskLocalVariables = new HashMap(2);
taskLocalVariables.put(VAR_PETALS_CORRELATED_FLOW_INSTANCE_ID, exchangeFlowAttibutes.getFlowInstanceId());
taskLocalVariables.put(VAR_PETALS_CORRELATED_FLOW_STEP_ID, exchangeFlowAttibutes.getFlowStepId());
this.taskService.setVariablesLocal(taskToComplete.getId(), taskLocalVariables);
// Before to complete the task, we set explicitly its assignee. It is not done by Activiti engine when
// completing the task.
this.taskService.setAssignee(taskToComplete.getId(), bpmnUserId);
// We complete the task
try {
this.identityService.setAuthenticatedUserId(bpmnUserId);
this.taskService.complete(taskId, taskVars);
} finally {
this.identityService.setAuthenticatedUserId(null);
}
// To prepare the output response, we add named value dedicated to this operation:
// - task local variables
// - process instance variables
// - the identifier of the process instance
// As the task is completed, it is retrieved from the history.
// TODO: Are task local variables different from the provided variables 'processVars' ? If no, don't
// retrieve them and use directly 'processVars'
final HistoricTaskInstance executedTask = this.historyService.createHistoricTaskInstanceQuery().finished()
.taskId(taskToComplete.getId()).includeProcessVariables().includeTaskLocalVariables().singleResult();
if (executedTask == null) {
// This exception should not occur
throw new OperationProcessingException(this.wsdlOperation, String.format(
"The just completed task '%s' is not found in the history for the process instance '%s'.",
this.actionId, processInstanceId));
}
for (final Entry processVariable : executedTask.getTaskLocalVariables().entrySet()) {
// TODO: Create unit test for these task local variables
outputNamedValues.put(
new QName(ActivitiOperation.SCHEMA_OUTPUT_XSLT_TASK_PARAMS, processVariable.getKey()),
XslUtils.convertBpmnVariableValueToXslParam(processVariable.getValue()));
}
for (final Entry processVariable : executedTask.getProcessVariables().entrySet()) {
outputNamedValues.put(new QName(ActivitiOperation.SCHEMA_OUTPUT_XSLT_PROCESS_INSTANCE_PARAMS,
processVariable.getKey()), XslUtils.convertBpmnVariableValueToXslParam(processVariable.getValue()));
}
outputNamedValues.put(new QName(ActivitiOperation.SCHEMA_OUTPUT_XSLT_SPECIAL_PARAMS,
SCHEMA_OUTPUT_XSLT_PARAM_PROCESS_INSTANCE_ID), processInstanceId);
outputNamedValues.put(new QName(ActivitiOperation.SCHEMA_OUTPUT_XSLT_SPECIAL_PARAMS,
SCHEMA_OUTPUT_XSLT_PARAM_USER_ID), bpmnUserId);
}
/**
*
* Investigate why no active task was found for the process instance identifier.
*
*
* Several possible causes:
*
the process instance id does not exist,
* the process instance is finished,
* the task was completed with a previous service call,
* the task is assigned to another user.
*
*
*
* @param processInstanceId
* The process instance identifier
* @param bpmnUserId
* The user identifier used for the task assignee
* @throws OperationProcessingFault
* The cause to have not found an active task for the process instance identifier
* @throws OperationProcessingException
* No cause found.
*/
private OperationProcessingException investigateMissingTask(final String processInstanceId, final String bpmnUserId) {
if (this.historyService.createHistoricProcessInstanceQuery().finished().processInstanceId(processInstanceId)
.singleResult() != null) {
// The process instance is finished, so the task is finished !
return new TaskCompletedException(this.wsdlOperation, processInstanceId, this.actionId);
} else if (this.runtimeService.createProcessInstanceQuery().processInstanceId(processInstanceId).singleResult() == null) {
// No active process instance found for the process instance identifier
return new ProcessInstanceNotFoundException(this.wsdlOperation, processInstanceId);
} else if (this.historyService.createHistoricTaskInstanceQuery().finished()
.processInstanceId(processInstanceId).taskDefinitionKey(this.actionId).singleResult() != null) {
// The task of the active process instance is finished
return new TaskCompletedException(this.wsdlOperation, processInstanceId, this.actionId);
} else if (this.taskService.createTaskQuery().processInstanceId(processInstanceId)
.taskDefinitionKey(this.actionId).singleResult() != null) {
// The task assignee is not the expected one
// TODO: Add a unit test
return new UnexpectedUserException(this.wsdlOperation, processInstanceId, this.actionId, bpmnUserId);
} else {
// This error case should not occur. If this error occurs, it is likely that an business error case is
// missing from the above conditions
return new OperationProcessingException(wsdlOperation, String.format(
"The task '%s' is not a current user task to complete for the process instance '%s'.",
this.actionId, processInstanceId));
}
}
@Override
public String getAction() {
return CompleteUserTaskAnnotatedOperation.BPMN_ACTION;
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy