cancel
Showing results for 
Search instead for 
Did you mean: 

Custom task listener and deleteCandidateGroup()

mandas
Champ in-the-making
Champ in-the-making
Hi,

I 've implemented a task listener for the 'create' event in order to change the candidate groups according to runtime data (I 'm not using spring, so adding an expression to 'candidateGroups' I think it's not supported otherwise). Inside the task listener I do:

delegateTask.deleteCandidateGroup("foo")
delegateTask.addCandidateGroup("bar")

Although a new IdentityLinkEntity for "bar" is created inside delegateTask.getCandidates(), the IdentityLinkEntity for "foo" is not removed, thus it is persisted to the database at the end of the transaction. Am I doing something wrong or is it a bug?

Thanks
12 REPLIES 12

jbarrez
Star Contributor
Star Contributor
Could you post your whole Delegate class?

mandas
Champ in-the-making
Champ in-the-making
import org.activiti.engine.delegate.DelegateTask;
import org.activiti.engine.delegate.TaskListener;

public class MyTaskListener implements TaskListener {

@Override
public void notify(DelegateTask delegateTask) {
  delegateTask.deleteCandidateGroup("foo");
  delegateTask.addCandidateGroup("bar");
}

}

frederikherema1
Star Contributor
Star Contributor
Is the candidate-group "foo" defined in the bpmn20.xml?

mandas
Champ in-the-making
Champ in-the-making
If you are referring to candidate group task property, yes. I debugged this class, and the IdentityLinkEntity of 'foo' was in delegateTask.getCandidates()

frederikherema1
Star Contributor
Star Contributor
It would be great if you should create a simple unit-test highlighting this issue (see the sticky in the forum with good tops on how to do this).

michj_74
Champ in-the-making
Champ in-the-making
I'm running into the same issue now. Has this been resolved?

Thanks,Mike

jbarrez
Star Contributor
Star Contributor
Are you still seeing the issue?
I think some work has been done in that area, but without a Jira issue in this post its hard to know if it has been fixed or not

michj_74
Champ in-the-making
Champ in-the-making
Hi,

yes, I'm still seeing it, I'm using activiti 5.12.1. The process definition is:

