I have a need to be able to access the name of the flat file that the current line being read is from. We are porting an application and some processes that are currently in place require this information.
Here is my current configuration:
private FlatFileItemReader<Invoice> invoiceFlatFileItemReader() throws Exception {
FlatFileItemReader<Invoice> reader = new FlatFileItemReader<>();
reader.setLinesToSkip(1); // header row
reader.setLineMapper(new InvoiceLineMapper());
reader.afterPropertiesSet();
return reader;
}
InvoiceLineMapper:
public class InvoiceLineMapper implements LineMapper<Invoice> {
@Override
public Invoice mapLine(String line, int lineNumber) throws Exception {
String[] fields = line.split(",");
ArrayList<String> fieldList = new ArrayList<>();
for (int i = 0; i < fields.length; i++) {
fieldList.add(fields[i].replaceAll("\"", "");
}
Invoice invoice = new Invoice();
invoice.setCustomerNumber(Integer.parseInt(fieldList.get(0));
invoice.setCustomerName(fieldList.get(1));
// set other things and stuff...
//Need to be able to set the file name on the model here
invoice.setFileName(theFileName);
}
}
I am delegating to the invoiceFlatFileItemReader()
via a MultiResourceItemReader
:
@Bean
public ItemReader<Invoice> invoiceItemReader() throws Exception {
ResourcePatternResolver resolver = new PathMatchingResourcePatterResolver(this.getClass().getClassLoader());
// currently being loaded from the classpath, eventually will be read from S3...that comes later
Resource[] resources = resolver.getResources("classpath*:**/InvoiceList*.csv*");
MultiResourceItemReader<Invoice> reader = new MultiResourceItemReader<>();
reader.setResources(resources);
reader.setDelegate(invoiceFlatFileItemReader());
return reader;
}
How can I get the name of the resource passed down the chain to where I can access it in the InvoiceLineMapper
class and add it to the model (which later gets persisted with a RepositoryItemWriter
)?
There is an interface ResourceAware
that your item can implement. If it does implement that interface, when using MultiResourceItemReader
, the resource will be injected automatically for you. You can read more about the ResourceAware
interface in the documentation here: http://docs.spring.io/spring-batch/apidocs/org/springframework/batch/item/ResourceAware.html
Here's what I ended up doing, just passing the MultiItemResourceReader
down the chain so that I could call getCurrentResource()
in the line mapper:
@Bean
public ItemReader<Invoice> invoiceItemReader() throws Exception {
ResourcePatternResolver resolver = new PathMatchingResourcePatterResolver(this.getClass().getClassLoader());
// currently being loaded from the classpath, eventually will be read from S3...that comes later
Resource[] resources = resolver.getResources("classpath*:**/InvoiceList*.csv*");
MultiResourceItemReader<Invoice> reader = new MultiResourceItemReader<>();
reader.setResources(resources);
reader.setDelegate(invoiceFlatFileItemReader(reader));
return reader;
}
private FlatFileItemReader<Invoice> invoiceFlatFileItemReader(MultiResourceItemReader delegator) throws Exception {
FlatFileItemReader<Invoice> reader = new FlatFileItemReader<>();
reader.setLinesToSkip(1); // header row
reader.setLineMapper(new InvoiceLineMapper(delegator));
reader.afterPropertiesSet();
return reader;
}
And then in InvoiceLineMapper
:
public class InvoiceLineMapper implements LineMapper<Invoice> {
private MultiResourceItemReader delegator;
public InvoiceLineMapper(MultiResourceItemReader delegator) {
this.delegator = delegator;
}
@Override
public Invoice mapLine(String line, int lineNumber) throws Exception {
String[] fields = line.split(",");
ArrayList<String> fieldList = new ArrayList<>();
for (int i = 0; i < fields.length; i++) {
fieldList.add(fields[i].replaceAll("\"", "");
}
Invoice invoice = new Invoice();
invoice.setCustomerNumber(Integer.parseInt(fieldList.get(0));
invoice.setCustomerName(fieldList.get(1));
// set other things and stuff...
Resource currentResource = delegator.getCurrentResource();
String[] fileName = currentResource.getFilename().split("/");
invoice.setFileName(filename[filename.length - 1]);
return invoice;
}
}