定时任务支持并发控制

master
RuoYi 6 years ago committed by Limy
parent 8e99823bbe
commit d373632cb6

@ -22,7 +22,7 @@ public class Global
/** /**
* *
*/ */
private static Global global = null; private static Global global;
/** /**
* *
@ -34,19 +34,14 @@ public class Global
} }
/** /**
* 线(使) *
*/ */
public static synchronized Global getInstance() public static synchronized Global getInstance()
{ {
if (global == null) if (global == null)
{ {
synchronized (Global.class)
{
if (global == null)
global = new Global(); global = new Global();
} }
}
return global; return global;
} }

@ -7,9 +7,10 @@ package com.ruoyi.common.constant;
*/ */
public interface ScheduleConstants 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"; public static final String MISFIRE_DEFAULT = "0";

@ -223,6 +223,7 @@ public class JSONObject extends LinkedHashMap<String, Object>
{ {
return endArray(matcher.group(1), matcher.group(2), new EndArrayCallback<Object>() return endArray(matcher.group(1), matcher.group(2), new EndArrayCallback<Object>()
{ {
@Override
public Object callback(JSONArray arr, int index) public Object callback(JSONArray arr, int index)
{ {
return elementAt(arr, index); return elementAt(arr, index);
@ -257,6 +258,7 @@ public class JSONObject extends LinkedHashMap<String, Object>
{ {
endArray(matcher.group(1), matcher.group(2), new EndArrayCallback<Void>() endArray(matcher.group(1), matcher.group(2), new EndArrayCallback<Void>()
{ {
@Override
public Void callback(JSONArray arr, int index) public Void callback(JSONArray arr, int index)
{ {
elementAt(arr, index, value); elementAt(arr, index, value);
@ -285,6 +287,7 @@ public class JSONObject extends LinkedHashMap<String, Object>
{ {
return endArray(matcher.group(1), matcher.group(2), new EndArrayCallback<JSONObject>() return endArray(matcher.group(1), matcher.group(2), new EndArrayCallback<JSONObject>()
{ {
@Override
public JSONObject callback(JSONArray arr, int index) public JSONObject callback(JSONArray arr, int index)
{ {
return objAt(arr, index); return objAt(arr, index);

@ -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);
}
}

@ -40,7 +40,7 @@ public class YamlUtil
if (map != null && !map.isEmpty() && qualifiedKey != null) if (map != null && !map.isEmpty() && qualifiedKey != null)
{ {
String input = String.valueOf(qualifiedKey); String input = String.valueOf(qualifiedKey);
if (!input.equals("")) if (!"".equals(input))
{ {
if (input.contains(".")) if (input.contains("."))
{ {

@ -647,8 +647,10 @@ public class ExcelUtil<T>
{ {
tempClass = tempClass.getSuperclass(); tempClass = tempClass.getSuperclass();
if (tempClass != null) if (tempClass != null)
{
tempFields.addAll(Arrays.asList(tempClass.getDeclaredFields())); tempFields.addAll(Arrays.asList(tempClass.getDeclaredFields()));
} }
}
putToFields(tempFields); putToFields(tempFields);
} }

@ -2,6 +2,7 @@ package com.ruoyi.quartz.controller;
import java.util.List; import java.util.List;
import org.apache.shiro.authz.annotation.RequiresPermissions; import org.apache.shiro.authz.annotation.RequiresPermissions;
import org.quartz.SchedulerException;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller; import org.springframework.stereotype.Controller;
import org.springframework.ui.ModelMap; 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.domain.AjaxResult;
import com.ruoyi.common.core.page.TableDataInfo; import com.ruoyi.common.core.page.TableDataInfo;
import com.ruoyi.common.enums.BusinessType; import com.ruoyi.common.enums.BusinessType;
import com.ruoyi.common.exception.job.TaskException;
import com.ruoyi.common.utils.poi.ExcelUtil; import com.ruoyi.common.utils.poi.ExcelUtil;
import com.ruoyi.quartz.domain.SysJob; import com.ruoyi.quartz.domain.SysJob;
import com.ruoyi.quartz.service.ISysJobService; import com.ruoyi.quartz.service.ISysJobService;
@ -65,19 +67,11 @@ public class SysJobController extends BaseController
@RequiresPermissions("monitor:job:remove") @RequiresPermissions("monitor:job:remove")
@PostMapping("/remove") @PostMapping("/remove")
@ResponseBody @ResponseBody
public AjaxResult remove(String ids) public AjaxResult remove(String ids) throws SchedulerException
{
try
{ {
jobService.deleteJobByIds(ids); jobService.deleteJobByIds(ids);
return success(); return success();
} }
catch (Exception e)
{
e.printStackTrace();
return error(e.getMessage());
}
}
@RequiresPermissions("monitor:job:detail") @RequiresPermissions("monitor:job:detail")
@GetMapping("/detail/{jobId}") @GetMapping("/detail/{jobId}")
@ -90,14 +84,12 @@ public class SysJobController extends BaseController
/** /**
* *
*
* @throws Exception
*/ */
@Log(title = "定时任务", businessType = BusinessType.UPDATE) @Log(title = "定时任务", businessType = BusinessType.UPDATE)
@RequiresPermissions("monitor:job:changeStatus") @RequiresPermissions("monitor:job:changeStatus")
@PostMapping("/changeStatus") @PostMapping("/changeStatus")
@ResponseBody @ResponseBody
public AjaxResult changeStatus(SysJob job) public AjaxResult changeStatus(SysJob job) throws SchedulerException
{ {
return toAjax(jobService.changeStatus(job)); return toAjax(jobService.changeStatus(job));
} }
@ -109,9 +101,10 @@ public class SysJobController extends BaseController
@RequiresPermissions("monitor:job:changeStatus") @RequiresPermissions("monitor:job:changeStatus")
@PostMapping("/run") @PostMapping("/run")
@ResponseBody @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) @Log(title = "定时任务", businessType = BusinessType.INSERT)
@RequiresPermissions("monitor:job:add") @RequiresPermissions("monitor:job:add")
@ -148,13 +140,12 @@ public class SysJobController extends BaseController
/** /**
* *
* @throws Exception
*/ */
@Log(title = "定时任务", businessType = BusinessType.UPDATE) @Log(title = "定时任务", businessType = BusinessType.UPDATE)
@RequiresPermissions("monitor:job:edit") @RequiresPermissions("monitor:job:edit")
@PostMapping("/edit") @PostMapping("/edit")
@ResponseBody @ResponseBody
public AjaxResult editSave(SysJob job ) throws Exception public AjaxResult editSave(SysJob job) throws SchedulerException, TaskException
{ {
return toAjax(jobService.updateJobCron(job)); return toAjax(jobService.updateJobCron(job));
} }

@ -47,6 +47,9 @@ public class SysJob extends BaseEntity implements Serializable
@Excel(name = "计划策略 ") @Excel(name = "计划策略 ")
private String misfirePolicy = ScheduleConstants.MISFIRE_DEFAULT; private String misfirePolicy = ScheduleConstants.MISFIRE_DEFAULT;
/** 是否并发执行0允许 1禁止 */
private String concurrent;
/** 任务状态0正常 1暂停 */ /** 任务状态0正常 1暂停 */
@Excel(name = "任务状态", readConverterExp = "0=正常,1=暂停") @Excel(name = "任务状态", readConverterExp = "0=正常,1=暂停")
private String status; private String status;
@ -130,6 +133,16 @@ public class SysJob extends BaseEntity implements Serializable
this.misfirePolicy = misfirePolicy; this.misfirePolicy = misfirePolicy;
} }
public String getConcurrent()
{
return concurrent;
}
public void setConcurrent(String concurrent)
{
this.concurrent = concurrent;
}
public String getStatus() public String getStatus()
{ {
return status; return status;
@ -151,6 +164,7 @@ public class SysJob extends BaseEntity implements Serializable
.append("cronExpression", getCronExpression()) .append("cronExpression", getCronExpression())
.append("nextValidTime", getNextValidTime()) .append("nextValidTime", getNextValidTime())
.append("misfirePolicy", getMisfirePolicy()) .append("misfirePolicy", getMisfirePolicy())
.append("concurrent", getConcurrent())
.append("status", getStatus()) .append("status", getStatus())
.append("createBy", getCreateBy()) .append("createBy", getCreateBy())
.append("createTime", getCreateTime()) .append("createTime", getCreateTime())

@ -1,7 +1,9 @@
package com.ruoyi.quartz.domain; package com.ruoyi.quartz.domain;
import java.util.Date;
import org.apache.commons.lang3.builder.ToStringBuilder; import org.apache.commons.lang3.builder.ToStringBuilder;
import org.apache.commons.lang3.builder.ToStringStyle; import org.apache.commons.lang3.builder.ToStringStyle;
import com.fasterxml.jackson.annotation.JsonFormat;
import com.ruoyi.common.annotation.Excel; import com.ruoyi.common.annotation.Excel;
import com.ruoyi.common.core.domain.BaseEntity; import com.ruoyi.common.core.domain.BaseEntity;
@ -46,6 +48,14 @@ public class SysJobLog extends BaseEntity
@Excel(name = "异常信息") @Excel(name = "异常信息")
private String exceptionInfo; 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() public Long getJobLogId()
{ {
return jobLogId; return jobLogId;
@ -126,6 +136,26 @@ public class SysJobLog extends BaseEntity
this.exceptionInfo = exceptionInfo; 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 @Override
public String toString() { public String toString() {
return new ToStringBuilder(this,ToStringStyle.MULTI_LINE_STYLE) return new ToStringBuilder(this,ToStringStyle.MULTI_LINE_STYLE)
@ -137,7 +167,8 @@ public class SysJobLog extends BaseEntity
.append("jobMessage", getJobMessage()) .append("jobMessage", getJobMessage())
.append("status", getStatus()) .append("status", getStatus())
.append("exceptionInfo", getExceptionInfo()) .append("exceptionInfo", getExceptionInfo())
.append("createTime", getCreateTime()) .append("startTime", getStartTime())
.append("endTime", getEndTime())
.toString(); .toString();
} }
} }

@ -1,6 +1,8 @@
package com.ruoyi.quartz.service; package com.ruoyi.quartz.service;
import java.util.List; import java.util.List;
import org.quartz.SchedulerException;
import com.ruoyi.common.exception.job.TaskException;
import com.ruoyi.quartz.domain.SysJob; import com.ruoyi.quartz.domain.SysJob;
/** /**
@ -32,7 +34,7 @@ public interface ISysJobService
* @param job * @param job
* @return * @return
*/ */
public int pauseJob(SysJob job); public int pauseJob(SysJob job) throws SchedulerException;
/** /**
* *
@ -40,7 +42,7 @@ public interface ISysJobService
* @param job * @param job
* @return * @return
*/ */
public int resumeJob(SysJob job); public int resumeJob(SysJob job) throws SchedulerException;
/** /**
* trigger * trigger
@ -48,7 +50,7 @@ public interface ISysJobService
* @param job * @param job
* @return * @return
*/ */
public int deleteJob(SysJob job); public int deleteJob(SysJob job) throws SchedulerException;
/** /**
* *
@ -56,7 +58,7 @@ public interface ISysJobService
* @param ids ID * @param ids ID
* @return * @return
*/ */
public void deleteJobByIds(String ids); public void deleteJobByIds(String ids) throws SchedulerException;
/** /**
* *
@ -64,7 +66,7 @@ public interface ISysJobService
* @param job * @param job
* @return * @return
*/ */
public int changeStatus(SysJob job); public int changeStatus(SysJob job) throws SchedulerException;
/** /**
* *
@ -72,7 +74,7 @@ public interface ISysJobService
* @param job * @param job
* @return * @return
*/ */
public int run(SysJob job); public void run(SysJob job) throws SchedulerException;
/** /**
* *
@ -80,7 +82,7 @@ public interface ISysJobService
* @param job * @param job
* @return * @return
*/ */
public int insertJobCron(SysJob job); public int insertJobCron(SysJob job) throws SchedulerException, TaskException;
/** /**
* *
@ -88,7 +90,7 @@ public interface ISysJobService
* @param job * @param job
* @return * @return
*/ */
public int updateJobCron(SysJob job); public int updateJobCron(SysJob job) throws SchedulerException, TaskException;
/** /**
* cron * cron

@ -4,11 +4,13 @@ import java.util.List;
import javax.annotation.PostConstruct; import javax.annotation.PostConstruct;
import org.quartz.CronTrigger; import org.quartz.CronTrigger;
import org.quartz.Scheduler; import org.quartz.Scheduler;
import org.quartz.SchedulerException;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional; import org.springframework.transaction.annotation.Transactional;
import com.ruoyi.common.constant.ScheduleConstants; import com.ruoyi.common.constant.ScheduleConstants;
import com.ruoyi.common.core.text.Convert; import com.ruoyi.common.core.text.Convert;
import com.ruoyi.common.exception.job.TaskException;
import com.ruoyi.quartz.domain.SysJob; import com.ruoyi.quartz.domain.SysJob;
import com.ruoyi.quartz.mapper.SysJobMapper; import com.ruoyi.quartz.mapper.SysJobMapper;
import com.ruoyi.quartz.service.ISysJobService; import com.ruoyi.quartz.service.ISysJobService;
@ -33,7 +35,7 @@ public class SysJobServiceImpl implements ISysJobService
* *
*/ */
@PostConstruct @PostConstruct
public void init() public void init() throws SchedulerException, TaskException
{ {
List<SysJob> jobList = jobMapper.selectJobAll(); List<SysJob> jobList = jobMapper.selectJobAll();
for (SysJob job : jobList) for (SysJob job : jobList)
@ -82,7 +84,7 @@ public class SysJobServiceImpl implements ISysJobService
*/ */
@Override @Override
@Transactional @Transactional
public int pauseJob(SysJob job) public int pauseJob(SysJob job) throws SchedulerException
{ {
job.setStatus(ScheduleConstants.Status.PAUSE.getValue()); job.setStatus(ScheduleConstants.Status.PAUSE.getValue());
int rows = jobMapper.updateJob(job); int rows = jobMapper.updateJob(job);
@ -100,7 +102,7 @@ public class SysJobServiceImpl implements ISysJobService
*/ */
@Override @Override
@Transactional @Transactional
public int resumeJob(SysJob job) public int resumeJob(SysJob job) throws SchedulerException
{ {
job.setStatus(ScheduleConstants.Status.NORMAL.getValue()); job.setStatus(ScheduleConstants.Status.NORMAL.getValue());
int rows = jobMapper.updateJob(job); int rows = jobMapper.updateJob(job);
@ -118,7 +120,7 @@ public class SysJobServiceImpl implements ISysJobService
*/ */
@Override @Override
@Transactional @Transactional
public int deleteJob(SysJob job) public int deleteJob(SysJob job) throws SchedulerException
{ {
int rows = jobMapper.deleteJobById(job.getJobId()); int rows = jobMapper.deleteJobById(job.getJobId());
if (rows > 0) if (rows > 0)
@ -136,7 +138,7 @@ public class SysJobServiceImpl implements ISysJobService
*/ */
@Override @Override
@Transactional @Transactional
public void deleteJobByIds(String ids) public void deleteJobByIds(String ids) throws SchedulerException
{ {
Long[] jobIds = Convert.toLongArray(ids); Long[] jobIds = Convert.toLongArray(ids);
for (Long jobId : jobIds) for (Long jobId : jobIds)
@ -153,7 +155,7 @@ public class SysJobServiceImpl implements ISysJobService
*/ */
@Override @Override
@Transactional @Transactional
public int changeStatus(SysJob job) public int changeStatus(SysJob job) throws SchedulerException
{ {
int rows = 0; int rows = 0;
String status = job.getStatus(); String status = job.getStatus();
@ -175,9 +177,9 @@ public class SysJobServiceImpl implements ISysJobService
*/ */
@Override @Override
@Transactional @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 @Override
@Transactional @Transactional
public int insertJobCron(SysJob job) public int insertJobCron(SysJob job) throws SchedulerException, TaskException
{ {
job.setStatus(ScheduleConstants.Status.PAUSE.getValue()); job.setStatus(ScheduleConstants.Status.PAUSE.getValue());
int rows = jobMapper.insertJob(job); int rows = jobMapper.insertJob(job);
@ -205,7 +207,7 @@ public class SysJobServiceImpl implements ISysJobService
*/ */
@Override @Override
@Transactional @Transactional
public int updateJobCron(SysJob job) public int updateJobCron(SysJob job) throws SchedulerException, TaskException
{ {
int rows = jobMapper.updateJob(job); int rows = jobMapper.updateJob(job);
if (rows > 0) if (rows > 0)

@ -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<Date> 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;
}

@ -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);
}
}
}

@ -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);
}
}

@ -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);
}
}

@ -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);
}
}
}

@ -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);
}
}
}

@ -2,6 +2,7 @@ package com.ruoyi.quartz.util;
import org.quartz.CronScheduleBuilder; import org.quartz.CronScheduleBuilder;
import org.quartz.CronTrigger; import org.quartz.CronTrigger;
import org.quartz.Job;
import org.quartz.JobBuilder; import org.quartz.JobBuilder;
import org.quartz.JobDataMap; import org.quartz.JobDataMap;
import org.quartz.JobDetail; import org.quartz.JobDetail;
@ -27,6 +28,18 @@ public class ScheduleUtils
{ {
private static final Logger log = LoggerFactory.getLogger(ScheduleUtils.class); private static final Logger log = LoggerFactory.getLogger(ScheduleUtils.class);
/**
* quartz
*
* @param sysJob
* @return
*/
private static Class<? extends Job> getQuartzJobClass(SysJob sysJob)
{
boolean isConcurrent = "0".equals(sysJob.getConcurrent());
return isConcurrent ? QuartzJobExecution.class : QuartzDisallowConcurrentExecution.class;
}
/** /**
* key * key
*/ */
@ -62,12 +75,11 @@ public class ScheduleUtils
/** /**
* *
*/ */
public static void createScheduleJob(Scheduler scheduler, SysJob job) public static void createScheduleJob(Scheduler scheduler, SysJob job) throws SchedulerException, TaskException
{
try
{ {
Class<? extends Job> jobClass = getQuartzJobClass(job);
// 构建job信息 // 构建job信息
JobDetail jobDetail = JobBuilder.newJob(ScheduleJob.class).withIdentity(getJobKey(job.getJobId())).build(); JobDetail jobDetail = JobBuilder.newJob(jobClass).withIdentity(getJobKey(job.getJobId())).build();
// 表达式调度构建器 // 表达式调度构建器
CronScheduleBuilder cronScheduleBuilder = CronScheduleBuilder.cronSchedule(job.getCronExpression()); CronScheduleBuilder cronScheduleBuilder = CronScheduleBuilder.cronSchedule(job.getCronExpression());
@ -88,122 +100,65 @@ public class ScheduleUtils
pauseJob(scheduler, job.getJobId()); pauseJob(scheduler, job.getJobId());
} }
} }
catch (SchedulerException e)
{
log.error("createScheduleJob 异常:", e);
}
catch (TaskException e)
{
log.error("createScheduleJob 异常:", e);
}
}
/** /**
* *
*/ */
public static void updateScheduleJob(Scheduler scheduler, SysJob job) public static void updateScheduleJob(Scheduler scheduler, SysJob job) throws SchedulerException, TaskException
{ {
try JobKey jobKey = getJobKey(job.getJobId());
{
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(); if (scheduler.checkExists(jobKey))
{
// 参数 // 先移除,然后做更新操作
trigger.getJobDataMap().put(ScheduleConstants.TASK_PROPERTIES, job); scheduler.deleteJob(jobKey);
}
scheduler.rescheduleJob(triggerKey, trigger); createScheduleJob(scheduler, job);
// 暂停任务 // 暂停任务
if (job.getStatus().equals(ScheduleConstants.Status.PAUSE.getValue())) if (job.getStatus().equals(ScheduleConstants.Status.PAUSE.getValue()))
{ {
pauseJob(scheduler, job.getJobId()); pauseJob(scheduler, job.getJobId());
} }
}
catch (SchedulerException e)
{
log.error("SchedulerException 异常:", e);
}
catch (TaskException e)
{
log.error("SchedulerException 异常:", e);
}
} }
/** /**
* *
*/ */
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(); JobDataMap dataMap = new JobDataMap();
dataMap.put(ScheduleConstants.TASK_PROPERTIES, job); dataMap.put(ScheduleConstants.TASK_PROPERTIES, job);
scheduler.triggerJob(getJobKey(job.getJobId()), dataMap); scheduler.triggerJob(getJobKey(job.getJobId()), dataMap);
rows = 1;
}
catch (SchedulerException e)
{
log.error("run 异常:", e);
}
return rows;
} }
/** /**
* *
*/ */
public static void pauseJob(Scheduler scheduler, Long jobId) public static void pauseJob(Scheduler scheduler, Long jobId) throws SchedulerException
{
try
{ {
scheduler.pauseJob(getJobKey(jobId)); scheduler.pauseJob(getJobKey(jobId));
} }
catch (SchedulerException e)
{
log.error("pauseJob 异常:", e);
}
}
/** /**
* *
*/ */
public static void resumeJob(Scheduler scheduler, Long jobId) public static void resumeJob(Scheduler scheduler, Long jobId) throws SchedulerException
{
try
{ {
scheduler.resumeJob(getJobKey(jobId)); scheduler.resumeJob(getJobKey(jobId));
} }
catch (SchedulerException e)
{
log.error("resumeJob 异常:", e);
}
}
/** /**
* *
*/ */
public static void deleteScheduleJob(Scheduler scheduler, Long jobId) public static void deleteScheduleJob(Scheduler scheduler, Long jobId) throws SchedulerException
{
try
{ {
scheduler.deleteJob(getJobKey(jobId)); scheduler.deleteJob(getJobKey(jobId));
} }
catch (SchedulerException e)
{
log.error("deleteScheduleJob 异常:", e);
}
}
public static CronScheduleBuilder handleCronScheduleMisfirePolicy(SysJob job, CronScheduleBuilder cb) public static CronScheduleBuilder handleCronScheduleMisfirePolicy(SysJob job, CronScheduleBuilder cb)
throws TaskException throws TaskException
@ -219,7 +174,8 @@ public class ScheduleUtils
case ScheduleConstants.MISFIRE_DO_NOTHING: case ScheduleConstants.MISFIRE_DO_NOTHING:
return cb.withMisfireHandlingInstructionDoNothing(); return cb.withMisfireHandlingInstructionDoNothing();
default: 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);
} }
} }
} }

@ -12,6 +12,7 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
<result property="methodParams" column="method_params" /> <result property="methodParams" column="method_params" />
<result property="cronExpression" column="cron_expression" /> <result property="cronExpression" column="cron_expression" />
<result property="misfirePolicy" column="misfire_policy" /> <result property="misfirePolicy" column="misfire_policy" />
<result property="concurrent" column="concurrent" />
<result property="status" column="status" /> <result property="status" column="status" />
<result property="createBy" column="create_by" /> <result property="createBy" column="create_by" />
<result property="createTime" column="create_time" /> <result property="createTime" column="create_time" />
@ -21,7 +22,7 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
</resultMap> </resultMap>
<sql id="selectJobVo"> <sql id="selectJobVo">
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 from sys_job
</sql> </sql>
@ -69,6 +70,7 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
<if test="methodParams != null">method_params = #{methodParams},</if> <if test="methodParams != null">method_params = #{methodParams},</if>
<if test="cronExpression != null and cronExpression != ''">cron_expression = #{cronExpression},</if> <if test="cronExpression != null and cronExpression != ''">cron_expression = #{cronExpression},</if>
<if test="misfirePolicy != null and misfirePolicy != ''">misfire_policy = #{misfirePolicy},</if> <if test="misfirePolicy != null and misfirePolicy != ''">misfire_policy = #{misfirePolicy},</if>
<if test="concurrent != null and concurrent != ''">concurrent = #{concurrent},</if>
<if test="status !=null">status = #{status},</if> <if test="status !=null">status = #{status},</if>
<if test="remark != null and remark != ''">remark = #{remark},</if> <if test="remark != null and remark != ''">remark = #{remark},</if>
<if test="updateBy != null and updateBy != ''">update_by = #{updateBy},</if> <if test="updateBy != null and updateBy != ''">update_by = #{updateBy},</if>
@ -86,6 +88,7 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
<if test="methodParams != null and methodParams != ''">method_params,</if> <if test="methodParams != null and methodParams != ''">method_params,</if>
<if test="cronExpression != null and cronExpression != ''">cron_expression,</if> <if test="cronExpression != null and cronExpression != ''">cron_expression,</if>
<if test="misfirePolicy != null and misfirePolicy != ''">misfire_policy,</if> <if test="misfirePolicy != null and misfirePolicy != ''">misfire_policy,</if>
<if test="concurrent != null and concurrent != ''">concurrent,</if>
<if test="status != null and status != ''">status,</if> <if test="status != null and status != ''">status,</if>
<if test="remark != null and remark != ''">remark,</if> <if test="remark != null and remark != ''">remark,</if>
<if test="createBy != null and createBy != ''">create_by,</if> <if test="createBy != null and createBy != ''">create_by,</if>
@ -98,6 +101,7 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
<if test="methodParams != null and methodParams != ''">#{methodParams},</if> <if test="methodParams != null and methodParams != ''">#{methodParams},</if>
<if test="cronExpression != null and cronExpression != ''">#{cronExpression},</if> <if test="cronExpression != null and cronExpression != ''">#{cronExpression},</if>
<if test="misfirePolicy != null and misfirePolicy != ''">#{misfirePolicy},</if> <if test="misfirePolicy != null and misfirePolicy != ''">#{misfirePolicy},</if>
<if test="concurrent != null and concurrent != ''">#{concurrent},</if>
<if test="status != null and status != ''">#{status},</if> <if test="status != null and status != ''">#{status},</if>
<if test="remark != null and remark != ''">#{remark},</if> <if test="remark != null and remark != ''">#{remark},</if>
<if test="createBy != null and createBy != ''">#{createBy},</if> <if test="createBy != null and createBy != ''">#{createBy},</if>

@ -43,6 +43,13 @@
<label class="radio-box"> <input type="radio" name="misfirePolicy" value="3" /> 放弃执行 </label> <label class="radio-box"> <input type="radio" name="misfirePolicy" value="3" /> 放弃执行 </label>
</div> </div>
</div> </div>
<div class="form-group">
<label class="col-sm-3 control-label">并发执行:</label>
<div class="col-sm-8">
<label class="radio-box"> <input type="radio" name="concurrent" value="0"/> 允许 </label>
<label class="radio-box"> <input type="radio" name="concurrent" value="1" th:checked="true"/> 禁止 </label>
</div>
</div>
<div class="form-group"> <div class="form-group">
<label class="col-sm-3 control-label">状态:</label> <label class="col-sm-3 control-label">状态:</label>
<div class="col-sm-8"> <div class="col-sm-8">

@ -82,6 +82,11 @@
<div class="form-control-static" th:if="${job.misfirePolicy == '2'}">执行一次</div> <div class="form-control-static" th:if="${job.misfirePolicy == '2'}">执行一次</div>
<div class="form-control-static" th:if="${job.misfirePolicy == '3'}">放弃执行</div> <div class="form-control-static" th:if="${job.misfirePolicy == '3'}">放弃执行</div>
</div> </div>
<div class="form-group">
<label class="col-sm-3 control-label">并发执行:</label>
<div class="form-control-static" th:class="${job.concurrent == '0' ? 'label label-primary' : 'label label-danger'}" th:text="${job.concurrent == '0' ? '允许' : '禁止'}">
</div>
</div>
<div class="form-group"> <div class="form-group">
<label class="col-sm-3 control-label">执行状态:</label> <label class="col-sm-3 control-label">执行状态:</label>
<div class="form-control-static" th:class="${job.status == '0' ? 'label label-primary' : 'label label-danger'}" th:text="${job.status == '0' ? '正常' : '暂停'}"> <div class="form-control-static" th:class="${job.status == '0' ? 'label label-primary' : 'label label-danger'}" th:text="${job.status == '0' ? '正常' : '暂停'}">

@ -44,6 +44,13 @@
<label class="radio-box"> <input type="radio" th:field="*{misfirePolicy}" name="misfirePolicy" value="3" /> 放弃执行 </label> <label class="radio-box"> <input type="radio" th:field="*{misfirePolicy}" name="misfirePolicy" value="3" /> 放弃执行 </label>
</div> </div>
</div> </div>
<div class="form-group">
<label class="col-sm-3 control-label">并发执行:</label>
<div class="col-sm-8">
<label class="radio-box"> <input type="radio" th:field="*{concurrent}" name="concurrent" value="0"/> 允许 </label>
<label class="radio-box"> <input type="radio" th:field="*{concurrent}" name="concurrent" value="1"/> 禁止 </label>
</div>
</div>
<div class="form-group"> <div class="form-group">
<label class="col-sm-3 control-label">状态:</label> <label class="col-sm-3 control-label">状态:</label>
<div class="col-sm-8"> <div class="col-sm-8">

@ -122,7 +122,7 @@
align: 'center', align: 'center',
formatter: function(value, row, index) { formatter: function(value, row, index) {
var actions = []; var actions = [];
actions.push('<a class="btn btn-primary btn-xs ' + statusFlag + '" href="#" onclick="run(\'' + row.jobId + '\')"><i class="fa fa-play-circle-o"></i> 执行</a> '); actions.push('<a class="btn btn-primary btn-xs ' + statusFlag + '" href="#" onclick="run(\'' + row.jobId + '\')"><i class="fa fa-play-circle-o"></i> 执行一次</a> ');
actions.push('<a class="btn btn-warning btn-xs ' + detailFlag + '" href="#" onclick="$.operate.detail(\'' + row.jobId + '\')"><i class="fa fa-search"></i>详细</a> '); actions.push('<a class="btn btn-warning btn-xs ' + detailFlag + '" href="#" onclick="$.operate.detail(\'' + row.jobId + '\')"><i class="fa fa-search"></i>详细</a> ');
return actions.join(''); return actions.join('');
} }
@ -142,7 +142,7 @@
/* 立即执行一次 */ /* 立即执行一次 */
function run(jobId) { function run(jobId) {
$.modal.confirm("确认要立即执行任务吗?", function() { $.modal.confirm("确认要立即执行一次任务吗?", function() {
$.operate.post(prefix + "/run", { "jobId": jobId}); $.operate.post(prefix + "/run", { "jobId": jobId});
}) })
} }

@ -568,6 +568,7 @@ create table sys_job (
method_params varchar(50) default null comment '方法参数', method_params varchar(50) default null comment '方法参数',
cron_expression varchar(255) default '' comment 'cron执行表达式', cron_expression varchar(255) default '' comment 'cron执行表达式',
misfire_policy varchar(20) default '3' comment '计划执行错误策略1立即执行 2执行一次 3放弃执行', 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暂停', status char(1) default '0' comment '状态0正常 1暂停',
create_by varchar(64) default '' comment '创建者', create_by varchar(64) default '' comment '创建者',
create_time datetime comment '创建时间', create_time datetime comment '创建时间',
@ -577,8 +578,8 @@ create table sys_job (
primary key (job_id, job_name, job_group) primary key (job_id, job_name, job_group)
) engine=innodb auto_increment=100 default charset=utf8 comment = '定时任务调度表'; ) 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(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', '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', '');
-- ---------------------------- -- ----------------------------
Loading…
Cancel
Save