1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16 package org.springframework.batch.admin.web;
17
18 import java.util.ArrayList;
19 import java.util.Arrays;
20 import java.util.Collection;
21 import java.util.HashSet;
22 import java.util.LinkedHashSet;
23 import java.util.List;
24 import java.util.TimeZone;
25
26 import javax.servlet.http.HttpServletRequest;
27
28 import org.springframework.batch.admin.domain.JobExecutionInfo;
29 import org.springframework.batch.admin.domain.JobInfo;
30 import org.springframework.batch.admin.domain.support.JobParametersExtractor;
31 import org.springframework.batch.admin.service.JobService;
32 import org.springframework.batch.core.JobExecution;
33 import org.springframework.batch.core.JobInstance;
34 import org.springframework.batch.core.JobParameters;
35 import org.springframework.batch.core.JobParametersInvalidException;
36 import org.springframework.batch.core.launch.NoSuchJobException;
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.beans.factory.annotation.Qualifier;
42 import org.springframework.stereotype.Controller;
43 import org.springframework.ui.ModelMap;
44 import org.springframework.util.StringUtils;
45 import org.springframework.validation.Errors;
46 import org.springframework.web.bind.annotation.ModelAttribute;
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.util.HtmlUtils;
51
52
53
54
55
56
57
58
59 @Controller
60 public class JobController {
61
62 private final JobService jobService;
63
64 private Collection<String> extensions = new HashSet<String>();
65
66 private TimeZone timeZone = TimeZone.getDefault();
67
68 private JobParametersExtractor jobParametersExtractor = new JobParametersExtractor();
69
70
71
72
73
74
75
76 public void setExtensions(Collection<String> extensions) {
77 this.extensions = new LinkedHashSet<String>(extensions);
78 }
79
80
81
82
83 @Autowired(required = false)
84 @Qualifier("userTimeZone")
85 public void setTimeZone(TimeZone timeZone) {
86 this.timeZone = timeZone;
87 }
88
89 @Autowired
90 public JobController(JobService jobService) {
91 super();
92 this.jobService = jobService;
93 extensions.addAll(Arrays.asList(".html", ".json", ".rss"));
94 }
95
96 @ModelAttribute("jobName")
97 public String getJobName(HttpServletRequest request) {
98 String path = request.getPathInfo();
99 int index = path.lastIndexOf("jobs/") + 5;
100 if (index >= 0) {
101 path = path.substring(index);
102 }
103 if (!path.contains(".")) {
104 return path;
105 }
106 for (String extension : extensions) {
107 if (path.endsWith(extension)) {
108 path = StringUtils.stripFilenameExtension(path);
109
110
111 break;
112 }
113 }
114 return path;
115 }
116
117 @RequestMapping(value = "/jobs/{jobName}", method = RequestMethod.POST)
118 public String launch(ModelMap model, @ModelAttribute("jobName") String jobName,
119 @ModelAttribute("launchRequest") LaunchRequest launchRequest, Errors errors,
120 @RequestParam(defaultValue = "execution") String origin) {
121
122 launchRequest.setJobName(jobName);
123 String params = launchRequest.jobParameters;
124
125 JobParameters jobParameters = jobParametersExtractor.fromString(params);
126
127 try {
128 JobExecution jobExecution = jobService.launch(jobName, jobParameters);
129 model.addAttribute(new JobExecutionInfo(jobExecution, timeZone));
130 }
131 catch (NoSuchJobException e) {
132 errors.reject("no.such.job", new Object[] { jobName }, "No such job: " + jobName);
133 }
134 catch (JobExecutionAlreadyRunningException e) {
135 errors.reject("job.already.running", "A job with this name and parameters is already running.");
136 }
137 catch (JobRestartException e) {
138 errors.reject("job.could.not.restart", "The job was not able to restart.");
139 }
140 catch (JobInstanceAlreadyCompleteException e) {
141 errors.reject("job.already.complete", "A job with this name and parameters already completed successfully.");
142 }
143 catch (JobParametersInvalidException e) {
144 errors.reject("job.parameters.invalid", "The job parameters are invalid according to the configuration.");
145 }
146
147 if (!"job".equals(origin)) {
148
149 return "jobs/execution";
150 }
151 else {
152
153 return details(model, jobName, errors, 0, 20);
154 }
155
156
157
158
159
160 }
161
162 @RequestMapping(value = "/jobs/{jobName}", method = RequestMethod.GET)
163 public String details(ModelMap model, @ModelAttribute("jobName") String jobName, Errors errors,
164 @RequestParam(defaultValue = "0") int startJobInstance, @RequestParam(defaultValue = "20") int pageSize) {
165
166 boolean launchable = jobService.isLaunchable(jobName);
167
168 try {
169
170 Collection<JobInstance> result = jobService.listJobInstances(jobName, startJobInstance, pageSize);
171 Collection<JobInstanceInfo> jobInstances = new ArrayList<JobInstanceInfo>();
172 model.addAttribute("jobParameters", jobParametersExtractor.fromJobParameters(jobService.getLastJobParameters(jobName)));
173
174 for (JobInstance jobInstance : result) {
175 Collection<JobExecution> jobExecutions = jobService.getJobExecutionsForJobInstance(jobName, jobInstance.getId());
176 jobInstances.add(new JobInstanceInfo(jobInstance, jobExecutions, timeZone));
177 }
178
179 model.addAttribute("jobInstances", jobInstances);
180 int total = jobService.countJobInstances(jobName);
181 TableUtils.addPagination(model, total, startJobInstance, pageSize, "JobInstance");
182 int count = jobService.countJobExecutionsForJob(jobName);
183 model.addAttribute("jobInfo", new JobInfo(jobName, count, launchable, jobService.isIncrementable(jobName)));
184
185 }
186 catch (NoSuchJobException e) {
187 errors.reject("no.such.job", new Object[] { jobName },
188 "There is no such job (" + HtmlUtils.htmlEscape(jobName) + ")");
189 }
190
191 return "jobs/job";
192
193 }
194
195 @RequestMapping(value = "/jobs", method = RequestMethod.GET)
196 public void jobs(ModelMap model, @RequestParam(defaultValue = "0") int startJob,
197 @RequestParam(defaultValue = "20") int pageSize) {
198 int total = jobService.countJobs();
199 TableUtils.addPagination(model, total, startJob, pageSize, "Job");
200 Collection<String> names = jobService.listJobs(startJob, pageSize);
201 List<JobInfo> jobs = new ArrayList<JobInfo>();
202 for (String name : names) {
203 int count = 0;
204 try {
205 count = jobService.countJobExecutionsForJob(name);
206 }
207 catch (NoSuchJobException e) {
208
209 }
210 boolean launchable = jobService.isLaunchable(name);
211 boolean incrementable = jobService.isIncrementable(name);
212 jobs.add(new JobInfo(name, count, null, launchable, incrementable));
213 }
214 model.addAttribute("jobs", jobs);
215 }
216
217 }