cancel
Showing results for 
Search instead for 
Did you mean: 

Problem Using Timer to Delay Re-executing Service Task

ct1
Champ in-the-making
Champ in-the-making
I am trying to call a Service task that checks for the existance of a file.  I want the Service task to run, wait 5 minutes, and run again, until the file is found.

The Timer duration is set to PT1M.

When I start the process. The service task runs, logs its start, fails to find the file, waits, runs again, fails to find the file, and then throws an exception:

Nov 23, 2011 11:46:24 AM org.activiti.engine.impl.bpmn.deployer.BpmnDeployer deploy
INFO: Processing resource CheckForAFile.png
CheckForFile.execute() was called at Wed Nov 23 11:46:24 CST 2011
CheckForFile.lookForFile() was called with thePathName = "C:\Users\thomacha\DataFiles\" and theFileN
ame = "theFileToLookFor.txt".
In CheckForFile.lookForFile() set fileFound = false
In CheckForFile.execute() set fileExists = false.
File does NOT exist.
CheckForFile.execute() was called at Wed Nov 23 11:47:25 CST 2011
CheckForFile.lookForFile() was called with thePathName = "C:\Users\thomacha\DataFiles\" and theFileN
ame = "theFileToLookFor.txt".
In CheckForFile.lookForFile() set fileFound = false
In CheckForFile.execute() set fileExists = false.
File does NOT exist.
Nov 23, 2011 11:48:25 AM org.activiti.engine.impl.interceptor.CommandContext close
SEVERE: Error while closing command context
org.activiti.engine.ActivitiOptimisticLockingException: TimerEntity[3424] was updated by another tra
nsaction concurrently
        at org.activiti.engine.impl.db.DbSqlSession.flushUpdates(DbSqlSession.java:435)
        at org.activiti.engine.impl.db.DbSqlSession.flush(DbSqlSession.java:348)
        at org.activiti.engine.impl.interceptor.CommandContext.flushSessions(CommandContext.java:147
)
        at org.activiti.engine.impl.interceptor.CommandContext.close(CommandContext.java:103)
        at org.activiti.engine.impl.interceptor.CommandContextInterceptor.execute(CommandContextInte
rceptor.java:49)
        at org.activiti.spring.SpringTransactionInterceptor$1.doInTransaction(SpringTransactionInter
ceptor.java:42)
        at org.springframework.transaction.support.TransactionTemplate.execute(TransactionTemplate.j
ava:130)
        at org.activiti.spring.SpringTransactionInterceptor.execute(SpringTransactionInterceptor.jav
a:40)
        at org.activiti.engine.impl.interceptor.LogInterceptor.execute(LogInterceptor.java:33)
        at org.activiti.engine.impl.jobexecutor.JobAcquisitionThread.run(JobAcquisitionThread.java:6
3)
Nov 23, 2011 11:48:25 AM org.activiti.engine.impl.jobexecutor.JobAcquisitionThread run
SEVERE: exception during job acquisition: TimerEntity[3424] was updated by another transaction concu
rrently
org.activiti.engine.ActivitiOptimisticLockingException: TimerEntity[3424] was updated by another tra
nsaction concurrently
        at org.activiti.engine.impl.db.DbSqlSession.flushUpdates(DbSqlSession.java:435)
        at org.activiti.engine.impl.db.DbSqlSession.flush(DbSqlSession.java:348)
        at org.activiti.engine.impl.interceptor.CommandContext.flushSessions(CommandContext.java:147
)
        at org.activiti.engine.impl.interceptor.CommandContext.close(CommandContext.java:103)
        at org.activiti.engine.impl.interceptor.CommandContextInterceptor.execute(CommandContextInte
rceptor.java:49)
        at org.activiti.spring.SpringTransactionInterceptor$1.doInTransaction(SpringTransactionInter
ceptor.java:42)
        at org.springframework.transaction.support.TransactionTemplate.execute(TransactionTemplate.j
ava:130)
        at org.activiti.spring.SpringTransactionInterceptor.execute(SpringTransactionInterceptor.jav
a:40)
        at org.activiti.engine.impl.interceptor.LogInterceptor.execute(LogInterceptor.java:33)
        at org.activiti.engine.impl.jobexecutor.JobAcquisitionThread.run(JobAcquisitionThread.java:6
3)
Nov 23, 2011 11:48:25 AM org.activiti.engine.impl.bpmn.deployer.BpmnDeployer deploy
INFO: Processing resource CheckForAFile.bpmn20.xml
Nov 23, 2011 11:48:25 AM org.activiti.engine.impl.bpmn.parser.BpmnParse parseDefinitionsAttributes
INFO: XMLSchema currently not supported as typeLanguage
Nov 23, 2011 11:48:25 AM org.activiti.engine.impl.bpmn.parser.BpmnParse parseDefinitionsAttributes

