diff --git a/spring-batch-core/src/main/java/org/springframework/batch/core/jsr/JobContext.java b/spring-batch-core/src/main/java/org/springframework/batch/core/jsr/JobContext.java index dcd63eb67..8574e06ac 100644 --- a/spring-batch-core/src/main/java/org/springframework/batch/core/jsr/JobContext.java +++ b/spring-batch-core/src/main/java/org/springframework/batch/core/jsr/JobContext.java @@ -17,6 +17,7 @@ package org.springframework.batch.core.jsr; import java.util.Properties; +import java.util.concurrent.atomic.AtomicBoolean; import javax.batch.runtime.BatchStatus; import org.springframework.batch.core.ExitStatus; @@ -36,14 +37,14 @@ public class JobContext implements javax.batch.runtime.context.JobContext { private Object transientUserData; private Properties properties; private JobExecution jobExecution; + private AtomicBoolean exitStatusSet = new AtomicBoolean(); - /** - * @param jobExecution for the related job - */ - public JobContext(JobExecution jobExecution, Properties properties) { + public void setJobExecution(JobExecution jobExecution) { Assert.notNull(jobExecution, "A JobExecution is required"); - this.jobExecution = jobExecution; + } + + public void setProperties(Properties properties) { this.properties = properties != null ? properties : new Properties(); } @@ -108,7 +109,7 @@ public class JobContext implements javax.batch.runtime.context.JobContext { */ @Override public String getExitStatus() { - return jobExecution.getExitStatus().getExitCode(); + return exitStatusSet.get() ? jobExecution.getExitStatus().getExitCode() : null; } /* (non-Javadoc) @@ -117,5 +118,6 @@ public class JobContext implements javax.batch.runtime.context.JobContext { @Override public void setExitStatus(String status) { jobExecution.setExitStatus(new ExitStatus(status)); + exitStatusSet.set(true); } } 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 a6ccfbcd6..d5c03afc3 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 @@ -38,6 +38,7 @@ 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; +import org.springframework.util.StringUtils; /** *

@@ -114,7 +115,7 @@ public class BatchPropertyBeanPostProcessor implements BeanPostProcessor, BeanFa String batchProperty = getBatchPropertyFieldValue(field, artifactProperties); - if (batchProperty != null) { + if (StringUtils.hasText(batchProperty)) { field.set(artifact, batchProperty); } 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 7b6312591..0e0d4d3b3 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 @@ -106,6 +106,7 @@ public class BatchPropertyContext { */ public Properties getStepArtifactProperties(String stepName, String artifactName) { Properties properties = new Properties(); + properties.putAll(getJobProperties()); properties.putAll(getStepProperties(stepName)); Map artifactProperties = stepArtifactProperties.get(stepName); diff --git a/spring-batch-core/src/main/java/org/springframework/batch/core/jsr/configuration/support/JobParameterResolvingBeanFactoryPostProcessor.java b/spring-batch-core/src/main/java/org/springframework/batch/core/jsr/configuration/support/JobParameterResolvingBeanFactoryPostProcessor.java index ab6a24640..a8920458b 100644 --- a/spring-batch-core/src/main/java/org/springframework/batch/core/jsr/configuration/support/JobParameterResolvingBeanFactoryPostProcessor.java +++ b/spring-batch-core/src/main/java/org/springframework/batch/core/jsr/configuration/support/JobParameterResolvingBeanFactoryPostProcessor.java @@ -67,12 +67,12 @@ public class JobParameterResolvingBeanFactoryPostProcessor implements BeanFactor } /** - * Sets this {@link BeanFactoryPostProcessor} to the lowest precedence so that - * it is executed as late as possible in the chain of {@link BeanFactoryPostProcessor}s + * Sets this {@link BeanFactoryPostProcessor} to the highest precedence so that + * it is executed as early as possible in the chain of {@link BeanFactoryPostProcessor}s */ @Override public int getOrder() { - return PriorityOrdered.LOWEST_PRECEDENCE; + return PriorityOrdered.HIGHEST_PRECEDENCE; } protected class JobParameterResolver { @@ -98,7 +98,7 @@ public class JobParameterResolvingBeanFactoryPostProcessor implements BeanFactor @Override public String resolveStringValue(String value) { if (value != null && ! "".equals(value)) { - String resolvedString = resolveJobProperties(value); + String resolvedString = resolveJobParameters(value); if (!"".equals(resolvedString)) { return jsrExpressionParser.parseExpression(resolvedString); @@ -108,7 +108,7 @@ public class JobParameterResolvingBeanFactoryPostProcessor implements BeanFactor return value; } - private String resolveJobProperties(String value) { + private String resolveJobParameters(String value) { StringBuffer valueBuffer = new StringBuffer(); Matcher jobParameterMatcher = JOB_PARAMETERS_PATTERN.matcher(value); diff --git a/spring-batch-core/src/main/java/org/springframework/batch/core/jsr/configuration/xml/FlowParser.java b/spring-batch-core/src/main/java/org/springframework/batch/core/jsr/configuration/xml/FlowParser.java index ac01e6ad2..94993a28d 100644 --- a/spring-batch-core/src/main/java/org/springframework/batch/core/jsr/configuration/xml/FlowParser.java +++ b/spring-batch-core/src/main/java/org/springframework/batch/core/jsr/configuration/xml/FlowParser.java @@ -41,13 +41,25 @@ import org.w3c.dom.NodeList; * as it is within a regular Spring Batch job/flow. * * @author Michael Minella + * @author Chris Schaefer * @since 3.0 */ public class FlowParser extends AbstractFlowParser { - private static final String DECISION_ELEMENT = "decision"; private static final String SPLIT_ELEMENT = "split"; private static final String STEP_ELEMENT = "step"; + private static final String FLOW_ELEMENT = "flow"; + private static final String ID_ATTRIBUTE = "id"; + private static final String NEXT_ATTRIBUTE = "next"; + private static final String STOP_ATTRIBUTE = "stop"; + private static final String FAIL_ATTRIBUTE = "fail"; + private static final String END_ATTRIBUTE = "end"; + private static final String ON_ATTRIBUTE = "on"; + private static final String TO_ATTRIBUTE = "to"; + private static final String RESTART_ATTRIBUTE = "restart"; + private static final String EXIT_STATUS_ATTRIBUTE = "exit-status"; + + private String jobFactoryRef; private StepParser stepParser = new StepParser(); private String flowName; @@ -57,6 +69,7 @@ public class FlowParser extends AbstractFlowParser { */ public FlowParser(String flowName, String jobFactoryRef) { super.setJobFactoryRef(jobFactoryRef); + this.jobFactoryRef = jobFactoryRef; this.flowName = flowName; } @@ -86,6 +99,12 @@ public class FlowParser extends AbstractFlowParser { stateTransitions.addAll(new SplitParser(flowName).parse(child, parserContext)); } else if(nodeName.equals(DECISION_ELEMENT)) { stateTransitions.addAll(new DecisionParser().parse(child, parserContext, flowName)); + } else if(nodeName.equals(FLOW_ELEMENT)) { + org.springframework.batch.core.jsr.configuration.xml.InlineFlowParser flowParser = + new org.springframework.batch.core.jsr.configuration.xml.InlineFlowParser(child.getAttribute(ID_ATTRIBUTE), jobFactoryRef); + flowParser.parse(child, parserContext); + + stateTransitions.addAll(parseFlow(child, parserContext)); } } } @@ -103,6 +122,19 @@ public class FlowParser extends AbstractFlowParser { builder.addPropertyValue("stateTransitions", managedList); } + private Collection parseFlow(Element element, ParserContext parserContext) { + String idAttribute = element.getAttribute(ID_ATTRIBUTE); + + BeanDefinitionBuilder stateBuilder = BeanDefinitionBuilder + .genericBeanDefinition("org.springframework.batch.core.job.flow.support.state.FlowState"); + + FlowParser flowParser = new FlowParser(idAttribute, jobFactoryRef); + + stateBuilder.addConstructorArgValue(flowParser.parse(element, parserContext)); + stateBuilder.addConstructorArgValue(idAttribute); + + return FlowParser.getNextElements(parserContext, null, stateBuilder.getBeanDefinition(), element); + } public static Collection getNextElements(ParserContext parserContext, BeanDefinition stateDef, Element element) { @@ -114,7 +146,7 @@ public class FlowParser extends AbstractFlowParser { Collection list = new ArrayList(); - String shortNextAttribute = element.getAttribute("next"); + String shortNextAttribute = element.getAttribute(NEXT_ATTRIBUTE); boolean hasNextAttribute = StringUtils.hasText(shortNextAttribute); if (hasNextAttribute) { list.add(getStateTransitionReference(parserContext, stateDef, null, shortNextAttribute)); @@ -122,7 +154,7 @@ public class FlowParser extends AbstractFlowParser { boolean transitionElementExists = false; List patterns = new ArrayList(); - for (String transitionName : new String[] { "next", "stop", "end", "fail" }) { + for (String transitionName : new String[] { NEXT_ATTRIBUTE, STOP_ATTRIBUTE, END_ATTRIBUTE, FAIL_ATTRIBUTE }) { List transitionElements = DomUtils.getChildElementsByTagName(element, transitionName); for (Element transitionElement : transitionElements) { verifyUniquePattern(transitionElement, patterns, element, parserContext); @@ -152,16 +184,18 @@ public class FlowParser extends AbstractFlowParser { protected static Collection parseTransitionElement(Element transitionElement, String stateId, BeanDefinition stateDef, ParserContext parserContext) { - FlowExecutionStatus status = getBatchStatusFromEndTransitionName(transitionElement.getNodeName()); - String onAttribute = transitionElement.getAttribute("on"); - String restartAttribute = transitionElement.getAttribute("restart"); - String nextAttribute = transitionElement.getAttribute("to"); + String onAttribute = transitionElement.getAttribute(ON_ATTRIBUTE); + String restartAttribute = transitionElement.getAttribute(RESTART_ATTRIBUTE); + String nextAttribute = transitionElement.getAttribute(TO_ATTRIBUTE); + if (!StringUtils.hasText(nextAttribute)) { nextAttribute = restartAttribute; } + boolean abandon = stateId != null && StringUtils.hasText(restartAttribute) && !restartAttribute.equals(stateId); - String exitCodeAttribute = transitionElement.getAttribute("exit-status"); + String exitCodeAttribute = transitionElement.getAttribute(EXIT_STATUS_ATTRIBUTE); return createTransition(status, onAttribute, nextAttribute, exitCodeAttribute, stateDef, parserContext, abandon); - }} + } +} diff --git a/spring-batch-core/src/main/java/org/springframework/batch/core/jsr/configuration/xml/InlineFlowParser.java b/spring-batch-core/src/main/java/org/springframework/batch/core/jsr/configuration/xml/InlineFlowParser.java new file mode 100644 index 000000000..ec4637711 --- /dev/null +++ b/spring-batch-core/src/main/java/org/springframework/batch/core/jsr/configuration/xml/InlineFlowParser.java @@ -0,0 +1,64 @@ +/* + * 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 org.springframework.batch.core.job.flow.support.DefaultStateTransitionComparator; +import org.springframework.beans.factory.config.BeanDefinition; +import org.springframework.beans.factory.config.RuntimeBeanReference; +import org.springframework.beans.factory.support.BeanDefinitionBuilder; +import org.springframework.beans.factory.xml.ParserContext; +import org.w3c.dom.Element; + +/** + *

+ * Parses a flow element in a JSR-352 job defintion. + *

+ * + * @author Chris Schaefer + * @since 3.0 + */ +public class InlineFlowParser extends FlowParser { + private final String flowName; + + /** + * Construct a {@link InlineFlowParser} with the specified name and using the + * provided job repository ref. + * + * @param flowName the name of the flow + * @param jobFactoryRef the reference to the {@link org.springframework.batch.core.configuration.xml.JobParserJobFactoryBean} + * from the enclosing tag + */ + public InlineFlowParser(String flowName, String jobFactoryRef) { + super(flowName, jobFactoryRef); + this.flowName = flowName; + } + + /** + * Parses the flow from the provided {@link Element}. + * + * @param element the top level element containing a flow definition + * @param parserContext the {@link org.springframework.beans.factory.xml.ParserContext} + * @param builder the {@link BeanDefinitionBuilder} to handle bean defintion operations + */ + @Override + protected void doParse(Element element, ParserContext parserContext, BeanDefinitionBuilder builder) { + builder.getRawBeanDefinition().setAttribute("flowName", flowName); + builder.addPropertyValue("name", flowName); + builder.addPropertyValue("stateTransitionComparator", new RuntimeBeanReference(DefaultStateTransitionComparator.STATE_TRANSITION_COMPARATOR)); + super.doParse(element, parserContext, builder); + builder.setRole(BeanDefinition.ROLE_INFRASTRUCTURE); + } +} diff --git a/spring-batch-core/src/main/java/org/springframework/batch/core/jsr/configuration/xml/ListenerParser.java b/spring-batch-core/src/main/java/org/springframework/batch/core/jsr/configuration/xml/ListenerParser.java index 82b0e1154..ec3af3c12 100644 --- a/spring-batch-core/src/main/java/org/springframework/batch/core/jsr/configuration/xml/ListenerParser.java +++ b/spring-batch-core/src/main/java/org/springframework/batch/core/jsr/configuration/xml/ListenerParser.java @@ -18,10 +18,12 @@ package org.springframework.batch.core.jsr.configuration.xml; import java.util.List; import org.springframework.batch.core.jsr.configuration.support.BatchArtifact; +import org.springframework.beans.factory.config.BeanDefinition; import org.springframework.beans.factory.config.RuntimeBeanReference; import org.springframework.beans.factory.parsing.CompositeComponentDefinition; 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.support.ManagedList; import org.springframework.beans.factory.xml.ParserContext; import org.springframework.util.xml.DomUtils; @@ -84,6 +86,8 @@ public class ListenerParser { BeanDefinitionBuilder bd = BeanDefinitionBuilder.genericBeanDefinition(listenerType); bd.addPropertyValue("delegate", new RuntimeBeanReference(beanName)); + applyListenerScope(beanName, parserContext.getRegistry()); + listeners.add(bd.getBeanDefinition()); new PropertyParser(beanName, parserContext, getBatchArtifactType(stepName), stepName).parseProperties(listenerElement); @@ -98,6 +102,15 @@ public class ListenerParser { return listeners; } + protected void applyListenerScope(String beanName, BeanDefinitionRegistry beanDefinitionRegistry) { + if (listenerType == JobListenerFactoryBean.class) { + if (beanDefinitionRegistry.containsBeanDefinition(beanName)) { + BeanDefinition beanDefinition = beanDefinitionRegistry.getBeanDefinition(beanName); + beanDefinition.setScope(BeanDefinition.SCOPE_SINGLETON); + } + } + } + private BatchArtifact.BatchArtifactType getBatchArtifactType(String stepName) { return (stepName != null && !"".equals(stepName)) ? BatchArtifact.BatchArtifactType.STEP_ARTIFACT : BatchArtifact.BatchArtifactType.ARTIFACT; 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 d0de1d8d2..13efbff67 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 @@ -55,6 +55,8 @@ import org.springframework.beans.factory.access.BeanFactoryLocator; import org.springframework.beans.factory.access.BeanFactoryReference; import org.springframework.beans.factory.config.AutowireCapableBeanFactory; import org.springframework.beans.factory.config.ConfigurableListableBeanFactory; +import org.springframework.beans.factory.support.AbstractBeanDefinition; +import org.springframework.beans.factory.support.BeanDefinitionBuilder; import org.springframework.context.ApplicationContext; import org.springframework.context.access.ContextSingletonBeanFactoryLocator; import org.springframework.context.support.GenericXmlApplicationContext; @@ -120,6 +122,7 @@ import org.springframework.util.Assert; */ public class JsrJobOperator implements JobOperator, InitializingBean { private static final String BATCH_PROPERTY_CONTEXT_BEAN_NAME = "batchPropertyContext"; + private static final String JSR_JOB_CONTEXT_BEAN_NAME = "jsr_jobContext"; private org.springframework.batch.core.launch.JobOperator batchJobOperator; private JobExplorer jobExplorer; @@ -456,12 +459,16 @@ public class JsrJobOperator implements JobOperator, InitializingBean { } batchContext.addBeanFactoryPostProcessor(new JobParameterResolvingBeanFactoryPostProcessor(params)); + + AbstractBeanDefinition beanDefinition = BeanDefinitionBuilder.genericBeanDefinition("org.springframework.batch.core.jsr.JobContext").getBeanDefinition(); + batchContext.registerBeanDefinition(JSR_JOB_CONTEXT_BEAN_NAME, beanDefinition); + batchContext.setParent(baseContext); try { batchContext.refresh(); } catch (BeanCreationException e) { - throw new JobStartException(e); + throw new JobRestartException(e); } final Job job = batchContext.getBean(Job.class); @@ -486,7 +493,9 @@ public class JsrJobOperator implements JobOperator, InitializingBean { BatchPropertyContext batchPropertyContext = factory.getBean(BATCH_PROPERTY_CONTEXT_BEAN_NAME, BatchPropertyContext.class); Properties properties = batchPropertyContext.getJobProperties(); - factory.registerSingleton(job.getName() + "_" + jobExecution.getId() + "_jobContext", new JobContext(jobExecution, properties)); + JobContext jobContext = factory.getBean(JSR_JOB_CONTEXT_BEAN_NAME, JobContext.class); + jobContext.setJobExecution(jobExecution); + jobContext.setProperties(properties); taskExecutor.execute(new Runnable() { @@ -562,6 +571,10 @@ public class JsrJobOperator implements JobOperator, InitializingBean { } batchContext.addBeanFactoryPostProcessor(new JobParameterResolvingBeanFactoryPostProcessor(params)); + + AbstractBeanDefinition beanDefinition = BeanDefinitionBuilder.genericBeanDefinition("org.springframework.batch.core.jsr.JobContext").getBeanDefinition(); + batchContext.registerBeanDefinition(JSR_JOB_CONTEXT_BEAN_NAME, beanDefinition); + batchContext.setParent(baseContext); try { @@ -590,7 +603,9 @@ public class JsrJobOperator implements JobOperator, InitializingBean { BatchPropertyContext batchPropertyContext = factory.getBean(BATCH_PROPERTY_CONTEXT_BEAN_NAME, BatchPropertyContext.class); Properties properties = batchPropertyContext.getJobProperties(); - factory.registerSingleton(job.getName() + "_" + jobExecution.getId() + "_jobContext", new JobContext(jobExecution, properties)); + JobContext jobContext = factory.getBean(JSR_JOB_CONTEXT_BEAN_NAME, JobContext.class); + jobContext.setJobExecution(jobExecution); + jobContext.setProperties(properties); taskExecutor.execute(new Runnable() { diff --git a/spring-batch-core/src/main/java/org/springframework/batch/core/step/AbstractStep.java b/spring-batch-core/src/main/java/org/springframework/batch/core/step/AbstractStep.java index 211eebdd4..ac529598d 100644 --- a/spring-batch-core/src/main/java/org/springframework/batch/core/step/AbstractStep.java +++ b/spring-batch-core/src/main/java/org/springframework/batch/core/step/AbstractStep.java @@ -47,6 +47,7 @@ import org.springframework.util.ClassUtils; * @author Ben Hale * @author Robert Kasanicky * @author Michael Minella + * @author Chris Schaefer */ public abstract class AbstractStep implements Step, InitializingBean, BeanNameAware { @@ -114,7 +115,7 @@ public abstract class AbstractStep implements Step, InitializingBean, BeanNameAw * @param startLimit the startLimit to set */ public void setStartLimit(int startLimit) { - this.startLimit = startLimit; + this.startLimit = startLimit == 0 ? Integer.MAX_VALUE : startLimit; } @Override diff --git a/spring-batch-core/src/test/java/org/springframework/batch/core/jsr/JobContextTests.java b/spring-batch-core/src/test/java/org/springframework/batch/core/jsr/JobContextTests.java index d01de801a..4e74d0cca 100644 --- a/spring-batch-core/src/test/java/org/springframework/batch/core/jsr/JobContextTests.java +++ b/spring-batch-core/src/test/java/org/springframework/batch/core/jsr/JobContextTests.java @@ -44,16 +44,20 @@ public class JobContextTests { public void setUp() throws Exception { MockitoAnnotations.initMocks(this); - Properties properties = new Properties(); - properties.put("jobLevelProperty1", "jobLevelValue1"); + Properties properties = new Properties(); + properties.put("jobLevelProperty1", "jobLevelValue1"); + + context = new JobContext(); + context.setProperties(properties); + context.setJobExecution(execution); - context = new JobContext(execution, properties); when(execution.getJobInstance()).thenReturn(instance); } @Test(expected=IllegalArgumentException.class) public void testCreateWithNull() { - context = new JobContext(null, null); + context = new JobContext(); + context.setJobExecution(null); } @Test @@ -91,12 +95,12 @@ public class JobContextTests { when(execution.getJobParameters()).thenReturn(params); - assertEquals("value1", execution.getJobParameters().getString("key1")); + assertEquals("value1", execution.getJobParameters().getString("key1")); } @Test public void testJobProperties() { - assertEquals("jobLevelValue1", context.getProperties().get("jobLevelProperty1")); + assertEquals("jobLevelValue1", context.getProperties().get("jobLevelProperty1")); } @Test @@ -108,12 +112,16 @@ public class JobContextTests { @Test public void testExitStatus() { - when(execution.getExitStatus()).thenReturn(new ExitStatus("exit")); - - assertEquals("exit", context.getExitStatus()); - context.setExitStatus("my exit status"); - verify(execution).setExitStatus(new ExitStatus("my exit status")); + + when(execution.getExitStatus()).thenReturn(new ExitStatus("exit")); + assertEquals("exit", context.getExitStatus()); + } + + @Test + public void testInitialNullExitStatus() { + when(execution.getExitStatus()).thenReturn(new ExitStatus("exit")); + assertEquals(null, context.getExitStatus()); } } 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 index 9b18aad86..84d02b1ca 100644 --- 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 @@ -170,11 +170,13 @@ public class BatchPropertyContextTests { batchPropertyContext.setStepArtifactPropertiesContextEntry(stepArtifactProperties); Properties artifactProperties = batchPropertyContext.getStepArtifactProperties("step1", "reader"); - assertEquals(4, artifactProperties.size()); + assertEquals(6, artifactProperties.size()); assertEquals("readerProperty1value", artifactProperties.getProperty("readerProperty1")); assertEquals("readerProperty2value", artifactProperties.getProperty("readerProperty2")); assertEquals("step1PropertyValue1", artifactProperties.getProperty("step1PropertyName1")); assertEquals("step1PropertyValue2", artifactProperties.getProperty("step1PropertyName2")); + assertEquals("jobProperty1value", artifactProperties.getProperty("jobProperty1")); + assertEquals("jobProperty2value", artifactProperties.getProperty("jobProperty2")); } @Test @@ -213,12 +215,14 @@ public class BatchPropertyContextTests { batchPropertyContext.setStepArtifactPropertiesContextEntry(partitionProperties); Properties artifactProperties = batchPropertyContext.getStepArtifactProperties("step2:partition0", "writer"); - assertEquals(6, artifactProperties.size()); + assertEquals(8, artifactProperties.size()); assertEquals("writerProperty1", artifactProperties.getProperty("writerProperty1Step")); assertEquals("writerProperty2", artifactProperties.getProperty("writerProperty2Step")); assertEquals("writerProperty1valuePartition0", artifactProperties.getProperty("writerProperty1")); assertEquals("writerProperty2valuePartition0", artifactProperties.getProperty("writerProperty2")); assertEquals("step2PropertyValue1", artifactProperties.getProperty("step2PropertyName1")); assertEquals("step2PropertyValue2", artifactProperties.getProperty("step2PropertyName2")); + assertEquals("jobProperty1value", artifactProperties.getProperty("jobProperty1")); + assertEquals("jobProperty2value", artifactProperties.getProperty("jobProperty2")); } } diff --git a/spring-batch-core/src/test/java/org/springframework/batch/core/jsr/configuration/xml/ListenerParserTests.java b/spring-batch-core/src/test/java/org/springframework/batch/core/jsr/configuration/xml/ListenerParserTests.java new file mode 100644 index 000000000..56026c4d0 --- /dev/null +++ b/spring-batch-core/src/test/java/org/springframework/batch/core/jsr/configuration/xml/ListenerParserTests.java @@ -0,0 +1,65 @@ +/* + * 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 org.junit.Test; +import org.springframework.batch.core.listener.StepListenerFactoryBean; +import org.springframework.beans.factory.config.BeanDefinition; +import org.springframework.beans.factory.support.AbstractBeanDefinition; +import org.springframework.beans.factory.support.BeanDefinitionBuilder; +import org.springframework.context.support.GenericApplicationContext; +import static org.junit.Assert.assertEquals; + +/** + *

+ * Test cases around scoping of job/step listeners when building their bean definitions. + *

+ * + * @author Chris Schaefer + */ +public class ListenerParserTests { + @Test + public void testStepListenerStepScoped() { + GenericApplicationContext applicationContext = new GenericApplicationContext(); + + AbstractBeanDefinition newBeanDefinition = BeanDefinitionBuilder.genericBeanDefinition("stepListener").getBeanDefinition(); + newBeanDefinition.setScope("step"); + + applicationContext.registerBeanDefinition("stepListener", newBeanDefinition); + + ListenerParser listenerParser = new ListenerParser(StepListenerFactoryBean.class, "listeners"); + listenerParser.applyListenerScope("stepListener", applicationContext); + + BeanDefinition beanDefinition = applicationContext.getBeanDefinition("stepListener"); + assertEquals("step", beanDefinition.getScope()); + } + + @Test + public void testJobListenerSingletonScoped() { + GenericApplicationContext applicationContext = new GenericApplicationContext(); + + AbstractBeanDefinition newBeanDefinition = BeanDefinitionBuilder.genericBeanDefinition("jobListener").getBeanDefinition(); + newBeanDefinition.setScope("step"); + + applicationContext.registerBeanDefinition("jobListener", newBeanDefinition); + + ListenerParser listenerParser = new ListenerParser(JobListenerFactoryBean.class, "jobExecutionListeners"); + listenerParser.applyListenerScope("jobListener", applicationContext); + + BeanDefinition beanDefinition = applicationContext.getBeanDefinition("jobListener"); + assertEquals("singleton", beanDefinition.getScope()); + } +}