Allow property injection into any artifact rather than just JSR interface impls.

* Remove isBatchArtifact check
* Reduce BatchArtifact.BatchArtifactType into BatchArtifactType
* Import JSR JSL definition from Spring config loaded by JsrJobOperator, update test
  for property injection into Tasklet and cleanup pre-parsing checks
This commit is contained in:
Chris Schaefer
2014-03-31 22:01:32 -04:00
parent 6fa04a2cee
commit 152abb11bd
16 changed files with 125 additions and 168 deletions

View File

@@ -1,116 +0,0 @@
/*
* Copyright 2013 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.jsr.configuration.support;
import javax.batch.api.Batchlet;
import javax.batch.api.Decider;
import javax.batch.api.chunk.CheckpointAlgorithm;
import javax.batch.api.chunk.ItemProcessor;
import javax.batch.api.chunk.ItemReader;
import javax.batch.api.chunk.ItemWriter;
import javax.batch.api.chunk.listener.ChunkListener;
import javax.batch.api.chunk.listener.ItemProcessListener;
import javax.batch.api.chunk.listener.ItemReadListener;
import javax.batch.api.chunk.listener.ItemWriteListener;
import javax.batch.api.chunk.listener.RetryProcessListener;
import javax.batch.api.chunk.listener.RetryReadListener;
import javax.batch.api.chunk.listener.RetryWriteListener;
import javax.batch.api.chunk.listener.SkipProcessListener;
import javax.batch.api.chunk.listener.SkipReadListener;
import javax.batch.api.chunk.listener.SkipWriteListener;
import javax.batch.api.listener.JobListener;
import javax.batch.api.listener.StepListener;
import javax.batch.api.partition.PartitionAnalyzer;
import javax.batch.api.partition.PartitionCollector;
import javax.batch.api.partition.PartitionMapper;
import javax.batch.api.partition.PartitionPlan;
import javax.batch.api.partition.PartitionReducer;
/**
* <p>
* Simple enum representing metadata about batch artifacts and types.
* </p>
*
* @author Chris Schaefer
*/
public enum BatchArtifact {
ITEM_READER(ItemReader.class),
ITEM_WRITER(ItemWriter.class),
ITEM_PROCESSOR(ItemProcessor.class),
CHECKPOINT_ALGORITHM(CheckpointAlgorithm.class),
BATCHLET(Batchlet.class),
ITEM_READ_LISTENER(ItemReadListener.class),
ITEM_PROCESS_LISTENER(ItemProcessListener.class),
ITEM_WRITE_LISTENER(ItemWriteListener.class),
JOB_LISTENER(JobListener.class),
STEP_LISTENER(StepListener.class),
CHUNK_LISTENER(ChunkListener.class),
SKIP_READ_LISTENER(SkipReadListener.class),
SKIP_PROCESS_LISTENER(SkipProcessListener.class),
SKIP_WRITER_LISTENER(SkipWriteListener.class),
RETRY_READ_LISTENER(RetryReadListener.class),
RETRY_PROCESS_LISTENER(RetryProcessListener.class),
RETRY_WRITE_LISTENER(RetryWriteListener.class),
PARTITION_MAPPER(PartitionMapper.class),
PARTITION_REDUCER(PartitionReducer.class),
PARTITION_COLLECTOR(PartitionCollector.class),
PARTITION_ANALYZER(PartitionAnalyzer.class),
PARTITION_PLAN(PartitionPlan.class),
DECIDER(Decider.class);
private Class<?> clazz;
private BatchArtifact(Class<?> clazz) {
this.clazz = clazz;
}
private Class<?> getBatchArtifactClass() {
return clazz;
}
/**
* <p>
* Determines if the provided artifact is a JSR-352 batch artifact.
* </p>
*
* @param artifact the artifact to check
* @return boolean answer based on check
*/
public static boolean isBatchArtifact(Object artifact) {
for (BatchArtifact batchArtifactType : BatchArtifact.values()) {
if (batchArtifactType.getBatchArtifactClass().isInstance(artifact)) {
return true;
}
}
return false;
}
/**
* <p>
* Enum to identify batch artifact types.
* </p>
*
* @author Chris Schaefer
* @since 3.0
*/
public enum BatchArtifactType {
STEP,
STEP_ARTIFACT,
ARTIFACT,
JOB
}
}

