1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17 package org.springframework.batch.admin.web;
18
19 import java.util.ArrayList;
20 import java.util.Collection;
21 import java.util.List;
22
23 import org.springframework.batch.admin.domain.JobExecutionInfo;
24 import org.springframework.batch.admin.domain.JobExecutionInfoResource;
25 import org.springframework.batch.admin.domain.NoSuchBatchJobException;
26 import org.springframework.batch.admin.domain.support.JobParametersExtractor;
27 import org.springframework.batch.core.BatchStatus;
28 import org.springframework.batch.core.Job;
29 import org.springframework.batch.core.JobExecution;
30 import org.springframework.batch.core.JobInstance;
31 import org.springframework.batch.core.JobParameters;
32 import org.springframework.batch.core.JobParametersInvalidException;
33 import org.springframework.batch.core.configuration.ListableJobLocator;
34 import org.springframework.batch.core.launch.JobExecutionNotRunningException;
35 import org.springframework.batch.core.launch.NoSuchJobException;
36 import org.springframework.batch.core.launch.NoSuchJobExecutionException;
37 import org.springframework.batch.core.repository.JobExecutionAlreadyRunningException;
38 import org.springframework.batch.core.repository.JobInstanceAlreadyCompleteException;
39 import org.springframework.batch.core.repository.JobRestartException;
40 import org.springframework.beans.factory.annotation.Autowired;
41 import org.springframework.data.domain.Pageable;
42 import org.springframework.hateoas.ExposesResourceFor;
43 import org.springframework.hateoas.PagedResources;
44 import org.springframework.hateoas.PagedResources.PageMetadata;
45 import org.springframework.http.HttpStatus;
46 import org.springframework.web.bind.annotation.PathVariable;
47 import org.springframework.web.bind.annotation.RequestMapping;
48 import org.springframework.web.bind.annotation.RequestMethod;
49 import org.springframework.web.bind.annotation.RequestParam;
50 import org.springframework.web.bind.annotation.ResponseStatus;
51 import org.springframework.web.bind.annotation.RestController;
52
53
54
55
56
57
58
59
60
61 @RestController
62 @RequestMapping("/batch/executions")
63 @ExposesResourceFor(JobExecutionInfoResource.class)
64 public class BatchJobExecutionsController extends AbstractBatchJobsController {
65
66 @Autowired
67 private ListableJobLocator jobLocator;
68
69
70
71
72
73
74
75
76
77 @RequestMapping(value = { "" }, method = RequestMethod.GET, produces = "application/json")
78 @ResponseStatus(HttpStatus.OK)
79 public PagedResources<JobExecutionInfoResource> list(Pageable pageable) throws NoSuchJobException {
80
81 Collection<JobExecutionInfoResource> resources = new ArrayList<JobExecutionInfoResource>();
82
83 for (JobExecution jobExecution : jobService.listJobExecutions(pageable.getOffset(), pageable.getPageSize())) {
84 Job job = jobLocator.getJob(jobExecution.getJobInstance().getJobName());
85
86 final JobExecutionInfoResource jobExecutionInfoResource = getJobExecutionInfoResource(jobExecution,
87 job.isRestartable());
88 resources.add(jobExecutionInfoResource);
89 }
90
91 return new PagedResources<JobExecutionInfoResource>(resources,
92 new PageMetadata(pageable.getPageSize(), pageable.getPageNumber(),
93 jobService.countJobExecutions()));
94 }
95
96
97
98
99
100
101
102
103
104 @RequestMapping(value = "", method = RequestMethod.GET, params = "jobname", produces = "application/json")
105 @ResponseStatus(HttpStatus.OK)
106 public Collection<JobExecutionInfoResource> executionsForJob(@RequestParam("jobname") String jobName,
107 @RequestParam(defaultValue = "0") int startJobExecution,
108 @RequestParam(defaultValue = "20") int pageSize) {
109
110 Collection<JobExecutionInfoResource> result = new ArrayList<JobExecutionInfoResource>();
111 try {
112 for (JobExecution jobExecution : jobService.listJobExecutionsForJob(jobName, startJobExecution, pageSize)) {
113 result.add(jobExecutionInfoResourceAssembler.toResource(new JobExecutionInfo(jobExecution, timeZone)));
114 }
115 }
116 catch (NoSuchJobException e) {
117 throw new NoSuchBatchJobException(jobName);
118 }
119 return result;
120 }
121
122
123
124
125
126
127
128 @RequestMapping(value = "", method = RequestMethod.POST, params = "jobname")
129 @ResponseStatus(HttpStatus.CREATED)
130 public void launchJob(@RequestParam("jobname") String name, @RequestParam(required = false) String jobParameters) throws JobParametersInvalidException, JobExecutionAlreadyRunningException, JobRestartException, JobInstanceAlreadyCompleteException, NoSuchJobException {
131 JobParameters params = new JobParameters();
132 if(jobParameters != null) {
133 JobParametersExtractor extractor = new JobParametersExtractor();
134 extractor.fromString(jobParameters);
135 }
136
137 jobService.launch(name, params);
138 }
139
140
141
142
143
144
145 private Collection<String> getJobDefinitionNames() {
146 int totalJobs = jobService.countJobs();
147 return jobService.listJobs(0, totalJobs);
148 }
149
150
151
152
153
154
155
156 private boolean isJobExecutionRestartable(JobExecution jobExecution) {
157 JobInstance jobInstance = jobExecution.getJobInstance();
158 BatchStatus status = jobExecution.getStatus();
159 try {
160 List<JobExecution> jobExecutionsForJobInstance = (List<JobExecution>) jobService.getJobExecutionsForJobInstance(
161 jobInstance.getJobName(), jobInstance.getId());
162 for (JobExecution jobExecutionForJobInstance : jobExecutionsForJobInstance) {
163 if (jobExecutionForJobInstance.getStatus() == BatchStatus.COMPLETED) {
164 return false;
165 }
166 }
167 }
168 catch (NoSuchJobException e) {
169 throw new NoSuchBatchJobException(jobInstance.getJobName());
170 }
171 return status.isGreaterThan(BatchStatus.STOPPING) && status.isLessThan(BatchStatus.ABANDONED);
172 }
173
174
175
176
177
178
179 @RequestMapping(value = "/{executionId}", method = RequestMethod.GET)
180 @ResponseStatus(HttpStatus.OK)
181 public JobExecutionInfoResource getJobExecutionInfo(@PathVariable long executionId) throws NoSuchJobExecutionException {
182
183 final JobExecution jobExecution;
184
185 try {
186 jobExecution = jobService.getJobExecution(executionId);
187 }
188 catch (org.springframework.batch.core.launch.NoSuchJobExecutionException e) {
189 throw new NoSuchJobExecutionException(String.format("Could not find jobExecution with id %s", String.valueOf(executionId)));
190 }
191
192 final Job job;
193 String jobName = jobExecution.getJobInstance().getJobName();
194
195 try {
196 job = jobLocator.getJob(jobName);
197 }
198 catch (NoSuchJobException e1) {
199 throw new NoSuchBatchJobException("The job '" + jobName + "' does not exist.");
200 }
201
202 return getJobExecutionInfoResource(jobExecution, job.isRestartable());
203 }
204
205 private JobExecutionInfoResource getJobExecutionInfoResource(JobExecution jobExecution,
206 boolean restartable) {
207
208 final JobExecutionInfoResource jobExecutionInfoResource = jobExecutionInfoResourceAssembler.toResource(new JobExecutionInfo(
209 jobExecution,
210 timeZone));
211 if (restartable) {
212
213
214
215 if (jobExecution.getStatus() != BatchStatus.COMPLETED) {
216 jobExecutionInfoResource.setRestartable(isJobExecutionRestartable(jobExecution));
217 }
218 }
219 else {
220
221 jobExecutionInfoResource.setRestartable(false);
222 }
223
224 return jobExecutionInfoResource;
225 }
226
227
228
229
230
231
232 @RequestMapping(value = { "/{executionId}" }, method = RequestMethod.PUT, params = "stop=true")
233 @ResponseStatus(HttpStatus.OK)
234 public void stopJobExecution(@PathVariable("executionId") long jobExecutionId) throws JobExecutionNotRunningException, NoSuchJobExecutionException {
235 try {
236 jobService.stop(jobExecutionId);
237 }
238 catch (org.springframework.batch.core.launch.JobExecutionNotRunningException e) {
239 throw new JobExecutionNotRunningException(String.format("Job execution with executionId %s is not running.", String.valueOf(jobExecutionId)));
240 }
241 catch (org.springframework.batch.core.launch.NoSuchJobExecutionException e) {
242 throw new NoSuchJobExecutionException(String.format("Could not find jobExecution with id %s", String.valueOf(jobExecutionId)));
243 }
244 }
245
246
247
248
249
250
251 @RequestMapping(value = { "/{executionId}" }, method = RequestMethod.PUT, params = "restart=true")
252 @ResponseStatus(HttpStatus.OK)
253 public void restartJobExecution(@PathVariable("executionId") long jobExecutionId) throws NoSuchJobExecutionException, JobExecutionAlreadyRunningException, JobParametersInvalidException, JobInstanceAlreadyCompleteException, JobRestartException, NoSuchJobException {
254
255 final JobExecution jobExecution;
256 try {
257 jobExecution = jobService.getJobExecution(jobExecutionId);
258 }
259 catch (org.springframework.batch.core.launch.NoSuchJobExecutionException e) {
260 throw new NoSuchJobExecutionException(String.format("Could not find jobExecution with id %s", String.valueOf(jobExecutionId)));
261 }
262
263 if (jobExecution.isRunning()) {
264 throw new JobExecutionAlreadyRunningException(
265 "Job Execution for this job is already running: " + jobExecution.getJobInstance());
266 }
267
268 final JobInstance lastInstance = jobExecution.getJobInstance();
269 final JobParameters jobParameters = jobExecution.getJobParameters();
270
271 final Job job;
272 try {
273 job = jobLocator.getJob(lastInstance.getJobName());
274 }
275 catch (NoSuchJobException e1) {
276 throw new NoSuchBatchJobException("The job '" + lastInstance.getJobName()
277 + "' does not exist.");
278 }
279 try {
280 job.getJobParametersValidator().validate(jobParameters);
281 }
282 catch (JobParametersInvalidException e) {
283 throw new JobParametersInvalidException(
284 "The Job Parameters for Job Execution " + jobExecution.getId()
285 + " are invalid.");
286 }
287
288 final BatchStatus status = jobExecution.getStatus();
289
290 if (status == BatchStatus.COMPLETED || status == BatchStatus.ABANDONED) {
291 throw new JobInstanceAlreadyCompleteException(
292 "Job Execution " + jobExecution.getId() + " is already complete.");
293 }
294
295 if (!job.isRestartable()) {
296 throw new JobRestartException(
297 "The job '" + lastInstance.getJobName() + "' is not restartable.");
298 }
299
300 jobService.launch(lastInstance.getJobName(), jobParameters);
301 }
302
303
304
305
306 @RequestMapping(value = { "" }, method = RequestMethod.PUT, params = "stop=true")
307 @ResponseStatus(HttpStatus.OK)
308 public void stopAll() {
309 jobService.stopAll();
310 }
311 }