EMMA Coverage Report (generated Fri Jan 30 13:20:29 EST 2009)
[all classes][org.springframework.batch.core.step]

COVERAGE SUMMARY FOR SOURCE FILE [AbstractStep.java]

nameclass, %method, %block, %line, %
AbstractStep.java100% (2/2)90%  (19/21)88%  (352/399)86%  (95.4/111)

COVERAGE BREAKDOWN BY CLASS AND METHOD

nameclass, %method, %block, %line, %
     
class AbstractStep100% (1/1)90%  (18/20)88%  (347/394)86%  (93.4/109)
isAllowStartIfComplete (): boolean 0%   (0/1)0%   (0/3)0%   (0/1)
setBeanName (String): void 0%   (0/1)0%   (0/7)0%   (0/3)
execute (StepExecution): void 100% (1/1)84%  (155/184)80%  (42.5/53)
getDefaultExitStatusForFailure (Throwable): ExitStatus 100% (1/1)88%  (51/58)90%  (9/10)
<static initializer> 100% (1/1)91%  (10/11)90%  (0.9/1)
AbstractStep (): void 100% (1/1)100% (14/14)100% (5/5)
AbstractStep (String): void 100% (1/1)100% (17/17)100% (6/6)
afterPropertiesSet (): void 100% (1/1)100% (5/5)100% (2/2)
determineBatchStatus (Throwable): BatchStatus 100% (1/1)100% (16/16)100% (5/5)
getCompositeListener (): StepExecutionListener 100% (1/1)100% (3/3)100% (1/1)
getJobRepository (): JobRepository 100% (1/1)100% (3/3)100% (1/1)
getName (): String 100% (1/1)100% (3/3)100% (1/1)
getStartLimit (): int 100% (1/1)100% (3/3)100% (1/1)
registerStepExecutionListener (StepExecutionListener): void 100% (1/1)100% (5/5)100% (2/2)
rethrow (Throwable): void 100% (1/1)100% (32/32)100% (9/9)
setAllowStartIfComplete (boolean): void 100% (1/1)100% (4/4)100% (2/2)
setJobRepository (JobRepository): void 100% (1/1)100% (4/4)100% (2/2)
setName (String): void 100% (1/1)100% (4/4)100% (2/2)
setStartLimit (int): void 100% (1/1)100% (4/4)100% (2/2)
setStepExecutionListeners (StepExecutionListener []): void 100% (1/1)100% (14/14)100% (3/3)
     
