In the last weeks we looked more deeply at the job executor and timer service of the PVM.
We see these use cases for the timer service:
- Timer service will be used in xpdl (deadlines) and in bpel (wait, pick...).
In all these cases,
- the task to execute on timer completion is to signal an execution.
- a timer needs to execute a task after a given delay or on a specific date.
In some cases, we would like to be able to have a task repeated at a specific rate.
In the extensions of the pvm, we want to be able to run with/without persistence, in standard/enterprise environment.
So we would like to create an interface for the TimerService.
This interface contains methods to schedule a task. A task can be scheduled at a specified date or after a specified delay.
A repeat delay can be specified.
A task is defined by an Activity and an Execution. When the timer is fired, the execute method of the activity is called, with the specified execution as parameter.
To allow more extensibility, activities are wrapped in an ObjectReference (just as ExternalActivities in Nodes).
As a result, the interface provides methods to schedule an activity:
public long schedule(ObjectReference<Activity> activityReference, Execution execution, Date date); public long schedule(ObjectReference<Activity> activityReference, Execution execution, long delay); public long schedule(ObjectReference<Activity> activityReference, Execution execution, Date date, long repeatDelay); public long schedule(ObjectReference<Activity> activityReference, Execution execution, long delay, long repeatDelay);
These methods return an id associated with the scheduled task. This id can be used to cancel a task.
This interface contains other methods to query the timer service:
public boolean cancel(long id); public Collection<ObjectReference<Activity>> getScheduledActivities(); public long getNextScheduledActivityTime(); public ObjectReference<Activity> getNextScheduledActivity(); public ObjectReference<Activity> getScheduledActivity(long id);
For our tests, Pascal has implemented this interface:
- in memory by using a JDK timer
- in db by using the pvm job executor
For the db implementation, we had to modify some parts of the pvm:
- the Timer class was not implemented (execute method was commented).
- the jobExecutor was executing all the tasks that were in the Job table in database.
So as to be able to execute a timer job only in a future time, we added a field "eligibleDate" to the job. A job cannot be executed before this eligible date.
- the jobExecutor wakes up when a new job is added. To wake up the jobExecutor when a task becomes eligible, we use a JDK timer that notifies the jobExecutor that a new job has been added. This way, we have a JDK timer that fire events, and a scheduled Job in db for persistence and crash recovery.
For crash recovery, a special method needs to be called. This method will recreate JDK timer for the persisted scheduled jobs.
Jobs that would have been executed during the crash have become eligible for the jobExecutor and are executed automatically on restart.
These two implementations work with basic tests.
While modifying the jobExecutor, we tried to see how to make it independent from the persistence.
We identified these issues:
- JobService is hibernate specific, we can create an interface for the service and have an in memory implementation
- JobExecutorThread obtains job Ids from the jobService, and creates a ExecuteJobCommand with this id. ExecuteJobCommand loads the job from the db. Maybe executeJobCommand can delegate the job loading to the jobService.
- JobExceptionHandler has the same behavior as ExecuteJobCommand.
- StandardMessageService stores the job directly in the db, maybe the job can be saved by the JobService.
- on completion, job are removing themselves from the db, maybe the job can call a remove method in the jobService.
Pascal tried to do these modifications, jobExecutor tests pass in db.
He tried to implement the memory version of JobService, but has not tested it yet.
What do you think of the proposed TimerAPI ? Do you see some use cases we cannot implement ?
What do you think about the modifications in the JobExecutor ?
Can this be a valid implementation for asynchronous jobs in memory ?
Pascal and Guillaume