There are multiple ways to handle exception in Spring Batch. In this post, I am going to show the basic one.
Spring Batch has a JobRepository, which keep track of the job and steps statuses. When a job failed, on restart of the spring batch picks the job where it is left previously.
Using 'ExecutionContext' of the step, lets track the status of step.
For example, I define a tasklet, which set 'step-successful' flag to 1 on its first execution and immediately throws an exception. When the Job is restarted, then application checks the flag 'step-successful' and if it exist then it finishes the step. This is one of the way to control exception handling.
@Bean
public Tasklet task1() {
Tasklet tasklet = new Tasklet() {
@Override
public RepeatStatus execute(StepContribution contribution, ChunkContext chunkContext) throws Exception {
ExecutionContext stepExecutionContext = chunkContext.getStepContext().getStepExecution()
.getExecutionContext();
if (stepExecutionContext.containsKey("step-successful")) {
System.out.println("Step 1: Found the key 'step-successful'");
return RepeatStatus.FINISHED;
}
System.out.println("Executing step 1 for first time");
stepExecutionContext.put("step-successful", 1);
throw new RuntimeException("Step1 failed to execute");
}
};
return tasklet;
}
Find the below working application.
Step 1: Create new maven project ‘exception-handling-hello-world’.
Step 2: Update pom.xml with maven dependencies.
pom.xml
<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.sample.app</groupId>
<artifactId>exception-handling-hello-world</artifactId>
<version>1</version>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.1.6.RELEASE</version>
</parent>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>
<dependencies>
<!-- https://mvnrepository.com/artifact/org.springframework.batch/spring-batch-core -->
<dependency>
<groupId>org.springframework.batch</groupId>
<artifactId>spring-batch-core</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
<dependency>
<groupId>com.h2database</groupId>
<artifactId>h2</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
</dependencies>
</project>
Step 3: Create new package ‘com.sample.app.configuration’ and define JobConfiguration.
JobConfiguration.java
package com.sample.app.configuration;
import org.springframework.batch.core.Job;
import org.springframework.batch.core.Step;
import org.springframework.batch.core.StepContribution;
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.repository.JobRepository;
import org.springframework.batch.core.scope.context.ChunkContext;
import org.springframework.batch.core.step.tasklet.Tasklet;
import org.springframework.batch.item.ExecutionContext;
import org.springframework.batch.repeat.RepeatStatus;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.transaction.PlatformTransactionManager;
@Configuration
@EnableBatchProcessing
public class JobConfiguration {
@Autowired
private JobBuilderFactory jobBuilderFactory;
@Autowired
private StepBuilderFactory stepBuilderFactory;
@Bean
public Tasklet task1() {
Tasklet tasklet = new Tasklet() {
@Override
public RepeatStatus execute(StepContribution contribution, ChunkContext chunkContext) throws Exception {
ExecutionContext stepExecutionContext = chunkContext.getStepContext().getStepExecution()
.getExecutionContext();
if (stepExecutionContext.containsKey("step-successful")) {
System.out.println("Step 1: Found the key 'step-successful'");
return RepeatStatus.FINISHED;
}
System.out.println("Executing step 1 for first time");
stepExecutionContext.put("step-successful", 1);
throw new RuntimeException("Step1 failed to execute");
}
};
return tasklet;
}
@Bean
public Tasklet task2() {
Tasklet tasklet = new Tasklet() {
@Override
public RepeatStatus execute(StepContribution contribution, ChunkContext chunkContext) throws Exception {
ExecutionContext stepExecutionContext = chunkContext.getStepContext().getStepExecution()
.getExecutionContext();
if (stepExecutionContext.containsKey("step-successful")) {
System.out.println("Step 2: Found the key 'step-successful'");
return RepeatStatus.FINISHED;
}
System.out.println("Executing step 2 for first time");
stepExecutionContext.put("step-successful", 1);
throw new RuntimeException("Step2 failed to execute");
}
};
return tasklet;
}
@Bean
public Step step1() throws Exception {
return this.stepBuilderFactory.get("step1").tasklet(task1()).build();
}
@Bean
public Step step2() throws Exception {
return this.stepBuilderFactory.get("step2").tasklet(task2()).build();
}
@Bean
public Job myJob(JobRepository jobRepository, PlatformTransactionManager platformTransactionManager)
throws Exception {
return jobBuilderFactory.get("My-First-Job").start(step1()).next(step2()).build();
}
}
Step 4: Define main application class.
App.java
package com.sample.app;
import org.springframework.batch.core.configuration.annotation.EnableBatchProcessing;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@EnableBatchProcessing
@SpringBootApplication
public class App {
public static void main(String[] args) {
SpringApplication.run(App.class, args);
}
}
Total project structure looks like below.
Run App.java.
You will see below messages in console.
Executing step 1 for first time
2020-04-14 12:12:28.976 ERROR 56871 --- [ main] o.s.batch.core.step.AbstractStep : Encountered an error executing step step1 in job My-First-Job
java.lang.RuntimeException: Step1 failed to execute
at com.sample.app.configuration.JobConfiguration$1.execute(JobConfiguration.java:44) ~[classes/:na]
at org.springframework.batch.core.step.tasklet.TaskletStep$ChunkTransactionCallback.doInTransaction(TaskletStep.java:407) ~[spring-batch-core-4.1.2.RELEASE.jar:4.1.2.RELEASE]
......
......
Open the url ‘http://localhost:8080/h2’ in browser. Connect to H2 database using username ‘krishna’ and password ‘password123’.
See the contents of 'BATCH_STEP_EXECUTION_CONTEXT' table
You will see a record with following SHORT_CONTEXT.
{
"batch.taskletType": "com.sample.app.configuration.JobConfiguration$1",
"step-successful": 1,
"batch.stepType": "org.springframework.batch.core.step.tasklet.TaskletStep"
}
As you observe above json, you can see the key "step-successful".
Stop the application and rerun.
Step 1: Found the key 'step-successful'
Executing step 2 for first time
2020-04-14 12:15:15.856 ERROR 56903 --- [ main] o.s.batch.core.step.AbstractStep : Encountered an error executing step step2 in job My-First-Job
java.lang.RuntimeException: Step2 failed to execute
at com.sample.app.configuration.JobConfiguration$2.execute(JobConfiguration.java:68) ~[classes/:na]
.....
.....
Stop the application and rerun, you will see below message in console.
Step 2: Found the key 'step-successful'
You can download complete working application form this link.
https://github.com/harikrishna553/springboot/tree/master/batch/exception-handling-hello-world
No comments:
Post a Comment