EMMA Coverage Report (generated Thu May 22 12:08:10 CDT 2014)
[all classes][org.springframework.batch.core.job.flow.support]

COVERAGE SUMMARY FOR SOURCE FILE [SimpleFlow.java]

nameclass, %method, %block, %line, %
SimpleFlow.java100% (1/1)100% (12/12)89%  (418/472)96%  (91.3/95)

COVERAGE BREAKDOWN BY CLASS AND METHOD

nameclass, %method, %block, %line, %
     
class SimpleFlow100% (1/1)100% (12/12)89%  (418/472)96%  (91.3/95)
isFlowContinued (State, FlowExecutionStatus, StepExecution): boolean 100% (1/1)67%  (32/48)82%  (6.5/8)
nextState (String, FlowExecutionStatus): State 100% (1/1)67%  (76/114)87%  (14.8/17)
<static initializer> 100% (1/1)100% (4/4)100% (1/1)
SimpleFlow (String): void 100% (1/1)100% (21/21)100% (6/6)
afterPropertiesSet (): void 100% (1/1)100% (3/3)100% (2/2)
getName (): String 100% (1/1)100% (3/3)100% (1/1)
getState (String): State 100% (1/1)100% (6/6)100% (1/1)
getStates (): Collection 100% (1/1)100% (7/7)100% (1/1)
initializeTransitions (): void 100% (1/1)100% (128/128)100% (30/30)
resume (String, FlowExecutor): FlowExecution 100% (1/1)100% (118/118)100% (21/21)
setStateTransitions (List): void 100% (1/1)100% (4/4)100% (2/2)
start (FlowExecutor): FlowExecution 100% (1/1)100% (16/16)100% (5/5)

