RESOLVED - issue BATCH-1372: Namespace support for partitioning

Also tidied up (changed) construction pattern for SimpleStepExecutionSplitter, so it doesn't need a step instance.
This commit is contained in:
dsyer
2009-12-09 07:36:41 +00:00
parent 743a5f84fe
commit 8e1f054bd2
13 changed files with 625 additions and 188 deletions

View File

@@ -1,7 +1,7 @@
<?xml version="1.0" encoding="UTF-8"?>
<beansProjectDescription>
<version>1</version>
<pluginVersion><![CDATA[2.2.7.200910202224-RELEASE]]></pluginVersion>
<pluginVersion><![CDATA[2.2.8.200911091054-RELEASE]]></pluginVersion>
<configSuffixes>
<configSuffix><![CDATA[xml]]></configSuffix>
</configSuffixes>
@@ -94,6 +94,13 @@
<config>src/test/resources/org/springframework/batch/core/scope/StepScopeProxyTargetClassIntegrationTests-context.xml</config>
<config>src/test/resources/org/springframework/batch/core/configuration/xml/StopAndRestartFailedJobParserTests-context.xml</config>
<config>src/test/resources/org/springframework/batch/core/launch/support/test-environment-with-registry-and-auto-register.xml</config>
<config>src/test/resources/org/springframework/batch/core/configuration/xml/ChunkElementIllegalAttributeParserTests-context.xml</config>
<config>src/test/resources/org/springframework/batch/core/configuration/xml/ChunkElementIllegalTransactionalAttributeParserTests-context.xml</config>
<config>src/test/resources/org/springframework/batch/core/configuration/xml/ChunkElementSimpleAttributeParserTests-context.xml</config>
<config>src/test/resources/org/springframework/batch/core/configuration/xml/ChunkElementTransactionalAttributeParserTests-context.xml</config>
<config>src/test/resources/org/springframework/batch/core/configuration/xml/FlowStepParserTests-context.xml</config>
<config>src/test/resources/org/springframework/batch/core/configuration/xml/JobParserWrongSchemaInRootTests-context.xml</config>
<config>src/test/resources/org/springframework/batch/core/configuration/xml/PartitionStepParserTests-context.xml</config>
</configs>
<configSets>
<configSet>

View File

