Quartz

Spring Quartz Scheduler Example

If your application has tasks that need advance scheduling, for example, a recurring maintenance jobs running every Wednesday at 12:00:00 pm. then Quartz may be your ideal solution. In our Quartz Scheduler Tutorial, we have seen how to setup, create a scheduler factory, scheduler, job, job details, jobDataMap, triggers, and listeners. In this article, we will make use of spring provided utility classes for Quartz to configure jobs and scheduling them.

1. Quartz Setup

In this example, we will use Maven as the build tool so all you need to do is add the below dependency to pom.xml which looks like below.

pom.xml:

<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
	<modelVersion>4.0.0</modelVersion>
	<groupId>com.javacodegeeks.camel</groupId>
	<artifactId>springQuartzScheduler</artifactId>
	<version>0.0.1-SNAPSHOT</version>
	<dependencies>
		<dependency>
			<groupId>org.quartz-scheduler</groupId>
			<artifactId>quartz</artifactId>
			<version>2.2.1</version>
		</dependency>
		<dependency>
			<groupId>org.springframework</groupId>
			<artifactId>spring-context</artifactId>
			<version>4.1.5.RELEASE</version>
		</dependency>
		<dependency>
			<groupId>org.springframework</groupId>
			<artifactId>spring-context-support</artifactId>
			<version>4.1.5.RELEASE</version>
		</dependency>
	</dependencies>
</project>

2. Spring provided Quartz support class

Spring provides the following classes.

  1. QuartzJobBean – Simple implementation of the Quartz Job interface. It will internally call executeInternal.
  2. JobDetailFactoryBean – It is a FactoryBean for creating a Quartz org.quartz.JobDetail. You can configure the job class and the job data using bean-style.
  3. SimpleTriggerFactoryBean – It is a FactoryBean for creating a Quartz org.quartz.SimpleTrigger
  4. CronTriggerFactoryBean – It is a FactoryBean for creating a Quartz org.quartz.CronTrigger
  5. SchedulerFactoryBean – It is a FactoryBean for creating the Quartz {@link org.quartz.Scheduler}

We will see in our next section our to configure a job and schedule it.

3. Creating and Configuring the Job

We need to first create a Quartz job that defines the job to be executed. For that, we’ll subclass Spring’s QuartzJobBean and implement executeInternal() method where we will define the actions that we want the job to execute.

MyJob:

package com.javacodegeeks.quartz;

import org.quartz.JobDetail;
import org.quartz.JobExecutionContext;
import org.quartz.JobExecutionException;
import org.springframework.scheduling.quartz.QuartzJobBean;

public class MyJob extends QuartzJobBean {
	private static int count;

	@Override
	protected void executeInternal(JobExecutionContext jobContext)
			throws JobExecutionException {
		System.out.println("--------------------------------------------------------------------");
		System.out.println("MyJob start: " + jobContext.getFireTime());
		JobDetail jobDetail = jobContext.getJobDetail();
		MyJobHelper jobHelper = (MyJobHelper) jobDetail.getJobDataMap().get("jobState");
		System.out.println("Example name is: " + jobHelper.getSomeStr());		
		System.out.println("MyJob end: " + jobContext.getJobRunTime() + ", key: " + jobDetail.getKey());
		System.out.println("MyJob next scheduled time: " + jobContext.getNextFireTime());
		System.out.println("--------------------------------------------------------------------");
		
		count++;
		System.out.println("Job count " + count);
		
		ILatch latch = (ILatch) jobDetail.getJobDataMap().get("jobLatch");
		if (latch != null) {
			latch.countDown();
			System.out.println("Job executed, release latch");
		}
	}

}

We configure the below class in spring and provide it in the job data.

MyJobHelper:

package com.javacodegeeks.quartz;

public class MyJobHelper {
	private String someStr;
	
	public MyJobHelper(String s) {
		this.someStr = s;
	}

	public String getSomeStr() {
		return someStr;
	}		
}

We also need a job latch, for the sake of this example so that it can run for a predefined times.

ILatch:

package com.javacodegeeks.quartz;

