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