cancel
Showing results for 
Search instead for 
Did you mean: 

Process instance behavior change on the fly

tomekzaremba
Champ in-the-making
Champ in-the-making
Hi,

I'm trying to provide the "goto" solution for running process instance. It should be available as rest endpoint (Activiti is integrated with Spring Boot). Purpose of that is to modify standard process execution in case of e.g. error in one of the service task or retry some already completed task.

I tried following snippet of code:


final ProcessDefinitionImpl definition = (ProcessDefinitionImpl) repositoryService //
                .getProcessDefinition(data.getProcessDefinitionId());
        final ExecutionEntity execution = (ExecutionEntity) runtimeService.createExecutionQuery() //
                .executionId(data.getExecutionId()) //
                .singleResult();

        final ActivityImpl desiredActivity = definition.findActivity(data.getActivityId());
        execution.setActivity(desiredActivity);

        AtomicOperation.ACTIVITY_START.execute(execution);


but it ends with following NPE:


Caused by: java.lang.NullPointerException: null
   at org.activiti.engine.impl.history.handler.ActivityInstanceStartHandler.notify(ActivityInstanceStartHandler.java:30) ~[activiti-engine-5.19.0.1.jar:5.19.0.1]
   at org.activiti.engine.impl.pvm.runtime.AbstractEventAtomicOperation.execute(AbstractEventAtomicOperation.java:42) ~[activiti-engine-5.19.0.1.jar:5.19.0.1]
   at pl.play.support.SupportRestController.gotoStep(SupportRestController.java:118) ~[classes/:na]


Can anyone can give me some hint how to implement such mechanism? Of course this has to change only given by my process instance - whole process definition should be unchanged.
7 REPLIES 7

martin_grofcik
Confirmed Champ
Confirmed Champ
Hi Tomek,

Goto is just simple change in the DB. (e.g. implement your own command (to overwrite activity id in the execution) and use it in the service.)

The problem is when
# you have to create several different executions from one execution (parallel gateway)
# call activity
….

regards
Martin

tomekzaremba
Champ in-the-making
Champ in-the-making
Thanks for immediate answer.

Can you explain a little bit more this command - how can I start it (what activiti service may be helpful for me)? You said also "use it in the service" - you mean executing it in my REST controller?

And the problems - why calling activity may be a problem? E.g. having such example:

<code>
serviceTask1 -> messageEvent1 -> serviceTask2 -> messageEvent2 -> serviceTask3 -> endEvent
</code>

and waiting for messageEvent1 I want to "goto" serviceTask3 when I have to (because serviceTask2 should be skipped in some support case). Am I wrong - is this simply calling another activity?

tomekzaremba
Champ in-the-making
Champ in-the-making
I ended with following code:

<java>
public class GotoCommand implements Command<Execution> {

    private final GotoCommandRequestData requestData;

    public GotoCommand(final GotoCommandRequestData requestData) {
        this.requestData = requestData;
    }

    @Override
    public Execution execute(final CommandContext commandContext) {
        final RepositoryService repositoryService = commandContext.getProcessEngineConfiguration().getRepositoryService();
        final RuntimeService runtimeService = commandContext.getProcessEngineConfiguration().getRuntimeService();

        final ProcessDefinitionImpl definition = (ProcessDefinitionImpl) repositoryService //
                .getProcessDefinition(requestData.getProcessDefinitionId());
        final ExecutionEntity execution = (ExecutionEntity) runtimeService.createExecutionQuery() //
                .executionId(requestData.getExecutionId()) //
                .singleResult();

        final ActivityImpl desiredActivity = definition.findActivity(requestData.getActivityId());
        execution.setActivity(desiredActivity);

        AtomicOperation.ACTIVITY_START.execute(execution);

        return execution;
    }
}
</java>

Execution is properly started and executed. But when process instance execution goes to messageEvent and I want to resume execution (via runtimeService.messageEventReceived) I end with following stack trace:

<java>
Caused by: org.postgresql.util.PSQLException: ERROR: update or delete on table "act_ru_execution" violates foreign key constraint "act_fk_exe_parent" on table "act_ru_execution"
  Szczegóły: Key (id_)=(477765) is still referenced from table "act_ru_execution".
at org.postgresql.core.v3.QueryExecutorImpl.receiveErrorResponse(QueryExecutorImpl.java:2182) ~[postgresql-9.4-1206-jdbc4.jar:9.4]
at org.postgresql.core.v3.QueryExecutorImpl.processResults(QueryExecutorImpl.java:1911) ~[postgresql-9.4-1206-jdbc4.jar:9.4]
at org.postgresql.core.v3.QueryExecutorImpl.execute(QueryExecutorImpl.java:173) ~[postgresql-9.4-1206-jdbc4.jar:9.4]
</java>

Is it possible to somehow avoid this error?

tomekzaremba
Champ in-the-making
Champ in-the-making
It's my fault - there was child execution for that I changed, so after process instance completes it can't delete all records from runtime tables.

FYI: I ended with such command:

<java>
@RequiredArgsConstructor
public class GotoActivityCommand implements Command<Execution> {

    private final GotoActivityCommandRequestData requestData;

    @Override
    public Execution execute(final CommandContext commandContext) {
        final RepositoryService repositoryService = commandContext.getProcessEngineConfiguration().getRepositoryService();
        final RuntimeService runtimeService = commandContext.getProcessEngineConfiguration().getRuntimeService();

        final ExecutionEntity execution = (ExecutionEntity) runtimeService.createExecutionQuery() //
                .executionId(requestData.getExecutionId()) //
                .singleResult();
        final ProcessDefinitionImpl definition = (ProcessDefinitionImpl) repositoryService //
                .getProcessDefinition(execution.getProcessDefinitionId());

        final ActivityImpl desiredActivity = definition.findActivity(requestData.getActivityId());
        execution.setActivity(desiredActivity);

        AtomicOperation.ACTIVITY_START.execute(execution);

        final List<Execution> childExecutions = runtimeService.createExecutionQuery() //
                .parentId(execution.getId()) //
                .list();

        childExecutions.forEach(e -> ((ExecutionEntity) e).deleteCascade("GOTO support execution"));

        return execution;
    }
}
</java>

jbarrez
Star Contributor
Star Contributor
What Martin is saying, is that it will work for the simple cases, but there might be cases (parallel, call activity cause it creates more than one execution) where it doesn't map 1-1.

martin_grofcik
Confirmed Champ
Confirmed Champ
Yes exactly,

You have already find one place where 2 executions are created. When you use GoTo you have to exactly know how engine behaves. This function could be used by some super admin user… :-).

Regards
Martin

tomekzaremba
Champ in-the-making
Champ in-the-making
@Martin - as you say - created goto function will be only available for super admin user - big red button used only in emergency cases Smiley Happy

Thanks for help and advice.