Technologies Used
Our Spring/Quartz example will be built as a stand alone
Java application using the following.
- Spring 3.1
- Maven
- Quartz 1.6
It is assumed that readers have a basic knowledge of Spring and Maven. To help you
get an example running quickly I’ll add a link to the full source code at the bottom of this post. Once you download the Maven project simply
import it into Eclipse and run a Maven build.
Creating a simple Project
Creating a simple Project
We'll start off by creating a very simple Spring project like the one shown in figure 1.0 below.
![]() |
| Figure 1.0 - Project Structure |
The main project components are described below.
- src/main/java folder - this folder
structure will contain our Java source. We'll have just 2 classes -
TestsService.java and RunTest.java, both of which are defined below.
- src/main/resources folder - this folder
will contain our Spring and log4J configuration files.
Creating a Simple Service
We'll start off by creating a class containing a simple service method that we'll
invoke with Quartz. The testServiceMethod method is very simple indeed and just
logs out the current time - this will allow us to see exactly when the method is invoked by
Quartz. The important thing to note is that this code is not coupled to Quartz
in any way - it's a simple POJO. This means that you can integrate Quartz with your application and configure it to invoke existing code without making
any changes to the code whatsoever. The service class is defined below.
package com.blog.samples.quartz;
import java.util.Date;
import org.apache.log4j.Logger;
import org.springframework.stereotype.Service;
@Service
public class TestService
{
private static final Logger logger_c = Logger.getLogger(TestService.class);
public void testServiceMethod()
{
Date date = new Date();
logger_c.debug("test service method invoked: " + date.toString());
}
}
Defining Spring/Quartz Configuration
Next we'll look at the Spring configuration and setting up Quartz in
particular. I've explained the various bean configurations with comments below.
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-3.0.xsd">
<!--
Our test service bean
-->
<bean id="testService" class="com.blog.samples.quartz.TestService"/>
<!--
Job Detail bean configuration specifies the target object (our service object defined above)
and the method we want to invoke on that object (testServiceMethod). The concurrent property
specifies whether or not multiple instances of this job can be invoked concurrently
-->
<bean id="jobDetail" class="org.springframework.scheduling.quartz.MethodInvokingJobDetailFactoryBean">
<property name="targetObject" ref="testService" />
<property name="targetMethod" value="testServiceMethod" />
<property name="concurrent" value="false" />
</bean>
<!--
The cron trigger bean allows us to specify the job that we want to invoke (jobDetail above)
and a cron expression that defines when the job should be invoked. My configuration below
will be invoked every 10 seconds
-->
<bean id="cronTrigger" class="org.springframework.scheduling.quartz.CronTriggerBean">
<property name="jobDetail" ref="jobDetail" />
<property name="cronExpression" value="0,10,20,30,40,50 * * * * ?" />
</bean>
<!--
The SchedulerFactoryBean takes a list of cron triggers - our example has just one
cron trigger but larger enterprise applications will typically have a number of different
cron trigger for different jobs. The quartz properties property allows you to specify
some specific quartz properties. In our simple example we tell Quartz not to check for updates
-->
<bean class="org.springframework.scheduling.quartz.SchedulerFactoryBean">
<property name="triggers">
<list>
<ref bean="cronTrigger" />
</list>
</property>
<property name="quartzProperties">
<props>
<prop key="org.quartz.scheduler.skipUpdateCheck">true</prop>
</props>
</property>
</bean>
</beans>
Creating a Test Class
Next we'll create a test class to run our Quartz application. This is a simple class
with a main method that loads the spring application context from the
classpath. Once the application context is loaded we sleep the thread for 100
seconds so as to allow the Quartz scheduler to run and invoke our service
method. Our Quartz cron definition was set up to run every 10 seconds so we
should see our service method called 10 times before our application context is
closed and the application shuts down. Our test class is defined below.
package com.blog.samples.quartz;
import org.apache.log4j.Logger;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class RunTest
{
private static final Logger logger_c = Logger.getLogger(RunTest.class);
public static void main (String [] args)
{
logger_c.debug("loading spring application context");
ClassPathXmlApplicationContext applicationContext = new ClassPathXmlApplicationContext("classpath:spring-config.xml");
try
{
/* sleep thread */
Thread.sleep(100000);
}
catch (InterruptedException ex)
{
logger_c.debug(ex);
}
/* close down spring application context */
applicationContext.stop();
logger_c.debug("exiting...");
}
}
Running the Application
When we run the main method above we see the following output to the console.You'll see that our service method is invoked by Quartz exactly every 10 seconds as expected.
DEBUG: [Sep-02 13:27:58,334] samples.quartz.RunTest - loading spring application context
INFO : [Sep-02 13:27:58,578] context.support.ClassPathXmlApplicationContext - Refreshing org.springframework.context.support.ClassPathXmlApplicationContext@314c194d: startup date [Sun Sep 02 13:27:58 BST 2012]; root of context hierarchy
INFO : [Sep-02 13:27:59,552] quartz.core.QuartzScheduler - Quartz Scheduler v.1.6.0 created.
INFO : [Sep-02 13:27:59,556] quartz.simpl.RAMJobStore - RAMJobStore initialized.
INFO : [Sep-02 13:27:59,556] quartz.impl.StdSchedulerFactory - Quartz scheduler 'org.springframework.scheduling.quartz.SchedulerFactoryBean#0' initialized from an externally provided properties instance.
INFO : [Sep-02 13:27:59,556] quartz.impl.StdSchedulerFactory - Quartz scheduler version: 1.6.0
INFO : [Sep-02 13:27:59,560] quartz.core.QuartzScheduler - JobFactory set to: org.springframework.scheduling.quartz.AdaptableJobFactory@5773ec72
INFO : [Sep-02 13:27:59,578] context.support.DefaultLifecycleProcessor - Starting beans in phase 2147483647
INFO : [Sep-02 13:27:59,579] scheduling.quartz.SchedulerFactoryBean - Starting Quartz Scheduler now
INFO : [Sep-02 13:27:59,579] quartz.core.QuartzScheduler - Scheduler org.springframework.scheduling.quartz.SchedulerFactoryBean#0_$_NON_CLUSTERED started.
DEBUG: [Sep-02 13:28:00,048] samples.quartz.TestService - test service method invoked: Sun Sep 02 13:28:00 BST 2012
DEBUG: [Sep-02 13:28:10,024] samples.quartz.TestService - test service method invoked: Sun Sep 02 13:28:10 BST 2012
DEBUG: [Sep-02 13:28:20,018] samples.quartz.TestService - test service method invoked: Sun Sep 02 13:28:20 BST 2012
DEBUG: [Sep-02 13:28:30,014] samples.quartz.TestService - test service method invoked: Sun Sep 02 13:28:30 BST 2012
DEBUG: [Sep-02 13:28:40,021] samples.quartz.TestService - test service method invoked: Sun Sep 02 13:28:40 BST 2012
DEBUG: [Sep-02 13:28:50,013] samples.quartz.TestService - test service method invoked: Sun Sep 02 13:28:50 BST 2012
DEBUG: [Sep-02 13:29:00,020] samples.quartz.TestService - test service method invoked: Sun Sep 02 13:29:00 BST 2012
DEBUG: [Sep-02 13:29:10,020] samples.quartz.TestService - test service method invoked: Sun Sep 02 13:29:10 BST 2012
DEBUG: [Sep-02 13:29:20,027] samples.quartz.TestService - test service method invoked: Sun Sep 02 13:29:20 BST 2012
DEBUG: [Sep-02 13:29:30,018] samples.quartz.TestService - test service method invoked: Sun Sep 02 13:29:30 BST 2012
INFO : [Sep-02 13:29:39,583] context.support.DefaultLifecycleProcessor - Stopping beans in phase 2147483647
INFO : [Sep-02 13:29:39,587] quartz.core.QuartzScheduler - Scheduler org.springframework.scheduling.quartz.SchedulerFactoryBean#0_$_NON_CLUSTERED paused.
DEBUG: [Sep-02 13:29:39,590] samples.quartz.RunTest - exiting...
Conclusion
The sample code in this tutorial is very simple indeed, but should
provide you with the information required to get Quartz scheduling up and
running within a Spring application. If you want to run this tutorial
locally you can grab the full source code here https://docs.google.com/open?id=0B_SZOyniHfc1M0FDeW4tT0J1SWs.

