RESOLVED - issue BATCH-1452: Stream closed exception when combining MultiResourceItemWriter and FlatFileItemWriter
This commit is contained in:
@@ -434,25 +434,31 @@ public class FlatFileItemWriter<T> extends ExecutionContextUserSupport implement
|
||||
catch (IOException ioe) {
|
||||
throw new ItemStreamException("Unable to close the the ItemWriter", ioe);
|
||||
}
|
||||
finally {
|
||||
if (!transactional) {
|
||||
closeStream();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void closeStream() {
|
||||
try {
|
||||
if (fileChannel != null) {
|
||||
fileChannel.close();
|
||||
}
|
||||
}
|
||||
catch (IOException ioe) {
|
||||
throw new ItemStreamException("Unable to close the the ItemWriter", ioe);
|
||||
}
|
||||
finally {
|
||||
try {
|
||||
if (fileChannel != null) {
|
||||
fileChannel.close();
|
||||
if (os != null) {
|
||||
os.close();
|
||||
}
|
||||
}
|
||||
catch (IOException ioe) {
|
||||
throw new ItemStreamException("Unable to close the the ItemWriter", ioe);
|
||||
}
|
||||
finally {
|
||||
try {
|
||||
if (os != null) {
|
||||
os.close();
|
||||
}
|
||||
}
|
||||
catch (IOException ioe) {
|
||||
throw new ItemStreamException("Unable to close the the ItemWriter", ioe);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -518,7 +524,11 @@ public class FlatFileItemWriter<T> extends ExecutionContextUserSupport implement
|
||||
try {
|
||||
Writer writer = Channels.newWriter(fileChannel, encoding);
|
||||
if (transactional) {
|
||||
return new TransactionAwareBufferedWriter(writer, getName());
|
||||
return new TransactionAwareBufferedWriter(writer, new Runnable() {
|
||||
public void run() {
|
||||
closeStream();
|
||||
}
|
||||
});
|
||||
} else {
|
||||
return new BufferedWriter(writer);
|
||||
}
|
||||
|
||||
@@ -64,20 +64,26 @@ public class MultiResourceItemWriter<T> extends ExecutionContextUserSupport impl
|
||||
|
||||
private boolean saveState = true;
|
||||
|
||||
private boolean opened = false;
|
||||
|
||||
public MultiResourceItemWriter() {
|
||||
setName(ClassUtils.getShortName(MultiResourceItemWriter.class));
|
||||
}
|
||||
|
||||
public void write(List<? extends T> items) throws Exception {
|
||||
if (!opened) {
|
||||
delegate.open(new ExecutionContext());
|
||||
opened = true;
|
||||
}
|
||||
delegate.write(items);
|
||||
currentResourceItemCount += items.size();
|
||||
if (currentResourceItemCount >= itemCountLimitPerResource) {
|
||||
delegate.close();
|
||||
resourceIndex++;
|
||||
currentResourceItemCount = 0;
|
||||
setResourceToDelegate();
|
||||
delegate.open(new ExecutionContext());
|
||||
opened = false;
|
||||
}
|
||||
delegate.write(items);
|
||||
currentResourceItemCount += items.size();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -120,7 +126,9 @@ public class MultiResourceItemWriter<T> extends ExecutionContextUserSupport impl
|
||||
public void close() throws ItemStreamException {
|
||||
resourceIndex = 1;
|
||||
currentResourceItemCount = 0;
|
||||
delegate.close();
|
||||
if (opened) {
|
||||
delegate.close();
|
||||
}
|
||||
}
|
||||
|
||||
public void open(ExecutionContext executionContext) throws ItemStreamException {
|
||||
@@ -131,14 +139,23 @@ public class MultiResourceItemWriter<T> extends ExecutionContextUserSupport impl
|
||||
setResourceToDelegate();
|
||||
}
|
||||
catch (IOException e) {
|
||||
throw new ItemStreamException("Couldn't open resource", e);
|
||||
throw new ItemStreamException("Couldn't assign resource", e);
|
||||
}
|
||||
|
||||
if (executionContext.containsKey(getKey(CURRENT_RESOURCE_ITEM_COUNT))) {
|
||||
// It's a restart
|
||||
delegate.open(executionContext);
|
||||
}
|
||||
else {
|
||||
opened = false;
|
||||
}
|
||||
delegate.open(executionContext);
|
||||
}
|
||||
|
||||
public void update(ExecutionContext executionContext) throws ItemStreamException {
|
||||
if (saveState) {
|
||||
delegate.update(executionContext);
|
||||
if (opened) {
|
||||
delegate.update(executionContext);
|
||||
}
|
||||
executionContext.putInt(getKey(CURRENT_RESOURCE_ITEM_COUNT), currentResourceItemCount);
|
||||
executionContext.putInt(getKey(RESOURCE_INDEX_KEY), resourceIndex);
|
||||
}
|
||||
|
||||
@@ -16,12 +16,13 @@
|
||||
|
||||
package org.springframework.batch.item.xml;
|
||||
|
||||
import java.io.BufferedWriter;
|
||||
import java.io.File;
|
||||
import java.io.FileOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.OutputStreamWriter;
|
||||
import java.io.UnsupportedEncodingException;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.io.Writer;
|
||||
import java.nio.channels.FileChannel;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
@@ -65,8 +66,8 @@ import org.springframework.xml.transform.StaxResult;
|
||||
* @author Robert Kasanicky
|
||||
*
|
||||
*/
|
||||
public class StaxEventItemWriter<T> extends ExecutionContextUserSupport implements ResourceAwareItemWriterItemStream<T>,
|
||||
InitializingBean {
|
||||
public class StaxEventItemWriter<T> extends ExecutionContextUserSupport implements
|
||||
ResourceAwareItemWriterItemStream<T>, InitializingBean {
|
||||
|
||||
private static final Log log = LogFactory.getLog(StaxEventItemWriter.class);
|
||||
|
||||
@@ -126,7 +127,9 @@ public class StaxEventItemWriter<T> extends ExecutionContextUserSupport implemen
|
||||
|
||||
private StaxWriterCallback footerCallback;
|
||||
|
||||
private TransactionAwareBufferedWriter transactionAwareBufferedWriter;
|
||||
private Writer bufferedWriter;
|
||||
|
||||
private boolean transactional = true;
|
||||
|
||||
public StaxEventItemWriter() {
|
||||
setName(ClassUtils.getShortName(StaxEventItemWriter.class));
|
||||
@@ -165,6 +168,16 @@ public class StaxEventItemWriter<T> extends ExecutionContextUserSupport implemen
|
||||
this.footerCallback = footerCallback;
|
||||
}
|
||||
|
||||
/**
|
||||
* Flag to indicate that writes should be deferred to the end of a
|
||||
* transaction if present. Defaults to true.
|
||||
*
|
||||
* @param transactional the flag to set
|
||||
*/
|
||||
public void setTransactional(boolean transactional) {
|
||||
this.transactional = transactional;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get used encoding.
|
||||
*
|
||||
@@ -324,11 +337,20 @@ public class StaxEventItemWriter<T> extends ExecutionContextUserSupport implemen
|
||||
// http://jira.springframework.org/browse/BATCH-761.
|
||||
outputFactory.setProperty("com.ctc.wstx.automaticEndElements", Boolean.FALSE);
|
||||
}
|
||||
|
||||
|
||||
try {
|
||||
transactionAwareBufferedWriter = new TransactionAwareBufferedWriter(
|
||||
new OutputStreamWriter(os, encoding), getName());
|
||||
delegateEventWriter = outputFactory.createXMLEventWriter(transactionAwareBufferedWriter);
|
||||
if (transactional) {
|
||||
bufferedWriter = new TransactionAwareBufferedWriter(new OutputStreamWriter(os, encoding),
|
||||
new Runnable() {
|
||||
public void run() {
|
||||
closeStream();
|
||||
}
|
||||
});
|
||||
}
|
||||
else {
|
||||
bufferedWriter = new BufferedWriter(new OutputStreamWriter(os, encoding));
|
||||
}
|
||||
delegateEventWriter = outputFactory.createXMLEventWriter(bufferedWriter);
|
||||
eventWriter = new NoStartEndDocumentStreamWriter(delegateEventWriter);
|
||||
if (!restarted) {
|
||||
startDocument(delegateEventWriter);
|
||||
@@ -390,9 +412,8 @@ public class StaxEventItemWriter<T> extends ExecutionContextUserSupport implemen
|
||||
// writer.writeEndDocument(); <- this doesn't work after restart
|
||||
// we need to write end tag of the root element manually
|
||||
|
||||
ByteBuffer bbuf = ByteBuffer.wrap(("</" + getRootTagName() + ">").getBytes());
|
||||
try {
|
||||
channel.write(bbuf);
|
||||
bufferedWriter.write("</" + getRootTagName() + ">");
|
||||
}
|
||||
catch (IOException ioe) {
|
||||
throw new DataAccessResourceFailureException("Unable to close file resource: [" + resource + "]", ioe);
|
||||
@@ -433,21 +454,34 @@ public class StaxEventItemWriter<T> extends ExecutionContextUserSupport implemen
|
||||
try {
|
||||
eventWriter.close();
|
||||
}
|
||||
catch (XMLStreamException xse) {
|
||||
log.error("Unable to close file resource: [" + resource + "] " + xse);
|
||||
catch (XMLStreamException e) {
|
||||
log.error("Unable to close file resource: [" + resource + "] " + e);
|
||||
}
|
||||
finally {
|
||||
try {
|
||||
channel.close();
|
||||
bufferedWriter.close();
|
||||
}
|
||||
catch (IOException ioe) {
|
||||
log.error("Unable to close file resource: [" + resource + "] " + ioe);
|
||||
catch (IOException e) {
|
||||
log.error("Unable to close file resource: [" + resource + "] " + e);
|
||||
}
|
||||
finally {
|
||||
if (!transactional) {
|
||||
closeStream();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void closeStream() {
|
||||
try {
|
||||
channel.close();
|
||||
}
|
||||
catch (IOException ioe) {
|
||||
log.error("Unable to close file resource: [" + resource + "] " + ioe);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Write the value objects and flush them to the file.
|
||||
*
|
||||
@@ -499,7 +533,10 @@ public class StaxEventItemWriter<T> extends ExecutionContextUserSupport implemen
|
||||
|
||||
try {
|
||||
eventWriter.flush();
|
||||
position = channel.position() + transactionAwareBufferedWriter.getBufferSize();
|
||||
position = channel.position();
|
||||
if (bufferedWriter instanceof TransactionAwareBufferedWriter) {
|
||||
position += ((TransactionAwareBufferedWriter) bufferedWriter).getBufferSize();
|
||||
}
|
||||
}
|
||||
catch (Exception e) {
|
||||
throw new DataAccessResourceFailureException("Unable to write to file resource: [" + resource + "]", e);
|
||||
|
||||
@@ -22,9 +22,9 @@ import org.springframework.transaction.support.TransactionSynchronizationAdapter
|
||||
import org.springframework.transaction.support.TransactionSynchronizationManager;
|
||||
|
||||
/**
|
||||
* Wrapper for a {@link Writer} that delays actually writing to the buffer if a
|
||||
* transaction is active. If a transaction is detected on the call to
|
||||
* {@link #write(String)} the parameter is buffered and passed on to the
|
||||
* Wrapper for a {@link Writer} that delays actually writing to or closing the
|
||||
* buffer if a transaction is active. If a transaction is detected on the call
|
||||
* to {@link #write(String)} the parameter is buffered and passed on to the
|
||||
* underlying writer only when the transaction is committed.
|
||||
*
|
||||
* @author Dave Syer
|
||||
@@ -34,19 +34,30 @@ public class TransactionAwareBufferedWriter extends Writer {
|
||||
|
||||
private static final String BUFFER_KEY_PREFIX = TransactionAwareBufferedWriter.class.getName() + ".BUFFER_KEY";
|
||||
|
||||
private static final String CLOSE_KEY_PREFIX = TransactionAwareBufferedWriter.class.getName() + ".CLOSE_KEY";
|
||||
|
||||
private final String bufferKey;
|
||||
|
||||
private final String closeKey;
|
||||
|
||||
private Writer writer;
|
||||
|
||||
private final Runnable closeCallback;
|
||||
|
||||
/**
|
||||
* Create a new instance with the underlying writer provided, and a callback
|
||||
* to execute on close. The callback should clean up related resources like
|
||||
* output streams or channels.
|
||||
*
|
||||
* @param writer actually writes to output
|
||||
* @param name used to identify the transactional buffer used by this
|
||||
* instance
|
||||
* @param closeCallback callback to execute on close
|
||||
*/
|
||||
public TransactionAwareBufferedWriter(Writer writer, String name) {
|
||||
public TransactionAwareBufferedWriter(Writer writer, Runnable closeCallback) {
|
||||
super();
|
||||
this.writer = writer;
|
||||
this.bufferKey = BUFFER_KEY_PREFIX + "." + name;
|
||||
this.closeCallback = closeCallback;
|
||||
this.bufferKey = BUFFER_KEY_PREFIX + "." + hashCode();
|
||||
this.closeKey = CLOSE_KEY_PREFIX + "." + hashCode();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -61,18 +72,34 @@ public class TransactionAwareBufferedWriter extends Writer {
|
||||
TransactionSynchronizationManager.registerSynchronization(new TransactionSynchronizationAdapter() {
|
||||
@Override
|
||||
public void afterCompletion(int status) {
|
||||
StringBuffer buffer = (StringBuffer) TransactionSynchronizationManager.getResource(bufferKey);
|
||||
if (status == STATUS_COMMITTED) {
|
||||
complete();
|
||||
}
|
||||
}
|
||||
|
||||
private void complete() {
|
||||
StringBuffer buffer = (StringBuffer) TransactionSynchronizationManager.getResource(bufferKey);
|
||||
if (buffer != null) {
|
||||
try {
|
||||
writer.write(buffer.toString());
|
||||
writer.flush();
|
||||
if (TransactionSynchronizationManager.hasResource(closeKey)) {
|
||||
writer.close();
|
||||
closeCallback.run();
|
||||
}
|
||||
}
|
||||
catch (IOException e) {
|
||||
throw new FlushFailedException("Could not write to output buffer", e);
|
||||
}
|
||||
finally {
|
||||
TransactionSynchronizationManager.unbindResource(bufferKey);
|
||||
if (TransactionSynchronizationManager.hasResource(closeKey)) {
|
||||
TransactionSynchronizationManager.unbindResource(closeKey);
|
||||
}
|
||||
}
|
||||
}
|
||||
TransactionSynchronizationManager.unbindResource(bufferKey);
|
||||
}
|
||||
|
||||
});
|
||||
|
||||
}
|
||||
@@ -108,7 +135,14 @@ public class TransactionAwareBufferedWriter extends Writer {
|
||||
*/
|
||||
@Override
|
||||
public void close() throws IOException {
|
||||
if (transactionActive()) {
|
||||
if (getCurrentBuffer().length() > 0) {
|
||||
TransactionSynchronizationManager.bindResource(closeKey, Boolean.TRUE);
|
||||
}
|
||||
return;
|
||||
}
|
||||
writer.close();
|
||||
closeCallback.run();
|
||||
}
|
||||
|
||||
/*
|
||||
|
||||
@@ -4,7 +4,6 @@ import java.io.BufferedReader;
|
||||
import java.io.File;
|
||||
import java.io.FileReader;
|
||||
|
||||
import org.junit.Before;
|
||||
import org.springframework.batch.item.ExecutionContext;
|
||||
import org.springframework.core.io.FileSystemResource;
|
||||
|
||||
@@ -22,12 +21,9 @@ public class AbstractMultiResourceItemWriterTests {
|
||||
|
||||
protected ResourceSuffixCreator suffixCreator = new SimpleResourceSuffixCreator();
|
||||
|
||||
protected ResourceAwareItemWriterItemStream<String> delegate;
|
||||
|
||||
protected ExecutionContext executionContext = new ExecutionContext();
|
||||
|
||||
@Before
|
||||
public void setUp() throws Exception {
|
||||
protected void setUp(ResourceAwareItemWriterItemStream<String> delegate) throws Exception {
|
||||
file = File.createTempFile(MultiResourceItemWriterFlatFileTests.class.getSimpleName(), null);
|
||||
tested.setResource(new FileSystemResource(file));
|
||||
tested.setDelegate(delegate);
|
||||
|
||||
@@ -4,11 +4,18 @@ import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.io.Writer;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.springframework.batch.item.file.transform.PassThroughLineAggregator;
|
||||
import org.springframework.batch.support.transaction.ResourcelessTransactionManager;
|
||||
import org.springframework.transaction.TransactionStatus;
|
||||
import org.springframework.transaction.support.TransactionCallback;
|
||||
import org.springframework.transaction.support.TransactionTemplate;
|
||||
|
||||
/**
|
||||
* Tests for {@link MultiResourceItemWriter} delegating to
|
||||
@@ -16,20 +23,42 @@ import org.springframework.batch.item.file.transform.PassThroughLineAggregator;
|
||||
*/
|
||||
public class MultiResourceItemWriterFlatFileTests extends AbstractMultiResourceItemWriterTests {
|
||||
|
||||
@Override
|
||||
/**
|
||||
* @author dsyer
|
||||
*
|
||||
*/
|
||||
private final class WriterCallback implements TransactionCallback {
|
||||
private List<? extends String> list;
|
||||
|
||||
public WriterCallback(List<? extends String> list) {
|
||||
super();
|
||||
this.list = list;
|
||||
}
|
||||
|
||||
public Object doInTransaction(TransactionStatus status) {
|
||||
try {
|
||||
tested.write(list);
|
||||
}
|
||||
catch (Exception e) {
|
||||
throw new IllegalStateException("Unexpected");
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
private FlatFileItemWriter<String> delegate;
|
||||
|
||||
@Before
|
||||
public void setUp() throws Exception {
|
||||
delegate = new FlatFileItemWriter<String>() {
|
||||
{
|
||||
setLineAggregator(new PassThroughLineAggregator<String>());
|
||||
}
|
||||
};
|
||||
super.setUp();
|
||||
delegate = new FlatFileItemWriter<String>();
|
||||
delegate.setLineAggregator(new PassThroughLineAggregator<String>());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testBasicMultiResourceWriteScenario() throws Exception {
|
||||
|
||||
super.setUp(delegate);
|
||||
|
||||
tested.write(Arrays.asList("1", "2", "3"));
|
||||
|
||||
File part1 = new File(file.getAbsolutePath() + suffixCreator.getSuffix(1));
|
||||
@@ -50,8 +79,80 @@ public class MultiResourceItemWriterFlatFileTests extends AbstractMultiResourceI
|
||||
assertEquals("6789", readFile(part3));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testUpdateAfterDelegateClose() throws Exception {
|
||||
|
||||
super.setUp(delegate);
|
||||
|
||||
tested.update(executionContext);
|
||||
assertEquals(0, executionContext.getInt(tested.getKey("resource.item.count")));
|
||||
assertEquals(1, executionContext.getInt(tested.getKey("resource.index")));
|
||||
tested.write(Arrays.asList("1", "2", "3"));
|
||||
tested.update(executionContext);
|
||||
assertEquals(0, executionContext.getInt(tested.getKey("resource.item.count")));
|
||||
assertEquals(2, executionContext.getInt(tested.getKey("resource.index")));
|
||||
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testMultiResourceWriteScenarioWithFooter() throws Exception {
|
||||
|
||||
delegate.setFooterCallback(new FlatFileFooterCallback() {
|
||||
public void writeFooter(Writer writer) throws IOException {
|
||||
writer.write("f");
|
||||
}
|
||||
});
|
||||
super.setUp(delegate);
|
||||
|
||||
tested.write(Arrays.asList("1", "2", "3"));
|
||||
|
||||
File part1 = new File(file.getAbsolutePath() + suffixCreator.getSuffix(1));
|
||||
assertTrue(part1.exists());
|
||||
|
||||
tested.write(Arrays.asList("4"));
|
||||
File part2 = new File(file.getAbsolutePath() + suffixCreator.getSuffix(2));
|
||||
assertTrue(part2.exists());
|
||||
|
||||
tested.close();
|
||||
|
||||
assertEquals("123f", readFile(part1));
|
||||
assertEquals("4f", readFile(part2));
|
||||
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testTransactionalMultiResourceWriteScenarioWithFooter() throws Exception {
|
||||
|
||||
delegate.setFooterCallback(new FlatFileFooterCallback() {
|
||||
public void writeFooter(Writer writer) throws IOException {
|
||||
writer.write("f");
|
||||
}
|
||||
});
|
||||
super.setUp(delegate);
|
||||
|
||||
ResourcelessTransactionManager transactionManager = new ResourcelessTransactionManager();
|
||||
|
||||
new TransactionTemplate(transactionManager).execute(new WriterCallback(Arrays.asList("1", "2", "3")));
|
||||
|
||||
File part1 = new File(file.getAbsolutePath() + suffixCreator.getSuffix(1));
|
||||
assertTrue(part1.exists());
|
||||
|
||||
new TransactionTemplate(transactionManager).execute(new WriterCallback(Arrays.asList("4")));
|
||||
File part2 = new File(file.getAbsolutePath() + suffixCreator.getSuffix(2));
|
||||
assertTrue(part2.exists());
|
||||
|
||||
tested.close();
|
||||
|
||||
assertEquals("123f", readFile(part1));
|
||||
assertEquals("4f", readFile(part2));
|
||||
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testRestart() throws Exception {
|
||||
|
||||
super.setUp(delegate);
|
||||
|
||||
tested.write(Arrays.asList("1", "2", "3"));
|
||||
|
||||
File part1 = new File(file.getAbsolutePath() + suffixCreator.getSuffix(1));
|
||||
@@ -75,4 +176,70 @@ public class MultiResourceItemWriterFlatFileTests extends AbstractMultiResourceI
|
||||
assertTrue(part3.exists());
|
||||
assertEquals("6789", readFile(part3));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testRestartWithFooter() throws Exception {
|
||||
|
||||
delegate.setFooterCallback(new FlatFileFooterCallback() {
|
||||
public void writeFooter(Writer writer) throws IOException {
|
||||
writer.write("f");
|
||||
}
|
||||
});
|
||||
super.setUp(delegate);
|
||||
|
||||
tested.write(Arrays.asList("1", "2", "3"));
|
||||
|
||||
File part1 = new File(file.getAbsolutePath() + suffixCreator.getSuffix(1));
|
||||
assertTrue(part1.exists());
|
||||
assertEquals("123f", readFile(part1));
|
||||
|
||||
tested.write(Arrays.asList("4"));
|
||||
File part2 = new File(file.getAbsolutePath() + suffixCreator.getSuffix(2));
|
||||
assertTrue(part2.exists());
|
||||
assertEquals("4", readFile(part2));
|
||||
|
||||
tested.update(executionContext);
|
||||
tested.close();
|
||||
tested.open(executionContext);
|
||||
|
||||
tested.write(Arrays.asList("5"));
|
||||
assertEquals("45f", readFile(part2));
|
||||
|
||||
tested.write(Arrays.asList("6", "7", "8", "9"));
|
||||
File part3 = new File(file.getAbsolutePath() + suffixCreator.getSuffix(3));
|
||||
assertTrue(part3.exists());
|
||||
assertEquals("6789f", readFile(part3));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testTransactionalRestartWithFooter() throws Exception {
|
||||
|
||||
delegate.setFooterCallback(new FlatFileFooterCallback() {
|
||||
public void writeFooter(Writer writer) throws IOException {
|
||||
writer.write("f");
|
||||
}
|
||||
});
|
||||
super.setUp(delegate);
|
||||
|
||||
ResourcelessTransactionManager transactionManager = new ResourcelessTransactionManager();
|
||||
|
||||
new TransactionTemplate(transactionManager).execute(new WriterCallback(Arrays.asList("1", "2", "3")));
|
||||
|
||||
File part1 = new File(file.getAbsolutePath() + suffixCreator.getSuffix(1));
|
||||
assertTrue(part1.exists());
|
||||
assertEquals("123f", readFile(part1));
|
||||
|
||||
new TransactionTemplate(transactionManager).execute(new WriterCallback(Arrays.asList("4")));
|
||||
File part2 = new File(file.getAbsolutePath() + suffixCreator.getSuffix(2));
|
||||
assertTrue(part2.exists());
|
||||
assertEquals("4", readFile(part2));
|
||||
|
||||
tested.update(executionContext);
|
||||
tested.close();
|
||||
tested.open(executionContext);
|
||||
|
||||
new TransactionTemplate(transactionManager).execute(new WriterCallback(Arrays.asList("5")));
|
||||
assertEquals("45f", readFile(part2));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -1,4 +1,7 @@
|
||||
package org.springframework.batch.item.file;
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.util.Arrays;
|
||||
@@ -8,10 +11,8 @@ import javax.xml.stream.XMLEventWriter;
|
||||
import javax.xml.stream.XMLStreamException;
|
||||
import javax.xml.transform.Result;
|
||||
|
||||
import static org.junit.Assert.*;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.springframework.batch.item.file.MultiResourceItemWriter;
|
||||
import org.springframework.batch.item.xml.StaxEventItemWriter;
|
||||
import org.springframework.oxm.Marshaller;
|
||||
import org.springframework.oxm.XmlMappingException;
|
||||
@@ -28,21 +29,19 @@ public class MultiResourceItemWriterXmlTests extends AbstractMultiResourceItemWr
|
||||
|
||||
final static private String xmlDocEnd = "</root>";
|
||||
|
||||
@Override
|
||||
private StaxEventItemWriter<String> delegate;
|
||||
|
||||
@Before
|
||||
public void setUp() throws Exception {
|
||||
delegate = new StaxEventItemWriter<String>() {
|
||||
{
|
||||
setMarshaller(new SimpleMarshaller());
|
||||
}
|
||||
};
|
||||
super.setUp();
|
||||
delegate = new StaxEventItemWriter<String>();
|
||||
delegate.setMarshaller(new SimpleMarshaller());
|
||||
}
|
||||
|
||||
/**
|
||||
* Writes object's toString representation as tag.
|
||||
*/
|
||||
private static class SimpleMarshaller implements Marshaller {
|
||||
|
||||
public void marshal(Object graph, Result result) throws XmlMappingException, IOException {
|
||||
Assert.isInstanceOf(StaxResult.class, result);
|
||||
|
||||
@@ -75,6 +74,8 @@ public class MultiResourceItemWriterXmlTests extends AbstractMultiResourceItemWr
|
||||
|
||||
@Test
|
||||
public void multiResourceWritingWithRestart() throws Exception {
|
||||
|
||||
setUp(delegate);
|
||||
|
||||
tested.write(Arrays.asList("1", "2", "3"));
|
||||
|
||||
|
||||
@@ -59,8 +59,8 @@ public class StaxEventItemWriterTests {
|
||||
|
||||
private List<? extends Object> items = Collections.singletonList(item);
|
||||
|
||||
private static final String TEST_STRING = "<!--" + ClassUtils.getShortName(StaxEventItemWriter.class)
|
||||
+ "-testString-->";
|
||||
private static final String TEST_STRING = "<" + ClassUtils.getShortName(StaxEventItemWriter.class)
|
||||
+ "-testString/>";
|
||||
|
||||
@Before
|
||||
public void setUp() throws Exception {
|
||||
@@ -77,7 +77,7 @@ public class StaxEventItemWriterTests {
|
||||
writer.open(executionContext);
|
||||
writer.write(items);
|
||||
writer.close();
|
||||
String content = outputFileContent();
|
||||
String content = getOutputFileContent();
|
||||
assertTrue("Wrong content: " + content, content.contains(TEST_STRING));
|
||||
}
|
||||
|
||||
@@ -100,9 +100,9 @@ public class StaxEventItemWriterTests {
|
||||
|
||||
// check the output is concatenation of 'before restart' and 'after
|
||||
// restart' writes.
|
||||
String outputFile = outputFileContent();
|
||||
String outputFile = getOutputFileContent();
|
||||
assertEquals(2, StringUtils.countOccurrencesOf(outputFile, TEST_STRING));
|
||||
assertTrue(outputFile.contains("<root>" + TEST_STRING + TEST_STRING + "</root>"));
|
||||
assertEquals("<root>" + TEST_STRING + TEST_STRING + "</root>", outputFile.replace(" ", ""));
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -147,7 +147,7 @@ public class StaxEventItemWriterTests {
|
||||
|
||||
// check the output is concatenation of 'before restart' and 'after
|
||||
// restart' writes.
|
||||
String outputFile = outputFileContent();
|
||||
String outputFile = getOutputFileContent();
|
||||
assertEquals(2, StringUtils.countOccurrencesOf(outputFile, TEST_STRING));
|
||||
assertTrue(outputFile.contains("<root>" + TEST_STRING + TEST_STRING + "</root>"));
|
||||
}
|
||||
@@ -175,7 +175,7 @@ public class StaxEventItemWriterTests {
|
||||
});
|
||||
writer.open(executionContext);
|
||||
writer.write(items);
|
||||
String content = outputFileContent();
|
||||
String content = getOutputFileContent();
|
||||
assertTrue("Wrong content: " + content, content.contains(("<header/>")));
|
||||
assertTrue("Wrong content: " + content, content.contains(TEST_STRING));
|
||||
}
|
||||
@@ -237,7 +237,7 @@ public class StaxEventItemWriterTests {
|
||||
writer.setRootElementAttributes(Collections.<String, String> singletonMap("attribute", "value"));
|
||||
writer.open(executionContext);
|
||||
writer.close();
|
||||
String content = outputFileContent();
|
||||
String content = getOutputFileContent();
|
||||
|
||||
assertTrue(content.contains("<testroot attribute=\"value\">"));
|
||||
assertTrue(content.contains("<header/>"));
|
||||
@@ -272,7 +272,8 @@ public class StaxEventItemWriterTests {
|
||||
|
||||
StaxResult staxResult = (StaxResult) result;
|
||||
try {
|
||||
staxResult.getXMLEventWriter().add(XMLEventFactory.newInstance().createComment(graph.toString()));
|
||||
staxResult.getXMLEventWriter().add(XMLEventFactory.newInstance().createStartElement("", "", graph.toString()));
|
||||
staxResult.getXMLEventWriter().add(XMLEventFactory.newInstance().createEndElement("", "", graph.toString()));
|
||||
}
|
||||
catch (XMLStreamException e) {
|
||||
throw new RuntimeException("Exception while writing to output file", e);
|
||||
@@ -288,8 +289,10 @@ public class StaxEventItemWriterTests {
|
||||
/**
|
||||
* @return output file content as String
|
||||
*/
|
||||
private String outputFileContent() throws IOException {
|
||||
return FileUtils.readFileToString(resource.getFile(), null);
|
||||
private String getOutputFileContent() throws IOException {
|
||||
String value = FileUtils.readFileToString(resource.getFile(), null);
|
||||
value = value.replace("<?xml version='1.0' encoding='UTF-8'?>", "");
|
||||
return value;
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -37,7 +37,16 @@ public class TransactionAwareBufferedWriterTests {
|
||||
|
||||
private Writer stringWriter = new StringWriter();
|
||||
|
||||
private TransactionAwareBufferedWriter writer = new TransactionAwareBufferedWriter(stringWriter, "someName");
|
||||
private TransactionAwareBufferedWriter writer = new TransactionAwareBufferedWriter(stringWriter, new Runnable() {
|
||||
public void run() {
|
||||
try {
|
||||
stringWriter.append("c");
|
||||
}
|
||||
catch (IOException e) {
|
||||
throw new IllegalStateException(e);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
private PlatformTransactionManager transactionManager = new ResourcelessTransactionManager();
|
||||
|
||||
@@ -53,6 +62,7 @@ public class TransactionAwareBufferedWriterTests {
|
||||
public void testWriteOutsideTransaction() throws Exception {
|
||||
writer.write("foo");
|
||||
writer.flush();
|
||||
// Not closed yet
|
||||
assertEquals("foo", stringWriter.toString());
|
||||
}
|
||||
|
||||
@@ -66,7 +76,7 @@ public class TransactionAwareBufferedWriterTests {
|
||||
public void testCloseOutsideTransaction() throws Exception {
|
||||
writer.write("foo");
|
||||
writer.close();
|
||||
assertEquals("foo", stringWriter.toString());
|
||||
assertEquals("fooc", stringWriter.toString());
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -86,7 +96,10 @@ public class TransactionAwareBufferedWriterTests {
|
||||
public void write(char[] cbuf, int off, int len) throws IOException {
|
||||
}
|
||||
};
|
||||
writer = new TransactionAwareBufferedWriter(mock, "someName");
|
||||
writer = new TransactionAwareBufferedWriter(mock, new Runnable() {
|
||||
public void run() {
|
||||
}
|
||||
});
|
||||
new TransactionTemplate(transactionManager).execute(new TransactionCallback() {
|
||||
public Object doInTransaction(TransactionStatus status) {
|
||||
try {
|
||||
@@ -117,6 +130,7 @@ public class TransactionAwareBufferedWriterTests {
|
||||
return null;
|
||||
}
|
||||
});
|
||||
// Not closed in transaction
|
||||
assertEquals("foo", stringWriter.toString());
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user