View Javadoc

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 }