I'm making the case for Activiti at my company and have been getting my feet wet by trying to figure out how to work with some long-running asynchronous processes. The receive task looks like the way to go, but I've already run into a problem.
My process is simply start -> receive -> end. When I start a process programmatically, I can signal the execution, and things work as expected. But when I start the process using Activiti Explorer (running as a standalone .war), after signaling the execution, I see an exception:
SEVERE: Error while closing command context org.apache.ibatis.exceptions.PersistenceException: ### Error updating database. Cause: org.h2.jdbc.JdbcSQLException: Referential integrity constraint violation: "ACT_FK_IDL_PROCINST: PUBLIC.ACT_RU_IDENTITYLINK FOREIGN KEY(PROC_INST_ID_) REFERENCES PUBLIC.ACT_RU_EXECUTION(ID_) ('3070')"; SQL statement: delete from ACT_RU_EXECUTION where ID_ = ? [23503-171] at org.h2.message.DbException.getJdbcSQLException(DbException.java:329) at org.h2.message.DbException.get(DbException.java:169) at org.h2.message.DbException.get(DbException.java:146) at org.h2.constraint.ConstraintReferential.checkRow(ConstraintReferential.java:414) at org.h2.constraint.ConstraintReferential.checkRowRefTable(ConstraintReferential.java:431) at org.h2.constraint.ConstraintReferential.checkRow(ConstraintReferential.java:307) at org.h2.table.Table.fireConstraints(Table.java:873) at org.h2.table.Table.fireAfterRow(Table.java:890) at org.h2.command.dml.Delete.update(Delete.java:99) at org.h2.command.CommandContainer.update(CommandContainer.java:75) at org.h2.command.Command.executeUpdate(Command.java:230) at org.h2.server.TcpServerThread.process(TcpServerThread.java:334) at org.h2.server.TcpServerThread.run(TcpServerThread.java:150) at java.lang.Thread.run(Unknown Source)
I can see the entries in ACT_RU_IDENTITYLINK that are created only when Explorer is used to start the process, but I am at a loss for what I need to do to make this work.
ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine(); if (processEngine == null) { log.error("Could not start Activiti Engine"); } else { RepositoryService repositoryService = processEngine.getRepositoryService(); repositoryService.createDeployment() .addClasspathResource("VacationRequest.bpmn20.xml") .deploy(); log.info("Number of process definitions: " + repositoryService.createProcessDefinitionQuery().count()); // This process will complete as expected String procId = processEngine.getRuntimeService().startProcessInstanceByKey("myVacationRequest").getId();
// Start a process in Activiti Explorer - it will result in an exception at the end of the process try { Thread.sleep(30000); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); }
// Lookup executions in our wait activity, and signal them to advance RuntimeService rs = processEngine.getRuntimeService(); List<Execution> executions = rs.createExecutionQuery().processDefinitionKey("myVacationRequest").activityId("wait2").list(); for (Execution execution : executions) { log.info("Found execution " + execution.getId()); try { rs.signal(execution.getId()); } catch (RuntimeException e) { System.out.println(e.getMessage()); } } }
Is there some other key step to using the Receive task that I'm missing?
<code> ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine(); if (processEngine == null) { log.error("Could not start Activiti Engine"); } else { RepositoryService repositoryService = processEngine.getRepositoryService(); repositoryService.createDeployment() .addClasspathResource("VacationRequest.bpmn20.xml") .deploy(); log.info("Number of process definitions: " + repositoryService.createProcessDefinitionQuery().count()); // This process will complete as expected String procId = processEngine.getRuntimeService().startProcessInstanceByKey("myVacationRequest").getId();
// Start a process in Activiti Explorer - it will result in an exception at the end of the process try { Thread.sleep(30000); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); }
// Lookup executions in our wait activity, and signal them to advance RuntimeService rs = processEngine.getRuntimeService(); List<Execution> executions = rs.createExecutionQuery().processDefinitionKey("myVacationRequest").activityId("wait2").list(); for (Execution execution : executions) { log.info("Found execution " + execution.getId()); try { rs.signal(execution.getId()); } catch (RuntimeException e) { System.out.println(e.getMessage()); } } }
I think my problem is an incorrect assumption about how Activiti Explorer was intended to be used with standalone applications - can someone confirm?
I had Explorer running in an Tomcat instance, and I was running the code in a separate process. While the deployed process appears in Explorer, I'm guessing the problem is related to starting a process in one engine, and then sending the signal another engine. If I deploy similar code to the Tomcat instance, I do not see the exception.
Is this not something that is supported? I was hoping all I had to do was point Explorer and my app to the same database and I'd be able to use Explorer for user tasks. Perhaps I need to integrate explorer into my app instead?
Looks like I found the problem - one project was using 5.9, vs Activiti Explorer 5.12.1. For some reason, Eclipse wasn't showing the latest versions when selecting dependencies, but after changing the XML directly, the problem has gone away, so perhaps it was just some different in DB schemas.