DEV Community

loizenai
loizenai

Posted on

Spring Boot Batch Restart Job Example

https://loizenai.com/spring-boot-batch-restart-job/

  • Tutorial: "Spring Boot Batch Restart Job Example with SpringBoot RestAPI Implementation"

SpringBatch provides a restartable mechanics with a Job. So in the tutorial, I will present to you the principle of this function with SpringBoot.

Technologies:

โ€“ Java 1.8
โ€“ Maven
โ€“ Spring Boot
โ€“ Spring Tool Suite

Overview - Spring Boot Batch Restart Job Example

Create a Batch Job which has a simple step with a Reader & Writer.

  • Reader reads a list file-paths then pass them to a Writer. The Write uses file paths to process content of each file.
    We creat a Batch Job, then we test a restartable function base on FileNotFound Error.

  • Note: a Batch Job is created with restartable default. Spring Batch also supports configuration to prevent the restartable function.

    Project Structure - Spring Boot Batch Restart Job Example

    [caption id="attachment_4892" align="alignnone" width="687"]Spring Boot Springbatch Restartable Project Structure - Spring Boot Batch Restart Job Example Spring Boot Springbatch Restartable Project Structure[/caption]

  • Step to do:

  • Create SpringBoot project

  • Create simple step

  • Config a Batch Job

  • Create a Job Launch controller

  • Config Batch Job Database

  • Enable Batch Job

  • Scenario Test

    Create SpringBoot Project

    Open Spring Tool Suite, on main menu, choose File->New->Spring Starter Project, input project info. Press Next then Finish, a Spring Boot project will be created successfully.

  • Open pom.xml file, add Web MVC, PostgreSQL and Spring Batch dependencies:


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

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

<!-- http://mvnrepository.com/artifact/postgresql/postgresql -->
<dependency>
    <groupId>postgresql</groupId>
    <artifactId>postgresql</artifactId>
    <version>9.1-901-1.jdbc4</version>
</dependency>

Create Reader & Writer for Batch Job Step - Spring Boot Batch Restart Job Example Create a Reader class:

package com.loizenai.springbatch.step;
 
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 Reader implements ItemReader{
 
    private String[] files = {"C:\\readfile\\1.txt"};
     
    public static int  count=0;
     
    Logger logger = LoggerFactory.getLogger(this.getClass());
     
    @Override
    public String read() throws Exception, UnexpectedInputException, ParseException, NonTransientResourceException {
         
        if(count < files.length){
            return files[count++];
        }else{
            count=0;
        }
        return null;
    }
}   
Create a Writer class:

package com.loizenai.springbatch.step;
 
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.List;
import java.util.stream.Stream;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.batch.item.ItemWriter;
 
public class Writer implements ItemWriter {
 
    Logger logger = LoggerFactory.getLogger(this.getClass());
     
    @Override
    public void write(List extends String> paths) throws Exception {
        for(String filePath : paths){
            System.out.println("filePath = " + filePath);
            try (Stream stream = Files.lines(Paths.get(filePath))) {
                stream.forEach(System.out::println);
            } catch (IOException e) {
                Reader.count = 0;
                throw(e);
            }
        }
    }
    
}

Configure a SpringBoot Batch Job

Config a Simple Batch Job:


package com.loizenai.springbatch.config;
 
import org.springframework.batch.core.Job;
import org.springframework.batch.core.Step;
import org.springframework.batch.core.configuration.annotation.JobBuilderFactory;
import org.springframework.batch.core.configuration.annotation.StepBuilderFactory;
import org.springframework.batch.core.launch.support.RunIdIncrementer;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

import com.loizenai.springbatch.step.Reader;
import com.loizenai.springbatch.step.Writer;
 
@Configuration
public class BatchConfig {
 
    @Autowired
    public JobBuilderFactory jobBuilderFactory;
 
