Task Scheduling in Spring

In many applications, various tasks (such as sending e-mail notifications to customers, running day-end jobs, doing data housekeeping and updating data in batches) need to be scheduled to run on a regular basis, either in a fixed interval (for example, every hour) or at a specific schedule (for example, at 8PM every day, from Monday to Friday). Task scheduling is composed mainly of three parts :

  • Task (which is the piece of business logic needed to run at a specific time or on a regular basis)
  • Trigger (which specifies the condition under which the task should be executed)
  • Scheduler (which executes the task based on the information from the trigger)

There are many ways to trigger the execution of a task in a Spring application. One way is to trigger a job externally from a scheduling system that already exists in the application deployment environment. For example, if the application is running on Unix platform, the crontab scheduler can be used. The job triggering can be done by sending a RESTful-WS request to the Spring application and having Spring’s MVC controller trigger the task.

Another way is to use the task scheduling support in Spring. Spring provides three options in terms of tasks scheduling :

  • Support of JDK timer : Spring supports JDK’s Timer object for task scheduling
  • Integration with Quartz : the Quartz Scheduler is a popular open source scheduling library
  • Spring’s own Spring TaskScheduler Abstraction : Spring 3 introduces the TaskScheduler abstraction, which provides a simple way to schedule tasks and supports most typical requirements

Spring TaskScheduler Abstraction

Spring’s TaskScheduler abstraction has mainly three participants :

  • Trigger interface : org.springframework.scheduling.Trigger interface provides support for defining the triggering mechanism. Spring provides two Trigger implementations : CronTrigger class supports triggering based on a cron expression ; PeriodicTrigger class supports triggering based on an initial delay and then a fixed interval
  • Task : task is the piece of business logic that needs to be scheduled. In Spring, a task can be specified as a method within any Spring bean
  • TaskScheduler interface : org.springframework.scheduling.TaskScheduler interface provides support for task scheduling. Spring provides three implementation classes of the TaskScheduler interface : TimerManagedTaskScheduler class (under the package org.springframework.scheduling.commonj) wraps CommonJ’s commonj.timers.TimerManager interface, which is commonly used in commercial JEE application servers such as WebSphere and WebLogic ; ConcurrentTaskScheduler and ThreadPoolTaskScheduler classes (both under the package org.springframework.scheduling.concurrent) wrap the java.util.concurrent.ScheduledThreadPoolExecutor class. Both classes support task execution from a shared thread pool

Scheduling tasks by using Spring’s TaskScheduler abstraction can be made by using either xml configuration or annotations.

@Scheduled annotation can be used to configure and schedule tasks. The rules that must be followed to annotate a method with @Scheduled are :

  • a method should have a void return type
  • a method should not accept any parameters

To enable support for scheduling tasks and the @Scheduled annotation in Spring, @EnableScheduling annotation must be added to configuration beans (annotated with @Configuration).

Tasks can be run after a fixed delay or at a fixed rate, also they can have initialDelay (same options that Concurrency API provides). For instance, @Scheduled(fixedRate = 1000) means that each new task starts 1000 milliseconds after another; @Scheduled(fixedDelay = 1000) means that each new task starts 1000 milliseconds after another task has finished an execution; @Scheduled(fixedDelay = 1000, initialDelay = 500) means that first execution will occur after 500 milliseconds.

Scheduled tasks don’t run in parallel by default. Even in the case of fixedRate the next task won’t be invoked until the previous one is done. In order to support parallel behavior in scheduled tasks, @Async annotation must be added. Also, @EnableAsync annotation must be added to Spring Bean. See example below :

@Component
@EnableAsync
public class ScheduledTasks {
    @Async
    @Scheduled(fixedRate = 1000)
    public void reportCurrentTime() throws InterruptedException {
        System.out.printf("The time now is %tT", LocalTime.now());
        System.out.println();
        TimeUnit.MILLISECONDS.sleep(2000);
    }
}

Each new task will be invoked each second, even if the previous task is not done. Without asynchronous facilities, in the above example it will take 3 seconds for each new task to be invoked.

Sometimes delays and rates are not enough, and developers need the flexibility of a cron expression to control the schedule of tasks :

@Scheduled(cron = "0 15 10 15 * ?")
public void scheduleTaskUsingCronExpression() {

long now = System.currentTimeMillis() / 1000;
System.out.println(
"schedule tasks using cron jobs - " + now);
}

In this example the task is scheduled to be executed at 10:15 AM on the 15th day of every month. By default, Spring will use the server’s local time zone for the cron expression. However, the zone attribute can be used to change this timezone : @Scheduled(cron = “0 15 10 15 * ?”, zone = “Europe/Paris”) – Spring will schedule the annotated method to run at 10:15 AM on the 15th day of every month in Paris time.

Hardcoding schedules is simple, but usually it is essential to control the schedule without re-compiling and re-deploying the entire app. Spring Expressions can be used to externalize the configuration of the tasks, and they are stored in the properties files :

  • fixedDelay task – @Scheduled(fixedDelayString = “${fixedDelay.in.milliseconds}”)
  • fixedRate task – @Scheduled(fixedRateString = “${fixedRate.in.milliseconds}”)
  • cron expression based task – @Scheduled(cron = “${cron.expression}”)

Leave a Reply

Your email address will not be published. Required fields are marked *