Spring Batch – Quartz Scheduler

Quartz Scheduler

This tutorial, spring batch quartz scheduler, will show you how to schedule the task repeatedly for reading a CSV file data and writing to XML file after some modification to the input CSV file using Quartz Scheduler API.

I’ll build a service that imports data from a CSV file, transforms it with custom code, and store the final results in xml file. And schedule the same task repeatedly using spring batch quartz scheduler API.

In my previous tutorial I have shown how to do the same thing using spring’s built-in TaskScheduler API.

You can read the tutorial Spring Batch to read what is Spring Batch and what are the usages of Spring Batch.

Related Posts:

Prerequisites

Java 8/11/19, Maven 3.8.5, Spring Boot 2.1.4/2.6.7/3.1.2, MySQL 8.0.26/8.0.31

Project Setup

Create a maven based project in your favorite IDE or tool. You can give the project name as spring-batch-quartz-scheduler.

For spring boot version 3.x you can use the following 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 http://maven.apache.org/xsd/maven-4.0.0.xsd">
	<modelVersion>4.0.0</modelVersion>

	<groupId>com.roytuts</groupId>
	<artifactId>spring-batch-quartz-scheduler</artifactId>
	<version>0.0.1-SNAPSHOT</version>

	<properties>
		<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
		<maven.compiler.source>19</maven.compiler.source>
		<maven.compiler.target>19</maven.compiler.target>
	</properties>

	<parent>
		<groupId>org.springframework.boot</groupId>
		<artifactId>spring-boot-starter-parent</artifactId>
		<version>3.1.2</version>
	</parent>

	<dependencies>
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-batch</artifactId>
		</dependency>

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

		<dependency>
			<groupId>org.springframework</groupId>
			<artifactId>spring-oxm</artifactId>
		</dependency>

		<dependency>
			<groupId>org.quartz-scheduler</groupId>
			<artifactId>quartz</artifactId>
		</dependency>

		<dependency>
			<groupId>jakarta.xml.bind</groupId>
			<artifactId>jakarta.xml.bind-api</artifactId>
		</dependency>

		<dependency>
			<groupId>org.glassfish.jaxb</groupId>
			<artifactId>jaxb-runtime</artifactId>
		</dependency>

		<dependency>
			<groupId>mysql</groupId>
			<artifactId>mysql-connector-java</artifactId>
			<version>8.0.31</version>
		</dependency>
	</dependencies>

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

For spring boot version 2.x you can use the following pom.xml file:

<?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 http://maven.apache.org/xsd/maven-4.0.0.xsd">
	<modelVersion>4.0.0</modelVersion>

	<groupId>com.roytuts</groupId>
	<artifactId>spring-batch-quartz-scheduler</artifactId>
	<version>0.0.1-SNAPSHOT</version>

	<properties>
		<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
		<maven.compiler.source>11</maven.compiler.source>
		<maven.compiler.target>11</maven.compiler.target>
	</properties>

	<parent>
		<groupId>org.springframework.boot</groupId>
		<artifactId>spring-boot-starter-parent</artifactId>
		<version>2.6.7</version>
	</parent>

	<dependencies>
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-batch</artifactId>
		</dependency>

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

		<dependency>
			<groupId>org.springframework</groupId>
			<artifactId>spring-oxm</artifactId>
		</dependency>

		<dependency>
			<groupId>org.quartz-scheduler</groupId>
			<artifactId>quartz</artifactId>
		</dependency>

		<dependency>
			<groupId>jakarta.xml.bind</groupId>
			<artifactId>jakarta.xml.bind-api</artifactId>
		</dependency>

		<dependency>
			<groupId>org.glassfish.jaxb</groupId>
			<artifactId>jaxb-runtime</artifactId>
		</dependency>

		<dependency>
			<groupId>mysql</groupId>
			<artifactId>mysql-connector-java</artifactId>
		</dependency>
	</dependencies>

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

I have added required dependencies, such as, Quartz API, MySQL database connector, JAXB API etc.

I need to add spring-context-support dependency for working with Quartz API.

VO Class

Create a model class Person.java which will represent a row of data for inputs and outputs.

I have made the below class JAXB annotation enabled for converting Java object to XML directly without writing our own code for converting.

For spring boot 3.x and 2.x, the import statements will be different.

@XmlRootElement(name = "person")
public class Person {

	private int id;
	private String firstName;
	private String lastName;

	@XmlAttribute(name = "id")
	public int getId() {
		return id;
	}

	public void setId(int id) {
		this.id = id;
	}

	@XmlElement(name = "firstName")
	public String getFirstName() {
		return firstName;
	}

	public void setFirstName(String firstName) {
		this.firstName = firstName;
	}

	@XmlElement(name = "lastName")
	public String getLastName() {
		return lastName;
	}

