Interview Questions

Spring Quartz Scheduler

In this article lets discuss about using quartz scheduler in spring application.

Quartz is an API used to schedule jobs in Java application. Consider a scenario, where it is required to execute a particular activity at 10 AM everyday. We can have a java method to call that particular functionality and configure quartz api to schedule the job. We can specify the schedule using a Cron expression.

What is a Cron Expression?

A Cron-Expression is a string consisting of 6 to 7 fields separated by white space.
The 6 mandatory and 1 optional fields are as follows:

Field Name Allowed Values Allowed Special Characters
Seconds 0-59 , - * /
Minutes 0-59 , - * /
Hours 0-23 , - * /
Day-of-month 1-31 , - * ? / L W C
Month 1-12 or JAN-DEC , - * /
Day-of-Week 1-7 or SUN-SAT , - * ? / L C #
Year (Optional) empty, 1970-2099 , - * /

For example, 0 0/15 * * * * expression will schedule the job to run every 15 minutes.
0 15 10 * * * expression will schedule the run the job at 10:15 AM everyday.

Lets take an example of scheduling a job has to be run every day at 12:15 AM.

First of all we need to create a job class which executes the job.

TestQuartzJob Class

package in.techdive.spring.quartz;

import org.quartz.JobExecutionContext;
import org.quartz.JobExecutionException;
import org.quartz.StatefulJob;

public class TestQuartzJob implements StatefulJob
{
        public TestQuartzJob()
        {
        }

        @Override
        public void execute(JobExecutionContext arg0) throws JobExecutionException
        {
                System.out.println("Job is executed by the scheduler");
        }
}

This class implements StatefulJob interface , so that if there are two Cron triggers executing the same job, they don't interfere with each other.

Next we need to create ApplicationJobScheduler class as follows.

ApplicationJobScheduler Class

package in.techdive.spring.quartz;

import java.text.ParseException;
import java.util.Date;

import org.quartz.CronExpression;
import org.quartz.CronTrigger;
import org.quartz.JobDetail;
import org.quartz.SchedulerException;
import org.quartz.impl.StdScheduler;

public class ApplicationJobScheduler
{

        private StdScheduler    stdScheduler;

        private String       cronExp;

        public StdScheduler getStdScheduler()
        {
                return stdScheduler;
        }

        public void setStdScheduler(StdScheduler stdScheduler)
        {
                this.stdScheduler = stdScheduler;
        }

        public String getCronExp()
        {
                return cronExp;
        }

        public void setCronExp(String cronExp)
        {
                this.cronExp = cronExp;
        }

        public ApplicationJobScheduler()
        {
        }

        public void init()
        {
                // Create a CronExpression object from the cronExp string set through injection

                CronExpression cexp = null;
                try
                {
                        cexp = new CronExpression(cronExp);
                        Date date = new Date(Long.parseLong(String.valueOf(System.currentTimeMillis())));
                        if (cexp != null && cexp.getNextValidTimeAfter(date) == null)
                        {
                                cexp = null;

                        }
                }
                catch (ParseException prse)
                {

                }

                // The scheduler will have jobdetail and triggers(cron triggers) associated with it.
                // Try getting the testJobDetail if it is already registered with the scheduler
                JobDetail testJobDetail = null;
                try
                {
                        testJobDetail = stdScheduler.getJobDetail("testJobDetail", "jobDetailGroup");
                }
                catch (SchedulerException e1)
                {
                        e1.printStackTrace();
                }
                // If we dont get any jobdetail, we will add new jobdetail
                if (testJobDetail == null)
                {

                        System.out.println(" Creating new Job Detail for test quartz job");

                        testJobDetail = new JobDetail("testJobDetail", "jobDetailGroup", TestQuartzJob.class);

                        // After creating jobDetail, create a crontrigger and set the cron expression
                        CronTrigger testCronTrigger = new CronTrigger("testCronTrigger", "DEFAULT");
                        try
                        {
                                if (cexp != null)
                                {
                                        testCronTrigger.setCronExpression(cexp);
                                        // scheduling the job using jobdetail and crontrigger
                                        stdScheduler.scheduleJob(testJobDetail, testCronTrigger);
                                }
                        }
                        catch (SchedulerException se)
                        {
                                System.out.println("Unable to schedule the services job. " + se);
                        }
                }
                else
                // else then reschedule , if cron expn is updated, then the updated cron expn will be used
                {
                        try
                        {
                                CronTrigger testCronTrigger = (CronTrigger) stdScheduler.getTrigger(
                                "testCronTrigger", "DEFAULT");
                                if (cexp != null && cexp.getCronExpression() != null)
                                {
                                        if (!cexp.getCronExpression().equals(testCronTrigger.getCronExpression()))
                                        {

                                                System.out
                                                .println(" The job Detail for test already exists, so rescheduling the same");

                                                CronTrigger cloneTrigger = (CronTrigger) testCronTrigger.clone();
                                                cloneTrigger.setCronExpression(cexp);
                                                stdScheduler.rescheduleJob("servicesCronTrigger", "DEFAULT", cloneTrigger);
                                        }
                                }
                        }
                        catch (SchedulerException e)
                        {
                                System.out.println("Error setting time for test job scheduler" + e);
                        }
                }

                // Start the scheduler using the start method
                try
                {
                        stdScheduler.start();
                }
                catch (SchedulerException e)
                {
                        System.out.println("Error scheduling test job " + e);
                }
        }
}

This class uses quartz StdScheduler class to schedule our job. To make the scheduler work, We need to create a job detail bean and a Cron trigger bean. Then the two beans need to be registered with the scheduler.

Finally we need to start the scheduler using its start() method. This functionality is present in the init() method. The init() method will be called when the first time the bean is created.

The ApplicationJobScheduler class has two properties cronExp and stdScheduler. These properties are set in the bean configuration file as follows.

quarts-context.xml

<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:util="http://www.springframework.org/schema/util"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.0.xsd http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util-2.0.xsd" >
<bean id="testJob" class="in.techdive.spring.quartz.TestQuartzJob">
</bean>
<bean id="applJobScheduler" class="in.techdive.spring.quartz.ApplicationJobScheduler" init-method="init">
<property name="stdScheduler" ref="sts" />
<property name="cronExp" value="0 15 12 * * ? " />
</bean>
<bean id="sts" class="org.springframework.scheduling.quartz.SchedulerFactoryBean">
<property name="overwriteExistingJobs" value="true" />
</bean>
</beans>

Finally lets create a main class to test whether the scheduler is executing our job or not.

QuartzJobSimulator Class

package in.techdive.spring.quartz;

import org.springframework.context.ApplicationContext;
import org.springframework.context.support.FileSystemXmlApplicationContext;

public class QuartzJobSimulator
{
        public QuartzJobSimulator()
        {
        }

        public static void main(String[] args)
        {
                ApplicationContext ctx = new FileSystemXmlApplicationContext("src\\quartz-context.xml");
        }
}

Output
The output is as follows

Creating new Job Detail for test quartz job

Job is executed by the scheduler

The above job will continue to run every day at 12:15 AM till the application is up. In this way quartz scheduler can be used in a spring application.