diff --git a/ruoyi-common/src/main/java/com/ruoyi/common/config/Global.java b/ruoyi-common/src/main/java/com/ruoyi/common/config/Global.java index 2455b1dc..3b09d457 100644 --- a/ruoyi-common/src/main/java/com/ruoyi/common/config/Global.java +++ b/ruoyi-common/src/main/java/com/ruoyi/common/config/Global.java @@ -22,7 +22,7 @@ public class Global /** * 当前对象实例 */ - private static Global global = null; + private static Global global; /** * 保存全局属性值 @@ -34,18 +34,13 @@ public class Global } /** - * 静态工厂方法 获取当前对象实例 多线程安全单例模式(使用双重同步锁) + * 静态工厂方法 */ - public static synchronized Global getInstance() { if (global == null) { - synchronized (Global.class) - { - if (global == null) - global = new Global(); - } + global = new Global(); } return global; } diff --git a/ruoyi-common/src/main/java/com/ruoyi/common/constant/ScheduleConstants.java b/ruoyi-common/src/main/java/com/ruoyi/common/constant/ScheduleConstants.java index c2701f7d..b8203666 100644 --- a/ruoyi-common/src/main/java/com/ruoyi/common/constant/ScheduleConstants.java +++ b/ruoyi-common/src/main/java/com/ruoyi/common/constant/ScheduleConstants.java @@ -7,9 +7,10 @@ package com.ruoyi.common.constant; */ public interface ScheduleConstants { - public static final String TASK_CLASS_NAME = "__TASK_CLASS_NAME__"; + public static final String TASK_CLASS_NAME = "TASK_CLASS_NAME"; - public static final String TASK_PROPERTIES = "__TASK_PROPERTIES__"; + /** 执行目标key */ + public static final String TASK_PROPERTIES = "TASK_PROPERTIES"; /** 默认 */ public static final String MISFIRE_DEFAULT = "0"; diff --git a/ruoyi-common/src/main/java/com/ruoyi/common/json/JSONObject.java b/ruoyi-common/src/main/java/com/ruoyi/common/json/JSONObject.java index e4c27a4d..24ef6ccb 100644 --- a/ruoyi-common/src/main/java/com/ruoyi/common/json/JSONObject.java +++ b/ruoyi-common/src/main/java/com/ruoyi/common/json/JSONObject.java @@ -223,6 +223,7 @@ public class JSONObject extends LinkedHashMap { return endArray(matcher.group(1), matcher.group(2), new EndArrayCallback() { + @Override public Object callback(JSONArray arr, int index) { return elementAt(arr, index); @@ -257,6 +258,7 @@ public class JSONObject extends LinkedHashMap { endArray(matcher.group(1), matcher.group(2), new EndArrayCallback() { + @Override public Void callback(JSONArray arr, int index) { elementAt(arr, index, value); @@ -285,6 +287,7 @@ public class JSONObject extends LinkedHashMap { return endArray(matcher.group(1), matcher.group(2), new EndArrayCallback() { + @Override public JSONObject callback(JSONArray arr, int index) { return objAt(arr, index); diff --git a/ruoyi-common/src/main/java/com/ruoyi/common/utils/ExceptionUtil.java b/ruoyi-common/src/main/java/com/ruoyi/common/utils/ExceptionUtil.java new file mode 100644 index 00000000..5ca4cb85 --- /dev/null +++ b/ruoyi-common/src/main/java/com/ruoyi/common/utils/ExceptionUtil.java @@ -0,0 +1,41 @@ +package com.ruoyi.common.utils; + +import java.io.PrintWriter; +import java.io.StringWriter; +import org.apache.commons.lang3.exception.ExceptionUtils; + +/** + * 错误信息处理类。 + * + * @author ruoyi + */ +public class ExceptionUtil +{ + + /** + * 获取exception的详细错误信息。 + */ + public static String getExceptionMessage(Throwable e) + { + StringWriter sw = new StringWriter(); + e.printStackTrace(new PrintWriter(sw, true)); + String str = sw.toString(); + return str; + } + + public static String getRootErrorMseeage(Exception e) + { + Throwable root = ExceptionUtils.getRootCause(e); + root = (root == null ? e : root); + if (root == null) + { + return ""; + } + String msg = root.getMessage(); + if (msg == null) + { + return "null"; + } + return StringUtils.defaultString(msg); + } +} diff --git a/ruoyi-common/src/main/java/com/ruoyi/common/utils/YamlUtil.java b/ruoyi-common/src/main/java/com/ruoyi/common/utils/YamlUtil.java index e8e05b5f..bb8a4ed6 100644 --- a/ruoyi-common/src/main/java/com/ruoyi/common/utils/YamlUtil.java +++ b/ruoyi-common/src/main/java/com/ruoyi/common/utils/YamlUtil.java @@ -40,7 +40,7 @@ public class YamlUtil if (map != null && !map.isEmpty() && qualifiedKey != null) { String input = String.valueOf(qualifiedKey); - if (!input.equals("")) + if (!"".equals(input)) { if (input.contains(".")) { diff --git a/ruoyi-common/src/main/java/com/ruoyi/common/utils/poi/ExcelUtil.java b/ruoyi-common/src/main/java/com/ruoyi/common/utils/poi/ExcelUtil.java index a727aec9..ff5c45ad 100644 --- a/ruoyi-common/src/main/java/com/ruoyi/common/utils/poi/ExcelUtil.java +++ b/ruoyi-common/src/main/java/com/ruoyi/common/utils/poi/ExcelUtil.java @@ -647,7 +647,9 @@ public class ExcelUtil { tempClass = tempClass.getSuperclass(); if (tempClass != null) + { tempFields.addAll(Arrays.asList(tempClass.getDeclaredFields())); + } } putToFields(tempFields); } diff --git a/ruoyi-quartz/src/main/java/com/ruoyi/quartz/controller/SysJobController.java b/ruoyi-quartz/src/main/java/com/ruoyi/quartz/controller/SysJobController.java index 1e534400..3318f72a 100644 --- a/ruoyi-quartz/src/main/java/com/ruoyi/quartz/controller/SysJobController.java +++ b/ruoyi-quartz/src/main/java/com/ruoyi/quartz/controller/SysJobController.java @@ -2,6 +2,7 @@ package com.ruoyi.quartz.controller; import java.util.List; import org.apache.shiro.authz.annotation.RequiresPermissions; +import org.quartz.SchedulerException; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Controller; import org.springframework.ui.ModelMap; @@ -15,6 +16,7 @@ import com.ruoyi.common.core.controller.BaseController; import com.ruoyi.common.core.domain.AjaxResult; import com.ruoyi.common.core.page.TableDataInfo; import com.ruoyi.common.enums.BusinessType; +import com.ruoyi.common.exception.job.TaskException; import com.ruoyi.common.utils.poi.ExcelUtil; import com.ruoyi.quartz.domain.SysJob; import com.ruoyi.quartz.service.ISysJobService; @@ -65,18 +67,10 @@ public class SysJobController extends BaseController @RequiresPermissions("monitor:job:remove") @PostMapping("/remove") @ResponseBody - public AjaxResult remove(String ids) + public AjaxResult remove(String ids) throws SchedulerException { - try - { - jobService.deleteJobByIds(ids); - return success(); - } - catch (Exception e) - { - e.printStackTrace(); - return error(e.getMessage()); - } + jobService.deleteJobByIds(ids); + return success(); } @RequiresPermissions("monitor:job:detail") @@ -90,14 +84,12 @@ public class SysJobController extends BaseController /** * 任务调度状态修改 - * - * @throws Exception */ @Log(title = "定时任务", businessType = BusinessType.UPDATE) @RequiresPermissions("monitor:job:changeStatus") @PostMapping("/changeStatus") @ResponseBody - public AjaxResult changeStatus(SysJob job) + public AjaxResult changeStatus(SysJob job) throws SchedulerException { return toAjax(jobService.changeStatus(job)); } @@ -109,9 +101,10 @@ public class SysJobController extends BaseController @RequiresPermissions("monitor:job:changeStatus") @PostMapping("/run") @ResponseBody - public AjaxResult run(SysJob job) + public AjaxResult run(SysJob job) throws SchedulerException { - return toAjax(jobService.run(job)); + jobService.run(job); + return success(); } /** @@ -125,7 +118,6 @@ public class SysJobController extends BaseController /** * 新增保存调度 - * @throws Exception */ @Log(title = "定时任务", businessType = BusinessType.INSERT) @RequiresPermissions("monitor:job:add") @@ -148,13 +140,12 @@ public class SysJobController extends BaseController /** * 修改保存调度 - * @throws Exception */ @Log(title = "定时任务", businessType = BusinessType.UPDATE) @RequiresPermissions("monitor:job:edit") @PostMapping("/edit") @ResponseBody - public AjaxResult editSave(SysJob job ) throws Exception + public AjaxResult editSave(SysJob job) throws SchedulerException, TaskException { return toAjax(jobService.updateJobCron(job)); } diff --git a/ruoyi-quartz/src/main/java/com/ruoyi/quartz/domain/SysJob.java b/ruoyi-quartz/src/main/java/com/ruoyi/quartz/domain/SysJob.java index 807c85ea..be66d2e3 100644 --- a/ruoyi-quartz/src/main/java/com/ruoyi/quartz/domain/SysJob.java +++ b/ruoyi-quartz/src/main/java/com/ruoyi/quartz/domain/SysJob.java @@ -47,6 +47,9 @@ public class SysJob extends BaseEntity implements Serializable @Excel(name = "计划策略 ") private String misfirePolicy = ScheduleConstants.MISFIRE_DEFAULT; + /** 是否并发执行(0允许 1禁止) */ + private String concurrent; + /** 任务状态(0正常 1暂停) */ @Excel(name = "任务状态", readConverterExp = "0=正常,1=暂停") private String status; @@ -130,6 +133,16 @@ public class SysJob extends BaseEntity implements Serializable this.misfirePolicy = misfirePolicy; } + public String getConcurrent() + { + return concurrent; + } + + public void setConcurrent(String concurrent) + { + this.concurrent = concurrent; + } + public String getStatus() { return status; @@ -139,7 +152,7 @@ public class SysJob extends BaseEntity implements Serializable { this.status = status; } - + @Override public String toString() { return new ToStringBuilder(this,ToStringStyle.MULTI_LINE_STYLE) @@ -151,6 +164,7 @@ public class SysJob extends BaseEntity implements Serializable .append("cronExpression", getCronExpression()) .append("nextValidTime", getNextValidTime()) .append("misfirePolicy", getMisfirePolicy()) + .append("concurrent", getConcurrent()) .append("status", getStatus()) .append("createBy", getCreateBy()) .append("createTime", getCreateTime()) @@ -159,4 +173,4 @@ public class SysJob extends BaseEntity implements Serializable .append("remark", getRemark()) .toString(); } -} +} \ No newline at end of file diff --git a/ruoyi-quartz/src/main/java/com/ruoyi/quartz/domain/SysJobLog.java b/ruoyi-quartz/src/main/java/com/ruoyi/quartz/domain/SysJobLog.java index dc2cff48..290b3493 100644 --- a/ruoyi-quartz/src/main/java/com/ruoyi/quartz/domain/SysJobLog.java +++ b/ruoyi-quartz/src/main/java/com/ruoyi/quartz/domain/SysJobLog.java @@ -1,7 +1,9 @@ package com.ruoyi.quartz.domain; +import java.util.Date; import org.apache.commons.lang3.builder.ToStringBuilder; import org.apache.commons.lang3.builder.ToStringStyle; +import com.fasterxml.jackson.annotation.JsonFormat; import com.ruoyi.common.annotation.Excel; import com.ruoyi.common.core.domain.BaseEntity; @@ -46,6 +48,14 @@ public class SysJobLog extends BaseEntity @Excel(name = "异常信息") private String exceptionInfo; + /** 开始时间 */ + @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss") + private Date startTime; + + /** 结束时间 */ + @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss") + private Date endTime; + public Long getJobLogId() { return jobLogId; @@ -125,6 +135,26 @@ public class SysJobLog extends BaseEntity { this.exceptionInfo = exceptionInfo; } + + public Date getStartTime() + { + return startTime; + } + + public void setStartTime(Date startTime) + { + this.startTime = startTime; + } + + public Date getEndTime() + { + return endTime; + } + + public void setEndTime(Date endTime) + { + this.endTime = endTime; + } @Override public String toString() { @@ -137,7 +167,8 @@ public class SysJobLog extends BaseEntity .append("jobMessage", getJobMessage()) .append("status", getStatus()) .append("exceptionInfo", getExceptionInfo()) - .append("createTime", getCreateTime()) + .append("startTime", getStartTime()) + .append("endTime", getEndTime()) .toString(); } } diff --git a/ruoyi-quartz/src/main/java/com/ruoyi/quartz/service/ISysJobService.java b/ruoyi-quartz/src/main/java/com/ruoyi/quartz/service/ISysJobService.java index 0cf3a000..1e94f5ec 100644 --- a/ruoyi-quartz/src/main/java/com/ruoyi/quartz/service/ISysJobService.java +++ b/ruoyi-quartz/src/main/java/com/ruoyi/quartz/service/ISysJobService.java @@ -1,6 +1,8 @@ package com.ruoyi.quartz.service; import java.util.List; +import org.quartz.SchedulerException; +import com.ruoyi.common.exception.job.TaskException; import com.ruoyi.quartz.domain.SysJob; /** @@ -32,7 +34,7 @@ public interface ISysJobService * @param job 调度信息 * @return 结果 */ - public int pauseJob(SysJob job); + public int pauseJob(SysJob job) throws SchedulerException; /** * 恢复任务 @@ -40,7 +42,7 @@ public interface ISysJobService * @param job 调度信息 * @return 结果 */ - public int resumeJob(SysJob job); + public int resumeJob(SysJob job) throws SchedulerException; /** * 删除任务后,所对应的trigger也将被删除 @@ -48,7 +50,7 @@ public interface ISysJobService * @param job 调度信息 * @return 结果 */ - public int deleteJob(SysJob job); + public int deleteJob(SysJob job) throws SchedulerException; /** * 批量删除调度信息 @@ -56,7 +58,7 @@ public interface ISysJobService * @param ids 需要删除的数据ID * @return 结果 */ - public void deleteJobByIds(String ids); + public void deleteJobByIds(String ids) throws SchedulerException; /** * 任务调度状态修改 @@ -64,7 +66,7 @@ public interface ISysJobService * @param job 调度信息 * @return 结果 */ - public int changeStatus(SysJob job); + public int changeStatus(SysJob job) throws SchedulerException; /** * 立即运行任务 @@ -72,7 +74,7 @@ public interface ISysJobService * @param job 调度信息 * @return 结果 */ - public int run(SysJob job); + public void run(SysJob job) throws SchedulerException; /** * 新增任务表达式 @@ -80,7 +82,7 @@ public interface ISysJobService * @param job 调度信息 * @return 结果 */ - public int insertJobCron(SysJob job); + public int insertJobCron(SysJob job) throws SchedulerException, TaskException; /** * 更新任务的时间表达式 @@ -88,8 +90,8 @@ public interface ISysJobService * @param job 调度信息 * @return 结果 */ - public int updateJobCron(SysJob job); - + public int updateJobCron(SysJob job) throws SchedulerException, TaskException; + /** * 校验cron表达式是否有效 * diff --git a/ruoyi-quartz/src/main/java/com/ruoyi/quartz/service/impl/SysJobServiceImpl.java b/ruoyi-quartz/src/main/java/com/ruoyi/quartz/service/impl/SysJobServiceImpl.java index 8b1bc54e..c024fec6 100644 --- a/ruoyi-quartz/src/main/java/com/ruoyi/quartz/service/impl/SysJobServiceImpl.java +++ b/ruoyi-quartz/src/main/java/com/ruoyi/quartz/service/impl/SysJobServiceImpl.java @@ -4,11 +4,13 @@ import java.util.List; import javax.annotation.PostConstruct; import org.quartz.CronTrigger; import org.quartz.Scheduler; +import org.quartz.SchedulerException; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; import com.ruoyi.common.constant.ScheduleConstants; import com.ruoyi.common.core.text.Convert; +import com.ruoyi.common.exception.job.TaskException; import com.ruoyi.quartz.domain.SysJob; import com.ruoyi.quartz.mapper.SysJobMapper; import com.ruoyi.quartz.service.ISysJobService; @@ -33,7 +35,7 @@ public class SysJobServiceImpl implements ISysJobService * 项目启动时,初始化定时器 */ @PostConstruct - public void init() + public void init() throws SchedulerException, TaskException { List jobList = jobMapper.selectJobAll(); for (SysJob job : jobList) @@ -82,7 +84,7 @@ public class SysJobServiceImpl implements ISysJobService */ @Override @Transactional - public int pauseJob(SysJob job) + public int pauseJob(SysJob job) throws SchedulerException { job.setStatus(ScheduleConstants.Status.PAUSE.getValue()); int rows = jobMapper.updateJob(job); @@ -100,7 +102,7 @@ public class SysJobServiceImpl implements ISysJobService */ @Override @Transactional - public int resumeJob(SysJob job) + public int resumeJob(SysJob job) throws SchedulerException { job.setStatus(ScheduleConstants.Status.NORMAL.getValue()); int rows = jobMapper.updateJob(job); @@ -118,7 +120,7 @@ public class SysJobServiceImpl implements ISysJobService */ @Override @Transactional - public int deleteJob(SysJob job) + public int deleteJob(SysJob job) throws SchedulerException { int rows = jobMapper.deleteJobById(job.getJobId()); if (rows > 0) @@ -136,7 +138,7 @@ public class SysJobServiceImpl implements ISysJobService */ @Override @Transactional - public void deleteJobByIds(String ids) + public void deleteJobByIds(String ids) throws SchedulerException { Long[] jobIds = Convert.toLongArray(ids); for (Long jobId : jobIds) @@ -153,7 +155,7 @@ public class SysJobServiceImpl implements ISysJobService */ @Override @Transactional - public int changeStatus(SysJob job) + public int changeStatus(SysJob job) throws SchedulerException { int rows = 0; String status = job.getStatus(); @@ -175,9 +177,9 @@ public class SysJobServiceImpl implements ISysJobService */ @Override @Transactional - public int run(SysJob job) + public void run(SysJob job) throws SchedulerException { - return ScheduleUtils.run(scheduler, selectJobById(job.getJobId())); + ScheduleUtils.run(scheduler, selectJobById(job.getJobId())); } /** @@ -187,7 +189,7 @@ public class SysJobServiceImpl implements ISysJobService */ @Override @Transactional - public int insertJobCron(SysJob job) + public int insertJobCron(SysJob job) throws SchedulerException, TaskException { job.setStatus(ScheduleConstants.Status.PAUSE.getValue()); int rows = jobMapper.insertJob(job); @@ -205,7 +207,7 @@ public class SysJobServiceImpl implements ISysJobService */ @Override @Transactional - public int updateJobCron(SysJob job) + public int updateJobCron(SysJob job) throws SchedulerException, TaskException { int rows = jobMapper.updateJob(job); if (rows > 0) @@ -214,7 +216,7 @@ public class SysJobServiceImpl implements ISysJobService } return rows; } - + /** * 校验cron表达式是否有效 * @@ -225,5 +227,5 @@ public class SysJobServiceImpl implements ISysJobService public boolean checkCronExpressionIsValid(String cronExpression) { return CronUtils.isValid(cronExpression); - } + } } diff --git a/ruoyi-quartz/src/main/java/com/ruoyi/quartz/util/AbstractQuartzJob.java b/ruoyi-quartz/src/main/java/com/ruoyi/quartz/util/AbstractQuartzJob.java new file mode 100644 index 00000000..7cd0893e --- /dev/null +++ b/ruoyi-quartz/src/main/java/com/ruoyi/quartz/util/AbstractQuartzJob.java @@ -0,0 +1,108 @@ +package com.ruoyi.quartz.util; + +import java.util.Date; +import org.quartz.Job; +import org.quartz.JobExecutionContext; +import org.quartz.JobExecutionException; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import com.ruoyi.common.constant.Constants; +import com.ruoyi.common.constant.ScheduleConstants; +import com.ruoyi.common.utils.ExceptionUtil; +import com.ruoyi.common.utils.StringUtils; +import com.ruoyi.common.utils.bean.BeanUtils; +import com.ruoyi.common.utils.spring.SpringUtils; +import com.ruoyi.quartz.domain.SysJob; +import com.ruoyi.quartz.domain.SysJobLog; +import com.ruoyi.quartz.service.ISysJobLogService; + +/** + * 抽象quartz调用 + * + * @author ruoyi + */ +public abstract class AbstractQuartzJob implements Job +{ + private static final Logger log = LoggerFactory.getLogger(AbstractQuartzJob.class); + + /** + * 线程本地变量 + */ + private static ThreadLocal threadLocal = new ThreadLocal<>(); + + @Override + public void execute(JobExecutionContext context) throws JobExecutionException + { + SysJob sysJob = new SysJob(); + BeanUtils.copyBeanProp(sysJob, context.getMergedJobDataMap().get(ScheduleConstants.TASK_PROPERTIES)); + try + { + before(context, sysJob); + if (sysJob != null) + { + doExecute(context, sysJob); + } + after(context, sysJob, null); + } + catch (Exception e) + { + log.error("任务执行异常 - :", e); + after(context, sysJob, e); + } + } + + /** + * 执行前 + * + * @param context 工作执行上下文对象 + * @param sysJob 系统计划任务 + */ + protected void before(JobExecutionContext context, SysJob sysJob) + { + threadLocal.set(new Date()); + } + + /** + * 执行后 + * + * @param context 工作执行上下文对象 + * @param sysScheduleJob 系统计划任务 + */ + protected void after(JobExecutionContext context, SysJob sysJob, Exception e) + { + Date startTime = threadLocal.get(); + threadLocal.remove(); + + final SysJobLog sysJobLog = new SysJobLog(); + sysJobLog.setJobName(sysJob.getJobName()); + sysJobLog.setJobGroup(sysJob.getJobGroup()); + sysJobLog.setMethodName(sysJob.getMethodName()); + sysJobLog.setMethodParams(sysJob.getMethodParams()); + sysJobLog.setStartTime(startTime); + sysJobLog.setEndTime(new Date()); + long runMs = sysJobLog.getEndTime().getTime() - sysJobLog.getStartTime().getTime(); + sysJobLog.setJobMessage(sysJobLog.getJobName() + " 总共耗时:" + runMs + "毫秒"); + if (e != null) + { + sysJobLog.setStatus(Constants.FAIL); + String errorMsg = StringUtils.substring(ExceptionUtil.getExceptionMessage(e), 0, 2000); + sysJobLog.setExceptionInfo(errorMsg); + } + else + { + sysJobLog.setStatus(Constants.SUCCESS); + } + + // 写入数据库当中 + SpringUtils.getBean(ISysJobLogService.class).addJobLog(sysJobLog); + } + + /** + * 执行方法,由子类重载 + * + * @param context 工作执行上下文对象 + * @param sysJob 系统计划任务 + * @throws Exception 执行过程中的异常 + */ + protected abstract void doExecute(JobExecutionContext context, SysJob sysJob) throws Exception; +} diff --git a/ruoyi-quartz/src/main/java/com/ruoyi/quartz/util/JobInvokeUtil.java b/ruoyi-quartz/src/main/java/com/ruoyi/quartz/util/JobInvokeUtil.java new file mode 100644 index 00000000..3a867404 --- /dev/null +++ b/ruoyi-quartz/src/main/java/com/ruoyi/quartz/util/JobInvokeUtil.java @@ -0,0 +1,57 @@ +package com.ruoyi.quartz.util; + +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; +import com.ruoyi.common.utils.StringUtils; +import com.ruoyi.common.utils.spring.SpringUtils; +import com.ruoyi.quartz.domain.SysJob; + +/** + * 任务执行工具 + * + * @author ruoyi + */ +public class JobInvokeUtil +{ + /** + * 执行方法 + * + * @param sysJob 系统任务 + */ + public static void invokeMethod(SysJob sysJob) throws Exception + { + Object bean = SpringUtils.getBean(sysJob.getJobName()); + String methodName = sysJob.getMethodName(); + String methodParams = sysJob.getMethodParams(); + + invokeSpringBean(bean, methodName, methodParams); + } + + /** + * 调用任务方法 + * + * @param bean 目标对象 + * @param methodName 方法名称 + * @param methodParams 方法参数 + * @throws InvocationTargetException + * @throws SecurityException + * @throws NoSuchMethodException + * @throws IllegalArgumentException + * @throws IllegalAccessException + */ + private static void invokeSpringBean(Object bean, String methodName, String methodParams) + throws NoSuchMethodException, SecurityException, IllegalAccessException, IllegalArgumentException, + InvocationTargetException + { + if (StringUtils.isNotEmpty(methodParams)) + { + Method method = bean.getClass().getDeclaredMethod(methodName, String.class); + method.invoke(bean, methodParams); + } + else + { + Method method = bean.getClass().getDeclaredMethod(methodName); + method.invoke(bean); + } + } +} diff --git a/ruoyi-quartz/src/main/java/com/ruoyi/quartz/util/QuartzDisallowConcurrentExecution.java b/ruoyi-quartz/src/main/java/com/ruoyi/quartz/util/QuartzDisallowConcurrentExecution.java new file mode 100644 index 00000000..96a6dcf8 --- /dev/null +++ b/ruoyi-quartz/src/main/java/com/ruoyi/quartz/util/QuartzDisallowConcurrentExecution.java @@ -0,0 +1,21 @@ +package com.ruoyi.quartz.util; + +import org.quartz.DisallowConcurrentExecution; +import org.quartz.JobExecutionContext; +import com.ruoyi.quartz.domain.SysJob; + +/** + * 定时任务处理(禁止并发执行) + * + * @author ruoyi + * + */ +@DisallowConcurrentExecution +public class QuartzDisallowConcurrentExecution extends AbstractQuartzJob +{ + @Override + protected void doExecute(JobExecutionContext context, SysJob sysJob) throws Exception + { + JobInvokeUtil.invokeMethod(sysJob); + } +} diff --git a/ruoyi-quartz/src/main/java/com/ruoyi/quartz/util/QuartzJobExecution.java b/ruoyi-quartz/src/main/java/com/ruoyi/quartz/util/QuartzJobExecution.java new file mode 100644 index 00000000..87a06bc1 --- /dev/null +++ b/ruoyi-quartz/src/main/java/com/ruoyi/quartz/util/QuartzJobExecution.java @@ -0,0 +1,19 @@ +package com.ruoyi.quartz.util; + +import org.quartz.JobExecutionContext; +import com.ruoyi.quartz.domain.SysJob; + +/** + * 定时任务处理(允许并发执行) + * + * @author ruoyi + * + */ +public class QuartzJobExecution extends AbstractQuartzJob +{ + @Override + protected void doExecute(JobExecutionContext context, SysJob sysJob) throws Exception + { + JobInvokeUtil.invokeMethod(sysJob); + } +} diff --git a/ruoyi-quartz/src/main/java/com/ruoyi/quartz/util/ScheduleJob.java b/ruoyi-quartz/src/main/java/com/ruoyi/quartz/util/ScheduleJob.java deleted file mode 100644 index 9b606bc5..00000000 --- a/ruoyi-quartz/src/main/java/com/ruoyi/quartz/util/ScheduleJob.java +++ /dev/null @@ -1,80 +0,0 @@ -package com.ruoyi.quartz.util; - -import java.util.Date; -import java.util.concurrent.Future; -import org.quartz.DisallowConcurrentExecution; -import org.quartz.JobExecutionContext; -import org.quartz.JobExecutionException; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor; -import org.springframework.scheduling.quartz.QuartzJobBean; -import com.ruoyi.common.constant.Constants; -import com.ruoyi.common.constant.ScheduleConstants; -import com.ruoyi.common.utils.StringUtils; -import com.ruoyi.common.utils.bean.BeanUtils; -import com.ruoyi.common.utils.spring.SpringUtils; -import com.ruoyi.quartz.domain.SysJob; -import com.ruoyi.quartz.domain.SysJobLog; -import com.ruoyi.quartz.service.ISysJobLogService; - -/** - * 定时任务处理 - * - * @author ruoyi - * - */ -@DisallowConcurrentExecution -public class ScheduleJob extends QuartzJobBean -{ - private static final Logger log = LoggerFactory.getLogger(ScheduleJob.class); - - private ThreadPoolTaskExecutor executor = SpringUtils.getBean("threadPoolTaskExecutor"); - - private final static ISysJobLogService jobLogService = SpringUtils.getBean(ISysJobLogService.class); - - @Override - protected void executeInternal(JobExecutionContext context) throws JobExecutionException - { - SysJob job = new SysJob(); - BeanUtils.copyBeanProp(job, context.getMergedJobDataMap().get(ScheduleConstants.TASK_PROPERTIES)); - - SysJobLog jobLog = new SysJobLog(); - jobLog.setJobName(job.getJobName()); - jobLog.setJobGroup(job.getJobGroup()); - jobLog.setMethodName(job.getMethodName()); - jobLog.setMethodParams(job.getMethodParams()); - jobLog.setCreateTime(new Date()); - - long startTime = System.currentTimeMillis(); - - try - { - // 执行任务 - log.info("任务开始执行 - 名称:{} 方法:{}", job.getJobName(), job.getMethodName()); - ScheduleRunnable task = new ScheduleRunnable(job.getJobName(), job.getMethodName(), job.getMethodParams()); - Future future = executor.submit(task); - future.get(); - long times = System.currentTimeMillis() - startTime; - // 任务状态 0:成功 1:失败 - jobLog.setStatus(Constants.SUCCESS); - jobLog.setJobMessage(job.getJobName() + " 总共耗时:" + times + "毫秒"); - - log.info("任务执行结束 - 名称:{} 耗时:{} 毫秒", job.getJobName(), times); - } - catch (Exception e) - { - log.info("任务执行失败 - 名称:{} 方法:{}", job.getJobName(), job.getMethodName()); - log.error("任务执行异常 - :", e); - long times = System.currentTimeMillis() - startTime; - jobLog.setJobMessage(job.getJobName() + " 总共耗时:" + times + "毫秒"); - // 任务状态 0:成功 1:失败 - jobLog.setStatus(Constants.FAIL); - jobLog.setExceptionInfo(StringUtils.substring(e.getMessage(), 0, 2000)); - } - finally - { - jobLogService.addJobLog(jobLog); - } - } -} diff --git a/ruoyi-quartz/src/main/java/com/ruoyi/quartz/util/ScheduleRunnable.java b/ruoyi-quartz/src/main/java/com/ruoyi/quartz/util/ScheduleRunnable.java deleted file mode 100644 index b890649a..00000000 --- a/ruoyi-quartz/src/main/java/com/ruoyi/quartz/util/ScheduleRunnable.java +++ /dev/null @@ -1,57 +0,0 @@ -package com.ruoyi.quartz.util; - -import java.lang.reflect.Method; -import org.springframework.util.ReflectionUtils; -import com.ruoyi.common.exception.BusinessException; -import com.ruoyi.common.utils.StringUtils; -import com.ruoyi.common.utils.spring.SpringUtils; - -/** - * 执行定时任务 - * - * @author ruoyi - * - */ -public class ScheduleRunnable implements Runnable -{ - private Object target; - private Method method; - private String params; - - public ScheduleRunnable(String beanName, String methodName, String params) - throws NoSuchMethodException, SecurityException - { - this.target = SpringUtils.getBean(beanName); - this.params = params; - - if (StringUtils.isNotEmpty(params)) - { - this.method = target.getClass().getDeclaredMethod(methodName, String.class); - } - else - { - this.method = target.getClass().getDeclaredMethod(methodName); - } - } - - @Override - public void run() - { - try - { - ReflectionUtils.makeAccessible(method); - if (StringUtils.isNotEmpty(params)) - { - method.invoke(target, params); - } - else - { - method.invoke(target); - } - } - catch (Exception e) - { - throw new BusinessException("执行定时任务失败", e); - } - } -} diff --git a/ruoyi-quartz/src/main/java/com/ruoyi/quartz/util/ScheduleUtils.java b/ruoyi-quartz/src/main/java/com/ruoyi/quartz/util/ScheduleUtils.java index 9a6b9ca6..4bfa85c0 100644 --- a/ruoyi-quartz/src/main/java/com/ruoyi/quartz/util/ScheduleUtils.java +++ b/ruoyi-quartz/src/main/java/com/ruoyi/quartz/util/ScheduleUtils.java @@ -2,6 +2,7 @@ package com.ruoyi.quartz.util; import org.quartz.CronScheduleBuilder; import org.quartz.CronTrigger; +import org.quartz.Job; import org.quartz.JobBuilder; import org.quartz.JobDataMap; import org.quartz.JobDetail; @@ -27,6 +28,18 @@ public class ScheduleUtils { private static final Logger log = LoggerFactory.getLogger(ScheduleUtils.class); + /** + * 得到quartz任务类 + * + * @param sysJob 执行计划 + * @return 具体执行任务类 + */ + private static Class getQuartzJobClass(SysJob sysJob) + { + boolean isConcurrent = "0".equals(sysJob.getConcurrent()); + return isConcurrent ? QuartzJobExecution.class : QuartzDisallowConcurrentExecution.class; + } + /** * 获取触发器key */ @@ -62,147 +75,89 @@ public class ScheduleUtils /** * 创建定时任务 */ - public static void createScheduleJob(Scheduler scheduler, SysJob job) + public static void createScheduleJob(Scheduler scheduler, SysJob job) throws SchedulerException, TaskException { - try - { - // 构建job信息 - JobDetail jobDetail = JobBuilder.newJob(ScheduleJob.class).withIdentity(getJobKey(job.getJobId())).build(); + Class jobClass = getQuartzJobClass(job); + // 构建job信息 + JobDetail jobDetail = JobBuilder.newJob(jobClass).withIdentity(getJobKey(job.getJobId())).build(); - // 表达式调度构建器 - CronScheduleBuilder cronScheduleBuilder = CronScheduleBuilder.cronSchedule(job.getCronExpression()); - cronScheduleBuilder = handleCronScheduleMisfirePolicy(job, cronScheduleBuilder); + // 表达式调度构建器 + CronScheduleBuilder cronScheduleBuilder = CronScheduleBuilder.cronSchedule(job.getCronExpression()); + cronScheduleBuilder = handleCronScheduleMisfirePolicy(job, cronScheduleBuilder); - // 按新的cronExpression表达式构建一个新的trigger - CronTrigger trigger = TriggerBuilder.newTrigger().withIdentity(getTriggerKey(job.getJobId())) - .withSchedule(cronScheduleBuilder).build(); + // 按新的cronExpression表达式构建一个新的trigger + CronTrigger trigger = TriggerBuilder.newTrigger().withIdentity(getTriggerKey(job.getJobId())) + .withSchedule(cronScheduleBuilder).build(); - // 放入参数,运行时的方法可以获取 - jobDetail.getJobDataMap().put(ScheduleConstants.TASK_PROPERTIES, job); + // 放入参数,运行时的方法可以获取 + jobDetail.getJobDataMap().put(ScheduleConstants.TASK_PROPERTIES, job); - scheduler.scheduleJob(jobDetail, trigger); + scheduler.scheduleJob(jobDetail, trigger); - // 暂停任务 - if (job.getStatus().equals(ScheduleConstants.Status.PAUSE.getValue())) - { - pauseJob(scheduler, job.getJobId()); - } - } - catch (SchedulerException e) + // 暂停任务 + if (job.getStatus().equals(ScheduleConstants.Status.PAUSE.getValue())) { - log.error("createScheduleJob 异常:", e); - } - catch (TaskException e) - { - log.error("createScheduleJob 异常:", e); + pauseJob(scheduler, job.getJobId()); } } /** * 更新定时任务 */ - public static void updateScheduleJob(Scheduler scheduler, SysJob job) + public static void updateScheduleJob(Scheduler scheduler, SysJob job) throws SchedulerException, TaskException { - try - { - TriggerKey triggerKey = getTriggerKey(job.getJobId()); - - // 表达式调度构建器 - CronScheduleBuilder cronScheduleBuilder = CronScheduleBuilder.cronSchedule(job.getCronExpression()); - cronScheduleBuilder = handleCronScheduleMisfirePolicy(job, cronScheduleBuilder); - - CronTrigger trigger = getCronTrigger(scheduler, job.getJobId()); - - // 按新的cronExpression表达式重新构建trigger - trigger = trigger.getTriggerBuilder().withIdentity(triggerKey).withSchedule(cronScheduleBuilder).build(); - - // 参数 - trigger.getJobDataMap().put(ScheduleConstants.TASK_PROPERTIES, job); - - scheduler.rescheduleJob(triggerKey, trigger); + JobKey jobKey = getJobKey(job.getJobId()); - // 暂停任务 - if (job.getStatus().equals(ScheduleConstants.Status.PAUSE.getValue())) - { - pauseJob(scheduler, job.getJobId()); - } - - } - catch (SchedulerException e) + // 判断是否存在 + if (scheduler.checkExists(jobKey)) { - log.error("SchedulerException 异常:", e); + // 先移除,然后做更新操作 + scheduler.deleteJob(jobKey); } - catch (TaskException e) + + createScheduleJob(scheduler, job); + + // 暂停任务 + if (job.getStatus().equals(ScheduleConstants.Status.PAUSE.getValue())) { - log.error("SchedulerException 异常:", e); + pauseJob(scheduler, job.getJobId()); } } /** * 立即执行任务 */ - public static int run(Scheduler scheduler, SysJob job) + public static void run(Scheduler scheduler, SysJob job) throws SchedulerException { - int rows = 0; - try - { - // 参数 - JobDataMap dataMap = new JobDataMap(); - dataMap.put(ScheduleConstants.TASK_PROPERTIES, job); + // 参数 + JobDataMap dataMap = new JobDataMap(); + dataMap.put(ScheduleConstants.TASK_PROPERTIES, job); - scheduler.triggerJob(getJobKey(job.getJobId()), dataMap); - rows = 1; - } - catch (SchedulerException e) - { - log.error("run 异常:", e); - } - return rows; + scheduler.triggerJob(getJobKey(job.getJobId()), dataMap); } /** * 暂停任务 */ - public static void pauseJob(Scheduler scheduler, Long jobId) + public static void pauseJob(Scheduler scheduler, Long jobId) throws SchedulerException { - try - { - scheduler.pauseJob(getJobKey(jobId)); - } - catch (SchedulerException e) - { - log.error("pauseJob 异常:", e); - } + scheduler.pauseJob(getJobKey(jobId)); } /** * 恢复任务 */ - public static void resumeJob(Scheduler scheduler, Long jobId) + public static void resumeJob(Scheduler scheduler, Long jobId) throws SchedulerException { - try - { - scheduler.resumeJob(getJobKey(jobId)); - } - catch (SchedulerException e) - { - log.error("resumeJob 异常:", e); - } + scheduler.resumeJob(getJobKey(jobId)); } /** * 删除定时任务 */ - public static void deleteScheduleJob(Scheduler scheduler, Long jobId) + public static void deleteScheduleJob(Scheduler scheduler, Long jobId) throws SchedulerException { - try - { - scheduler.deleteJob(getJobKey(jobId)); - } - catch (SchedulerException e) - { - log.error("deleteScheduleJob 异常:", e); - } + scheduler.deleteJob(getJobKey(jobId)); } public static CronScheduleBuilder handleCronScheduleMisfirePolicy(SysJob job, CronScheduleBuilder cb) @@ -219,7 +174,8 @@ public class ScheduleUtils case ScheduleConstants.MISFIRE_DO_NOTHING: return cb.withMisfireHandlingInstructionDoNothing(); default: - throw new TaskException("The task misfire policy '" + job.getMisfirePolicy() + "' cannot be used in cron schedule tasks", Code.CONFIG_ERROR); + throw new TaskException("The task misfire policy '" + job.getMisfirePolicy() + + "' cannot be used in cron schedule tasks", Code.CONFIG_ERROR); } } } diff --git a/ruoyi-quartz/src/main/resources/mapper/quartz/SysJobMapper.xml b/ruoyi-quartz/src/main/resources/mapper/quartz/SysJobMapper.xml index 758dbb3a..0e4a6a74 100644 --- a/ruoyi-quartz/src/main/resources/mapper/quartz/SysJobMapper.xml +++ b/ruoyi-quartz/src/main/resources/mapper/quartz/SysJobMapper.xml @@ -12,6 +12,7 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" + @@ -21,7 +22,7 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" - select job_id, job_name, job_group, method_name, method_params, cron_expression, misfire_policy, status, create_by, create_time, remark + select job_id, job_name, job_group, method_name, method_params, cron_expression, misfire_policy, concurrent, status, create_by, create_time, remark from sys_job @@ -69,6 +70,7 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" method_params = #{methodParams}, cron_expression = #{cronExpression}, misfire_policy = #{misfirePolicy}, + concurrent = #{concurrent}, status = #{status}, remark = #{remark}, update_by = #{updateBy}, @@ -86,6 +88,7 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" method_params, cron_expression, misfire_policy, + concurrent, status, remark, create_by, @@ -98,6 +101,7 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" #{methodParams}, #{cronExpression}, #{misfirePolicy}, + #{concurrent}, #{status}, #{remark}, #{createBy}, diff --git a/ruoyi-quartz/src/main/resources/templates/monitor/job/add.html b/ruoyi-quartz/src/main/resources/templates/monitor/job/add.html index d78f6f50..e8b5e625 100644 --- a/ruoyi-quartz/src/main/resources/templates/monitor/job/add.html +++ b/ruoyi-quartz/src/main/resources/templates/monitor/job/add.html @@ -43,6 +43,13 @@ +
+ +
+ + +
+
diff --git a/ruoyi-quartz/src/main/resources/templates/monitor/job/detail.html b/ruoyi-quartz/src/main/resources/templates/monitor/job/detail.html index f75d8b7b..1e0f5491 100644 --- a/ruoyi-quartz/src/main/resources/templates/monitor/job/detail.html +++ b/ruoyi-quartz/src/main/resources/templates/monitor/job/detail.html @@ -82,6 +82,11 @@
执行一次
放弃执行
+
+ +
+
+
diff --git a/ruoyi-quartz/src/main/resources/templates/monitor/job/edit.html b/ruoyi-quartz/src/main/resources/templates/monitor/job/edit.html index 02bc3327..3497d140 100644 --- a/ruoyi-quartz/src/main/resources/templates/monitor/job/edit.html +++ b/ruoyi-quartz/src/main/resources/templates/monitor/job/edit.html @@ -44,6 +44,13 @@
+
+ +
+ + +
+
diff --git a/ruoyi-quartz/src/main/resources/templates/monitor/job/job.html b/ruoyi-quartz/src/main/resources/templates/monitor/job/job.html index 9194ad41..b2b1b0fe 100644 --- a/ruoyi-quartz/src/main/resources/templates/monitor/job/job.html +++ b/ruoyi-quartz/src/main/resources/templates/monitor/job/job.html @@ -122,7 +122,7 @@ align: 'center', formatter: function(value, row, index) { var actions = []; - actions.push(' 执行 '); + actions.push(' 执行一次 '); actions.push('详细 '); return actions.join(''); } @@ -142,7 +142,7 @@ /* 立即执行一次 */ function run(jobId) { - $.modal.confirm("确认要立即执行任务吗?", function() { + $.modal.confirm("确认要立即执行一次任务吗?", function() { $.operate.post(prefix + "/run", { "jobId": jobId}); }) } diff --git a/sql/ry_20190215.sql b/sql/ry_20190313.sql similarity index 97% rename from sql/ry_20190215.sql rename to sql/ry_20190313.sql index 49d5541b..e6ee374a 100644 --- a/sql/ry_20190215.sql +++ b/sql/ry_20190313.sql @@ -568,6 +568,7 @@ create table sys_job ( method_params varchar(50) default null comment '方法参数', cron_expression varchar(255) default '' comment 'cron执行表达式', misfire_policy varchar(20) default '3' comment '计划执行错误策略(1立即执行 2执行一次 3放弃执行)', + concurrent char default '1' comment '是否并发执行(0允许 1禁止)', status char(1) default '0' comment '状态(0正常 1暂停)', create_by varchar(64) default '' comment '创建者', create_time datetime comment '创建时间', @@ -577,8 +578,8 @@ create table sys_job ( primary key (job_id, job_name, job_group) ) engine=innodb auto_increment=100 default charset=utf8 comment = '定时任务调度表'; -insert into sys_job values(1, 'ryTask', '系统默认(无参)', 'ryNoParams', '', '0/10 * * * * ?', '3', '1', 'admin', '2018-03-16 11-33-00', 'ry', '2018-03-16 11-33-00', ''); -insert into sys_job values(2, 'ryTask', '系统默认(有参)', 'ryParams', 'ry', '0/20 * * * * ?', '3', '1', 'admin', '2018-03-16 11-33-00', 'ry', '2018-03-16 11-33-00', ''); +insert into sys_job values(1, 'ryTask', '系统默认(无参)', 'ryNoParams', '', '0/10 * * * * ?', '3', '1', '1', 'admin', '2018-03-16 11-33-00', 'ry', '2018-03-16 11-33-00', ''); +insert into sys_job values(2, 'ryTask', '系统默认(有参)', 'ryParams', 'ry', '0/20 * * * * ?', '3', '1', '1', 'admin', '2018-03-16 11-33-00', 'ry', '2018-03-16 11-33-00', ''); -- ----------------------------