	public void setLastName(String lastName) {
		this.lastName = lastName;
	}

	@Override
	public String toString() {
		return "Person [id=" + id + ", firstName=" + firstName + ", lastName=" + lastName + "]";
	}

}

FieldSetMapper Class

Create below mapper class which will map the CSV file row item to Java object.

Each row in CSV file has first field as integer value, second field as string value and third field as string value.

public class PersonFieldSetMapper implements FieldSetMapper<Person> {

	@Override
	public Person mapFieldSet(FieldSet fieldSet) {
		Person person = new Person();
		person.setId(fieldSet.readInt(0));
		person.setFirstName(fieldSet.readString(1));
		person.setLastName(fieldSet.readString(2));
		return person;
	}

}

ItemProcessor Class

Create an intermediate processor.

A common paradigm in batch processing is to ingest data, transform it, and then pipe it out somewhere else.

Here I write a simple transformer that converts the initial characters of the names to uppercase. You can write other processing logic according to your project requirements.

public class PersonItemProcessor implements ItemProcessor<Person, Person> {

	@Override
	public Person process(Person person) throws Exception {
		System.out.println("Processing: " + person);

		final String initCapFirstName = person.getFirstName().substring(0, 1).toUpperCase()
				+ person.getFirstName().substring(1);
		final String initCapLastName = person.getLastName().substring(0, 1).toUpperCase()
				+ person.getLastName().substring(1);

		Person transformedPerson = new Person();
		transformedPerson.setId(person.getId());
		transformedPerson.setFirstName(initCapFirstName);
		transformedPerson.setLastName(initCapLastName);

		return transformedPerson;
	}

}

Input CSV File

Create below CSV file called person.csv under src/main/resources directory.

This is a very simple file that contains each row with id, first name and last name.

1000,soumitra,roy
1001,souvik,sanyal
1002,arup,chatterjee
1003,suman,mukherjee
1004,debina,guha
1005,liton,sarkar
1006,debabrata,poddar

Scheduler Class

Create below scheduler class in spring batch quartz scheduler example, which extends QuartzJobBean, which acts as a bridge between Quartz and Spring Batch.

public class SpringBatchQuartzScheduler extends QuartzJobBean {

	private JobLocator jobLocator;
	private JobLauncher jobLauncher;

	public void setJobLocator(JobLocator jobLocator) {
		this.jobLocator = jobLocator;
	}

	public void setJobLauncher(JobLauncher jobLauncher) {
		this.jobLauncher = jobLauncher;
	}

	@Override
	protected void executeInternal(JobExecutionContext context) {
		Map<String, Object> jobMap = context.getMergedJobDataMap();
		String jobName = (String) jobMap.get("jobName");

		try {
			JobExecution execution = jobLauncher.run(jobLocator.getJob(jobName),
					new JobParametersBuilder().addLong("timestamp", System.currentTimeMillis()).toJobParameters());
			System.out.println("Job Status : " + execution.getStatus());
		} catch (Exception ex) {
			ex.printStackTrace();
		}

		System.out.println("Done");
	}

}

Spring Batch Configuration

I have created this Spring Configuration class to define several beans for Spring Batch processing.

I have defined beans, such as, ItemProcessor, TransactionManager, JobRepository, DataSource, JobLauncher, Step, Job etc. for our Spring Batch processing.

I used Jaxb2Marshaller to marshal Java POJO to XML.

I write the final output into an XML file (person<today’s date>.xml) under C:/workspace directory.

For spring boot version 3.x use the following configuration class. In spring boot 3.x you don’t need to use @EnableBatchProcessing annotation for configuration class. The JobBuilderFactory has been replaced by JobBuilder and StepBuilderFactory has been replaced by StepBuilder class.

@Configuration
public class SpringBatchConfig {
	@Bean
	@Scope(value = BeanDefinition.SCOPE_PROTOTYPE)
	public Person person() {
		return new Person();
	}

	@Bean
	@Scope(value = BeanDefinition.SCOPE_PROTOTYPE)
	public ItemProcessor<Person, Person> itemProcessor() {
		return new PersonItemProcessor();
	}

	@Bean
	public DataSource dataSource() {
		DriverManagerDataSource dataSource = new DriverManagerDataSource();
		dataSource.setDriverClassName("com.mysql.cj.jdbc.Driver");
		dataSource.setUrl("jdbc:mysql://localhost:3306/roytuts");
		dataSource.setUsername("root");
		dataSource.setPassword("root");
		ResourceDatabasePopulator databasePopulator = new ResourceDatabasePopulator();
		databasePopulator.addScript(new ClassPathResource("org/springframework/batch/core/schema-drop-mysql.sql"));
		databasePopulator.addScript(new ClassPathResource("org/springframework/batch/core/schema-mysql.sql"));
		DatabasePopulatorUtils.execute(databasePopulator, dataSource);
		return dataSource;
	}

