cancel
Showing results for 
Search instead for 
Did you mean: 

How to verify user task assignment at runtime?

raj1
Champ in-the-making
Champ in-the-making
I have a Business Process with two user tasks ut1 and ut2 namely. Both usertasks have same candidate group Management. After deploying the process, at runtime, the requirement is to ensure that usertasks ut1 and ut2 are preformed by different users in the Candidate groups Management. For e.g.  if Kermit performs the task ut1, he(Kermit) cannot claim & perform the task ut2 and also ut2 has to be claimed & performed by another user in Candidate group Management.

To achieve this:

I will have to create Task listener for the user task ut2 on the event "assignment", then in the task listener get history of the usertask ut1 and check if the user assigned for task ut2 and the user performed the task ut1 are same.

If they are same then mark the task ut2 as unassigned.

Is this the correct approach?

Please guide me as how to go about and achieve this requirement?
21 REPLIES 21

heymjo
Champ on-the-rise
Champ on-the-rise
i was just looking at this recently, what you describe is the four-eyes principle (but can be extended to any number of eyes really)


<userTask id="payment_execute" name="Execute Payment" >
  <extensionElements>
    <!– upon task creation the listener is called –>
    <activiti:taskListener event="create" class="my.FourEyesTaskListener">
       <!– here we set the id of the other task –>
       <activiti:field name="fourEyesTask" stringValue="payment_approve" />
    </activiti:taskListener>
  </extensionElements>
</userTask>

and the task listener something like


public class FourEyesTaskListener implements TaskListener {

  // injected by activiti
  private String fourEyesTask;

  // see http://www.activiti.org/javadocs/org/activiti/engine/delegate/DelegateTask.html
  public void notify(DelegateTask delegateTask) {
    // find out the user id that executed the four eyes task
    String userId = ….
    //
    delegateTask.deleteCandidateUser(userId);
  }
}

so yes pretty much as you describe it but i would use deleteCandidateUser instead of unassign so that the second user would never even see the task for assignment, and also the listener runs on 'create' event instead of 'assignment'.

HTH
Jorg

raj1
Champ in-the-making
Champ in-the-making
Thanks for your reply. I get the approach but to obtain the user id of the executed task I need to use the History service provided by the Activiti Engine . History service can be accessed via the instance of Process engine using getHistoryService();
How do I obtain the instance of process engine inside the task listener? I am not sure if my understanding is correct.


Please guide me!!!

heymjo
Champ on-the-rise
Champ on-the-rise
Well if you use Spring you can make the listener a managed bean and have it wire the historyservice to the listener.

frederikherema1
Star Contributor
Star Contributor
The engine API shouldn't be used from within service-tasks, execution- and tasklisteners now. If you do, it can mess up transactions/connections, especially if you update data that is related to the current process…

raj1
Champ in-the-making
Champ in-the-making
Frederikheremans, thanks for your reply.

Is there any work arround available currently in Activiti to achieve the above mentioned requirement of verifying user task assignment at runtime?

Kindly guide me!!!

frederikherema1
Star Contributor
Star Contributor
The only way you achieve this, is to use the internal sessions (you hear it, internal - no guerantees of backward compatibility):

org.activiti.engine.impl.context.Context.getCommandContext()
   .getHistoricTaskInstanceManager()
   ….

heymjo
Champ on-the-rise
Champ on-the-rise
Hi,

Can you please elaborate on this

The engine API shouldn't be used from within service-tasks, execution- and tasklisteners now. If you do, it can mess up transactions/connections,

It is also said here http://forums.activiti.org/en/viewtopic.php?f=6&t=1353. In what way could connections/transactions be messed up , and if so is that not a serious limitation of the engine ? Introspecting the process from a listener seems something a lot of people will want to do.

My use case is that of four-eyes principle. For this i need to lookup a previously executed task on the same process instance, obtain the user that executed it, and then exclude that user from the candidate users on the current task.

frederikherema1
Star Contributor
Star Contributor
When activiti executes a command (for example complete task) it will have a single command-context where modifications to entities are stored. Once the command finishes, all modifications are flushed onto the SQLConnection. Depending on your transaction-strategy, these changes will be comitted.

When you use the API from within a taskListener, you actually execute a new command from within a running command. If the datasource is transaction-aware (in case of spring), you'll use the same connection as the command-context of the calling command. If not, a fresh one is used. In both cases, all changes done in the first command, aren't visible to the second command, since the changes aren't flushed yet.

Most of the time (when reading stuff from activiti in the second API-call) you won't see an issue. But when, eg. the taksListener/executionListener is executed, when other activities are executed before that, that part of the data will be invisible. The same with process/execution-state of the process that is currently running and caling the listener…

I agree that this is a limitation of the system, but exposing the full API from within the context isn't safe (eg. signalling the same process etc. from within the process).

Anyone else thoughts about this?

heymjo
Champ on-the-rise
Champ on-the-rise
i agree that the processengine cannot be completely reentrant. But there is probably a big portion of the api which is 'safe' to use and it would suffice to just document this.

However, i just tried to let Spring wire the historyservice to a listener bean and it throws a NPE. Looking at the way the activiti spring config works it does not look trivial to avoid this:


  <bean id="myProcessEngineConfiguration" class="org.activiti.spring.SpringProcessEngineConfiguration">
   …..
    <property name="beans">
      <map>
        <entry key="fourEyesTaskListener" value-ref="fourEyesTaskListener"/>
      </map>
    </property>
  </bean>

  <bean id="fourEyesTaskListener" class="my.FourEyesTaskListener"/>

This listener is referenced from the process engine config and will be initialized first. Since the listener holds an @Autowired reference to the ProcessEngine factory bean it will throw NPE because this bean has not been instantiated yet (it is instantiated by the process engine configuration bean) 😐 I might need to resort to a BeanFactoryPostProcessor but it's not so nice a solution.

So i tried going via Context.getCommandContext().getHistoricTaskInstanceManager() but i cannot seem to obtain the specific task as via the historyservice

        HistoricTaskInstance instance = processEngine.getHistoryService().createHistoricTaskInstanceQuery()
                .processInstanceId(delegateTask.getExecution().getProcessInstanceId())
                .taskName(fourEyesTask.getExpressionText()).singleResult();