Quartz Implementation in Java

In this post, java development India based experts will explain the concept of Quartz. You will also learn the method of setting up the Quartz in this article. You can ask experts if anything bothers you.

Technology

Quartz is the open source Java technology for scheduling background jobs. If we want to execute the task for regular interval or at some specific time then quartz schedulers will be used, it will also support the transaction management like JTA.

Some of the Features of Quartz

  • It can be embedded in any standalone or web/application server.
  • It can be executed in distributed transaction management like XA Transactions.
  • It can be run as separate standalone application.
  • Quartz can be started as cluster of standalone application (with load balance) for execution of background jobs.

Quartz setup

If we are using maven build tool then we can add the below dependency in pom.xml file:

<Dependency>
<groupId>org.quartz-scheduler</groupId>
<artifactId>quartz</artifactId>
<version>2.2.3</version>
</Dependency>

If we are using Gradle build tool we can add the group in build.gradle file

Compile group: 'org.quartz-scheduler', name: 'quartz', version: '2.2.3'

If we are not using any build tools then we can download the jar from http://central.maven.org/maven2/org/quartz-scheduler/quartz/2.2.3/quartz-2.2.3.jar and we need to add to classpath.

Key classes in Quartz

  1. Scheduler: It is the main API which interacts with Scheduler.
  2. Job: It is the interface provided by Quartz frameworks for creating custom jobs and it will be executed by Scheduler.
  3. JobDetail: It will hold the all details about running instance of the job.
  4. Trigger: It is the component which defines the job schedule.
  5. JobBuilder: It is the builder class, which will use to create and build the job details.
  6. TriggerBuilder: It is the builder class which will use to define/build the triggers.

Quartz Scheduler Examples

Creating Scheduler object:

SchedulerFactory is the interface provided by Quartz for creating the schedulers.

Setting properties to SchedulerFactory:

By default a “quartz.properties” is loaded from the ‘current working directory’. If that fails, then the “quartz.properties” file located (as a resource) in the org/quartz package is loaded. If we want to use a file other than these defaults, you must define the system property ‘org.quartz.properties’ to point to the file you want.

SchedulerFactory also has a method initialize, we can pass the properties to SchedulerFactory instance.SchedulerFactory has a method getScheduler to create Scheduler object.

try {
    StdSchedulerFactory factory = new StdSchedulerFactory();
    Scheduler scheduler =factory.getScheduler();
    scheduler.start();
    scheduler.shutdown();
} catch (SchedulerException e) {
    e.printStackTrace();
}

After creating the scheduler we need to start the scheduler, without starting the scheduler triggers will not be fired on scheduler.

Creating jobs:

We can create a job implementing org.quartz.Job interface and implementing the execute method. The implemented must provide the default no-arg constructor.JobDataMap will holds the parameters for current running job.

public class HelloWorldJob implements Job {
    @Override
    public void execute(JobExecutionContext jobContext) throws JobExecutionException {
        System.out.println("Hello World!!");
        System.out.println("HelloWorldJob start: " + jobContext.getFireTime());
        JobDetail jobDetail = jobContext.getJobDetail();
        System.out.println("Example name is: " + jobDetail.getJobDataMap().getString("example"));
        System.out.println("HelloWorldJob end: " + jobContext.getJobRunTime() + ", key: " +            jobDetail.getKey());
        System.out.println("HelloWorldJob next scheduled time: " + jobContext.getNextFireTime());
    }
}

Creating JobDetail object

It holds all properties for a job, JobDetail object is created/defined using JobBuilder.

Creating JobDetail with data:
JobDetail jobDetail = JobBuilder.newJob(HelloWorldJob.class).usingJobData("example","Hello World job running").withIdentity("HelloWorldJob", "group1").build();

Or

JobDataMap data = new JobDataMap();
data.put("example", "Hello World job running");
JobDetail jobDetail = JobBuilder.newJob(HelloWorldJob.class).usingJobData(data).withIdentity("HelloWorldJob", "group1").build();

Creating JobDetail without data:

