Updated javadocs where necessary.

This commit is contained in:
lucasward
2009-04-08 18:39:21 +00:00
parent ea0d5ef95f
commit d6744b0be9
59 changed files with 273 additions and 77 deletions

View File

@@ -15,15 +15,41 @@
*/
package org.springframework.batch.core;
import org.springframework.batch.item.ItemProcessor;
/**
* Listener interface for the processing of an item. Implementations
* of this interface will be notified before and after an item is
* passed to the {@link ItemProcessor} and in the event of any
* exceptions thrown by the processor.
*
* @author Dave Syer
*
*/
public interface ItemProcessListener<T, S> extends StepListener {
/**
* Called before {@link ItemProcessor#process(Object)}.
*
* @param item to be processed.
*/
void beforeProcess(T item);
/**
* Called after {@link ItemProcessor#process(Object)} returns. If the
* processor returns null, this method will still be called, with
* a null result, allowing for notification of 'filtered' items.
*
* @param item to be processed
* @param result of processing
*/
void afterProcess(T item, S result);
/**
* Called if an exception was thrown from {@link ItemProcessor#process(Object)}.
*
* @param item attempted to be processed
* @param e - exception thrown during processing.
*/
void onProcessError(T item, Exception e);
}

View File

@@ -20,7 +20,9 @@ import java.util.List;
import org.springframework.batch.item.ItemWriter;
/**
* Listener interface around the writing of an item.
* Listener interface for the writing of items. Implementations
* of this interface will be notified before, after, and in case
* of any exception thrown while writing a list of items.
*
* @author Lucas Ward
*
@@ -35,9 +37,10 @@ public interface ItemWriteListener<S> extends StepListener {
void beforeWrite(List<? extends S> items);
/**
* Called after {@link ItemWriter#write(java.util.List)} If the item is last
* in a chunk, this will be called before any transaction is committed, and
* before {@link ChunkListener#afterChunk()}
* Called after {@link ItemWriter#write(java.util.List)} This will be
* called before any transaction is committed, and before
* {@link ChunkListener#afterChunk()}
*
* @param items written items
*/
void afterWrite(List<? extends S> items);

View File