<code>
<?xml version="1.0" encoding="UTF-8"?>
<definitions xmlns="http://www.omg.org/spec/BPMN/20100524/MODEL" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:activiti="http://activiti.org/bpmn" xmlns:bpmndi="http://www.omg.org/spec/BPMN/20100524/DI" xmlnsSmiley Surprisedmgdc="http://www.omg.org/spec/DD/20100524/DC" xmlnsSmiley Surprisedmgdi="http://www.omg.org/spec/DD/20100524/DI" typeLanguage="http://www.w3.org/2001/XMLSchema" expressionLanguage="http://www.w3.org/1999/XPath" targetNamespace="http://www.activiti.org/test">
  <collaboration id="Collaboration">
    <participant id="pool1" name="test-NPP-MAIN-NPP" processRef="process_pool15"></participant>
  </collaboration>
  <process id="process_pool15" name="test-NPP-MAIN-NPP-1" isExecutable="true">
    <laneSet id="laneSet_process_pool15">
      <lane id="groupid.pmnpp" name="groupid.pmnpp">
        <flowNodeRef>startevent1</flowNodeRef>
        <flowNodeRef>usertask1</flowNodeRef>
      </lane>
      <lane id="marketing" name="Marketing">
        <flowNodeRef>usertask3</flowNodeRef>
        <flowNodeRef>endevent1</flowNodeRef>
      </lane>
    </laneSet>
    <startEvent id="startevent1" name="Start"></startEvent>
    <userTask activiti:candidateGroups="groupid.pmnpp" id="usertask1" name="Create New Product"  activiti:formKey="myFormKey"><extensionElements><activiti:taskListener event="create" class="com.mycompany.MyTaskAssigner" /></extensionElements></userTask>
    <sequenceFlow id="flow2" sourceRef="startevent1" targetRef="usertask1"></sequenceFlow>
    <sequenceFlow id="flow3" sourceRef="usertask1" targetRef="usertask3"></sequenceFlow>
    <userTask activiti:candidateGroups="groupid.pmnpp" id="usertask3" name="Verify Product" activiti:async="true"  activiti:formKey="myFormKey"><extensionElements><activiti:taskListener event="create" class="com.mycompany.MyTaskAssigner" /></extensionElements></userTask>
    <endEvent id="endevent1" name="End"></endEvent>
    <sequenceFlow id="flow4" sourceRef="usertask3" targetRef="endevent1"></sequenceFlow>
  </process>
  <bpmndi:BPMNDiagram id="BPMNDiagram_Collaboration">
    <bpmndi:BPMNPlane bpmnElement="Collaboration" id="BPMNPlane_Collaboration">
      <bpmndi:BPMNShape bpmnElement="pool1" id="BPMNShape_pool1">
        <omgdc:Bounds height="520.0" width="500.0" x="110.0" y="70.0"></omgdc:Bounds>
      </bpmndi:BPMNShape>
      <bpmndi:BPMNShape bpmnElement="groupid.pmnpp" id="BPMNShape_groupid.pmnpp">
        <omgdc:Bounds height="260.0" width="480.0" x="130.0" y="70.0"></omgdc:Bounds>
      </bpmndi:BPMNShape>
      <bpmndi:BPMNShape bpmnElement="marketing" id="BPMNShape_marketing">
        <omgdc:Bounds height="260.0" width="480.0" x="130.0" y="330.0"></omgdc:Bounds>
      </bpmndi:BPMNShape>
      <bpmndi:BPMNShape bpmnElement="startevent1" id="BPMNShape_startevent1">
        <omgdc:Bounds height="35.0" width="35.0" x="180.0" y="198.0"></omgdc:Bounds>
      </bpmndi:BPMNShape>
      <bpmndi:BPMNShape bpmnElement="usertask1" id="BPMNShape_usertask1">
        <omgdc:Bounds height="71.0" width="145.0" x="320.0" y="180.0"></omgdc:Bounds>
      </bpmndi:BPMNShape>
      <bpmndi:BPMNShape bpmnElement="usertask3" id="BPMNShape_usertask3">
        <omgdc:Bounds height="55.0" width="105.0" x="310.0" y="390.0"></omgdc:Bounds>
      </bpmndi:BPMNShape>
      <bpmndi:BPMNShape bpmnElement="endevent1" id="BPMNShape_endevent1">
        <omgdc:Bounds height="35.0" width="35.0" x="470.0" y="400.0"></omgdc:Bounds>
      </bpmndi:BPMNShape>
      <bpmndi:BPMNEdge bpmnElement="flow2" id="BPMNEdge_flow2">
        <omgdi:waypoint x="215.0" y="215.0"></omgdi:waypoint>
        <omgdi:waypoint x="320.0" y="215.0"></omgdi:waypoint>
      </bpmndi:BPMNEdge>
      <bpmndi:BPMNEdge bpmnElement="flow3" id="BPMNEdge_flow3">
        <omgdi:waypoint x="392.0" y="251.0"></omgdi:waypoint>
        <omgdi:waypoint x="362.0" y="390.0"></omgdi:waypoint>
      </bpmndi:BPMNEdge>
      <bpmndi:BPMNEdge bpmnElement="flow4" id="BPMNEdge_flow4">
        <omgdi:waypoint x="415.0" y="417.0"></omgdi:waypoint>
        <omgdi:waypoint x="470.0" y="417.0"></omgdi:waypoint>
      </bpmndi:BPMNEdge>
    </bpmndi:BPMNPlane>
  </bpmndi:BPMNDiagram>
</definitions>
</code>

The task assigner code is:

<code>
package com.mycompany;

import java.util.HashSet;
import java.util.Set;
import java.util.StringTokenizer;

import org.activiti.engine.delegate.DelegateTask;
import org.activiti.engine.delegate.TaskListener;
import org.activiti.engine.task.IdentityLink;

public class MyTaskAssigner implements TaskListener {

private static final long serialVersionUID = 1L;

@Override
public void notify(DelegateTask delegateTask) {
  Set<IdentityLink> candidates = new HashSet<IdentityLink>();
  candidates.addAll(delegateTask.getCandidates());
  for (IdentityLink candidate : candidates) {
   if (candidate.getGroupId() != null) {
    if (candidate.getGroupId().contains(",")) {
     StringTokenizer t = new StringTokenizer(candidate.getGroupId(), ",");
     while (t.hasMoreTokens()) {
      delegateTask.deleteCandidateGroup(t.nextToken());
     }
    } else {
     delegateTask.deleteCandidateGroup(candidate.getGroupId());
    }
   }
  }
}

}
</code>

When the process is executing I see that it jumps into the task assigner and delegateTask.deleteCandidateGroup() is being called but the assignment to the group remains unchanged. Am I doing something wrong?

Thanks,
Mike

frederikherema1
Star Contributor
Star Contributor
The issue here is that the deleteIdentityLink() method will *try to* delete the identity-link in the database. Since the task isn't actually committed yet, the delete won't do anything. When the task-create call is done, they command-context will flush the pending entities that are created (process-instance, task and identity-links). Since the identity-links for candidate group are already queued for creation, they will be created.

This is an issue indeed and needs to be fixed. However, as a workaround (and perhaps a more valid approach for this kind of use case) I suggest using an expressions in the "activiti:candidateGroup" attribute, pointing to a bean in the context that decides what candidates are applicable for this task, at the time it's started. This will avoid having to use the listener and make use of the candidate-expression, where it was designed for. Eg. activiti:candidateGroups="${candidateChecker.getGroupsForTask(variableRelevantForDecision}".