Scheduling Recurring Tasks In Java Applications
By Tom White
2004-01-23
Reader Rating:

Implementing the scheduling framework
In the previous section, we learned how to use the scheduling framework and compared it with the Java timer framework. Next, I'll show you how the framework is implemented. In addition to the ScheduleIterator interface shown in Listing 3, there are two other classes -- Scheduler and SchedulerTask -- that make up the framework. These classes actually use Timer and TimerTask under the covers, since a schedule is really no more than a series of one-shot timers. Listings 5 and 6 show the source code for the two classes:
Listing 5. Scheduler
package org.tiling.scheduling;
import java.util.Date;
import java.util.Timer;
import java.util.TimerTask;
public class Scheduler {
class SchedulerTimerTask extends TimerTask {
private SchedulerTask schedulerTask;
private ScheduleIterator iterator;
public SchedulerTimerTask(SchedulerTask schedulerTask,
ScheduleIterator iterator) {
this.schedulerTask = schedulerTask;
this.iterator = iterator;
}
public void run() {
schedulerTask.run();
reschedule(schedulerTask, iterator);
}
}
private final Timer timer = new Timer();
public Scheduler() {
}
public void cancel() {
timer.cancel();
}
public void schedule(SchedulerTask schedulerTask,
ScheduleIterator iterator) {
Date time = iterator.next();
if (time == null) {
schedulerTask.cancel();
} else {
synchronized(schedulerTask.lock) {
if (schedulerTask.state != SchedulerTask.VIRGIN) {
throw new IllegalStateException("Task already
scheduled " + "or cancelled");
}
schedulerTask.state = SchedulerTask.SCHEDULED;
schedulerTask.timerTask =
new SchedulerTimerTask(schedulerTask, iterator);
timer.schedule(schedulerTask.timerTask, time);
}
}
}
private void reschedule(SchedulerTask schedulerTask,
ScheduleIterator iterator) {
Date time = iterator.next();
if (time == null) {
schedulerTask.cancel();
} else {
synchronized(schedulerTask.lock) {
if (schedulerTask.state != SchedulerTask.CANCELLED) {
schedulerTask.timerTask =
new SchedulerTimerTask(schedulerTask, iterator);
timer.schedule(schedulerTask.timerTask, time);
}
}
}
}
}
|
Listing 6 shows the source code for the SchedulerTask class:
Listing 6. SchedulerTask
package org.tiling.scheduling;
import java.util.TimerTask;
public abstract class SchedulerTask implements Runnable {
final Object lock = new Object();
int state = VIRGIN;
static final int VIRGIN = 0;
static final int SCHEDULED = 1;
static final int CANCELLED = 2;
TimerTask timerTask;
protected SchedulerTask() {
}
public abstract void run();
public boolean cancel() {
synchronized(lock) {
if (timerTask != null) {
timerTask.cancel();
}
boolean result = (state == SCHEDULED);
state = CANCELLED;
return result;
}
}
public long scheduledExecutionTime() {
synchronized(lock) {
return timerTask == null ? 0 : timerTask.scheduledExecutionTime();
}
}
}
|
Like the egg timer, every instance of Scheduler owns an instance of Timer to provide the underlying scheduling. Instead of the single one-shot timer used to implement the egg timer, Scheduler strings together a chain of one-shot timers to execute a SchedulerTask class at the times specified by a ScheduleIterator.
Consider the public schedule() method on Scheduler -- this is the entry point for scheduling because it is the method a client calls. (The only other public method, cancel(), is described in Canceling tasks.) The time of the first execution of the SchedulerTask is discovered by calling next() on the ScheduleIterator interface. The scheduling is then kicked off by calling the one-shot schedule() method on the underlying Timer class for execution at this time. The TimerTask object supplied for one-shot execution is an instance of the nested SchedulerTimerTask class, which packages up the task and the iterator. At the allotted time, the run() method is called on the nested class, which uses the packaged task and iterator references to reschedule the next execution of the task. The reschedule() method is very similar to the schedule() method, except that it is private and performs a slightly different set of state checks on SchedulerTask. The rescheduling process repeats indefinitely, constructing a new nested class instance for each scheduled execution, until the task or the scheduler is cancelled (or the JVM shuts down).
Like its counterpart TimerTask, SchedulerTask goes through a series of states during its lifetime. When created, it is in a VIRGIN state, which simply means it has never been scheduled. Once scheduled, it shifts to a SCHEDULED state, then later to a CANCELLED state if the task is cancelled by one of the methods described below. Managing the correct state transitions, such as ensuring that a non-VIRGIN task is not scheduled twice, adds extra complexity to the Scheduler and SchedulerTask classes. Whenever an operation is performed that might change the state of the task, the code must synchronize on the task's lock object.
Canceling tasks
There are three ways to cancel a scheduled task. The first is to call the cancel() method on the SchedulerTask. This is like calling cancel() on a TimerTask: the task will never run again, although it will run to completion if already running. The return value of the cancel() method is a boolean that indicates whether further scheduled tasks might have run had cancel() not been called. More precisely, it returns true if the task was in a SCHEDULED state immediately prior to calling cancel(). If you try to reschedule a cancelled (or even scheduled) task, Scheduler throws an IllegalStateException.
The second way to cancel a scheduled task is for ScheduleIterator to return null. This is simply a shortcut for the first way, as the Scheduler class calls cancel() on the SchedulerTask class. Canceling a task this way is useful if you want the iterator -- rather than the task -- to control when the scheduling stops.
The third way is to cancel the whole Scheduler by calling its cancel() method. This cancels all the scheduler's tasks and leaves it in a state where no more tasks may be scheduled on it.
First published by IBM developerWorks
If you found this article interesting, you may want to read these as well:
» Build and Implement A Single Sign-On Solution
» Eye On Performance: A Load Of Stress
» A Brief History Of Garbage Collection
|