OPEN - issue BATCH-788: Remove flush/clear from ItemWriter
Infrastructure and core working with no calls to flush (I think)
This commit is contained in:
@@ -1,4 +1,4 @@
|
||||
#Wed Jul 16 09:54:03 CEST 2008
|
||||
#Tue Aug 19 18:12:49 BST 2008
|
||||
eclipse.preferences.version=1
|
||||
org.eclipse.jdt.core.compiler.codegen.inlineJsrBytecode=enabled
|
||||
org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.5
|
||||
@@ -13,7 +13,7 @@ org.eclipse.jdt.core.compiler.problem.assertIdentifier=error
|
||||
org.eclipse.jdt.core.compiler.problem.autoboxing=ignore
|
||||
org.eclipse.jdt.core.compiler.problem.deprecation=warning
|
||||
org.eclipse.jdt.core.compiler.problem.deprecationInDeprecatedCode=disabled
|
||||
org.eclipse.jdt.core.compiler.problem.deprecationWhenOverridingDeprecatedMethod=disabled
|
||||
org.eclipse.jdt.core.compiler.problem.deprecationWhenOverridingDeprecatedMethod=enabled
|
||||
org.eclipse.jdt.core.compiler.problem.discouragedReference=warning
|
||||
org.eclipse.jdt.core.compiler.problem.emptyStatement=ignore
|
||||
org.eclipse.jdt.core.compiler.problem.enumIdentifier=error
|
||||
@@ -66,6 +66,7 @@ org.eclipse.jdt.core.compiler.problem.unnecessaryElse=ignore
|
||||
org.eclipse.jdt.core.compiler.problem.unnecessaryTypeCheck=ignore
|
||||
org.eclipse.jdt.core.compiler.problem.unqualifiedFieldAccess=ignore
|
||||
org.eclipse.jdt.core.compiler.problem.unusedDeclaredThrownException=ignore
|
||||
org.eclipse.jdt.core.compiler.problem.unusedDeclaredThrownExceptionExemptExceptionAndThrowable=enabled
|
||||
org.eclipse.jdt.core.compiler.problem.unusedDeclaredThrownExceptionIncludeDocCommentReference=enabled
|
||||
org.eclipse.jdt.core.compiler.problem.unusedDeclaredThrownExceptionIncludeUncheckedExceptions=disabled
|
||||
org.eclipse.jdt.core.compiler.problem.unusedDeclaredThrownExceptionWhenOverriding=disabled
|
||||
|
||||
@@ -144,18 +144,14 @@ public class ItemOrientedStepHandler<T, S> implements StepHandler {
|
||||
|
||||
/**
|
||||
* @throws ClearFailedException
|
||||
* @see org.springframework.batch.item.ItemWriter#clear()
|
||||
*/
|
||||
public void clear() throws ClearFailedException {
|
||||
itemWriter.clear();
|
||||
}
|
||||
|
||||
/**
|
||||
* @throws FlushFailedException
|
||||
* @see org.springframework.batch.item.ItemWriter#flush()
|
||||
*/
|
||||
public void flush() throws FlushFailedException {
|
||||
itemWriter.flush();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -63,15 +63,13 @@ public interface StepHandler {
|
||||
|
||||
/**
|
||||
* Implementations should delegate to an {@link ItemWriter}.
|
||||
*
|
||||
* @see org.springframework.batch.item.ItemWriter#flush()
|
||||
* @deprecated
|
||||
*/
|
||||
public void flush() throws FlushFailedException;
|
||||
|
||||
/**
|
||||
* Implementations should delegate to an {@link ItemWriter}.
|
||||
*
|
||||
* @see org.springframework.batch.item.ItemWriter#clear()
|
||||
* @deprecated
|
||||
*/
|
||||
public void clear() throws ClearFailedException;
|
||||
|
||||
|
||||
@@ -442,9 +442,7 @@ public class SkipLimitStepFactoryBeanTests extends TestCase {
|
||||
|
||||
protected final Log logger = LogFactory.getLog(getClass());
|
||||
|
||||
private List<Object> written = new ArrayList<Object>();
|
||||
|
||||
private int flushIndex = -1;
|
||||
private List<Object> written = TransactionAwareProxyFactory.createTransactionalList();
|
||||
|
||||
private final Collection<String> failures;
|
||||
|
||||
@@ -461,13 +459,9 @@ public class SkipLimitStepFactoryBeanTests extends TestCase {
|
||||
}
|
||||
|
||||
public void clear() throws ClearFailedException {
|
||||
for (int i = flushIndex + 1; i < written.size(); i++) {
|
||||
written.remove(written.size() - 1);
|
||||
}
|
||||
}
|
||||
|
||||
public void flush() throws FlushFailedException {
|
||||
flushIndex = written.size() - 1;
|
||||
}
|
||||
|
||||
public void write(List<? extends String> items) throws Exception {
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
#Tue Jul 15 10:10:09 CEST 2008
|
||||
#Tue Aug 19 17:57:24 BST 2008
|
||||
eclipse.preferences.version=1
|
||||
org.eclipse.jdt.core.compiler.codegen.inlineJsrBytecode=enabled
|
||||
org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.5
|
||||
@@ -13,7 +13,7 @@ org.eclipse.jdt.core.compiler.problem.assertIdentifier=error
|
||||
org.eclipse.jdt.core.compiler.problem.autoboxing=ignore
|
||||
org.eclipse.jdt.core.compiler.problem.deprecation=warning
|
||||
org.eclipse.jdt.core.compiler.problem.deprecationInDeprecatedCode=disabled
|
||||
org.eclipse.jdt.core.compiler.problem.deprecationWhenOverridingDeprecatedMethod=disabled
|
||||
org.eclipse.jdt.core.compiler.problem.deprecationWhenOverridingDeprecatedMethod=enabled
|
||||
org.eclipse.jdt.core.compiler.problem.discouragedReference=warning
|
||||
org.eclipse.jdt.core.compiler.problem.emptyStatement=ignore
|
||||
org.eclipse.jdt.core.compiler.problem.enumIdentifier=error
|
||||
@@ -66,6 +66,7 @@ org.eclipse.jdt.core.compiler.problem.unnecessaryElse=ignore
|
||||
org.eclipse.jdt.core.compiler.problem.unnecessaryTypeCheck=ignore
|
||||
org.eclipse.jdt.core.compiler.problem.unqualifiedFieldAccess=ignore
|
||||
org.eclipse.jdt.core.compiler.problem.unusedDeclaredThrownException=ignore
|
||||
org.eclipse.jdt.core.compiler.problem.unusedDeclaredThrownExceptionExemptExceptionAndThrowable=enabled
|
||||
org.eclipse.jdt.core.compiler.problem.unusedDeclaredThrownExceptionIncludeDocCommentReference=enabled
|
||||
org.eclipse.jdt.core.compiler.problem.unusedDeclaredThrownExceptionIncludeUncheckedExceptions=disabled
|
||||
org.eclipse.jdt.core.compiler.problem.unusedDeclaredThrownExceptionWhenOverriding=disabled
|
||||
|
||||
@@ -16,7 +16,8 @@
|
||||
package org.springframework.batch.item;
|
||||
|
||||
/**
|
||||
* Unchecked exception indicating that an error has occurred while trying to call {@link ItemWriter#clear()}
|
||||
* Unchecked exception indicating that an error has occurred while trying to
|
||||
* clear a buffer on a rollback.
|
||||
*
|
||||
* @author Lucas Ward
|
||||
* @author Ben Hale
|
||||
@@ -24,7 +25,8 @@ package org.springframework.batch.item;
|
||||
public class ClearFailedException extends ItemWriterException {
|
||||
|
||||
/**
|
||||
* Create a new {@link ClearFailedException} based on a message and another exception.
|
||||
* Create a new {@link ClearFailedException} based on a message and another
|
||||
* exception.
|
||||
*
|
||||
* @param message the message for this exception
|
||||
* @param cause the other exception
|
||||
|
||||
@@ -16,7 +16,7 @@
|
||||
package org.springframework.batch.item;
|
||||
|
||||
/**
|
||||
* Unchecked exception indicating that an error has occurred while trying to call {@link ItemWriter#flush()}
|
||||
* Unchecked exception indicating that an error has occurred while trying to flush a buffer.
|
||||
*
|
||||
* @author Lucas Ward
|
||||
* @author Ben Hale
|
||||
|
||||
@@ -56,6 +56,7 @@ public interface ItemWriter<T> {
|
||||
* @throws FlushFailedException in case of an error. If this exception is
|
||||
* thrown the writer may be in an inconsistent state and manual intervention
|
||||
* might be required to reconcile the data with persistent output.
|
||||
* @deprecated
|
||||
*/
|
||||
void flush() throws FlushFailedException;
|
||||
|
||||
@@ -65,6 +66,7 @@ public interface ItemWriter<T> {
|
||||
* @throws ClearFailedException in case of an error. If this exception is
|
||||
* thrown the writer may be in an inconsistent state and manual intervention
|
||||
* might be required to reconcile the data with persistent output.
|
||||
* @deprecated
|
||||
*/
|
||||
void clear() throws ClearFailedException;
|
||||
}
|
||||
|
||||
@@ -86,8 +86,6 @@ public class FlatFileItemWriter<T> extends ExecutionContextUserSupport implement
|
||||
|
||||
private String encoding = OutputState.DEFAULT_CHARSET;
|
||||
|
||||
private List<String> lineBuffer = new ArrayList<String>();
|
||||
|
||||
private List<String> headerLines = new ArrayList<String>();
|
||||
|
||||
private String lineSeparator = DEFAULT_LINE_SEPARATOR;
|
||||
@@ -185,16 +183,24 @@ public class FlatFileItemWriter<T> extends ExecutionContextUserSupport implement
|
||||
*/
|
||||
public void write(List<? extends T> items) throws Exception {
|
||||
|
||||
for (T item : items) {
|
||||
|
||||
if (getOutputState().isInitialized()) {
|
||||
lineBuffer.add(lineAggregator.aggregate(item) + lineSeparator);
|
||||
}
|
||||
else {
|
||||
throw new WriterNotOpenException("Writer must be open before it can be written to");
|
||||
}
|
||||
|
||||
if (!getOutputState().isInitialized()) {
|
||||
throw new WriterNotOpenException("Writer must be open before it can be written to");
|
||||
}
|
||||
|
||||
OutputState state = getOutputState();
|
||||
|
||||
for (T item : items) {
|
||||
String line = lineAggregator.aggregate(item) + lineSeparator;
|
||||
try {
|
||||
state.write(line);
|
||||
} catch (IOException e) {
|
||||
throw new FlushFailedException(
|
||||
"Could not write data. The file may be corrupt.", e);
|
||||
}
|
||||
}
|
||||
|
||||
state.mark();
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -222,7 +228,7 @@ public class FlatFileItemWriter<T> extends ExecutionContextUserSupport implement
|
||||
}
|
||||
}
|
||||
|
||||
private void doOpen(ExecutionContext executionContext) {
|
||||
private void doOpen(ExecutionContext executionContext) throws ItemStreamException {
|
||||
OutputState outputState = getOutputState();
|
||||
if (executionContext.containsKey(getKey(RESTART_DATA_NAME))) {
|
||||
outputState.restoreFrom(executionContext);
|
||||
@@ -234,8 +240,14 @@ public class FlatFileItemWriter<T> extends ExecutionContextUserSupport implement
|
||||
throw new ItemStreamException("Failed to initialize writer", ioe);
|
||||
}
|
||||
if (outputState.lastMarkedByteOffsetPosition == 0) {
|
||||
for (String line : headerLines) {
|
||||
lineBuffer.add(line + lineSeparator);
|
||||
try {
|
||||
for (String line : headerLines) {
|
||||
outputState.write(line + lineSeparator);
|
||||
}
|
||||
}
|
||||
catch (IOException e) {
|
||||
throw new FlushFailedException(
|
||||
"Could not write headers. The file may be corrupt.", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -264,17 +276,6 @@ public class FlatFileItemWriter<T> extends ExecutionContextUserSupport implement
|
||||
}
|
||||
|
||||
public void flush() throws FlushFailedException {
|
||||
OutputState state = getOutputState();
|
||||
for (String line : lineBuffer) {
|
||||
try {
|
||||
state.write(line);
|
||||
}
|
||||
catch (IOException e) {
|
||||
throw new FlushFailedException("Failed to write line to output file: " + line, e);
|
||||
}
|
||||
}
|
||||
lineBuffer.clear();
|
||||
state.mark();
|
||||
}
|
||||
|
||||
// Returns object representing state.
|
||||
@@ -501,7 +502,6 @@ public class FlatFileItemWriter<T> extends ExecutionContextUserSupport implement
|
||||
}
|
||||
|
||||
public void clear() throws ClearFailedException {
|
||||
lineBuffer.clear();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -20,8 +20,7 @@ import org.springframework.batch.item.FlushFailedException;
|
||||
import org.springframework.batch.item.ItemWriter;
|
||||
|
||||
/**
|
||||
* Abstract {@link ItemWriter} that allows for base classes to only implement
|
||||
* the {@link #flush()} and {@link #clear()} methods if they need it.
|
||||
* Abstract {@link ItemWriter}.
|
||||
*
|
||||
* @author Lucas Ward
|
||||
*/
|
||||
|
||||
@@ -3,8 +3,6 @@ package org.springframework.batch.item.support;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
|
||||
import org.springframework.batch.item.ClearFailedException;
|
||||
import org.springframework.batch.item.FlushFailedException;
|
||||
import org.springframework.batch.item.ItemWriter;
|
||||
|
||||
/**
|
||||
@@ -15,7 +13,7 @@ import org.springframework.batch.item.ItemWriter;
|
||||
* @author Robert Kasanicky
|
||||
* @author Dave Syer
|
||||
*/
|
||||
public class CompositeItemWriter<T> implements ItemWriter<T> {
|
||||
public class CompositeItemWriter<T> extends AbstractItemWriter<T> {
|
||||
|
||||
private List<ItemWriter<? super T>> delegates;
|
||||
|
||||
@@ -32,16 +30,4 @@ public class CompositeItemWriter<T> implements ItemWriter<T> {
|
||||
}
|
||||
}
|
||||
|
||||
public void clear() throws ClearFailedException {
|
||||
for (ItemWriter<? super T> writer : delegates) {
|
||||
writer.clear();
|
||||
}
|
||||
}
|
||||
|
||||
public void flush() throws FlushFailedException {
|
||||
for (ItemWriter<? super T> writer : delegates) {
|
||||
writer.flush();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -109,18 +109,11 @@ public class StaxEventItemWriter<T> extends ExecutionContextUserSupport implemen
|
||||
// byte offset in file channel at last commit point
|
||||
private long lastCommitPointPosition = 0;
|
||||
|
||||
// processed record count at last commit point
|
||||
private long lastCommitPointRecordCount = 0;
|
||||
|
||||
// current count of processed records
|
||||
private long currentRecordCount = 0;
|
||||
|
||||
private boolean saveState = true;
|
||||
|
||||
// holds the list of items for writing before they are actually written on
|
||||
// #flush()
|
||||
private List<T> buffer = new ArrayList<T>();
|
||||
|
||||
private List<? extends T> headers = new ArrayList<T>();
|
||||
|
||||
public StaxEventItemWriter() {
|
||||
@@ -345,6 +338,8 @@ public class StaxEventItemWriter<T> extends ExecutionContextUserSupport implemen
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
writer.flush();
|
||||
|
||||
}
|
||||
|
||||
@@ -384,8 +379,8 @@ public class StaxEventItemWriter<T> extends ExecutionContextUserSupport implemen
|
||||
log.error(e);
|
||||
}
|
||||
|
||||
flush();
|
||||
try {
|
||||
delegateEventWriter.flush();
|
||||
endDocument(delegateEventWriter);
|
||||
eventWriter.close();
|
||||
channel.close();
|
||||
@@ -399,15 +394,25 @@ public class StaxEventItemWriter<T> extends ExecutionContextUserSupport implemen
|
||||
}
|
||||
|
||||
/**
|
||||
* Write the value object to internal buffer.
|
||||
* Write the value objects and flush them to the file.
|
||||
*
|
||||
* @param item the value object
|
||||
* @see #flush()
|
||||
* @param items the value object
|
||||
*/
|
||||
public void write(List<? extends T> item) {
|
||||
public void write(List<? extends T> items) {
|
||||
|
||||
currentRecordCount+=item.size();
|
||||
buffer.addAll(item);
|
||||
currentRecordCount+=items.size();
|
||||
|
||||
for (T item : items) {
|
||||
serializer.serializeObject(eventWriter, item);
|
||||
}
|
||||
try {
|
||||
eventWriter.flush();
|
||||
}
|
||||
catch (XMLStreamException e) {
|
||||
throw new FlushFailedException("Failed to flush the events", e);
|
||||
}
|
||||
|
||||
lastCommitPointPosition = getPosition();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -468,28 +473,12 @@ public class StaxEventItemWriter<T> extends ExecutionContextUserSupport implemen
|
||||
* Writes buffered items to XML stream and marks restore point.
|
||||
*/
|
||||
public void flush() throws FlushFailedException {
|
||||
|
||||
for (T item : buffer) {
|
||||
serializer.serializeObject(eventWriter, item);
|
||||
}
|
||||
try {
|
||||
eventWriter.flush();
|
||||
}
|
||||
catch (XMLStreamException e) {
|
||||
throw new FlushFailedException("Failed to flush the events", e);
|
||||
}
|
||||
buffer.clear();
|
||||
|
||||
lastCommitPointPosition = getPosition();
|
||||
lastCommitPointRecordCount = currentRecordCount;
|
||||
}
|
||||
|
||||
/**
|
||||
* Clear the output buffer
|
||||
*/
|
||||
public void clear() throws ClearFailedException {
|
||||
currentRecordCount = lastCommitPointRecordCount;
|
||||
buffer.clear();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -198,49 +198,13 @@ public class FlatFileItemWriterTests extends TestCase {
|
||||
assertEquals("1|2|", lineFromFile);
|
||||
}
|
||||
|
||||
public void testRollback() throws Exception {
|
||||
writer.open(executionContext);
|
||||
writer.write(Collections.singletonList("testLine1"));
|
||||
// rollback
|
||||
rollback();
|
||||
writer.flush();
|
||||
writer.close(null);
|
||||
String lineFromFile = readLine();
|
||||
assertEquals(null, lineFromFile);
|
||||
}
|
||||
|
||||
public void testCommit() throws Exception {
|
||||
writer.open(executionContext);
|
||||
writer.write(Collections.singletonList("testLine1"));
|
||||
// rollback
|
||||
commit();
|
||||
writer.close(null);
|
||||
String lineFromFile = readLine();
|
||||
assertEquals("testLine1", lineFromFile);
|
||||
}
|
||||
|
||||
public void testRestart() throws Exception {
|
||||
|
||||
writer.open(executionContext);
|
||||
|
||||
// write some lines
|
||||
writer.write(Arrays.asList(new String[] { "testLine1", "testLine2", "testLine3" }));
|
||||
|
||||
// commit
|
||||
commit();
|
||||
|
||||
// this will be rolled back...
|
||||
writer.write(Collections.singletonList("this will be rolled back"));
|
||||
|
||||
// rollback
|
||||
rollback();
|
||||
|
||||
// write more lines
|
||||
writer.write(Arrays.asList(new String[] {"testLine4", "testLine5"}));
|
||||
|
||||
// commit
|
||||
commit();
|
||||
|
||||
// get restart data
|
||||
writer.update(executionContext);
|
||||
// close template
|
||||
@@ -248,19 +212,15 @@ public class FlatFileItemWriterTests extends TestCase {
|
||||
|
||||
// init with correct data
|
||||
writer.open(executionContext);
|
||||
|
||||
// write more lines
|
||||
writer.write(Arrays.asList(new String[] {"testLine6","testLine7","testLine8"}));
|
||||
|
||||
commit();
|
||||
|
||||
// get statistics
|
||||
writer.update(executionContext);
|
||||
// close template
|
||||
writer.close(executionContext);
|
||||
|
||||
// verify what was written to the file
|
||||
for (int i = 1; i < 9; i++) {
|
||||
for (int i = 1; i <= 8; i++) {
|
||||
assertEquals("testLine" + i, readLine());
|
||||
}
|
||||
|
||||
|
||||
@@ -31,6 +31,7 @@ import org.springframework.oxm.Marshaller;
|
||||
import org.springframework.oxm.XmlMappingException;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.util.ClassUtils;
|
||||
import org.springframework.util.StringUtils;
|
||||
import org.springframework.xml.transform.StaxResult;
|
||||
|
||||
/**
|
||||
@@ -58,56 +59,13 @@ public class StaxEventItemWriterTests {
|
||||
private static final String TEST_STRING = "<!--" + ClassUtils.getShortName(StaxEventItemWriter.class)
|
||||
+ "-testString-->";
|
||||
|
||||
private static final int NOT_FOUND = -1;
|
||||
|
||||
@Before
|
||||
public void setUp() throws Exception {
|
||||
resource = new FileSystemResource(File.createTempFile("StaxEventWriterOutputSourceTests", "xml"));
|
||||
resource = new FileSystemResource(File.createTempFile("StaxEventWriterOutputSourceTests", ".xml"));
|
||||
writer = createItemWriter();
|
||||
executionContext = new ExecutionContext();
|
||||
}
|
||||
|
||||
/**
|
||||
* Flush should pass buffered items to Serializer.
|
||||
*/
|
||||
@Test
|
||||
public void testFlush() throws Exception {
|
||||
writer.open(executionContext);
|
||||
InputCheckMarshaller marshaller = new InputCheckMarshaller();
|
||||
MarshallingEventWriterSerializer<Object> serializer = new MarshallingEventWriterSerializer<Object>(marshaller);
|
||||
writer.setSerializer(serializer);
|
||||
|
||||
// see asserts in the marshaller
|
||||
writer.write(items);
|
||||
assertFalse(marshaller.wasCalled);
|
||||
|
||||
writer.flush();
|
||||
assertTrue(marshaller.wasCalled);
|
||||
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testClear() throws Exception {
|
||||
writer.open(executionContext);
|
||||
writer.write(Arrays.asList(new Object[] {item, item}));
|
||||
writer.clear();
|
||||
// writer.write(item);
|
||||
writer.flush();
|
||||
assertFalse(contains(outputFileContent(), TEST_STRING));
|
||||
}
|
||||
|
||||
/**
|
||||
* Rolled back records should not be written to output file.
|
||||
*/
|
||||
@Test
|
||||
public void testRollback() throws Exception {
|
||||
writer.open(executionContext);
|
||||
writer.write(items);
|
||||
// rollback
|
||||
writer.clear();
|
||||
assertFalse(outputFileContent().contains(TEST_STRING));
|
||||
}
|
||||
|
||||
/**
|
||||
* Item is written to the output file only after flush.
|
||||
*/
|
||||
@@ -115,11 +73,9 @@ public class StaxEventItemWriterTests {
|
||||
public void testWriteAndFlush() throws Exception {
|
||||
writer.open(executionContext);
|
||||
writer.write(items);
|
||||
writer.close(executionContext);
|
||||
String content = outputFileContent();
|
||||
assertFalse(content.contains(TEST_STRING));
|
||||
writer.flush();
|
||||
content = outputFileContent();
|
||||
assertTrue("Wrong content: "+content, contains(content, TEST_STRING));
|
||||
assertTrue("Wrong content: "+content, content.contains(TEST_STRING));
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -130,7 +86,6 @@ public class StaxEventItemWriterTests {
|
||||
writer.open(executionContext);
|
||||
// write item
|
||||
writer.write(items);
|
||||
writer.flush();
|
||||
writer.update(executionContext);
|
||||
writer.close(executionContext);
|
||||
|
||||
@@ -143,14 +98,8 @@ public class StaxEventItemWriterTests {
|
||||
// check the output is concatenation of 'before restart' and 'after
|
||||
// restart' writes.
|
||||
String outputFile = outputFileContent();
|
||||
int firstRecord = outputFile.indexOf(TEST_STRING);
|
||||
int secondRecord = outputFile.indexOf(TEST_STRING, firstRecord + TEST_STRING.length());
|
||||
int thirdRecord = outputFile.indexOf(TEST_STRING, secondRecord + TEST_STRING.length());
|
||||
|
||||
// (two records should be written)
|
||||
assertTrue(firstRecord != NOT_FOUND);
|
||||
assertTrue(secondRecord != NOT_FOUND);
|
||||
assertEquals(NOT_FOUND, thirdRecord);
|
||||
|
||||
assertEquals(2, StringUtils.countOccurrencesOf(outputFile, TEST_STRING));
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -165,9 +114,9 @@ public class StaxEventItemWriterTests {
|
||||
writer.write(items);
|
||||
writer.flush();
|
||||
String content = outputFileContent();
|
||||
assertTrue("Wrong content: "+content, contains(content, "<!--" + header1 + "-->"));
|
||||
assertTrue("Wrong content: "+content, contains(content, "<!--" + header2 + "-->"));
|
||||
assertTrue("Wrong content: "+content, contains(content, TEST_STRING));
|
||||
assertTrue("Wrong content: "+content, content.contains(("<!--" + header1 + "-->")));
|
||||
assertTrue("Wrong content: "+content, content.contains(("<!--" + header2 + "-->")));
|
||||
assertTrue("Wrong content: "+content, content.contains(TEST_STRING));
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -179,13 +128,12 @@ public class StaxEventItemWriterTests {
|
||||
writer.setHeaderItems(new Object[] {header});
|
||||
writer.open(executionContext);
|
||||
writer.write(items);
|
||||
writer.clear();
|
||||
writer.open(executionContext);
|
||||
writer.write(items);
|
||||
writer.flush();
|
||||
writer.close(executionContext);
|
||||
String content = outputFileContent();
|
||||
assertEquals("Wrong content: "+content, 1, countContains(content, "<!--" + header + "-->"));
|
||||
assertEquals("Wrong content: "+content, 1, countContains(content, TEST_STRING));
|
||||
assertEquals("Wrong content: "+content, 1, StringUtils.countOccurrencesOf(content, ("<!--" + header + "-->")));
|
||||
assertEquals("Wrong content: "+content, 1, StringUtils.countOccurrencesOf(content, TEST_STRING));
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -197,16 +145,15 @@ public class StaxEventItemWriterTests {
|
||||
writer.setHeaderItems(new Object[] {header});
|
||||
writer.open(executionContext);
|
||||
writer.write(items);
|
||||
writer.flush();
|
||||
writer.update(executionContext);
|
||||
writer.close(executionContext);
|
||||
writer.open(executionContext);
|
||||
writer.write(items);
|
||||
writer.clear();
|
||||
writer.flush();
|
||||
writer.close(executionContext);
|
||||
String content = outputFileContent();
|
||||
assertEquals("Wrong content: "+content, 1, countContains(content, "<!--" + header + "-->"));
|
||||
assertEquals("Wrong content: "+content, 1, countContains(content, TEST_STRING));
|
||||
assertEquals("Wrong content: "+content, 1, StringUtils.countOccurrencesOf(content, ("<!--" + header + "-->")));
|
||||
// THis test is not transactional, so the body gets written twice, but at least there's only one header
|
||||
assertEquals("Wrong content: "+content, 2, StringUtils.countOccurrencesOf(content, TEST_STRING));
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -240,13 +187,10 @@ public class StaxEventItemWriterTests {
|
||||
}
|
||||
});
|
||||
writer.open(executionContext);
|
||||
writer.flush();
|
||||
|
||||
assertTrue(outputFileContent().indexOf("<testroot attribute=\"value\"") != NOT_FOUND);
|
||||
|
||||
writer.close(null);
|
||||
assertTrue(outputFileContent().indexOf("<testroot attribute=\"value\">") != NOT_FOUND);
|
||||
assertTrue(outputFileContent().endsWith("</testroot>"));
|
||||
String content = outputFileContent();
|
||||
assertTrue(content.contains("<testroot attribute=\"value\">"));
|
||||
assertTrue(content.endsWith("</testroot>"));
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -267,25 +211,6 @@ public class StaxEventItemWriterTests {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks the received parameters.
|
||||
*/
|
||||
private class InputCheckMarshaller implements Marshaller {
|
||||
|
||||
boolean wasCalled = false;
|
||||
|
||||
public void marshal(Object graph, Result result) {
|
||||
wasCalled = true;
|
||||
assertTrue(result instanceof StaxResult);
|
||||
assertSame(item, graph);
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public boolean supports(Class clazz) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Writes object's toString representation as XML comment.
|
||||
*/
|
||||
@@ -336,18 +261,4 @@ public class StaxEventItemWriterTests {
|
||||
|
||||
return source;
|
||||
}
|
||||
|
||||
private boolean contains(String str, String searchStr) {
|
||||
return str.indexOf(searchStr) != -1;
|
||||
}
|
||||
|
||||
private int countContains(String str, String searchStr) {
|
||||
int begin = -1;
|
||||
int count = 0;
|
||||
while (str.indexOf(searchStr, begin+1) > begin) {
|
||||
count++;
|
||||
begin = str.indexOf(searchStr, begin);
|
||||
}
|
||||
return count;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
package org.springframework.batch.integration.chunk;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import org.apache.commons.logging.Log;
|
||||
@@ -15,23 +14,16 @@ import org.springframework.batch.item.ItemStream;
|
||||
import org.springframework.batch.item.ItemStreamException;
|
||||
import org.springframework.batch.item.ItemWriter;
|
||||
import org.springframework.batch.repeat.ExitStatus;
|
||||
import org.springframework.batch.repeat.RepeatContext;
|
||||
import org.springframework.integration.message.BlockingSource;
|
||||
import org.springframework.integration.message.GenericMessage;
|
||||
import org.springframework.integration.message.Message;
|
||||
import org.springframework.integration.message.MessageTarget;
|
||||
import org.springframework.transaction.support.TransactionSynchronizationManager;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
public class ChunkMessageChannelItemWriter<T> extends StepExecutionListenerSupport implements ItemWriter<T>, ItemStream {
|
||||
|
||||
private static final Log logger = LogFactory.getLog(ChunkMessageChannelItemWriter.class);
|
||||
|
||||
/**
|
||||
* Key for items processed in the current transaction {@link RepeatContext}.
|
||||
*/
|
||||
private static final String ITEMS_PROCESSED = ChunkMessageChannelItemWriter.class.getName() + ".ITEMS_PROCESSED";
|
||||
|
||||
static final String ACTUAL = "ACTUAL";
|
||||
|
||||
static final String EXPECTED = "EXPECTED";
|
||||
@@ -65,38 +57,16 @@ public class ChunkMessageChannelItemWriter<T> extends StepExecutionListenerSuppo
|
||||
}
|
||||
|
||||
public void write(List<? extends T> items) throws Exception {
|
||||
bindTransactionResources();
|
||||
for (T item : items) {
|
||||
getProcessed().add(item);
|
||||
logger.debug("Added item to chunk: " + item);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Flush the buffer, sending the items as a chunk message to be processed by
|
||||
* a {@link ChunkHandler}. To avoid overwhelming the receivers, this method
|
||||
* will block until the number of chunks pending is less than the throttle
|
||||
* limit.
|
||||
*
|
||||
* @see org.springframework.batch.item.ItemWriter#flush()
|
||||
*/
|
||||
public void flush() throws FlushFailedException {
|
||||
|
||||
bindTransactionResources(); // in case we are called outside a
|
||||
// transaction
|
||||
|
||||
// Block until expecting <= throttle limit - can Spring
|
||||
// Integration do that for me?
|
||||
while (localState.getExpecting() > throttleLimit) {
|
||||
getNextResult(100);
|
||||
}
|
||||
|
||||
List<T> processed = getProcessed();
|
||||
if (!items.isEmpty()) {
|
||||
|
||||
if (!processed.isEmpty()) {
|
||||
|
||||
logger.debug("Dispatching chunk: " + processed);
|
||||
ChunkRequest<T> request = new ChunkRequest<T>(processed, localState.getJobId(), localState.getSkipCount());
|
||||
logger.debug("Dispatching chunk: " + items);
|
||||
ChunkRequest<T> request = new ChunkRequest<T>(items, localState.getJobId(), localState.getSkipCount());
|
||||
GenericMessage<ChunkRequest<T>> message = new GenericMessage<ChunkRequest<T>>(request);
|
||||
target.send(message);
|
||||
localState.expected++;
|
||||
@@ -106,8 +76,20 @@ public class ChunkMessageChannelItemWriter<T> extends StepExecutionListenerSuppo
|
||||
// Short little timeout to look for an immediate reply.
|
||||
getNextResult(1);
|
||||
|
||||
unbindTransactionResources();
|
||||
}
|
||||
|
||||
/**
|
||||
* No-op.
|
||||
* @see org.springframework.batch.item.ItemWriter#flush()
|
||||
*/
|
||||
public void flush() throws FlushFailedException {
|
||||
}
|
||||
|
||||
/**
|
||||
* No-op.
|
||||
* @see org.springframework.batch.item.ItemWriter#clear()
|
||||
*/
|
||||
public void clear() throws ClearFailedException {
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -179,7 +161,6 @@ public class ChunkMessageChannelItemWriter<T> extends StepExecutionListenerSuppo
|
||||
* otherwise return null.
|
||||
*/
|
||||
private void getNextResult(long timeout) {
|
||||
// TODO: use the timeout
|
||||
Message<ChunkResponse> message = source.receive(timeout);
|
||||
if (message != null) {
|
||||
ChunkResponse payload = message.getPayload();
|
||||
@@ -196,50 +177,6 @@ public class ChunkMessageChannelItemWriter<T> extends StepExecutionListenerSuppo
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Accessor for the list of processed items in this transaction.
|
||||
*
|
||||
* @return the processed
|
||||
*/
|
||||
private List<T> getProcessed() {
|
||||
Assert.state(TransactionSynchronizationManager.hasResource(ITEMS_PROCESSED),
|
||||
"Processed items not bound to transaction.");
|
||||
@SuppressWarnings("unchecked")
|
||||
List<T> processed = (List<T>) TransactionSynchronizationManager.getResource(ITEMS_PROCESSED);
|
||||
return processed;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set up the {@link RepeatContext} as a transaction resource.
|
||||
*
|
||||
* @param context the context to set
|
||||
*/
|
||||
private void bindTransactionResources() {
|
||||
if (TransactionSynchronizationManager.hasResource(ITEMS_PROCESSED)) {
|
||||
return;
|
||||
}
|
||||
TransactionSynchronizationManager.bindResource(ITEMS_PROCESSED, new ArrayList<Object>());
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove the transaction resource associated with this context.
|
||||
*/
|
||||
private void unbindTransactionResources() {
|
||||
if (!TransactionSynchronizationManager.hasResource(ITEMS_PROCESSED)) {
|
||||
return;
|
||||
}
|
||||
TransactionSynchronizationManager.unbindResource(ITEMS_PROCESSED);
|
||||
}
|
||||
|
||||
/**
|
||||
* Clear the buffer.
|
||||
*
|
||||
* @see org.springframework.batch.item.ItemWriter#clear()
|
||||
*/
|
||||
public void clear() throws ClearFailedException {
|
||||
unbindTransactionResources();
|
||||
}
|
||||
|
||||
private static class LocalState {
|
||||
private long actual;
|
||||
|
||||
|
||||
@@ -15,16 +15,15 @@
|
||||
*/
|
||||
package org.springframework.batch.sample.common;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import static org.junit.Assert.assertEquals;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
import junit.framework.TestCase;
|
||||
|
||||
import org.springframework.batch.item.ClearFailedException;
|
||||
import org.springframework.batch.item.FlushFailedException;
|
||||
import org.springframework.batch.item.ItemWriter;
|
||||
import org.junit.Test;
|
||||
import org.springframework.batch.item.support.AbstractItemWriter;
|
||||
import org.springframework.batch.support.transaction.TransactionAwareProxyFactory;
|
||||
|
||||
/**
|
||||
* Unit test class that was used as part of the Reference Documentation. I'm
|
||||
@@ -34,45 +33,24 @@ import org.springframework.batch.item.ItemWriter;
|
||||
* @author Lucas Ward
|
||||
*
|
||||
*/
|
||||
public class CustomItemWriterTests extends TestCase {
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
*
|
||||
* @see junit.framework.TestCase#setUp()
|
||||
*/
|
||||
protected void setUp() throws Exception {
|
||||
super.setUp();
|
||||
}
|
||||
public class CustomItemWriterTests {
|
||||
|
||||
@Test
|
||||
public void testFlush() throws Exception {
|
||||
|
||||
CustomItemWriter<String> itemWriter = new CustomItemWriter<String>();
|
||||
itemWriter.write(Collections.singletonList("1"));
|
||||
assertEquals(0, itemWriter.getOutput().size());
|
||||
itemWriter.flush();
|
||||
assertEquals(1, itemWriter.getOutput().size());
|
||||
itemWriter.write(Arrays.asList(new String[] {"2","3"}));
|
||||
itemWriter.clear();
|
||||
assertEquals(1, itemWriter.getOutput().size());
|
||||
assertEquals(3, itemWriter.getOutput().size());
|
||||
}
|
||||
|
||||
public class CustomItemWriter<T> implements ItemWriter<T> {
|
||||
public class CustomItemWriter<T> extends AbstractItemWriter<T> {
|
||||
|
||||
List<T> output = new ArrayList<T>();
|
||||
|
||||
List<T> buffer = new ArrayList<T>();
|
||||
List<T> output = TransactionAwareProxyFactory.createTransactionalList();
|
||||
|
||||
public void write(List<? extends T> items) throws Exception {
|
||||
buffer.addAll(items);
|
||||
}
|
||||
|
||||
public void clear() throws ClearFailedException {
|
||||
buffer.clear();
|
||||
}
|
||||
|
||||
public void flush() throws FlushFailedException {
|
||||
output.addAll(buffer);
|
||||
output.addAll(items);
|
||||
}
|
||||
|
||||
public List<T> getOutput() {
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<projectDescription>
|
||||
<name>spring-batch</name>
|
||||
<name>src</name>
|
||||
<comment></comment>
|
||||
<projects>
|
||||
</projects>
|
||||
|
||||
Reference in New Issue
Block a user