Spring Quartz Tutorial

In this post I’ll show you how to use Quartz scheduling with a Spring application. Quartz can be easily integrated with Spring to invoke schedule tasks that call your existing business logic. One of the things I really like about Springs support for Quartz is that you don’t have to write any Quartz specific code or amend your existing code to use Quartz scheduling. You simply configure Quartz alongside your existing bean definitions and point it at the logic that you’d like to invoke.

Tech Stack

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

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()); 
  } 
}

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:context="http://www.springframework.org/schema/context" 
       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-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...

Wrapping Up

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.