BATCH-1443: add XML support for JobStep
This commit is contained in:
@@ -57,10 +57,18 @@ public abstract class AbstractStepParser {
|
||||
|
||||
private static final String REF_ELE = "ref";
|
||||
|
||||
private static final String REF_ATTR = "ref";
|
||||
|
||||
private static final String TASKLET_ELE = "tasklet";
|
||||
|
||||
private static final String PARTITION_ELE = "partition";
|
||||
|
||||
private static final String JOB_ELE = "job";
|
||||
|
||||
private static final String JOB_PARAMS_EXTRACTOR_ATTR = "job-parameters-extractor";
|
||||
|
||||
private static final String JOB_LAUNCHER_ATTR = "job-launcher";
|
||||
|
||||
private static final String STEP_ATTR = "step";
|
||||
|
||||
private static final String PARTITIONER_ATTR = "partitioner";
|
||||
@@ -118,6 +126,12 @@ public abstract class AbstractStepParser {
|
||||
parsePartition(stepElement, partitionElement, bd, parserContext, stepUnderspecified);
|
||||
}
|
||||
|
||||
Element jobElement = DomUtils.getChildElementByTagName(stepElement, JOB_ELE);
|
||||
if (jobElement != null) {
|
||||
boolean stepUnderspecified = CoreNamespaceUtils.isUnderspecified(stepElement);
|
||||
parseJob(stepElement, jobElement, bd, parserContext, stepUnderspecified);
|
||||
}
|
||||
|
||||
String parentRef = stepElement.getAttribute(PARENT_ATTR);
|
||||
if (StringUtils.hasText(parentRef)) {
|
||||
bd.setParentName(parentRef);
|
||||
@@ -187,6 +201,33 @@ public abstract class AbstractStepParser {
|
||||
|
||||
}
|
||||
|
||||
private void parseJob(Element stepElement, Element jobElement, AbstractBeanDefinition bd,
|
||||
ParserContext parserContext, boolean stepUnderspecified) {
|
||||
|
||||
bd.setBeanClass(StepParserStepFactoryBean.class);
|
||||
bd.setAttribute("isNamespaceStep", true);
|
||||
String jobRef = jobElement.getAttribute(REF_ATTR);
|
||||
|
||||
if (!StringUtils.hasText(jobRef)) {
|
||||
parserContext.getReaderContext().error("You must specify a job", jobElement);
|
||||
return;
|
||||
}
|
||||
|
||||
MutablePropertyValues propertyValues = bd.getPropertyValues();
|
||||
propertyValues.addPropertyValue("job", new RuntimeBeanReference(jobRef));
|
||||
|
||||
String jobParametersExtractor = jobElement.getAttribute(JOB_PARAMS_EXTRACTOR_ATTR);
|
||||
String jobLauncher = jobElement.getAttribute(JOB_LAUNCHER_ATTR);
|
||||
|
||||
if (StringUtils.hasText(jobParametersExtractor)) {
|
||||
propertyValues.addPropertyValue("jobParametersExtractor", new RuntimeBeanReference(jobParametersExtractor));
|
||||
}
|
||||
if (StringUtils.hasText(jobLauncher)) {
|
||||
propertyValues.addPropertyValue("jobLauncher", new RuntimeBeanReference(jobLauncher));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private void parseTasklet(Element stepElement, Element taskletElement, AbstractBeanDefinition bd,
|
||||
ParserContext parserContext, boolean stepUnderspecified) {
|
||||
|
||||
|
||||
@@ -21,11 +21,14 @@ import java.util.HashSet;
|
||||
import java.util.Map;
|
||||
|
||||
import org.springframework.batch.classify.BinaryExceptionClassifier;
|
||||
import org.springframework.batch.core.Job;
|
||||
import org.springframework.batch.core.Step;
|
||||
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.launch.JobLauncher;
|
||||
import org.springframework.batch.core.launch.support.SimpleJobLauncher;
|
||||
import org.springframework.batch.core.partition.PartitionHandler;
|
||||
import org.springframework.batch.core.partition.support.PartitionStep;
|
||||
import org.springframework.batch.core.partition.support.Partitioner;
|
||||
@@ -35,6 +38,8 @@ 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.job.JobParametersExtractor;
|
||||
import org.springframework.batch.core.step.job.JobStep;
|
||||
import org.springframework.batch.core.step.tasklet.Tasklet;
|
||||
import org.springframework.batch.core.step.tasklet.TaskletStep;
|
||||
import org.springframework.batch.item.ItemProcessor;
|
||||
@@ -94,6 +99,15 @@ class StepParserStepFactoryBean<I, O> implements FactoryBean, BeanNameAware {
|
||||
//
|
||||
private Flow flow;
|
||||
|
||||
//
|
||||
// Job Elements
|
||||
//
|
||||
private Job job;
|
||||
|
||||
private JobLauncher jobLauncher;
|
||||
|
||||
private JobParametersExtractor jobParametersExtractor;
|
||||
|
||||
//
|
||||
// Partition Elements
|
||||
//
|
||||
@@ -196,6 +210,11 @@ class StepParserStepFactoryBean<I, O> implements FactoryBean, BeanNameAware {
|
||||
configureFlowStep(ts);
|
||||
return ts;
|
||||
}
|
||||
else if (job != null) {
|
||||
JobStep ts = new JobStep();
|
||||
configureJobStep(ts);
|
||||
return ts;
|
||||
}
|
||||
else if (step != null) {
|
||||
PartitionStep ts = new PartitionStep();
|
||||
configurePartitionStep(ts);
|
||||
@@ -379,6 +398,24 @@ class StepParserStepFactoryBean<I, O> implements FactoryBean, BeanNameAware {
|
||||
}
|
||||
}
|
||||
|
||||
@SuppressWarnings("serial")
|
||||
private void configureJobStep(JobStep ts) throws Exception {
|
||||
configureAbstractStep(ts);
|
||||
if (job != null) {
|
||||
ts.setJob(job);
|
||||
}
|
||||
if (jobParametersExtractor != null) {
|
||||
ts.setJobParametersExtractor(jobParametersExtractor);
|
||||
}
|
||||
if (jobLauncher == null) {
|
||||
SimpleJobLauncher jobLauncher = new SimpleJobLauncher();
|
||||
jobLauncher.setJobRepository(jobRepository);
|
||||
jobLauncher.afterPropertiesSet();
|
||||
this.jobLauncher = jobLauncher;
|
||||
}
|
||||
ts.setJobLauncher(jobLauncher);
|
||||
}
|
||||
|
||||
private void validateFaultTolerantSettings() {
|
||||
validateDependency("skippable-exception-classes", skippableExceptionClasses, "skip-limit", skipLimit, true);
|
||||
validateDependency("retryable-exception-classes", retryableExceptionClasses, "retry-limit", retryLimit, true);
|
||||
@@ -474,6 +511,25 @@ class StepParserStepFactoryBean<I, O> implements FactoryBean, BeanNameAware {
|
||||
this.flow = flow;
|
||||
}
|
||||
|
||||
// =========================================================
|
||||
// Job Attributes
|
||||
// =========================================================
|
||||
|
||||
/**
|
||||
* @param flow the flow to set
|
||||
*/
|
||||
public void setJob(Job job) {
|
||||
this.job = job;
|
||||
}
|
||||
|
||||
public void setJobParametersExtractor(JobParametersExtractor jobParametersExtractor) {
|
||||
this.jobParametersExtractor = jobParametersExtractor;
|
||||
}
|
||||
|
||||
public void setJobLauncher(JobLauncher jobLauncher) {
|
||||
this.jobLauncher = jobLauncher;
|
||||
}
|
||||
|
||||
// =========================================================
|
||||
// Partition Attributes
|
||||
// =========================================================
|
||||
|
||||
@@ -40,6 +40,8 @@ public class DefaultJobParametersExtractor implements JobParametersExtractor {
|
||||
|
||||
private Set<String> keys = new HashSet<String>();
|
||||
|
||||
private boolean useAllParentParameters = true;
|
||||
|
||||
/**
|
||||
* The key names to pull out of the execution context or job parameters, if
|
||||
* they exist. If a key doesn't exist in the execution context then the job
|
||||
@@ -64,6 +66,11 @@ public class DefaultJobParametersExtractor implements JobParametersExtractor {
|
||||
JobParametersBuilder builder = new JobParametersBuilder();
|
||||
Map<String, JobParameter> jobParameters = stepExecution.getJobParameters().getParameters();
|
||||
ExecutionContext executionContext = stepExecution.getExecutionContext();
|
||||
if (useAllParentParameters) {
|
||||
for (String key : jobParameters.keySet()) {
|
||||
builder.addParameter(key, jobParameters.get(key));
|
||||
}
|
||||
}
|
||||
for (String key : keys) {
|
||||
if (key.endsWith("(long)")) {
|
||||
key = key.replace("(long)", "");
|
||||
|
||||
@@ -80,10 +80,8 @@ public class JobStep extends AbstractStep {
|
||||
/**
|
||||
* The {@link JobParametersExtractor} is used to extract
|
||||
* {@link JobParametersExtractor} from the {@link StepExecution} to run the
|
||||
* {@link Job}. By default an instance will be provided that simply creates
|
||||
* empty {@link JobParameters}. This is unlikely to be very useful, since
|
||||
* the {@link Job} normally cannot be run with empty parameters more than
|
||||
* once.
|
||||
* {@link Job}. By default an instance will be provided that simply copies
|
||||
* the {@link JobParameters} from the parent job.
|
||||
*
|
||||
* @param jobParametersExtractor the {@link JobParametersExtractor} to set
|
||||
*/
|
||||
|
||||
@@ -330,7 +330,7 @@
|
||||
<xsd:appinfo>
|
||||
<tool:annotation kind="ref">
|
||||
<tool:expected-type
|
||||
type="org.springframework.core.task.TaskExecutor" />
|
||||
type="java:org.springframework.core.task.TaskExecutor" />
|
||||
</tool:annotation>
|
||||
</xsd:appinfo>
|
||||
</xsd:annotation>
|
||||
@@ -403,6 +403,52 @@
|
||||
<xsd:choice minOccurs="0" maxOccurs="1">
|
||||
<xsd:element name="tasklet" type="taskletType" />
|
||||
<xsd:element name="partition" type="partitionType" />
|
||||
<xsd:element name="job">
|
||||
<xsd:complexType>
|
||||
<xsd:attribute name="ref">
|
||||
<xsd:annotation>
|
||||
<xsd:documentation
|
||||
source="java:org.springframework.batch.core.Job"><![CDATA[
|
||||
The job that will execute in this step.
|
||||
]]></xsd:documentation>
|
||||
<xsd:appinfo>
|
||||
<tool:annotation kind="ref">
|
||||
<tool:expected-type
|
||||
type="org.springframework.batch.core.Job" />
|
||||
</tool:annotation>
|
||||
</xsd:appinfo>
|
||||
</xsd:annotation>
|
||||
</xsd:attribute>
|
||||
<xsd:attribute name="job-launcher">
|
||||
<xsd:annotation>
|
||||
<xsd:documentation
|
||||
source="java:org.springframework.batch.core.launch.JobLauncher"><![CDATA[
|
||||
The job that will execute in this step.
|
||||
]]></xsd:documentation>
|
||||
<xsd:appinfo>
|
||||
<tool:annotation kind="ref">
|
||||
<tool:expected-type
|
||||
type="java:org.springframework.batch.core.launch.JobLauncher" />
|
||||
</tool:annotation>
|
||||
</xsd:appinfo>
|
||||
</xsd:annotation>
|
||||
</xsd:attribute>
|
||||
<xsd:attribute name="job-parameters-extractor">
|
||||
<xsd:annotation>
|
||||
<xsd:documentation
|
||||
source="java:org.springframework.batch.core.step.job.JobParametersExtractor"><![CDATA[
|
||||
The job parameters extractor to convert step execution into job parameters.
|
||||
]]></xsd:documentation>
|
||||
<xsd:appinfo>
|
||||
<tool:annotation kind="ref">
|
||||
<tool:expected-type
|
||||
type="java:org.springframework.batch.core.step.job.JobParametersExtractor" />
|
||||
</tool:annotation>
|
||||
</xsd:appinfo>
|
||||
</xsd:annotation>
|
||||
</xsd:attribute>
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
<xsd:element name="flow">
|
||||
<xsd:complexType>
|
||||
<xsd:attribute name="parent" type="xsd:string" use="required">
|
||||
|
||||
@@ -0,0 +1,97 @@
|
||||
/*
|
||||
* 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.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 JobStepParserTests {
|
||||
|
||||
@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 testFlowStep() 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, job1.flow, s4]", stepNames.toString());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testFlowExternalStep() 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("[job2.s1, job2.flow, job2.s4]", stepNames.toString());
|
||||
}
|
||||
|
||||
private List<String> getStepNames(JobExecution jobExecution) {
|
||||
List<String> list = new ArrayList<String>();
|
||||
for (StepExecution stepExecution : jobExecution.getStepExecutions()) {
|
||||
list.add(stepExecution.getStepName());
|
||||
}
|
||||
return list;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -44,6 +44,14 @@ public class DefaultJobParametersExtractorJobParametersTests {
|
||||
assertEquals("{foo=bar}", jobParameters.toString());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetAllJobParameters() throws Exception {
|
||||
StepExecution stepExecution = getStepExecution("foo=bar,spam=bucket");
|
||||
extractor.setKeys(new String[] {"foo", "bar"});
|
||||
JobParameters jobParameters = extractor.getJobParameters(null, stepExecution);
|
||||
assertEquals("{spam=bucket, foo=bar}", jobParameters.toString());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetNamedLongStringParameters() throws Exception {
|
||||
StepExecution stepExecution = getStepExecution("foo=bar");
|
||||
|
||||
@@ -0,0 +1,38 @@
|
||||
<?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" parent="step1" next="job1.flow" />
|
||||
<step id="job1.flow" next="s4">
|
||||
<job ref="flow" />
|
||||
</step>
|
||||
<step id="s4" parent="step4" />
|
||||
</job>
|
||||
|
||||
<job id="job2">
|
||||
<step id="job2.s1" parent="step1" next="job2.flow" />
|
||||
<step id="job2.flow" parent="flow.step" next="job2.s4" />
|
||||
<step id="job2.s4" parent="step4" />
|
||||
</job>
|
||||
|
||||
<job id="flow">
|
||||
<step id="s2" parent="step2" next="s3" />
|
||||
<step id="s3" parent="step3" />
|
||||
</job>
|
||||
|
||||
<step id="flow.step" abstract="true">
|
||||
<job ref="flow" job-launcher="jobLauncher" job-parameters-extractor="jobParametersExtractor"/>
|
||||
</step>
|
||||
|
||||
<beans:bean id="jobParametersExtractor" class="org.springframework.batch.core.step.job.DefaultJobParametersExtractor"/>
|
||||
|
||||
<beans:bean id="jobLauncher" class="org.springframework.batch.core.launch.support.SimpleJobLauncher">
|
||||
<beans:property name="jobRepository" ref="jobRepository"/>
|
||||
</beans:bean>
|
||||
|
||||
</beans:beans>
|
||||
Reference in New Issue
Block a user