Quartz Scheduler (1)— An Introduction

Manvendra P Singh
4 min readNov 16, 2021

It's a common use case to have an enterprise application, perform specific work, at a specific time or in response to a specific action. In other words, “There is an ask to execute a Job upon a predefined Trigger”.

This brings us to the need for a Scheduling System. A system, where Jobs & Trigger can be registered and the system will manage the remaining complexity. Thankfully for the Java systems, Quartz is for rescue. It‘s an open-source library that has been extensively used in enterprise applications for more than a decade.

Components in Quartz Sub System:

Following are the all major component in the Quartz subsystem:

  • Scheduler: It’s the control room of Quartz. It maintains everything required for scheduling, such as managing listeners, scheduling jobs, clustering, transactions & job persistence. It maintains a registry of JobDetails, Listeners & Triggers, and executes Job & Listeners when their associated Trigger is fired.
  • SchedulerFactory: It’s a factory Interface for creating a scheduler instance as per the configured environment. The environment can be configured by properties, yml, or java-code, as explained in the code segment below.
  • Job: Interface implemented by the class that contains the application code to execute upon a trigger fire.
  • JobDetail: Interface implemented by the class which contains the execution-specific information about a Job. A Scheduler never stores an actual instance of any Job class, instead it registers the instance of Triggers with JobDetails. When a Trigger is fired, Scheduler picks a Thread from a predefined ThreadPool and uses the properties from associated JobDetails to execute the Job on that thread. Typical properties in JobDetails are job name, job class, job data map, isConcurrentExectionDisallowed, etc.
  • JobBuilder: A builder pattern implementation used to create a JobDetail instances
  • Trigger: Configuration to decide when/how-long/how-many-times a Job needs to run.
  • TriggerBuilder: A builder pattern implementation to create a Trigger instance
  • ThreadPool: Pool of those threads which are reserved by Quartz to run Jobs code upon a trigger fire.
  • JobStore: A place where the scheduler keeps the information about Job/Trigger/Calendar etc.
  • DataSources: Used when using a JDBCJobSore.
  • TriggerListeners — Receive events related to triggers, if configured.
  • SchedulerListeners — Receive notification of events within the Scheduler itself, like addition/removal of a job/trigger, etc.

Let’s Code

We will build a very simple application that will use a quartz scheduler & print Hello world at a regular interval. In the next blog, we will integrate Quartz with Springboot.

  1. Maven project
    Create a maven project and add the following dependency
<dependency> 
<groupid>org.quartz-scheduler</groupid> <artifactid>quartz</artifactid>
<version>2.3.2</version> </dependency>

2. Main class
Create a class containing the main method. I named QuartzExample but any name will do.

3. Create a scheduler instance.
A Scheduler instance can only be created from a SchedulerFactory.
So first we need to create an instance of SchedulerFactory.
There are already a few SchedulerFactory implementations provided within quartz. For this example, I will use StdSchedulerFactory.

StdSchedulerFactory factory = new StdSchedulerFactory();
Scheduler scheduler = factory.getScheduler();

Now that we have an instance of the Scheduler, we can attach our Trigger and JobDetails to it. But keep in mind nothing will happen until we call start() on our scheduler.

NOTE: Configuring StdSchedulerFactory :
In the above code, I created StdSchedulerFactory by using the default constructor.
When you use the default constructor, quartz checks if a system-property
org.quartz.properties is set.
If the system property is set, then quartz tries to load the file from this property. If nothing is found, then quartz tries to load the properties from a file named
quartz.propertiesin classpath. If no files are found, quartz is started with defaults

Another way to load quartz configuration properties is, In factory constructor, you can either provide a property file name Or a java.util.Properties instance by setting properties in java code itself.

4. Create a Job class.
For this, we need to implement the Job interface and write a start point for our business code under execute method.

public class Hello implements Job {
@Override
public void execute(JobExecutionContext context)
throws JobExecutionException {
System.out.println(
"Hello from the Job !"
+ context.getRefireCount());
}
}

5. Create JobDetail & Trigger.
For this, we can use the JobBuilder & TriggerBuilder classes from Quartz.

JobDetail jobDetail 
= JobBuilder
.newJob()
.ofType(Hello.class)
.withIdentity("Hello-job-name")
.build();
SimpleTrigger simpleTrigger
= TriggerBuilder
.newTrigger()
.withIdentity("Hello-trigger-name")
.forJob(jobDetail)
.withSchedule(SimpleScheduleBuilder
.simpleSchedule()
.withIntervalInSeconds(1)
.withRepeatCount(5))
.build();

NOTE: You can also provide the group in the identity step, but if nothing is provided the DEFAULT group is used.

6. Register Job & Trigger with Scheduler.
scheduler.scheduleJob(jobDetail ,simpleTrigger);
However, when we run QuartzExample.java nothing happens. This is because we have not started the scheduler yet. Let's start the scheduler.
scheduler.start();

Now when we run QuartzExample.java, we can see our scheduler printing a Hello message repeatedly.

This is all for this time, please do provide your feedback by comments or applauds. In next blog we will revisit all these steps by using SpringBoot.

But before we finish, below is the complete code for your reference.

package com.mps.example;

import org.quartz.*;
import org.quartz.impl.StdSchedulerFactory;

public class QuartzExample {
public static void main(String[] args) {
try {
StdSchedulerFactory factory = new StdSchedulerFactory();
Scheduler scheduler = factory.getScheduler();

JobDetail jobDetail = JobBuilder
.newJob()
.ofType(Hello.class)
.withIdentity("Hello-job-name")
.build();

SimpleTrigger simpleTrigger = TriggerBuilder
.newTrigger()
.withIdentity("Hello-trigger-name")
.forJob(jobDetail)
.withSchedule(SimpleScheduleBuilder
.simpleSchedule()
.withIntervalInSeconds(1)
.withRepeatCount(5))
.build();

scheduler.scheduleJob(jobDetail, simpleTrigger);
scheduler.start();

} catch (SchedulerException e) {
e.printStackTrace();
}
}
}

--

--