I expected the cycle to continue uninterrupted.  What do you suggest I do?


Here is the BPMN2.0 file:


<?xml version="1.0" encoding="UTF-8"?>
<definitions xmlns="http://www.omg.org/spec/BPMN/20100524/MODEL" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:activiti="http://activiti.org/bpmn" xmlns:bpmndi="http://www.omg.org/spec/BPMN/20100524/DI" xmlns:omgdc="http://www.omg.org/spec/DD/20100524/DC" xmlns:omgdi="http://www.omg.org/spec/DD/20100524/DI" typeLanguage="http://www.w3.org/2001/XMLSchema" expressionLanguage="http://www.w3.org/1999/XPath" targetNamespace="http://www.activiti.org/test">
  <process id="CheckForAFile" name="CheckForAFile">
    <documentation>Place documentation for the 'CheckForAFile' process here.</documentation>
    <startEvent id="startevent1" name="Start"></startEvent>
    <sequenceFlow id="flow1" name="" sourceRef="startevent1" targetRef="checkFileExists"></sequenceFlow>
    <serviceTask id="checkFileExists" name="Check File Exists" activiti:class="opt.argo.com.CheckForFile"></serviceTask>
    <exclusiveGateway id="exclusivegateway1" name="Exclusive Gateway"></exclusiveGateway>
    <sequenceFlow id="flow2" name="" sourceRef="checkFileExists" targetRef="exclusivegateway1"></sequenceFlow>
    <sequenceFlow id="doesExist" name="Yes" sourceRef="exclusivegateway1" targetRef="logThatFileExists">
      <conditionExpression xsi:type="tFormalExpression"><![CDATA[${fileExists == true}]]></conditionExpression>
    </sequenceFlow>
    <scriptTask id="logThatFileExists" name="Log That File Exists" scriptFormat="groovy">
      <script><![CDATA[print "File does exist. \n"]]></script>
    </scriptTask>
    <sequenceFlow id="fileNotExist" name="File Does Not Exist" sourceRef="exclusivegateway1" targetRef="logThatFileNotExist">
      <conditionExpression xsi:type="tFormalExpression"><![CDATA[${fileExists != true}]]></conditionExpression>
    </sequenceFlow>
    <scriptTask id="logThatFileNotExist" name="Log That File Does Not Exist" scriptFormat="groovy">
      <script><![CDATA[print "File does NOT exist. \n"]]></script>
    </scriptTask>
    <endEvent id="endevent1" name="End"></endEvent>
    <sequenceFlow id="flow3" name="" sourceRef="logThatFileExists" targetRef="endevent1"></sequenceFlow>
    <intermediateCatchEvent id="wait5minutes" name="Wait 5 Minutes">
      <timerEventDefinition>
        <timeDuration>PT1M</timeDuration>
      </timerEventDefinition>
    </intermediateCatchEvent>
    <sequenceFlow id="flow4" name="" sourceRef="logThatFileNotExist" targetRef="wait5minutes"></sequenceFlow>
    <sequenceFlow id="flow5" name="" sourceRef="wait5minutes" targetRef="checkFileExists"></sequenceFlow>
  </process>
  <bpmndi:BPMNDiagram id="BPMNDiagram_CheckForAFile">
    <bpmndi:BPMNPlane bpmnElement="CheckForAFile" id="BPMNPlane_CheckForAFile">
      <bpmndi:BPMNShape bpmnElement="startevent1" id="BPMNShape_startevent1">
        <omgdc:Bounds height="35" width="35" x="150" y="220"></omgdc:Bounds>
      </bpmndi:BPMNShape>
      <bpmndi:BPMNShape bpmnElement="checkFileExists" id="BPMNShape_checkFileExists">
        <omgdc:Bounds height="55" width="105" x="230" y="210"></omgdc:Bounds>
      </bpmndi:BPMNShape>
      <bpmndi:BPMNShape bpmnElement="exclusivegateway1" id="BPMNShape_exclusivegateway1">
        <omgdc:Bounds height="40" width="40" x="430" y="217"></omgdc:Bounds>
      </bpmndi:BPMNShape>
      <bpmndi:BPMNShape bpmnElement="logThatFileExists" id="BPMNShape_logThatFileExists">
        <omgdc:Bounds height="55" width="105" x="520" y="120"></omgdc:Bounds>
      </bpmndi:BPMNShape>
      <bpmndi:BPMNShape bpmnElement="logThatFileNotExist" id="BPMNShape_logThatFileNotExist">
        <omgdc:Bounds height="55" width="105" x="520" y="300"></omgdc:Bounds>
      </bpmndi:BPMNShape>
      <bpmndi:BPMNShape bpmnElement="endevent1" id="BPMNShape_endevent1">
        <omgdc:Bounds height="35" width="35" x="760" y="220"></omgdc:Bounds>
      </bpmndi:BPMNShape>
      <bpmndi:BPMNShape bpmnElement="wait5minutes" id="BPMNShape_wait5minutes">
        <omgdc:Bounds height="35" width="35" x="265" y="400"></omgdc:Bounds>
      </bpmndi:BPMNShape>
      <bpmndi:BPMNEdge bpmnElement="flow1" id="BPMNEdge_flow1">
        <omgdi:waypoint x="185" y="237"></omgdi:waypoint>
        <omgdi:waypoint x="230" y="237"></omgdi:waypoint>
      </bpmndi:BPMNEdge>
      <bpmndi:BPMNEdge bpmnElement="flow2" id="BPMNEdge_flow2">
        <omgdi:waypoint x="335" y="237"></omgdi:waypoint>
        <omgdi:waypoint x="430" y="237"></omgdi:waypoint>
      </bpmndi:BPMNEdge>
      <bpmndi:BPMNEdge bpmnElement="doesExist" id="BPMNEdge_doesExist">
        <omgdi:waypoint x="450" y="217"></omgdi:waypoint>
        <omgdi:waypoint x="450" y="147"></omgdi:waypoint>
        <omgdi:waypoint x="520" y="147"></omgdi:waypoint>
      </bpmndi:BPMNEdge>
      <bpmndi:BPMNEdge bpmnElement="fileNotExist" id="BPMNEdge_fileNotExist">
        <omgdi:waypoint x="450" y="257"></omgdi:waypoint>
        <omgdi:waypoint x="450" y="327"></omgdi:waypoint>
        <omgdi:waypoint x="520" y="327"></omgdi:waypoint>
      </bpmndi:BPMNEdge>
      <bpmndi:BPMNEdge bpmnElement="flow3" id="BPMNEdge_flow3">
        <omgdi:waypoint x="625" y="147"></omgdi:waypoint>
        <omgdi:waypoint x="777" y="147"></omgdi:waypoint>
        <omgdi:waypoint x="777" y="220"></omgdi:waypoint>
      </bpmndi:BPMNEdge>
      <bpmndi:BPMNEdge bpmnElement="flow4" id="BPMNEdge_flow4">
        <omgdi:waypoint x="572" y="355"></omgdi:waypoint>
        <omgdi:waypoint x="572" y="417"></omgdi:waypoint>
        <omgdi:waypoint x="300" y="417"></omgdi:waypoint>
      </bpmndi:BPMNEdge>
      <bpmndi:BPMNEdge bpmnElement="flow5" id="BPMNEdge_flow5">
        <omgdi:waypoint x="282" y="400"></omgdi:waypoint>
        <omgdi:waypoint x="282" y="265"></omgdi:waypoint>
      </bpmndi:BPMNEdge>
    </bpmndi:BPMNPlane>
  </bpmndi:BPMNDiagram>
