简单的quartz 可视化监听管理界面
spring-quartz。
导包、配置,不在此介绍。
简单的quartz管理界面,包括触发器的暂停、恢复、删除、修改(暂无),任务的运行、触发添加、创建,删除。
扩展内容:日志的管理,添加和创建触发器的操作(暂无实现)
本文原创,禁止转载。
扩展内容(暂不实现)
前端代码:
<%-- Created by IntelliJ IDEA. User: ttttt Date: 2017/5/31 Time: 15:43 To change this template use File | Settings | File Templates. --%> <%@ page contentType="text/html;charset=UTF-8" language="java" %> <%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %> <%@ taglib prefix="fmt" uri="http://java.sun.com/jsp/jstl/fmt" %> <html> <head> <title>Title</title> <link rel="stylesheet" type="text/css" href="/bootstrap/css/bootstrap.min.css"> <script type="text/javascript" src="/js/jquery-2.1.1.min.js"/> <script type="text/javascript" src="/bootstrap/js/bootstrap.min.js"></script> </head> <body> <h1>调度详情</h1> <table id="table_report" class="table table-striped table-bordered table-hover"> <thead> <tr> <!-- th class="text-center">序号</th--> <th class="text-center">任务组名称</th> <th class="text-center">定时任务名称</th> <th class="text-center">触发器组名称</th> <th class="text-center">触发器名称</th> <%--<th class="text-center">任务类名</th>--%> <th class="text-center">触发器描述</th> <th class="text-center">时间表达式</th> <th class="text-center">任务状态</th> <th class="text-center">开始时间</th> <th class="text-center">上次重启时间</th> <th class="text-center">上次执行时间</th> <%--<th class="text-center">下次运行时间</th>--%> <th class="text-center">操作</th> </tr> </thead> <tbody> <!-- 开始循环 --> <c:choose> <c:when test="${not empty jobInfos && jobInfos.size()>0}"> <c:forEach items="${jobInfos}" var="var" varStatus="vs"> <tr> <td class='text-center' style="width: auto;">${var.jobGroup}</td> <td class='text-center' style="width: auto;">${var.jobName}</td> <td class='text-center' style="width: auto;">${var.triggerGroupName}</td> <td class='text-center' style="width: auto;">${var.triggerName}</td> <%--<td class='text-center' style="width: auto;">${var.jobClass}</td>--%> <td class='text-center' style="width: auto;">${var.description}</td> <td class='text-center' style="width: auto;">${var.cronExpr}</td> <td class='text-center' style="width: auto;"> <c:if test="${var.jobStatus == 'NONE'}"> <span class="label">未知</span> </c:if> <c:if test="${var.jobStatus == 'NORMAL'}"> <span class="label label-success arrowed">正常运行</span> </c:if> <c:if test="${var.jobStatus == 'PAUSED'}"> <span class="label label-warning">暂停状态</span> </c:if> <c:if test="${var.jobStatus == 'COMPLETE'}"> <span class="label label-important arrowed-in">完成状态</span> </c:if> <c:if test="${var.jobStatus == 'ERROR'}"> <span class="label label-info arrowed-in-right arrowed">错误状态</span> </c:if> <c:if test="${var.jobStatus == 'BLOCKED'}"> <span class="label label-danger">锁定状态</span> </c:if> </td> <td class='text-center' style="width: auto;"><fmt:formatDate value="${var.startTime}" pattern="yyyy-MM-dd HH:mm:ss"/></td> <td class='text-center' style="width: auto;"><fmt:formatDate value="${var.reStartTime}" pattern="yyyy-MM-dd HH:mm:ss"/></td> <td class='text-center' style="width: auto;"><fmt:formatDate value="${var.previousFireTime}" pattern="yyyy-MM-dd HH:mm:ss"/></td> <%--<td class='text-center' style="width: auto;"><fmt:formatDate value="${var.nextFireTime}"--%> <%--pattern="yyyy-MM-dd HH:mm:ss"/></td>--%> <td class='text-center' style="width: auto;"> <a class="btn btn-sm btn-warning" onclick="pauseJob('${var.triggerName}','${var.triggerGroupName}');">暂停</a> <a class="btn btn-sm btn-success" onclick="resumeJob('${var.triggerName}','${var.triggerGroupName}');">恢复</a> <a class="btn btn-sm btn-info" onclick="triggerJob('${var.jobName}','${var.jobGroup}');">修改</a> <a class="btn btn-sm btn-danger" onclick="deleteJob('${var.triggerName}','${var.triggerGroupName}');">删除</a> </td><!-- cron,jobName,jobGroupName,triggerName,triggerGroupName --> </tr> </c:forEach> </c:when> <c:otherwise> <tr class="main_info"> <td colspan="100" class="text-center">没有相关数据</td> </tr> </c:otherwise> </c:choose> </tbody> </table> <h1>任务详情</h1> <table id="table_detail_report" class="table table-striped table-bordered table-hover"> <thead> <tr> <!-- th class="text-center">序号</th--> <th class="text-center">任务组名称</th> <th class="text-center">任务名称</th> <th class="text-center">任务类名</th> <th class="text-center">任务描述</th> <th class="text-center">持久性</th> <th class="text-center">禁止并发</th> <th class="text-center">数据更新</th> <th class="text-center">恢复性</th> <th class="text-center">关联调度器数量</th> <th class="text-center">操作</th> </tr> </thead> <tbody> <!-- 开始循环 --> <c:choose> <c:when test="${not empty jobDetailInfos && jobDetailInfos.size()>0}"> <c:forEach items="${jobDetailInfos}" var="var" varStatus="vs"> <tr> <td class='text-center' style="width: auto;">${var.jobGroup}</td> <td class='text-center' style="width: auto;">${var.jobName}</td> <td class='text-center' style="width: auto;">${var.jobClass}</td> <td class='text-center' style="width: auto;">${var.description}</td> <td class='text-center' style="width: auto;"> <c:if test="${var.duration}"> 是 </c:if> <c:if test="${!var.duration}"> 否 </c:if> </td> <td class='text-center' style="width: auto;"> <c:if test="${var.concurrentExectionDisallowed}"> 是 </c:if> <c:if test="${!var.concurrentExectionDisallowed}"> 否 </c:if> </td> <td class='text-center' style="width: auto;"> <c:if test="${var.persistJobDataAfterExecution}"> 是 </c:if> <c:if test="${!var.persistJobDataAfterExecution}"> 否 </c:if> </td> <td class='text-center' style="width: auto;"> <c:if test="${var.requestsRecovery}"> 是 </c:if> <c:if test="${!var.requestsRecovery}"> 否 </c:if> </td> <td class='text-center' style="width: auto;">${var.triggerCount}</td> <td class='text-center' style="width: auto;"> <a class="btn btn-sm btn-success" onclick="triggerJob('${var.jobName}','${var.jobGroup}');">运行一次</a> <a class="btn btn-sm btn-warning" href="/quartz/addJobTrigger?jobName=${var.jobName}&&jobGroup=${var.jobGroup}">添加调度</a> <a class="btn btn-sm btn-info" href="/quartz/addJobTrigger?jobName=${var.jobName}&&jobGroup=${var.jobGroup}">创建调度</a> <a class="btn btn-sm btn-danger" onclick="deleteJobDetail('${var.jobName}','${var.jobGroup}');">删除</a> </td><!-- cron,jobName,jobGroupName,triggerName,triggerGroupName --> </tr> </c:forEach> </c:when> <c:otherwise> <tr class="main_info"> <td colspan="100" class="text-center">没有相关数据</td> </tr> </c:otherwise> </c:choose> </tbody> </table> <h1>添加/修改 调度</h1> <form class="form-horizontal" action="quartzAdd/add"> <div class="form-group"> <label for="jobName" class="col-sm-1 control-label">任务名称</label> <div class="col-sm-3"> <input type="text" class="form-control" id="jobName" disabled placeholder="任务名称" value="${jobDetailInfo.jobName}"> </div> </div> <div class="form-group"> <label for="jobGroupName" class="col-sm-1 control-label">任务组名称</label> <div class="col-sm-3"> <input type="text" class="form-control" id="jobGroupName" disabled placeholder="任务组名称" value="${jobDetailInfo.jobGroup}"> </div> </div> <div class="form-group"> <label for="jobClass" class="col-sm-1 control-label">任务类</label> <div class="col-sm-3"> <input type="text" class="form-control" id="jobClass" disabled placeholder="任务类" value="${jobDetailInfo.jobClass}"> </div> </div> <c:forEach items="${jobDetailInfo.jobDataMap}" var="var" varStatus="vs"> <div class="form-group"> <label for="jobData${vs.count}" class="col-sm-1 control-label">${var.key} (初始化)</label> <div class="col-sm-3"> <input type="text" class="form-control" id="jobData${vs.count}" disable placeholder="当前任务值:${var.value}"> </div> </div> </c:forEach> <div class="form-group"> <label for="triggerName" class="col-sm-1 control-label">触发器名称</label> <div class="col-sm-3"> <input type="text" class="form-control" id="triggerName" placeholder="触发器名称"> </div> </div> <div class="form-group"> <label for="triggerGroupName" class="col-sm-1 control-label">触发器组名称</label> <div class="col-sm-3"> <input type="text" class="form-control" id="triggerGroupName" placeholder="触发器组名称"> </div> </div> <div class="form-group"> <label for="cron" class="col-sm-1 control-label">cron表达式</label> <div class="col-sm-3"> <input type="text" class="form-control" id="cron" placeholder="例如: 0 0/5 * 6L * 1,2 ?"> </div> </div> <div class="form-group"> <label class="col-sm-1 control-label">是否立即执行</label> <div class="col-sm-3"> <label class="radio-inline"> <input type="radio" name="radio" id="inlineRadio1" value="1"> 是 </label> <label class="radio-inline"> <input type="radio" name="radio" id="inlineRadio2" value="0"> 否 </label> </div> </div> <div class="form-group"> <div class="col-sm-offset-1 col-sm-3"> <button type="submit" class="btn btn-default">添加调度</button> </div> </div> </form> <h1>创建调度</h1> <form class="form-horizontal" action="quartzAdd/add"> <div class="form-group"> <label for="jobName1" class="col-sm-1 control-label">任务名称</label> <div class="col-sm-3"> <input type="text" class="form-control" id="jobName1" placeholder="任务名称"> </div> </div> <div class="form-group"> <label for="jobGroupName1" class="col-sm-1 control-label">任务组名称</label> <div class="col-sm-3"> <input type="text" class="form-control" id="jobGroupName1" placeholder="任务组名称"> </div> </div> <div class="form-group"> <label for="jobClass1" class="col-sm-1 control-label">任务类</label> <div class="col-sm-3"> <input type="text" class="form-control" id="jobClass1" disabled placeholder="任务类" value="${jobDetailInfo.jobClass}"> </div> </div> <div class="form-group"> <label for="triggerName1" class="col-sm-1 control-label">触发器名称</label> <div class="col-sm-3"> <input type="text" class="form-control" id="triggerName1" placeholder="触发器名称"> </div> </div> <div class="form-group"> <label for="triggerGroupName1" class="col-sm-1 control-label">触发器组名称</label> <div class="col-sm-3"> <input type="text" class="form-control" id="triggerGroupName1" placeholder="触发器组名称"> </div> </div> <div class="form-group"> <label for="cron1" class="col-sm-1 control-label">cron表达式</label> <div class="col-sm-3"> <input type="text" class="form-control" id="cron1" placeholder="例如: 0 0/5 * 6L * 1,2 ?"> </div> </div> <div class="form-group"> <label class="col-sm-1 control-label">是否立即执行</label> <div class="col-sm-3"> <label class="radio-inline"> <input type="radio" name="radio" id="inlineRadio11" value="1"> 是 </label> <label class="radio-inline"> <input type="radio" name="radio" id="inlineRadio12" value="0"> 否 </label> </div> </div> <div class="form-group"> <div class="col-sm-offset-1 col-sm-3"> <button type="submit" class="btn btn-default">创建调度</button> </div> </div> </form> <br/> <script> function deleteJobDetail(jobName, jobGroup) { if(confirm("是否删除该任务详情。")){ $.ajax({ type: "POST", url: "/quartz/deleteJobDetail", data: {"jobName": jobName, "jobGroup": jobGroup}, success: function (data) { if(data){ location.reload(); }else{ alert("删除失败。"); } }, fail: function () { alert("操作失败。"); } }); } } function triggerJob(jobName, jobGroup) { if(confirm("是否立即运行一次。")){ $.ajax({ type: "POST", url: "/quartz/triggerJob", data: {"jobName": jobName, "jobGroup": jobGroup}, success: function (data) { if(data){ location.reload(); }else{ alert("删除失败。"); } }, fail: function () { alert("操作失败。"); } }); } } function deleteJob(triggerName, triggerGroup) { if(confirm("是否删除该任务。")){ $.ajax({ type: "POST", url: "/quartz/deleteJob", data: {"triggerName": triggerName, "triggerGroup": triggerGroup}, success: function (data) { if(data){ location.reload(); }else{ alert("删除失败。"); } }, fail: function () { alert("操作失败。"); } }); } } function resumeJob(triggerName, triggerGroup) { $.ajax({ type: "POST", url: "/quartz/resumeJob", data: {"triggerName": triggerName, "triggerGroup": triggerGroup}, success: function (data) { if(data){ location.reload(); }else{ alert("恢复失败。"); } }, fail: function () { alert("操作失败。"); } }); } function pauseJob(triggerName, triggerGroup) { $.ajax({ type: "POST", url: "/quartz/pauseJob", data: {"triggerName": triggerName, "triggerGroup": triggerGroup}, success: function (data) { if(data){ location.reload(); }else{ alert("暂停失败。"); } }, fail: function () { alert("操作失败。"); } }); } </script> </body> </html>
Controllere代码:
package com.sky.controller; import com.sky.bean.JobDetailManage; import com.sky.bean.JobEntity; import com.sky.util.QuartzManager; import com.sky.util.QuartzUtils; import com.sun.istack.internal.NotNull; import org.quartz.*; import org.quartz.impl.StdSchedulerFactory; import org.quartz.impl.matchers.GroupMatcher; import org.springframework.scheduling.quartz.QuartzJobBean; import org.springframework.stereotype.Controller; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.ResponseBody; import org.springframework.web.servlet.ModelAndView; import java.util.ArrayList; import java.util.List; import java.util.Set; /** * @author sky */ @Controller public class JobController { private Scheduler quartzScheduler; @RequestMapping("/quartz") public ModelAndView quartz() { ModelAndView mv = new ModelAndView(); mv.setViewName("quartz"); JobKey jobKey = JobKey.jobKey("hello4", "group1"); try { mv.addObject("jobInfos", getSchedulerJobInfo()); mv.addObject("jobDetailInfos", getJobDetailInfos()); mv.addObject("jobDetailInfo", getJobDetailInfo(jobKey)); } catch (SchedulerException e) { } return mv; } //暂停任务 @PostMapping("quartz/pauseJob") @ResponseBody public boolean pauseJob(@NotNull String triggerName, @NotNull String triggerGroup) { TriggerKey triggerKey = TriggerKey.triggerKey(triggerName, triggerGroup); try { quartzScheduler.pauseTrigger(triggerKey); } catch (SchedulerException e) { System.out.println(e.getMessage()); } return true; } //恢复任务 @PostMapping("quartz/resumeJob") @ResponseBody public boolean resumeJob(@NotNull String triggerName, @NotNull String triggerGroup) throws Exception { TriggerKey triggerKey = TriggerKey.triggerKey(triggerName, triggerGroup); quartzScheduler.resumeTrigger(triggerKey); return true; } //删除任务 @PostMapping("quartz/deleteJob") @ResponseBody public boolean deleteJob(@NotNull String triggerName, @NotNull String triggerGroup) throws Exception { JobKey jobKey = JobKey.jobKey(triggerName, triggerGroup); TriggerKey triggerKey = TriggerKey.triggerKey(triggerName, triggerGroup); quartzScheduler.pauseTrigger(triggerKey); return quartzScheduler.unscheduleJob(triggerKey); } //立即运行任务 @PostMapping("quartz/triggerJob") @ResponseBody public boolean triggerJob(@NotNull String jobName, @NotNull String jobGroup) throws Exception { JobKey jobKey = JobKey.jobKey(jobName, jobGroup); quartzScheduler.triggerJob(jobKey); return true; } //删除任务详情 @PostMapping("quartz/deleteJobDetail") @ResponseBody public boolean deleteJobDetail(@NotNull String jobName, @NotNull String jobGroup) throws Exception { JobKey jobKey = JobKey.jobKey(jobName, jobGroup); List<Trigger> triggers = (List<Trigger>) quartzScheduler.getTriggersOfJob(jobKey); if (triggers.size() > 0) { return false; } return quartzScheduler.deleteJob(jobKey); } // @SuppressWarnings("unchecked") private List<JobEntity> getSchedulerJobInfo() throws SchedulerException { SchedulerFactory schedulerFactory = new StdSchedulerFactory(); quartzScheduler = schedulerFactory.getScheduler(); List<JobEntity> jobInfos = new ArrayList<JobEntity>(); List<String> triggerGroupNames = quartzScheduler.getTriggerGroupNames(); for (String triggerGroupName : triggerGroupNames) { Set<TriggerKey> triggerKeySet = quartzScheduler.getTriggerKeys(GroupMatcher.triggerGroupEquals(triggerGroupName)); for (TriggerKey triggerKey : triggerKeySet) { Trigger t = quartzScheduler.getTrigger(triggerKey); JobEntity jobInfo = new JobEntity(); if (t instanceof CronTrigger) { CronTrigger trigger = (CronTrigger) t; jobInfo.setCronExpr(trigger.getCronExpression()); } else if (t instanceof SimpleTrigger) { SimpleTrigger trigger = (SimpleTrigger) t; jobInfo.setCronExpr(trigger.getRepeatCount() + 1 + "-" + trigger.getTimesTriggered() + "(" + trigger.getRepeatInterval() + ")"); } JobKey jobKey = t.getJobKey(); JobDetail jd = quartzScheduler.getJobDetail(jobKey); jobInfo.setJobName(jobKey.getName()); jobInfo.setJobGroup(jobKey.getGroup()); jobInfo.setTriggerName(triggerKey.getName()); jobInfo.setTriggerGroupName(triggerKey.getGroup()); jobInfo.setNextFireTime(t.getNextFireTime()); jobInfo.setPreviousFireTime(t.getPreviousFireTime()); jobInfo.setStartTime(t.getStartTime()); jobInfo.setEndTime(t.getEndTime()); jobInfo.setJobClass(jd.getJobClass().getCanonicalName()); jobInfo.setDescription(t.getDescription()); Trigger.TriggerState triggerState = quartzScheduler.getTriggerState(t.getKey()); jobInfo.setJobStatus(triggerState.toString());// NONE无, NORMAL正常, PAUSED暂停, COMPLETE完全, ERROR错误, BLOCKED阻塞 JobDataMap map = quartzScheduler.getJobDetail(jobKey).getJobDataMap(); // jobInfo.setCount(Integer.parseInt((String) map.get("count"))); if (null != map) { jobInfo.setJobDataMap(map); } else { jobInfo.setJobDataMap(new JobDataMap()); } jobInfos.add(jobInfo); } } return jobInfos; } // @SuppressWarnings("unchecked") private List<JobDetailManage> getJobDetailInfos() throws SchedulerException { List<JobDetailManage> jobDetailInfos = new ArrayList<JobDetailManage>(); for (String groupName : quartzScheduler.getJobGroupNames()) { for (JobKey jobKey : quartzScheduler.getJobKeys(GroupMatcher.jobGroupEquals(groupName))) { jobDetailInfos.add(getJobDetailInfo(jobKey)); } } return jobDetailInfos; } // @SuppressWarnings("unchecked") private JobDetailManage getJobDetailInfo(JobKey jobKey) throws SchedulerException { JobDetail jobDetail = quartzScheduler.getJobDetail(jobKey); List<Trigger> triggers = (List<Trigger>) quartzScheduler.getTriggersOfJob(jobKey); if(jobDetail != null){ JobDetailManage jobDetailManage = new JobDetailManage(); jobDetailManage.setJobName(jobKey.getName()); jobDetailManage.setJobGroup(jobKey.getGroup()); jobDetailManage.setDescription(jobDetail.getDescription()); jobDetailManage.setDuration(jobDetail.isDurable()); jobDetailManage.setJobClass(jobDetail.getJobClass().getCanonicalName()); jobDetailManage.setJobDataMap(jobDetail.getJobDataMap()); jobDetailManage.setPersistJobDataAfterExecution(jobDetail.isPersistJobDataAfterExecution()); jobDetailManage.setConcurrentExectionDisallowed(jobDetail.isConcurrentExectionDisallowed()); jobDetailManage.setRequestsRecovery(jobDetail.requestsRecovery()); jobDetailManage.setTriggerCount(triggers.size()); return jobDetailManage; } return null; } }
JobDetailManage类:
package com.sky.bean; import org.quartz.JobDataMap; /** * @author sky */ public class JobDetailManage { private String jobName; private String jobGroup; private String description; private Boolean duration; private String jobClass; private JobDataMap jobDataMap; private Boolean concurrentExectionDisallowed; private Boolean persistJobDataAfterExecution; private Boolean requestsRecovery; private Integer triggerCount; //get set }
JobEntity类:
package com.sky.bean; import org.quartz.JobDataMap; import java.util.Date; /** * 调度器信息 * * @author sky */ public class JobEntity { private int jobId; private String jobType; private String jobGroup; private String jobName; private String triggerName; private String triggerGroupName; private String cronExpr; private String description; private Date previousFireTime; private Date nextFireTime; private String jobStatus; private Date startTime; private Date endTime; private Date reStartTime; private String jobClass; private int count; private JobDataMap jobDataMap; //get set }