diff --git a/spring-batch-integration/src/main/java/org/springframework/batch/integration/chunk/ChunkHandler.java b/spring-batch-integration/src/main/java/org/springframework/batch/integration/chunk/ChunkHandler.java index 9c7bfc84e..b771a7aba 100644 --- a/spring-batch-integration/src/main/java/org/springframework/batch/integration/chunk/ChunkHandler.java +++ b/spring-batch-integration/src/main/java/org/springframework/batch/integration/chunk/ChunkHandler.java @@ -1,8 +1,7 @@ package org.springframework.batch.integration.chunk; - public interface ChunkHandler { - ChunkResponse handleChunk(ChunkRequest chunk); + ChunkResponse handleChunk(ChunkRequest chunk); } \ No newline at end of file diff --git a/spring-batch-integration/src/main/java/org/springframework/batch/integration/chunk/ChunkProcessor.java b/spring-batch-integration/src/main/java/org/springframework/batch/integration/chunk/ChunkProcessor.java new file mode 100644 index 000000000..bd5bd1027 --- /dev/null +++ b/spring-batch-integration/src/main/java/org/springframework/batch/integration/chunk/ChunkProcessor.java @@ -0,0 +1,32 @@ +/* + * Copyright 2006-2007 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.springframework.batch.integration.chunk; + +import java.util.Collection; + +import org.springframework.transaction.annotation.Propagation; +import org.springframework.transaction.annotation.Transactional; + +/** + * @author Dave Syer + */ +public interface ChunkProcessor { + + // This is transactional with REQUIRES_NEW because we need to force rollback + @Transactional(propagation = Propagation.REQUIRES_NEW) + int process(Collection items, int skipCount) throws Exception; + +} diff --git a/spring-batch-integration/src/main/java/org/springframework/batch/integration/chunk/ChunkProcessorChunkHandler.java b/spring-batch-integration/src/main/java/org/springframework/batch/integration/chunk/ChunkProcessorChunkHandler.java new file mode 100644 index 000000000..2d7e69a8a --- /dev/null +++ b/spring-batch-integration/src/main/java/org/springframework/batch/integration/chunk/ChunkProcessorChunkHandler.java @@ -0,0 +1,54 @@ +package org.springframework.batch.integration.chunk; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.springframework.batch.repeat.ExitStatus; +import org.springframework.beans.factory.InitializingBean; +import org.springframework.integration.annotation.Handler; +import org.springframework.util.Assert; + +public class ChunkProcessorChunkHandler implements ChunkHandler, InitializingBean { + + private static final Log logger = LogFactory.getLog(ChunkProcessorChunkHandler.class); + + private ChunkProcessor chunkProcessor; + + /* (non-Javadoc) + * @see org.springframework.beans.factory.InitializingBean#afterPropertiesSet() + */ + public void afterPropertiesSet() throws Exception { + Assert.notNull(chunkProcessor, "A ChunkProcessor must be provided"); + } + + /** + * Public setter for the {@link ChunkProcessor}. + * @param chunkProcessor the chunkProcessor to set + */ + public void setChunkProcessor(ChunkProcessor chunkProcessor) { + this.chunkProcessor = chunkProcessor; + } + + /* + * (non-Javadoc) + * @see org.springframework.integration.batch.slave.ChunkHandler#handleChunk(java.util.Collection) + */ + @Handler + public ChunkResponse handleChunk(ChunkRequest chunkRequest) { + + logger.debug("Handling chunk: " + chunkRequest); + + int skipCount = 0; + try { + skipCount = chunkProcessor.process(chunkRequest.getItems(), chunkRequest.getSkipCount()); + } + catch (Exception e) { + logger.debug("Failed chunk", e); + return new ChunkResponse(ExitStatus.FAILED.addExitDescription(e.getClass().getName() + ": " + + e.getMessage()), chunkRequest.getJobId(), skipCount); + } + + logger.debug("Completed chunk handling with " + skipCount + " skips"); + return new ChunkResponse(ExitStatus.CONTINUABLE, chunkRequest.getJobId(), skipCount); + + } +} diff --git a/spring-batch-integration/src/main/java/org/springframework/batch/integration/chunk/ItemWriterChunkHandler.java b/spring-batch-integration/src/main/java/org/springframework/batch/integration/chunk/ItemWriterChunkHandler.java deleted file mode 100644 index 233fd1fe5..000000000 --- a/spring-batch-integration/src/main/java/org/springframework/batch/integration/chunk/ItemWriterChunkHandler.java +++ /dev/null @@ -1,85 +0,0 @@ -package org.springframework.batch.integration.chunk; - -import java.util.Collections; - -import org.apache.commons.logging.Log; -import org.apache.commons.logging.LogFactory; -import org.springframework.batch.core.SkipListener; -import org.springframework.batch.core.listener.CompositeSkipListener; -import org.springframework.batch.core.step.skip.ItemSkipPolicy; -import org.springframework.batch.core.step.skip.NeverSkipItemSkipPolicy; -import org.springframework.batch.item.ItemWriter; -import org.springframework.batch.repeat.ExitStatus; -import org.springframework.integration.annotation.Handler; -import org.springframework.transaction.annotation.Transactional; - -public class ItemWriterChunkHandler implements ChunkHandler { - - private static final Log logger = LogFactory.getLog(ItemWriterChunkHandler.class); - - private ItemWriter itemWriter; - - private ItemSkipPolicy itemSkipPolicy = new NeverSkipItemSkipPolicy(); - - private CompositeSkipListener skipListener = new CompositeSkipListener(); - - public void setItemSkipPolicy(ItemSkipPolicy itemSkipPolicy) { - this.itemSkipPolicy = itemSkipPolicy; - } - - public void setItemWriter(ItemWriter itemWriter) { - this.itemWriter = itemWriter; - } - - public void registerSkipListener(SkipListener listener) { - skipListener.register(listener); - } - - public void setSkipListeners(SkipListener[] skipListeners) { - for (SkipListener listener : skipListeners) { - registerSkipListener(listener); - } - } - - /* - * (non-Javadoc) - * @see org.springframework.integration.batch.slave.ChunkHandler#handleChunk(java.util.Collection) - */ - @Handler - @Transactional - public ChunkResponse handleChunk(ChunkRequest chunk) { - - logger.debug("Handling chunk: " + chunk); - - int parentSkipCount = chunk.getSkipCount(); - int skipCount = 0; - - try { - for (T item : chunk.getItems()) { - try { - itemWriter.write(Collections.singletonList(item)); - } - catch (Exception e) { - if (itemSkipPolicy.shouldSkip(e, parentSkipCount + skipCount)) { - logger.debug("Skipping item on exception", e); - skipCount++; - skipListener.onSkipInWrite(item, e); - } else { - logger.debug("Cannot skip, re-throwing"); - throw e; - } - } - } - } - catch (Exception e) { - logger.debug("Failed chunk", e); - // TODO: need to force rollback as well - return new ChunkResponse(ExitStatus.FAILED.addExitDescription(e.getClass().getName() + ": " - + e.getMessage()), chunk.getJobId(), skipCount); - } - - logger.debug("Completed chunk handling with " + skipCount + " skips"); - return new ChunkResponse(ExitStatus.CONTINUABLE, chunk.getJobId(), skipCount); - - } -} diff --git a/spring-batch-integration/src/main/java/org/springframework/batch/integration/chunk/SimpleChunkProcessor.java b/spring-batch-integration/src/main/java/org/springframework/batch/integration/chunk/SimpleChunkProcessor.java new file mode 100644 index 000000000..4ca82260c --- /dev/null +++ b/spring-batch-integration/src/main/java/org/springframework/batch/integration/chunk/SimpleChunkProcessor.java @@ -0,0 +1,78 @@ +/* + * Copyright 2006-2007 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.springframework.batch.integration.chunk; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; + +import org.springframework.batch.item.ItemProcessor; +import org.springframework.batch.item.ItemWriter; +import org.springframework.beans.factory.InitializingBean; +import org.springframework.util.Assert; + +/** + * @author Dave Syer + * + */ +public class SimpleChunkProcessor implements ChunkProcessor, InitializingBean { + + private ItemProcessor itemProcessor; + + private ItemWriter itemWriter; + + /* + * (non-Javadoc) + * @see org.springframework.beans.factory.InitializingBean#afterPropertiesSet() + */ + public void afterPropertiesSet() throws Exception { + Assert.notNull(itemProcessor, "An ItemProcessor must be provided"); + Assert.notNull(itemWriter, "An ItemWriter must be provided"); + } + + /** + * @param itemWriter + */ + public void setItemWriter(ItemWriter itemWriter) { + this.itemWriter = itemWriter; + } + + /** + * Public setter for the {@link ItemProcessor}. + * @param itemProcessor the {@link ItemProcessor} to set + */ + public void setItemProcessor(ItemProcessor itemProcessor) { + this.itemProcessor = itemProcessor; + } + + /* + * (non-Javadoc) + * @see org.springframework.batch.integration.chunk.ChunkProcessor#process(java.util.Collection, + * int) + */ + public int process(Collection items, int parentSkipCount) throws Exception { + + List processed = new ArrayList(); + for (S item : items) { + processed.add(itemProcessor.process(item)); + } + itemWriter.write(processed); + + return 0; + + } + +} diff --git a/spring-batch-integration/src/test/java/org/springframework/batch/integration/chunk/ChunkProcessorChunkHandlerTests.java b/spring-batch-integration/src/test/java/org/springframework/batch/integration/chunk/ChunkProcessorChunkHandlerTests.java new file mode 100644 index 000000000..36f817cfd --- /dev/null +++ b/spring-batch-integration/src/test/java/org/springframework/batch/integration/chunk/ChunkProcessorChunkHandlerTests.java @@ -0,0 +1,34 @@ +package org.springframework.batch.integration.chunk; + +import static org.junit.Assert.assertEquals; + +import java.util.Collection; + +import org.junit.Test; +import org.springframework.batch.repeat.ExitStatus; +import org.springframework.util.StringUtils; + +public class ChunkProcessorChunkHandlerTests { + + private ChunkProcessorChunkHandler handler = new ChunkProcessorChunkHandler(); + + protected int count = 0; + + @SuppressWarnings("unchecked") + @Test + public void testVanillaHandleChunk() { + handler.setChunkProcessor(new ChunkProcessor() { + public int process(Collection items, int skipCount) throws Exception { + count+=items.size(); + return 0; + } + }); + ChunkResponse response = handler.handleChunk(new ChunkRequest(StringUtils.commaDelimitedListToSet("foo,bar"), + 12L, 10)); + assertEquals(0, response.getSkipCount()); + assertEquals(new Long(12L), response.getJobId()); + assertEquals(ExitStatus.CONTINUABLE, response.getExitStatus()); + assertEquals(2, count); + } + +} diff --git a/spring-batch-integration/src/test/java/org/springframework/batch/integration/chunk/ItemWriterChunkHandlerTests.java b/spring-batch-integration/src/test/java/org/springframework/batch/integration/chunk/ItemWriterChunkHandlerTests.java deleted file mode 100644 index 7a0f07ba3..000000000 --- a/spring-batch-integration/src/test/java/org/springframework/batch/integration/chunk/ItemWriterChunkHandlerTests.java +++ /dev/null @@ -1,85 +0,0 @@ -package org.springframework.batch.integration.chunk; - -import static org.junit.Assert.assertEquals; - -import java.util.List; - -import org.junit.Test; -import org.springframework.batch.core.SkipListener; -import org.springframework.batch.core.listener.SkipListenerSupport; -import org.springframework.batch.core.step.skip.AlwaysSkipItemSkipPolicy; -import org.springframework.batch.item.ItemWriter; -import org.springframework.batch.repeat.ExitStatus; -import org.springframework.util.StringUtils; - -public class ItemWriterChunkHandlerTests { - - private ItemWriterChunkHandler handler = new ItemWriterChunkHandler(); - - protected int count = 0; - - private SkipListenerSupport listener = new SkipListenerSupport() { - @Override - public void onSkipInWrite(Object item, Throwable t) { - count++; - } - }; - - @SuppressWarnings("unchecked") - @Test - public void testVanillaHandleChunk() { - handler.setItemWriter(new ItemWriter() { - public void write(List items) throws Exception { - count+=items.size(); - } - }); - ChunkResponse response = handler.handleChunk(new ChunkRequest(StringUtils.commaDelimitedListToSet("foo,bar"), - 12L, 10)); - assertEquals(0, response.getSkipCount()); - assertEquals(new Long(12L), response.getJobId()); - assertEquals(ExitStatus.CONTINUABLE, response.getExitStatus()); - assertEquals(2, count); - } - - @SuppressWarnings("unchecked") - @Test - public void testSetItemSkipPolicy() { - handler.setItemWriter(new ItemWriter() { - public void write(List items) throws Exception { - count+=items.size(); - throw new RuntimeException("Planned failure"); - } - }); - handler.setItemSkipPolicy(new AlwaysSkipItemSkipPolicy()); - ChunkResponse response = handler.handleChunk(new ChunkRequest(StringUtils.commaDelimitedListToSet("foo,bar"), - 12L, 10)); - assertEquals(2, response.getSkipCount()); - assertEquals(new Long(12L), response.getJobId()); - assertEquals(ExitStatus.CONTINUABLE, response.getExitStatus()); - assertEquals(2, count); - } - - @SuppressWarnings("unchecked") - @Test - public void testRegisterSkipListener() { - handler.setItemWriter(new ItemWriter() { - public void write(List items) throws Exception { - count+=items.size(); - throw new RuntimeException("Planned failure"); - } - }); - handler.setItemSkipPolicy(new AlwaysSkipItemSkipPolicy()); - handler.registerSkipListener(listener); - ChunkResponse response = handler.handleChunk(new ChunkRequest(StringUtils.commaDelimitedListToSet("foo,bar"), - 12L, 10)); - assertEquals(2, response.getSkipCount()); - assertEquals(4, count); - } - - @Test - public void testSetSkipListeners() { - handler.setSkipListeners(new SkipListener[] { listener }); - testRegisterSkipListener(); - } - -} diff --git a/spring-batch-integration/src/test/resources/integration-context.xml b/spring-batch-integration/src/test/resources/integration-context.xml index 0d876a36d..239c19019 100644 --- a/spring-batch-integration/src/test/resources/integration-context.xml +++ b/spring-batch-integration/src/test/resources/integration-context.xml @@ -10,8 +10,10 @@ http://www.springframework.org/schema/context/spring-context-2.5.xsd http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-2.5.xsd"> - + - - + + + + \ No newline at end of file diff --git a/spring-batch-integration/src/test/resources/job-execution-context.xml b/spring-batch-integration/src/test/resources/job-execution-context.xml index 10798967a..4fa57e18a 100644 --- a/spring-batch-integration/src/test/resources/job-execution-context.xml +++ b/spring-batch-integration/src/test/resources/job-execution-context.xml @@ -8,7 +8,7 @@ http://www.springframework.org/schema/integration/spring-integration-1.0.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-2.5.xsd"> - + \ No newline at end of file diff --git a/spring-batch-integration/src/test/resources/org/springframework/batch/integration/chunk/ChunkMessageItemWriterIntegrationTests-context.xml b/spring-batch-integration/src/test/resources/org/springframework/batch/integration/chunk/ChunkMessageItemWriterIntegrationTests-context.xml index b604d272a..011bd4899 100644 --- a/spring-batch-integration/src/test/resources/org/springframework/batch/integration/chunk/ChunkMessageItemWriterIntegrationTests-context.xml +++ b/spring-batch-integration/src/test/resources/org/springframework/batch/integration/chunk/ChunkMessageItemWriterIntegrationTests-context.xml @@ -11,7 +11,7 @@ http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-2.5.xsd"> - + @@ -22,9 +22,16 @@ class="org.springframework.batch.support.transaction.ResourcelessTransactionManager" /> - - - + + + + + + + + + + diff --git a/spring-batch-integration/src/test/resources/org/springframework/batch/integration/file/ResourceSplitterIntegrationTests-context.xml b/spring-batch-integration/src/test/resources/org/springframework/batch/integration/file/ResourceSplitterIntegrationTests-context.xml index 1a3f63769..8650e111d 100644 --- a/spring-batch-integration/src/test/resources/org/springframework/batch/integration/file/ResourceSplitterIntegrationTests-context.xml +++ b/spring-batch-integration/src/test/resources/org/springframework/batch/integration/file/ResourceSplitterIntegrationTests-context.xml @@ -11,8 +11,10 @@ http://www.springframework.org/schema/integration http://www.springframework.org/schema/integration/spring-integration-1.0.xsd http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-2.5.xsd"> - + - + + + \ No newline at end of file diff --git a/spring-batch-integration/src/test/resources/org/springframework/batch/integration/item/MessageChannelItemWriterIntegrationTests-context.xml b/spring-batch-integration/src/test/resources/org/springframework/batch/integration/item/MessageChannelItemWriterIntegrationTests-context.xml index e534226fb..72351415d 100644 --- a/spring-batch-integration/src/test/resources/org/springframework/batch/integration/item/MessageChannelItemWriterIntegrationTests-context.xml +++ b/spring-batch-integration/src/test/resources/org/springframework/batch/integration/item/MessageChannelItemWriterIntegrationTests-context.xml @@ -11,7 +11,7 @@ http://www.springframework.org/schema/integration http://www.springframework.org/schema/integration/spring-integration-1.0.xsd http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-2.5.xsd"> - + diff --git a/spring-batch-integration/src/test/resources/org/springframework/batch/integration/job/MessageOrientedStepIntegrationTests-context.xml b/spring-batch-integration/src/test/resources/org/springframework/batch/integration/job/MessageOrientedStepIntegrationTests-context.xml index 3c4fb06a0..307a32369 100644 --- a/spring-batch-integration/src/test/resources/org/springframework/batch/integration/job/MessageOrientedStepIntegrationTests-context.xml +++ b/spring-batch-integration/src/test/resources/org/springframework/batch/integration/job/MessageOrientedStepIntegrationTests-context.xml @@ -15,14 +15,15 @@ http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-2.5.xsd"> - + - + + + - - diff --git a/spring-batch-integration/src/test/resources/org/springframework/batch/integration/launch/JobLaunchingMessageHandlerIntegrationTests-context.xml b/spring-batch-integration/src/test/resources/org/springframework/batch/integration/launch/JobLaunchingMessageHandlerIntegrationTests-context.xml index cb8a5df78..48fa16248 100644 --- a/spring-batch-integration/src/test/resources/org/springframework/batch/integration/launch/JobLaunchingMessageHandlerIntegrationTests-context.xml +++ b/spring-batch-integration/src/test/resources/org/springframework/batch/integration/launch/JobLaunchingMessageHandlerIntegrationTests-context.xml @@ -14,13 +14,15 @@ - + - - - + + + + + - +