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

COVERAGE SUMMARY FOR SOURCE FILE [AbstractJob.java]

nameclass, %method, %block, %line, %
AbstractJob.java100% (1/1)95%  (21/22)92%  (317/344)91%  (76.7/84)

COVERAGE BREAKDOWN BY CLASS AND METHOD

nameclass, %method, %block, %line, %
     
class AbstractJob100% (1/1)95%  (21/22)92%  (317/344)91%  (76.7/84)
registerJobExecutionListener (JobExecutionListener): void 0%   (0/1)0%   (0/5)0%   (0/2)
getDefaultExitStatusForFailure (Throwable): ExitStatus 100% (1/1)74%  (26/35)83%  (5/6)
execute (JobExecution): void 100% (1/1)92%  (154/167)89%  (33.7/38)
<static initializer> 100% (1/1)100% (4/4)100% (1/1)
AbstractJob (): void 100% (1/1)100% (16/16)100% (5/5)
AbstractJob (String): void 100% (1/1)100% (19/19)100% (6/6)
afterPropertiesSet (): void 100% (1/1)100% (5/5)100% (2/2)
getJobParametersIncrementer (): JobParametersIncrementer 100% (1/1)100% (3/3)100% (1/1)
getJobParametersValidator (): JobParametersValidator 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)
handleStep (Step, JobExecution): StepExecution 100% (1/1)100% (6/6)100% (1/1)
isRestartable (): boolean 100% (1/1)100% (3/3)100% (1/1)
setBeanName (String): void 100% (1/1)100% (7/7)100% (3/3)
setJobExecutionListeners (JobExecutionListener []): void 100% (1/1)100% (15/15)100% (3/3)
setJobParametersIncrementer (JobParametersIncrementer): void 100% (1/1)100% (4/4)100% (2/2)
setJobParametersValidator (JobParametersValidator): void 100% (1/1)100% (4/4)100% (2/2)
setJobRepository (JobRepository): void 100% (1/1)100% (10/10)100% (3/3)
setName (String): void 100% (1/1)100% (4/4)100% (2/2)
setRestartable (boolean): void 100% (1/1)100% (4/4)100% (2/2)
toString (): String 100% (1/1)100% (16/16)100% (1/1)
updateStatus (JobExecution, BatchStatus): void 100% (1/1)100% (8/8)100% (3/3)

