Behavior of Multi Instance Tasks/Subprocesses on emtpy collections

Options
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
12-02-2014 03:18 AM
In the application we are currently developing, we have the scenario of a multi-instance subprocesses which has a collection that might be empty. Our expectation was that if the collection was empty, Activiti would just skip the sub-process altogether and continue the execution after the sub-process, same as e.g. Java would execute a for-each-loop on an empty collection. Instead, we found out that Actviti 5.17.0-SNAPSHOT will throw an exception like this:
Older versions will throw an NPE instead at this point, so I guess this exception was introduced in 5.17.0-SNAPSHOT.
My question is, is there any reason why this exception is thrown instead of just skipping the multi instance activity (which would still fulfill the specification of creating an instance for each element of the collection) and continue the process after it? If not, I could prepare a pull request to change this behavior to our expectations.
org.activiti.engine.ActivitiIllegalArgumentException: Invalid number of instances: must be positive integer value, but was 0 at org.activiti.engine.impl.bpmn.behavior.ParallelMultiInstanceBehavior.createInstances(ParallelMultiInstanceBehavior.java:41) at org.activiti.engine.impl.bpmn.behavior.MultiInstanceActivityBehavior.execute(MultiInstanceActivityBehavior.java:90) at org.activiti.engine.impl.pvm.runtime.AtomicOperationActivityExecute.execute(AtomicOperationActivityExecute.java:60) at org.activiti.engine.impl.interceptor.CommandContext.performOperation(CommandContext.java:96) at org.activiti.engine.impl.persistence.entity.ExecutionEntity.performOperationSync(ExecutionEntity.java:621) at org.activiti.engine.impl.persistence.entity.ExecutionEntity.performOperation(ExecutionEntity.java:616) at org.activiti.engine.impl.pvm.runtime.AtomicOperationTransitionNotifyListenerStart.eventNotificationsCompleted(AtomicOperationTransitionNotifyListenerStart.java:52) at org.activiti.engine.impl.pvm.runtime.AbstractEventAtomicOperation.execute(AbstractEventAtomicOperation.java:56) at org.activiti.engine.impl.interceptor.CommandContext.performOperation(CommandContext.java:96) at org.activiti.engine.impl.persistence.entity.ExecutionEntity.performOperationSync(ExecutionEntity.java:621) at org.activiti.engine.impl.persistence.entity.ExecutionEntity.performOperation(ExecutionEntity.java:616) at org.activiti.engine.impl.pvm.runtime.AbstractEventAtomicOperation.execute(AbstractEventAtomicOperation.java:49) at org.activiti.engine.impl.interceptor.CommandContext.performOperation(CommandContext.java:96) at org.activiti.engine.impl.persistence.entity.ExecutionEntity.performOperationSync(ExecutionEntity.java:621) at org.activiti.engine.impl.persistence.entity.ExecutionEntity.performOperation(ExecutionEntity.java:616) …
Older versions will throw an NPE instead at this point, so I guess this exception was introduced in 5.17.0-SNAPSHOT.
My question is, is there any reason why this exception is thrown instead of just skipping the multi instance activity (which would still fulfill the specification of creating an instance for each element of the collection) and continue the process after it? If not, I could prepare a pull request to change this behavior to our expectations.
Labels:
- Labels:
-
Archive
8 REPLIES 8
Options
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
12-05-2014 10:11 AM
Are you using current master version?
Cause, the code looks like it incorporates the '0' case: https://github.com/Activiti/Activiti/blob/master/modules/activiti-engine/src/main/java/org/activiti/...
Also, the exception you get seems changed : https://github.com/Activiti/Activiti/blob/master/modules/activiti-engine/src/main/java/org/activiti/...
Cause, the code looks like it incorporates the '0' case: https://github.com/Activiti/Activiti/blob/master/modules/activiti-engine/src/main/java/org/activiti/...
Also, the exception you get seems changed : https://github.com/Activiti/Activiti/blob/master/modules/activiti-engine/src/main/java/org/activiti/...

Options
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
12-05-2014 10:23 AM
From the code you posted, it seems like the code does now work the way I had expected, I will re-check my test case against the current master state, if it indeed does work, my question is answered.