	@Bean
	public PlatformTransactionManager transactionManager() {
		return new ResourcelessTransactionManager();
	}

	@Bean
	public JobRepository jbRepository(DataSource dataSource, PlatformTransactionManager transactionManager)
			throws Exception {
		JobRepositoryFactoryBean factory = new JobRepositoryFactoryBean();
		factory.setDatabaseType(DatabaseType.MYSQL.getProductName());
		factory.setDataSource(dataSource);
		factory.setTransactionManager(transactionManager);
		factory.setIncrementerFactory(new DefaultDataFieldMaxValueIncrementerFactory(dataSource) {
			@Override
			public DataFieldMaxValueIncrementer getIncrementer(String incrementerType, String incrementerName) {
				return new SqlServerSequenceMaxValueIncrementer(dataSource, incrementerName);
			}
		});
		factory.afterPropertiesSet();
		return factory.getObject();
	}

	@Bean
	public JobLauncher jbLauncher(JobRepository jbRepository) {
		TaskExecutorJobLauncher jobLauncher = new TaskExecutorJobLauncher();
		jobLauncher.setJobRepository(jbRepository);
		return jobLauncher;
	}

	@Bean
	public BeanWrapperFieldSetMapper<Person> beanWrapperFieldSetMapper() {
		BeanWrapperFieldSetMapper<Person> fieldSetMapper = new BeanWrapperFieldSetMapper<>();
		fieldSetMapper.setPrototypeBeanName("person");
		return fieldSetMapper;
	}

	@Bean
	public FlatFileItemReader<Person> fileItemReader(BeanWrapperFieldSetMapper<Person> beanWrapperFieldSetMapper) {
		FlatFileItemReader<Person> fileItemReader = new FlatFileItemReader<>();
		fileItemReader.setResource(new ClassPathResource("person.csv"));
		DelimitedLineTokenizer delimitedLineTokenizer = new DelimitedLineTokenizer();
		delimitedLineTokenizer.setNames("id", "firstName", "lastName");
		DefaultLineMapper<Person> defaultLineMapper = new DefaultLineMapper<>();
		defaultLineMapper.setLineTokenizer(delimitedLineTokenizer);
		defaultLineMapper.setFieldSetMapper(beanWrapperFieldSetMapper);
		fileItemReader.setLineMapper(defaultLineMapper);
		return fileItemReader;
	}

	@Bean(destroyMethod = "")
	public StaxEventItemWriter<Person> staxEventItemWriter(Jaxb2Marshaller marshaller) {
		StaxEventItemWriter<Person> staxEventItemWriter = new StaxEventItemWriter<>();
		staxEventItemWriter.setResource(new FileSystemResource("C:/workspace/person" + LocalDate.now() + ".xml"));
		staxEventItemWriter.setMarshaller(marshaller);
		staxEventItemWriter.setRootTagName("personInfo");
		return staxEventItemWriter;
	}

	@Bean
	public Job jobCsvXml(JobRepository jobRepository, Step step) {
		// return new JobBuilder("jobCsvXml", jobRepository).flow(step).end().build();
		return new JobBuilder("jobCsvXml", jobRepository).start(step).build();
	}

	@Bean
	public Step step1(JobRepository jobRepository, PlatformTransactionManager transactionManager,
			ItemReader<Person> reader, ItemWriter<Person> writer, ItemProcessor<Person, Person> processor) {
		return new StepBuilder("step1", jobRepository).<Person, Person>chunk(2, transactionManager).reader(reader)
				.processor(processor).writer(writer).build();
	}

	@Bean
	public Jaxb2Marshaller jaxb2Marshaller() {
		Jaxb2Marshaller jaxb2Marshaller = new Jaxb2Marshaller();
		jaxb2Marshaller.setClassesToBeBound(Person.class);
		return jaxb2Marshaller;
	}

	@Bean
	public JobRegistryBeanPostProcessor jobRegistryBeanPostProcessor(JobRegistry jobRegistry) {
		JobRegistryBeanPostProcessor jobRegistryBeanPostProcessor = new JobRegistryBeanPostProcessor();
		jobRegistryBeanPostProcessor.setJobRegistry(jobRegistry);
		return jobRegistryBeanPostProcessor;
	}

	@Bean
	public JobDetailFactoryBean jobDetailFactoryBean(Job jobCsvXml, JobLauncher jobLauncher, JobRegistry jobLocator) {
		JobDetailFactoryBean jobDetailFactoryBean = new JobDetailFactoryBean();
		jobDetailFactoryBean.setJobClass(SpringBatchQuartzScheduler.class);
		Map<String, Object> map = new HashMap<>();
		map.put("jobName", jobCsvXml.getName());
		map.put("jobLauncher", jobLauncher);
		map.put("jobLocator", jobLocator);
		jobDetailFactoryBean.setJobDataAsMap(map);
		return jobDetailFactoryBean;
	}

