Spring Boot + Spring Batch Job Scheduler
Overview
In this example, I'll show you how to use the Spring Batch framework to run multiple batch jobs in parallel.
Spring Batch Tutorial :
Spring Batch Overview - Architecture
Spring Boot + Spring Batch + MySQL Simple Example
Spring Boot + Spring Batch Listener Example
- Spring Boot + Spring Batch Job Scheduler Example
Spring Batch Interview Questions and Answers
Project Structure
Create Spring Boot Maven project
Add the spring-boot-starter-batch in 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.techgeeknext</groupId>
<artifactId>spring-boot-spring-batch-scheduler-jobs</artifactId>
<version>0.0.1-SNAPSHOT</version>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<maven.compiler.source>12</maven.compiler.source>
<maven.compiler.target>12</maven.compiler.target>
</properties>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.4.2</version>
</parent>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-batch</artifactId>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
Application Properties
Add spring.batch.job.enabled
to false
in application.properties to stop all jobs in the context from being executed by default when the application starts up.
spring.batch.job.enabled=false
spring.batch.initializer.enabled=false
spring.main.allow-bean-definition-overriding=true
Initialize a Spring Batch Database: Setting spring.batch.initialize-schema
to ALWAYS
value by default create the tables if you are using an embedded database. For more information can visit
Spring Batch official documentation.
If you don't add this entry in application.properties, it will throw below exception.
INFO 15304 --- [nio-8080-exec-1] c.t.s.c.BatchJobLauncherController
: PreparedStatementCallback; bad SQL grammar [SELECT JOB_INSTANCE_ID,JOB_NAME
from BATCH_JOB_INSTANCE where JOB_NAME = ? and JOB_KEY = ?];
nested exception is java.sql.SQLSyntaxErrorException: Table 'batchexampledb.batch_job_instance'
doesn't exist
Take a look at our suggested posts:
Configure batch job scheduler
In this example, I have created multiple jobs to run in parallel with three jobs that will run at a fixed scheduled time.
Jobs
Jobs are created from steps, where each step can have a reader, a processor, and a writer.TransactionManager
TransactionManager used to begin and commit transactions during processing.JobRepository
JobRepository in Spring Batch allow persistence. It offers CRUD operations for JobLauncher, Job, and Step instantiations.JonLauncher
JonLauncher is a simple interface for launching a Job with a set of JobParameters.
You can configure batch job scheduling with annotation @EnableScheduling
and method annotated with @Scheduled
followed by cron job time details, so that execution logic will execute at given time.
package com.techgeeknext.spring.batch.scheduler.config;
import com.techgeeknext.spring.batch.scheduler.listener.EmployeeJobListener;
import com.techgeeknext.spring.batch.scheduler.listener.EmployeeReadListener;
import com.techgeeknext.spring.batch.scheduler.listener.EmployeeStepListener;
import com.techgeeknext.spring.batch.scheduler.listener.EmployeeWriteListener;
import com.techgeeknext.spring.batch.scheduler.model.Employee;
import com.techgeeknext.spring.batch.scheduler.processor.EmployeeJobProcessor;
import com.techgeeknext.spring.batch.scheduler.reader.EmployeeJobReader;
import com.techgeeknext.spring.batch.scheduler.writer.EmployeeJobWriter;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.batch.core.*;
import org.springframework.batch.core.configuration.JobRegistry;
import org.springframework.batch.core.configuration.annotation.EnableBatchProcessing;
import org.springframework.batch.core.configuration.annotation.JobBuilderFactory;
import org.springframework.batch.core.configuration.annotation.StepBuilderFactory;
import org.springframework.batch.core.configuration.support.JobRegistryBeanPostProcessor;
import org.springframework.batch.core.explore.JobExplorer;
import org.springframework.batch.core.launch.JobLauncher;
import org.springframework.batch.core.launch.JobOperator;
import org.springframework.batch.core.launch.support.RunIdIncrementer;
import org.springframework.batch.core.launch.support.SimpleJobLauncher;
import org.springframework.batch.core.launch.support.SimpleJobOperator;
import org.springframework.batch.core.repository.JobRepository;
import org.springframework.batch.item.ItemProcessor;
import org.springframework.batch.item.ItemReader;
import org.springframework.batch.item.ItemWriter;
import org.springframework.batch.item.support.ListItemReader;
import org.springframework.batch.repeat.RepeatStatus;
import org.springframework.batch.support.transaction.ResourcelessTransactionManager;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.task.TaskExecutor;
import org.springframework.scheduling.annotation.EnableScheduling;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Map;
@Configuration
@EnableScheduling
@EnableBatchProcessing
public class SchedulerJobConfiguration {
private static final Logger LOG = LoggerFactory.getLogger(SchedulerJobConfiguration.class);
@Autowired
private JobExplorer jobExplorer;
@Autowired
private JobRepository jobRepository;
@Autowired
private JobBuilderFactory jobBuilderFactory;
@Autowired
private StepBuilderFactory stepBuilderFactory;
// @Scheduled(cron = "*/5 * * * * *")
@Scheduled(fixedRate = 10000)
public void runJob1() {
Map<String, JobParameter> jobConfigMap = new HashMap<>();
jobConfigMap.put("time", new JobParameter(System.currentTimeMillis()));
JobParameters parameters = new JobParameters(jobConfigMap);
try {
jobLauncher().run(job1(), parameters);
} catch (Exception ex) {
System.err.println(ex.getMessage());
}
}
// @Scheduled(cron = "*/5 * * * * *")
@Scheduled(fixedRate = 10000)
public void runJob2() {
Map<String, JobParameter> jobConfigMap = new HashMap<>();
jobConfigMap.put("time", new JobParameter(System.currentTimeMillis()));
JobParameters parameters = new JobParameters(jobConfigMap);
try {
jobLauncher().run(job2(), parameters);
} catch (Exception ex) {
System.err.println(ex.getMessage());
}
}
// @Scheduled(cron = "*/5 * * * * *")
@Scheduled(fixedRate = 10000)
public void runJob3() {
Map<String, JobParameter> jobConfigMap = new HashMap<>();
jobConfigMap.put("time", new JobParameter(System.currentTimeMillis()));
JobParameters parameters = new JobParameters(jobConfigMap);
try {
jobLauncher().run(job3(), parameters);
} catch (Exception ex) {
System.err.println(ex.getMessage());
}
}
@Bean
public ItemReader<Employee> employeeJobReader() {
return new EmployeeJobReader(new Employee("1100", "TechGeekNext-Employee"));
}
@Bean
public ItemProcessor<Employee, Employee> employeeJobProcessor() {
return new EmployeeJobProcessor();
}
@Bean
public ItemWriter<Employee> employeeJobWriter() {
return new EmployeeJobWriter();
}
@Bean
public ItemReadListener<Employee> employeeReadListener() {
return new EmployeeReadListener<Employee>();
}
@Bean
public StepExecutionListener employeeStepListener() {
return new EmployeeStepListener();
}
@Bean
public ItemWriteListener<Employee> employeeWriteListener() {
return new EmployeeWriteListener<Employee>();
}
@Bean
public JobExecutionListener employeeJobListener() {
return new EmployeeJobListener();
}
@Bean
public Step step1() {
return stepBuilderFactory.get("Step1").listener(employeeStepListener()).listener(employeeReadListener())
.listener(employeeWriteListener()).tasklet((contribution, chunkContext) -> {
LOG.info("-------Tasklet completed--------");
return RepeatStatus.FINISHED;
}).build();
}
@Bean
public Step step2() {
return stepBuilderFactory.get("Step2").listener(employeeStepListener()).listener(employeeReadListener())
.listener(employeeWriteListener()).<String, String>chunk(3)
.reader(new ListItemReader<>(Arrays.asList("11", "22", "33", "44", "55")))
.processor(new ItemProcessor<String, String>() {
@Override
public String process(String item) throws Exception {
return String.valueOf(Integer.parseInt(item) * -1);
}
}).writer(items -> {
for (String item : items) {
LOG.info("items : {} ", items);
}
}).build();
}
@Bean
public Step step3() {
return this.stepBuilderFactory.get("Step3").listener(employeeStepListener()).listener(employeeReadListener())
.listener(employeeWriteListener()).tasklet((contribution, chunkContext) -> {
LOG.info("Step3 tasklet.");
return RepeatStatus.FINISHED;
}).build();
}
@Bean
public Job job1() {
return this.jobBuilderFactory.get("Job1").listener(employeeJobListener()).incrementer(new RunIdIncrementer())
.start(step1()).next(step2()).build();
}
@Bean
public Job job2() {
return this.jobBuilderFactory.get("Job2").listener(employeeJobListener()).incrementer(new RunIdIncrementer())
.start(step3()).build();
}
@Bean
public Job job3() {
Step step = stepBuilderFactory.get("Job3Step").listener(employeeReadListener()).listener(employeeStepListener())
.listener(employeeWriteListener()).<Employee, Employee>chunk(1).reader(employeeJobReader()).processor(employeeJobProcessor())
.writer(employeeJobWriter()).build();
return jobBuilderFactory.get("Job3").listener(employeeJobListener()).start(step).build();
}
@Bean
public ResourcelessTransactionManager transactionManager() {
return new ResourcelessTransactionManager();
}
@Bean
public TaskExecutor taskExecutor() {
ThreadPoolTaskExecutor taskExecutor = new ThreadPoolTaskExecutor();
taskExecutor.setCorePoolSize(15);
taskExecutor.setMaxPoolSize(20);
taskExecutor.setQueueCapacity(30);
return taskExecutor;
}
@Bean
public JobLauncher jobLauncher() throws Exception {
SimpleJobLauncher jobLauncher = new SimpleJobLauncher();
jobLauncher.setTaskExecutor(taskExecutor());
jobLauncher.setJobRepository(jobRepository);
jobLauncher.afterPropertiesSet();
return jobLauncher;
}
@Bean
public JobOperator jobOperator(JobRegistry jobRegistry) throws Exception {
SimpleJobOperator jobOperator = new SimpleJobOperator();
jobOperator.setJobExplorer(jobExplorer);
jobOperator.setJobLauncher(jobLauncher());
jobOperator.setJobRegistry(jobRegistry);
jobOperator.setJobRepository(jobRepository);
return jobOperator;
}
@Bean
public JobRegistryBeanPostProcessor jobRegistryBeanPostProcessor(JobRegistry jobRegistry) {
JobRegistryBeanPostProcessor jobRegistryBeanPostProcessor = new JobRegistryBeanPostProcessor();
jobRegistryBeanPostProcessor.setJobRegistry(jobRegistry);
return jobRegistryBeanPostProcessor;
}
}
Read Data With ItemReader
Create EmployeeJobReader Class by implementing ItemReader interface and override read method to read the data.
package com.techgeeknext.spring.batch.scheduler.step;
import com.techgeeknext.spring.batch.scheduler.model.Employee;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.batch.item.ItemReader;
import org.springframework.batch.item.NonTransientResourceException;
import org.springframework.batch.item.ParseException;
import org.springframework.batch.item.UnexpectedInputException;
public class EmployeeJobReader implements ItemReader<Employee> {
private static final Logger LOG = LoggerFactory.getLogger(EmployeeJobReader.class);
private Employee employee;
public EmployeeJobReader(Employee item) {
this.employee = employee;
}
@Override
public Employee read() throws Exception, UnexpectedInputException, ParseException, NonTransientResourceException {
LOG.info("EmployeeJobReader read() employee: {}", employee);
return employee;
}
}
Process Data With ItemProcessor
Create EmployeeJobProcessor class by implementing ItemProcessor interface and override process method to write our business logic.
package com.techgeeknext.spring.batch.scheduler.step;
import com.techgeeknext.spring.batch.scheduler.model.Employee;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.batch.item.ItemProcessor;
public class EmployeeJobProcessor implements ItemProcessor<Employee, Employee> {
private static final Logger LOG = LoggerFactory.getLogger(EmployeeJobProcessor.class);
@Override
public Employee process(Employee employee) throws Exception {
LOG.info("EmployeeJobProcessor process employee: {}", employee);
Employee employee1 = new Employee();
employee1.setId(employee.getId());
employee1.setName(employee.getName());
return employee1;
}
}
Write Data to Destination With ItemWriter
Crate EmployeeJobWriter class, implement ItemWriter interface and override write method to write received data to destination.
package com.techgeeknext.spring.batch.scheduler.step;
import com.techgeeknext.spring.batch.scheduler.model.Employee;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.batch.item.ItemWriter;
import java.util.List;
public class EmployeeJobWriter implements ItemWriter<Employee> {
private static final Logger LOG = LoggerFactory.getLogger(EmployeeJobWriter.class);
private Employee emp = new Employee();
@Override
public void write(List<? extends Employee> employee) throws Exception {
LOG.info("EmployeeJobWriter write() employee: {}", employee);
emp.setId(employee.get(0).getId());
emp.setName(employee.get(0).getName());
}
public Employee getOutput() {
return emp;
}
}
Listeners
I have created listeners for ItemReader, ItemWriter, Step, Job. Spring Batch Listeners allow in intercepting Job or Step executions and allowing some activities to be performed.
Read Listener
While reading input, you can do some activities. Before and after reading the input, you can do several tasks.
package com.techgeeknext.spring.batch.scheduler.listener;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.batch.core.ItemReadListener;
import org.springframework.stereotype.Component;
@Component
public class EmployeeReadListener<T> implements ItemReadListener<T> {
private static final Logger LOG = LoggerFactory.getLogger(EmployeeReadListener.class);
@Override
public void beforeRead()
{
System.out.println("ReaderListener::beforeRead()");
}
@Override
public void afterRead(T employee) {
LOG.info("EmployeeReadListener afterRead employee: {}", employee);
}
@Override
public void onReadError(Exception ex) {
LOG.info("EmployeeReadListener onReadError exception: {}", ex);
}
}
Write Listener
You can execute certain activities before and after writing your data, much like the read listener.
package com.techgeeknext.spring.batch.scheduler.listener;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.batch.core.ItemWriteListener;
import org.springframework.stereotype.Component;
import java.util.List;
@Component
public class EmployeeWriteListener<S> implements ItemWriteListener<S> {
private static final Logger LOG = LoggerFactory.getLogger(EmployeeWriteListener.class);
@Override
public void beforeWrite(List<? extends S> employees) {
LOG.info("EmployeeWriteListener beforeWrite employee: {}", employees);
}
@Override
public void afterWrite(List<? extends S> employees) {
LOG.info("EmployeeWriteListener afterWrite employee: {}", employees);
}
@Override
public void onWriteError(Exception exception, List<? extends S> employee) {
LOG.info("EmployeeWriteListener onWriteError employee {} with exception", employee, exception);
}
}
Step Listener
You can do several activities before and after each step execution.
package com.techgeeknext.spring.batch.scheduler.listener;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.batch.core.ExitStatus;
import org.springframework.batch.core.StepExecution;
import org.springframework.batch.core.StepExecutionListener;
import org.springframework.stereotype.Component;
@Component
public class EmployeeStepListener implements StepExecutionListener {
private static final Logger LOG = LoggerFactory.getLogger(EmployeeStepListener.class);
@Override
public void beforeStep(StepExecution stepExecution) {
LOG.info("EmployeeStepListener beforeStep with Step {} and completed for job {}", stepExecution.getStepName(),stepExecution.getJobExecution().getJobInstance().getJobName());
}
@Override
public ExitStatus afterStep(StepExecution stepExecution) {
LOG.info("EmployeeStepListener afterStep with Step {} and completed for job {}", stepExecution.getStepName(),stepExecution.getJobExecution().getJobInstance().getJobName());
return null;
}
}
Job Listener
You can use this job listener to keep track of the status of each job and even stop one.
package com.techgeeknext.spring.batch.scheduler.listener;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.batch.core.BatchStatus;
import org.springframework.batch.core.JobExecution;
import org.springframework.batch.core.JobExecutionListener;
import org.springframework.batch.core.launch.JobExecutionNotRunningException;
import org.springframework.batch.core.launch.JobOperator;
import org.springframework.batch.core.launch.NoSuchJobExecutionException;
import org.springframework.beans.factory.annotation.Autowired;
public class EmployeeJobListener implements JobExecutionListener {
private static final Logger LOG = LoggerFactory.getLogger(EmployeeJobListener.class);
private JobExecution activeJob;
@Autowired
private JobOperator jobOperator;
@Override
public void beforeJob(JobExecution jobExecution) {
final String jobName = jobExecution.getJobInstance().getJobName();
final BatchStatus batchStatus = jobExecution.getStatus();
LOG.info("EmployeeJobListener beforeJob with job {} and status {}", jobName,batchStatus.isRunning());
synchronized (jobExecution) {
if (activeJob != null && activeJob.isRunning()) {
LOG.info("EmployeeJobListener beforeJob isRunning with job {} and status {}", jobName,batchStatus.isRunning());
try {
jobOperator.stop(jobExecution.getId());
} catch (NoSuchJobExecutionException | JobExecutionNotRunningException e) {
e.printStackTrace();
}
} else {
activeJob = jobExecution;
}
}
}
@Override
public void afterJob(JobExecution jobExecution) {
final String jobName = jobExecution.getJobInstance().getJobName();
final BatchStatus jobExecutionStatus = jobExecution.getStatus();
LOG.info("EmployeeJobListener afterJob afterJob with job {} and status {}", jobName,jobExecutionStatus.getBatchStatus());
synchronized (jobExecution) {
if (jobExecution == activeJob) {
activeJob = null;
}
}
}
}
Test
When you run the application, you'll notice that all of the jobs started one after the other and run in parallel. . ____ _ __ _ _
/\\ / ___'_ __ _ _(_)_ __ __ _ \ \ \ \
( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \
\\/ ___)| |_)| | | | | || (_| | ) ) ) )
' |____| .__|_| |_|_| |_\__, | / / / /
=========|_|==============|___/=/_/_/_/
:: Spring Boot :: (v2.4.2)
18:42:35.198 INFO 15040 --- [main] .j.SpringBootSpringBatchSchedulerJobsApp : Starting SpringBootSpringBatchSchedulerJobsApp using Java 12.0.2 (D:\spring-boot-spring-batch-scheduler-jobs\target\classes started D:\spring-boot-spring-batch-scheduler-jobs)
18:42:35.200 INFO 15040 --- [main] .j.SpringBootSpringBatchSchedulerJobsApp : No active profile set, falling back to default profiles: default
18:42:35.679 INFO 15040 --- [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)
18:42:35.709 INFO 15040 --- [main] trationDelegate$BeanPostProcessorChecker : Bean 'jobExplorer' of type [com.sun.proxy.$Proxy34] is not eligible for getting processed by all BeanPostProcessors (for example: not eligible for auto-proxying)
18:42:35.713 INFO 15040 --- [main] trationDelegate$BeanPostProcessorChecker : Bean 'jobRepository' of type [com.sun.proxy.$Proxy32] is not eligible for getting processed by all BeanPostProcessors (for example: not eligible for auto-proxying)
18:42:35.715 INFO 15040 --- [main] trationDelegate$BeanPostProcessorChecker : Bean 'jobBuilders' of type [org.springframework.batch.core.configuration.annotation.JobBuilderFactory] is not eligible for getting processed by all BeanPostProcessors (for example: not eligible for auto-proxying)
18:42:35.716 INFO 15040 --- [main] trationDelegate$BeanPostProcessorChecker : Bean 'stepBuilders' of type [org.springframework.batch.core.configuration.annotation.StepBuilderFactory] is not eligible for getting processed by all BeanPostProcessors (for example: not eligible for auto-proxying)
18:42:35.717 INFO 15040 --- [main] trationDelegate$BeanPostProcessorChecker : Bean 'schedulerJobConfiguration' of type [com.techgeeknext.spring.batch.scheduler.jobs.config.SchedulerJobConfiguration$$EnhancerBySpringCGLIB$$de66b35b] is not eligible for getting processed by all BeanPostProcessors (for example: not eligible for auto-proxying)
18:42:35.723 INFO 15040 --- [main] trationDelegate$BeanPostProcessorChecker : Bean 'jobRegistry' of type [com.sun.proxy.$Proxy38] is not eligible for getting processed by all BeanPostProcessors (for example: not eligible for auto-proxying)
18:42:35.744 INFO 15040 --- [main] o.s.s.concurrent.ThreadPoolTaskExecutor : Initializing ExecutorService 'taskExecutor'
18:42:35.795 WARN 15040 --- [main] o.s.b.c.c.a.DefaultBatchConfigurer : No datasource was provided...using a Map based JobRepository
18:42:35.795 WARN 15040 --- [main] o.s.b.c.c.a.DefaultBatchConfigurer : No transaction manager was provided, using a ResourcelessTransactionManager
18:42:35.808 INFO 15040 --- [main] o.s.b.c.l.support.SimpleJobLauncher : No TaskExecutor has been set, defaulting to synchronous executor.
18:42:35.864 INFO 15040 --- [main] o.s.s.c.ThreadPoolTaskScheduler : Initializing ExecutorService 'taskScheduler'
18:42:35.904 INFO 15040 --- [main] .j.SpringBootSpringBatchSchedulerJobsApp : Started SpringBootSpringBatchSchedulerJobsApp in 1.161 seconds (JVM running for 1.491)
18:42:35.946 INFO 15040 --- [ taskExecutor-1] o.s.b.c.l.support.SimpleJobLauncher : Job: [SimpleJob: [name=Job1]] launched with the following parameters: [{time=1623762755899}]
18:42:35.951 INFO 15040 --- [ taskExecutor-2] o.s.b.c.l.support.SimpleJobLauncher : Job: [SimpleJob: [name=Job2]] launched with the following parameters: [{time=1623762755945}]
18:42:35.957 INFO 15040 --- [ taskExecutor-3] o.s.b.c.l.support.SimpleJobLauncher : Job: [SimpleJob: [name=Job3]] launched with the following parameters: [{time=1623762755951}]
18:42:35.975 INFO 15040 --- [ taskExecutor-2] c.t.s.b.s.j.l.EmployeeJobListener:
EmployeeJobListener beforeJob with job Job2 and status true
18:42:35.974 INFO 15040 --- [ taskExecutor-1] c.t.s.b.s.j.l.EmployeeJobListener:
EmployeeJobListener beforeJob with job Job1 and status true
18:42:35.976 INFO 15040 --- [ taskExecutor-3] c.t.s.b.s.j.l.EmployeeJobListener:
EmployeeJobListener beforeJob with job Job3 and status true
18:42:35.976 INFO 15040 --- [ taskExecutor-3] c.t.s.b.s.j.l.EmployeeJobListener:
EmployeeJobListener beforeJob isRunning with job Job3 and status true
18:42:35.976 INFO 15040 --- [ taskExecutor-1] c.t.s.b.s.j.l.EmployeeJobListener:
EmployeeJobListener beforeJob isRunning with job Job1 and status true
18:42:35.980 INFO 15040 --- [ taskExecutor-2] o.s.batch.core.job.SimpleStepHandler :
Executing step: [Step3]
18:42:35.985 INFO 15040 --- [ taskExecutor-2] c.t.s.b.s.j.l.EmployeeStepListener :
EmployeeStepListener beforeStep with Step Step3 and completed for job Job2
18:42:35.993 INFO 15040 --- [ taskExecutor-2] c.t.s.b.s.j.c.SchedulerJobConfiguration :
Step3 tasklet.
18:42:35.997 INFO 15040 --- [ taskExecutor-1] o.s.batch.core.job.SimpleStepHandler :
Executing step: [Step1]
18:42:35.998 INFO 15040 --- [ taskExecutor-3] o.s.batch.core.job.SimpleStepHandler :
Executing step: [Job3Step]
18:42:35.999 INFO 15040 --- [ taskExecutor-2] c.t.s.b.s.j.l.EmployeeStepListener :
EmployeeStepListener afterStep with Step Step3 and completed for job Job2
18:42:36.000 INFO 15040 --- [ taskExecutor-3] o.s.b.c.r.support.SimpleJobRepository :
Parent JobExecution is stopped, so passing message on to StepExecution
18:42:36.000 INFO 15040 --- [ taskExecutor-1] o.s.b.c.r.support.SimpleJobRepository :
Parent JobExecution is stopped, so passing message on to StepExecution
18:42:36.000 INFO 15040 --- [ taskExecutor-1] c.t.s.b.s.j.l.EmployeeStepListener :
EmployeeStepListener beforeStep with Step Step1 and completed for job Job1
18:42:36.000 INFO 15040 --- [ taskExecutor-3] c.t.s.b.s.j.l.EmployeeStepListener :
EmployeeStepListener beforeStep with Step Job3Step and completed for job Job3
18:42:36.002 INFO 15040 --- [ taskExecutor-1] o.s.b.c.s.ThreadStepInterruptionPolicy :
Step interrupted through StepExecution
18:42:36.002 INFO 15040 --- [ taskExecutor-3] o.s.b.c.s.ThreadStepInterruptionPolicy :
Step interrupted through StepExecution
18:42:36.003 INFO 15040 --- [ taskExecutor-1] o.s.batch.core.step.AbstractStep :
Encountered interruption executing step Step1 in job Job1 : Job interrupted status detected.
18:42:36.003 INFO 15040 --- [ taskExecutor-3] o.s.batch.core.step.AbstractStep :
Encountered interruption executing step Job3Step in job Job3 : Job interrupted status detected.
18:42:36.003 INFO 15040 --- [ taskExecutor-1] c.t.s.b.s.j.l.EmployeeStepListener :
EmployeeStepListener afterStep with Step Step1 and completed for job Job1
18:42:36.003 INFO 15040 --- [ taskExecutor-3] c.t.s.b.s.j.l.EmployeeStepListener :
EmployeeStepListener afterStep with Step Job3Step and completed for job Job3
18:42:36.009 INFO 15040 --- [ taskExecutor-1] o.s.batch.core.step.AbstractStep :
Step: [Step1] executed in 11ms
18:42:36.009 INFO 15040 --- [ taskExecutor-2] o.s.batch.core.step.AbstractStep :
Step: [Step3] executed in 28ms
18:42:36.009 INFO 15040 --- [ taskExecutor-3] o.s.batch.core.step.AbstractStep :
Step: [Job3Step] executed in 10ms
18:42:36.011 INFO 15040 --- [ taskExecutor-1] o.s.b.c.r.support.SimpleJobRepository : Parent JobExecution is stopped, so passing message on to StepExecution
18:42:36.011 INFO 15040 --- [ taskExecutor-3] o.s.b.c.r.support.SimpleJobRepository : Parent JobExecution is stopped, so passing message on to StepExecution
18:42:36.012 INFO 15040 --- [ taskExecutor-1] o.s.batch.core.job.AbstractJob: Encountered interruption executing job: Job interrupted by step execution
18:42:36.013 INFO 15040 --- [ taskExecutor-3] o.s.batch.core.job.AbstractJob: Encountered interruption executing job: Job interrupted by step execution
18:42:36.014 INFO 15040 --- [ taskExecutor-2] c.t.s.b.s.j.l.EmployeeJobListener:
EmployeeJobListener afterJob afterJob with job Job2 and status COMPLETED
18:42:36.014 INFO 15040 --- [ taskExecutor-3] c.t.s.b.s.j.l.EmployeeJobListener:
EmployeeJobListener afterJob afterJob with job Job3 and status STOPPED
18:42:36.014 INFO 15040 --- [ taskExecutor-1] c.t.s.b.s.j.l.EmployeeJobListener: EmployeeJobListener afterJob afterJob with job Job1 and status STOPPED
18:42:36.018 INFO 15040 --- [ taskExecutor-2] o.s.b.c.l.support.SimpleJobLauncher : Job: [SimpleJob: [name=Job2]] completed with the following parameters: [{time=1623762755945}] and the following status: [COMPLETED] in 44ms
18:42:36.018 INFO 15040 --- [ taskExecutor-1] o.s.b.c.l.support.SimpleJobLauncher : Job: [SimpleJob: [name=Job1]] completed with the following parameters: [{time=1623762755899}] and the following status: [STOPPED] in 44ms
18:42:36.018 INFO 15040 --- [ taskExecutor-3] o.s.b.c.l.support.SimpleJobLauncher : Job: [SimpleJob: [name=Job3]] completed with the following parameters: [{time=1623762755951}] and the following status: [STOPPED] in 44ms
18:42:45.920 INFO 15040 --- [ taskExecutor-4] o.s.b.c.l.support.SimpleJobLauncher : Job: [SimpleJob: [name=Job1]] launched with the following parameters: [{time=1623762765904}]
18:42:45.927 INFO 15040 --- [ taskExecutor-4] c.t.s.b.s.j.l.EmployeeJobListener: EmployeeJobListener beforeJob with job Job1 and status true
18:42:45.929 INFO 15040 --- [ taskExecutor-5] o.s.b.c.l.support.SimpleJobLauncher : Job: [SimpleJob: [name=Job2]] launched with the following parameters: [{time=1623762765919}]
18:42:45.938 INFO 15040 --- [ taskExecutor-4] o.s.batch.core.job.SimpleStepHandler : Executing step: [Step1]
18:42:45.939 INFO 15040 --- [ taskExecutor-5] c.t.s.b.s.j.l.EmployeeJobListener: EmployeeJobListener beforeJob with job Job2 and status true
18:42:45.940 INFO 15040 --- [ taskExecutor-5] c.t.s.b.s.j.l.EmployeeJobListener: EmployeeJobListener beforeJob isRunning with job Job2 and status true
18:42:45.942 INFO 15040 --- [ taskExecutor-6] o.s.b.c.l.support.SimpleJobLauncher : Job: [SimpleJob: [name=Job3]] launched with the following parameters: [{time=1623762765928}]
18:42:45.946 INFO 15040 --- [ taskExecutor-4] c.t.s.b.s.j.l.EmployeeStepListener : EmployeeStepListener beforeStep with Step Step1 and completed for job Job1
18:42:45.948 INFO 15040 --- [ taskExecutor-6] c.t.s.b.s.j.l.EmployeeJobListener: EmployeeJobListener beforeJob with job Job3 and status true
18:42:45.949 INFO 15040 --- [ taskExecutor-6] c.t.s.b.s.j.l.EmployeeJobListener: EmployeeJobListener beforeJob isRunning with job Job3 and status true
18:42:45.949 INFO 15040 --- [ taskExecutor-4] c.t.s.b.s.j.c.SchedulerJobConfiguration :
-------Tasklet completed--------
18:42:45.952 INFO 15040 --- [ taskExecutor-4] c.t.s.b.s.j.l.EmployeeStepListener :
EmployeeStepListener afterStep with Step Step1 and completed for job Job1
18:42:45.954 INFO 15040 --- [ taskExecutor-5] o.s.batch.core.job.SimpleStepHandler :
Executing step: [Step3]
18:42:45.954 INFO 15040 --- [ taskExecutor-4] o.s.batch.core.step.AbstractStep :
Step: [Step1] executed in 15ms
18:42:45.956 INFO 15040 --- [ taskExecutor-5] o.s.b.c.r.support.SimpleJobRepository : Parent JobExecution is stopped, so passing message on to StepExecution
18:42:45.956 INFO 15040 --- [ taskExecutor-5] c.t.s.b.s.j.l.EmployeeStepListener : EmployeeStepListener beforeStep with Step Step3 and completed for job Job2
18:42:45.957 INFO 15040 --- [ taskExecutor-5] o.s.b.c.s.ThreadStepInterruptionPolicy : Step interrupted through StepExecution
18:42:45.957 INFO 15040 --- [ taskExecutor-6] o.s.batch.core.job.SimpleStepHandler : Executing step: [Job3Step]
18:42:45.958 INFO 15040 --- [ taskExecutor-5] o.s.batch.core.step.AbstractStep : Encountered interruption executing step Step3 in job Job2 : Job interrupted status detected.
18:42:45.958 INFO 15040 --- [ taskExecutor-5] c.t.s.b.s.j.l.EmployeeStepListener :
EmployeeStepListener afterStep with Step Step3 and completed for job Job2
18:42:45.959 INFO 15040 --- [ taskExecutor-5] o.s.batch.core.step.AbstractStep : Step: [Step3] executed in 5ms
18:42:45.959 INFO 15040 --- [ taskExecutor-6] o.s.b.c.r.support.SimpleJobRepository : Parent JobExecution is stopped, so passing message on to StepExecution
18:42:45.959 INFO 15040 --- [ taskExecutor-6] c.t.s.b.s.j.l.EmployeeStepListener :
EmployeeStepListener beforeStep with Step Job3Step and completed for job Job3
18:42:45.960 INFO 15040 --- [ taskExecutor-6] o.s.b.c.s.ThreadStepInterruptionPolicy : Step interrupted through StepExecution
18:42:45.960 INFO 15040 --- [ taskExecutor-4] o.s.batch.core.job.SimpleStepHandler : Executing step: [Step2]
18:42:45.960 INFO 15040 --- [ taskExecutor-5] o.s.b.c.r.support.SimpleJobRepository : Parent JobExecution is stopped, so passing message on to StepExecution
18:42:45.960 INFO 15040 --- [ taskExecutor-6] o.s.batch.core.step.AbstractStep : Encountered interruption executing step Job3Step in job Job3 : Job interrupted status detected.
18:42:45.961 INFO 15040 --- [ taskExecutor-6] c.t.s.b.s.j.l.EmployeeStepListener :
EmployeeStepListener afterStep with Step Job3Step and completed for job Job3
18:42:45.961 INFO 15040 --- [ taskExecutor-5] o.s.batch.core.job.AbstractJob: Encountered interruption executing job: Job interrupted by step execution
18:42:45.962 INFO 15040 --- [ taskExecutor-6] o.s.batch.core.step.AbstractStep : Step: [Job3Step] executed in 5ms
18:42:45.962 INFO 15040 --- [ taskExecutor-5] c.t.s.b.s.j.l.EmployeeJobListener: EmployeeJobListener afterJob afterJob with job Job2 and status STOPPED
18:42:45.962 INFO 15040 --- [ taskExecutor-4] c.t.s.b.s.j.l.EmployeeStepListener : EmployeeStepListener beforeStep with Step Step2 and completed for job Job1
18:42:45.963 INFO 15040 --- [ taskExecutor-6] o.s.b.c.r.support.SimpleJobRepository : Parent JobExecution is stopped, so passing message on to StepExecution
18:42:45.965 INFO 15040 --- [ taskExecutor-6] o.s.batch.core.job.AbstractJob: Encountered interruption executing job: Job interrupted by step execution
18:42:45.965 INFO 15040 --- [ taskExecutor-6] c.t.s.b.s.j.l.EmployeeJobListener:
EmployeeJobListener afterJob afterJob with job Job3 and status STOPPED
18:42:45.965 INFO 15040 --- [ taskExecutor-5] o.s.b.c.l.support.SimpleJobLauncher :
Job: [SimpleJob: [name=Job2]] completed with the following parameters: [{time=1623762765919}] and the following status: [STOPPED] in 32ms
18:42:45.968 INFO 15040 --- [ taskExecutor-6] o.s.b.c.l.support.SimpleJobLauncher :
Job: [SimpleJob: [name=Job3]] completed with the following parameters: [{time=1623762765928}] and the following status: [STOPPED] in 22ms
18:42:45.968 INFO 15040 --- [ taskExecutor-4] c.t.s.b.s.j.c.SchedulerJobConfiguration :
items : [-11, -22, -33]
18:42:45.969 INFO 15040 --- [ taskExecutor-4] c.t.s.b.s.j.c.SchedulerJobConfiguration :
items : [-11, -22, -33]
18:42:45.969 INFO 15040 --- [ taskExecutor-4] c.t.s.b.s.j.c.SchedulerJobConfiguration :
items : [-11, -22, -33]
18:42:45.973 INFO 15040 --- [ taskExecutor-4] c.t.s.b.s.j.c.SchedulerJobConfiguration :
items : [-44, -55]
18:42:45.973 INFO 15040 --- [ taskExecutor-4] c.t.s.b.s.j.c.SchedulerJobConfiguration :
items : [-44, -55]
18:42:45.977 INFO 15040 --- [ taskExecutor-4] c.t.s.b.s.j.l.EmployeeStepListener :
EmployeeStepListener afterStep with Step Step2 and completed for job Job1
18:42:45.978 INFO 15040 --- [ taskExecutor-4] o.s.batch.core.step.AbstractStep :
Step: [Step2] executed in 18ms
18:42:45.990 INFO 15040 --- [ taskExecutor-4] c.t.s.b.s.j.l.EmployeeJobListener:
EmployeeJobListener afterJob afterJob with job Job1 and status COMPLETED
18:42:45.993 INFO 15040 --- [ taskExecutor-4] o.s.b.c.l.support.SimpleJobLauncher :
Job: [SimpleJob: [name=Job1]] completed with the following parameters:
[{time=1623762765904}] and the following status: [COMPLETED] in 69ms
Download Source Code
The full source code for this article can be found on below.Download it here - Spring Boot + Spring Batch Job Scheduler Example