Given a Spring Batch job that uses chunk oriented processing, I have a requirement to write the same data to different files depending on the value of an attribute. In other words, I need the output file name be decided at runtime depending on the value of an attribute.
I found this answer on SO and it looks like I can use a ClassifierCompositeItemWriter
to dynamically create writers at runtime.
What I want to understand is that whether the ClassifierCompositeItemWriter
will create a new ItemWriter
instance for every single write? If not, how does the ClassifierCompositeItemWriter
know what attribute value from the domain object to use for caching the ItemWriter
instances so that they can be reused? (rather than creating a new ItemWriter
per object..)
Do I need to implement the caching of wrtiers myself or is this handled by the ClassfierCompositeItemWriter
automatically?
Assuming that the cahcing of writers needs to implemented manually, I came up with the following implementation. However, I would assume this is something ClassfierCompositeItemWriter
would be doing itself?
public abstract class AbstractFileWriterClassifier<T> implements Classifier<T, ItemWriter<T>> {
private static final long serialVersionUID = 906600779221731593L;
private transient LineAggregator<T> lineAggregator;
private transient FlatFileHeaderCallback headerCallback;
private transient String baseFilePath;
private transient boolean appendAllowed;
private transient Map<String, ItemWriter<T>> cachedItemWriters;
public AbstractFileWriterClassifier() {
cachedItemWriters = new ConcurrentHashMap<>(5);
}
@Override
public final ItemWriter<T> classify(T item) {
final String dynamicFilePath = getDynamicFilePath(item);
// Get existing writer or create a new one if it doesn't exist for the key
return cachedItemWriters.computeIfAbsent(dynamicFilePath, dynamicPath -> {
String fileName = baseFilePath + dynamicPath;
FlatFileItemWriter<T> itemWriter = new FlatFileItemWriter<>();
itemWriter.setResource(new FileSystemResource(fileName));
itemWriter.setAppendAllowed(appendAllowed);
itemWriter.setLineAggregator(lineAggregator);
itemWriter.setHeaderCallback(headerCallback);
itemWriter.open(new ExecutionContext());
return itemWriter;
});
}
/**
* Derives the dynamic part of the file path using the item
*
* @param item
* @return
*/
protected abstract String getDynamicFilePath(T item);
}