1/*
2 * Copyright 2006-2014 the original author or authors.
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 *      http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16package org.springframework.batch.core.job.flow.support;
17 
18import org.apache.commons.logging.Log;
19import org.apache.commons.logging.LogFactory;
20import org.springframework.batch.core.Step;
21import org.springframework.batch.core.StepExecution;
22import org.springframework.batch.core.job.flow.Flow;
23import org.springframework.batch.core.job.flow.FlowExecution;
24import org.springframework.batch.core.job.flow.FlowExecutionException;
25import org.springframework.batch.core.job.flow.FlowExecutionStatus;
26import org.springframework.batch.core.job.flow.FlowExecutor;
27import org.springframework.batch.core.job.flow.State;
28import org.springframework.beans.factory.InitializingBean;
29 
30import java.util.ArrayList;
31import java.util.Collection;
32import java.util.HashMap;
33import java.util.HashSet;
34import java.util.List;
35import java.util.Map;
36import java.util.Set;
37import java.util.SortedSet;
38import java.util.TreeSet;
39 
40/**
41 * A {@link Flow} that branches conditionally depending on the exit status of
42 * the last {@link State}. The input parameters are the state transitions (in no
43 * particular order). The start state name can be specified explicitly (and must
44 * exist in the set of transitions), or computed from the existing transitions,
45 * if unambiguous.
46 *
47 * @author Dave Syer
48 * @author Michael Minella
49 * @since 2.0
50 */
51public class SimpleFlow implements Flow, InitializingBean {
52 
53        private static final Log logger = LogFactory.getLog(SimpleFlow.class);
54 
55        private State startState;
56 
57        private Map<String, SortedSet<StateTransition>> transitionMap = new HashMap<String, SortedSet<StateTransition>>();
58 
59        private Map<String, State> stateMap = new HashMap<String, State>();
60 
61        private List<StateTransition> stateTransitions = new ArrayList<StateTransition>();
62 
63        private final String name;
64 
65        /**
66         * Create a flow with the given name.
67         *
68         * @param name the name of the flow
69         */
70        public SimpleFlow(String name) {
71                this.name = name;
72        }
73 
74        /**
75         * Get the name for this flow.
76         *
77         * @see Flow#getName()
78         */
79        @Override
80        public String getName() {
81                return name;
82        }
83 
84        /**
85         * Public setter for the stateTransitions.
86         *
87         * @param stateTransitions the stateTransitions to set
88         */
89        public void setStateTransitions(List<StateTransition> stateTransitions) {
90 
91                this.stateTransitions = stateTransitions;
92        }
93 
94        /**
95         * {@inheritDoc}
96         */
97        @Override
98        public State getState(String stateName) {
99                return stateMap.get(stateName);
100        }
101 
102        /**
103         * {@inheritDoc}
104         */
105        @Override
106        public Collection<State> getStates() {
107                return new HashSet<State>(stateMap.values());
108        }
109 
110        /**
111         * Locate start state and pre-populate data structures needed for execution.
112         *
113         * @see InitializingBean#afterPropertiesSet()
114         */
115        @Override
116        public void afterPropertiesSet() throws Exception {
117                initializeTransitions();
118        }
119 
120        /**
121         * @see Flow#start(FlowExecutor)
122         */
123        @Override
124        public FlowExecution start(FlowExecutor executor) throws FlowExecutionException {
125                if (startState == null) {
126                        initializeTransitions();
127                }
128                State state = startState;
129                String stateName = state.getName();
130                return resume(stateName, executor);
131        }
132 
133        /**
134         * @see Flow#resume(String, FlowExecutor)
135         */
136        @Override
137        public FlowExecution resume(String stateName, FlowExecutor executor) throws FlowExecutionException {
138 
139                FlowExecutionStatus status = FlowExecutionStatus.UNKNOWN;
140                State state = stateMap.get(stateName);
141 
142                logger.debug("Resuming state="+stateName+" with status="+status);
143                StepExecution stepExecution = null;
144 
145                // Terminate if there are no more states
146                while (isFlowContinued(state, status, stepExecution)) {
147                        stateName = state.getName();
148 
149                        try {
150                                logger.debug("Handling state="+stateName);
151                                status = state.handle(executor);
152                                stepExecution = executor.getStepExecution();
153                        }
154                        catch (FlowExecutionException e) {
155                                executor.close(new FlowExecution(stateName, status));
156                                throw e;
157                        }
158                        catch (Exception e) {
159                                executor.close(new FlowExecution(stateName, status));
160                                throw new FlowExecutionException(String.format("Ended flow=%s at state=%s with exception", name,
161                                                                                                                                          stateName), e);
162                        }
163 
164                        logger.debug("Completed state="+stateName+" with status="+status);
165 
166                        state = nextState(stateName, status);
167                }
168 
169                FlowExecution result = new FlowExecution(stateName, status);
170                executor.close(result);
171                return result;
172 
173        }
174 
175        private boolean isFlowContinued(State state, FlowExecutionStatus status, StepExecution stepExecution) {
176                boolean continued = true;
177 
178                continued = state != null && status!=FlowExecutionStatus.STOPPED;
179 
180                if(stepExecution != null) {
181                        Boolean reRun = (Boolean) stepExecution.getExecutionContext().get("batch.restart");
182                        Boolean executed = (Boolean) stepExecution.getExecutionContext().get("batch.executed");
183 
184                        if((executed == null || !executed) && reRun != null && reRun && status == FlowExecutionStatus.STOPPED && !state.getName().endsWith(stepExecution.getStepName())) {
185                                continued = true;
186                        }
187                }
188 
189                return continued;
190        }
191 
192        /**
193         * @return the next {@link Step} (or null if this is the end)
194         * @throws org.springframework.batch.core.job.flow.FlowExecutionException
195         */
196        private State nextState(String stateName, FlowExecutionStatus status) throws FlowExecutionException {
197 
198                Set<StateTransition> set = transitionMap.get(stateName);
199 
200                if (set == null) {
201                        throw new FlowExecutionException(String.format("No transitions found in flow=%s for state=%s", getName(),
202                                                                                                                                  stateName));
203                }
204 
205                String next = null;
206                String exitCode = status.getName();
207                for (StateTransition stateTransition : set) {
208                        if (stateTransition.matches(exitCode) || (exitCode.equals("PENDING") && stateTransition.matches("STOPPED"))) {
209                                if (stateTransition.isEnd()) {
210                                        // End of job
211                                        return null;
212                                }
213                                next = stateTransition.getNext();
214                                break;
215                        }
216                }
217 
218                if (next == null) {
219                        throw new FlowExecutionException(String.format("Next state not found in flow=%s for state=%s with exit status=%s", getName(), stateName, status.getName()));
220                }
221 
222                if (!stateMap.containsKey(next)) {
223                        throw new FlowExecutionException(String.format("Next state not specified in flow=%s for next=%s",
224                                                                                                                                  getName(), next));
225                }
226 
227                return stateMap.get(next);
228 
229        }
230 
231        /**
232         * Analyse the transitions provided and generate all the information needed
233         * to execute the flow.
234         */
235        private void initializeTransitions() {
236                startState = null;
237                transitionMap.clear();
238                stateMap.clear();
239                boolean hasEndStep = false;
240 
241                if (stateTransitions.isEmpty()) {
242                        throw new IllegalArgumentException("No start state was found. You must specify at least one step in a job.");
243                }
244 
245                for (StateTransition stateTransition : stateTransitions) {
246                        State state = stateTransition.getState();
247                        String stateName = state.getName();
248                        stateMap.put(stateName, state);
249                }
250 
251                for (StateTransition stateTransition : stateTransitions) {
252 
253                        State state = stateTransition.getState();
254 
255                        if (!stateTransition.isEnd()) {
256 
257                                String next = stateTransition.getNext();
258 
259                                if (!stateMap.containsKey(next)) {
260                                        throw new IllegalArgumentException("Missing state for [" + stateTransition + "]");
261                                }
262 
263                        }
264                        else {
265                                hasEndStep = true;
266                        }
267 
268                        String name = state.getName();
269 
270                        SortedSet<StateTransition> set = transitionMap.get(name);
271                        if (set == null) {
272                                set = new TreeSet<StateTransition>();
273                                transitionMap.put(name, set);
274                        }
275                        set.add(stateTransition);
276 
277                }
278 
279                if (!hasEndStep) {
280                        throw new IllegalArgumentException(
281                                                                                                          "No end state was found.  You must specify at least one transition with no next state.");
282                }
283 
284                startState = stateTransitions.get(0).getState();
285 
286        }
287}

[all classes][org.springframework.batch.core.job.flow.support]
EMMA 2.0.5312 (C) Vladimir Roubtsov