Automatically unassign certain UserTaks on assignement

I like to implement the following functionality in camunda:
Given a pool / set of UserTasks, on every claim on one of these task every other task in the same pool should be unclaimed. This should also work over multiple process instances. Which UserTasks belong to a certain pool / set should be defined only in BPMN.

I already exerperimented with TaskListeners and boundary signal events, but with no success.

Anybody has an idea on how one could implement such a behavior?

Nobody any idea?
I think what I need is some sort of ‘marker’ for a task. So on assignment on such a task i could the query for all other task with such a marker and unassign them. But I could not find a solution for that yet.

@Salabasti you would need to write a task listener attached to each of the group of tasks, for the assign action. When assign occurs, you would do search using the Task API find all user task instances that are active in your group (you will need to pick a way to find which are in the group for your process instance (such as using specific Task IDs)). Then you need to execute a UnAssign on each of the user tasks in the group).

If you explain your use case, there may be a better way to model your needs.

Hey Stephen,

Thanks for your reply. That is exactly what i did so far and it is working fine. But using a prefix in the task id as a ‘marker’ is kinda dirty in my opinion.

My use case is as follows:
I want to track the time of work for predefined tasks, that are started in parallel. It should work some sort like a todo list, but with structured bpmn user-tasks. Time measure should start at any claim of a task, pause on unclaim and stop on complete. All of these task can only be performed once at a time, so that the time measurement can not overlap.

This is actually part of my master thesis. Later i want to analyse the processing time of the tasks in addition to other data i collected for the underlying business process.

You have the “mark” your tasks that are in the group of user tasks somehow… so you need to pick a marking scheme; If you mark your tasks with something like extensions, then you will need to parse the BPMN xml each time.

as a simple solution, how about you make it so if a user is claimed/assigned to Task A, and they try and claim task B, it will stop them. and they have to unclaim task A before they can claim Task B. This way your “duration” will be clean and you can use the Claim duration as a ~actual work duration.

Take a look at Limit number of concurrently running instances of a process definition - #9 by wke-rec which limits starting a process instance. You can use the same logic on a Task Listener for assignment to ensure that only 1 of the tasks is currently claimed.
Again you need to choose how you want to “group” your tasks together. Easiest way is with the Task IDs. Harder way is with “Extension” values, that will require you to parse the BPMN xml: Get Extension Properties - Helper Script

I was already looking in the property extention, but that seemed tricky. I think i will just go with prefixes for the task ids, since this is actually working pretty good. Thanks again for your help!

To sum it up, here is the assignement listener class that did the trick for me:

public class TaskAssignmentListener implements TaskListener {
public void notify(DelegateTask delegateTask) {		
	final String localTaskDefinitionKey = delegateTask.getTaskDefinitionKey();
	final String localTaskAssignee = delegateTask.getAssignee();
	final ProcessEngine defaultEngine = ProcessEngines.getDefaultProcessEngine();
	final List<Task> activeTasks = defaultEngine.getTaskService().createTaskQuery().active().list();
	for (Task task : activeTasks) {						
		final String taskDefinitionKey = task.getTaskDefinitionKey();
		final String taskAssignee = task.getAssignee();
		if (taskDefinitionKey.startsWith("SINGLEASSIGN") &&
				!taskDefinitionKey.equals(localTaskDefinitionKey) &&
				taskAssignee != null &&
				taskAssignee.equals(localTaskAssignee)) {
			defaultEngine.getTaskService().claim(task.getId(), null);

THanks for sharing