	@Bean
	public CronTriggerFactoryBean cronTriggerFactoryBean(JobDetailFactoryBean jobDetail) {
		CronTriggerFactoryBean cronTriggerFactoryBean = new CronTriggerFactoryBean();
		cronTriggerFactoryBean.setJobDetail(jobDetail.getObject());
		cronTriggerFactoryBean.setStartDelay(2000);
		cronTriggerFactoryBean.setCronExpression("*/2 * * * * ?");
		return cronTriggerFactoryBean;
	}

	@Bean
	public SchedulerFactoryBean schedulerFactoryBean(CronTriggerFactoryBean cronTrigger) {
		SchedulerFactoryBean schedulerFactoryBean = new SchedulerFactoryBean();
		schedulerFactoryBean.setTriggers(cronTrigger.getObject());
		return schedulerFactoryBean;
	}

}

For spring boot version 2.x use the following configuration class:

@Configuration
@EnableBatchProcessing
public class SpringBatchConfig {
	@Bean
	@Scope(value = BeanDefinition.SCOPE_PROTOTYPE)
	public Person person() {
		return new Person();
	}

	@Bean
	@Scope(value = BeanDefinition.SCOPE_PROTOTYPE)
	public ItemProcessor<Person, Person> itemProcessor() {
		return new PersonItemProcessor();
	}

	@Bean
	public DataSource dataSource() {
		DriverManagerDataSource dataSource = new DriverManagerDataSource();
		dataSource.setDriverClassName("com.mysql.cj.jdbc.Driver");
		dataSource.setUrl("jdbc:mysql://localhost:3306/roytuts");
		dataSource.setUsername("root");
		dataSource.setPassword("root");
		ResourceDatabasePopulator databasePopulator = new ResourceDatabasePopulator();
		databasePopulator.addScript(new ClassPathResource("org/springframework/batch/core/schema-drop-mysql.sql"));
		databasePopulator.addScript(new ClassPathResource("org/springframework/batch/core/schema-mysql.sql"));
		DatabasePopulatorUtils.execute(databasePopulator, dataSource);
		return dataSource;
	}

	@Bean
	public ResourcelessTransactionManager txManager() {
		return new ResourcelessTransactionManager();
	}

	@Bean
	public JobRepository jbRepository(DataSource dataSource, ResourcelessTransactionManager transactionManager)
			throws Exception {
		JobRepositoryFactoryBean factory = new JobRepositoryFactoryBean();
		factory.setDatabaseType(DatabaseType.MYSQL.getProductName());
		factory.setDataSource(dataSource);
		factory.setTransactionManager(transactionManager);
		return factory.getObject();
	}

	@Bean
	public JobLauncher jbLauncher(JobRepository jbRepository) {
		SimpleJobLauncher jobLauncher = new SimpleJobLauncher();
		jobLauncher.setJobRepository(jbRepository);
		return jobLauncher;
	}

	@Bean
	public BeanWrapperFieldSetMapper<Person> beanWrapperFieldSetMapper() {
		BeanWrapperFieldSetMapper<Person> fieldSetMapper = new BeanWrapperFieldSetMapper<>();
		fieldSetMapper.setPrototypeBeanName("person");
		return fieldSetMapper;
	}

	@Bean
	public FlatFileItemReader<Person> fileItemReader(BeanWrapperFieldSetMapper<Person> beanWrapperFieldSetMapper) {
		FlatFileItemReader<Person> fileItemReader = new FlatFileItemReader<>();
		fileItemReader.setResource(new ClassPathResource("person.csv"));
		DelimitedLineTokenizer delimitedLineTokenizer = new DelimitedLineTokenizer();
		delimitedLineTokenizer.setNames("id", "firstName", "lastName");
		DefaultLineMapper<Person> defaultLineMapper = new DefaultLineMapper<>();
		defaultLineMapper.setLineTokenizer(delimitedLineTokenizer);
		defaultLineMapper.setFieldSetMapper(beanWrapperFieldSetMapper);
		fileItemReader.setLineMapper(defaultLineMapper);
		return fileItemReader;
	}

	@Bean(destroyMethod = "")
	public StaxEventItemWriter<Person> staxEventItemWriter(Jaxb2Marshaller marshaller) {
		StaxEventItemWriter<Person> staxEventItemWriter = new StaxEventItemWriter<>();
		staxEventItemWriter.setResource(new FileSystemResource("C:/workspace/person" + LocalDate.now() + ".xml"));
		staxEventItemWriter.setMarshaller(marshaller);
		staxEventItemWriter.setRootTagName("personInfo");
		return staxEventItemWriter;
	}

