BATCH-2002: JSR Property Substitution Language

This commit is contained in:
Chris Schaefer
2013-08-20 11:44:57 -04:00
parent fa43549e6c
commit 3f85c34f00
10 changed files with 582 additions and 88 deletions

View File

@@ -49,9 +49,16 @@ import javax.batch.api.partition.PartitionReducer;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.batch.core.scope.StepScope;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.beans.factory.BeanFactoryAware;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.config.BeanExpressionContext;
import org.springframework.beans.factory.config.BeanExpressionResolver;
import org.springframework.beans.factory.config.BeanPostProcessor;
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
import org.springframework.context.expression.StandardBeanExpressionResolver;
import org.springframework.util.ReflectionUtils;
/**
@@ -64,10 +71,12 @@ import org.springframework.util.ReflectionUtils;
* @author Michael Minella
* @since 3.0
*/
public class BatchPropertyBeanPostProcessor implements BeanPostProcessor {
@Autowired
public class BatchPropertyBeanPostProcessor implements BeanPostProcessor, BeanFactoryAware {
private BatchPropertyContext batchPropertyContext;
private Log logger = LogFactory.getLog(getClass());
private ConfigurableListableBeanFactory beanFactory;
private BeanExpressionContext beanExpressionContext;
private BeanExpressionResolver expressionResolver = new StandardBeanExpressionResolver();
private Set<Class<? extends Annotation>> requiredAnnotations = new HashSet<Class<? extends Annotation>>();
public BatchPropertyBeanPostProcessor() {
@@ -96,12 +105,12 @@ public class BatchPropertyBeanPostProcessor implements BeanPostProcessor {
ClassLoader cl = BatchPropertyBeanPostProcessor.class.getClassLoader();
try {
this.requiredAnnotations.add((Class<? extends Annotation>) cl.loadClass("javax.inject.Inject"));
requiredAnnotations.add((Class<? extends Annotation>) cl.loadClass("javax.inject.Inject"));
} catch (ClassNotFoundException ex) {
logger.warn("javax.inject.Inject not found - @BatchProperty marked fields will not be processed.");
}
this.requiredAnnotations.add(BatchProperty.class);
requiredAnnotations.add(BatchProperty.class);
}
private boolean isBatchArtifact(Object bean) {
@@ -162,7 +171,9 @@ public class BatchPropertyBeanPostProcessor implements BeanPostProcessor {
private String getBatchProperty(String propertyKey, Properties batchArtifactProperties) {
if (batchArtifactProperties.containsKey(propertyKey)) {
return (String) batchArtifactProperties.get(propertyKey);
String propertyValue = (String) batchArtifactProperties.get(propertyKey);
return (String) expressionResolver.evaluate(propertyValue, beanExpressionContext);
}
return null;
@@ -170,7 +181,7 @@ public class BatchPropertyBeanPostProcessor implements BeanPostProcessor {
private boolean isAnnotated(Field field) {
for (Class<? extends Annotation> annotation : requiredAnnotations) {
if(!field.isAnnotationPresent(annotation)) {
if (!field.isAnnotationPresent(annotation)) {
return false;
}
}
@@ -186,4 +197,20 @@ public class BatchPropertyBeanPostProcessor implements BeanPostProcessor {
public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
return bean;
}
@Override
public void setBeanFactory(BeanFactory beanFactory) throws BeansException {
if (!(beanFactory instanceof ConfigurableListableBeanFactory)) {
throw new IllegalArgumentException(
"BatchPropertyBeanPostProcessor requires a ConfigurableListableBeanFactory");
}
this.beanFactory = (ConfigurableListableBeanFactory) beanFactory;
this.beanExpressionContext = new BeanExpressionContext(this.beanFactory, this.beanFactory.getBean(StepScope.class));
}
@Autowired
public void setBatchPropertyContext(BatchPropertyContext batchPropertyContext) {
this.batchPropertyContext = batchPropertyContext;
}
}

View File

@@ -31,11 +31,13 @@ import java.util.concurrent.ConcurrentHashMap;
* @since 3.0
*/
public class BatchPropertyContext {
private static final String JOB_ARTIFACT_PROPERTY_PREFIX = "job-";
private ConcurrentHashMap<String, Properties> batchProperties = new ConcurrentHashMap<String, Properties>();
/**
* <p>
* Adds each of the provided {@link BatchPropertyContext} objects to the existing propery
* Adds each of the provided {@link BatchPropertyContext} objects to the existing property
* context.
* </p>
*
@@ -61,19 +63,21 @@ public class BatchPropertyContext {
}
}
/**
* <p>
* Obtains all properties for the specific batch artifact / bean name without any job level
* properties.
* </p>
*
* @param beanName the batch artifact / bean name to obtain {@link Properties} for
* @return the step level {@link Properties}
*/
public Properties getStepLevelProperties(String beanName) {
Properties properties = new Properties();
String originalBeanName = getOriginalBeanName(beanName);
if (batchProperties.containsKey(beanName)) {
properties.putAll(batchProperties.get(beanName));
} else {
if(beanName.startsWith("scopedTarget")) {
beanName = beanName.substring(13);
}
if(batchProperties.containsKey(beanName)) {
properties.putAll(batchProperties.get(beanName));
}
if (batchProperties.containsKey(originalBeanName)) {
properties.putAll(batchProperties.get(originalBeanName));
}
return properties;
@@ -82,7 +86,8 @@ public class BatchPropertyContext {
/**
* <p>
* Obtains the batch {@link Properties} for the provided bean name / batch artifact. The returned
* {@link Properties} will also contain any job level properties that have been set.
* {@link Properties} will also contain any job level properties that have been set. Job level
* properties will not override existing lower level artifact properties.
* </p>
*
* @param beanName the bean name representing the batch artifact to obtain properties for
@@ -90,32 +95,53 @@ public class BatchPropertyContext {
*/
public Properties getBatchProperties(String beanName) {
Properties properties = new Properties();
String originalBeanName = getOriginalBeanName(beanName);
if (batchProperties.containsKey(beanName)) {
properties.putAll(batchProperties.get(beanName));
} else {
if(beanName.startsWith("scopedTarget")) {
beanName = beanName.substring(13);
}
if(batchProperties.containsKey(beanName)) {
properties.putAll(batchProperties.get(beanName));
}
if (batchProperties.containsKey(originalBeanName)) {
properties.putAll(batchProperties.get(originalBeanName));
}
for (String jobLevelProperty : batchProperties.keySet()) {
if (jobLevelProperty.startsWith("job-")) {
if (batchProperties.containsKey(jobLevelProperty)) {
properties.putAll(batchProperties.get(jobLevelProperty));
}
Properties jobLevelProperties = getJobProperties();
break;
for (String jobLevelProperty : jobLevelProperties.stringPropertyNames()) {
if (!properties.containsKey(jobLevelProperty)) {
properties.put(jobLevelProperty, jobLevelProperties.getProperty(jobLevelProperty));
}
}
return properties;
}
/**
* <p>
* Obtains all {@link Properties} that reside at the Job level configuration.
* </p>
*
* @return the job level {@link Properties}
*/
public Properties getJobProperties() {
Properties jobProperties = new Properties();
for (String jobLevelProperty : batchProperties.keySet()) {
if (jobLevelProperty.startsWith(JOB_ARTIFACT_PROPERTY_PREFIX)) {
if (batchProperties.containsKey(jobLevelProperty)) {
jobProperties.putAll(batchProperties.get(jobLevelProperty));
break;
}
}
}
return jobProperties;
}
protected String getOriginalBeanName(String beanName) {
if (beanName.startsWith("scopedTarget")) {
return beanName.substring(13);
}
return beanName;
}
/**
* <p>
* Simple object to encapsulate batch properties of a given bean / batch artifact.

View File

@@ -66,7 +66,6 @@ public class JobParser extends AbstractSingleBeanDefinitionParser {
parserContext.getRegistry().registerBeanDefinition("stepContextFactory", stepContextBeanDefinition);
new ListnerParser(JobListenerFactoryBean.class, "jobExecutionListeners").parseListeners(element, parserContext, builder);
new PropertyParser("job-" + jobName, parserContext).parseProperties(element);
new PropertyParser(PropertyParser.JOB_ARTIFACT_PROPERTY_PREFIX + jobName, parserContext).parseProperties(element);
}
}

View File

@@ -15,7 +15,9 @@
*/
package org.springframework.batch.core.jsr.configuration.xml;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Properties;
import org.springframework.batch.core.jsr.configuration.support.BatchPropertyContext;
import org.springframework.beans.factory.config.BeanDefinition;
@@ -28,30 +30,35 @@ import org.w3c.dom.Element;
/**
* <p>
* Parser for the &lt;properties /&gt; element defined by JSR-352.
* Parser for the &lt;properties /&gt; element defined by JSR-352. Parsed job level properties are
* also registered as a Map similar to systemProperties. Job level properties can be obtained for
* example through SPeL by referencing #{jobProperties['key']}.
* </p>
*
* @author Chris Schaefer
* @since 3.0
*/
public class PropertyParser {
private static final String PROPERTY_ELEMENT = "property";
private static final String PROPERTIES_ELEMENT = "properties";
private static final String PROPERTY_NAME_ATTRIBUTE = "name";
private static final String PROPERTY_VALUE_ATTRIBUTE = "value";
private static final String BATCH_CONTEXT_ENTRIES_PROPERTY_NAME = "batchContextEntries";
private static final String BATCH_PROPERTY_CONTEXT_BEAN_CLASS_NAME = "org.springframework.batch.core.jsr.configuration.support.BatchPropertyContext";
private static final String BATCH_PROPERTY_CONTEXT_BEAN_NAME = "batchPropertyContext";
static final String JOB_ARTIFACT_PROPERTY_PREFIX = "job-";
private static final String PROPERTY_ELEMENT = "property";
private static final String PROPERTIES_ELEMENT = "properties";
private static final String PROPERTY_NAME_ATTRIBUTE = "name";
private static final String PROPERTY_VALUE_ATTRIBUTE = "value";
private static final String JOB_PROPERTIES_BEAN_NAME = "jobProperties";
private static final String BATCH_CONTEXT_ENTRIES_PROPERTY_NAME = "batchContextEntries";
private static final String BATCH_PROPERTY_CONTEXT_BEAN_CLASS_NAME = "org.springframework.batch.core.jsr.configuration.support.BatchPropertyContext";
private static final String BATCH_PROPERTY_CONTEXT_BEAN_NAME = "batchPropertyContext";
private String beanName;
private ParserContext parserContext;
private String beanName;
private ParserContext parserContext;
public PropertyParser(String beanName, ParserContext parserContext) {
this.beanName = beanName;
this.parserContext = parserContext;
public PropertyParser(String beanName, ParserContext parserContext) {
this.beanName = beanName;
this.parserContext = parserContext;
registerBatchPropertyContext();
}
registerBatchPropertyContext();
registerJobProperties();
}
/**
* <p>
@@ -60,27 +67,29 @@ public class PropertyParser {
* which represent the property entries key and value.
* </p>
*
* @param element
* @param element the element to parse looking for &lt;properties /&gt;
*/
public void parseProperties(Element element) {
List<Element> propertiesElements = DomUtils.getChildElementsByTagName(element, PROPERTIES_ELEMENT);
public void parseProperties(Element element) {
List<Element> propertiesElements = DomUtils.getChildElementsByTagName(element, PROPERTIES_ELEMENT);
Properties properties = new Properties();
Properties properties = new Properties();
if (propertiesElements.size() == 1) {
List<Element> propertyElements = DomUtils.getChildElementsByTagName(propertiesElements.get(0), PROPERTY_ELEMENT);
if (propertiesElements.size() == 1) {
List<Element> propertyElements = DomUtils.getChildElementsByTagName(propertiesElements.get(0), PROPERTY_ELEMENT);
for (Element propertyElement : propertyElements) {
properties.put(propertyElement.getAttribute(PROPERTY_NAME_ATTRIBUTE), propertyElement.getAttribute(PROPERTY_VALUE_ATTRIBUTE));
}
for (Element propertyElement : propertyElements) {
properties.put(propertyElement.getAttribute(PROPERTY_NAME_ATTRIBUTE), propertyElement.getAttribute(PROPERTY_VALUE_ATTRIBUTE));
}
addProperties(properties);
} else if (propertiesElements.size() > 1) {
parserContext.getReaderContext().error("The <properties> element may not appear more than once in a single <listener>.", element);
}
}
addProperties(properties);
} else if (propertiesElements.size() > 1) {
parserContext.getReaderContext().error("The <properties> element may not appear more than once in a single <listener>.", element);
}
private void addProperties(Properties properties) {
setJobProperties(properties);
}
private void addProperties(Properties properties) {
BeanDefinition beanDefinition = parserContext.getRegistry().getBeanDefinition(BATCH_PROPERTY_CONTEXT_BEAN_NAME);
BatchPropertyContext batchPropertyContext = new BatchPropertyContext();
@@ -92,17 +101,41 @@ public class PropertyParser {
managedList.add(batchPropertyContextEntry);
beanDefinition.getPropertyValues().addPropertyValue(BATCH_CONTEXT_ENTRIES_PROPERTY_NAME, managedList);
}
}
private void registerBatchPropertyContext() {
if (!parserContext.getRegistry().containsBeanDefinition(BATCH_PROPERTY_CONTEXT_BEAN_NAME)) {
BeanDefinitionBuilder batchPropertyContextBeanDefinitionBuilder =
BeanDefinitionBuilder.genericBeanDefinition(BATCH_PROPERTY_CONTEXT_BEAN_CLASS_NAME);
private void registerBatchPropertyContext() {
if (!parserContext.getRegistry().containsBeanDefinition(BATCH_PROPERTY_CONTEXT_BEAN_NAME)) {
BeanDefinitionBuilder batchPropertyContextBeanDefinitionBuilder =
BeanDefinitionBuilder.genericBeanDefinition(BATCH_PROPERTY_CONTEXT_BEAN_CLASS_NAME);
AbstractBeanDefinition batchPropertyContextBeanDefinition = batchPropertyContextBeanDefinitionBuilder.getBeanDefinition();
batchPropertyContextBeanDefinition.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);
AbstractBeanDefinition batchPropertyContextBeanDefinition = batchPropertyContextBeanDefinitionBuilder.getBeanDefinition();
batchPropertyContextBeanDefinition.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);
parserContext.getRegistry().registerBeanDefinition(BATCH_PROPERTY_CONTEXT_BEAN_NAME, batchPropertyContextBeanDefinition);
}
}
parserContext.getRegistry().registerBeanDefinition(BATCH_PROPERTY_CONTEXT_BEAN_NAME, batchPropertyContextBeanDefinition);
}
}
private void registerJobProperties() {
if (!parserContext.getRegistry().containsBeanDefinition(JOB_PROPERTIES_BEAN_NAME)) {
AbstractBeanDefinition jobPropertiesBeanDefinition = BeanDefinitionBuilder.genericBeanDefinition(HashMap.class).getBeanDefinition();
jobPropertiesBeanDefinition.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);
parserContext.getRegistry().registerBeanDefinition(JOB_PROPERTIES_BEAN_NAME, jobPropertiesBeanDefinition);
}
}
private void setJobProperties(Properties properties) {
if (beanName.startsWith(JOB_ARTIFACT_PROPERTY_PREFIX)) {
Map<String, String> jobProperties = new HashMap<String, String>();
if (properties != null) {
for (String param : properties.stringPropertyNames()) {
jobProperties.put(param, properties.getProperty(param));
}
}
BeanDefinition jobPropertiesBeanDefinition = parserContext.getRegistry().getBeanDefinition(JOB_PROPERTIES_BEAN_NAME);
jobPropertiesBeanDefinition.getConstructorArgumentValues().addGenericArgumentValue(jobProperties);
}
}
}

View File

@@ -502,6 +502,7 @@ public class JsrJobOperator implements JobOperator {
batchContext.setParent(baseContext);
batchContext.refresh();
final Job job = batchContext.getBean(Job.class);
Assert.notNull(jobName, "The job name must not be null.");

View File

@@ -0,0 +1,214 @@
/*
* 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 java.util.ArrayList;
import java.util.List;
import java.util.Properties;
import org.junit.Before;
import org.junit.Test;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;
/**
* <p>
* Test cases around {@link BatchPropertyContext}.
* </p>
*
* @author Chris Schaefer
*/
public class BatchPropertyContextTests {
private List<BatchPropertyContext.BatchPropertyContextEntry> entries = new ArrayList<BatchPropertyContext.BatchPropertyContextEntry>();
@Before
public void setUp() {
BatchPropertyContext batchPropertyContext = new BatchPropertyContext();
Properties bean1Properties = new Properties();
bean1Properties.setProperty("readerName", "bean1readerName");
bean1Properties.setProperty("readerWriter", "bean1writerName");
entries.add(batchPropertyContext.new BatchPropertyContextEntry("bean1", bean1Properties));
Properties bean2Properties = new Properties();
bean2Properties.setProperty("readerName", "bean2readerName");
bean2Properties.setProperty("readerWriter", "bean2writerName");
entries.add(batchPropertyContext.new BatchPropertyContextEntry("bean2", bean2Properties));
Properties jobProperties = new Properties();
jobProperties.setProperty("jobProperty1", "jobProperty1value");
jobProperties.setProperty("jobProperty2", "jobProperty2value");
entries.add(batchPropertyContext.new BatchPropertyContextEntry("job-testJob", jobProperties));
}
@Test
public void testAddBatchContextEntries() {
BatchPropertyContext batchPropertyContext = new BatchPropertyContext();
batchPropertyContext.setBatchContextEntries(entries);
Properties bean1 = batchPropertyContext.getBatchProperties("bean1");
assertEquals(4, bean1.size());
assertEquals("bean1readerName", bean1.getProperty("readerName"));
assertEquals("bean1writerName", bean1.getProperty("readerWriter"));
assertEquals("jobProperty1value", bean1.getProperty("jobProperty1"));
assertEquals("jobProperty2value", bean1.getProperty("jobProperty2"));
Properties bean2 = batchPropertyContext.getBatchProperties("bean2");
assertEquals(4, bean2.size());
assertEquals("bean2readerName", bean2.getProperty("readerName"));
assertEquals("bean2writerName", bean2.getProperty("readerWriter"));
assertEquals("jobProperty1value", bean2.getProperty("jobProperty1"));
assertEquals("jobProperty2value", bean2.getProperty("jobProperty2"));
Properties jobProperties = batchPropertyContext.getBatchProperties("job-testJob");
assertEquals(2, jobProperties.size());
assertEquals("jobProperty1value", jobProperties.getProperty("jobProperty1"));
assertEquals("jobProperty2value", jobProperties.getProperty("jobProperty2"));
}
@Test
public void testAddBatchContextEntriesToExistingArtifact() {
BatchPropertyContext batchPropertyContext = new BatchPropertyContext();
Properties bean2Properties = new Properties();
bean2Properties.setProperty("processorName", "bean2processorName");
entries.add(batchPropertyContext.new BatchPropertyContextEntry("bean2", bean2Properties));
batchPropertyContext.setBatchContextEntries(entries);
Properties bean2 = batchPropertyContext.getBatchProperties("bean2");
assertEquals(5, bean2.size());
assertEquals("bean2readerName", bean2.getProperty("readerName"));
assertEquals("bean2writerName", bean2.getProperty("readerWriter"));
assertEquals("bean2processorName", bean2.getProperty("processorName"));
assertEquals("jobProperty1value", bean2.getProperty("jobProperty1"));
assertEquals("jobProperty2value", bean2.getProperty("jobProperty2"));
}
@Test
public void testGetStepLevelProperties() {
BatchPropertyContext batchPropertyContext = new BatchPropertyContext();
batchPropertyContext.setBatchContextEntries(entries);
Properties bean1 = batchPropertyContext.getStepLevelProperties("bean1");
assertEquals(2, bean1.size());
assertEquals("bean1readerName", bean1.getProperty("readerName"));
assertEquals("bean1writerName", bean1.getProperty("readerWriter"));
Properties bean2 = batchPropertyContext.getStepLevelProperties("bean2");
assertEquals(2, bean2.size());
assertEquals("bean2readerName", bean2.getProperty("readerName"));
assertEquals("bean2writerName", bean2.getProperty("readerWriter"));
}
@Test
public void testGetScopedStepLevelProperties() {
BatchPropertyContext batchPropertyContext = new BatchPropertyContext();
Properties scopedBeanProperties = new Properties();
scopedBeanProperties.setProperty("scopedBeanName", "scopedBeanValue");
entries.add(batchPropertyContext.new BatchPropertyContextEntry("scopedBean", scopedBeanProperties));
batchPropertyContext.setBatchContextEntries(entries);
Properties bean1 = batchPropertyContext.getStepLevelProperties("bean1");
assertEquals(2, bean1.size());
assertEquals("bean1readerName", bean1.getProperty("readerName"));
assertEquals("bean1writerName", bean1.getProperty("readerWriter"));
Properties bean2 = batchPropertyContext.getStepLevelProperties("bean2");
assertEquals(2, bean2.size());
assertEquals("bean2readerName", bean2.getProperty("readerName"));
assertEquals("bean2writerName", bean2.getProperty("readerWriter"));
Properties scopedBean = batchPropertyContext.getStepLevelProperties("scopedTarget.scopedBean");
assertEquals(1, scopedBean.size());
assertEquals("scopedBeanValue", scopedBean.getProperty("scopedBeanName"));
}
@Test
public void testGetScopedBatchProperties() {
BatchPropertyContext batchPropertyContext = new BatchPropertyContext();
Properties scopedBeanProperties = new Properties();
scopedBeanProperties.setProperty("scopedBeanName", "scopedBeanValue");
entries.add(batchPropertyContext.new BatchPropertyContextEntry("scopedBean", scopedBeanProperties));
batchPropertyContext.setBatchContextEntries(entries);
Properties bean1 = batchPropertyContext.getBatchProperties("bean1");
assertEquals(4, bean1.size());
assertEquals("bean1readerName", bean1.getProperty("readerName"));
assertEquals("bean1writerName", bean1.getProperty("readerWriter"));
assertEquals("jobProperty1value", bean1.getProperty("jobProperty1"));
assertEquals("jobProperty2value", bean1.getProperty("jobProperty2"));
Properties bean2 = batchPropertyContext.getBatchProperties("bean2");
assertEquals(4, bean2.size());
assertEquals("bean2readerName", bean2.getProperty("readerName"));
assertEquals("bean2writerName", bean2.getProperty("readerWriter"));
assertEquals("jobProperty1value", bean2.getProperty("jobProperty1"));
assertEquals("jobProperty2value", bean2.getProperty("jobProperty2"));
Properties scopedBean = batchPropertyContext.getBatchProperties("scopedTarget.scopedBean");
assertEquals(3, scopedBean.size());
assertEquals("scopedBeanValue", scopedBean.getProperty("scopedBeanName"));
assertEquals("jobProperty1value", scopedBean.getProperty("jobProperty1"));
assertEquals("jobProperty2value", scopedBean.getProperty("jobProperty2"));
}
@Test
public void testGetOriginalBeanName() {
BatchPropertyContext batchPropertyContext = new BatchPropertyContext();
String originalName = batchPropertyContext.getOriginalBeanName("scopedTarget.myBean");
assertTrue(originalName.equals("myBean"));
}
@Test
public void testJobProperties() {
BatchPropertyContext batchPropertyContext = new BatchPropertyContext();
batchPropertyContext.setBatchContextEntries(entries);
Properties jobProperties = batchPropertyContext.getJobProperties();
assertEquals(2, jobProperties.size());
assertEquals("jobProperty1value", jobProperties.getProperty("jobProperty1"));
assertEquals("jobProperty2value", jobProperties.getProperty("jobProperty2"));
}
@Test
public void testJobNonOverridingJobProperties() {
BatchPropertyContext batchPropertyContext = new BatchPropertyContext();
Properties jobProperties = new Properties();
jobProperties.setProperty("readerName", "testJobreaderName");
entries.add(batchPropertyContext.new BatchPropertyContextEntry("job-testJob", jobProperties));
batchPropertyContext.setBatchContextEntries(entries);
Properties bean1 = batchPropertyContext.getBatchProperties("bean1");
assertEquals(4, bean1.size());
assertEquals("bean1readerName", bean1.getProperty("readerName"));
assertEquals("bean1writerName", bean1.getProperty("readerWriter"));
assertEquals("jobProperty1value", bean1.getProperty("jobProperty1"));
assertEquals("jobProperty2value", bean1.getProperty("jobProperty2"));
Properties testJobBean = batchPropertyContext.getBatchProperties("job-testJob");
assertEquals(3, testJobBean.size());
assertEquals("testJobreaderName", testJobBean.getProperty("readerName"));
assertEquals("jobProperty1value", testJobBean.getProperty("jobProperty1"));
assertEquals("jobProperty2value", testJobBean.getProperty("jobProperty2"));
}
}

View File

@@ -0,0 +1,148 @@
/*
* 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.xml;
import java.io.Serializable;
import java.util.List;
import javax.batch.api.BatchProperty;
import javax.batch.api.chunk.ItemProcessor;
import javax.batch.api.chunk.ItemReader;
import javax.batch.api.chunk.ItemWriter;
import javax.inject.Inject;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.batch.core.ExitStatus;
import org.springframework.batch.core.Job;
import org.springframework.batch.core.JobExecution;
import org.springframework.batch.core.JobParametersBuilder;
import org.springframework.batch.core.launch.JobLauncher;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import static org.junit.Assert.assertEquals;
/**
* <p>
* Test cases for JSR-352 job property substitution.
* </p>
*
* TODO: enhance test cases with more complex substitutions
*
* @author Chris Schaefer
* @since 3.0
*/
@ContextConfiguration
@RunWith(SpringJUnit4ClassRunner.class)
public class JobPropertySubstitutionTests {
@Autowired
private Job job;
@Autowired
private JobLauncher jobLauncher;
@Autowired
private TestItemWriter testItemWriter;
@Autowired
private TestItemReader testItemReader;
@Test
public void testSystemPropertySubstitution() throws Exception {
assertEquals(System.getProperty("file.separator"), testItemReader.readerPropertyName1);
}
@Test
public void testJobPropertySubstitution() throws Exception {
assertEquals("jobPropertyValue1", testItemWriter.writerPropertyName1);
}
@Test
public void testJobParameterSubstitution() throws Exception {
JobExecution jobExecution = jobLauncher.run(job,
new JobParametersBuilder().addString("testParam", "testParamValue").toJobParameters());
assertEquals(ExitStatus.COMPLETED, jobExecution.getExitStatus());
}
public static final class TestItemReader implements ItemReader {
private int cnt;
@Inject
@BatchProperty
String readerPropertyName1;
@Override
public void open(Serializable serializable) throws Exception {
}
@Override
public void close() throws Exception {
}
@Override
public Object readItem() throws Exception {
if (cnt == 0) {
cnt++;
return "blah";
}
return null;
}
@Override
public Serializable checkpointInfo() throws Exception {
return null;
}
}
public static final class TestItemWriter implements ItemWriter {
@Inject
@BatchProperty
String writerPropertyName1;
@Override
public void open(Serializable serializable) throws Exception {
}
@Override
public void close() throws Exception {
}
@Override
public void writeItems(List<Object> objects) throws Exception {
System.out.println(objects);
}
@Override
public Serializable checkpointInfo() throws Exception {
return null;
}
}
public static final class TestItemProcessor implements ItemProcessor {
@Inject
@BatchProperty
String processorProperty1;
@Override
public Object processItem(Object item) throws Exception {
assertEquals("testParamValue", processorProperty1);
return item;
}
}
}

View File

@@ -41,7 +41,6 @@ import org.springframework.batch.core.JobExecution;
import org.springframework.batch.core.JobParameters;
import org.springframework.batch.core.launch.JobLauncher;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationContext;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
@@ -82,9 +81,6 @@ public class JobPropertyTests {
@Autowired
private TestBatchlet testBatchlet;
@Autowired
private ApplicationContext applicationContext;
@Test
public void testJobLevelPropertiesInItemReader() throws Exception {
assertEquals("jobPropertyValue1", testItemReader.getJobPropertyName1());

View File

@@ -0,0 +1,50 @@
<?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
http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/jobXML_1_0.xsd">
<!-- Not implemented (partition relatated): JSR 8.2.6.2, 8.2.6.3.1, 8.2.6.4.1, 8.2.6.5.1, 8.2.6.6.1 -->
<job id="job1" xmlns="http://xmlns.jcp.org/xml/ns/javaee" version="1.0">
<properties>
<property name="jobPropertyName1" value="jobPropertyValue1"/>
</properties>
<step id="step1">
<chunk>
<reader ref="testReader">
<properties>
<property name="readerPropertyName1" value="#{systemProperties['file.separator']}"/>
</properties>
</reader>
<processor ref="testProcessor">
<properties>
<property name="processorProperty1" value="#{jobParameters['testParam']}"/>
</properties>
</processor>
<writer ref="testWriter">
<properties>
<property name="writerPropertyName1" value="#{jobProperties['jobPropertyName1']}"/>
</properties>
</writer>
</chunk>
</step>
</job>
<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="jobRepository" class="org.springframework.batch.core.repository.support.MapJobRepositoryFactoryBean"/>
<bean id="testReader" class="org.springframework.batch.core.jsr.configuration.xml.JobPropertySubstitutionTests$TestItemReader"/>
<bean id="testWriter" class="org.springframework.batch.core.jsr.configuration.xml.JobPropertySubstitutionTests$TestItemWriter"/>
<bean id="testProcessor" class="org.springframework.batch.core.jsr.configuration.xml.JobPropertySubstitutionTests$TestItemProcessor"
scope="step"/>
</beans>

View File

@@ -106,20 +106,20 @@
<bean id="jobRepository" class="org.springframework.batch.core.repository.support.MapJobRepositoryFactoryBean"/>
<bean id="testReader" class="org.springframework.batch.core.jsr.configuration.xml.JobPropertyTests.TestItemReader"/>
<bean id="testReader" class="org.springframework.batch.core.jsr.configuration.xml.JobPropertyTests$TestItemReader"/>
<bean id="testProcessor" class="org.springframework.batch.core.jsr.configuration.xml.JobPropertyTests.TestItemProcessor"/>
<bean id="testProcessor" class="org.springframework.batch.core.jsr.configuration.xml.JobPropertyTests$TestItemProcessor"/>
<bean id="testWriter" class="org.springframework.batch.core.jsr.configuration.xml.JobPropertyTests.TestItemWriter"/>
<bean id="testWriter" class="org.springframework.batch.core.jsr.configuration.xml.JobPropertyTests$TestItemWriter"/>
<bean id="testCheckpointAlgorithm" class="org.springframework.batch.core.jsr.configuration.xml.JobPropertyTests.TestCheckpointAlgorithm"/>
<bean id="testCheckpointAlgorithm" class="org.springframework.batch.core.jsr.configuration.xml.JobPropertyTests$TestCheckpointAlgorithm"/>
<bean id="testBatchlet" class="org.springframework.batch.core.jsr.configuration.xml.JobPropertyTests.TestBatchlet"/>
<bean id="testBatchlet" class="org.springframework.batch.core.jsr.configuration.xml.JobPropertyTests$TestBatchlet"/>
<bean id="testStepListener" class="org.springframework.batch.core.jsr.configuration.xml.JobPropertyTests.TestStepListener"/>
<bean id="testStepListener" class="org.springframework.batch.core.jsr.configuration.xml.JobPropertyTests$TestStepListener"/>
<bean id="testDecider" class="org.springframework.batch.core.jsr.configuration.xml.JobPropertyTests.TestDecider"/>
<bean id="testDecider" class="org.springframework.batch.core.jsr.configuration.xml.JobPropertyTests$TestDecider"/>
<bean id="injectTestObj" class="org.springframework.batch.core.jsr.configuration.xml.JobPropertyTests.InjectTestObj"
<bean id="injectTestObj" class="org.springframework.batch.core.jsr.configuration.xml.JobPropertyTests$InjectTestObj"
c:name="Chris"/>
</beans>