JobDetail jobDetail = JobBuilder.newJob(HelloWorldJob.class).withIdentity("HelloWorldJob", "group1").build();

Creating triggers

It is used to define/build Triggers which is used schedule the jobs by repeating after certain count, or in regular intervals.

Trigger trigger = TriggerBuilder.newTrigger().withIdentity("myTrigger", "group1")
.startNow()
.withSchedule(SimpleScheduleBuilder.simpleSchedule()
.withRepeatCount(5)
.withIntervalInSeconds(2))
.build();

Triggering the scheduler:

Scheduler has a method scheduleJob which will start the job.

scheduler.scheduleJob(jobDetail, trigger);

Stopping Scheduler

Scheduler has shutdown method, which is used to stop the scheduler.

scheduler.shutdown();

After following all the steps the sample program will look like this:

QuartzTest.java

package org.sample;
import org.quartz.JobBuilder;
import org.quartz.JobDataMap;
import org.quartz.JobDetail;
import org.quartz.Scheduler;
import org.quartz.SchedulerException;
import org.quartz.SimpleScheduleBuilder;
import org.quartz.Trigger;
import org.quartz.TriggerBuilder;
import org.quartz.impl.StdSchedulerFactory;
public class QuartzTest {
public static void main(String[] args){
try {
StdSchedulerFactory factory = new StdSchedulerFactory();
Scheduler scheduler =factory.getScheduler();
scheduler.start();
JobDataMap data = new JobDataMap();
data.put("example", "Hello World job running");
JobDetail jobDetail = JobBuilder.newJob(HelloWorldJob.class).usingJobData(data).withIdentity("HelloWorldJob", "group1").build();
Trigger trigger = TriggerBuilder.newTrigger().withIdentity("myTrigger", "group1")
.startNow()
.withSchedule(SimpleScheduleBuilder.simpleSchedule ()
.withRepeatCount(5)
.withIntervalInSeconds(2))
.build();
scheduler.scheduleJob(jobDetail, trigger);
} catch (SchedulerException e) {
e.printStackTrace();
}
}
}

HelloWorldJob.java

package org.sample;
import org.quartz.Job;
import org.quartz.JobDetail;
import org.quartz.JobExecutionContext;
import org.quartz.JobExecutionException;
 
public class HelloWorldJob implements Job {
@Override
public void execute(JobExecutionContext jobContext) throws JobExecutionException {
System.out.println("Hello World!!");
System.out.println("HelloWorldJob start: " + jobContext.getFireTime());
JobDetail jobDetail = jobContext.getJobDetail();
System.out.println("Example name is: " + jobDetail.getJobDataMap().getString("example"));
System.out.println("HelloWorldJob end: " + jobContext.getJobRunTime() + ", key: " + jobDetail.getKey());
System.out.println("HelloWorldJob next scheduled time: " + jobContext.getNextFireTime());
}
}

HelloWorldJob job will be executed 5 times with 2 sec interval time.

Output:

Hello World!!
HelloWorldJob start: Wed Jul 27 17:11:15 IST 2016
Example name is: Hello World job running
HelloWorldJob end: -1, key: group1.HelloWorldJob
HelloWorldJob next scheduled time: Wed Jul 27 17:11:17 IST 2016
Hello World!!
HelloWorldJob start: Wed Jul 27 17:11:17 IST 2016
Example name is: Hello World job running
HelloWorldJob end: -1, key: group1.HelloWorldJob
HelloWorldJob next scheduled time: Wed Jul 27 17:11:19 IST 2016

Exception Handling

The quartz jobs will throw 2 types of Exceptions.

RuntimeException: It throws this exception if it find any exception at runtime.

