Get / Preview next task

Is there a way that i can get or prevew the next step of a task using the Process Engine API?

If the task has not be initialized yet (the token/engine has not reached the point where the task is active), you cannot “view” the task. The only option is to parse the BPMN xml and retrieve the user tasks in the model.

It works like this because the “next” task is not known until the execution evaluates the data after the activity before the next user task.

consider scenario such as:

The outcomes of scripts, expressions, delegates, etc in each of the gateways could have 1 or more possible outcomes.

Thanks Stephen, what i was looking for is exactly something to parse the model and get all the possible next steps.

I can do that with the RepositoryService.getBpmnModelInstance ?

You would use the Model API https://docs.camunda.org/manual/7.9/user-guide/model-api/bpmn-model-api/

At its most basic: I believe you would do something like: “Get Flow Node of the user task in question, getting the “To” value (leaving the user task), and get each task connected to the other other of the sequence flow and looking for other user tasks”.

The complication will become:

You can have many sequence flows that leave a Task. You have have gateways or other Sync tasks that come after your user task, but the user task is still the “next task”, you can have 1n user tasks, and if you are dealing with sub-processes and Multi-instance, you get a whole bunch of other scenarios to deal with :wink:

1 Like

@Rayel here is a little snippet of code that might be relevant

String taskId = task().getTaskDefinitionKey()
UserTask userTask = modelUserTasks().getModelElementById(taskId)
Collection<FlowNode> sequenceFlows = userTask.getSucceedingNodes().filterByType(UserTask).list()
sequenceFlows.each {
   println it.getName()
}

This is from a unit test. So task() is just a helper that is returning the current task that the process is waiting on.

You get the task Def Id (the id value in the BPMN of the Task), and get the model element from the BpmnModelInstance. Then there is a helper method for doing a query on all flow nodes.

If you want to go further in the flow nodes then you will need to write a recursion loop i believe, to get each node on each subsequent activity. Looping in the BPMN is also likely a issue, where it would overflow (most likely)

1 Like

Thanks @StephenOTT

For who may need, here is my solution to evaluate the gateway nodes and get the next possible task…

I dont know if it will work for every scenario, but worked for my process flow.

	public List<UserTask> getNextTasks(String processDefinitionId, String taskDefinitionKey, Map<String, Object> vars) {
		BpmnModelInstance modelInstance = repositoryService.getBpmnModelInstance(processDefinitionId);		
		ModelElementInstance instance = modelInstance.getModelElementById(taskDefinitionKey);
		FlowNode flowNode = (FlowNode)instance;
		return getOutgoingTask(flowNode, vars);
	}
	private List<UserTask> getOutgoingTask(FlowNode node, Map<String, Object> vars) {
		List<UserTask> possibleTasks = new ArrayList<>();
		for(SequenceFlow sf: node.getOutgoing()) {
			if (sf.getName() != null) {
				LOGGER.info("Entering flow node {}", sf.getName());
			}
			boolean next = true;
			if (sf.getConditionExpression() != null) {
				ExpressionFactory factory = new ExpressionFactoryImpl();
				SimpleContext context = new SimpleContext(new SimpleResolver());
				
				LOGGER.info("Generating map vars {}", vars.toString());
				for (Map.Entry<String, Object> v : vars.entrySet()) {
					if(v.getValue() instanceof Boolean) {
						factory.createValueExpression(context, "${"+ v.getKey() +"}", Boolean.class).setValue(context, v.getValue());
					}else if(v.getValue() instanceof java.util.Date) {
						factory.createValueExpression(context, "${"+ v.getKey() +"}", java.util.Date.class).setValue(context, v.getValue());
					}else {
						factory.createValueExpression(context, "${"+ v.getKey() +"}", String.class).setValue(context, v.getValue());
					}
				}
				ValueExpression expr1 = factory.createValueExpression(context, sf.getConditionExpression().getTextContent(), boolean.class);
				
				next = (Boolean)expr1.getValue(context);
				LOGGER.info("Evaluating expression {} to result {}", sf.getConditionExpression().getTextContent(), expr1.getValue(context));
				
			}
			
			if (next && sf.getTarget() != null) {
				if (sf.getTarget() instanceof UserTask) {
					LOGGER.info("Found next user task {}", sf.getTarget().getName());
					possibleTasks.add((UserTask)sf.getTarget());
					break;
				}
				
				possibleTasks.addAll(getOutgoing(sf.getTarget(), vars));
			}
			
		}
		return possibleTasks;
	}
4 Likes

Thanks for sharing!

@StephenOTT @Rayel What if interrupting boundary event(conditional event) is attached to the activity and it has different flow connected to other activity. The above code will fail to read the outgoing sequence flow from the boundary event.

@aravindhrs you need to get the outgoing sequence flows and then check what activity class/type is on the other end (the target) of the sequence flow