DZone
Thanks for visiting DZone today,
Edit Profile
  • Manage Email Subscriptions
  • How to Post to DZone
  • Article Submission Guidelines
Sign Out View Profile
  • Post an Article
  • Manage My Drafts
Over 2 million developers have joined DZone.
Log In / Join
Refcards Trend Reports
Events Video Library
Refcards
Trend Reports

Events

View Events Video Library

Zones

Culture and Methodologies Agile Career Development Methodologies Team Management
Data Engineering AI/ML Big Data Data Databases IoT
Software Design and Architecture Cloud Architecture Containers Integration Microservices Performance Security
Coding Frameworks Java JavaScript Languages Tools
Testing, Deployment, and Maintenance Deployment DevOps and CI/CD Maintenance Monitoring and Observability Testing, Tools, and Frameworks
Culture and Methodologies
Agile Career Development Methodologies Team Management
Data Engineering
AI/ML Big Data Data Databases IoT
Software Design and Architecture
Cloud Architecture Containers Integration Microservices Performance Security
Coding
Frameworks Java JavaScript Languages Tools
Testing, Deployment, and Maintenance
Deployment DevOps and CI/CD Maintenance Monitoring and Observability Testing, Tools, and Frameworks

Last call! Secure your stack and shape the future! Help dev teams across the globe navigate their software supply chain security challenges.

Modernize your data layer. Learn how to design cloud-native database architectures to meet the evolving demands of AI and GenAI workloads.

Releasing software shouldn't be stressful or risky. Learn how to leverage progressive delivery techniques to ensure safer deployments.

Avoid machine learning mistakes and boost model performance! Discover key ML patterns, anti-patterns, data strategies, and more.

Related

  • Clustered Quartz Scheduler With Spring Boot and MongoDB
  • Dynamic Schedulers and Custom Cross-Server Schedule Lock
  • How Spring and Hibernate Simplify Web and Database Management
  • Functional Endpoints: Alternative to Controllers in WebFlux

Trending

  • Event-Driven Architectures: Designing Scalable and Resilient Cloud Solutions
  • Unlocking the Potential of Apache Iceberg: A Comprehensive Analysis
  • Measuring the Impact of AI on Software Engineering Productivity
  • Mastering Advanced Traffic Management in Multi-Cloud Kubernetes: Scaling With Multiple Istio Ingress Gateways
  1. DZone
  2. Coding
  3. Frameworks
  4. Integrating Quartz With Spring

Integrating Quartz With Spring

Quartz is job scheduler backed up by most popular RDBMSes. It is really convenient and gets integrated with Spring quite easy.

By 
Emmanouil Gkatziouras user avatar
Emmanouil Gkatziouras
DZone Core CORE ·
Jul. 07, 16 · Tutorial
Likes (5)
Comment
Save
Tweet
Share
42.1K Views

Join the DZone community and get the full member experience.

Join For Free

When it comes to scheduling jobs in a Java application, Quartz is the first tool that comes into consideration.

Quartz is job scheduler backed up by most popular RDBMSes. It is really convenient and gets integrated with Spring quite easy.

In order to create the Quartz schema you have to download the Quartz distribution and extract the folder located in quartz-2.2.3/docs/dbTables/.

Choose the Quartz schema according to the database that you use. In our case we will use a local h2 database therefore I will use the tables_h2.sql schema.

In order to avoid any manual SQL actions, I will use the Spring boot database initialization feature.

Let’s start with our gradle file.

group 'com.gkatzioura'
version '1.0-SNAPSHOT'
 
apply plugin: 'java'
 
sourceCompatibility = 1.8
 
buildscript {
    repositories {
        mavenCentral()
    }
    dependencies {
        classpath("org.springframework.boot:spring-boot-gradle-plugin:1.3.3.RELEASE")
    }
}
 
apply plugin: 'idea'
apply plugin: 'spring-boot'
 
repositories {
    mavenCentral()
}
 
dependencies {
    compile group: 'org.springframework.boot', name: 'spring-boot-starter-web', version: '1.3.3.RELEASE'
    compile group: 'org.springframework', name: 'spring-context-support', version: '4.2.4.RELEASE'
    compile group: 'org.springframework', name:'spring-jdbc', version: '4.2.4.RELEASE'
    compile group: 'org.quartz-scheduler', name: 'quartz', version: '2.2.3'
    compile group: 'ch.qos.logback', name: 'logback-core', version:'1.1.3'
    compile group: 'ch.qos.logback', name: 'logback-classic',version:'1.1.3'
    compile group: 'org.slf4j', name: 'slf4j-api',version:'1.7.13'
    compile group: 'com.h2database', name: 'h2', version:'1.4.192'
    testCompile group: 'junit', name: 'junit', version: '4.11'
}

