I have to issues with Spring Batch. Both regarding the JobParameters that are passed in via the command line.
First issue:
I'm using Eclipse to develop my application and test it. Therefore, I added Program arguments
to the Run Configurations
. These arguments are:
-ts=${current_date} -path="file.csv"
Running the application will throw an exception. The exception is:
Caused by: org.springframework.batch.core.repository.JobInstanceAlreadyCompleteException:
A job instance already exists and is complete for parameters={ts=20210211_1631, path=file.csv}.
If you want to run this job again, change the parameters.
As you can see the JobParameters should be different for each execution, because one of the parameters is a timestamp that is changing each minute. I had a look at this question Spring Batch: execute same job with different parameters, but here the solution is to set a new name for each job execution (e.g. name + System.currentTimeMillis()
). Is there another solution to this problem? I don't want to create a 'random' name for the job each time it is executed. My Job is implemented as this:
@Bean(name = "inJob")
public Job inJob(JobRepository jobRepository) {
return jobBuilderFactory.get("inJob")
.repository(jobRepository)
.incrementer(new RunIdIncrementer())
.start(truncateTable())
.next(loadCsv())
.next(updateType())
.build();
}
I'm using a custom implementation of the JobRepository to store the metadata in a different database schema:
@Override
public JobRepository createJobRepository() throws Exception {
JobRepositoryFactoryBean factory = new JobRepositoryFactoryBean();
factory.setDataSource(dataSource);
factory.setTransactionManager(transactionManager);
factory.setTablePrefix("logging.BATCH_");
return factory.getObject();
}
Second issue:
My second issue is accessing the JobParameters. One of the above parameters is a file path I want to use in the FlatFileItemReader:
@Bean(name = "inReader")
@StepScope
public FlatFileItemReader<CsvInfile> inReader() {
FlatFileItemReader<CsvInfile> reader = new FlatFileItemReader<CsvInfile>();
reader.setResource(new FileSystemResource(path));
DefaultLineMapper<CsvInfile> lineMapper = new DefaultLineMapper<>();
lineMapper.setFieldSetMapper(new CsvInfileFieldMapper());
DelimitedLineTokenizer tokenizer = new DelimitedLineTokenizer();
tokenizer.setDelimiter("|");
tokenizer.setNames(ccn.names);
lineMapper.setLineTokenizer(tokenizer);
reader.setLineMapper(lineMapper);
reader.setLinesToSkip(1);
reader.open(new ExecutionContext());
return reader;
}
To get the path from the JobParameters I used the BeforeStep annotation to load the JobParameters and copy them on local variables. Unfortunately this is not working. The variable will be null
and the execution fails, because the file can't be opened.
private String path;
@BeforeStep
public void beforeStep(StepExecution stepExecution) {
JobParameters jobParameters = stepExecution.getJobParameters();
this.path = jobParameters.getString("path");
}
How can I access the JobParameters within my reader? I want to pass in the file path as command line argument and then read this file.