Helping ordinary people create extraordinary websites!
HOME TUTORIALS SCRIPTS WEB HOSTING BLOG FORUM
Get Our Newsletter
Email:

Scheduling Recurring Tasks In Java Applications

By Tom White
2004-01-23


Scheduling a recurring task

Timer allows tasks to be scheduled for repeated execution by specifying a fixed rate of execution or a fixed delay between executions. However, there are many applications that have more complex scheduling requirements. For example, an alarm clock that sounds a wake-up call every morning at the same time cannot simply use a fixed rate schedule of 86400000 milliseconds (24 hours), because the alarm would be too late or early on the days the clocks go forward or backward (if your time zone uses daylight saving time). The solution is to use calendar arithmetic to calculate the next scheduled occurrence of a daily event. This is precisely what the scheduling framework supports. Consider the AlarmClock implementation in Listing 2 (see Resources to download the source code for the scheduling framework, as well as a JAR file containing the framework and examples):

Listing 2. AlarmClock class


package org.tiling.scheduling.examples;

import java.text.SimpleDateFormat;

import java.util.Date;

import org.tiling.scheduling.Scheduler;
import org.tiling.scheduling.SchedulerTask;
import org.tiling.scheduling.examples.iterators.DailyIterator;

public class AlarmClock {

private final Scheduler scheduler = new Scheduler();
private final SimpleDateFormat dateFormat =
new SimpleDateFormat("dd MMM yyyy HH:mm:ss.SSS");
private final int hourOfDay, minute, second;

public AlarmClock(int hourOfDay, int minute, int second) {
this.hourOfDay = hourOfDay;
this.minute = minute;
this.second = second;
}

public void start() {
scheduler.schedule(new SchedulerTask() {
public void run() {
soundAlarm();
}
private void soundAlarm() {
System.out.println("Wake up! " +
"It's " + dateFormat.format(new Date()));
// Start a new thread to sound an alarm...
}
}, new DailyIterator(hourOfDay, minute, second));
}

public static void main(String[] args) {
AlarmClock alarmClock = new AlarmClock(7, 0, 0);
alarmClock.start();
}
}


Notice how similar the code is to the egg timer application. The AlarmClock instance owns a Scheduler instance (rather than a Timer) to provide the necessary scheduling. When started, the alarm clock schedules a SchedulerTask (rather than a TimerTask) to play the alarm. And instead of scheduling the task for execution after a fixed delay, the alarm clock uses a DailyIterator class to describe its schedule. In this case, it simply schedules the task at 7:00 AM every day. Here is the output from a typical run:


Wake up! It's 24 Aug 2003 07:00:00.023
Wake up! It's 25 Aug 2003 07:00:00.001
Wake up! It's 26 Aug 2003 07:00:00.058
Wake up! It's 27 Aug 2003 07:00:00.015
Wake up! It's 28 Aug 2003 07:00:00.002
...


DailyIterator implements ScheduleIterator, an interface that specifies the scheduled execution times of a SchedulerTask as a series of java.util.Date objects. The next() method then iterates over the Date objects in chronological order. A return value of null causes the task to be cancelled (that is, it will never be run again) -- indeed, an attempt to reschedule will cause an exception to be thrown. Listing 3 contains the ScheduleIterator interface:

Listing 3. ScheduleIterator interface


package org.tiling.scheduling;

import java.util.Date;

public interface ScheduleIterator {
public Date next();
}


DailyIterator's next() method returns Date objects that represent the same time each day (7:00 AM), as shown in Listing 4. So if you call next() on a newly constructed DailyIterator class, you will get 7:00 AM of the day on or after the date passed into the constructor. Subsequent calls to next() will return 7:00 AM on subsequent days, repeating forever. To achieve this behavior DailyIterator uses a java.util.Calendar instance. The constructor sets up the calendar so that the first invocation of next() returns the correct Date simply by adding a day onto the calendar. Note that the code contains no explicit reference to daylight saving time corrections; it doesn't need to because the Calendar implementation (in this case GregorianCalendar) takes care of this.

Listing 4. DailyIterator class


package org.tiling.scheduling.examples.iterators;

import org.tiling.scheduling.ScheduleIterator;

import java.util.Calendar;
import java.util.Date;

/**
* A DailyIterator class returns a sequence of dates on subsequent days
* representing the same time each day.
*/
public class DailyIterator implements ScheduleIterator {
private final int hourOfDay, minute, second;
private final Calendar calendar = Calendar.getInstance();

public DailyIterator(int hourOfDay, int minute, int second) {
this(hourOfDay, minute, second, new Date());
}

public DailyIterator(int hourOfDay, int minute, int second, Date date) {
this.hourOfDay = hourOfDay;
this.minute = minute;
this.second = second;
calendar.setTime(date);
calendar.set(Calendar.HOUR_OF_DAY, hourOfDay);
calendar.set(Calendar.MINUTE, minute);
calendar.set(Calendar.SECOND, second);
calendar.set(Calendar.MILLISECOND, 0);
if (!calendar.getTime().before(date)) {
calendar.add(Calendar.DATE, -1);
}
}

public Date next() {
calendar.add(Calendar.DATE, 1);
return calendar.getTime();
}

}



Tutorial Pages:
» Introduction
» Scheduling a one-shot task
» Scheduling a recurring task
» Implementing the scheduling framework
» Extending the cron facility
» Real-time guarantees
» Conclusion
» Resources


First published by IBM developerWorks


 | Bookmark
Related Tutorials:
» All about JAXP, Part 1
» Make Database Queries Without the Database
» Load List Values for Improved Efficiency
» 2 Ways To Implement Session Tracking
» A Simple Way to Read an XML File in Java
» Develop Aspect-Oriented Java Applications with Eclipse and AJDT