Tuesday, 1 December 2020

Spring Batch : Basic Exception Handling

 

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 

 

 



 

 

 

 

Previous                                                    Next                                                    Home

No comments:

Post a Comment