</definitions>
Thank you.
2 REPLIES 2

mhw
Champ in-the-making
Champ in-the-making
Hi,

I have tried your process:
<?xml version="1.0" encoding="UTF-8"?>
<definitions xmlns="http://www.omg.org/spec/BPMN/20100524/MODEL" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:activiti="http://activiti.org/bpmn" xmlns:bpmndi="http://www.omg.org/spec/BPMN/20100524/DI" xmlnsSmiley Surprisedmgdc="http://www.omg.org/spec/DD/20100524/DC" xmlnsSmiley Surprisedmgdi="http://www.omg.org/spec/DD/20100524/DI" typeLanguage="http://www.w3.org/2001/XMLSchema" expressionLanguage="http://www.w3.org/1999/XPath" targetNamespace="http://www.activiti.org/test">
  <process id="CheckForAFile" name="CheckForAFile">
    <documentation>Process loops as long as variable 'fileExists' is false.</documentation>
    <startEvent id="startevent1" name="Start"></startEvent>
    <sequenceFlow id="flow1" name="" sourceRef="startevent1" targetRef="checkFileExists"></sequenceFlow>
    <serviceTask id="checkFileExists" name="Check File Exists" activiti:class="com.bigfirm.CheckForFile"></serviceTask>
    <exclusiveGateway id="exclusivegateway1" name="Exclusive Gateway"></exclusiveGateway>
    <sequenceFlow id="flow2" name="" sourceRef="checkFileExists" targetRef="exclusivegateway1"></sequenceFlow>
    <sequenceFlow id="doesExist" name="Yes" sourceRef="exclusivegateway1" targetRef="logThatFileExists">
      <conditionExpression xsi:type="tFormalExpression"><![CDATA[${fileExists == true}]]></conditionExpression>
    </sequenceFlow>
    <scriptTask id="logThatFileExists" name="Log That File Exists" scriptFormat="groovy">
      <script><![CDATA[print "File does exist. \n"]]></script>
    </scriptTask>
    <sequenceFlow id="fileNotExist" name="File Does Not Exist" sourceRef="exclusivegateway1" targetRef="logThatFileNotExist">
      <conditionExpression xsi:type="tFormalExpression"><![CDATA[${fileExists != true}]]></conditionExpression>
    </sequenceFlow>
    <scriptTask id="logThatFileNotExist" name="Log That File Does Not Exist" scriptFormat="groovy">
      <script><![CDATA[print "File does NOT exist. \n"]]></script>
    </scriptTask>
    <sequenceFlow id="flow3" name="" sourceRef="logThatFileExists" targetRef="endevent1"></sequenceFlow>
    <intermediateCatchEvent id="waitAMinute" name="Wait one minutes">
      <timerEventDefinition>
        <timeDuration>PT1S</timeDuration>
      </timerEventDefinition>
    </intermediateCatchEvent>
    <sequenceFlow id="flow4" name="" sourceRef="logThatFileNotExist" targetRef="waitAMinute"></sequenceFlow>
    <sequenceFlow id="flow5" name="" sourceRef="waitAMinute" targetRef="checkFileExists"></sequenceFlow>
    <endEvent id="endevent1" name="End"></endEvent>
  </process>