@@ -21,6 +21,7 @@ import org.springframework.beans.BeanMetadataElement;
import org.springframework.beans.MutablePropertyValues;
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.beans.factory.config.RuntimeBeanReference;
import org.springframework.beans.factory.config.TypedStringValue;
import org.springframework.beans.factory.parsing.CompositeComponentDefinition;
import org.springframework.beans.factory.support.AbstractBeanDefinition;
import org.springframework.beans.factory.support.BeanDefinitionBuilder;
@@ -58,6 +59,20 @@ public abstract class AbstractStepParser {
private static final String TASKLET_ELE = "tasklet";
private static final String PARTITION_ELE = "partition";
private static final String STEP_ATTR = "step";
private static final String PARTITIONER_ATTR = "partitioner";
private static final String HANDLER_ATTR = "handler";
private static final String HANDLER_ELE = "handler";
private static final String TASK_EXECUTOR_ATTR = "task-executor";
private static final String GRID_SIZE_ATTR = "grid-size";
private static final String FLOW_ELE = "flow";
private static final String CHUNK_ELE = "chunk";
@@ -86,17 +101,23 @@ public abstract class AbstractStepParser {
AbstractBeanDefinition bd = builder.getRawBeanDefinition();
Element taskletElement = DomUtils.getChildElementByTagName(stepElement, TASKLET_ELE);
if (taskletElement!=null) {
if (taskletElement != null) {
boolean stepUnderspecified = CoreNamespaceUtils.isUnderspecified(stepElement);
parseTasklet(stepElement, taskletElement, bd, parserContext, stepUnderspecified);
}
Element flowElement = DomUtils.getChildElementByTagName(stepElement, FLOW_ELE);
if (flowElement!=null) {
if (flowElement != null) {
boolean stepUnderspecified = CoreNamespaceUtils.isUnderspecified(stepElement);
parseFlow(stepElement, flowElement, bd, parserContext, stepUnderspecified);
}
Element partitionElement = DomUtils.getChildElementByTagName(stepElement, PARTITION_ELE);
if (partitionElement != null) {
boolean stepUnderspecified = CoreNamespaceUtils.isUnderspecified(stepElement);
parsePartition(stepElement, partitionElement, bd, parserContext, stepUnderspecified);
}
String parentRef = stepElement.getAttribute(PARENT_ATTR);
if (StringUtils.hasText(parentRef)) {
bd.setParentName(parentRef);
@@ -125,6 +146,47 @@ public abstract class AbstractStepParser {
}
private void parsePartition(Element stepElement, Element partitionElement, AbstractBeanDefinition bd,
ParserContext parserContext, boolean stepUnderspecified) {
bd.setBeanClass(StepParserStepFactoryBean.class);
bd.setAttribute("isNamespaceStep", true);
String stepRef = partitionElement.getAttribute(STEP_ATTR);
String partitionerRef = partitionElement.getAttribute(PARTITIONER_ATTR);
String handlerRef = partitionElement.getAttribute(HANDLER_ATTR);
if (!StringUtils.hasText(stepRef)) {
parserContext.getReaderContext().error("You must specify a step", partitionElement);
return;
}
if (!StringUtils.hasText(partitionerRef)) {
parserContext.getReaderContext().error("You must specify a partitioner", partitionElement);
return;
}
MutablePropertyValues propertyValues = bd.getPropertyValues();
propertyValues.addPropertyValue("step", new RuntimeBeanReference(stepRef));
propertyValues.addPropertyValue("partitioner", new RuntimeBeanReference(partitionerRef));
if (!StringUtils.hasText(handlerRef)) {
Element handlerElement = DomUtils.getChildElementByTagName(partitionElement, HANDLER_ELE);
if (handlerElement != null) {
String taskExecutorRef = partitionElement.getAttribute(TASK_EXECUTOR_ATTR);
if (StringUtils.hasText(taskExecutorRef)) {
propertyValues.addPropertyValue("taskExecutor", new RuntimeBeanReference(taskExecutorRef));
}
String gridSize = partitionElement.getAttribute(GRID_SIZE_ATTR);
if (StringUtils.hasText(gridSize)) {
propertyValues.addPropertyValue("gridSize", new TypedStringValue(gridSize));
}
}
}
else {
propertyValues.addPropertyValue("partitionHandler", new RuntimeBeanReference(handlerRef));
}
}
private void parseTasklet(Element stepElement, Element taskletElement, AbstractBeanDefinition bd,
ParserContext parserContext, boolean stepUnderspecified) {
@@ -182,7 +244,7 @@ public abstract class AbstractStepParser {
}
bd.getPropertyValues().addPropertyValue("flow", flowDefinition);
}
private void validateTaskletAttributesAndSubelements(Element taskletElement, ParserContext parserContext,
@@ -328,7 +390,7 @@ public abstract class AbstractStepParser {
if (StringUtils.hasText(allowStartIfComplete)) {
propertyValues.addPropertyValue("allowStartIfComplete", allowStartIfComplete);
}
String taskExecutorBeanId = taskletElement.getAttribute("task-executor");
String taskExecutorBeanId = taskletElement.getAttribute(TASK_EXECUTOR_ATTR);
if (StringUtils.hasText(taskExecutorBeanId)) {
RuntimeBeanReference taskExecutorRef = new RuntimeBeanReference(taskExecutorBeanId);
propertyValues.addPropertyValue("taskExecutor", taskExecutorRef);

View File

@@ -26,7 +26,14 @@ import org.springframework.batch.core.StepExecutionListener;
import org.springframework.batch.core.StepListener;
import org.springframework.batch.core.job.flow.Flow;
import org.springframework.batch.core.job.flow.FlowStep;
import org.springframework.batch.core.partition.PartitionHandler;
import org.springframework.batch.core.partition.support.PartitionStep;
import org.springframework.batch.core.partition.support.Partitioner;
import org.springframework.batch.core.partition.support.SimplePartitioner;
import org.springframework.batch.core.partition.support.SimpleStepExecutionSplitter;
import org.springframework.batch.core.partition.support.TaskExecutorPartitionHandler;
import org.springframework.batch.core.repository.JobRepository;
import org.springframework.batch.core.step.AbstractStep;
import org.springframework.batch.core.step.item.FaultTolerantStepFactoryBean;
import org.springframework.batch.core.step.item.SimpleStepFactoryBean;
import org.springframework.batch.core.step.tasklet.Tasklet;
@@ -42,6 +49,7 @@ import org.springframework.batch.retry.RetryListener;
import org.springframework.batch.retry.policy.MapRetryContextCache;
import org.springframework.beans.factory.BeanNameAware;
import org.springframework.beans.factory.FactoryBean;
import org.springframework.core.task.SyncTaskExecutor;
import org.springframework.core.task.TaskExecutor;
import org.springframework.transaction.PlatformTransactionManager;
import org.springframework.transaction.annotation.Isolation;
@@ -81,12 +89,25 @@ class StepParserStepFactoryBean<I, O> implements FactoryBean, BeanNameAware {
private Tasklet tasklet;
private PlatformTransactionManager transactionManager;
//
// Floe Elements
// Flow Elements
//
private Flow flow;
//
// Partition Elements
//
private Partitioner partitioner;
private static final int DEFAULT_GRID_SIZE = 6;
private Step step;
private PartitionHandler partitionHandler;
private int gridSize = DEFAULT_GRID_SIZE;
//
// Tasklet Elements
//
@@ -176,12 +197,62 @@ class StepParserStepFactoryBean<I, O> implements FactoryBean, BeanNameAware {
configureFlowStep(ts);
return ts;
}
else if (step != null) {
PartitionStep ts = new PartitionStep();
configurePartitionStep(ts);
return ts;
}
else {
throw new IllegalStateException("Step [" + name
+ "] has neither a <chunk/> element nor a 'ref' attribute referencing a Tasklet.");
}
}
private void configureAbstractStep(AbstractStep ts) {
if (name != null) {
ts.setName(name);
}
if (allowStartIfComplete != null) {
ts.setAllowStartIfComplete(allowStartIfComplete);
}
if (jobRepository != null) {
ts.setJobRepository(jobRepository);
}
if (startLimit != null) {
ts.setStartLimit(startLimit);
}
if (listeners != null) {
int i = 0;
StepExecutionListener[] newListeners = new StepExecutionListener[listeners.length];
for (StepListener listener : listeners) {
newListeners[i++] = (StepExecutionListener) listener;
}
ts.setStepExecutionListeners(newListeners);
}
}
private void configurePartitionStep(PartitionStep ts) {
Assert.state(partitioner != null, "A Partitioner must be provided for a partition step");
Assert.state(step != null, "A Step must be provided for a partition step");
configureAbstractStep(ts);
if (partitionHandler != null) {
ts.setPartitionHandler(partitionHandler);
}
else {
TaskExecutorPartitionHandler partitionHandler = new TaskExecutorPartitionHandler();
partitionHandler.setStep(step);
if (taskExecutor == null) {
taskExecutor = new SyncTaskExecutor();
}
partitionHandler.setGridSize(gridSize);
partitionHandler.setTaskExecutor(taskExecutor);
ts.setPartitionHandler(partitionHandler);
}
SimpleStepExecutionSplitter splitter = new SimpleStepExecutionSplitter(jobRepository, step,
new SimplePartitioner());
ts.setStepExecutionSplitter(splitter);
}
private void configureSimple(SimpleStepFactoryBean<I, O> fb) {
if (name != null) {
fb.setBeanName(name);
@@ -271,32 +342,13 @@ class StepParserStepFactoryBean<I, O> implements FactoryBean, BeanNameAware {
@SuppressWarnings("serial")
private void configureTaskletStep(TaskletStep ts) {
if (name != null) {
ts.setName(name);
}
if (allowStartIfComplete != null) {
ts.setAllowStartIfComplete(allowStartIfComplete);
}
if (jobRepository != null) {
ts.setJobRepository(jobRepository);
}
if (startLimit != null) {
ts.setStartLimit(startLimit);
}
configureAbstractStep(ts);
if (tasklet != null) {
ts.setTasklet(tasklet);
}
if (transactionManager != null) {
ts.setTransactionManager(transactionManager);
}
if (listeners != null) {
int i = 0;
StepExecutionListener[] newListeners = new StepExecutionListener[listeners.length];
for (StepListener listener : listeners) {
newListeners[i++] = (StepExecutionListener) listener;
}
ts.setStepExecutionListeners(newListeners);
}
if (transactionTimeout != null || propagation != null || isolation != null
|| noRollbackExceptionClasses != null) {
DefaultTransactionAttribute attribute = new DefaultTransactionAttribute();
@@ -323,42 +375,27 @@ class StepParserStepFactoryBean<I, O> implements FactoryBean, BeanNameAware {
@SuppressWarnings("serial")
private void configureFlowStep(FlowStep ts) {
if (name != null) {
ts.setName(name);
}
if (allowStartIfComplete != null) {
ts.setAllowStartIfComplete(allowStartIfComplete);
}
if (jobRepository != null) {
ts.setJobRepository(jobRepository);
}
if (startLimit != null) {
ts.setStartLimit(startLimit);
}
configureAbstractStep(ts);
if (flow != null) {
ts.setFlow(flow);
}
if (listeners != null) {
int i = 0;
StepExecutionListener[] newListeners = new StepExecutionListener[listeners.length];
for (StepListener listener : listeners) {
newListeners[i++] = (StepExecutionListener) listener;
}
ts.setStepExecutionListeners(newListeners);
}
}
private void validateFaultTolerantSettings() {
validateDependency("skippable-exception-classes", skippableExceptionClasses, "skip-limit", skipLimit, true);
validateDependency("retryable-exception-classes", retryableExceptionClasses, "retry-limit", retryLimit, true);
validateAtLeastOneDependency("processor-transactional", processorTransactional, "'retry-limit' or 'skip-limit'", retryLimit, skipLimit);
validateAtLeastOneDependency("processor-transactional", processorTransactional,
"'retry-limit' or 'skip-limit'", retryLimit, skipLimit);
validateDependency("retry-listeners", retryListeners, "retry-limit", retryLimit, false);
if (isPresent(processorTransactional) && !processorTransactional && isPresent(readerTransactionalQueue) && readerTransactionalQueue) {
throw new IllegalArgumentException("The field 'processor-transactional' cannot be false if 'reader-transactional-queue' is true");
if (isPresent(processorTransactional) && !processorTransactional && isPresent(readerTransactionalQueue)
&& readerTransactionalQueue) {
throw new IllegalArgumentException(
"The field 'processor-transactional' cannot be false if 'reader-transactional-queue' is true");
}
}
private void validateAtLeastOneDependency(String dependantName, Boolean dependantValue, String name, Object... values) {
private void validateAtLeastOneDependency(String dependantName, Boolean dependantValue, String name,
Object... values) {
boolean oneIsPresent = false;
for (Object value : values) {
if (isPresent(value)) {
@@ -427,7 +464,7 @@ class StepParserStepFactoryBean<I, O> implements FactoryBean, BeanNameAware {
this.name = name;
}
}
// =========================================================
// Flow Attributes
// =========================================================
@@ -439,6 +476,38 @@ class StepParserStepFactoryBean<I, O> implements FactoryBean, BeanNameAware {
this.flow = flow;
}
// =========================================================
// Partition Attributes
// =========================================================
/**
* @param partitioner the partitioner to set
*/
public void setPartitioner(Partitioner partitioner) {
this.partitioner = partitioner;
}
/**
* @param partitionHandler the partitionHandler to set
*/
public void setPartitionHandler(PartitionHandler partitionHandler) {
this.partitionHandler = partitionHandler;
}
/**
* @param gridSize the gridSize to set
*/
public void setGridSize(int gridSize) {
this.gridSize = gridSize;
}
/**
* @param step the step to set
*/
public void setStep(Step step) {
this.step = step;
}
// =========================================================
// Tasklet Attributes
// =========================================================

View File

@@ -30,6 +30,8 @@ import org.springframework.batch.core.StepExecution;
import org.springframework.batch.core.partition.StepExecutionSplitter;
import org.springframework.batch.core.repository.JobRepository;
import org.springframework.batch.item.ExecutionContext;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.util.Assert;
/**
* Generic implementation of {@link StepExecutionSplitter} that delegates to a
@@ -44,20 +46,22 @@ import org.springframework.batch.item.ExecutionContext;
* @author Dave Syer
* @since 2.0
*/
public class SimpleStepExecutionSplitter implements StepExecutionSplitter {
public class SimpleStepExecutionSplitter implements StepExecutionSplitter, InitializingBean {
private static final String STEP_NAME_SEPARATOR = ":";
private final String stepName;
private String stepName;
private final Partitioner partitioner;
private Partitioner partitioner;
private final Step step;
private boolean allowStartIfComplete = false;
private final JobRepository jobRepository;
private JobRepository jobRepository;
public SimpleStepExecutionSplitter(JobRepository jobRepository, Step step) {
this(jobRepository, step, new SimplePartitioner());
/**
* Default constructor for convenience in configuration.
*/
public SimpleStepExecutionSplitter() {
}
/**
@@ -65,17 +69,73 @@ public class SimpleStepExecutionSplitter implements StepExecutionSplitter {
* properties.
*
* @param jobRepository the {@link JobRepository}
* @param step the target step (a local version of it)
* @param step the target step (a local version of it), used to extract the
* name and allowStartIfComplete flags
* @param partitioner a {@link Partitioner} to use for generating input
* parameters
*/
public SimpleStepExecutionSplitter(JobRepository jobRepository, Step step, Partitioner partitioner) {
this.jobRepository = jobRepository;
this.step = step;
this.allowStartIfComplete = step.isAllowStartIfComplete();
this.partitioner = partitioner;
this.stepName = step.getName();
}
/**
* Check mandatory properties (step name, job repository and partitioner).
*
* @see InitializingBean#afterPropertiesSet()
*/
public void afterPropertiesSet() throws Exception {
Assert.state(jobRepository != null, "A JobRepository is required");
Assert.state(stepName != null, "A step name is required");
Assert.state(partitioner != null, "A Partitioner is required");
}
/**
* Flag to indicate that the partition target step is allowed to start if an
* execution is complete. Should be the same as the value that would be
* returned by the {@link Step} itself from its own properties. Defaults to
* false.
*
* @see Step#isAllowStartIfComplete()
*
* @param allowStartIfComplete the value to set
*/
public void setAllowStartIfComplete(boolean allowStartIfComplete) {
this.allowStartIfComplete = allowStartIfComplete;
}
/**
* The job repository that will be used to manage the persistence of the
* delegate step executions.
*
* @param jobRepository the JobRepository to set
*/
public void setJobRepository(JobRepository jobRepository) {
this.jobRepository = jobRepository;
}
/**
* The {@link Partitioner} that will be used to generate step execution meta
* data for the target step.
*
* @param partitioner the partitioner to set
*/
public void setPartitioner(Partitioner partitioner) {
this.partitioner = partitioner;
}
/**
* The name of the target step that will be executed across the partitions.
* Mandatory with no default.
*
* @param stepName the step name to set
*/
public void setStepName(String stepName) {
this.stepName = stepName;
}
/**
* @see StepExecutionSplitter#getStepName()
*/
@@ -144,11 +204,12 @@ public class SimpleStepExecutionSplitter implements StepExecutionSplitter {
stepExecution.setExecutionContext(context);
}
return shouldStart(step, lastStepExecution) || isRestart;
return shouldStart(allowStartIfComplete, lastStepExecution) || isRestart;
}
private boolean shouldStart(Step step, StepExecution lastStepExecution) throws JobExecutionException {
private boolean shouldStart(boolean allowStartIfComplete, StepExecution lastStepExecution)
throws JobExecutionException {
if (lastStepExecution == null) {
return true;
@@ -162,7 +223,7 @@ public class SimpleStepExecutionSplitter implements StepExecutionSplitter {
+ "so it may be dangerous to proceed. " + "Manual intervention is probably necessary.");
}
if (stepStatus == BatchStatus.COMPLETED && step.isAllowStartIfComplete() == false) {
if (stepStatus == BatchStatus.COMPLETED && !allowStartIfComplete) {
// step is complete, false should be returned, indicating that the
// step should not be started
return false;

View File

@@ -107,34 +107,13 @@
</xsd:documentation>
</xsd:annotation>
<xsd:complexType>
<xsd:sequence>
<xsd:element ref="description" minOccurs="0" />
<xsd:choice minOccurs="0" maxOccurs="1">
<xsd:element name="tasklet" type="taskletType" />
<xsd:element name="flow">
<xsd:complexType>
<xsd:attribute name="parent" type="xsd:string" use="required">
<xsd:annotation>
<xsd:documentation
source="java:org.springframework.batch.core.job.flow.Flow"><![CDATA[
The flow that will execute in this step.
]]></xsd:documentation>
<xsd:appinfo>
<tool:annotation kind="parent">
<tool:expected-type
type="org.springframework.batch.core.job.flow.Flow" />
</tool:annotation>
</xsd:appinfo>
</xsd:annotation>
</xsd:attribute>
</xsd:complexType>
</xsd:element>
</xsd:choice>
</xsd:sequence>
<xsd:attribute name="id" type="xsd:ID" use="required" />
<xsd:attributeGroup ref="parentAttribute" />
<xsd:attributeGroup ref="abstractAttribute" />
<xsd:attributeGroup ref="jobRepositoryAttribute" />
<xsd:complexContent>
<xsd:extension base="stepType">
<xsd:attribute name="id" type="xsd:ID" use="required" />
<xsd:attributeGroup ref="abstractAttribute" />
<xsd:attributeGroup ref="jobRepositoryAttribute" />
</xsd:extension>
</xsd:complexContent>
</xsd:complexType>
</xsd:element>
@@ -295,34 +274,12 @@
</xsd:documentation>
</xsd:annotation>
<xsd:complexType>
<xsd:sequence>
<xsd:element ref="description" minOccurs="0" />
<xsd:choice minOccurs="0" maxOccurs="1">
<xsd:element name="tasklet" type="taskletType" />
<xsd:element name="flow">
<xsd:complexType>
<xsd:attribute name="parent" type="xsd:string" use="required">
<xsd:annotation>
<xsd:documentation
source="java:org.springframework.batch.core.job.flow.Flow"><![CDATA[
The flow that will execute in this step.
]]></xsd:documentation>
<xsd:appinfo>
<tool:annotation kind="parent">
<tool:expected-type
type="org.springframework.batch.core.job.flow.Flow" />
</tool:annotation>
</xsd:appinfo>
</xsd:annotation>
</xsd:attribute>
</xsd:complexType>
</xsd:element>
</xsd:choice>
<xsd:group ref="transitions" minOccurs="0" maxOccurs="unbounded" />
</xsd:sequence>
<xsd:attribute name="id" type="xsd:ID" use="required" />
<xsd:attributeGroup ref="parentAttribute" />
<xsd:attributeGroup ref="nextAttribute" />
<xsd:complexContent>
<xsd:extension base="stepType">
<xsd:attribute name="id" type="xsd:ID" use="required" />
<xsd:attributeGroup ref="nextAttribute" />
</xsd:extension>
</xsd:complexContent>
</xsd:complexType>
</xsd:element>
<xsd:element name="split">
@@ -343,7 +300,8 @@
</xsd:annotation>
<xsd:complexType>
<xsd:group ref="flowGroup" minOccurs="0" maxOccurs="unbounded" />
<xsd:attribute name="parent" type="xsd:string" use="optional">
<xsd:attribute name="parent" type="xsd:string"
use="optional">
<xsd:annotation>
<xsd:documentation
source="java:org.springframework.batch.core.job.flow.Flow"><![CDATA[
@@ -439,6 +397,109 @@
</xsd:choice>
</xsd:group>
<xsd:complexType name="stepType">
<xsd:sequence>
<xsd:element ref="description" minOccurs="0" />
<xsd:choice minOccurs="0" maxOccurs="1">
<xsd:element name="tasklet" type="taskletType" />
<xsd:element name="partition" type="partitionType" />
<xsd:element name="flow">
<xsd:complexType>
<xsd:attribute name="parent" type="xsd:string" use="required">
<xsd:annotation>
<xsd:documentation
source="java:org.springframework.batch.core.job.flow.Flow"><![CDATA[
The flow that will execute in this step.
]]></xsd:documentation>
<xsd:appinfo>
<tool:annotation kind="parent">
<tool:expected-type
type="org.springframework.batch.core.job.flow.Flow" />
</tool:annotation>
</xsd:appinfo>
</xsd:annotation>
</xsd:attribute>
</xsd:complexType>
</xsd:element>
</xsd:choice>
<xsd:group ref="transitions" minOccurs="0" maxOccurs="unbounded" />
</xsd:sequence>
<xsd:attributeGroup ref="parentAttribute" />
</xsd:complexType>
<xsd:complexType name="partitionType">
<xsd:all>
<xsd:element name="handler" minOccurs="0" maxOccurs="1">
<xsd:annotation>
<xsd:documentation><![CDATA[
Inline specification of a simple TaskExecutorPartitionHandler]]>
</xsd:documentation>
</xsd:annotation>
<xsd:complexType>
<xsd:attribute name="task-executor">
<xsd:annotation>
<xsd:documentation><![CDATA[
Reference to a TaskExecutor]]>
</xsd:documentation>
<xsd:appinfo>
<tool:annotation kind="ref">
<tool:expected-type
type="org.springframework.core.task.TaskExecutor" />
</tool:annotation>
</xsd:appinfo>
</xsd:annotation>
</xsd:attribute>
<xsd:attribute name="grid-size">
<xsd:annotation>
<xsd:documentation><![CDATA[
Grid size for the handler. Defaults to 6.]]>
</xsd:documentation>
<xsd:appinfo>
<tool:annotation kind="direct"/>
</xsd:appinfo>
</xsd:annotation>
</xsd:attribute>
</xsd:complexType>
</xsd:element>
</xsd:all>
<xsd:attribute name="handler">
<xsd:annotation>
<xsd:documentation><![CDATA[
Reference to a PartitionHandler]]>
</xsd:documentation>
<xsd:appinfo>
<tool:annotation kind="ref">
<tool:expected-type type="org.springframework.core.partition.PartitionHandler" />
</tool:annotation>
</xsd:appinfo>
</xsd:annotation>
</xsd:attribute>
<xsd:attribute name="partitioner">
<xsd:annotation>
<xsd:documentation><![CDATA[
Reference to a Partitioner]]>
</xsd:documentation>
<xsd:appinfo>
<tool:annotation kind="ref">
<tool:expected-type type="org.springframework.core.partition.support.Partitioner" />
</tool:annotation>
</xsd:appinfo>
</xsd:annotation>
</xsd:attribute>
<xsd:attribute name="step">
<xsd:annotation>
<xsd:documentation><![CDATA[
Reference to a Step]]>
</xsd:documentation>
<xsd:appinfo>
<tool:annotation kind="ref">
<tool:expected-type type="org.springframework.core.Step" />
</tool:annotation>
</xsd:appinfo>
</xsd:annotation>
</xsd:attribute>
</xsd:complexType>
<xsd:complexType name="taskletType">
<xsd:all>
<xsd:element name="chunk" type="chunkTaskletType"

View File

@@ -0,0 +1,99 @@
/*
* Copyright 2006-2007 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.configuration.xml;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.batch.core.BatchStatus;
import org.springframework.batch.core.Job;
import org.springframework.batch.core.JobExecution;
import org.springframework.batch.core.JobParameters;
import org.springframework.batch.core.StepExecution;
import org.springframework.batch.core.repository.JobRepository;
import org.springframework.batch.core.repository.support.MapJobRepositoryFactoryBean;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
/**
* @author Dave Syer
*
*/
@ContextConfiguration
@RunWith(SpringJUnit4ClassRunner.class)
public class PartitionStepParserTests {
@Autowired
@Qualifier("job1")
private Job job1;
@Autowired
@Qualifier("job2")
private Job job2;
@Autowired
private JobRepository jobRepository;
@Autowired
private MapJobRepositoryFactoryBean mapJobRepositoryFactoryBean;
@Before
public void setUp() {
mapJobRepositoryFactoryBean.clear();
}
@Test
public void testDefaultHandlerStep() throws Exception {
assertNotNull(job1);
JobExecution jobExecution = jobRepository.createJobExecution(job1.getName(), new JobParameters());
job1.execute(jobExecution);
assertEquals(BatchStatus.COMPLETED, jobExecution.getStatus());
List<String> stepNames = getStepNames(jobExecution);
assertEquals(7, stepNames.size());
assertEquals("[s1, step1:partition0, step1:partition1, step1:partition2, step1:partition3, step1:partition4, step1:partition5]", stepNames.toString());
}
@Test
public void testHandlerRefStep() throws Exception {
assertNotNull(job2);
JobExecution jobExecution = jobRepository.createJobExecution(job2.getName(), new JobParameters());
job2.execute(jobExecution);
assertEquals(BatchStatus.COMPLETED, jobExecution.getStatus());
List<String> stepNames = getStepNames(jobExecution);
assertEquals(3, stepNames.size());
assertEquals("[s2, s3, step1:partition0]", stepNames.toString());
}
private List<String> getStepNames(JobExecution jobExecution) {
List<String> list = new ArrayList<String>();
for (StepExecution stepExecution : jobExecution.getStepExecutions()) {
list.add(stepExecution.getStepName());
}
Collections.sort(list);
return list;
}
}

View File

@@ -56,7 +56,7 @@ public class PartitionStepTests {
@Test
public void testVanillaStepExecution() throws Exception {
step.setStepExecutionSplitter(new SimpleStepExecutionSplitter(jobRepository, remote));
step.setStepExecutionSplitter(new SimpleStepExecutionSplitter(jobRepository, remote, new SimplePartitioner()));
step.setPartitionHandler(new PartitionHandler() {
public Collection<StepExecution> handle(StepExecutionSplitter stepSplitter, StepExecution stepExecution)
throws Exception {
@@ -80,7 +80,7 @@ public class PartitionStepTests {
@Test
public void testFailedStepExecution() throws Exception {
step.setStepExecutionSplitter(new SimpleStepExecutionSplitter(jobRepository, remote));
step.setStepExecutionSplitter(new SimpleStepExecutionSplitter(jobRepository, remote, new SimplePartitioner()));
step.setPartitionHandler(new PartitionHandler() {
public Collection<StepExecution> handle(StepExecutionSplitter stepSplitter, StepExecution stepExecution)
throws Exception {
@@ -104,7 +104,7 @@ public class PartitionStepTests {
@Test
public void testStoppedStepExecution() throws Exception {
step.setStepExecutionSplitter(new SimpleStepExecutionSplitter(jobRepository, remote));
step.setStepExecutionSplitter(new SimpleStepExecutionSplitter(jobRepository, remote, new SimplePartitioner()));
step.setPartitionHandler(new PartitionHandler() {
public Collection<StepExecution> handle(StepExecutionSplitter stepSplitter, StepExecution stepExecution)
throws Exception {
@@ -135,7 +135,7 @@ public class PartitionStepTests {
result.getExecutionContext().put("aggregated", true);
}
});
step.setStepExecutionSplitter(new SimpleStepExecutionSplitter(jobRepository, remote));
step.setStepExecutionSplitter(new SimpleStepExecutionSplitter(jobRepository, remote, new SimplePartitioner()));
step.setPartitionHandler(new PartitionHandler() {
public Collection<StepExecution> handle(StepExecutionSplitter stepSplitter, StepExecution stepExecution)
throws Exception {

View File

@@ -1,6 +1,7 @@
package org.springframework.batch.core.partition.support;
import static org.junit.Assert.*;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
import java.util.Collections;
import java.util.Map;
@@ -11,8 +12,6 @@ import org.junit.Test;
import org.springframework.batch.core.JobExecution;
import org.springframework.batch.core.Step;
import org.springframework.batch.core.StepExecution;
import org.springframework.batch.core.partition.support.Partitioner;
import org.springframework.batch.core.partition.support.SimpleStepExecutionSplitter;
import org.springframework.batch.core.repository.JobRepository;
import org.springframework.batch.core.repository.support.MapJobRepositoryFactoryBean;
import org.springframework.batch.core.step.tasklet.TaskletStep;
@@ -35,10 +34,11 @@ public class SimpleStepExecutionSplitterTests {
@Test
public void testSimpleStepExecutionProviderJobRepositoryStep() throws Exception {
SimpleStepExecutionSplitter splitter = new SimpleStepExecutionSplitter(jobRepository, step);
SimpleStepExecutionSplitter splitter = new SimpleStepExecutionSplitter(jobRepository, step,
new SimplePartitioner());
Set<StepExecution> execs = splitter.split(stepExecution, 2);
assertEquals(2, execs.size());
for (StepExecution execution : execs) {
assertNotNull("step execution partition is saved", execution.getId());
}
@@ -57,14 +57,16 @@ public class SimpleStepExecutionSplitterTests {
@Test
public void testRememberGridSize() throws Exception {
SimpleStepExecutionSplitter provider = new SimpleStepExecutionSplitter(jobRepository, step);
SimpleStepExecutionSplitter provider = new SimpleStepExecutionSplitter(jobRepository, step,
new SimplePartitioner());
assertEquals(2, provider.split(stepExecution, 2).size());
assertEquals(2, provider.split(stepExecution, 3).size());
}
@Test
public void testGetStepName() {
SimpleStepExecutionSplitter provider = new SimpleStepExecutionSplitter(jobRepository, step);
SimpleStepExecutionSplitter provider = new SimpleStepExecutionSplitter(jobRepository, step,
new SimplePartitioner());
assertEquals("step", provider.getStepName());
}

View File

@@ -0,0 +1,36 @@
<?xml version="1.0" encoding="UTF-8"?>
<beans:beans xmlns="http://www.springframework.org/schema/batch"
xmlns:beans="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/batch http://www.springframework.org/schema/batch/spring-batch-2.1.xsd
http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.5.xsd">
<beans:import resource="common-context.xml" />
<job id="job1">
<step id="s1">
<partition step="step1" partitioner="partitioner">
<handler task-executor="taskExecutor" grid-size="2" />
</partition>
</step>
</job>
<job id="job2">
<step id="s2" next="s3">
<partition step="step1" handler="handler" partitioner="partitioner"/>
</step>
<step id="s3" parent="step2"/>
</job>
<bean id="handler"
class="org.springframework.batch.core.partition.support.TaskExecutorPartitionHandler"
xmlns="http://www.springframework.org/schema/beans">
<property name="step" ref="step1"/>
</bean>
<beans:bean id="taskExecutor"
class="org.springframework.core.task.SimpleAsyncTaskExecutor" />
<beans:bean id="partitioner"
class="org.springframework.batch.core.partition.support.SimplePartitioner" />
</beans:beans>

View File

@@ -1,18 +1,21 @@
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans" xmlns:p="http://www.springframework.org/schema/p"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:p="http://www.springframework.org/schema/p" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="
http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd
http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx.xsd">
<import resource="classpath:/org/springframework/batch/core/repository/dao/data-source-context.xml" />
<import
resource="classpath:/org/springframework/batch/core/repository/dao/data-source-context.xml" />
<bean id="job1" parent="simpleJob">
<property name="steps">
<bean name="step1:master" class="org.springframework.batch.core.partition.support.PartitionStep">
<bean name="step1:master"
class="org.springframework.batch.core.partition.support.PartitionStep">
<property name="partitionHandler">
<bean class="org.springframework.batch.core.partition.support.TaskExecutorPartitionHandler">
<bean
class="org.springframework.batch.core.partition.support.TaskExecutorPartitionHandler">
<property name="taskExecutor">
<bean class="org.springframework.core.task.SimpleAsyncTaskExecutor" />
</property>
@@ -21,9 +24,14 @@
</bean>
</property>
<property name="stepExecutionSplitter">
<bean class="org.springframework.batch.core.partition.support.SimpleStepExecutionSplitter">
<constructor-arg ref="jobRepository" />
<constructor-arg ref="step1" />
<bean
class="org.springframework.batch.core.partition.support.SimpleStepExecutionSplitter">
<property name="jobRepository" ref="jobRepository" />
<property name="stepName" value="step1" />
<property name="partitioner">
<bean
class="org.springframework.batch.core.partition.support.SimplePartitioner" />
</property>
</bean>
</property>
<property name="jobRepository" ref="jobRepository" />
@@ -32,7 +40,8 @@
</bean>
<bean id="step1" parent="simpleStep">
<property name="itemReader">
<bean class="org.springframework.batch.core.partition.ExampleItemReader" scope="step">
<bean class="org.springframework.batch.core.partition.ExampleItemReader"
scope="step">
<!--
<property name="resource"
value="#{stepAttributes[jobParameters['resource']]}"/>
@@ -44,23 +53,29 @@
</property>
</bean>
<bean id="simpleJob" class="org.springframework.batch.core.job.SimpleJob" abstract="true">
<bean id="simpleJob" class="org.springframework.batch.core.job.SimpleJob"
abstract="true">
<property name="jobRepository" ref="jobRepository" />
<property name="restartable" value="true" />
</bean>
<bean id="simpleStep" class="org.springframework.batch.core.step.item.SimpleStepFactoryBean" abstract="true">
<bean id="simpleStep"
class="org.springframework.batch.core.step.item.SimpleStepFactoryBean"
abstract="true">
<property name="transactionManager" ref="transactionManager" />
<property name="jobRepository" ref="jobRepository" />
<property name="startLimit" value="100" />
<property name="commitInterval" value="1" />
</bean>
<bean id="jobLauncher" class="org.springframework.batch.core.launch.support.SimpleJobLauncher">
<bean id="jobLauncher"
class="org.springframework.batch.core.launch.support.SimpleJobLauncher">
<property name="jobRepository" ref="jobRepository" />
</bean>
<bean id="jobRepository" class="org.springframework.batch.core.repository.support.JobRepositoryFactoryBean"
p:databaseType="hsql" p:dataSource-ref="dataSource" p:transactionManager-ref="transactionManager" />
<bean id="jobRepository"
class="org.springframework.batch.core.repository.support.JobRepositoryFactoryBean"
p:databaseType="hsql" p:dataSource-ref="dataSource"
p:transactionManager-ref="transactionManager" />
<bean class="org.springframework.batch.core.scope.StepScope" />

View File

@@ -68,8 +68,11 @@
<property name="stepExecutionSplitter">
<bean
class="org.springframework.batch.core.partition.support.SimpleStepExecutionSplitter">
<constructor-arg ref="jobRepository" />
<constructor-arg ref="step1" />
<property name="jobRepository" ref="jobRepository" />
<property name="stepName" ref="step1" />
<property name="partitioner">
<bean class="org.springframework.batch.core.partition.support.SimplePartitioner"/>
</property>
</bean>
</property>
<property name="jobRepository" ref="jobRepository" />

View File

@@ -1,7 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:aop="http://www.springframework.org/schema/aop"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:aop="http://www.springframework.org/schema/aop" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-2.0.xsd
http://www.springframework.org/schema/batch http://www.springframework.org/schema/batch/spring-batch-2.1.xsd
http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.0.xsd">
@@ -9,88 +8,108 @@
<job id="partitionJob" xmlns="http://www.springframework.org/schema/batch">
<step id="step" parent="step1:master" />
</job>
<bean name="step1:master" class="org.springframework.batch.core.partition.support.PartitionStep">
<bean name="step1:master"
class="org.springframework.batch.core.partition.support.PartitionStep">
<property name="jobRepository" ref="jobRepository" />
<property name="stepExecutionSplitter">
<bean class="org.springframework.batch.core.partition.support.SimpleStepExecutionSplitter">
<constructor-arg ref="jobRepository" />
<constructor-arg ref="step1" />
<constructor-arg>
<bean class="org.springframework.batch.core.partition.support.MultiResourcePartitioner">
<property name="resources" value="classpath:data/iosample/input/delimited*.csv" />
<bean
class="org.springframework.batch.core.partition.support.SimpleStepExecutionSplitter">
<property name="jobRepository" ref="jobRepository" />
<property name="stepName" value="step1" />
<property name="partitioner">
<bean
class="org.springframework.batch.core.partition.support.MultiResourcePartitioner">
<property name="resources"
value="classpath:data/iosample/input/delimited*.csv" />
</bean>
</constructor-arg>
</property>
</bean>
</property>
<property name="partitionHandler">
<bean class="org.springframework.batch.core.partition.support.TaskExecutorPartitionHandler">
<bean
class="org.springframework.batch.core.partition.support.TaskExecutorPartitionHandler">
<property name="taskExecutor">
<bean class="org.springframework.core.task.SyncTaskExecutor" />
</property>
<property name="step" ref="step1" />
<property name="gridSize" value="2"/>
<property name="gridSize" value="2" />
</bean>
</property>
</bean>
<step id="step1" xmlns="http://www.springframework.org/schema/batch" job-repository="jobRepository">
<step id="step1" xmlns="http://www.springframework.org/schema/batch">
<tasklet transaction-manager="transactionManager">
<chunk writer="itemWriter" reader="itemReader" processor="itemProcessor" commit-interval="5" />
<chunk writer="itemWriter" reader="itemReader" processor="itemProcessor"
commit-interval="5" />
<listeners>
<listener ref="fileNameListener" />
</listeners>
</tasklet>
</step>
<bean id="fileNameListener" class="org.springframework.batch.sample.common.OutputFileListener" scope="step">
<property name="path" value="file:./target/output/file/"/>
<bean id="fileNameListener"
class="org.springframework.batch.sample.common.OutputFileListener"
scope="step">
<property name="path" value="file:./target/output/file/" />
</bean>
<bean id="itemReader" scope="step" autowire-candidate="false" parent="itemReaderParent">
<bean id="itemReader" scope="step" autowire-candidate="false"
parent="itemReaderParent">
<property name="resource" value="#{stepExecutionContext[fileName]}" />
</bean>
<bean id="inputTestReader" class="org.springframework.batch.item.file.MultiResourceItemReader">
<bean id="inputTestReader"
class="org.springframework.batch.item.file.MultiResourceItemReader">
<property name="resources" value="classpath:data/iosample/input/delimited*.csv" />
<property name="delegate" ref="testItemReader" />
</bean>
<bean id="outputTestReader" class="org.springframework.batch.item.file.MultiResourceItemReader" scope="prototype">
<bean id="outputTestReader"
class="org.springframework.batch.item.file.MultiResourceItemReader"
scope="prototype">
<property name="resources" value="file:target/output/file/delimited*.csv" />
<property name="delegate" ref="testItemReader" />
</bean>
<bean id="testItemReader" parent="itemReaderParent"/>
<bean id="testItemReader" parent="itemReaderParent" />
<bean id="itemReaderParent" class="org.springframework.batch.item.file.FlatFileItemReader" abstract="true">
<bean id="itemReaderParent" class="org.springframework.batch.item.file.FlatFileItemReader"
abstract="true">
<property name="lineMapper">
<bean class="org.springframework.batch.item.file.mapping.DefaultLineMapper">
<property name="lineTokenizer">
<bean class="org.springframework.batch.item.file.transform.DelimitedLineTokenizer">
<bean
class="org.springframework.batch.item.file.transform.DelimitedLineTokenizer">
<property name="delimiter" value="," />
<property name="names" value="name,credit" />
</bean>
</property>
<property name="fieldSetMapper">
<bean class="org.springframework.batch.item.file.mapping.BeanWrapperFieldSetMapper">
<property name="targetType" value="org.springframework.batch.sample.domain.trade.CustomerCredit" />
<bean
class="org.springframework.batch.item.file.mapping.BeanWrapperFieldSetMapper">
<property name="targetType"
value="org.springframework.batch.sample.domain.trade.CustomerCredit" />
</bean>
</property>
</bean>
</property>
</bean>
<bean id="itemProcessor" class="org.springframework.batch.sample.domain.trade.internal.CustomerCreditIncreaseProcessor" />
<bean id="itemProcessor"
class="org.springframework.batch.sample.domain.trade.internal.CustomerCreditIncreaseProcessor" />
<bean id="itemWriter" class="org.springframework.batch.item.file.FlatFileItemWriter" scope="step">
<bean id="itemWriter" class="org.springframework.batch.item.file.FlatFileItemWriter"
scope="step">
<property name="resource" value="#{stepExecutionContext[outputFile]}" />
<property name="lineAggregator">
<bean class="org.springframework.batch.item.file.transform.DelimitedLineAggregator">
<bean
class="org.springframework.batch.item.file.transform.DelimitedLineAggregator">
<property name="delimiter" value="," />
<property name="fieldExtractor">
<bean class="org.springframework.batch.item.file.transform.BeanWrapperFieldExtractor">
<bean
class="org.springframework.batch.item.file.transform.BeanWrapperFieldExtractor">
<property name="names" value="name,credit" />
</bean>
</property>

View File

@@ -15,16 +15,16 @@
<property name="stepExecutionSplitter">
<bean
class="org.springframework.batch.core.partition.support.SimpleStepExecutionSplitter">
<constructor-arg ref="jobRepository" />
<constructor-arg ref="step1" />
<constructor-arg>
<property name="jobRepository" ref="jobRepository" />
<property name="stepName" value="step1" />
<property name="partitioner">
<bean
class="org.springframework.batch.sample.common.ColumnRangePartitioner">
<property name="dataSource" ref="dataSource" />
<property name="table" value="CUSTOMER" />
<property name="column" value="ID" />
</bean>
</constructor-arg>
</property>
</bean>
</property>
<property name="partitionHandler">
@@ -34,13 +34,13 @@
<bean class="org.springframework.core.task.SyncTaskExecutor" />
</property>
<property name="step" ref="step1" />
<property name="gridSize" value="2"/>
<property name="gridSize" value="2" />
</bean>
</property>
</bean>
<step id="step1" xmlns="http://www.springframework.org/schema/batch">
<tasklet job-repository="jobRepository" transaction-manager="transactionManager">
<tasklet transaction-manager="transactionManager">
<chunk writer="itemWriter" reader="itemReader" processor="itemProcessor"
commit-interval="5" />
<listeners>
@@ -52,7 +52,7 @@
<bean id="fileNameListener"
class="org.springframework.batch.sample.common.OutputFileListener"
scope="step">
<property name="path" value="file:./target/output/jdbc/"/>
<property name="path" value="file:./target/output/jdbc/" />
</bean>
<bean id="itemReader" scope="step" autowire-candidate="false"
@@ -89,14 +89,17 @@
<property name="lineMapper">
<bean class="org.springframework.batch.item.file.mapping.DefaultLineMapper">
<property name="lineTokenizer">
<bean class="org.springframework.batch.item.file.transform.DelimitedLineTokenizer">
<bean
class="org.springframework.batch.item.file.transform.DelimitedLineTokenizer">
<property name="delimiter" value="," />
<property name="names" value="id,name,credit" />
</bean>
</property>
<property name="fieldSetMapper">
<bean class="org.springframework.batch.item.file.mapping.BeanWrapperFieldSetMapper">
<property name="targetType" value="org.springframework.batch.sample.domain.trade.CustomerCredit" />
<bean
class="org.springframework.batch.item.file.mapping.BeanWrapperFieldSetMapper">
<property name="targetType"
value="org.springframework.batch.sample.domain.trade.CustomerCredit" />
</bean>
</property>
</bean>