From a939b194fe253263b13e75519e67bd662e48f1e6 Mon Sep 17 00:00:00 2001 From: Michael Minella Date: Thu, 11 Jul 2013 16:20:49 -0500 Subject: [PATCH 01/15] Merged pull request #195 from stephlag/patch-1 --- .../batch/item/support/CompositeItemProcessor.java | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/spring-batch-infrastructure/src/main/java/org/springframework/batch/item/support/CompositeItemProcessor.java b/spring-batch-infrastructure/src/main/java/org/springframework/batch/item/support/CompositeItemProcessor.java index 27d7ac5b3..eabea1227 100644 --- a/spring-batch-infrastructure/src/main/java/org/springframework/batch/item/support/CompositeItemProcessor.java +++ b/spring-batch-infrastructure/src/main/java/org/springframework/batch/item/support/CompositeItemProcessor.java @@ -28,7 +28,7 @@ import org.springframework.util.Assert; * transformation is the entry value of the next).
*
* - * Note the user is responsible for injecting a chain of {@link ItemProcessor} s + * Note the user is responsible for injecting a chain of {@link ItemProcessor}s * that conforms to declared input and output types. * * @author Robert Kasanicky @@ -37,7 +37,7 @@ public class CompositeItemProcessor implements ItemProcessor, Initia private List> delegates; - @Override + @Override @SuppressWarnings("unchecked") public O process(I item) throws Exception { Object result = item; @@ -51,7 +51,7 @@ public class CompositeItemProcessor implements ItemProcessor, Initia return (O) result; } - @Override + @Override public void afterPropertiesSet() throws Exception { Assert.notNull(delegates, "The 'delegates' may not be null"); Assert.notEmpty(delegates, "The 'delegates' may not be empty"); From a65bcc48d1de153d412c8ad7445497e67278bacb Mon Sep 17 00:00:00 2001 From: jpraet Date: Sun, 30 Jun 2013 14:02:29 +0200 Subject: [PATCH 02/15] BATCH-2054: StaxEventItemWriter fails on a NullPointerException with Spring OXM 3.2.x. --- .../batch/item/xml/StaxEventItemWriter.java | 2 +- .../stax/NoStartEndDocumentStreamWriter.java | 6 +++++ .../item/xml/StaxEventItemWriterTests.java | 26 +++++++++++++++++++ .../stax/NoStartEndDocumentWriterTests.java | 13 ++++++++++ 4 files changed, 46 insertions(+), 1 deletion(-) diff --git a/spring-batch-infrastructure/src/main/java/org/springframework/batch/item/xml/StaxEventItemWriter.java b/spring-batch-infrastructure/src/main/java/org/springframework/batch/item/xml/StaxEventItemWriter.java index c81c36250..30dd1840f 100644 --- a/spring-batch-infrastructure/src/main/java/org/springframework/batch/item/xml/StaxEventItemWriter.java +++ b/spring-batch-infrastructure/src/main/java/org/springframework/batch/item/xml/StaxEventItemWriter.java @@ -658,7 +658,7 @@ ResourceAwareItemWriterItemStream, InitializingBean { finally { try { - eventWriter.close(); + delegateEventWriter.close(); } catch (XMLStreamException e) { log.error("Unable to close file resource: [" + resource + "] " + e); diff --git a/spring-batch-infrastructure/src/main/java/org/springframework/batch/item/xml/stax/NoStartEndDocumentStreamWriter.java b/spring-batch-infrastructure/src/main/java/org/springframework/batch/item/xml/stax/NoStartEndDocumentStreamWriter.java index 039b7b3de..dea26eb7e 100644 --- a/spring-batch-infrastructure/src/main/java/org/springframework/batch/item/xml/stax/NoStartEndDocumentStreamWriter.java +++ b/spring-batch-infrastructure/src/main/java/org/springframework/batch/item/xml/stax/NoStartEndDocumentStreamWriter.java @@ -39,4 +39,10 @@ public class NoStartEndDocumentStreamWriter extends AbstractEventWriterWrapper { wrappedEventWriter.add(event); } } + + // prevents OXM Marshallers from closing the XMLEventWriter + @Override + public void close() throws XMLStreamException { + flush(); + } } diff --git a/spring-batch-infrastructure/src/test/java/org/springframework/batch/item/xml/StaxEventItemWriterTests.java b/spring-batch-infrastructure/src/test/java/org/springframework/batch/item/xml/StaxEventItemWriterTests.java index a467fdaf7..df60339e1 100644 --- a/spring-batch-infrastructure/src/test/java/org/springframework/batch/item/xml/StaxEventItemWriterTests.java +++ b/spring-batch-infrastructure/src/test/java/org/springframework/batch/item/xml/StaxEventItemWriterTests.java @@ -726,6 +726,32 @@ public class StaxEventItemWriterTests { assertEquals("Wrong content: " + content, "", content); } + + /** + * Test with OXM Marshaller that closes the XMLEventWriter. + */ + // BATCH-2054 + @Test + public void testMarshallingClosingEventWriter() throws Exception { + writer.setMarshaller(new SimpleMarshaller() { + @Override + public void marshal(Object graph, Result result) throws XmlMappingException, IOException { + super.marshal(graph, result); + try { + StaxUtils.getXmlEventWriter(result).close(); + } catch (Exception e) { + throw new RuntimeException("Exception while writing to output file", e); + } + } + + }); + writer.afterPropertiesSet(); + + writer.open(executionContext); + + writer.write(items); + writer.write(items); + } /** * Writes object's toString representation as XML comment. diff --git a/spring-batch-infrastructure/src/test/java/org/springframework/batch/item/xml/stax/NoStartEndDocumentWriterTests.java b/spring-batch-infrastructure/src/test/java/org/springframework/batch/item/xml/stax/NoStartEndDocumentWriterTests.java index b0a1f1afd..1de835ede 100644 --- a/spring-batch-infrastructure/src/test/java/org/springframework/batch/item/xml/stax/NoStartEndDocumentWriterTests.java +++ b/spring-batch-infrastructure/src/test/java/org/springframework/batch/item/xml/stax/NoStartEndDocumentWriterTests.java @@ -7,6 +7,9 @@ import javax.xml.stream.events.XMLEvent; import junit.framework.TestCase; import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.never; +import static org.mockito.Mockito.times; /** * Tests for {@link NoStartEndDocumentStreamWriter} @@ -44,4 +47,14 @@ public class NoStartEndDocumentWriterTests extends TestCase { writer.add(eventFactory.createEndDocument()); } + + /** + * Close is not delegated to the wrapped writer. Instead, the wrapped writer is flushed. + */ + public void testClose() throws Exception { + writer.close(); + + verify(wrappedWriter, times(1)).flush(); + verify(wrappedWriter, never()).close(); + } } From a4c00d9b78c731f2d029bfac66d45726091a3507 Mon Sep 17 00:00:00 2001 From: jpraet Date: Thu, 13 Jun 2013 21:36:31 +0200 Subject: [PATCH 03/15] BATCH-1984: CompositeItemProcessor.setDelegates argument has limiting generic type --- .../item/support/CompositeItemProcessor.java | 20 ++++-- .../support/CompositeItemProcessorTests.java | 62 +++++++++++++------ 2 files changed, 57 insertions(+), 25 deletions(-) diff --git a/spring-batch-infrastructure/src/main/java/org/springframework/batch/item/support/CompositeItemProcessor.java b/spring-batch-infrastructure/src/main/java/org/springframework/batch/item/support/CompositeItemProcessor.java index eabea1227..c1f9d57d4 100644 --- a/spring-batch-infrastructure/src/main/java/org/springframework/batch/item/support/CompositeItemProcessor.java +++ b/spring-batch-infrastructure/src/main/java/org/springframework/batch/item/support/CompositeItemProcessor.java @@ -35,21 +35,31 @@ import org.springframework.util.Assert; */ public class CompositeItemProcessor implements ItemProcessor, InitializingBean { - private List> delegates; + private List> delegates; @Override @SuppressWarnings("unchecked") public O process(I item) throws Exception { Object result = item; - for (ItemProcessor delegate : delegates) { + for (ItemProcessor delegate : delegates) { if (result == null) { return null; } - result = delegate.process(result); + + result = processItem(delegate, result); } return (O) result; } + + /* + * Helper method to work around wildcard capture compiler error: see http://docs.oracle.com/javase/tutorial/java/generics/capture.html + * The method process(capture#1-of ?) in the type ItemProcessor is not applicable for the arguments (Object) + */ + @SuppressWarnings("unchecked") + private Object processItem(ItemProcessor processor, Object input) throws Exception { + return processor.process((T) input); + } @Override public void afterPropertiesSet() throws Exception { @@ -57,8 +67,8 @@ public class CompositeItemProcessor implements ItemProcessor, Initia Assert.notEmpty(delegates, "The 'delegates' may not be empty"); } - public void setDelegates(List> delegates) { + public void setDelegates(List> delegates) { this.delegates = delegates; - } + } } diff --git a/spring-batch-infrastructure/src/test/java/org/springframework/batch/item/support/CompositeItemProcessorTests.java b/spring-batch-infrastructure/src/test/java/org/springframework/batch/item/support/CompositeItemProcessorTests.java index 925fa9e65..7dcfa6010 100644 --- a/spring-batch-infrastructure/src/test/java/org/springframework/batch/item/support/CompositeItemProcessorTests.java +++ b/spring-batch-infrastructure/src/test/java/org/springframework/batch/item/support/CompositeItemProcessorTests.java @@ -1,9 +1,10 @@ package org.springframework.batch.item.support; -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.when; +import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertSame; import static org.junit.Assert.fail; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; import java.util.ArrayList; @@ -11,7 +12,6 @@ import org.junit.Assert; import org.junit.Before; import org.junit.Test; import org.springframework.batch.item.ItemProcessor; -import org.springframework.batch.item.support.CompositeItemProcessor; /** * Tests for {@link CompositeItemProcessor}. @@ -22,23 +22,23 @@ import org.springframework.batch.item.support.CompositeItemProcessor; public class CompositeItemProcessorTests { private CompositeItemProcessor composite = new CompositeItemProcessor(); - + private ItemProcessor processor1; private ItemProcessor processor2; - - @SuppressWarnings("unchecked") + + @SuppressWarnings({ "unchecked", "serial" }) @Before public void setUp() throws Exception { processor1 = mock(ItemProcessor.class); processor2 = mock(ItemProcessor.class); - - composite.setDelegates(new ArrayList>() {{ - add(processor1); add(processor2); + + composite.setDelegates(new ArrayList>() {{ + add(processor1); add(processor2); }}); - + composite.afterPropertiesSet(); } - + /** * Regular usage scenario - item is passed through the processing chain, * return value of the of the last transformation is returned by the composite. @@ -50,20 +50,42 @@ public class CompositeItemProcessorTests { Object itemAfterSecondTransformation = new Object(); when(processor1.process(item)).thenReturn(itemAfterFirstTransfromation); - + when(processor2.process(itemAfterFirstTransfromation)).thenReturn(itemAfterSecondTransformation); - + assertSame(itemAfterSecondTransformation, composite.process(item)); } - + /** - * The list of transformers must not be null or empty and + * Test that the CompositeItemProcessor can work with generic types for the ItemProcessor delegates. + */ + @Test + @SuppressWarnings({"unchecked", "serial"}) + public void testItemProcessorGenerics() throws Exception { + CompositeItemProcessor composite = new CompositeItemProcessor(); + final ItemProcessor processor1 = mock(ItemProcessor.class); + final ItemProcessor processor2 = mock(ItemProcessor.class); + composite.setDelegates(new ArrayList>() {{ + add(processor1); add(processor2); + }}); + composite.afterPropertiesSet(); + + when(processor1.process("input")).thenReturn(5); + + when(processor2.process(5)).thenReturn("output"); + + assertEquals("output", composite.process("input")); + + } + + /** + * The list of transformers must not be null or empty and * can contain only instances of {@link ItemProcessor}. */ @Test public void testAfterPropertiesSet() throws Exception { - + // value not set composite.setDelegates(null); try { @@ -73,7 +95,7 @@ public class CompositeItemProcessorTests { catch (IllegalArgumentException e) { // expected } - + // empty list composite.setDelegates(new ArrayList>()); try { @@ -83,12 +105,12 @@ public class CompositeItemProcessorTests { catch (IllegalArgumentException e) { // expected } - + } - + @Test public void testFilteredItemInFirstProcessor() throws Exception{ - + Object item = new Object(); when(processor1.process(item)).thenReturn(null); Assert.assertEquals(null,composite.process(item)); From e32ac40e9f93dbee77710463906b2c971b7dbe5c Mon Sep 17 00:00:00 2001 From: jpraet Date: Sat, 15 Jun 2013 11:04:45 +0200 Subject: [PATCH 04/15] BATCH-1977: add missing migration scripts for DB2 and Derby --- .../batch/core/migration/migration-db2.sql | 28 +++++++++++++++++++ .../batch/core/migration/migration-derby.sql | 28 +++++++++++++++++++ 2 files changed, 56 insertions(+) create mode 100644 spring-batch-core/src/main/resources/org/springframework/batch/core/migration/migration-db2.sql create mode 100644 spring-batch-core/src/main/resources/org/springframework/batch/core/migration/migration-derby.sql diff --git a/spring-batch-core/src/main/resources/org/springframework/batch/core/migration/migration-db2.sql b/spring-batch-core/src/main/resources/org/springframework/batch/core/migration/migration-db2.sql new file mode 100644 index 000000000..c3f08c886 --- /dev/null +++ b/spring-batch-core/src/main/resources/org/springframework/batch/core/migration/migration-db2.sql @@ -0,0 +1,28 @@ + +-- create the requisite table + +CREATE TABLE BATCH_JOB_EXECUTION_PARAMS ( + JOB_EXECUTION_ID BIGINT NOT NULL , + TYPE_CD VARCHAR(6) NOT NULL , + KEY_NAME VARCHAR(100) NOT NULL , + STRING_VAL VARCHAR(250) , + DATE_VAL TIMESTAMP DEFAULT NULL , + LONG_VAL BIGINT , + DOUBLE_VAL DOUBLE PRECISION , + IDENTIFYING CHAR(1) NOT NULL , + constraint JOB_EXEC_PARAMS_FK foreign key (JOB_EXECUTION_ID) + references BATCH_JOB_EXECUTION(JOB_EXECUTION_ID) +) ; + +-- insert script that 'copies' existing batch_job_params to batch_job_execution_params +-- sets new params to identifying ones +-- verified on h2, + +INSERT INTO BATCH_JOB_EXECUTION_PARAMS + ( JOB_EXECUTION_ID , TYPE_CD, KEY_NAME, STRING_VAL, DATE_VAL, LONG_VAL, DOUBLE_VAL, IDENTIFYING ) +SELECT + JE.JOB_EXECUTION_ID , JP.TYPE_CD , JP.KEY_NAME , JP.STRING_VAL , JP.DATE_VAL , JP.LONG_VAL , JP.DOUBLE_VAL , 1 +FROM + BATCH_JOB_PARAMS JP,BATCH_JOB_EXECUTION JE +WHERE + JP.JOB_INSTANCE_ID = JE.JOB_INSTANCE_ID; \ No newline at end of file diff --git a/spring-batch-core/src/main/resources/org/springframework/batch/core/migration/migration-derby.sql b/spring-batch-core/src/main/resources/org/springframework/batch/core/migration/migration-derby.sql new file mode 100644 index 000000000..c3f08c886 --- /dev/null +++ b/spring-batch-core/src/main/resources/org/springframework/batch/core/migration/migration-derby.sql @@ -0,0 +1,28 @@ + +-- create the requisite table + +CREATE TABLE BATCH_JOB_EXECUTION_PARAMS ( + JOB_EXECUTION_ID BIGINT NOT NULL , + TYPE_CD VARCHAR(6) NOT NULL , + KEY_NAME VARCHAR(100) NOT NULL , + STRING_VAL VARCHAR(250) , + DATE_VAL TIMESTAMP DEFAULT NULL , + LONG_VAL BIGINT , + DOUBLE_VAL DOUBLE PRECISION , + IDENTIFYING CHAR(1) NOT NULL , + constraint JOB_EXEC_PARAMS_FK foreign key (JOB_EXECUTION_ID) + references BATCH_JOB_EXECUTION(JOB_EXECUTION_ID) +) ; + +-- insert script that 'copies' existing batch_job_params to batch_job_execution_params +-- sets new params to identifying ones +-- verified on h2, + +INSERT INTO BATCH_JOB_EXECUTION_PARAMS + ( JOB_EXECUTION_ID , TYPE_CD, KEY_NAME, STRING_VAL, DATE_VAL, LONG_VAL, DOUBLE_VAL, IDENTIFYING ) +SELECT + JE.JOB_EXECUTION_ID , JP.TYPE_CD , JP.KEY_NAME , JP.STRING_VAL , JP.DATE_VAL , JP.LONG_VAL , JP.DOUBLE_VAL , 1 +FROM + BATCH_JOB_PARAMS JP,BATCH_JOB_EXECUTION JE +WHERE + JP.JOB_INSTANCE_ID = JE.JOB_INSTANCE_ID; \ No newline at end of file From 0c892da257dd98a3b1c9a3c2830a543ab0c878ad Mon Sep 17 00:00:00 2001 From: jpraet Date: Sat, 15 Jun 2013 21:31:38 +0200 Subject: [PATCH 05/15] BATCH-2036: Output incorrect when using processor-transactional="false" and skips. --- .../item/FaultTolerantChunkProcessor.java | 7 +- .../FaultTolerantChunkProcessorTests.java | 74 +++++++++++++++++++ 2 files changed, 78 insertions(+), 3 deletions(-) diff --git a/spring-batch-core/src/main/java/org/springframework/batch/core/step/item/FaultTolerantChunkProcessor.java b/spring-batch-core/src/main/java/org/springframework/batch/core/step/item/FaultTolerantChunkProcessor.java index 664db55a9..c3956ac49 100755 --- a/spring-batch-core/src/main/java/org/springframework/batch/core/step/item/FaultTolerantChunkProcessor.java +++ b/spring-batch-core/src/main/java/org/springframework/batch/core/step/item/FaultTolerantChunkProcessor.java @@ -223,7 +223,9 @@ public class FaultTolerantChunkProcessor extends SimpleChunkProcessor extends SimpleChunkProcessor processedItems = new ArrayList(); + processor.setProcessorTransactional(false); + processor.setProcessSkipPolicy(new AlwaysSkipItemSkipPolicy()); + processor.setItemProcessor(new ItemProcessor() { + @Override + public String process(String item) throws Exception { + processedItems.add(item); + if (item.contains("fail")) { + throw new IllegalArgumentException("Expected Skippable Exception!"); + } + if (item.contains("skip")) { + return null; + } + return item; + } + }); + processor.afterPropertiesSet(); + Chunk inputs = new Chunk(Arrays.asList("1", "2", "skip", "skip", "3", "fail", "fail", "4", "5")); + try { + processor.process(contribution, inputs); + fail("Expected IllegalArgumentException"); + } catch (IllegalArgumentException e) { + assertEquals("Expected Skippable Exception!", e.getMessage()); + } + try { + processor.process(contribution, inputs); + fail("Expected IllegalArgumentException"); + } catch (IllegalArgumentException e) { + assertEquals("Expected Skippable Exception!", e.getMessage()); + } + processor.process(contribution, inputs); + assertEquals(5, list.size()); + assertEquals("[1, 2, 3, 4, 5]", list.toString()); + assertEquals(2, contribution.getFilterCount()); + assertEquals(2, contribution.getProcessSkipCount()); + assertEquals(9, processedItems.size()); + assertEquals("[1, 2, skip, skip, 3, fail, fail, 4, 5]", processedItems.toString()); + } + + @Test + // BATCH-2036 + public void testProcessFilterAndSkippableExceptionNoRollback() throws Exception { + final List processedItems = new ArrayList(); + processor.setProcessorTransactional(false); + processor.setProcessSkipPolicy(new AlwaysSkipItemSkipPolicy()); + processor.setItemProcessor(new ItemProcessor() { + @Override + public String process(String item) throws Exception { + processedItems.add(item); + if (item.contains("fail")) { + throw new IllegalArgumentException("Expected Skippable Exception!"); + } + if (item.contains("skip")) { + return null; + } + return item; + } + }); + processor.setRollbackClassifier(new BinaryExceptionClassifier(Collections + .> singleton(IllegalArgumentException.class), false)); + processor.afterPropertiesSet(); + Chunk inputs = new Chunk(Arrays.asList("1", "2", "skip", "skip", "3", "fail", "fail", "4", "5")); + processor.process(contribution, inputs); + assertEquals(5, list.size()); + assertEquals("[1, 2, 3, 4, 5]", list.toString()); + assertEquals(2, contribution.getFilterCount()); + assertEquals(2, contribution.getProcessSkipCount()); + assertEquals(9, processedItems.size()); + assertEquals("[1, 2, skip, skip, 3, fail, fail, 4, 5]", processedItems.toString()); + } protected void processAndExpectPlannedRuntimeException(Chunk chunk) throws Exception { From 36e3bda486ea9d4739033fbb17135a19b6f2d89f Mon Sep 17 00:00:00 2001 From: jpraet Date: Sat, 29 Jun 2013 13:54:24 +0200 Subject: [PATCH 06/15] BATCH-2052: StaxEventItemWriter should only force sync once per chunk --- .../batch/item/file/FlatFileItemWriter.java | 3 +- .../batch/item/xml/StaxEventItemWriter.java | 26 ++-- .../TransactionAwareBufferedWriter.java | 30 +++- .../TransactionAwareBufferedWriterTests.java | 132 ++++++++++++------ 4 files changed, 129 insertions(+), 62 deletions(-) diff --git a/spring-batch-infrastructure/src/main/java/org/springframework/batch/item/file/FlatFileItemWriter.java b/spring-batch-infrastructure/src/main/java/org/springframework/batch/item/file/FlatFileItemWriter.java index 62fd65f5e..2664c102c 100644 --- a/spring-batch-infrastructure/src/main/java/org/springframework/batch/item/file/FlatFileItemWriter.java +++ b/spring-batch-infrastructure/src/main/java/org/springframework/batch/item/file/FlatFileItemWriter.java @@ -614,6 +614,7 @@ InitializingBean { }); writer.setEncoding(encoding); + writer.setForceSync(forceSync); return writer; } else { @@ -627,7 +628,7 @@ InitializingBean { } }; - return new BufferedWriter(writer); + return writer; } } catch (UnsupportedCharsetException ucse) { diff --git a/spring-batch-infrastructure/src/main/java/org/springframework/batch/item/xml/StaxEventItemWriter.java b/spring-batch-infrastructure/src/main/java/org/springframework/batch/item/xml/StaxEventItemWriter.java index 30dd1840f..9b4468fae 100644 --- a/spring-batch-infrastructure/src/main/java/org/springframework/batch/item/xml/StaxEventItemWriter.java +++ b/spring-batch-infrastructure/src/main/java/org/springframework/batch/item/xml/StaxEventItemWriter.java @@ -395,7 +395,6 @@ ResourceAwareItemWriterItemStream, InitializingBean { /** * Helper method for opening output source at given file position */ - @SuppressWarnings("resource") private void open(long position, boolean restarted) { File file; @@ -443,25 +442,20 @@ ResourceAwareItemWriterItemStream, InitializingBean { }); writer.setEncoding(encoding); + writer.setForceSync(forceSync); bufferedWriter = writer; } else { - Writer writer = new BufferedWriter(new OutputStreamWriter(os, encoding)) { - @Override - public void flush() throws IOException { - super.flush(); - if (forceSync) { - channel.force(false); - } - } - }; - bufferedWriter = writer; + bufferedWriter = new BufferedWriter(new OutputStreamWriter(os, encoding)); } delegateEventWriter = createXmlEventWriter(outputFactory, bufferedWriter); eventWriter = new NoStartEndDocumentStreamWriter(delegateEventWriter); initNamespaceContext(delegateEventWriter); if (!restarted) { startDocument(delegateEventWriter); + if (forceSync) { + channel.force(false); + } } } catch (XMLStreamException xse) { @@ -470,8 +464,10 @@ ResourceAwareItemWriterItemStream, InitializingBean { catch (UnsupportedEncodingException e) { throw new DataAccessResourceFailureException("Unable to write to file resource: [" + resource + "] with encoding=[" + encoding + "]", e); + } + catch (IOException e) { + throw new DataAccessResourceFailureException("Unable to write to file resource: [" + resource + "]", e); } - } /** @@ -716,9 +712,15 @@ ResourceAwareItemWriterItemStream, InitializingBean { } try { eventWriter.flush(); + if (forceSync) { + channel.force(false); + } } catch (XMLStreamException e) { throw new WriteFailedException("Failed to flush the events", e); + } + catch (IOException e) { + throw new WriteFailedException("Failed to flush the events", e); } } diff --git a/spring-batch-infrastructure/src/main/java/org/springframework/batch/support/transaction/TransactionAwareBufferedWriter.java b/spring-batch-infrastructure/src/main/java/org/springframework/batch/support/transaction/TransactionAwareBufferedWriter.java index 5074fd49b..5bd5d978a 100644 --- a/spring-batch-infrastructure/src/main/java/org/springframework/batch/support/transaction/TransactionAwareBufferedWriter.java +++ b/spring-batch-infrastructure/src/main/java/org/springframework/batch/support/transaction/TransactionAwareBufferedWriter.java @@ -48,12 +48,14 @@ public class TransactionAwareBufferedWriter extends Writer { private FileChannel channel; private final Runnable closeCallback; - + // default encoding for writing to output files - set to UTF-8. private static final String DEFAULT_CHARSET = "UTF-8"; - + private String encoding = DEFAULT_CHARSET; - + + private boolean forceSync = false; + /** * Create a new instance with the underlying file channel provided, and a callback * to execute on close. The callback should clean up related resources like @@ -74,6 +76,19 @@ public class TransactionAwareBufferedWriter extends Writer { this.encoding = encoding; } + /** + * Flag to indicate that changes should be force-synced to disk on flush. + * Defaults to false, which means that even with a local disk changes could + * be lost if the OS crashes in between a write and a cache flush. Setting + * to true may result in slower performance for usage patterns involving + * many frequent writes. + * + * @param forceSync the flag value to set + */ + public void setForceSync(boolean forceSync) { + this.forceSync = forceSync; + } + /** * @return */ @@ -88,7 +103,7 @@ public class TransactionAwareBufferedWriter extends Writer { public void afterCompletion(int status) { clear(); } - + @Override public void beforeCommit(boolean readOnly) { try { @@ -112,6 +127,9 @@ public class TransactionAwareBufferedWriter extends Writer { if(bytesWritten != bufferLength) { throw new IOException("All bytes to be written were not successfully written"); } + if (forceSync) { + channel.force(false); + } if (TransactionSynchronizationManager.hasResource(closeKey)) { closeCallback.run(); } @@ -145,7 +163,7 @@ public class TransactionAwareBufferedWriter extends Writer { if (!transactionActive()) { return 0L; } - try { + try { return getCurrentBuffer().toString().getBytes(encoding).length; } catch (UnsupportedEncodingException e) { throw new WriteFailedException("Could not determine buffer size because of unsupported encoding: " + encoding, e); @@ -182,7 +200,7 @@ public class TransactionAwareBufferedWriter extends Writer { */ @Override public void flush() throws IOException { - if (!transactionActive()) { + if (!transactionActive() && forceSync) { channel.force(false); } } diff --git a/spring-batch-infrastructure/src/test/java/org/springframework/batch/support/transaction/TransactionAwareBufferedWriterTests.java b/spring-batch-infrastructure/src/test/java/org/springframework/batch/support/transaction/TransactionAwareBufferedWriterTests.java index d562543e3..857532d84 100644 --- a/spring-batch-infrastructure/src/test/java/org/springframework/batch/support/transaction/TransactionAwareBufferedWriterTests.java +++ b/spring-batch-infrastructure/src/test/java/org/springframework/batch/support/transaction/TransactionAwareBufferedWriterTests.java @@ -19,6 +19,9 @@ import static org.junit.Assert.assertEquals; import static org.junit.Assert.fail; import static org.mockito.Matchers.anyObject; import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.never; +import static org.mockito.Mockito.times; +import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; import java.io.IOException; @@ -26,14 +29,14 @@ import java.nio.ByteBuffer; import java.nio.channels.FileChannel; import org.junit.Before; -import org.junit.Ignore; import org.junit.Test; import org.mockito.ArgumentCaptor; +import org.mockito.invocation.InvocationOnMock; +import org.mockito.stubbing.Answer; import org.springframework.transaction.PlatformTransactionManager; import org.springframework.transaction.TransactionStatus; import org.springframework.transaction.support.TransactionCallback; import org.springframework.transaction.support.TransactionTemplate; -//import org.easymock.Capture; /** * @author Dave Syer @@ -46,13 +49,13 @@ public class TransactionAwareBufferedWriterTests { private FileChannel fileChannel; private TransactionAwareBufferedWriter writer; - + @Before public void init() { fileChannel = mock(FileChannel.class); - + writer = new TransactionAwareBufferedWriter(fileChannel, new Runnable() { - @Override + @Override public void run() { try { ByteBuffer bb = ByteBuffer.wrap("c".getBytes()); @@ -63,7 +66,7 @@ public class TransactionAwareBufferedWriterTests { } } }); - + writer.setEncoding("UTF-8"); } @@ -77,45 +80,63 @@ public class TransactionAwareBufferedWriterTests { */ @Test public void testWriteOutsideTransaction() throws Exception { -// Capture bb = new Capture(); ArgumentCaptor bb = ArgumentCaptor.forClass(ByteBuffer.class); -// when(fileChannel.write(capture(bb))).thenReturn(3); when(fileChannel.write(bb.capture())).thenReturn(3); - fileChannel.force(false); writer.write("foo"); writer.flush(); // Not closed yet - + String s = getStringFromByteBuffer(bb.getValue()); - + assertEquals("foo", s); + + verify(fileChannel, never()).force(false); } @Test - public void testBufferSizeOutsideTransaction() throws Exception { -// Capture bb = new Capture(); + public void testWriteOutsideTransactionForceSync() throws Exception { + writer.setForceSync(true); ArgumentCaptor bb = ArgumentCaptor.forClass(ByteBuffer.class); when(fileChannel.write(bb.capture())).thenReturn(3); writer.write("foo"); - + writer.flush(); + // Not closed yet + + String s = getStringFromByteBuffer(bb.getValue()); + + assertEquals("foo", s); + + verify(fileChannel, times(1)).force(false); + } + + @Test + public void testBufferSizeOutsideTransaction() throws Exception { + ArgumentCaptor bb = ArgumentCaptor.forClass(ByteBuffer.class); + when(fileChannel.write(bb.capture())).thenReturn(3); + + writer.write("foo"); + assertEquals(0, writer.getBufferSize()); } - - @Ignore //TODO - need to fix capture test + @Test public void testCloseOutsideTransaction() throws Exception { - ArgumentCaptor writeBuffer = ArgumentCaptor.forClass(ByteBuffer.class); - ArgumentCaptor commitBuffer = ArgumentCaptor.forClass(ByteBuffer.class); - when(fileChannel.write(writeBuffer.capture())).thenReturn(4); - when(fileChannel.write(commitBuffer.capture())).thenReturn(1); + ArgumentCaptor byteBufferCaptor = ArgumentCaptor.forClass(ByteBuffer.class); + + when(fileChannel.write(byteBufferCaptor.capture())).thenAnswer(new Answer() { + @Override + public Integer answer(InvocationOnMock invocation) throws Throwable { + return ((ByteBuffer) invocation.getArguments()[0]).remaining(); + } + }); writer.write("foo"); writer.close(); - - assertEquals("foo", getStringFromByteBuffer(writeBuffer.getValue())); - assertEquals("c", getStringFromByteBuffer(commitBuffer.getValue())); + + assertEquals("foo", getStringFromByteBuffer(byteBufferCaptor.getAllValues().get(0))); + assertEquals("c", getStringFromByteBuffer(byteBufferCaptor.getAllValues().get(1))); } @Test @@ -124,7 +145,7 @@ public class TransactionAwareBufferedWriterTests { when(fileChannel.write((ByteBuffer)anyObject())).thenReturn(3); new TransactionTemplate(transactionManager).execute(new TransactionCallback() { - @Override + @Override public Object doInTransaction(TransactionStatus status) { try { writer.write("foo"); @@ -137,7 +158,32 @@ public class TransactionAwareBufferedWriterTests { return null; } }); - + + verify(fileChannel, never()).force(false); + } + + @Test + @SuppressWarnings({"unchecked", "rawtypes"}) + public void testFlushInTransactionForceSync() throws Exception { + writer.setForceSync(true); + when(fileChannel.write((ByteBuffer)anyObject())).thenReturn(3); + + new TransactionTemplate(transactionManager).execute(new TransactionCallback() { + @Override + public Object doInTransaction(TransactionStatus status) { + try { + writer.write("foo"); + writer.flush(); + } + catch (IOException e) { + throw new IllegalStateException("Unexpected IOException", e); + } + assertEquals(3, writer.getBufferSize()); + return null; + } + }); + + verify(fileChannel, times(1)).force(false); } @Test @@ -145,9 +191,9 @@ public class TransactionAwareBufferedWriterTests { public void testWriteWithCommit() throws Exception { ArgumentCaptor bb = ArgumentCaptor.forClass(ByteBuffer.class); when(fileChannel.write(bb.capture())).thenReturn(3); - + new TransactionTemplate(transactionManager).execute(new TransactionCallback() { - @Override + @Override public Object doInTransaction(TransactionStatus status) { try { writer.write("foo"); @@ -159,7 +205,7 @@ public class TransactionAwareBufferedWriterTests { return null; } }); - + assertEquals(0, writer.getBufferSize()); } @@ -170,7 +216,7 @@ public class TransactionAwareBufferedWriterTests { when(fileChannel.write(bb.capture())).thenReturn(3); new TransactionTemplate(transactionManager).execute(new TransactionCallback() { - @Override + @Override public Object doInTransaction(TransactionStatus status) { try { writer.write("foo"); @@ -182,10 +228,10 @@ public class TransactionAwareBufferedWriterTests { return null; } }); - + assertEquals(0, writer.getBufferSize()); } - + @Test @SuppressWarnings({"unchecked", "rawtypes"}) // BATCH-1959 @@ -194,7 +240,7 @@ public class TransactionAwareBufferedWriterTests { when(fileChannel.write(bb.capture())).thenReturn(5); new TransactionTemplate(transactionManager).execute(new TransactionCallback() { - @Override + @Override public Object doInTransaction(TransactionStatus status) { try { writer.write("fóó"); @@ -206,21 +252,21 @@ public class TransactionAwareBufferedWriterTests { return null; } }); - + assertEquals(0, writer.getBufferSize()); - } + } @Test @SuppressWarnings({"unchecked", "rawtypes"}) // BATCH-1959 public void testBufferSizeInTransactionWithMultiByteCharacterUTF16BE() throws Exception { writer.setEncoding("UTF-16BE"); - + ArgumentCaptor bb = ArgumentCaptor.forClass(ByteBuffer.class); when(fileChannel.write(bb.capture())).thenReturn(6); new TransactionTemplate(transactionManager).execute(new TransactionCallback() { - @Override + @Override public Object doInTransaction(TransactionStatus status) { try { writer.write("fóó"); @@ -232,16 +278,16 @@ public class TransactionAwareBufferedWriterTests { return null; } }); - + assertEquals(0, writer.getBufferSize()); - } + } @Test @SuppressWarnings({"unchecked", "rawtypes"}) public void testWriteWithRollback() throws Exception { try { new TransactionTemplate(transactionManager).execute(new TransactionCallback() { - @Override + @Override public Object doInTransaction(TransactionStatus status) { try { writer.write("foo"); @@ -267,19 +313,19 @@ public class TransactionAwareBufferedWriterTests { testWriteWithRollback(); testWriteWithCommit(); } - + @Test @SuppressWarnings({"unchecked", "rawtypes"}) public void testExceptionOnFlush() throws Exception { writer = new TransactionAwareBufferedWriter(fileChannel, new Runnable() { - @Override + @Override public void run() { } }); try { new TransactionTemplate(transactionManager).execute(new TransactionCallback() { - @Override + @Override public Object doInTransaction(TransactionStatus status) { try { writer.write("foo"); @@ -290,7 +336,7 @@ public class TransactionAwareBufferedWriterTests { return null; } }); - + fail("Exception was not thrown"); } catch (FlushFailedException ffe) { assertEquals("Could not write to output buffer", ffe.getMessage()); From d78454361ce020bc294501b1b34620c34851ab61 Mon Sep 17 00:00:00 2001 From: jpraet Date: Fri, 12 Jul 2013 21:06:02 +0200 Subject: [PATCH 07/15] BATCH-1849: Item was not picked up after restarting a failed job!!! --- .../item/database/JdbcPagingItemReader.java | 19 +++++++++-- ...tDataSourceItemReaderIntegrationTests.java | 34 +++++++++++++++++++ 2 files changed, 50 insertions(+), 3 deletions(-) diff --git a/spring-batch-infrastructure/src/main/java/org/springframework/batch/item/database/JdbcPagingItemReader.java b/spring-batch-infrastructure/src/main/java/org/springframework/batch/item/database/JdbcPagingItemReader.java index 9074fb1b5..3ba6c8089 100644 --- a/spring-batch-infrastructure/src/main/java/org/springframework/batch/item/database/JdbcPagingItemReader.java +++ b/spring-batch-infrastructure/src/main/java/org/springframework/batch/item/database/JdbcPagingItemReader.java @@ -50,7 +50,7 @@ import org.springframework.util.ClassUtils; * specified in {@link #setPageSize(int)}. Additional pages are requested when * needed as {@link #read()} method is called, returning an object corresponding * to current position. On restart it uses the last sort key value to locate the - * first page to read (so it doesn't matter if the successfully processed itmes + * first page to read (so it doesn't matter if the successfully processed items * have been removed or modified). *

* @@ -94,6 +94,8 @@ public class JdbcPagingItemReader extends AbstractPagingItemReader impleme private String remainingPagesSql; private Map startAfterValues; + + private Map previousStartAfterValues; private int fetchSize = VALUE_NOT_SET; @@ -210,6 +212,7 @@ public class JdbcPagingItemReader extends AbstractPagingItemReader impleme } else { + previousStartAfterValues = startAfterValues; if (logger.isDebugEnabled()) { logger.debug("SQL used for reading remaining pages: [" + remainingPagesSql + "]"); } @@ -230,10 +233,20 @@ public class JdbcPagingItemReader extends AbstractPagingItemReader impleme @Override public void update(ExecutionContext executionContext) throws ItemStreamException { super.update(executionContext); - if (isSaveState() && startAfterValues != null) { - executionContext.put(getExecutionContextKey(START_AFTER_VALUE), startAfterValues); + if (isSaveState()) { + if (isAtEndOfPage() && startAfterValues != null) { + // restart on next page + executionContext.put(getExecutionContextKey(START_AFTER_VALUE), startAfterValues); + } else if (previousStartAfterValues != null) { + // restart on current page + executionContext.put(getExecutionContextKey(START_AFTER_VALUE), previousStartAfterValues); + } } } + + private boolean isAtEndOfPage() { + return getCurrentItemCount() % getPageSize() == 0; + } @Override @SuppressWarnings("unchecked") diff --git a/spring-batch-infrastructure/src/test/java/org/springframework/batch/item/database/AbstractDataSourceItemReaderIntegrationTests.java b/spring-batch-infrastructure/src/test/java/org/springframework/batch/item/database/AbstractDataSourceItemReaderIntegrationTests.java index e792e6778..94bbab6eb 100644 --- a/spring-batch-infrastructure/src/test/java/org/springframework/batch/item/database/AbstractDataSourceItemReaderIntegrationTests.java +++ b/spring-batch-infrastructure/src/test/java/org/springframework/batch/item/database/AbstractDataSourceItemReaderIntegrationTests.java @@ -111,6 +111,40 @@ public abstract class AbstractDataSourceItemReaderIntegrationTests { assertEquals(3, fooAfterRestart.getValue()); } + /* + * Restart scenario - read records, save restart data, create new input + * source and restore from restart data - the new input source should + * continue where the old one finished. + */ + @Transactional @Test + public void testRestartOnSecondPage() throws Exception { + + getAsItemStream(reader).open(executionContext); + + Foo foo1 = reader.read(); + assertEquals(1, foo1.getValue()); + Foo foo2 = reader.read(); + assertEquals(2, foo2.getValue()); + Foo foo3 = reader.read(); + assertEquals(3, foo3.getValue()); + Foo foo4 = reader.read(); + assertEquals(4, foo4.getValue()); + + getAsItemStream(reader).update(executionContext); + + getAsItemStream(reader).close(); + + // create new input source + reader = createItemReader(); + + getAsItemStream(reader).open(executionContext); + + Foo foo5 = reader.read(); + assertEquals(5, foo5.getValue()); + + assertNull(reader.read()); + } + /* * Reading from an input source and then trying to restore causes an error. */ From eae42d80d34aaac5607c4dd10adba55c797774b4 Mon Sep 17 00:00:00 2001 From: jpraet Date: Sun, 30 Jun 2013 14:50:06 +0200 Subject: [PATCH 08/15] BATCH-2050: AbstractItemCountingItemStreamItemReader.read() shouldn't be final --- .../batch/core/configuration/annotation/StepScope.java | 2 +- .../batch/item/database/AbstractCursorItemReader.java | 2 +- .../batch/item/database/HibernateItemWriter.java | 4 ++-- .../springframework/batch/item/database/JpaItemWriter.java | 2 +- .../batch/item/file/transform/DelimitedLineTokenizer.java | 2 +- .../support/AbstractItemCountingItemStreamItemReader.java | 2 +- 6 files changed, 7 insertions(+), 7 deletions(-) diff --git a/spring-batch-core/src/main/java/org/springframework/batch/core/configuration/annotation/StepScope.java b/spring-batch-core/src/main/java/org/springframework/batch/core/configuration/annotation/StepScope.java index 23439e6eb..cee43408f 100644 --- a/spring-batch-core/src/main/java/org/springframework/batch/core/configuration/annotation/StepScope.java +++ b/spring-batch-core/src/main/java/org/springframework/batch/core/configuration/annotation/StepScope.java @@ -23,7 +23,7 @@ import org.springframework.context.annotation.ScopedProxyMode; * } * * - *

Marking a @Bean as @StepScope is equivalent to marking it as @Scope(value="step", proxyMode=INTERFACES)

+ *

Marking a @Bean as @StepScope is equivalent to marking it as @Scope(value="step", proxyMode=TARGET_CLASS)

* * @author Dave Syer * diff --git a/spring-batch-infrastructure/src/main/java/org/springframework/batch/item/database/AbstractCursorItemReader.java b/spring-batch-infrastructure/src/main/java/org/springframework/batch/item/database/AbstractCursorItemReader.java index 24eaa9e49..a7f51fc6a 100644 --- a/spring-batch-infrastructure/src/main/java/org/springframework/batch/item/database/AbstractCursorItemReader.java +++ b/spring-batch-infrastructure/src/main/java/org/springframework/batch/item/database/AbstractCursorItemReader.java @@ -394,7 +394,7 @@ implements InitializingBean { * Execute the statement to open the cursor. */ @Override - protected final void doOpen() throws Exception { + protected void doOpen() throws Exception { Assert.state(!initialized, "Stream is already initialized. Close before re-opening."); Assert.isNull(rs, "ResultSet still open! Close before re-opening."); diff --git a/spring-batch-infrastructure/src/main/java/org/springframework/batch/item/database/HibernateItemWriter.java b/spring-batch-infrastructure/src/main/java/org/springframework/batch/item/database/HibernateItemWriter.java index 494fb9537..01b80c2e9 100644 --- a/spring-batch-infrastructure/src/main/java/org/springframework/batch/item/database/HibernateItemWriter.java +++ b/spring-batch-infrastructure/src/main/java/org/springframework/batch/item/database/HibernateItemWriter.java @@ -81,7 +81,7 @@ public class HibernateItemWriter implements ItemWriter, InitializingBean { * * @param sessionFactory session factory to be used by the writer */ - public final void setSessionFactory(SessionFactory sessionFactory) { + public void setSessionFactory(SessionFactory sessionFactory) { this.sessionFactory = sessionFactory; } @@ -101,7 +101,7 @@ public class HibernateItemWriter implements ItemWriter, InitializingBean { * @see org.springframework.batch.item.ItemWriter#write(java.util.List) */ @Override - public final void write(List items) { + public void write(List items) { if(sessionFactory == null) { doWrite(hibernateTemplate, items); hibernateTemplate.flush(); diff --git a/spring-batch-infrastructure/src/main/java/org/springframework/batch/item/database/JpaItemWriter.java b/spring-batch-infrastructure/src/main/java/org/springframework/batch/item/database/JpaItemWriter.java index 803a24809..cafe2dd7b 100644 --- a/spring-batch-infrastructure/src/main/java/org/springframework/batch/item/database/JpaItemWriter.java +++ b/spring-batch-infrastructure/src/main/java/org/springframework/batch/item/database/JpaItemWriter.java @@ -76,7 +76,7 @@ public class JpaItemWriter implements ItemWriter, InitializingBean { * @see org.springframework.batch.item.ItemWriter#write(java.util.List) */ @Override - public final void write(List items) { + public void write(List items) { EntityManager entityManager = EntityManagerFactoryUtils.getTransactionalEntityManager(entityManagerFactory); if (entityManager == null) { throw new DataAccessResourceFailureException("Unable to obtain a transactional EntityManager"); diff --git a/spring-batch-infrastructure/src/main/java/org/springframework/batch/item/file/transform/DelimitedLineTokenizer.java b/spring-batch-infrastructure/src/main/java/org/springframework/batch/item/file/transform/DelimitedLineTokenizer.java index 242fa1d20..084822f0e 100644 --- a/spring-batch-infrastructure/src/main/java/org/springframework/batch/item/file/transform/DelimitedLineTokenizer.java +++ b/spring-batch-infrastructure/src/main/java/org/springframework/batch/item/file/transform/DelimitedLineTokenizer.java @@ -117,7 +117,7 @@ public class DelimitedLineTokenizer extends AbstractLineTokenizer { * * @see #DEFAULT_QUOTE_CHARACTER */ - public final void setQuoteCharacter(char quoteCharacter) { + public void setQuoteCharacter(char quoteCharacter) { this.quoteCharacter = quoteCharacter; this.quoteString = "" + quoteCharacter; } diff --git a/spring-batch-infrastructure/src/main/java/org/springframework/batch/item/support/AbstractItemCountingItemStreamItemReader.java b/spring-batch-infrastructure/src/main/java/org/springframework/batch/item/support/AbstractItemCountingItemStreamItemReader.java index 6e9e20950..5c3e5c950 100644 --- a/spring-batch-infrastructure/src/main/java/org/springframework/batch/item/support/AbstractItemCountingItemStreamItemReader.java +++ b/spring-batch-infrastructure/src/main/java/org/springframework/batch/item/support/AbstractItemCountingItemStreamItemReader.java @@ -75,7 +75,7 @@ public abstract class AbstractItemCountingItemStreamItemReader extends Abstra } @Override - public final T read() throws Exception, UnexpectedInputException, ParseException { + public T read() throws Exception, UnexpectedInputException, ParseException { if (currentItemCount >= maxItemCount) { return null; } From fb8aa61cf384d2c82b4547f096f324001e930195 Mon Sep 17 00:00:00 2001 From: Michael Minella Date: Thu, 25 Jul 2013 10:12:41 -0500 Subject: [PATCH 09/15] BATCH-2069: Updated manifest to make spring data dependencies optional --- spring-batch-infrastructure/template.mf | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/spring-batch-infrastructure/template.mf b/spring-batch-infrastructure/template.mf index f02cca61c..b33f95ca5 100644 --- a/spring-batch-infrastructure/template.mf +++ b/spring-batch-infrastructure/template.mf @@ -4,7 +4,7 @@ Bundle-Name: Spring Batch Infrastructure Bundle-Vendor: Spring Bundle-Version: ${version} Bundle-ManifestVersion: 2 -Import-Template: +Import-Template: com.thoughtworks.xstream.*;version="[1.3,1.4)";resolution:=optional, org.codehaus.jettison.*;version="[1.0,1.1)";resolution:=optional, org.codehaus.jackson.*;version="[1.0.1,1.1)";resolution:=optional, @@ -33,6 +33,12 @@ Import-Template: org.springframework.amqp.*;version="[1.1.0,2.0.0)";resolution:=optional, org.springframework.retry.*;version="[1.0.0,2.0.0)";resolution:=optional, org.springframework.classify.*;version="[1.0.0,2.0.0)";resolution:=optional, + org.springframework.data.domain.*;version="[1.5.0,2.0.0)";resolution:=optional, + org.springframework.data.gemfire.*;version="[1.3.0,2.0.0)";resolution:=optional, + org.springframework.data.mongodb.*;version="[1.1.0,2.0.0)";resolution:=optional, + org.springframework.data.neo4j.*;version="[2.2.0,3.0.0)";resolution:=optional, + org.springframework.data.repository.*;version="[1.5.0,2.0.0)";resolution:=optional, + com.mongodb.*;version="[2.1.9,3.0.0)";resolution:=optional, javax.sql.*;version="0";resolution:=optional, javax.jms;version="0";resolution:=optional, javax.persistence;version="0";resolution:=optional, From 6bb3526e8e91b83888a3a784579503b51dd7d732 Mon Sep 17 00:00:00 2001 From: Michael Minella Date: Fri, 26 Jul 2013 10:47:50 -0500 Subject: [PATCH 10/15] BATCH-2056: Updated documentation --- .../annotation/EnableBatchProcessing.java | 4 +- src/site/docbook/reference/job.xml | 133 ++++- src/site/docbook/reference/retry.xml | 13 +- src/site/docbook/reference/whatsnew.xml | 476 ++++-------------- 4 files changed, 211 insertions(+), 415 deletions(-) diff --git a/spring-batch-core/src/main/java/org/springframework/batch/core/configuration/annotation/EnableBatchProcessing.java b/spring-batch-core/src/main/java/org/springframework/batch/core/configuration/annotation/EnableBatchProcessing.java index aaf15b2dc..177d58d6e 100644 --- a/spring-batch-core/src/main/java/org/springframework/batch/core/configuration/annotation/EnableBatchProcessing.java +++ b/spring-batch-core/src/main/java/org/springframework/batch/core/configuration/annotation/EnableBatchProcessing.java @@ -37,7 +37,7 @@ import org.springframework.transaction.PlatformTransactionManager; *
  * @Configuration
  * @EnableBatchProcessing
- * @Import(DataSourceCnfiguration.class)
+ * @Import(DataSourceConfiguration.class)
  * public class AppConfig {
  *
  * 	@Autowired
@@ -167,4 +167,4 @@ public @interface EnableBatchProcessing {
 	 */
 	boolean modular() default false;
 
-}
\ No newline at end of file
+}
diff --git a/src/site/docbook/reference/job.xml b/src/site/docbook/reference/job.xml
index f6b8dae94..997f97eaf 100644
--- a/src/site/docbook/reference/job.xml
+++ b/src/site/docbook/reference/job.xml
@@ -226,12 +226,101 @@ catch (JobRestartException e) {
     
   
 
+  
+ Java Config + + Spring 3 brought the ability to configure applications via java instead + of XML. As of Spring Batch 2.2.0, batch jobs can be configured using the same + java config. There are two components for the java based configuration: + the @EnableBatchConfiguration annotation and two builders. + + The @EnableBatchProcessing works similarly to the other + @Enable* annotations in the Spring family. In this case, + @EnableBatchProcessing provides a base configuration for + building batch jobs. Within this base configuration, an instance of + StepScope is createded in addition to a number of beans made + available to be autowired: + + + + + JobRepository - bean name "jobRepository" + + + JobLauncher - bean name "jobLauncher" + + + JobRegistry - bean name "jobRegistry" + + + PlatformTransactionManager - bean name "transactionManager" + + + JobBuilderFactory - bean name "jobBuilders" + + + StepBuilderFactory - bean name "stepBuilders" + + + + The core interface for this configuration is the BatchConfigurer. + The default implementation provides the beans mentioned above and requires a + DataSource as a bean within the context to be provided. This data + source will be used by the JobRepository. + + + + Only one configuration class needs to have the + @EnableBatchProcessing annotation. Once you have a class + annotated with it, you will have all of the above available. + + + With the base configuration in place, a user can use the provided builder factories + to configure a job. Below is an example of a two step job configured via the + JobBuilderFactory and the StepBuilderFactory. + + @Configuration +@EnableBatchProcessing +@Import(DataSourceCnfiguration.class) +public class AppConfig { + + @Autowired + private JobBuilderFactory jobs; + + @Autowired + private StepBuilderFactory steps; + + @Bean + public Job job() { + return jobs.get("myJob").start(step1()).next(step2()).build(); + } + + @Bean + protected Step step1(ItemReader<Person> reader, ItemProcessor<Person, Person> processor, ItemWriter<Person> writer) { + return steps.get("step1") + .<Person, Person> chunk(10) + .reader(reader) + .processor(processor) + .writer(writer) + .build(); + } + + @Bean + protected Step step2(Tasklet tasklet) { + return steps.get("step2") + .tasklet(tasklet) + .build(); + } +} + +
+
- + Configuring a JobRepository - + As described in earlier, the JobRepository @@ -246,7 +335,7 @@ catch (JobRestartException e) { collaborators. However, there are still a few configuration options available: - + ]]> - + None of the configuration options listed above are required except the id. If they are not set, the defaults shown above will be used. They @@ -265,7 +354,7 @@ catch (JobRestartException e) { length of the long VARCHAR columns in the sample schema scripts - used to store things like exit code descriptions. If you don't modify the schema and you don't use multi-byte characters you shouldn't need to change it. + used to store things like exit code descriptions. If you don't modify the schema and you don't use multi-byte characters you shouldn't need to change it.
Transaction Configuration for the JobRepository @@ -297,7 +386,7 @@ catch (JobRestartException e) { - @@ -315,7 +404,7 @@ catch (JobRestartException e) { classpath.
- +
Changing the Table Prefix @@ -342,7 +431,7 @@ catch (JobRestartException e) {
- +
In-Memory Repository @@ -354,7 +443,7 @@ catch (JobRestartException e) { this reason, Spring batch provides an in-memory Map version of the job repository: - ]]> @@ -373,7 +462,7 @@ catch (JobRestartException e) { ResourcelessTransactionManager useful.
- +
Non-standard Database Types in a Repository @@ -404,7 +493,7 @@ catch (JobRestartException e) { on and wire one up manually in the normal Spring way.
- +
@@ -777,7 +866,7 @@ public class JobLauncherController { JobRepository, it can be easily configured via a factory bean: - ]]> Earlier in this @@ -787,7 +876,7 @@ public class JobLauncherController { JobExplorer is working with the same tables, it too needs the ability to set a prefix: - p:tablePrefix="BATCH_" ]]>
@@ -899,30 +988,30 @@ public class JobLauncherController { List getExecutions(long instanceId) throws NoSuchJobInstanceException; - List getJobInstances(String jobName, int start, int count) + List getJobInstances(String jobName, int start, int count) throws NoSuchJobException; Set getRunningExecutions(String jobName) throws NoSuchJobException; String getParameters(long executionId) throws NoSuchJobExecutionException; - Long start(String jobName, String parameters) + Long start(String jobName, String parameters) throws NoSuchJobException, JobInstanceAlreadyExistsException; - Long restart(long executionId) + Long restart(long executionId) throws JobInstanceAlreadyCompleteException, NoSuchJobExecutionException, NoSuchJobException, JobRestartException; - Long startNextInstance(String jobName) - throws NoSuchJobException, JobParametersNotFoundException, JobRestartException, + Long startNextInstance(String jobName) + throws NoSuchJobException, JobParametersNotFoundException, JobRestartException, JobExecutionAlreadyRunningException, JobInstanceAlreadyCompleteException; - boolean stop(long executionId) + boolean stop(long executionId) throws NoSuchJobExecutionException, JobExecutionNotRunningException; String getSummary(long executionId) throws NoSuchJobExecutionException; - Map getStepExecutionSummaries(long executionId) + Map getStepExecutionSummaries(long executionId) throws NoSuchJobExecutionException; Set getJobNames(); @@ -996,8 +1085,8 @@ public class JobLauncherController { as shown below: RetryTemplate + + The retry functionality was pulled out of Spring Batch as of 2.2.0. + It is now part of a new library, Spring Retry. + + To make processing more robust and less prone to failure, sometimes it helps to automatically retry a failed operation in case it might succeed on a subsequent attempt. Errors that are susceptible to this kind @@ -22,13 +27,13 @@ <T> T execute(RetryCallback<T> retryCallback) throws Exception; - <T> T execute(RetryCallback<T> retryCallback, RecoveryCallback<T> recoveryCallback) + <T> T execute(RetryCallback<T> retryCallback, RecoveryCallback<T> recoveryCallback) throws Exception; - <T> T execute(RetryCallback<T> retryCallback, RetryState retryState) + <T> T execute(RetryCallback<T> retryCallback, RetryState retryState) throws Exception, ExhaustedRetryException; - <T> T execute(RetryCallback<T> retryCallback, RecoveryCallback<T> recoveryCallback, + <T> T execute(RetryCallback<T> retryCallback, RecoveryCallback<T> recoveryCallback, RetryState retryState) throws Exception; }The basic callback is a simple interface that allows you to @@ -276,7 +281,7 @@ template.execute(new RetryCallback<Foo>() { BackOffContext start(RetryContext context); - void backOff(BackOffContext backOffContext) + void backOff(BackOffContext backOffContext) throws BackOffInterruptedException; }A BackoffPolicy is free to implement diff --git a/src/site/docbook/reference/whatsnew.xml b/src/site/docbook/reference/whatsnew.xml index ef639bf6a..0c66f1d51 100644 --- a/src/site/docbook/reference/whatsnew.xml +++ b/src/site/docbook/reference/whatsnew.xml @@ -2,427 +2,129 @@ - What's New in Spring Batch 2.0 + What's New in Spring Batch 2.2 - The Spring Batch 2.0 release has six major themes: + The Spring Batch 2.2 release has six major themes: - Java 5 + Spring Data Integration - Non Sequential Step Execution + Java Configuration - Chunk oriented processing + Spring Retry - Meta Data enhancements - - - - Scalability - - - - Configuration + Job Parameters -
- Java 5 +
+ Spring Data Integration - The 1.x releases of Spring Batch were all based on Java 1.4. This - prevented the framework from using many enhancements provided in Java 5 - such as generics, parameterized types, etc. The entire framework has been - updated to utilize these features. As a result, Java - 1.4 is no longer supported. Most of the interfaces developers - work with have been updated to support generic types. As an example, the - ItemReader interface from 1.1 is below: + Since the 2.0 release of Spring Batch, the Spring Data project has brought + support for the NoSQL movement to Spring. The 2.2 release of Spring Batch has added + support for MongoDB, Neo4j and Gemfire natively through the Spring Data abstractions. - public interface ItemReader { - - Object read() throws Exception; - - void mark() throws MarkFailedException; - - void reset() throws ResetFailedException; -} - - As you can see, the read method returns an - Object. The 2.0 version is below: - - public interface ItemReader<T> { - - T read() throws Exception, UnexpectedInputException, ParseException; - -} - - As you can see, ItemReader now supports the - generic type, T, which is returned from read. You - may also notice that mark and - reset have been removed. This is due to step - processing strategy changes, which are discussed below. Many other - interfaces have been similarly updated. + This release has also added support for writing to any custom Spring Data Repository a + user may write. The RepositoryItemReader and + RepositoryItemWriter each wrap a repository implementation ( + PagingAndSortingRepository and CrudRepository + respectively) to retrieve data from and persist data to.
-
- Chunk Oriented Processing +
+ Java Configuration - Previously, the default processing strategy provided by Spring Batch - was item-oriented processing: + Until 2.2.0 the only option for configuring a job was via XML (either through the batch DSL or + by hand). However, in 2.2.0, Java based configuration has been added as a way to define Spring Batch + Jobs. To support this new configuration option, an annotation and builder classes have been added. What + was previously defined as this: - - - - + <batch> + <job-repository/> - - - - + <job id="myJob"> + <step id="step1".../> + <step id="step2".../> + </job> - In item-oriented processing, the ItemReader - returns one Object (the 'item') which is then - handed to the ItemWriter, periodically committing - when the number of items hits the commit interval. For example, if the - commit interval is 5, ItemReader and - ItemWriter will each be called 5 times. This is - illustrated in a simplified code example below: + <beans:bean id="transactionManager".../> - for(int i = 0; i < commitInterval; i++){ - Object item = itemReader.read(); - itemWriter.write(item); + <beans:bean id="jobLauncher" class="org.springframework.batch.core.launch.support.SimpleJobLauncher"> + <beans:property name="jobRepository" ref="jobRepository"/> + </beans:bean> +</batch> + + + Can now be configured using the @EnableBatchProcessing annotation and the + provided JobBuilderFactory and StepBuilderFactory as show below: + + @Configuration + @EnableBatchProcessing + @Import(DataSourceCnfiguration.class) + public class AppConfig { + + @Autowired + private JobBuilderFactory jobs; + + @Bean + public Job job() { + return jobs.get("myJob").start(step1()).next(step2()).build(); + } + + @Bean + protected Step step1() { + ... + } + + @Bean + protected Step step2() { + ... + } } - Both the ItemReader and - ItemWriter interfaces were completely geared toward - this approach: - - public interface ItemReader { - - Object read() throws Exception; - - void mark() throws MarkFailedException; - - void reset() throws ResetFailedException; -} - - public interface ItemWriter { - - void write(Object item) throws Exception; - - void flush() throws FlushFailedException; - - void clear() throws ClearFailedException; -} - - Because the 'scope' of the processing was one item, supporting - rollback scenarios required additional methods, which is what - mark, reset, - flush, and clear - provided. If, after successfully reading and writing 2 items, the third - has an error while writing, the transaction would need to be rolled back. - In this case, the clear method on the writer - would be called, indicating that it should clear - its buffer, and reset would be called on the - ItemReader, indicating that it should return back - to the last position it was at when mark was - called. (Both mark and - flush are called on commit) - - In 2.0, this strategy has been changed to a chunk-oriented - approach: - - - - - - - - - - - - Using the same example from above, if the commit interval is five, - read will be called 5 times, and write once. The items read will be - aggregated into a list, that will ultimately be written out, as the - simplified example below illustrates: - - List items = new Arraylist(); -for(int i = 0; i < commitInterval; i++){ - items.add(itemReader.read()); -} -itemWriter.write(items); - - This approach not only allows for much simpler processing and - scalability approaches, it also makes the - ItemReader and ItemWriter - interfaces much cleaner: - - public interface ItemReader<T> { - - T read() throws Exception, UnexpectedInputException, ParseException; - -} - - public interface ItemWriter<T> { - - void write(List<? extends T> items) throws Exception; - -} - - As you can see, the interfaces no longer contain the - mark, reset, - flush, and clear - methods. This makes the creation of readers and writers much more - straightforward for developers. In the case of - ItemReader, the interface is now forward-only. The - framework will buffer read items for developers in the case of rollback - (though there are exceptions if the underlying resource is transactional - see: ). - ItemWriter is also simplified, since it gets the - entire 'chunk' of items at once, rather than one at a time, it can decide - to flush any resources (such as a file or hibernate session) before - returning control to the Step. More detailed - information on chunk-oriented processing can be found in . Reader and writer implementation - information can be found in . - -
- ItemProcessor - - Previously, Steps had only two - dependencies, ItemReader and - ItemWriter: - - - - - - - - - - - - The basic configuration above is fairly robust. However, there are - many cases where the item needs to be transformed before writing. In 1.x - this can be achieved using the composite pattern: - - - - - - - - - - - - This approach works. However, it requires an extra layer between - either the reader or the writer and the Step. - Furthermore, the ItemWriter would need to be - registered separately as an ItemStream with the - Step. For this reason, the - ItemTransfomer was renamed to - ItemProcessor and moved up to the same level as - ItemReader and - ItemWriter: - - - - - - - - - - -
+ The @EnableBatchProcessing annotation makes a number + of common dependencies available for autowiring by default. This list includes a + JobRepsitory, JobLauncher, + JobRegistry, PlatformTransactionManager, + JobBuilderFactory, and a StepBuilderFactory. + More information on how to configure Jobs and Steps with the new + Java config can be found in
-
- Configuration Enhancements +
+ Spring Retry - Until 2.0, the only option for configuring batch jobs has been - normal spring bean configuration. However, in 2.0 there is a new namespace - for configuration. For example, in 1.1, configuring a job looked like the - following: - - <bean id="footballJob" - class="org.springframework.batch.core.job.SimpleJob"> - <property name="steps"> - <list> - <!-- Step bean details ommitted for clarity --> - <bean id="playerload"/> - <bean id="gameLoad"/> - <bean id="playerSummarization"/> - </list> - </property> - <property name="jobRepository" ref="jobRepository" /> -</bean> - - In 2.0, the equivalent would be: - - <job id="footballJob"> - <!-- Step bean details ommitted for clarity --> - <step id="playerload" next="gameLoad"/> - <step id="gameLoad" next="playerSummarization"/> - <step id="playerSummarization"/> -</job> - - More information on how to configure Jobs and Steps with the new - namespace can be found in , and . + The ability to retry an operation via the RetryTemplate + has always been a feature of Spring Batch. That ability has been identified as a + useful feature for other frameworks (Spring Integration for example). With the 2.2.0 + release, the retry logic has been extracted from Spring Batch into it's own library + called Spring Retry. With this change, there are two main impacts. The first is + that the majority of the org.springframework.batch.retry package + has been moved into this new library. With that move, the package name has also + dropped the batch to become org.springframework.retry.
-
- Meta Data Access Improvements +
+ Job Parameters - The JobRepository interface represents basic - CRUD operations with Job meta-data. However, it may - also be useful to query the meta-data. For that reason, the - JobExplorer and JobOperator - interfaces have been created: + Prior to the 2.2.0 release of Spring Batch, all parameters pass to a job execution + were used as part of the identity of the job. This limited the ability to change job + parameters during a rerun of a job. To accommodate this use case, 2.2.0 introduced the + idea of non-identifying job parameters. - - - - - - - - - - - More information on the new meta data features can be found in . It is also worth noting that Jobs can now - be stopped via the database, removing the requirement to maintain a handle - to the JobExecution on the JVM the job was launched - in. + By default, job parameters in 2.2.0 are still identifying. However, Spring Batch + now allows a user to specify a parameter not be used in the identity of a job instance. + In order to support this change, the domain model for batch changed. Before 2.2.0, job + parameters were associated with a JobInstance. 2.2.0 and beyond, + they are associated with a JobExecution. This also required the + underlying database schema for the job repository to change.
-
- Non Sequential Step Execution - - 2.0 has also seen improvements in how steps can be configured. - Rather than requiring that they solely be sequential: - - - - - - - - - - - - They may now be conditional: - - - - - - - - - - - - This new 'conditional flow' support is made easy to configure via - the new namespace: - - <job id="job"> - <step id="stepA"> - <next on="FAILED" to="stepB" /> - <next on="*" to="stepC" /> - </step> - <step id="stepB" next="stepC" /> - <step id="stepC" /> -</job> - - More details on how to configure non sequential steps can be found - in . -
- -
- Scalability - - Spring Batch 1.x was always intended as a single VM, possibly - multi-threaded model, but many features were built into it that support - parallel execution in multiple processes. Many projects have successfully - implemented a scalable solution relying on the quality of service features - of Spring Batch to ensure that processing only happens in the correct - sequence. In 2.0 those features have been exposed more explicitly. There - are two approaches to scalability: remote chunking, and - partitioning. - -
- Remote Chunking - - Remote chunking is a technique for dividing up the work of a step - without any explicit knowledge of the structure of the data. Any input - source can be split up dynamically by reading it in a single process (as - per normal in 1.x) and sending the items as a chunk to a remote worker - process. The remote process implements a listener pattern, responding to - the request, processing the data and sending an asynchronous reply. The - transport for the request and reply has to be durable with guaranteed - delivery and a single consumer, and those features are readily available - with any JMS implementation. But Spring Batch is building the remote - chunking feature on top of Spring Integration, therefore it is agnostic - to the actual implementation of the message middleware. More details can - be found in -
- -
- Partitioning - - Partitioning is an alternative approach which in contrast depends - on having some knowledge of the structure of the input data, like a - range of primary keys, or the name of a file to process. The advantage - of this model is that the processors of each element in a partition can - act as if they are a single step in a normal Spring Batch job. They - don't have to implement any special or new patterns, which makes them - easy to configure and test. Partitioning in principle is more scalable - than remote chunking because there is no serialization bottleneck - arising from reading all the input data in one place. - - In Spring Batch 2.0 partitioning is supported by two interfaces: - PartitionHandler and - StepExecutionSplitter. The - PartitionHandler is the one that knows about the - execution fabric - it has to transmit requests to remote steps and - collect the results using whatever grid or remoting technology is - available. PartitionHandler is an SPI, and Spring - Batch provides one implementation out of the box for local execution - through a TaskExecutor. This will be useful - immediately when parallel processing of heavily IO bound tasks is - required, since in those cases remote execution only complicates the - deployment and doesn't necessarily help much with the performance. Other - implementations will be specific to the execution fabric. (e.g. one of - the grid providers such as IBM, Oracle, Terracotta, Appistry etc.), - Spring Batch makes no preference for any of grid provider over another. - More details can be found in -
-
From 3553d244e2147294ee16401441b51b2fc82a6eb8 Mon Sep 17 00:00:00 2001 From: jpraet Date: Thu, 13 Jun 2013 22:27:31 +0200 Subject: [PATCH 11/15] BATCH-2038: DerbyPagingQueryProvider does not work with Derby 10.10.1.1 --- .../support/DerbyPagingQueryProvider.java | 26 +++++++++++++++---- .../DerbyPagingQueryProviderTests.java | 13 +++++++++- 2 files changed, 33 insertions(+), 6 deletions(-) diff --git a/spring-batch-infrastructure/src/main/java/org/springframework/batch/item/database/support/DerbyPagingQueryProvider.java b/spring-batch-infrastructure/src/main/java/org/springframework/batch/item/database/support/DerbyPagingQueryProvider.java index 97f6fe5a2..0de89956d 100644 --- a/spring-batch-infrastructure/src/main/java/org/springframework/batch/item/database/support/DerbyPagingQueryProvider.java +++ b/spring-batch-infrastructure/src/main/java/org/springframework/batch/item/database/support/DerbyPagingQueryProvider.java @@ -35,18 +35,34 @@ import org.springframework.jdbc.support.JdbcUtils; * @since 2.0 */ public class DerbyPagingQueryProvider extends SqlWindowingPagingQueryProvider { - - private String version; + + private static final String MINIMAL_DERBY_VERSION = "10.4.1.3"; @Override public void init(DataSource dataSource) throws Exception { super.init(dataSource); - version = JdbcUtils.extractDatabaseMetaData(dataSource, "getDatabaseProductVersion").toString(); - if ("10.4.1.3".compareTo(version) > 0) { - throw new InvalidDataAccessResourceUsageException("Apache Derby version " + version + " is not supported by this class, Only version 10.4.1.3 or later is supported"); + String version = JdbcUtils.extractDatabaseMetaData(dataSource, "getDatabaseProductVersion").toString(); + if (!isDerbyVersionSupported(version)) { + throw new InvalidDataAccessResourceUsageException("Apache Derby version " + version + " is not supported by this class, Only version " + MINIMAL_DERBY_VERSION + " or later is supported"); } } + // derby version numbering is M.m.f.p [ {alpha|beta} ] see http://db.apache.org/derby/papers/versionupgrade.html#Basic+Numbering+Scheme + private boolean isDerbyVersionSupported(String version) { + String[] minimalVersionParts = MINIMAL_DERBY_VERSION.split("\\."); + String[] versionParts = version.split("[\\. ]"); + for (int i = 0; i < minimalVersionParts.length; i++) { + int minimalVersionPart = Integer.valueOf(minimalVersionParts[i]); + int versionPart = Integer.valueOf(versionParts[i]); + if (versionPart < minimalVersionPart) { + return false; + } else if (versionPart > minimalVersionPart) { + return true; + } + } + return true; + } + @Override protected String getOrderedQueryAlias() { return "TMP_ORDERED"; diff --git a/spring-batch-infrastructure/src/test/java/org/springframework/batch/item/database/support/DerbyPagingQueryProviderTests.java b/spring-batch-infrastructure/src/test/java/org/springframework/batch/item/database/support/DerbyPagingQueryProviderTests.java index d4a44524d..4536f8013 100644 --- a/spring-batch-infrastructure/src/test/java/org/springframework/batch/item/database/support/DerbyPagingQueryProviderTests.java +++ b/spring-batch-infrastructure/src/test/java/org/springframework/batch/item/database/support/DerbyPagingQueryProviderTests.java @@ -54,7 +54,18 @@ public class DerbyPagingQueryProviderTests extends AbstractSqlPagingQueryProvide } @Test - public void testInitWithUnsupportedVErsion() throws Exception { + public void testInitWithRecentVersion() throws Exception { + DataSource ds = mock(DataSource.class); + Connection con = mock(Connection.class); + DatabaseMetaData dmd = mock(DatabaseMetaData.class); + when(dmd.getDatabaseProductVersion()).thenReturn("10.10.1.1"); + when(con.getMetaData()).thenReturn(dmd); + when(ds.getConnection()).thenReturn(con); + pagingQueryProvider.init(ds); + } + + @Test + public void testInitWithUnsupportedVersion() throws Exception { DataSource ds = mock(DataSource.class); Connection con = mock(Connection.class); DatabaseMetaData dmd = mock(DatabaseMetaData.class); From 5e577e03bbd4b34c9b4ab8a3e239c3c2712cd2f9 Mon Sep 17 00:00:00 2001 From: Michael Minella Date: Fri, 26 Jul 2013 12:48:47 -0500 Subject: [PATCH 12/15] Updated for 2.2.1 release --- src/site/apt/migration/2.2.0-2.2.1.apt | 28 ++++++++++++++++++++++++++ src/site/apt/migration/index.apt | 2 ++ 2 files changed, 30 insertions(+) create mode 100755 src/site/apt/migration/2.2.0-2.2.1.apt diff --git a/src/site/apt/migration/2.2.0-2.2.1.apt b/src/site/apt/migration/2.2.0-2.2.1.apt new file mode 100755 index 000000000..026be3bb3 --- /dev/null +++ b/src/site/apt/migration/2.2.0-2.2.1.apt @@ -0,0 +1,28 @@ +Spring Batch 2.2.1 Release Notes + +* Bug + + * {{{http://jira.springsource.org/browse/BATCH-1849}[BATCH-1849]}} - Item was not picked up after restarting a failed job!!! + + * {{{http://jira.springsource.org/browse/BATCH-1973}[BATCH-1973]}} - processor-transactional="false" in chunk definition does not have stable behavior + + * {{{http://jira.springsource.org/browse/BATCH-2036}[BATCH-2036]}} - Output incorrect when using processor-transactional="false" and skips. + + * {{{http://jira.springsource.org/browse/BATCH-2038}[BATCH-2038]}} - DerbyPagingQueryProvider does not work with Derby 10.10.1.1 + + * {{{http://jira.springsource.org/browse/BATCH-2050}[BATCH-2050]}} - AbstractItemCountingItemStreamItemReader.read() shouldn't be final + + * {{{http://jira.springsource.org/browse/BATCH-2054}[BATCH-2054]}} - StaxEventItemWriter fails on a NullPointerException with Spring OXM 3.2.x. + +* Improvement + + * {{{http://jira.springsource.org/browse/BATCH-1984}[BATCH-1984]}} - CompositeItemProcessor.setDelegates argument has limiting generic type + + * {{{http://jira.springsource.org/browse/BATCH-2052}[BATCH-2052]}} - StaxEventItemWriter should only force sync once per chunk + + * {{{http://jira.springsource.org/browse/BATCH-2069}[BATCH-2069]}} - Relax OSGI dependencies on nosql jars + +* Task + + * {{{http://jira.springsource.org/browse/BATCH-2056}[BATCH-2056]}} - Update 'What's New' in Reference Document + diff --git a/src/site/apt/migration/index.apt b/src/site/apt/migration/index.apt index 4491bdb2d..d4ed1cf5f 100644 --- a/src/site/apt/migration/index.apt +++ b/src/site/apt/migration/index.apt @@ -13,6 +13,8 @@ Links: + * {{{./2.2.0-2.2.1.html}2.2.0-2.2.1}} + * {{{./2.2.0.RC2-2.2.0.html}2.2.0.RC2-2.2.0}} * {{{./2.2.0.RC1-2.2.0.RC2.html}2.2.0.RC1-2.2.0.RC2}} From f2d76c9cd321c918cc6692112304fcfbaf4a3174 Mon Sep 17 00:00:00 2001 From: Michael Minella Date: Fri, 26 Jul 2013 13:15:19 -0500 Subject: [PATCH 13/15] Updated version literals prior to release 2.2.1 --- archetypes/simple-cli/pom.xml | 2 +- src/site/docbook/reference/index.xml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/archetypes/simple-cli/pom.xml b/archetypes/simple-cli/pom.xml index 91edf6896..d6c7d78a2 100644 --- a/archetypes/simple-cli/pom.xml +++ b/archetypes/simple-cli/pom.xml @@ -15,7 +15,7 @@ 3.2.0.RELEASE - 3.0.0.BUILD-SNAPSHOT + 2.2.1.RELEASE false 4.10 1.6 diff --git a/src/site/docbook/reference/index.xml b/src/site/docbook/reference/index.xml index 9bf68e7a2..f9b862d25 100644 --- a/src/site/docbook/reference/index.xml +++ b/src/site/docbook/reference/index.xml @@ -5,7 +5,7 @@ Spring Batch - Reference Documentation - Spring Batch 2.2.0.RELEASE + Spring Batch 2.2.1.RELEASE From 6669780a74a5340edaf39113959822e3194291e0 Mon Sep 17 00:00:00 2001 From: Michael Minella Date: Fri, 26 Jul 2013 13:19:45 -0500 Subject: [PATCH 14/15] [maven-release-plugin] prepare release 2.2.1.RELEASE --- archetypes/pom.xml | 2 +- archetypes/simple-cli/pom.xml | 2 +- pom.xml | 4 ++-- spring-batch-core-tests/pom.xml | 2 +- spring-batch-core/pom.xml | 2 +- spring-batch-infrastructure-tests/pom.xml | 4 ++-- spring-batch-infrastructure/pom.xml | 2 +- spring-batch-parent/pom.xml | 4 ++-- spring-batch-samples/pom.xml | 2 +- spring-batch-test/pom.xml | 2 +- 10 files changed, 13 insertions(+), 13 deletions(-) diff --git a/archetypes/pom.xml b/archetypes/pom.xml index 32c0b15e2..f8bdc8eb2 100644 --- a/archetypes/pom.xml +++ b/archetypes/pom.xml @@ -9,7 +9,7 @@ org.springframework.batch spring-batch-parent - 3.0.0.BUILD-SNAPSHOT + 2.2.1.RELEASE ../spring-batch-parent diff --git a/archetypes/simple-cli/pom.xml b/archetypes/simple-cli/pom.xml index d6c7d78a2..f2e2e0545 100644 --- a/archetypes/simple-cli/pom.xml +++ b/archetypes/simple-cli/pom.xml @@ -3,7 +3,7 @@ 4.0.0 org.springframework.batch spring-batch-simple-cli - 3.0.0.BUILD-SNAPSHOT + 2.2.1.RELEASE jar Commandline http://www.springframework.org/spring-batch/archetypes/simple-cli-archetype diff --git a/pom.xml b/pom.xml index 0bff6044b..9debc7475 100644 --- a/pom.xml +++ b/pom.xml @@ -6,7 +6,7 @@ Spring Batch Spring Batch provides tools for enterprise batch or bulk processing. It can be used to wire up jobs, and track their execution, or simply as an optimization for repetitive processing in a transactional environment. Spring Batch is part of the Spring Portfolio. - 3.0.0.BUILD-SNAPSHOT + 2.2.1.RELEASE pom spring-batch-parent @@ -23,7 +23,7 @@ http://github.com/SpringSource/spring-batch scm:git:git://github.com/SpringSource/spring-batch.git scm:git:ssh://git@github.com/SpringSource/spring-batch.git - HEAD + 2.2.1.RELEASE JIRA diff --git a/spring-batch-core-tests/pom.xml b/spring-batch-core-tests/pom.xml index 199d9de5c..a1cb73fae 100644 --- a/spring-batch-core-tests/pom.xml +++ b/spring-batch-core-tests/pom.xml @@ -8,7 +8,7 @@ org.springframework.batch spring-batch-parent - 3.0.0.BUILD-SNAPSHOT + 2.2.1.RELEASE ../spring-batch-parent diff --git a/spring-batch-core/pom.xml b/spring-batch-core/pom.xml index cd9ed54c7..29ef85f3d 100644 --- a/spring-batch-core/pom.xml +++ b/spring-batch-core/pom.xml @@ -9,7 +9,7 @@ org.springframework.batch spring-batch-parent - 3.0.0.BUILD-SNAPSHOT + 2.2.1.RELEASE ../spring-batch-parent diff --git a/spring-batch-infrastructure-tests/pom.xml b/spring-batch-infrastructure-tests/pom.xml index e49977afc..abc3af971 100644 --- a/spring-batch-infrastructure-tests/pom.xml +++ b/spring-batch-infrastructure-tests/pom.xml @@ -7,7 +7,7 @@ org.springframework.batch spring-batch-parent - 3.0.0.BUILD-SNAPSHOT + 2.2.1.RELEASE ../spring-batch-parent @@ -57,7 +57,7 @@ org.springframework.batch spring-batch-infrastructure - 3.0.0.BUILD-SNAPSHOT + 2.2.1.RELEASE org.hsqldb diff --git a/spring-batch-infrastructure/pom.xml b/spring-batch-infrastructure/pom.xml index e9fd5ff72..41f392342 100644 --- a/spring-batch-infrastructure/pom.xml +++ b/spring-batch-infrastructure/pom.xml @@ -12,7 +12,7 @@ org.springframework.batch spring-batch-parent - 3.0.0.BUILD-SNAPSHOT + 2.2.1.RELEASE ../spring-batch-parent diff --git a/spring-batch-parent/pom.xml b/spring-batch-parent/pom.xml index 13d97eeb3..e581994f3 100644 --- a/spring-batch-parent/pom.xml +++ b/spring-batch-parent/pom.xml @@ -3,7 +3,7 @@ 4.0.0 org.springframework.batch spring-batch-parent - 3.0.0.BUILD-SNAPSHOT + 2.2.1.RELEASE Spring Batch Parent Spring Batch parent project. Defines dependencies and common configuration for the build process. http://static.springframework.org/spring-batch/${project.artifactId} @@ -12,7 +12,7 @@ http://github.com/SpringSource/spring-batch scm:git:git://github.com/SpringSource/spring-batch.git scm:git:git://github.com/SpringSource/spring-batch.git - HEAD + 2.2.1.RELEASE diff --git a/spring-batch-samples/pom.xml b/spring-batch-samples/pom.xml index 93b31e5b0..9054b30eb 100644 --- a/spring-batch-samples/pom.xml +++ b/spring-batch-samples/pom.xml @@ -9,7 +9,7 @@ org.springframework.batch spring-batch-parent - 3.0.0.BUILD-SNAPSHOT + 2.2.1.RELEASE ../spring-batch-parent diff --git a/spring-batch-test/pom.xml b/spring-batch-test/pom.xml index 6e0632316..6722ecacc 100755 --- a/spring-batch-test/pom.xml +++ b/spring-batch-test/pom.xml @@ -8,7 +8,7 @@ org.springframework.batch spring-batch-parent - 3.0.0.BUILD-SNAPSHOT + 2.2.1.RELEASE ../spring-batch-parent From 853c960bdf74f4ee766c947f313df08b83aded83 Mon Sep 17 00:00:00 2001 From: Michael Minella Date: Fri, 26 Jul 2013 13:19:49 -0500 Subject: [PATCH 15/15] [maven-release-plugin] prepare for next development iteration --- archetypes/pom.xml | 2 +- archetypes/simple-cli/pom.xml | 4 ++-- pom.xml | 4 ++-- spring-batch-core-tests/pom.xml | 2 +- spring-batch-core/pom.xml | 2 +- spring-batch-infrastructure-tests/pom.xml | 4 ++-- spring-batch-infrastructure/pom.xml | 2 +- spring-batch-parent/pom.xml | 4 ++-- spring-batch-samples/pom.xml | 2 +- spring-batch-test/pom.xml | 2 +- 10 files changed, 14 insertions(+), 14 deletions(-) diff --git a/archetypes/pom.xml b/archetypes/pom.xml index f8bdc8eb2..03f12915d 100644 --- a/archetypes/pom.xml +++ b/archetypes/pom.xml @@ -9,7 +9,7 @@ org.springframework.batch spring-batch-parent - 2.2.1.RELEASE + 2.2.2.BUILD-SNAPSHOT ../spring-batch-parent diff --git a/archetypes/simple-cli/pom.xml b/archetypes/simple-cli/pom.xml index f2e2e0545..3d3a0e7a8 100644 --- a/archetypes/simple-cli/pom.xml +++ b/archetypes/simple-cli/pom.xml @@ -3,7 +3,7 @@ 4.0.0 org.springframework.batch spring-batch-simple-cli - 2.2.1.RELEASE + 2.2.2.BUILD-SNAPSHOT jar Commandline http://www.springframework.org/spring-batch/archetypes/simple-cli-archetype @@ -15,7 +15,7 @@ 3.2.0.RELEASE - 2.2.1.RELEASE + 2.2.2.BUILD-SNAPSHOT false 4.10 1.6 diff --git a/pom.xml b/pom.xml index 9debc7475..a0355621f 100644 --- a/pom.xml +++ b/pom.xml @@ -6,7 +6,7 @@ Spring Batch Spring Batch provides tools for enterprise batch or bulk processing. It can be used to wire up jobs, and track their execution, or simply as an optimization for repetitive processing in a transactional environment. Spring Batch is part of the Spring Portfolio. - 2.2.1.RELEASE + 2.2.2.BUILD-SNAPSHOT pom spring-batch-parent @@ -23,7 +23,7 @@ http://github.com/SpringSource/spring-batch scm:git:git://github.com/SpringSource/spring-batch.git scm:git:ssh://git@github.com/SpringSource/spring-batch.git - 2.2.1.RELEASE + HEAD JIRA diff --git a/spring-batch-core-tests/pom.xml b/spring-batch-core-tests/pom.xml index a1cb73fae..67f418a9d 100644 --- a/spring-batch-core-tests/pom.xml +++ b/spring-batch-core-tests/pom.xml @@ -8,7 +8,7 @@ org.springframework.batch spring-batch-parent - 2.2.1.RELEASE + 2.2.2.BUILD-SNAPSHOT ../spring-batch-parent diff --git a/spring-batch-core/pom.xml b/spring-batch-core/pom.xml index 29ef85f3d..32fb9dce7 100644 --- a/spring-batch-core/pom.xml +++ b/spring-batch-core/pom.xml @@ -9,7 +9,7 @@ org.springframework.batch spring-batch-parent - 2.2.1.RELEASE + 2.2.2.BUILD-SNAPSHOT ../spring-batch-parent diff --git a/spring-batch-infrastructure-tests/pom.xml b/spring-batch-infrastructure-tests/pom.xml index abc3af971..3c36e5747 100644 --- a/spring-batch-infrastructure-tests/pom.xml +++ b/spring-batch-infrastructure-tests/pom.xml @@ -7,7 +7,7 @@ org.springframework.batch spring-batch-parent - 2.2.1.RELEASE + 2.2.2.BUILD-SNAPSHOT ../spring-batch-parent @@ -57,7 +57,7 @@ org.springframework.batch spring-batch-infrastructure - 2.2.1.RELEASE + 2.2.2.BUILD-SNAPSHOT org.hsqldb diff --git a/spring-batch-infrastructure/pom.xml b/spring-batch-infrastructure/pom.xml index 41f392342..674a1bb4c 100644 --- a/spring-batch-infrastructure/pom.xml +++ b/spring-batch-infrastructure/pom.xml @@ -12,7 +12,7 @@ org.springframework.batch spring-batch-parent - 2.2.1.RELEASE + 2.2.2.BUILD-SNAPSHOT ../spring-batch-parent diff --git a/spring-batch-parent/pom.xml b/spring-batch-parent/pom.xml index e581994f3..e5ba7a661 100644 --- a/spring-batch-parent/pom.xml +++ b/spring-batch-parent/pom.xml @@ -3,7 +3,7 @@ 4.0.0 org.springframework.batch spring-batch-parent - 2.2.1.RELEASE + 2.2.2.BUILD-SNAPSHOT Spring Batch Parent Spring Batch parent project. Defines dependencies and common configuration for the build process. http://static.springframework.org/spring-batch/${project.artifactId} @@ -12,7 +12,7 @@ http://github.com/SpringSource/spring-batch scm:git:git://github.com/SpringSource/spring-batch.git scm:git:git://github.com/SpringSource/spring-batch.git - 2.2.1.RELEASE + HEAD diff --git a/spring-batch-samples/pom.xml b/spring-batch-samples/pom.xml index 9054b30eb..2ca49ff06 100644 --- a/spring-batch-samples/pom.xml +++ b/spring-batch-samples/pom.xml @@ -9,7 +9,7 @@ org.springframework.batch spring-batch-parent - 2.2.1.RELEASE + 2.2.2.BUILD-SNAPSHOT ../spring-batch-parent diff --git a/spring-batch-test/pom.xml b/spring-batch-test/pom.xml index 6722ecacc..946f62f2b 100755 --- a/spring-batch-test/pom.xml +++ b/spring-batch-test/pom.xml @@ -8,7 +8,7 @@ org.springframework.batch spring-batch-parent - 2.2.1.RELEASE + 2.2.2.BUILD-SNAPSHOT ../spring-batch-parent