    @Autowired
    public StepBuilderFactory stepBuilderFactory;
 
     
    @Bean
    public Job job() {
        return jobBuilderFactory.get("job")
                .incrementer(new RunIdIncrementer())
                .flow(step1())
                .end()
                .build();
    }
 
    @Bean
    public Step step1() {
        return stepBuilderFactory.get("step1")
                . chunk(1)
                .reader(new Reader())
                .writer(new Writer())
                .build();
    }

}

Create a SpringBoot Batch Job Launch Controller

  • Create a simple Controller for Job Launching:

package com.loizenai.springbatch.controller;
 
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.batch.core.Job;
import org.springframework.batch.core.JobExecution;
import org.springframework.batch.core.JobParameters;
import org.springframework.batch.core.launch.JobLauncher;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
 
@RestController
public class JobLauncherController {
 
    @Autowired
    JobLauncher jobLauncher;
 
    @Autowired
    Job job;
 
    @RequestMapping("/launchjob")
    public String handle() throws Exception {
 
        Logger logger = LoggerFactory.getLogger(this.getClass());
        JobExecution jobExecution = null;
        try {
            jobExecution = jobLauncher.run(job, new JobParameters());
        } catch (Exception e) {
            logger.info(e.getMessage());
        }
 
        return "jobExecution's info: Id = " + jobExecution.getId() + " ,status = " + jobExecution.getExitStatus();
    }
}

Configure SpringBoot Batch Job Database

Config DataBase's info for Spring Batch:

code class="language-java">
spring.datasource.url=jdbc:postgresql://localhost/testdb
spring.datasource.username=postgres
spring.datasource.password=123
spring.batch.job.enabled=false

Enable SpringBoot Batch Job

In main class of Spring Boot project, use @EnableBatchProcessing to enable Spring Batch.


package com.loizenai.springbatch;

import org.springframework.batch.core.configuration.annotation.EnableBatchProcessing;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

@SpringBootApplication
@EnableBatchProcessing
public class SpringBatchRestartableFunctionApplication {

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

Scenario Testing - SpringBoot Batch Restartable Function

  • Build & Run the Spring Boot Batch Job.

The Reader will forward a file name: C:\readfile\1.txt to Writer. Delete a file C:\readfile\1.txt, then makes a launch request: http://localhost:8080/launchjob

A Job Instance is created with Empty Parameters:


Job: [FlowJob: [name=job]] launched with the following parameters: [{}]

Investigate Server Logs:

Result FAIL:


Job: [FlowJob: [name=job]] completed with the following parameters: [{}] and the following status: [FAILED]

Cause:


2017-01-13 20:21:29.216  INFO 11012 --- [nio-8080-exec-1] o.s.batch.core.job.SimpleStepHandler     : Executing step: [step1]
filePath = C:\readfile\1.txt
2017-01-13 20:21:39.781 ERROR 11012 --- [nio-8080-exec-1] o.s.batch.core.step.AbstractStep         : Encountered an error executing step step1 in job job

java.nio.file.NoSuchFileException: C:\readfile\1.txt
    at sun.nio.fs.WindowsException.translateToIOException(WindowsException.java:79) ~[na:1.8.0_101]
    at sun.nio.fs.WindowsException.rethrowAsIOException(WindowsException.java:97) ~[na:1.8.0_101]
    at sun.nio.fs.WindowsException.rethrowAsIOException(WindowsException.java:102) ~[na:1.8.0_101]
    at sun.nio.fs.WindowsFileSystemProvider.newByteChannel(WindowsFileSystemProvider.java:230) ~[na:1.8.0_101]
  • Now create a C:\readfile\1.txt with content:

line1
line2
line3
line4
line5

Then restart the Job Instance (with parameters: [{}]) again by http://localhost:8080/launchjob

Result: COMPLETED


Job: [FlowJob: [name=job]] completed with the following parameters: [{}] and the following status: [COMPLETED]

filePath = C:\readfile\1.txt
line1
line2
line3
line4
line5

Make the launching request again: http://localhost:8080/launchjob, result:


Step already complete or not restartable, so no action to execute: StepExecution: id=1000, version=4, name=step1, status=COMPLETED, exitStatus=COMPLETED, readCount=1, filterCount=0, writeCount=1 readSkipCount=0, writeSkipCount=0, processSkipCount=0, commitCount=2, rollbackCount=0, exitDescription=
Job: [FlowJob: [name=job]] completed with the following parameters: [{}] and the following status: [COMPLETED]

Prevent Restartable

SpringBatch also supports a config to prevent restartable ability of a Job even if a Job Instance's Execution with FAIL status.

How to configure? Just use a little code .preventRestart(), details with BatchConfig class:


package com.loizenai.springbatch.config;
 
import org.springframework.batch.core.Job;
import org.springframework.batch.core.Step;
import org.springframework.batch.core.configuration.annotation.JobBuilderFactory;
import org.springframework.batch.core.configuration.annotation.StepBuilderFactory;
import org.springframework.batch.core.launch.support.RunIdIncrementer;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

import com.loizenai.springbatch.step.Reader;
import com.loizenai.springbatch.step.Writer;
 
@Configuration
public class BatchConfig {
 
