diff --git a/spring-batch-core/src/main/java/org/springframework/batch/core/jsr/configuration/support/BatchPropertyBeanPostProcessor.java b/spring-batch-core/src/main/java/org/springframework/batch/core/jsr/configuration/support/BatchPropertyBeanPostProcessor.java index a2dc5ef68..d403235ba 100644 --- a/spring-batch-core/src/main/java/org/springframework/batch/core/jsr/configuration/support/BatchPropertyBeanPostProcessor.java +++ b/spring-batch-core/src/main/java/org/springframework/batch/core/jsr/configuration/support/BatchPropertyBeanPostProcessor.java @@ -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> requiredAnnotations = new HashSet>(); public BatchPropertyBeanPostProcessor() { @@ -96,12 +105,12 @@ public class BatchPropertyBeanPostProcessor implements BeanPostProcessor { ClassLoader cl = BatchPropertyBeanPostProcessor.class.getClassLoader(); try { - this.requiredAnnotations.add((Class) cl.loadClass("javax.inject.Inject")); + requiredAnnotations.add((Class) 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 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; + } } diff --git a/spring-batch-core/src/main/java/org/springframework/batch/core/jsr/configuration/support/BatchPropertyContext.java b/spring-batch-core/src/main/java/org/springframework/batch/core/jsr/configuration/support/BatchPropertyContext.java index 0bd41e09c..038879489 100644 --- a/spring-batch-core/src/main/java/org/springframework/batch/core/jsr/configuration/support/BatchPropertyContext.java +++ b/spring-batch-core/src/main/java/org/springframework/batch/core/jsr/configuration/support/BatchPropertyContext.java @@ -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 batchProperties = new ConcurrentHashMap(); /** *

- * 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. *

* @@ -61,19 +63,21 @@ public class BatchPropertyContext { } } + /** + *

+ * Obtains all properties for the specific batch artifact / bean name without any job level + * properties. + *

+ * + * @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 { /** *

* 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. *

* * @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; } + /** + *

+ * Obtains all {@link Properties} that reside at the Job level configuration. + *

+ * + * @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; + } + /** *

* Simple object to encapsulate batch properties of a given bean / batch artifact. diff --git a/spring-batch-core/src/main/java/org/springframework/batch/core/jsr/configuration/xml/JobParser.java b/spring-batch-core/src/main/java/org/springframework/batch/core/jsr/configuration/xml/JobParser.java index e1798ba08..33633bc88 100644 --- a/spring-batch-core/src/main/java/org/springframework/batch/core/jsr/configuration/xml/JobParser.java +++ b/spring-batch-core/src/main/java/org/springframework/batch/core/jsr/configuration/xml/JobParser.java @@ -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); } - } diff --git a/spring-batch-core/src/main/java/org/springframework/batch/core/jsr/configuration/xml/PropertyParser.java b/spring-batch-core/src/main/java/org/springframework/batch/core/jsr/configuration/xml/PropertyParser.java index 6477220dd..f83373e82 100644 --- a/spring-batch-core/src/main/java/org/springframework/batch/core/jsr/configuration/xml/PropertyParser.java +++ b/spring-batch-core/src/main/java/org/springframework/batch/core/jsr/configuration/xml/PropertyParser.java @@ -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; /** *

- * Parser for the <properties /> element defined by JSR-352. + * Parser for the <properties /> 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']}. *

* * @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(); + } /** *

@@ -60,27 +67,29 @@ public class PropertyParser { * which represent the property entries key and value. *

* - * @param element + * @param element the element to parse looking for <properties /> */ - public void parseProperties(Element element) { - List propertiesElements = DomUtils.getChildElementsByTagName(element, PROPERTIES_ELEMENT); + public void parseProperties(Element element) { + List propertiesElements = DomUtils.getChildElementsByTagName(element, PROPERTIES_ELEMENT); - Properties properties = new Properties(); + Properties properties = new Properties(); - if (propertiesElements.size() == 1) { - List propertyElements = DomUtils.getChildElementsByTagName(propertiesElements.get(0), PROPERTY_ELEMENT); + if (propertiesElements.size() == 1) { + List 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 element may not appear more than once in a single .", element); - } - } + addProperties(properties); + } else if (propertiesElements.size() > 1) { + parserContext.getReaderContext().error("The element may not appear more than once in a single .", 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 jobProperties = new HashMap(); + + 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); + } + } } diff --git a/spring-batch-core/src/main/java/org/springframework/batch/core/jsr/launch/JsrJobOperator.java b/spring-batch-core/src/main/java/org/springframework/batch/core/jsr/launch/JsrJobOperator.java index 1bff85181..77c756da9 100644 --- a/spring-batch-core/src/main/java/org/springframework/batch/core/jsr/launch/JsrJobOperator.java +++ b/spring-batch-core/src/main/java/org/springframework/batch/core/jsr/launch/JsrJobOperator.java @@ -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."); diff --git a/spring-batch-core/src/test/java/org/springframework/batch/core/jsr/configuration/support/BatchPropertyContextTests.java b/spring-batch-core/src/test/java/org/springframework/batch/core/jsr/configuration/support/BatchPropertyContextTests.java new file mode 100644 index 000000000..9b37adeba --- /dev/null +++ b/spring-batch-core/src/test/java/org/springframework/batch/core/jsr/configuration/support/BatchPropertyContextTests.java @@ -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; + +/** + *

+ * Test cases around {@link BatchPropertyContext}. + *

+ * + * @author Chris Schaefer + */ +public class BatchPropertyContextTests { + private List entries = new ArrayList(); + + @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")); + } +} diff --git a/spring-batch-core/src/test/java/org/springframework/batch/core/jsr/configuration/xml/JobPropertySubstitutionTests.java b/spring-batch-core/src/test/java/org/springframework/batch/core/jsr/configuration/xml/JobPropertySubstitutionTests.java new file mode 100644 index 000000000..7ce702823 --- /dev/null +++ b/spring-batch-core/src/test/java/org/springframework/batch/core/jsr/configuration/xml/JobPropertySubstitutionTests.java @@ -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; + +/** + *

+ * Test cases for JSR-352 job property substitution. + *

+ * + * 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 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; + } + } +} diff --git a/spring-batch-core/src/test/java/org/springframework/batch/core/jsr/configuration/xml/JobPropertyTests.java b/spring-batch-core/src/test/java/org/springframework/batch/core/jsr/configuration/xml/JobPropertyTests.java index 049c432ee..cbfe10563 100644 --- a/spring-batch-core/src/test/java/org/springframework/batch/core/jsr/configuration/xml/JobPropertyTests.java +++ b/spring-batch-core/src/test/java/org/springframework/batch/core/jsr/configuration/xml/JobPropertyTests.java @@ -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()); diff --git a/spring-batch-core/src/test/resources/org/springframework/batch/core/jsr/configuration/xml/JobPropertySubstitutionTests-context.xml b/spring-batch-core/src/test/resources/org/springframework/batch/core/jsr/configuration/xml/JobPropertySubstitutionTests-context.xml new file mode 100644 index 000000000..d301fa3f3 --- /dev/null +++ b/spring-batch-core/src/test/resources/org/springframework/batch/core/jsr/configuration/xml/JobPropertySubstitutionTests-context.xml @@ -0,0 +1,50 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/spring-batch-core/src/test/resources/org/springframework/batch/core/jsr/configuration/xml/JobPropertyTests-context.xml b/spring-batch-core/src/test/resources/org/springframework/batch/core/jsr/configuration/xml/JobPropertyTests-context.xml index c92f35684..41d5936b9 100644 --- a/spring-batch-core/src/test/resources/org/springframework/batch/core/jsr/configuration/xml/JobPropertyTests-context.xml +++ b/spring-batch-core/src/test/resources/org/springframework/batch/core/jsr/configuration/xml/JobPropertyTests-context.xml @@ -106,20 +106,20 @@ - + - + - + - + - + - + - + -