001/* 002 * Copyright 2006-2018 the original author or authors. 003 * 004 * Licensed under the Apache License, Version 2.0 (the "License"); 005 * you may not use this file except in compliance with the License. 006 * You may obtain a copy of the License at 007 * 008 * https://www.apache.org/licenses/LICENSE-2.0 009 * 010 * Unless required by applicable law or agreed to in writing, software 011 * distributed under the License is distributed on an "AS IS" BASIS, 012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 013 * See the License for the specific language governing permissions and 014 * limitations under the License. 015 */ 016 017package org.springframework.batch.core; 018 019import java.io.IOException; 020import java.io.ObjectInputStream; 021import java.util.ArrayList; 022import java.util.Collection; 023import java.util.Collections; 024import java.util.Date; 025import java.util.HashSet; 026import java.util.LinkedHashSet; 027import java.util.List; 028import java.util.Set; 029import java.util.concurrent.CopyOnWriteArrayList; 030 031import org.springframework.batch.item.ExecutionContext; 032import org.springframework.lang.Nullable; 033 034/** 035 * Batch domain object representing the execution of a job. 036 * 037 * @author Lucas Ward 038 * @author Michael Minella 039 * @author Mahmoud Ben Hassine 040 * @author Dimitrios Liapis 041 * 042 */ 043@SuppressWarnings("serial") 044public class JobExecution extends Entity { 045 046 private final JobParameters jobParameters; 047 048 private JobInstance jobInstance; 049 050 private volatile Collection<StepExecution> stepExecutions = Collections.synchronizedSet(new LinkedHashSet<>()); 051 052 private volatile BatchStatus status = BatchStatus.STARTING; 053 054 private volatile Date startTime = null; 055 056 private volatile Date createTime = new Date(System.currentTimeMillis()); 057 058 private volatile Date endTime = null; 059 060 private volatile Date lastUpdated = null; 061 062 private volatile ExitStatus exitStatus = ExitStatus.UNKNOWN; 063 064 private volatile ExecutionContext executionContext = new ExecutionContext(); 065 066 private transient volatile List<Throwable> failureExceptions = new CopyOnWriteArrayList<>(); 067 068 private final String jobConfigurationName; 069 070 public JobExecution(JobExecution original) { 071 this.jobParameters = original.getJobParameters(); 072 this.jobInstance = original.getJobInstance(); 073 this.stepExecutions = original.getStepExecutions(); 074 this.status = original.getStatus(); 075 this.startTime = original.getStartTime(); 076 this.createTime = original.getCreateTime(); 077 this.endTime = original.getEndTime(); 078 this.lastUpdated = original.getLastUpdated(); 079 this.exitStatus = original.getExitStatus(); 080 this.executionContext = original.getExecutionContext(); 081 this.failureExceptions = original.getFailureExceptions(); 082 this.jobConfigurationName = original.getJobConfigurationName(); 083 this.setId(original.getId()); 084 this.setVersion(original.getVersion()); 085 } 086 087 /** 088 * Because a JobExecution isn't valid unless the job is set, this 089 * constructor is the only valid one from a modeling point of view. 090 * 091 * @param job the job of which this execution is a part 092 * @param id {@link Long} that represents the id for the JobExecution. 093 * @param jobParameters {@link JobParameters} instance for this JobExecution. 094 * @param jobConfigurationName {@link String} instance that represents the 095 * job configuration name (used with JSR-352). 096 */ 097 public JobExecution(JobInstance job, Long id, @Nullable JobParameters jobParameters, String jobConfigurationName) { 098 super(id); 099 this.jobInstance = job; 100 this.jobParameters = jobParameters == null ? new JobParameters() : jobParameters; 101 this.jobConfigurationName = jobConfigurationName; 102 } 103 104 public JobExecution(JobInstance job, JobParameters jobParameters, String jobConfigurationName) { 105 this(job, null, jobParameters, jobConfigurationName); 106 } 107 108 public JobExecution(Long id, JobParameters jobParameters, String jobConfigurationName) { 109 this(null, id, jobParameters, jobConfigurationName); 110 } 111 112 /** 113 * Constructor for transient (unsaved) instances. 114 * 115 * @param job the enclosing {@link JobInstance} 116 * @param jobParameters {@link JobParameters} instance for this JobExecution. 117 */ 118 public JobExecution(JobInstance job, JobParameters jobParameters) { 119 this(job, null, jobParameters, null); 120 } 121 122 public JobExecution(Long id, JobParameters jobParameters) { 123 this(null, id, jobParameters, null); 124 } 125 126 public JobExecution(Long id) { 127 this(null, id, null, null); 128 } 129 130 public JobParameters getJobParameters() { 131 return this.jobParameters; 132 } 133 134 public Date getEndTime() { 135 return endTime; 136 } 137 138 public void setJobInstance(JobInstance jobInstance) { 139 this.jobInstance = jobInstance; 140 } 141 142 public void setEndTime(Date endTime) { 143 this.endTime = endTime; 144 } 145 146 public Date getStartTime() { 147 return startTime; 148 } 149 150 public void setStartTime(Date startTime) { 151 this.startTime = startTime; 152 } 153 154 public BatchStatus getStatus() { 155 return status; 156 } 157 158 /** 159 * Set the value of the status field. 160 * 161 * @param status the status to set 162 */ 163 public void setStatus(BatchStatus status) { 164 this.status = status; 165 } 166 167 /** 168 * Upgrade the status field if the provided value is greater than the 169 * existing one. Clients using this method to set the status can be sure 170 * that they don't overwrite a failed status with an successful one. 171 * 172 * @param status the new status value 173 */ 174 public void upgradeStatus(BatchStatus status) { 175 this.status = this.status.upgradeTo(status); 176 } 177 178 /** 179 * Convenience getter for for the id of the enclosing job. Useful for DAO 180 * implementations. 181 * 182 * @return the id of the enclosing job 183 */ 184 public Long getJobId() { 185 if (jobInstance != null) { 186 return jobInstance.getId(); 187 } 188 return null; 189 } 190 191 /** 192 * @param exitStatus {@link ExitStatus} instance to be used for job execution. 193 */ 194 public void setExitStatus(ExitStatus exitStatus) { 195 this.exitStatus = exitStatus; 196 } 197 198 /** 199 * @return the exitCode 200 */ 201 public ExitStatus getExitStatus() { 202 return exitStatus; 203 } 204 205 /** 206 * @return the Job that is executing. 207 */ 208 public JobInstance getJobInstance() { 209 return jobInstance; 210 } 211 212 /** 213 * Accessor for the step executions. 214 * 215 * @return the step executions that were registered 216 */ 217 public Collection<StepExecution> getStepExecutions() { 218 return Collections.unmodifiableList(new ArrayList<>(stepExecutions)); 219 } 220 221 /** 222 * Register a step execution with the current job execution. 223 * @param stepName the name of the step the new execution is associated with 224 * @return {@link StepExecution} an empty {@code StepExecution} associated with this 225 * {@code JobExecution}. 226 */ 227 public StepExecution createStepExecution(String stepName) { 228 StepExecution stepExecution = new StepExecution(stepName, this); 229 this.stepExecutions.add(stepExecution); 230 return stepExecution; 231 } 232 233 /** 234 * Test if this {@link JobExecution} indicates that it is running. It should 235 * be noted that this does not necessarily mean that it has been persisted 236 * as such yet. 237 * 238 * @return true if the end time is null and the start time is not null 239 */ 240 public boolean isRunning() { 241 return startTime != null && endTime == null; 242 } 243 244 /** 245 * Test if this {@link JobExecution} indicates that it has been signalled to 246 * stop. 247 * @return true if the status is {@link BatchStatus#STOPPING} 248 */ 249 public boolean isStopping() { 250 return status == BatchStatus.STOPPING; 251 } 252 253 /** 254 * Signal the {@link JobExecution} to stop. Iterates through the associated 255 * {@link StepExecution}s, calling {@link StepExecution#setTerminateOnly()}. 256 * 257 */ 258 public void stop() { 259 for (StepExecution stepExecution : stepExecutions) { 260 stepExecution.setTerminateOnly(); 261 } 262 status = BatchStatus.STOPPING; 263 } 264 265 /** 266 * Sets the {@link ExecutionContext} for this execution 267 * 268 * @param executionContext the context 269 */ 270 public void setExecutionContext(ExecutionContext executionContext) { 271 this.executionContext = executionContext; 272 } 273 274 /** 275 * Returns the {@link ExecutionContext} for this execution. The content is 276 * expected to be persisted after each step completion (successful or not). 277 * 278 * @return the context 279 */ 280 public ExecutionContext getExecutionContext() { 281 return executionContext; 282 } 283 284 /** 285 * @return the time when this execution was created. 286 */ 287 public Date getCreateTime() { 288 return createTime; 289 } 290 291 /** 292 * @param createTime creation time of this execution. 293 */ 294 public void setCreateTime(Date createTime) { 295 this.createTime = createTime; 296 } 297 298 public String getJobConfigurationName() { 299 return this.jobConfigurationName; 300 } 301 302 /** 303 * Package private method for re-constituting the step executions from 304 * existing instances. 305 * @param stepExecution execution to be added 306 */ 307 void addStepExecution(StepExecution stepExecution) { 308 stepExecutions.add(stepExecution); 309 } 310 311 /** 312 * Get the date representing the last time this JobExecution was updated in 313 * the JobRepository. 314 * 315 * @return Date representing the last time this JobExecution was updated. 316 */ 317 public Date getLastUpdated() { 318 return lastUpdated; 319 } 320 321 /** 322 * Set the last time this JobExecution was updated. 323 * 324 * @param lastUpdated {@link Date} instance to mark job execution's lastUpdated attribute. 325 */ 326 public void setLastUpdated(Date lastUpdated) { 327 this.lastUpdated = lastUpdated; 328 } 329 330 public List<Throwable> getFailureExceptions() { 331 return failureExceptions; 332 } 333 334 /** 335 * Add the provided throwable to the failure exception list. 336 * 337 * @param t {@link Throwable} instance to be added failure exception list. 338 */ 339 public synchronized void addFailureException(Throwable t) { 340 this.failureExceptions.add(t); 341 } 342 343 /** 344 * Return all failure causing exceptions for this JobExecution, including 345 * step executions. 346 * 347 * @return List<Throwable> containing all exceptions causing failure for 348 * this JobExecution. 349 */ 350 public synchronized List<Throwable> getAllFailureExceptions() { 351 352 Set<Throwable> allExceptions = new HashSet<>(failureExceptions); 353 for (StepExecution stepExecution : stepExecutions) { 354 allExceptions.addAll(stepExecution.getFailureExceptions()); 355 } 356 357 return new ArrayList<>(allExceptions); 358 } 359 360 /** 361 * Deserialize and ensure transient fields are re-instantiated when read 362 * back. 363 * 364 * @param stream instance of {@link ObjectInputStream}. 365 * 366 * @throws IOException thrown if error occurs during read. 367 * @throws ClassNotFoundException thrown if class is not found. 368 */ 369 private void readObject(ObjectInputStream stream) throws IOException, ClassNotFoundException { 370 stream.defaultReadObject(); 371 failureExceptions = new ArrayList<>(); 372 } 373 374 /* 375 * (non-Javadoc) 376 * 377 * @see org.springframework.batch.core.domain.Entity#toString() 378 */ 379 @Override 380 public String toString() { 381 return super.toString() 382 + String.format(", startTime=%s, endTime=%s, lastUpdated=%s, status=%s, exitStatus=%s, job=[%s], jobParameters=[%s]", 383 startTime, endTime, lastUpdated, status, exitStatus, jobInstance, jobParameters); 384 } 385 386 /** 387 * Add some step executions. For internal use only. 388 * @param stepExecutions step executions to add to the current list 389 */ 390 public void addStepExecutions(List<StepExecution> stepExecutions) { 391 if (stepExecutions!=null) { 392 this.stepExecutions.removeAll(stepExecutions); 393 this.stepExecutions.addAll(stepExecutions); 394 } 395 } 396}