BATCH-1712: use parent bean def for handler to avoid clashes of step instances
This commit is contained in:
@@ -1,6 +1,7 @@
|
||||
#Tue Feb 01 21:15:00 GMT 2011
|
||||
#Tue Mar 15 12:05:10 GMT 2011
|
||||
//com.springsource.sts.config.flow.coordinates\:http\://www.springframework.org/schema/batch\:/spring-batch-core/src/test/resources/org/springframework/batch/core/configuration/xml/JobExecutionListenerMethodAttributeParserTests-context.xml=<?xml version\="1.0" encoding\="UTF-8"?>\n<graph>\n<element type\="job">\n<structure end\="995" endstart\="989" start\="511" startend\="586"/>\n<bounds height\="118" width\="77" x\="15" y\="17"/>\n</element>\n</graph>
|
||||
//com.springsource.sts.config.flow.coordinates\:http\://www.springframework.org/schema/batch\:/spring-batch-core/src/test/resources/org/springframework/batch/core/configuration/xml/JobRepositoryDefaultParserTests-context.xml=<?xml version\="1.0" encoding\="UTF-8"?>\n<graph/>
|
||||
//com.springsource.sts.config.flow.coordinates\:http\://www.springframework.org/schema/batch\:/spring-batch-core/src/test/resources/org/springframework/batch/core/configuration/xml/PartitionStepParserTests-context.xml=<?xml version\="1.0" encoding\="UTF-8"?>\n<graph>\n<element clazz\="JobModelElement" type\="job">\n<structure end\="689" endstart\="683" start\="510" startend\="525"/>\n<bounds height\="128" width\="84" x\="15" y\="17"/>\n</element>\n<element clazz\="JobModelElement" type\="job">\n<structure end\="860" endstart\="854" start\="692" startend\="707"/>\n<bounds height\="172" width\="84" x\="207" y\="17"/>\n</element>\n<element clazz\="JobModelElement" type\="job">\n<structure end\="1018" endstart\="1012" start\="863" startend\="878"/>\n<bounds height\="128" width\="84" x\="111" y\="17"/>\n</element>\n<element clazz\="JobModelElement" type\="job">\n<structure end\="1271" endstart\="1265" start\="1021" startend\="1036"/>\n<bounds height\="128" width\="84" x\="303" y\="17"/>\n</element>\n<element clazz\="JobModelElement" type\="job">\n<structure end\="1388" endstart\="1382" start\="1274" startend\="1289"/>\n<bounds height\="128" width\="84" x\="399" y\="17"/>\n</element>\n</graph>
|
||||
//com.springsource.sts.config.flow.coordinates\:http\://www.springframework.org/schema/batch\:/spring-batch-core/src/test/resources/org/springframework/batch/core/configuration/xml/StepListenerInStepParserTests-context.xml=<?xml version\="1.0" encoding\="UTF-8"?>\n<graph>\n<element clazz\="JobModelElement" type\="job">\n<structure end\="1832" endstart\="1826" start\="510" startend\="524"/>\n<bounds height\="268" width\="77" x\="15" y\="17"/>\n</element>\n</graph>
|
||||
//com.springsource.sts.config.flow.coordinates\:http\://www.springframework.org/schema/batch\:/spring-batch-core/src/test/resources/org/springframework/batch/core/configuration/xml/StepListenerMethodAttributeParserTests-context.xml=<?xml version\="1.0" encoding\="UTF-8"?>\n<graph>\n<element type\="job">\n<structure end\="854" endstart\="848" start\="511" startend\="525"/>\n<bounds height\="118" width\="77" x\="15" y\="17"/>\n</element>\n</graph>
|
||||
//com.springsource.sts.config.flow.coordinates\:http\://www.springframework.org/schema/batch\:/spring-batch-core/src/test/resources/org/springframework/batch/core/configuration/xml/StepListenerParserTests-context.xml=<?xml version\="1.0" encoding\="UTF-8"?>\n<graph>\n<element clazz\="JobModelElement" type\="job">\n<structure end\="1650" endstart\="1644" start\="510" startend\="524"/>\n<bounds height\="218" width\="77" x\="15" y\="17"/>\n</element>\n</graph>
|
||||
|
||||
@@ -157,24 +157,11 @@ public abstract class AbstractStepParser {
|
||||
return;
|
||||
}
|
||||
|
||||
Element inlineStepElement = DomUtils.getChildElementByTagName(partitionElement, STEP_ELE);
|
||||
if (inlineStepElement == null && !StringUtils.hasText(stepRef)) {
|
||||
parserContext.getReaderContext().error("You must specify a step", partitionElement);
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
MutablePropertyValues propertyValues = bd.getPropertyValues();
|
||||
|
||||
if (StringUtils.hasText(stepRef)) {
|
||||
propertyValues.addPropertyValue("step", new RuntimeBeanReference(stepRef));
|
||||
} else if( inlineStepElement!=null) {
|
||||
AbstractBeanDefinition stepDefinition = parseStep(inlineStepElement, parserContext, jobFactoryRef);
|
||||
propertyValues.addPropertyValue("step", stepDefinition );
|
||||
}
|
||||
|
||||
propertyValues.addPropertyValue("partitioner", new RuntimeBeanReference(partitionerRef));
|
||||
|
||||
boolean customHandler = false;
|
||||
if (!StringUtils.hasText(handlerRef)) {
|
||||
Element handlerElement = DomUtils.getChildElementByTagName(partitionElement, HANDLER_ELE);
|
||||
if (handlerElement != null) {
|
||||
@@ -188,7 +175,24 @@ public abstract class AbstractStepParser {
|
||||
}
|
||||
}
|
||||
} else {
|
||||
propertyValues.addPropertyValue("partitionHandler", new RuntimeBeanReference(handlerRef));
|
||||
customHandler = true;
|
||||
BeanDefinition partitionHandler = BeanDefinitionBuilder.genericBeanDefinition().getRawBeanDefinition();
|
||||
partitionHandler.setParentName(handlerRef);
|
||||
propertyValues.addPropertyValue("partitionHandler", partitionHandler);
|
||||
}
|
||||
|
||||
Element inlineStepElement = DomUtils.getChildElementByTagName(partitionElement, STEP_ELE);
|
||||
if (inlineStepElement == null && !StringUtils.hasText(stepRef) && !customHandler) {
|
||||
parserContext.getReaderContext().error("You must specify a step", partitionElement);
|
||||
return;
|
||||
}
|
||||
|
||||
if (StringUtils.hasText(stepRef)) {
|
||||
propertyValues.addPropertyValue("step", new RuntimeBeanReference(stepRef));
|
||||
} else if( inlineStepElement!=null) {
|
||||
AbstractBeanDefinition stepDefinition = parseStep(inlineStepElement, parserContext, jobFactoryRef);
|
||||
stepDefinition.getPropertyValues().addPropertyValue("name", stepElement.getAttribute(ID_ATTR));
|
||||
propertyValues.addPropertyValue("step", stepDefinition );
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -22,6 +22,8 @@ import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
import org.springframework.batch.classify.BinaryExceptionClassifier;
|
||||
import org.springframework.batch.core.ChunkListener;
|
||||
import org.springframework.batch.core.Job;
|
||||
@@ -59,6 +61,7 @@ import org.springframework.batch.retry.RetryPolicy;
|
||||
import org.springframework.batch.retry.backoff.BackOffPolicy;
|
||||
import org.springframework.batch.retry.policy.MapRetryContextCache;
|
||||
import org.springframework.batch.retry.policy.RetryContextCache;
|
||||
import org.springframework.beans.factory.BeanCreationException;
|
||||
import org.springframework.beans.factory.BeanNameAware;
|
||||
import org.springframework.beans.factory.FactoryBean;
|
||||
import org.springframework.core.task.SyncTaskExecutor;
|
||||
@@ -85,6 +88,8 @@ import org.springframework.util.Assert;
|
||||
*/
|
||||
class StepParserStepFactoryBean<I, O> implements FactoryBean, BeanNameAware {
|
||||
|
||||
private static final Log logger = LogFactory.getLog(StepParserStepFactoryBean.class);
|
||||
|
||||
//
|
||||
// Step Attributes
|
||||
//
|
||||
@@ -234,15 +239,11 @@ class StepParserStepFactoryBean<I, O> implements FactoryBean, BeanNameAware {
|
||||
configureJobStep(ts);
|
||||
return ts;
|
||||
}
|
||||
else if (step != null) {
|
||||
else {
|
||||
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.");
|
||||
}
|
||||
}
|
||||
|
||||
public boolean requiresTransactionManager() {
|
||||
@@ -277,7 +278,6 @@ class StepParserStepFactoryBean<I, O> implements FactoryBean, BeanNameAware {
|
||||
|
||||
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);
|
||||
|
||||
PartitionHandler handler;
|
||||
@@ -300,17 +300,39 @@ class StepParserStepFactoryBean<I, O> implements FactoryBean, BeanNameAware {
|
||||
|
||||
// BATCH-1659
|
||||
if (handler instanceof TaskExecutorPartitionHandler) {
|
||||
// Only for a local partition handler is the step required
|
||||
Assert.state(step != null,
|
||||
"A Step must be provided for a partition step with a TaskExecutorPartitionHandler");
|
||||
try {
|
||||
TaskExecutorPartitionHandler taskExecutorPartitionHandler = (TaskExecutorPartitionHandler) handler;
|
||||
taskExecutorPartitionHandler.setStep(step);
|
||||
taskExecutorPartitionHandler.afterPropertiesSet();
|
||||
}
|
||||
catch (Exception e) {
|
||||
throw new RuntimeException(e);
|
||||
throw new BeanCreationException("Could not configure TaskExecutorPartitionHandler", e);
|
||||
}
|
||||
}
|
||||
else {
|
||||
// Only for a local partition handler is the step required
|
||||
Assert.state(step == null,
|
||||
"A Step must not be provided for a custom partition handler (unless it extends TaskExecutorPartitionHandler). "
|
||||
+ "It should be configured with its own step reference.");
|
||||
}
|
||||
|
||||
SimpleStepExecutionSplitter splitter = new SimpleStepExecutionSplitter(jobRepository, step, partitioner);
|
||||
boolean allowStartIfComplete = this.allowStartIfComplete !=null ? this.allowStartIfComplete : false;
|
||||
String name = this.name;
|
||||
if (step != null) {
|
||||
try {
|
||||
allowStartIfComplete = step.isAllowStartIfComplete();
|
||||
name = step.getName();
|
||||
}
|
||||
catch (Exception e) {
|
||||
logger.info("Ignored exception from step asking for name and allowStartIfComplete flag. "
|
||||
+ "Using default from enclosing PartitionStep ("+name+","+allowStartIfComplete+").");
|
||||
}
|
||||
}
|
||||
SimpleStepExecutionSplitter splitter = new SimpleStepExecutionSplitter(jobRepository, allowStartIfComplete,
|
||||
name, partitioner);
|
||||
ts.setStepExecutionSplitter(splitter);
|
||||
}
|
||||
|
||||
@@ -578,6 +600,13 @@ class StepParserStepFactoryBean<I, O> implements FactoryBean, BeanNameAware {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param name the name to set
|
||||
*/
|
||||
public void setName(String name) {
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
// =========================================================
|
||||
// Flow Attributes
|
||||
// =========================================================
|
||||
|
||||
@@ -66,6 +66,23 @@ public class SimpleStepExecutionSplitter implements StepExecutionSplitter, Initi
|
||||
public SimpleStepExecutionSplitter() {
|
||||
}
|
||||
|
||||
/**
|
||||
* Construct a {@link SimpleStepExecutionSplitter} from its mandatory
|
||||
* properties.
|
||||
*
|
||||
* @param jobRepository the {@link JobRepository}
|
||||
* @param allowStartIfComplete flag specifying preferences on restart
|
||||
* @param stepName the target step name
|
||||
* @param partitioner a {@link Partitioner} to use for generating input
|
||||
* parameters
|
||||
*/
|
||||
public SimpleStepExecutionSplitter(JobRepository jobRepository, boolean allowStartIfComplete, String stepName, Partitioner partitioner) {
|
||||
this.jobRepository = jobRepository;
|
||||
this.allowStartIfComplete = allowStartIfComplete;
|
||||
this.partitioner = partitioner;
|
||||
this.stepName = stepName;
|
||||
}
|
||||
|
||||
/**
|
||||
* Construct a {@link SimpleStepExecutionSplitter} from its mandatory
|
||||
* properties.
|
||||
@@ -75,7 +92,10 @@ public class SimpleStepExecutionSplitter implements StepExecutionSplitter, Initi
|
||||
* name and allowStartIfComplete flags
|
||||
* @param partitioner a {@link Partitioner} to use for generating input
|
||||
* parameters
|
||||
*
|
||||
* @deprecated use {@link #SimpleStepExecutionSplitter(JobRepository, boolean, String, Partitioner)} instead
|
||||
*/
|
||||
@Deprecated
|
||||
public SimpleStepExecutionSplitter(JobRepository jobRepository, Step step, Partitioner partitioner) {
|
||||
this.jobRepository = jobRepository;
|
||||
this.allowStartIfComplete = step.isAllowStartIfComplete();
|
||||
|
||||
@@ -34,12 +34,13 @@ import org.springframework.beans.factory.InitializingBean;
|
||||
import org.springframework.core.task.SyncTaskExecutor;
|
||||
import org.springframework.core.task.TaskExecutor;
|
||||
import org.springframework.core.task.TaskRejectedException;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
/**
|
||||
* A {@link PartitionHandler} that uses a {@link TaskExecutor} to execute the
|
||||
* partitioned {@link Step} locally in multiple threads. This can be an effective
|
||||
* approach for scaling batch steps that are IO intensive, like directory and
|
||||
* filesystem scanning and copying.
|
||||
* partitioned {@link Step} locally in multiple threads. This can be an
|
||||
* effective approach for scaling batch steps that are IO intensive, like
|
||||
* directory and filesystem scanning and copying.
|
||||
*
|
||||
* @author Dave Syer
|
||||
* @since 2.0
|
||||
@@ -53,15 +54,14 @@ public class TaskExecutorPartitionHandler implements PartitionHandler, Initializ
|
||||
private Step step;
|
||||
|
||||
public void afterPropertiesSet() throws Exception {
|
||||
// Assert.notNull(step, "A Step must be provided.");
|
||||
}
|
||||
|
||||
/**
|
||||
* Passed to the {@link StepExecutionSplitter} in the
|
||||
* {@link #handle(StepExecutionSplitter, StepExecution)} method, instructing
|
||||
* it how many {@link StepExecution} instances are required, ideally. The
|
||||
* {@link StepExecutionSplitter} is allowed to ignore the grid size in the case of
|
||||
* a restart, since the input data partitions must be preserved.
|
||||
* {@link StepExecutionSplitter} is allowed to ignore the grid size in the
|
||||
* case of a restart, since the input data partitions must be preserved.
|
||||
*
|
||||
* @param gridSize the number of step executions that will be created
|
||||
*/
|
||||
@@ -96,6 +96,8 @@ public class TaskExecutorPartitionHandler implements PartitionHandler, Initializ
|
||||
public Collection<StepExecution> handle(StepExecutionSplitter stepExecutionSplitter,
|
||||
StepExecution masterStepExecution) throws Exception {
|
||||
|
||||
Assert.notNull(step, "A Step must be provided.");
|
||||
|
||||
Set<Future<StepExecution>> tasks = new HashSet<Future<StepExecution>>(gridSize);
|
||||
|
||||
Collection<StepExecution> result = new ArrayList<StepExecution>();
|
||||
|
||||
@@ -20,6 +20,8 @@ import static org.junit.Assert.assertNotNull;
|
||||
|
||||
import java.lang.reflect.Field;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
@@ -27,6 +29,8 @@ import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.springframework.batch.core.*;
|
||||
import org.springframework.batch.core.partition.PartitionHandler;
|
||||
import org.springframework.batch.core.partition.StepExecutionSplitter;
|
||||
import org.springframework.batch.core.partition.support.PartitionStep;
|
||||
import org.springframework.batch.core.partition.support.TaskExecutorPartitionHandler;
|
||||
import org.springframework.batch.core.repository.JobRepository;
|
||||
@@ -41,7 +45,6 @@ import org.springframework.test.context.ContextConfiguration;
|
||||
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
|
||||
import org.springframework.util.ReflectionUtils;
|
||||
|
||||
|
||||
/**
|
||||
* @author Dave Syer
|
||||
* @author Josh Long
|
||||
@@ -50,102 +53,185 @@ import org.springframework.util.ReflectionUtils;
|
||||
@RunWith(SpringJUnit4ClassRunner.class)
|
||||
public class PartitionStepParserTests implements ApplicationContextAware {
|
||||
|
||||
@Autowired
|
||||
@Qualifier("job1")
|
||||
private Job job1;
|
||||
@Autowired
|
||||
@Qualifier("job1")
|
||||
private Job job1;
|
||||
|
||||
@Autowired
|
||||
@Qualifier("job2")
|
||||
private Job job2;
|
||||
@Autowired
|
||||
@Qualifier("job2")
|
||||
private Job job2;
|
||||
|
||||
@Autowired
|
||||
@Qualifier("job3")
|
||||
private Job job3;
|
||||
@Autowired
|
||||
@Qualifier("job3")
|
||||
private Job job3;
|
||||
|
||||
@Autowired
|
||||
private JobRepository jobRepository;
|
||||
@Autowired
|
||||
@Qualifier("job4")
|
||||
private Job job4;
|
||||
|
||||
@Autowired
|
||||
private MapJobRepositoryFactoryBean mapJobRepositoryFactoryBean;
|
||||
@Autowired
|
||||
@Qualifier("job5")
|
||||
private Job job5;
|
||||
|
||||
private ApplicationContext applicationContext;
|
||||
@Autowired
|
||||
@Qualifier("nameStoringTasklet")
|
||||
private NameStoringTasklet nameStoringTasklet;
|
||||
|
||||
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
|
||||
this.applicationContext = applicationContext;
|
||||
}
|
||||
@Autowired
|
||||
private JobRepository jobRepository;
|
||||
|
||||
@Before
|
||||
public void setUp() {
|
||||
mapJobRepositoryFactoryBean.clear();
|
||||
}
|
||||
@Autowired
|
||||
private MapJobRepositoryFactoryBean mapJobRepositoryFactoryBean;
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
private <T> T accessPrivateField(Object o, String fieldName) {
|
||||
Field field = ReflectionUtils.findField(o.getClass(), fieldName);
|
||||
boolean previouslyAccessibleValue = field.isAccessible();
|
||||
field.setAccessible(true);
|
||||
T val = (T) ReflectionUtils.getField(field, o);
|
||||
field.setAccessible(previouslyAccessibleValue);
|
||||
return val;
|
||||
}
|
||||
private ApplicationContext applicationContext;
|
||||
|
||||
/**
|
||||
* BATCH-1509 we now support the ability define steps inline for partitioned steps.
|
||||
* this demonstates that the execution proceeds as expected and that the partitionhandler has a reference to the inline step definition
|
||||
*/
|
||||
@Test
|
||||
public void testNestedPartitionStepStepReference() throws Throwable {
|
||||
assertNotNull("the reference to the job3 configured in the XML file must not be null", job3);
|
||||
JobExecution jobExecution = jobRepository.createJobExecution(job3.getName(), new JobParameters());
|
||||
private List<String> savedStepNames = new ArrayList<String>();
|
||||
|
||||
job3.execute(jobExecution);
|
||||
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
|
||||
this.applicationContext = applicationContext;
|
||||
}
|
||||
|
||||
for (StepExecution se : jobExecution.getStepExecutions()) {
|
||||
String stepExecutionName = se.getStepName();
|
||||
if (stepExecutionName.equalsIgnoreCase("j3s1")) { // the partitioned step
|
||||
PartitionStep partitionStep = (PartitionStep) this.applicationContext.getBean(stepExecutionName);
|
||||
@Before
|
||||
public void setUp() {
|
||||
nameStoringTasklet.setStepNamesList(savedStepNames);
|
||||
mapJobRepositoryFactoryBean.clear();
|
||||
}
|
||||
|
||||
// prove that the reference in the {@link TaskExecutorPartitionHandler} is the step configured inline
|
||||
TaskExecutorPartitionHandler taskExecutorPartitionHandler = accessPrivateField(partitionStep, "partitionHandler");
|
||||
TaskletStep taskletStep = accessPrivateField(taskExecutorPartitionHandler, "step");
|
||||
@SuppressWarnings("unchecked")
|
||||
private <T> T accessPrivateField(Object o, String fieldName) {
|
||||
Field field = ReflectionUtils.findField(o.getClass(), fieldName);
|
||||
boolean previouslyAccessibleValue = field.isAccessible();
|
||||
field.setAccessible(true);
|
||||
T val = (T) ReflectionUtils.getField(field, o);
|
||||
field.setAccessible(previouslyAccessibleValue);
|
||||
return val;
|
||||
}
|
||||
|
||||
assertNotNull("the taskletStep wasn't configured with a step. " +
|
||||
"We're trusting that the factory ensured " +
|
||||
"a reference was given.", taskletStep);
|
||||
}
|
||||
}
|
||||
assertEquals(BatchStatus.COMPLETED, jobExecution.getStatus());
|
||||
}
|
||||
@Test
|
||||
public void testCustomHandlerRefStep() throws Exception {
|
||||
assertNotNull(job5);
|
||||
JobExecution jobExecution = jobRepository.createJobExecution(job5.getName(), new JobParameters());
|
||||
job5.execute(jobExecution);
|
||||
assertEquals(BatchStatus.COMPLETED, jobExecution.getStatus());
|
||||
List<String> stepNames = getStepNames(jobExecution);
|
||||
assertEquals(1, stepNames.size());
|
||||
assertEquals("[j5s1]", stepNames.toString());
|
||||
}
|
||||
|
||||
@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(3, stepNames.size());
|
||||
assertEquals("[s1, step1:partition0, step1:partition1]", stepNames.toString());
|
||||
}
|
||||
/**
|
||||
* BATCH-1509 we now support the ability define steps inline for partitioned
|
||||
* steps. this demonstates that the execution proceeds as expected and that
|
||||
* the partitionhandler has a reference to the inline step definition
|
||||
*/
|
||||
@Test
|
||||
public void testNestedPartitionStep() throws Throwable {
|
||||
assertNotNull("the reference to the job4 configured in the XML file must not be null", job4);
|
||||
JobExecution jobExecution = jobRepository.createJobExecution(job4.getName(), new JobParameters());
|
||||
|
||||
@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(5, stepNames.size());
|
||||
assertEquals("[s2, s3, step1:partition0, step1:partition1, step1:partition2]", stepNames.toString());
|
||||
}
|
||||
job4.execute(jobExecution);
|
||||
|
||||
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;
|
||||
}
|
||||
for (StepExecution se : jobExecution.getStepExecutions()) {
|
||||
String stepExecutionName = se.getStepName();
|
||||
if (stepExecutionName.equalsIgnoreCase("j4s1")) { // the partitioned
|
||||
// step
|
||||
PartitionStep partitionStep = (PartitionStep) this.applicationContext.getBean(stepExecutionName);
|
||||
|
||||
// prove that the reference in the {@link
|
||||
// TaskExecutorPartitionHandler} is the step configured inline
|
||||
TaskExecutorPartitionHandler taskExecutorPartitionHandler = accessPrivateField(partitionStep,
|
||||
"partitionHandler");
|
||||
TaskletStep taskletStep = accessPrivateField(taskExecutorPartitionHandler, "step");
|
||||
|
||||
assertNotNull("the taskletStep wasn't configured with a step. "
|
||||
+ "We're trusting that the factory ensured " + "a reference was given.", taskletStep);
|
||||
}
|
||||
}
|
||||
assertEquals(BatchStatus.COMPLETED, jobExecution.getStatus());
|
||||
// Step names not saved by this one (it geosn't have that tasklet)
|
||||
assertEquals("[]", savedStepNames.toString());
|
||||
List<String> stepNames = getStepNames(jobExecution);
|
||||
assertEquals(4, stepNames.size());
|
||||
assertEquals("[j4s1, j4s1:partition0, j4s1:partition1, j4s1:partition2]", stepNames.toString());
|
||||
}
|
||||
|
||||
/**
|
||||
* BATCH-1509 we now support the ability define steps inline for partitioned
|
||||
* steps. this demonstates that the execution proceeds as expected and that
|
||||
* the partitionhandler has a reference to the inline step definition
|
||||
*/
|
||||
@Test
|
||||
public void testNestedPartitionStepStepReference() throws Throwable {
|
||||
assertNotNull("the reference to the job3 configured in the XML file must not be null", job3);
|
||||
JobExecution jobExecution = jobRepository.createJobExecution(job3.getName(), new JobParameters());
|
||||
|
||||
job3.execute(jobExecution);
|
||||
|
||||
for (StepExecution se : jobExecution.getStepExecutions()) {
|
||||
String stepExecutionName = se.getStepName();
|
||||
if (stepExecutionName.equalsIgnoreCase("j3s1")) { // the partitioned
|
||||
// step
|
||||
PartitionStep partitionStep = (PartitionStep) this.applicationContext.getBean(stepExecutionName);
|
||||
|
||||
// prove that the reference in the {@link
|
||||
// TaskExecutorPartitionHandler} is the step configured inline
|
||||
TaskExecutorPartitionHandler taskExecutorPartitionHandler = accessPrivateField(partitionStep,
|
||||
"partitionHandler");
|
||||
TaskletStep taskletStep = accessPrivateField(taskExecutorPartitionHandler, "step");
|
||||
|
||||
assertNotNull("the taskletStep wasn't configured with a step. "
|
||||
+ "We're trusting that the factory ensured " + "a reference was given.", taskletStep);
|
||||
}
|
||||
}
|
||||
assertEquals(BatchStatus.COMPLETED, jobExecution.getStatus());
|
||||
Collections.sort(savedStepNames);
|
||||
assertEquals("[j3s1:partition0, j3s1:partition1, j3s1:partition2]", savedStepNames.toString());
|
||||
List<String> stepNames = getStepNames(jobExecution);
|
||||
assertEquals(4, stepNames.size());
|
||||
assertEquals("[j3s1, j3s1:partition0, j3s1:partition1, j3s1:partition2]", stepNames.toString());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testDefaultHandlerStep() throws Exception {
|
||||
assertNotNull(job1);
|
||||
JobExecution jobExecution = jobRepository.createJobExecution(job1.getName(), new JobParameters());
|
||||
job1.execute(jobExecution);
|
||||
assertEquals(BatchStatus.COMPLETED, jobExecution.getStatus());
|
||||
Collections.sort(savedStepNames);
|
||||
assertEquals("[step1:partition0, step1:partition1]", savedStepNames.toString());
|
||||
List<String> stepNames = getStepNames(jobExecution);
|
||||
assertEquals(3, stepNames.size());
|
||||
assertEquals("[s1, step1:partition0, step1:partition1]", 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());
|
||||
Collections.sort(savedStepNames);
|
||||
assertEquals("[s3, step1:partition0, step1:partition1, step1:partition2]", savedStepNames.toString());
|
||||
List<String> stepNames = getStepNames(jobExecution);
|
||||
assertEquals(5, stepNames.size());
|
||||
assertEquals("[s2, s3, step1:partition0, step1:partition1, step1:partition2]", 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;
|
||||
}
|
||||
|
||||
public static class CustomPartitionHandler implements PartitionHandler {
|
||||
|
||||
public Collection<StepExecution> handle(StepExecutionSplitter stepSplitter, StepExecution stepExecution)
|
||||
throws Exception {
|
||||
return Arrays.asList(stepExecution);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -10,7 +10,6 @@ import java.util.Set;
|
||||
import java.util.TreeSet;
|
||||
|
||||
import org.junit.Before;
|
||||
import org.junit.Ignore;
|
||||
import org.junit.Test;
|
||||
import org.springframework.batch.core.ExitStatus;
|
||||
import org.springframework.batch.core.JobExecution;
|
||||
@@ -60,12 +59,11 @@ public class TaskExecutorPartitionHandlerTests {
|
||||
handler.afterPropertiesSet();
|
||||
}
|
||||
|
||||
@Ignore //TODO update or simply remove?
|
||||
@Test
|
||||
public void testAfterPropertiesSet() throws Exception {
|
||||
public void testNullStep() throws Exception {
|
||||
handler = new TaskExecutorPartitionHandler();
|
||||
try {
|
||||
handler.afterPropertiesSet();
|
||||
handler.handle(stepExecutionSplitter, stepExecution);
|
||||
fail("Expected IllegalArgumentException");
|
||||
}
|
||||
catch (IllegalArgumentException e) {
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
<?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"
|
||||
<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">
|
||||
|
||||
@@ -16,39 +16,48 @@
|
||||
|
||||
<job id="job2">
|
||||
<step id="s2" next="s3">
|
||||
<partition step="step1" handler="handler" partitioner="partitioner"/>
|
||||
<partition step="step1" handler="handler" partitioner="partitioner" />
|
||||
</step>
|
||||
<step id="s3" parent="step2"/>
|
||||
<step id="s3" parent="step2" />
|
||||
</job>
|
||||
|
||||
<job id="job3">
|
||||
<step id="j3s1" >
|
||||
<partition handler="handler1" partitioner="partitioner">
|
||||
<step parent="step2"/>
|
||||
<job id="job3">
|
||||
<step id="j3s1">
|
||||
<partition handler="handler" partitioner="partitioner">
|
||||
<step parent="step2" />
|
||||
</partition>
|
||||
</step>
|
||||
</job>
|
||||
|
||||
<job id="job4">
|
||||
<step id="j4s1">
|
||||
<partition handler="handler" partitioner="partitioner">
|
||||
<step>
|
||||
<tasklet>
|
||||
<chunk reader="reader" writer="writer" commit-interval="10" />
|
||||
</tasklet>
|
||||
</step>
|
||||
</partition>
|
||||
</step>
|
||||
</job>
|
||||
|
||||
<bean id="handler"
|
||||
class="org.springframework.batch.core.partition.support.TaskExecutorPartitionHandler"
|
||||
xmlns="http://www.springframework.org/schema/beans">
|
||||
<property name="step" ref="step1"/>
|
||||
<property name="gridSize" value="3"/>
|
||||
<job id="job5">
|
||||
<step id="j5s1">
|
||||
<partition handler="handler5" partitioner="partitioner" />
|
||||
</step>
|
||||
</job>
|
||||
|
||||
<!-- for the inline case, demonstrates that we correctly ensure a reference to a step for both the default handler setup
|
||||
or a custom PartitionHandler -->
|
||||
<bean id="handler" class="org.springframework.batch.core.partition.support.TaskExecutorPartitionHandler" xmlns="http://www.springframework.org/schema/beans">
|
||||
<property name="gridSize" value="3" />
|
||||
</bean>
|
||||
|
||||
<beans:bean id="taskExecutor"
|
||||
class="org.springframework.core.task.SyncTaskExecutor" />
|
||||
<beans:bean id="handler5"
|
||||
class="org.springframework.batch.core.configuration.xml.PartitionStepParserTests$CustomPartitionHandler" />
|
||||
|
||||
<beans:bean id="partitioner"
|
||||
class="org.springframework.batch.core.partition.support.SimplePartitioner" />
|
||||
<beans:bean id="taskExecutor" class="org.springframework.core.task.SyncTaskExecutor" />
|
||||
|
||||
<!--
|
||||
for the inline case, demonstrates that we correctly ensure a reference to a step for both the default handler setup or a custom PartitionHandler
|
||||
-->
|
||||
<beans:bean id="handler1" class="org.springframework.batch.core.partition.support.TaskExecutorPartitionHandler">
|
||||
<!-- nb: we're not configuring a step! -->
|
||||
<beans:property name="gridSize" value="10" />
|
||||
</beans:bean>
|
||||
<beans:bean id="partitioner" class="org.springframework.batch.core.partition.support.SimplePartitioner" />
|
||||
|
||||
</beans:beans>
|
||||
Reference in New Issue
Block a user