diff --git a/spring-batch-core/src/main/java/org/springframework/batch/core/listener/AbstractListenerFactoryBean.java b/spring-batch-core/src/main/java/org/springframework/batch/core/listener/AbstractListenerFactoryBean.java index 86095900d..3ce781154 100644 --- a/spring-batch-core/src/main/java/org/springframework/batch/core/listener/AbstractListenerFactoryBean.java +++ b/spring-batch-core/src/main/java/org/springframework/batch/core/listener/AbstractListenerFactoryBean.java @@ -183,6 +183,9 @@ public abstract class AbstractListenerFactoryBean implements FactoryBean, Initia * interface, or contains the marker annotations */ public static boolean isListener(Object target, Class listenerType, ListenerMetaData[] metaDataValues) { + if (target == null) { + return false; + } if (listenerType.isInstance(target)) { return true; } diff --git a/spring-batch-core/src/main/java/org/springframework/batch/core/step/item/SimpleChunkProcessor.java b/spring-batch-core/src/main/java/org/springframework/batch/core/step/item/SimpleChunkProcessor.java index d01cb0a91..07a04f157 100755 --- a/spring-batch-core/src/main/java/org/springframework/batch/core/step/item/SimpleChunkProcessor.java +++ b/spring-batch-core/src/main/java/org/springframework/batch/core/step/item/SimpleChunkProcessor.java @@ -113,6 +113,13 @@ public class SimpleChunkProcessor implements ChunkProcessor, Initializi * @throws Exception */ protected final O doProcess(I item) throws Exception { + + if (itemProcessor==null) { + @SuppressWarnings("unchecked") + O result = (O) item; + return result; + } + try { listener.beforeProcess(item); O result = itemProcessor.process(item); @@ -123,6 +130,7 @@ public class SimpleChunkProcessor implements ChunkProcessor, Initializi listener.onProcessError(item, e); throw e; } + } /** @@ -132,6 +140,11 @@ public class SimpleChunkProcessor implements ChunkProcessor, Initializi * @throws Exception */ protected final void doWrite(List items) throws Exception { + + if (itemWriter==null) { + return; + } + try { listener.beforeWrite(items); writeItems(items); @@ -141,6 +154,7 @@ public class SimpleChunkProcessor implements ChunkProcessor, Initializi listener.onWriteError(e, items); throw e; } + } /** @@ -153,7 +167,9 @@ public class SimpleChunkProcessor implements ChunkProcessor, Initializi } protected void writeItems(List items) throws Exception { - itemWriter.write(items); + if (itemWriter!=null) { + itemWriter.write(items); + } } public final void process(StepContribution contribution, Chunk inputs) throws Exception { diff --git a/spring-batch-core/src/main/java/org/springframework/batch/core/step/item/SimpleStepFactoryBean.java b/spring-batch-core/src/main/java/org/springframework/batch/core/step/item/SimpleStepFactoryBean.java index d674cc84a..caaa04847 100755 --- a/spring-batch-core/src/main/java/org/springframework/batch/core/step/item/SimpleStepFactoryBean.java +++ b/spring-batch-core/src/main/java/org/springframework/batch/core/step/item/SimpleStepFactoryBean.java @@ -77,6 +77,8 @@ public class SimpleStepFactoryBean implements FactoryBean, BeanNameAware { private ItemReader itemReader; + private ItemProcessor itemProcessor; + private ItemWriter itemWriter; private PlatformTransactionManager transactionManager; @@ -97,13 +99,6 @@ public class SimpleStepFactoryBean implements FactoryBean, BeanNameAware { protected final Log logger = LogFactory.getLog(getClass()); - private ItemProcessor itemProcessor = new ItemProcessor() { - @SuppressWarnings("unchecked") - public S process(T item) throws Exception { - return (S) item; - } - }; - private int commitInterval = 0; private TaskExecutor taskExecutor; @@ -469,9 +464,9 @@ public class SimpleStepFactoryBean implements FactoryBean, BeanNameAware { */ protected void applyConfiguration(TaskletStep step) { - Assert.notNull(getItemReader(), "ItemReader must be provided"); - Assert.notNull(getItemWriter(), "ItemWriter must be provided"); - Assert.notNull(transactionManager, "TransactionManager must be provided"); + Assert.state(getItemReader()!=null, "ItemReader must be provided"); + Assert.state(getItemWriter()!=null || getItemProcessor()!=null, "ItemWriter or ItemProcessor must be provided"); + Assert.state(transactionManager!=null, "TransactionManager must be provided"); step.setTransactionManager(transactionManager); step.setTransactionAttribute(getTransactionAttribute()); diff --git a/spring-batch-core/src/test/java/org/springframework/batch/core/step/item/FaultTolerantStepFactoryBeanTests.java b/spring-batch-core/src/test/java/org/springframework/batch/core/step/item/FaultTolerantStepFactoryBeanTests.java index 81d12f1f9..b8089479d 100644 --- a/spring-batch-core/src/test/java/org/springframework/batch/core/step/item/FaultTolerantStepFactoryBeanTests.java +++ b/spring-batch-core/src/test/java/org/springframework/batch/core/step/item/FaultTolerantStepFactoryBeanTests.java @@ -296,6 +296,30 @@ public class FaultTolerantStepFactoryBeanTests { .getName())); } + @Test + public void testNullWriter() throws Exception { + + factory.setItemWriter(null); + Step step = (Step) factory.getObject(); + + step.execute(stepExecution); + + assertEquals(0, stepExecution.getSkipCount()); + assertEquals(0, stepExecution.getReadSkipCount()); + assertEquals(5, stepExecution.getReadCount()); + // Write count is incremented even if nothing happens + assertEquals(5, stepExecution.getWriteCount()); + assertEquals(0, stepExecution.getFilterCount()); + assertEquals(0, stepExecution.getRollbackCount()); + + // writer skips "4" + assertTrue(reader.getRead().contains("4")); + + assertEquals(BatchStatus.COMPLETED, stepExecution.getStatus()); + assertStepExecutionsAreEqual(stepExecution, repository.getLastStepExecution(jobExecution.getJobInstance(), step + .getName())); + } + /** * Check items causing errors are skipped as expected. */ diff --git a/spring-batch-core/src/test/java/org/springframework/batch/core/step/item/SimpleStepFactoryBeanTests.java b/spring-batch-core/src/test/java/org/springframework/batch/core/step/item/SimpleStepFactoryBeanTests.java index b8c6b510a..bb44e6ac9 100644 --- a/spring-batch-core/src/test/java/org/springframework/batch/core/step/item/SimpleStepFactoryBeanTests.java +++ b/spring-batch-core/src/test/java/org/springframework/batch/core/step/item/SimpleStepFactoryBeanTests.java @@ -47,6 +47,7 @@ import org.springframework.batch.core.repository.dao.MapJobInstanceDao; import org.springframework.batch.core.repository.dao.MapStepExecutionDao; import org.springframework.batch.core.repository.support.SimpleJobRepository; import org.springframework.batch.core.step.AbstractStep; +import org.springframework.batch.item.ItemProcessor; import org.springframework.batch.item.ItemReader; import org.springframework.batch.item.ItemWriter; import org.springframework.batch.item.support.ListItemReader; @@ -83,7 +84,7 @@ public class SimpleStepFactoryBeanTests { job.setBeanName("simpleJob"); } - @Test(expected = IllegalArgumentException.class) + @Test(expected = IllegalStateException.class) public void testMandatoryProperties() throws Exception { new SimpleStepFactoryBean().getObject(); } @@ -325,11 +326,15 @@ public class SimpleStepFactoryBeanTests { final List listenerCalls = new ArrayList(); - class TestItemListenerWriter implements ItemWriter, ItemReadListener, - ItemWriteListener, ItemProcessListener, ChunkListener { + class TestItemListenerWriter implements ItemWriter, ItemProcessor, + ItemReadListener, ItemWriteListener, ItemProcessListener, ChunkListener { public void write(List items) throws Exception { } + public String process(String item) throws Exception { + return item; + } + public void afterRead(String item) { listenerCalls.add("read"); } @@ -369,7 +374,9 @@ public class SimpleStepFactoryBeanTests { } - factory.setItemWriter(new TestItemListenerWriter()); + TestItemListenerWriter itemWriter = new TestItemListenerWriter(); + factory.setItemWriter(itemWriter); + factory.setItemProcessor(itemWriter); Step step = (Step) factory.getObject(); @@ -409,7 +416,7 @@ public class SimpleStepFactoryBeanTests { } TestItemListenerWriter itemWriter = new TestItemListenerWriter(); - factory.setListeners(new StepListener[] {itemWriter}); + factory.setListeners(new StepListener[] { itemWriter }); factory.setItemWriter(itemWriter); Step step = (Step) factory.getObject(); @@ -425,6 +432,31 @@ public class SimpleStepFactoryBeanTests { } + @Test + public void testNullWriter() throws Exception { + + SimpleStepFactoryBean factory = getStepFactory(new String[] { "foo", "bar", "spam" }); + factory.setItemWriter(null); + factory.setItemProcessor(new ItemProcessor() { + public String process(String item) throws Exception { + written.add(item); + return null; + } + }); + + Step step = (Step) factory.getObject(); + + job.setSteps(Collections.singletonList(step)); + + JobExecution jobExecution = repository.createJobExecution(job.getName(), new JobParameters()); + + job.execute(jobExecution); + + assertEquals(BatchStatus.COMPLETED, jobExecution.getStatus()); + assertEquals("[foo, bar, spam]", written.toString()); + + } + private SimpleStepFactoryBean getStepFactory(String... args) throws Exception { SimpleStepFactoryBean factory = new SimpleStepFactoryBean();