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.
QuartzJobBean
– Simple implementation of the Quartz Job interface. It will internally callexecuteInternal
.JobDetailFactoryBean
– It is aFactoryBean
for creating a Quartzorg.quartz.JobDetail
. You can configure the job class and the job data using bean-style.SimpleTriggerFactoryBean
– It is aFactoryBean
for creating a Quartzorg.quartz.SimpleTrigger
CronTriggerFactoryBean
– It is aFactoryBean
for creating a Quartzorg.quartz.CronTrigger
SchedulerFactoryBean
– It is aFactoryBean
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.
You can download the full source code of this example here: springQuartzSchedulerExample.zip