1/*
2 * Copyright 2006-2009 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 */
16 
17package org.springframework.batch.core.job;
18 
19import java.util.Collection;
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.ExitStatus;
26import org.springframework.batch.core.Job;
27import org.springframework.batch.core.JobExecution;
28import org.springframework.batch.core.JobExecutionException;
29import org.springframework.batch.core.JobExecutionListener;
30import org.springframework.batch.core.JobInterruptedException;
31import org.springframework.batch.core.JobParametersIncrementer;
32import org.springframework.batch.core.JobParametersValidator;
33import org.springframework.batch.core.StartLimitExceededException;
34import org.springframework.batch.core.Step;
35import org.springframework.batch.core.StepExecution;
36import org.springframework.batch.core.launch.NoSuchJobException;
37import org.springframework.batch.core.launch.support.ExitCodeMapper;
38import org.springframework.batch.core.listener.CompositeJobExecutionListener;
39import org.springframework.batch.core.repository.JobRepository;
40import org.springframework.batch.core.repository.JobRestartException;
41import org.springframework.batch.core.step.StepLocator;
42import org.springframework.batch.repeat.RepeatException;
43import org.springframework.beans.factory.BeanNameAware;
44import org.springframework.beans.factory.InitializingBean;
45import org.springframework.util.Assert;
46import org.springframework.util.ClassUtils;
47 
48/**
49 * Abstract implementation of the {@link Job} interface. Common dependencies
50 * such as a {@link JobRepository}, {@link JobExecutionListener}s, and various
51 * configuration parameters are set here. Therefore, common error handling and
52 * listener calling activities are abstracted away from implementations.
53 * 
54 * @author Lucas Ward
55 * @author Dave Syer
56 */
57public abstract class AbstractJob implements Job, StepLocator, BeanNameAware,
58                InitializingBean {
59 
60        protected static final Log logger = LogFactory.getLog(AbstractJob.class);
61 
62        private String name;
63 
64        private boolean restartable = true;
65 
66        private JobRepository jobRepository;
67 
68        private CompositeJobExecutionListener listener = new CompositeJobExecutionListener();
69 
70        private JobParametersIncrementer jobParametersIncrementer;
71 
72        private JobParametersValidator jobParametersValidator = new DefaultJobParametersValidator();
73 
74        private StepHandler stepHandler;
75 
76        /**
77         * Default constructor.
78         */
79        public AbstractJob() {
80                super();
81        }
82 
83        /**
84         * Convenience constructor to immediately add name (which is mandatory but
85         * not final).
86         * 
87         * @param name
88         */
89        public AbstractJob(String name) {
90                super();
91                this.name = name;
92        }
93 
94        /**
95         * A validator for job parameters. Defaults to a vanilla
96         * {@link DefaultJobParametersValidator}.
97         * 
98         * @param jobParametersValidator
99         *            a validator instance
100         */
101        public void setJobParametersValidator(
102                        JobParametersValidator jobParametersValidator) {
103                this.jobParametersValidator = jobParametersValidator;
104        }
105 
106        /**
107         * Assert mandatory properties: {@link JobRepository}.
108         * 
109         * @see InitializingBean#afterPropertiesSet()
110         */
111        public void afterPropertiesSet() throws Exception {
112                Assert.notNull(jobRepository, "JobRepository must be set");
113        }
114 
115        /**
116         * Set the name property if it is not already set. Because of the order of
117         * the callbacks in a Spring container the name property will be set first
118         * if it is present. Care is needed with bean definition inheritance - if a
119         * parent bean has a name, then its children need an explicit name as well,
120         * otherwise they will not be unique.
121         * 
122         * @see org.springframework.beans.factory.BeanNameAware#setBeanName(java.lang.String)
123         */
124        public void setBeanName(String name) {
125                if (this.name == null) {
126                        this.name = name;
127                }
128        }
129 
130        /**
131         * Set the name property. Always overrides the default value if this object
132         * is a Spring bean.
133         * 
134         * @see #setBeanName(java.lang.String)
135         */
136        public void setName(String name) {
137                this.name = name;
138        }
139 
140        /*
141         * (non-Javadoc)
142         * 
143         * @see org.springframework.batch.core.domain.IJob#getName()
144         */
145        public String getName() {
146                return name;
147        }
148 
149        /**
150         * Retrieve the step with the given name. If there is no Step with the given
151         * name, then return null.
152         * 
153         * @param stepName
154         * @return the Step
155         */
156        public abstract Step getStep(String stepName);
157 
158        /**
159         * Retrieve the step names.
160         * 
161         * @return the step names
162         */
163        public abstract Collection<String> getStepNames();
164 
165        public JobParametersValidator getJobParametersValidator() {
166                return jobParametersValidator;
167        }
168 
169        /**
170         * Boolean flag to prevent categorically a job from restarting, even if it
171         * has failed previously.
172         * 
173         * @param restartable
174         *            the value of the flag to set (default true)
175         */
176        public void setRestartable(boolean restartable) {
177                this.restartable = restartable;
178        }
179 
180        /**
181         * @see Job#isRestartable()
182         */
183        public boolean isRestartable() {
184                return restartable;
185        }
186 
187        /**
188         * Public setter for the {@link JobParametersIncrementer}.
189         * 
190         * @param jobParametersIncrementer
191         *            the {@link JobParametersIncrementer} to set
192         */
193        public void setJobParametersIncrementer(
194                        JobParametersIncrementer jobParametersIncrementer) {
195                this.jobParametersIncrementer = jobParametersIncrementer;
196        }
197 
198        /*
199         * (non-Javadoc)
200         * 
201         * @see org.springframework.batch.core.Job#getJobParametersIncrementer()
202         */
203        public JobParametersIncrementer getJobParametersIncrementer() {
204                return this.jobParametersIncrementer;
205        }
206 
207        /**
208         * Public setter for injecting {@link JobExecutionListener}s. They will all
209         * be given the listener callbacks at the appropriate point in the job.
210         * 
211         * @param listeners
212         *            the listeners to set.
213         */
214        public void setJobExecutionListeners(JobExecutionListener[] listeners) {
215                for (int i = 0; i < listeners.length; i++) {
216                        this.listener.register(listeners[i]);
217                }
218        }
219 
220        /**
221         * Register a single listener for the {@link JobExecutionListener}
222         * callbacks.
223         * 
224         * @param listener
225         *            a {@link JobExecutionListener}
226         */
227        public void registerJobExecutionListener(JobExecutionListener listener) {
228                this.listener.register(listener);
229        }
230 
231        /**
232         * Public setter for the {@link JobRepository} that is needed to manage the
233         * state of the batch meta domain (jobs, steps, executions) during the life
234         * of a job.
235         * 
236         * @param jobRepository
237         */
238        public void setJobRepository(JobRepository jobRepository) {
239                this.jobRepository = jobRepository;
240                stepHandler = new SimpleStepHandler(jobRepository);
241        }
242 
243        /**
244         * Convenience method for subclasses to access the job repository.
245         * 
246         * @return the jobRepository
247         */
248        protected JobRepository getJobRepository() {
249                return jobRepository;
250        }
251 
252        /**
253         * Extension point for subclasses allowing them to concentrate on processing
254         * logic and ignore listeners and repository calls. Implementations usually
255         * are concerned with the ordering of steps, and delegate actual step
256         * processing to {@link #handleStep(Step, JobExecution)}.
257         * 
258         * @param execution
259         *            the current {@link JobExecution}
260         * 
261         * @throws JobExecutionException
262         *             to signal a fatal batch framework error (not a business or
263         *             validation exception)
264         */
265        abstract protected void doExecute(JobExecution execution)
266                        throws JobExecutionException;
267 
268        /**
269         * Run the specified job, handling all listener and repository calls, and
270         * delegating the actual processing to {@link #doExecute(JobExecution)}.
271         * 
272         * @see Job#execute(JobExecution)
273         * @throws StartLimitExceededException
274         *             if start limit of one of the steps was exceeded
275         */
276        public final void execute(JobExecution execution) {
277 
278                logger.debug("Job execution starting: " + execution);
279 
280                try {
281 
282                        jobParametersValidator.validate(execution.getJobInstance()
283                                        .getJobParameters());
284 
285                        if (execution.getStatus() != BatchStatus.STOPPING) {
286 
287                                execution.setStartTime(new Date());
288                                updateStatus(execution, BatchStatus.STARTED);
289 
290                                listener.beforeJob(execution);
291 
292                                try {
293                                        doExecute(execution);
294                                        logger.debug("Job execution complete: " + execution);
295                                } catch (RepeatException e) {
296                                        throw e.getCause();
297                                }
298                        } else {
299 
300                                // The job was already stopped before we even got this far. Deal
301                                // with it in the same way as any other interruption.
302                                execution.setStatus(BatchStatus.STOPPED);
303                                execution.setExitStatus(ExitStatus.COMPLETED);
304                                logger.debug("Job execution was stopped: " + execution);
305 
306                        }
307 
308                } catch (JobInterruptedException e) {
309                        logger.info("Encountered interruption executing job: "
310                                        + e.getMessage());
311                        if (logger.isDebugEnabled()) {
312                                logger.debug("Full exception", e);
313                        }
314                        execution.setExitStatus(getDefaultExitStatusForFailure(e));
315                        execution.setStatus(BatchStatus.max(BatchStatus.STOPPED, e.getStatus()));
316                        execution.addFailureException(e);
317                } catch (Throwable t) {
318                        logger.error("Encountered fatal error executing job", t);
319                        execution.setExitStatus(getDefaultExitStatusForFailure(t));
320                        execution.setStatus(BatchStatus.FAILED);
321                        execution.addFailureException(t);
322                } finally {
323 
324                        if (execution.getStatus().isLessThanOrEqualTo(BatchStatus.STOPPED)
325                                        && execution.getStepExecutions().isEmpty()) {
326                                ExitStatus exitStatus = execution.getExitStatus();
327                                execution
328                                                .setExitStatus(exitStatus.and(ExitStatus.NOOP
329                                                                .addExitDescription("All steps already completed or no steps configured for this job.")));
330                        }
331 
332                        execution.setEndTime(new Date());
333 
334                        try {
335                                listener.afterJob(execution);
336                        } catch (Exception e) {
337                                logger.error("Exception encountered in afterStep callback", e);
338                        }
339 
340                        jobRepository.update(execution);
341 
342                }
343 
344        }
345 
346        /**
347         * Convenience method for subclasses to delegate the handling of a specific
348         * step in the context of the current {@link JobExecution}. Clients of this
349         * method do not need access to the {@link JobRepository}, nor do they need
350         * to worry about populating the execution context on a restart, nor
351         * detecting the interrupted state (in job or step execution).
352         * 
353         * @param step
354         *            the {@link Step} to execute
355         * @param execution
356         *            the current {@link JobExecution}
357         * @return the {@link StepExecution} corresponding to this step
358         * 
359         * @throws JobInterruptedException
360         *             if the {@link JobExecution} has been interrupted, and in
361         *             particular if {@link BatchStatus#ABANDONED} or
362         *             {@link BatchStatus#STOPPING} is detected
363         * @throws StartLimitExceededException
364         *             if the start limit has been exceeded for this step
365         * @throws JobRestartException
366         *             if the job is in an inconsistent state from an earlier
367         *             failure
368         */
369        protected final StepExecution handleStep(Step step, JobExecution execution)
370                        throws JobInterruptedException, JobRestartException,
371                        StartLimitExceededException {
372                return stepHandler.handleStep(step, execution);
373 
374        }
375 
376        /**
377         * Default mapping from throwable to {@link ExitStatus}.
378         * 
379         * @param ex
380         *            the cause of the failure
381         * @return an {@link ExitStatus}
382         */
383        private ExitStatus getDefaultExitStatusForFailure(Throwable ex) {
384                ExitStatus exitStatus;
385                if (ex instanceof JobInterruptedException
386                                || ex.getCause() instanceof JobInterruptedException) {
387                        exitStatus = ExitStatus.STOPPED
388                                        .addExitDescription(JobInterruptedException.class.getName());
389                } else if (ex instanceof NoSuchJobException
390                                || ex.getCause() instanceof NoSuchJobException) {
391                        exitStatus = new ExitStatus(ExitCodeMapper.NO_SUCH_JOB, ex
392                                        .getClass().getName());
393                } else {
394                        exitStatus = ExitStatus.FAILED.addExitDescription(ex);
395                }
396 
397                return exitStatus;
398        }
399 
400        private void updateStatus(JobExecution jobExecution, BatchStatus status) {
401                jobExecution.setStatus(status);
402                jobRepository.update(jobExecution);
403        }
404 
405        public String toString() {
406                return ClassUtils.getShortName(getClass()) + ": [name=" + name + "]";
407        }
408 
409}

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