1 | /* |
2 | * Copyright 2006-2013 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 | |
17 | package org.springframework.batch.core; |
18 | |
19 | import java.io.IOException; |
20 | import java.io.ObjectInputStream; |
21 | import java.util.ArrayList; |
22 | import java.util.Collection; |
23 | import java.util.Collections; |
24 | import java.util.Date; |
25 | import java.util.HashSet; |
26 | import java.util.List; |
27 | import java.util.Set; |
28 | import java.util.concurrent.CopyOnWriteArrayList; |
29 | import java.util.concurrent.CopyOnWriteArraySet; |
30 | |
31 | import org.springframework.batch.item.ExecutionContext; |
32 | |
33 | /** |
34 | * Batch domain object representing the execution of a job. |
35 | * |
36 | * @author Lucas Ward |
37 | * @author Michael Minella |
38 | * |
39 | */ |
40 | @SuppressWarnings("serial") |
41 | public class JobExecution extends Entity { |
42 | |
43 | private final JobParameters jobParameters; |
44 | |
45 | private JobInstance jobInstance; |
46 | |
47 | private volatile Collection<StepExecution> stepExecutions = new CopyOnWriteArraySet<StepExecution>(); |
48 | |
49 | private volatile BatchStatus status = BatchStatus.STARTING; |
50 | |
51 | private volatile Date startTime = null; |
52 | |
53 | private volatile Date createTime = new Date(System.currentTimeMillis()); |
54 | |
55 | private volatile Date endTime = null; |
56 | |
57 | private volatile Date lastUpdated = null; |
58 | |
59 | private volatile ExitStatus exitStatus = ExitStatus.UNKNOWN; |
60 | |
61 | private volatile ExecutionContext executionContext = new ExecutionContext(); |
62 | |
63 | private transient volatile List<Throwable> failureExceptions = new CopyOnWriteArrayList<Throwable>(); |
64 | |
65 | /** |
66 | * Because a JobExecution isn't valid unless the job is set, this |
67 | * constructor is the only valid one from a modeling point of view. |
68 | * |
69 | * @param job the job of which this execution is a part |
70 | */ |
71 | public JobExecution(JobInstance job, Long id, JobParameters jobParameters) { |
72 | super(id); |
73 | this.jobInstance = job; |
74 | this.jobParameters = jobParameters == null ? new JobParameters() : jobParameters; |
75 | } |
76 | |
77 | /** |
78 | * Constructor for transient (unsaved) instances. |
79 | * |
80 | * @param job the enclosing {@link JobInstance} |
81 | */ |
82 | public JobExecution(JobInstance job, JobParameters jobParameters) { |
83 | this(job, null, jobParameters); |
84 | } |
85 | |
86 | public JobExecution(Long id, JobParameters jobParameters) { |
87 | this(null, id, jobParameters); |
88 | } |
89 | |
90 | public JobExecution(Long id) { |
91 | this(null, id, null); |
92 | } |
93 | |
94 | public JobParameters getJobParameters() { |
95 | return this.jobParameters; |
96 | } |
97 | |
98 | public Date getEndTime() { |
99 | return endTime; |
100 | } |
101 | |
102 | public void setJobInstance(JobInstance jobInstance) { |
103 | this.jobInstance = jobInstance; |
104 | } |
105 | |
106 | public void setEndTime(Date endTime) { |
107 | this.endTime = endTime; |
108 | } |
109 | |
110 | public Date getStartTime() { |
111 | return startTime; |
112 | } |
113 | |
114 | public void setStartTime(Date startTime) { |
115 | this.startTime = startTime; |
116 | } |
117 | |
118 | public BatchStatus getStatus() { |
119 | return status; |
120 | } |
121 | |
122 | /** |
123 | * Set the value of the status field. |
124 | * |
125 | * @param status the status to set |
126 | */ |
127 | public void setStatus(BatchStatus status) { |
128 | this.status = status; |
129 | } |
130 | |
131 | /** |
132 | * Upgrade the status field if the provided value is greater than the |
133 | * existing one. Clients using this method to set the status can be sure |
134 | * that they don't overwrite a failed status with an successful one. |
135 | * |
136 | * @param status the new status value |
137 | */ |
138 | public void upgradeStatus(BatchStatus status) { |
139 | this.status = this.status.upgradeTo(status); |
140 | } |
141 | |
142 | /** |
143 | * Convenience getter for for the id of the enclosing job. Useful for DAO |
144 | * implementations. |
145 | * |
146 | * @return the id of the enclosing job |
147 | */ |
148 | public Long getJobId() { |
149 | if (jobInstance != null) { |
150 | return jobInstance.getId(); |
151 | } |
152 | return null; |
153 | } |
154 | |
155 | /** |
156 | * @param exitStatus |
157 | */ |
158 | public void setExitStatus(ExitStatus exitStatus) { |
159 | this.exitStatus = exitStatus; |
160 | } |
161 | |
162 | /** |
163 | * @return the exitCode |
164 | */ |
165 | public ExitStatus getExitStatus() { |
166 | return exitStatus; |
167 | } |
168 | |
169 | /** |
170 | * @return the Job that is executing. |
171 | */ |
172 | public JobInstance getJobInstance() { |
173 | return jobInstance; |
174 | } |
175 | |
176 | /** |
177 | * Accessor for the step executions. |
178 | * |
179 | * @return the step executions that were registered |
180 | */ |
181 | public Collection<StepExecution> getStepExecutions() { |
182 | return Collections.unmodifiableList(new ArrayList<StepExecution>(stepExecutions)); |
183 | } |
184 | |
185 | /** |
186 | * Register a step execution with the current job execution. |
187 | * @param stepName the name of the step the new execution is associated with |
188 | */ |
189 | public StepExecution createStepExecution(String stepName) { |
190 | StepExecution stepExecution = new StepExecution(stepName, this); |
191 | this.stepExecutions.add(stepExecution); |
192 | return stepExecution; |
193 | } |
194 | |
195 | /** |
196 | * Test if this {@link JobExecution} indicates that it is running. It should |
197 | * be noted that this does not necessarily mean that it has been persisted |
198 | * as such yet. |
199 | * @return true if the end time is null |
200 | */ |
201 | public boolean isRunning() { |
202 | return endTime == null; |
203 | } |
204 | |
205 | /** |
206 | * Test if this {@link JobExecution} indicates that it has been signalled to |
207 | * stop. |
208 | * @return true if the status is {@link BatchStatus#STOPPING} |
209 | */ |
210 | public boolean isStopping() { |
211 | return status == BatchStatus.STOPPING; |
212 | } |
213 | |
214 | /** |
215 | * Signal the {@link JobExecution} to stop. Iterates through the associated |
216 | * {@link StepExecution}s, calling {@link StepExecution#setTerminateOnly()}. |
217 | * |
218 | */ |
219 | public void stop() { |
220 | for (StepExecution stepExecution : stepExecutions) { |
221 | stepExecution.setTerminateOnly(); |
222 | } |
223 | status = BatchStatus.STOPPING; |
224 | } |
225 | |
226 | /** |
227 | * Sets the {@link ExecutionContext} for this execution |
228 | * |
229 | * @param executionContext the context |
230 | */ |
231 | public void setExecutionContext(ExecutionContext executionContext) { |
232 | this.executionContext = executionContext; |
233 | } |
234 | |
235 | /** |
236 | * Returns the {@link ExecutionContext} for this execution. The content is |
237 | * expected to be persisted after each step completion (successful or not). |
238 | * |
239 | * @return the context |
240 | */ |
241 | public ExecutionContext getExecutionContext() { |
242 | return executionContext; |
243 | } |
244 | |
245 | /** |
246 | * @return the time when this execution was created. |
247 | */ |
248 | public Date getCreateTime() { |
249 | return createTime; |
250 | } |
251 | |
252 | /** |
253 | * @param createTime creation time of this execution. |
254 | */ |
255 | public void setCreateTime(Date createTime) { |
256 | this.createTime = createTime; |
257 | } |
258 | |
259 | /** |
260 | * Package private method for re-constituting the step executions from |
261 | * existing instances. |
262 | * @param stepExecution |
263 | */ |
264 | void addStepExecution(StepExecution stepExecution) { |
265 | stepExecutions.add(stepExecution); |
266 | } |
267 | |
268 | /** |
269 | * Get the date representing the last time this JobExecution was updated in |
270 | * the JobRepository. |
271 | * |
272 | * @return Date representing the last time this JobExecution was updated. |
273 | */ |
274 | public Date getLastUpdated() { |
275 | return lastUpdated; |
276 | } |
277 | |
278 | /** |
279 | * Set the last time this JobExecution was updated. |
280 | * |
281 | * @param lastUpdated |
282 | */ |
283 | public void setLastUpdated(Date lastUpdated) { |
284 | this.lastUpdated = lastUpdated; |
285 | } |
286 | |
287 | public List<Throwable> getFailureExceptions() { |
288 | return failureExceptions; |
289 | } |
290 | |
291 | /** |
292 | * Add the provided throwable to the failure exception list. |
293 | * |
294 | * @param t |
295 | */ |
296 | public synchronized void addFailureException(Throwable t) { |
297 | this.failureExceptions.add(t); |
298 | } |
299 | |
300 | /** |
301 | * Return all failure causing exceptions for this JobExecution, including |
302 | * step executions. |
303 | * |
304 | * @return List<Throwable> containing all exceptions causing failure for |
305 | * this JobExecution. |
306 | */ |
307 | public synchronized List<Throwable> getAllFailureExceptions() { |
308 | |
309 | Set<Throwable> allExceptions = new HashSet<Throwable>(failureExceptions); |
310 | for (StepExecution stepExecution : stepExecutions) { |
311 | allExceptions.addAll(stepExecution.getFailureExceptions()); |
312 | } |
313 | |
314 | return new ArrayList<Throwable>(allExceptions); |
315 | } |
316 | |
317 | /** |
318 | * Deserialise and ensure transient fields are re-instantiated when read |
319 | * back |
320 | */ |
321 | private void readObject(ObjectInputStream stream) throws IOException, ClassNotFoundException { |
322 | stream.defaultReadObject(); |
323 | failureExceptions = new ArrayList<Throwable>(); |
324 | } |
325 | |
326 | /* |
327 | * (non-Javadoc) |
328 | * |
329 | * @see org.springframework.batch.core.domain.Entity#toString() |
330 | */ |
331 | @Override |
332 | public String toString() { |
333 | return super.toString() |
334 | + String.format(", startTime=%s, endTime=%s, lastUpdated=%s, status=%s, exitStatus=%s, job=[%s], jobParameters=[%s]", |
335 | startTime, endTime, lastUpdated, status, exitStatus, jobInstance, jobParameters); |
336 | } |
337 | |
338 | /** |
339 | * Add some step executions. For internal use only. |
340 | * @param stepExecutions step executions to add to the current list |
341 | */ |
342 | public void addStepExecutions(List<StepExecution> stepExecutions) { |
343 | if (stepExecutions!=null) { |
344 | this.stepExecutions.removeAll(stepExecutions); |
345 | this.stepExecutions.addAll(stepExecutions); |
346 | } |
347 | } |
348 | |
349 | } |