EMMA Coverage Report (generated Tue May 06 07:29:23 PDT 2008)
[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)90%  (311/345)88%  (90.6/103)

COVERAGE BREAKDOWN BY CLASS AND METHOD

nameclass, %method, %block, %line, %
     
class AbstractStep100% (1/1)90%  (18/20)90%  (306/340)88%  (88.6/101)
isAllowStartIfComplete (): boolean 0%   (0/1)0%   (0/3)0%   (0/1)
setBeanName (String): void 0%   (0/1)0%   (0/7)0%   (0/3)
getDefaultExitStatusForFailure (Throwable): ExitStatus 100% (1/1)88%  (51/58)83%  (10/12)
execute (StepExecution): void 100% (1/1)88%  (120/136)86%  (37.7/44)
<static initializer> 100% (1/1)91%  (10/11)90%  (0.9/1)
AbstractStep (): void 100% (1/1)100% (11/11)100% (4/4)
AbstractStep (String): void 100% (1/1)100% (14/14)100% (5/5)
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, Exception): 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;
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 the shouldAllowStartIfComplete.
126         * 
127         * @param allowStartIfComplete the shouldAllowStartIfComplete to set
128         */
129        public void setAllowStartIfComplete(boolean allowStartIfComplete) {
130                this.allowStartIfComplete = allowStartIfComplete;
131        }
132 
133        /**
134         * Convenient constructor for setting only the name property.
135         * 
136         * @param name
137         */
138        public AbstractStep(String name) {
139                this.name = name;
140        }
141 
142        protected abstract ExitStatus doExecute(StepExecution stepExecution) throws Exception;
143 
144        protected abstract void open(ExecutionContext ctx) throws Exception;
145 
146        protected abstract void close(ExecutionContext ctx) throws Exception;
147 
148        /**
149         * Template method for step execution logic - calls abstract methods for
150         * resource initialization ({@link #open(ExecutionContext)}), execution
151         * logic ({@link #doExecute(StepExecution)}) and resource closing ({@link #close(ExecutionContext)}).
152         */
153        public final void execute(StepExecution stepExecution) throws JobInterruptedException, UnexpectedJobExecutionException {
154                stepExecution.setStartTime(new Date());
155                stepExecution.setStatus(BatchStatus.STARTED);
156 
157                ExitStatus exitStatus = ExitStatus.FAILED;
158                Exception commitException = null;
159 
160                try {
161                        getCompositeListener().beforeStep(stepExecution);
162                        try {
163                                open(stepExecution.getExecutionContext());
164                        }
165                        catch (Exception e) {
166                                throw new UnexpectedJobExecutionException("Failed to initialize the step", e);
167                        }
168                        exitStatus = doExecute(stepExecution);
169                        exitStatus = exitStatus.and(getCompositeListener().afterStep(stepExecution));
170 
171                        try {
172                                getJobRepository().saveOrUpdateExecutionContext(stepExecution);
173                                stepExecution.setStatus(BatchStatus.COMPLETED);
174                        }
175                        catch (Exception e) {
176                                commitException = e;
177                                stepExecution.setStatus(BatchStatus.UNKNOWN);
178                        }
179 
180                }
181                catch (Throwable e) {
182 
183                        logger.error("Encountered an error executing the step");
184                        stepExecution.setStatus(determineBatchStatus(e));
185                        exitStatus = getDefaultExitStatusForFailure(e);
186 
187                        try {
188                                exitStatus = exitStatus.and(getCompositeListener().onErrorInStep(stepExecution, e));
189                        }
190                        catch (Exception ex) {
191                                logger.error("Encountered an error on listener close.", ex);
192                        }
193                        rethrow(e);
194                }
195                finally {
196 
197                        stepExecution.setExitStatus(exitStatus);
198                        stepExecution.setEndTime(new Date());
199 
200                        try {
201                                getJobRepository().saveOrUpdate(stepExecution);
202                        }
203                        catch (Exception e) {
204                                commitException = e;
205                        }
206 
207                        try {
208                                close(stepExecution.getExecutionContext());
209                        }
210                        catch (Exception e) {
211                                logger.error("Exception while closing step's resources", e);
212                                throw new UnexpectedJobExecutionException("Exception while closing step's resources", e);
213                        }
214 
215                        if (commitException != null) {
216                                logger.error("Encountered an error saving batch meta data."
217                                                + "This job is now in an unknown state and should not be restarted.", commitException);
218                                throw new UnexpectedJobExecutionException("Encountered an error saving batch meta data.",
219                                                commitException);
220                        }
221                }
222        }
223 
224        private static void rethrow(Throwable e) throws JobInterruptedException {
225                if (e instanceof Error) {
226                        throw (Error) e;
227                }
228                if (e instanceof JobInterruptedException) {
229                        throw (JobInterruptedException) e;
230                }
231                else if (e.getCause() instanceof JobInterruptedException) {
232                        throw (JobInterruptedException) e.getCause();
233                }
234                else if (e instanceof RuntimeException) {
235                        throw (RuntimeException) e;
236                }
237                throw new UnexpectedJobExecutionException("Unexpected checked exception in step execution", e);
238        }
239 
240        /**
241         * Determine the step status based on the exception.
242         */
243        private static BatchStatus determineBatchStatus(Throwable e) {
244                if (e instanceof FatalException) {
245                        return BatchStatus.UNKNOWN;
246                }
247                else if (e instanceof JobInterruptedException || e.getCause() instanceof JobInterruptedException) {
248                        return BatchStatus.STOPPED;
249                }
250                else {
251                        return BatchStatus.FAILED;
252                }
253        }
254 
255        /**
256         * Register a step listener for callbacks at the appropriate stages in a
257         * step execution.
258         * 
259         * @param listener a {@link StepExecutionListener}
260         */
261        public void registerStepExecutionListener(StepExecutionListener listener) {
262                this.listener.register(listener);
263        }
264 
265        /**
266         * Register each of the objects as listeners.
267         * 
268         * @param listeners an array of listener objects of known types.
269         */
270        public void setStepExecutionListeners(StepExecutionListener[] listeners) {
271                for (int i = 0; i < listeners.length; i++) {
272                        registerStepExecutionListener(listeners[i]);
273                }
274        }
275 
276        /**
277         * @return composite listener that delegates to all registered listeners.
278         */
279        protected StepExecutionListener getCompositeListener() {
280                return listener;
281        }
282 
283        /**
284         * Public setter for {@link JobRepository}.
285         * 
286         * @param jobRepository is a mandatory dependence (no default).
287         */
288        public void setJobRepository(JobRepository jobRepository) {
289                this.jobRepository = jobRepository;
290        }
291 
292        protected JobRepository getJobRepository() {
293                return jobRepository;
294        }
295 
296        /**
297         * Default mapping from throwable to {@link ExitStatus}. Clients can modify
298         * the exit code using a {@link StepExecutionListener}.
299         * 
300         * @param ex the cause of the failure
301         * @return an {@link ExitStatus}
302         */
303        private ExitStatus getDefaultExitStatusForFailure(Throwable ex) {
304                ExitStatus exitStatus;
305                if (ex instanceof JobInterruptedException || ex.getCause() instanceof JobInterruptedException) {
306                        exitStatus = new ExitStatus(false, JOB_INTERRUPTED, JobInterruptedException.class.getName());
307                }
308                else if (ex instanceof NoSuchJobException || ex.getCause() instanceof NoSuchJobException) {
309                        exitStatus = new ExitStatus(false, ExitCodeMapper.NO_SUCH_JOB);
310                }
311                else {
312                        String message = "";
313                        StringWriter writer = new StringWriter();
314                        ex.printStackTrace(new PrintWriter(writer));
315                        message = writer.toString();
316                        exitStatus = ExitStatus.FAILED.addExitDescription(message);
317                }
318 
319                return exitStatus;
320        }
321 
322        /**
323         * Signals a fatal exception - e.g. unable to persist batch metadata or
324         * rollback transaction. Throwing this exception will result in storing
325         * {@link BatchStatus#UNKNOWN} as step's status.
326         */
327        protected static class FatalException extends RuntimeException {
328                public FatalException(String string, Exception e) {
329                        super(string, e);
330                }
331        }
332 
333}

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