/* * Copyright 2009-2014 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.springframework.batch.admin.web; import java.io.IOException; import java.util.ArrayList; import java.util.Collection; import java.util.Date; import java.util.HashMap; import java.util.Map; import java.util.TimeZone; import com.fasterxml.jackson.databind.ObjectMapper; import org.springframework.batch.admin.domain.JobExecutionInfo; import org.springframework.batch.admin.domain.StepExecutionHistory; import org.springframework.batch.admin.domain.StepExecutionInfo; import org.springframework.batch.admin.domain.StepExecutionProgress; import org.springframework.batch.admin.service.JobService; import org.springframework.batch.admin.service.NoSuchStepExecutionException; import org.springframework.batch.core.JobExecution; import org.springframework.batch.core.StepExecution; import org.springframework.batch.core.launch.NoSuchJobExecutionException; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Qualifier; import org.springframework.stereotype.Controller; import org.springframework.ui.Model; import org.springframework.validation.Errors; import org.springframework.web.bind.annotation.ModelAttribute; import org.springframework.web.bind.annotation.PathVariable; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestMethod; /** * Controller for step executions. * * @author Dave Syer * */ @Controller public class StepExecutionController { private JobService jobService; private ObjectMapper objectMapper; private TimeZone timeZone = TimeZone.getDefault(); /** * @param timeZone the timeZone to set */ @Autowired(required=false) @Qualifier("userTimeZone") public void setTimeZone(TimeZone timeZone) { this.timeZone = timeZone; } @Autowired public void setObjectMapper(ObjectMapper objectMapper) { this.objectMapper = objectMapper; } @Autowired public StepExecutionController(JobService jobService) { super(); this.jobService = jobService; } @RequestMapping(value = "/jobs/executions/{jobExecutionId}/steps", method = RequestMethod.GET) public String list(Model model, @PathVariable Long jobExecutionId, @ModelAttribute("date") Date date, Errors errors) { Collection<StepExecutionInfo> result = new ArrayList<StepExecutionInfo>(); try { for (StepExecution stepExecution : jobService.getStepExecutions(jobExecutionId)) { result.add(new StepExecutionInfo(stepExecution, timeZone)); } JobExecution jobExecution = jobService.getJobExecution(jobExecutionId); model.addAttribute(new JobExecutionInfo(jobExecution, timeZone)); } catch (NoSuchJobExecutionException e) { errors.reject("no.such.job.execution", new Object[] { jobExecutionId }, "There is no such job execution (" + jobExecutionId + ")"); } model.addAttribute("stepExecutions", result); return "jobs/executions/steps"; } @RequestMapping(value = "/jobs/executions/{jobExecutionId}/steps/{stepExecutionId}", method = RequestMethod.GET) public String detail(Model model, @PathVariable Long jobExecutionId, @PathVariable Long stepExecutionId, @ModelAttribute("date") Date date, Errors errors) { try { StepExecution stepExecution = jobService.getStepExecution(jobExecutionId, stepExecutionId); model.addAttribute(new StepExecutionInfo(stepExecution, timeZone)); } catch (NoSuchStepExecutionException e) { errors.reject("no.such.step.execution", new Object[] { stepExecutionId }, "There is no such step execution (" + stepExecutionId + ")"); } catch (NoSuchJobExecutionException e) { errors.reject("no.such.job.execution", new Object[] { jobExecutionId }, "There is no such job execution (" + jobExecutionId + ")"); } return "jobs/executions/step"; } @RequestMapping(value = "/jobs/executions/{jobExecutionId}/steps/{stepExecutionId}/progress", method = RequestMethod.GET) public String history(Model model, @PathVariable Long jobExecutionId, @PathVariable Long stepExecutionId, @ModelAttribute("date") Date date, Errors errors) { try { StepExecution stepExecution = jobService.getStepExecution(jobExecutionId, stepExecutionId); model.addAttribute(new StepExecutionInfo(stepExecution, timeZone)); String stepName = stepExecution.getStepName(); if (stepName.contains(":partition")) { // assume we want to compare all partitions stepName = stepName.replaceAll("(:partition).*", "$1*"); } String jobName = stepExecution.getJobExecution().getJobInstance().getJobName(); StepExecutionHistory stepExecutionHistory = computeHistory(jobName, stepName); model.addAttribute(stepExecutionHistory); model.addAttribute(new StepExecutionProgress(stepExecution, stepExecutionHistory)); } catch (NoSuchStepExecutionException e) { errors.reject("no.such.step.execution", new Object[] { stepExecutionId }, "There is no such step execution (" + stepExecutionId + ")"); } catch (NoSuchJobExecutionException e) { errors.reject("no.such.job.execution", new Object[] { jobExecutionId }, "There is no such job execution (" + jobExecutionId + ")"); } return "jobs/executions/step/progress"; } private StepExecutionHistory computeHistory(String jobName, String stepName) { int total = jobService.countStepExecutionsForStep(jobName, stepName); StepExecutionHistory stepExecutionHistory = new StepExecutionHistory(stepName); for (int i = 0; i < total; i += 1000) { for (StepExecution stepExecution : jobService.listStepExecutionsForStep(jobName, stepName, i, 1000)) { stepExecutionHistory.append(stepExecution); } } return stepExecutionHistory; } @RequestMapping(value = "/jobs/executions/{jobExecutionId}/steps/{stepExecutionId}/execution-context", method = RequestMethod.GET) public String getStepExecutionContext(Model model, @PathVariable Long jobExecutionId, @PathVariable Long stepExecutionId, @ModelAttribute("date") Date date, Errors errors) { try { StepExecution stepExecution = jobService.getStepExecution(jobExecutionId, stepExecutionId); Map<String, Object> executionMap = new HashMap<String, Object>(); for (Map.Entry<String, Object> entry : stepExecution.getExecutionContext().entrySet()) { executionMap.put(entry.getKey(), entry.getValue()); } model.addAttribute("stepExecutionContext", objectMapper.writeValueAsString(executionMap)); model.addAttribute("stepExecutionId", stepExecutionId); model.addAttribute("stepName", stepExecution.getStepName()); model.addAttribute("jobExecutionId", jobExecutionId); } catch (NoSuchJobExecutionException e) { errors.reject("no.such.job.execution", new Object[] { jobExecutionId }, "There is no such job execution (" + jobExecutionId + ")"); } catch (NoSuchStepExecutionException e) { errors.reject("no.such.step.execution", new Object[] { stepExecutionId }, "There is no such step execution (" + stepExecutionId + ")"); } catch (IOException e) { errors.reject("serialization.error", new Object[] { jobExecutionId }, "Error serializing execution context for step execution (" + stepExecutionId + ")"); } return "jobs/executions/step/execution-context"; } }