Spring Batch — Validate Job Parameters

In this example, we’ll validate the Job parameters, and accept those values in the Tasklet.

ParameterValidator.java — Exception for Job to signal that some JobParameters are invalid.

public class ParameterValidator implements JobParametersValidator {

@Override
public void validate(JobParameters jobParameters) throws JobParametersInvalidException {
String fileName = jobParameters.getString("fileName");

if (!StringUtils.hasText(fileName)) {
throw new JobParametersInvalidException("fileName parameter is missing");
} else if (!StringUtils.endsWithIgnoreCase(fileName, "csv")) {
throw new JobParametersInvalidException("fileName parameter does not use csv file extension");
}
}
}

JobParameterIncrementor — Interface for obtaining the next JobParameters in a sequence.

import org.springframework.batch.core.JobParameters;
import org.springframework.batch.core.JobParametersBuilder;
import org.springframework.batch.core.JobParametersIncrementer;

import java.util.Date;

public class DailyJobTimestamper implements JobParametersIncrementer {
@Override
public JobParameters getNext(JobParameters jobParameters) {
return new JobParametersBuilder(jobParameters)
.addDate("currentDate", new Date())
.toJobParameters();
}
}

JobConstantLogger.java

import org.springframework.batch.core.JobExecution;
import org.springframework.batch.core.annotation.AfterJob;
import org.springframework.batch.core.annotation.BeforeJob;

public class JobLoggerListener {

@BeforeJob
public void beforeJob(JobExecution jobExecution) {
final String START_MESSAGE = "----> %s is beginning execution";
System.out.println(String.format(START_MESSAGE, jobExecution.getJobInstance().getJobName()));
}

@AfterJob
public void afterJob(JobExecution jobExecution) {
final String END_MESSAGE = "-----> %s has completed with the status %s";
System.out.println(String.format(END_MESSAGE, jobExecution.getJobInstance().getJobName(),
jobExecution.getStatus()));
}
}

HelloWorldJobApplication.java

import com.apress.springbatch.helloworld.batch.DailyJobTimestamper;
import com.apress.springbatch.helloworld.batch.JobLoggerListener;
import com.apress.springbatch.helloworld.batch.ParameterValidator;
import org.springframework.batch.core.Job;
import org.springframework.batch.core.Step;
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.annotation.StepScope;
import org.springframework.batch.core.job.CompositeJobParametersValidator;
import org.springframework.batch.core.job.DefaultJobParametersValidator;
import org.springframework.batch.core.listener.JobListenerFactoryBean;
import org.springframework.batch.core.step.tasklet.Tasklet;
import org.springframework.batch.repeat.RepeatStatus;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.Bean;

import java.util.Arrays;

import static java.lang.System.out;

@EnableBatchProcessing
@SpringBootApplication
public class HelloWorldJobApplication {

@Autowired
private JobBuilderFactory jobBuilderFactory;

@Autowired
private StepBuilderFactory stepBuilderFactory;

// Composite JobParametersValidator that passes the job parameters through a sequence of injected JobParametersValidators
@Bean
public CompositeJobParametersValidator validator() {
final CompositeJobParametersValidator validator = new CompositeJobParametersValidator();
final DefaultJobParametersValidator defaultJobParametersValidator = new
DefaultJobParametersValidator(new String[]{"fileName"}, new String[]{"name", "currentDate"});

defaultJobParametersValidator.afterPropertiesSet();
validator.setValidators(Arrays.asList(new ParameterValidator(), defaultJobParametersValidator));
return validator;
}

@Bean
public Step step1() {
return this.stepBuilderFactory.get("step1")
.tasklet(this.helloWorldTasklet(null, null)) // late binding
.build();
}


@Bean
public Job job() {
return this.jobBuilderFactory.get("basicJob")
.start(step1())
.validator(validator())
.incrementer(new DailyJobTimestamper())
.listener(JobListenerFactoryBean.getListener(new JobLoggerListener()))
.build();

}

@Bean
@StepScope
public Tasklet helloWorldTasklet(@Value("#{jobParameters['name']}") String name,
@Value("#{jobParameters['fileName']}") String fileName) {

return (stepContribution, chunkContext) -> {
out.println(String.format("Hi %s with file %s, Hello, world from Spring Batch!", name, fileName));
return RepeatStatus.FINISHED;
};
}

public static void main(String[] args) {
SpringApplication.run(HelloWorldJobApplication.class, "name=Prateek", "fileName=customer.csv");
}
}

Loggers —

2021–12–03 18:33:29,528 INFO org.springframework.boot.autoconfigure.batch.JobLauncherCommandLineRunner : Running default command line with: [name=Prateek, fileName=customer.csv]
2021–12–03 18:33:29,716 INFO org.springframework.batch.core.launch.support.SimpleJobLauncher : Job: [SimpleJob: [name=basicJob]] launched with the following parameters: [{name=Prateek, currentDate=1638536609669, fileName=customer.csv}]
— → basicJob is beginning execution
2021–12–03 18:33:29,747 INFO org.springframework.batch.core.job.SimpleStepHandler : Executing step: [step1]
Hi Prateek with file customer.csv, Hello, world from Spring Batch!
— — -> basicJob has completed with the status COMPLETED
2021–12–03 18:33:29,825 INFO org.springframework.batch.core.launch.support.SimpleJobLauncher : Job: [SimpleJob: [name=basicJob]] completed with the following parameters: [{name=Prateek, currentDate=1638536609669, fileName=customer.csv}] and the following status: [COMPLETED]
2021–12–03 18:33:29,825 INFO com.zaxxer.hikari.HikariDataSource : HikariPool-1 — Shutdown initiated…
2021–12–03 18:33:29,841 INFO com.zaxxer.hikari.HikariDataSource : HikariPool-1 — Shutdown completed.