cancel
Showing results for 
Search instead for 
Did you mean: 

Java Service Task: Passivate w/out Receive Task

jjfutt
Champ in-the-making
Champ in-the-making
Attached above is an illustration of a problem we are trying to solve. We have tasks that are implemented as 'Java Service Tasks'. However, these tasks are long running asynchronous tasks that invoke a request to do something (for example send a message to a JMS queue), but then have to wait for an asynchronous response. Currently, we achieve this by placing a 'Receive' task immediately following the Java Service Task which causes the Activiti process to pause and passivate. Then, when a JMS message is received, our external application layer resumes the process by signaling the Process Engine with the id of the Receive Task. However, this is complicated because (a ) we have to send the id of the receive task to our Java Task so it can be used for correlation and used in signaling the process (b ) Visually it forces us to break up the process into two nodes, a Java Invoke and a Receive.

We have moved to Activiti from JBPM and JBPM provides a 'Domain Specific Task' similar to Java Service Task. However, unlike the Activiti Delegate in Java Service Task, their Domain Specific Task allows us to exit out of the delegate method, without calling it the node as 'complete'. This way the engine knows that the method execution is complete, but the it's not okay to proceed forward yet. Until an external application signals the process to resume. However, in the case of JBPM, we can signal with the id of the original Java Task and that eliminates the need to have an extra 'Receive' like task, and also doesn't require the Java Task to know about the id of another node from the process.

I know that there have been recent improvements and enhancements in the engine, but reading the documentation, it doesn't seem that there is any new capability that would solve our problems. So I'd like your advice on:

(i) whether there is a way to complete the Java User Task delegate method, but prevent the engine from proceeding forward until an external process tells it to move on?

(ii) Another way to describe the same need: We want to passivate the Activiti process while waiting for the asynchronous response because otherwise the Java Method might timeout, and also because without a passivation the process will continue to occupy a thread of execution. So we are essentially looking for a way to passivate/pause after the invocation of Java call, until an asynchronous response is received.

Thanks in advance.

NOTE: The process shown in the attached diagram is a very simplified version created solely for the purpose of illustrating this question. Hence the long descriptive names for the process nodes.
11 REPLIES 11

ronald_van_kuij
Champ on-the-rise
Champ on-the-rise
Implement a service task which implements ActivityBehavior… it's in the docs:

http://www.activiti.org/userguide/index.html#bpmnJavaServiceTask

jjfutt
Champ in-the-making
Champ in-the-making
Thank you for your response Ronald.

I also found the same suggestion (Use ActivitiBehavior) instead of the Delegate in another thread (http://forums.activiti.org/en/viewtopic.php?f=6&t=1929&view=next).

I have looked at the source of UserTask which extends ActivitiBehavior to see how it accomplishes this task. One thing that is not readily clear and I am unable to find details of it in the documentation is once the 'execute' method is complete, what mechanism should be used by an external job/process to interact with the Task and tell it to 'resume'.

I see that the 'UserTask' has a following method that responds to a signal and calls the 'leave' method (which is exactly what I want too):

[attachment=0]activitiUserTask.png[/attachment]

However does it mean that:

(i) The only way to leave a ServiceTask (ActivitiBehavior) at the desired time is to call the leave method from within the Service Task.
(ii) If so, is 'signal' the only want for an external process to interact with the Service Task and tell it to execute the leave method. If there is documentation on hos to implement this mechanism, can yo please point me to it.

Thank you in advance for your help.

ronald_van_kuij
Champ on-the-rise
Champ on-the-rise
All you assumptions are correct. You always need to have a reference to 'something' 'somewhere'. Whether you pass it on to the external service, or use some 'join table' to store a reference to the external service and a task. That's always how callback things work.

jjfutt
Champ in-the-making
Champ in-the-making
thank you very much for your response. I did understand that some kind of reference would need to be held onto for a future call back, but just was making sure that I am implementing the appropriate and recommended mechanism.

I am pasting an abbreviated version of the code below, so if someone else looks at this thread, they have an example. Also, I'd appreciate if you could alert me if you see something incorrect being done in the following block.


public class TestTask extends TaskActivityBehavior
{
private Expression testAttribute;

        @Override
public void execute(ActivityExecution execution)
{
  String commentString = (String) testAttribute.getValue(execution);
  // do something

                String callbackId = execution.getId();

                //  Save this id somewhere, or pass it to the outgoing asynchronous task,
                // as this will be needed to signal this activity later when the long running external task is complete

}

public void signal(ActivityExecution execution, String signalName, Object signalData) throws Exception
{
  // do something
  leave(execution);
}

}

And external application, process, upon completion of the external task, calls the signal with the callBackId (execution.getId()) retrieved and saved previously.

// Retrieve the execution using the callBack Id saved previously
this.processEngine.getRuntimeService().signal(previouslySavedCallBackId);

jorell
Champ in-the-making
Champ in-the-making
jjfutt,
Thanks for this, it was exactly what I was looking for. I do have one question though, how do you pass back variables into the execution. So so your TestTask makes an external call and expects some variables in the process to be updated based on the result of that external call. In this case you not only need to signal your task to move your process forward but you also need to update some variables before that happens.
Thanks for your help.

jbarrez
Star Contributor
Star Contributor
The signal method takes a second parameter, a map of variables.

boekhold
Champ in-the-making
Champ in-the-making
If you use the method from jjfutt's post, does Activiti (5.17) keep open any DB transactions between leaving the "execute()" method and the call to "leave()" inside "signal()"?

Also, in this use-case, I suppose that the "signalName" parameter to "signal()" will be null? And what about signalData, should I cast that to Map<String,Object>?

Maarten

jbarrez
Star Contributor
Star Contributor
> If you use the method from jjfutt's post, does Activiti (5.17) keep open any DB transactions between leaving the "execute()" method and the call to "leave()" inside "signal()"?

No.

> Also, in this use-case, I suppose that the "signalName" parameter to "signal()" will be null

Indeed.

> And what about signalData, should I cast that to Map?

Not needed if you dont use it. It is something you pass into the API, so you choose what you want.

boekhold
Champ in-the-making
Champ in-the-making
I'm studying TaskActivityBehavior and its super class AbstractBpmnActivityBehavior, and I'm wondering: if I extend TaskActivityBehavior and override signal(execution, signalName, signalData), do I need to include a check for "signalName == compensationDone" as well like in AbstractBpmbActivityBehavior?

<code>
  @Override
  public void signal(ActivityExecution execution, String signalName, Object signalData) throws Exception {
    if("compensationDone".equals(signalName)) {
      signalCompensationDone(execution, signalData);
    } else {
      // Insert my own logic followed by leave(execution)
    }
  }
</code>

Maarten