With Quartz Scheduler, we have a plan

Introduction

Most of the work we do programming is automatizing tedious or repetive tasks. Then, the next obvious step would be a way to plan when should they start, or how to make them stop. For this task we use “Schedulers”.
We can use the basic Java TimerTasks, but that would be a huge effort as it is a very basic tool, and we may need something more advanced. One of the best advantages of using a framework is that we have tested scalable solutions which may be easily adapted to our need, hence, meet Quartz, a free open source library available under Apache 2.0 licence.
The idea is simple: we want to execute taks: which we will wrap in an extension of the class “Job”. Those Jobs will be handled in a main class which contains an Scheduler instance, which will check when it’s the right time to run them, and then it will execute those taks.

Process

1. Setting up the Maven dependencies

The first step is getting the testing dependencies into our project, and that’s something we can do via Maven by adding them to the pom.xml file.
❕These were the stable versions when the post was originally written

1
2
3
4
5
6
7
8
9
10
<dependency>
<groupId>org.quartz-scheduler</groupId>
<artifactId>quartz</artifactId>
<version>2.2.1</version>
</dependency>
<dependency>
<groupId>org.quartz-scheduler</groupId>
<artifactId>quartz-jobs</artifactId>
<version>2.2.1</version>
</dependency>

2. The basic elements

2.1. The scheduler itself
The Schedulers instances are created by the library factories. The jobs can be scheduled anytime, but none will run unless we call the start method.

1
2
3
4
5
// Grab the Scheduler instance from the Factory
Scheduler scheduler = StdSchedulerFactory.getDefaultScheduler();

// and start it off
scheduler.start();

2.2. Job
The most basic execution unit. This will wrap thee repetitive task you’ll need to plan. The method execute is the most important part, as it is the “main” which will run.
Let’s see a useful example, we are going to prepare a task which will check what is already planned, and reload the time of execution.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
@DisallowConcurrentExecution
public class ReloaderJob implements Job {
public ReloaderJob() {
}

/**
* @see org.quartz.Job#execute(org.quartz.JobExecutionContext)
*/
@Override
public void execute(JobExecutionContext context) throws JobExecutionException {
SchedulerMain scheduler = (SchedulerMain) context.getJobDetail().getJobDataMap()
.getWrappedMap().get(AbstractJob.KEY_GET_RELOAD);
scheduler.reloadSchedulings();
}
}

2.3 JobDetail
The details to run them: what (Job class -> try to think of this as a facade for the real task) and when (Trigger).
If there are multiple parameters to pass, it may be good to use a DataMap to handle them:

1
2
3
4
5
6
7
8
9
10
11
public class TestJob implements Job {
public void execute(JobExecutionContext context) throws JobExecutionException {
JobKey key = context.getJobDetail().getKey();
JobDataMap dataMap = context.getJobDetail().getJobDataMap();

String jobSays = dataMap.getString("jobSays");
float myValue = dataMap.getFloat("myValue");
System.out.println("Instance " + key + " of TestJob says: " + jobSays
+ ", and value is: " + myValue);
}
}

2.4. Basic triggers

And then schedule those jobs with triggers that define at what time(s) the job should run.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
// define the job and tie it to our MyJob class
JobDetail job = newJob(MyJob.class)
.withIdentity("job1", "group1")
.build();
// Trigger the job to run now, and then repeat every 40 seconds
Trigger trigger = newTrigger()
.withIdentity("trigger1", "group1")
.startNow()
.withSchedule(simpleSchedule()
.withIntervalInSeconds(40)
.repeatForever())
.build();
// Tell quartz to schedule the job using our trigger
scheduler.scheduleJob(job, trigger);

2.5. CronTrigger

The class which handles the syntax to specify when to run a planned job.
Its syntax basics are:

  • Seconds (0–59)
  • Minutes (0–59)
  • Hours (0–23)
  • Day-of-Month (1–31)
  • Month (JAN, FEB, MAR, APR, MAY, JUN, JUL, AUG, SEP, OCT, NOV, DEC)
  • Day-of-Week (SUN, MON, TUE, WED, THU, FRI, SAT)
  • Year (optional field)

A few more complex examples:

  • 0 0/5 * * * ? → every 5 minutes
  • 10 0/5 * * * ? → every 5 minutes, at 10 seconds after the minute (10:00:10 am, 10:05:10 am, …)
  • 0 30 10–13 ? * WED,FRI → at 10:30, 11:30, 12:30, and 13:30, on every Wednesday and Friday

3. Basic architecture

The best way to understand how does this work is creating a quick example: 

3.1.- Scheduler prototype

Enumerator of the jobs to run: it has the full list of jobs, so every new job must be specified here.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
/**
* Defined jobs to display, containing name and the class to
* execute when triggered.
*/
public enum ScheduledJob {
TESTJOB("Test", org.anaklusmos.scheduler.jobs.TestJob.class),
RELOADERJOB("Reloader", org.anaklusmos.scheduler.jobs.Reloader.class);

private String name;
private Class<? extends Job> jobClass;

private ScheduledJob(String name, Class<? extends Job> jobClass) {
this.name = name;
this.jobClass = jobClass;
}

public String getName() {
return name;
}

public Class<? extends Job> getJobClass() {
return jobClass;
}
}