From 07cce7a7e4fd3ca807da966fb5f421c7c3088362 Mon Sep 17 00:00:00 2001 From: Dave Syer Date: Wed, 5 Dec 2012 11:28:00 +0000 Subject: [PATCH] BATCH-1913: add javadocs and some more convenience methods --- .../xml/StepParserStepFactoryBean.java | 2 +- .../builder/AbstractTaskletStepBuilder.java | 110 +++++++++- .../builder/FaultTolerantStepBuilder.java | 195 +++++++++++++++--- .../core/step/builder/FlowStepBuilder.java | 23 ++- .../core/step/builder/JobStepBuilder.java | 35 +++- .../step/builder/PartitionStepBuilder.java | 94 +++++++-- .../core/step/builder/SimpleStepBuilder.java | 167 ++++++++++++--- .../batch/core/step/builder/StepBuilder.java | 57 ++++- .../step/builder/StepBuilderException.java | 7 + .../core/step/builder/StepBuilderHelper.java | 22 +- .../core/step/builder/TaskletStepBuilder.java | 12 ++ .../step/factory/SimpleStepFactoryBean.java | 2 +- .../xml/StepParserStepFactoryBeanTests.java | 5 +- 13 files changed, 633 insertions(+), 98 deletions(-) diff --git a/spring-batch-core/src/main/java/org/springframework/batch/core/configuration/xml/StepParserStepFactoryBean.java b/spring-batch-core/src/main/java/org/springframework/batch/core/configuration/xml/StepParserStepFactoryBean.java index d62486d2a..0012c76bb 100644 --- a/spring-batch-core/src/main/java/org/springframework/batch/core/configuration/xml/StepParserStepFactoryBean.java +++ b/spring-batch-core/src/main/java/org/springframework/batch/core/configuration/xml/StepParserStepFactoryBean.java @@ -391,7 +391,7 @@ class StepParserStepFactoryBean implements FactoryBean, BeanNameAware { builder.reader(itemReader); builder.writer(itemWriter); builder.processor(itemProcessor); - builder.completionPolicy(chunkCompletionPolicy); + builder.chunk(chunkCompletionPolicy); return builder.build(); } diff --git a/spring-batch-core/src/main/java/org/springframework/batch/core/step/builder/AbstractTaskletStepBuilder.java b/spring-batch-core/src/main/java/org/springframework/batch/core/step/builder/AbstractTaskletStepBuilder.java index f840b0ff5..a946d142b 100644 --- a/spring-batch-core/src/main/java/org/springframework/batch/core/step/builder/AbstractTaskletStepBuilder.java +++ b/spring-batch-core/src/main/java/org/springframework/batch/core/step/builder/AbstractTaskletStepBuilder.java @@ -1,9 +1,25 @@ +/* + * Copyright 2012-2013 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.core.step.builder; import java.util.LinkedHashSet; import java.util.Set; import org.springframework.batch.core.ChunkListener; +import org.springframework.batch.core.Step; import org.springframework.batch.core.step.tasklet.Tasklet; import org.springframework.batch.core.step.tasklet.TaskletStep; import org.springframework.batch.item.ItemStream; @@ -16,6 +32,16 @@ import org.springframework.core.task.SyncTaskExecutor; import org.springframework.core.task.TaskExecutor; import org.springframework.transaction.interceptor.TransactionAttribute; +/** + * Base class for step builders that want to build a {@link TaskletStep}. Handles common concerns across all tasklet + * step variants, which are mostly to do with the type of tasklet they carry. + * + * @author Dave Syer + * + * @since 2.2 + * + * @param the type of builder represented + */ public abstract class AbstractTaskletStepBuilder> extends StepBuilderHelper> { @@ -39,6 +65,12 @@ public abstract class AbstractTaskletStepBuilder listener(ChunkListener listener) { listeners.add(listener); return this; } + /** + * Register a stream for callbacks that manage restart data. + * + * @param stream the stream to register + * @return this for fluent chaining + */ public AbstractTaskletStepBuilder stream(ItemStream stream) { streams.add(stream); return this; } + /** + * Provide a task executor to use when executing the tasklet. Default is to use a single-threaded (synchronous) + * executor. + * + * @param taskExecutor the task executor to register + * @return this for fluent chaining + */ public AbstractTaskletStepBuilder taskExecutor(TaskExecutor taskExecutor) { this.taskExecutor = taskExecutor; return this; } + /** + * In the case of an asynchronous {@link #taskExecutor(TaskExecutor)} the number of concurrent tasklet executions + * can be throttled (beyond any throttling provided by a thread pool). The throttle limit should be less than the + * data source pool size used in the job repository for this step. + * + * @param throttleLimit maximium number of concurrent tasklet executions allowed + * @return this for fluent chaining + */ public AbstractTaskletStepBuilder throttleLimit(int throttleLimit) { this.throttleLimit = throttleLimit; return this; } - + + /** + * Sets the exception handler to use in the case of tasklet failures. Default is to rethrow everything. + * + * @param exceptionHandler the exception handler + * @return this for fluent chaining + */ public AbstractTaskletStepBuilder exceptionHandler(ExceptionHandler exceptionHandler) { this.exceptionHandler = exceptionHandler; return this; } + /** + * Sets the repeat template used for iterating the tasklet execution. By default it will terminate only when the + * tasklet returns FINISHED (or null). + * + * @param repeatTemplate a repeat template with rules for iterating + * @return this for fluent chaining + */ public AbstractTaskletStepBuilder stepOperations(RepeatOperations repeatTemplate) { this.stepOperations = repeatTemplate; return this; } + /** + * Sets the transaction attributes for the tasklet execution. Defaults to the default values for the transaction + * manager, but can be manipulated to provide longer timeouts for instance. + * + * @param transactionAttribute a transaction attribute set + * @return this for fluent chaining + */ public AbstractTaskletStepBuilder transactionAttribute(TransactionAttribute transactionAttribute) { this.transactionAttribute = transactionAttribute; return this; } - protected ItemStream[] getStreams() { - return streams.toArray(new ItemStream[0]); - } - + /** + * Convenience method for subclasses to access the step operations that were injected by user. + * + * @return the repeat operations used to iterate the tasklet executions + */ protected RepeatOperations getStepOperations() { return stepOperations; } - + + /** + * Convenience method for subclasses to access the exception handler that was injected by user. + * + * @return the exception handler + */ protected ExceptionHandler getExceptionHandler() { return exceptionHandler; } - + + /** + * Convenience method for subclasses to determine if the step is concurrent. + * + * @return true if the tasklet is going to be run in multiple threads + */ protected boolean concurrent() { boolean concurrent = taskExecutor != null && !(taskExecutor instanceof SyncTaskExecutor); return concurrent; diff --git a/spring-batch-core/src/main/java/org/springframework/batch/core/step/builder/FaultTolerantStepBuilder.java b/spring-batch-core/src/main/java/org/springframework/batch/core/step/builder/FaultTolerantStepBuilder.java index 4f46342eb..b7b3f3d11 100644 --- a/spring-batch-core/src/main/java/org/springframework/batch/core/step/builder/FaultTolerantStepBuilder.java +++ b/spring-batch-core/src/main/java/org/springframework/batch/core/step/builder/FaultTolerantStepBuilder.java @@ -1,5 +1,5 @@ /* - * Copyright 2006-2011 the original author or authors. + * Copyright 2006-2012 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. @@ -71,8 +71,12 @@ import org.springframework.transaction.interceptor.TransactionAttribute; import org.springframework.util.Assert; /** + * A step builder for fully fault tolerant chunk-oriented item processing steps. Extends {@link SimpleStepBuilder} with + * additional properties for retry and skip of failed items. + * * @author Dave Syer * + * @since 2.2 */ public class FaultTolerantStepBuilder extends SimpleStepBuilder { @@ -110,10 +114,29 @@ public class FaultTolerantStepBuilder extends SimpleStepBuilder { private boolean processorTransactional = true; + /** + * Create a new builder initialized with any properties in the parent. The parent is copied, so it can be re-used. + * + * @param parent a parent helper containing common step properties + */ public FaultTolerantStepBuilder(StepBuilderHelper parent) { super(parent); } + /** + * Create a new builder initialized with any properties in the parent. The parent is copied, so it can be re-used. + * + * @param parent a parent helper containing common step properties + */ + protected FaultTolerantStepBuilder(SimpleStepBuilder parent) { + super(parent); + } + + /** + * Create a new chunk oriented tasklet with reader, writer and processor as provided. + * + * @see org.springframework.batch.core.step.builder.SimpleStepBuilder#createTasklet() + */ @Override protected Tasklet createTasklet() { Assert.state(getReader() != null, "ItemReader must be provided"); @@ -127,6 +150,12 @@ public class FaultTolerantStepBuilder extends SimpleStepBuilder { return tasklet; } + /** + * Register a skip listener. + * + * @param listener the listener to register + * @return this for fluent chaining + */ public FaultTolerantStepBuilder listener(SkipListener listener) { skipListeners.add(listener); return this; @@ -137,78 +166,177 @@ public class FaultTolerantStepBuilder extends SimpleStepBuilder { super.listener(new TerminateOnExceptionChunkListenerDelegate(listener)); return this; } - + @Override public AbstractTaskletStepBuilder> transactionAttribute( TransactionAttribute transactionAttribute) { return super.transactionAttribute(getTransactionAttribute(transactionAttribute)); } - + + /** + * Register a retry listener. + * + * @param listener the listener to register + * @return this for fluent chaining + */ public FaultTolerantStepBuilder listener(RetryListener listener) { retryListeners.add(listener); return this; } + /** + * Sets the key generator for identifying retried items. Retry across transaction boundaries requires items to be + * identified when they are encountered again. The default strategy is to use the items themselves, relying on their + * own implementation to ensure that they can be identified. Often a key generator is not necessary as long as the + * items have reliable hash code and equals implementations, or the reader is not transactional (the default) and + * the item processor either is itself not transactional (not the default) or does not create new items. + * + * @param keyGenerator a key generator for the stateful retry + * @return this for fluent chaining + */ public FaultTolerantStepBuilder keyGenerator(KeyGenerator keyGenerator) { this.keyGenerator = keyGenerator; return this; } - + + /** + * The maximum number of times to try a failed item. Zero and one both translate to try only once and do not retry. + * Ignored if an explicit {@link #retryPolicy} is set. + * + * @param retryLimit the retry limit (default 0) + * @return this for fluent chaining + */ public FaultTolerantStepBuilder retryLimit(int retryLimit) { this.retryLimit = retryLimit; return this; } - + + /** + * Provide an explicit retry policy instead of using the {@link #retryLimit(int)} and retryable exceptions provided + * elsewhere. Can be used to retry different exceptions a different number of times, for instance. + * + * @param retryPolicy a retry policy + * @return this for fluent chaining + */ public FaultTolerantStepBuilder retryPolicy(RetryPolicy retryPolicy) { this.retryPolicy = retryPolicy; return this; } - + + /** + * Provide a backoff policy to prevent items being retried immediately (e.g. in case the failure was caused by a + * remote resource failure that might take some time to be resolved). Ignored if an explicit {@link #retryPolicy} is + * set. + * + * @param backOffPolicy the back off policy to use (default no backoff) + * @return this for fluent chaining + */ public FaultTolerantStepBuilder backOffPolicy(BackOffPolicy backOffPolicy) { this.backOffPolicy = backOffPolicy; return this; } - + + /** + * Provide an explicit retry context cache. Retry is stateful across transactions in the case of failures in item + * processing or writing, so some information about the context for subsequent retries has to be stored. + * + * @param retryContextCache cache for retry contexts in between transactions (default to standard in-memory + * implementation) + * @return this for fluent chaining + */ public FaultTolerantStepBuilder retryContextCache(RetryContextCache retryContextCache) { this.retryContextCache = retryContextCache; return this; } - + + /** + * Sets the maximium number of failed items to skip before the step fails. Ignored if an explicit + * {@link #skipPolicy(SkipPolicy)} is provided. + * + * @param skipLimit the skip limit to set + * @return this for fluent chaining + */ public FaultTolerantStepBuilder skipLimit(int skipLimit) { this.skipLimit = skipLimit; return this; } - - public FaultTolerantStepBuilder skipPolicy(SkipPolicy skipPolicy) { - this.skipPolicy = skipPolicy; - return this; - } - - public FaultTolerantStepBuilder noRollback(Class type) { - noRollbackExceptionClasses.add(type); - return this; - } - - public FaultTolerantStepBuilder noRetry(Class type) { - retryableExceptionClasses.put(type, false); - return this; - } - - public FaultTolerantStepBuilder retry(Class type) { - retryableExceptionClasses.put(type, true); - return this; - } + /** + * Explicitly prevent certain exceptions (and subclasses) from being skipped. + * + * @param type the non-skippable exception + * @return this for fluent chaining + */ public FaultTolerantStepBuilder noSkip(Class type) { skippableExceptionClasses.put(type, false); return this; } + /** + * Explicitly request certain exceptions (and subclasses) to be skipped. + * + * @param type + * @return this for fluent chaining + */ public FaultTolerantStepBuilder skip(Class type) { skippableExceptionClasses.put(type, true); return this; } - + + /** + * Provide an explicit policy for managing skips. A skip policy determines which exceptions are skippable and how + * many times. + * + * @param skipPolicy the skip policy + * @return this for fluent chaining + */ + public FaultTolerantStepBuilder skipPolicy(SkipPolicy skipPolicy) { + this.skipPolicy = skipPolicy; + return this; + } + + /** + * Mark this exception as ignorable during item read or processing operations. Processing continues with no + * additional callbacks (use skips instead if you need to be notified). Ignored during write because there is no + * guarantee of skip and retry without rollback. + * + * @param type the exception to mark as no rollback + * @return this for fluent chaining + */ + public FaultTolerantStepBuilder noRollback(Class type) { + noRollbackExceptionClasses.add(type); + return this; + } + + /** + * Explicitly ask for an exception (and subclasses) to be excluded from retry. + * + * @param type the exception to exclude from retry + * @return this for fluent chaining + */ + public FaultTolerantStepBuilder noRetry(Class type) { + retryableExceptionClasses.put(type, false); + return this; + } + + /** + * Explicitly ask for an exception (and subclasses) to be retried. + * + * @param type the exception to retry + * @return this for fluent chaining + */ + public FaultTolerantStepBuilder retry(Class type) { + retryableExceptionClasses.put(type, true); + return this; + } + + /** + * Mark the item processor as non-transactional (default is the opposite). If this flag is set the results of item + * processing are cached across transactions in between retries and during skip processing, otherwise the processor + * will be called in every transaction. + * + * @return this for fluent chaining + */ public FaultTolerantStepBuilder processorNonTransactional() { this.processorTransactional = false; return this; @@ -224,8 +352,9 @@ public class FaultTolerantStepBuilder extends SimpleStepBuilder { // In cases where multiple nested item readers are registered, // they all want to get the open() and close() callbacks. chunkMonitor.registerItemStream(stream); - } else { - super.stream(stream); + } + else { + super.stream(stream); } return this; } @@ -263,7 +392,7 @@ public class FaultTolerantStepBuilder extends SimpleStepBuilder { chunkProcessor.setRollbackClassifier(getRollbackClassifier()); chunkProcessor.setKeyGenerator(keyGenerator); detectStreamInReader(); - + ArrayList listeners = new ArrayList(getItemListeners()); listeners.addAll(skipListeners); chunkProcessor.setListeners(listeners); @@ -367,7 +496,7 @@ public class FaultTolerantStepBuilder extends SimpleStepBuilder { Map, Boolean> map = new HashMap, Boolean>( skippableExceptionClasses); map.put(ForceRollbackForWriteSkipException.class, true); - LimitCheckingItemSkipPolicy limitCheckingItemSkipPolicy = new LimitCheckingItemSkipPolicy(skipLimit , map); + LimitCheckingItemSkipPolicy limitCheckingItemSkipPolicy = new LimitCheckingItemSkipPolicy(skipLimit, map); if (skipPolicy == null) { Assert.state(!(skippableExceptionClasses.isEmpty() && skipLimit > 0), "If a skip limit is provided then skippable exceptions must also be specified"); diff --git a/spring-batch-core/src/main/java/org/springframework/batch/core/step/builder/FlowStepBuilder.java b/spring-batch-core/src/main/java/org/springframework/batch/core/step/builder/FlowStepBuilder.java index 68933ea16..21ff66850 100644 --- a/spring-batch-core/src/main/java/org/springframework/batch/core/step/builder/FlowStepBuilder.java +++ b/spring-batch-core/src/main/java/org/springframework/batch/core/step/builder/FlowStepBuilder.java @@ -20,22 +20,43 @@ import org.springframework.batch.core.job.flow.Flow; import org.springframework.batch.core.job.flow.FlowStep; /** + * A step builder for {@link FlowStep} instances. A flow step delegates processing to a nested flow composed of other + * steps. + * * @author Dave Syer * + * @since 2.2 */ public class FlowStepBuilder extends StepBuilderHelper { private Flow flow; + /** + * Create a new builder initialized with any properties in the parent. The parent is copied, so it can be re-used. + * + * @param parent a parent helper containing common step properties + */ public FlowStepBuilder(StepBuilderHelper parent) { super(parent); } + /** + * Provide a flow to execute during the step. + * + * @param flow the flow to execute + * @return this for fluent chaining + */ public FlowStepBuilder flow(Flow flow) { this.flow = flow; return this; } + /** + * Build a step that executes the flow provided, normally composed of other steps. The flow is not executed in a + * transaction because the individual steps are supposed to manage their own transaction state. + * + * @return a flow step + */ public Step build() { FlowStep step = new FlowStep(); step.setName(getName()); @@ -45,7 +66,7 @@ public class FlowStepBuilder extends StepBuilderHelper { step.afterPropertiesSet(); } catch (Exception e) { - throw new IllegalStateException(e); + throw new StepBuilderException(e); } return step; } diff --git a/spring-batch-core/src/main/java/org/springframework/batch/core/step/builder/JobStepBuilder.java b/spring-batch-core/src/main/java/org/springframework/batch/core/step/builder/JobStepBuilder.java index 66e8411cf..9bbb30d9d 100644 --- a/spring-batch-core/src/main/java/org/springframework/batch/core/step/builder/JobStepBuilder.java +++ b/spring-batch-core/src/main/java/org/springframework/batch/core/step/builder/JobStepBuilder.java @@ -23,8 +23,12 @@ import org.springframework.batch.core.step.job.JobParametersExtractor; import org.springframework.batch.core.step.job.JobStep; /** + * A step builder for {@link JobStep} instances. A job step executes a nested {@link Job} with parameters taken from the + * parent job or from the step execution. + * * @author Dave Syer * + * @since 2.2 */ public class JobStepBuilder extends StepBuilderHelper { @@ -34,25 +38,54 @@ public class JobStepBuilder extends StepBuilderHelper { private JobParametersExtractor jobParametersExtractor; + /** + * Create a new builder initialized with any properties in the parent. The parent is copied, so it can be re-used. + * + * @param parent a parent helper containing common step properties + */ public JobStepBuilder(StepBuilderHelper parent) { super(parent); } + /** + * Provide a job to execute during the step. + * + * @param job the job to execute + * @return this for fluent chaining + */ public JobStepBuilder job(Job job) { this.job = job; return this; } + /** + * Add a job launcher. Defaults to a simple job launcher. + * + * @param jobLauncher the job launcher to use + * @return this for fluent chaining + */ public JobStepBuilder launcher(JobLauncher jobLauncher) { this.jobLauncher = jobLauncher; return this; } + /** + * Provide a job parameters extractor. Useful for extracting job parameters from the parent step execution context + * or job parameters. + * + * @param jobParametersExtractor the job parameters extractor to use + * @return this for fluent chaining + */ public JobStepBuilder parametersExtractor(JobParametersExtractor jobParametersExtractor) { this.jobParametersExtractor = jobParametersExtractor; return this; } + /** + * Build a step from the job provided. + * + * @return a new job step + */ public Step build() { JobStep step = new JobStep(); @@ -80,7 +113,7 @@ public class JobStepBuilder extends StepBuilderHelper { step.afterPropertiesSet(); } catch (Exception e) { - throw new IllegalStateException(e); + throw new StepBuilderException(e); } return step; diff --git a/spring-batch-core/src/main/java/org/springframework/batch/core/step/builder/PartitionStepBuilder.java b/spring-batch-core/src/main/java/org/springframework/batch/core/step/builder/PartitionStepBuilder.java index 47b9ce7d1..99640ef79 100644 --- a/spring-batch-core/src/main/java/org/springframework/batch/core/step/builder/PartitionStepBuilder.java +++ b/spring-batch-core/src/main/java/org/springframework/batch/core/step/builder/PartitionStepBuilder.java @@ -27,8 +27,12 @@ import org.springframework.core.task.SyncTaskExecutor; import org.springframework.core.task.TaskExecutor; /** + * Step builder for {@link PartitionStep} instances. A partition step executes the same step (possibly remotely) + * multiple times with different input parameters (in the form of execution context). Useful for parallelization. + * * @author Dave Syer * + * @since 2.2 */ public class PartitionStepBuilder extends StepBuilderHelper { @@ -50,36 +54,107 @@ public class PartitionStepBuilder extends StepBuilderHelper parent) { super(parent); } + /** + * Add a partitioner which can be used to create a {@link StepExecutionSplitter}. Use either this or an explicit + * {@link #splitter(StepExecutionSplitter)} but not both. + * + * @param slaveStepName the name of the slave step (used to construct step execution names) + * @param partitioner a partitioner to use + * @return this for fluent chaining + */ public PartitionStepBuilder partitioner(String slaveStepName, Partitioner partitioner) { this.stepName = slaveStepName; this.partitioner = partitioner; return this; } + /** + * Provide an actual step instance to execute in parallel. If an explicit + * {@link #partitionHandler(PartitionHandler)} is provided, the step is optional and is only used to extract + * configuration data (name and other basic properties of a step). + * + * @param step a step to execute in parallel + * @return this for fluent chaining + */ public PartitionStepBuilder step(Step step) { this.step = step; return this; } + /** + * Provide a task executor to use when constructing a {@link PartitionHandler} from the {@link #step(Step)}. Mainly + * used for running a step locally in parallel, but can be used to execute remotely if the step is remote. Not used + * if an explicit {@link #partitionHandler(PartitionHandler)} is provided. + * + * @param taskExecutor a task executor to use when executing steps in parallel + * @return this for fluent chaining + */ public PartitionStepBuilder taskExecutor(TaskExecutor taskExecutor) { this.taskExecutor = taskExecutor; return this; } + /** + * Provide an explicit partition handler that will carry out the work of the partition step. The partition handler + * is the main SPI for adapting a partition step to a specific distributed computation environment. Optional if you + * only need local or remote processing through the Step interface. + * + * @see #step(Step) for setting up a default handler that works with a local or remote Step + * + * @param partitionHandler a partition handler + * @return this for fluent chaining + */ public PartitionStepBuilder partitionHandler(PartitionHandler partitionHandler) { this.partitionHandler = partitionHandler; return this; } + /** + * A hint to the {@link #splitter(StepExecutionSplitter)} about how many step executions are required. If running + * locally or remotely through a {@link #taskExecutor(TaskExecutor)} determines precisely the number of step + * execution sin the first attempt at a partition step execution. + * + * @param gridSize the grid size + * @return this for fluent chaining + */ public PartitionStepBuilder gridSize(int gridSize) { this.gridSize = gridSize; return this; } + /** + * Provide an explicit {@link StepExecutionSplitter} instead of having one build from the + * {@link #partitioner(String, Partitioner)}. USeful if you need more control over the splitting. + * + * @param splitter a step execution splitter + * @return this for fluent chaining + */ + public PartitionStepBuilder splitter(StepExecutionSplitter splitter) { + this.splitter = splitter; + return this; + } + + /** + * Provide a step execution aggregator for aggregating partitioned step executions into a single result for the + * {@link PartitionStep} itself. Default is a simple implementation that works in most cases. + * + * @param aggregator a step execution aggregator + * @return this for fluent chaining + */ + public PartitionStepBuilder aggregator(StepExecutionAggregator aggregator) { + this.aggregator = aggregator; + return this; + } + public Step build() { PartitionStep step = new PartitionStep(); @@ -100,9 +175,10 @@ public class PartitionStepBuilder extends StepBuilderHelper extends AbstractTaskletStepBuilder> { @@ -65,10 +72,42 @@ public class SimpleStepBuilder extends AbstractTaskletStepBuilder parent) { super(parent); } + + /** + * Create a new builder initialized with any properties in the parent. The parent is copied, so it can be re-used. + * + * @param parent a parent helper containing common step properties + */ + protected SimpleStepBuilder(SimpleStepBuilder parent) { + super(parent); + this.chunkSize = parent.chunkSize; + this.completionPolicy = parent.completionPolicy; + this.chunkOperations = parent.chunkOperations; + this.reader = parent.reader; + this.writer = parent.writer; + this.processor = parent.processor; + this.itemListeners = parent.itemListeners; + this.readerTransactionalQueue = parent.readerTransactionalQueue; + } + + public FaultTolerantStepBuilder faultTolerant() { + FaultTolerantStepBuilder builder = new FaultTolerantStepBuilder(this); + return builder; + } + /** + * Build a step with the reader, writer, processor as provided. + * + * @see org.springframework.batch.core.step.builder.AbstractTaskletStepBuilder#build() + */ @Override public TaskletStep build() { registerAsStreamsAndListeners(reader, processor, writer); @@ -89,38 +128,14 @@ public class SimpleStepBuilder extends AbstractTaskletStepBuilder readerIsTransactionalQueue() { - this.readerTransactionalQueue = true; - return this; - } - - public SimpleStepBuilder listener(ItemReadListener listener) { - itemListeners.add(listener); - return this; - } - - public SimpleStepBuilder listener(ItemWriteListener listener) { - itemListeners.add(listener); - return this; - } - - public SimpleStepBuilder listener(ItemProcessListener listener) { - itemListeners.add(listener); - return this; - } - - public SimpleStepBuilder chunkOperations(RepeatOperations repeatTemplate) { - this.chunkOperations = repeatTemplate; - return this; - } - - public SimpleStepBuilder completionPolicy(CompletionPolicy completionPolicy) { - Assert.state(chunkSize == 0 || completionPolicy == null, - "You must specify either a chunkCompletionPolicy or a commitInterval but not both."); - this.completionPolicy = completionPolicy; - return this; - } - + /** + * Sets the chunk size or commit interval for this step. This is the maximum number of items that will be read + * before processing starts in a single transaction. Not compatible with {@link #completionPolicy(CompletionPolicy)} + * . + * + * @param chunkSize the chunk size (a.k.a commit interval) + * @return this for fluent chaining + */ public SimpleStepBuilder chunk(int chunkSize) { Assert.state(completionPolicy == null || chunkSize == 0, "You must specify either a chunkCompletionPolicy or a commitInterval but not both."); @@ -128,26 +143,114 @@ public class SimpleStepBuilder extends AbstractTaskletStepBuilder chunk(CompletionPolicy completionPolicy) { + Assert.state(chunkSize == 0 || completionPolicy == null, + "You must specify either a chunkCompletionPolicy or a commitInterval but not both."); this.completionPolicy = completionPolicy; return this; } + /** + * An item reader that provides a stream of items. Will be automatically registered as a {@link #stream(ItemStream)} + * or listener if it implements the corresponding interface. By default assumed to be non-transactional. + * + * @see #readerTransactionalQueue + * @param reader an item reader + * @return this for fluent chaining + */ public SimpleStepBuilder reader(ItemReader reader) { this.reader = reader; return this; } + /** + * An item writer that writes a chunk of items. Will be automatically registered as a {@link #stream(ItemStream)} or + * listener if it implements the corresponding interface. + * + * @param writer an item writer + * @return this for fluent chaining + */ public SimpleStepBuilder writer(ItemWriter writer) { this.writer = writer; return this; } + /** + * An item processor that processes or transforms a stream of items. Will be automatically registered as a + * {@link #stream(ItemStream)} or listener if it implements the corresponding interface. + * + * @param processor an item processor + * @return this for fluent chaining + */ public SimpleStepBuilder processor(ItemProcessor processor) { this.processor = processor; return this; } + /** + * Sets a flag to say that the reader is transactional (usually a queue), which is to say that failed items might be + * rolled back and re-presented in a subsequent transaction. Default is false, meaning that the items are read + * outside a transaction and possibly cached. + * + * @return this for fluent chaining + */ + public SimpleStepBuilder readerIsTransactionalQueue() { + this.readerTransactionalQueue = true; + return this; + } + + /** + * Register an item reader listener. + * + * @param listener the listener to register + * @return this for fluent chaining + */ + public SimpleStepBuilder listener(ItemReadListener listener) { + itemListeners.add(listener); + return this; + } + + /** + * Register an item writer listener. + * + * @param listener the listener to register + * @return this for fluent chaining + */ + public SimpleStepBuilder listener(ItemWriteListener listener) { + itemListeners.add(listener); + return this; + } + + /** + * Register an item processor listener. + * + * @param listener the listener to register + * @return this for fluent chaining + */ + public SimpleStepBuilder listener(ItemProcessListener listener) { + itemListeners.add(listener); + return this; + } + + /** + * Instead of a {@link #chunk(int) chunk size} or {@link #chunk(CompletionPolicy) completion policy} you can provide + * a complete repeat operations instance that handles the iteration over the item reader. + * + * @param repeatTemplate a cmplete repeat template for the chunk + * @return this for fluent chaining + */ + public SimpleStepBuilder chunkOperations(RepeatOperations repeatTemplate) { + this.chunkOperations = repeatTemplate; + return this; + } + protected RepeatOperations createChunkOperations() { RepeatOperations repeatOperations = chunkOperations; if (repeatOperations == null) { 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 ed31cb85d..09b524642 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 @@ -15,7 +15,6 @@ */ package org.springframework.batch.core.step.builder; - import org.springframework.batch.core.Job; import org.springframework.batch.core.Step; import org.springframework.batch.core.job.flow.Flow; @@ -24,39 +23,93 @@ import org.springframework.batch.core.step.tasklet.Tasklet; 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) { super(name); } + /** + * Build a step with a custom tasklet, not necessarily item processing. + * + * @param tasklet a tasklet + * @return a {@link TaskletStepBuilder} + */ public TaskletStepBuilder tasklet(Tasklet tasklet) { return new TaskletStepBuilder(this).tasklet(tasklet); } + /** + * 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. + * + * @param chunkSize the chunk size (commit interval) + * @return a {@link SimpleStepBuilder} + */ public SimpleStepBuilder chunk(int chunkSize) { return new SimpleStepBuilder(this).chunk(chunkSize); } + /** + * 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. + * + * @param completionPolicy the completion policy to use to control chunk processing + * @return a {@link SimpleStepBuilder} + */ public SimpleStepBuilder chunk(CompletionPolicy completionPolicy) { - return new SimpleStepBuilder(this).completionPolicy(completionPolicy); + return new SimpleStepBuilder(this).chunk(completionPolicy); } + /** + * 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} + */ public PartitionStepBuilder partitioner(String stepName, Partitioner partitioner) { return new PartitionStepBuilder(this).partitioner(stepName, partitioner); } + /** + * Create a partition step builder for a remote (or local) step. + * + * @param step the step to execute in parallel + * @param partitioner a partitioner to be used to construct new step executions + * @return a PartitionStepBuilder + */ public PartitionStepBuilder partitioner(Step step) { return new PartitionStepBuilder(this).step(step); } + /** + * Create a new step builder that will execute a job. + * + * @param job a job to execute + * @return a {@link JobStepBuilder} + */ public JobStepBuilder job(Job job) { return new JobStepBuilder(this).job(job); } + /** + * Create a new step builder that will execute a flow. + * + * @param flow a flow to execute + * @return a {@link FlowStepBuilder} + */ public FlowStepBuilder flow(Flow flow) { return new FlowStepBuilder(this).flow(flow); } diff --git a/spring-batch-core/src/main/java/org/springframework/batch/core/step/builder/StepBuilderException.java b/spring-batch-core/src/main/java/org/springframework/batch/core/step/builder/StepBuilderException.java index 0538d3e54..1415817aa 100644 --- a/spring-batch-core/src/main/java/org/springframework/batch/core/step/builder/StepBuilderException.java +++ b/spring-batch-core/src/main/java/org/springframework/batch/core/step/builder/StepBuilderException.java @@ -15,6 +15,13 @@ */ package org.springframework.batch.core.step.builder; +/** + * Utility exception thrown by builders when they encounter unexpected checked exceptions. + * + * @author Dave Syer + * + * @since 2.2 + */ public class StepBuilderException extends RuntimeException { public StepBuilderException(Exception e) { diff --git a/spring-batch-core/src/main/java/org/springframework/batch/core/step/builder/StepBuilderHelper.java b/spring-batch-core/src/main/java/org/springframework/batch/core/step/builder/StepBuilderHelper.java index bb95db50f..572d47c3d 100644 --- a/spring-batch-core/src/main/java/org/springframework/batch/core/step/builder/StepBuilderHelper.java +++ b/spring-batch-core/src/main/java/org/springframework/batch/core/step/builder/StepBuilderHelper.java @@ -28,8 +28,12 @@ import org.springframework.batch.core.step.tasklet.TaskletStep; import org.springframework.transaction.PlatformTransactionManager; /** + * A base class and utility for other step builders providing access to common properties like job repository and + * transaction manager. + * * @author Dave Syer * + * @since 2.2 */ public abstract class StepBuilderHelper> { @@ -42,8 +46,13 @@ public abstract class StepBuilderHelper> { properties.name = name; } + /** + * Create a new builder initialized with any properties in the parent. The parent is copied, so it can be re-used. + * + * @param parent a parent helper containing common step properties + */ protected StepBuilderHelper(StepBuilderHelper parent) { - this.properties = parent.properties; + this.properties = new CommonStepProperties(parent.properties); } public StepBuilderHelper repository(JobRepository jobRepository) { @@ -127,6 +136,17 @@ public abstract class StepBuilderHelper> { private PlatformTransactionManager transactionManager; + public CommonStepProperties() { + } + + public CommonStepProperties(CommonStepProperties properties) { + this.name = properties.name; + this.startLimit = properties.startLimit; + this.allowStartIfComplete = properties.allowStartIfComplete; + this.jobRepository = properties.jobRepository; + this.transactionManager = properties.transactionManager; + } + public JobRepository getJobRepository() { return jobRepository; } diff --git a/spring-batch-core/src/main/java/org/springframework/batch/core/step/builder/TaskletStepBuilder.java b/spring-batch-core/src/main/java/org/springframework/batch/core/step/builder/TaskletStepBuilder.java index 9289dd4cc..81edc487b 100644 --- a/spring-batch-core/src/main/java/org/springframework/batch/core/step/builder/TaskletStepBuilder.java +++ b/spring-batch-core/src/main/java/org/springframework/batch/core/step/builder/TaskletStepBuilder.java @@ -18,17 +18,29 @@ package org.springframework.batch.core.step.builder; import org.springframework.batch.core.step.tasklet.Tasklet; /** + * Builder for tasklet step based on a custom tasklet (not item oriented). + * * @author Dave Syer * + * @since 2.2 */ public class TaskletStepBuilder extends AbstractTaskletStepBuilder { private Tasklet tasklet; + /** + * Create a new builder initialized with any properties in the parent. The parent is copied, so it can be re-used. + * + * @param parent a parent helper containing common step properties + */ public TaskletStepBuilder(StepBuilderHelper parent) { super(parent); } + /** + * @param tasklet tehe tasklet to use + * @return this for fluent chaining + */ public TaskletStepBuilder tasklet(Tasklet tasklet) { this.tasklet = tasklet; return this; diff --git a/spring-batch-core/src/main/java/org/springframework/batch/core/step/factory/SimpleStepFactoryBean.java b/spring-batch-core/src/main/java/org/springframework/batch/core/step/factory/SimpleStepFactoryBean.java index 414cc730e..a0a983799 100755 --- a/spring-batch-core/src/main/java/org/springframework/batch/core/step/factory/SimpleStepFactoryBean.java +++ b/spring-batch-core/src/main/java/org/springframework/batch/core/step/factory/SimpleStepFactoryBean.java @@ -473,7 +473,7 @@ public class SimpleStepFactoryBean implements FactoryBean, BeanNameAware { builder.startLimit(startLimit); builder.allowStartIfComplete(allowStartIfComplete); builder.chunk(commitInterval); - builder.completionPolicy(chunkCompletionPolicy); + builder.chunk(chunkCompletionPolicy); builder.chunkOperations(chunkOperations); builder.stepOperations(stepOperations); builder.taskExecutor(taskExecutor); diff --git a/spring-batch-core/src/test/java/org/springframework/batch/core/configuration/xml/StepParserStepFactoryBeanTests.java b/spring-batch-core/src/test/java/org/springframework/batch/core/configuration/xml/StepParserStepFactoryBeanTests.java index 218863208..5678cb4f7 100644 --- a/spring-batch-core/src/test/java/org/springframework/batch/core/configuration/xml/StepParserStepFactoryBeanTests.java +++ b/spring-batch-core/src/test/java/org/springframework/batch/core/configuration/xml/StepParserStepFactoryBeanTests.java @@ -35,6 +35,7 @@ import org.springframework.batch.core.partition.support.SimplePartitioner; import org.springframework.batch.core.partition.support.TaskExecutorPartitionHandler; import org.springframework.batch.core.step.JobRepositorySupport; import org.springframework.batch.core.step.StepSupport; +import org.springframework.batch.core.step.builder.StepBuilderException; import org.springframework.batch.core.step.item.ChunkOrientedTasklet; import org.springframework.batch.core.step.tasklet.TaskletStep; import org.springframework.batch.item.ItemStream; @@ -55,7 +56,7 @@ import org.springframework.transaction.annotation.Propagation; */ public class StepParserStepFactoryBeanTests { - @Test(expected = IllegalStateException.class) + @Test(expected = StepBuilderException.class) public void testNothingSet() throws Exception { StepParserStepFactoryBean fb = new StepParserStepFactoryBean(); fb.getObject(); @@ -88,7 +89,7 @@ public class StepParserStepFactoryBeanTests { assertTrue(stepOperations instanceof TaskExecutorRepeatTemplate); } - @Test(expected = IllegalStateException.class) + @Test(expected = StepBuilderException.class) public void testSkipLimitSet() throws Exception { StepParserStepFactoryBean fb = new StepParserStepFactoryBean(); fb.setName("step");