diff --git a/spring-batch-core/pom.xml b/spring-batch-core/pom.xml
index e87696d4d..0314d3ed2 100644
--- a/spring-batch-core/pom.xml
+++ b/spring-batch-core/pom.xml
@@ -5,6 +5,7 @@
jar
Core
Core domain for batch processing, expressing a domain of Jobs, Steps, Chunks, etc.
+ http://static.springframework.org/spring-batch/${project.artifactId}
org.springframework.batch
spring-batch-parent
diff --git a/spring-batch-core/src/main/java/org/springframework/batch/core/configuration/xml/AbstractFlowParser.java b/spring-batch-core/src/main/java/org/springframework/batch/core/configuration/xml/AbstractFlowParser.java
index 58a09109c..2823dddc5 100644
--- a/spring-batch-core/src/main/java/org/springframework/batch/core/configuration/xml/AbstractFlowParser.java
+++ b/spring-batch-core/src/main/java/org/springframework/batch/core/configuration/xml/AbstractFlowParser.java
@@ -25,7 +25,6 @@ import java.util.Map;
import java.util.Set;
import org.springframework.batch.core.job.flow.FlowExecutionStatus;
-import org.springframework.batch.core.job.flow.support.SimpleFlow;
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.beans.factory.parsing.CompositeComponentDefinition;
import org.springframework.beans.factory.support.BeanDefinitionBuilder;
@@ -81,20 +80,8 @@ public abstract class AbstractFlowParser extends AbstractSingleBeanDefinitionPar
// For generating unique state names for end transitions
private static int endCounter = 0;
- private String flowName;
-
private String jobFactoryRef;
- /**
- * Convenience method for subclasses to set up the flow name for error
- * reporting.
- *
- * @param flowName
- */
- protected void setFlowName(String flowName) {
- this.flowName = flowName;
- }
-
/**
* Convenience method for subclasses to set the job factory reference if it
* is available (null is fine, but the quality of error reports is better if
@@ -112,8 +99,8 @@ public abstract class AbstractFlowParser extends AbstractSingleBeanDefinitionPar
* @see AbstractSingleBeanDefinitionParser#getBeanClass(Element)
*/
@Override
- protected Class getBeanClass(Element element) {
- return SimpleFlow.class;
+ protected Class> getBeanClass(Element element) {
+ return SimpleFlowFactoryBean.class;
}
/**
@@ -166,6 +153,7 @@ public abstract class AbstractFlowParser extends AbstractSingleBeanDefinitionPar
}
}
+ String flowName = (String) builder.getRawBeanDefinition().getAttribute("flowName");
if (!stepExists && !StringUtils.hasText(element.getAttribute("parent"))) {
parserContext.getReaderContext().error("The flow [" + flowName + "] must contain at least one step",
element);
@@ -180,7 +168,6 @@ public abstract class AbstractFlowParser extends AbstractSingleBeanDefinitionPar
}
}
- builder.addConstructorArgValue(flowName);
ManagedList managedList = new ManagedList();
@SuppressWarnings( { "unchecked", "unused" })
boolean dummy = managedList.addAll(stateTransitions);
diff --git a/spring-batch-core/src/main/java/org/springframework/batch/core/configuration/xml/FlowElementParser.java b/spring-batch-core/src/main/java/org/springframework/batch/core/configuration/xml/FlowElementParser.java
index cfa0be742..8a23b641f 100644
--- a/spring-batch-core/src/main/java/org/springframework/batch/core/configuration/xml/FlowElementParser.java
+++ b/spring-batch-core/src/main/java/org/springframework/batch/core/configuration/xml/FlowElementParser.java
@@ -17,9 +17,11 @@ package org.springframework.batch.core.configuration.xml;
import java.util.Collection;
+import org.springframework.beans.MutablePropertyValues;
import org.springframework.beans.factory.config.BeanDefinition;
-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.GenericBeanDefinition;
import org.springframework.beans.factory.xml.ParserContext;
import org.w3c.dom.Element;
@@ -49,7 +51,12 @@ public class FlowElementParser {
BeanDefinitionBuilder stateBuilder =
BeanDefinitionBuilder.genericBeanDefinition("org.springframework.batch.core.job.flow.support.state.FlowState");
- stateBuilder.addConstructorArgValue(new RuntimeBeanReference(refAttribute));
+
+ AbstractBeanDefinition flowDefinition = new GenericBeanDefinition();
+ flowDefinition.setParentName(refAttribute);
+ MutablePropertyValues propertyValues = flowDefinition.getPropertyValues();
+ propertyValues.addPropertyValue("name", idAttribute);
+ stateBuilder.addConstructorArgValue(flowDefinition);
stateBuilder.addConstructorArgValue(idAttribute);
return InlineFlowParser.getNextElements(parserContext, stateBuilder.getBeanDefinition(), element);
diff --git a/spring-batch-core/src/main/java/org/springframework/batch/core/configuration/xml/InlineFlowParser.java b/spring-batch-core/src/main/java/org/springframework/batch/core/configuration/xml/InlineFlowParser.java
index 23f680e2e..49a88311c 100644
--- a/spring-batch-core/src/main/java/org/springframework/batch/core/configuration/xml/InlineFlowParser.java
+++ b/spring-batch-core/src/main/java/org/springframework/batch/core/configuration/xml/InlineFlowParser.java
@@ -15,7 +15,6 @@
*/
package org.springframework.batch.core.configuration.xml;
-import org.springframework.batch.core.job.flow.support.SimpleFlow;
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.beans.factory.support.BeanDefinitionBuilder;
import org.springframework.beans.factory.xml.ParserContext;
@@ -27,6 +26,8 @@ import org.w3c.dom.Element;
*/
public class InlineFlowParser extends AbstractFlowParser {
+ private final String flowName;
+
/**
* Construct a {@link InlineFlowParser} with the specified name and using the
* provided job repository ref.
@@ -36,21 +37,11 @@ public class InlineFlowParser extends AbstractFlowParser {
* from the enclosing tag
*/
public InlineFlowParser(String flowName, String jobFactoryRef) {
- setFlowName(flowName);
+ this.flowName = flowName;
setJobFactoryRef(jobFactoryRef);
}
- /*
- * (non-Javadoc)
- *
- * @see AbstractSingleBeanDefinitionParser#getBeanClass(Element)
- */
- @Override
- protected Class getBeanClass(Element element) {
- return SimpleFlow.class;
- }
-
/**
* @param element the top level element containing a flow definition
* @param parserContext the {@link ParserContext}
@@ -58,6 +49,8 @@ public class InlineFlowParser extends AbstractFlowParser {
@Override
protected void doParse(Element element, ParserContext parserContext, BeanDefinitionBuilder builder) {
+ builder.getRawBeanDefinition().setAttribute("flowName", flowName);
+ builder.addPropertyValue("name", flowName);
super.doParse(element, parserContext, builder);
builder.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);
parserContext.popAndRegisterContainingComponent();
diff --git a/spring-batch-core/src/main/java/org/springframework/batch/core/configuration/xml/SimpleFlowFactoryBean.java b/spring-batch-core/src/main/java/org/springframework/batch/core/configuration/xml/SimpleFlowFactoryBean.java
new file mode 100644
index 000000000..4f4a0daa4
--- /dev/null
+++ b/spring-batch-core/src/main/java/org/springframework/batch/core/configuration/xml/SimpleFlowFactoryBean.java
@@ -0,0 +1,179 @@
+package org.springframework.batch.core.configuration.xml;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import org.springframework.batch.core.JobInterruptedException;
+import org.springframework.batch.core.Step;
+import org.springframework.batch.core.StepExecution;
+import org.springframework.batch.core.job.flow.FlowExecutionStatus;
+import org.springframework.batch.core.job.flow.FlowExecutor;
+import org.springframework.batch.core.job.flow.State;
+import org.springframework.batch.core.job.flow.support.SimpleFlow;
+import org.springframework.batch.core.job.flow.support.StateTransition;
+import org.springframework.batch.core.job.flow.support.state.AbstractState;
+import org.springframework.batch.core.job.flow.support.state.StepState;
+import org.springframework.beans.factory.FactoryBean;
+import org.springframework.beans.factory.InitializingBean;
+import org.springframework.util.Assert;
+
+/**
+ * Convenience factory for SimpleFlow instances for use in XML namespace. It
+ * replaces the states in the input with proxies that have a unique name formed
+ * from the flow name and the original state name (unless the name is already in
+ * that form, in which case it is not modified).
+ *
+ * @author Dave Syer
+ *
+ */
+public class SimpleFlowFactoryBean implements FactoryBean, InitializingBean {
+
+ private String name;
+
+ private List stateTransitions;
+
+ private String prefix;
+
+ /**
+ * The name of the flow that is created by this factory.
+ *
+ * @param name the value of the name
+ */
+ public void setName(String name) {
+ this.name = name;
+ this.prefix = name + ".";
+ }
+
+ /**
+ * The raw state transitions for the flow. They will be transformed into
+ * proxies that have the same behaviour but unique names prefixed with the
+ * flow name.
+ *
+ * @param name the value of the name
+ */
+ public void setStateTransitions(List stateTransitions) {
+ this.stateTransitions = stateTransitions;
+ }
+
+ /**
+ * Check mandatory properties (name).
+ *
+ * @throws Exception
+ */
+ public void afterPropertiesSet() throws Exception {
+ Assert.hasText(name, "The flow must have a name");
+ }
+
+ public Object getObject() throws Exception {
+
+ SimpleFlow flow = new SimpleFlow(name);
+
+ List updatedTransitions = new ArrayList();
+ for (StateTransition stateTransition : stateTransitions) {
+ State state = getProxyState(stateTransition.getState());
+ updatedTransitions.add(StateTransition.switchOriginAndDestination(stateTransition, state, getNext(stateTransition.getNext())));
+ }
+
+ flow.setStateTransitions(updatedTransitions);
+ flow.afterPropertiesSet();
+ return flow;
+
+ }
+
+ private String getNext(String next) {
+ if (next == null) {
+ return null;
+ }
+ return (next.startsWith(this.prefix) ? "" : this.prefix) + next;
+ }
+
+ /**
+ * Convenience method to get a state that proxies the input but with a
+ * different name, appropriate to this flow. If the state is a StepState
+ * then the step name is also changed.
+ *
+ * @param state
+ * @return
+ */
+ private State getProxyState(State state) {
+ String oldName = state.getName();
+ if (oldName.startsWith(prefix)) {
+ return state;
+ }
+ String stateName = prefix + oldName;
+ if (state instanceof StepState) {
+ Step step = ((StepState) state).getStep();
+ return new StepState(stateName, new DelegateStep(step, stateName));
+ }
+ return new DelegateState(stateName, state);
+ }
+
+ public Class> getObjectType() {
+ return SimpleFlow.class;
+ }
+
+ public boolean isSingleton() {
+ return true;
+ }
+
+ /**
+ * A State that proxies a delegate and changes its name but leaves its
+ * behaviour unchanged.
+ *
+ * @author Dave Syer
+ *
+ */
+ private static class DelegateState extends AbstractState {
+ private final State state;
+
+ private DelegateState(String name, State state) {
+ super(name);
+ this.state = state;
+ }
+
+ public boolean isEndState() {
+ return state.isEndState();
+ }
+
+ @Override
+ public FlowExecutionStatus handle(FlowExecutor executor) throws Exception {
+ return state.handle(executor);
+ }
+ }
+
+ /**
+ * A Step that proxies a delegate and changes its name but leaves its
+ * behaviour unchanged.
+ *
+ * @author Dave Syer
+ *
+ */
+ private static class DelegateStep implements Step {
+
+ private final Step step;
+
+ private final String name;
+
+ private DelegateStep(Step step, String name) {
+ this.step = step;
+ this.name = name;
+ }
+
+ public boolean isAllowStartIfComplete() {
+ return step.isAllowStartIfComplete();
+ }
+
+ public int getStartLimit() {
+ return step.getStartLimit();
+ }
+
+ public String getName() {
+ return name;
+ }
+
+ public void execute(StepExecution stepExecution) throws JobInterruptedException {
+ step.execute(stepExecution);
+ }
+ }
+
+}
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
index 499d9ff5c..1064e01f5 100644
--- 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
@@ -18,9 +18,12 @@ package org.springframework.batch.core.configuration.xml;
import java.util.Collection;
import java.util.List;
+import org.springframework.beans.MutablePropertyValues;
import org.springframework.beans.factory.config.BeanDefinition;
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.GenericBeanDefinition;
import org.springframework.beans.factory.support.ManagedList;
import org.springframework.beans.factory.xml.ParserContext;
import org.springframework.util.StringUtils;
@@ -86,23 +89,28 @@ public class SplitParser {
@SuppressWarnings("unchecked")
Collection