Spring+Quartz实现动态添加定时任务
概述
详细
一、准备工作
1.java环境搭建,具体参考包中的webapp/resources/doc/平台开发环境安装Guide_V1.0.docx文档
2.使用源码中的webapp/resources/doc/init.sql初始化表结构及数据
t_timetask 任务表
t_timetask_log 任务运行日志
3.数据库连接配置在cloud_parent中的pom.xml中,数据库名称ffxl_cloud,默认账号root,密码123456,同样可在pom.xml中修改
4.运行quartz项目,此处注意,使用的端口号需要与platform_parent下pom.xml中的quartz.job.url的一致,程序中用的是8080端口,具体使用哪个配置,请参考maven中profiles的使用
5.运行admin项目,注意,此处端口要与quartz不同,程序中用的是80端口,浏览器中输入http://localhost/admin 运行结果如图:
二、代码引入
1、文件引入顺序:lib_parent → platform_parent → cloud_parent
2、代码结构
三、程序实现
quartz项目部分代码
1.quartz项目启动时,初始化数据库中的定时任务
package com.ffxl.quartz.init; import java.util.ArrayList; import java.util.List; import org.quartz.CronScheduleBuilder; import org.quartz.CronTrigger; import org.quartz.JobBuilder; import org.quartz.JobDetail; import org.quartz.Scheduler; import org.quartz.SchedulerException; import org.quartz.TriggerBuilder; import org.quartz.TriggerKey; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.BeansException; import org.springframework.context.ApplicationContext; import org.springframework.context.ApplicationContextAware; import org.springframework.scheduling.quartz.SchedulerFactoryBean; import com.ffxl.cloud.model.STimetask; import com.ffxl.cloud.model.STimetaskExample; import com.ffxl.cloud.model.base.BaseSTimetaskExample.Criteria; import com.ffxl.cloud.model.warpper.ScheduleJob; import com.ffxl.cloud.service.STimetaskService; import com.ffxl.quartz.task.util.QuartzJobFactory; import com.ffxl.quartz.task.util.QuartzJobFactoryDisallowConcurrentExecution; /** * 根据上下文获取spring类 * * @author */ public class InitQuartzJob implements ApplicationContextAware{ private static final Logger logger = LoggerFactory.getLogger(InitQuartzJob.class); private static ApplicationContext appCtx; public static SchedulerFactoryBean schedulerFactoryBean = null; @Override public void setApplicationContext(ApplicationContext applicationContext) throws BeansException { if (this.appCtx == null) { this.appCtx = applicationContext; } } public static void init() { schedulerFactoryBean = (SchedulerFactoryBean) appCtx.getBean(SchedulerFactoryBean.class); Scheduler scheduler = schedulerFactoryBean.getScheduler(); try { logger.info(scheduler.getSchedulerName()); } catch (SchedulerException e1) { // TODO Auto-generated catch block e1.printStackTrace(); } // 这里从数据库中获取任务信息数据 STimetaskService sTimetaskService = (STimetaskService) appCtx.getBean(STimetaskService.class); STimetaskExample example = new STimetaskExample(); Criteria c = example.createCriteria(); c.andJobStatusEqualTo("1"); // 已发布的定时任务 List<STimetask> list = sTimetaskService.selectByExample(example); List<ScheduleJob> jobList = new ArrayList<ScheduleJob>(); for (STimetask sTimetask : list) { ScheduleJob job1 = new ScheduleJob(); job1.setJobId(sTimetask.getId()); job1.setJobGroup(sTimetask.getGroupName()); // 任务组 job1.setJobName(sTimetask.getName());// 任务名称 job1.setJobStatus(sTimetask.getJobStatus()); // 任务发布状态 job1.setIsConcurrent(sTimetask.getConcurrent() ? "1" : "0"); // 运行状态 job1.setCronExpression(sTimetask.getCron()); job1.setBeanClass(sTimetask.getBeanName());// 一个以所给名字注册的bean的实例 job1.setMethodName(sTimetask.getMethodName()); job1.setJobData(sTimetask.getJobData()); // 参数 jobList.add(job1); } for (ScheduleJob job : jobList) { try { addJob(job); } catch (SchedulerException e) { // TODO Auto-generated catch block e.printStackTrace(); } } } /** * 添加任务 * * @param scheduleJob * @throws SchedulerException */ public static void addJob(ScheduleJob job) throws SchedulerException { if (job == null || !ScheduleJob.STATUS_RUNNING.equals(job.getJobStatus())) { return; } Scheduler scheduler = schedulerFactoryBean.getScheduler(); logger.debug(scheduler + "...........................................add"); TriggerKey triggerKey = TriggerKey.triggerKey(job.getJobName(), job.getJobGroup()); CronTrigger trigger = (CronTrigger) scheduler.getTrigger(triggerKey); // 不存在,创建一个 if (null == trigger) { Class clazz = ScheduleJob.CONCURRENT_IS.equals(job.getIsConcurrent()) ? QuartzJobFactory.class : QuartzJobFactoryDisallowConcurrentExecution.class; JobDetail jobDetail = JobBuilder.newJob(clazz).withIdentity(job.getJobName(), job.getJobGroup()).usingJobData("data", job.getJobData()).build(); jobDetail.getJobDataMap().put("scheduleJob", job); CronScheduleBuilder scheduleBuilder = CronScheduleBuilder.cronSchedule(job.getCronExpression()); trigger = TriggerBuilder.newTrigger().withDescription(job.getJobId().toString()).withIdentity(job.getJobName(), job.getJobGroup()) .withSchedule(scheduleBuilder).build(); scheduler.scheduleJob(jobDetail, trigger); } else { // Trigger已存在,那么更新相应的定时设置 CronScheduleBuilder scheduleBuilder = CronScheduleBuilder.cronSchedule(job.getCronExpression()); // 按新的cronExpression表达式重新构建trigger trigger = trigger.getTriggerBuilder().withIdentity(triggerKey).usingJobData("data", job.getJobData()).withSchedule(scheduleBuilder).build(); // 按新的trigger重新设置job执行 scheduler.rescheduleJob(triggerKey, trigger); } } }
2.提供job对应的操作服务
package com.ffxl.quartz.task; import java.io.BufferedInputStream; import java.io.IOException; import java.io.PrintWriter; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Set; import javax.servlet.ServletInputStream; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import net.sf.json.JSONArray; import org.apache.log4j.Logger; import org.quartz.CronScheduleBuilder; import org.quartz.CronTrigger; import org.quartz.JobDetail; import org.quartz.JobExecutionContext; import org.quartz.JobKey; import org.quartz.Scheduler; import org.quartz.SchedulerException; import org.quartz.Trigger; import org.quartz.TriggerKey; import org.quartz.impl.matchers.GroupMatcher; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.scheduling.quartz.SchedulerFactoryBean; import org.springframework.stereotype.Component; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.ResponseBody; import com.alibaba.fastjson.JSONObject; import com.ffxl.cloud.annotation.ControllerLogAnnotation; import com.ffxl.cloud.model.STimetask; import com.ffxl.cloud.model.warpper.ScheduleJob; import com.ffxl.platform.util.JsonResult; import com.ffxl.platform.util.StringUtil; import com.ffxl.quartz.init.InitQuartzJob; @Component @RequestMapping(value = "/opt") public class JobSerlvet { public final Logger log = Logger.getLogger(this.getClass()); @Autowired private SchedulerFactoryBean schedulerFactoryBean; /** * 获取所有计划中的任务列表 * * @return * @throws SchedulerException * @throws IOException */ @RequestMapping(value="/getAllJob") @ResponseBody @ControllerLogAnnotation(description = "获取所有计划中的任务列表") public void getAllJob(HttpServletRequest request,HttpServletResponse response) throws SchedulerException, IOException { Scheduler scheduler = schedulerFactoryBean.getScheduler(); GroupMatcher<JobKey> matcher = GroupMatcher.anyJobGroup(); Set<JobKey> jobKeys = scheduler.getJobKeys(matcher); List<ScheduleJob> jobList = new ArrayList<ScheduleJob>(); for (JobKey jobKey : jobKeys) { List<? extends Trigger> triggers = scheduler.getTriggersOfJob(jobKey); for (Trigger trigger : triggers) { ScheduleJob job = new ScheduleJob(); job.setJobId(trigger.getDescription());//description 放的是job的id job.setJobName(jobKey.getName()); job.setJobGroup(jobKey.getGroup()); job.setDescription("触发器:" + trigger.getKey()); Trigger.TriggerState triggerState = scheduler.getTriggerState(trigger.getKey()); job.setJobStatus(triggerState.name()); if (trigger instanceof CronTrigger) { CronTrigger cronTrigger = (CronTrigger) trigger; String cronExpression = cronTrigger.getCronExpression(); job.setCronExpression(cronExpression); } jobList.add(job); } } //输出 if(jobList.size() >0){ JSONArray listArray=JSONArray.fromObject(jobList); Map<String,Object> m =new HashMap<String, Object>(); m.put("job", listArray); response.setHeader("Content-type", "text/html;charset=UTF-8"); response.setCharacterEncoding("UTF-8"); PrintWriter out = response.getWriter(); out.write("{\"code\":\"2000\",\"message\":\"成功\",\"data\":"+m+"}"); out.close(); }else{ response.setHeader("Content-type", "text/html;charset=UTF-8"); response.setCharacterEncoding("UTF-8"); PrintWriter out = response.getWriter(); out.write("{\"code\":\"5000\",\"message\":\"没有计划任务\"}"); out.close(); } } /** * 所有正在运行的job * * @return * @throws SchedulerException * @throws IOException */ @RequestMapping(value="/getRunningJob") @ResponseBody public void getRunningJob(HttpServletRequest request,HttpServletResponse response) throws SchedulerException, IOException { Scheduler scheduler = schedulerFactoryBean.getScheduler(); List<JobExecutionContext> executingJobs = scheduler.getCurrentlyExecutingJobs(); List<ScheduleJob> jobList = new ArrayList<ScheduleJob>(executingJobs.size()); for (JobExecutionContext executingJob : executingJobs) { ScheduleJob job = new ScheduleJob(); JobDetail jobDetail = executingJob.getJobDetail(); JobKey jobKey = jobDetail.getKey(); Trigger trigger = executingJob.getTrigger(); job.setJobName(jobKey.getName()); job.setJobGroup(jobKey.getGroup()); job.setDescription("触发器:" + trigger.getKey()); Trigger.TriggerState triggerState = scheduler.getTriggerState(trigger.getKey()); job.setJobStatus(triggerState.name()); if (trigger instanceof CronTrigger) { CronTrigger cronTrigger = (CronTrigger) trigger; String cronExpression = cronTrigger.getCronExpression(); job.setCronExpression(cronExpression); } jobList.add(job); } //输出 if(jobList.size() >0){ JSONArray listArray=JSONArray.fromObject(jobList); Map<String,Object> m =new HashMap<String, Object>(); m.put("job", listArray); response.setHeader("Content-type", "text/html;charset=UTF-8"); response.setCharacterEncoding("UTF-8"); PrintWriter out = response.getWriter(); out.write("{\"code\":\"2000\",\"message\":\"成功\",\"data\":"+m+"}"); out.close(); }else{ response.setHeader("Content-type", "text/html;charset=UTF-8"); response.setCharacterEncoding("UTF-8"); PrintWriter out = response.getWriter(); out.write("{\"code\":\"5000\",\"message\":\"没有正在执行的任务\"}"); out.close(); } } /** * 添加任务 * * @param * @throws SchedulerException * @throws IOException */ @RequestMapping(value="/addJob") @ResponseBody public void addJob(HttpServletRequest request,HttpServletResponse response) throws SchedulerException, IOException { StringBuffer info=new StringBuffer(); ServletInputStream in = request.getInputStream(); BufferedInputStream buf = new BufferedInputStream(in); byte[] buffer=new byte[1024]; int iRead; while((iRead=buf.read(buffer))!=-1){ info.append(new String(buffer,0,iRead,"UTF-8")); } // 释放资源 buf.close(); in.close(); ScheduleJob job = new ScheduleJob(); if(info!=null&&!StringUtil.isEmpty(info.toString())){ JSONObject json = JSONObject.parseObject(info.toString()); STimetask sTimetask = JSONObject.toJavaObject(json, STimetask.class); if(sTimetask !=null){ job.setJobId(sTimetask.getId()); job.setJobGroup(sTimetask.getGroupName()); //任务组 job.setJobName(sTimetask.getName());// 任务名称 job.setJobStatus(sTimetask.getJobStatus()); // 任务发布状态 job.setIsConcurrent(sTimetask.getConcurrent()?"1":"0"); // 运行状态 job.setCronExpression(sTimetask.getCron()); job.setBeanClass(sTimetask.getBeanName());// 一个以所给名字注册的bean的实例 job.setMethodName(sTimetask.getMethodName()); job.setJobData(sTimetask.getJobData()); //参数 } } InitQuartzJob.addJob(job); //输入 response.setHeader("Content-type", "text/html;charset=UTF-8"); response.setCharacterEncoding("UTF-8"); PrintWriter out = response.getWriter(); out.write("{\"code\":\"2000\",\"message\":\"成功\"}"); out.close(); } /** * 暂停一个job * * @param scheduleJob * @throws SchedulerException * @throws IOException */ @RequestMapping(value="/pauseJob") @ResponseBody public void pauseJob(HttpServletRequest request,HttpServletResponse response) throws SchedulerException, IOException { StringBuffer info=new StringBuffer(); ServletInputStream in = request.getInputStream(); BufferedInputStream buf = new BufferedInputStream(in); byte[] buffer=new byte[1024]; int iRead; while((iRead=buf.read(buffer))!=-1){ info.append(new String(buffer,0,iRead,"UTF-8")); } // 释放资源 buf.close(); in.close(); if(info!=null&&!StringUtil.isEmpty(info.toString())){ JSONObject json = JSONObject.parseObject(info.toString()); STimetask sTimetask = JSONObject.toJavaObject(json, STimetask.class); if(sTimetask !=null){ Scheduler scheduler = schedulerFactoryBean.getScheduler(); JobKey jobKey = JobKey.jobKey(sTimetask.getName(), sTimetask.getGroupName()); scheduler.pauseJob(jobKey); //输出 response.setHeader("Content-type", "text/html;charset=UTF-8"); response.setCharacterEncoding("UTF-8"); PrintWriter out = response.getWriter(); out.write("{\"code\":\"2000\",\"message\":\"成功\"}"); out.close(); }else{ //输出 response.setHeader("Content-type", "text/html;charset=UTF-8"); response.setCharacterEncoding("UTF-8"); PrintWriter out = response.getWriter(); out.write("{\"code\":\"5000\",\"message\":\"任务不存在\"}"); out.close(); } } } /** * 恢复一个job * * @param scheduleJob * @throws SchedulerException * @throws IOException */ @RequestMapping(value="/resumeJob") @ResponseBody public void resumeJob(HttpServletRequest request,HttpServletResponse response) throws SchedulerException, IOException { StringBuffer info=new StringBuffer(); ServletInputStream in = request.getInputStream(); BufferedInputStream buf = new BufferedInputStream(in); byte[] buffer=new byte[1024]; int iRead; while((iRead=buf.read(buffer))!=-1){ info.append(new String(buffer,0,iRead,"UTF-8")); } // 释放资源 buf.close(); in.close(); if(info!=null&&!StringUtil.isEmpty(info.toString())){ JSONObject json = JSONObject.parseObject(info.toString()); STimetask sTimetask = JSONObject.toJavaObject(json, STimetask.class); if(sTimetask !=null){ Scheduler scheduler = schedulerFactoryBean.getScheduler(); JobKey jobKey = JobKey.jobKey(sTimetask.getName(), sTimetask.getGroupName()); scheduler.resumeJob(jobKey); //输出 response.setHeader("Content-type", "text/html;charset=UTF-8"); response.setCharacterEncoding("UTF-8"); PrintWriter out = response.getWriter(); out.write("{\"code\":\"2000\",\"message\":\"成功\"}"); out.close(); }else{ //输出 response.setHeader("Content-type", "text/html;charset=UTF-8"); response.setCharacterEncoding("UTF-8"); PrintWriter out = response.getWriter(); out.write("{\"code\":\"5000\",\"message\":\"任务不存在\"}"); out.close(); } } } /** * 删除一个job * * @param scheduleJob * @throws SchedulerException * @throws IOException */ @RequestMapping(value="/deleteJob") @ResponseBody public void deleteJob(HttpServletRequest request,HttpServletResponse response) throws SchedulerException, IOException { StringBuffer info=new StringBuffer(); ServletInputStream in = request.getInputStream(); BufferedInputStream buf = new BufferedInputStream(in); byte[] buffer=new byte[1024]; int iRead; while((iRead=buf.read(buffer))!=-1){ info.append(new String(buffer,0,iRead,"UTF-8")); } // 释放资源 buf.close(); in.close(); if(info!=null&&!StringUtil.isEmpty(info.toString())){ JSONObject json = JSONObject.parseObject(info.toString()); STimetask sTimetask = JSONObject.toJavaObject(json, STimetask.class); if(sTimetask !=null){ Scheduler scheduler = schedulerFactoryBean.getScheduler(); JobKey jobKey = JobKey.jobKey(sTimetask.getName(), sTimetask.getGroupName()); scheduler.deleteJob(jobKey); //输出 response.setHeader("Content-type", "text/html;charset=UTF-8"); response.setCharacterEncoding("UTF-8"); PrintWriter out = response.getWriter(); out.write("{\"code\":\"2000\",\"message\":\"成功\"}"); out.close(); }else{ //输出 response.setHeader("Content-type", "text/html;charset=UTF-8"); response.setCharacterEncoding("UTF-8"); PrintWriter out = response.getWriter(); out.write("{\"code\":\"5000\",\"message\":\"任务不存在\"}"); out.close(); } } } /** * 立即执行job * * @param scheduleJob * @throws SchedulerException * @throws IOException */ @RequestMapping(value="/runAJobNow") @ResponseBody public void runAJobNow(HttpServletRequest request,HttpServletResponse response) throws SchedulerException, IOException { StringBuffer info=new StringBuffer(); ServletInputStream in = request.getInputStream(); BufferedInputStream buf = new BufferedInputStream(in); byte[] buffer=new byte[1024]; int iRead; while((iRead=buf.read(buffer))!=-1){ info.append(new String(buffer,0,iRead,"UTF-8")); } // 释放资源 buf.close(); in.close(); if(info!=null&&!StringUtil.isEmpty(info.toString())){ JSONObject json = JSONObject.parseObject(info.toString()); STimetask sTimetask = JSONObject.toJavaObject(json, STimetask.class); if(sTimetask !=null){ Scheduler scheduler = schedulerFactoryBean.getScheduler(); JobKey jobKey = JobKey.jobKey(sTimetask.getName(), sTimetask.getGroupName()); scheduler.triggerJob(jobKey); //输出 response.setHeader("Content-type", "text/html;charset=UTF-8"); response.setCharacterEncoding("UTF-8"); PrintWriter out = response.getWriter(); out.write("{\"code\":\"2000\",\"message\":\"成功\"}"); out.close(); }else{ //输出 response.setHeader("Content-type", "text/html;charset=UTF-8"); response.setCharacterEncoding("UTF-8"); PrintWriter out = response.getWriter(); out.write("{\"code\":\"5000\",\"message\":\"任务不存在\"}"); out.close(); } } } /** * 更新job时间表达式 * * @param scheduleJob * @throws SchedulerException * @throws IOException */ @RequestMapping(value="/updateJobCron") @ResponseBody public void updateJobCron(HttpServletRequest request,HttpServletResponse response) throws SchedulerException{ try { StringBuffer info=new StringBuffer(); ServletInputStream in; in = request.getInputStream(); BufferedInputStream buf = new BufferedInputStream(in); byte[] buffer=new byte[1024]; int iRead; while((iRead=buf.read(buffer))!=-1){ info.append(new String(buffer,0,iRead,"UTF-8")); } // 释放资源 buf.close(); in.close(); if(info!=null&&!StringUtil.isEmpty(info.toString())){ JSONObject json = JSONObject.parseObject(info.toString()); STimetask sTimetask = JSONObject.toJavaObject(json, STimetask.class); if(sTimetask !=null){ Scheduler scheduler = schedulerFactoryBean.getScheduler(); JobKey jobKey = JobKey.jobKey(sTimetask.getName(), sTimetask.getGroupName()); TriggerKey triggerKey = TriggerKey.triggerKey(sTimetask.getName(), sTimetask.getGroupName()); CronTrigger trigger = (CronTrigger) scheduler.getTrigger(triggerKey); CronScheduleBuilder scheduleBuilder = CronScheduleBuilder.cronSchedule(sTimetask.getCron()); trigger = trigger.getTriggerBuilder().withIdentity(triggerKey).withSchedule(scheduleBuilder).build(); scheduler.rescheduleJob(triggerKey, trigger); //输出 response.setHeader("Content-type", "text/html;charset=UTF-8"); response.setCharacterEncoding("UTF-8"); PrintWriter out = response.getWriter(); out.write("{\"code\":\"2000\",\"message\":\"成功\"}"); out.close(); }else{ //输出 response.setHeader("Content-type", "text/html;charset=UTF-8"); response.setCharacterEncoding("UTF-8"); PrintWriter out = response.getWriter(); out.write("{\"code\":\"5000\",\"message\":\"任务不存在\"}"); out.close(); } } }catch (IOException e) { // TODO Auto-generated catch block e.printStackTrace(); } } }
3.编写 定时清除timeTaskLog 7天之前的记录
@Component public class TimeTaskLogDispatchController { private static final Logger LOGGER = Logger.getLogger(TimeTaskLogDispatchController.class); /** * 定时清除timeTaskLog 7天之前的记录 */ public void deleteTimeTaskLog(String data) { LOGGER.info("【定时清除timeTaskLog 7天之前的记录】>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>开始执行"); STimetaskLogService bActiveService = (STimetaskLogService) ApplicationContextUtils.getBean(STimetaskLogService.class); Date currentDate = new Date(); int day = -7; Date deleteDate = DateUtil.getAfterNumDay(currentDate, day); int ret = bActiveService.deleteLog(deleteDate); if(ret >0){ LOGGER.info("【定时清除timeTaskLog 7天之前的记录】>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>执行成功"); }else{ LOGGER.info("【定时清除timeTaskLog 7天之前的记录】>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>执行失败"); } } }
4.dispatcher-servlet.xml中添加如下配置
<!-- 初始化springUtils --> <bean id="springUtils" class="com.ffxl.quartz.task.util.SpringUtils" /> <!-- 初始化Scheduler --> <bean id="schedulerFactoryBean" class="org.springframework.scheduling.quartz.SchedulerFactoryBean" /> <!-- 初始化job --> <bean id="initQuartzJob" class="com.ffxl.quartz.init.InitQuartzJob" init-method="init" lazy-init="false" />
admin项目部分代码
1.可视化项目的controller层
package com.ffxl.admin.controller.task; import java.text.DateFormat; import java.text.SimpleDateFormat; import java.util.ArrayList; import java.util.Date; import java.util.HashMap; import java.util.List; import java.util.Map; import javax.servlet.http.HttpSession; import net.sf.json.JSONArray; import net.sf.json.JSONObject; import net.sf.json.JsonConfig; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.propertyeditors.CustomDateEditor; import org.springframework.stereotype.Controller; import org.springframework.web.bind.WebDataBinder; import org.springframework.web.bind.annotation.InitBinder; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.ResponseBody; import org.springframework.web.servlet.ModelAndView; import com.ffxl.admin.controller.BaseController; import com.ffxl.admin.util.DataTablesUtil; import com.ffxl.cloud.model.STimetask; import com.ffxl.cloud.model.warpper.ScheduleJob; import com.ffxl.cloud.service.STimetaskService; import com.ffxl.platform.constant.Const; import com.ffxl.platform.core.Page; import com.ffxl.platform.exception.BusinessException; import com.ffxl.platform.util.CronUtil; import com.ffxl.platform.util.DateUtil; import com.ffxl.platform.util.HttpConnectUtil; import com.ffxl.platform.util.JsonDateValueProcessor; import com.ffxl.platform.util.JsonResult; import com.ffxl.platform.util.Message; import com.ffxl.platform.util.StringUtil; /** * 定时任务 * @author wison * */ @Controller @RequestMapping(value = "/task") public class TimeTaskController extends BaseController { private static String JOB_URL = Const.QUARTZ_JOB_URL; private static String ALL_JOB = JOB_URL+"/opt/getAllJob"; //所有计划中的任务列表 private static String RUNNING_JOB = JOB_URL+"/opt/getRunningJob";//所有正在运行的job private static String ADD_JOB = JOB_URL+"/opt/addJob";//添加任务 private static String PAUSE_JOB =JOB_URL+ "/opt/pauseJob";//暂停一个job private static String RESUME_JOB = JOB_URL+"/opt/resumeJob";//恢复一个job private static String DELETE_JOB = JOB_URL+"/opt/deleteJob";//删除一个job private static String RUNA_JOB =JOB_URL+ "/opt/runAJobNow";//立即执行job private static String UPDATE_JOB = JOB_URL+"/opt/updateJobCron";//更新job时间表达式 private static final Logger logger = LoggerFactory.getLogger(TimeTaskController.class); @Autowired private STimetaskService stimetaskService; @InitBinder public void initBinder(WebDataBinder binder) { DateFormat fmt = new SimpleDateFormat(DateUtil.STANDARD_DATE_FORMAT_STR); CustomDateEditor dateEditor = new CustomDateEditor(fmt, true); binder.registerCustomEditor(Date.class, dateEditor); } /** * 列表页面跳转 * @return */ @RequestMapping(value="/list") public ModelAndView userList(STimetask task){ ModelAndView mv = this.getModelAndView(); mv.setViewName("system/timeTaskList"); return mv; } /** * 列表 * @return */ @RequestMapping(value="/task_list") @ResponseBody public JsonResult taskList(DataTablesUtil dataTables, STimetask task, Page page, HttpSession session){ List<STimetask> list = stimetaskService.selectByPage(task, page); // 查询task的运行状态 String result = HttpConnectUtil.httpRequest(RUNNING_JOB, Const.REQUEST_METHOD_POST, null); if(result!=null){ JSONObject jsonResult = JSONObject.fromObject(result); Map<String, ScheduleJob> map = new HashMap<String, ScheduleJob>(); if( jsonResult.get("code").equals("2000")){ JSONObject js = (JSONObject) jsonResult.get("data"); JSONArray dataArray = (JSONArray) js.get("job"); if(dataArray.size() > 0){ List<ScheduleJob> jobList = JSONArray.toList(dataArray,ScheduleJob.class); for(ScheduleJob job: jobList){ map.put(job.getJobId().toString(), job); } } } for(STimetask st: list){ if(map.containsKey(st.getId())){ st.setConcurrent(true); } } } // 查询task的计划状态 String planResult = HttpConnectUtil.httpRequest(ALL_JOB, Const.REQUEST_METHOD_POST, null); if(planResult!=null){ JSONObject jsonPlanResult = JSONObject.fromObject(planResult); Map<String, ScheduleJob> planMap = new HashMap<String, ScheduleJob>(); if(jsonPlanResult.get("code").equals("2000")){ JSONObject js = (JSONObject) jsonPlanResult.get("data"); JSONArray dataArray = (JSONArray) js.get("job"); if(dataArray.size() > 0){ List<ScheduleJob> jobList = JSONArray.toList(dataArray,ScheduleJob.class); for(ScheduleJob job: jobList){ planMap.put(job.getJobId().toString(), job); } } } for(STimetask st: list){ if(planMap.containsKey(st.getId())){ String status = planMap.get(st.getId()).getJobStatus(); st.setPlanStatus(status); } } } //返回dataTable所需数据 dataTables = this.getDataTables(page, dataTables, list); return new JsonResult("2000", dataTables); } /** * 立即执行一次job * 用于测试任务是否正确 * @param id * @return */ @RequestMapping(value="/run_task2job") @ResponseBody public JsonResult run_task2job(String id){ //查询task STimetask stimetask = stimetaskService.selectByPrimaryKey(id); JsonConfig jsonConfig = new JsonConfig(); jsonConfig.registerJsonValueProcessor(Date.class, new JsonDateValueProcessor()); JSONObject jsonArray = JSONObject.fromObject(stimetask,jsonConfig); String result = HttpConnectUtil.httpRequest(RUNA_JOB, Const.REQUEST_METHOD_POST, jsonArray.toString()); logger.info(result); if(result ==null){ return new JsonResult("5000", "定时项目未启动",null); }else{ return new JsonResult("2000", null); } } /** * 添加job到计划列表 * @param id * @return */ @RequestMapping(value="/add_task2job") @ResponseBody public JsonResult add_task2job(String id){ //查询task STimetask stimetask = stimetaskService.selectByPrimaryKey(id); JsonConfig jsonConfig = new JsonConfig(); jsonConfig.registerJsonValueProcessor(Date.class, new JsonDateValueProcessor()); JSONObject jsonArray = JSONObject.fromObject(stimetask,jsonConfig); String result = HttpConnectUtil.httpRequest(ADD_JOB, Const.REQUEST_METHOD_POST, jsonArray.toString()); logger.info(result); if(result ==null){ return new JsonResult("5000", "定时项目未启动",null); }else{ return new JsonResult("2000", null); } } /** * 从计划列表中暂停job * @param id * @return */ @RequestMapping(value="/stop_task2job") @ResponseBody public JsonResult stop_task2job(String id){ //查询task STimetask stimetask = stimetaskService.selectByPrimaryKey(id); JsonConfig jsonConfig = new JsonConfig(); jsonConfig.registerJsonValueProcessor(Date.class, new JsonDateValueProcessor()); JSONObject jsonArray = JSONObject.fromObject(stimetask,jsonConfig); String result = HttpConnectUtil.httpRequest(PAUSE_JOB, Const.REQUEST_METHOD_POST, jsonArray.toString()); logger.info(result); if(result ==null){ return new JsonResult("5000", "定时项目未启动",null); }else{ return new JsonResult("2000", null); } } /** * 从计划列表中移除job * @param id * @return */ @RequestMapping(value="/remove_task2job") @ResponseBody public JsonResult remove_task2job(String id){ //查询task STimetask stimetask = stimetaskService.selectByPrimaryKey(id); JsonConfig jsonConfig = new JsonConfig(); jsonConfig.registerJsonValueProcessor(Date.class, new JsonDateValueProcessor()); JSONObject jsonArray = JSONObject.fromObject(stimetask,jsonConfig); String result = HttpConnectUtil.httpRequest(DELETE_JOB, Const.REQUEST_METHOD_POST, jsonArray.toString()); logger.info(result); if(result ==null){ return new JsonResult("5000", "定时项目未启动",null); }else{ return new JsonResult("2000", null); } } /** * 变更job状态 * @param id * @return */ @RequestMapping(value="/update_task") @ResponseBody public JsonResult update_task(String ids,String type){ //查询task String[] idArray = ids.split(","); Map<String,String> selectedIdMap = new HashMap<String,String>(); List<String> idList = new ArrayList<String>(); for (int i = 0; i < idArray.length; i++) { idList.add(idArray[i]); } int ret = stimetaskService.updatebyOperate(idList,type); if(ret >0){ return new JsonResult(true); }else{ return new JsonResult(false); } } /** * 删除job * @param id * @return */ @RequestMapping(value="/delete_task") @ResponseBody public JsonResult delete_task(String ids){ //查询task String[] idArray = ids.split(","); Map<String,String> selectedIdMap = new HashMap<String,String>(); List<String> idList = new ArrayList<String>(); for (int i = 0; i < idArray.length; i++) { idList.add(idArray[i]); } int ret = stimetaskService.deleteByIds(idList); if(ret >0){ return new JsonResult(true); }else{ return new JsonResult(false); } } /** * 详情页面 * @return */ @RequestMapping(value="/task_detail") public ModelAndView detail(String id){ ModelAndView mv = this.getModelAndView(); STimetask model = new STimetask(); model = stimetaskService.selectByPrimaryKey(id); mv.addObject("model", model); mv.setViewName("system/timeTaskDetail"); return mv; } /** * 解析cron * @return */ @RequestMapping(value="/analysis_cron") @ResponseBody public JsonResult analysisCron(String cron){ try { Date date = new Date(); String dateStr = DateUtil.formatStandardDatetime(date); List<String> dateList = CronUtil.cronAlgBuNums(cron, dateStr, 5); return new JsonResult("2000", dateList); } catch (Exception e) { e.printStackTrace(); return new JsonResult("5000", null); } } /** * 验证名称是否存在 * @param id * @param groupName * @param name * @return * */ @RequestMapping(value="/check_name") @ResponseBody public Boolean check_name(String id, String groupName, String name){ if(StringUtil.isEmpty(groupName,name)){ throw new BusinessException(Message.M4003); } STimetask task = new STimetask(); task.setId(id); task.setGroupName(groupName); task.setName(name); STimetask queryTask = stimetaskService.checkName(task); if(queryTask !=null){ logger.debug("组.任务名 exists,return false"); return false; }else{ logger.debug("组.任务名 not exists,return true"); return true; } } /** * 保存 * @return */ @RequestMapping(value="/task_save") @ResponseBody public JsonResult userSave(STimetask task, HttpSession session){ //获取系统操作人员 String longName = "admin"; task.setModifyUserId(longName); try{ int ret= stimetaskService.insertOrUpdateByUser(task,longName); if(ret > 0){ return new JsonResult("2000",task); }else{ return new JsonResult("5000"); } }catch(BusinessException e){ return new JsonResult("5001",e.getMessage(),null); } } }
2.前端页面
可以看到在这里我把定时任务的状态分为两大类,任务状态跟业务有关,分为已发布和未发布;计划状态跟定时任务的运行有关,分为None,正常运行,已暂停,任务执行中,线程阻塞,未计划,错误
2.1计划状态
function formatPlan(value, rowData, rowIndex){ if(value=="None"){ return "<span class='text-danger'>None</span>" } if(value=="NORMAL"){ return "<span class='text-success'>正常运行</span>" } if(value=="PAUSED"){ return "<span class='text-yellow'>已暂停</span>" } if(value=="COMPLETE"){ return "<span class='text-success'>任务执行中</span>" } if(value=="BLOCKED"){ return "<span class='text-danger'>线程阻塞</span>" } if(value=="ERROR"){ return "<span class='text-danger'>错误</span>" }else{ return "<span class='text-danger'>未计划</span>" } }
2.2操作逻辑
function formatOpt(value, rowData, rowIndex) { var msg = ""; msg+="<a href='#' class='btnstyle' onclick='showDetail(\"" + rowData.id + "\")'>编辑</a>"; //已发布, if(rowData.jobStatus=='1'){ var value = rowData.planStatus; if(value=="None"|| value==null){ msg +='<a href="#"class="btnstyle" onclick="addJob(\''+rowData.id+'\')" ' +'onMouseOver="popTip(this,\' '+rowData+' \' )" class="btn btn-info btn-xs" data-toggle="tooltip"' +' data-placement="top" title="定时任务按照计划开始执行" >计划</a>'; } if(value=="NORMAL"){ msg += '<a href="#"class="btnstyle" onclick="runJob(\''+rowData.id+'\')" ' +'onMouseOver="popTip(this,\' '+rowData+' \' )" class="btn btn-info btn-xs" data-toggle="tooltip"' +' data-placement="top" title="紧执行一次" >立即执行</a>' + '<a href="#"class="btnstyle" onclick="stopJob(\''+rowData.id+'\')" ' +'onMouseOver="popTip(this,\' '+rowData+' \' )" class="btn btn-info btn-xs" data-toggle="tooltip"' +' data-placement="top" title="定时任务暂时停止执行" >暂停</a>' +'<a href="#"class="btnstyle" onclick="removeJob(\''+rowData.id+'\')" ' +'onMouseOver="popTip(this,\' '+rowData+' \' )" class="btn btn-info btn-xs" data-toggle="tooltip"' +' data-placement="top" title="定时任务从计划列表中移除" >移除</a>'; } if(value=="PAUSED"){ msg += '<a href="#"class="btnstyle" onclick="runJob(\''+rowData.id+'\')" ' +'onMouseOver="popTip(this,\' '+rowData+' \' )" class="btn btn-info btn-xs" data-toggle="tooltip"' +' data-placement="top" title="紧执行一次" >立即执行</a>' + '<a href="#"class="btnstyle" onclick="addJob(\''+rowData.id+'\')" ' +'onMouseOver="popTip(this,\' '+rowData+' \' )" class="btn btn-info btn-xs" data-toggle="tooltip"' +' data-placement="top" title="定时任务按照计划开始执行" >计划</a>' +'<a href="#"class="btnstyle" onclick="removeJob(\''+rowData.id+'\')" ' +'onMouseOver="popTip(this,\' '+rowData+' \' )" class="btn btn-info btn-xs" data-toggle="tooltip"' +' data-placement="top" title="定时任务从计划列表中移除" >移除</a>'; } if(value=="COMPLETE"){ msg += '<a href="#"class="btnstyle" onclick="runJob(\''+rowData.id+'\')" ' +'onMouseOver="popTip(this,\' '+rowData+' \' )" class="btn btn-info btn-xs" data-toggle="tooltip"' +' data-placement="top" title="紧执行一次" >立即执行</a>' } if(value=="BLOCKED"){ msg += '<a href="#"class="btnstyle" onclick="runJob(\''+rowData.id+'\')" ' +'onMouseOver="popTip(this,\' '+rowData+' \' )" class="btn btn-info btn-xs" data-toggle="tooltip"' +' data-placement="top" title="紧执行一次" >立即执行</a>' } if(value=="ERROR"){ } } return msg; }
简单概述为:已发布的定时任务出现【计划】按钮;执行【计划】后,定时任务正常运行,且出现【立即执行】【暂停】【移除】三个按钮,【立即执行】用于测试当前定时任务的业务逻辑是否正确,【暂停】很容易理解,就是把当前任务暂停,及时到达cron定义的时间,定时任务也不会执行,【移除】仅仅是把计划列表中相应的任务暂时清除掉,你可以理解为清除缓存中的定时任务,并不是物理删除
3.定时任务详情
3.点击规则弹出页面如下,可自行修改界面,这里我是从网上随便找了个插件,然后按照自己想要的修改下源码