	@Bean
	public Job jobCsvXml(JobBuilderFactory jobBuilderFactory, Step step) {
		return jobBuilderFactory.get("jobCsvXml").incrementer(new RunIdIncrementer()).flow(step).end().build();
	}

	@Bean
	public Step step1(StepBuilderFactory stepBuilderFactory, ResourcelessTransactionManager transactionManager,
			ItemReader<Person> reader, ItemWriter<Person> writer, ItemProcessor<Person, Person> processor) {
		return stepBuilderFactory.get("step1").transactionManager(transactionManager).<Person, Person>chunk(2)
				.reader(reader).processor(processor).writer(writer).build();
	}

	@Bean
	public Jaxb2Marshaller jaxb2Marshaller() {
		Jaxb2Marshaller jaxb2Marshaller = new Jaxb2Marshaller();
		jaxb2Marshaller.setClassesToBeBound(Person.class);
		return jaxb2Marshaller;
	}

	@Bean
	public JobRegistryBeanPostProcessor jobRegistryBeanPostProcessor(JobRegistry jobRegistry) {
		JobRegistryBeanPostProcessor jobRegistryBeanPostProcessor = new JobRegistryBeanPostProcessor();
		jobRegistryBeanPostProcessor.setJobRegistry(jobRegistry);
		return jobRegistryBeanPostProcessor;
	}

	@Bean
	public JobDetailFactoryBean jobDetailFactoryBean(Job jobCsvXml, JobLauncher jobLauncher, JobRegistry jobLocator) {
		JobDetailFactoryBean jobDetailFactoryBean = new JobDetailFactoryBean();
		jobDetailFactoryBean.setJobClass(SpringBatchQuartzScheduler.class);
		Map<String, Object> map = new HashMap<>();
		map.put("jobName", jobCsvXml.getName());
		map.put("jobLauncher", jobLauncher);
		map.put("jobLocator", jobLocator);
		jobDetailFactoryBean.setJobDataAsMap(map);
		return jobDetailFactoryBean;
	}

	@Bean
	public CronTriggerFactoryBean cronTriggerFactoryBean(JobDetailFactoryBean jobDetail) {
		CronTriggerFactoryBean cronTriggerFactoryBean = new CronTriggerFactoryBean();
		cronTriggerFactoryBean.setJobDetail(jobDetail.getObject());
		cronTriggerFactoryBean.setStartDelay(2000);
		cronTriggerFactoryBean.setCronExpression("*/2 * * * * ?");
		return cronTriggerFactoryBean;
	}

	@Bean
	public SchedulerFactoryBean schedulerFactoryBean(CronTriggerFactoryBean cronTrigger) {
		SchedulerFactoryBean schedulerFactoryBean = new SchedulerFactoryBean();
		schedulerFactoryBean.setTriggers(cronTrigger.getObject());
		return schedulerFactoryBean;
	}

}

A default simple implementation of the Job interface is provided by Spring Batch in the form of the SimpleJob class which creates some standard functionality on top of Job, however the batch namespace abstracts away the need to instantiate it directly.

Step is a domain object that encapsulates an independent, sequential phase of a batch job. Therefore, every Job is composed entirely of one or more steps. A Step contains all of the information necessary to define and control the actual batch processing.

ItemReader is an abstraction that represents the retrieval of input for a Step, one item at a time.

ItemWriter is an abstraction that represents the output of a Step, one batch or chunk of items at a time. Generally, an item writer has no knowledge of the input it will receive next, only the item that was passed in its current invocation.

ItemProcessor is an abstraction that represents the business processing of an item. While the  ItemReader reads one item, and the ItemWriter writes them, the ItemProcessor provides access to transform or apply other business processing. If, while processing the item, it is determined that the item is not valid, returning null indicates that the item should not be written out.

TransactionManager – it will be used to begin and commit transactions during processing.

Chunk – The number of items that will be processed before the transaction is committed.

JobRepository is the persistence mechanism. It provides CRUD operations for JobLauncherJob and Step implementations. When a Job is first launched, a JobExecution is obtained from the repository, and during the course of execution StepExecution and JobExecution implementations are persisted by passing them to the repository.

JonLauncher represents a simple interface for launching a Job with a given set of JobParameters.

SchedulerFactoryBean that schedules the job for execution at regular interval that is specified in CronTriggerFactoryBean and starts the job at 2 seconds delay as specified in CronTriggerFactoryBean.

The JobRegistryBeanPostProcessor bean registers Job beans with JobRegistry, so that QuartzJobBean is able to get the Job bean via JobLocator(JobRegister).

Spring Boot Main Class

Create below class for launching spring batch job.

