2 Replies Latest reply on Jan 20, 2006 8:04 PM by Jason Long

    EJB3 Timer Scheduler

    Ian Terrell Newbie

      I'm using jBPM in an EJB3 application, and I didn't feel that the web tier was the proper place for the scheduler. I wrote a stateless session bean that uses EJB3 timers for scheduling instead. It's basically the same algorithm (oh, and same code :) that's in the servlet, just now instead of using threads directly it lets the application server handle the details of the timing. I took out the listeners for now.

      Obviously since it needs to be started on startup I have a servlet calling down to the start() method, but this fits in nicely since my application has other timers that need to be started on application startup.

      To use transactions I had to add these parameters to jbpm.sar/jbpm.sar.cfg.jar/jbpm.hibernate.properties:


      package com.something.workflow;
      public interface Scheduler {
       void start();
       void stop();

      package com.something.workflow.impl;
      import static java.lang.Math.min;
      import java.util.Collection;
      import java.util.Date;
      import java.util.Iterator;
      import javax.annotation.Resource;
      import javax.ejb.SessionContext;
      import javax.ejb.Stateless;
      import javax.ejb.Timeout;
      import org.apache.commons.logging.Log;
      import org.apache.commons.logging.LogFactory;
      import org.jbpm.calendar.BusinessCalendar;
      import org.jbpm.calendar.Duration;
      import org.jbpm.db.JbpmSession;
      import org.jbpm.db.JbpmSessionFactory;
      import org.jbpm.db.SchedulerSession;
      import org.jbpm.scheduler.exe.Timer;
      import com.something.workflow.Scheduler;
      public class SchedulerBean implements Scheduler {
       private static final Log log = LogFactory.getLog(SchedulerBean.class);
       static JbpmSessionFactory jbpmSessionFactory = JbpmSessionFactory.getInstance();
       static BusinessCalendar businessCalendar = new BusinessCalendar();
       private SessionContext ctx;
       private static final long DEFAULT_TIMEOUT = 5000L;
       public void start()
       log.debug("starting Scheduler");
       public void timeoutHandler(javax.ejb.Timer timer)
       long time = min(executeTimers(),DEFAULT_TIMEOUT);
       public void stop() {
       log.debug("stopping Scheduler");
       Collection timers = ctx.getTimerService().getTimers();
       for (Object o : timers) {
       javax.ejb.Timer t = (javax.ejb.Timer)o;
       private long executeTimers() {
       long millisTillNextTimerIsDue = DEFAULT_TIMEOUT;
       boolean isDueDateInPast = true;
       JbpmSession jbpmSession = jbpmSessionFactory.openJbpmSessionAndBeginTransaction();
       try {
       SchedulerSession schedulerSession = new SchedulerSession(jbpmSession);
       log.debug("checking for jBPM timers");
       Iterator iter = schedulerSession.findTimersByDueDate();
       while ((iter.hasNext()) && (isDueDateInPast)) {
       Timer timer = (Timer) iter.next();
       log.debug("found timer "+timer);
       // if this timer is due
       if (timer.isDue()) {
       log.debug("executing timer '"+timer+"'");
       // execute
       // if there was an exception, just save the timer
       if (timer.getException()!=null) {
       // if repeat is specified
       else if (timer.getRepeat()!=null) {
       // update timer by adding the repeat duration
       Date dueDate = timer.getDueDate();
       // suppose that it took the timer runner thread a
       // very long time to execute the timers.
       // then the repeat action dueDate could already have passed.
       while (dueDate.getTime()<=System.currentTimeMillis()) {
       dueDate = businessCalendar.add(dueDate,new Duration(timer.getRepeat()));
       timer.setDueDate( dueDate );
       // save the updated timer in the database
       log.debug("saving updated timer for repetition '"+timer+"' in '"+(dueDate.getTime()-System.currentTimeMillis())+"' millis");
       else {
       // delete this timer
       log.debug("deleting timer '"+timer+"'");
       else { // this is the first timer that is not yet due
       isDueDateInPast = false;
       millisTillNextTimerIsDue = timer.getDueDate().getTime() - System.currentTimeMillis();
       finally {
       return millisTillNextTimerIsDue;

      Did I duplicate anything already in jBPM? I looked around for this first, but didn't find it. Anyone see any reasons to not use this approach?

      I appreciate all advice,