View Javadoc

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