EMMA Coverage Report (generated Thu Jan 24 13:37:04 CST 2013)
[all classes][org.springframework.batch.core.step]

COVERAGE SUMMARY FOR SOURCE FILE [AbstractStep.java]

nameclass, %method, %block, %line, %
AbstractStep.java100% (1/1)100% (22/22)97%  (366/379)99%  (102.8/104)

COVERAGE BREAKDOWN BY CLASS AND METHOD

nameclass, %method, %block, %line, %
     
class AbstractStep100% (1/1)100% (22/22)97%  (366/379)99%  (102.8/104)
getDefaultExitStatusForFailure (Throwable): ExitStatus 100% (1/1)74%  (26/35)83%  (5/6)
execute (StepExecution): void 100% (1/1)98%  (214/218)100% (59.8/60)
<static initializer> 100% (1/1)100% (4/4)100% (1/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)
close (ExecutionContext): void 100% (1/1)100% (1/1)100% (1/1)
determineBatchStatus (Throwable): BatchStatus 100% (1/1)100% (11/11)100% (3/3)
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)
isAllowStartIfComplete (): boolean 100% (1/1)100% (3/3)100% (1/1)
open (ExecutionContext): void 100% (1/1)100% (1/1)100% (1/1)
registerStepExecutionListener (StepExecutionListener): void 100% (1/1)100% (5/5)100% (2/2)
setAllowStartIfComplete (boolean): void 100% (1/1)100% (4/4)100% (2/2)
setBeanName (String): void 100% (1/1)100% (7/7)100% (3/3)
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)
toString (): String 100% (1/1)100% (16/16)100% (1/1)

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

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