EMMA Coverage Report (generated Fri Aug 21 15:59:46 BST 2009)
[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%  (369/403)95%  (81/85)

COVERAGE BREAKDOWN BY CLASS AND METHOD

nameclass, %method, %block, %line, %
     
class SimpleFlow100% (1/1)100% (11/11)92%  (369/403)95%  (81/85)
nextState (String, FlowExecutionStatus): State 100% (1/1)68%  (72/106)79%  (15/19)
<static initializer> 100% (1/1)100% (4/4)100% (2/2)
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% (124/124)100% (27/27)
resume (String, FlowExecutor): FlowExecution 100% (1/1)100% (109/109)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                        stateMap.put(state.getName(), state);
225                }
226 
227                for (StateTransition stateTransition : stateTransitions) {
228 
229                        State state = stateTransition.getState();
230 
231                        if (!stateTransition.isEnd()) {
232 
233                                String next = stateTransition.getNext();
234 
235                                if (!stateMap.containsKey(next)) {
236                                        throw new IllegalArgumentException("Missing state for [" + stateTransition + "]");
237                                }
238 
239                        }
240                        else {
241                                hasEndStep = true;
242                        }
243 
244                        String name = state.getName();
245 
246                        SortedSet<StateTransition> set = transitionMap.get(name);
247                        if (set == null) {
248                                set = new TreeSet<StateTransition>();
249                                transitionMap.put(name, set);
250                        }
251                        set.add(stateTransition);
252 
253                }
254 
255                if (!hasEndStep) {
256                        throw new IllegalArgumentException(
257                                        "No end state was found.  You must specify at least one transition with no next state.");
258                }
259 
260                startState = stateTransitions.get(0).getState();
261 
262        }
263}

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