Hi,
ReplyDeleteIt is posible deploy a spring-quartz project in a cluster mode?
The cluster has many nodes and each node is in a different location (is not possible to synchronize the clocks of the machines).
http://quartz-scheduler.org/documentation/quartz-2.x/configuration/ConfigJDBCJobStoreClustering
Yes it is possible to set Quartz up in a cluster. I actually did this a few weeks ago in work. We had to deploy our solution onto a WebSphere cluster and to ensure that each Quartz job was only invoked on one node at any given time, I used a JDBCJobStore instead of the standard in memory job store(RAMJobStore). This means that the invocation of Quartz jobs are managed centrally in the database as opposed to be being managed in memory by each node in the cluster.
ReplyDeleteIn very simple terms, Quartz cluster support works by placing a lock on a Job in the database when the first node attempts to process that job. Subsequent nodes are not allowed to invoke the same the job as it is already being processed. This simple mechanism ensures that only one node in your cluster processes a scheduled task. This is the kind of behaviour we wanted in a cluster but I'm sure that other approaches can be taken. If you'd like I can put together a quick post on setting up Quartz in a cluster?
Could you please provide a sample configuration for JDBCJobStore in spring? Thanks
DeleteSource now available on gitub at
ReplyDeletehttps://github.com/briansjavablog/spring-quartz-tutorial
Hi Brian,
ReplyDeleteI have Quartz clustered with 2 nodes, running on 2 different machines.
Both running as standalone programs.
I'm using JDBCJobStore.
org.quartz.jobStore.class=org.quartz.impl.jdbcjobstore.JobStoreTX
org.quartz.jobStore.driverDelegateClass=org.quartz.impl.jdbcjobstore.DB2v7Delegate
I have a job scheduled to run every 20 mins. (0 0/20 * * * ?) with cron trigger.
Surprisingly, The job gets triggered twice given time interval. Once on each node, randomly
It has run twice at these times yesterday. 8:20, 10:40, 14:20,20:00, 20:40.
Please let me know, is there anything wrong?
Thank you for providing this sample. Most other Spring Quartz samples are way too complicated for me.
ReplyDeletethank u so much for providing these examples.....
ReplyDeleteThanks for the example, how can we use quartz and activemq together. I just want send message based on some crone time.
ReplyDeleteCombing Quartz with JMS is pretty straight forward. You'll want to use the MethodInvokingJobDetailFactoryBean, that's the jobDetail bean in this tutorial, to invoke the JMSTemplate described in my JMS ActiveMQ tutorial. You should be able to get this functionality up and running very quickly by combining the code samples from both tutorials.
DeleteThanks for the example and detailed comments.
ReplyDelete