    @Autowired
    public JobBuilderFactory jobBuilderFactory;
 
    @Autowired
    public StepBuilderFactory stepBuilderFactory;
 
     
    @Bean
    public Job job() {
        return jobBuilderFactory.get("job")
                .preventRestart()
                .incrementer(new RunIdIncrementer())
                .flow(step1())
                .end()
                .build();
    }
 
    @Bean
    public Step step1() {
        return stepBuilderFactory.get("step1")
                . chunk(1)
                .reader(new Reader())
                .writer(new Writer())
                .build();
    }

}

Build & Run Spring Boot project again, delete C:\readfile\1.txt file.

Make a request: http://localhost:8080/launchjob, -> Result: FAIL with Log details:


2017-01-13 20:35:08.664  INFO 5092 --- [nio-8080-exec-1] o.s.batch.core.job.SimpleStepHandler     : Executing step: [step1]
filePath = C:\readfile\1.txt
2017-01-13 20:35:21.296 ERROR 5092 --- [nio-8080-exec-1] o.s.batch.core.step.AbstractStep         : Encountered an error executing step step1 in job job

java.nio.file.NoSuchFileException: C:\readfile\1.txt
    at sun.nio.fs.WindowsException.translateToIOException(WindowsException.java:79) ~[na:1.8.0_101]
    at sun.nio.fs.WindowsException.rethrowAsIOException(WindowsException.java:97) ~[na:1.8.0_101]
    at sun.nio.fs.WindowsException.rethrowAsIOException(WindowsException.java:102) ~[na:1.8.0_101]
    at sun.nio.fs.WindowsFileSystemProvider.newByteChannel(WindowsFileSystemProvider.java:230) ~[na:1.8.0_101]

Create a file: C:\readfile\1.txt, then makes request http://localhost:8080/launchjob again, result FAIL:


2017-01-13 20:37:31.672  INFO 5092 --- [nio-8080-exec-3] c.j.s.controller.JobLauncherController   : JobInstance already exists and is not restartable
2017-01-13 20:37:31.693 ERROR 5092 --- [nio-8080-exec-3] o.a.c.c.C.[.[.[/].[dispatcherServlet]    : Servlet.service() for servlet [dispatcherServlet] in context with path [] threw exception [Request processing failed; nested exception is java.lang.NullPointerException] with root cause

java.lang.NullPointerException: null
    at com.loizenai.springbatch.controller.JobLauncherController.handle(JobLauncherController.java:33) ~[classes/:na]
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) ~[na:1.8.0_101]

Read More - Spring Boot Batch Restart Job Example

Related posts:

Top comments (0)