Apart from the Quartz, Spring, and h2 dependencies, we add the spring-jdbc dependencies since we want to have the database initialized through spring.

We will also add an application.yml file:

spring:
  datasource:
    continueOnError: true
org:
  quartz:
    scheduler:
      instanceName: spring-boot-quartz-demo
      instanceId: AUTO
    threadPool:
      threadCount: 5
job:
  startDelay: 0
  repeatInterval: 60000
  description: Sample job
  key: StatisticsJob

Due to the schema creation statements (lack of create if not exists statements), I set spring.datasource.continueOnError to false. According to your implementation, the workaround will vary.

The application class:

package com.gkatzioura.springquartz;
 
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.ApplicationContext;
 
/**
 * Created by gkatzioura on 6/6/16.
 */
@SpringBootApplication
public class Application {
 
    public static void main(String[] args) {
 
        SpringApplication springApplication = new SpringApplication();
        ApplicationContext ctx = springApplication.run(Application.class,args);
    }
}

The h2 datasource configuration needed by Quartz:

package com.gkatzioura.springquartz.config;
 
import org.h2.jdbcx.JdbcDataSource;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
 
import javax.sql.DataSource;
 
/**
 * Created by gkatzioura on 6/6/16.
 */
@Configuration
public class QuartzDataSource {
 
    //Since it a test database it will be located at the temp directory
    private static final String TMP_DIR = System.getProperty("java.io.tmpdir");
 
    @Bean
    public DataSource dataSource() {
 
        JdbcDataSource ds = new JdbcDataSource();
        ds.setURL("jdbc:h2:"+TMP_DIR+"/test");
 
        return ds;
    }
 
}

In our case we want to send ‘spam’ emails every minute, so we define a simple email service:

package com.gkatzioura.springquartz.service;
 
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Service;
 
/**
 * Created by gkatzioura on 6/7/16.
 */
@Service
public class EmailService {
 
    private static final Logger LOGGER = LoggerFactory.getLogger(EmailService.class);
 
    public void sendSpam() {
 
        LOGGER.info("Should send emails");
    }
 
}

I will also implement a SpringBeanJobFactory:

package com.gkatzioura.springquartz.quartz;
 
import org.quartz.spi.TriggerFiredBundle;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.config.AutowireCapableBeanFactory;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.scheduling.quartz.SpringBeanJobFactory;
 
/**
 * Created by gkatzioura on 6/7/16.
 */
public class QuartzJobFactory extends SpringBeanJobFactory implements ApplicationContextAware {
 
    private transient AutowireCapableBeanFactory beanFactory;
 
    @Override
    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
 
        beanFactory = applicationContext.getAutowireCapableBeanFactory();
    }
 
    @Override
    protected Object createJobInstance(TriggerFiredBundle bundle) throws Exception {
 
        final Object job = super.createJobInstance(bundle);
        beanFactory.autowireBean(job);
        return job;
    }
}

QuartzJobFactory will create the job instance and the will use the application context in order to inject any dependencies defined.

The next step is defining our job:

package com.gkatzioura.springquartz.job;
 
import com.gkatzioura.springquartz.service.EmailService;
import org.quartz.Job;
import org.quartz.JobExecutionContext;
import org.quartz.JobExecutionException;
import org.springframework.beans.factory.annotation.Autowired;
 
/**
 * Created by gkatzioura on 6/6/16.
 */
public class EmailJob implements Job {
 
    @Autowired
    private EmailService cronService;
 
    @Override
    public void execute(JobExecutionContext context) throws JobExecutionException {
 
        cronService.sendSpam();
    }
}

Last step is adding the Quartz config:

package com.gkatzioura.springquartz.config;
 
 
import com.gkatzioura.springquartz.job.EmailJob;
import com.gkatzioura.springquartz.quartz.QuartzJobFactory;
import org.quartz.SimpleTrigger;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.scheduling.quartz.JobDetailFactoryBean;
import org.springframework.scheduling.quartz.SchedulerFactoryBean;
import org.springframework.scheduling.quartz.SimpleTriggerFactoryBean;
 
