Added new EndState to support JSR behavior for setting the JobExecution#exitStatus
This commit is contained in:
@@ -44,33 +44,33 @@ import org.w3c.dom.NodeList;
|
||||
*/
|
||||
public abstract class AbstractFlowParser extends AbstractSingleBeanDefinitionParser {
|
||||
|
||||
private static final String ID_ATTR = "id";
|
||||
protected static final String ID_ATTR = "id";
|
||||
|
||||
private static final String STEP_ELE = "step";
|
||||
protected static final String STEP_ELE = "step";
|
||||
|
||||
private static final String FLOW_ELE = "flow";
|
||||
protected static final String FLOW_ELE = "flow";
|
||||
|
||||
private static final String DECISION_ELE = "decision";
|
||||
protected static final String DECISION_ELE = "decision";
|
||||
|
||||
private static final String SPLIT_ELE = "split";
|
||||
protected static final String SPLIT_ELE = "split";
|
||||
|
||||
private static final String NEXT_ATTR = "next";
|
||||
protected static final String NEXT_ATTR = "next";
|
||||
|
||||
private static final String NEXT_ELE = "next";
|
||||
protected static final String NEXT_ELE = "next";
|
||||
|
||||
private static final String END_ELE = "end";
|
||||
protected static final String END_ELE = "end";
|
||||
|
||||
private static final String FAIL_ELE = "fail";
|
||||
protected static final String FAIL_ELE = "fail";
|
||||
|
||||
private static final String STOP_ELE = "stop";
|
||||
protected static final String STOP_ELE = "stop";
|
||||
|
||||
private static final String ON_ATTR = "on";
|
||||
protected static final String ON_ATTR = "on";
|
||||
|
||||
private static final String TO_ATTR = "to";
|
||||
protected static final String TO_ATTR = "to";
|
||||
|
||||
private static final String RESTART_ATTR = "restart";
|
||||
protected static final String RESTART_ATTR = "restart";
|
||||
|
||||
private static final String EXIT_CODE_ATTR = "exit-code";
|
||||
protected static final String EXIT_CODE_ATTR = "exit-code";
|
||||
|
||||
private static final InlineStepParser stepParser = new InlineStepParser();
|
||||
|
||||
@@ -79,7 +79,7 @@ public abstract class AbstractFlowParser extends AbstractSingleBeanDefinitionPar
|
||||
private static final DecisionParser decisionParser = new DecisionParser();
|
||||
|
||||
// For generating unique state names for end transitions
|
||||
private static int endCounter = 0;
|
||||
protected static int endCounter = 0;
|
||||
|
||||
private String jobFactoryRef;
|
||||
|
||||
|
||||
@@ -29,8 +29,6 @@ import org.w3c.dom.Element;
|
||||
*/
|
||||
public class TopLevelFlowParser extends AbstractFlowParser {
|
||||
|
||||
private static final String ID_ATTR = "id";
|
||||
|
||||
private static final String ABSTRACT_ATTR = "abstract";
|
||||
|
||||
/**
|
||||
|
||||
@@ -108,12 +108,23 @@ public class EndState extends AbstractState {
|
||||
}
|
||||
}
|
||||
|
||||
executor.addExitStatus(code);
|
||||
setExitStatus(executor, code);
|
||||
|
||||
return status;
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Performs any logic to update the exit status for the current flow.
|
||||
*
|
||||
* @param executor {@link FlowExecutor} for the current flow
|
||||
* @param code The exit status to save
|
||||
*/
|
||||
protected void setExitStatus(FlowExecutor executor, String code) {
|
||||
executor.addExitStatus(code);
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
*
|
||||
|
||||
@@ -46,26 +46,15 @@ import org.w3c.dom.NodeList;
|
||||
* @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 static final List<String> TRANSITION_TYPES = new ArrayList<String>();
|
||||
|
||||
static {
|
||||
TRANSITION_TYPES.add(NEXT_ATTRIBUTE);
|
||||
TRANSITION_TYPES.add(STOP_ATTRIBUTE);
|
||||
TRANSITION_TYPES.add(END_ATTRIBUTE);
|
||||
TRANSITION_TYPES.add(FAIL_ATTRIBUTE);
|
||||
TRANSITION_TYPES.add(NEXT_ELE);
|
||||
TRANSITION_TYPES.add(STOP_ELE);
|
||||
TRANSITION_TYPES.add(END_ELE);
|
||||
TRANSITION_TYPES.add(FAIL_ELE);
|
||||
}
|
||||
|
||||
private String flowName;
|
||||
@@ -103,13 +92,13 @@ public class FlowParser extends AbstractFlowParser {
|
||||
if (node instanceof Element) {
|
||||
String nodeName = node.getLocalName();
|
||||
Element child = (Element) node;
|
||||
if (nodeName.equals(STEP_ELEMENT)) {
|
||||
if (nodeName.equals(STEP_ELE)) {
|
||||
stateTransitions.addAll(stepParser.parse(child, parserContext, builder));
|
||||
} else if(nodeName.equals(SPLIT_ELEMENT)) {
|
||||
} else if(nodeName.equals(SPLIT_ELE)) {
|
||||
stateTransitions.addAll(new SplitParser(flowName).parse(child, parserContext));
|
||||
} else if(nodeName.equals(DECISION_ELEMENT)) {
|
||||
} else if(nodeName.equals(DECISION_ELE)) {
|
||||
stateTransitions.addAll(new DecisionParser().parse(child, parserContext, flowName));
|
||||
} else if(nodeName.equals(FLOW_ELEMENT)) {
|
||||
} else if(nodeName.equals(FLOW_ELE)) {
|
||||
stateTransitions.addAll(parseFlow(child, parserContext, builder));
|
||||
}
|
||||
}
|
||||
@@ -194,9 +183,9 @@ public class FlowParser extends AbstractFlowParser {
|
||||
protected static Collection<BeanDefinition> parseTransitionElement(Element transitionElement, String stateId,
|
||||
BeanDefinition stateDef, ParserContext parserContext) {
|
||||
FlowExecutionStatus status = getBatchStatusFromEndTransitionName(transitionElement.getNodeName());
|
||||
String onAttribute = transitionElement.getAttribute(ON_ATTRIBUTE);
|
||||
String restartAttribute = transitionElement.getAttribute(RESTART_ATTRIBUTE);
|
||||
String nextAttribute = transitionElement.getAttribute(TO_ATTRIBUTE);
|
||||
String onAttribute = transitionElement.getAttribute(ON_ATTR);
|
||||
String restartAttribute = transitionElement.getAttribute(RESTART_ATTR);
|
||||
String nextAttribute = transitionElement.getAttribute(TO_ATTR);
|
||||
|
||||
if (!StringUtils.hasText(nextAttribute)) {
|
||||
nextAttribute = restartAttribute;
|
||||
@@ -207,4 +196,60 @@ public class FlowParser extends AbstractFlowParser {
|
||||
|
||||
return createTransition(status, onAttribute, nextAttribute, exitCodeAttribute, stateDef, parserContext, abandon);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param status The batch status that this transition will set. Use
|
||||
* BatchStatus.UNKNOWN if not applicable.
|
||||
* @param on The pattern that this transition should match. Use null for
|
||||
* "no restriction" (same as "*").
|
||||
* @param next The state to which this transition should go. Use null if not
|
||||
* applicable.
|
||||
* @param exitCode The exit code that this transition will set. Use null to
|
||||
* default to batchStatus.
|
||||
* @param stateDef The bean definition for the current state
|
||||
* @param parserContext the parser context for the bean factory
|
||||
* @param a collection of
|
||||
* {@link org.springframework.batch.core.job.flow.support.StateTransition}
|
||||
* references
|
||||
*/
|
||||
protected static Collection<BeanDefinition> createTransition(FlowExecutionStatus status, String on, String next,
|
||||
String exitCode, BeanDefinition stateDef, ParserContext parserContext, boolean abandon) {
|
||||
|
||||
BeanDefinition endState = null;
|
||||
|
||||
if (status.isEnd()) {
|
||||
|
||||
BeanDefinitionBuilder endBuilder = BeanDefinitionBuilder
|
||||
.genericBeanDefinition("org.springframework.batch.core.jsr.job.flow.support.state.EndState");
|
||||
|
||||
boolean exitCodeExists = StringUtils.hasText(exitCode);
|
||||
|
||||
endBuilder.addConstructorArgValue(status);
|
||||
|
||||
endBuilder.addConstructorArgValue(exitCodeExists ? exitCode : status.getName());
|
||||
|
||||
String endName = (status == FlowExecutionStatus.STOPPED ? STOP_ELE
|
||||
: status == FlowExecutionStatus.FAILED ? FAIL_ELE : END_ELE)
|
||||
+ (endCounter++);
|
||||
endBuilder.addConstructorArgValue(endName);
|
||||
|
||||
endBuilder.addConstructorArgValue(abandon);
|
||||
|
||||
String nextOnEnd = exitCodeExists ? null : next;
|
||||
endState = getStateTransitionReference(parserContext, endBuilder.getBeanDefinition(), null, nextOnEnd);
|
||||
next = endName;
|
||||
|
||||
}
|
||||
|
||||
Collection<BeanDefinition> list = new ArrayList<BeanDefinition>();
|
||||
list.add(getStateTransitionReference(parserContext, stateDef, on, next));
|
||||
if (endState != null) {
|
||||
//
|
||||
// Must be added after the state to ensure that the state is the
|
||||
// first in the list
|
||||
//
|
||||
list.add(endState);
|
||||
}
|
||||
return list;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,85 @@
|
||||
/*
|
||||
* 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.job.flow.support.state;
|
||||
|
||||
import org.springframework.batch.core.ExitStatus;
|
||||
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;
|
||||
|
||||
/**
|
||||
* {@link State} implementation for ending a job per JSR-352 rules if it is
|
||||
* in progress and continuing if just starting.
|
||||
*
|
||||
* @author Michael Minella
|
||||
* @since 3.0
|
||||
*/
|
||||
public class EndState extends org.springframework.batch.core.job.flow.support.state.EndState {
|
||||
|
||||
/**
|
||||
* @param status The {@link FlowExecutionStatus} to end with
|
||||
* @param name The name of the state
|
||||
*/
|
||||
public EndState(FlowExecutionStatus status, String name) {
|
||||
super(status, status.getName(), name);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param status The {@link FlowExecutionStatus} to end with
|
||||
* @param name The name of the state
|
||||
*/
|
||||
public EndState(FlowExecutionStatus status, String code, String name) {
|
||||
super(status, code, name, false);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param status The {@link FlowExecutionStatus} to end with
|
||||
* @param name The name of the state
|
||||
* @param abandon flag to indicate that previous step execution can be
|
||||
* marked as abandoned (if there is one)
|
||||
*
|
||||
*/
|
||||
public EndState(FlowExecutionStatus status, String code, String name, boolean abandon) {
|
||||
super(status, code, name, abandon);
|
||||
}
|
||||
|
||||
/* (non-Javadoc)
|
||||
* @see org.springframework.batch.core.job.flow.support.state.EndState#setExitStatus(org.springframework.batch.core.job.flow.FlowExecutor, java.lang.String)
|
||||
*/
|
||||
@Override
|
||||
protected void setExitStatus(FlowExecutor executor, String code) {
|
||||
StepExecution stepExecution = executor.getStepExecution();
|
||||
|
||||
if(!isNonDefaultExitStatus(code)) {
|
||||
stepExecution.getJobExecution().setExitStatus(new ExitStatus(code));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param curStatus the exit code to be evaluated
|
||||
* @return true if the value matches a known exit code
|
||||
*/
|
||||
protected boolean isNonDefaultExitStatus(String curStatus) {
|
||||
return curStatus == null ||
|
||||
curStatus.equals(ExitStatus.COMPLETED.getExitCode()) ||
|
||||
curStatus.equals(ExitStatus.EXECUTING.getExitCode()) ||
|
||||
curStatus.equals(ExitStatus.FAILED.getExitCode()) ||
|
||||
curStatus.equals(ExitStatus.NOOP.getExitCode()) ||
|
||||
curStatus.equals(ExitStatus.STOPPED.getExitCode()) ||
|
||||
curStatus.equals(ExitStatus.UNKNOWN.getExitCode());
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,51 @@
|
||||
/*
|
||||
* 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.job.flow.support.state;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
|
||||
import javax.batch.api.AbstractBatchlet;
|
||||
import javax.batch.runtime.BatchRuntime;
|
||||
import javax.batch.runtime.BatchStatus;
|
||||
import javax.batch.runtime.JobExecution;
|
||||
|
||||
import org.junit.Test;
|
||||
import org.springframework.batch.core.jsr.JsrTestUtils;
|
||||
|
||||
/**
|
||||
* Tests for the JSR-352 version of {@link EndState}
|
||||
*
|
||||
* @author Michael Minella
|
||||
*/
|
||||
public class EndStateTests {
|
||||
|
||||
@Test
|
||||
public void test() throws Exception {
|
||||
JobExecution jobExecution = JsrTestUtils.runJob("jobWithEndTransition", null, 10000l);
|
||||
|
||||
assertEquals(BatchStatus.COMPLETED, jobExecution.getBatchStatus());
|
||||
assertEquals("SUCCESS", jobExecution.getExitStatus());
|
||||
assertEquals(1, BatchRuntime.getJobOperator().getStepExecutions(jobExecution.getExecutionId()).size());
|
||||
}
|
||||
|
||||
public static class EndStateBatchlet extends AbstractBatchlet {
|
||||
|
||||
@Override
|
||||
public String process() throws Exception {
|
||||
return "GOOD";
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<job id="job" xmlns="http://xmlns.jcp.org/xml/ns/javaee" version="1.0">
|
||||
<step id="step1">
|
||||
<batchlet ref="org.springframework.batch.core.jsr.job.flow.support.state.EndStateTests$EndStateBatchlet"/>
|
||||
<next on="UNUSED" to="step2"/>
|
||||
<end on="GOOD" exit-status="SUCCESS"/>
|
||||
<fail on="UNUSED"/>
|
||||
</step>
|
||||
<step id="step2">
|
||||
<batchlet ref="org.springframework.batch.core.jsr.job.flow.support.state.EndStateTests$EndStateBatchlet"/>
|
||||
</step>
|
||||
</job>
|
||||
Reference in New Issue
Block a user