1 Reply Latest reply on Nov 5, 2007 4:33 PM by jeffj55374

    Repeating Timer job doesn't run again if first JobExecutorTh

    jeffj55374

      Hi,
      I have encountered one undesirable behavior and one bug related to repeating Timers. I'd like to hear what th experts think of my analysis. Assuming I haven't missed anything or have an incorrect understanding of the system, I'll submit an issue.

      Note that I'm running JBPM as a standalone application using the JobExecutor mechanism to deal with Timers and async nodes. JBPM Ver. 3.2.2

      The Issue
      The second and subsequent executions of a timer's action can only be executed by the same JobExecutorThread instance that processed the first execution because the job (Timer) is never unlocked by that thread.

      The Bad Behavior
      Since only the initial JobExecutorThread can process the Timer, the second and subsequent executions of a repeating timer may be significantly delayed if the JobExecutorThread that processed the initial execution is processing long running tasks. Even if other threads are idle, the Timer will not be processed.

      The Bug
      The second and subsequent executions of the Timer's action will never occur if the thread that processed the initial execution terminates. Even though other JobExecutorThreads are running, the Timer will never fire again because it is locked by the initial thread which no longer exists.

      The basic functionality of JobExecutorThread.run() is:

      while (active)
       determine available jobs. (jobs that are due and not locked by other threads)
       lock the job
       create JBPM context
       execute the job
       if the job is a ExecuteNodeJob
       delete the job
       if the job is a ExecuteActionJob
       delete the job
       if the job is a Timer
       if the timer is not a repeating timer
       delete the job (Timer)
       else
       // Nothing is done. Note that the timer is still locked
       // Really should job.setLockOwner(null);
       // and job.setLockTime(null);
       // This would allow any other job executor thread to
       // to process this Timer when it becomes due.
       close JBPM context // Commits the job deletion or the updated timer
      

       protected void executeJob(Job job) {
       JbpmContext jbpmContext = jbpmConfiguration.createJbpmContext();
       try {
       JobSession jobSession = jbpmContext.getJobSession();
       job = jobSession.loadJob(job.getId());
      
       try {
       log.debug("executing job "+job);
       if (job.execute(jbpmContext)) {
       jobSession.deleteJob(job);
       }
      
       // **** I think this code should be added:
       else {
       job.setLockOwner(null);
       job.setLockTime(null);
       }
       // ****
       } catch (Exception e) {
       // Exception handling code removed.
       }
      
       // if this job is locked too long
       long totalLockTimeInMillis = System.currentTimeMillis() - job.getLockTime().getTime();
       if (totalLockTimeInMillis>maxLockTime) {
       jbpmContext.setRollbackOnly();
       }
      
       } finally {
       try {
       jbpmContext.close();
       } catch (JbpmPersistenceException e) {
       // Exception handling code removed.
       }
       }
       }
      


      Here's the task node definition that creates the timer.
      <task-node name="FileAvailableCheck">
       <task name="CheckForFile">
       <timer duedate="30 seconds" repeat="30 seconds"
       name="CheckForFileTimer" >
       <action class="com.digitalriver.mds.transform.FileAvailableCheckTask"/>
       </timer>
       </task>
       <transition name="FileReady" to="Transform"></transition>
      </task-node>