public interface ILatch {
	void countDown();
}

Now we will configure our job in spring. Note that we won’t be configuring the job class as it is but instead we will configure JobDetailFactoryBean bean and then feed the Job implementation using the jobClass property. Next, we provide the job data using JobDetail’s jobDataAsMap property takes a java.util.Map. Our map contains two values, each one a reference to existing bean.

springQuartzSchedulerContext.xml:

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">

	<bean name="someJob" class="org.springframework.scheduling.quartz.JobDetailFactoryBean">
		<property name="jobClass" value="com.javacodegeeks.quartz.MyJob" />
		<property name="jobDataAsMap">
			<map>
				<entry key="jobState" value-ref="jobDataBean" />
				<entry key="jobLatch" value-ref="jobLatch" />
			</map>
		</property>
	</bean>
	
	<bean name="jobLatch" class="com.javacodegeeks.quartz.SpringQuartzSchedulerExample"/>

	<bean name="jobDataBean" class="com.javacodegeeks.quartz.MyJobHelper">
		<constructor-arg index="0">
			<value>Spring Quartz Example</value>
		</constructor-arg>
	</bean>
</beans>

Each time a job is run, it will release the latch. Once the latch is totally released, scheduler will shutdown.

SpringQuartzSchedulerExample:

package com.javacodegeeks.quartz;

import java.util.concurrent.CountDownLatch;

import org.springframework.context.support.ClassPathXmlApplicationContext;

public class SpringQuartzSchedulerExample implements ILatch {
	private int repeatCount = 3;
	private CountDownLatch latch;
	
	public SpringQuartzSchedulerExample() {
		System.out.println("Create count down latch for 3");
		latch = new CountDownLatch(repeatCount + 1);
	}

	public void countDown() {
		latch.countDown();
	}

	public void waitTillJobsExecute() throws InterruptedException {
		latch.await();
	}
}

4. Scheduling Job

Now that the job is defined, we will have to schedule it. We would like to first configure the simple trigger first. We will define the SimpleTriggerFactoryBean which in turn will create Quartz’s org.quartz.Trigger. We will specify the interval job will wait between two runs and how many times the job will repeat.

springQuartzSchedulerContext.xml:

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">

	<bean name="someJob" class="org.springframework.scheduling.quartz.JobDetailFactoryBean">
		<property name="jobClass" value="com.javacodegeeks.quartz.MyJob" />
		<property name="jobDataAsMap">
			<map>
				<entry key="jobState" value-ref="jobDataBean" />
				<entry key="jobLatch" value-ref="jobLatch" />
			</map>
		</property>
	</bean>
	
	<bean name="jobLatch" class="com.javacodegeeks.quartz.SpringQuartzSchedulerExample"/>

	<bean name="jobDataBean" class="com.javacodegeeks.quartz.MyJobHelper">
		<constructor-arg index="0">
			<value>Spring Quartz Example</value>
		</constructor-arg>
	</bean>

	<bean id="jobTrigger"
		class="org.springframework.scheduling.quartz.SimpleTriggerFactoryBean">
		<property name="repeatInterval" value="1000" />
		<property name="repeatCount" value="3" />
		<property name="jobDetail" ref="someJob" />
	</bean>
</beans>

5. Starting the Job

We have created a job, scheduled it, its the time to start the job. In order to start a Quartz job, we’ll use Spring’s SchedulerFactoryBean. We will use triggers property to set the references to trigger beans. In our case just one simple trigger jobTrigger.

springQuartzSchedulerContext.xml:

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">

	<bean name="someJob" class="org.springframework.scheduling.quartz.JobDetailFactoryBean">
		<property name="jobClass" value="com.javacodegeeks.quartz.MyJob" />
		<property name="jobDataAsMap">
			<map>
				<entry key="jobState" value-ref="jobDataBean" />
				<entry key="jobLatch" value-ref="jobLatch" />
			</map>
		</property>
	</bean>
	
	<bean name="jobLatch" class="com.javacodegeeks.quartz.SpringQuartzSchedulerExample"/>

	<bean name="jobDataBean" class="com.javacodegeeks.quartz.MyJobHelper">
		<constructor-arg index="0">
			<value>Spring Quartz Example</value>
		</constructor-arg>
	</bean>

	<bean id="jobTrigger"
		class="org.springframework.scheduling.quartz.SimpleTriggerFactoryBean">
		<property name="repeatInterval" value="1000" />
		<property name="repeatCount" value="3" />
		<property name="jobDetail" ref="someJob" />
	</bean>

	<bean class="org.springframework.scheduling.quartz.SchedulerFactoryBean">
		<property name="triggers">
			<list>
				<ref bean="jobTrigger" />
			</list>
		</property>
	</bean>