View File

@@ -0,0 +1,31 @@
/*
* Copyright 2014 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.jsr.configuration.support;
/**
* <p>
* Enum to identify batch artifact types.
* </p>
*
* @author Chris Schaefer
* @since 3.0
*/
public enum BatchArtifactType {
STEP,
STEP_ARTIFACT,
ARTIFACT,
JOB
}

View File

@@ -73,10 +73,6 @@ public class BatchPropertyBeanPostProcessor implements BeanPostProcessor, BeanFa
@Override
public Object postProcessBeforeInitialization(final Object artifact, String artifactName) throws BeansException {
if (! BatchArtifact.isBatchArtifact(artifact)) {
return artifact;
}
Properties artifactProperties = getArtifactProperties(artifactName);
if (artifactProperties.isEmpty()) {

View File

@@ -15,7 +15,7 @@
*/
package org.springframework.batch.core.jsr.configuration.xml;
import org.springframework.batch.core.jsr.configuration.support.BatchArtifact;
import org.springframework.batch.core.jsr.configuration.support.BatchArtifactType;
import org.springframework.batch.core.step.tasklet.Tasklet;
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.beans.factory.config.RuntimeBeanReference;
@@ -50,6 +50,6 @@ public class BatchletParser extends AbstractSingleBeanDefinitionParser {
bd.setRole(BeanDefinition.ROLE_SUPPORT);
bd.setSource(parserContext.extractSource(batchletElement));
new PropertyParser(taskletRef, parserContext, BatchArtifact.BatchArtifactType.STEP_ARTIFACT, stepName).parseProperties(batchletElement);
new PropertyParser(taskletRef, parserContext, BatchArtifactType.STEP_ARTIFACT, stepName).parseProperties(batchletElement);
}
}

View File

@@ -18,7 +18,7 @@ package org.springframework.batch.core.jsr.configuration.xml;
import java.util.List;
import org.springframework.batch.core.configuration.xml.ExceptionElementParser;
import org.springframework.batch.core.jsr.configuration.support.BatchArtifact;
import org.springframework.batch.core.jsr.configuration.support.BatchArtifactType;
import org.springframework.batch.core.step.item.ChunkOrientedTasklet;
import org.springframework.batch.item.ItemProcessor;
import org.springframework.batch.item.ItemReader;
@@ -129,19 +129,19 @@ public class ChunkParser {
propertyValues.addPropertyValue("stepItemReader", new RuntimeBeanReference(artifactName));
}
new PropertyParser(artifactName, parserContext, BatchArtifact.BatchArtifactType.STEP_ARTIFACT, stepName).parseProperties(nestedElement);
new PropertyParser(artifactName, parserContext, BatchArtifactType.STEP_ARTIFACT, stepName).parseProperties(nestedElement);
} else if(name.equals(PROCESSOR_ELEMENT)) {
if (StringUtils.hasText(artifactName)) {
propertyValues.addPropertyValue("stepItemProcessor", new RuntimeBeanReference(artifactName));
}
new PropertyParser(artifactName, parserContext, BatchArtifact.BatchArtifactType.STEP_ARTIFACT, stepName).parseProperties(nestedElement);
new PropertyParser(artifactName, parserContext, BatchArtifactType.STEP_ARTIFACT, stepName).parseProperties(nestedElement);
} else if(name.equals(WRITER_ELEMENT)) {
if (StringUtils.hasText(artifactName)) {
propertyValues.addPropertyValue("stepItemWriter", new RuntimeBeanReference(artifactName));
}
new PropertyParser(artifactName, parserContext, BatchArtifact.BatchArtifactType.STEP_ARTIFACT, stepName).parseProperties(nestedElement);
new PropertyParser(artifactName, parserContext, BatchArtifactType.STEP_ARTIFACT, stepName).parseProperties(nestedElement);
} else if(name.equals(SKIPPABLE_EXCEPTION_CLASSES_ELEMENT)) {
ManagedMap exceptionClasses = new ExceptionElementParser().parse(element, parserContext, SKIPPABLE_EXCEPTION_CLASSES_ELEMENT);
if(exceptionClasses != null) {
@@ -177,7 +177,7 @@ public class ChunkParser {
propertyValues.addPropertyValue("stepChunkCompletionPolicy", new RuntimeBeanReference(name));
}
new PropertyParser(name, parserContext, BatchArtifact.BatchArtifactType.STEP_ARTIFACT, stepName).parseProperties(checkpointAlgorithmElement);
new PropertyParser(name, parserContext, BatchArtifactType.STEP_ARTIFACT, stepName).parseProperties(checkpointAlgorithmElement);
} else if(elements.size() > 1){
parserContext.getReaderContext().error(
"The <checkpoint-algorithm/> element may not appear more than once in a single <"

View File

@@ -30,6 +30,7 @@ import org.springframework.beans.factory.support.AbstractBeanDefinition;
import org.springframework.beans.factory.support.BeanDefinitionBuilder;
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
import org.springframework.beans.factory.xml.DefaultBeanDefinitionDocumentReader;
import org.springframework.util.ClassUtils;
import org.w3c.dom.Element;
import org.w3c.dom.NamedNodeMap;
import org.w3c.dom.Node;
@@ -244,23 +245,24 @@ public class JsrBeanDefinitionDocumentReader extends DefaultBeanDefinitionDocume
referenceCountMap.put(resolvedValue, 0);
}
// possibly fully qualified class name in ref tag in the jobXML
if(!registry.containsBeanDefinition(resolvedValue)) {
boolean isClass = isClass(resolvedValue);
Integer referenceCount = referenceCountMap.get(resolvedValue);
// possibly fully qualified class name in ref tag in the JSL or pointer to bean/artifact ref.
if(isClass && !registry.containsBeanDefinition(resolvedValue)) {
AbstractBeanDefinition beanDefinition = BeanDefinitionBuilder.genericBeanDefinition(resolvedValue)
.getBeanDefinition();
beanDefinition.setScope("step");
registry.registerBeanDefinition(resolvedValue, beanDefinition);
newNodeValue = resolvedValue;
}
if (referenceCountMap.containsKey(resolvedValue)) {
Integer referenceCount = referenceCountMap.get(resolvedValue);
referenceCount++;
referenceCountMap.put(resolvedValue, referenceCount);
newNodeValue = resolvedValue + referenceCount;
} else {
if(registry.containsBeanDefinition(resolvedValue)) {
referenceCount++;
referenceCountMap.put(resolvedValue, referenceCount);
newNodeValue = resolvedValue + referenceCount;
BeanDefinition beanDefinition = registry.getBeanDefinition(resolvedValue);
registry.registerBeanDefinition(newNodeValue, beanDefinition);
}
@@ -282,6 +284,16 @@ public class JsrBeanDefinitionDocumentReader extends DefaultBeanDefinitionDocume
}
}
private boolean isClass(String className) {
try {
Class.forName(className, false, ClassUtils.getDefaultClassLoader());
} catch (ClassNotFoundException e) {
return false;
}
return true;
}
protected Properties getJobParameters() {
return propertyMap.get(JOB_PARAMETERS_KEY_NAME);
}

View File

@@ -18,7 +18,7 @@ package org.springframework.batch.core.jsr.configuration.xml;
import java.util.Collection;
import org.springframework.batch.core.job.flow.JobExecutionDecider;
import org.springframework.batch.core.jsr.configuration.support.BatchArtifact;
import org.springframework.batch.core.jsr.configuration.support.BatchArtifactType;
import org.springframework.batch.core.jsr.job.flow.support.state.JsrStepState;
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.beans.factory.config.RuntimeBeanReference;
@@ -62,7 +62,7 @@ public class JsrDecisionParser {
factoryDefinition.setAttribute("jobParserJobFactoryBeanRef", jobFactoryRef);
}
new PropertyParser(refAttribute, parserContext, BatchArtifact.BatchArtifactType.STEP_ARTIFACT, idAttribute).parseProperties(element);
new PropertyParser(refAttribute, parserContext, BatchArtifactType.STEP_ARTIFACT, idAttribute).parseProperties(element);
return FlowParser.getNextElements(parserContext, stateBuilder.getBeanDefinition(), element);
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2013 the original author or authors.
* Copyright 2013-2014 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.
@@ -17,7 +17,7 @@ package org.springframework.batch.core.jsr.configuration.xml;
import org.springframework.batch.core.configuration.xml.CoreNamespaceUtils;
import org.springframework.batch.core.jsr.JsrStepContextFactoryBean;
import org.springframework.batch.core.jsr.configuration.support.BatchArtifact;
import org.springframework.batch.core.jsr.configuration.support.BatchArtifactType;
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.beans.factory.support.AbstractBeanDefinition;
import org.springframework.beans.factory.support.BeanDefinitionBuilder;
@@ -61,7 +61,7 @@ public class JsrJobParser extends AbstractSingleBeanDefinitionParser {
builder.addPropertyValue("restartable", restartableAttribute);
}
new PropertyParser(jobName, parserContext, BatchArtifact.BatchArtifactType.JOB).parseProperties(element);
new PropertyParser(jobName, parserContext, BatchArtifactType.JOB).parseProperties(element);
BeanDefinition flowDef = new FlowParser(jobName, jobName).parse(element, parserContext);
builder.addPropertyValue("flow", flowDef);

View File

@@ -17,7 +17,7 @@ package org.springframework.batch.core.jsr.configuration.xml;
import java.util.List;
import org.springframework.batch.core.jsr.configuration.support.BatchArtifact;
import org.springframework.batch.core.jsr.configuration.support.BatchArtifactType;
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.beans.factory.config.RuntimeBeanReference;
import org.springframework.beans.factory.parsing.CompositeComponentDefinition;
@@ -135,8 +135,8 @@ public class ListenerParser {
return SCOPE_STEP;
}
private BatchArtifact.BatchArtifactType getBatchArtifactType(String stepName) {
return (stepName != null && !"".equals(stepName)) ? BatchArtifact.BatchArtifactType.STEP_ARTIFACT
: BatchArtifact.BatchArtifactType.ARTIFACT;
private BatchArtifactType getBatchArtifactType(String stepName) {
return (stepName != null && !"".equals(stepName)) ? BatchArtifactType.STEP_ARTIFACT
: BatchArtifactType.ARTIFACT;
}
}

View File

@@ -19,7 +19,7 @@ import java.util.List;
import java.util.concurrent.ConcurrentLinkedQueue;
import java.util.concurrent.locks.ReentrantLock;
import org.springframework.batch.core.jsr.configuration.support.BatchArtifact.BatchArtifactType;
import org.springframework.batch.core.jsr.configuration.support.BatchArtifactType;
import org.springframework.batch.core.jsr.partition.JsrPartitionHandler;
import org.springframework.beans.MutablePropertyValues;
import org.springframework.beans.factory.config.RuntimeBeanReference;

View File

@@ -20,7 +20,7 @@ import java.util.List;
import java.util.Map;
import java.util.Properties;
import org.springframework.batch.core.jsr.configuration.support.BatchArtifact;
import org.springframework.batch.core.jsr.configuration.support.BatchArtifactType;
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.beans.factory.support.ManagedMap;
import org.springframework.beans.factory.xml.ParserContext;
@@ -50,15 +50,15 @@ public class PropertyParser {
private String beanName;
private String stepName;
private ParserContext parserContext;
private BatchArtifact.BatchArtifactType batchArtifactType;
private BatchArtifactType batchArtifactType;
public PropertyParser(String beanName, ParserContext parserContext, BatchArtifact.BatchArtifactType batchArtifactType) {
public PropertyParser(String beanName, ParserContext parserContext, BatchArtifactType batchArtifactType) {
this.beanName = beanName;
this.parserContext = parserContext;
this.batchArtifactType = batchArtifactType;
}
public PropertyParser(String beanName, ParserContext parserContext, BatchArtifact.BatchArtifactType batchArtifactType, String stepName) {
public PropertyParser(String beanName, ParserContext parserContext, BatchArtifactType batchArtifactType, String stepName) {
this(beanName, parserContext, batchArtifactType);
this.stepName = stepName;
}
@@ -110,13 +110,13 @@ public class PropertyParser {
Object propertyValue;
BeanDefinition beanDefinition = parserContext.getRegistry().getBeanDefinition(BATCH_PROPERTY_CONTEXT_BEAN_NAME);
if(batchArtifactType.equals(BatchArtifact.BatchArtifactType.JOB)) {
if(batchArtifactType.equals(BatchArtifactType.JOB)) {
propertyValue = getJobProperties(properties);
} else if (batchArtifactType.equals(BatchArtifact.BatchArtifactType.STEP)) {
} else if (batchArtifactType.equals(BatchArtifactType.STEP)) {
propertyValue = getProperties(stepName, properties);
} else if (batchArtifactType.equals(BatchArtifact.BatchArtifactType.ARTIFACT)) {
} else if (batchArtifactType.equals(BatchArtifactType.ARTIFACT)) {
propertyValue = getProperties(beanName, properties);
} else if (batchArtifactType.equals(BatchArtifact.BatchArtifactType.STEP_ARTIFACT)) {
} else if (batchArtifactType.equals(BatchArtifactType.STEP_ARTIFACT)) {
propertyValue = getStepArtifactProperties(beanDefinition, properties);
} else {
throw new IllegalStateException("Unhandled BatchArtifactType of: " + batchArtifactType);
@@ -163,7 +163,7 @@ public class PropertyParser {
}
private void setJobPropertiesBean(Properties properties) {
if (batchArtifactType.equals(BatchArtifact.BatchArtifactType.JOB)) {
if (batchArtifactType.equals(BatchArtifactType.JOB)) {
Map<String, String> jobProperties = new HashMap<String, String>();
if (properties != null && !properties.isEmpty()) {
@@ -177,14 +177,14 @@ public class PropertyParser {
}
}
private String getPropertyName(BatchArtifact.BatchArtifactType batchArtifactType) {
if(batchArtifactType.equals(BatchArtifact.BatchArtifactType.JOB)) {
private String getPropertyName(BatchArtifactType batchArtifactType) {
if(batchArtifactType.equals(BatchArtifactType.JOB)) {
return JOB_PROPERTIES_PROPERTY_NAME;
} else if (batchArtifactType.equals(BatchArtifact.BatchArtifactType.STEP)) {
} else if (batchArtifactType.equals(BatchArtifactType.STEP)) {
return STEP_PROPERTIES_PROPERTY_NAME;
} else if (batchArtifactType.equals(BatchArtifact.BatchArtifactType.ARTIFACT)) {
} else if (batchArtifactType.equals(BatchArtifactType.ARTIFACT)) {
return ARTIFACT_PROPERTIES_PROPERTY_NAME;
} else if (batchArtifactType.equals(BatchArtifact.BatchArtifactType.STEP_ARTIFACT)) {
} else if (batchArtifactType.equals(BatchArtifactType.STEP_ARTIFACT)) {
return STEP_ARTIFACT_PROPERTIES_PROPERTY_NAME;
} else {
throw new IllegalStateException("Unhandled BatchArtifactType of: " + batchArtifactType);

View File

@@ -17,7 +17,7 @@ package org.springframework.batch.core.jsr.configuration.xml;
import java.util.Collection;
import org.springframework.batch.core.jsr.configuration.support.BatchArtifact;
import org.springframework.batch.core.jsr.configuration.support.BatchArtifactType;
import org.springframework.batch.core.jsr.job.flow.support.state.JsrStepState;
import org.springframework.batch.core.listener.StepListenerFactoryBean;
import org.springframework.beans.factory.config.BeanDefinition;
@@ -76,7 +76,7 @@ public class StepParser extends AbstractSingleBeanDefinitionParser {
}
new ListenerParser(StepListenerFactoryBean.class, "listeners").parseListeners(element, parserContext, bd, stepName);
new PropertyParser(stepName, parserContext, BatchArtifact.BatchArtifactType.STEP, stepName).parseProperties(element);
new PropertyParser(stepName, parserContext, BatchArtifactType.STEP, stepName).parseProperties(element);
// look at all nested elements
NodeList children = element.getChildNodes();

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2013 the original author or authors.
* Copyright 2013-2014 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.
@@ -33,7 +33,11 @@ import javax.batch.runtime.context.JobContext;
import javax.inject.Inject;
import org.junit.Test;
import org.springframework.batch.core.StepContribution;
import org.springframework.batch.core.jsr.JsrTestUtils;
import org.springframework.batch.core.scope.context.ChunkContext;
import org.springframework.batch.core.step.tasklet.Tasklet;
import org.springframework.batch.repeat.RepeatStatus;
import static junit.framework.Assert.assertEquals;
@@ -53,7 +57,7 @@ public class JobPropertyTests {
jobParameters.setProperty("deciderName", "stepDecider");
jobParameters.setProperty("deciderNumber", "1");
JobExecution jobExecution = JsrTestUtils.runJob("jsrJobPropertyTests", jobParameters, 5000L);
JobExecution jobExecution = JsrTestUtils.runJob("jsrJobPropertyTestsContext", jobParameters, 5000L);
assertEquals(BatchStatus.COMPLETED, jobExecution.getBatchStatus());
}
@@ -278,4 +282,17 @@ public class JobPropertyTests {
public void stop() throws Exception {
}
}
public static class TestTasklet implements Tasklet {
@Inject
@BatchProperty
private String p1;
@Override
public RepeatStatus execute(StepContribution contribution, ChunkContext chunkContext) throws Exception {
org.springframework.util.Assert.isTrue("p1val".equals(p1));
return RepeatStatus.FINISHED;
}
}
}

View File

@@ -149,7 +149,6 @@ public class JsrBeanDefinitionDocumentReaderTests {
assertTrue("exitStatusSettingStepListener3ClassBeanDefinition bean definition not found", applicationContext.containsBeanDefinition("org.springframework.batch.core.jsr.step.listener.ExitStatusSettingStepListener3"));
assertTrue("testBatchlet bean definition not found", applicationContext.containsBeanDefinition("testBatchlet"));
assertTrue("testBatchlet1 bean definition not found", applicationContext.containsBeanDefinition("testBatchlet1"));
assertTrue("testBatchlet2 bean definition not found", applicationContext.containsBeanDefinition("testBatchlet2"));
}
@Test

View File

@@ -69,7 +69,7 @@
</properties>
<next on="*" to="#{jobParameters['unresolving.prop']}?:#{jobProperties['step2name']};"/>
</decision>
<step id="step2">
<step id="step2" next="step3">
<!-- JSR Section 8.2.3 -->
<properties>
<property name="step2PropertyName1" value="step2PropertyValue1"/>
@@ -99,4 +99,11 @@
</properties>
</batchlet>
</step>
<step id="step3">
<batchlet ref="testSpringTasklet">
<properties>
<property name="p1" value="p1val"/>
</properties>
</batchlet>
</step>
</job>

View File

@@ -0,0 +1,11 @@
<?xml version="1.0" encoding="UTF-8"?>
<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">
<import resource="jsrJobPropertyTests.xml"/>
<bean id="testSpringTasklet"
class="org.springframework.batch.core.jsr.configuration.xml.JobPropertyTests$TestTasklet"
scope="step"/>
</beans>