@@ -162,9 +162,7 @@ public class JobParameters implements Serializable {
}
/**
* Get a map of all parameters, including string, long, and date. It should
* be noted that a Collections$UnmodifiableMap is returned, ensuring
* immutability.
* Get a map of all parameters, including string, long, and date.
*
* @return an unmodifiable map containing all parameters.
*/
@@ -173,7 +171,7 @@ public class JobParameters implements Serializable {
}
/**
* @return true if the prameters is empty, false otherwise.
* @return true if the parameters is empty, false otherwise.
*/
public boolean isEmpty(){
return parameters.isEmpty();

View File

@@ -23,16 +23,17 @@ import java.util.Map;
import org.springframework.util.Assert;
/**
* Helper class for creating {@link JobParameters}. Useful because of all
* {@link JobParameters} are immutable, and require 3 separate maps of the three
* supported types to ensure typesafety. Once created, it can be used in the
* Helper class for creating {@link JobParameters}. Useful because all
* {@link JobParameter} objects are immutable, and must be instantiated separately
* to ensure typesafety. Once created, it can be used in the
* same was a java.lang.StringBuilder (except, order is irrelevant), by adding
* various parameters types and creating a valid JobRuntimeParametres once
* various parameter types and creating a valid {@link JobParameters} once
* finished.
*
* @author Lucas Ward
* @since 1.0
* @see JobParameters
* @see JobParameter
*/
public class JobParametersBuilder {
@@ -67,7 +68,7 @@ public class JobParametersBuilder {
}
/**
* Add a new Date parameter for the given key.
* Add a new {@link Date} parameter for the given key.
*
* @param key - parameter accessor.
* @param parameter - runtime parameter

View File

@@ -27,10 +27,8 @@ import org.springframework.util.Assert;
/**
* Batch domain object representation the execution of a step. Unlike
* JobExecution, there are four additional properties: itemCount, commitCount,
* rollbackCount and execution context. These values represent how many items
* the step has processed, how many times it has been committed and rolled back,
* and any other information the developer wishes to store, respectively.
* {@link JobExecution}, there are additional properties related the
* processing of items such as commit count, etc.
*
* @author Lucas Ward
* @author Dave Syer

View File

@@ -26,8 +26,9 @@ package org.springframework.batch.core;
public interface StepExecutionListener extends StepListener {
/**
* Initialise the state of the listener with the {@link StepExecution} from
* Initialize the state of the listener with the {@link StepExecution} from
* the current scope.
*
* @param stepExecution
*/
void beforeStep(StepExecution stepExecution);

View File

@@ -16,8 +16,17 @@
package org.springframework.batch.core.configuration.support;
import org.springframework.batch.core.Job;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ConfigurableApplicationContext;
/**
* Factory for the creation of {@link ApplicationContext}s. This interface
* is primarily useful when creating a new {@link ApplicationContext} per
* execution of a {@link Job}.
*
* @author Lucas Ward
*/
public interface ApplicationContextFactory {
ConfigurableApplicationContext createApplicationContext();

View File

@@ -24,6 +24,13 @@ import org.springframework.context.support.AbstractXmlApplicationContext;
import org.springframework.core.io.Resource;
import org.springframework.util.Assert;
/**
* {@link ApplicationContextFactory} implementation that takes a parent context and a path
* to the context to create. Each time the createApplicationContext method is called, a new
* {@link ApplicationContext} will be returned. It should be noted that if a path isn't
* set, the parent will always be returned.
*
*/
public class ClassPathXmlApplicationContextFactory implements ApplicationContextFactory, ApplicationContextAware {
private ConfigurableApplicationContext parent;

View File

@@ -25,6 +25,7 @@ import org.springframework.batch.core.StepExecution;
/**
* @author Dave Syer
*
* @since 2.0
*/
public interface JobExplorer {

View File

@@ -32,6 +32,7 @@ import org.springframework.beans.factory.FactoryBean;
* @see MapJobExplorerFactoryBean
*
* @author Dave Syer
* @since 2.0
*/
public abstract class AbstractJobExplorerFactoryBean implements FactoryBean {

View File

@@ -43,6 +43,7 @@ import org.springframework.util.Assert;
* to describe what kind of database they are using.
*
* @author Dave Syer
* @since 2.0
*/
public class JobExplorerFactoryBean extends AbstractJobExplorerFactoryBean
implements InitializingBean {

View File

@@ -31,6 +31,7 @@ import org.springframework.beans.factory.FactoryBean;
* {@link SimpleJobExplorer} using in-memory DAO implementations.
*
* @author Dave Syer
* @since 2.0
*/
public class MapJobExplorerFactoryBean extends AbstractJobExplorerFactoryBean {

View File

@@ -29,10 +29,7 @@ import org.springframework.batch.core.repository.dao.JobInstanceDao;
import org.springframework.batch.core.repository.dao.StepExecutionDao;
/**
*
* <p>
* Implementation of {@link JobExplorer} using the injected DAOs.
* <p>
*
* @author Dave Syer
* @author Lucas Ward

View File

@@ -45,10 +45,10 @@ import org.springframework.util.Assert;
import org.springframework.util.ClassUtils;
/**
* Batch domain object representing a job. Job is an explicit abstraction
* representing the configuration of a job specified by a developer. It should
* be noted that restart policy is applied to the job as a whole and not to a
* step.
* Abstract implementation of the {@link Job} interface. Common dependencies such as a
* {@link JobRepository}, {@link JobExecutionListener}s, and various configuration
* parameters are set here. Therefore, common error handling and listener calling
* activities are abstracted away from implementations.
*
* @author Lucas Ward
* @author Dave Syer

View File

@@ -32,7 +32,8 @@ import org.springframework.batch.core.repository.JobRestartException;
/**
* Simple implementation of {@link Job} interface providing the ability to run a
* {@link JobExecution}. Sequentially executes a job by iterating through its
* list of steps.
* list of steps. Any {@link Step} that fails will fail the job. The job is
* considered complete when all steps have been executed.
*
* @author Lucas Ward
* @author Dave Syer

View File

@@ -19,7 +19,7 @@ import java.util.Collection;
/**
* @author Dave Syer
*
* @since 2.0
*/
public interface Flow {

View File

@@ -18,7 +18,7 @@ package org.springframework.batch.core.job.flow;
/**
* @author Dave Syer
*
* @since 2.0
*/
public class FlowExecution implements Comparable<FlowExecution> {

View File

@@ -27,7 +27,7 @@ import org.springframework.batch.core.repository.JobRestartException;
* its execution step by step.
*
* @author Dave Syer
*
* @since 2.0
*/
public interface FlowExecutor {

View File

@@ -20,6 +20,7 @@ import java.util.HashSet;
import org.springframework.batch.core.BatchStatus;
import org.springframework.batch.core.ExitStatus;
import org.springframework.batch.core.Job;
import org.springframework.batch.core.JobExecution;
import org.springframework.batch.core.JobExecutionException;
import org.springframework.batch.core.JobInterruptedException;
@@ -31,8 +32,13 @@ import org.springframework.batch.core.repository.JobRestartException;
import org.springframework.batch.core.step.StepHolder;
/**
* @author Dave Syer
* Implementation of the {@link Job} interface that allows for complex flows
* of steps, rather than requiring sequential execution. In general, this
* job implementation was designed to be used behind a parser, allowing for
* a namespace to abstract away details.
*
* @author Dave Syer
* @since 2.0
*/
public class FlowJob extends AbstractJob {

View File

@@ -19,8 +19,13 @@ import org.springframework.batch.core.JobExecution;
import org.springframework.batch.core.StepExecution;
/**
* @author Dave Syer
* Interface allowing for programmatic access to the decision on what the status
* of a flow should be. For example, if some condition that's stored in the
* database indicates that the job should stop for a manual check, a decider
* implementation could check that value to determine the status of the flow.
*
* @author Dave Syer
* @since 2.0
*/
public interface JobExecutionDecider {

View File

@@ -17,7 +17,7 @@ package org.springframework.batch.core.job.flow;
/**
* @author Dave Syer
*
* @since 2.0
*/
public interface State {

View File

@@ -43,7 +43,7 @@ import org.springframework.beans.factory.InitializingBean;
* if unambiguous.
*
* @author Dave Syer
*
* @since 2.0
*/
public class SimpleFlow implements Flow, InitializingBean {

View File

@@ -28,7 +28,7 @@ import org.springframework.util.StringUtils;
* execution of the originating State.
*
* @author Dave Syer
*
* @since 2.0
*/
public class StateTransition implements Comparable<StateTransition> {

View File

@@ -22,7 +22,7 @@ import org.springframework.batch.core.job.flow.State;
/**
* @author Dave Syer
*
* @since 2.0
*/
public abstract class AbstractState implements State {

View File

@@ -21,8 +21,10 @@ import org.springframework.batch.core.job.flow.FlowExecutor;
import org.springframework.batch.core.job.flow.JobExecutionDecider;
/**
* @author Dave Syer
* State that requires a decider to make the status decision.
*
* @author Dave Syer
* @since 2.0
*/
public class DecisionState extends AbstractState {

View File

@@ -25,7 +25,7 @@ import org.springframework.batch.core.job.flow.State;
* continuing if just starting.
*
* @author Dave Syer
*
* @since 2.0
*/
public class EndState extends AbstractState {

View File

@@ -25,7 +25,7 @@ import org.springframework.batch.core.job.flow.FlowExecutionStatus;
* single exit status.
*
* @author Dave Syer
*
* @since 2.0
*/
public interface FlowExecutionAggregator {

View File

@@ -22,8 +22,13 @@ import org.springframework.batch.core.job.flow.FlowExecution;
import org.springframework.batch.core.job.flow.FlowExecutionStatus;
/**
* @author Dave Syer
* Implementation of the {@link FlowExecutionAggregator} interface that aggregates
* {@link FlowExecutionStatus}', using the status with the high precedence as the
* aggregate status. See {@link FlowExecutionStatus} for details on status
* precedence.
*
* @author Dave Syer
* @since 2.0
*/
public class MaxValueFlowExecutionAggregator implements FlowExecutionAggregator {

View File

@@ -36,7 +36,7 @@ import org.springframework.core.task.TaskRejectedException;
* parallel subflows.
*
* @author Dave Syer
*
* @since 2.0
*/
public class SplitState extends AbstractState {

View File

@@ -27,7 +27,7 @@ import org.springframework.batch.core.step.StepHolder;
* execute the specified {@link Step}.
*
* @author Dave Syer
*
* @since 2.0
*/
public class StepState extends AbstractState implements StepHolder {

View File

@@ -18,8 +18,11 @@ package org.springframework.batch.core.launch;
import org.springframework.batch.core.JobExecutionException;
/**
* Execution indicating that a JobExecution that is not currently running has
* been requested to stop.
*
* @author Dave Syer
*
* @since 2.0
*/
public class JobExecutionNotRunningException extends JobExecutionException {

View File

@@ -42,7 +42,9 @@ public interface JobLauncher {
* always be returned by this method, regardless of whether or not the
* execution was successful. If there is a past {@link JobExecution} which
* has paused, the same {@link JobExecution} is returned instead of a new
* one created.
* one created. A exception will only be thrown if there is a failure to
* start the job. If the job encounters some error while processing, the
* JobExecution will be returned, and the status will need to be inspected.
*
* @return the {@link JobExecution} if it returns synchronously. If the
* implementation is asynchronous, the status might well be unknown.

View File

@@ -31,13 +31,13 @@ import org.springframework.batch.core.repository.JobInstanceAlreadyCompleteExcep
import org.springframework.batch.core.repository.JobRestartException;
/**
* A really low level interface for inspecting and controlling jobs with access
* Low level interface for inspecting and controlling jobs with access
* only to primitive and collection types. Suitable for a command-line client
* (e.g. that launches a new process for each operation), or a remote launcher
* like a JMX console.
*
* @author Dave Syer
*
* @since 2.0
*/
public interface JobOperator {

View File

@@ -28,8 +28,9 @@ package org.springframework.batch.core.launch.support;
public class JvmSystemExiter implements SystemExiter {
/**
* Delegate call to System.exit() with the argument provided. Do not use
* this at home children!
* Delegate call to System.exit() with the argument provided. This should only
* be used in a scenario where a particular status needs to be returned to
* a Batch scheduler.
*
* @see org.springframework.batch.core.launch.support.SystemExiter#exit(int)
*/

View File

@@ -33,6 +33,7 @@ import org.springframework.batch.core.JobParameters;
import org.springframework.batch.core.JobParametersIncrementer;
import org.springframework.batch.core.StepExecution;
import org.springframework.batch.core.UnexpectedJobExecutionException;
import org.springframework.batch.core.configuration.JobRegistry;
import org.springframework.batch.core.configuration.ListableJobRegistry;
import org.springframework.batch.core.converter.DefaultJobParametersConverter;
import org.springframework.batch.core.converter.JobParametersConverter;
@@ -55,9 +56,20 @@ import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.Assert;
/**
* Simple implementation of the JobOperator interface. Due to the amount of
* functionality the implementation is combining, the following dependencies
* are required:
*
* <ul>
* <li> {@link JobLauncher}
* <li> {@link JobExplorer}
* <li> {@link JobRepository}
* <li> {@link JobRegistry}
* </ul>
*
* @author Dave Syer
* @author Lucas Ward
*
* @since 2.0
*/
public class SimpleJobOperator implements JobOperator, InitializingBean {

View File

@@ -30,7 +30,7 @@ import org.springframework.batch.item.ExecutionContext;
* caller.
*
* @author Dave Syer
*
* @since 2.0
*/
public interface PartitionHandler {

View File

@@ -27,7 +27,7 @@ import org.springframework.batch.core.StepExecution;
* execution independent from the fabric they are going to run on.
*
* @author Dave Syer
*
* @since 2.0
*/
public interface StepExecutionSplitter {

View File

@@ -32,7 +32,7 @@ import org.springframework.util.Assert;
* ignored.
*
* @author Dave Syer
*
* @since 2.0
*/
public class MultiResourcePartitioner implements Partitioner {

View File

@@ -33,7 +33,7 @@ import org.springframework.util.Assert;
* load using a {@link PartitionHandler}.
*
* @author Dave Syer
*
* @since 2.0
*/
public class PartitionStep extends AbstractStep {

View File

@@ -27,7 +27,7 @@ import org.springframework.batch.item.ExecutionContext;
* key ranges, or a set of unique filenames.
*
* @author Dave Syer
*
* @since 2.0
*/
public interface Partitioner {

View File

@@ -28,7 +28,7 @@ import org.springframework.batch.item.ExecutionContext;
* size.
*
* @author Dave Syer
*
* @since 2.0
*/
public class SimplePartitioner implements Partitioner {

View File

@@ -42,7 +42,7 @@ import org.springframework.batch.item.ExecutionContext;
* <code>{step1:partition0, step1:partition1, ...}</code>.
*
* @author Dave Syer
*
* @since 2.0
*/
public class SimpleStepExecutionSplitter implements StepExecutionSplitter {

View File

@@ -28,7 +28,7 @@ import org.springframework.util.Assert;
* into a single result.
*
* @author Dave Syer
*
* @since 2.0
*/
public class StepExecutionAggregator {

View File

@@ -43,7 +43,7 @@ import org.springframework.util.Assert;
* filesystem scanning and copying.
*
* @author Dave Syer
*
* @since 2.0
*/
public class TaskExecutorPartitionHandler implements PartitionHandler, InitializingBean {

View File

@@ -29,7 +29,7 @@ import org.springframework.transaction.annotation.Isolation;
/**
* <p>
* Repository responsible for persistence of batch metadata entities.
* Repository responsible for persistence of batch meta-data entities.
* </p>
*
* @see JobInstance

View File

@@ -19,8 +19,7 @@ import java.sql.PreparedStatement;
import java.sql.SQLException;
import java.util.List;
import org.springframework.batch.core.JobParameters;
import org.springframework.batch.core.StepExecutionListener;
import org.springframework.batch.item.database.JdbcCursorItemReader;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.jdbc.core.PreparedStatementSetter;
import org.springframework.jdbc.core.SqlTypeValue;
@@ -28,9 +27,13 @@ import org.springframework.jdbc.core.StatementCreatorUtils;
import org.springframework.util.Assert;
/**
* Implementation of the {@link PreparedStatementSetter} interface that also
* implements {@link StepExecutionListener} and uses {@link JobParameters} to
* set the parameters on a PreparedStatement.
* Implementation of the {@link PreparedStatementSetter} interface that accepts
* a list of values to be set on a PreparedStatement. This is usually used in
* conjunction with the {@link JdbcCursorItemReader} to allow for the replacement
* of bind variables when generating the cursor. The order of the list will be
* used to determine the ordering of setting variables. For example, the first
* item in the list will be the first bind variable set. (i.e. it will
* correspond to the first '?' in the SQL statement)
*
* @author Lucas Ward
*
@@ -38,24 +41,24 @@ import org.springframework.util.Assert;
public class ListPreparedStatementSetter implements
PreparedStatementSetter, InitializingBean {
private List<?> parameterKeys;
private List<?> parameters;
public void setValues(PreparedStatement ps) throws SQLException {
for (int i = 0; i < parameterKeys.size(); i++) {
StatementCreatorUtils.setParameterValue(ps, i + 1, SqlTypeValue.TYPE_UNKNOWN, parameterKeys.get(i));
for (int i = 0; i < parameters.size(); i++) {
StatementCreatorUtils.setParameterValue(ps, i + 1, SqlTypeValue.TYPE_UNKNOWN, parameters.get(i));
}
}
/**
* The parameter names that will be pulled from the {@link JobParameters}.
* The parameter values that will be set on the PreparedStatement.
* It is assumed that their order in the List is the order of the parameters
* in the PreparedStatement.
*/
public void setParameters(List<?> parameterKeys) {
this.parameterKeys = parameterKeys;
public void setParameters(List<?> parameters) {
this.parameters = parameters;
}
public void afterPropertiesSet() throws Exception {
Assert.notNull(parameterKeys, "Parameters names must be provided");
Assert.notNull(parameters, "Parameters must be provided");
}
}

View File

@@ -70,7 +70,7 @@ import org.springframework.util.StringValueResolver;
* accessors provided as a convenience for step and job attributes.
*
* @author Dave Syer
*
* @since 2.0
*/
public class StepScope implements Scope, BeanFactoryPostProcessor, Ordered {

View File

@@ -7,7 +7,7 @@ import org.springframework.batch.core.Step;
* access to the underlying instance.
*
* @author Dave Syer
*
* @since 2.0
*/
public interface StepHolder {

View File

@@ -30,7 +30,7 @@ import java.util.List;
* skipped items are then available through the chunk.
*
* @author Dave Syer
*
* @since 2.0
*/
public class Chunk<W> implements Iterable<W> {

View File

@@ -29,7 +29,7 @@ import org.springframework.batch.item.ItemStreamException;
* and the wrapped {@link ItemStream}.
*
* @author Dave Syer
*
* @since 2.0
*/
class ChunkMonitor implements ItemStream {

View File

@@ -18,6 +18,11 @@ package org.springframework.batch.core.step.item;
import org.springframework.batch.core.StepContribution;
/**
* Interface defined for processing {@link Chunk}s.
*
* @since 2.0
*/
public interface ChunkProcessor<I> {
void process(StepContribution contribution, Chunk<I> chunk) throws Exception;

View File

@@ -18,6 +18,13 @@ package org.springframework.batch.core.step.item;
import org.springframework.batch.core.StepContribution;
/**
* Interface for providing {@link Chunk}s to be processed, used by the
* {@link ChunkOrientedTasklet}
*
* @since 2.0
* @see ChunkOrientedTasklet
*/
public interface ChunkProvider<T> {
Chunk<T> provide(StepContribution contribution) throws Exception;

View File

@@ -37,6 +37,11 @@ import org.springframework.batch.retry.RetryContext;
import org.springframework.batch.retry.RetryException;
import org.springframework.batch.retry.support.DefaultRetryState;
/**
* FaultTolerant implementation of the {@link ChunkProcessor} interface, that
* allows for skipping or retry of items that cause exceptions during writing.
*
*/
public class FaultTolerantChunkProcessor<I, O> extends SimpleChunkProcessor<I, O> {
private SkipPolicy itemProcessSkipPolicy = new LimitCheckingItemSkipPolicy(0);

View File

@@ -24,6 +24,12 @@ import org.springframework.batch.core.step.skip.SkipPolicy;
import org.springframework.batch.item.ItemReader;
import org.springframework.batch.repeat.RepeatOperations;
/**
* FaultTolerant implementation of the {@link ChunkProcessor} interface, that
* allows for skipping or retry of items that cause exceptions during reading
* or processing.
*
*/
public class FaultTolerantChunkProvider<I> extends SimpleChunkProvider<I> {
private SkipPolicy skipPolicy = new LimitCheckingItemSkipPolicy(0);

View File

@@ -16,6 +16,10 @@
package org.springframework.batch.core.step.item;
/**
* Interface for defining keys to uniquely identify items.
* this can be useful if the item itself cannot be modified to
* properly override equals.
*
* @author Dave Syer
*
*/

View File

@@ -9,7 +9,7 @@ import org.springframework.batch.item.UnexpectedInputException;
/**
* Convenience wrapper for an ItemReader that keeps track of how many items
* successfully processed.
* were successfully processed.
*/
class OffsetItemReader<T> implements ItemReader<T>, ItemStream {

View File

@@ -26,6 +26,12 @@ import org.springframework.batch.item.ItemWriter;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.util.Assert;
/**
* Simple implementation of the {@link ChunkProcessor} interface that handles basic
* item writing and processing. Any exceptions encountered will be rethrown.
*
* @see ChunkOrientedTasklet
*/
public class SimpleChunkProcessor<I, O> implements ChunkProcessor<I>, InitializingBean {
private ItemProcessor<? super I, ? extends O> itemProcessor;

View File

@@ -30,10 +30,11 @@ import org.springframework.batch.repeat.RepeatOperations;
import org.springframework.batch.repeat.RepeatStatus;
/**
* Simple implementation of the ChunkProvider interface that does basic
* chunk providing from an {@link ItemReader}.
*
* @author Dave Syer
*
* @param <I> input item type
* @see ChunkOrientedTasklet
*/
public class SimpleChunkProvider<I> implements ChunkProvider<I> {

View File

@@ -230,6 +230,40 @@ public class FaultTolerantStepFactoryBeanTests {
.getName()));
}
@Test
public void testProcessFilter() throws Exception {
reader = new SkipReaderStub(new String[] { "1", "2", "3", "4", "5" }, NO_FAILURES);
factory.setItemReader(reader);
writer = new SkipWriterStub(NO_FAILURES);
factory.setItemWriter(writer);
FilterProcessorStub processor = new FilterProcessorStub(Arrays.asList(new String[] { "4" }));
factory.setItemProcessor(processor);
ItemProcessListenerStub<String, String> listenerStub = new ItemProcessListenerStub<String, String>();
factory.setListeners(new StepListener[]{listenerStub});
Step step = (Step) factory.getObject();
step.execute(stepExecution);
assertEquals(0, stepExecution.getSkipCount());
assertEquals(0, stepExecution.getReadSkipCount());
assertEquals(5, stepExecution.getReadCount());
assertEquals(1, stepExecution.getFilterCount());
assertEquals(0, stepExecution.getRollbackCount());
assertTrue(listenerStub.isFilterEncountered());
// writer skips "4"
assertTrue(reader.processed.contains("4"));
assertFalse(writer.written.contains("4"));
List<String> expectedOutput = Arrays.asList(StringUtils.commaDelimitedListToStringArray("1,2,3,5"));
assertEquals(expectedOutput, writer.written);
assertEquals(BatchStatus.COMPLETED, stepExecution.getStatus());
assertStepExecutionsAreEqual(stepExecution, repository.getLastStepExecution(jobExecution.getJobInstance(), step
.getName()));
}
/**
* Check items causing errors are skipped as expected.
*/
@@ -683,6 +717,22 @@ public class FaultTolerantStepFactoryBeanTests {
}
}
private static class FilterProcessorStub implements ItemProcessor<String, String> {
private final Collection<String> failures;
public FilterProcessorStub(Collection<String> failures) {
this.failures = failures;
}
public String process(String item) throws Exception {
if (failures.contains(item)) {
return null;
}
return item;
}
}
/**
* Simple item reader that supports skip functionality.
@@ -725,6 +775,34 @@ public class FaultTolerantStepFactoryBeanTests {
}
}
private static class ItemProcessListenerStub<T,S> implements ItemProcessListener<T, S>{
private boolean errorEncountered = false;
private boolean filterEncountered = false;
public void afterProcess(T item, S result) {
if(result == null){
filterEncountered = true;
}
}
public void beforeProcess(T item) {
}
public void onProcessError(T item, Exception e) {
errorEncountered = true;
}
public boolean isErrorEncountered() {
return errorEncountered;
}
public boolean isFilterEncountered() {
return filterEncountered;
}
}
/**
* Simple item writer that supports skip functionality.