@SpringBootApplication
public class App {

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

}

Testing Spring Batch Quartz Scheduler

Run the above class, you will see the below output for spring batch quartz scheduler example.

11.916  INFO 9124 --- [           main] trationDelegate$BeanPostProcessorChecker : Bean 'springBatchConfig' of type [com.roytuts.spring.batch.quartz.scheduler.config.SpringBatchConfig$$EnhancerBySpringCGLIB$$d33a0cd] is not eligible for getting processed by all BeanPostProcessors (for example: not eligible for auto-proxying)
13.318  INFO 9124 --- [           main] trationDelegate$BeanPostProcessorChecker : Bean 'dataSource' of type [org.springframework.jdbc.datasource.DriverManagerDataSource] is not eligible for getting processed by all BeanPostProcessors (for example: not eligible for auto-proxying)
13.337  INFO 9124 --- [           main] trationDelegate$BeanPostProcessorChecker : Bean 'org.springframework.batch.core.configuration.annotation.SimpleBatchConfiguration' of type [org.springframework.batch.core.configuration.annotation.SimpleBatchConfiguration] is not eligible for getting processed by all BeanPostProcessors (for example: not eligible for auto-proxying)
13.343  INFO 9124 --- [           main] trationDelegate$BeanPostProcessorChecker : Bean 'jobRegistry' of type [com.sun.proxy.$Proxy46] is not eligible for getting processed by all BeanPostProcessors (for example: not eligible for auto-proxying)
13.789  INFO 9124 --- [           main] o.s.b.c.l.support.SimpleJobLauncher      : No TaskExecutor has been set, defaulting to synchronous executor.
14.118  INFO 9124 --- [           main] o.s.b.c.r.s.JobRepositoryFactoryBean     : No database type set, using meta data indicating: MYSQL
14.118  INFO 9124 --- [           main] o.s.b.c.l.support.SimpleJobLauncher      : No TaskExecutor has been set, defaulting to synchronous executor.
14.200  INFO 9124 --- [           main] org.quartz.impl.StdSchedulerFactory      : Using default implementation for ThreadExecutor
14.221  INFO 9124 --- [           main] org.quartz.core.SchedulerSignalerImpl    : Initialized Scheduler Signaller of type: class org.quartz.core.SchedulerSignalerImpl
14.221  INFO 9124 --- [           main] org.quartz.core.QuartzScheduler          : Quartz Scheduler v.2.3.2 created.
14.223  INFO 9124 --- [           main] org.quartz.simpl.RAMJobStore             : RAMJobStore initialized.
14.224  INFO 9124 --- [           main] org.quartz.core.QuartzScheduler          : Scheduler meta-data: Quartz Scheduler (v2.3.2) 'schedulerFactoryBean' with instanceId 'NON_CLUSTERED'
  Scheduler class: 'org.quartz.core.QuartzScheduler' - running locally.
  NOT STARTED.
  Currently in standby mode.
  Number of jobs executed: 0
  Using thread pool 'org.quartz.simpl.SimpleThreadPool' - with 10 threads.
  Using job-store 'org.quartz.simpl.RAMJobStore' - which does not support persistence. and is not clustered.

