I configured a StaxEventItemWriter like this:
@Bean
public StaxEventItemWriter<Foo> fooWriter() {
return new StaxEventItemWriterBuilder<Foo>()
.name("fooWriter")
.marshaller(marshaller())
.rootTagName("foos")
.build();
}
And a MultiResourceItemWriter like this:
@Bean
public MultiResourceItemWriter<Foo> multiFooWriter() {
return new MultiResourceItemWriterBuilder<Foo>()
.name("multiFooWriter")
.delegate(fooWriter())
.itemCountLimitPerResource(100)
.resourceSuffixCreator(index -> "-" + index + ".xml")
.resource(new FileSystemResource("foo"))
.build();
}
However, when I run a batch using these writers, I get the following exception:
Caused by: java.nio.channels.ClosedChannelException: null
at java.base/sun.nio.ch.FileChannelImpl.ensureOpen(FileChannelImpl.java:160) ~[na:na]
at java.base/sun.nio.ch.FileChannelImpl.write(FileChannelImpl.java:284) ~[na:na]
at org.springframework.batch.support.transaction.TransactionAwareBufferedWriter$1.complete(TransactionAwareBufferedWriter.java:121) ~[spring-batch-infrastructure-5.2.4.jar:5.2.4]
at org.springframework.batch.support.transaction.TransactionAwareBufferedWriter$1.beforeCommit(TransactionAwareBufferedWriter.java:106) ~[spring-batch-infrastructure-5.2.4.jar:5.2.4]
... 44 common frames omitted
I noticed it seems to have something to do with the transactional flushing, so I tried disabling transactions on the StaxEventItemWriter and then it indeed works:
@Bean
public StaxEventItemWriter<Foo> fooWriter() {
return new StaxEventItemWriterBuilder<Foo>()
.name("fooWriter")
.marshaller(marshaller())
.rootTagName("foos")
.transactional(false) // Adding this works
.build();
}
In addition, if I write to a single large XML file (eg. by using fooWriter() directly in my Step configuration in stead of multiFooWriter()), then the job also succeeds.
Is there a reason why StaxEventItemWriter combined with MultiResourceItemWriter does not work with transactions?
For completeness of the example, this is the Foo class I used and the ItemReader (but that's irrelevant to the question as the problem occurs with any reader/JAXB class):
@Getter
@XmlRootElement(name = "foo")
@XmlType(propOrder = { "itemCount" })
@NoArgsConstructor
public class Foo implements ItemCountAware {
private int itemCount;
@Override
@XmlElement(name = "count")
public void setItemCount(int count) {
this.itemCount = count;
}
}
For the reader I'm using AbstractItemCountingItemStreamItemReader in combination with ItemCountAware to generate some unique Foo POJO's (but it's irrelevant to the problem):
@Component
public class FooReader extends AbstractItemCountingItemStreamItemReader<Foo> {
public FooReader() {
setName("fooReader");
setMaxItemCount(10000);
}
@Override
protected Foo doRead() {
return new Foo();
}
@Override
protected void doOpen() {
}
@Override
protected void doClose() {
}
}
MultiResourceItemWriterinstead ofItemWriteras that will also expose additional callbacks methods. Try to be as specific as you can when returning from@Beanmethods.TransactionAwareBufferedWriterand/or theMultiResourceItemWriteras that manages the writing and opening of files itself.