Options
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
12-08-2014 07:46 AM
There seems to be a difference between multi-instance subprocesses and multi-instance tasks. When using multi-instance tasks there is no problem with the current master version (at last the problem was in 5.16). But using multi-instance subprocesses with an empty collection results in a NullPointerException in MultiInstanceActivityBehavior.java in line 223.
Edit: The problem exists only if you use parallel multi-instance subprocesses. If you use sequential ones then it works as expected.
Edit: The problem exists only if you use parallel multi-instance subprocesses. If you use sequential ones then it works as expected.
Options
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
12-08-2014 08:52 AM
Hmmm i see. Could you create a simple example process that demonstrates this problem? Then I can jump in straight away at fixing the bug.

Options
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
01-23-2015 09:08 AM
Hi,
here is the simple example process with a parallel multi instance subprocess.
[img]https://www.dropbox.com/s/wrel8dbq33v83xj/ProcessWithMultiInstance.jpg?dl=1[/img]
Process definition: https://www.dropbox.com/s/oy8l3v8x5jbg04z/ProcessWithMultiInstance.bpmn?dl=0
If you start the process instance with an empty collection, activiti throw a null-pointer execption.
<java>
java.lang.NullPointerException
at org.activiti.engine.impl.bpmn.behavior.MultiInstanceActivityBehavior.setLoopVariable(MultiInstanceActivityBehavior.java:223)
at org.activiti.engine.impl.bpmn.behavior.ParallelMultiInstanceBehavior.leave(ParallelMultiInstanceBehavior.java:116)
at org.activiti.engine.impl.bpmn.behavior.MultiInstanceActivityBehavior.execute(MultiInstanceActivityBehavior.java:96)
at org.activiti.engine.impl.pvm.runtime.AtomicOperationActivityExecute.execute(AtomicOperationActivityExecute.java:60)
at org.activiti.engine.impl.interceptor.CommandContext.performOperation(CommandContext.java:96)
at org.activiti.engine.impl.persistence.entity.ExecutionEntity.performOperationSync(ExecutionEntity.java:631)
at org.activiti.engine.impl.persistence.entity.ExecutionEntity.performOperation(ExecutionEntity.java:626)
at org.activiti.engine.impl.pvm.runtime.AtomicOperationTransitionNotifyListenerStart.eventNotificationsCompleted(AtomicOperationTransitionNotifyListenerStart.java:52)
</java>
Test case:
<java>
@Deployment(resources = "ProcessWithMultiInstance.bpmn")
@Test
public void multiInstanceWithEmptyCollection()
{
Map<String, Object> vars = new HashMap<String, Object>();
vars.put("messages", Collections.EMPTY_LIST);
activitiRule.getRuntimeService().startProcessInstanceByKey("myProcess", vars);
assertThat(activitiRule.getHistoryService().createHistoricProcessInstanceQuery().count(), is(1L));
}
</java>
Complete test case: https://www.dropbox.com/s/n83f17b0441521w/MultiInstanceTest.java?dl=0
Greetings.
here is the simple example process with a parallel multi instance subprocess.
[img]https://www.dropbox.com/s/wrel8dbq33v83xj/ProcessWithMultiInstance.jpg?dl=1[/img]
Process definition: https://www.dropbox.com/s/oy8l3v8x5jbg04z/ProcessWithMultiInstance.bpmn?dl=0
If you start the process instance with an empty collection, activiti throw a null-pointer execption.
<java>
java.lang.NullPointerException
at org.activiti.engine.impl.bpmn.behavior.MultiInstanceActivityBehavior.setLoopVariable(MultiInstanceActivityBehavior.java:223)
at org.activiti.engine.impl.bpmn.behavior.ParallelMultiInstanceBehavior.leave(ParallelMultiInstanceBehavior.java:116)
at org.activiti.engine.impl.bpmn.behavior.MultiInstanceActivityBehavior.execute(MultiInstanceActivityBehavior.java:96)
at org.activiti.engine.impl.pvm.runtime.AtomicOperationActivityExecute.execute(AtomicOperationActivityExecute.java:60)
at org.activiti.engine.impl.interceptor.CommandContext.performOperation(CommandContext.java:96)
at org.activiti.engine.impl.persistence.entity.ExecutionEntity.performOperationSync(ExecutionEntity.java:631)
at org.activiti.engine.impl.persistence.entity.ExecutionEntity.performOperation(ExecutionEntity.java:626)
at org.activiti.engine.impl.pvm.runtime.AtomicOperationTransitionNotifyListenerStart.eventNotificationsCompleted(AtomicOperationTransitionNotifyListenerStart.java:52)
</java>
Test case:
<java>
@Deployment(resources = "ProcessWithMultiInstance.bpmn")
@Test
public void multiInstanceWithEmptyCollection()
{
Map<String, Object> vars = new HashMap<String, Object>();
vars.put("messages", Collections.EMPTY_LIST);
activitiRule.getRuntimeService().startProcessInstanceByKey("myProcess", vars);
assertThat(activitiRule.getHistoryService().createHistoricProcessInstanceQuery().count(), is(1L));
}
</java>
Complete test case: https://www.dropbox.com/s/n83f17b0441521w/MultiInstanceTest.java?dl=0
Greetings.
Options
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
02-10-2015 12:02 PM
You are correct. Fixed it : https://github.com/Activiti/Activiti/commit/053d8e7965de8e42758f196bbc098d46e0be0221
Thanks for the easy to use process definition and test. This really helped me speed up to resolve the problem!
Thanks for the easy to use process definition and test. This really helped me speed up to resolve the problem!

