package net.sourceforge.fenixedu.presentationTier.Action.commons; import java.util.ArrayList; import java.util.Collections; import java.util.List; import java.util.Set; import java.util.SortedSet; import java.util.TreeSet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import net.sourceforge.fenixedu.domain.Person; import net.sourceforge.fenixedu.domain.exceptions.DomainException; import net.sourceforge.fenixedu.domain.jobs.JobExecutionFileInput; import net.sourceforge.fenixedu.domain.jobs.JobExecutionRequestor; import net.sourceforge.fenixedu.domain.util.Email; import net.sourceforge.fenixedu.presentationTier.Action.base.FenixDispatchAction; import org.apache.struts.action.ActionForm; import org.apache.struts.action.ActionForward; import org.apache.struts.action.ActionMapping; import pt.ist.fenixWebFramework.renderers.utils.RenderUtils; import pt.ist.fenixWebFramework.renderers.validators.HtmlChainValidator; import pt.ist.fenixWebFramework.renderers.validators.HtmlValidator; import pt.ist.fenixWebFramework.services.Service; import pt.ist.fenixWebFramework.struts.annotations.Forward; import pt.ist.fenixWebFramework.struts.annotations.Forwards; import com.qubit.fenixframework.plugins.job.JavaCodeJob; import com.qubit.fenixframework.plugins.job.Job; import com.qubit.fenixframework.plugins.job.JobExecutionArguments; import com.qubit.fenixframework.plugins.job.JobExecutionContext; import com.qubit.fenixframework.plugins.job.utils.JobScheduleBean; import com.qubit.fenixframework.plugins.job.utils.JobScheduleBean.Periodicity; @Forwards({ @Forward(name = "status", path = "/commons/jobs/status.jsp"), @Forward(name = "queue", path = "/commons/jobs/queue.jsp"), @Forward(name = "schedule", path = "/commons/jobs/schedule.jsp"), @Forward(name = "editJavaCodeJob", path = "/commons/jobs/editJavaCodeJob.jsp"), @Forward(name = "viewCode", path = "/commons/jobs/viewCode.jsp"), @Forward(name = "listJobExecutions", path = "/commons/jobs/listJobExecutions.jsp") }) public abstract class JobManagementDispatchAction extends FenixDispatchAction { public static class PeriodInnerValueValidator extends HtmlValidator { private String periodicity; public PeriodInnerValueValidator() { super(); } public PeriodInnerValueValidator(HtmlChainValidator htmlChainValidator) { super(htmlChainValidator); } public String getPeriodicity() { return periodicity; } public void setPeriodicity(String periodicity) { this.periodicity = periodicity; } @Override public void performValidation() { String value = getComponent().getValue(); setValid(JobScheduleBean.isValid(Periodicity.valueOf(periodicity), value)); } } public ActionForward status(ActionMapping mapping, ActionForm actionForm, HttpServletRequest request, HttpServletResponse response) { Set jobs = getJobList(); SortedSet executions = new TreeSet(); for (Job job : jobs) { executions.addAll(job.getOrderedFinishedOrQueuedExecutionContexts()); } List top = new ArrayList(); int count = 0; for (JobExecutionContext context : executions) { if (count++ >= 5) break; top.add(context); } request.setAttribute("jobs", jobs); request.setAttribute("executions", top); return mapping.findForward("status"); } protected abstract Set getJobList(); public ActionForward createJavaCodeJob(ActionMapping mapping, ActionForm form, HttpServletRequest request, HttpServletResponse response) throws Exception { request.setAttribute("job", new JavaCodeJobBean()); return mapping.findForward("editJavaCodeJob"); } public ActionForward copyJavaCodeJob(ActionMapping mapping, ActionForm form, HttpServletRequest request, HttpServletResponse response) throws Exception { JavaCodeJob job = getDomainObject(request, "job"); request.setAttribute("job", new JavaCodeJobBean(job.getClassName(), job.getSource())); return mapping.findForward("editJavaCodeJob"); } public ActionForward editJavaCodeJob(ActionMapping mapping, ActionForm form, HttpServletRequest request, HttpServletResponse response) throws Exception { JavaCodeJob job = getDomainObject(request, "job"); request.setAttribute("job", new JavaCodeJobBean(job)); return mapping.findForward("editJavaCodeJob"); } public ActionForward saveJavaCodeJob(ActionMapping mapping, ActionForm form, HttpServletRequest request, HttpServletResponse response) throws Exception { JavaCodeJobBean job = (JavaCodeJobBean) getRenderedObject("job"); try { job.saveJob(); } catch (DomainException e) { addActionMessage(request, e.getKey(), e.getArgs()); request.setAttribute("job", job); return mapping.findForward("editJavaCodeJob"); } return redirectToStatus(mapping, form, request, response); } public ActionForward viewCode(ActionMapping mapping, ActionForm form, HttpServletRequest request, HttpServletResponse response) throws Exception { JavaCodeJob job = getDomainObject(request, "job"); request.setAttribute("job", job); return mapping.findForward("viewCode"); } public ActionForward prepareQueue(ActionMapping mapping, ActionForm actionForm, HttpServletRequest request, HttpServletResponse response) { try { Job job = getDomainObject(request, "job"); request.setAttribute("job", job); Class argType = job.getExecutor().getContextArgumentsType(); JobExecutionArguments arguments = argType.newInstance(); initializeArguments(arguments); if (arguments.isFormInputNeeded()) { request.setAttribute("arguments", arguments); request.setAttribute("schema", "job.arguments.edit." + argType.getSimpleName()); return mapping.findForward("queue"); } queue(job, arguments); return redirectToStatus(mapping, actionForm, request, response); } catch (InstantiationException e) { addActionMessage(request, "error"); } catch (IllegalAccessException e) { addActionMessage(request, "error"); } return redirectToStatus(mapping, actionForm, request, response); } protected void initializeArguments(JobExecutionArguments arguments) {} public ActionForward queuePostback(ActionMapping mapping, ActionForm actionForm, HttpServletRequest request, HttpServletResponse response) { Job job = getDomainObject(request, "job"); JobExecutionArguments arguments = (JobExecutionArguments) getRenderedObject("arguments"); RenderUtils.invalidateViewState(); request.setAttribute("job", job); request.setAttribute("arguments", arguments); request.setAttribute("schema", "job.arguments.edit." + arguments.getClass().getSimpleName()); return mapping.findForward("queue"); } public ActionForward queue(ActionMapping mapping, ActionForm actionForm, HttpServletRequest request, HttpServletResponse response) { Job job = getDomainObject(request, "job"); JobExecutionArguments arguments = (JobExecutionArguments) getRenderedObject("arguments"); if (arguments instanceof JobExecutionFileInput) { ((JobExecutionFileInput) arguments).saveFile(); } queue(job, arguments); return redirectToStatus(mapping, actionForm, request, response); } @Service protected void queue(Job job, JobExecutionArguments arguments) { job.queue(arguments); } public ActionForward prepareSchedule(ActionMapping mapping, ActionForm actionForm, HttpServletRequest request, HttpServletResponse response) { try { Job job = getDomainObject(request, "job"); request.setAttribute("job", job); Class argType = job.getExecutor().getContextArgumentsType(); JobExecutionArguments arguments = argType.newInstance(); request.setAttribute("arguments", arguments); if (arguments.isFormInputNeeded()) { request.setAttribute("schema", "job.arguments.edit." + argType.getSimpleName()); } JobScheduleBean schedule = new JobScheduleBean(job); schedule.setPeriodicity(Periodicity.HOURLY); schedule.updateInnerValuesMap(); request.setAttribute("schedule", schedule); return mapping.findForward("schedule"); } catch (InstantiationException e) { addActionMessage(request, "error"); } catch (IllegalAccessException e) { addActionMessage(request, "error"); } return redirectToStatus(mapping, actionForm, request, response); } public ActionForward schedulePostback(ActionMapping mapping, ActionForm form, HttpServletRequest request, HttpServletResponse response) throws Exception { Job job = getDomainObject(request, "job"); JobExecutionArguments arguments = (JobExecutionArguments) getRenderedObject("arguments"); JobScheduleBean schedule = (JobScheduleBean) getRenderedObject("schedule"); RenderUtils.invalidateViewState(); schedule.updateInnerValuesMap(); request.setAttribute("job", job); request.setAttribute("arguments", arguments); request.setAttribute("schedule", schedule); return mapping.findForward("schedule"); } public ActionForward schedule(ActionMapping mapping, ActionForm actionForm, HttpServletRequest request, HttpServletResponse response) { Job job = getDomainObject(request, "job"); JobExecutionArguments arguments = (JobExecutionArguments) getRenderedObject("arguments"); if (arguments instanceof JobExecutionFileInput) { ((JobExecutionFileInput) arguments).saveFile(); } schedule(job, arguments, (JobScheduleBean) getRenderedObject("schedule")); return redirectToStatus(mapping, actionForm, request, response); } @Service protected void schedule(Job job, JobExecutionArguments arguments, JobScheduleBean schedule) { schedule.updateInnerValuesMap(); job.schedule(schedule.getSchedule(), arguments); } @Service public ActionForward stall(ActionMapping mapping, ActionForm form, HttpServletRequest request, HttpServletResponse response) throws Exception { JobExecutionContext context = getDomainObject(request, "context"); context.stall(); return status(mapping, form, request, response); } @Service public ActionForward unstall(ActionMapping mapping, ActionForm form, HttpServletRequest request, HttpServletResponse response) throws Exception { JobExecutionContext context = getDomainObject(request, "context"); context.unstall(); return status(mapping, form, request, response); } @Service public ActionForward cancel(ActionMapping mapping, ActionForm actionForm, HttpServletRequest request, HttpServletResponse response) { JobExecutionContext context = getDomainObject(request, "context"); context.delete(); return redirectToStatus(mapping, actionForm, request, response); } @Service public ActionForward delete(ActionMapping mapping, ActionForm actionForm, HttpServletRequest request, HttpServletResponse response) { Job job = getDomainObject(request, "job"); job.delete(); return redirectToStatus(mapping, actionForm, request, response); } public ActionForward reportError(ActionMapping mapping, ActionForm actionForm, HttpServletRequest request, HttpServletResponse response) { JobExecutionContext context = getDomainObject(request, "context"); StringBuilder body = new StringBuilder(); body.append("Job Failure Error Report\n\n"); body.append("Job: (" + context.getJob().getExternalId() + ") - " + context.getJob().getExecutor().getExecutionDescription() + "\n"); body.append("Context: (" + context.getExternalId() + ") - " + context.getRequest() + "\n"); if (context.getArguments() instanceof JobExecutionRequestor) { body.append("Requestor: " + ((JobExecutionRequestor) context.getArguments()).getRequestor().getUsername() + "\n"); } body.append("Arguments: " + context.getArguments().getDescription() + "\n\n"); body.append("Execution Info: " + context.getAttemptsCount() + " attempts " + context.getLog().getDurationString() + "\n\n\n\n"); body.append("Stacktrace:\n\n"); body.append(context.getLog().getOutput()); Person reporter = getUserView(request).getPerson(); reportError(context, body.toString(), reporter); addActionMessage(request, "message.jobs.errorReportSubmitted"); return redirectToStatus(mapping, actionForm, request, response); } @Service private void reportError(JobExecutionContext context, String body, Person reporter) { new Email(reporter.getName(), reporter.getInstitutionalEmail(), null, Collections.singleton("ajuda.fenix@iscte.pt"), null, null, "Job Failure Error Report: " + context.getJob().getExecutor().getExecutionDescription(), body); } public ActionForward listExecutions(ActionMapping mapping, ActionForm actionForm, HttpServletRequest request, HttpServletResponse response) { Job job = getDomainObject(request, "job"); request.setAttribute("job", job); return mapping.findForward("listJobExecutions"); } public ActionForward repeat(ActionMapping mapping, ActionForm actionForm, HttpServletRequest request, HttpServletResponse response) { JobExecutionContext context = getDomainObject(request, "context"); repeat(context); return redirectToStatus(mapping, actionForm, request, response); } @Service private void repeat(JobExecutionContext context) { context.getJob().queue(context.getArguments()); } private ActionForward redirectToStatus(ActionMapping mapping, ActionForm actionForm, HttpServletRequest request, HttpServletResponse response) { return sendProtectedRedirect("/job.do?method=status", request); } }