Make candidates upper case

Hi,

to avoid problems during task query we tried to make all candidates to upper case in the notifyCreate method of a TaskListener. We used following code in the listener:

delegateTask.getCandidates().forEach(candidate -> {
    ... // check if group or user
	String groupId = candidate.getGroupId();
	if(!StringUtils.isAllUpperCase(groupId)) { // make candidate group upper case
		delegateTask.deleteCandidateGroup(groupId);
		delegateTask.addCandidateGroup(StringUtils.upperCase(groupId));
	}
}

When that code gets executed the old lower case group is not removed from the identity link table. How can we achieve it, so that only the upper case group is in the identity link table? Or how can we achieve it that all groupIds and userIds are case insensitive or all upper case?

Regards

Alexander

I’m not an experienced Java programmer, but isn’t there a method like toUpperCase() you can use and simply force the value to upper case?

I use an upper case method to make all candidates and the assignee to upper case when a task is created.

I have a different problem:

When I have a candidate definition on a task, lets say candidate group=“group1” and I use the above code I have two identity link entries in the database.

group1 and GROUP1

So when I first delete the group1 and then add the GROUP1 I have two entries. My question is: Is that the correct behaviour or is it a bug?

Regards

Alexander

Hi @Alexander,

I assume that your code is executed in a create listener, so I tested it by myself and observe the same behaviour, that is, new (uppercase) links are created but the old (mixed-case) are not deleted.

I discussed that problem with @Niall (blame on him if we are wrong :wink:) and we agreed on the following explanation:
When the process engine calls our create listener, the delegate task is already created in memory and the corresponding CREATE-statements are sent to database, but the database transaction is not committed yet. In particular, the IdentityLinks are in memory, but not in database. The next commit happens when we are in the next “wait state”, that is, when the task is successfully created and we are waiting for some human interaction.
That said, in the create listener, addCandidate() works as expected. It updates the data in memory and fires some CREATE-statements to the database (which also will be committed later). Different from that, deleteCandidateGroup() is supposed to remove already existing IdentityLinks from database. This is the only possible semantics, because deleting links from memory that are not in database would mess up the current transaction. Hence, in your example, that method simply does nothing.

All ways that come into my mind to achieve exactly what you are trying to do are somewhat awkward and hackish.

I suggest that you explain in more detail what your problem is and why you need that transformation at that point in time, maybe we can find a better solution together.

Best,
Ragnar

Hi Ragnar,

thanks for the detailed explanation. I feared that it is something you explained.

The main point with formatting all upper or lower case is simple to avoid strange assignee and task inbox behaviour during execution of the process and the queries for tasks.

So when a developer writes “Administrators” in the workflow definition as candidates we wanted to make it upper case, so when we use a task query we always get all needed tasks. Else we will get issues why do I not see the task although the candidate group is correct.

Because the candidates or the assignee could be expressions as well, we wanted to do that as late as possible to get all the values in correct format.

We would not need that if the query for task would be case insensitive for assignee, owner and candidates. But currently as I know it, it is not.

Regards

Alexander

Hi guys,

I consider this a bug and created a JIRA issue: https://app.camunda.com/jira/browse/CAM-8281

@Ragnar: Your explanation is very good. One correction: any modifying database statements are only executed immediately before the running engine command ends (to reduce the duration of write locks to a minimum). So the INSERT statements have not been issued yet when the task listener is executed. That doesn’t change the result, though :slight_smile:

@Alexander: I haven’t got a good workaround for you. You could achieve proper deletion by using internal API like so:

DelegateTask task = ..;
TaskEntity taskEntity = (TaskEntity) task;
List<IdentityLinkEntity> identityLinks = taskEntity.getIdentityLinks();

Iterator<IdentityLinkEntity> linkIt = identityLinks.iterator();
while (linkIt.hasNext())
{
  IdentityLinkEntity link = linkIt.next();
  if (<check condition>)
  {
    link.delete(); // => this removes the in-memory identity link
    linkIt.remove();
  }
}

Note that this uses internal API and may break with any future version. I also recommend to test this properly.

Cheers,
Thorben

1 Like

Hi,

first: I am generous enough to not only share the blame but also the compliments, so, @Niall: very good explanation! :+1:

@thorben: Thanks for clarification, let’s talk about that weird MyBatis stuff over a beer soon, would be happy to learn more.

@Alexander: Here is another workaround. Even much more ugly but not using internal API: Hook into deployment process and modify BPMN-model programmatically. Here, you could also modify expressions by wrapping the existing expression into something that changes the result to uppercase. Nevertheless, I do not recommend since it is a lot of work and, especially in case of expressions, risky. But if you are interested, I can try to find some snippets helping you (I know that there are people using such a mechanism to automatically set asyncBefore and asyncAfter programmatically for all user tasks on deployment).

Best,
Ragnar

Hi,

I also have to thank everybody for the deep explanation. I think will live for now with the old groups getting not deleted. That is not nice but works.

I also had in mind to parse BPMN during deployment, but we did decide against it because of the risks breaking expressions. We already use that mechanism for other changes on the BPMN workflows (e.g. checking that for script task the script format is set, also a common error).

So thanks everybody and I will wait for a hopefully coming bug fix :slight_smile:

Regards

Alexander

Hi,

now I have an acceptable work around, I tried something similar to Thorbens hint. So I cast the IdentityLink to IdentityLinkEntity and call the entity.delete(false) method on them (we do not want history entries for cleanup). That removes the link from the database as well. We are using Camunda in version 7.7.0.

So it seems that in our version the entity.delete() does not only remove the link from the in-memory store, but from the database as well when all operation get commited.

Regards

Alexander