diff --git a/spring-batch-core/src/main/java/org/springframework/batch/core/configuration/xml/DecisionParser.java b/spring-batch-core/src/main/java/org/springframework/batch/core/configuration/xml/DecisionParser.java new file mode 100644 index 000000000..7ca06d1e9 --- /dev/null +++ b/spring-batch-core/src/main/java/org/springframework/batch/core/configuration/xml/DecisionParser.java @@ -0,0 +1,74 @@ +/* + * Copyright 2006-2007 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.springframework.batch.core.configuration.xml; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; + +import org.springframework.batch.core.job.flow.DecisionState; +import org.springframework.batch.core.job.flow.JobExecutionDecider; +import org.springframework.batch.flow.StateTransition; +import org.springframework.beans.factory.config.RuntimeBeanReference; +import org.springframework.beans.factory.support.BeanDefinitionBuilder; +import org.springframework.beans.factory.xml.ParserContext; +import org.springframework.util.xml.DomUtils; +import org.w3c.dom.Element; + +/** + * Internal parser for the <decision/> elements inside a job. A decision + * element references a bean definition for a {@link JobExecutionDecider} and + * goes on to list a set of transitions to other states with <next + * on="pattern" to="stepName"/>. Used by the {@link JobParser}. + * + * @see JobParser + * + * @author Dave Syer + * + */ +public class DecisionParser { + + /** + * Parse the decision and turn it into a list of transitions. + * + * @param element the <decision/gt; element to parse + * @param parserContext the parser context for the bean factory + * @return a collection of bean definitions for {@link StateTransition} + * instances objects + */ + public Collection parse(Element element, ParserContext parserContext) { + + String refAttribute = element.getAttribute("decider"); + + Collection list = new ArrayList(); + + @SuppressWarnings("unchecked") + List nextElements = (List) DomUtils.getChildElementsByTagName(element, "next"); + + for (Element nextElement : nextElements) { + String onAttribute = nextElement.getAttribute("on"); + String nextAttribute = nextElement.getAttribute("to"); + BeanDefinitionBuilder stateBuilder = BeanDefinitionBuilder.genericBeanDefinition(DecisionState.class); + stateBuilder.addConstructorArgValue(new RuntimeBeanReference(refAttribute)); + stateBuilder.addConstructorArgValue(parserContext.getReaderContext().generateBeanName(stateBuilder.getBeanDefinition())); + list.add(StepParser.getStateTransitionReference(parserContext, stateBuilder.getBeanDefinition(), + onAttribute, nextAttribute)); + } + + return list; + + } +} diff --git a/spring-batch-core/src/main/java/org/springframework/batch/core/configuration/xml/FlowParser.java b/spring-batch-core/src/main/java/org/springframework/batch/core/configuration/xml/FlowParser.java new file mode 100644 index 000000000..7df142b58 --- /dev/null +++ b/spring-batch-core/src/main/java/org/springframework/batch/core/configuration/xml/FlowParser.java @@ -0,0 +1,87 @@ +/* + * Copyright 2006-2007 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.springframework.batch.core.configuration.xml; + +import java.util.ArrayList; +import java.util.List; + +import org.springframework.batch.flow.Flow; +import org.springframework.batch.flow.SimpleFlow; +import org.springframework.beans.factory.config.RuntimeBeanReference; +import org.springframework.beans.factory.support.AbstractBeanDefinition; +import org.springframework.beans.factory.support.BeanDefinitionBuilder; +import org.springframework.beans.factory.support.ManagedList; +import org.springframework.beans.factory.xml.ParserContext; +import org.springframework.util.xml.DomUtils; +import org.w3c.dom.Element; + +/** + * @author Dave Syer + * + */ +public class FlowParser { + + /** + * @param element the top level element containing a flow definition + * @param parserContext the {@link ParserContext} + * @param flowName the name of the flow + * @return a bean definition for a {@link Flow} + */ + public AbstractBeanDefinition parse(Element element, ParserContext parserContext, String flowName) { + List stateTransitions = new ArrayList(); + + @SuppressWarnings("unchecked") + List stepElements = (List) DomUtils.getChildElementsByTagName(element, "step"); + StepParser stepParser = new StepParser(); + for (Element stepElement : stepElements) { + stateTransitions.addAll(stepParser.parse(stepElement, parserContext)); + } + + @SuppressWarnings("unchecked") + List decisionElements = (List) DomUtils.getChildElementsByTagName(element, "decision"); + DecisionParser decisionParser = new DecisionParser(); + for (Element stepElement : decisionElements) { + stateTransitions.addAll(decisionParser.parse(stepElement, parserContext)); + } + + @SuppressWarnings("unchecked") + List pauseElements = (List) DomUtils.getChildElementsByTagName(element, "pause"); + PauseParser pauseParser = new PauseParser(); + for (Element stepElement : pauseElements) { + stateTransitions.add(pauseParser.parse(stepElement, parserContext)); + } + + @SuppressWarnings("unchecked") + List splitElements = (List) DomUtils.getChildElementsByTagName(element, "split"); + SplitParser splitParser = new SplitParser(); + for (Element stepElement : splitElements) { + stateTransitions.addAll(splitParser.parse(stepElement, parserContext)); + } + + BeanDefinitionBuilder flowBuilder = BeanDefinitionBuilder.genericBeanDefinition(SimpleFlow.class); + flowBuilder.addConstructorArgValue(flowName ); + ManagedList managedList = new ManagedList(); + @SuppressWarnings( { "unchecked", "unused" }) + boolean dummy = managedList.addAll(stateTransitions); + flowBuilder.addPropertyValue("stateTransitions", managedList); + AbstractBeanDefinition flowDef = flowBuilder.getBeanDefinition(); + parserContext.getReaderContext().registerWithGeneratedName(flowDef); + + return flowDef; + + } + +} diff --git a/spring-batch-core/src/main/java/org/springframework/batch/core/configuration/xml/JobParser.java b/spring-batch-core/src/main/java/org/springframework/batch/core/configuration/xml/JobParser.java index d36f09e79..0c5e75581 100644 --- a/spring-batch-core/src/main/java/org/springframework/batch/core/configuration/xml/JobParser.java +++ b/spring-batch-core/src/main/java/org/springframework/batch/core/configuration/xml/JobParser.java @@ -15,21 +15,14 @@ */ package org.springframework.batch.core.configuration.xml; -import java.util.ArrayList; -import java.util.List; - import org.springframework.batch.core.Job; import org.springframework.batch.core.job.flow.FlowJob; import org.springframework.batch.core.repository.JobRepository; -import org.springframework.batch.flow.SimpleFlow; -import org.springframework.beans.factory.config.RuntimeBeanReference; import org.springframework.beans.factory.support.AbstractBeanDefinition; import org.springframework.beans.factory.support.BeanDefinitionBuilder; -import org.springframework.beans.factory.support.ManagedList; import org.springframework.beans.factory.xml.AbstractBeanDefinitionParser; import org.springframework.beans.factory.xml.ParserContext; import org.springframework.util.StringUtils; -import org.springframework.util.xml.DomUtils; import org.w3c.dom.Element; /** @@ -62,22 +55,11 @@ public class JobParser extends AbstractBeanDefinitionParser { } builder.addPropertyReference("jobRepository", repositoryAttribute); - StepParser stepParser = new StepParser(); - List stepTransitions = new ArrayList(); - - @SuppressWarnings("unchecked") - List stepElements = (List) DomUtils.getChildElementsByTagName(element, "step"); - for (Element stepElement : stepElements) { - stepTransitions.addAll(stepParser.parse(stepElement, parserContext)); - } - ManagedList managedList = new ManagedList(); - @SuppressWarnings( { "unchecked", "unused" }) - boolean dummy = managedList.addAll(stepTransitions); - BeanDefinitionBuilder flowBuilder = BeanDefinitionBuilder.genericBeanDefinition(SimpleFlow.class); - flowBuilder.addConstructorArgValue(jobName ); - flowBuilder.addPropertyValue("stateTransitions", managedList); - builder.addPropertyValue("flow", flowBuilder.getBeanDefinition()); + FlowParser flowParser = new FlowParser(); + AbstractBeanDefinition flowDef = flowParser.parse(element, parserContext, jobName); + builder.addPropertyValue("flow", flowDef); + return builder.getBeanDefinition(); } diff --git a/spring-batch-core/src/main/java/org/springframework/batch/core/configuration/xml/PauseParser.java b/spring-batch-core/src/main/java/org/springframework/batch/core/configuration/xml/PauseParser.java new file mode 100644 index 000000000..70ab87a3a --- /dev/null +++ b/spring-batch-core/src/main/java/org/springframework/batch/core/configuration/xml/PauseParser.java @@ -0,0 +1,56 @@ +/* + * Copyright 2006-2007 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.springframework.batch.core.configuration.xml; + +import org.springframework.batch.core.job.flow.PauseState; +import org.springframework.batch.flow.StateTransition; +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; + +/** + * Internal parser for the <pause/> elements inside a job. A pause element + * causes the job flow to end and will resume on the next execution at the next + * state. Used by the {@link JobParser}. + * + * @see JobParser + * + * @author Dave Syer + * + */ +public class PauseParser { + + /** + * Parse the pause and turn it into a transition. + * + * @param element the <pause/gt; element to parse + * @param parserContext the parser context for the bean factory + * @return a bean definitions for a {@link StateTransition} + * instances objects + */ + public RuntimeBeanReference parse(Element element, ParserContext parserContext) { + + String nextAttribute = element.getAttribute("next"); + String idAttribute = element.getAttribute("id"); + + BeanDefinitionBuilder stateBuilder = BeanDefinitionBuilder.genericBeanDefinition(PauseState.class); + stateBuilder.addConstructorArgValue(idAttribute); + return StepParser.getStateTransitionReference(parserContext, stateBuilder.getBeanDefinition(), "*", + nextAttribute); + + } +} diff --git a/spring-batch-core/src/main/java/org/springframework/batch/core/configuration/xml/SplitParser.java b/spring-batch-core/src/main/java/org/springframework/batch/core/configuration/xml/SplitParser.java new file mode 100644 index 000000000..96bc48b6d --- /dev/null +++ b/spring-batch-core/src/main/java/org/springframework/batch/core/configuration/xml/SplitParser.java @@ -0,0 +1,116 @@ +/* + * Copyright 2006-2007 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.springframework.batch.core.configuration.xml; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; + +import org.springframework.batch.core.job.flow.JobExecutionDecider; +import org.springframework.batch.flow.SplitState; +import org.springframework.batch.flow.StateTransition; +import org.springframework.beans.factory.BeanCreationException; +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.support.ManagedList; +import org.springframework.beans.factory.xml.ParserContext; +import org.springframework.util.StringUtils; +import org.springframework.util.xml.DomUtils; +import org.w3c.dom.Element; + +/** + * Internal parser for the <split/> elements inside a job. A split element + * references a bean definition for a {@link JobExecutionDecider} and goes on to + * list a set of transitions to other states with <next on="pattern" + * to="stepName"/>. Used by the {@link JobParser}. + * + * @see JobParser + * + * @author Dave Syer + * + */ +public class SplitParser { + + /** + * Parse the split and turn it into a list of transitions. + * + * @param element the <split/gt; element to parse + * @param parserContext the parser context for the bean factory + * @return a collection of bean definitions for {@link StateTransition} + * instances objects + */ + public Collection parse(Element element, ParserContext parserContext) { + + String idAttribute = element.getAttribute("id"); + + @SuppressWarnings("unchecked") + List flowElements = (List) DomUtils.getChildElementsByTagName(element, "flow"); + + BeanDefinitionBuilder stateBuilder = BeanDefinitionBuilder.genericBeanDefinition(SplitState.class); + + Collection flows = new ArrayList(); + FlowParser flowParser = new FlowParser(); + int i = 0; + for (Element nextElement : flowElements) { + flows.add(flowParser.parse(nextElement, parserContext, idAttribute+"#"+i)); + i++; + } + ManagedList managedList = new ManagedList(); + @SuppressWarnings( { "unchecked", "unused" }) + boolean dummy = managedList.addAll(flows); + + stateBuilder.addConstructorArgValue(managedList); + stateBuilder.addConstructorArgValue(idAttribute); + + // TODO: extract common code from StepParser + // TODO: allow TaskExecutor etc. to be set + + Collection list = new ArrayList(); + + String shortNextAttribute = element.getAttribute("next"); + boolean hasNextAttribute = StringUtils.hasText(shortNextAttribute); + if (hasNextAttribute) { + list.add(StepParser.getStateTransitionReference(parserContext, stateBuilder.getBeanDefinition(), null, + shortNextAttribute)); + } + + @SuppressWarnings("unchecked") + List nextElements = (List) DomUtils.getChildElementsByTagName(element, "next"); + + // If there are no next elements then this must be an end state + if (nextElements.isEmpty() && !hasNextAttribute) { + list.add(StepParser.getStateTransitionReference(parserContext, stateBuilder.getBeanDefinition(), null, null)); + } + else { + // Otherwise we need to capture the "to" state + for (Element nextElement : nextElements) { + String onAttribute = nextElement.getAttribute("on"); + String nextAttribute = nextElement.getAttribute("to"); + if (hasNextAttribute && onAttribute.equals("*")) { + throw new BeanCreationException("Duplicate transition pattern found for '*' " + + "(only specify one of next= attribute at step level and next element with on='*')"); + } + list.add(StepParser.getStateTransitionReference(parserContext, stateBuilder.getBeanDefinition(), + onAttribute, nextAttribute)); + } + } + + return list; + + } + +} diff --git a/spring-batch-core/src/main/java/org/springframework/batch/core/configuration/xml/StepParser.java b/spring-batch-core/src/main/java/org/springframework/batch/core/configuration/xml/StepParser.java index 05d66773b..ab993037b 100644 --- a/spring-batch-core/src/main/java/org/springframework/batch/core/configuration/xml/StepParser.java +++ b/spring-batch-core/src/main/java/org/springframework/batch/core/configuration/xml/StepParser.java @@ -23,6 +23,7 @@ import org.springframework.batch.core.Step; import org.springframework.batch.core.job.flow.StepState; import org.springframework.batch.flow.StateTransition; import org.springframework.beans.factory.BeanCreationException; +import org.springframework.beans.factory.config.BeanDefinition; import org.springframework.beans.factory.config.RuntimeBeanReference; import org.springframework.beans.factory.parsing.BeanComponentDefinition; import org.springframework.beans.factory.support.AbstractBeanDefinition; @@ -92,20 +93,33 @@ public class StepParser { } /** - * @param parserContext the parser context - * @param stepReference a reference to the step implementation - * @param on the pattern value - * @param next the next step id - * @return a bean definition for a {@link StepTransition} + * @param parserContext + * @param runtimeBeanReference + * @param onAttribute + * @param nextAttribute + * @return */ private RuntimeBeanReference getStateTransitionReference(ParserContext parserContext, - RuntimeBeanReference stepReference, String on, String next) { + RuntimeBeanReference runtimeBeanReference, String onAttribute, String nextAttribute) { + BeanDefinitionBuilder stateBuilder = BeanDefinitionBuilder.genericBeanDefinition(StepState.class); + stateBuilder.addConstructorArgValue(runtimeBeanReference); + return getStateTransitionReference(parserContext, stateBuilder.getBeanDefinition(), onAttribute, + nextAttribute); + } + + /** + * @param parserContext the parser context + * @param stateDefinition a reference to the state implementation + * @param on the pattern value + * @param next the next step id + * @return a bean definition for a {@link StateTransition} + */ + public static RuntimeBeanReference getStateTransitionReference(ParserContext parserContext, + BeanDefinition stateDefinition, String on, + String next) { BeanDefinitionBuilder nextBuilder = BeanDefinitionBuilder.genericBeanDefinition(StateTransition.class); - BeanDefinitionBuilder stateBuilder = BeanDefinitionBuilder.genericBeanDefinition(StepState.class); - stateBuilder.addConstructorArgValue(stepReference); - - nextBuilder.addConstructorArgValue(stateBuilder.getBeanDefinition()); + nextBuilder.addConstructorArgValue(stateDefinition); if (StringUtils.hasText(on)) { nextBuilder.addConstructorArgValue(on); @@ -114,7 +128,8 @@ public class StepParser { if (StringUtils.hasText(next)) { nextBuilder.setFactoryMethod("createStateTransition"); nextBuilder.addConstructorArgValue(next); - } else { + } + else { nextBuilder.setFactoryMethod("createEndStateTransition"); } diff --git a/spring-batch-core/src/main/java/org/springframework/batch/core/job/flow/DecisionState.java b/spring-batch-core/src/main/java/org/springframework/batch/core/job/flow/DecisionState.java index 8d3cac558..2942839fa 100644 --- a/spring-batch-core/src/main/java/org/springframework/batch/core/job/flow/DecisionState.java +++ b/spring-batch-core/src/main/java/org/springframework/batch/core/job/flow/DecisionState.java @@ -13,7 +13,7 @@ public class DecisionState extends AbstractState { /** * @param name */ - DecisionState(String name, JobExecutionDecider decider) { + DecisionState(JobExecutionDecider decider, String name) { super(name); this.decider = decider; } diff --git a/spring-batch-core/src/main/resources/org/springframework/batch/core/configuration/xml/spring-batch-2.0.xsd b/spring-batch-core/src/main/resources/org/springframework/batch/core/configuration/xml/spring-batch-2.0.xsd index af0212fba..92628ac90 100644 --- a/spring-batch-core/src/main/resources/org/springframework/batch/core/configuration/xml/spring-batch-2.0.xsd +++ b/spring-batch-core/src/main/resources/org/springframework/batch/core/configuration/xml/spring-batch-2.0.xsd @@ -30,7 +30,7 @@ - + @@ -76,12 +76,22 @@ specific match will be chosen to select the next step. Hint: always include a de - + Declares job should be paused at this point and provides pointer where execution should continue. + + + + + + + + + @@ -112,13 +122,18 @@ The decider is a reference to a JobExecutionDecider that can produce a status to - - - - A subflow within a job, having the same format as a job, but without a separate identity. - - - + + + + + + A subflow within a job, having the same format as a job, but without a separate identity. + + + + + + @@ -165,5 +180,5 @@ The decider is a reference to a JobExecutionDecider that can produce a status to - + \ No newline at end of file diff --git a/spring-batch-core/src/test/java/org/springframework/batch/core/configuration/xml/DecisionJobParserTests.java b/spring-batch-core/src/test/java/org/springframework/batch/core/configuration/xml/DecisionJobParserTests.java new file mode 100644 index 000000000..615dbe5d9 --- /dev/null +++ b/spring-batch-core/src/test/java/org/springframework/batch/core/configuration/xml/DecisionJobParserTests.java @@ -0,0 +1,72 @@ +/* + * Copyright 2006-2007 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.springframework.batch.core.configuration.xml; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; + +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.springframework.batch.core.BatchStatus; +import org.springframework.batch.core.Job; +import org.springframework.batch.core.JobExecution; +import org.springframework.batch.core.JobParameters; +import org.springframework.batch.core.job.flow.JobExecutionDecider; +import org.springframework.batch.core.repository.JobRepository; +import org.springframework.batch.core.repository.support.MapJobRepositoryFactoryBean; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Qualifier; +import org.springframework.test.context.ContextConfiguration; +import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; + + +/** + * @author Dave Syer + * + */ +@ContextConfiguration +@RunWith(SpringJUnit4ClassRunner.class) +public class DecisionJobParserTests { + + @Autowired + @Qualifier("job") + private Job job; + + @Autowired + private JobRepository jobRepository; + + @Before + public void setUp() { + MapJobRepositoryFactoryBean.clear(); + } + + @Test + public void testDecisionState() throws Exception { + assertNotNull(job); + JobExecution jobExecution = jobRepository.createJobExecution(job.getName(), new JobParameters()); + job.execute(jobExecution); + assertEquals(BatchStatus.COMPLETED, jobExecution.getStatus()); + assertEquals(1, jobExecution.getStepExecutions().size()); + } + + public static class TestDecider implements JobExecutionDecider { + public String decide(JobExecution jobExecution) { + return "FOO"; + } + } + +} diff --git a/spring-batch-core/src/test/java/org/springframework/batch/core/configuration/xml/PauseJobParserTests.java b/spring-batch-core/src/test/java/org/springframework/batch/core/configuration/xml/PauseJobParserTests.java new file mode 100644 index 000000000..6e96dffda --- /dev/null +++ b/spring-batch-core/src/test/java/org/springframework/batch/core/configuration/xml/PauseJobParserTests.java @@ -0,0 +1,67 @@ +/* + * Copyright 2006-2007 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.springframework.batch.core.configuration.xml; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; + +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.springframework.batch.core.BatchStatus; +import org.springframework.batch.core.Job; +import org.springframework.batch.core.JobExecution; +import org.springframework.batch.core.JobParameters; +import org.springframework.batch.core.repository.JobRepository; +import org.springframework.batch.core.repository.support.MapJobRepositoryFactoryBean; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Qualifier; +import org.springframework.test.context.ContextConfiguration; +import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; + +/** + * @author Dave Syer + * + */ +@ContextConfiguration +@RunWith(SpringJUnit4ClassRunner.class) +public class PauseJobParserTests { + + @Autowired + @Qualifier("job") + private Job job; + + @Autowired + private JobRepository jobRepository; + + @Before + public void setUp() { + MapJobRepositoryFactoryBean.clear(); + } + + @Test + public void testPauseState() throws Exception { + assertNotNull(job); + JobExecution jobExecution = jobRepository.createJobExecution(job.getName(), new JobParameters()); + job.execute(jobExecution); + assertEquals(BatchStatus.PAUSED, jobExecution.getStatus()); + assertEquals(1, jobExecution.getStepExecutions().size()); + job.execute(jobExecution); + assertEquals(BatchStatus.COMPLETED, jobExecution.getStatus()); + assertEquals(2, jobExecution.getStepExecutions().size()); + } + +} diff --git a/spring-batch-core/src/test/java/org/springframework/batch/core/configuration/xml/SplitJobParserTests.java b/spring-batch-core/src/test/java/org/springframework/batch/core/configuration/xml/SplitJobParserTests.java new file mode 100644 index 000000000..980d584d5 --- /dev/null +++ b/spring-batch-core/src/test/java/org/springframework/batch/core/configuration/xml/SplitJobParserTests.java @@ -0,0 +1,65 @@ +/* + * Copyright 2006-2007 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.springframework.batch.core.configuration.xml; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; + +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.springframework.batch.core.BatchStatus; +import org.springframework.batch.core.Job; +import org.springframework.batch.core.JobExecution; +import org.springframework.batch.core.JobParameters; +import org.springframework.batch.core.repository.JobRepository; +import org.springframework.batch.core.repository.support.MapJobRepositoryFactoryBean; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Qualifier; +import org.springframework.test.context.ContextConfiguration; +import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; + + +/** + * @author Dave Syer + * + */ +@ContextConfiguration +@RunWith(SpringJUnit4ClassRunner.class) +public class SplitJobParserTests { + + @Autowired + @Qualifier("job") + private Job job; + + @Autowired + private JobRepository jobRepository; + + @Before + public void setUp() { + MapJobRepositoryFactoryBean.clear(); + } + + @Test + public void testSplitJob() throws Exception { + assertNotNull(job); + JobExecution jobExecution = jobRepository.createJobExecution(job.getName(), new JobParameters()); + job.execute(jobExecution); + assertEquals(BatchStatus.COMPLETED, jobExecution.getStatus()); + assertEquals(4, jobExecution.getStepExecutions().size()); + } + +} diff --git a/spring-batch-core/src/test/java/org/springframework/batch/core/job/flow/FlowJobTests.java b/spring-batch-core/src/test/java/org/springframework/batch/core/job/flow/FlowJobTests.java index bde04f0ba..06476c718 100644 --- a/spring-batch-core/src/test/java/org/springframework/batch/core/job/flow/FlowJobTests.java +++ b/spring-batch-core/src/test/java/org/springframework/batch/core/job/flow/FlowJobTests.java @@ -164,9 +164,9 @@ public class FlowJobTests { }; Collection> transitions = new ArrayList>(); transitions.add(StateTransition.createStateTransition(new StepState(new StubStep("step1")), "*", "decision")); - transitions.add(StateTransition.createStateTransition(new DecisionState("decision", decider), "*", "step2")); + transitions.add(StateTransition.createStateTransition(new DecisionState(decider, "decision"), "*", "step2")); transitions.add(StateTransition - .createStateTransition(new DecisionState("decision", decider), "SWITCH", "step3")); + .createStateTransition(new DecisionState(decider, "decision"), "SWITCH", "step3")); transitions.add(StateTransition.createEndStateTransition(new StepState(new StubStep("step2")), "*")); transitions.add(StateTransition.createEndStateTransition(new StepState(new StubStep("step3")), "*")); flow.setStateTransitions(transitions); diff --git a/spring-batch-core/src/test/resources/org/springframework/batch/core/configuration/xml/DecisionJobParserTests-context.xml b/spring-batch-core/src/test/resources/org/springframework/batch/core/configuration/xml/DecisionJobParserTests-context.xml new file mode 100644 index 000000000..b1d23c297 --- /dev/null +++ b/spring-batch-core/src/test/resources/org/springframework/batch/core/configuration/xml/DecisionJobParserTests-context.xml @@ -0,0 +1,20 @@ + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/spring-batch-core/src/test/resources/org/springframework/batch/core/configuration/xml/PauseJobParserTests-context.xml b/spring-batch-core/src/test/resources/org/springframework/batch/core/configuration/xml/PauseJobParserTests-context.xml new file mode 100644 index 000000000..05adbafac --- /dev/null +++ b/spring-batch-core/src/test/resources/org/springframework/batch/core/configuration/xml/PauseJobParserTests-context.xml @@ -0,0 +1,15 @@ + + + + + + + + + + + + \ No newline at end of file diff --git a/spring-batch-core/src/test/resources/org/springframework/batch/core/configuration/xml/SplitJobParserTests-context.xml b/spring-batch-core/src/test/resources/org/springframework/batch/core/configuration/xml/SplitJobParserTests-context.xml new file mode 100644 index 000000000..6bf996787 --- /dev/null +++ b/spring-batch-core/src/test/resources/org/springframework/batch/core/configuration/xml/SplitJobParserTests-context.xml @@ -0,0 +1,22 @@ + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/spring-batch-core/src/test/resources/org/springframework/batch/core/configuration/xml/common-context.xml b/spring-batch-core/src/test/resources/org/springframework/batch/core/configuration/xml/common-context.xml index 2f280b025..1f92263ac 100644 --- a/spring-batch-core/src/test/resources/org/springframework/batch/core/configuration/xml/common-context.xml +++ b/spring-batch-core/src/test/resources/org/springframework/batch/core/configuration/xml/common-context.xml @@ -21,5 +21,6 @@ + \ No newline at end of file diff --git a/spring-batch-infrastructure/src/main/java/org/springframework/batch/flow/SimpleFlow.java b/spring-batch-infrastructure/src/main/java/org/springframework/batch/flow/SimpleFlow.java index b8b4a70c8..d90be80bd 100644 --- a/spring-batch-infrastructure/src/main/java/org/springframework/batch/flow/SimpleFlow.java +++ b/spring-batch-infrastructure/src/main/java/org/springframework/batch/flow/SimpleFlow.java @@ -37,7 +37,7 @@ import com.sun.org.apache.xerces.internal.impl.xpath.XPath.Step; * @author Dave Syer * */ -public class SimpleFlow implements Flow { +public class SimpleFlow implements Flow, InitializingBean { private State startState; @@ -87,7 +87,7 @@ public class SimpleFlow implements Flow { } /** - * Locate start step and pre-populate data structures needed for execution. + * Locate start state and pre-populate data structures needed for execution. * * @see InitializingBean#afterPropertiesSet() */ @@ -132,7 +132,7 @@ public class SimpleFlow implements Flow { * @return the next {@link Step} (or null if this is the end) * @throws JobExecutionException */ - private State nextState(String stepName, String status) throws FlowExecutionException { + private State nextState(String stateName, String status) throws FlowExecutionException { // Special status value indicating that a state wishes to pause // execution @@ -140,11 +140,11 @@ public class SimpleFlow implements Flow { return null; } - Set> set = transitionMap.get(stepName); + Set> set = transitionMap.get(stateName); if (set == null) { throw new FlowExecutionException(String.format("No transitions found in flow=%s for state=%s", getName(), - stepName)); + stateName)); } String next = null; @@ -161,7 +161,7 @@ public class SimpleFlow implements Flow { if (next == null) { throw new FlowExecutionException(String.format( - "Next state not found in flow=%s for step=%s with exit status=%s", getName(), stepName, status)); + "Next state not found in flow=%s for state=%s with exit status=%s", getName(), stateName, status)); } if (!stateMap.containsKey(next)) { @@ -183,9 +183,9 @@ public class SimpleFlow implements Flow { stateMap.clear(); boolean hasEndStep = false; - for (StateTransition stepTransition : stateTransitions) { - State step = stepTransition.getState(); - stateMap.put(step.getName(), step); + for (StateTransition stateTransition : stateTransitions) { + State state = stateTransition.getState(); + stateMap.put(state.getName(), state); } for (StateTransition stateTransition : stateTransitions) { @@ -197,7 +197,7 @@ public class SimpleFlow implements Flow { String next = stateTransition.getNext(); if (!stateMap.containsKey(next)) { - throw new IllegalArgumentException("Missing step for [" + stateTransition + "]"); + throw new IllegalArgumentException("Missing state for [" + stateTransition + "]"); } } @@ -218,7 +218,7 @@ public class SimpleFlow implements Flow { if (!hasEndStep) { throw new IllegalArgumentException( - "No end step was found. You must specify at least one transition with no next state."); + "No end state was found. You must specify at least one transition with no next state."); } if (startStateName != null) { @@ -237,16 +237,16 @@ public class SimpleFlow implements Flow { Set nextStateNames = new HashSet(); - for (StateTransition stepTransition : stateTransitions) { - nextStateNames.add(stepTransition.getNext()); + for (StateTransition stateTransition : stateTransitions) { + nextStateNames.add(stateTransition.getNext()); } - for (StateTransition stepTransition : stateTransitions) { - State state = stepTransition.getState(); + for (StateTransition stateTransition : stateTransitions) { + State state = stateTransition.getState(); if (!nextStateNames.contains(state.getName())) { if (startState != null && !startState.getName().equals(state.getName())) { throw new IllegalArgumentException(String.format( - "Multiple possible start steps found: [%s, %s]. " + "Multiple possible start states found: [%s, %s]. " + "Please specify one explicitly with the startStateName property.", startState .getName(), state.getName())); } diff --git a/spring-batch-infrastructure/src/main/java/org/springframework/batch/flow/SplitState.java b/spring-batch-infrastructure/src/main/java/org/springframework/batch/flow/SplitState.java index fde283e32..806ecba57 100644 --- a/spring-batch-infrastructure/src/main/java/org/springframework/batch/flow/SplitState.java +++ b/spring-batch-infrastructure/src/main/java/org/springframework/batch/flow/SplitState.java @@ -42,7 +42,7 @@ public class SplitState extends AbstractState { /** * @param name */ - public SplitState(String name, Collection> flows) { + public SplitState(Collection> flows, String name) { super(name); this.flows = flows; } diff --git a/spring-batch-infrastructure/src/test/java/org/springframework/batch/flow/SplitStateTests.java b/spring-batch-infrastructure/src/test/java/org/springframework/batch/flow/SplitStateTests.java index 8302482dd..14ac8a0f9 100644 --- a/spring-batch-infrastructure/src/test/java/org/springframework/batch/flow/SplitStateTests.java +++ b/spring-batch-infrastructure/src/test/java/org/springframework/batch/flow/SplitStateTests.java @@ -45,7 +45,7 @@ public class SplitStateTests { flows.add(flow1); flows.add(flow2); - SplitState state = new SplitState("foo", flows); + SplitState state = new SplitState(flows, "foo"); EasyMock.expect(flow1.start(null)).andReturn(new FlowExecution("step1", FlowExecution.COMPLETED)); EasyMock.expect(flow2.start(null)).andReturn(new FlowExecution("step1", FlowExecution.COMPLETED)); @@ -69,7 +69,7 @@ public class SplitStateTests { flows.add(flow1); flows.add(flow2); - SplitState state = new SplitState("foo", flows); + SplitState state = new SplitState(flows, "foo"); state.setTaskExecutor(new SimpleAsyncTaskExecutor()); EasyMock.expect(flow1.start(null)).andReturn(new FlowExecution("step1", FlowExecution.COMPLETED));