</definitions>

with a unit test:
…extends PluggableActivitiTestCase {
public void testLoopWaitForFile() {
  Map<String, Object> variables = new HashMap<String, Object>();
  variables.put("fileExists", false);

  ProcessInstance pi = runtimeService
    .startProcessInstanceByKey("CheckForAFile", variables);
  assertNotNull(pi);

  assertTrue(
    "History is turned off",
    config.getHistoryLevel() > ProcessEngineConfigurationImpl.HISTORYLEVEL_NONE);

  /*
   * assert process is in the wait state
   */
  Execution execution = runtimeService.createExecutionQuery()
    .processInstanceId(pi.getId()).activityId("waitAMinute")
    .singleResult();
  assertNotNull("Process is not in the wait state as expected.",
    execution);

  long startTime = System.currentTimeMillis();

  boolean fileCreated = false;
  File file = null;
  while (pi != null && !fileCreated) {
   try {
    Thread.sleep(1000);
   } catch (InterruptedException e) {
    fail("Test was interrupted.");
   }
   if (System.currentTimeMillis() - startTime > 1000) {
    file = new File("bin/com/…/res/testWaitForFile");
    try {
     file.createNewFile();
    } catch (IOException e) {
     fail("Unable to create new file.\n"
       + PrintHelper.getStackTrace(e));
    }
    try {
     Thread.sleep(2 * 1000);
    } catch (InterruptedException e) {
     fail("Test was interrupted.");
    }
    fileCreated = true;
   }
  }

  while(System.currentTimeMillis() - startTime < 3 * 60 * 1000){

   try {
    Thread.sleep(1000);
   } catch (InterruptedException e) {
    // TODO Auto-generated catch block
    e.printStackTrace();
   }
  }
  if (null != file) {
   file.delete();
  }
  pi = runtimeService.createProcessInstanceQuery().singleResult();
  HistoricProcessInstance hpi = historyService
    .createHistoricProcessInstanceQuery().singleResult();
  assertNull(pi);
  assertNotNull(hpi);
  assertEquals("End activity endevent1 not reached.", "endevent1",
    hpi.getEndActivityId());

}


My delegate class:
public class CheckForFile implements JavaDelegate {

@Override
public void execute(DelegateExecution execution) throws Exception {
  boolean fileExists = false;
 
  URL resource = this.getClass().getClassLoader().getResource(
    "com/…/res/testWaitForFile");
 
  if (resource != null) {
   File file = new File(resource.toURI());
   if (file.exists()) {
    fileExists = true;
   }
  }
  //execution.setVariable("fileExists", fileExists);
}

}
It would be run forever if I the unit test would not stop after tree minutes. It prints continually:
File does NOT exist.
File does NOT exist.
File does NOT exist.
File does NOT exist.
File does NOT exist.
If I uncomment the line in the listener everything is working fine:
execution.setVariable("fileExists", fileExists);The history tables then look like this:
SELECT * FROM ACT_HI_ACTINST;
ID_   PROC_DEF_ID_   PROC_INST_ID_   EXECUTION_ID_   ACT_ID_   ACT_NAME_   ACT_TYPE_   ASSIGNEE_   START_TIME_   END_TIME_   DURATION_ 
319 CheckForAFile:1:317 318 318 startevent1 Start startEvent null 2011-12-02 09:29:32.415 2011-12-02 09:29:32.415 0
322 CheckForAFile:1:317 318 318 checkFileExists Check File Exists serviceTask null 2011-12-02 09:29:32.415 2011-12-02 09:29:32.415 0
324 CheckForAFile:1:317 318 318 exclusivegateway1 Exclusive Gateway exclusiveGateway null 2011-12-02 09:29:32.415 2011-12-02 09:29:32.415 0
325 CheckForAFile:1:317 318 318 logThatFileNotExist Log That File Does Not Exist scriptTask null 2011-12-02 09:29:32.415 2011-12-02 09:29:32.43 15
328 CheckForAFile:1:317 318 318 checkFileExists Check File Exists serviceTask null 2011-12-02 09:30:32.467 2011-12-02 09:30:32.467 0
330 CheckForAFile:1:317 318 318 exclusivegateway1 Exclusive Gateway exclusiveGateway null 2011-12-02 09:30:32.467 2011-12-02 09:30:32.467 0
331 CheckForAFile:1:317 318 318 logThatFileNotExist Log That File Does Not Exist scriptTask null 2011-12-02 09:30:32.467 2011-12-02 09:30:32.498 31
334 CheckForAFile:1:317 318 318 checkFileExists Check File Exists serviceTask null 2011-12-02 09:31:32.535 2011-12-02 09:31:32.535 0
336 CheckForAFile:1:317 318 318 exclusivegateway1 Exclusive Gateway exclusiveGateway null 2011-12-02 09:31:32.535 2011-12-02 09:31:32.535 0
337 CheckForAFile:1:317 318 318 logThatFileExists Log That File Exists scriptTask null 2011-12-02 09:31:32.535 2011-12-02 09:31:32.551 16

Best Regards

Michael

ct1
Champ in-the-making
Champ in-the-making
Michael,

I really appreciate your taking the time to test this process.

A couple of things come to mind. 

Does running your unit test create its own process engine?  Could you try launching the process by a user (say, kermit) within Activiti Explorer and use the same engine Explorer uses?  The exception I got suggested the the timer I was using was updated by another transaction concurrently with my process.
[size=85](Nov 23, 2011 11:48:25 AM org.activiti.engine.impl.interceptor.CommandContext close
SEVERE: Error while closing command context
org.activiti.engine.ActivitiOptimisticLockingException: TimerEntity[3424] was updated by another tra
nsaction concurrently
at org.activiti.engine.impl.db.DbSqlSession.flushUpdates(DbSqlSession.java:435)
at org.activiti.engine.impl.db.DbSqlSession.flush(DbSqlSession.java:348)
at org.activiti.engine.impl.interceptor.CommandContext.flushSessions(CommandContext.java:147
)[/size]
I am not sure running a unit test exposes the timer to possible updates by other transactions the way running within Explorer may.

The second thing, the timers.  Could you remove the sleeps in your code, just launch the process, and let the intermediate timer (waitAMinute, redefined to a delay of one minute rather than one second) be the only controller of looping through the process?

Thanks again for checking this out.