任务地址支持配置多个,进行failover

v1.3
xueli.xue 9 years ago
parent e58f77ddee
commit 71e12272c3

@ -150,17 +150,19 @@ CREATE TABLE `xxl_job_qrtz_trigger_info` (
`job_cron` varchar(128) NOT NULL COMMENT '任务执行CORN', `job_cron` varchar(128) NOT NULL COMMENT '任务执行CORN',
`job_desc` varchar(255) NOT NULL, `job_desc` varchar(255) NOT NULL,
`job_class` varchar(255) NOT NULL COMMENT '任务执行JobBean', `job_class` varchar(255) NOT NULL COMMENT '任务执行JobBean',
`job_data` varchar(512) DEFAULT NULL COMMENT '任务执行数据',
`add_time` datetime DEFAULT NULL, `add_time` datetime DEFAULT NULL,
`update_time` datetime DEFAULT NULL, `update_time` datetime DEFAULT NULL,
`author` varchar(64) DEFAULT NULL COMMENT '作者', `author` varchar(64) DEFAULT NULL COMMENT '作者',
`alarm_email` varchar(255) DEFAULT NULL COMMENT '报警邮件', `alarm_email` varchar(255) DEFAULT NULL COMMENT '报警邮件',
`alarm_threshold` int(11) DEFAULT NULL COMMENT '报警阀值(连续失败次数)', `alarm_threshold` int(11) DEFAULT NULL COMMENT '报警阀值(连续失败次数)',
`executor_address` varchar(255) DEFAULT NULL COMMENT '执行器地址,有多个则逗号分隔',
`executor_handler` varchar(255) DEFAULT NULL COMMENT '执行器任务handler',
`executor_param` varchar(255) DEFAULT NULL COMMENT '执行器任务参数',
`glue_switch` int(11) DEFAULT '0' COMMENT 'GLUE模式开关0-否1-是', `glue_switch` int(11) DEFAULT '0' COMMENT 'GLUE模式开关0-否1-是',
`glue_source` text COMMENT 'GLUE源代码', `glue_source` text COMMENT 'GLUE源代码',
`glue_remark` varchar(128) DEFAULT NULL COMMENT 'GLUE备注', `glue_remark` varchar(128) DEFAULT NULL COMMENT 'GLUE备注',
PRIMARY KEY (`id`) PRIMARY KEY (`id`)
); ) ENGINE=InnoDB AUTO_INCREMENT=4 DEFAULT CHARSET=utf8;
CREATE TABLE `xxl_job_qrtz_trigger_log` ( CREATE TABLE `xxl_job_qrtz_trigger_log` (
`id` int(11) NOT NULL AUTO_INCREMENT, `id` int(11) NOT NULL AUTO_INCREMENT,
@ -169,7 +171,9 @@ CREATE TABLE `xxl_job_qrtz_trigger_log` (
`job_cron` varchar(128) NOT NULL COMMENT '任务执行CORN表达式', `job_cron` varchar(128) NOT NULL COMMENT '任务执行CORN表达式',
`job_desc` varchar(255) NOT NULL, `job_desc` varchar(255) NOT NULL,
`job_class` varchar(255) NOT NULL COMMENT '任务执行JobBean', `job_class` varchar(255) NOT NULL COMMENT '任务执行JobBean',
`job_data` varchar(512) DEFAULT NULL COMMENT '任务执行数据', `executor_address` varchar(255) DEFAULT NULL COMMENT '执行器地址,本次执行的地址',
`executor_handler` varchar(255) DEFAULT NULL COMMENT '执行器任务handler',
`executor_param` varchar(255) DEFAULT NULL COMMENT 'executor_param',
`trigger_time` datetime DEFAULT NULL COMMENT '调度-时间', `trigger_time` datetime DEFAULT NULL COMMENT '调度-时间',
`trigger_status` varchar(255) DEFAULT NULL COMMENT '调度-结果', `trigger_status` varchar(255) DEFAULT NULL COMMENT '调度-结果',
`trigger_msg` varchar(2048) DEFAULT NULL COMMENT '调度-日志', `trigger_msg` varchar(2048) DEFAULT NULL COMMENT '调度-日志',
@ -177,7 +181,7 @@ CREATE TABLE `xxl_job_qrtz_trigger_log` (
`handle_status` varchar(255) DEFAULT NULL COMMENT '执行-状态', `handle_status` varchar(255) DEFAULT NULL COMMENT '执行-状态',
`handle_msg` varchar(2048) DEFAULT NULL COMMENT '执行-日志', `handle_msg` varchar(2048) DEFAULT NULL COMMENT '执行-日志',
PRIMARY KEY (`id`) PRIMARY KEY (`id`)
); ) ENGINE=InnoDB AUTO_INCREMENT=33 DEFAULT CHARSET=utf8;
CREATE TABLE `xxl_job_qrtz_trigger_logglue` ( CREATE TABLE `xxl_job_qrtz_trigger_logglue` (
`id` int(11) NOT NULL AUTO_INCREMENT, `id` int(11) NOT NULL AUTO_INCREMENT,
@ -188,7 +192,8 @@ CREATE TABLE `xxl_job_qrtz_trigger_logglue` (
`add_time` timestamp NULL DEFAULT NULL, `add_time` timestamp NULL DEFAULT NULL,
`update_time` timestamp NULL DEFAULT NULL ON UPDATE CURRENT_TIMESTAMP, `update_time` timestamp NULL DEFAULT NULL ON UPDATE CURRENT_TIMESTAMP,
PRIMARY KEY (`id`) PRIMARY KEY (`id`)
) ; ) ENGINE=InnoDB AUTO_INCREMENT=5 DEFAULT CHARSET=utf8;
commit; commit;

@ -1,14 +1,9 @@
package com.xxl.job.admin.controller; package com.xxl.job.admin.controller;
import java.util.HashMap;
import java.util.List;
import java.util.Map; import java.util.Map;
import javax.annotation.Resource; import javax.annotation.Resource;
import org.apache.commons.lang.StringUtils;
import org.quartz.CronExpression;
import org.quartz.SchedulerException;
import org.springframework.stereotype.Controller; import org.springframework.stereotype.Controller;
import org.springframework.ui.Model; import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestMapping;
@ -16,15 +11,8 @@ import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.ResponseBody; import org.springframework.web.bind.annotation.ResponseBody;
import com.xxl.job.admin.core.constant.Constants.JobGroupEnum; import com.xxl.job.admin.core.constant.Constants.JobGroupEnum;
import com.xxl.job.admin.core.jobbean.RemoteHttpJobBean;
import com.xxl.job.admin.core.model.ReturnT; import com.xxl.job.admin.core.model.ReturnT;
import com.xxl.job.admin.core.model.XxlJobInfo; import com.xxl.job.admin.service.IXxlJobService;
import com.xxl.job.admin.core.util.DynamicSchedulerUtil;
import com.xxl.job.admin.dao.IXxlJobInfoDao;
import com.xxl.job.admin.dao.IXxlJobLogDao;
import com.xxl.job.admin.dao.IXxlJobLogGlueDao;
import com.xxl.job.core.handler.HandlerRepository;
import com.xxl.job.core.util.JacksonUtil;
/** /**
* index controller * index controller
@ -35,11 +23,7 @@ import com.xxl.job.core.util.JacksonUtil;
public class JobInfoController { public class JobInfoController {
@Resource @Resource
private IXxlJobInfoDao xxlJobInfoDao; private IXxlJobService xxlJobService;
@Resource
public IXxlJobLogDao xxlJobLogDao;
@Resource
private IXxlJobLogGlueDao xxlJobLogGlueDao;
@RequestMapping @RequestMapping
public String index(Model model) { public String index(Model model) {
@ -53,219 +37,52 @@ public class JobInfoController {
@RequestParam(required = false, defaultValue = "10") int length, @RequestParam(required = false, defaultValue = "10") int length,
String jobGroup, String jobName, String filterTime) { String jobGroup, String jobName, String filterTime) {
// page list return xxlJobService.pageList(start, length, jobGroup, jobName, filterTime);
List<XxlJobInfo> list = xxlJobInfoDao.pageList(start, length, jobGroup, jobName);
int list_count = xxlJobInfoDao.pageListCount(start, length, jobGroup, jobName);
// fill job info
if (list!=null && list.size()>0) {
for (XxlJobInfo jobInfo : list) {
DynamicSchedulerUtil.fillJobInfo(jobInfo);
}
}
// package result
Map<String, Object> maps = new HashMap<String, Object>();
maps.put("recordsTotal", list_count); // 总记录数
maps.put("recordsFiltered", list_count); // 过滤后的总记录数
maps.put("data", list); // 分页列表
return maps;
} }
@RequestMapping("/add") @RequestMapping("/add")
@ResponseBody @ResponseBody
public ReturnT<String> add(String jobGroup, String jobName, String jobCron, String jobDesc, public ReturnT<String> add(String jobGroup, String jobName, String jobCron, String jobDesc,
String handler_address, String handler_name, String handler_params, String executorAddress, String executorHandler, String executorParam,
String author, String alarmEmail, int alarmThreshold, String author, String alarmEmail, int alarmThreshold,
int glueSwitch, String glueSource, String glueRemark) { int glueSwitch, String glueSource, String glueRemark) {
// valid return xxlJobService.add(jobGroup, jobName, jobCron, jobDesc, executorAddress, executorHandler, executorParam,
if (JobGroupEnum.match(jobGroup) == null) { author, alarmEmail, alarmThreshold, glueSwitch, glueSource, glueRemark);
return new ReturnT<String>(500, "请选择“任务组”");
}
if (StringUtils.isBlank(jobName)) {
return new ReturnT<String>(500, "请输入“任务名”");
}
if (!CronExpression.isValidExpression(jobCron)) {
return new ReturnT<String>(500, "“corn”不合法");
}
if (StringUtils.isBlank(jobDesc)) {
return new ReturnT<String>(500, "请输入“任务描述”");
}
if (StringUtils.isBlank(handler_address)) {
return new ReturnT<String>(500, "请输入“执行器地址”");
}
if (glueSwitch==0 && StringUtils.isBlank(handler_name)) {
return new ReturnT<String>(500, "请输入“jobHandler”");
}
if (StringUtils.isBlank(author)) {
return new ReturnT<String>(500, "请输入“负责人”");
}
if (StringUtils.isBlank(alarmEmail)) {
return new ReturnT<String>(500, "请输入“报警邮件”");
}
if (alarmThreshold < 0) {
alarmThreshold = 0;
}
try {
if (DynamicSchedulerUtil.checkExists(jobName, jobGroup)) {
return new ReturnT<String>(500, "此任务已存在,请更换任务组或任务名");
}
} catch (SchedulerException e1) {
e1.printStackTrace();
return new ReturnT<String>(500, "此任务已存在,请更换任务组或任务名");
}
// parse jobDataMap
HashMap<String, String> jobDataMap = new HashMap<String, String>();
jobDataMap.put(HandlerRepository.HANDLER_ADDRESS, handler_address);
jobDataMap.put(HandlerRepository.HANDLER_NAME, handler_name);
jobDataMap.put(HandlerRepository.HANDLER_PARAMS, handler_params);
// Backup to the database
XxlJobInfo jobInfo = new XxlJobInfo();
jobInfo.setJobGroup(jobGroup);
jobInfo.setJobName(jobName);
jobInfo.setJobCron(jobCron);
jobInfo.setJobDesc(jobDesc);
jobInfo.setJobClass(RemoteHttpJobBean.class.getName());
jobInfo.setJobData(JacksonUtil.writeValueAsString(jobDataMap));
jobInfo.setAuthor(author);
jobInfo.setAlarmEmail(alarmEmail);
jobInfo.setAlarmThreshold(alarmThreshold);
jobInfo.setGlueSwitch(glueSwitch);
jobInfo.setGlueSource(glueSource);
jobInfo.setGlueRemark(glueRemark);
xxlJobInfoDao.save(jobInfo);
try {
// add job 2 quartz
boolean result = DynamicSchedulerUtil.addJob(jobInfo);
if (result) {
return ReturnT.SUCCESS;
} else {
xxlJobInfoDao.delete(jobGroup, jobName);
return new ReturnT<String>(500, "新增任务失败");
}
} catch (SchedulerException e) {
e.printStackTrace();
}
return ReturnT.FAIL;
} }
@RequestMapping("/reschedule") @RequestMapping("/reschedule")
@ResponseBody @ResponseBody
public ReturnT<String> reschedule(String jobGroup, String jobName, String jobCron, String jobDesc, public ReturnT<String> reschedule(String jobGroup, String jobName, String jobCron, String jobDesc,
String handler_address, String handler_name, String handler_params, String executorAddress, String executorHandler, String executorParam,
String author, String alarmEmail, int alarmThreshold, int glueSwitch) { String author, String alarmEmail, int alarmThreshold, int glueSwitch) {
// valid return xxlJobService.reschedule(jobGroup, jobName, jobCron, jobDesc, executorAddress, executorHandler, executorParam, author,
if (JobGroupEnum.match(jobGroup) == null) { alarmEmail, alarmThreshold, glueSwitch);
return new ReturnT<String>(500, "请选择“任务组”");
}
if (StringUtils.isBlank(jobName)) {
return new ReturnT<String>(500, "请输入“任务名”");
}
if (!CronExpression.isValidExpression(jobCron)) {
return new ReturnT<String>(500, "“corn”不合法");
}
if (StringUtils.isBlank(jobDesc)) {
return new ReturnT<String>(500, "请输入“任务描述”");
}
if (StringUtils.isBlank(handler_address)) {
return new ReturnT<String>(500, "请输入“执行器地址”");
}
if (glueSwitch==0 && StringUtils.isBlank(handler_name)) {
return new ReturnT<String>(500, "请输入“jobHandler”");
}
if (StringUtils.isBlank(author)) {
return new ReturnT<String>(500, "请输入“负责人”");
}
if (StringUtils.isBlank(alarmEmail)) {
return new ReturnT<String>(500, "请输入“报警邮件”");
}
if (alarmThreshold < 0) {
alarmThreshold = 0;
}
// parse jobDataMap
HashMap<String, String> jobDataMap = new HashMap<String, String>();
jobDataMap.put(HandlerRepository.HANDLER_ADDRESS, handler_address);
jobDataMap.put(HandlerRepository.HANDLER_NAME, handler_name);
jobDataMap.put(HandlerRepository.HANDLER_PARAMS, handler_params);
XxlJobInfo jobInfo = xxlJobInfoDao.load(jobGroup, jobName);
jobInfo.setJobCron(jobCron);
jobInfo.setJobDesc(jobDesc);
jobInfo.setJobData(JacksonUtil.writeValueAsString(jobDataMap));
jobInfo.setAuthor(author);
jobInfo.setAlarmEmail(alarmEmail);
jobInfo.setAlarmThreshold(alarmThreshold);
jobInfo.setGlueSwitch(glueSwitch);
try {
// fresh quartz
DynamicSchedulerUtil.rescheduleJob(jobInfo);
// fresh db
xxlJobInfoDao.update(jobInfo);
return ReturnT.SUCCESS;
} catch (SchedulerException e) {
e.printStackTrace();
}
return ReturnT.FAIL;
} }
@RequestMapping("/remove") @RequestMapping("/remove")
@ResponseBody @ResponseBody
public ReturnT<String> remove(String jobGroup, String jobName) { public ReturnT<String> remove(String jobGroup, String jobName) {
try { return xxlJobService.remove(jobGroup, jobName);
DynamicSchedulerUtil.removeJob(jobName, jobGroup);
xxlJobInfoDao.delete(jobGroup, jobName);
xxlJobLogDao.delete(jobGroup, jobName);
xxlJobLogGlueDao.delete(jobGroup, jobName);
return ReturnT.SUCCESS;
} catch (SchedulerException e) {
e.printStackTrace();
}
return ReturnT.FAIL;
} }
@RequestMapping("/pause") @RequestMapping("/pause")
@ResponseBody @ResponseBody
public ReturnT<String> pause(String jobGroup, String jobName) { public ReturnT<String> pause(String jobGroup, String jobName) {
try { return xxlJobService.pause(jobGroup, jobName);
DynamicSchedulerUtil.pauseJob(jobName, jobGroup); // jobStatus do not store
return ReturnT.SUCCESS;
} catch (SchedulerException e) {
e.printStackTrace();
return ReturnT.FAIL;
}
} }
@RequestMapping("/resume") @RequestMapping("/resume")
@ResponseBody @ResponseBody
public ReturnT<String> resume(String jobGroup, String jobName) { public ReturnT<String> resume(String jobGroup, String jobName) {
try { return xxlJobService.resume(jobGroup, jobName);
DynamicSchedulerUtil.resumeJob(jobName, jobGroup);
return ReturnT.SUCCESS;
} catch (SchedulerException e) {
e.printStackTrace();
return ReturnT.FAIL;
}
} }
@RequestMapping("/trigger") @RequestMapping("/trigger")
@ResponseBody @ResponseBody
public ReturnT<String> triggerJob(String jobGroup, String jobName) { public ReturnT<String> triggerJob(String jobGroup, String jobName) {
try { return xxlJobService.triggerJob(jobGroup, jobName);
DynamicSchedulerUtil.triggerJob(jobName, jobGroup);
return ReturnT.SUCCESS;
} catch (SchedulerException e) {
e.printStackTrace();
return ReturnT.FAIL;
}
} }
} }

@ -20,10 +20,10 @@ import com.xxl.job.admin.core.constant.Constants.JobGroupEnum;
import com.xxl.job.admin.core.model.ReturnT; import com.xxl.job.admin.core.model.ReturnT;
import com.xxl.job.admin.core.model.XxlJobLog; import com.xxl.job.admin.core.model.XxlJobLog;
import com.xxl.job.admin.dao.IXxlJobLogDao; import com.xxl.job.admin.dao.IXxlJobLogDao;
import com.xxl.job.core.handler.HandlerRepository; import com.xxl.job.core.handler.HandlerRepository.ActionEnum;
import com.xxl.job.core.handler.HandlerRepository.HandlerParamEnum;
import com.xxl.job.core.util.HttpUtil; import com.xxl.job.core.util.HttpUtil;
import com.xxl.job.core.util.HttpUtil.RemoteCallBack; import com.xxl.job.core.util.HttpUtil.RemoteCallBack;
import com.xxl.job.core.util.JacksonUtil;
/** /**
* index controller * index controller
@ -41,7 +41,7 @@ public class JobLogController {
model.addAttribute("jobGroup", jobGroup); model.addAttribute("jobGroup", jobGroup);
model.addAttribute("jobName", jobName); model.addAttribute("jobName", jobName);
model.addAttribute("JobGroupList", JobGroupEnum.values()); model.addAttribute("JobGroupList", JobGroupEnum.values());
return "joblog/index"; return "joblog/joblog.index";
} }
@RequestMapping("/pageList") @RequestMapping("/pageList")
@ -101,21 +101,18 @@ public class JobLogController {
if (log == null) { if (log == null) {
return new ReturnT<String>(500, "参数异常"); return new ReturnT<String>(500, "参数异常");
} }
if (!RemoteCallBack.SUCCESS.equals(log.getTriggerStatus())) {
// server address return new ReturnT<String>(500, "调度失败,无法查看执行日志");
@SuppressWarnings("unchecked")
Map<String, String> jobDataMap = JacksonUtil.readValue(log.getJobData(), Map.class);
String handler_address = jobDataMap.get(HandlerRepository.HANDLER_ADDRESS);
if (!handler_address.startsWith("http")){
handler_address = "http://" + handler_address + "/";
} }
// trigger id, trigger time // trigger id, trigger time
Map<String, String> reqMap = new HashMap<String, String>(); Map<String, String> reqMap = new HashMap<String, String>();
reqMap.put(HandlerRepository.NAMESPACE, HandlerRepository.NameSpaceEnum.LOG.name()); reqMap.put(HandlerParamEnum.TIMESTAMP.name(), String.valueOf(System.currentTimeMillis()));
reqMap.put(HandlerRepository.TRIGGER_LOG_ID, String.valueOf(id)); reqMap.put(HandlerParamEnum.ACTION.name(), ActionEnum.LOG.name());
reqMap.put(HandlerRepository.TRIGGER_TIMESTAMP, String.valueOf(log.getTriggerTime().getTime())); reqMap.put(HandlerParamEnum.LOG_ID.name(), String.valueOf(id));
reqMap.put(HandlerParamEnum.LOG_DATE.name(), String.valueOf(log.getTriggerTime().getTime()));
RemoteCallBack callBack = HttpUtil.post(handler_address, reqMap); RemoteCallBack callBack = HttpUtil.post(HttpUtil.addressToUrl(log.getExecutorAddress()), reqMap);
if (HttpUtil.RemoteCallBack.SUCCESS.equals(callBack.getStatus())) { if (HttpUtil.RemoteCallBack.SUCCESS.equals(callBack.getStatus())) {
return new ReturnT<String>(callBack.getMsg()); return new ReturnT<String>(callBack.getMsg());
} else { } else {
@ -138,23 +135,19 @@ public class JobLogController {
if (log == null) { if (log == null) {
return new ReturnT<String>(500, "参数异常"); return new ReturnT<String>(500, "参数异常");
} }
if (!RemoteCallBack.SUCCESS.equals(log.getTriggerStatus())) {
// server address return new ReturnT<String>(500, "调度失败,无法终止日志");
@SuppressWarnings("unchecked")
Map<String, String> jobDataMap = JacksonUtil.readValue(log.getJobData(), Map.class);
String handler_address = jobDataMap.get(HandlerRepository.HANDLER_ADDRESS);
if (!handler_address.startsWith("http")){
handler_address = "http://" + handler_address + "/";
} }
String handler_name = jobDataMap.get(HandlerRepository.HANDLER_NAME);
// trigger id, trigger time // request
Map<String, String> reqMap = new HashMap<String, String>(); Map<String, String> reqMap = new HashMap<String, String>();
reqMap.put(HandlerRepository.NAMESPACE, HandlerRepository.NameSpaceEnum.KILL.name()); reqMap.put(HandlerParamEnum.TIMESTAMP.name(), String.valueOf(System.currentTimeMillis()));
reqMap.put(HandlerRepository.HANDLER_NAME, handler_name); reqMap.put(HandlerParamEnum.ACTION.name(), ActionEnum.KILL.name());
reqMap.put(HandlerRepository.TRIGGER_TIMESTAMP, String.valueOf(System.currentTimeMillis())); reqMap.put(HandlerParamEnum.EXECUTOR_HANDLER.name(), log.getExecutorHandler());
reqMap.put(HandlerParamEnum.JOB_GROUP.name(), log.getJobGroup());
reqMap.put(HandlerParamEnum.JOB_NAME.name(), log.getJobName());
RemoteCallBack callBack = HttpUtil.post(handler_address, reqMap); RemoteCallBack callBack = HttpUtil.post(HttpUtil.addressToUrl(log.getExecutorAddress()), reqMap);
if (HttpUtil.RemoteCallBack.SUCCESS.equals(callBack.getStatus())) { if (HttpUtil.RemoteCallBack.SUCCESS.equals(callBack.getStatus())) {
log.setHandleStatus(HttpUtil.RemoteCallBack.FAIL); log.setHandleStatus(HttpUtil.RemoteCallBack.FAIL);
log.setHandleMsg("人为操作主动终止"); log.setHandleMsg("人为操作主动终止");

@ -1,8 +1,8 @@
package com.xxl.job.admin.core.jobbean; package com.xxl.job.admin.core.jobbean;
import java.text.MessageFormat;
import java.util.Date; import java.util.Date;
import java.util.HashMap; import java.util.HashMap;
import java.util.Map;
import org.quartz.JobExecutionContext; import org.quartz.JobExecutionContext;
import org.quartz.JobExecutionException; import org.quartz.JobExecutionException;
@ -16,10 +16,10 @@ import com.xxl.job.admin.core.model.XxlJobInfo;
import com.xxl.job.admin.core.model.XxlJobLog; import com.xxl.job.admin.core.model.XxlJobLog;
import com.xxl.job.admin.core.thread.JobMonitorHelper; import com.xxl.job.admin.core.thread.JobMonitorHelper;
import com.xxl.job.admin.core.util.DynamicSchedulerUtil; import com.xxl.job.admin.core.util.DynamicSchedulerUtil;
import com.xxl.job.core.handler.HandlerRepository; import com.xxl.job.core.handler.HandlerRepository.ActionEnum;
import com.xxl.job.core.handler.HandlerRepository.HandlerParamEnum;
import com.xxl.job.core.util.HttpUtil; import com.xxl.job.core.util.HttpUtil;
import com.xxl.job.core.util.HttpUtil.RemoteCallBack; import com.xxl.job.core.util.HttpUtil.RemoteCallBack;
import com.xxl.job.core.util.JacksonUtil;
/** /**
* http job bean * http job bean
@ -30,14 +30,12 @@ import com.xxl.job.core.util.JacksonUtil;
public class RemoteHttpJobBean extends QuartzJobBean { public class RemoteHttpJobBean extends QuartzJobBean {
private static Logger logger = LoggerFactory.getLogger(RemoteHttpJobBean.class); private static Logger logger = LoggerFactory.getLogger(RemoteHttpJobBean.class);
@SuppressWarnings("unchecked")
@Override @Override
protected void executeInternal(JobExecutionContext context) protected void executeInternal(JobExecutionContext context)
throws JobExecutionException { throws JobExecutionException {
JobKey jobKey = context.getTrigger().getJobKey(); JobKey jobKey = context.getTrigger().getJobKey();
XxlJobInfo jobInfo = DynamicSchedulerUtil.xxlJobInfoDao.load(jobKey.getGroup(), jobKey.getName()); XxlJobInfo jobInfo = DynamicSchedulerUtil.xxlJobInfoDao.load(jobKey.getGroup(), jobKey.getName());
HashMap<String, String> jobDataMap = (HashMap<String, String>) JacksonUtil.readValueRefer(jobInfo.getJobData(), Map.class);
// save log // save log
XxlJobLog jobLog = new XxlJobLog(); XxlJobLog jobLog = new XxlJobLog();
jobLog.setJobGroup(jobInfo.getJobGroup()); jobLog.setJobGroup(jobInfo.getJobGroup());
@ -45,35 +43,30 @@ public class RemoteHttpJobBean extends QuartzJobBean {
jobLog.setJobCron(jobInfo.getJobCron()); jobLog.setJobCron(jobInfo.getJobCron());
jobLog.setJobDesc(jobInfo.getJobDesc()); jobLog.setJobDesc(jobInfo.getJobDesc());
jobLog.setJobClass(jobInfo.getJobClass()); jobLog.setJobClass(jobInfo.getJobClass());
jobLog.setJobData(jobInfo.getJobData());
jobLog.setJobClass(RemoteHttpJobBean.class.getName());
jobLog.setJobData(jobInfo.getJobData());
DynamicSchedulerUtil.xxlJobLogDao.save(jobLog); DynamicSchedulerUtil.xxlJobLogDao.save(jobLog);
logger.info(">>>>>>>>>>> xxl-job trigger start, jobLog:{}", jobLog); logger.info(">>>>>>>>>>> xxl-job trigger start, jobId:{}", jobLog.getId());
// trigger request // trigger request
HashMap<String, String> params = new HashMap<String, String>(); HashMap<String, String> params = new HashMap<String, String>();
params.put(HandlerRepository.TRIGGER_TIMESTAMP, String.valueOf(System.currentTimeMillis())); params.put(HandlerParamEnum.TIMESTAMP.name(), String.valueOf(System.currentTimeMillis()));
params.put(HandlerRepository.NAMESPACE, HandlerRepository.NameSpaceEnum.RUN.name()); params.put(HandlerParamEnum.ACTION.name(), ActionEnum.RUN.name());
params.put(HandlerRepository.TRIGGER_LOG_ID, String.valueOf(jobLog.getId()));
params.put(HandlerRepository.TRIGGER_LOG_ADDRESS, XxlJobLogCallbackServer.getTrigger_log_address());
params.put(HandlerRepository.HANDLER_NAME, jobDataMap.get(HandlerRepository.HANDLER_NAME)); params.put(HandlerParamEnum.LOG_ADDRESS.name(), XxlJobLogCallbackServer.getTrigger_log_address());
params.put(HandlerRepository.HANDLER_PARAMS, jobDataMap.get(HandlerRepository.HANDLER_PARAMS)); params.put(HandlerParamEnum.LOG_ID.name(), String.valueOf(jobLog.getId()));
params.put(HandlerRepository.HANDLER_GLUE_SWITCH, String.valueOf(jobInfo.getGlueSwitch())); params.put(HandlerParamEnum.EXECUTOR_HANDLER.name(), jobInfo.getExecutorHandler());
params.put(HandlerRepository.HANDLER_JOB_GROUP, jobInfo.getJobGroup()); params.put(HandlerParamEnum.EXECUTOR_PARAMS.name(), jobInfo.getExecutorParam());
params.put(HandlerRepository.HANDLER_JOB_NAME, jobInfo.getJobName());
params.put(HandlerParamEnum.GLUE_SWITCH.name(), String.valueOf(jobInfo.getGlueSwitch()));
params.put(HandlerParamEnum.JOB_GROUP.name(), jobInfo.getJobGroup());
params.put(HandlerParamEnum.JOB_NAME.name(), jobInfo.getJobName());
// handler address, jetty (servlet dead) // failover trigger
String handler_address = jobDataMap.get(HandlerRepository.HANDLER_ADDRESS); RemoteCallBack callback = failoverTrigger(jobInfo.getExecutorAddress(), params, jobLog);
jobLog.setExecutorHandler(jobInfo.getGlueSwitch()==0?jobInfo.getExecutorHandler():"GLUE任务");
RemoteCallBack callback = HttpUtil.post(HttpUtil.addressToUrl(handler_address), params); jobLog.setExecutorParam(jobInfo.getExecutorParam());
logger.info(">>>>>>>>>>> xxl-job trigger http response, jobLog.id:{}, jobLog:{}, callback:{}", jobLog.getId(), jobLog, callback); logger.info(">>>>>>>>>>> xxl-job failoverTrigger response, jobId:{}, callback:{}", jobLog.getId(), callback);
// update trigger info // update trigger info
jobLog.setTriggerTime(new Date()); jobLog.setTriggerTime(new Date());
jobLog.setTriggerStatus(callback.getStatus()); jobLog.setTriggerStatus(callback.getStatus());
@ -83,7 +76,45 @@ public class RemoteHttpJobBean extends QuartzJobBean {
// monitor triger // monitor triger
JobMonitorHelper.monitor(jobLog.getId()); JobMonitorHelper.monitor(jobLog.getId());
logger.info(">>>>>>>>>>> xxl-job trigger end, jobLog.id:{}, jobLog:{}", jobLog.getId(), jobLog); logger.info(">>>>>>>>>>> xxl-job trigger end, jobId:{}", jobLog.getId());
} }
/**
* failover for trigger remote address
* @param addressArr
* @return
*/
public RemoteCallBack failoverTrigger(String handler_address, HashMap<String, String> handler_params, XxlJobLog jobLog){
if (handler_address.split(",").length > 1) {
String failoverMessage = "";
for (String address : handler_address.split(",")) {
HashMap<String, String> params = new HashMap<String, String>();
params.put(HandlerParamEnum.TIMESTAMP.name(), String.valueOf(System.currentTimeMillis()));
params.put(HandlerParamEnum.ACTION.name(), ActionEnum.BEAT.name());
RemoteCallBack beatResult = HttpUtil.post(HttpUtil.addressToUrl(address), params);
failoverMessage += MessageFormat.format("BEAT running, <br>>>>[address] : {0}, <br>>>>[status] : {1}, <br>>>>[msg] : {2} <br><hr>", address, beatResult.getStatus(), beatResult.getMsg());
if (RemoteCallBack.SUCCESS.equals(beatResult.getStatus())) {
jobLog.setExecutorAddress(address);
RemoteCallBack triggerCallback = HttpUtil.post(HttpUtil.addressToUrl(address), handler_params);
triggerCallback.setStatus(RemoteCallBack.SUCCESS);
failoverMessage += MessageFormat.format("Trigger running, <br>>>>[address] : {0}, <br>>>>[status] : {1}, <br>>>>[msg] : {2} <br><hr>", address, triggerCallback.getStatus(), triggerCallback.getMsg());
triggerCallback.setMsg(failoverMessage);
return triggerCallback;
}
}
RemoteCallBack result = new RemoteCallBack();
result.setStatus(RemoteCallBack.FAIL);
result.setMsg(failoverMessage);
return result;
} else {
jobLog.setExecutorAddress(handler_address);
RemoteCallBack triggerCallback = HttpUtil.post(HttpUtil.addressToUrl(handler_address), handler_params);
String failoverMessage = MessageFormat.format("Trigger running, <br>>>>[address] : {0}, <br>>>>[status] : {1}, <br>>>>[msg] : {2} <br><hr>", handler_address, triggerCallback.getStatus(), triggerCallback.getMsg());
triggerCallback.setMsg(failoverMessage);
return triggerCallback;
}
}
} }

@ -15,7 +15,7 @@ public class XxlJobInfo {
private String jobCron; // 任务执行CRON表达式 【base on quartz】 private String jobCron; // 任务执行CRON表达式 【base on quartz】
private String jobDesc; private String jobDesc;
private String jobClass; // 任务执行JobBean 【base on quartz】 private String jobClass; // 任务执行JobBean 【base on quartz】
private String jobData; // 任务执行数据 Map-JSON-String //private String jobData; // 任务执行数据 Map-JSON-String
private Date addTime; private Date addTime;
private Date updateTime; private Date updateTime;
@ -24,6 +24,10 @@ public class XxlJobInfo {
private String alarmEmail; // 报警邮件 private String alarmEmail; // 报警邮件
private int alarmThreshold; // 报警阀值 private int alarmThreshold; // 报警阀值
private String executorAddress; // 执行器地址,有多个则逗号分隔
private String executorHandler; // 执行器任务Handler名称
private String executorParam; // 执行器,任务参数
private int glueSwitch; // GLUE模式开关0-否1-是 private int glueSwitch; // GLUE模式开关0-否1-是
private String glueSource; // GLUE源代码 private String glueSource; // GLUE源代码
private String glueRemark; // GLUE备注 private String glueRemark; // GLUE备注
@ -79,14 +83,6 @@ public class XxlJobInfo {
this.jobClass = jobClass; this.jobClass = jobClass;
} }
public String getJobData() {
return jobData;
}
public void setJobData(String jobData) {
this.jobData = jobData;
}
public Date getAddTime() { public Date getAddTime() {
return addTime; return addTime;
} }
@ -127,12 +123,28 @@ public class XxlJobInfo {
this.alarmThreshold = alarmThreshold; this.alarmThreshold = alarmThreshold;
} }
public String getJobStatus() { public String getExecutorAddress() {
return jobStatus; return executorAddress;
} }
public void setJobStatus(String jobStatus) { public void setExecutorAddress(String executorAddress) {
this.jobStatus = jobStatus; this.executorAddress = executorAddress;
}
public String getExecutorHandler() {
return executorHandler;
}
public void setExecutorHandler(String executorHandler) {
this.executorHandler = executorHandler;
}
public String getExecutorParam() {
return executorParam;
}
public void setExecutorParam(String executorParam) {
this.executorParam = executorParam;
} }
public int getGlueSwitch() { public int getGlueSwitch() {
@ -159,13 +171,12 @@ public class XxlJobInfo {
this.glueRemark = glueRemark; this.glueRemark = glueRemark;
} }
@Override public String getJobStatus() {
public String toString() { return jobStatus;
return "XxlJobInfo [id=" + id + ", jobGroup=" + jobGroup + ", jobName=" + jobName + ", jobCron=" + jobCron }
+ ", jobDesc=" + jobDesc + ", jobClass=" + jobClass + ", jobData=" + jobData + ", addTime=" + addTime
+ ", updateTime=" + updateTime + ", author=" + author + ", alarmEmail=" + alarmEmail public void setJobStatus(String jobStatus) {
+ ", alarmThreshold=" + alarmThreshold + ", glueSwitch=" + glueSwitch + ", glueSource=" + glueSource this.jobStatus = jobStatus;
+ ", glueRemark=" + glueRemark + ", jobStatus=" + jobStatus + "]";
} }
} }