class AbstractStep$FatalException100% (1/1)100% (1/1)100% (5/5)100% (2/2)
AbstractStep$FatalException (String, Throwable): void 100% (1/1)100% (5/5)100% (2/2)

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.step;
17 
18import java.io.PrintWriter;
19import java.io.StringWriter;
20import java.util.Date;
21 
22import org.apache.commons.logging.Log;
23import org.apache.commons.logging.LogFactory;
24import org.springframework.batch.core.BatchStatus;
25import org.springframework.batch.core.JobInterruptedException;
26import org.springframework.batch.core.Step;
27import org.springframework.batch.core.StepExecution;
28import org.springframework.batch.core.StepExecutionListener;
29import org.springframework.batch.core.UnexpectedJobExecutionException;
30import org.springframework.batch.core.launch.support.ExitCodeMapper;
31import org.springframework.batch.core.listener.CompositeStepExecutionListener;
32import org.springframework.batch.core.repository.JobRepository;
33import org.springframework.batch.core.repository.NoSuchJobException;
34import org.springframework.batch.item.ExecutionContext;
35import org.springframework.batch.repeat.ExitStatus;
36import org.springframework.beans.factory.BeanNameAware;
37import org.springframework.beans.factory.InitializingBean;
38import org.springframework.util.Assert;
39 
40/**
41 * A {@link Step} implementation that provides common behavior to subclasses,
42 * including registering and calling listeners.
43 * 
44 * @author Dave Syer
45 * @author Ben Hale
46 * @author Robert Kasanicky
47 */
48public abstract class AbstractStep implements Step, InitializingBean, BeanNameAware {
49 
50        /**
51         * Exit code for interrupted status.
52         */
53        public static final String JOB_INTERRUPTED = "JOB_INTERRUPTED";
54 
55        private static final Log logger = LogFactory.getLog(AbstractStep.class);
56 
57        private String name;
58 
59        private int startLimit = Integer.MAX_VALUE;
60 
61        private boolean allowStartIfComplete = false;
62 
63        private CompositeStepExecutionListener listener = new CompositeStepExecutionListener();
64 
65        private JobRepository jobRepository;
66 
67        /**
68         * Default constructor.
69         */
70        public AbstractStep() {
71                super();
72        }
73 
74        public void afterPropertiesSet() throws Exception {
75                Assert.notNull(jobRepository, "JobRepository is mandatory");
76        }
77 
78        public String getName() {
79                return this.name;
80        }
81 
82        /**
83         * Set the name property. Always overrides the default value if this object
84         * is a Spring bean.
85         * 
86         * @see #setBeanName(java.lang.String)
87         */
88        public void setName(String name) {
89                this.name = name;
90        }
91 
92        /**
93         * Set the name property if it is not already set. Because of the order of
94         * the callbacks in a Spring container the name property will be set first
95         * if it is present. Care is needed with bean definition inheritance - if a
96         * parent bean has a name, then its children need an explicit name as well,
97         * otherwise they will not be unique.
98         * 
99         * @see org.springframework.beans.factory.BeanNameAware#setBeanName(java.lang.String)
100         */
101        public void setBeanName(String name) {
102                if (this.name == null) {
103                        this.name = name;
104                }
105        }
106 
107        public int getStartLimit() {
108                return this.startLimit;
109        }
110 
111        /**
112         * Public setter for the startLimit.
113         * 
114         * @param startLimit the startLimit to set
115         */
116        public void setStartLimit(int startLimit) {
117                this.startLimit = startLimit;
118        }
119 
120        public boolean isAllowStartIfComplete() {
121                return this.allowStartIfComplete;
122        }
123 
124        /**
125         * Public setter for flag that determines whether the step should start
126         * again if it is already complete. Defaults to false.
127         * 
128         * @param allowStartIfComplete the value of the flag to set
129         */
130        public void setAllowStartIfComplete(boolean allowStartIfComplete) {
131                this.allowStartIfComplete = allowStartIfComplete;
132        }
133 
134        /**
135         * Convenient constructor for setting only the name property.
136         * 
137         * @param name
138         */
139        public AbstractStep(String name) {
140                this.name = name;
141        }
142 
143        protected abstract ExitStatus doExecute(StepExecution stepExecution) throws Exception;
144 
145        protected abstract void open(ExecutionContext ctx) throws Exception;
146 
147        protected abstract void close(ExecutionContext ctx) throws Exception;
148 
149        /**
150         * Template method for step execution logic - calls abstract methods for
151         * resource initialization ({@link #open(ExecutionContext)}), execution
152         * logic ({@link #doExecute(StepExecution)}) and resource closing ({@link #close(ExecutionContext)}).
153         */
154        public final void execute(StepExecution stepExecution) throws JobInterruptedException,
155                        UnexpectedJobExecutionException {
156                stepExecution.setStartTime(new Date());
157                stepExecution.setStatus(BatchStatus.STARTED);
158                
159                getJobRepository().saveOrUpdate(stepExecution);
160 
161                ExitStatus exitStatus = ExitStatus.FAILED;
162                Exception commitException = null;
163 
164                try {
165                        getCompositeListener().beforeStep(stepExecution);
166                        try {
167                                open(stepExecution.getExecutionContext());
168                        }
169                        catch (Exception e) {
170                                throw new UnexpectedJobExecutionException("Failed to initialize the step", e);
171                        }
172                        exitStatus = doExecute(stepExecution);
173 
174                        // Check if someone is trying to stop us
175                        if (stepExecution.isTerminateOnly()) {
176                                stepExecution.setStatus(BatchStatus.STOPPED);
177                                throw new JobInterruptedException("JobExecution interrupted.");
178                        }
179 
180                        stepExecution.setStatus(BatchStatus.COMPLETED);
181                        exitStatus = exitStatus.and(getCompositeListener().afterStep(stepExecution));
182 
183                        try {
184                                getJobRepository().saveOrUpdate(stepExecution);
185                                getJobRepository().saveOrUpdateExecutionContext(stepExecution);
186                        }
187                        catch (Exception e) {
188                                commitException = e;
189                                exitStatus = exitStatus.and(ExitStatus.UNKNOWN);
190                        }
191 
192                }
193                catch (Throwable e) {
194 
195                        logger.error("Encountered an error executing the step: " + e.getClass() + ": " + e.getMessage(), e);
196                        stepExecution.setStatus(determineBatchStatus(e));
197                        exitStatus = getDefaultExitStatusForFailure(e);
198 
199                        try {
200                                exitStatus = exitStatus.and(getCompositeListener().onErrorInStep(stepExecution, e));
201                                getJobRepository().saveOrUpdateExecutionContext(stepExecution);
202                        }
203                        catch (Exception ex) {
204                                logger.error("Encountered an error on listener error callback.", ex);
205                        }
206                        rethrow(e);
207                }
208                finally {
209 
210                        stepExecution.setExitStatus(exitStatus);
211                        stepExecution.setEndTime(new Date());
212 
213                        try {
214                                getJobRepository().saveOrUpdate(stepExecution);
215                        }
216                        catch (Exception e) {
217                                if (commitException == null) {
218                                        commitException = e;
219                                }
220                                else {
221                                        logger.error("Exception while updating step execution after commit exception", e);
222                                }
223                        }
224 
225                        try {
226                                close(stepExecution.getExecutionContext());
227                        }
228                        catch (Exception e) {
229                                logger.error("Exception while closing step execution resources", e);
230                                throw new UnexpectedJobExecutionException("Exception while closing step resources", e);
231                        }
232 
233                        if (commitException != null) {
234                                stepExecution.setStatus(BatchStatus.UNKNOWN);
235                                logger.error("Encountered an error saving batch meta data."
236                                                + "This job is now in an unknown state and should not be restarted.", commitException);
237                                throw new UnexpectedJobExecutionException("Encountered an error saving batch meta data.",
238                                                commitException);
239                        }
240                }
241        }
242 
243        private static void rethrow(Throwable e) throws JobInterruptedException {
244                if (e instanceof Error) {
245                        throw (Error) e;
246                }
247                if (e instanceof JobInterruptedException) {
248                        throw (JobInterruptedException) e;
249                }
250                else if (e.getCause() instanceof JobInterruptedException) {
251                        throw (JobInterruptedException) e.getCause();
252                }
253                else if (e instanceof RuntimeException) {
254                        throw (RuntimeException) e;
255                }
256                throw new UnexpectedJobExecutionException("Unexpected checked exception in step execution", e);
257        }
258 
259        /**
260         * Determine the step status based on the exception.
261         */
262        private static BatchStatus determineBatchStatus(Throwable e) {
263                if (e instanceof FatalException) {
264                        return BatchStatus.UNKNOWN;
265                }
266                else if (e instanceof JobInterruptedException || e.getCause() instanceof JobInterruptedException) {
267                        return BatchStatus.STOPPED;
268                }
269                else {
270                        return BatchStatus.FAILED;
271                }
272        }
273 
274        /**
275         * Register a step listener for callbacks at the appropriate stages in a
276         * step execution.
277         * 
278         * @param listener a {@link StepExecutionListener}
279         */
280        public void registerStepExecutionListener(StepExecutionListener listener) {
281                this.listener.register(listener);
282        }
283 
284        /**
285         * Register each of the objects as listeners.
286         * 
287         * @param listeners an array of listener objects of known types.
288         */
289        public void setStepExecutionListeners(StepExecutionListener[] listeners) {
290                for (int i = 0; i < listeners.length; i++) {
291                        registerStepExecutionListener(listeners[i]);
292                }
293        }
294 
295        /**
296         * @return composite listener that delegates to all registered listeners.
297         */
298        protected StepExecutionListener getCompositeListener() {
299                return listener;
300        }
301 
302        /**
303         * Public setter for {@link JobRepository}.
304         * 
305         * @param jobRepository is a mandatory dependence (no default).
306         */
307        public void setJobRepository(JobRepository jobRepository) {
308                this.jobRepository = jobRepository;
309        }
310 
311        protected JobRepository getJobRepository() {
312                return jobRepository;
313        }
314 
315        /**
316         * Default mapping from throwable to {@link ExitStatus}. Clients can modify
317         * the exit code using a {@link StepExecutionListener}.
318         * 
319         * @param ex the cause of the failure
320         * @return an {@link ExitStatus}
321         */
322        private ExitStatus getDefaultExitStatusForFailure(Throwable ex) {
323                ExitStatus exitStatus;
324                if (ex instanceof JobInterruptedException || ex.getCause() instanceof JobInterruptedException) {
325                        exitStatus = new ExitStatus(false, JOB_INTERRUPTED, JobInterruptedException.class.getName());
326                }
327                else if (ex instanceof NoSuchJobException || ex.getCause() instanceof NoSuchJobException) {
328                        exitStatus = new ExitStatus(false, ExitCodeMapper.NO_SUCH_JOB);
329                }
330                else {
331                        String message = "";
332                        StringWriter writer = new StringWriter();
333                        ex.printStackTrace(new PrintWriter(writer));
334                        message = writer.toString();
335                        exitStatus = ExitStatus.FAILED.addExitDescription(message);
336                }
337 
338                return exitStatus;
339        }
340 
341        /**
342         * Signals a fatal exception - e.g. unable to persist batch metadata or
343         * rollback transaction. Throwing this exception will result in storing
344         * {@link BatchStatus#UNKNOWN} as step's status.
345         */
346        protected static class FatalException extends RuntimeException {
347                public FatalException(String string, Throwable e) {
348                        super(string, e);
349                }
350        }
351 
352}

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