diff --git a/spring-batch-core/src/main/java/org/springframework/batch/core/step/builder/StepBuilder.java b/spring-batch-core/src/main/java/org/springframework/batch/core/step/builder/StepBuilder.java index 500ae1722..b710849e1 100644 --- a/spring-batch-core/src/main/java/org/springframework/batch/core/step/builder/StepBuilder.java +++ b/spring-batch-core/src/main/java/org/springframework/batch/core/step/builder/StepBuilder.java @@ -24,16 +24,16 @@ import org.springframework.batch.repeat.CompletionPolicy; /** * Convenient entry point for building all kinds of steps. Use this as a factory for fluent builders of any step. - * + * * @author Dave Syer - * + * * @since 2.2 */ public class StepBuilder extends StepBuilderHelper { /** * Initialize a step builder for a step with the given name. - * + * * @param name the name of the step */ public StepBuilder(String name) { @@ -42,7 +42,7 @@ public class StepBuilder extends StepBuilderHelper { /** * Build a step with a custom tasklet, not necessarily item processing. - * + * * @param tasklet a tasklet * @return a {@link TaskletStepBuilder} */ @@ -54,12 +54,12 @@ public class StepBuilder extends StepBuilderHelper { * Build a step that processes items in chunks with the size provided. To extend the step to being fault tolerant, * call the {@link SimpleStepBuilder#faultTolerant()} method on the builder. In most cases you will want to * parameterize your call to this method, to preserve the type safety of your readers and writers, e.g. - * + * *
 	 * new StepBuilder("step1").<Order, Ledger> chunk(100).reader(new OrderReader()).writer(new LedgerWriter())
 	 * // ... etc.
 	 * 
- * + * * @param chunkSize the chunk size (commit interval) * @return a {@link SimpleStepBuilder} * @param the type of item to be processed as input @@ -73,12 +73,12 @@ public class StepBuilder extends StepBuilderHelper { * Build a step that processes items in chunks with the completion policy provided. To extend the step to being * fault tolerant, call the {@link SimpleStepBuilder#faultTolerant()} method on the builder. In most cases you will * want to parameterize your call to this method, to preserve the type safety of your readers and writers, e.g. - * + * *
 	 * new StepBuilder("step1").<Order, Ledger> chunk(100).reader(new OrderReader()).writer(new LedgerWriter())
 	 * // ... etc.
 	 * 
- * + * * @param completionPolicy the completion policy to use to control chunk processing * @return a {@link SimpleStepBuilder} * @param the type of item to be processed as input @@ -90,7 +90,7 @@ public class StepBuilder extends StepBuilderHelper { /** * Create a partition step builder for a remote (or local) step. - * + * * @param stepName the name of the remote or delegate step * @param partitioner a partitioner to be used to construct new step executions * @return a {@link PartitionStepBuilder} @@ -101,7 +101,7 @@ public class StepBuilder extends StepBuilderHelper { /** * Create a partition step builder for a remote (or local) step. - * + * * @param step the step to execute in parallel * @return a PartitionStepBuilder */ @@ -111,7 +111,7 @@ public class StepBuilder extends StepBuilderHelper { /** * Create a new step builder that will execute a job. - * + * * @param job a job to execute * @return a {@link JobStepBuilder} */ @@ -121,7 +121,7 @@ public class StepBuilder extends StepBuilderHelper { /** * Create a new step builder that will execute a flow. - * + * * @param flow a flow to execute * @return a {@link FlowStepBuilder} */ diff --git a/spring-batch-core/src/test/java/org/springframework/batch/core/step/builder/RegisterMultiListenerTest.java b/spring-batch-core/src/test/java/org/springframework/batch/core/step/builder/RegisterMultiListenerTest.java index 79278a652..f40c2cc74 100644 --- a/spring-batch-core/src/test/java/org/springframework/batch/core/step/builder/RegisterMultiListenerTest.java +++ b/spring-batch-core/src/test/java/org/springframework/batch/core/step/builder/RegisterMultiListenerTest.java @@ -1,19 +1,21 @@ package org.springframework.batch.core.step.builder; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertTrue; + import java.util.List; import javax.sql.DataSource; -import static org.junit.Assert.*; - +import org.junit.After; import org.junit.Test; -import org.junit.runner.RunWith; +import org.springframework.batch.core.BatchStatus; import org.springframework.batch.core.ChunkListener; import org.springframework.batch.core.ExitStatus; import org.springframework.batch.core.ItemWriteListener; import org.springframework.batch.core.Job; +import org.springframework.batch.core.JobExecution; import org.springframework.batch.core.JobParameters; -import org.springframework.batch.core.JobParametersInvalidException; import org.springframework.batch.core.SkipListener; import org.springframework.batch.core.Step; import org.springframework.batch.core.StepExecution; @@ -22,9 +24,6 @@ import org.springframework.batch.core.configuration.annotation.EnableBatchProces import org.springframework.batch.core.configuration.annotation.JobBuilderFactory; import org.springframework.batch.core.configuration.annotation.StepBuilderFactory; import org.springframework.batch.core.launch.JobLauncher; -import org.springframework.batch.core.repository.JobExecutionAlreadyRunningException; -import org.springframework.batch.core.repository.JobInstanceAlreadyCompleteException; -import org.springframework.batch.core.repository.JobRestartException; import org.springframework.batch.core.scope.context.ChunkContext; import org.springframework.batch.item.ItemReader; import org.springframework.batch.item.ItemWriter; @@ -32,72 +31,95 @@ import org.springframework.batch.item.NonTransientResourceException; import org.springframework.batch.item.ParseException; import org.springframework.batch.item.UnexpectedInputException; import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.config.AutowireCapableBeanFactory; +import org.springframework.context.annotation.AnnotationConfigApplicationContext; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; +import org.springframework.context.support.GenericApplicationContext; +import org.springframework.jdbc.datasource.embedded.EmbeddedDatabase; import org.springframework.jdbc.datasource.embedded.EmbeddedDatabaseBuilder; import org.springframework.jdbc.datasource.embedded.EmbeddedDatabaseType; -import org.springframework.test.context.ContextConfiguration; -import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; /** * Test for registering a listener class that implements different listeners interfaces * just once in java based configuration. - * + * * @author Tobias Flohre */ -@ContextConfiguration(classes=RegisterMultiListenerTest.MultiListenerTestConfiguration.class) -@RunWith(SpringJUnit4ClassRunner.class) public class RegisterMultiListenerTest { - + @Autowired private JobLauncher jobLauncher; - + @Autowired private Job job; - + @Autowired private CallChecker callChecker; - + + @Autowired + private EmbeddedDatabase dataSource; + + private GenericApplicationContext context; + + @After + public void tearDown() { + jobLauncher = null; + job = null; + callChecker = null; + + if(dataSource != null) { + dataSource.shutdown(); + } + + if(context != null) { + context.close(); + } + } + @Test - public void testMultiListener() throws JobExecutionAlreadyRunningException, JobRestartException, JobInstanceAlreadyCompleteException, JobParametersInvalidException{ - jobLauncher.run(job, new JobParameters()); + public void testMultiListenerSimpleStep() throws Exception { + bootstrap(MultiListenerFaultTolerantTestConfiguration.class); + + JobExecution execution = jobLauncher.run(job, new JobParameters()); + assertEquals(BatchStatus.COMPLETED, execution.getStatus()); assertTrue("beforeStep hasn't been called",callChecker.beforeStepCalled); assertTrue("beforeChunk hasn't been called",callChecker.beforeChunkCalled); assertTrue("beforeWrite hasn't been called",callChecker.beforeWriteCalled); assertTrue("skipInWrite hasn't been called",callChecker.skipInWriteCalled); } - - @Configuration - @EnableBatchProcessing - public static class MultiListenerTestConfiguration{ - + + @Test + public void testMultiListenerFaultTolerantStep() throws Exception { + bootstrap(MultiListenerTestConfiguration.class); + + JobExecution execution = jobLauncher.run(job, new JobParameters()); + assertEquals(BatchStatus.FAILED, execution.getStatus()); + assertTrue("beforeStep hasn't been called",callChecker.beforeStepCalled); + assertTrue("beforeChunk hasn't been called",callChecker.beforeChunkCalled); + assertTrue("beforeWrite hasn't been called",callChecker.beforeWriteCalled); + } + + private void bootstrap(Class configurationClass) { + context = new AnnotationConfigApplicationContext(configurationClass); + context.getAutowireCapableBeanFactory().autowireBeanProperties(this, AutowireCapableBeanFactory.AUTOWIRE_BY_TYPE, false); + } + + public static abstract class MultiListenerTestConfigurationSupport { + @Autowired - private JobBuilderFactory jobBuilders; - + protected JobBuilderFactory jobBuilders; + @Autowired - private StepBuilderFactory stepBuilders; - + protected StepBuilderFactory stepBuilders; + @Bean public Job testJob(){ return jobBuilders.get("testJob") .start(step()) .build(); } - - @Bean - public Step step(){ - return stepBuilders.get("step") - .listener(listener()) - .chunk(1) - .reader(reader()) - .writer(writer()) - .faultTolerant() - .skipLimit(1) - .skip(MySkippableException.class) - .build(); - } - @Bean public DataSource dataSource(){ EmbeddedDatabaseBuilder embeddedDatabaseBuilder = new EmbeddedDatabaseBuilder(); @@ -105,31 +127,40 @@ public class RegisterMultiListenerTest { .setType(EmbeddedDatabaseType.HSQL) .build(); } - + @Bean public CallChecker callChecker(){ return new CallChecker(); } - + @Bean public MultiListener listener(){ return new MultiListener(callChecker()); } - + @Bean public ItemReader reader(){ return new ItemReader(){ + private int count = 0; + @Override public String read() throws Exception, - UnexpectedInputException, ParseException, - NonTransientResourceException { - return "item"; + UnexpectedInputException, ParseException, + NonTransientResourceException { + count++; + + if(count < 5) { + System.err.println("returning item with count " + count); + return "item" + count; + } else { + return null; + } } - + }; } - + @Bean public ItemWriter writer(){ return new ItemWriter(){ @@ -137,25 +168,64 @@ public class RegisterMultiListenerTest { @Override public void write(List items) throws Exception { - throw new MySkippableException(); + if(items.contains("item2")) { + throw new MySkippableException(); + } } - + }; } - + + public abstract Step step(); } - + + @Configuration + @EnableBatchProcessing + public static class MultiListenerFaultTolerantTestConfiguration extends MultiListenerTestConfigurationSupport{ + + @Override + @Bean + public Step step(){ + return stepBuilders.get("step") + .listener(listener()) + .chunk(2) + .reader(reader()) + .writer(writer()) + .faultTolerant() + .skipLimit(1) + .listener((SkipListener) listener()) + .skip(MySkippableException.class) + .build(); + } + } + + @Configuration + @EnableBatchProcessing + public static class MultiListenerTestConfiguration extends MultiListenerTestConfigurationSupport{ + + @Override + @Bean + public Step step(){ + return stepBuilders.get("step") + .listener(listener()) + .chunk(2) + .reader(reader()) + .writer(writer()) + .build(); + } + } + private static class CallChecker { boolean beforeStepCalled = false; boolean beforeChunkCalled = false; boolean beforeWriteCalled = false; boolean skipInWriteCalled = false; } - + private static class MultiListener implements StepExecutionListener, ChunkListener, ItemWriteListener, SkipListener{ - + private CallChecker callChecker; - + private MultiListener(CallChecker callChecker) { super(); this.callChecker = callChecker; @@ -167,6 +237,7 @@ public class RegisterMultiListenerTest { @Override public void onSkipInWrite(String item, Throwable t) { + System.err.println("skipWrite was called"); callChecker.skipInWriteCalled = true; } @@ -186,6 +257,7 @@ public class RegisterMultiListenerTest { @Override public void onWriteError(Exception exception, List items) { + System.err.println("write error was called"); } @Override @@ -210,13 +282,13 @@ public class RegisterMultiListenerTest { public ExitStatus afterStep(StepExecution stepExecution) { return null; } - + } - + private static class MySkippableException extends RuntimeException{ private static final long serialVersionUID = 1L; - + } }