</beans>

Let’s now run the example. We will load the spring context and it will automatically start the quartz scheduler. Next, we wait till the jobs are run.

SpringQuartzSchedulerExample:

package com.javacodegeeks.quartz;

import java.util.concurrent.CountDownLatch;

import org.springframework.context.support.ClassPathXmlApplicationContext;

public class SpringQuartzSchedulerExample implements ILatch {
	private int repeatCount = 3;
	private CountDownLatch latch;
	
	public SpringQuartzSchedulerExample() {
		System.out.println("Create count down latch for 3");
		latch = new CountDownLatch(repeatCount + 1);
	}

	public static void main(String[] args) throws Exception {
		ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext(
				"springQuartzSchedulerContext.xml");
		try {
			SpringQuartzSchedulerExample example = (SpringQuartzSchedulerExample) context
					.getBean("jobLatch");
			example.waitTillJobsExecute();
			System.out.println("All triggers executed. Shutdown scheduler");
		} finally {
			context.close();
		}
	}

	public void countDown() {
		latch.countDown();
	}

	public void waitTillJobsExecute() throws InterruptedException {
		latch.await();
	}
}

Output:

Sep 22, 2015 5:42:40 PM org.springframework.context.support.ClassPathXmlApplicationContext prepareRefresh
INFO: Refreshing org.springframework.context.support.ClassPathXmlApplicationContext@1a6c5a9e: startup date [Tue Sep 22 17:42:40 IST 2015]; root of context hierarchy
Sep 22, 2015 5:42:40 PM org.springframework.beans.factory.xml.XmlBeanDefinitionReader loadBeanDefinitions
INFO: Loading XML bean definitions from class path resource [springQuartzSchedulerContext.xml]
Create count down latch for 3
SLF4J: Failed to load class "org.slf4j.impl.StaticLoggerBinder".
SLF4J: Defaulting to no-operation (NOP) logger implementation
SLF4J: See http://www.slf4j.org/codes.html#StaticLoggerBinder for further details.
Sep 22, 2015 5:42:40 PM org.springframework.context.support.DefaultLifecycleProcessor start
INFO: Starting beans in phase 2147483647
Sep 22, 2015 5:42:40 PM org.springframework.scheduling.quartz.SchedulerFactoryBean startScheduler
INFO: Starting Quartz Scheduler now
--------------------------------------------------------------------
MyJob start: Tue Sep 22 17:42:40 IST 2015
Example name is: Spring Quartz Example
MyJob end: -1, key: DEFAULT.someJob
MyJob next scheduled time: Tue Sep 22 17:42:41 IST 2015
--------------------------------------------------------------------
Job count 1
Job executed, release latch
--------------------------------------------------------------------
MyJob start: Tue Sep 22 17:42:41 IST 2015
Example name is: Spring Quartz Example
MyJob end: -1, key: DEFAULT.someJob
MyJob next scheduled time: Tue Sep 22 17:42:42 IST 2015
--------------------------------------------------------------------
Job count 2
Job executed, release latch
--------------------------------------------------------------------
MyJob start: Tue Sep 22 17:42:42 IST 2015
Example name is: Spring Quartz Example
MyJob end: -1, key: DEFAULT.someJob
MyJob next scheduled time: Tue Sep 22 17:42:43 IST 2015
--------------------------------------------------------------------
Job count 3
Job executed, release latch
--------------------------------------------------------------------
MyJob start: Tue Sep 22 17:42:43 IST 2015
Example name is: Spring Quartz Example
MyJob end: -1, key: DEFAULT.someJob
MyJob next scheduled time: null
--------------------------------------------------------------------
Job count 4
Job executed, release latch
All triggers executed. Shutdown scheduler
Sep 22, 2015 5:42:43 PM org.springframework.context.support.ClassPathXmlApplicationContext doClose
INFO: Closing org.springframework.context.support.ClassPathXmlApplicationContext@1a6c5a9e: startup date [Tue Sep 22 17:42:40 IST 2015]; root of context hierarchy
Sep 22, 2015 5:42:43 PM org.springframework.context.support.DefaultLifecycleProcessor stop
INFO: Stopping beans in phase 2147483647
Sep 22, 2015 5:42:43 PM org.springframework.scheduling.quartz.SchedulerFactoryBean destroy
INFO: Shutting down Quartz Scheduler

