Quartz with Spring Boot, Quartz JobListener Example - How to keep track the status of the running job?

Hello everyone, today we will show you how to configure Quartz with Spring Boot and how to keep track the status of the running job.


Tech stack

  • Spring Boot 2.6.7
  • Java 17
  • Quartz Scheduler
  • Quartz JobListener
  • RAMJobStore for persisting quartz job.
  • Maven

Quartz is a richly featured, open-source job scheduling library that can be integrated within virtually any Java application - from the smallest stand-alone application to the largest e-commerce system. 
Listeners are objects that you create to perform actions based on events occurring within the scheduler. As you can probably guess, TriggerListeners receive events related to triggers, and JobListeners receive events related to jobs.
RAMJobStore is the simplest JobStore to use. It keeps all of its data in RAM.

Final Project Directory:



Maven[pom.xml]

<?xml version="1.0" encoding="UTF-8"?>
<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
https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.6.7</version>
<relativePath /> <!-- lookup parent from repository -->
</parent>
<groupId>com.knf.dev.demo</groupId>
<artifactId>spring-quartz-joblistener-demo</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>spring-quartz-joblistener-demo</name>
<description>Demo project for Spring Boot</description>
<properties>
<java.version>17</java.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
</dependency>

<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-quartz</artifactId>
</dependency>

</dependencies>

<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>

</project>



Service [TestService.java]

package com.example.demo.service;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Service;

@Service
public class TestService {

private static final Logger LOG = LoggerFactory.
getLogger(TestService.class);

public void processData() {
LOG.info("Inside Test Service");
}
}
TestService is a simple service which will prints "Inside Test Service" in the console log. We will also write a quartz job to invoke this service.


Job[MySimpleJob.java]

package com.example.demo.job;

import org.quartz.Job;
import org.quartz.JobExecutionContext;
import org.springframework.beans.factory.annotation.Autowired;
import com.example.demo.service.TestService;

public class MySimpleJob implements Job {

@Autowired
private TestService service;

@Override
public void execute(JobExecutionContext jobExecutionContext) {
service.processData();
}
}



SpringBeanJobFactory[AutowiringSpringBeanJobFactory.java]

package com.example.demo.config;

import org.quartz.spi.TriggerFiredBundle;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.config.AutowireCapableBeanFactory;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.scheduling.quartz.SpringBeanJobFactory;

public final class AutowiringSpringBeanJobFactory
extends SpringBeanJobFactory
implements ApplicationContextAware {

private static final Logger LOG = LoggerFactory.
getLogger(AutowiringSpringBeanJobFactory.class);

private transient AutowireCapableBeanFactory beanFactory;

@Override
public void setApplicationContext
(final ApplicationContext context) {
beanFactory = context.getAutowireCapableBeanFactory();
}

@Override
protected Object createJobInstance
(final TriggerFiredBundle bundle) throws Exception {
final Object job = super.createJobInstance(bundle);
LOG.info("create "+bundle.getJobDetail().
getKey().getName()+" instance");
beanFactory.autowireBean(job);
return job;
}
}
Create a custom SpringBeanJobFactory for enabling spring auto wiring for the MySimpleJob and TestService.


SchedulerConfig.java

package com.example.demo.config;

import java.io.IOException;
import java.util.Properties;
import org.quartz.JobDetail;
import org.quartz.SimpleTrigger;
import org.quartz.Trigger;
import org.quartz.spi.JobFactory;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.beans.factory.config.PropertiesFactoryBean;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.io.ClassPathResource;
import org.springframework.scheduling.quartz.JobDetailFactoryBean;
import org.springframework.scheduling.quartz.SchedulerFactoryBean;
import org.springframework.scheduling.quartz.SimpleTriggerFactoryBean;

import com.example.demo.job.MySimpleJob;

