BATCH-1913: add javadocs and some more convenience methods

This commit is contained in:
Dave Syer
2012-12-05 11:28:00 +00:00
parent d325250423
commit 07cce7a7e4
13 changed files with 633 additions and 98 deletions

View File

@@ -391,7 +391,7 @@ class StepParserStepFactoryBean<I, O> implements FactoryBean, BeanNameAware {
builder.reader(itemReader);
builder.writer(itemWriter);
builder.processor(itemProcessor);
builder.completionPolicy(chunkCompletionPolicy);
builder.chunk(chunkCompletionPolicy);
return builder.build();
}

View File

@@ -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 <B> the type of builder represented
*/
public abstract class AbstractTaskletStepBuilder<B extends AbstractTaskletStepBuilder<B>> extends
StepBuilderHelper<AbstractTaskletStepBuilder<B>> {
@@ -39,6 +65,12 @@ public abstract class AbstractTaskletStepBuilder<B extends AbstractTaskletStepBu
protected abstract Tasklet createTasklet();
/**
* Build the step from the components collected by the fluent setters. Delegates first to {@link #enhance(Step)} and
* then to {@link #createTasklet()} in subclasses to create the actual tasklet.
*
* @return a tasklet step fully configured and read to execute
*/
public TaskletStep build() {
TaskletStep step = new TaskletStep(getName());
@@ -68,66 +100,124 @@ public abstract class AbstractTaskletStepBuilder<B extends AbstractTaskletStepBu
step.setStepOperations(stepOperations);
step.setTasklet(createTasklet());
step.setStreams(getStreams());
step.setStreams(streams.toArray(new ItemStream[0]));
try {
step.afterPropertiesSet();
}
catch (Exception e) {
throw new IllegalStateException(e);
throw new StepBuilderException(e);
}
return step;
}
/**
* Register a chunk listener.
*
* @param listener the listener to register
* @return this for fluent chaining
*/
public AbstractTaskletStepBuilder<B> 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<B> 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<B> 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<B> 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<B> 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<B> 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<B> 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;

View File

@@ -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<I, O> extends SimpleStepBuilder<I, O> {
@@ -110,10 +114,29 @@ public class FaultTolerantStepBuilder<I, O> extends SimpleStepBuilder<I, O> {
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<I, O> 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<I, O> extends SimpleStepBuilder<I, O> {
return tasklet;
}
/**
* Register a skip listener.
*
* @param listener the listener to register
* @return this for fluent chaining
*/
public FaultTolerantStepBuilder<I, O> listener(SkipListener<? super I, ? super O> listener) {
skipListeners.add(listener);
return this;
@@ -137,78 +166,177 @@ public class FaultTolerantStepBuilder<I, O> extends SimpleStepBuilder<I, O> {
super.listener(new TerminateOnExceptionChunkListenerDelegate(listener));
return this;
}
@Override
public AbstractTaskletStepBuilder<SimpleStepBuilder<I, O>> 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<I, O> 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<I, O> 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<I, O> 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<I, O> 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<I, O> 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<I, O> 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<I, O> skipLimit(int skipLimit) {
this.skipLimit = skipLimit;
return this;
}
public FaultTolerantStepBuilder<I, O> skipPolicy(SkipPolicy skipPolicy) {
this.skipPolicy = skipPolicy;
return this;
}
public FaultTolerantStepBuilder<I, O> noRollback(Class<? extends Throwable> type) {
noRollbackExceptionClasses.add(type);
return this;
}
public FaultTolerantStepBuilder<I, O> noRetry(Class<? extends Throwable> type) {
retryableExceptionClasses.put(type, false);
return this;
}
public FaultTolerantStepBuilder<I, O> retry(Class<? extends Throwable> 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<I, O> noSkip(Class<? extends Throwable> 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<I, O> skip(Class<? extends Throwable> 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<I, O> 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<I, O> noRollback(Class<? extends Throwable> 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<I, O> noRetry(Class<? extends Throwable> 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<I, O> retry(Class<? extends Throwable> 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<I, O> processorNonTransactional() {
this.processorTransactional = false;
return this;
@@ -224,8 +352,9 @@ public class FaultTolerantStepBuilder<I, O> extends SimpleStepBuilder<I, O> {
// 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<I, O> extends SimpleStepBuilder<I, O> {
chunkProcessor.setRollbackClassifier(getRollbackClassifier());
chunkProcessor.setKeyGenerator(keyGenerator);
detectStreamInReader();
ArrayList<StepListener> listeners = new ArrayList<StepListener>(getItemListeners());
listeners.addAll(skipListeners);
chunkProcessor.setListeners(listeners);
@@ -367,7 +496,7 @@ public class FaultTolerantStepBuilder<I, O> extends SimpleStepBuilder<I, O> {
Map<Class<? extends Throwable>, Boolean> map = new HashMap<Class<? extends Throwable>, 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");

View File

@@ -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<FlowStepBuilder> {
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<FlowStepBuilder> {
step.afterPropertiesSet();
}
catch (Exception e) {
throw new IllegalStateException(e);
throw new StepBuilderException(e);
}
return step;
}

View File

@@ -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<JobStepBuilder> {
@@ -34,25 +38,54 @@ public class JobStepBuilder extends StepBuilderHelper<JobStepBuilder> {
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<JobStepBuilder> {
step.afterPropertiesSet();
}
catch (Exception e) {
throw new IllegalStateException(e);
throw new StepBuilderException(e);
}
return step;

View File

@@ -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<PartitionStepBuilder> {
@@ -50,36 +54,107 @@ public class PartitionStepBuilder extends StepBuilderHelper<PartitionStepBuilder
private String stepName;
/**
* 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 PartitionStepBuilder(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<PartitionStepBuilder
step.setPartitionHandler(partitionHandler);
}
if (splitter!=null) {
if (splitter != null) {
step.setStepExecutionSplitter(splitter);
} else {
}
else {
boolean allowStartIfComplete = isAllowStartIfComplete();
String name = stepName;
@@ -126,7 +202,7 @@ public class PartitionStepBuilder extends StepBuilderHelper<PartitionStepBuilder
}
if (aggregator!=null) {
if (aggregator != null) {
step.setStepExecutionAggregator(aggregator);
}
@@ -134,21 +210,11 @@ public class PartitionStepBuilder extends StepBuilderHelper<PartitionStepBuilder
step.afterPropertiesSet();
}
catch (Exception e) {
throw new IllegalStateException(e);
throw new StepBuilderException(e);
}
return step;
}
public PartitionStepBuilder splitter(StepExecutionSplitter splitter) {
this.splitter = splitter;
return this;
}
public PartitionStepBuilder aggregator(StepExecutionAggregator aggregator) {
this.aggregator = aggregator;
return this;
}
}

View File

@@ -42,8 +42,15 @@ import org.springframework.batch.repeat.support.RepeatTemplate;
import org.springframework.util.Assert;
/**
* Step builder for simple item processing (chunk oriented) steps. Items are read and cached in chunks, and then
* processed (transformed) and written (optionally either the processor or the writer can be omitted) all in the same
* transaction.
*
* @see FaultTolerantStepBuilder for a step that handles retry and skip of failed items
*
* @author Dave Syer
*
* @since 2.2
*/
public class SimpleStepBuilder<I, O> extends AbstractTaskletStepBuilder<SimpleStepBuilder<I, O>> {
@@ -65,10 +72,42 @@ public class SimpleStepBuilder<I, O> extends AbstractTaskletStepBuilder<SimpleSt
private boolean readerTransactionalQueue = false;
/**
* 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 SimpleStepBuilder(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 SimpleStepBuilder(SimpleStepBuilder<I, O> 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<I, O> faultTolerant() {
FaultTolerantStepBuilder<I, O> builder = new FaultTolerantStepBuilder<I, O>(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<I, O> extends AbstractTaskletStepBuilder<SimpleSt
return tasklet;
}
public SimpleStepBuilder<I, O> readerIsTransactionalQueue() {
this.readerTransactionalQueue = true;
return this;
}
public SimpleStepBuilder<I, O> listener(ItemReadListener<? super I> listener) {
itemListeners.add(listener);
return this;
}
public SimpleStepBuilder<I, O> listener(ItemWriteListener<? super O> listener) {
itemListeners.add(listener);
return this;
}
public SimpleStepBuilder<I, O> listener(ItemProcessListener<? super I, ? super O> listener) {
itemListeners.add(listener);
return this;
}
public SimpleStepBuilder<I, O> chunkOperations(RepeatOperations repeatTemplate) {
this.chunkOperations = repeatTemplate;
return this;
}
public SimpleStepBuilder<I, O> 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<I, O> 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<I, O> extends AbstractTaskletStepBuilder<SimpleSt
return this;
}
/**
* Sets a completion policy for the chunk processing. Items are read until this policy determines that a chunk is
* complete, giving more control than with just the {@link #chunk(int) chunk size} (or commit interval).
*
* @param completionPolicy a completion policy for the chunk
* @return this for fluent chaining
*/
public SimpleStepBuilder<I, O> 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<I, O> reader(ItemReader<? extends I> 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<I, O> writer(ItemWriter<? super O> 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<I, O> processor(ItemProcessor<? super I, ? extends O> 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<I, O> readerIsTransactionalQueue() {
this.readerTransactionalQueue = true;
return this;
}
/**
* Register an item reader listener.
*
* @param listener the listener to register
* @return this for fluent chaining
*/
public SimpleStepBuilder<I, O> listener(ItemReadListener<? super I> listener) {
itemListeners.add(listener);
return this;
}
/**
* Register an item writer listener.
*
* @param listener the listener to register
* @return this for fluent chaining
*/
public SimpleStepBuilder<I, O> listener(ItemWriteListener<? super O> listener) {
itemListeners.add(listener);
return this;
}
/**
* Register an item processor listener.
*
* @param listener the listener to register
* @return this for fluent chaining
*/
public SimpleStepBuilder<I, O> listener(ItemProcessListener<? super I, ? super O> 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<I, O> chunkOperations(RepeatOperations repeatTemplate) {
this.chunkOperations = repeatTemplate;
return this;
}
protected RepeatOperations createChunkOperations() {
RepeatOperations repeatOperations = chunkOperations;
if (repeatOperations == null) {

View File

@@ -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<StepBuilder> {
/**
* 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 <I, O> SimpleStepBuilder<I, O> chunk(int chunkSize) {
return new SimpleStepBuilder<I, O>(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 <I, O> SimpleStepBuilder<I, O> chunk(CompletionPolicy completionPolicy) {
return new SimpleStepBuilder<I, O>(this).completionPolicy(completionPolicy);
return new SimpleStepBuilder<I, O>(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);
}

View File

@@ -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) {

View File

@@ -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<B extends StepBuilderHelper<B>> {
@@ -42,8 +46,13 @@ public abstract class StepBuilderHelper<B extends StepBuilderHelper<B>> {
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<B> repository(JobRepository jobRepository) {
@@ -127,6 +136,17 @@ public abstract class StepBuilderHelper<B extends StepBuilderHelper<B>> {
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;
}

View File

@@ -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<TaskletStepBuilder> {
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;

View File

@@ -473,7 +473,7 @@ public class SimpleStepFactoryBean<T, S> 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);

View File

@@ -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<Object, Object> fb = new StepParserStepFactoryBean<Object, Object>();
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<Object, Object> fb = new StepParserStepFactoryBean<Object, Object>();
fb.setName("step");