14.224  INFO 9124 --- [           main] org.quartz.impl.StdSchedulerFactory      : Quartz scheduler 'schedulerFactoryBean' initialized from an externally provided properties instance.
14.224  INFO 9124 --- [           main] org.quartz.impl.StdSchedulerFactory      : Quartz scheduler version: 2.3.2
14.226  INFO 9124 --- [           main] org.quartz.core.QuartzScheduler          : JobFactory set to: org.springframework.scheduling.quartz.AdaptableJobFactory@363c32cc
14.366  INFO 9124 --- [           main] o.s.s.quartz.SchedulerFactoryBean        : Starting Quartz Scheduler now
14.366  INFO 9124 --- [           main] org.quartz.core.QuartzScheduler          : Scheduler schedulerFactoryBean_$_NON_CLUSTERED started.
14.378  INFO 9124 --- [           main] c.r.spring.batch.quartz.scheduler.App    : Started App in 3.979 seconds (JVM running for 4.632)
14.381  INFO 9124 --- [           main] o.s.b.a.b.JobLauncherApplicationRunner   : Running default command line with: []
14.675  INFO 9124 --- [           main] o.s.b.c.l.support.SimpleJobLauncher      : Job: [FlowJob: [name=jobCsvXml]] launched with the following parameters: [{run.id=1}]
14.892  INFO 9124 --- [           main] o.s.batch.core.job.SimpleStepHandler     : Executing step: [step1]
Processing: Person [id=1000, firstName=soumitra, lastName=roy]
Processing: Person [id=1001, firstName=souvik, lastName=sanyal]
Processing: Person [id=1002, firstName=arup, lastName=chatterjee]
Processing: Person [id=1003, firstName=suman, lastName=mukherjee]
Processing: Person [id=1004, firstName=debina, lastName=guha]
Processing: Person [id=1005, firstName=liton, lastName=sarkar]
Processing: Person [id=1006, firstName=debabrata, lastName=poddar]
15.368  INFO 9124 --- [           main] o.s.batch.core.step.AbstractStep         : Step: [step1] executed in 475ms
15.468  INFO 9124 --- [           main] o.s.b.c.l.support.SimpleJobLauncher      : Job: [FlowJob: [name=jobCsvXml]] completed with the following parameters: [{run.id=1}] and the following status: [COMPLETED] in 739ms
16.146  INFO 9124 --- [ryBean_Worker-1] o.s.b.c.l.support.SimpleJobLauncher      : Job: [FlowJob: [name=jobCsvXml]] launched with the following parameters: [{timestamp=1653573976036}]
16.290  INFO 9124 --- [ryBean_Worker-1] o.s.batch.core.job.SimpleStepHandler     : Executing step: [step1]
Processing: Person [id=1000, firstName=soumitra, lastName=roy]
Processing: Person [id=1001, firstName=souvik, lastName=sanyal]
Processing: Person [id=1002, firstName=arup, lastName=chatterjee]
Processing: Person [id=1003, firstName=suman, lastName=mukherjee]
Processing: Person [id=1004, firstName=debina, lastName=guha]
Processing: Person [id=1005, firstName=liton, lastName=sarkar]
Processing: Person [id=1006, firstName=debabrata, lastName=poddar]
16.577  INFO 9124 --- [ryBean_Worker-1] o.s.batch.core.step.AbstractStep         : Step: [step1] executed in 286ms
16.651  INFO 9124 --- [ryBean_Worker-1] o.s.b.c.l.support.SimpleJobLauncher      : Job: [FlowJob: [name=jobCsvXml]] completed with the following parameters: [{timestamp=1653573976036}] and the following status: [COMPLETED] in 479ms
Job Status : COMPLETED
Done
18.108  INFO 9124 --- [ryBean_Worker-2] o.s.b.c.l.support.SimpleJobLauncher      : Job: [FlowJob: [name=jobCsvXml]] launched with the following parameters: [{timestamp=1653573978004}]
18.239  INFO 9124 --- [ryBean_Worker-2] o.s.batch.core.job.SimpleStepHandler     : Executing step: [step1]
Processing: Person [id=1000, firstName=soumitra, lastName=roy]
Processing: Person [id=1001, firstName=souvik, lastName=sanyal]
Processing: Person [id=1002, firstName=arup, lastName=chatterjee]
Processing: Person [id=1003, firstName=suman, lastName=mukherjee]
Processing: Person [id=1004, firstName=debina, lastName=guha]
Processing: Person [id=1005, firstName=liton, lastName=sarkar]
Processing: Person [id=1006, firstName=debabrata, lastName=poddar]
18.497  INFO 9124 --- [ryBean_Worker-2] o.s.batch.core.step.AbstractStep         : Step: [step1] executed in 257ms
18.566  INFO 9124 --- [ryBean_Worker-2] o.s.b.c.l.support.SimpleJobLauncher      : Job: [FlowJob: [name=jobCsvXml]] completed with the following parameters: [{timestamp=1653573978004}] and the following status: [COMPLETED] in 433ms
Job Status : COMPLETED
Done
20.134  INFO 9124 --- [ryBean_Worker-3] o.s.b.c.l.support.SimpleJobLauncher      : Job: [FlowJob: [name=jobCsvXml]] launched with the following parameters: [{timestamp=1653573980014}]
20.266  INFO 9124 --- [ryBean_Worker-3] o.s.batch.core.job.SimpleStepHandler     : Executing step: [step1]
Processing: Person [id=1000, firstName=soumitra, lastName=roy]
Processing: Person [id=1001, firstName=souvik, lastName=sanyal]
Processing: Person [id=1002, firstName=arup, lastName=chatterjee]
Processing: Person [id=1003, firstName=suman, lastName=mukherjee]
Processing: Person [id=1004, firstName=debina, lastName=guha]
Processing: Person [id=1005, firstName=liton, lastName=sarkar]
Processing: Person [id=1006, firstName=debabrata, lastName=poddar]
20.533  INFO 9124 --- [ryBean_Worker-3] o.s.batch.core.step.AbstractStep         : Step: [step1] executed in 267ms
20.604  INFO 9124 --- [ryBean_Worker-3] o.s.b.c.l.support.SimpleJobLauncher      : Job: [FlowJob: [name=jobCsvXml]] completed with the following parameters: [{timestamp=1653573980014}] and the following status: [COMPLETED] in 444ms
Job Status : COMPLETED
Done
22.134  INFO 9124 --- [ryBean_Worker-4] o.s.b.c.l.support.SimpleJobLauncher      : Job: [FlowJob: [name=jobCsvXml]] launched with the following parameters: [{timestamp=1653573982012}]
22.259  INFO 9124 --- [ryBean_Worker-4] o.s.batch.core.job.SimpleStepHandler     : Executing step: [step1]
Processing: Person [id=1000, firstName=soumitra, lastName=roy]
Processing: Person [id=1001, firstName=souvik, lastName=sanyal]
Processing: Person [id=1002, firstName=arup, lastName=chatterjee]
Processing: Person [id=1003, firstName=suman, lastName=mukherjee]
Processing: Person [id=1004, firstName=debina, lastName=guha]
Processing: Person [id=1005, firstName=liton, lastName=sarkar]
Processing: Person [id=1006, firstName=debabrata, lastName=poddar]
22.525  INFO 9124 --- [ryBean_Worker-4] o.s.batch.core.step.AbstractStep         : Step: [step1] executed in 266ms
22.591  INFO 9124 --- [ryBean_Worker-4] o.s.b.c.l.support.SimpleJobLauncher      : Job: [FlowJob: [name=jobCsvXml]] completed with the following parameters: [{timestamp=1653573982012}] and the following status: [COMPLETED] in 431ms
Job Status : COMPLETED
Done
24.121  INFO 9124 --- [ryBean_Worker-5] o.s.b.c.l.support.SimpleJobLauncher      : Job: [FlowJob: [name=jobCsvXml]] launched with the following parameters: [{timestamp=1653573984012}]
24.243  INFO 9124 --- [ryBean_Worker-5] o.s.batch.core.job.SimpleStepHandler     : Executing step: [step1]
Processing: Person [id=1000, firstName=soumitra, lastName=roy]
Processing: Person [id=1001, firstName=souvik, lastName=sanyal]
Processing: Person [id=1002, firstName=arup, lastName=chatterjee]
Processing: Person [id=1003, firstName=suman, lastName=mukherjee]
Processing: Person [id=1004, firstName=debina, lastName=guha]
Processing: Person [id=1005, firstName=liton, lastName=sarkar]
Processing: Person [id=1006, firstName=debabrata, lastName=poddar]
24.523  INFO 9124 --- [ryBean_Worker-5] o.s.batch.core.step.AbstractStep         : Step: [step1] executed in 280ms
24.592  INFO 9124 --- [ryBean_Worker-5] o.s.b.c.l.support.SimpleJobLauncher      : Job: [FlowJob: [name=jobCsvXml]] completed with the following parameters: [{timestamp=1653573984012}] and the following status: [COMPLETED] in 447ms
Job Status : COMPLETED
Done
26.106  INFO 9124 --- [ryBean_Worker-6] o.s.b.c.l.support.SimpleJobLauncher      : Job: [FlowJob: [name=jobCsvXml]] launched with the following parameters: [{timestamp=1653573986003}]
26.233  INFO 9124 --- [ryBean_Worker-6] o.s.batch.core.job.SimpleStepHandler     : Executing step: [step1]
Processing: Person [id=1000, firstName=soumitra, lastName=roy]
Processing: Person [id=1001, firstName=souvik, lastName=sanyal]
Processing: Person [id=1002, firstName=arup, lastName=chatterjee]
Processing: Person [id=1003, firstName=suman, lastName=mukherjee]
Processing: Person [id=1004, firstName=debina, lastName=guha]
Processing: Person [id=1005, firstName=liton, lastName=sarkar]
Processing: Person [id=1006, firstName=debabrata, lastName=poddar]
26.499  INFO 9124 --- [ryBean_Worker-6] o.s.batch.core.step.AbstractStep         : Step: [step1] executed in 265ms
26.563  INFO 9124 --- [ryBean_Worker-6] o.s.b.c.l.support.SimpleJobLauncher      : Job: [FlowJob: [name=jobCsvXml]] completed with the following parameters: [{timestamp=1653573986003}] and the following status: [COMPLETED] in 435ms
Job Status : COMPLETED
Done
...

In the above output you see the job name, step name and also which row item from csv file is being processed.

You see also from the above output that the step1 has been executed repeatedly until you stop the execution of the task.

You also see the sql scripts have been executed and below tables have been created in the MySQL database with job details for spring batch quartz scheduler.

spring batch quartz scheduler

You will also see the above tables have been populated automatically with their status, job name, version etc.

That’s all. Hope you have understood spring batch quartz scheduler example. In real application you might handle much more complex situation but here you got only a concept of it.

Source Code

Download

Leave a Reply

Your email address will not be published. Required fields are marked *