EMMA Coverage Report (generated Tue May 06 07:29:23 PDT 2008)
[all classes][org.springframework.batch.core.repository.support]

COVERAGE SUMMARY FOR SOURCE FILE [SimpleJobRepository.java]

nameclass, %method, %block, %line, %
SimpleJobRepository.java100% (1/1)88%  (7/8)93%  (228/245)96%  (66/69)

COVERAGE BREAKDOWN BY CLASS AND METHOD

nameclass, %method, %block, %line, %
     
class SimpleJobRepository100% (1/1)88%  (7/8)93%  (228/245)96%  (66/69)
SimpleJobRepository (): void 0%   (0/1)0%   (0/3)0%   (0/2)
createJobExecution (Job, JobParameters): JobExecution 100% (1/1)84%  (74/88)95%  (18/19)
SimpleJobRepository (JobInstanceDao, JobExecutionDao, StepExecutionDao): void 100% (1/1)100% (12/12)100% (5/5)
getLastStepExecution (JobInstance, Step): StepExecution 100% (1/1)100% (63/63)100% (17/17)
getStepExecutionCount (JobInstance, Step): int 100% (1/1)100% (27/27)100% (8/8)
saveOrUpdate (JobExecution): void 100% (1/1)100% (20/20)100% (7/7)
saveOrUpdate (StepExecution): void 100% (1/1)100% (24/24)100% (8/8)
saveOrUpdateExecutionContext (StepExecution): void 100% (1/1)100% (8/8)100% (3/3)

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 */
16 
17package org.springframework.batch.core.repository.support;
18 
19import java.util.ArrayList;
20import java.util.Iterator;
21import java.util.List;
22 
23import org.springframework.batch.core.BatchStatus;
24import org.springframework.batch.core.Job;
25import org.springframework.batch.core.JobExecution;
26import org.springframework.batch.core.JobInstance;
27import org.springframework.batch.core.JobParameters;
28import org.springframework.batch.core.Step;
29import org.springframework.batch.core.StepExecution;
30import org.springframework.batch.core.repository.JobExecutionAlreadyRunningException;
31import org.springframework.batch.core.repository.JobInstanceAlreadyCompleteException;
32import org.springframework.batch.core.repository.JobRepository;
33import org.springframework.batch.core.repository.JobRestartException;
34import org.springframework.batch.core.repository.dao.JobExecutionDao;
35import org.springframework.batch.core.repository.dao.JobInstanceDao;
36import org.springframework.batch.core.repository.dao.StepExecutionDao;
37import org.springframework.transaction.annotation.Isolation;
38import org.springframework.util.Assert;
39 
40/**
41 * 
42 * <p>
43 * Implementation of {@link JobRepository} that stores JobInstances,
44 * JobExecutions, and StepExecutions using the injected DAOs.
45 * <p>
46 * 
47 * @author Lucas Ward
48 * @author Dave Syer
49 * @author Robert Kasanicky
50 * 
51 * @see JobRepository
52 * @see JobInstanceDao
53 * @see JobExecutionDao
54 * @see StepExecutionDao
55 * 
56 */
57public class SimpleJobRepository implements JobRepository {
58 
59        private JobInstanceDao jobInstanceDao;
60 
61        private JobExecutionDao jobExecutionDao;
62 
63        private StepExecutionDao stepExecutionDao;
64 
65        /**
66         * Provide default constructor with low visibility in case user wants to use
67         * use aop:proxy-target-class="true" for transaction interceptor.
68         */
69        SimpleJobRepository() {
70        }
71 
72        public SimpleJobRepository(JobInstanceDao jobInstanceDao, JobExecutionDao jobExecutionDao,
73                        StepExecutionDao stepExecutionDao) {
74                super();
75                this.jobInstanceDao = jobInstanceDao;
76                this.jobExecutionDao = jobExecutionDao;
77                this.stepExecutionDao = stepExecutionDao;
78        }
79 
80        /**
81         * <p>
82         * Create a {@link JobExecution} based on the passed in {@link Job} and
83         * {@link JobParameters}. However, unique identification of a job can only
84         * come from the database, and therefore must come from JobDao by either
85         * creating a new job instance or finding an existing one, which will ensure
86         * that the id of the job instance is populated with the correct value.
87         * </p>
88         * 
89         * <p>
90         * There are two ways in which the method determines if a job should be
91         * created or an existing one should be returned. The first is
92         * restartability. The {@link Job} restartable property will be checked
93         * first. If it is false, a new job will be created, regardless of whether
94         * or not one exists. If it is true, the {@link JobInstanceDao} will be
95         * checked to determine if the job already exists, if it does, it's steps
96         * will be populated (there must be at least 1) and a new
97         * {@link JobExecution} will be returned. If no job instance is found, a new
98         * one will be created.
99         * </p>
100         * 
101         * <p>
102         * A check is made to see if any job executions are already running, and an
103         * exception will be thrown if one is detected. To detect a running job
104         * execution we use the {@link JobExecutionDao}:
105         * <ol>
106         * <li>First we find all jobs which match the given {@link JobParameters}
107         * and job name</li>
108         * <li>What happens then depends on how many existing job instances we
109         * find:
110         * <ul>
111         * <li>If there are none, or the {@link Job} is marked restartable, then we
112         * create a new {@link JobInstance}</li>
113         * <li>If there is more than one and the {@link Job} is not marked as
114         * restartable, it is an error. This could be caused by a job whose
115         * restartable flag has changed to be more strict (true not false)
116         * <em>after</em> it has been executed at least once.</li>
117         * <li>If there is precisely one existing {@link JobInstance} then we check
118         * the {@link JobExecution} instances for that job, and if any of them tells
119         * us it is running (see {@link JobExecution#isRunning()}) then it is an
120         * error.</li>
121         * </ul>
122         * </li>
123         * </ol>
124         * If this method is run in a transaction (as it normally would be) with
125         * isolation level at {@link Isolation#REPEATABLE_READ} or better, then this
126         * method should block if another transaction is already executing it (for
127         * the same {@link JobParameters} and job name). The first transaction to
128         * complete in this scenario obtains a valid {@link JobExecution}, and
129         * others throw {@link JobExecutionAlreadyRunningException} (or timeout).
130         * There are no such guarantees if the {@link JobInstanceDao} and
131         * {@link JobExecutionDao} do not respect the transaction isolation levels
132         * (e.g. if using a non-relational data-store, or if the platform does not
133         * support the higher isolation levels).
134         * </p>
135         * 
136         * @see JobRepository#createJobExecution(Job, JobParameters)
137         * 
138         */
139        public JobExecution createJobExecution(Job job, JobParameters jobParameters)
140                        throws JobExecutionAlreadyRunningException, JobRestartException, JobInstanceAlreadyCompleteException {
141 
142                Assert.notNull(job, "Job must not be null.");
143                Assert.notNull(jobParameters, "JobParameters must not be null.");
144 
145                /*
146                 * Find all jobs matching the runtime information.
147                 * 
148                 * If this method is transactional, and the isolation level is
149                 * REPEATABLE_READ or better, another launcher trying to start the same
150                 * job in another thread or process will block until this transaction
151                 * has finished.
152                 */
153 
154                JobInstance jobInstance = jobInstanceDao.getJobInstance(job, jobParameters);
155 
156                // existing job instance found
157                if (jobInstance != null) {
158                        if (!job.isRestartable()) {
159                                throw new JobRestartException("JobInstance already exists and is not restartable");
160                        }
161 
162                        List executions = jobExecutionDao.findJobExecutions(jobInstance);
163 
164                        // check for running executions and find the last started
165                        for (Iterator iterator = executions.iterator(); iterator.hasNext();) {
166                                JobExecution execution = (JobExecution) iterator.next();
167                                if (execution.isRunning()) {
168                                        throw new JobExecutionAlreadyRunningException("A job execution for this job is already running: "
169                                                        + jobInstance);
170                                }
171                                if (execution.getStatus() == BatchStatus.COMPLETED) {
172                                        throw new JobInstanceAlreadyCompleteException(
173                                                        "A job instance already exists and is complete for parameters=" + jobParameters
174                                                                        + ".  If you want to run this job again, change the parameters.");
175                                }
176                        }
177                }
178                else {
179                        // no job found, create one
180                        jobInstance = jobInstanceDao.createJobInstance(job, jobParameters);
181                }
182 
183                JobExecution jobExecution = new JobExecution(jobInstance);
184 
185                // Save the JobExecution so that it picks up an ID (useful for clients
186                // monitoring asynchronous executions):
187                saveOrUpdate(jobExecution);
188 
189                return jobExecution;
190 
191        }
192 
193        /**
194         * Save or Update a JobExecution. A JobExecution is considered one
195         * 'execution' of a particular job. Therefore, it must have it's jobId field
196         * set before it is passed into this method. It also has it's own unique
197         * identifier, because it must be updatable separately. If an id isn't found,
198         * a new JobExecution is created, if one is found, the current row is
199         * updated.
200         * 
201         * @param jobExecution to be stored.
202         * @throws IllegalArgumentException if jobExecution is null.
203         */
204        public void saveOrUpdate(JobExecution jobExecution) {
205 
206                Assert.notNull(jobExecution, "JobExecution cannot be null.");
207                Assert.notNull(jobExecution.getJobId(), "JobExecution must have a Job ID set.");
208 
209                if (jobExecution.getId() == null) {
210                        // existing instance
211                        jobExecutionDao.saveJobExecution(jobExecution);
212                }
213                else {
214                        // new execution
215                        jobExecutionDao.updateJobExecution(jobExecution);
216                }
217        }
218 
219        /**
220         * Save or Update the given StepExecution. If it's id is null, it will be
221         * saved and an id will be set, otherwise it will be updated. It should be
222         * noted that assigning an ID randomly will likely cause an exception
223         * depending on the StepDao implementation.
224         * 
225         * @param stepExecution to be saved.
226         * @throws IllegalArgumentException if stepExecution is null.
227         */
228        public void saveOrUpdate(StepExecution stepExecution) {
229 
230                Assert.notNull(stepExecution, "StepExecution cannot be null.");
231                Assert.notNull(stepExecution.getStepName(), "StepExecution's step name cannot be null.");
232                Assert.notNull(stepExecution.getJobExecutionId(), "StepExecution must belong to persisted JobExecution");
233 
234                if (stepExecution.getId() == null) {
235                        stepExecutionDao.saveStepExecution(stepExecution);
236                }
237                else {
238                        // existing execution, update
239                        stepExecutionDao.updateStepExecution(stepExecution);
240                }
241        }
242 
243        /*
244         * (non-Javadoc)
245         * @see org.springframework.batch.core.repository.JobRepository#saveOrUpdateExecutionContext(org.springframework.batch.core.domain.StepExecution)
246         */
247        public void saveOrUpdateExecutionContext(StepExecution stepExecution) {
248                saveOrUpdate(stepExecution);
249                stepExecutionDao.saveOrUpdateExecutionContext(stepExecution);
250        }
251 
252        /**
253         * @return the last execution of the step within given job instance
254         */
255        public StepExecution getLastStepExecution(JobInstance jobInstance, Step step) {
256                List jobExecutions = jobExecutionDao.findJobExecutions(jobInstance);
257                List stepExecutions = new ArrayList(jobExecutions.size());
258                for (Iterator iterator = jobExecutions.iterator(); iterator.hasNext();) {
259                        JobExecution jobExecution = (JobExecution) iterator.next();
260                        StepExecution stepExecution = stepExecutionDao.getStepExecution(jobExecution, step);
261                        if (stepExecution != null) {
262                                stepExecutions.add(stepExecution);
263                        }
264                }
265                StepExecution latest = null;
266                for (Iterator iterator = stepExecutions.iterator(); iterator.hasNext();) {
267                        StepExecution stepExecution = (StepExecution) iterator.next();
268                        if (latest == null) {
269                                latest = stepExecution;
270                        }
271                        if (latest.getStartTime().getTime() < stepExecution.getStartTime().getTime()) {
272                                latest = stepExecution;
273                        }
274                }
275                return latest;
276        }
277 
278        /**
279         * @return number of executions of the step within given job instance
280         */
281        public int getStepExecutionCount(JobInstance jobInstance, Step step) {
282                int count = 0;
283                List jobExecutions = jobExecutionDao.findJobExecutions(jobInstance);
284                for (Iterator iterator = jobExecutions.iterator(); iterator.hasNext();) {
285                        JobExecution jobExecution = (JobExecution) iterator.next();
286                        if (stepExecutionDao.getStepExecution(jobExecution, step) != null) {
287                                count++;
288                        }
289                }
290                return count;
291        }
292 
293}

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