Closed
Description
Bug description
Spring Batch framework not creating an emty output files when data doesn't flow through that Classifier
with the MultiResourceItemWriter
Environment
Spring Boot v2.7.1, Java version - 11
Steps to reproduce
Here is the code
Employee.java
@AllArgsConstructor
@NoArgsConstructor
@Data
@Builder
public class Employee {
private String empId;
private String firstName;
private String lastName;
private String role;
@Override
public String toString() {
return empId + ","+ firstName+ ","+ lastName+ ","+ role;
}
}
package com.example;
import org.springframework.batch.item.ItemWriter;
import org.springframework.classify.Classifier;
import lombok.Setter;
@Setter
public class EmployeeClassifier implements Classifier<Employee, ItemWriter<? super Employee>> {
private static final long serialVersionUID = 1L;
private ItemWriter<Employee> javaDeveloperFileItemWriter;
private ItemWriter<Employee> pythonDeveloperFileItemWriter;
private ItemWriter<Employee> cloudDeveloperFileItemWriter;
public EmployeeClassifier() {
}
public EmployeeClassifier(ItemWriter<Employee> javaDeveloperFileItemWriter,
ItemWriter<Employee> pythonDeveloperFileItemWriter,
ItemWriter<Employee> cloudDeveloperFileItemWriter) {
this.javaDeveloperFileItemWriter = javaDeveloperFileItemWriter;
this.pythonDeveloperFileItemWriter = pythonDeveloperFileItemWriter;
this.cloudDeveloperFileItemWriter = cloudDeveloperFileItemWriter;
}
@Override
public ItemWriter<? super Employee> classify(Employee employee) {
if(employee.getRole().equals("Java Developer")){
return javaDeveloperFileItemWriter;
}
else if(employee.getRole().equals("Python Developer")){
return pythonDeveloperFileItemWriter;
}
return cloudDeveloperFileItemWriter;
}
}
package com.example;
import org.springframework.batch.item.file.mapping.FieldSetMapper;
import org.springframework.batch.item.file.transform.FieldSet;
import org.springframework.validation.BindException;
public class EmployeeFieldSetMapper implements FieldSetMapper<Employee> {
@Override
public Employee mapFieldSet(FieldSet fieldSet) throws BindException {
return Employee.builder()
.empId(fieldSet.readRawString("empId"))
.firstName(fieldSet.readRawString("firstName"))
.lastName(fieldSet.readRawString("lastName"))
.role(fieldSet.readRawString("role"))
.build();
}
}
package com.example;
import org.springframework.batch.item.file.mapping.FieldSetMapper;
import org.springframework.batch.item.file.transform.FieldSet;
import org.springframework.validation.BindException;
public class EmployeeFieldSetMapper implements FieldSetMapper<Employee> {
@Override
public Employee mapFieldSet(FieldSet fieldSet) throws BindException {
return Employee.builder()
.empId(fieldSet.readRawString("empId"))
.firstName(fieldSet.readRawString("firstName"))
.lastName(fieldSet.readRawString("lastName"))
.role(fieldSet.readRawString("role"))
.build();
}
}
package com.example;
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.item.ItemWriter;
import org.springframework.batch.item.file.FlatFileItemReader;
import org.springframework.batch.item.file.FlatFileItemWriter;
import org.springframework.batch.item.file.builder.FlatFileItemReaderBuilder;
import org.springframework.batch.item.file.builder.FlatFileItemWriterBuilder;
import org.springframework.batch.item.file.builder.MultiResourceItemWriterBuilder;
import org.springframework.batch.item.file.mapping.DefaultLineMapper;
import org.springframework.batch.item.file.transform.DelimitedLineTokenizer;
import org.springframework.batch.item.file.transform.PassThroughLineAggregator;
import org.springframework.batch.item.support.ClassifierCompositeItemWriter;
import org.springframework.batch.item.support.builder.ClassifierCompositeItemWriterBuilder;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.classify.Classifier;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.io.ClassPathResource;
import org.springframework.core.io.FileSystemResource;
@Configuration
public class MyJobConfig {
@Autowired
private JobBuilderFactory jobBuilderFactory;
@Autowired
private StepBuilderFactory stepBuilderFactory;
@Bean
public FlatFileItemReader<Employee> itemReader() {
DelimitedLineTokenizer tokenizer = new DelimitedLineTokenizer();
tokenizer.setNames("empId", "firstName", "lastName", "role");
DefaultLineMapper<Employee> employeeLineMapper = new DefaultLineMapper<>();
employeeLineMapper.setLineTokenizer(tokenizer);
employeeLineMapper.setFieldSetMapper(new EmployeeFieldSetMapper());
employeeLineMapper.afterPropertiesSet();
return new FlatFileItemReaderBuilder<Employee>()
.name("flatFileReader")
.linesToSkip(1)
.resource(new ClassPathResource("employee.csv"))
.lineMapper(employeeLineMapper)
.build();
}
@Bean
public ClassifierCompositeItemWriter<Employee> classifierCompositeItemWriter() throws Exception {
Classifier<Employee, ItemWriter<? super Employee>> classifier =
new EmployeeClassifier(javaDeveloperItemWriter(), pythonDeveloperItemWriter(), cloudDeveloperItemWriter());
return new ClassifierCompositeItemWriterBuilder<Employee>()
.classifier(classifier)
.build();
}
@Bean
public ItemWriter<Employee> javaDeveloperItemWriter() {
FlatFileItemWriter<Employee> itemWriter = new FlatFileItemWriterBuilder<Employee>()
.lineAggregator(new PassThroughLineAggregator<>())
.name("itemsWriter")
.build();
return new MultiResourceItemWriterBuilder<Employee>()
.name("javaDeveloperItemWriter")
.delegate(itemWriter)
.resource(new FileSystemResource("javaDeveloper-employee.csv"))
.itemCountLimitPerResource(2)
.resourceSuffixCreator(index -> "-" + index)
.build();
}
@Bean
public ItemWriter<Employee> pythonDeveloperItemWriter() {
FlatFileItemWriter<Employee> itemWriter = new FlatFileItemWriterBuilder<Employee>()
.lineAggregator(new PassThroughLineAggregator<>())
.name("itemsWriter")
.build();
return new MultiResourceItemWriterBuilder<Employee>()
.name("pythonDeveloperItemWriter")
.delegate(itemWriter)
.resource(new FileSystemResource("pythonDeveloper-employee.csv"))
.itemCountLimitPerResource(2)
.resourceSuffixCreator(index -> "-" + index)
.build();
}
@Bean
public ItemWriter<Employee> cloudDeveloperItemWriter() {
FlatFileItemWriter<Employee> itemWriter = new FlatFileItemWriterBuilder<Employee>()
.lineAggregator(new PassThroughLineAggregator<>())
.name("itemsWriter")
.build();
return new MultiResourceItemWriterBuilder<Employee>()
.name("cloudDeveloperItemWriter")
.delegate(itemWriter)
.resource(new FileSystemResource("cloudDeveloper-employee.csv"))
.itemCountLimitPerResource(2)
.resourceSuffixCreator(index -> "-" + index)
.build();
}
@Bean
public Step step() throws Exception {
return stepBuilderFactory.get("step")
.<Employee, Employee>chunk(1)
.reader(itemReader())
.writer(classifierCompositeItemWriter())
.build();
}
@Bean
public Job job() throws Exception {
return jobBuilderFactory.get("job")
.start(step())
.build();
}
}
employee.csv
empId,firstName,lastName,role
1,John ,Doe,Java Developer
2,Jane ,Doe,Python Developer
empId,firstName,lastName,role
1,John ,Doe,Java Developer
2,Jane ,Doe,Python Developer
package com.example;
import org.springframework.batch.core.configuration.annotation.EnableBatchProcessing;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration;
@EnableBatchProcessing
@SpringBootApplication(exclude = {DataSourceAutoConfiguration.class})
public class SpringBatchMultipleFilesWithCompositeApplication {
public static void main(String[] args) {
SpringApplication.run(SpringBatchMultipleFilesWithCompositeApplication.class, args);
}
}
Expected behavior
Even though there is no data for the role cloud develoer, expectation from Spring Batch to create an empty file with headers for the cloud developer.
Or am I missing anything?
I was hoping to see cloudDeveloper-1.csv file