Options
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
02-27-2015 09:01 AM
The important fix is provided by ParallelMultiInstanceBehavior.java
After applying the fix in activiti-engine-5.16.4.jar I got the below error when executing my project
<code>
Caused by: org.apache.ibatis.exceptions.PersistenceException:
### Error updating database. Cause: java.sql.SQLIntegrityConstraintViolationException: ORA-02291: integrity constraint (BB.ACT_FK_EXE_PROCINST) violated - parent key not found
### The error may involve org.activiti.engine.impl.persistence.entity.ExecutionEntity.insertExecution-Inline
### The error occurred while setting parameters
### SQL: insert into ACT_RU_EXECUTION (ID_, REV_, PROC_INST_ID_, BUSINESS_KEY_, PROC_DEF_ID_, ACT_ID_, IS_ACTIVE_, IS_CONCURRENT_, IS_SCOPE_,IS_EVENT_SCOPE_, PARENT_ID_, SUPER_EXEC_, SUSPENSION_STATE_, CACHED_ENT_STATE_, TENANT_ID_, NAME_) values ( ?, 1, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ? )
### Cause: java.sql.SQLIntegrityConstraintViolationException: ORA-02291: integrity constraint (BB.ACT_FK_EXE_PROCINST) violated - parent key not found
at org.apache.ibatis.exceptions.ExceptionFactory.wrapException(ExceptionFactory.java:23)
at org.apache.ibatis.session.defaults.DefaultSqlSession.update(DefaultSqlSession.java:150)
at org.apache.ibatis.session.defaults.DefaultSqlSession.insert(DefaultSqlSession.java:137)
at org.activiti.engine.impl.db.DbSqlSession.flushInserts(DbSqlSession.java:758)
at org.activiti.engine.impl.db.DbSqlSession.flush(DbSqlSession.java:590)
at org.activiti.engine.impl.interceptor.CommandContext.flushSessions(CommandContext.java:211)
at org.activiti.engine.impl.interceptor.CommandContext.close(CommandContext.java:137)
at org.activiti.engine.impl.interceptor.CommandContextInterceptor.execute(CommandContextInterceptor.java:66)
at org.activiti.spring.SpringTransactionInterceptor$1.doInTransaction(SpringTransactionInterceptor.java:47)
at org.springframework.transaction.support.TransactionTemplate.execute(TransactionTemplate.java:133)
at org.activiti.spring.SpringTransactionInterceptor.execute(SpringTransactionInterceptor.java:45)
at org.activiti.engine.impl.interceptor.LogInterceptor.execute(LogInterceptor.java:31)
at org.activiti.engine.impl.cfg.CommandExecutorImpl.execute(CommandExecutorImpl.java:40)
at org.activiti.engine.impl.cfg.CommandExecutorImpl.execute(CommandExecutorImpl.java:35)
at org.activiti.engine.impl.RuntimeServiceImpl.startProcessInstanceById(RuntimeServiceImpl.java:101)
at org.activiti.explorer.ui.process.listener.StartProcessInstanceClickListener.buttonClick(StartProcessInstanceClickListener.java:71)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:606)
at com.vaadin.event.ListenerMethod.receiveEvent(ListenerMethod.java:510)
… 33 more
Caused by: java.sql.SQLIntegrityConstraintViolationException: ORA-02291: integrity constraint (BB.ACT_FK_EXE_PROCINST) violated - parent key not found
at oracle.jdbc.driver.T4CTTIoer.processError(T4CTTIoer.java:445)
at oracle.jdbc.driver.T4CTTIoer.processError(T4CTTIoer.java:396)
at oracle.jdbc.driver.T4C8Oall.processError(T4C8Oall.java:879)
at oracle.jdbc.driver.T4CTTIfun.receive(T4CTTIfun.java:450)
at oracle.jdbc.driver.T4CTTIfun.doRPC(T4CTTIfun.java:192)
at oracle.jdbc.driver.T4C8Oall.doOALL(T4C8Oall.java:531)
at oracle.jdbc.driver.T4CPreparedStatement.doOall8(T4CPreparedStatement.java:207)
at oracle.jdbc.driver.T4CPreparedStatement.executeForRows(T4CPreparedStatement.java:1044)
at oracle.jdbc.driver.OracleStatement.doExecuteWithTimeout(OracleStatement.java:1329)
at oracle.jdbc.driver.OraclePreparedStatement.executeInternal(OraclePreparedStatement.java:3593)
at oracle.jdbc.driver.OraclePreparedStatement.execute(OraclePreparedStatement.java:3694)
at oracle.jdbc.driver.OraclePreparedStatementWrapper.execute(OraclePreparedStatementWrapper.java:1378)
at org.apache.ibatis.executor.statement.PreparedStatementHandler.update(PreparedStatementHandler.java:41)
at org.apache.ibatis.executor.statement.RoutingStatementHandler.update(RoutingStatementHandler.java:66)
at org.apache.ibatis.executor.SimpleExecutor.doUpdate(SimpleExecutor.java:45)
at org.apache.ibatis.executor.BaseExecutor.update(BaseExecutor.java:100)
at org.apache.ibatis.executor.CachingExecutor.update(CachingExecutor.java:75)
at org.apache.ibatis.session.defaults.DefaultSqlSession.update(DefaultSqlSession.java:148)
… 52 more
</code>
After applying the fix in activiti-engine-5.16.4.jar I got the below error when executing my project
<code>
Caused by: org.apache.ibatis.exceptions.PersistenceException:
### Error updating database. Cause: java.sql.SQLIntegrityConstraintViolationException: ORA-02291: integrity constraint (BB.ACT_FK_EXE_PROCINST) violated - parent key not found
### The error may involve org.activiti.engine.impl.persistence.entity.ExecutionEntity.insertExecution-Inline
### The error occurred while setting parameters
### SQL: insert into ACT_RU_EXECUTION (ID_, REV_, PROC_INST_ID_, BUSINESS_KEY_, PROC_DEF_ID_, ACT_ID_, IS_ACTIVE_, IS_CONCURRENT_, IS_SCOPE_,IS_EVENT_SCOPE_, PARENT_ID_, SUPER_EXEC_, SUSPENSION_STATE_, CACHED_ENT_STATE_, TENANT_ID_, NAME_) values ( ?, 1, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ? )
### Cause: java.sql.SQLIntegrityConstraintViolationException: ORA-02291: integrity constraint (BB.ACT_FK_EXE_PROCINST) violated - parent key not found
at org.apache.ibatis.exceptions.ExceptionFactory.wrapException(ExceptionFactory.java:23)
at org.apache.ibatis.session.defaults.DefaultSqlSession.update(DefaultSqlSession.java:150)
at org.apache.ibatis.session.defaults.DefaultSqlSession.insert(DefaultSqlSession.java:137)
at org.activiti.engine.impl.db.DbSqlSession.flushInserts(DbSqlSession.java:758)
at org.activiti.engine.impl.db.DbSqlSession.flush(DbSqlSession.java:590)
at org.activiti.engine.impl.interceptor.CommandContext.flushSessions(CommandContext.java:211)
at org.activiti.engine.impl.interceptor.CommandContext.close(CommandContext.java:137)
at org.activiti.engine.impl.interceptor.CommandContextInterceptor.execute(CommandContextInterceptor.java:66)
at org.activiti.spring.SpringTransactionInterceptor$1.doInTransaction(SpringTransactionInterceptor.java:47)
at org.springframework.transaction.support.TransactionTemplate.execute(TransactionTemplate.java:133)
at org.activiti.spring.SpringTransactionInterceptor.execute(SpringTransactionInterceptor.java:45)
at org.activiti.engine.impl.interceptor.LogInterceptor.execute(LogInterceptor.java:31)
at org.activiti.engine.impl.cfg.CommandExecutorImpl.execute(CommandExecutorImpl.java:40)
at org.activiti.engine.impl.cfg.CommandExecutorImpl.execute(CommandExecutorImpl.java:35)
at org.activiti.engine.impl.RuntimeServiceImpl.startProcessInstanceById(RuntimeServiceImpl.java:101)
at org.activiti.explorer.ui.process.listener.StartProcessInstanceClickListener.buttonClick(StartProcessInstanceClickListener.java:71)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:606)
at com.vaadin.event.ListenerMethod.receiveEvent(ListenerMethod.java:510)
… 33 more
Caused by: java.sql.SQLIntegrityConstraintViolationException: ORA-02291: integrity constraint (BB.ACT_FK_EXE_PROCINST) violated - parent key not found
at oracle.jdbc.driver.T4CTTIoer.processError(T4CTTIoer.java:445)
at oracle.jdbc.driver.T4CTTIoer.processError(T4CTTIoer.java:396)
at oracle.jdbc.driver.T4C8Oall.processError(T4C8Oall.java:879)
at oracle.jdbc.driver.T4CTTIfun.receive(T4CTTIfun.java:450)
at oracle.jdbc.driver.T4CTTIfun.doRPC(T4CTTIfun.java:192)
at oracle.jdbc.driver.T4C8Oall.doOALL(T4C8Oall.java:531)
at oracle.jdbc.driver.T4CPreparedStatement.doOall8(T4CPreparedStatement.java:207)
at oracle.jdbc.driver.T4CPreparedStatement.executeForRows(T4CPreparedStatement.java:1044)
at oracle.jdbc.driver.OracleStatement.doExecuteWithTimeout(OracleStatement.java:1329)
at oracle.jdbc.driver.OraclePreparedStatement.executeInternal(OraclePreparedStatement.java:3593)
at oracle.jdbc.driver.OraclePreparedStatement.execute(OraclePreparedStatement.java:3694)
at oracle.jdbc.driver.OraclePreparedStatementWrapper.execute(OraclePreparedStatementWrapper.java:1378)
at org.apache.ibatis.executor.statement.PreparedStatementHandler.update(PreparedStatementHandler.java:41)
at org.apache.ibatis.executor.statement.RoutingStatementHandler.update(RoutingStatementHandler.java:66)
at org.apache.ibatis.executor.SimpleExecutor.doUpdate(SimpleExecutor.java:45)
at org.apache.ibatis.executor.BaseExecutor.update(BaseExecutor.java:100)
at org.apache.ibatis.executor.CachingExecutor.update(CachingExecutor.java:75)
at org.apache.ibatis.session.defaults.DefaultSqlSession.update(DefaultSqlSession.java:148)
… 52 more
</code>

Options
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
02-27-2015 10:48 AM
I've fixed this with a simpler solution:
<code>
<subProcess id="subprocess1" name="Sub Process">
<multiInstanceLoopCharacteristics isSequential="false" activiti:collection="list2process">
<!– completionCondition>${nrOfCompletedInstances/nrOfInstances > 0.5}</completionCondition –>
</multiInstanceLoopCharacteristics>
</code>
And Java hack :
<code>
if (f.size() > 1) {
f.remove(0);
execution.setVariable("list2process", f);
}
</code>
<code>
<subProcess id="subprocess1" name="Sub Process">
<multiInstanceLoopCharacteristics isSequential="false" activiti:collection="list2process">
<!– completionCondition>${nrOfCompletedInstances/nrOfInstances > 0.5}</completionCondition –>
</multiInstanceLoopCharacteristics>
</code>
And Java hack :
<code>
if (f.size() > 1) {
f.remove(0);
execution.setVariable("list2process", f);
}
</code>