JobExecutionException: An exception that can be thrown by a Quartz job to indicate to the Quartz Scheduler that an error occurred while executing, and whether or not the Job requests to be re-fired immediately (using the same JobExecutionContext, or whether it wants to be unscheduled. If the flag for ‘refire immediately’ is set, the flags for unscheduling the Job are ignored.

Concurrent Execution of Jobs

We can allow/disallow the same job which can run either concurrently or not.

Quartz framework provides one annotation DisallowConcurrentExecution, if we mark the job with annotation job will not run concurrently.

@DisallowConcurrentExecution: An annotation that marks a Job class as one that must not have multiple instances executed concurrently (where instance is based-upon a JobDetail definition – or in other words based upon a JobKey).

Triggers

Quartz provides different types of triggers, but mainly we will use simple triggers and cron triggers.

Simple Triggers:

We already used simple triggers previous, simple triggers will start at specific time and it will be repeated at given interval of time and it will repeated till specified repeated count.

Triggering simple triggers now:

Trigger trigger = TriggerBuilder.newTrigger()
.withIdentity("myTrigger", "group1")
.startNow()
.withSchedule(SimpleScheduleBuilder.simpleSchedule()
.withRepeatCount(5)
.withIntervalInSeconds(2))
.build();

Trigger will be executed immediately and it will be executed for 2 sec and it will be repeated 5 times.

Triggering simple trigger at specified time:

Trigger trigger = TriggerBuilder.newTrigger()
.withIdentity("myTrigger", "group1")
.startAt(DateBuilder.todayAt(10, 20, 30))
.withSchedule(SimpleScheduleBuilder.simpleSchedule()
.withRepeatCount(5)
.withIntervalInSeconds(2))
.build();

Trigger will be executed at today 10AM 20 min 30sec. And it will be executed for 2 sec and it will be repeated 5 times.

Triggering the job infinity times:

Trigger trigger = TriggerBuilder.newTrigger()
.withIdentity("myTrigger", "group1")
.startAt(DateBuilder.todayAt(10, 20, 30))
.withSchedule(SimpleScheduleBuilder.simpleSchedule()
.withIntervalInSeconds(2)
.repeatForever())
.build();

Trigger will be executed at today 10AM 20 min 30sec. And it will be executed with interval of 2 sec infinite times.

Cron Triggers:

Cron triggers will use cron expressions to trigger the job.CronTrigger instances are built using TriggerBuilder and another helper class called CronScheduleBuilder which we can use to set the CronTrigger-specific properties. Cron-Expressions are used to configure instances of CronTrigger.

Cron Expressions:

It is expression to inform scheduler when to execute the job, it contains 7 fields.

  • Seconds
  • Minutes
  • Hours
  • Day-of-Month
  • Month
  • Day-of-Week
  • Year (optional field)

List of special characters used in cron expression:

  • * means any suppose if we specified it for seconds means every second.
  • ? Useful when you need to specify something in one of the two fields in which the character is allowed, but not the other.
  • used to specify ranges.
  • , to specify additional values
  • / used to specify increments. For example, “0/15” in the second’s field means “the seconds 0, 15, 30, and 45″.

Eg: 0 15 10 * *? * means every day 10.15 AM.

Creating cron Trigger:

Trigger trigger = TriggerBuilder.newTrigger()
.withIdentity("myTrigger", "group1")
.startAt(DateBuilder.todayAt(10, 20, 20))
.withSchedule(CronScheduleBuilder.cronSchedule("0 1 * * *? *"))
.build();

The above trigger will starts at today 10 AM 20 mins 20 sec. And it will be executed in the interval of 1 min.

Sample application for Cron triggers:

QuartzTest.java

package org.sample;
import org.quartz.CronScheduleBuilder;
import org.quartz.JobBuilder;
import org.quartz.JobDataMap;
import org.quartz.JobDetail;
import org.quartz.Scheduler;
import org.quartz.SchedulerException;
import org.quartz.Trigger;
import org.quartz.TriggerBuilder;
import org.quartz.impl.StdSchedulerFactory;
 
public class QuartzTest {
public static void main(String[] args){
try {
StdSchedulerFactory factory = new StdSchedulerFactory();
Scheduler scheduler =factory.getScheduler();
scheduler.start();
JobDataMap data = new JobDataMap();
data.put("example", "Hello World job running");
JobDetail jobDetail = JobBuilder.newJob(HelloWorldJob.class).usingJobData(data).withIdentity("HelloWorldJob", "group1").build();
Trigger trigger = TriggerBuilder.newTrigger().withIdentity("myTrigger", "group1")
.startNow()
.withSchedule(CronScheduleBuilder.cronSchedule("0/5 * * * *? *"))
.build();
scheduler.scheduleJob(jobDetail, trigger);
} catch (SchedulerException e) {
e.printStackTrace();
}
}
}

Output:

Hello World!!
HelloWorldJob start: Wed Jul 27 21:04:15 IST 2016
Example name is: Hello World job running
HelloWorldJob end: -1, key: group1.HelloWorldJob
HelloWorldJob next scheduled time: Wed Jul 27 21:04:20 IST 2016
Hello World!!
HelloWorldJob start: Wed Jul 27 21:04:20 IST 2016
Example name is: Hello World job running
HelloWorldJob end: -1, key: group1.HelloWorldJob
HelloWorldJob next scheduled time: Wed Jul 27 21:04:25 IST 2016

Scheduler Listeners

Quartz provides org.quartz.SchedulerListener interface for listeners. The listener will be executed in below situations:

  • The addition of a job or trigger
  • The removal of a job or trigger
  • An error within the Scheduler
  • The shutdown of the Scheduler

We create a custom listeners by implementing SchedulerListener interface, and we can add the listener using below line:

scheduler.getListenerManager().addSchedulerListener(new MySchedulerListener(scheduler));

MySchedulerListener is the custom listener.

MySchedulerListener.java

package org.sample;
import org.quartz.JobDetail;
import org.quartz.JobKey;
import org.quartz.Scheduler;
import org.quartz.SchedulerException;
import org.quartz.SchedulerListener;
import org.quartz.Trigger;
import org.quartz.TriggerKey;
public class MySchedulerListener implements SchedulerListener {
private final Scheduler scheduler;
public MySchedulerListener(Scheduler scheduler) {
this.scheduler = scheduler;
}
public void jobScheduled(Trigger trigger) {
System.out.println("Job scheduled: " + trigger.getKey().getName());
}
public void jobUnscheduled(TriggerKey triggerKey) {
System.out.println("Job Unscheduled: " + triggerKey.getName());
}
public void triggerFinalized(Trigger trigger) {
System.out.println("Job trigger finalized:”
+ trigger.getKey().getName());
}
public void triggerPaused(TriggerKey triggerKey) {
System.out.println("Job trigger paused: " + triggerKey.getName());
}
public void triggersPaused(String triggerGroup) {
System.out.println("Job triggers paused for trigger group:”
+ triggerGroup);
}
public void triggerResumed(TriggerKey triggerKey) {
System.out.println("Job triggers resumed for trigger: " + triggerKey);
}
public void triggersResumed(String triggerGroup) {
System.out.println("Job triggers resumed for trigger group:”
+ triggerGroup);
}
public void jobAdded(JobDetail jobDetail) {
System.out.println("Job added: " + jobDetail.getKey().getName());
}
public void jobDeleted(JobKey jobKey) {
System.out.println("Job deleted: " + jobKey.getName());
}
public void jobPaused(JobKey jobKey) {
System.out.println("Jobs paused for job: " + jobKey);
}
public void jobsPaused(String jobGroup) {
System.out.println("Jobs paused for job group: " + jobGroup);
}
public void jobResumed(JobKey jobKey) {
System.out.println("Job resumed: " + jobKey.getName());
}
public void jobsResumed(String jobGroup) {
System.out.println("Jobs resumed for job group: " + jobGroup);
}
public void schedulerError(String msg, SchedulerException cause) {
System.out.println("Scheduler Error: " + cause);
}
public void schedulerInStandbyMode() {
try {
System.out.println("Scheduler put in standby mode:”
+ scheduler.getSchedulerName());
} catch (SchedulerException e) {
System.out.println("Error getting scheduler name" + e);
}
}
public void schedulerStarted() {
try {
System.out.println("Scheduler started:”
+ scheduler.getSchedulerName());
} catch (SchedulerException e) {
System.out.println("Error getting scheduler name" + e);
}
}
 
public void schedulerShutdown() {
try {
System.out.println("Scheduler shutdown: "
+ scheduler.getSchedulerName());
} catch (SchedulerException e) {
System.out.println("Error getting scheduler name" + e);
}
}
public void schedulerShuttingdown() {
try {
System.out.println("Scheduler shutting down:”
+ scheduler.getSchedulerName());
} catch (SchedulerException e) {
System.out.println("Error getting scheduler name" + e);
}
}
public void schedulingDataCleared() {
try {
System.out.println ("Scheduler data cleared:”
+ scheduler.getSchedulerName());
} catch (SchedulerException e) {
System.out.println("Error getting scheduler name" + e);
}
}
public void schedulerStarting() {
try {
System.out.println ("Scheduler starting:”
+ scheduler.getSchedulerName());
} catch (SchedulerException e) {
System.out.println("Error getting scheduler name" + e);
}
}
}

And updated QuartzTest.java:

package org.sample;
import org.quartz.CronScheduleBuilder;
import org.quartz.JobBuilder;
import org.quartz.JobDataMap;
import org.quartz.JobDetail;
import org.quartz.Scheduler;
import org.quartz.SchedulerException;
import org.quartz.Trigger;
import org.quartz.TriggerBuilder;
import org.quartz.impl.StdSchedulerFactory;
public class QuartzTest {
public static void main(String[] args){
try {
StdSchedulerFactory factory = new StdSchedulerFactory();
Scheduler scheduler =factory.getScheduler();
scheduler.getListenerManager().addSchedulerListener(new MySchedulerListener(scheduler));
scheduler.start();
JobDataMap data = new JobDataMap();
data.put("example", "Hello World job running");
JobDetail jobDetail = JobBuilder.newJob(HelloWorldJob.class).usingJobData(data).withIdentity("HelloWorldJob", "group1").build();
Trigger trigger = TriggerBuilder.newTrigger().withIdentity("myTrigger", "group1")
.startNow()
.withSchedule(CronScheduleBuilder.cronSchedule("0/5 * * * *? *"))
.build();
scheduler.scheduleJob(jobDetail, trigger);
} catch (SchedulerException e) {
e.printStackTrace();
}
}
}

Output:

Scheduler starting: DefaultQuartzScheduler
Scheduler started: DefaultQuartzScheduler
Job added: HelloWorldJob
Job scheduled: myTrigger
Hello World!!
HelloWorldJob start: Wed Jul 27 21:15:05 IST 2016
Example name is: Hello World job running
HelloWorldJob end: -1, key: group1.HelloWorldJob
HelloWorldJob next scheduled time: Wed Jul 27 21:15:10 IST 2016
Hello World!!
HelloWorldJob start: Wed Jul 27 21:15:10 IST 2016
Example name is: Hello World job running
HelloWorldJob end: -1, key: group1.HelloWorldJob
HelloWorldJob next scheduled time: Wed Jul 27 21:15:15 IST 2016
Hello World!!
HelloWorldJob start: Wed Jul 27 21:15:15 IST 2016
Example name is: Hello World job running
HelloWorldJob end: -1, key: group1.HelloWorldJob
HelloWorldJob next scheduled time: Wed Jul 27 21:15:20 IST 2016

Conclusion

Quartz is the framework for scheduling background jobs, it provides two types of triggers (simple, cron) we can implement Job interface to create custom job, we can also create SchedulerListener listener for scheduler. Quartz also have Job Listener for listening different events for jobs.

Aaronjacobson

Aaron Jacobson is experienced and innovative web apps developer at TechnoLigent which are fastest growing company in the IT industry. It has passionate and professional team of developers in various area such as asp .net development, Microsoft dynamics CRM, Java Offshore Development, Hadoop Big Data, Pentaho BI, Oracle, Mobile application development on Android, iOS and other cross platform.

Leave a Reply

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