Added new EndState to support JSR behavior for setting the JobExecution#exitStatus

This commit is contained in:
Michael Minella
2013-12-18 13:13:22 -06:00
parent d76209df10
commit 0e2edd295f
7 changed files with 242 additions and 40 deletions

View File

@@ -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;

View File

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

View File

@@ -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)
*

View File

@@ -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;
}
}

View File

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

View File

@@ -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";
}
}
}

View File

@@ -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>