@Configuration
public class SchedulerConfig {

private static final Logger LOG = LoggerFactory.
getLogger(SchedulerConfig.class);

@Bean
public JobDetailFactoryBean mySimpleJob() {

var factoryBean = new JobDetailFactoryBean();
factoryBean.setJobClass(MySimpleJob.class);
factoryBean.setDurability(true);
return factoryBean;
}

@Bean
public JobFactory jobFactory
(ApplicationContext applicationContext) {

var jobFactory = new AutowiringSpringBeanJobFactory();
jobFactory.setApplicationContext(applicationContext);
return jobFactory;
}

@Bean
public SchedulerFactoryBean schedulerFactoryBean
(JobFactory jobFactory, Trigger simpleJobTrigger)
throws IOException {

var factory = new SchedulerFactoryBean();
factory.setJobFactory(jobFactory);
factory.setQuartzProperties(quartzProperties());
factory.setTriggers(simpleJobTrigger);
LOG.info("starting jobs....");
return factory;
}

@Bean
public SimpleTriggerFactoryBean simpleJobTrigger
(@Qualifier("mySimpleJob") JobDetail jobDetail,
@Value("${mysimplejob.frequency}") long frequency) {

LOG.info("mySimpleJobTriggered");

var factoryBean = new SimpleTriggerFactoryBean();
factoryBean.setJobDetail(jobDetail);
factoryBean.setStartDelay(0L);
factoryBean.setRepeatInterval(frequency);
factoryBean.
setRepeatCount(SimpleTrigger.REPEAT_INDEFINITELY);
return factoryBean;
}

@Bean
public Properties quartzProperties() throws IOException {

var propertiesFactoryBean = new PropertiesFactoryBean();
propertiesFactoryBean.
setLocation(new ClassPathResource("/quartz.properties"));
propertiesFactoryBean.afterPropertiesSet();
return propertiesFactoryBean.getObject();
}
}



JobListener[GlobalJobListener.java]

package com.example.demo.listener;

import org.quartz.JobExecutionContext;
import org.quartz.JobExecutionException;
import org.quartz.JobListener;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

public class GlobalJobListener implements JobListener {

private static final String
TRIGGER_LISTENER_NAME = "GlobalJobListener";
private static final Logger
LOG = LoggerFactory.getLogger(GlobalJobListener.class);

@Override
public String getName() {
return TRIGGER_LISTENER_NAME;
}

@Override
public void jobToBeExecuted(JobExecutionContext context) {

var triggerName = context.getTrigger().getKey().toString();
var jobName = context.getJobDetail().getKey().toString();
LOG.info("trigger : " + triggerName + " is going to fire");
LOG.info("job : " + jobName + "is going to fire");

}

@Override
public void jobWasExecuted(JobExecutionContext context,
JobExecutionException jobException) {

var triggerName = context.getTrigger().getKey().toString();
var jobName = context.getJobDetail().getKey().toString();
LOG.info("trigger : " + triggerName + " is fired");
LOG.info("job : " + jobName + " is fired");

}

@Override
public void jobExecutionVetoed(JobExecutionContext context) {
// TODO Auto-generated method stub

}
}
Listeners are objects that you create to perform actions based on events occurring within the scheduler. As you can probably guess, TriggerListeners receive events related to triggers, and JobListeners receive events related to jobs.


JobListenerConfig.java

package com.example.demo.listener;

import javax.annotation.PostConstruct;
import org.quartz.SchedulerException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.scheduling.quartz.SchedulerFactoryBean;
import org.springframework.stereotype.Component;

@Component
public class JobListenerConfig {

@Autowired
private SchedulerFactoryBean schedulerFactoryBean;

@PostConstruct
public void addListeners() throws SchedulerException {

schedulerFactoryBean.getScheduler().
getListenerManager().addJobListener(new GlobalJobListener());
}
}

Registering listener with the scheduler.


application.properties

mysimplejob.frequency=500



quartz.properties

org.quartz.scheduler.instanceName=spring-quartz-demo
org.quartz.scheduler.instanceId=AUTO
org.quartz.threadPool.threadCount=10
org.quartz.jobStore.class=org.quartz.simpl.RAMJobStore



DemoApplication.java

package com.example.demo;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.Import;

import com.example.demo.config.SchedulerConfig;

@Import({ SchedulerConfig.class })
@SpringBootApplication
public class DemoApplication {

public static void main(String[] args) {
SpringApplication.run(DemoApplication.class, args);
}
}



Local Setup and Run the application

Step1: Download or clone the source code to a local machine. - Click here


Step2: mvn clean install


Step3: Run the Spring Boot application - mvn spring-boot:run

Download the source code

Popular posts from this blog

Learn Java 8 streams with an example - print odd/even numbers from Array and List

Java Stream API - How to convert List of objects to another List of objects using Java streams?

Registration and Login with Spring Boot + Spring Security + Thymeleaf

Java, Spring Boot Mini Project - Library Management System - Download

ReactJS, Spring Boot JWT Authentication Example

Spring Boot + Mockito simple application with 100% code coverage

Top 5 Java ORM tools - 2024

Java - Blowfish Encryption and decryption Example

Spring boot video streaming example-HTML5

Google Cloud Storage + Spring Boot - File Upload, Download, and Delete