import javax.sql.DataSource;
import java.util.Properties;
 
/**
 * Created by gkatzioura on 6/7/16.
 */
@Configuration
public class QuartzConfig {
 
    @Value("${org.quartz.scheduler.instanceName}")
    private String instanceName;
 
    @Value("${org.quartz.scheduler.instanceId}")
    private String instanceId;
 
    @Value("${org.quartz.threadPool.threadCount}")
    private String threadCount;
 
    @Value("${job.startDelay}")
    private Long startDelay;
 
    @Value("${job.repeatInterval}")
    private Long repeatInterval;
 
    @Value("${job.description}")
    private String description;
 
    @Value("${job.key}")
    private String key;
 
    @Autowired
    private DataSource dataSource;
 
    @Bean
    public org.quartz.spi.JobFactory jobFactory(ApplicationContext applicationContext) {
 
        QuartzJobFactory sampleJobFactory = new QuartzJobFactory();
        sampleJobFactory.setApplicationContext(applicationContext);
        return sampleJobFactory;
    }
 
    @Bean
    public SchedulerFactoryBean schedulerFactoryBean(ApplicationContext applicationContext) {
 
        SchedulerFactoryBean factory = new SchedulerFactoryBean();
 
        factory.setOverwriteExistingJobs(true);
        factory.setJobFactory(jobFactory(applicationContext));
 
        Properties quartzProperties = new Properties();
        quartzProperties.setProperty("org.quartz.scheduler.instanceName",instanceName);
        quartzProperties.setProperty("org.quartz.scheduler.instanceId",instanceId);
        quartzProperties.setProperty("org.quartz.threadPool.threadCount",threadCount);
 
        factory.setDataSource(dataSource);
 
        factory.setQuartzProperties(quartzProperties);
        factory.setTriggers(emailJobTrigger().getObject());
 
        return factory;
    }
 
    @Bean(name = "emailJobTrigger")
    public SimpleTriggerFactoryBean emailJobTrigger() {
 
 
        SimpleTriggerFactoryBean factoryBean = new SimpleTriggerFactoryBean();
        factoryBean.setJobDetail(emailJobDetails().getObject());
        factoryBean.setStartDelay(startDelay);
        factoryBean.setRepeatInterval(repeatInterval);
        factoryBean.setRepeatCount(SimpleTrigger.REPEAT_INDEFINITELY);
        factoryBean.setMisfireInstruction(SimpleTrigger.MISFIRE_INSTRUCTION_RESCHEDULE_NEXT_WITH_REMAINING_COUNT);
        return factoryBean;
    }
 
    @Bean(name = "emailJobDetails")
    public JobDetailFactoryBean emailJobDetails() {
 
        JobDetailFactoryBean jobDetailFactoryBean = new JobDetailFactoryBean();
        jobDetailFactoryBean.setJobClass(EmailJob.class);
        jobDetailFactoryBean.setDescription(description);
        jobDetailFactoryBean.setDurability(true);
        jobDetailFactoryBean.setName(key);
 
        return jobDetailFactoryBean;
    }
}

What we did is creating a scheduler factory bean using the QuartzJobFactory we defined and we registered the triggers needed for our jobs to run. In our case we implemented a simple trigger running every minute.

You can find the source code on GitHub. 

Quartz (scheduler) Spring Framework job scheduling

Published at DZone with permission of Emmanouil Gkatziouras, DZone MVB. See the original article here.

Opinions expressed by DZone contributors are their own.

Related

  • Clustered Quartz Scheduler With Spring Boot and MongoDB
  • Dynamic Schedulers and Custom Cross-Server Schedule Lock
  • How Spring and Hibernate Simplify Web and Database Management
  • Functional Endpoints: Alternative to Controllers in WebFlux

Partner Resources

×

Comments

The likes didn't load as expected. Please refresh the page and try again.

ABOUT US

  • About DZone
  • Support and feedback
  • Community research
  • Sitemap

ADVERTISE

  • Advertise with DZone

CONTRIBUTE ON DZONE

  • Article Submission Guidelines
  • Become a Contributor
  • Core Program
  • Visit the Writers' Zone

LEGAL

  • Terms of Service
  • Privacy Policy

CONTACT US

  • 3343 Perimeter Hill Drive
  • Suite 100
  • Nashville, TN 37211
  • support@dzone.com

Let's be friends: