BATCH-2095: Change JSR version of Split to execute flows in separate threads.

This commit is contained in:
Chris Schaefer
2013-10-02 15:30:46 -04:00
parent 34f9fa7341
commit df53ea12b3
6 changed files with 275 additions and 54 deletions

View File

@@ -18,10 +18,14 @@ package org.springframework.batch.core.jsr.configuration.xml;
import java.util.Collection;
import java.util.List;
import org.springframework.beans.PropertyValue;
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.beans.factory.config.RuntimeBeanReference;
import org.springframework.beans.factory.support.BeanDefinitionBuilder;
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
import org.springframework.beans.factory.support.ManagedList;
import org.springframework.beans.factory.xml.ParserContext;
import org.springframework.core.task.SimpleAsyncTaskExecutor;
import org.springframework.util.xml.DomUtils;
import org.w3c.dom.Element;
@@ -29,9 +33,12 @@ import org.w3c.dom.Element;
* Parses a <split /> element as defined in JSR-352.
*
* @author Michael Minella
* @author Chris Schaefer
* @since 3.0
*/
public class SplitParser {
private static final String TASK_EXECUTOR_PROPERTY_NAME = "taskExecutor";
private static final String JSR_352_SPLIT_TASK_EXECUTOR_BEAN_NAME = "jsr352splitTaskExecutor";
private String jobFactoryRef;
@@ -63,6 +70,25 @@ public class SplitParser {
stateBuilder.addConstructorArgValue(flows);
stateBuilder.addConstructorArgValue(idAttribute);
PropertyValue propertyValue = getSplitTaskExecutorPropertyValue(parserContext.getRegistry());
stateBuilder.addPropertyValue(propertyValue.getName(), propertyValue.getValue());
return FlowParser.getNextElements(parserContext, null, stateBuilder.getBeanDefinition(), element);
}
protected PropertyValue getSplitTaskExecutorPropertyValue(BeanDefinitionRegistry beanDefinitionRegistry) {
PropertyValue propertyValue;
if (hasBeanDefinition(beanDefinitionRegistry, JSR_352_SPLIT_TASK_EXECUTOR_BEAN_NAME)) {
propertyValue = new PropertyValue(TASK_EXECUTOR_PROPERTY_NAME, new RuntimeBeanReference(JSR_352_SPLIT_TASK_EXECUTOR_BEAN_NAME));
} else {
propertyValue = new PropertyValue(TASK_EXECUTOR_PROPERTY_NAME, new SimpleAsyncTaskExecutor());
}
return propertyValue;
}
private boolean hasBeanDefinition(BeanDefinitionRegistry beanDefinitionRegistry, String beanName) {
return beanDefinitionRegistry.containsBeanDefinition(beanName);
}
}

View File

@@ -19,7 +19,7 @@ import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
import org.junit.Ignore;
import org.junit.Assert;
import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.ExpectedException;
@@ -29,16 +29,19 @@ import org.springframework.batch.core.Job;
import org.springframework.batch.core.JobExecution;
import org.springframework.batch.core.JobParameters;
import org.springframework.batch.core.launch.JobLauncher;
import org.springframework.beans.PropertyValue;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.config.RuntimeBeanReference;
import org.springframework.beans.factory.parsing.BeanDefinitionParsingException;
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import org.springframework.core.task.SimpleAsyncTaskExecutor;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
@ContextConfiguration({"SplitParsingTests-context.xml", "jsr-base-context.xml"})
@ContextConfiguration()
@RunWith(SpringJUnit4ClassRunner.class)
public class SplitParsingTests {
@Autowired
public Job job;
@@ -49,7 +52,6 @@ public class SplitParsingTests {
public ExpectedException expectedException = ExpectedException.none();
@Test
@Ignore
public void test() throws Exception {
JobExecution execution = jobLauncher.run(job, new JobParameters());
assertEquals(BatchStatus.COMPLETED, execution.getStatus());
@@ -57,15 +59,33 @@ public class SplitParsingTests {
}
@Test
@Ignore
public void testOneFlowInSplit() {
try {
new ClassPathXmlApplicationContext("/org/springframework/batch/core/jsr/configuration/xml/invalid-split-context.xml");
} catch (BeanDefinitionParsingException bdpe) {
assertTrue(bdpe.getMessage().indexOf("A <split/> must contain at least two 'flow' elements.") >= 0);
assertTrue(bdpe.getMessage().contains("A <split/> must contain at least two 'flow' elements."));
return;
}
fail("Expected exception was not thrown");
}
@Test
public void testUserSpecifiedTaskExecutor() {
ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("/org/springframework/batch/core/jsr/configuration/xml/user-specified-split-task-executor-context.xml");
BeanDefinitionRegistry registry = (BeanDefinitionRegistry) context.getBeanFactory();
PropertyValue propertyValue = new SplitParser(null).getSplitTaskExecutorPropertyValue(registry);
RuntimeBeanReference runtimeBeanReferenceValue = (RuntimeBeanReference) propertyValue.getValue();
Assert.assertTrue("RuntimeBeanReference should have a name of jsr352splitTaskExecutor" , "jsr352splitTaskExecutor".equals(runtimeBeanReferenceValue.getBeanName()));
}
@Test
public void testDefaultTaskExecutor() {
ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("/org/springframework/batch/core/jsr/configuration/xml/default-split-task-executor-context.xml");
BeanDefinitionRegistry registry = (BeanDefinitionRegistry) context.getBeanFactory();
PropertyValue propertyValue = new SplitParser(null).getSplitTaskExecutorPropertyValue(registry);
Assert.assertTrue("Task executor not an instance of SimpleAsyncTaskExecutor" , (propertyValue.getValue() instanceof SimpleAsyncTaskExecutor));
}
}

View File

@@ -1,31 +1,68 @@
<?xml version="1.0" encoding="UTF-8"?>
<job id="job1" xmlns="http://xmlns.jcp.org/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/jobXML_1_0.xsd"
version="1.0">
<step id="step1" next="step2">
<batchlet ref="step1Ref" />
</step>
<split id="step2" next="step3">
<flow id="step2a">
<step id="step2aStep1">
<batchlet ref="step1Ref" />
</step>
</flow>
<flow id="step2b">
<step id="step2bStep1" next="step2bStep2">
<batchlet ref="step1Ref" />
</step>
<step id="step2bStep2">
<chunk checkpoint-policy="item" item-count="3">
<reader ref="generatingItemReader1"/>
<processor ref="countingItemProcessor"/>
<writer ref="sysoutItemWriter"/>
</chunk>
</step>
</flow>
</split>
<step id="step3">
<batchlet ref="step1Ref" />
</step>
</job>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:util="http://www.springframework.org/schema/util"
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/util http://www.springframework.org/schema/util/spring-util.xsd
http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/jobXML_1_0.xsd">
<job id="job1" xmlns="http://xmlns.jcp.org/xml/ns/javaee" version="1.0">
<step id="step1" next="step2">
<batchlet ref="step1Ref"/>
</step>
<split id="step2" next="step3">
<flow id="step2a">
<step id="step2aStep1">
<batchlet ref="step1Ref"/>
</step>
</flow>
<flow id="step2b">
<step id="step2bStep1" next="step2bStep2">
<batchlet ref="step1Ref"/>
</step>
<step id="step2bStep2">
<chunk checkpoint-policy="item" item-count="3">
<reader ref="generatingItemReader1"/>
<processor ref="countingItemProcessor"/>
<writer ref="sysoutItemWriter"/>
</chunk>
</step>
</flow>
</split>
<step id="step3">
<batchlet ref="step1Ref"/>
</step>
</job>
<bean id="step1Ref" class="org.springframework.batch.core.step.tasklet.TaskletSupport"/>
<bean id="generatingItemReader1" class="org.springframework.batch.item.support.ListItemReader">
<constructor-arg>
<list>
<value>One</value>
<value>Two</value>
<value>Three</value>
<value>Four</value>
<value>Five</value>
</list>
</constructor-arg>
</bean>
<bean id="countingItemProcessor" class="org.springframework.batch.core.jsr.configuration.xml.CountingItemProcessor"/>
<bean id="sysoutItemWriter" class="org.springframework.batch.item.adapter.ItemWriterAdapter">
<property name="targetObject">
<util:constant static-field="java.lang.System.out"/>
</property>
<property name="targetMethod" value="println"/>
</bean>
<bean id="jobRepository" class="org.springframework.batch.core.repository.support.MapJobRepositoryFactoryBean"/>
<bean id="transactionManager" class="org.springframework.batch.support.transaction.ResourcelessTransactionManager"/>
<bean id="jobLauncher" class="org.springframework.batch.core.launch.support.SimpleJobLauncher">
<property name="jobRepository" ref="jobRepository"/>
</bean>
</beans>

View File

@@ -0,0 +1,65 @@
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:util="http://www.springframework.org/schema/util"
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/util http://www.springframework.org/schema/util/spring-util.xsd
http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/jobXML_1_0.xsd">
<job id="job1" xmlns="http://xmlns.jcp.org/xml/ns/javaee" version="1.0">
<step id="step1" next="step2">
<batchlet ref="step1Ref"/>
</step>
<split id="step2" next="step3">
<flow id="step2a">
<step id="step2aStep1">
<batchlet ref="step1Ref"/>
</step>
</flow>
<flow id="step2b">
<step id="step2bStep1" next="step2bStep2">
<batchlet ref="step1Ref"/>
</step>
<step id="step2bStep2">
<chunk checkpoint-policy="item" item-count="3">
<reader ref="generatingItemReader1"/>
<processor ref="countingItemProcessor"/>
<writer ref="sysoutItemWriter"/>
</chunk>
</step>
</flow>
</split>
<step id="step3">
<batchlet ref="step1Ref"/>
</step>
</job>
<bean id="step1Ref" class="org.springframework.batch.core.step.tasklet.TaskletSupport"/>
<bean id="generatingItemReader1" class="org.springframework.batch.item.support.ListItemReader">
<constructor-arg>
<list>
<value>One</value>
<value>Two</value>
</list>
</constructor-arg>
</bean>
<bean id="countingItemProcessor" class="org.springframework.batch.core.jsr.configuration.xml.CountingItemProcessor"/>
<bean id="sysoutItemWriter" class="org.springframework.batch.item.adapter.ItemWriterAdapter">
<property name="targetObject">
<util:constant static-field="java.lang.System.out"/>
</property>
<property name="targetMethod" value="println"/>
</bean>
<bean id="jobRepository" class="org.springframework.batch.core.repository.support.MapJobRepositoryFactoryBean"/>
<bean id="transactionManager" class="org.springframework.batch.support.transaction.ResourcelessTransactionManager"/>
<bean id="jobLauncher" class="org.springframework.batch.core.launch.support.SimpleJobLauncher">
<property name="jobRepository" ref="jobRepository"/>
</bean>
</beans>

View File

@@ -1,19 +1,25 @@
<?xml version="1.0" encoding="UTF-8"?>
<job id="job1" xmlns="http://xmlns.jcp.org/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/jobXML_1_0.xsd"
version="1.0">
<step id="step1" next="step2">
<batchlet ref="step1Ref" />
</step>
<split id="step2" next="step3">
<flow id="step2a">
<step id="step2aStep1">
<batchlet ref="step1Ref" />
</step>
</flow>
</split>
<step id="step3">
<batchlet ref="step1Ref" />
</step>
</job>
<beans xmlns="http://www.springframework.org/schema/beans"
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://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/jobXML_1_0.xsd">
<job id="job1" xmlns="http://xmlns.jcp.org/xml/ns/javaee" version="1.0">
<step id="step1" next="step2">
<batchlet ref="step1Ref"/>
</step>
<split id="step2" next="step3">
<flow id="step2a">
<step id="step2aStep1">
<batchlet ref="step1Ref"/>
</step>
</flow>
</split>
<step id="step3">
<batchlet ref="step1Ref"/>
</step>
</job>
<bean id="step1Ref" class="org.springframework.batch.core.step.tasklet.TaskletSupport"/>
</beans>

View File

@@ -0,0 +1,67 @@
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:util="http://www.springframework.org/schema/util"
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/util http://www.springframework.org/schema/util/spring-util.xsd
http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/jobXML_1_0.xsd">
<job id="job1" xmlns="http://xmlns.jcp.org/xml/ns/javaee" version="1.0">
<step id="step1" next="step2">
<batchlet ref="step1Ref"/>
</step>
<split id="step2" next="step3">
<flow id="step2a">
<step id="step2aStep1">
<batchlet ref="step1Ref"/>
</step>
</flow>
<flow id="step2b">
<step id="step2bStep1" next="step2bStep2">
<batchlet ref="step1Ref"/>
</step>
<step id="step2bStep2">
<chunk checkpoint-policy="item" item-count="3">
<reader ref="generatingItemReader1"/>
<processor ref="countingItemProcessor"/>
<writer ref="sysoutItemWriter"/>
</chunk>
</step>
</flow>
</split>
<step id="step3">
<batchlet ref="step1Ref"/>
</step>
</job>
<bean id="step1Ref" class="org.springframework.batch.core.step.tasklet.TaskletSupport"/>
<bean id="generatingItemReader1" class="org.springframework.batch.item.support.ListItemReader">
<constructor-arg>
<list>
<value>One</value>
<value>Two</value>
</list>
</constructor-arg>
</bean>
<bean id="countingItemProcessor" class="org.springframework.batch.core.jsr.configuration.xml.CountingItemProcessor"/>
<bean id="sysoutItemWriter" class="org.springframework.batch.item.adapter.ItemWriterAdapter">
<property name="targetObject">
<util:constant static-field="java.lang.System.out"/>
</property>
<property name="targetMethod" value="println"/>
</bean>
<bean id="jobRepository" class="org.springframework.batch.core.repository.support.MapJobRepositoryFactoryBean"/>
<bean id="transactionManager" class="org.springframework.batch.support.transaction.ResourcelessTransactionManager"/>
<bean id="jobLauncher" class="org.springframework.batch.core.launch.support.SimpleJobLauncher">
<property name="jobRepository" ref="jobRepository"/>
</bean>
<bean id="jsr352splitTaskExecutor" class="org.springframework.core.task.SyncTaskExecutor"/>
</beans>