EMMA Coverage Report (generated Thu Jan 24 13:37:04 CST 2013)
[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% (11/11)92%  (376/410)98%  (82/84)

COVERAGE BREAKDOWN BY CLASS AND METHOD

nameclass, %method, %block, %line, %
     
class SimpleFlow100% (1/1)100% (11/11)92%  (376/410)98%  (82/84)
nextState (String, FlowExecutionStatus): State 100% (1/1)68%  (72/106)88%  (14/16)
<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% (112/112)100% (19/19)
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-2007 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 java.util.ArrayList;
19import java.util.Collection;
20import java.util.HashMap;
21import java.util.HashSet;
22import java.util.List;
23import java.util.Map;
24import java.util.Set;
25import java.util.SortedSet;
26import java.util.TreeSet;
27 
28import org.apache.commons.logging.Log;
29import org.apache.commons.logging.LogFactory;
30import org.springframework.batch.core.JobExecutionException;
31import org.springframework.batch.core.Step;
32import org.springframework.batch.core.job.flow.Flow;
33import org.springframework.batch.core.job.flow.FlowExecution;
34import org.springframework.batch.core.job.flow.FlowExecutionException;
35import org.springframework.batch.core.job.flow.FlowExecutionStatus;
36import org.springframework.batch.core.job.flow.FlowExecutor;
37import org.springframework.batch.core.job.flow.State;
38import org.springframework.beans.factory.InitializingBean;
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 * @since 2.0
49 */
50public class SimpleFlow implements Flow, InitializingBean {
51 
52        private static final Log logger = LogFactory.getLog(SimpleFlow.class);
53 
54        private State startState;
55 
56        private Map<String, SortedSet<StateTransition>> transitionMap = new HashMap<String, SortedSet<StateTransition>>();
57 
58        private Map<String, State> stateMap = new HashMap<String, State>();
59 
60        private List<StateTransition> stateTransitions = new ArrayList<StateTransition>();
61 
62        private final String name;
63 
64        /**
65         * Create a flow with the given name.
66         * 
67         * @param name the name of the flow
68         */
69        public SimpleFlow(String name) {
70                this.name = name;
71        }
72 
73        /**
74         * Get the name for this flow.
75         * 
76         * @see Flow#getName()
77         */
78        public String getName() {
79                return name;
80        }
81 
82        /**
83         * Public setter for the stateTransitions.
84         * 
85         * @param stateTransitions the stateTransitions to set
86         */
87        public void setStateTransitions(List<StateTransition> stateTransitions) {
88 
89                this.stateTransitions = stateTransitions;
90        }
91 
92        /**
93         * {@inheritDoc}
94         */
95        public State getState(String stateName) {
96                return stateMap.get(stateName);
97        }
98        
99        /**
100         * {@inheritDoc}
101         */
102        public Collection<State> getStates() {
103                return new HashSet<State>(stateMap.values());
104        }
105 
106        /**
107         * Locate start state and pre-populate data structures needed for execution.
108         * 
109         * @see InitializingBean#afterPropertiesSet()
110         */
111        public void afterPropertiesSet() throws Exception {
112                initializeTransitions();
113        }
114 
115        /**
116         * @see Flow#start(FlowExecutor)
117         */
118        public FlowExecution start(FlowExecutor executor) throws FlowExecutionException {
119                if (startState == null) {
120                        initializeTransitions();
121                }
122                State state = startState;
123                String stateName = state.getName();
124                return resume(stateName, executor);
125        }
126 
127        /**
128         * @see Flow#resume(String, FlowExecutor)
129         */
130        public FlowExecution resume(String stateName, FlowExecutor executor) throws FlowExecutionException {
131 
132                FlowExecutionStatus status = FlowExecutionStatus.UNKNOWN;
133                State state = stateMap.get(stateName);
134 
135                logger.debug("Resuming state="+stateName+" with status="+status);
136 
137                // Terminate if there are no more states
138                while (state != null && status!=FlowExecutionStatus.STOPPED) {
139 
140                        stateName = state.getName();
141 
142                        try {
143                                logger.debug("Handling state="+stateName);
144                                status = state.handle(executor);
145                        }
146                        catch (FlowExecutionException e) {
147                                executor.close(new FlowExecution(stateName, status));
148                                throw e;
149                        }
150                        catch (Exception e) {
151                                executor.close(new FlowExecution(stateName, status));
152                                throw new FlowExecutionException(String.format("Ended flow=%s at state=%s with exception", name,
153                                                stateName), e);
154                        }
155                        
156                        logger.debug("Completed state="+stateName+" with status="+status);
157 
158                        state = nextState(stateName, status);
159 
160                }
161 
162                FlowExecution result = new FlowExecution(stateName, status);
163                executor.close(result);
164                return result;
165 
166        }
167 
168        /**
169         * @return the next {@link Step} (or null if this is the end)
170         * @throws JobExecutionException
171         */
172        private State nextState(String stateName, FlowExecutionStatus status) throws FlowExecutionException {
173 
174                Set<StateTransition> set = transitionMap.get(stateName);
175 
176                if (set == null) {
177                        throw new FlowExecutionException(String.format("No transitions found in flow=%s for state=%s", getName(),
178                                        stateName));
179                }
180 
181                String next = null;
182                String exitCode = status.getName();
183                for (StateTransition stateTransition : set) {
184                        if (stateTransition.matches(exitCode)) {
185                                if (stateTransition.isEnd()) {
186                                        // End of job
187                                        return null;
188                                }
189                                next = stateTransition.getNext();
190                                break;
191                        }
192                }
193 
194                if (next == null) {
195                        throw new FlowExecutionException(String.format(
196                                        "Next state not found in flow=%s for state=%s with exit status=%s", getName(), stateName, status.getName()));
197                }
198 
199                if (!stateMap.containsKey(next)) {
200                        throw new FlowExecutionException(String.format("Next state not specified in flow=%s for next=%s",
201                                        getName(), next));
202                }
203 
204                return stateMap.get(next);
205 
206        }
207 
208        /**
209         * Analyse the transitions provided and generate all the information needed
210         * to execute the flow.
211         */
212        private void initializeTransitions() {
213                startState = null;
214                transitionMap.clear();
215                stateMap.clear();
216                boolean hasEndStep = false;
217 
218                if (stateTransitions.isEmpty()) {
219                        throw new IllegalArgumentException("No start state was found. You must specify at least one step in a job.");
220                }
221 
222                for (StateTransition stateTransition : stateTransitions) {
223                        State state = stateTransition.getState();
224                        String stateName = state.getName();
225                        stateMap.put(stateName, state);
226                }
227 
228                for (StateTransition stateTransition : stateTransitions) {
229 
230                        State state = stateTransition.getState();
231 
232                        if (!stateTransition.isEnd()) {
233 
234                                String next = stateTransition.getNext();
235 
236                                if (!stateMap.containsKey(next)) {
237                                        throw new IllegalArgumentException("Missing state for [" + stateTransition + "]");
238                                }
239 
240                        }
241                        else {
242                                hasEndStep = true;
243                        }
244 
245                        String name = state.getName();
246 
247                        SortedSet<StateTransition> set = transitionMap.get(name);
248                        if (set == null) {
249                                set = new TreeSet<StateTransition>();
250                                transitionMap.put(name, set);
251                        }
252                        set.add(stateTransition);
253 
254                }
255 
256                if (!hasEndStep) {
257                        throw new IllegalArgumentException(
258                                        "No end state was found.  You must specify at least one transition with no next state.");
259                }
260 
261                startState = stateTransitions.get(0).getState();
262 
263        }
264}

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