6. Configuring a Cron Job

We have seen an example of simple trigger. We will now configure CronTriggerFactoryBean to create a Quartz CronTrigger. We will use this trigger class to have more control on when the job will run. It follows unix cron tool style of expression. We will set the cron expression to specify exact times (and days) that a job will run.

The cron expression is set using cronExpression. In our example, we want the job to run every 2 seconds.

<property name="cronExpression" value="0/2 * * * * ?" />

springQuartzSchedulerCronContext.xml:

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">

	<bean name="someJob"
		class="org.springframework.scheduling.quartz.JobDetailFactoryBean">
		<property name="jobClass" value="com.javacodegeeks.quartz.MyJob" />
		<property name="jobDataAsMap">
			<map>
				<entry key="jobState" value-ref="jobDataBean" />
			</map>
		</property>
	</bean>

	<bean name="jobDataBean" class="com.javacodegeeks.quartz.MyJobHelper">
		<constructor-arg index="0">
			<value>Spring Quartz Example</value>
		</constructor-arg>
	</bean>

	<bean class="org.springframework.scheduling.quartz.SchedulerFactoryBean">
		<property name="triggers">
			<list>
				<ref bean="cronTrigger" />
			</list>
		</property>
	</bean>

	<bean id="cronTrigger" class="org.springframework.scheduling.quartz.CronTriggerFactoryBean">
		<property name="jobDetail" ref="someJob" />
		<property name="cronExpression" value="0/2 * * * * ?" />
	</bean>
</beans>

Simply load the context and it will start the scheduler. We call Thread.sleep(6000) to let the scheduler run for 6 seconds.

SpringQuartzCronExample:

package com.javacodegeeks.quartz;

import org.springframework.context.support.ClassPathXmlApplicationContext;

public class SpringQuartzCronExample {
	
	public static void main(String[] args) throws Exception {
		ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext(
				"springQuartzSchedulerCronContext.xml");
		try {
		Thread.sleep(6000);
		} finally {
			context.close();
		}
	}
}

We have set the cron expression so that the job can run every 2 seconds. Since our scheduler runs for 6 seconds, the job fires three times.

Output:

Sep 22, 2015 6:09:41 PM org.springframework.context.support.ClassPathXmlApplicationContext prepareRefresh
INFO: Refreshing org.springframework.context.support.ClassPathXmlApplicationContext@1a6c5a9e: startup date [Tue Sep 22 18:09:41 IST 2015]; root of context hierarchy
Sep 22, 2015 6:09:41 PM org.springframework.beans.factory.xml.XmlBeanDefinitionReader loadBeanDefinitions
INFO: Loading XML bean definitions from class path resource [springQuartzSchedulerCronContext.xml]
SLF4J: Failed to load class "org.slf4j.impl.StaticLoggerBinder".
SLF4J: Defaulting to no-operation (NOP) logger implementation
SLF4J: See http://www.slf4j.org/codes.html#StaticLoggerBinder for further details.
Sep 22, 2015 6:09:41 PM org.springframework.context.support.DefaultLifecycleProcessor start
INFO: Starting beans in phase 2147483647
Sep 22, 2015 6:09:41 PM org.springframework.scheduling.quartz.SchedulerFactoryBean startScheduler
INFO: Starting Quartz Scheduler now
--------------------------------------------------------------------
MyJob start: Tue Sep 22 18:09:42 IST 2015
Example name is: Spring Quartz Example
MyJob end: -1, key: DEFAULT.someJob
MyJob next scheduled time: Tue Sep 22 18:09:44 IST 2015
--------------------------------------------------------------------
Job count 1
--------------------------------------------------------------------
MyJob start: Tue Sep 22 18:09:44 IST 2015
Example name is: Spring Quartz Example
MyJob end: -1, key: DEFAULT.someJob
MyJob next scheduled time: Tue Sep 22 18:09:46 IST 2015
--------------------------------------------------------------------
Job count 2
--------------------------------------------------------------------
MyJob start: Tue Sep 22 18:09:46 IST 2015
Example name is: Spring Quartz Example
MyJob end: -1, key: DEFAULT.someJob
MyJob next scheduled time: Tue Sep 22 18:09:48 IST 2015
--------------------------------------------------------------------
Job count 3
Sep 22, 2015 6:09:47 PM org.springframework.context.support.ClassPathXmlApplicationContext doClose
INFO: Closing org.springframework.context.support.ClassPathXmlApplicationContext@1a6c5a9e: startup date [Tue Sep 22 18:09:41 IST 2015]; root of context hierarchy
Sep 22, 2015 6:09:47 PM org.springframework.context.support.DefaultLifecycleProcessor stop
INFO: Stopping beans in phase 2147483647
Sep 22, 2015 6:09:47 PM org.springframework.scheduling.quartz.SchedulerFactoryBean destroy
INFO: Shutting down Quartz Scheduler

7. Invoking Methods

If your requirement is just to schedule a single method call, you can do that without writing a separate QuartzJobBean class. You need to configure spring provided MethodInvokingJobDetailFactoryBean, set the bean name and method to be executed using targetObject and targetMethod.

TestBean:

package com.javacodegeeks.quartz;

public class TestBean {
	public void doSomething() {
		System.out.println("doSomething Called");
	}
}

springQuartzMethodInvocationContext.xml:

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">

	<bean class="org.springframework.scheduling.quartz.SchedulerFactoryBean" />

	<bean class="org.springframework.scheduling.quartz.SchedulerAccessorBean">
		<property name="triggers">
			<list>
				<ref bean="methodInvocationTrigger" />
			</list>
		</property>
	</bean>

	<bean id="methodInvocationTrigger"
		class="org.springframework.scheduling.quartz.SimpleTriggerFactoryBean">
		<property name="jobDetail" ref="methodInvocationTask" />
		<property name="repeatInterval" value="1000" />
		<property name="repeatCount" value="3" />
	</bean>


	<bean name="testBean" class="com.javacodegeeks.quartz.TestBean" />

	<bean id="methodInvocationTask"
		class="org.springframework.scheduling.quartz.MethodInvokingJobDetailFactoryBean">
		<property name="targetObject" ref="testBean" />
		<property name="targetMethod" value="doSomething" />
	</bean>

</beans>

SpringQuartzMethodInvocationExample:

package com.javacodegeeks.quartz;

import org.springframework.context.support.ClassPathXmlApplicationContext;

public class SpringQuartzMethodInvocationExample {

	public static void main(String[] args) throws Exception {
		ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext(
				"springQuartzMethodInvocationContext.xml");
		try {
			Thread.sleep(3000);
		} finally {
			context.close();
		}
	}
}

8. Download the Eclipse Project

This was an example about Spring Quartz Scheduler.

Download
You can download the full source code of this example here: springQuartzSchedulerExample.zip

Ram Mokkapaty

Ram holds a master's degree in Machine Design from IT B.H.U. His expertise lies in test driven development and re-factoring. He is passionate about open source technologies and actively blogs on various java and open-source technologies like spring. He works as a principal Engineer in the logistics domain.
Subscribe
Notify of
guest

This site uses Akismet to reduce spam. Learn how your comment data is processed.

0 Comments
Inline Feedbacks
View all comments
Back to top button