@ -16,7 +16,10 @@ public class XxlJobLog {
private String jobCron; private String jobCron;
private String jobDesc; private String jobDesc;
private String jobClass; private String jobClass;
private String jobData;
private String executorAddress; // 执行器地址,有多个则逗号分隔
private String executorHandler; // 执行器任务Handler名称
private String executorParam; // 执行器,任务参数
// trigger info // trigger info
private Date triggerTime; private Date triggerTime;
@ -27,6 +30,7 @@ public class XxlJobLog {
private Date handleTime; private Date handleTime;
private String handleStatus; private String handleStatus;
private String handleMsg; private String handleMsg;
public int getId() { public int getId() {
return id; return id;
} }
@ -63,11 +67,23 @@ public class XxlJobLog {
public void setJobClass(String jobClass) { public void setJobClass(String jobClass) {
this.jobClass = jobClass; this.jobClass = jobClass;
} }
public String getJobData() { public String getExecutorAddress() {
return jobData; return executorAddress;
}
public void setExecutorAddress(String executorAddress) {
this.executorAddress = executorAddress;
}
public String getExecutorHandler() {
return executorHandler;
} }
public void setJobData(String jobData) { public void setExecutorHandler(String executorHandler) {
this.jobData = jobData; this.executorHandler = executorHandler;
}
public String getExecutorParam() {
return executorParam;
}
public void setExecutorParam(String executorParam) {
this.executorParam = executorParam;
} }
public Date getTriggerTime() { public Date getTriggerTime() {
return triggerTime; return triggerTime;
@ -106,12 +122,4 @@ public class XxlJobLog {
this.handleMsg = handleMsg; this.handleMsg = handleMsg;
} }
@Override
public String toString() {
return "XxlJobLog [id=" + id + ", jobGroup=" + jobGroup + ", jobName=" + jobName + ", jobCron=" + jobCron
+ ", jobDesc=" + jobDesc + ", jobClass=" + jobClass + ", jobData=" + jobData + ", triggerTime="
+ triggerTime + ", triggerStatus=" + triggerStatus + ", triggerMsg=" + triggerMsg + ", handleTime="
+ handleTime + ", handleStatus=" + handleStatus + ", handleMsg=" + handleMsg + "]";
}
} }

@ -12,7 +12,6 @@ import org.quartz.CronScheduleBuilder;
import org.quartz.CronTrigger; import org.quartz.CronTrigger;
import org.quartz.Job; import org.quartz.Job;
import org.quartz.JobBuilder; import org.quartz.JobBuilder;
import org.quartz.JobDataMap;
import org.quartz.JobDetail; import org.quartz.JobDetail;
import org.quartz.JobKey; import org.quartz.JobKey;
import org.quartz.Scheduler; import org.quartz.Scheduler;
@ -35,7 +34,6 @@ import com.xxl.job.admin.core.callback.XxlJobLogCallbackServer;
import com.xxl.job.admin.core.model.XxlJobInfo; import com.xxl.job.admin.core.model.XxlJobInfo;
import com.xxl.job.admin.dao.IXxlJobInfoDao; import com.xxl.job.admin.dao.IXxlJobInfoDao;
import com.xxl.job.admin.dao.IXxlJobLogDao; import com.xxl.job.admin.dao.IXxlJobLogDao;
import com.xxl.job.core.util.JacksonUtil;
/** /**
* base quartz scheduler util * base quartz scheduler util
@ -151,7 +149,7 @@ public final class DynamicSchedulerUtil implements ApplicationContextAware, Init
} }
// addJob 新增 // addJob 新增
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
public static boolean addJob(XxlJobInfo jobInfo) throws SchedulerException { public static boolean addJob(XxlJobInfo jobInfo) throws SchedulerException {
// TriggerKey : name + group // TriggerKey : name + group
TriggerKey triggerKey = TriggerKey.triggerKey(jobInfo.getJobName(), jobInfo.getJobGroup()); TriggerKey triggerKey = TriggerKey.triggerKey(jobInfo.getJobName(), jobInfo.getJobGroup());
@ -176,10 +174,11 @@ public final class DynamicSchedulerUtil implements ApplicationContextAware, Init
} }
JobDetail jobDetail = JobBuilder.newJob(jobClass_).withIdentity(jobKey).build(); JobDetail jobDetail = JobBuilder.newJob(jobClass_).withIdentity(jobKey).build();
if (jobInfo.getJobData()!=null) { /*if (jobInfo.getJobData()!=null) {
JobDataMap jobDataMap = jobDetail.getJobDataMap(); JobDataMap jobDataMap = jobDetail.getJobDataMap();
jobDataMap.putAll(JacksonUtil.readValue(jobInfo.getJobData(), Map.class)); // JobExecutionContext context.getMergedJobDataMap().get("mailGuid"); jobDataMap.putAll(JacksonUtil.readValue(jobInfo.getJobData(), Map.class));
} // JobExecutionContext context.getMergedJobDataMap().get("mailGuid");
}*/
// schedule : jobDetail + cronTrigger // schedule : jobDetail + cronTrigger
Date date = scheduler.scheduleJob(jobDetail, cronTrigger); Date date = scheduler.scheduleJob(jobDetail, cronTrigger);
@ -189,7 +188,6 @@ public final class DynamicSchedulerUtil implements ApplicationContextAware, Init
} }
// reschedule // reschedule
@SuppressWarnings("unchecked")
public static boolean rescheduleJob(XxlJobInfo jobInfo) throws SchedulerException { public static boolean rescheduleJob(XxlJobInfo jobInfo) throws SchedulerException {
// TriggerKey valid if_exists // TriggerKey valid if_exists
@ -210,9 +208,9 @@ public final class DynamicSchedulerUtil implements ApplicationContextAware, Init
// JobDetail-JobDataMap fresh // JobDetail-JobDataMap fresh
JobDetail jobDetail = scheduler.getJobDetail(jobKey); JobDetail jobDetail = scheduler.getJobDetail(jobKey);
JobDataMap jobDataMap = jobDetail.getJobDataMap(); /*JobDataMap jobDataMap = jobDetail.getJobDataMap();
jobDataMap.clear(); jobDataMap.clear();
jobDataMap.putAll(JacksonUtil.readValue(jobInfo.getJobData(), Map.class)); jobDataMap.putAll(JacksonUtil.readValue(jobInfo.getJobData(), Map.class));*/
// Trigger fresh // Trigger fresh
HashSet<Trigger> triggerSet = new HashSet<Trigger>(); HashSet<Trigger> triggerSet = new HashSet<Trigger>();

@ -63,15 +63,12 @@ public class XxlJobLogDaoImpl implements IXxlJobLogDao {
@Override @Override
public int save(XxlJobLog xxlJobLog) { public int save(XxlJobLog xxlJobLog) {
if (xxlJobLog!=null && xxlJobLog.getJobData().length()>2000) {
xxlJobLog.setJobData(xxlJobLog.getJobData().substring(0, 2000));
}
return sqlSessionTemplate.insert("XxlJobLogMapper.save", xxlJobLog); return sqlSessionTemplate.insert("XxlJobLogMapper.save", xxlJobLog);
} }
@Override @Override
public int updateTriggerInfo(XxlJobLog xxlJobLog) { public int updateTriggerInfo(XxlJobLog xxlJobLog) {
if (xxlJobLog!=null && xxlJobLog.getTriggerMsg()!=null && xxlJobLog.getTriggerMsg().length()>2000) { if (xxlJobLog.getTriggerMsg()!=null && xxlJobLog.getTriggerMsg().length()>2000) {
xxlJobLog.setTriggerMsg(xxlJobLog.getTriggerMsg().substring(0, 2000)); xxlJobLog.setTriggerMsg(xxlJobLog.getTriggerMsg().substring(0, 2000));
} }
return sqlSessionTemplate.update("XxlJobLogMapper.updateTriggerInfo", xxlJobLog); return sqlSessionTemplate.update("XxlJobLogMapper.updateTriggerInfo", xxlJobLog);
@ -79,7 +76,7 @@ public class XxlJobLogDaoImpl implements IXxlJobLogDao {
@Override @Override
public int updateHandleInfo(XxlJobLog xxlJobLog) { public int updateHandleInfo(XxlJobLog xxlJobLog) {
if (xxlJobLog!=null && xxlJobLog.getHandleMsg()!=null && xxlJobLog.getHandleMsg().length()>2000) { if (xxlJobLog.getHandleMsg()!=null && xxlJobLog.getHandleMsg().length()>2000) {
xxlJobLog.setHandleMsg(xxlJobLog.getHandleMsg().substring(0, 2000)); xxlJobLog.setHandleMsg(xxlJobLog.getHandleMsg().substring(0, 2000));
} }
return sqlSessionTemplate.update("XxlJobLogMapper.updateHandleInfo", xxlJobLog); return sqlSessionTemplate.update("XxlJobLogMapper.updateHandleInfo", xxlJobLog);

@ -0,0 +1,33 @@
package com.xxl.job.admin.service;
import java.util.Map;
import com.xxl.job.admin.core.model.ReturnT;
/**
* core job service for xxl-job
*
* @author xuxueli 2016-5-28 15:30:33
*/
public interface IXxlJobService {
public Map<String, Object> pageList(int start, int length, String jobGroup, String jobName, String filterTime);
public ReturnT<String> add(String jobGroup, String jobName, String jobCron, String jobDesc,
String executorAddress, String executorHandler, String executorParam,
String author, String alarmEmail, int alarmThreshold,
int glueSwitch, String glueSource, String glueRemark);
public ReturnT<String> reschedule(String jobGroup, String jobName, String jobCron, String jobDesc,
String handler_address, String handler_name, String handler_params,
String author, String alarmEmail, int alarmThreshold, int glueSwitch);
public ReturnT<String> remove(String jobGroup, String jobName);
public ReturnT<String> pause(String jobGroup, String jobName);
public ReturnT<String> resume(String jobGroup, String jobName);
public ReturnT<String> triggerJob(String jobGroup, String jobName);
}

@ -0,0 +1,241 @@
package com.xxl.job.admin.service.impl;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import javax.annotation.Resource;
import org.apache.commons.lang.StringUtils;
import org.quartz.CronExpression;
import org.quartz.SchedulerException;
import org.springframework.stereotype.Service;
import com.xxl.job.admin.core.constant.Constants.JobGroupEnum;
import com.xxl.job.admin.core.jobbean.RemoteHttpJobBean;
import com.xxl.job.admin.core.model.ReturnT;
import com.xxl.job.admin.core.model.XxlJobInfo;
import com.xxl.job.admin.core.util.DynamicSchedulerUtil;
import com.xxl.job.admin.dao.IXxlJobInfoDao;
import com.xxl.job.admin.dao.IXxlJobLogDao;
import com.xxl.job.admin.dao.IXxlJobLogGlueDao;
import com.xxl.job.admin.service.IXxlJobService;
/**
* core job service for xxl-job
* @author xuxueli 2016-5-28 15:30:33
*/
@Service
public class XxlJobServiceImpl implements IXxlJobService {
@Resource
private IXxlJobInfoDao xxlJobInfoDao;
@Resource
public IXxlJobLogDao xxlJobLogDao;
@Resource
private IXxlJobLogGlueDao xxlJobLogGlueDao;
@Override
public Map<String, Object> pageList(int start, int length, String jobGroup, String jobName, String filterTime) {
// page list
List<XxlJobInfo> list = xxlJobInfoDao.pageList(start, length, jobGroup, jobName);
int list_count = xxlJobInfoDao.pageListCount(start, length, jobGroup, jobName);
// fill job info
if (list!=null && list.size()>0) {
for (XxlJobInfo jobInfo : list) {
DynamicSchedulerUtil.fillJobInfo(jobInfo);
}
}
// package result
Map<String, Object> maps = new HashMap<String, Object>();
maps.put("recordsTotal", list_count); // 总记录数
maps.put("recordsFiltered", list_count); // 过滤后的总记录数
maps.put("data", list); // 分页列表
return maps;
}
@Override
public ReturnT<String> add(String jobGroup, String jobName, String jobCron, String jobDesc,
String executorAddress, String executorHandler, String executorParam,
String author, String alarmEmail, int alarmThreshold,
int glueSwitch, String glueSource, String glueRemark) {
// valid
if (JobGroupEnum.match(jobGroup) == null) {
return new ReturnT<String>(500, "请选择“任务组”");
}
if (StringUtils.isBlank(jobName)) {
return new ReturnT<String>(500, "请输入“任务名”");
}
if (!CronExpression.isValidExpression(jobCron)) {
return new ReturnT<String>(500, "请输入格式正确的“corn”");
}
if (StringUtils.isBlank(jobDesc)) {
return new ReturnT<String>(500, "请输入“任务描述”");
}
if (StringUtils.isBlank(executorAddress)) {
return new ReturnT<String>(500, "请输入“执行器地址”");
}
if (glueSwitch==0 && StringUtils.isBlank(executorHandler)) {
return new ReturnT<String>(500, "请输入“jobHandler”");
}
if (StringUtils.isBlank(author)) {
return new ReturnT<String>(500, "请输入“负责人”");
}
if (StringUtils.isBlank(alarmEmail)) {
return new ReturnT<String>(500, "请输入“报警邮件”");
}
if (alarmThreshold < 0) {
alarmThreshold = 0;
}
try {
if (DynamicSchedulerUtil.checkExists(jobName, jobGroup)) {
return new ReturnT<String>(500, "此任务已存在,请更换任务组或任务名");
}
} catch (SchedulerException e1) {
e1.printStackTrace();
return new ReturnT<String>(500, "此任务已存在,请更换任务组或任务名");
}
// Backup to the database
XxlJobInfo jobInfo = new XxlJobInfo();
jobInfo.setJobGroup(jobGroup);
jobInfo.setJobName(jobName);
jobInfo.setJobCron(jobCron);
jobInfo.setJobDesc(jobDesc);
jobInfo.setJobClass(RemoteHttpJobBean.class.getName());
jobInfo.setAuthor(author);
jobInfo.setAlarmEmail(alarmEmail);
jobInfo.setAlarmThreshold(alarmThreshold);
jobInfo.setGlueSwitch(glueSwitch);
jobInfo.setGlueSource(glueSource);
jobInfo.setGlueRemark(glueRemark);
jobInfo.setExecutorAddress(executorAddress);
jobInfo.setExecutorHandler(executorHandler);
jobInfo.setExecutorParam(executorParam);
xxlJobInfoDao.save(jobInfo);
try {
// add job 2 quartz
boolean result = DynamicSchedulerUtil.addJob(jobInfo);
if (result) {
return ReturnT.SUCCESS;
} else {
xxlJobInfoDao.delete(jobGroup, jobName);
return new ReturnT<String>(500, "新增任务失败");
}
} catch (SchedulerException e) {
e.printStackTrace();
}
return ReturnT.FAIL;
}
@Override
public ReturnT<String> reschedule(String jobGroup, String jobName, String jobCron, String jobDesc,
String executorAddress, String executorHandler, String executorParam,
String author, String alarmEmail, int alarmThreshold, int glueSwitch) {
// valid
if (JobGroupEnum.match(jobGroup) == null) {
return new ReturnT<String>(500, "请选择“任务组”");
}
if (StringUtils.isBlank(jobName)) {
return new ReturnT<String>(500, "请输入“任务名”");
}
if (!CronExpression.isValidExpression(jobCron)) {
return new ReturnT<String>(500, "请输入格式正确的“corn”");
}
if (StringUtils.isBlank(jobDesc)) {
return new ReturnT<String>(500, "请输入“任务描述”");
}
if (StringUtils.isBlank(executorAddress)) {
return new ReturnT<String>(500, "请输入“执行器地址”");
}
if (glueSwitch==0 && StringUtils.isBlank(executorHandler)) {
return new ReturnT<String>(500, "请输入“jobHandler”");
}
if (StringUtils.isBlank(author)) {
return new ReturnT<String>(500, "请输入“负责人”");
}
if (StringUtils.isBlank(alarmEmail)) {
return new ReturnT<String>(500, "请输入“报警邮件”");
}
if (alarmThreshold < 0) {
alarmThreshold = 0;
}
XxlJobInfo jobInfo = xxlJobInfoDao.load(jobGroup, jobName);
jobInfo.setJobCron(jobCron);
jobInfo.setJobDesc(jobDesc);
jobInfo.setAuthor(author);
jobInfo.setAlarmEmail(alarmEmail);
jobInfo.setAlarmThreshold(alarmThreshold);
jobInfo.setGlueSwitch(glueSwitch);
jobInfo.setExecutorAddress(executorAddress);
jobInfo.setExecutorHandler(executorHandler);
jobInfo.setExecutorParam(executorParam);
try {
// fresh quartz
DynamicSchedulerUtil.rescheduleJob(jobInfo);
// fresh db
xxlJobInfoDao.update(jobInfo);
return ReturnT.SUCCESS;
} catch (SchedulerException e) {
e.printStackTrace();
}
return ReturnT.FAIL;
}
@Override
public ReturnT<String> remove(String jobGroup, String jobName) {
try {
DynamicSchedulerUtil.removeJob(jobName, jobGroup);
xxlJobInfoDao.delete(jobGroup, jobName);
xxlJobLogDao.delete(jobGroup, jobName);
xxlJobLogGlueDao.delete(jobGroup, jobName);
return ReturnT.SUCCESS;
} catch (SchedulerException e) {
e.printStackTrace();
}
return ReturnT.FAIL;
}
@Override
public ReturnT<String> pause(String jobGroup, String jobName) {
try {
DynamicSchedulerUtil.pauseJob(jobName, jobGroup); // jobStatus do not store
return ReturnT.SUCCESS;
} catch (SchedulerException e) {
e.printStackTrace();
return ReturnT.FAIL;
}
}
@Override
public ReturnT<String> resume(String jobGroup, String jobName) {
try {
DynamicSchedulerUtil.resumeJob(jobName, jobGroup);
return ReturnT.SUCCESS;
} catch (SchedulerException e) {
e.printStackTrace();
return ReturnT.FAIL;
}
}
@Override
public ReturnT<String> triggerJob(String jobGroup, String jobName) {
try {
DynamicSchedulerUtil.triggerJob(jobName, jobGroup);
return ReturnT.SUCCESS;
} catch (SchedulerException e) {
e.printStackTrace();
return ReturnT.FAIL;
}
}
}

@ -9,7 +9,7 @@
http://www.springframework.org/schema/util http://www.springframework.org/schema/util
http://www.springframework.org/schema/util/spring-util.xsd"> http://www.springframework.org/schema/util/spring-util.xsd">
<context:component-scan base-package="com.xxl.job.admin.dao" /> <context:component-scan base-package="com.xxl.job.admin.service, com.xxl.job.admin.dao" />
<bean id="freemarkerConfig" class="org.springframework.web.servlet.view.freemarker.FreeMarkerConfigurer"> <bean id="freemarkerConfig" class="org.springframework.web.servlet.view.freemarker.FreeMarkerConfigurer">
<property name="templateLoaderPath" value="/WEB-INF/template/" /> <property name="templateLoaderPath" value="/WEB-INF/template/" />

@ -48,7 +48,6 @@
</bean> </bean>
<!-- part 2 :for tx --> <!-- part 2 :for tx -->
<!--
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager"> <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource" /> <property name="dataSource" ref="dataSource" />
</bean> </bean>
@ -68,9 +67,8 @@
</tx:advice> </tx:advice>
<aop:config> <aop:config>
<aop:pointcut id="txoperation" expression="execution(* com.xxl.job.admin.service.imp.*.*(..))" /> <aop:pointcut id="txoperation" expression="execution(* com.xxl.job.admin.service.impl.*.*(..))" />
<aop:advisor pointcut-ref="txoperation" advice-ref="txAdvice" /> <aop:advisor pointcut-ref="txoperation" advice-ref="txAdvice" />
</aop:config> </aop:config>
-->
</beans> </beans>

@ -11,7 +11,6 @@
<result column="job_cron" property="jobCron" /> <result column="job_cron" property="jobCron" />
<result column="job_desc" property="jobDesc" /> <result column="job_desc" property="jobDesc" />
<result column="job_class" property="jobClass" /> <result column="job_class" property="jobClass" />
<result column="job_data" property="jobData" />
<result column="add_time" property="addTime" /> <result column="add_time" property="addTime" />
<result column="update_time" property="updateTime" /> <result column="update_time" property="updateTime" />
@ -20,6 +19,10 @@
<result column="alarm_email" property="alarmEmail" /> <result column="alarm_email" property="alarmEmail" />
<result column="alarm_threshold" property="alarmThreshold" /> <result column="alarm_threshold" property="alarmThreshold" />
<result column="executor_address" property="executorAddress" />
<result column="executor_handler" property="executorHandler" />
<result column="executor_param" property="executorParam" />
<result column="glue_switch" property="glueSwitch" /> <result column="glue_switch" property="glueSwitch" />
<result column="glue_source" property="glueSource" /> <result column="glue_source" property="glueSource" />
<result column="glue_remark" property="glueRemark" /> <result column="glue_remark" property="glueRemark" />
@ -32,12 +35,14 @@
t.job_cron, t.job_cron,
t.job_desc, t.job_desc,
t.job_class, t.job_class,
t.job_data,
t.add_time, t.add_time,
t.update_time, t.update_time,
t.author, t.author,
t.alarm_email, t.alarm_email,
t.alarm_threshold, t.alarm_threshold,
t.executor_address,
t.executor_handler,
t.executor_param,
t.glue_switch, t.glue_switch,
t.glue_source, t.glue_source,
t.glue_remark t.glue_remark
@ -78,12 +83,14 @@
job_cron, job_cron,
job_desc, job_desc,
job_class, job_class,
job_data,
add_time, add_time,
update_time, update_time,
author, author,
alarm_email, alarm_email,
alarm_threshold, alarm_threshold,
executor_address,
executor_handler,
executor_param,
glue_switch, glue_switch,
glue_source, glue_source,
glue_remark glue_remark
@ -93,12 +100,14 @@
#{jobCron}, #{jobCron},
#{jobDesc}, #{jobDesc},
#{jobClass}, #{jobClass},
#{jobData},
NOW(), NOW(),
NOW(), NOW(),
#{author}, #{author},
#{alarmEmail}, #{alarmEmail},
#{alarmThreshold}, #{alarmThreshold},
#{executorAddress},
#{executorHandler},
#{executorParam},
#{glueSwitch}, #{glueSwitch},
#{glueSource}, #{glueSource},
#{glueRemark} #{glueRemark}
@ -120,11 +129,13 @@
SET SET
job_cron = #{jobCron}, job_cron = #{jobCron},
job_desc = #{jobDesc}, job_desc = #{jobDesc},
job_data = #{jobData},
update_time = NOW(), update_time = NOW(),
author = #{author}, author = #{author},
alarm_email = #{alarmEmail}, alarm_email = #{alarmEmail},
alarm_threshold = #{alarmThreshold}, alarm_threshold = #{alarmThreshold},
executor_address = #{executorAddress},
executor_handler = #{executorHandler},
executor_param = #{executorParam},
glue_switch = #{glueSwitch}, glue_switch = #{glueSwitch},
glue_source = #{glueSource}, glue_source = #{glueSource},
glue_remark = #{glueRemark} glue_remark = #{glueRemark}

@ -11,7 +11,10 @@
<result column="job_cron" property="jobCron" /> <result column="job_cron" property="jobCron" />
<result column="job_desc" property="jobDesc" /> <result column="job_desc" property="jobDesc" />
<result column="job_class" property="jobClass" /> <result column="job_class" property="jobClass" />
<result column="job_data" property="jobData" />
<result column="executor_address" property="executorAddress" />
<result column="executor_handler" property="executorHandler" />
<result column="executor_param" property="executorParam" />
<result column="trigger_time" property="triggerTime" /> <result column="trigger_time" property="triggerTime" />
<result column="trigger_status" property="triggerStatus" /> <result column="trigger_status" property="triggerStatus" />
@ -20,6 +23,7 @@
<result column="handle_time" property="handleTime" /> <result column="handle_time" property="handleTime" />
<result column="handle_status" property="handleStatus" /> <result column="handle_status" property="handleStatus" />
<result column="handle_msg" property="handleMsg" /> <result column="handle_msg" property="handleMsg" />
</resultMap> </resultMap>
<sql id="Base_Column_List"> <sql id="Base_Column_List">
@ -29,7 +33,9 @@
t.job_cron, t.job_cron,
t.job_desc, t.job_desc,
t.job_class, t.job_class,
t.job_data, t.executor_address,
t.executor_handler,
t.executor_param,
t.trigger_time, t.trigger_time,
t.trigger_status, t.trigger_status,
t.trigger_msg, t.trigger_msg,
@ -98,14 +104,18 @@
`job_cron`, `job_cron`,
`job_desc`, `job_desc`,
`job_class`, `job_class`,
`job_data` `executor_address`,
`executor_handler`,
`executor_param`
) VALUES ( ) VALUES (
#{jobGroup}, #{jobGroup},
#{jobName}, #{jobName},
#{jobCron}, #{jobCron},
#{jobDesc}, #{jobDesc},
#{jobClass}, #{jobClass},
#{jobData} #{executorAddress},
#{executorHandler},
#{executorParam}
); );
<selectKey resultType="java.lang.Integer" order="AFTER" keyProperty="id"> <selectKey resultType="java.lang.Integer" order="AFTER" keyProperty="id">
SELECT LAST_INSERT_ID() SELECT LAST_INSERT_ID()
@ -117,7 +127,10 @@
SET SET
`trigger_time`= #{triggerTime}, `trigger_time`= #{triggerTime},
`trigger_status`= #{triggerStatus}, `trigger_status`= #{triggerStatus},
`trigger_msg`= #{triggerMsg} `trigger_msg`= #{triggerMsg},
`executor_address`= #{executorAddress},
`executor_handler`= #{executorHandler},
`executor_param`= #{executorParam}
WHERE `id`= #{id} WHERE `id`= #{id}
</update> </update>

@ -72,7 +72,9 @@
<th name="jobDesc" >描述</th> <th name="jobDesc" >描述</th>
<th name="jobCron" >Cron</th> <th name="jobCron" >Cron</th>
<th name="jobClass" >JobBean</th> <th name="jobClass" >JobBean</th>
<th name="jobData" >任务数据</th> <th name="executorAddress" >执行器地址</th>
<th name="executorHandler" >JobHandler</th>
<th name="executorParam" >任务参数</th>
<th name="addTime" >新增时间</th> <th name="addTime" >新增时间</th>
<th name="updateTime" >更新时间</th> <th name="updateTime" >更新时间</th>
<th name="author" >负责人</th> <th name="author" >负责人</th>
@ -126,13 +128,13 @@
</div> </div>
<div class="form-group"> <div class="form-group">
<label for="lastname" class="col-sm-2 control-label">执行器地址<font color="red">*</font></label> <label for="lastname" class="col-sm-2 control-label">执行器地址<font color="red">*</font></label>
<div class="col-sm-4"><input type="text" class="form-control" name="handler_address" placeholder="请输入“执行器地址”" maxlength="200" ></div> <div class="col-sm-4"><input type="text" class="form-control" name="executorAddress" placeholder="请输入“执行器地址”,多个地址逗号分隔" maxlength="200" ></div>
<label for="lastname" class="col-sm-2 control-label">jobHandler<font color="red">*</font></label> <label for="lastname" class="col-sm-2 control-label">jobHandler<font color="red">*</font></label>
<div class="col-sm-4"><input type="text" class="form-control" name="handler_name" placeholder="请输入“jobHandler”" maxlength="200" ></div> <div class="col-sm-4"><input type="text" class="form-control" name="executorHandler" placeholder="请输入“jobHandler”" maxlength="200" ></div>
</div> </div>
<div class="form-group"> <div class="form-group">
<label for="firstname" class="col-sm-2 control-label">执行参数<font color="black">*</font></label> <label for="firstname" class="col-sm-2 control-label">执行参数<font color="black">*</font></label>
<div class="col-sm-4"><input type="text" class="form-control" name="handler_params" placeholder="请输入“执行参数”" maxlength="100" ></div> <div class="col-sm-4"><input type="text" class="form-control" name="executorParam" placeholder="请输入“执行参数”" maxlength="100" ></div>
<label for="lastname" class="col-sm-2 control-label">负责人<font color="red">*</font></label> <label for="lastname" class="col-sm-2 control-label">负责人<font color="red">*</font></label>
<div class="col-sm-4"><input type="text" class="form-control" name="author" placeholder="请输入“负责人”" maxlength="200" ></div> <div class="col-sm-4"><input type="text" class="form-control" name="author" placeholder="请输入“负责人”" maxlength="200" ></div>
</div> </div>
@ -207,13 +209,13 @@ public class DemoJobHandler extends IJobHandler {
<div class="form-group"> <div class="form-group">
<label for="lastname" class="col-sm-2 control-label">执行器地址<font color="red">*</font></label> <label for="lastname" class="col-sm-2 control-label">执行器地址<font color="red">*</font></label>
<div class="col-sm-4"><input type="text" class="form-control" name="handler_address" placeholder="请输入“执行器地址”" maxlength="200" ></div> <div class="col-sm-4"><input type="text" class="form-control" name="executorAddress" placeholder="请输入“执行器地址”,多个地址逗号分隔" maxlength="200" ></div>
<label for="lastname" class="col-sm-2 control-label">jobHandler<font color="red">*</font></label> <label for="lastname" class="col-sm-2 control-label">jobHandler<font color="red">*</font></label>
<div class="col-sm-4"><input type="text" class="form-control" name="handler_name" placeholder="请输入“jobHandler”" maxlength="200" ></div> <div class="col-sm-4"><input type="text" class="form-control" name="executorHandler" placeholder="请输入“jobHandler”" maxlength="200" ></div>
</div> </div>
<div class="form-group"> <div class="form-group">
<label for="firstname" class="col-sm-2 control-label">执行参数<font color="black">*</font></label> <label for="firstname" class="col-sm-2 control-label">执行参数<font color="black">*</font></label>
<div class="col-sm-4"><input type="text" class="form-control" name="handler_params" placeholder="请输入“执行参数”" maxlength="100" ></div> <div class="col-sm-4"><input type="text" class="form-control" name="executorParam" placeholder="请输入“执行参数”" maxlength="100" ></div>
<label for="lastname" class="col-sm-2 control-label">负责人<font color="red">*</font></label> <label for="lastname" class="col-sm-2 control-label">负责人<font color="red">*</font></label>
<div class="col-sm-4"><input type="text" class="form-control" name="author" placeholder="请输入“负责人”" maxlength="200" ></div> <div class="col-sm-4"><input type="text" class="form-control" name="author" placeholder="请输入“负责人”" maxlength="200" ></div>
</div> </div>

@ -78,7 +78,9 @@
<th name="jobCron" >Cron</th> <th name="jobCron" >Cron</th>
<th name="jobDesc" >描述</th> <th name="jobDesc" >描述</th>
<th name="jobClass" >JobBean</th> <th name="jobClass" >JobBean</th>
<th name="jobData" >任务数据</th> <th name="executorAddress" >执行器地址</th>
<th name="executorHandler" >JobHandler</th>
<th name="executorParam" >任务参数</th>
<th name="triggerTime" >调度时间</th> <th name="triggerTime" >调度时间</th>
<th name="triggerStatus" >调度结果</th> <th name="triggerStatus" >调度结果</th>
<th name="triggerMsg" >调度日志</th> <th name="triggerMsg" >调度日志</th>

@ -33,17 +33,9 @@ $(function() {
{ "data": 'jobDesc', "visible" : true}, { "data": 'jobDesc', "visible" : true},
{ "data": 'jobCron', "visible" : true}, { "data": 'jobCron', "visible" : true},
{ "data": 'jobClass', "visible" : false}, { "data": 'jobClass', "visible" : false},
{ { "data": 'executorAddress', "visible" : false},
"data": 'jobData', { "data": 'executorHandler', "visible" : false},
"visible" : true, { "data": 'executorParam', "visible" : false},
"render": function ( data, type, row ) {
var _jobData = eval('(' + data + ')'); // row.jobData
var html = "<p title='" + data + "'>执行器:" + _jobData.handler_name +
"<br>执行参数:" + _jobData.handler_params +
"<br>执行器地址:" + _jobData.handler_address + "</p>";
return html;
}
},
{ {
"data": 'addTime', "data": 'addTime',
"visible" : false, "visible" : false,
@ -90,15 +82,12 @@ $(function() {
var logUrl = base_url +'/joblog?jobGroup='+ row.jobGroup +'&jobName='+ row.jobName; var logUrl = base_url +'/joblog?jobGroup='+ row.jobGroup +'&jobName='+ row.jobName;
// log url // log url
var codeHtml = ""; var codeBtn = "";
if(row.glueSwitch != 0){ if(row.glueSwitch > 0){
var codeUrl = base_url +'/jobcode?jobGroup='+ row.jobGroup +'&jobName='+ row.jobName; var codeUrl = base_url +'/jobcode?jobGroup='+ row.jobGroup +'&jobName='+ row.jobName;
codeHtml = '<button class="btn btn-warning btn-xs" type="button" onclick="javascript:window.open(\'' + codeUrl + '\')" >GLUE</button> ' codeBtn = '<button class="btn btn-warning btn-xs" type="button" onclick="javascript:window.open(\'' + codeUrl + '\')" >GLUE</button> '
} }
// job data
var jobDataMap = eval('(' + row.jobData + ')');
var html = '<p id="'+ row.id +'" '+ var html = '<p id="'+ row.id +'" '+
' jobGroup="'+ row.jobGroup +'" '+ ' jobGroup="'+ row.jobGroup +'" '+
' jobName="'+ row.jobName +'" '+ ' jobName="'+ row.jobName +'" '+
@ -106,9 +95,9 @@ $(function() {
' jobDesc="'+ row.jobDesc +'" '+ ' jobDesc="'+ row.jobDesc +'" '+
' jobClass="'+ row.jobClass +'" '+ ' jobClass="'+ row.jobClass +'" '+
' jobData="'+ row.jobData +'" '+ ' jobData="'+ row.jobData +'" '+
' handler_params="'+jobDataMap.handler_params +'" '+ ' executorAddress="'+row.executorAddress +'" '+
' handler_address="'+ jobDataMap.handler_address +'" '+ ' executorHandler="'+ row.executorHandler +'" '+
' handler_name="'+ jobDataMap.handler_name +'" '+ ' executorParam="'+ row.executorParam +'" '+
' author="'+ row.author +'" '+ ' author="'+ row.author +'" '+
' alarmEmail="'+ row.alarmEmail +'" '+ ' alarmEmail="'+ row.alarmEmail +'" '+
' alarmThreshold="'+ row.alarmThreshold +'" '+ ' alarmThreshold="'+ row.alarmThreshold +'" '+
@ -118,7 +107,7 @@ $(function() {
pause_resume + pause_resume +
'<button class="btn btn-primary btn-xs" type="job_del" type="button" onclick="javascript:window.open(\'' + logUrl + '\')" >日志</button><br> '+ '<button class="btn btn-primary btn-xs" type="job_del" type="button" onclick="javascript:window.open(\'' + logUrl + '\')" >日志</button><br> '+
'<button class="btn btn-warning btn-xs update" type="button">编辑</button> '+ '<button class="btn btn-warning btn-xs update" type="button">编辑</button> '+
codeHtml + codeBtn +
'<button class="btn btn-danger btn-xs job_operate" type="job_del" type="button">删除</button> '+ '<button class="btn btn-danger btn-xs job_operate" type="job_del" type="button">删除</button> '+
'</p>'; '</p>';
@ -236,11 +225,11 @@ $(function() {
required : true , required : true ,
maxlength: 200 maxlength: 200
}, },
handler_address : { executorAddress : {
required : true , required : true ,
maxlength: 200 maxlength: 200
}, },
handler_name : { executorHandler : {
required : true , required : true ,
maxlength: 200 maxlength: 200
}, },
@ -271,11 +260,11 @@ $(function() {
required :"请输入“任务描述”." , required :"请输入“任务描述”." ,
maxlength:"“任务描述”长度不应超过200位" maxlength:"“任务描述”长度不应超过200位"
}, },
handler_address : { executorAddress : {
required :"请输入“执行器地址”." , required :"请输入“执行器地址”." ,
maxlength:"“执行器地址”长度不应超过200位" maxlength:"“执行器地址”长度不应超过200位"
}, },
handler_name : { executorHandler : {
required : "请输入“jobHandler”." , required : "请输入“jobHandler”." ,
maxlength: "“jobHandler”长度不应超过200位" maxlength: "“jobHandler”长度不应超过200位"
}, },
@ -359,9 +348,9 @@ $(function() {
$("#updateModal .form input[name='jobName']").val($(this).parent('p').attr("jobName")); $("#updateModal .form input[name='jobName']").val($(this).parent('p').attr("jobName"));
$("#updateModal .form input[name='jobCron']").val($(this).parent('p').attr("jobCron")); $("#updateModal .form input[name='jobCron']").val($(this).parent('p').attr("jobCron"));
$("#updateModal .form input[name='jobDesc']").val($(this).parent('p').attr("jobDesc")); $("#updateModal .form input[name='jobDesc']").val($(this).parent('p').attr("jobDesc"));
$("#updateModal .form input[name='handler_address']").val($(this).parent('p').attr("handler_address")); $("#updateModal .form input[name='executorAddress']").val($(this).parent('p').attr("executorAddress"));
$("#updateModal .form input[name='handler_name']").val($(this).parent('p').attr("handler_name")); $("#updateModal .form input[name='executorHandler']").val($(this).parent('p').attr("executorHandler"));
$("#updateModal .form input[name='handler_params']").val($(this).parent('p').attr("handler_params")); $("#updateModal .form input[name='executorParam']").val($(this).parent('p').attr("executorParam"));
$("#updateModal .form input[name='author']").val($(this).parent('p').attr("author")); $("#updateModal .form input[name='author']").val($(this).parent('p').attr("author"));
$("#updateModal .form input[name='alarmEmail']").val($(this).parent('p').attr("alarmEmail")); $("#updateModal .form input[name='alarmEmail']").val($(this).parent('p').attr("alarmEmail"));
$("#updateModal .form input[name='alarmThreshold']").val($(this).parent('p').attr("alarmThreshold")); $("#updateModal .form input[name='alarmThreshold']").val($(this).parent('p').attr("alarmThreshold"));
@ -393,11 +382,11 @@ $(function() {
required : true , required : true ,
maxlength: 200 maxlength: 200
}, },
handler_address : { executorAddress : {
required : true , required : true ,
maxlength: 200 maxlength: 200
}, },
handler_name : { executorHandler : {
required : true , required : true ,
maxlength: 200 maxlength: 200
}, },
@ -423,11 +412,11 @@ $(function() {
required :"请输入“任务描述”." , required :"请输入“任务描述”." ,
maxlength:"“任务描述”长度不应超过200位" maxlength:"“任务描述”长度不应超过200位"
}, },
handler_address : { executorAddress : {
required :"请输入“执行器地址”." , required :"请输入“执行器地址”." ,
maxlength:"“执行器地址”长度不应超过200位" maxlength:"“执行器地址”长度不应超过200位"
}, },
handler_name : { executorHandler : {
required : "请输入“jobHandler”." , required : "请输入“jobHandler”." ,
maxlength: "“jobHandler”长度不应超过200位" maxlength: "“jobHandler”长度不应超过200位"
}, },

@ -60,22 +60,14 @@ $(function() {
return data; return data;
} }
}, },
{ "data": 'jobName'}, { "data": 'jobName', "visible" : false},
{ "data": 'jobCron', "visible" : false}, { "data": 'jobCron', "visible" : false},
{ "data": 'jobDesc', "visible" : false}, { "data": 'jobDesc', "visible" : false},
{ "data": 'jobClass', "visible" : false}, { "data": 'jobClass', "visible" : false},
{
"data": 'jobData', { "data": 'executorAddress', "visible" : true},
"visible" : true, { "data": 'executorHandler', "visible" : true},
"render": function ( data, type, row ) { { "data": 'executorParam', "visible" : true},
var _jobData = eval('(' + data + ')'); // row.jobData
var html = "<p title='" + data + "'>JobHandler" + _jobData.handler_name +
"<br>执行参数:" + _jobData.handler_params +
"<br>执行器地址:" + _jobData.handler_address + "</p>";
return data?'<a class="logMsg" href="javascript:;" >查看<span style="display:none;">'+ html +'</span></a>':"无";
}
},
{ {
"data": 'triggerTime', "data": 'triggerTime',
"render": function ( data, type, row ) { "render": function ( data, type, row ) {
@ -106,12 +98,6 @@ $(function() {
"render": function ( data, type, row ) { "render": function ( data, type, row ) {
// better support expression or string, not function // better support expression or string, not function
return function () { return function () {
// local job do not support trigger detail log, now
var _jobData = eval('(' + row.jobData + ')');
if (!_jobData.handler_address) {
return;
}
if (row.triggerStatus == 'SUCCESS'){ if (row.triggerStatus == 'SUCCESS'){
var temp = '<a href="javascript:;" class="logDetail" _id="'+ row.id +'">执行日志</a>'; var temp = '<a href="javascript:;" class="logDetail" _id="'+ row.id +'">执行日志</a>';
if(!row.handleStatus){ if(!row.handleStatus){

@ -34,7 +34,6 @@ public class XxlJobInfoTest {
info.setJobName("job_name"); info.setJobName("job_name");
info.setJobCron("jobCron"); info.setJobCron("jobCron");
info.setJobClass("jobClass"); info.setJobClass("jobClass");
info.setJobData("jobData");
int count = xxlJobInfoDao.save(info); int count = xxlJobInfoDao.save(info);
System.out.println(count); System.out.println(count);
System.out.println(info.getId()); System.out.println(info.getId());
@ -48,7 +47,6 @@ public class XxlJobInfoTest {
XxlJobInfo item = xxlJobInfoDao.load(null ,"job_name"); XxlJobInfo item = xxlJobInfoDao.load(null ,"job_name");
item.setJobCron("jobCron2"); item.setJobCron("jobCron2");
item.setJobData("jobData2");
xxlJobInfoDao.update(item); xxlJobInfoDao.update(item);
} }

@ -28,7 +28,6 @@ public class XxlJobLogTest {
xxlJobLog.setJobName("job_name"); xxlJobLog.setJobName("job_name");
xxlJobLog.setJobCron("jobCron"); xxlJobLog.setJobCron("jobCron");
xxlJobLog.setJobClass("jobClass"); xxlJobLog.setJobClass("jobClass");
xxlJobLog.setJobData("jobData");
int count = xxlJobLogDao.save(xxlJobLog); int count = xxlJobLogDao.save(xxlJobLog);
System.out.println(count); System.out.println(count);
System.out.println(xxlJobLog.getId()); System.out.println(xxlJobLog.getId());

@ -19,20 +19,49 @@ import com.xxl.job.core.util.JacksonUtil;
public class HandlerRepository { public class HandlerRepository {
private static Logger logger = LoggerFactory.getLogger(HandlerRepository.class); private static Logger logger = LoggerFactory.getLogger(HandlerRepository.class);
public static final String NAMESPACE = "namespace"; public enum HandlerParamEnum{
public enum NameSpaceEnum{RUN, KILL, LOG} /**
* trigger timestamp
public static final String HANDLER_ADDRESS = "handler_address"; */
public static final String HANDLER_PARAMS = "handler_params"; TIMESTAMP,
/**
public static final String HANDLER_GLUE_SWITCH = "handler_glue_switch"; * trigger action
public static final String HANDLER_NAME = "handler_name"; */
public static final String HANDLER_JOB_GROUP = "handler_job_group"; ACTION,
public static final String HANDLER_JOB_NAME = "handler_job_name"; /**
* remote executor jobhandler
public static final String TRIGGER_LOG_ID = "trigger_log_id"; */
public static final String TRIGGER_LOG_ADDRESS = "trigger_log_address"; EXECUTOR_HANDLER,
public static final String TRIGGER_TIMESTAMP = "trigger_timestamp"; /**
* params of jobhandler
*/
EXECUTOR_PARAMS,
/**
* switch of job, if open glue model
*/
GLUE_SWITCH,
/**
* job group
*/
JOB_GROUP,
/**
* job name
*/
JOB_NAME,
/**
* address for callback log
*/
LOG_ADDRESS,
/**
* log id
*/
LOG_ID,
/**
* log date
*/
LOG_DATE
}
public enum ActionEnum{RUN, KILL, LOG, BEAT}
public static ConcurrentHashMap<String, HandlerThread> handlerTreadMap = new ConcurrentHashMap<String, HandlerThread>(); public static ConcurrentHashMap<String, HandlerThread> handlerTreadMap = new ConcurrentHashMap<String, HandlerThread>();
@ -53,27 +82,26 @@ public class HandlerRepository {
callback.setStatus(RemoteCallBack.FAIL); callback.setStatus(RemoteCallBack.FAIL);
// check namespace // check namespace
String namespace = _param.get(HandlerRepository.NAMESPACE); String namespace = _param.get(HandlerParamEnum.ACTION.name());
if (namespace==null || namespace.trim().length()==0) { if (namespace==null || namespace.trim().length()==0) {
callback.setMsg("param[NAMESPACE] can not be null."); callback.setMsg("param[NAMESPACE] can not be null.");
return JacksonUtil.writeValueAsString(callback); return JacksonUtil.writeValueAsString(callback);
} }
// encryption check
// parse namespace long timestamp = _param.get(HandlerParamEnum.TIMESTAMP.name())!=null?Long.valueOf(_param.get(HandlerParamEnum.TIMESTAMP.name())):-1;
if (namespace.equals(HandlerRepository.NameSpaceEnum.RUN.name())) { if (System.currentTimeMillis() - timestamp > 60000) {
// encryption check callback.setMsg("Timestamp check failed.");
long timestamp = _param.get(HandlerRepository.TRIGGER_TIMESTAMP)!=null?Long.valueOf(_param.get(HandlerRepository.TRIGGER_TIMESTAMP)):-1; return JacksonUtil.writeValueAsString(callback);
if (System.currentTimeMillis() - timestamp > 60000) { }
callback.setMsg("Timestamp check failed.");
return JacksonUtil.writeValueAsString(callback);
}
// parse namespace
if (namespace.equals(ActionEnum.RUN.name())) {
// push data to queue // push data to queue
String handler_glue_switch = _param.get(HandlerRepository.HANDLER_GLUE_SWITCH); String handler_glue_switch = _param.get(HandlerParamEnum.GLUE_SWITCH.name());
HandlerThread handlerThread = null; HandlerThread handlerThread = null;
if ("0".equals(handler_glue_switch)) { if ("0".equals(handler_glue_switch)) {
// bean model // bean model
String handler_name = _param.get(HandlerRepository.HANDLER_NAME); String handler_name = _param.get(HandlerParamEnum.EXECUTOR_HANDLER.name());
if (handler_name == null || handler_name.trim().length()==0) { if (handler_name == null || handler_name.trim().length()==0) {
callback.setMsg("bean model handler[HANDLER_NAME] not found."); callback.setMsg("bean model handler[HANDLER_NAME] not found.");
return JacksonUtil.writeValueAsString(callback); return JacksonUtil.writeValueAsString(callback);
@ -85,70 +113,81 @@ public class HandlerRepository {
} }
} else { } else {
// glue // glue
String handler_job_group = _param.get(HandlerRepository.HANDLER_JOB_GROUP); String job_group = _param.get(HandlerParamEnum.JOB_GROUP.name());
String handler_job_name = _param.get(HandlerRepository.HANDLER_JOB_NAME); String job_name = _param.get(HandlerParamEnum.JOB_NAME.name());
if (handler_job_group == null || handler_job_group.trim().length()==0 || handler_job_name == null || handler_job_name.trim().length()==0) { if (job_group == null || job_group.trim().length()==0 || job_name == null || job_name.trim().length()==0) {
callback.setMsg("glue model handler[job group or name] is null."); callback.setMsg("glue model handler[job group or name] is null.");
return JacksonUtil.writeValueAsString(callback); return JacksonUtil.writeValueAsString(callback);
} }
String glueHandleName = "glue_".concat(handler_job_group).concat("_").concat(handler_job_name); String glueHandleName = "glue_".concat(job_group).concat("_").concat(job_name);
handlerThread = handlerTreadMap.get(glueHandleName); handlerThread = handlerTreadMap.get(glueHandleName);
if (handlerThread==null) { if (handlerThread==null) {
HandlerRepository.regist(glueHandleName, new GlueJobHandler(handler_job_group, handler_job_name)); HandlerRepository.regist(glueHandleName, new GlueJobHandler(job_group, job_name));
} }
handlerThread = handlerTreadMap.get(glueHandleName); handlerThread = handlerTreadMap.get(glueHandleName);
} }
handlerThread.pushData(_param); handlerThread.pushData(_param);
callback.setStatus(RemoteCallBack.SUCCESS); callback.setStatus(RemoteCallBack.SUCCESS);
} else if (namespace.equals(HandlerRepository.NameSpaceEnum.LOG.name())) { } else if (namespace.equals(ActionEnum.LOG.name())) {
String trigger_log_id = _param.get(HandlerRepository.TRIGGER_LOG_ID); String log_id = _param.get(HandlerParamEnum.LOG_ID.name());
String trigger_timestamp = _param.get(HandlerRepository.TRIGGER_TIMESTAMP); String log_date = _param.get(HandlerParamEnum.LOG_DATE.name());
if (trigger_log_id==null || trigger_timestamp==null) { if (log_id==null || log_date==null) {
callback.setMsg("trigger_log_id | trigger_timestamp can not be null."); callback.setMsg("LOG_ID | LOG_DATE can not be null.");
return JacksonUtil.writeValueAsString(callback); return JacksonUtil.writeValueAsString(callback);
} }
int logId = -1; int logId = -1;
Date triggerDate = null; Date triggerDate = null;
try { try {
logId = Integer.valueOf(trigger_log_id); logId = Integer.valueOf(log_id);
triggerDate = new Date(Long.valueOf(trigger_timestamp)); triggerDate = new Date(Long.valueOf(log_date));
} catch (Exception e) { } catch (Exception e) {
} }
if (logId<=0 || triggerDate==null) { if (logId<=0 || triggerDate==null) {
callback.setMsg("trigger_log_id | trigger_timestamp is not parsed valid."); callback.setMsg("LOG_ID | LOG_DATE parse error.");
return JacksonUtil.writeValueAsString(callback); return JacksonUtil.writeValueAsString(callback);
} }
String logConteng = XxlJobFileAppender.readLog(triggerDate, trigger_log_id); String logConteng = XxlJobFileAppender.readLog(triggerDate, log_id);
callback.setStatus(RemoteCallBack.SUCCESS); callback.setStatus(RemoteCallBack.SUCCESS);
callback.setMsg(logConteng); callback.setMsg(logConteng);
} else if (namespace.equals(HandlerRepository.NameSpaceEnum.KILL.name())) { } else if (namespace.equals(ActionEnum.KILL.name())) {
// encryption check
long timestamp = _param.get(HandlerRepository.TRIGGER_TIMESTAMP)!=null?Long.valueOf(_param.get(HandlerRepository.TRIGGER_TIMESTAMP)):-1;
if (System.currentTimeMillis() - timestamp > 60000) {
callback.setMsg("Timestamp check failed.");
return JacksonUtil.writeValueAsString(callback);
}
// kill handlerThread, and create new one // kill handlerThread, and create new one
String handler_name = _param.get(HandlerRepository.HANDLER_NAME); String handler_glue_switch = _param.get(HandlerParamEnum.GLUE_SWITCH.name());
if (handler_name!=null && handler_name.trim().length()>0) { String handlerName = null;
HandlerThread handlerThread = handlerTreadMap.get(handler_name); if ("0".equals(handler_glue_switch)) {
if (handlerThread != null) { String executor_handler = _param.get(HandlerParamEnum.EXECUTOR_HANDLER.name());
IJobHandler handler = handlerThread.getHandler(); if (executor_handler==null) {
handlerThread.toStop(); callback.setMsg("bean job , param[EXECUTOR_HANDLER] is null");
handlerThread.interrupt(); return JacksonUtil.writeValueAsString(callback);
regist(handler_name, handler); }
callback.setStatus(RemoteCallBack.SUCCESS); handlerName = executor_handler;
} else { } else {
callback.setMsg("handler[" + handler_name + "] not found."); // glue
String job_group = _param.get(HandlerParamEnum.JOB_GROUP.name());
String job_name = _param.get(HandlerParamEnum.JOB_NAME.name());
if (job_group==null || job_group.trim().length()==0 || job_name==null || job_name.trim().length()==0) {
callback.setMsg("glue job , param[JOB_GROUP or JOB_NAME] is null");
return JacksonUtil.writeValueAsString(callback);
} }
}else{ handlerName = "glue_".concat(job_group).concat("_").concat(job_name);
callback.setMsg("param[HANDLER_NAME] can not be null."); }
HandlerThread handlerThread = handlerTreadMap.get(handlerName);
if (handlerThread != null) {
IJobHandler handler = handlerThread.getHandler();
handlerThread.toStop();
handlerThread.interrupt();
regist(handlerName, handler);
callback.setStatus(RemoteCallBack.SUCCESS);
} else {
callback.setMsg("job handler[" + handlerName + "] not found.");
} }
} else if (namespace.equals(ActionEnum.BEAT.name())) {
callback.setStatus(RemoteCallBack.SUCCESS);
callback.setMsg(null);
} else { } else {
callback.setMsg("param[NAMESPACE] is not valid."); callback.setMsg("param[Action] is not valid.");
return JacksonUtil.writeValueAsString(callback); return JacksonUtil.writeValueAsString(callback);
} }

@ -11,6 +11,7 @@ import org.eclipse.jetty.util.ConcurrentHashSet;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
import com.xxl.job.core.handler.HandlerRepository.HandlerParamEnum;
import com.xxl.job.core.handler.IJobHandler.JobHandleStatus; import com.xxl.job.core.handler.IJobHandler.JobHandleStatus;
import com.xxl.job.core.log.XxlJobFileAppender; import com.xxl.job.core.log.XxlJobFileAppender;
import com.xxl.job.core.util.HttpUtil; import com.xxl.job.core.util.HttpUtil;
@ -47,7 +48,7 @@ public class HandlerThread extends Thread{
} }
public void pushData(Map<String, String> param) { public void pushData(Map<String, String> param) {
if (param.get(HandlerRepository.TRIGGER_LOG_ID)!=null && !logIdSet.contains(param.get(HandlerRepository.TRIGGER_LOG_ID))) { if (param.get(HandlerParamEnum.LOG_ID.name())!=null && !logIdSet.contains(param.get(HandlerParamEnum.LOG_ID.name()))) {
handlerDataQueue.offer(param); handlerDataQueue.offer(param);
} }
} }
@ -60,9 +61,9 @@ public class HandlerThread extends Thread{
Map<String, String> handlerData = handlerDataQueue.poll(); Map<String, String> handlerData = handlerDataQueue.poll();
if (handlerData!=null) { if (handlerData!=null) {
i= 0; i= 0;
String trigger_log_address = handlerData.get(HandlerRepository.TRIGGER_LOG_ADDRESS); String trigger_log_address = handlerData.get(HandlerParamEnum.LOG_ADDRESS.name());
String trigger_log_id = handlerData.get(HandlerRepository.TRIGGER_LOG_ID); String trigger_log_id = handlerData.get(HandlerParamEnum.LOG_ID.name());
String handler_params = handlerData.get(HandlerRepository.HANDLER_PARAMS); String handler_params = handlerData.get(HandlerParamEnum.EXECUTOR_PARAMS.name());
logIdSet.remove(trigger_log_id); logIdSet.remove(trigger_log_id);
// parse param // parse param

@ -1,8 +1,6 @@
package com.xxl.job.core.util; package com.xxl.job.core.util;
import java.io.IOException; import java.io.IOException;
import java.io.PrintWriter;
import java.io.StringWriter;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
@ -98,9 +96,10 @@ public class HttpUtil {
} }
} catch (Exception e) { } catch (Exception e) {
e.printStackTrace(); e.printStackTrace();
StringWriter out = new StringWriter(); /*StringWriter out = new StringWriter();
e.printStackTrace(new PrintWriter(out)); e.printStackTrace(new PrintWriter(out));
callback.setMsg(out.toString()); callback.setMsg(out.toString());*/
callback.setMsg(e.getMessage());
} finally{ } finally{
if (httpPost!=null) { if (httpPost!=null) {
httpPost.releaseConnection(); httpPost.releaseConnection();

Loading…
Cancel
Save