upgrade(xxl-job): 升级 3.4.1

This commit is contained in:
PIGCLOUD committed 2026-08-01 11:58:02 +08:00
1 parent 87489ed353
commit d6ef8e4e98
126 files changed
+2451 -2223

No files matched your search

@@ -53,14 +53,13 @@ public class XxlJobAutoConfiguration {
xxlJobSpringExecutor.setLogPath(executor.getLogPath());
xxlJobSpringExecutor.setLogRetentionDays(executor.getLogRetentionDays());
// 如果配置为空则获取注册中心的服务列表 "http://pigx-xxl:9080/xxl-job-admin"
// XXL-JOB 3.4.1 起调度中心地址由客户端自动追加 /api,不再拼接 context-path。
if (!StringUtils.hasText(xxlJobProperties.getAdmin().getAddresses())) {
String serverList = discoveryClient.getServices()
.stream()
.filter(s -> s.contains(XXL_JOB_ADMIN))
.flatMap(s -> discoveryClient.getInstances(s).stream())
.map(instance -> String.format("http://%s:%s/%s", instance.getHost(), instance.getPort(),
XXL_JOB_ADMIN))
.map(instance -> String.format("http://%s:%s", instance.getHost(), instance.getPort()))
.collect(Collectors.joining(","));
xxlJobSpringExecutor.setAdminAddresses(serverList);
}
@@ -10,7 +10,7 @@ import org.springframework.boot.autoconfigure.SpringBootApplication;
public class PigxJobAdminApplication {
public static void main(String[] args) {
SpringApplication.run(PigxJobAdminApplication.class, args);
SpringApplication.run(PigxJobAdminApplication.class, args);
}
}
@@ -1,8 +1,20 @@
package com.xxl.job.admin.constant;
package com.xxl.job.admin.business.constant;
/**
* Trigger Status
*
* @author xuxueli 2019-05-04
*/
public enum TriggerStatus {
/**
* Stopped
*/
STOPPED(0, "stopped"),
/**
* Running
*/
RUNNING(1, "running");
private int value;
@@ -1,11 +1,12 @@
package com.xxl.job.admin.controller.biz;
package com.xxl.job.admin.business.controller;
import com.xxl.job.admin.mapper.XxlJobInfoMapper;
import com.xxl.job.admin.mapper.XxlJobLogGlueMapper;
import com.xxl.job.admin.model.XxlJobInfo;
import com.xxl.job.admin.model.XxlJobLogGlue;
import com.xxl.job.admin.util.I18nUtil;
import com.xxl.job.admin.util.JobGroupPermissionUtil;
import com.xxl.job.admin.business.mapper.XxlJobInfoMapper;
import com.xxl.job.admin.business.mapper.XxlJobLogGlueMapper;
import com.xxl.job.admin.business.model.XxlJobInfo;
import com.xxl.job.admin.business.model.XxlJobLogGlue;
import com.xxl.job.admin.framework.util.I18nUtil;
import com.xxl.job.admin.framework.util.JobGroupPermissionUtil;
import com.xxl.job.admin.framework.util.XssUtil;
import com.xxl.job.core.glue.GlueTypeEnum;
import com.xxl.sso.core.model.LoginInfo;
import com.xxl.tool.core.StringTool;
@@ -59,7 +60,7 @@ public class JobCodeController {
model.addAttribute("jobInfo", jobInfo);
model.addAttribute("jobLogGlues", jobLogGlues);
return "biz/job.code";
return "business/job.code";
}
@RequestMapping("/save")
@@ -79,6 +80,9 @@ public class JobCodeController {
if (glueRemark.length() < 4 || glueRemark.length() > 100) {
return Response.ofFail(I18nUtil.getString("jobinfo_glue_remark_limit"));
}
if (XssUtil.hasXss(glueRemark)) {
return Response.ofFail(I18nUtil.getString("jobinfo_glue_remark") + I18nUtil.getString("system_invalid"));
}
XxlJobInfo existsJobInfo = xxlJobInfoMapper.loadById(id);
if (existsJobInfo == null) {
return Response.ofFail(I18nUtil.getString("jobinfo_glue_jobid_invalid"));
@@ -1,14 +1,15 @@
package com.xxl.job.admin.controller.biz;
package com.xxl.job.admin.business.controller;
import com.xxl.job.admin.constant.Consts;
import com.xxl.job.admin.mapper.XxlJobGroupMapper;
import com.xxl.job.admin.mapper.XxlJobInfoMapper;
import com.xxl.job.admin.mapper.XxlJobRegistryMapper;
import com.xxl.job.admin.model.XxlJobGroup;
import com.xxl.job.admin.model.XxlJobRegistry;
import com.xxl.job.admin.util.I18nUtil;
import com.xxl.job.admin.business.mapper.XxlJobGroupMapper;
import com.xxl.job.admin.business.mapper.XxlJobInfoMapper;
import com.xxl.job.admin.business.mapper.XxlJobRegistryMapper;
import com.xxl.job.admin.business.model.XxlJobGroup;
import com.xxl.job.admin.business.model.XxlJobRegistry;
import com.xxl.job.admin.framework.constant.Consts;
import com.xxl.job.admin.framework.util.I18nUtil;
import com.xxl.job.admin.framework.util.XssUtil;
import com.xxl.job.core.constant.Const;
import com.xxl.job.core.constant.RegistType;
import com.xxl.job.core.constant.RegistTypeEnum;
import com.xxl.sso.core.annotation.XxlSso;
import com.xxl.tool.core.CollectionTool;
import com.xxl.tool.core.StringTool;
@@ -43,7 +44,7 @@ public class JobGroupController {
@RequestMapping
@XxlSso(role = Consts.ADMIN_ROLE)
public String index(Model model) {
return "biz/group.list";
return "business/group.list";
}
@RequestMapping("/pageList")
@@ -71,27 +72,31 @@ public class JobGroupController {
@XxlSso(role = Consts.ADMIN_ROLE)
public Response<String> insert(XxlJobGroup xxlJobGroup) {
// valid
// valid appname
if (StringTool.isBlank(xxlJobGroup.getAppname())) {
return Response.ofFail((I18nUtil.getString("system_please_input") + "AppName"));
}
if (xxlJobGroup.getAppname().length() < 4 || xxlJobGroup.getAppname().length() > 64) {
return Response.ofFail(I18nUtil.getString("jobgroup_field_appname_length"));
}
if (xxlJobGroup.getAppname().contains(">") || xxlJobGroup.getAppname().contains("<")) {
if (XssUtil.hasXss(xxlJobGroup.getAppname())) {
return Response.ofFail("AppName" + I18nUtil.getString("system_invalid"));
}
// valid title
if (StringTool.isBlank(xxlJobGroup.getTitle())) {
return Response.ofFail((I18nUtil.getString("system_please_input") + I18nUtil.getString("jobgroup_field_title")));
}
if (xxlJobGroup.getTitle().contains(">") || xxlJobGroup.getTitle().contains("<")) {
if (XssUtil.hasXss(xxlJobGroup.getTitle())) {
return Response.ofFail(I18nUtil.getString("jobgroup_field_title") + I18nUtil.getString("system_invalid"));
}
if (xxlJobGroup.getAddressType() != 0) {
// valid addressList
if (StringTool.isBlank(xxlJobGroup.getAddressList())) {
return Response.ofFail(I18nUtil.getString("jobgroup_field_addressType_limit"));
}
if (xxlJobGroup.getAddressList().contains(">") || xxlJobGroup.getAddressList().contains("<")) {
if (XssUtil.hasXss(xxlJobGroup.getAddressList())) {
return Response.ofFail(I18nUtil.getString("jobgroup_field_registryList") + I18nUtil.getString("system_invalid"));
}
@@ -117,16 +122,26 @@ public class JobGroupController {
@ResponseBody
@XxlSso(role = Consts.ADMIN_ROLE)
public Response<String> update(XxlJobGroup xxlJobGroup) {
// valid
// valid appname
if (StringTool.isBlank(xxlJobGroup.getAppname())) {
return Response.ofFail((I18nUtil.getString("system_please_input") + "AppName"));
}
if (xxlJobGroup.getAppname().length() < 4 || xxlJobGroup.getAppname().length() > 64) {
return Response.ofFail(I18nUtil.getString("jobgroup_field_appname_length"));
}
if (XssUtil.hasXss(xxlJobGroup.getAppname())) {
return Response.ofFail("AppName" + I18nUtil.getString("system_invalid"));
}
// valid title
if (StringTool.isBlank(xxlJobGroup.getTitle())) {
return Response.ofFail((I18nUtil.getString("system_please_input") + I18nUtil.getString("jobgroup_field_title")));
}
if (XssUtil.hasXss(xxlJobGroup.getTitle())) {
return Response.ofFail(I18nUtil.getString("jobgroup_field_title") + I18nUtil.getString("system_invalid"));
}
if (xxlJobGroup.getAddressType() == 0) {
// 0=自动注册
List<String> registryList = findRegistryByAppName(xxlJobGroup.getAppname());
@@ -138,9 +153,15 @@ public class JobGroupController {
xxlJobGroup.setAddressList(addressListStr);
} else {
// 1=手动录入
// valid addressList
if (StringTool.isBlank(xxlJobGroup.getAddressList())) {
return Response.ofFail(I18nUtil.getString("jobgroup_field_addressType_limit"));
}
if (XssUtil.hasXss(xxlJobGroup.getAddressList())) {
return Response.ofFail(I18nUtil.getString("jobgroup_field_registryList") + I18nUtil.getString("system_invalid"));
}
String[] addresss = xxlJobGroup.getAddressList().split(",");
for (String item : addresss) {
if (StringTool.isBlank(item)) {
@@ -164,7 +185,7 @@ public class JobGroupController {
List<XxlJobRegistry> list = xxlJobRegistryMapper.findAll(Const.DEAD_TIMEOUT, new Date());
if (CollectionTool.isNotEmpty(list)) {
for (XxlJobRegistry item : list) {
if (!RegistType.EXECUTOR.name().equals(item.getRegistryGroup())) {
if (!RegistTypeEnum.EXECUTOR.name().equals(item.getRegistryGroup())) {
continue;
}
@@ -211,7 +232,7 @@ public class JobGroupController {
// remove group
int ret = xxlJobGroupMapper.remove(id);
// remove registry-data
xxlJobRegistryMapper.removeByRegistryGroupAndKey(RegistType.EXECUTOR.name(), xxlJobGroup.getAppname());
xxlJobRegistryMapper.removeByRegistryGroupAndKey(RegistTypeEnum.EXECUTOR.name(), xxlJobGroup.getAppname());
return (ret > 0) ? Response.ofSuccess() : Response.ofFail();
}
@@ -1,15 +1,15 @@
package com.xxl.job.admin.controller.biz;
package com.xxl.job.admin.business.controller;
import com.xxl.job.admin.mapper.XxlJobGroupMapper;
import com.xxl.job.admin.model.XxlJobGroup;
import com.xxl.job.admin.model.XxlJobInfo;
import com.xxl.job.admin.scheduler.exception.XxlJobException;
import com.xxl.job.admin.scheduler.misfire.MisfireStrategyEnum;
import com.xxl.job.admin.scheduler.route.ExecutorRouteStrategyEnum;
import com.xxl.job.admin.scheduler.type.ScheduleTypeEnum;
import com.xxl.job.admin.service.XxlJobService;
import com.xxl.job.admin.util.I18nUtil;
import com.xxl.job.admin.util.JobGroupPermissionUtil;
import com.xxl.job.admin.business.mapper.XxlJobGroupMapper;
import com.xxl.job.admin.business.model.XxlJobGroup;
import com.xxl.job.admin.business.model.XxlJobInfo;
import com.xxl.job.admin.business.scheduler.exception.XxlJobException;
import com.xxl.job.admin.business.scheduler.misfire.MisfireStrategyEnum;
import com.xxl.job.admin.business.scheduler.route.ExecutorRouteStrategyEnum;
import com.xxl.job.admin.business.scheduler.type.ScheduleTypeEnum;
import com.xxl.job.admin.business.service.XxlJobService;
import com.xxl.job.admin.framework.util.I18nUtil;
import com.xxl.job.admin.framework.util.JobGroupPermissionUtil;
import com.xxl.job.core.constant.ExecutorBlockStrategyEnum;
import com.xxl.job.core.glue.GlueTypeEnum;
import com.xxl.sso.core.helper.XxlSsoHelper;
@@ -76,7 +76,7 @@ public class JobInfoController {
model.addAttribute("JobGroupList", jobGroupList);
model.addAttribute("jobGroup", jobGroup);
return "biz/job.list";
return "business/job.list";
}
@RequestMapping("/pageList")
@@ -1,16 +1,17 @@
package com.xxl.job.admin.controller.biz;
package com.xxl.job.admin.business.controller;
import com.xxl.job.admin.mapper.XxlJobGroupMapper;
import com.xxl.job.admin.mapper.XxlJobInfoMapper;
import com.xxl.job.admin.mapper.XxlJobLogMapper;
import com.xxl.job.admin.model.XxlJobGroup;
import com.xxl.job.admin.model.XxlJobInfo;
import com.xxl.job.admin.model.XxlJobLog;
import com.xxl.job.admin.scheduler.config.XxlJobAdminBootstrap;
import com.xxl.job.admin.scheduler.exception.XxlJobException;
import com.xxl.job.admin.service.XxlJobService;
import com.xxl.job.admin.util.I18nUtil;
import com.xxl.job.admin.util.JobGroupPermissionUtil;
import com.xxl.job.admin.business.mapper.XxlJobGroupMapper;
import com.xxl.job.admin.business.mapper.XxlJobInfoMapper;
import com.xxl.job.admin.business.mapper.XxlJobLogMapper;
import com.xxl.job.admin.business.model.XxlJobGroup;
import com.xxl.job.admin.business.model.XxlJobInfo;
import com.xxl.job.admin.business.model.XxlJobLog;
import com.xxl.job.admin.business.model.dto.XxlJobLogDTO;
import com.xxl.job.admin.business.scheduler.config.XxlJobAdminBootstrap;
import com.xxl.job.admin.business.scheduler.exception.XxlJobException;
import com.xxl.job.admin.business.service.XxlJobService;
import com.xxl.job.admin.framework.util.I18nUtil;
import com.xxl.job.admin.framework.util.JobGroupPermissionUtil;
import com.xxl.job.core.context.XxlJobContext;
import com.xxl.job.core.openapi.ExecutorBiz;
import com.xxl.job.core.openapi.model.KillRequest;
@@ -105,18 +106,18 @@ public class JobLogController {
model.addAttribute("jobGroup", jobGroup);
model.addAttribute("jobId", jobId);
return "biz/log.list";
return "business/log.list";
}
@RequestMapping("/pageList")
@ResponseBody
public Response<PageModel<XxlJobLog>> pageList(HttpServletRequest request,
@RequestParam(required = false, defaultValue = "0") int offset,
@RequestParam(required = false, defaultValue = "10") int pagesize,
@RequestParam int jobGroup,
@RequestParam int jobId,
@RequestParam int logStatus,
@RequestParam String filterTime) {
public Response<PageModel<XxlJobLogDTO>> pageList(HttpServletRequest request,
@RequestParam(required = false, defaultValue = "0") int offset,
@RequestParam(required = false, defaultValue = "10") int pagesize,
@RequestParam int jobGroup,
@RequestParam int jobId,
@RequestParam int logStatus,
@RequestParam String filterTime) {
// valid jobGroup permission
JobGroupPermissionUtil.validJobGroupPermission(request, jobGroup);
@@ -141,9 +142,12 @@ public class JobLogController {
List<XxlJobLog> list = xxlJobLogMapper.pageList(offset, pagesize, jobGroup, jobId, triggerTimeStart, triggerTimeEnd, logStatus);
int list_count = xxlJobLogMapper.pageListCount(offset, pagesize, jobGroup, jobId, triggerTimeStart, triggerTimeEnd, logStatus);
// model > dto
List<XxlJobLogDTO> listDTO = list.stream().map(XxlJobLogDTO::new).toList();
// package result
PageModel<XxlJobLog> pageModel = new PageModel<>();
pageModel.setData(list);
PageModel<XxlJobLogDTO> pageModel = new PageModel<>();
pageModel.setData(listDTO);
pageModel.setTotal(list_count);
return Response.ofSuccess(pageModel);
@@ -285,7 +289,7 @@ public class JobLogController {
model.addAttribute("handleCode", jobLog.getHandleCode());
model.addAttribute("logId", jobLog.getId());
model.addAttribute("jobInfo", jobInfo);
return "biz/log.detail";
return "business/log.detail";
}
@RequestMapping("/logDetailCat")
@@ -1,6 +1,6 @@
package com.xxl.job.admin.mapper;
package com.xxl.job.admin.business.mapper;
import com.xxl.job.admin.model.XxlJobGroup;
import com.xxl.job.admin.business.model.XxlJobGroup;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Param;
@@ -1,6 +1,6 @@
package com.xxl.job.admin.mapper;
package com.xxl.job.admin.business.mapper;
import com.xxl.job.admin.model.XxlJobInfo;
import com.xxl.job.admin.business.model.XxlJobInfo;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Param;
@@ -1,4 +1,4 @@
package com.xxl.job.admin.mapper;
package com.xxl.job.admin.business.mapper;
import org.apache.ibatis.annotations.Mapper;
@@ -0,0 +1,25 @@
package com.xxl.job.admin.business.mapper;
import com.xxl.job.admin.business.model.XxlJobLogGlue;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Param;
import java.util.List;
/**
* job log for glue
*
* @author xuxueli 2016-5-19 18:04:56
*/
@Mapper
public interface XxlJobLogGlueMapper {
public int save(XxlJobLogGlue xxlJobLogGlue);
public List<XxlJobLogGlue> findByJobId(@Param("jobId") int jobId);
public int removeOld(@Param("jobId") int jobId, @Param("limit") int limit);
public int deleteByJobId(@Param("jobId") int jobId);
}
@@ -1,6 +1,6 @@
package com.xxl.job.admin.mapper;
package com.xxl.job.admin.business.mapper;
import com.xxl.job.admin.model.XxlJobLog;
import com.xxl.job.admin.business.model.XxlJobLog;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Param;
@@ -1,6 +1,6 @@
package com.xxl.job.admin.mapper;
package com.xxl.job.admin.business.mapper;
import com.xxl.job.admin.model.XxlJobLogReport;
import com.xxl.job.admin.business.model.XxlJobLogReport;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Param;
@@ -1,6 +1,6 @@
package com.xxl.job.admin.mapper;
package com.xxl.job.admin.business.mapper;
import com.xxl.job.admin.model.XxlJobRegistry;
import com.xxl.job.admin.business.model.XxlJobRegistry;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Param;
@@ -1,4 +1,4 @@
package com.xxl.job.admin.model;
package com.xxl.job.admin.business.model;
import com.xxl.tool.core.StringTool;
@@ -0,0 +1,237 @@
package com.xxl.job.admin.business.model;
import java.util.Date;
/**
* xxl-job info
*
* @author xuxueli 2016-1-12 18:25:49
*/
public class XxlJobInfo {
private int id; // 主键ID
private int jobGroup; // 执行器主键ID
private String jobDesc;
private Date addTime;
private Date updateTime;
private String author; // 负责人
private String alarmEmail; // 报警邮件
private String scheduleType; // 调度类型:ScheduleTypeEnum
private String scheduleConf; // 调度配置,值含义取决于调度类型
private String misfireStrategy; // 调度过期策略:MisfireStrategyEnum
private String executorRouteStrategy; // 执行器路由策略:ExecutorRouteStrategyEnum
private String executorHandler; // 执行器,任务Handler名称
private String executorParam; // 执行器,任务参数
private String executorBlockStrategy; // 阻塞处理策略:ExecutorBlockStrategyEnum
private int executorTimeout; // 任务执行超时时间,单位秒
private int executorFailRetryCount; // 失败重试次数
private String glueType; // GLUE类型:GlueTypeEnum
private String glueSource; // GLUE源代码
private String glueRemark; // GLUE备注
private Date glueUpdatetime; // GLUE更新时间
private String childJobId; // 子任务ID,多个逗号分隔
private int triggerStatus; // 调度状态:TriggerStatus
private long triggerLastTime; // 上次调度时间
private long triggerNextTime; // 下次调度时间
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public int getJobGroup() {
return jobGroup;
}
public void setJobGroup(int jobGroup) {
this.jobGroup = jobGroup;
}
public String getJobDesc() {
return jobDesc;
}
public void setJobDesc(String jobDesc) {
this.jobDesc = jobDesc;
}
public Date getAddTime() {
return addTime;
}
public void setAddTime(Date addTime) {
this.addTime = addTime;
}
public Date getUpdateTime() {
return updateTime;
}
public void setUpdateTime(Date updateTime) {
this.updateTime = updateTime;
}
public String getAuthor() {
return author;
}
public void setAuthor(String author) {
this.author = author;
}
public String getAlarmEmail() {
return alarmEmail;
}
public void setAlarmEmail(String alarmEmail) {
this.alarmEmail = alarmEmail;
}
public String getScheduleType() {
return scheduleType;
}
public void setScheduleType(String scheduleType) {
this.scheduleType = scheduleType;
}
public String getScheduleConf() {
return scheduleConf;
}
public void setScheduleConf(String scheduleConf) {
this.scheduleConf = scheduleConf;
}
public String getMisfireStrategy() {
return misfireStrategy;
}
public void setMisfireStrategy(String misfireStrategy) {
this.misfireStrategy = misfireStrategy;
}
public String getExecutorRouteStrategy() {
return executorRouteStrategy;
}
public void setExecutorRouteStrategy(String executorRouteStrategy) {
this.executorRouteStrategy = executorRouteStrategy;
}
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 String getExecutorBlockStrategy() {
return executorBlockStrategy;
}
public void setExecutorBlockStrategy(String executorBlockStrategy) {
this.executorBlockStrategy = executorBlockStrategy;
}
public int getExecutorTimeout() {
return executorTimeout;
}
public void setExecutorTimeout(int executorTimeout) {
this.executorTimeout = executorTimeout;
}
public int getExecutorFailRetryCount() {
return executorFailRetryCount;
}
public void setExecutorFailRetryCount(int executorFailRetryCount) {
this.executorFailRetryCount = executorFailRetryCount;
}
public String getGlueType() {
return glueType;
}
public void setGlueType(String glueType) {
this.glueType = glueType;
}
public String getGlueSource() {
return glueSource;
}
public void setGlueSource(String glueSource) {
this.glueSource = glueSource;
}
public String getGlueRemark() {
return glueRemark;
}
public void setGlueRemark(String glueRemark) {
this.glueRemark = glueRemark;
}
public Date getGlueUpdatetime() {
return glueUpdatetime;
}
public void setGlueUpdatetime(Date glueUpdatetime) {
this.glueUpdatetime = glueUpdatetime;
}
public String getChildJobId() {
return childJobId;
}
public void setChildJobId(String childJobId) {
this.childJobId = childJobId;
}
public int getTriggerStatus() {
return triggerStatus;
}
public void setTriggerStatus(int triggerStatus) {
this.triggerStatus = triggerStatus;
}
public long getTriggerLastTime() {
return triggerLastTime;
}
public void setTriggerLastTime(long triggerLastTime) {
this.triggerLastTime = triggerLastTime;
}
public long getTriggerNextTime() {
return triggerNextTime;
}
public void setTriggerNextTime(long triggerNextTime) {
this.triggerNextTime = triggerNextTime;
}
}
@@ -0,0 +1,158 @@
package com.xxl.job.admin.business.model;
import java.util.Date;
/**
* xxl-job log, used to track trigger process
*
* @author xuxueli 2015-12-19 23:19:09
*/
public class XxlJobLog {
private long id;
// job info
private int jobGroup;
private int jobId;
// execute info
private String executorAddress;
private String executorHandler;
private String executorParam;
private String executorShardingParam;
private int executorFailRetryCount;
// trigger info
private Date triggerTime;
private int triggerCode;
private String triggerMsg;
// handle info
private Date handleTime;
private int handleCode;
private String handleMsg;
// alarm info
private int alarmStatus;
public long getId() {
return id;
}
public void setId(long id) {
this.id = id;
}
public int getJobGroup() {
return jobGroup;
}
public void setJobGroup(int jobGroup) {
this.jobGroup = jobGroup;
}
public int getJobId() {
return jobId;
}
public void setJobId(int jobId) {
this.jobId = jobId;
}
public String getExecutorAddress() {
return executorAddress;
}
public void setExecutorAddress(String executorAddress) {
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 String getExecutorShardingParam() {
return executorShardingParam;
}
public void setExecutorShardingParam(String executorShardingParam) {
this.executorShardingParam = executorShardingParam;
}
public int getExecutorFailRetryCount() {
return executorFailRetryCount;
}
public void setExecutorFailRetryCount(int executorFailRetryCount) {
this.executorFailRetryCount = executorFailRetryCount;
}
public Date getTriggerTime() {
return triggerTime;
}
public void setTriggerTime(Date triggerTime) {
this.triggerTime = triggerTime;
}
public int getTriggerCode() {
return triggerCode;
}
public void setTriggerCode(int triggerCode) {
this.triggerCode = triggerCode;
}
public String getTriggerMsg() {
return triggerMsg;
}
public void setTriggerMsg(String triggerMsg) {
this.triggerMsg = triggerMsg;
}
public Date getHandleTime() {
return handleTime;
}
public void setHandleTime(Date handleTime) {
this.handleTime = handleTime;
}
public int getHandleCode() {
return handleCode;
}
public void setHandleCode(int handleCode) {
this.handleCode = handleCode;
}
public String getHandleMsg() {
return handleMsg;
}
public void setHandleMsg(String handleMsg) {
this.handleMsg = handleMsg;
}
public int getAlarmStatus() {
return alarmStatus;
}
public void setAlarmStatus(int alarmStatus) {
this.alarmStatus = alarmStatus;
}
}
@@ -0,0 +1,76 @@
package com.xxl.job.admin.business.model;
import java.util.Date;
/**
* xxl-job log for glue, used to track job code process
*
* @author xuxueli 2016-5-19 17:57:46
*/
public class XxlJobLogGlue {
private int id;
private int jobId; // 任务主键ID
private String glueType; // GLUE类型 #com.xxl.job.core.glue.GlueTypeEnum
private String glueSource;
private String glueRemark;
private Date addTime;
private Date updateTime;
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public int getJobId() {
return jobId;
}
public void setJobId(int jobId) {
this.jobId = jobId;
}
public String getGlueType() {
return glueType;
}
public void setGlueType(String glueType) {
this.glueType = glueType;
}
public String getGlueSource() {
return glueSource;
}
public void setGlueSource(String glueSource) {
this.glueSource = glueSource;
}
public String getGlueRemark() {
return glueRemark;
}
public void setGlueRemark(String glueRemark) {
this.glueRemark = glueRemark;
}
public Date getAddTime() {
return addTime;
}
public void setAddTime(Date addTime) {
this.addTime = addTime;
}
public Date getUpdateTime() {
return updateTime;
}
public void setUpdateTime(Date updateTime) {
this.updateTime = updateTime;
}
}
@@ -1,4 +1,4 @@
package com.xxl.job.admin.model;
package com.xxl.job.admin.business.model;
import java.util.Date;
@@ -1,4 +1,4 @@
package com.xxl.job.admin.model;
package com.xxl.job.admin.business.model;
import java.util.Date;
@@ -0,0 +1,172 @@
package com.xxl.job.admin.business.model.dto;
import com.xxl.job.admin.business.model.XxlJobLog;
import com.xxl.tool.core.DateTool;
public class XxlJobLogDTO {
private long id;
// job info
private int jobGroup;
private int jobId;
// execute info
private String executorAddress;
private String executorHandler;
private String executorParam;
private String executorShardingParam;
private int executorFailRetryCount;
// trigger info
private String triggerTime;
private int triggerCode;
private String triggerMsg;
// handle info
private String handleTime;
private int handleCode;
private String handleMsg;
// alarm info
private int alarmStatus;
public XxlJobLogDTO(XxlJobLog xxlJobLog) {
this.id = xxlJobLog.getId();
this.jobGroup = xxlJobLog.getJobGroup();
this.jobId = xxlJobLog.getJobId();
this.executorAddress = xxlJobLog.getExecutorAddress();
this.executorHandler = xxlJobLog.getExecutorHandler();
this.executorParam = xxlJobLog.getExecutorParam();
this.executorShardingParam = xxlJobLog.getExecutorShardingParam();
this.executorFailRetryCount = xxlJobLog.getExecutorFailRetryCount();
this.triggerTime = DateTool.formatDateTime(xxlJobLog.getTriggerTime());
this.triggerCode = xxlJobLog.getTriggerCode();
this.triggerMsg = xxlJobLog.getTriggerMsg();
this.handleTime = DateTool.formatDateTime(xxlJobLog.getHandleTime());
this.handleCode = xxlJobLog.getHandleCode();
this.handleMsg = xxlJobLog.getHandleMsg();
this.alarmStatus = xxlJobLog.getAlarmStatus();
}
public long getId() {
return id;
}
public void setId(long id) {
this.id = id;
}
public int getJobGroup() {
return jobGroup;
}
public void setJobGroup(int jobGroup) {
this.jobGroup = jobGroup;
}
public int getJobId() {
return jobId;
}
public void setJobId(int jobId) {
this.jobId = jobId;
}
public String getExecutorAddress() {
return executorAddress;
}
public void setExecutorAddress(String executorAddress) {
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 String getExecutorShardingParam() {
return executorShardingParam;
}
public void setExecutorShardingParam(String executorShardingParam) {
this.executorShardingParam = executorShardingParam;
}
public int getExecutorFailRetryCount() {
return executorFailRetryCount;
}
public void setExecutorFailRetryCount(int executorFailRetryCount) {
this.executorFailRetryCount = executorFailRetryCount;
}
public String getTriggerTime() {
return triggerTime;
}
public void setTriggerTime(String triggerTime) {
this.triggerTime = triggerTime;
}
public int getTriggerCode() {
return triggerCode;
}
public void setTriggerCode(int triggerCode) {
this.triggerCode = triggerCode;
}
public String getTriggerMsg() {
return triggerMsg;
}
public void setTriggerMsg(String triggerMsg) {
this.triggerMsg = triggerMsg;
}
public String getHandleTime() {
return handleTime;
}
public void setHandleTime(String handleTime) {
this.handleTime = handleTime;
}
public int getHandleCode() {
return handleCode;
}
public void setHandleCode(int handleCode) {
this.handleCode = handleCode;
}
public String getHandleMsg() {
return handleMsg;
}
public void setHandleMsg(String handleMsg) {
this.handleMsg = handleMsg;
}
public int getAlarmStatus() {
return alarmStatus;
}
public void setAlarmStatus(int alarmStatus) {
this.alarmStatus = alarmStatus;
}
}
@@ -1,7 +1,7 @@
package com.xxl.job.admin.scheduler.alarm;
package com.xxl.job.admin.business.scheduler.alarm;
import com.xxl.job.admin.model.XxlJobInfo;
import com.xxl.job.admin.model.XxlJobLog;
import com.xxl.job.admin.business.model.XxlJobInfo;
import com.xxl.job.admin.business.model.XxlJobLog;
/**
* @author xuxueli 2020-01-19
@@ -1,7 +1,7 @@
package com.xxl.job.admin.scheduler.alarm;
package com.xxl.job.admin.business.scheduler.alarm;
import com.xxl.job.admin.model.XxlJobInfo;
import com.xxl.job.admin.model.XxlJobLog;
import com.xxl.job.admin.business.model.XxlJobInfo;
import com.xxl.job.admin.business.model.XxlJobLog;
import com.xxl.tool.core.CollectionTool;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -1,11 +1,11 @@
package com.xxl.job.admin.scheduler.alarm.impl;
package com.xxl.job.admin.business.scheduler.alarm.impl;
import com.xxl.job.admin.model.XxlJobGroup;
import com.xxl.job.admin.model.XxlJobInfo;
import com.xxl.job.admin.model.XxlJobLog;
import com.xxl.job.admin.scheduler.alarm.JobAlarm;
import com.xxl.job.admin.scheduler.config.XxlJobAdminBootstrap;
import com.xxl.job.admin.util.I18nUtil;
import com.xxl.job.admin.business.model.XxlJobGroup;
import com.xxl.job.admin.business.model.XxlJobInfo;
import com.xxl.job.admin.business.model.XxlJobLog;
import com.xxl.job.admin.business.scheduler.alarm.JobAlarm;
import com.xxl.job.admin.business.scheduler.config.XxlJobAdminBootstrap;
import com.xxl.job.admin.framework.util.I18nUtil;
import com.xxl.job.core.context.XxlJobContext;
import jakarta.mail.internet.MimeMessage;
import org.slf4j.Logger;
@@ -1,12 +1,12 @@
package com.xxl.job.admin.scheduler.complete;
package com.xxl.job.admin.business.scheduler.complete;
import com.xxl.job.admin.mapper.XxlJobInfoMapper;
import com.xxl.job.admin.mapper.XxlJobLogMapper;
import com.xxl.job.admin.model.XxlJobInfo;
import com.xxl.job.admin.model.XxlJobLog;
import com.xxl.job.admin.scheduler.config.XxlJobAdminBootstrap;
import com.xxl.job.admin.scheduler.trigger.TriggerTypeEnum;
import com.xxl.job.admin.util.I18nUtil;
import com.xxl.job.admin.business.mapper.XxlJobInfoMapper;
import com.xxl.job.admin.business.mapper.XxlJobLogMapper;
import com.xxl.job.admin.business.model.XxlJobInfo;
import com.xxl.job.admin.business.model.XxlJobLog;
import com.xxl.job.admin.business.scheduler.config.XxlJobAdminBootstrap;
import com.xxl.job.admin.business.scheduler.trigger.TriggerTypeEnum;
import com.xxl.job.admin.framework.util.I18nUtil;
import com.xxl.job.core.context.XxlJobContext;
import com.xxl.tool.core.StringTool;
import com.xxl.tool.response.Response;
@@ -1,10 +1,10 @@
package com.xxl.job.admin.scheduler.config;
package com.xxl.job.admin.business.scheduler.config;
import com.xxl.job.admin.mapper.*;
import com.xxl.job.admin.scheduler.alarm.JobAlarmer;
import com.xxl.job.admin.scheduler.complete.JobCompleter;
import com.xxl.job.admin.scheduler.thread.*;
import com.xxl.job.admin.scheduler.trigger.JobTrigger;
import com.xxl.job.admin.business.mapper.*;
import com.xxl.job.admin.business.scheduler.alarm.JobAlarmer;
import com.xxl.job.admin.business.scheduler.complete.JobCompleter;
import com.xxl.job.admin.business.scheduler.thread.*;
import com.xxl.job.admin.business.scheduler.trigger.JobTrigger;
import com.xxl.job.core.constant.Const;
import com.xxl.job.core.openapi.ExecutorBiz;
import com.xxl.tool.core.StringTool;
@@ -170,7 +170,7 @@ public class XxlJobAdminBootstrap implements InitializingBean, DisposableBean {
@Value("${xxl.job.i18n}")
private String i18n;
@Value("${xxl.job.accessToken:}")
@Value("${xxl.job.accessToken}")
private String accessToken;
@Value("${xxl.job.timeout}")
@@ -1,4 +1,4 @@
package com.xxl.job.admin.scheduler.cron;
package com.xxl.job.admin.business.scheduler.cron;
/*
* All content copyright Terracotta, Inc., unless otherwise indicated. All rights reserved.
* Copyright IBM Corp. 2024, 2025
@@ -1,4 +1,4 @@
package com.xxl.job.admin.scheduler.exception;
package com.xxl.job.admin.business.scheduler.exception;
/**
* @author xuxueli 2019-05-04 23:19:29
@@ -1,4 +1,4 @@
package com.xxl.job.admin.scheduler.misfire;
package com.xxl.job.admin.business.scheduler.misfire;
/**
* Misfire Handler
@@ -1,8 +1,8 @@
package com.xxl.job.admin.scheduler.misfire;
package com.xxl.job.admin.business.scheduler.misfire;
import com.xxl.job.admin.scheduler.misfire.strategy.MisfireDoNothing;
import com.xxl.job.admin.scheduler.misfire.strategy.MisfireFireOnceNow;
import com.xxl.job.admin.util.I18nUtil;
import com.xxl.job.admin.business.scheduler.misfire.strategy.MisfireDoNothing;
import com.xxl.job.admin.business.scheduler.misfire.strategy.MisfireFireOnceNow;
import com.xxl.job.admin.framework.util.I18nUtil;
/**
* @author xuxueli 2020-10-29 21:11:23
@@ -1,6 +1,6 @@
package com.xxl.job.admin.scheduler.misfire.strategy;
package com.xxl.job.admin.business.scheduler.misfire.strategy;
import com.xxl.job.admin.scheduler.misfire.MisfireHandler;
import com.xxl.job.admin.business.scheduler.misfire.MisfireHandler;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -1,8 +1,8 @@
package com.xxl.job.admin.scheduler.misfire.strategy;
package com.xxl.job.admin.business.scheduler.misfire.strategy;
import com.xxl.job.admin.scheduler.config.XxlJobAdminBootstrap;
import com.xxl.job.admin.scheduler.misfire.MisfireHandler;
import com.xxl.job.admin.scheduler.trigger.TriggerTypeEnum;
import com.xxl.job.admin.business.scheduler.config.XxlJobAdminBootstrap;
import com.xxl.job.admin.business.scheduler.misfire.MisfireHandler;
import com.xxl.job.admin.business.scheduler.trigger.TriggerTypeEnum;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -1,6 +1,6 @@
package com.xxl.job.admin.scheduler.openapi;
package com.xxl.job.admin.business.scheduler.openapi;
import com.xxl.job.admin.scheduler.config.XxlJobAdminBootstrap;
import com.xxl.job.admin.business.scheduler.config.XxlJobAdminBootstrap;
import com.xxl.job.core.constant.Const;
import com.xxl.job.core.openapi.AdminBiz;
import com.xxl.job.core.openapi.model.CallbackRequest;
@@ -1,7 +1,7 @@
package com.xxl.job.admin.scheduler.route;
package com.xxl.job.admin.business.scheduler.route;
import com.xxl.job.admin.scheduler.route.strategy.*;
import com.xxl.job.admin.util.I18nUtil;
import com.xxl.job.admin.business.scheduler.route.strategy.*;
import com.xxl.job.admin.framework.util.I18nUtil;
/**
* Created by xuxueli on 17/3/10.
@@ -1,4 +1,4 @@
package com.xxl.job.admin.scheduler.route;
package com.xxl.job.admin.business.scheduler.route;
import com.xxl.job.core.openapi.model.TriggerRequest;
import com.xxl.tool.response.Response;
@@ -1,8 +1,8 @@
package com.xxl.job.admin.scheduler.route.strategy;
package com.xxl.job.admin.business.scheduler.route.strategy;
import com.xxl.job.admin.scheduler.config.XxlJobAdminBootstrap;
import com.xxl.job.admin.scheduler.route.ExecutorRouter;
import com.xxl.job.admin.util.I18nUtil;
import com.xxl.job.admin.business.scheduler.config.XxlJobAdminBootstrap;
import com.xxl.job.admin.business.scheduler.route.ExecutorRouter;
import com.xxl.job.admin.framework.util.I18nUtil;
import com.xxl.job.core.openapi.ExecutorBiz;
import com.xxl.job.core.openapi.model.IdleBeatRequest;
import com.xxl.job.core.openapi.model.TriggerRequest;
@@ -1,6 +1,6 @@
package com.xxl.job.admin.scheduler.route.strategy;
package com.xxl.job.admin.business.scheduler.route.strategy;
import com.xxl.job.admin.scheduler.route.ExecutorRouter;
import com.xxl.job.admin.business.scheduler.route.ExecutorRouter;
import com.xxl.job.core.openapi.model.TriggerRequest;
import com.xxl.tool.response.Response;
@@ -1,8 +1,8 @@
package com.xxl.job.admin.scheduler.route.strategy;
package com.xxl.job.admin.business.scheduler.route.strategy;
import com.xxl.job.admin.scheduler.config.XxlJobAdminBootstrap;
import com.xxl.job.admin.scheduler.route.ExecutorRouter;
import com.xxl.job.admin.util.I18nUtil;
import com.xxl.job.admin.business.scheduler.config.XxlJobAdminBootstrap;
import com.xxl.job.admin.business.scheduler.route.ExecutorRouter;
import com.xxl.job.admin.framework.util.I18nUtil;
import com.xxl.job.core.openapi.ExecutorBiz;
import com.xxl.job.core.openapi.model.TriggerRequest;
import com.xxl.tool.response.Response;
@@ -1,6 +1,6 @@
package com.xxl.job.admin.scheduler.route.strategy;
package com.xxl.job.admin.business.scheduler.route.strategy;
import com.xxl.job.admin.scheduler.route.ExecutorRouter;
import com.xxl.job.admin.business.scheduler.route.ExecutorRouter;
import com.xxl.job.core.openapi.model.TriggerRequest;
import com.xxl.tool.response.Response;
@@ -1,6 +1,6 @@
package com.xxl.job.admin.scheduler.route.strategy;
package com.xxl.job.admin.business.scheduler.route.strategy;
import com.xxl.job.admin.scheduler.route.ExecutorRouter;
import com.xxl.job.admin.business.scheduler.route.ExecutorRouter;
import com.xxl.job.core.openapi.model.TriggerRequest;
import com.xxl.tool.response.Response;
@@ -1,6 +1,6 @@
package com.xxl.job.admin.scheduler.route.strategy;
package com.xxl.job.admin.business.scheduler.route.strategy;
import com.xxl.job.admin.scheduler.route.ExecutorRouter;
import com.xxl.job.admin.business.scheduler.route.ExecutorRouter;
import com.xxl.job.core.openapi.model.TriggerRequest;
import com.xxl.tool.response.Response;
@@ -1,6 +1,6 @@
package com.xxl.job.admin.scheduler.route.strategy;
package com.xxl.job.admin.business.scheduler.route.strategy;
import com.xxl.job.admin.scheduler.route.ExecutorRouter;
import com.xxl.job.admin.business.scheduler.route.ExecutorRouter;
import com.xxl.job.core.openapi.model.TriggerRequest;
import com.xxl.tool.response.Response;
@@ -1,6 +1,6 @@
package com.xxl.job.admin.scheduler.route.strategy;
package com.xxl.job.admin.business.scheduler.route.strategy;
import com.xxl.job.admin.scheduler.route.ExecutorRouter;
import com.xxl.job.admin.business.scheduler.route.ExecutorRouter;
import com.xxl.job.core.openapi.model.TriggerRequest;
import com.xxl.tool.response.Response;
@@ -1,6 +1,6 @@
package com.xxl.job.admin.scheduler.route.strategy;
package com.xxl.job.admin.business.scheduler.route.strategy;
import com.xxl.job.admin.scheduler.route.ExecutorRouter;
import com.xxl.job.admin.business.scheduler.route.ExecutorRouter;
import com.xxl.job.core.openapi.model.TriggerRequest;
import com.xxl.tool.response.Response;
@@ -1,10 +1,11 @@
package com.xxl.job.admin.scheduler.thread;
package com.xxl.job.admin.business.scheduler.thread;
import com.xxl.job.admin.model.XxlJobLog;
import com.xxl.job.admin.scheduler.config.XxlJobAdminBootstrap;
import com.xxl.job.admin.util.I18nUtil;
import com.xxl.job.admin.business.model.XxlJobLog;
import com.xxl.job.admin.business.scheduler.config.XxlJobAdminBootstrap;
import com.xxl.job.admin.framework.util.I18nUtil;
import com.xxl.job.core.context.XxlJobContext;
import com.xxl.job.core.openapi.model.CallbackRequest;
import com.xxl.tool.concurrent.CyclicThread;
import com.xxl.tool.core.DateTool;
import com.xxl.tool.response.Response;
import org.slf4j.Logger;
@@ -25,15 +26,14 @@ public class JobCompleteHelper {
// ---------------------- monitor ----------------------
private ThreadPoolExecutor callbackThreadPool = null;
private Thread monitorThread;
private volatile boolean toStop = false;
private CyclicThread jobMonitorThread;
/**
* start
*/
public void start() {
// for callback
// 1、callbackThreadPool
callbackThreadPool = new ThreadPoolExecutor(
2,
20,
@@ -55,88 +55,55 @@ public class JobCompleteHelper {
});
// for monitor
monitorThread = new Thread(new Runnable() {
// 2、jobMonitorThread
jobMonitorThread = new CyclicThread("JobCompleteHelper#jobMonitorThread", true, new Runnable() {
@Override
public void run() {
// 任务结果丢失处理:调度记录停留在 "运行中" 状态超过10min,且对应执行器心跳注册失败不在线,则将本地调度主动标记失败;
Date losedTime = DateTool.addMinutes(new Date(), -10);
List<Long> losedJobIds = XxlJobAdminBootstrap.getInstance().getXxlJobLogMapper().findLostJobIds(losedTime);
// wait for JobTriggerPoolHelper-init
try {
TimeUnit.MILLISECONDS.sleep(50);
} catch (Throwable e) {
if (!toStop) {
logger.error(e.getMessage(), e);
}
}
if (losedJobIds != null && losedJobIds.size() > 0) {
for (Long logId : losedJobIds) {
// monitor
while (!toStop) {
try {
// 任务结果丢失处理:调度记录停留在 "运行中" 状态超过10min,且对应执行器心跳注册失败不在线,则将本地调度主动标记失败;
Date losedTime = DateTool.addMinutes(new Date(), -10);
List<Long> losedJobIds = XxlJobAdminBootstrap.getInstance().getXxlJobLogMapper().findLostJobIds(losedTime);
XxlJobLog jobLog = new XxlJobLog();
jobLog.setId(logId);
if (losedJobIds != null && losedJobIds.size() > 0) {
for (Long logId : losedJobIds) {
jobLog.setHandleTime(new Date());
jobLog.setHandleCode(XxlJobContext.HANDLE_CODE_FAIL);
jobLog.setHandleMsg(I18nUtil.getString("joblog_lost_fail"));
XxlJobLog jobLog = new XxlJobLog();
jobLog.setId(logId);
jobLog.setHandleTime(new Date());
jobLog.setHandleCode(XxlJobContext.HANDLE_CODE_FAIL);
jobLog.setHandleMsg(I18nUtil.getString("joblog_lost_fail"));
XxlJobAdminBootstrap.getInstance().getJobCompleter().complete(jobLog);
}
}
} catch (Throwable e) {
if (!toStop) {
logger.error(">>>>>>>>>>> xxl-job, job fail monitor thread error:{}", e);
}
}
try {
TimeUnit.SECONDS.sleep(60);
} catch (Throwable e) {
if (!toStop) {
logger.error(e.getMessage(), e);
}
XxlJobAdminBootstrap.getInstance().getJobCompleter().complete(jobLog);
}
}
logger.info(">>>>>>>>>>> xxl-job, JobLosedMonitorHelper stop");
}
});
monitorThread.setDaemon(true);
monitorThread.setName("xxl-job, admin JobLosedMonitorHelper");
monitorThread.start();
}, 60 * 1000L, true);
jobMonitorThread.start();
}
/**
* stop
*/
public void stop() {
toStop = true;
// stop registryOrRemoveThreadPool
// 1、callbackThreadPool
callbackThreadPool.shutdownNow();
// stop monitorThread (interrupt and wait)
monitorThread.interrupt();
try {
monitorThread.join();
} catch (Throwable e) {
logger.error(e.getMessage(), e);
}
// 2、jobMonitorThread
jobMonitorThread.stop();
}
// ---------------------- helper ----------------------
/**
* callback
*
* @param callbackParamList callback param
* @return callback result
*/
public Response<String> callback(List<CallbackRequest> callbackParamList) {
callbackThreadPool.execute(new Runnable() {
@@ -0,0 +1,82 @@
package com.xxl.job.admin.business.scheduler.thread;
import com.xxl.job.admin.business.model.XxlJobInfo;
import com.xxl.job.admin.business.model.XxlJobLog;
import com.xxl.job.admin.business.scheduler.config.XxlJobAdminBootstrap;
import com.xxl.job.admin.business.scheduler.trigger.TriggerTypeEnum;
import com.xxl.job.admin.framework.util.I18nUtil;
import com.xxl.tool.concurrent.CyclicThread;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.List;
/**
* job fail-monitor helper
*
* @author xuxueli 2015-9-1 18:05:56
*/
public class JobFailAlarmMonitorHelper {
private static Logger logger = LoggerFactory.getLogger(JobFailAlarmMonitorHelper.class);
// ---------------------- monitor ----------------------
/**
* monitor thread
*/
private CyclicThread monitorThread;
/**
* start
*/
public void start() {
monitorThread = new CyclicThread("JobFailAlarmMonitorHelper#monitorThread", true, new Runnable() {
@Override
public void run() {
List<Long> failLogIds = XxlJobAdminBootstrap.getInstance().getXxlJobLogMapper().findFailJobLogIds(1000);
if (failLogIds != null && !failLogIds.isEmpty()) {
for (long failLogId : failLogIds) {
// lock log
int lockRet = XxlJobAdminBootstrap.getInstance().getXxlJobLogMapper().updateAlarmStatus(failLogId, 0, -1);
if (lockRet < 1) {
continue;
}
XxlJobLog log = XxlJobAdminBootstrap.getInstance().getXxlJobLogMapper().load(failLogId);
XxlJobInfo info = XxlJobAdminBootstrap.getInstance().getXxlJobInfoMapper().loadById(log.getJobId());
// 1、fail retry monitor
if (log.getExecutorFailRetryCount() > 0) {
XxlJobAdminBootstrap.getInstance().getJobTriggerPoolHelper().trigger(log.getJobId(), TriggerTypeEnum.RETRY, (log.getExecutorFailRetryCount() - 1), log.getExecutorShardingParam(), log.getExecutorParam(), null);
String retryMsg = "<br><br><span style=\"color:#00c0ef;\" > >>>>>>>>>>>" + I18nUtil.getString("jobconf_trigger_type_retry") + "<<<<<<<<<<< </span><br>";
log.setTriggerMsg(log.getTriggerMsg() + retryMsg);
XxlJobAdminBootstrap.getInstance().getXxlJobLogMapper().updateTriggerInfo(log);
}
// 2、fail alarm monitor
int newAlarmStatus = 0; // 告警状态:0-默认、-1=锁定状态、1-无需告警、2-告警成功、3-告警失败
if (info != null) {
boolean alarmResult = XxlJobAdminBootstrap.getInstance().getJobAlarmer().alarm(info, log);
newAlarmStatus = alarmResult ? 2 : 3;
} else {
newAlarmStatus = 1;
}
XxlJobAdminBootstrap.getInstance().getXxlJobLogMapper().updateAlarmStatus(failLogId, -1, newAlarmStatus);
}
}
}
}, 10 * 1000L, true);
monitorThread.start();
}
/**
* stop
*/
public void stop() {
monitorThread.stop();
}
}
@@ -0,0 +1,128 @@
package com.xxl.job.admin.business.scheduler.thread;
import com.xxl.job.admin.business.model.XxlJobLogReport;
import com.xxl.job.admin.business.scheduler.config.XxlJobAdminBootstrap;
import com.xxl.tool.concurrent.CyclicThread;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.Calendar;
import java.util.Date;
import java.util.List;
import java.util.Map;
import java.util.concurrent.atomic.AtomicLong;
/**
* job log report helper
*
* @author xuxueli 2019-11-22
*/
public class JobLogReportHelper {
private static final Logger logger = LoggerFactory.getLogger(JobLogReportHelper.class);
private CyclicThread logReportThread;
private AtomicLong lastCleanLogTime;
/**
* start
*/
public void start() {
/**
* last clean log time ( Thread-safe concurrent reading and writing )
*/
lastCleanLogTime = new AtomicLong(0);
// log report thread
logReportThread = new CyclicThread("JobLogReportHelper#logReportThread", true, new Runnable() {
@Override
public void run() {
// 1、log-report refresh: refresh log report in 3 days
for (int i = 0; i < 3; i++) {
// today
Calendar itemDay = Calendar.getInstance();
itemDay.add(Calendar.DAY_OF_MONTH, -i);
itemDay.set(Calendar.HOUR_OF_DAY, 0);
itemDay.set(Calendar.MINUTE, 0);
itemDay.set(Calendar.SECOND, 0);
itemDay.set(Calendar.MILLISECOND, 0);
Date todayFrom = itemDay.getTime();
itemDay.set(Calendar.HOUR_OF_DAY, 23);
itemDay.set(Calendar.MINUTE, 59);
itemDay.set(Calendar.SECOND, 59);
itemDay.set(Calendar.MILLISECOND, 999);
Date todayTo = itemDay.getTime();
// refresh log-report every minute
XxlJobLogReport xxlJobLogReport = new XxlJobLogReport();
xxlJobLogReport.setTriggerDay(todayFrom);
xxlJobLogReport.setRunningCount(0);
xxlJobLogReport.setSucCount(0);
xxlJobLogReport.setFailCount(0);
xxlJobLogReport.setUpdateTime(new Date());
// fill count-data
Map<String, Object> triggerCountMap = XxlJobAdminBootstrap.getInstance().getXxlJobLogMapper().findLogReport(todayFrom, todayTo);
if (triggerCountMap != null && !triggerCountMap.isEmpty()) {
int triggerDayCount = triggerCountMap.containsKey("triggerDayCount") ? Integer.parseInt(String.valueOf(triggerCountMap.get("triggerDayCount"))) : 0;
int triggerDayCountRunning = triggerCountMap.containsKey("triggerDayCountRunning") ? Integer.parseInt(String.valueOf(triggerCountMap.get("triggerDayCountRunning"))) : 0;
int triggerDayCountSuc = triggerCountMap.containsKey("triggerDayCountSuc") ? Integer.parseInt(String.valueOf(triggerCountMap.get("triggerDayCountSuc"))) : 0;
int triggerDayCountFail = triggerDayCount - triggerDayCountRunning - triggerDayCountSuc;
xxlJobLogReport.setRunningCount(triggerDayCountRunning);
xxlJobLogReport.setSucCount(triggerDayCountSuc);
xxlJobLogReport.setFailCount(triggerDayCountFail);
}
// do refresh:
XxlJobAdminBootstrap.getInstance().getXxlJobLogReportMapper().saveOrUpdate(xxlJobLogReport); // 0-fail; 1-save suc; 2-update suc;
/*if (ret < 1) {
XxlJobAdminBootstrap.getInstance().getXxlJobLogReportMapper().save(xxlJobLogReport);
}*/
}
// 2、log-clean: switch open & once each day
if (XxlJobAdminBootstrap.getInstance().getLogretentiondays() > 0
&& System.currentTimeMillis() - lastCleanLogTime.longValue() > 24 * 60 * 60 * 1000) {
// expire-time
Calendar expiredDay = Calendar.getInstance();
expiredDay.add(Calendar.DAY_OF_MONTH, -1 * XxlJobAdminBootstrap.getInstance().getLogretentiondays());
expiredDay.set(Calendar.HOUR_OF_DAY, 0);
expiredDay.set(Calendar.MINUTE, 0);
expiredDay.set(Calendar.SECOND, 0);
expiredDay.set(Calendar.MILLISECOND, 0);
Date clearBeforeTime = expiredDay.getTime();
// clean expired log
List<Long> logIds = null;
do {
logIds = XxlJobAdminBootstrap.getInstance().getXxlJobLogMapper().findClearLogIds(0, 0, clearBeforeTime, 0, 1000);
if (logIds != null && !logIds.isEmpty()) {
XxlJobAdminBootstrap.getInstance().getXxlJobLogMapper().clearLog(logIds);
}
} while (logIds != null && !logIds.isEmpty());
// update clean time
lastCleanLogTime.set(System.currentTimeMillis());
}
}
}, 60 * 1000L, true);
logReportThread.start();
}
/**
* stop
*/
public void stop() {
logReportThread.stop();
}
}
@@ -0,0 +1,204 @@
package com.xxl.job.admin.business.scheduler.thread;
import com.xxl.job.admin.business.model.XxlJobGroup;
import com.xxl.job.admin.business.model.XxlJobRegistry;
import com.xxl.job.admin.business.scheduler.config.XxlJobAdminBootstrap;
import com.xxl.job.core.constant.Const;
import com.xxl.job.core.constant.RegistTypeEnum;
import com.xxl.job.core.openapi.model.RegistryRequest;
import com.xxl.tool.concurrent.CyclicThread;
import com.xxl.tool.core.StringTool;
import com.xxl.tool.response.Response;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.*;
import java.util.concurrent.*;
/**
* job registry instance helper
*
* @author xuxueli 2016-10-02 19:10:24
*/
public class JobRegistryHelper {
private static final Logger logger = LoggerFactory.getLogger(JobRegistryHelper.class);
/**
* registry or remove thread pool
*/
private ThreadPoolExecutor registryOrRemoveThreadPool = null;
/**
* registry monitor thread
*/
private CyclicThread registryMonitorThread;
/**
* start
*/
public void start() {
// 1、for registry or remove
registryOrRemoveThreadPool = new ThreadPoolExecutor(
2,
10,
30L,
TimeUnit.SECONDS,
new LinkedBlockingQueue<Runnable>(2000),
new ThreadFactory() {
@Override
public Thread newThread(Runnable r) {
return new Thread(r, "xxl-job, admin JobRegistryMonitorHelper-registryOrRemoveThreadPool-" + r.hashCode());
}
},
new RejectedExecutionHandler() {
@Override
public void rejectedExecution(Runnable r, ThreadPoolExecutor executor) {
r.run();
logger.warn(">>>>>>>>>>> xxl-job, registry or remove too fast, match threadpool rejected handler(run now).");
}
});
// 2、for registry monitor
registryMonitorThread = new CyclicThread("JobRegistryHelper#registryMonitorThread", true, new Runnable() {
@Override
public void run() {
// auto registry group
List<XxlJobGroup> groupList = XxlJobAdminBootstrap.getInstance().getXxlJobGroupMapper().findByAddressType(0);
if (groupList != null && !groupList.isEmpty()) {
// remove dead address (admin/executor)
List<Integer> ids = XxlJobAdminBootstrap.getInstance().getXxlJobRegistryMapper().findDead(Const.DEAD_TIMEOUT, new Date());
if (ids != null && !ids.isEmpty()) {
XxlJobAdminBootstrap.getInstance().getXxlJobRegistryMapper().removeDead(ids);
}
// fresh online address (admin/executor)
HashMap<String, List<String>> appAddressMap = new HashMap<String, List<String>>();
List<XxlJobRegistry> list = XxlJobAdminBootstrap.getInstance().getXxlJobRegistryMapper().findAll(Const.DEAD_TIMEOUT, new Date());
if (list != null) {
for (XxlJobRegistry item : list) {
if (RegistTypeEnum.EXECUTOR.name().equals(item.getRegistryGroup())) {
String appname = item.getRegistryKey();
List<String> registryList = appAddressMap.get(appname);
if (registryList == null) {
registryList = new ArrayList<String>();
}
if (!registryList.contains(item.getRegistryValue())) {
registryList.add(item.getRegistryValue());
}
appAddressMap.put(appname, registryList);
}
}
}
// fresh group address
for (XxlJobGroup group : groupList) {
List<String> registryList = appAddressMap.get(group.getAppname());
String addressListStr = null;
if (registryList != null && !registryList.isEmpty()) {
Collections.sort(registryList);
StringBuilder addressListSB = new StringBuilder();
for (String item : registryList) {
addressListSB.append(item).append(",");
}
addressListStr = addressListSB.toString();
addressListStr = addressListStr.substring(0, addressListStr.length() - 1);
}
group.setAddressList(addressListStr);
group.setUpdateTime(new Date());
XxlJobAdminBootstrap.getInstance().getXxlJobGroupMapper().update(group);
}
}
}
}, Const.BEAT_TIMEOUT * 1000L, true);
registryMonitorThread.start();
}
/**
* stop
*/
public void stop() {
// 1、registryOrRemoveThreadPool
registryOrRemoveThreadPool.shutdownNow();
// 2、registryMonitorThread
registryMonitorThread.stop();
}
// ---------------------- tool ----------------------
/**
* registry
*/
public Response<String> registry(RegistryRequest registryParam) {
// valid
if (StringTool.isBlank(registryParam.getRegistryGroup())
|| StringTool.isBlank(registryParam.getRegistryKey())
|| StringTool.isBlank(registryParam.getRegistryValue())) {
return Response.ofFail("Illegal Argument.");
}
// async execute
registryOrRemoveThreadPool.execute(new Runnable() {
@Override
public void run() {
// 0-fail; 1-save suc; 2-update suc;
int ret = XxlJobAdminBootstrap.getInstance().getXxlJobRegistryMapper().registrySaveOrUpdate(registryParam.getRegistryGroup(), registryParam.getRegistryKey(), registryParam.getRegistryValue(), new Date());
if (ret == 1) {
// fresh (add)
freshGroupRegistryInfo(registryParam);
}
/*int ret = XxlJobAdminConfig.getAdminConfig().getXxlJobRegistryDao().registryUpdate(registryParam.getRegistryGroup(), registryParam.getRegistryKey(), registryParam.getRegistryValue(), new Date());
if (ret < 1) {
XxlJobAdminConfig.getAdminConfig().getXxlJobRegistryDao().registrySave(registryParam.getRegistryGroup(), registryParam.getRegistryKey(), registryParam.getRegistryValue(), new Date());
// fresh
freshGroupRegistryInfo(registryParam);
}*/
}
});
return Response.ofSuccess();
}
/**
* registry remove
*/
public Response<String> registryRemove(RegistryRequest registryParam) {
// valid
if (StringTool.isBlank(registryParam.getRegistryGroup())
|| StringTool.isBlank(registryParam.getRegistryKey())
|| StringTool.isBlank(registryParam.getRegistryValue())) {
return Response.ofFail("Illegal Argument.");
}
// async execute
registryOrRemoveThreadPool.execute(new Runnable() {
@Override
public void run() {
int ret = XxlJobAdminBootstrap.getInstance().getXxlJobRegistryMapper().registryDelete(registryParam.getRegistryGroup(), registryParam.getRegistryKey(), registryParam.getRegistryValue());
if (ret > 0) {
// fresh (delete)
freshGroupRegistryInfo(registryParam);
}
}
});
return Response.ofSuccess();
}
private void freshGroupRegistryInfo(RegistryRequest registryParam) {
// Under consideration, prevent affecting core tables
}
}
@@ -1,11 +1,11 @@
package com.xxl.job.admin.scheduler.thread;
package com.xxl.job.admin.business.scheduler.thread;
import com.xxl.job.admin.constant.TriggerStatus;
import com.xxl.job.admin.model.XxlJobInfo;
import com.xxl.job.admin.scheduler.config.XxlJobAdminBootstrap;
import com.xxl.job.admin.scheduler.misfire.MisfireStrategyEnum;
import com.xxl.job.admin.scheduler.trigger.TriggerTypeEnum;
import com.xxl.job.admin.scheduler.type.ScheduleTypeEnum;
import com.xxl.job.admin.business.constant.TriggerStatus;
import com.xxl.job.admin.business.model.XxlJobInfo;
import com.xxl.job.admin.business.scheduler.config.XxlJobAdminBootstrap;
import com.xxl.job.admin.business.scheduler.misfire.MisfireStrategyEnum;
import com.xxl.job.admin.business.scheduler.trigger.TriggerTypeEnum;
import com.xxl.job.admin.business.scheduler.type.ScheduleTypeEnum;
import com.xxl.tool.core.CollectionTool;
import com.xxl.tool.core.MapTool;
import org.slf4j.Logger;
@@ -44,7 +44,11 @@ public class JobScheduleHelper {
*/
public void start() {
// schedule thread
// init thread flag
scheduleThreadToStop = false;
ringThreadToStop = false;
// 1、schedule thread
scheduleThread = new Thread(new Runnable() {
@Override
public void run() {
@@ -191,8 +195,7 @@ public class JobScheduleHelper {
scheduleThread.setName("xxl-job, admin JobScheduleHelper#scheduleThread");
scheduleThread.start();
// ring thread
// 2、ring thread
ringThread = new Thread(new Runnable() {
@Override
public void run() {
@@ -348,7 +351,7 @@ public class JobScheduleHelper {
}
}
// stop ring (wait job-in-memory stop)
// 2、stop ring (wait job-in-memory stop)
ringThreadToStop = true;
try {
TimeUnit.SECONDS.sleep(1);
@@ -1,7 +1,7 @@
package com.xxl.job.admin.scheduler.thread;
package com.xxl.job.admin.business.scheduler.thread;
import com.xxl.job.admin.scheduler.config.XxlJobAdminBootstrap;
import com.xxl.job.admin.scheduler.trigger.TriggerTypeEnum;
import com.xxl.job.admin.business.scheduler.config.XxlJobAdminBootstrap;
import com.xxl.job.admin.business.scheduler.trigger.TriggerTypeEnum;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -1,14 +1,14 @@
package com.xxl.job.admin.scheduler.trigger;
package com.xxl.job.admin.business.scheduler.trigger;
import com.xxl.job.admin.mapper.XxlJobGroupMapper;
import com.xxl.job.admin.mapper.XxlJobInfoMapper;
import com.xxl.job.admin.mapper.XxlJobLogMapper;
import com.xxl.job.admin.model.XxlJobGroup;
import com.xxl.job.admin.model.XxlJobInfo;
import com.xxl.job.admin.model.XxlJobLog;
import com.xxl.job.admin.scheduler.config.XxlJobAdminBootstrap;
import com.xxl.job.admin.scheduler.route.ExecutorRouteStrategyEnum;
import com.xxl.job.admin.util.I18nUtil;
import com.xxl.job.admin.business.mapper.XxlJobGroupMapper;
import com.xxl.job.admin.business.mapper.XxlJobInfoMapper;
import com.xxl.job.admin.business.mapper.XxlJobLogMapper;
import com.xxl.job.admin.business.model.XxlJobGroup;
import com.xxl.job.admin.business.model.XxlJobInfo;
import com.xxl.job.admin.business.model.XxlJobLog;
import com.xxl.job.admin.business.scheduler.config.XxlJobAdminBootstrap;
import com.xxl.job.admin.business.scheduler.route.ExecutorRouteStrategyEnum;
import com.xxl.job.admin.framework.util.I18nUtil;
import com.xxl.job.core.constant.ExecutorBlockStrategyEnum;
import com.xxl.job.core.context.XxlJobContext;
import com.xxl.job.core.openapi.ExecutorBiz;
@@ -1,6 +1,6 @@
package com.xxl.job.admin.scheduler.trigger;
package com.xxl.job.admin.business.scheduler.trigger;
import com.xxl.job.admin.util.I18nUtil;
import com.xxl.job.admin.framework.util.I18nUtil;
/**
* trigger type enum
@@ -1,6 +1,6 @@
package com.xxl.job.admin.scheduler.type;
package com.xxl.job.admin.business.scheduler.type;
import com.xxl.job.admin.model.XxlJobInfo;
import com.xxl.job.admin.business.model.XxlJobInfo;
import java.util.Date;
@@ -1,9 +1,9 @@
package com.xxl.job.admin.scheduler.type;
package com.xxl.job.admin.business.scheduler.type;
import com.xxl.job.admin.scheduler.type.strategy.CronScheduleType;
import com.xxl.job.admin.scheduler.type.strategy.FixRateScheduleType;
import com.xxl.job.admin.scheduler.type.strategy.NoneScheduleType;
import com.xxl.job.admin.util.I18nUtil;
import com.xxl.job.admin.business.scheduler.type.strategy.CronScheduleType;
import com.xxl.job.admin.business.scheduler.type.strategy.FixRateScheduleType;
import com.xxl.job.admin.business.scheduler.type.strategy.NoneScheduleType;
import com.xxl.job.admin.framework.util.I18nUtil;
/**
* @author xuxueli 2020-10-29 21:11:23
@@ -1,8 +1,8 @@
package com.xxl.job.admin.scheduler.type.strategy;
package com.xxl.job.admin.business.scheduler.type.strategy;
import com.xxl.job.admin.model.XxlJobInfo;
import com.xxl.job.admin.scheduler.cron.CronExpression;
import com.xxl.job.admin.scheduler.type.ScheduleType;
import com.xxl.job.admin.business.model.XxlJobInfo;
import com.xxl.job.admin.business.scheduler.cron.CronExpression;
import com.xxl.job.admin.business.scheduler.type.ScheduleType;
import java.util.Date;
@@ -1,7 +1,7 @@
package com.xxl.job.admin.scheduler.type.strategy;
package com.xxl.job.admin.business.scheduler.type.strategy;
import com.xxl.job.admin.model.XxlJobInfo;
import com.xxl.job.admin.scheduler.type.ScheduleType;
import com.xxl.job.admin.business.model.XxlJobInfo;
import com.xxl.job.admin.business.scheduler.type.ScheduleType;
import com.xxl.tool.core.DateTool;
import java.util.Date;
@@ -1,7 +1,7 @@
package com.xxl.job.admin.scheduler.type.strategy;
package com.xxl.job.admin.business.scheduler.type.strategy;
import com.xxl.job.admin.model.XxlJobInfo;
import com.xxl.job.admin.scheduler.type.ScheduleType;
import com.xxl.job.admin.business.model.XxlJobInfo;
import com.xxl.job.admin.business.scheduler.type.ScheduleType;
import java.util.Date;
@@ -0,0 +1,63 @@
package com.xxl.job.admin.business.service;
import com.xxl.job.admin.business.model.XxlJobInfo;
import com.xxl.sso.core.model.LoginInfo;
import com.xxl.tool.response.PageModel;
import com.xxl.tool.response.Response;
import java.util.Date;
import java.util.Map;
/**
* core job action for xxl-job
*
* @author xuxueli 2016-5-28 15:30:33
*/
public interface XxlJobService {
/**
* page list
*/
public Response<PageModel<XxlJobInfo>> pageList(int offset, int pagesize, int jobGroup, int triggerStatus, String jobDesc, String executorHandler, String author);
/**
* add job
*/
public Response<String> add(XxlJobInfo jobInfo, LoginInfo loginInfo);
/**
* update job
*/
public Response<String> update(XxlJobInfo jobInfo, LoginInfo loginInfo);
/**
* remove job
*/
public Response<String> remove(int id, LoginInfo loginInfo);
/**
* start job
*/
public Response<String> start(int id, LoginInfo loginInfo);
/**
* stop job
*/
public Response<String> stop(int id, LoginInfo loginInfo);
/**
* trigger
*/
public Response<String> trigger(LoginInfo loginInfo, int jobId, String executorParam, String addressList);
/**
* dashboard info
*/
public Map<String, Object> dashboardInfo();
/**
* chart info
*/
public Response<Map<String, Object>> chartInfo(Date startDate, Date endDate);
}
@@ -1,6 +1,6 @@
package com.xxl.job.admin.service.impl;
package com.xxl.job.admin.business.service.impl;
import com.xxl.job.admin.scheduler.config.XxlJobAdminBootstrap;
import com.xxl.job.admin.business.scheduler.config.XxlJobAdminBootstrap;
import com.xxl.job.core.openapi.AdminBiz;
import com.xxl.job.core.openapi.model.CallbackRequest;
import com.xxl.job.core.openapi.model.RegistryRequest;
@@ -0,0 +1,532 @@
package com.xxl.job.admin.business.service.impl;
import com.xxl.job.admin.business.constant.TriggerStatus;
import com.xxl.job.admin.business.mapper.*;
import com.xxl.job.admin.business.model.XxlJobGroup;
import com.xxl.job.admin.business.model.XxlJobInfo;
import com.xxl.job.admin.business.model.XxlJobLogReport;
import com.xxl.job.admin.business.scheduler.config.XxlJobAdminBootstrap;
import com.xxl.job.admin.business.scheduler.cron.CronExpression;
import com.xxl.job.admin.business.scheduler.misfire.MisfireStrategyEnum;
import com.xxl.job.admin.business.scheduler.route.ExecutorRouteStrategyEnum;
import com.xxl.job.admin.business.scheduler.thread.JobScheduleHelper;
import com.xxl.job.admin.business.scheduler.trigger.TriggerTypeEnum;
import com.xxl.job.admin.business.scheduler.type.ScheduleTypeEnum;
import com.xxl.job.admin.business.service.XxlJobService;
import com.xxl.job.admin.framework.util.I18nUtil;
import com.xxl.job.admin.framework.util.JobGroupPermissionUtil;
import com.xxl.job.core.constant.ExecutorBlockStrategyEnum;
import com.xxl.job.core.glue.GlueTypeEnum;
import com.xxl.sso.core.model.LoginInfo;
import com.xxl.tool.core.DateTool;
import com.xxl.tool.core.StringTool;
import com.xxl.tool.json.GsonTool;
import com.xxl.tool.response.PageModel;
import com.xxl.tool.response.Response;
import jakarta.annotation.Resource;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Service;
import java.text.MessageFormat;
import java.util.*;
/**
* core job action for xxl-job
*
* @author xuxueli 2016-5-28 15:30:33
*/
@Service
public class XxlJobServiceImpl implements XxlJobService {
private static Logger logger = LoggerFactory.getLogger(XxlJobServiceImpl.class);
@Resource
private XxlJobGroupMapper xxlJobGroupMapper;
@Resource
private XxlJobInfoMapper xxlJobInfoMapper;
@Resource
public XxlJobLogMapper xxlJobLogMapper;
@Resource
private XxlJobLogGlueMapper xxlJobLogGlueMapper;
@Resource
private XxlJobLogReportMapper xxlJobLogReportMapper;
@Override
public Response<PageModel<XxlJobInfo>> pageList(int offset, int pagesize, int jobGroup, int triggerStatus, String jobDesc, String executorHandler, String author) {
// page list
List<XxlJobInfo> list = xxlJobInfoMapper.pageList(offset, pagesize, jobGroup, triggerStatus, jobDesc, executorHandler, author);
int list_count = xxlJobInfoMapper.pageListCount(offset, pagesize, jobGroup, triggerStatus, jobDesc, executorHandler, author);
// package result
PageModel<XxlJobInfo> pageModel = new PageModel<>();
pageModel.setData(list);
pageModel.setTotal(list_count);
return Response.ofSuccess(pageModel);
}
@Override
public Response<String> add(XxlJobInfo jobInfo, LoginInfo loginInfo) {
// valid base
XxlJobGroup group = xxlJobGroupMapper.load(jobInfo.getJobGroup());
if (group == null) {
return Response.ofFail(I18nUtil.getString("system_please_choose") + I18nUtil.getString("jobinfo_field_jobgroup"));
}
if (StringTool.isBlank(jobInfo.getJobDesc())) {
return Response.ofFail((I18nUtil.getString("system_please_input") + I18nUtil.getString("jobinfo_field_jobdesc")));
}
if (StringTool.isBlank(jobInfo.getAuthor())) {
return Response.ofFail((I18nUtil.getString("system_please_input") + I18nUtil.getString("jobinfo_field_author")));
}
// valid trigger
ScheduleTypeEnum scheduleTypeEnum = ScheduleTypeEnum.match(jobInfo.getScheduleType(), null);
if (scheduleTypeEnum == null) {
return Response.ofFail((I18nUtil.getString("schedule_type") + I18nUtil.getString("system_invalid")));
}
if (scheduleTypeEnum == ScheduleTypeEnum.CRON) {
if (jobInfo.getScheduleConf() == null || !CronExpression.isValidExpression(jobInfo.getScheduleConf())) {
return Response.ofFail("Cron" + I18nUtil.getString("system_invalid"));
}
} else if (scheduleTypeEnum == ScheduleTypeEnum.FIX_RATE/* || scheduleTypeEnum == ScheduleTypeEnum.FIX_DELAY*/) {
if (jobInfo.getScheduleConf() == null) {
return Response.ofFail((I18nUtil.getString("schedule_type")));
}
try {
int fixSecond = Integer.parseInt(jobInfo.getScheduleConf());
if (fixSecond < 1) {
return Response.ofFail((I18nUtil.getString("schedule_type") + I18nUtil.getString("system_invalid")));
}
} catch (Exception e) {
return Response.ofFail((I18nUtil.getString("schedule_type") + I18nUtil.getString("system_invalid")));
}
}
// valid job
if (GlueTypeEnum.match(jobInfo.getGlueType()) == null) {
return Response.ofFail((I18nUtil.getString("jobinfo_field_gluetype") + I18nUtil.getString("system_invalid")));
}
if (GlueTypeEnum.BEAN == GlueTypeEnum.match(jobInfo.getGlueType()) && StringTool.isBlank(jobInfo.getExecutorHandler())) {
return Response.ofFail((I18nUtil.getString("system_please_input") + "JobHandler"));
}
// 》fix "\r" in shell
if (GlueTypeEnum.GLUE_SHELL == GlueTypeEnum.match(jobInfo.getGlueType()) && jobInfo.getGlueSource() != null) {
jobInfo.setGlueSource(jobInfo.getGlueSource().replaceAll("\r", ""));
}
// valid advanced
if (ExecutorRouteStrategyEnum.match(jobInfo.getExecutorRouteStrategy(), null) == null) {
return Response.ofFail((I18nUtil.getString("jobinfo_field_executorRouteStrategy") + I18nUtil.getString("system_invalid")));
}
if (MisfireStrategyEnum.match(jobInfo.getMisfireStrategy(), null) == null) {
return Response.ofFail((I18nUtil.getString("misfire_strategy") + I18nUtil.getString("system_invalid")));
}
if (ExecutorBlockStrategyEnum.match(jobInfo.getExecutorBlockStrategy(), null) == null) {
return Response.ofFail((I18nUtil.getString("jobinfo_field_executorBlockStrategy") + I18nUtil.getString("system_invalid")));
}
// 》ChildJobId valid
if (StringTool.isNotBlank(jobInfo.getChildJobId())) {
String[] childJobIds = jobInfo.getChildJobId().split(",");
for (String childJobIdItem : childJobIds) {
if (StringTool.isNotBlank(childJobIdItem) && StringTool.isNumeric(childJobIdItem)) {
XxlJobInfo childJobInfo = xxlJobInfoMapper.loadById(Integer.parseInt(childJobIdItem));
if (childJobInfo == null) {
return Response.ofFail(
MessageFormat.format((I18nUtil.getString("jobinfo_field_childJobId") + "({0})" + I18nUtil.getString("system_not_found")), childJobIdItem));
}
// valid jobGroup permission
if (!JobGroupPermissionUtil.hasJobGroupPermission(loginInfo, childJobInfo.getJobGroup())) {
return Response.ofFail(
MessageFormat.format((I18nUtil.getString("jobinfo_field_childJobId") + "({0})" + I18nUtil.getString("system_permission_limit")), childJobIdItem));
}
} else {
return Response.ofFail(
MessageFormat.format((I18nUtil.getString("jobinfo_field_childJobId") + "({0})" + I18nUtil.getString("system_invalid")), childJobIdItem));
}
}
// join , avoid "xxx,,"
String temp = "";
for (String item : childJobIds) {
temp += item + ",";
}
temp = temp.substring(0, temp.length() - 1);
jobInfo.setChildJobId(temp);
}
// add in db
jobInfo.setAddTime(new Date());
jobInfo.setUpdateTime(new Date());
jobInfo.setGlueUpdatetime(new Date());
// remove the whitespace
jobInfo.setExecutorHandler(jobInfo.getExecutorHandler().trim());
xxlJobInfoMapper.save(jobInfo);
if (jobInfo.getId() < 1) {
return Response.ofFail((I18nUtil.getString("jobinfo_field_add") + I18nUtil.getString("system_fail")));
}
// write operation log
logger.info(">>>>>>>>>>> xxl-job operation log: operator = {}, type = {}, content = {}",
loginInfo.getUserName(), "jobinfo-save", GsonTool.toJson(jobInfo));
return Response.ofSuccess(String.valueOf(jobInfo.getId()));
}
@Override
public Response<String> update(XxlJobInfo jobInfo, LoginInfo loginInfo) {
// valid base
if (StringTool.isBlank(jobInfo.getJobDesc())) {
return Response.ofFail((I18nUtil.getString("system_please_input") + I18nUtil.getString("jobinfo_field_jobdesc")));
}
if (StringTool.isBlank(jobInfo.getAuthor())) {
return Response.ofFail((I18nUtil.getString("system_please_input") + I18nUtil.getString("jobinfo_field_author")));
}
// valid trigger
ScheduleTypeEnum scheduleTypeEnum = ScheduleTypeEnum.match(jobInfo.getScheduleType(), null);
if (scheduleTypeEnum == null) {
return Response.ofFail((I18nUtil.getString("schedule_type") + I18nUtil.getString("system_invalid")));
}
if (scheduleTypeEnum == ScheduleTypeEnum.CRON) {
if (jobInfo.getScheduleConf() == null || !CronExpression.isValidExpression(jobInfo.getScheduleConf())) {
return Response.ofFail("Cron" + I18nUtil.getString("system_invalid"));
}
} else if (scheduleTypeEnum == ScheduleTypeEnum.FIX_RATE /*|| scheduleTypeEnum == ScheduleTypeEnum.FIX_DELAY*/) {
if (jobInfo.getScheduleConf() == null) {
return Response.ofFail((I18nUtil.getString("schedule_type") + I18nUtil.getString("system_invalid")));
}
try {
int fixSecond = Integer.parseInt(jobInfo.getScheduleConf());
if (fixSecond < 1) {
return Response.ofFail((I18nUtil.getString("schedule_type") + I18nUtil.getString("system_invalid")));
}
} catch (Exception e) {
return Response.ofFail((I18nUtil.getString("schedule_type") + I18nUtil.getString("system_invalid")));
}
}
// valid advanced
if (ExecutorRouteStrategyEnum.match(jobInfo.getExecutorRouteStrategy(), null) == null) {
return Response.ofFail((I18nUtil.getString("jobinfo_field_executorRouteStrategy") + I18nUtil.getString("system_invalid")));
}
if (MisfireStrategyEnum.match(jobInfo.getMisfireStrategy(), null) == null) {
return Response.ofFail((I18nUtil.getString("misfire_strategy") + I18nUtil.getString("system_invalid")));
}
if (ExecutorBlockStrategyEnum.match(jobInfo.getExecutorBlockStrategy(), null) == null) {
return Response.ofFail((I18nUtil.getString("jobinfo_field_executorBlockStrategy") + I18nUtil.getString("system_invalid")));
}
// 》ChildJobId valid
if (StringTool.isNotBlank(jobInfo.getChildJobId())) {
String[] childJobIds = jobInfo.getChildJobId().split(",");
for (String childJobIdItem : childJobIds) {
if (StringTool.isNotBlank(childJobIdItem) && StringTool.isNumeric(childJobIdItem)) {
// parse child
int childJobId = Integer.parseInt(childJobIdItem);
if (childJobId == jobInfo.getId()) {
return Response.ofFail((I18nUtil.getString("jobinfo_field_childJobId") + "(" + childJobId + ")" + I18nUtil.getString("system_invalid")));
}
// valid child
XxlJobInfo childJobInfo = xxlJobInfoMapper.loadById(childJobId);
if (childJobInfo == null) {
return Response.ofFail(
MessageFormat.format((I18nUtil.getString("jobinfo_field_childJobId") + "({0})" + I18nUtil.getString("system_not_found")), childJobIdItem));
}
// valid jobGroup permission
if (!JobGroupPermissionUtil.hasJobGroupPermission(loginInfo, childJobInfo.getJobGroup())) {
return Response.ofFail(
MessageFormat.format((I18nUtil.getString("jobinfo_field_childJobId") + "({0})" + I18nUtil.getString("system_permission_limit")), childJobIdItem));
}
} else {
return Response.ofFail(
MessageFormat.format((I18nUtil.getString("jobinfo_field_childJobId") + "({0})" + I18nUtil.getString("system_invalid")), childJobIdItem));
}
}
// join , avoid "xxx,,"
String temp = "";
for (String item : childJobIds) {
temp += item + ",";
}
temp = temp.substring(0, temp.length() - 1);
jobInfo.setChildJobId(temp);
}
// group valid
XxlJobGroup jobGroup = xxlJobGroupMapper.load(jobInfo.getJobGroup());
if (jobGroup == null) {
return Response.ofFail((I18nUtil.getString("jobinfo_field_jobgroup") + I18nUtil.getString("system_invalid")));
}
// stage job info
XxlJobInfo exists_jobInfo = xxlJobInfoMapper.loadById(jobInfo.getId());
if (exists_jobInfo == null) {
return Response.ofFail((I18nUtil.getString("jobinfo_field_id") + I18nUtil.getString("system_not_found")));
}
// next trigger time (5s后生效,避开预读周期)
long nextTriggerTime = exists_jobInfo.getTriggerNextTime();
boolean scheduleDataNotChanged = jobInfo.getScheduleType().equals(exists_jobInfo.getScheduleType())
&& jobInfo.getScheduleConf().equals(exists_jobInfo.getScheduleConf()); // 触发配置如果不变,避免重复计算;
if (exists_jobInfo.getTriggerStatus() == TriggerStatus.RUNNING.getValue() && !scheduleDataNotChanged) {
try {
// generate next trigger time
Date nextValidTime = scheduleTypeEnum.getScheduleType().generateNextTriggerTime(jobInfo, new Date(System.currentTimeMillis() + JobScheduleHelper.PRE_READ_MS));
if (nextValidTime == null) {
return Response.ofFail((I18nUtil.getString("schedule_type") + I18nUtil.getString("system_invalid")));
}
nextTriggerTime = nextValidTime.getTime();
} catch (Exception e) {
logger.error(e.getMessage(), e);
return Response.ofFail((I18nUtil.getString("schedule_type") + I18nUtil.getString("system_invalid")));
}
}
exists_jobInfo.setJobGroup(jobInfo.getJobGroup());
exists_jobInfo.setJobDesc(jobInfo.getJobDesc());
exists_jobInfo.setAuthor(jobInfo.getAuthor());
exists_jobInfo.setAlarmEmail(jobInfo.getAlarmEmail());
exists_jobInfo.setScheduleType(jobInfo.getScheduleType());
exists_jobInfo.setScheduleConf(jobInfo.getScheduleConf());
exists_jobInfo.setMisfireStrategy(jobInfo.getMisfireStrategy());
exists_jobInfo.setExecutorRouteStrategy(jobInfo.getExecutorRouteStrategy());
// remove the whitespace
exists_jobInfo.setExecutorHandler(jobInfo.getExecutorHandler().trim());
exists_jobInfo.setExecutorParam(jobInfo.getExecutorParam());
exists_jobInfo.setExecutorBlockStrategy(jobInfo.getExecutorBlockStrategy());
exists_jobInfo.setExecutorTimeout(jobInfo.getExecutorTimeout());
exists_jobInfo.setExecutorFailRetryCount(jobInfo.getExecutorFailRetryCount());
exists_jobInfo.setChildJobId(jobInfo.getChildJobId());
exists_jobInfo.setTriggerNextTime(nextTriggerTime);
exists_jobInfo.setUpdateTime(new Date());
xxlJobInfoMapper.update(exists_jobInfo);
// write operation log
logger.info(">>>>>>>>>>> xxl-job operation log: operator = {}, type = {}, content = {}",
loginInfo.getUserName(), "jobinfo-update", GsonTool.toJson(exists_jobInfo));
return Response.ofSuccess();
}
@Override
public Response<String> remove(int id, LoginInfo loginInfo) {
// valid job
XxlJobInfo xxlJobInfo = xxlJobInfoMapper.loadById(id);
if (xxlJobInfo == null) {
return Response.ofSuccess();
}
// valid jobGroup permission
if (!JobGroupPermissionUtil.hasJobGroupPermission(loginInfo, xxlJobInfo.getJobGroup())) {
return Response.ofFail(I18nUtil.getString("system_permission_limit"));
}
xxlJobInfoMapper.delete(id);
xxlJobLogMapper.delete(id);
xxlJobLogGlueMapper.deleteByJobId(id);
// write operation log
logger.info(">>>>>>>>>>> xxl-job operation log: operator = {}, type = {}, content = {}",
loginInfo.getUserName(), "jobinfo-remove", id);
return Response.ofSuccess();
}
@Override
public Response<String> start(int id, LoginInfo loginInfo) {
// load and valid
XxlJobInfo xxlJobInfo = xxlJobInfoMapper.loadById(id);
if (xxlJobInfo == null) {
return Response.ofFail(I18nUtil.getString("jobinfo_glue_jobid_invalid"));
}
// valid jobGroup permission
if (!JobGroupPermissionUtil.hasJobGroupPermission(loginInfo, xxlJobInfo.getJobGroup())) {
return Response.ofFail(I18nUtil.getString("system_permission_limit"));
}
// valid ScheduleType: can not be none
ScheduleTypeEnum scheduleTypeEnum = ScheduleTypeEnum.match(xxlJobInfo.getScheduleType(), ScheduleTypeEnum.NONE);
if (ScheduleTypeEnum.NONE == scheduleTypeEnum) {
return Response.ofFail(I18nUtil.getString("schedule_type_none_limit_start"));
}
// next trigger time (5s后生效,避开预读周期)
long nextTriggerTime = 0;
try {
// generate next trigger time
Date nextValidTime = scheduleTypeEnum.getScheduleType().generateNextTriggerTime(xxlJobInfo, new Date(System.currentTimeMillis() + JobScheduleHelper.PRE_READ_MS));
if (nextValidTime == null) {
return Response.ofFail((I18nUtil.getString("schedule_type") + I18nUtil.getString("system_invalid")));
}
nextTriggerTime = nextValidTime.getTime();
} catch (Exception e) {
logger.error(e.getMessage(), e);
return Response.ofFail((I18nUtil.getString("schedule_type") + I18nUtil.getString("system_invalid")));
}
xxlJobInfo.setTriggerStatus(TriggerStatus.RUNNING.getValue());
xxlJobInfo.setTriggerLastTime(0);
xxlJobInfo.setTriggerNextTime(nextTriggerTime);
xxlJobInfo.setUpdateTime(new Date());
xxlJobInfoMapper.update(xxlJobInfo);
// write operation log
logger.info(">>>>>>>>>>> xxl-job operation log: operator = {}, type = {}, content = {}",
loginInfo.getUserName(), "jobinfo-start", id);
return Response.ofSuccess();
}
@Override
public Response<String> stop(int id, LoginInfo loginInfo) {
// load and valid
XxlJobInfo xxlJobInfo = xxlJobInfoMapper.loadById(id);
if (xxlJobInfo == null) {
return Response.ofFail(I18nUtil.getString("jobinfo_glue_jobid_invalid"));
}
// valid jobGroup permission
if (!JobGroupPermissionUtil.hasJobGroupPermission(loginInfo, xxlJobInfo.getJobGroup())) {
return Response.ofFail(I18nUtil.getString("system_permission_limit"));
}
// stop
xxlJobInfo.setTriggerStatus(TriggerStatus.STOPPED.getValue());
xxlJobInfo.setTriggerLastTime(0);
xxlJobInfo.setTriggerNextTime(0);
xxlJobInfo.setUpdateTime(new Date());
xxlJobInfoMapper.update(xxlJobInfo);
// write operation log
logger.info(">>>>>>>>>>> xxl-job operation log: operator = {}, type = {}, content = {}",
loginInfo.getUserName(), "jobinfo-stop", id);
return Response.ofSuccess();
}
@Override
public Response<String> trigger(LoginInfo loginInfo, int jobId, String executorParam, String addressList) {
// valid job
XxlJobInfo xxlJobInfo = xxlJobInfoMapper.loadById(jobId);
if (xxlJobInfo == null) {
return Response.ofFail(I18nUtil.getString("jobinfo_glue_jobid_invalid"));
}
// valid jobGroup permission
if (!JobGroupPermissionUtil.hasJobGroupPermission(loginInfo, xxlJobInfo.getJobGroup())) {
return Response.ofFail(I18nUtil.getString("system_permission_limit"));
}
// force cover job param
if (executorParam == null) {
executorParam = "";
}
XxlJobAdminBootstrap.getInstance().getJobTriggerPoolHelper().trigger(jobId, TriggerTypeEnum.MANUAL, -1, null, executorParam, addressList);
// write operation log
logger.info(">>>>>>>>>>> xxl-job operation log: operator = {}, type = {}, content = {}",
loginInfo.getUserName(), "jobinfo-trigger", jobId);
return Response.ofSuccess();
}
@Override
public Map<String, Object> dashboardInfo() {
int jobInfoCount = xxlJobInfoMapper.findAllCount();
int jobLogCount = 0;
int jobLogSuccessCount = 0;
XxlJobLogReport xxlJobLogReport = xxlJobLogReportMapper.queryLogReportTotal();
if (xxlJobLogReport != null) {
jobLogCount = xxlJobLogReport.getRunningCount() + xxlJobLogReport.getSucCount() + xxlJobLogReport.getFailCount();
jobLogSuccessCount = xxlJobLogReport.getSucCount();
}
// executor count
Set<String> executorAddressSet = new HashSet<String>();
List<XxlJobGroup> groupList = xxlJobGroupMapper.findAll();
if (groupList != null && !groupList.isEmpty()) {
for (XxlJobGroup group : groupList) {
if (group.getRegistryList() != null && !group.getRegistryList().isEmpty()) {
executorAddressSet.addAll(group.getRegistryList());
}
}
}
int executorCount = executorAddressSet.size();
Map<String, Object> dashboardMap = new HashMap<String, Object>();
dashboardMap.put("jobInfoCount", jobInfoCount);
dashboardMap.put("jobLogCount", jobLogCount);
dashboardMap.put("jobLogSuccessCount", jobLogSuccessCount);
dashboardMap.put("executorCount", executorCount);
return dashboardMap;
}
@Override
public Response<Map<String, Object>> chartInfo(Date startDate, Date endDate) {
// process
List<String> triggerDayList = new ArrayList<String>();
List<Integer> triggerDayCountRunningList = new ArrayList<Integer>();
List<Integer> triggerDayCountSucList = new ArrayList<Integer>();
List<Integer> triggerDayCountFailList = new ArrayList<Integer>();
int triggerCountRunningTotal = 0;
int triggerCountSucTotal = 0;
int triggerCountFailTotal = 0;
List<XxlJobLogReport> logReportList = xxlJobLogReportMapper.queryLogReport(startDate, endDate);
if (logReportList != null && !logReportList.isEmpty()) {
for (XxlJobLogReport item : logReportList) {
String day = DateTool.formatDate(item.getTriggerDay());
int triggerDayCountRunning = item.getRunningCount();
int triggerDayCountSuc = item.getSucCount();
int triggerDayCountFail = item.getFailCount();
triggerDayList.add(day);
triggerDayCountRunningList.add(triggerDayCountRunning);
triggerDayCountSucList.add(triggerDayCountSuc);
triggerDayCountFailList.add(triggerDayCountFail);
triggerCountRunningTotal += triggerDayCountRunning;
triggerCountSucTotal += triggerDayCountSuc;
triggerCountFailTotal += triggerDayCountFail;
}
} else {
for (int i = -6; i <= 0; i++) {
triggerDayList.add(DateTool.formatDate(DateTool.addDays(new Date(), i)));
triggerDayCountRunningList.add(0);
triggerDayCountSucList.add(0);
triggerDayCountFailList.add(0);
}
}
Map<String, Object> result = new HashMap<String, Object>();
result.put("triggerDayList", triggerDayList);
result.put("triggerDayCountRunningList", triggerDayCountRunningList);
result.put("triggerDayCountSucList", triggerDayCountSucList);
result.put("triggerDayCountFailList", triggerDayCountFailList);
result.put("triggerCountRunningTotal", triggerCountRunningTotal);
result.put("triggerCountSucTotal", triggerCountSucTotal);
result.put("triggerCountFailTotal", triggerCountFailTotal);
return Response.ofSuccess(result);
}
}
@@ -1,7 +0,0 @@
package com.xxl.job.admin.constant;
public class Consts {
public static final String ADMIN_ROLE = "ADMIN";
}
@@ -0,0 +1,15 @@
package com.xxl.job.admin.framework.constant;
/**
* System Constants
*
* @author xuxueli 2018-08-24 03:43
*/
public class Consts {
/**
* Admin Role
*/
public static final String ADMIN_ROLE = "ADMIN";
}
@@ -1,9 +1,9 @@
package com.xxl.job.admin.controller.base;
package com.xxl.job.admin.framework.controller;
import com.xxl.job.admin.constant.Consts;
import com.xxl.job.admin.model.dto.XxlBootResourceDTO;
import com.xxl.job.admin.service.XxlJobService;
import com.xxl.job.admin.util.I18nUtil;
import com.xxl.job.admin.business.service.XxlJobService;
import com.xxl.job.admin.framework.constant.Consts;
import com.xxl.job.admin.framework.model.dto.XxlBootResourceDTO;
import com.xxl.job.admin.framework.util.I18nUtil;
import com.xxl.sso.core.annotation.XxlSso;
import com.xxl.sso.core.helper.XxlSsoHelper;
import com.xxl.sso.core.model.LoginInfo;
@@ -48,7 +48,7 @@ public class IndexController {
List<XxlBootResourceDTO> resourceList = findResourceList(request);
model.addAttribute("resourceList", resourceList);
return "base/index";
return "framework/base/index";
}
/**
@@ -87,7 +87,7 @@ public class IndexController {
Map<String, Object> dashboardMap = xxlJobService.dashboardInfo();
model.addAllAttributes(dashboardMap);
return "base/dashboard";
return "framework/base/dashboard";
}
@RequestMapping("/chartInfo")
@@ -103,7 +103,7 @@ public class IndexController {
@RequestMapping("/help")
@XxlSso
public String help() {
return "base/help";
return "framework/base/help";
}
@RequestMapping(value = "/errorpage")
@@ -113,7 +113,7 @@ public class IndexController {
String exceptionMsg = "HTTP Status Code: " + response.getStatus();
mv.addObject("exceptionMsg", exceptionMsg);
mv.setViewName("common/common.errorpage");
mv.setViewName("framework/common/common.errorpage");
return mv;
}
@@ -1,11 +1,11 @@
package com.xxl.job.admin.controller.biz;
package com.xxl.job.admin.framework.controller;
import com.xxl.job.admin.constant.Consts;
import com.xxl.job.admin.mapper.XxlJobGroupMapper;
import com.xxl.job.admin.mapper.XxlJobUserMapper;
import com.xxl.job.admin.model.XxlJobGroup;
import com.xxl.job.admin.model.XxlJobUser;
import com.xxl.job.admin.util.I18nUtil;
import com.xxl.job.admin.business.mapper.XxlJobGroupMapper;
import com.xxl.job.admin.business.model.XxlJobGroup;
import com.xxl.job.admin.framework.constant.Consts;
import com.xxl.job.admin.framework.mapper.XxlJobUserMapper;
import com.xxl.job.admin.framework.model.XxlJobUser;
import com.xxl.job.admin.framework.util.I18nUtil;
import com.xxl.sso.core.annotation.XxlSso;
import com.xxl.sso.core.helper.XxlSsoHelper;
import com.xxl.sso.core.model.LoginInfo;
@@ -25,6 +25,8 @@ import org.springframework.web.bind.annotation.ResponseBody;
import java.util.List;
/**
* job user controller
*
* @author xuxueli 2019-05-04 16:39:50
*/
@Controller
@@ -44,7 +46,7 @@ public class JobUserController {
List<XxlJobGroup> groupList = xxlJobGroupMapper.findAll();
model.addAttribute("groupList", groupList);
return "biz/user.list";
return "business/user.list";
}
@RequestMapping("/pageList")
@@ -1,8 +1,8 @@
package com.xxl.job.admin.controller.base;
package com.xxl.job.admin.framework.controller;
import com.xxl.job.admin.mapper.XxlJobUserMapper;
import com.xxl.job.admin.model.XxlJobUser;
import com.xxl.job.admin.util.I18nUtil;
import com.xxl.job.admin.framework.mapper.XxlJobUserMapper;
import com.xxl.job.admin.framework.model.XxlJobUser;
import com.xxl.job.admin.framework.util.I18nUtil;
import com.xxl.sso.core.annotation.XxlSso;
import com.xxl.sso.core.helper.XxlSsoHelper;
import com.xxl.sso.core.model.LoginInfo;
@@ -43,7 +43,7 @@ public class LoginController {
modelAndView.setView(new RedirectView("/", true, false));
return modelAndView;
}
return new ModelAndView("base/login");
return new ModelAndView("framework/base/login");
}
@RequestMapping(value = "/doLogin", method = RequestMethod.POST)
@@ -1,6 +1,6 @@
package com.xxl.job.admin.mapper;
package com.xxl.job.admin.framework.mapper;
import com.xxl.job.admin.model.XxlJobUser;
import com.xxl.job.admin.framework.model.XxlJobUser;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Param;
@@ -0,0 +1,65 @@
package com.xxl.job.admin.framework.model;
/**
* xxl job user
*
* @author xuxueli 2019-05-04 16:43:12
*/
public class XxlJobUser {
private int id;
private String username; // 账号
private String password; // 密码
private String token; // 登录token
private int role; // 角色:0-普通用户、1-管理员
private String permission; // 权限:执行器ID列表,多个逗号分割
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
public String getToken() {
return token;
}
public void setToken(String token) {
this.token = token;
}
public int getRole() {
return role;
}
public void setRole(int role) {
this.role = role;
}
public String getPermission() {
return permission;
}
public void setPermission(String permission) {
this.permission = permission;
}
}
@@ -1,4 +1,4 @@
package com.xxl.job.admin.model.dto;
package com.xxl.job.admin.framework.model.dto;
import java.io.Serializable;
import java.util.Date;
@@ -1,4 +1,4 @@
package com.xxl.job.admin.util;
package com.xxl.job.admin.framework.util;
import com.xxl.job.core.constant.ExecutorBlockStrategyEnum;
import com.xxl.tool.core.PropTool;
@@ -1,7 +1,7 @@
package com.xxl.job.admin.util;
package com.xxl.job.admin.framework.util;
import com.xxl.job.admin.constant.Consts;
import com.xxl.job.admin.model.XxlJobGroup;
import com.xxl.job.admin.business.model.XxlJobGroup;
import com.xxl.job.admin.framework.constant.Consts;
import com.xxl.sso.core.helper.XxlSsoHelper;
import com.xxl.sso.core.model.LoginInfo;
import com.xxl.tool.core.StringTool;
@@ -0,0 +1,102 @@
package com.xxl.job.admin.framework.util;
import java.util.regex.Pattern;
public class XssUtil {
/**
* 定义常见的 XSS 攻击正则模式
* <p>
* 1. <script> 标签及其变体
* 2. javascript: vbscript: 等伪协议
* 3. 常见的事件处理器 (onload, onclick, onerror 等)
* 4. eval(), expression() 等危险函数
*/
private static final Pattern[] PATTERNS = {
Pattern.compile("<(no)?script[^>]*>.*?</(no)?script>", Pattern.CASE_INSENSITIVE | Pattern.DOTALL),
Pattern.compile("javascript:", Pattern.CASE_INSENSITIVE),
Pattern.compile("vbscript:", Pattern.CASE_INSENSITIVE),
Pattern.compile("on(load|error|click|mouseover|focus|blur|submit|change|input)\\s*=", Pattern.CASE_INSENSITIVE),
Pattern.compile("eval\\s*\\(", Pattern.CASE_INSENSITIVE),
Pattern.compile("expression\\s*\\(", Pattern.CASE_INSENSITIVE),
Pattern.compile("url\\s*\\(", Pattern.CASE_INSENSITIVE),
Pattern.compile("<iframe[^>]*>", Pattern.CASE_INSENSITIVE),
Pattern.compile("<object[^>]*>", Pattern.CASE_INSENSITIVE),
Pattern.compile("<embed[^>]*>", Pattern.CASE_INSENSITIVE)
};
/**
* check XSS attack patterns in a string
*
* @param value original string
* @return true if XSS attack detected, false otherwise
*/
public static boolean hasXss(String value) {
if (value == null || value.isEmpty()) {
return false;
}
// 遍历所有正则模式进行匹配
for (Pattern pattern : PATTERNS) {
if (pattern.matcher(value).find()) {
return true;
}
}
return false;
}
/**
* 清洗字符串,移除潜在的 XSS 攻击代码
*
* @param value original string
* @return cleaned string
*/
public static String cleanXss(String value) {
if (value == null || value.isEmpty()) {
return value;
}
String result = value;
// 1. 移除危险标签和脚本
for (Pattern pattern : PATTERNS) {
result = pattern.matcher(result).replaceAll("");
}
// 2. 特殊字符 HTML 实体转义
result = escapeHtml(result);
return result;
}
/**
* 基础 HTML 字符转义
*/
private static String escapeHtml(String input) {
if (input == null) {
return null;
}
return input.replace("&", "&amp;")
.replace("<", "&lt;")
.replace(">", "&gt;")
.replace("\"", "&quot;")
.replace("'", "&#x27;");
}
/*public static void main(String[] args) {
String safeInput = "Hello World";
String xssInput1 = "<script>alert('XSS')</script>";
String xssInput2 = "<img src=x onerror=alert(1)>";
String xssInput3 = "javascript:alert(1)";
System.out.println("Safe Input Has XSS: " + hasXss(safeInput)); // false
System.out.println("XSS Input 1 Has XSS: " + hasXss(xssInput1)); // true
System.out.println("XSS Input 2 Has XSS: " + hasXss(xssInput2)); // true
System.out.println("XSS Input 3 Has XSS: " + hasXss(xssInput3)); // true
System.out.println("Cleaned XSS 1: " + cleanXss(xssInput1)); // alert('XSS') (script tags removed, content escaped if needed)
System.out.println("Cleaned XSS 2: " + cleanXss(xssInput2)); // (img tag and event handler removed)
}*/
}
@@ -1,6 +1,6 @@
//package com.xxl.job.admin.web.interceptor;
//package com.xxl.job.admin.framework.web.interceptor;
//
//import com.xxl.job.admin.util.I18nUtil;
//import com.xxl.job.admin.framework.util.I18nUtil;
//import com.xxl.tool.freemarker.FtlTool;
//import jakarta.servlet.http.HttpServletRequest;
//import jakarta.servlet.http.HttpServletResponse;
@@ -1,4 +1,4 @@
//package com.xxl.job.admin.util.old;
//package com.xxl.job.admin.framework.util.old;
//
//import com.fasterxml.jackson.core.JsonGenerationException;
//import com.fasterxml.jackson.core.JsonParseException;
@@ -14,7 +14,7 @@
// * “@DisallowConcurrentExecution” disable concurrent, thread size can not be only one, better given more
// * @author xuxueli 2015-12-17 18:20:34
// */
/// /@DisallowConcurrentExecution
////@DisallowConcurrentExecution
//public class RemoteHttpJobBean extends QuartzJobBean {
// private static Logger logger = LoggerFactory.getLogger(RemoteHttpJobBean.class);
//
@@ -1,4 +1,4 @@
package com.xxl.job.admin.web.error;
package com.xxl.job.admin.framework.web.error;
import org.springframework.boot.web.error.ErrorPage;
import org.springframework.boot.web.error.ErrorPageRegistrar;
@@ -0,0 +1,65 @@
package com.xxl.job.admin.framework.web.error;
import com.xxl.job.admin.business.scheduler.exception.XxlJobException;
import com.xxl.tool.json.GsonTool;
import com.xxl.tool.response.Response;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.method.HandlerMethod;
import org.springframework.web.servlet.HandlerExceptionResolver;
import org.springframework.web.servlet.ModelAndView;
import java.io.IOException;
/**
* common exception resolver
*
* @author xuxueli 2016-1-6 19:22:18
*/
@Component
public class WebHandlerExceptionResolver implements HandlerExceptionResolver {
private static transient Logger logger = LoggerFactory.getLogger(WebHandlerExceptionResolver.class);
@Override
public ModelAndView resolveException(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) {
if (!(ex instanceof XxlJobException)) {
logger.error("WebExceptionResolver:{}", ex);
}
// parse isJson
boolean isJson = false;
if (handler instanceof HandlerMethod) {
HandlerMethod method = (HandlerMethod) handler;
isJson = method.getMethodAnnotation(ResponseBody.class) != null;
}
// process error
ModelAndView mv = new ModelAndView();
if (isJson) {
try {
// errorMsg
String errorMsg = GsonTool.toJson(Response.ofFail(ex.toString()));
// write response
response.setStatus(HttpServletResponse.SC_OK);
response.setContentType("application/json;charset=UTF-8");
response.getWriter().println(errorMsg);
} catch (IOException e) {
logger.error(e.getMessage(), e);
}
return mv;
} else {
mv.addObject("exceptionMsg", ex.toString());
mv.setViewName("framework/common/common.errorpage");
return mv;
}
}
}
@@ -1,8 +1,8 @@
package com.xxl.job.admin.web.xxlsso;
package com.xxl.job.admin.framework.web.xxlsso;
import com.xxl.job.admin.constant.Consts;
import com.xxl.job.admin.mapper.XxlJobUserMapper;
import com.xxl.job.admin.model.XxlJobUser;
import com.xxl.job.admin.framework.constant.Consts;
import com.xxl.job.admin.framework.mapper.XxlJobUserMapper;
import com.xxl.job.admin.framework.model.XxlJobUser;
import com.xxl.sso.core.model.LoginInfo;
import com.xxl.sso.core.store.LoginStore;
import com.xxl.tool.core.MapTool;
@@ -1,4 +1,4 @@
package com.xxl.job.admin.web.xxlsso;
package com.xxl.job.admin.framework.web.xxlsso;
import com.xxl.sso.core.auth.interceptor.XxlSsoWebInterceptor;
import com.xxl.sso.core.bootstrap.XxlSsoBootstrap;
@@ -1,24 +0,0 @@
package com.xxl.job.admin.mapper;
import com.xxl.job.admin.model.XxlJobLogGlue;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Param;
import java.util.List;
/**
* job log for glue
* @author xuxueli 2016-5-19 18:04:56
*/
@Mapper
public interface XxlJobLogGlueMapper {
public int save(XxlJobLogGlue xxlJobLogGlue);
public List<XxlJobLogGlue> findByJobId(@Param("jobId") int jobId);
public int removeOld(@Param("jobId") int jobId, @Param("limit") int limit);
public int deleteByJobId(@Param("jobId") int jobId);
}
@@ -1,237 +0,0 @@
package com.xxl.job.admin.model;
import java.util.Date;
/**
* xxl-job info
*
* @author xuxueli 2016-1-12 18:25:49
*/
public class XxlJobInfo {
private int id; // 主键ID
private int jobGroup; // 执行器主键ID
private String jobDesc;
private Date addTime;
private Date updateTime;
private String author; // 负责人
private String alarmEmail; // 报警邮件
private String scheduleType; // 调度类型:ScheduleTypeEnum
private String scheduleConf; // 调度配置,值含义取决于调度类型
private String misfireStrategy; // 调度过期策略:MisfireStrategyEnum
private String executorRouteStrategy; // 执行器路由策略:ExecutorRouteStrategyEnum
private String executorHandler; // 执行器,任务Handler名称
private String executorParam; // 执行器,任务参数
private String executorBlockStrategy; // 阻塞处理策略:ExecutorBlockStrategyEnum
private int executorTimeout; // 任务执行超时时间,单位秒
private int executorFailRetryCount; // 失败重试次数
private String glueType; // GLUE类型:GlueTypeEnum
private String glueSource; // GLUE源代码
private String glueRemark; // GLUE备注
private Date glueUpdatetime; // GLUE更新时间
private String childJobId; // 子任务ID,多个逗号分隔
private int triggerStatus; // 调度状态:TriggerStatus
private long triggerLastTime; // 上次调度时间
private long triggerNextTime; // 下次调度时间
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public int getJobGroup() {
return jobGroup;
}
public void setJobGroup(int jobGroup) {
this.jobGroup = jobGroup;
}
public String getJobDesc() {
return jobDesc;
}
public void setJobDesc(String jobDesc) {
this.jobDesc = jobDesc;
}
public Date getAddTime() {
return addTime;
}
public void setAddTime(Date addTime) {
this.addTime = addTime;
}
public Date getUpdateTime() {
return updateTime;
}
public void setUpdateTime(Date updateTime) {
this.updateTime = updateTime;
}
public String getAuthor() {
return author;
}
public void setAuthor(String author) {
this.author = author;
}
public String getAlarmEmail() {
return alarmEmail;
}
public void setAlarmEmail(String alarmEmail) {
this.alarmEmail = alarmEmail;
}
public String getScheduleType() {
return scheduleType;
}
public void setScheduleType(String scheduleType) {
this.scheduleType = scheduleType;
}
public String getScheduleConf() {
return scheduleConf;
}
public void setScheduleConf(String scheduleConf) {
this.scheduleConf = scheduleConf;
}
public String getMisfireStrategy() {
return misfireStrategy;
}
public void setMisfireStrategy(String misfireStrategy) {
this.misfireStrategy = misfireStrategy;
}
public String getExecutorRouteStrategy() {
return executorRouteStrategy;
}
public void setExecutorRouteStrategy(String executorRouteStrategy) {
this.executorRouteStrategy = executorRouteStrategy;
}
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 String getExecutorBlockStrategy() {
return executorBlockStrategy;
}
public void setExecutorBlockStrategy(String executorBlockStrategy) {
this.executorBlockStrategy = executorBlockStrategy;
}
public int getExecutorTimeout() {
return executorTimeout;
}
public void setExecutorTimeout(int executorTimeout) {
this.executorTimeout = executorTimeout;
}
public int getExecutorFailRetryCount() {
return executorFailRetryCount;
}
public void setExecutorFailRetryCount(int executorFailRetryCount) {
this.executorFailRetryCount = executorFailRetryCount;
}
public String getGlueType() {
return glueType;
}
public void setGlueType(String glueType) {
this.glueType = glueType;
}
public String getGlueSource() {
return glueSource;
}
public void setGlueSource(String glueSource) {
this.glueSource = glueSource;
}
public String getGlueRemark() {
return glueRemark;
}
public void setGlueRemark(String glueRemark) {
this.glueRemark = glueRemark;
}
public Date getGlueUpdatetime() {
return glueUpdatetime;
}
public void setGlueUpdatetime(Date glueUpdatetime) {
this.glueUpdatetime = glueUpdatetime;
}
public String getChildJobId() {
return childJobId;
}
public void setChildJobId(String childJobId) {
this.childJobId = childJobId;
}
public int getTriggerStatus() {
return triggerStatus;
}
public void setTriggerStatus(int triggerStatus) {
this.triggerStatus = triggerStatus;
}
public long getTriggerLastTime() {
return triggerLastTime;
}
public void setTriggerLastTime(long triggerLastTime) {
this.triggerLastTime = triggerLastTime;
}
public long getTriggerNextTime() {
return triggerNextTime;
}
public void setTriggerNextTime(long triggerNextTime) {
this.triggerNextTime = triggerNextTime;
}
}
@@ -1,157 +0,0 @@
package com.xxl.job.admin.model;
import java.util.Date;
/**
* xxl-job log, used to track trigger process
* @author xuxueli 2015-12-19 23:19:09
*/
public class XxlJobLog {
private long id;
// job info
private int jobGroup;
private int jobId;
// execute info
private String executorAddress;
private String executorHandler;
private String executorParam;
private String executorShardingParam;
private int executorFailRetryCount;
// trigger info
private Date triggerTime;
private int triggerCode;
private String triggerMsg;
// handle info
private Date handleTime;
private int handleCode;
private String handleMsg;
// alarm info
private int alarmStatus;
public long getId() {
return id;
}
public void setId(long id) {
this.id = id;
}
public int getJobGroup() {
return jobGroup;
}
public void setJobGroup(int jobGroup) {
this.jobGroup = jobGroup;
}
public int getJobId() {
return jobId;
}
public void setJobId(int jobId) {
this.jobId = jobId;
}
public String getExecutorAddress() {
return executorAddress;
}
public void setExecutorAddress(String executorAddress) {
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 String getExecutorShardingParam() {
return executorShardingParam;
}
public void setExecutorShardingParam(String executorShardingParam) {
this.executorShardingParam = executorShardingParam;
}
public int getExecutorFailRetryCount() {
return executorFailRetryCount;
}
public void setExecutorFailRetryCount(int executorFailRetryCount) {
this.executorFailRetryCount = executorFailRetryCount;
}
public Date getTriggerTime() {
return triggerTime;
}
public void setTriggerTime(Date triggerTime) {
this.triggerTime = triggerTime;
}
public int getTriggerCode() {
return triggerCode;
}
public void setTriggerCode(int triggerCode) {
this.triggerCode = triggerCode;
}
public String getTriggerMsg() {
return triggerMsg;
}
public void setTriggerMsg(String triggerMsg) {
this.triggerMsg = triggerMsg;
}
public Date getHandleTime() {
return handleTime;
}
public void setHandleTime(Date handleTime) {
this.handleTime = handleTime;
}
public int getHandleCode() {
return handleCode;
}
public void setHandleCode(int handleCode) {
this.handleCode = handleCode;
}
public String getHandleMsg() {
return handleMsg;
}
public void setHandleMsg(String handleMsg) {
this.handleMsg = handleMsg;
}
public int getAlarmStatus() {
return alarmStatus;
}
public void setAlarmStatus(int alarmStatus) {
this.alarmStatus = alarmStatus;
}
}
@@ -1,75 +0,0 @@
package com.xxl.job.admin.model;
import java.util.Date;
/**
* xxl-job log for glue, used to track job code process
* @author xuxueli 2016-5-19 17:57:46
*/
public class XxlJobLogGlue {
private int id;
private int jobId; // 任务主键ID
private String glueType; // GLUE类型 #com.xxl.job.core.glue.GlueTypeEnum
private String glueSource;
private String glueRemark;
private Date addTime;
private Date updateTime;
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public int getJobId() {
return jobId;
}
public void setJobId(int jobId) {
this.jobId = jobId;
}
public String getGlueType() {
return glueType;
}
public void setGlueType(String glueType) {
this.glueType = glueType;
}
public String getGlueSource() {
return glueSource;
}
public void setGlueSource(String glueSource) {
this.glueSource = glueSource;
}
public String getGlueRemark() {
return glueRemark;
}
public void setGlueRemark(String glueRemark) {
this.glueRemark = glueRemark;
}
public Date getAddTime() {
return addTime;
}
public void setAddTime(Date addTime) {
this.addTime = addTime;
}
public Date getUpdateTime() {
return updateTime;
}
public void setUpdateTime(Date updateTime) {
this.updateTime = updateTime;
}
}
@@ -1,63 +0,0 @@
package com.xxl.job.admin.model;
/**
* @author xuxueli 2019-05-04 16:43:12
*/
public class XxlJobUser {
private int id;
private String username; // 账号
private String password; // 密码
private String token; // 登录token
private int role; // 角色:0-普通用户、1-管理员
private String permission; // 权限:执行器ID列表,多个逗号分割
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
public String getToken() {
return token;
}
public void setToken(String token) {
this.token = token;
}
public int getRole() {
return role;
}
public void setRole(int role) {
this.role = role;
}
public String getPermission() {
return permission;
}
public void setPermission(String permission) {
this.permission = permission;
}
}
@@ -1,113 +0,0 @@
package com.xxl.job.admin.scheduler.thread;
import com.xxl.job.admin.model.XxlJobInfo;
import com.xxl.job.admin.model.XxlJobLog;
import com.xxl.job.admin.scheduler.config.XxlJobAdminBootstrap;
import com.xxl.job.admin.scheduler.trigger.TriggerTypeEnum;
import com.xxl.job.admin.util.I18nUtil;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.List;
import java.util.concurrent.TimeUnit;
/**
* job fail-monitor helper
*
* @author xuxueli 2015-9-1 18:05:56
*/
public class JobFailAlarmMonitorHelper {
private static Logger logger = LoggerFactory.getLogger(JobFailAlarmMonitorHelper.class);
// ---------------------- monitor ----------------------
private Thread monitorThread;
private volatile boolean toStop = false;
/**
* start
*/
public void start() {
monitorThread = new Thread(new Runnable() {
@Override
public void run() {
// monitor
while (!toStop) {
try {
List<Long> failLogIds = XxlJobAdminBootstrap.getInstance().getXxlJobLogMapper().findFailJobLogIds(1000);
if (failLogIds != null && !failLogIds.isEmpty()) {
for (long failLogId : failLogIds) {
// lock log
int lockRet = XxlJobAdminBootstrap.getInstance().getXxlJobLogMapper().updateAlarmStatus(failLogId, 0, -1);
if (lockRet < 1) {
continue;
}
XxlJobLog log = XxlJobAdminBootstrap.getInstance().getXxlJobLogMapper().load(failLogId);
XxlJobInfo info = XxlJobAdminBootstrap.getInstance().getXxlJobInfoMapper().loadById(log.getJobId());
// 1、fail retry monitor
if (log.getExecutorFailRetryCount() > 0) {
XxlJobAdminBootstrap.getInstance().getJobTriggerPoolHelper().trigger(log.getJobId(), TriggerTypeEnum.RETRY, (log.getExecutorFailRetryCount() - 1), log.getExecutorShardingParam(), log.getExecutorParam(), null);
String retryMsg = "<br><br><span style=\"color:#00c0ef;\" > >>>>>>>>>>>" + I18nUtil.getString("jobconf_trigger_type_retry") + "<<<<<<<<<<< </span><br>";
log.setTriggerMsg(log.getTriggerMsg() + retryMsg);
XxlJobAdminBootstrap.getInstance().getXxlJobLogMapper().updateTriggerInfo(log);
}
// 2、fail alarm monitor
int newAlarmStatus = 0; // 告警状态:0-默认、-1=锁定状态、1-无需告警、2-告警成功、3-告警失败
if (info != null) {
boolean alarmResult = XxlJobAdminBootstrap.getInstance().getJobAlarmer().alarm(info, log);
newAlarmStatus = alarmResult ? 2 : 3;
} else {
newAlarmStatus = 1;
}
XxlJobAdminBootstrap.getInstance().getXxlJobLogMapper().updateAlarmStatus(failLogId, -1, newAlarmStatus);
}
}
} catch (Throwable e) {
if (!toStop) {
logger.error(">>>>>>>>>>> xxl-job, job fail monitor thread error:{}", e.getMessage(), e);
}
}
try {
TimeUnit.SECONDS.sleep(10);
} catch (Throwable e) {
if (!toStop) {
logger.error(e.getMessage(), e);
}
}
}
logger.info(">>>>>>>>>>> xxl-job, job fail monitor thread stop");
}
});
monitorThread.setDaemon(true);
monitorThread.setName("xxl-job, admin JobFailMonitorHelper");
monitorThread.start();
}
/**
* stop
*/
public void stop() {
toStop = true;
// interrupt and wait
monitorThread.interrupt();
try {
monitorThread.join();
} catch (Throwable e) {
logger.error(e.getMessage(), e);
}
}
}
@@ -1,162 +0,0 @@
package com.xxl.job.admin.scheduler.thread;
import com.xxl.job.admin.model.XxlJobLogReport;
import com.xxl.job.admin.scheduler.config.XxlJobAdminBootstrap;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.Calendar;
import java.util.Date;
import java.util.List;
import java.util.Map;
import java.util.concurrent.TimeUnit;
/**
* job log report helper
*
* @author xuxueli 2019-11-22
*/
public class JobLogReportHelper {
private static final Logger logger = LoggerFactory.getLogger(JobLogReportHelper.class);
private Thread logReportThread;
private volatile boolean toStop = false;
/**
* start
*/
public void start() {
logReportThread = new Thread(new Runnable() {
@Override
public void run() {
// last clean log time
long lastCleanLogTime = 0;
while (!toStop) {
// 1、log-report refresh: refresh log report in 3 days
try {
for (int i = 0; i < 3; i++) {
// today
Calendar itemDay = Calendar.getInstance();
itemDay.add(Calendar.DAY_OF_MONTH, -i);
itemDay.set(Calendar.HOUR_OF_DAY, 0);
itemDay.set(Calendar.MINUTE, 0);
itemDay.set(Calendar.SECOND, 0);
itemDay.set(Calendar.MILLISECOND, 0);
Date todayFrom = itemDay.getTime();
itemDay.set(Calendar.HOUR_OF_DAY, 23);
itemDay.set(Calendar.MINUTE, 59);
itemDay.set(Calendar.SECOND, 59);
itemDay.set(Calendar.MILLISECOND, 999);
Date todayTo = itemDay.getTime();
// refresh log-report every minute
XxlJobLogReport xxlJobLogReport = new XxlJobLogReport();
xxlJobLogReport.setTriggerDay(todayFrom);
xxlJobLogReport.setRunningCount(0);
xxlJobLogReport.setSucCount(0);
xxlJobLogReport.setFailCount(0);
xxlJobLogReport.setUpdateTime(new Date());
// fill count-data
Map<String, Object> triggerCountMap = XxlJobAdminBootstrap.getInstance().getXxlJobLogMapper().findLogReport(todayFrom, todayTo);
if (triggerCountMap != null && !triggerCountMap.isEmpty()) {
int triggerDayCount = triggerCountMap.containsKey("triggerDayCount") ? Integer.parseInt(String.valueOf(triggerCountMap.get("triggerDayCount"))) : 0;
int triggerDayCountRunning = triggerCountMap.containsKey("triggerDayCountRunning") ? Integer.parseInt(String.valueOf(triggerCountMap.get("triggerDayCountRunning"))) : 0;
int triggerDayCountSuc = triggerCountMap.containsKey("triggerDayCountSuc") ? Integer.parseInt(String.valueOf(triggerCountMap.get("triggerDayCountSuc"))) : 0;
int triggerDayCountFail = triggerDayCount - triggerDayCountRunning - triggerDayCountSuc;
xxlJobLogReport.setRunningCount(triggerDayCountRunning);
xxlJobLogReport.setSucCount(triggerDayCountSuc);
xxlJobLogReport.setFailCount(triggerDayCountFail);
}
// do refresh:
XxlJobAdminBootstrap.getInstance().getXxlJobLogReportMapper().saveOrUpdate(xxlJobLogReport); // 0-fail; 1-save suc; 2-update suc;
/*if (ret < 1) {
XxlJobAdminBootstrap.getInstance().getXxlJobLogReportMapper().save(xxlJobLogReport);
}*/
}
} catch (Throwable e) {
if (!toStop) {
logger.error(">>>>>>>>>>> xxl-job, JobLogReportHelper(log-report refresh) error:{}", e.getMessage(), e);
}
}
// 2、log-clean: switch open & once each day
try {
if (XxlJobAdminBootstrap.getInstance().getLogretentiondays() > 0
&& System.currentTimeMillis() - lastCleanLogTime > 24 * 60 * 60 * 1000) {
// expire-time
Calendar expiredDay = Calendar.getInstance();
expiredDay.add(Calendar.DAY_OF_MONTH, -1 * XxlJobAdminBootstrap.getInstance().getLogretentiondays());
expiredDay.set(Calendar.HOUR_OF_DAY, 0);
expiredDay.set(Calendar.MINUTE, 0);
expiredDay.set(Calendar.SECOND, 0);
expiredDay.set(Calendar.MILLISECOND, 0);
Date clearBeforeTime = expiredDay.getTime();
// clean expired log
List<Long> logIds = null;
do {
logIds = XxlJobAdminBootstrap.getInstance().getXxlJobLogMapper().findClearLogIds(0, 0, clearBeforeTime, 0, 1000);
if (logIds != null && !logIds.isEmpty()) {
XxlJobAdminBootstrap.getInstance().getXxlJobLogMapper().clearLog(logIds);
}
} while (logIds != null && !logIds.isEmpty());
// update clean time
lastCleanLogTime = System.currentTimeMillis();
}
} catch (Throwable e) {
if (!toStop) {
logger.error(">>>>>>>>>>> xxl-job, JobLogReportHelper(log-clean) error:{}", e.getMessage(), e);
}
}
try {
TimeUnit.MINUTES.sleep(1);
} catch (Throwable e) {
if (!toStop) {
logger.error(e.getMessage(), e);
}
}
}
logger.info(">>>>>>>>>>> xxl-job, job log report thread stop");
}
});
logReportThread.setDaemon(true);
logReportThread.setName("xxl-job, admin JobLogReportHelper");
logReportThread.start();
}
/**
* stop
*/
public void stop() {
toStop = true;
// interrupt and wait
logReportThread.interrupt();
try {
logReportThread.join();
} catch (Throwable e) {
logger.error(e.getMessage(), e);
}
}
}
@@ -1,222 +0,0 @@
package com.xxl.job.admin.scheduler.thread;
import com.xxl.job.admin.model.XxlJobGroup;
import com.xxl.job.admin.model.XxlJobRegistry;
import com.xxl.job.admin.scheduler.config.XxlJobAdminBootstrap;
import com.xxl.job.core.constant.Const;
import com.xxl.job.core.constant.RegistType;
import com.xxl.job.core.openapi.model.RegistryRequest;
import com.xxl.tool.core.StringTool;
import com.xxl.tool.response.Response;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.*;
import java.util.concurrent.*;
/**
* job registry instance helper
*
* @author xuxueli 2016-10-02 19:10:24
*/
public class JobRegistryHelper {
private static Logger logger = LoggerFactory.getLogger(JobRegistryHelper.class);
private ThreadPoolExecutor registryOrRemoveThreadPool = null;
private Thread registryMonitorThread;
private volatile boolean toStop = false;
/**
* start
*/
public void start() {
// for registry or remove
registryOrRemoveThreadPool = new ThreadPoolExecutor(
2,
10,
30L,
TimeUnit.SECONDS,
new LinkedBlockingQueue<Runnable>(2000),
new ThreadFactory() {
@Override
public Thread newThread(Runnable r) {
return new Thread(r, "xxl-job, admin JobRegistryMonitorHelper-registryOrRemoveThreadPool-" + r.hashCode());
}
},
new RejectedExecutionHandler() {
@Override
public void rejectedExecution(Runnable r, ThreadPoolExecutor executor) {
r.run();
logger.warn(">>>>>>>>>>> xxl-job, registry or remove too fast, match threadpool rejected handler(run now).");
}
});
// for monitor
registryMonitorThread = new Thread(new Runnable() {
@Override
public void run() {
while (!toStop) {
try {
// auto registry group
List<XxlJobGroup> groupList = XxlJobAdminBootstrap.getInstance().getXxlJobGroupMapper().findByAddressType(0);
if (groupList != null && !groupList.isEmpty()) {
// remove dead address (admin/executor)
List<Integer> ids = XxlJobAdminBootstrap.getInstance().getXxlJobRegistryMapper().findDead(Const.DEAD_TIMEOUT, new Date());
if (ids != null && !ids.isEmpty()) {
XxlJobAdminBootstrap.getInstance().getXxlJobRegistryMapper().removeDead(ids);
}
// fresh online address (admin/executor)
HashMap<String, List<String>> appAddressMap = new HashMap<String, List<String>>();
List<XxlJobRegistry> list = XxlJobAdminBootstrap.getInstance().getXxlJobRegistryMapper().findAll(Const.DEAD_TIMEOUT, new Date());
if (list != null) {
for (XxlJobRegistry item : list) {
if (RegistType.EXECUTOR.name().equals(item.getRegistryGroup())) {
String appname = item.getRegistryKey();
List<String> registryList = appAddressMap.get(appname);
if (registryList == null) {
registryList = new ArrayList<String>();
}
if (!registryList.contains(item.getRegistryValue())) {
registryList.add(item.getRegistryValue());
}
appAddressMap.put(appname, registryList);
}
}
}
// fresh group address
for (XxlJobGroup group : groupList) {
List<String> registryList = appAddressMap.get(group.getAppname());
String addressListStr = null;
if (registryList != null && !registryList.isEmpty()) {
Collections.sort(registryList);
StringBuilder addressListSB = new StringBuilder();
for (String item : registryList) {
addressListSB.append(item).append(",");
}
addressListStr = addressListSB.toString();
addressListStr = addressListStr.substring(0, addressListStr.length() - 1);
}
group.setAddressList(addressListStr);
group.setUpdateTime(new Date());
XxlJobAdminBootstrap.getInstance().getXxlJobGroupMapper().update(group);
}
}
} catch (Throwable e) {
if (!toStop) {
logger.error(">>>>>>>>>>> xxl-job, job registry monitor thread error:{}", e);
}
}
try {
TimeUnit.SECONDS.sleep(Const.BEAT_TIMEOUT);
} catch (Throwable e) {
if (!toStop) {
logger.error(">>>>>>>>>>> xxl-job, job registry monitor thread error:{}", e);
}
}
}
logger.info(">>>>>>>>>>> xxl-job, job registry monitor thread stop");
}
});
registryMonitorThread.setDaemon(true);
registryMonitorThread.setName("xxl-job, admin JobRegistryMonitorHelper-registryMonitorThread");
registryMonitorThread.start();
}
/**
* stop
*/
public void stop() {
toStop = true;
// stop registryOrRemoveThreadPool
registryOrRemoveThreadPool.shutdownNow();
// stop monitor (interrupt and wait)
registryMonitorThread.interrupt();
try {
registryMonitorThread.join();
} catch (Throwable e) {
logger.error(e.getMessage(), e);
}
}
// ---------------------- tool ----------------------
/**
* registry
*/
public Response<String> registry(RegistryRequest registryParam) {
// valid
if (StringTool.isBlank(registryParam.getRegistryGroup())
|| StringTool.isBlank(registryParam.getRegistryKey())
|| StringTool.isBlank(registryParam.getRegistryValue())) {
return Response.ofFail("Illegal Argument.");
}
// async execute
registryOrRemoveThreadPool.execute(new Runnable() {
@Override
public void run() {
// 0-fail; 1-save suc; 2-update suc;
int ret = XxlJobAdminBootstrap.getInstance().getXxlJobRegistryMapper().registrySaveOrUpdate(registryParam.getRegistryGroup(), registryParam.getRegistryKey(), registryParam.getRegistryValue(), new Date());
if (ret == 1) {
// fresh (add)
freshGroupRegistryInfo(registryParam);
}
/*int ret = XxlJobAdminConfig.getAdminConfig().getXxlJobRegistryDao().registryUpdate(registryParam.getRegistryGroup(), registryParam.getRegistryKey(), registryParam.getRegistryValue(), new Date());
if (ret < 1) {
XxlJobAdminConfig.getAdminConfig().getXxlJobRegistryDao().registrySave(registryParam.getRegistryGroup(), registryParam.getRegistryKey(), registryParam.getRegistryValue(), new Date());
// fresh
freshGroupRegistryInfo(registryParam);
}*/
}
});
return Response.ofSuccess();
}
/**
* registry remove
*/
public Response<String> registryRemove(RegistryRequest registryParam) {
// valid
if (StringTool.isBlank(registryParam.getRegistryGroup())
|| StringTool.isBlank(registryParam.getRegistryKey())
|| StringTool.isBlank(registryParam.getRegistryValue())) {
return Response.ofFail("Illegal Argument.");
}
// async execute
registryOrRemoveThreadPool.execute(new Runnable() {
@Override
public void run() {
int ret = XxlJobAdminBootstrap.getInstance().getXxlJobRegistryMapper().registryDelete(registryParam.getRegistryGroup(), registryParam.getRegistryKey(), registryParam.getRegistryValue());
if (ret > 0) {
// fresh (delete)
freshGroupRegistryInfo(registryParam);
}
}
});
return Response.ofSuccess();
}
private void freshGroupRegistryInfo(RegistryRequest registryParam) {
// Under consideration, prevent affecting core tables
}
}
@@ -1,63 +0,0 @@
package com.xxl.job.admin.service;
import com.xxl.job.admin.model.XxlJobInfo;
import com.xxl.sso.core.model.LoginInfo;
import com.xxl.tool.response.PageModel;
import com.xxl.tool.response.Response;
import java.util.Date;
import java.util.Map;
/**
* core job action for xxl-job
*
* @author xuxueli 2016-5-28 15:30:33
*/
public interface XxlJobService {
/**
* page list
*/
public Response<PageModel<XxlJobInfo>> pageList(int offset, int pagesize, int jobGroup, int triggerStatus, String jobDesc, String executorHandler, String author);
/**
* add job
*/
public Response<String> add(XxlJobInfo jobInfo, LoginInfo loginInfo);
/**
* update job
*/
public Response<String> update(XxlJobInfo jobInfo, LoginInfo loginInfo);
/**
* remove job
*/
public Response<String> remove(int id, LoginInfo loginInfo);
/**
* start job
*/
public Response<String> start(int id, LoginInfo loginInfo);
/**
* stop job
*/
public Response<String> stop(int id, LoginInfo loginInfo);
/**
* trigger
*/
public Response<String> trigger(LoginInfo loginInfo, int jobId, String executorParam, String addressList);
/**
* dashboard info
*/
public Map<String, Object> dashboardInfo();
/**
* chart info
*/
public Response<Map<String, Object>> chartInfo(Date startDate, Date endDate);
}
@@ -1,531 +0,0 @@
package com.xxl.job.admin.service.impl;
import com.xxl.job.admin.constant.TriggerStatus;
import com.xxl.job.admin.mapper.*;
import com.xxl.job.admin.model.XxlJobGroup;
import com.xxl.job.admin.model.XxlJobInfo;
import com.xxl.job.admin.model.XxlJobLogReport;
import com.xxl.job.admin.scheduler.config.XxlJobAdminBootstrap;
import com.xxl.job.admin.scheduler.cron.CronExpression;
import com.xxl.job.admin.scheduler.misfire.MisfireStrategyEnum;
import com.xxl.job.admin.scheduler.route.ExecutorRouteStrategyEnum;
import com.xxl.job.admin.scheduler.thread.JobScheduleHelper;
import com.xxl.job.admin.scheduler.trigger.TriggerTypeEnum;
import com.xxl.job.admin.scheduler.type.ScheduleTypeEnum;
import com.xxl.job.admin.service.XxlJobService;
import com.xxl.job.admin.util.I18nUtil;
import com.xxl.job.admin.util.JobGroupPermissionUtil;
import com.xxl.job.core.constant.ExecutorBlockStrategyEnum;
import com.xxl.job.core.glue.GlueTypeEnum;
import com.xxl.sso.core.model.LoginInfo;
import com.xxl.tool.core.DateTool;
import com.xxl.tool.core.StringTool;
import com.xxl.tool.json.GsonTool;
import com.xxl.tool.response.PageModel;
import com.xxl.tool.response.Response;
import jakarta.annotation.Resource;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Service;
import java.text.MessageFormat;
import java.util.*;
/**
* core job action for xxl-job
* @author xuxueli 2016-5-28 15:30:33
*/
@Service
public class XxlJobServiceImpl implements XxlJobService {
private static Logger logger = LoggerFactory.getLogger(XxlJobServiceImpl.class);
@Resource
private XxlJobGroupMapper xxlJobGroupMapper;
@Resource
private XxlJobInfoMapper xxlJobInfoMapper;
@Resource
public XxlJobLogMapper xxlJobLogMapper;
@Resource
private XxlJobLogGlueMapper xxlJobLogGlueMapper;
@Resource
private XxlJobLogReportMapper xxlJobLogReportMapper;
@Override
public Response<PageModel<XxlJobInfo>> pageList(int offset, int pagesize, int jobGroup, int triggerStatus, String jobDesc, String executorHandler, String author) {
// page list
List<XxlJobInfo> list = xxlJobInfoMapper.pageList(offset, pagesize, jobGroup, triggerStatus, jobDesc, executorHandler, author);
int list_count = xxlJobInfoMapper.pageListCount(offset, pagesize, jobGroup, triggerStatus, jobDesc, executorHandler, author);
// package result
PageModel<XxlJobInfo> pageModel = new PageModel<>();
pageModel.setData(list);
pageModel.setTotal(list_count);
return Response.ofSuccess(pageModel);
}
@Override
public Response<String> add(XxlJobInfo jobInfo, LoginInfo loginInfo) {
// valid base
XxlJobGroup group = xxlJobGroupMapper.load(jobInfo.getJobGroup());
if (group == null) {
return Response.ofFail(I18nUtil.getString("system_please_choose") + I18nUtil.getString("jobinfo_field_jobgroup"));
}
if (StringTool.isBlank(jobInfo.getJobDesc())) {
return Response.ofFail((I18nUtil.getString("system_please_input") + I18nUtil.getString("jobinfo_field_jobdesc")));
}
if (StringTool.isBlank(jobInfo.getAuthor())) {
return Response.ofFail((I18nUtil.getString("system_please_input") + I18nUtil.getString("jobinfo_field_author")));
}
// valid trigger
ScheduleTypeEnum scheduleTypeEnum = ScheduleTypeEnum.match(jobInfo.getScheduleType(), null);
if (scheduleTypeEnum == null) {
return Response.ofFail((I18nUtil.getString("schedule_type") + I18nUtil.getString("system_invalid")));
}
if (scheduleTypeEnum == ScheduleTypeEnum.CRON) {
if (jobInfo.getScheduleConf() == null || !CronExpression.isValidExpression(jobInfo.getScheduleConf())) {
return Response.ofFail("Cron" + I18nUtil.getString("system_invalid"));
}
} else if (scheduleTypeEnum == ScheduleTypeEnum.FIX_RATE/* || scheduleTypeEnum == ScheduleTypeEnum.FIX_DELAY*/) {
if (jobInfo.getScheduleConf() == null) {
return Response.ofFail((I18nUtil.getString("schedule_type")));
}
try {
int fixSecond = Integer.parseInt(jobInfo.getScheduleConf());
if (fixSecond < 1) {
return Response.ofFail((I18nUtil.getString("schedule_type") + I18nUtil.getString("system_invalid")));
}
} catch (Exception e) {
return Response.ofFail((I18nUtil.getString("schedule_type") + I18nUtil.getString("system_invalid")));
}
}
// valid job
if (GlueTypeEnum.match(jobInfo.getGlueType()) == null) {
return Response.ofFail((I18nUtil.getString("jobinfo_field_gluetype") + I18nUtil.getString("system_invalid")));
}
if (GlueTypeEnum.BEAN == GlueTypeEnum.match(jobInfo.getGlueType()) && StringTool.isBlank(jobInfo.getExecutorHandler())) {
return Response.ofFail((I18nUtil.getString("system_please_input") + "JobHandler"));
}
// 》fix "\r" in shell
if (GlueTypeEnum.GLUE_SHELL == GlueTypeEnum.match(jobInfo.getGlueType()) && jobInfo.getGlueSource() != null) {
jobInfo.setGlueSource(jobInfo.getGlueSource().replaceAll("\r", ""));
}
// valid advanced
if (ExecutorRouteStrategyEnum.match(jobInfo.getExecutorRouteStrategy(), null) == null) {
return Response.ofFail((I18nUtil.getString("jobinfo_field_executorRouteStrategy") + I18nUtil.getString("system_invalid")));
}
if (MisfireStrategyEnum.match(jobInfo.getMisfireStrategy(), null) == null) {
return Response.ofFail((I18nUtil.getString("misfire_strategy") + I18nUtil.getString("system_invalid")));
}
if (ExecutorBlockStrategyEnum.match(jobInfo.getExecutorBlockStrategy(), null) == null) {
return Response.ofFail((I18nUtil.getString("jobinfo_field_executorBlockStrategy") + I18nUtil.getString("system_invalid")));
}
// 》ChildJobId valid
if (StringTool.isNotBlank(jobInfo.getChildJobId())) {
String[] childJobIds = jobInfo.getChildJobId().split(",");
for (String childJobIdItem : childJobIds) {
if (StringTool.isNotBlank(childJobIdItem) && StringTool.isNumeric(childJobIdItem)) {
XxlJobInfo childJobInfo = xxlJobInfoMapper.loadById(Integer.parseInt(childJobIdItem));
if (childJobInfo == null) {
return Response.ofFail(
MessageFormat.format((I18nUtil.getString("jobinfo_field_childJobId") + "({0})" + I18nUtil.getString("system_not_found")), childJobIdItem));
}
// valid jobGroup permission
if (!JobGroupPermissionUtil.hasJobGroupPermission(loginInfo, childJobInfo.getJobGroup())) {
return Response.ofFail(
MessageFormat.format((I18nUtil.getString("jobinfo_field_childJobId") + "({0})" + I18nUtil.getString("system_permission_limit")), childJobIdItem));
}
} else {
return Response.ofFail(
MessageFormat.format((I18nUtil.getString("jobinfo_field_childJobId") + "({0})" + I18nUtil.getString("system_invalid")), childJobIdItem));
}
}
// join , avoid "xxx,,"
String temp = "";
for (String item : childJobIds) {
temp += item + ",";
}
temp = temp.substring(0, temp.length() - 1);
jobInfo.setChildJobId(temp);
}
// add in db
jobInfo.setAddTime(new Date());
jobInfo.setUpdateTime(new Date());
jobInfo.setGlueUpdatetime(new Date());
// remove the whitespace
jobInfo.setExecutorHandler(jobInfo.getExecutorHandler().trim());
xxlJobInfoMapper.save(jobInfo);
if (jobInfo.getId() < 1) {
return Response.ofFail((I18nUtil.getString("jobinfo_field_add") + I18nUtil.getString("system_fail")));
}
// write operation log
logger.info(">>>>>>>>>>> xxl-job operation log: operator = {}, type = {}, content = {}",
loginInfo.getUserName(), "jobinfo-save", GsonTool.toJson(jobInfo));
return Response.ofSuccess(String.valueOf(jobInfo.getId()));
}
@Override
public Response<String> update(XxlJobInfo jobInfo, LoginInfo loginInfo) {
// valid base
if (StringTool.isBlank(jobInfo.getJobDesc())) {
return Response.ofFail((I18nUtil.getString("system_please_input") + I18nUtil.getString("jobinfo_field_jobdesc")));
}
if (StringTool.isBlank(jobInfo.getAuthor())) {
return Response.ofFail((I18nUtil.getString("system_please_input") + I18nUtil.getString("jobinfo_field_author")));
}
// valid trigger
ScheduleTypeEnum scheduleTypeEnum = ScheduleTypeEnum.match(jobInfo.getScheduleType(), null);
if (scheduleTypeEnum == null) {
return Response.ofFail((I18nUtil.getString("schedule_type") + I18nUtil.getString("system_invalid")));
}
if (scheduleTypeEnum == ScheduleTypeEnum.CRON) {
if (jobInfo.getScheduleConf() == null || !CronExpression.isValidExpression(jobInfo.getScheduleConf())) {
return Response.ofFail("Cron" + I18nUtil.getString("system_invalid"));
}
} else if (scheduleTypeEnum == ScheduleTypeEnum.FIX_RATE /*|| scheduleTypeEnum == ScheduleTypeEnum.FIX_DELAY*/) {
if (jobInfo.getScheduleConf() == null) {
return Response.ofFail((I18nUtil.getString("schedule_type") + I18nUtil.getString("system_invalid")));
}
try {
int fixSecond = Integer.parseInt(jobInfo.getScheduleConf());
if (fixSecond < 1) {
return Response.ofFail((I18nUtil.getString("schedule_type") + I18nUtil.getString("system_invalid")));
}
} catch (Exception e) {
return Response.ofFail((I18nUtil.getString("schedule_type") + I18nUtil.getString("system_invalid")));
}
}
// valid advanced
if (ExecutorRouteStrategyEnum.match(jobInfo.getExecutorRouteStrategy(), null) == null) {
return Response.ofFail((I18nUtil.getString("jobinfo_field_executorRouteStrategy") + I18nUtil.getString("system_invalid")));
}
if (MisfireStrategyEnum.match(jobInfo.getMisfireStrategy(), null) == null) {
return Response.ofFail((I18nUtil.getString("misfire_strategy") + I18nUtil.getString("system_invalid")));
}
if (ExecutorBlockStrategyEnum.match(jobInfo.getExecutorBlockStrategy(), null) == null) {
return Response.ofFail((I18nUtil.getString("jobinfo_field_executorBlockStrategy") + I18nUtil.getString("system_invalid")));
}
// 》ChildJobId valid
if (StringTool.isNotBlank(jobInfo.getChildJobId())) {
String[] childJobIds = jobInfo.getChildJobId().split(",");
for (String childJobIdItem : childJobIds) {
if (StringTool.isNotBlank(childJobIdItem) && StringTool.isNumeric(childJobIdItem)) {
// parse child
int childJobId = Integer.parseInt(childJobIdItem);
if (childJobId == jobInfo.getId()) {
return Response.ofFail((I18nUtil.getString("jobinfo_field_childJobId") + "(" + childJobId + ")" + I18nUtil.getString("system_invalid")));
}
// valid child
XxlJobInfo childJobInfo = xxlJobInfoMapper.loadById(childJobId);
if (childJobInfo == null) {
return Response.ofFail(
MessageFormat.format((I18nUtil.getString("jobinfo_field_childJobId") + "({0})" + I18nUtil.getString("system_not_found")), childJobIdItem));
}
// valid jobGroup permission
if (!JobGroupPermissionUtil.hasJobGroupPermission(loginInfo, childJobInfo.getJobGroup())) {
return Response.ofFail(
MessageFormat.format((I18nUtil.getString("jobinfo_field_childJobId") + "({0})" + I18nUtil.getString("system_permission_limit")), childJobIdItem));
}
} else {
return Response.ofFail(
MessageFormat.format((I18nUtil.getString("jobinfo_field_childJobId") + "({0})" + I18nUtil.getString("system_invalid")), childJobIdItem));
}
}
// join , avoid "xxx,,"
String temp = "";
for (String item : childJobIds) {
temp += item + ",";
}
temp = temp.substring(0, temp.length() - 1);
jobInfo.setChildJobId(temp);
}
// group valid
XxlJobGroup jobGroup = xxlJobGroupMapper.load(jobInfo.getJobGroup());
if (jobGroup == null) {
return Response.ofFail((I18nUtil.getString("jobinfo_field_jobgroup") + I18nUtil.getString("system_invalid")));
}
// stage job info
XxlJobInfo exists_jobInfo = xxlJobInfoMapper.loadById(jobInfo.getId());
if (exists_jobInfo == null) {
return Response.ofFail((I18nUtil.getString("jobinfo_field_id") + I18nUtil.getString("system_not_found")));
}
// next trigger time (5s后生效,避开预读周期)
long nextTriggerTime = exists_jobInfo.getTriggerNextTime();
boolean scheduleDataNotChanged = jobInfo.getScheduleType().equals(exists_jobInfo.getScheduleType())
&& jobInfo.getScheduleConf().equals(exists_jobInfo.getScheduleConf()); // 触发配置如果不变,避免重复计算;
if (exists_jobInfo.getTriggerStatus() == TriggerStatus.RUNNING.getValue() && !scheduleDataNotChanged) {
try {
// generate next trigger time
Date nextValidTime = scheduleTypeEnum.getScheduleType().generateNextTriggerTime(jobInfo, new Date(System.currentTimeMillis() + JobScheduleHelper.PRE_READ_MS));
if (nextValidTime == null) {
return Response.ofFail((I18nUtil.getString("schedule_type") + I18nUtil.getString("system_invalid")));
}
nextTriggerTime = nextValidTime.getTime();
} catch (Exception e) {
logger.error(e.getMessage(), e);
return Response.ofFail((I18nUtil.getString("schedule_type") + I18nUtil.getString("system_invalid")));
}
}
exists_jobInfo.setJobGroup(jobInfo.getJobGroup());
exists_jobInfo.setJobDesc(jobInfo.getJobDesc());
exists_jobInfo.setAuthor(jobInfo.getAuthor());
exists_jobInfo.setAlarmEmail(jobInfo.getAlarmEmail());
exists_jobInfo.setScheduleType(jobInfo.getScheduleType());
exists_jobInfo.setScheduleConf(jobInfo.getScheduleConf());
exists_jobInfo.setMisfireStrategy(jobInfo.getMisfireStrategy());
exists_jobInfo.setExecutorRouteStrategy(jobInfo.getExecutorRouteStrategy());
// remove the whitespace
exists_jobInfo.setExecutorHandler(jobInfo.getExecutorHandler().trim());
exists_jobInfo.setExecutorParam(jobInfo.getExecutorParam());
exists_jobInfo.setExecutorBlockStrategy(jobInfo.getExecutorBlockStrategy());
exists_jobInfo.setExecutorTimeout(jobInfo.getExecutorTimeout());
exists_jobInfo.setExecutorFailRetryCount(jobInfo.getExecutorFailRetryCount());
exists_jobInfo.setChildJobId(jobInfo.getChildJobId());
exists_jobInfo.setTriggerNextTime(nextTriggerTime);
exists_jobInfo.setUpdateTime(new Date());
xxlJobInfoMapper.update(exists_jobInfo);
// write operation log
logger.info(">>>>>>>>>>> xxl-job operation log: operator = {}, type = {}, content = {}",
loginInfo.getUserName(), "jobinfo-update", GsonTool.toJson(exists_jobInfo));
return Response.ofSuccess();
}
@Override
public Response<String> remove(int id, LoginInfo loginInfo) {
// valid job
XxlJobInfo xxlJobInfo = xxlJobInfoMapper.loadById(id);
if (xxlJobInfo == null) {
return Response.ofSuccess();
}
// valid jobGroup permission
if (!JobGroupPermissionUtil.hasJobGroupPermission(loginInfo, xxlJobInfo.getJobGroup())) {
return Response.ofFail(I18nUtil.getString("system_permission_limit"));
}
xxlJobInfoMapper.delete(id);
xxlJobLogMapper.delete(id);
xxlJobLogGlueMapper.deleteByJobId(id);
// write operation log
logger.info(">>>>>>>>>>> xxl-job operation log: operator = {}, type = {}, content = {}",
loginInfo.getUserName(), "jobinfo-remove", id);
return Response.ofSuccess();
}
@Override
public Response<String> start(int id, LoginInfo loginInfo) {
// load and valid
XxlJobInfo xxlJobInfo = xxlJobInfoMapper.loadById(id);
if (xxlJobInfo == null) {
return Response.ofFail(I18nUtil.getString("jobinfo_glue_jobid_invalid"));
}
// valid jobGroup permission
if (!JobGroupPermissionUtil.hasJobGroupPermission(loginInfo, xxlJobInfo.getJobGroup())) {
return Response.ofFail(I18nUtil.getString("system_permission_limit"));
}
// valid ScheduleType: can not be none
ScheduleTypeEnum scheduleTypeEnum = ScheduleTypeEnum.match(xxlJobInfo.getScheduleType(), ScheduleTypeEnum.NONE);
if (ScheduleTypeEnum.NONE == scheduleTypeEnum) {
return Response.ofFail(I18nUtil.getString("schedule_type_none_limit_start"));
}
// next trigger time (5s后生效,避开预读周期)
long nextTriggerTime = 0;
try {
// generate next trigger time
Date nextValidTime = scheduleTypeEnum.getScheduleType().generateNextTriggerTime(xxlJobInfo, new Date(System.currentTimeMillis() + JobScheduleHelper.PRE_READ_MS));
if (nextValidTime == null) {
return Response.ofFail((I18nUtil.getString("schedule_type") + I18nUtil.getString("system_invalid")));
}
nextTriggerTime = nextValidTime.getTime();
} catch (Exception e) {
logger.error(e.getMessage(), e);
return Response.ofFail((I18nUtil.getString("schedule_type") + I18nUtil.getString("system_invalid")));
}
xxlJobInfo.setTriggerStatus(TriggerStatus.RUNNING.getValue());
xxlJobInfo.setTriggerLastTime(0);
xxlJobInfo.setTriggerNextTime(nextTriggerTime);
xxlJobInfo.setUpdateTime(new Date());
xxlJobInfoMapper.update(xxlJobInfo);
// write operation log
logger.info(">>>>>>>>>>> xxl-job operation log: operator = {}, type = {}, content = {}",
loginInfo.getUserName(), "jobinfo-start", id);
return Response.ofSuccess();
}
@Override
public Response<String> stop(int id, LoginInfo loginInfo) {
// load and valid
XxlJobInfo xxlJobInfo = xxlJobInfoMapper.loadById(id);
if (xxlJobInfo == null) {
return Response.ofFail(I18nUtil.getString("jobinfo_glue_jobid_invalid"));
}
// valid jobGroup permission
if (!JobGroupPermissionUtil.hasJobGroupPermission(loginInfo, xxlJobInfo.getJobGroup())) {
return Response.ofFail(I18nUtil.getString("system_permission_limit"));
}
// stop
xxlJobInfo.setTriggerStatus(TriggerStatus.STOPPED.getValue());
xxlJobInfo.setTriggerLastTime(0);
xxlJobInfo.setTriggerNextTime(0);
xxlJobInfo.setUpdateTime(new Date());
xxlJobInfoMapper.update(xxlJobInfo);
// write operation log
logger.info(">>>>>>>>>>> xxl-job operation log: operator = {}, type = {}, content = {}",
loginInfo.getUserName(), "jobinfo-stop", id);
return Response.ofSuccess();
}
@Override
public Response<String> trigger(LoginInfo loginInfo, int jobId, String executorParam, String addressList) {
// valid job
XxlJobInfo xxlJobInfo = xxlJobInfoMapper.loadById(jobId);
if (xxlJobInfo == null) {
return Response.ofFail(I18nUtil.getString("jobinfo_glue_jobid_invalid"));
}
// valid jobGroup permission
if (!JobGroupPermissionUtil.hasJobGroupPermission(loginInfo, xxlJobInfo.getJobGroup())) {
return Response.ofFail(I18nUtil.getString("system_permission_limit"));
}
// force cover job param
if (executorParam == null) {
executorParam = "";
}
XxlJobAdminBootstrap.getInstance().getJobTriggerPoolHelper().trigger(jobId, TriggerTypeEnum.MANUAL, -1, null, executorParam, addressList);
// write operation log
logger.info(">>>>>>>>>>> xxl-job operation log: operator = {}, type = {}, content = {}",
loginInfo.getUserName(), "jobinfo-trigger", jobId);
return Response.ofSuccess();
}
@Override
public Map<String, Object> dashboardInfo() {
int jobInfoCount = xxlJobInfoMapper.findAllCount();
int jobLogCount = 0;
int jobLogSuccessCount = 0;
XxlJobLogReport xxlJobLogReport = xxlJobLogReportMapper.queryLogReportTotal();
if (xxlJobLogReport != null) {
jobLogCount = xxlJobLogReport.getRunningCount() + xxlJobLogReport.getSucCount() + xxlJobLogReport.getFailCount();
jobLogSuccessCount = xxlJobLogReport.getSucCount();
}
// executor count
Set<String> executorAddressSet = new HashSet<String>();
List<XxlJobGroup> groupList = xxlJobGroupMapper.findAll();
if (groupList != null && !groupList.isEmpty()) {
for (XxlJobGroup group : groupList) {
if (group.getRegistryList() != null && !group.getRegistryList().isEmpty()) {
executorAddressSet.addAll(group.getRegistryList());
}
}
}
int executorCount = executorAddressSet.size();
Map<String, Object> dashboardMap = new HashMap<String, Object>();
dashboardMap.put("jobInfoCount", jobInfoCount);
dashboardMap.put("jobLogCount", jobLogCount);
dashboardMap.put("jobLogSuccessCount", jobLogSuccessCount);
dashboardMap.put("executorCount", executorCount);
return dashboardMap;
}
@Override
public Response<Map<String, Object>> chartInfo(Date startDate, Date endDate) {
// process
List<String> triggerDayList = new ArrayList<String>();
List<Integer> triggerDayCountRunningList = new ArrayList<Integer>();
List<Integer> triggerDayCountSucList = new ArrayList<Integer>();
List<Integer> triggerDayCountFailList = new ArrayList<Integer>();
int triggerCountRunningTotal = 0;
int triggerCountSucTotal = 0;
int triggerCountFailTotal = 0;
List<XxlJobLogReport> logReportList = xxlJobLogReportMapper.queryLogReport(startDate, endDate);
if (logReportList != null && !logReportList.isEmpty()) {
for (XxlJobLogReport item : logReportList) {
String day = DateTool.formatDate(item.getTriggerDay());
int triggerDayCountRunning = item.getRunningCount();
int triggerDayCountSuc = item.getSucCount();
int triggerDayCountFail = item.getFailCount();
triggerDayList.add(day);
triggerDayCountRunningList.add(triggerDayCountRunning);
triggerDayCountSucList.add(triggerDayCountSuc);
triggerDayCountFailList.add(triggerDayCountFail);
triggerCountRunningTotal += triggerDayCountRunning;
triggerCountSucTotal += triggerDayCountSuc;
triggerCountFailTotal += triggerDayCountFail;
}
} else {
for (int i = -6; i <= 0; i++) {
triggerDayList.add(DateTool.formatDate(DateTool.addDays(new Date(), i)));
triggerDayCountRunningList.add(0);
triggerDayCountSucList.add(0);
triggerDayCountFailList.add(0);
}
}
Map<String, Object> result = new HashMap<String, Object>();
result.put("triggerDayList", triggerDayList);
result.put("triggerDayCountRunningList", triggerDayCountRunningList);
result.put("triggerDayCountSucList", triggerDayCountSucList);
result.put("triggerDayCountFailList", triggerDayCountFailList);
result.put("triggerCountRunningTotal", triggerCountRunningTotal);
result.put("triggerCountSucTotal", triggerCountSucTotal);
result.put("triggerCountFailTotal", triggerCountFailTotal);
return Response.ofSuccess(result);
}
}
@@ -1,65 +0,0 @@
package com.xxl.job.admin.web.error;
import com.xxl.job.admin.scheduler.exception.XxlJobException;
import com.xxl.tool.json.GsonTool;
import com.xxl.tool.response.Response;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.method.HandlerMethod;
import org.springframework.web.servlet.HandlerExceptionResolver;
import org.springframework.web.servlet.ModelAndView;
import java.io.IOException;
/**
* common exception resolver
*
* @author xuxueli 2016-1-6 19:22:18
*/
@Component
public class WebHandlerExceptionResolver implements HandlerExceptionResolver {
private static transient Logger logger = LoggerFactory.getLogger(WebHandlerExceptionResolver.class);
@Override
public ModelAndView resolveException(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) {
if (!(ex instanceof XxlJobException)) {
logger.error("WebExceptionResolver:{}", ex);
}
// parse isJson
boolean isJson = false;
if (handler instanceof HandlerMethod) {
HandlerMethod method = (HandlerMethod) handler;
isJson = method.getMethodAnnotation(ResponseBody.class) != null;
}
// process error
ModelAndView mv = new ModelAndView();
if (isJson) {
try {
// errorMsg
String errorMsg = GsonTool.toJson(Response.ofFail(ex.toString()));
// write response
response.setStatus(HttpServletResponse.SC_OK);
response.setContentType("application/json;charset=UTF-8");
response.getWriter().println(errorMsg);
} catch (IOException e) {
logger.error(e.getMessage(), e);
}
return mv;
} else {
mv.addObject("exceptionMsg", ex.toString());
mv.setViewName("common/common.errorpage");
return mv;
}
}
}
@@ -1,8 +1,6 @@
# 此配置只适合开发测试环境,详细配置参考: http://t.cn/A64RaHJm
server:
port: 9080
servlet:
context-path: /xxl-job-admin
spring:
application:
@@ -14,10 +12,10 @@ spring:
discovery:
server-addr: ${NACOS_HOST:pigx-register}:${NACOS_PORT:8848}
metadata:
management.context-path: ${server.servlet.context-path}/actuator
management.context-path: /actuator
config:
server-addr: ${spring.cloud.nacos.discovery.server-addr}
config:
import:
- optional:nacos:application-@profiles.active@.yml
- optional:nacos:${spring.application.name}-@profiles.active@.yml
- optional:nacos:${spring.application.name}-@profiles.active@.yml
@@ -1,6 +1,6 @@
admin_name=Scheduling Center
admin_name_full=Distributed Task Scheduling Platform\uFF5CXXL-JOB
admin_version=3.4.1-SNAPSHOT
admin_version=3.4.1
admin_i18n=en
## system
@@ -1,6 +1,6 @@
admin_name=\u4EFB\u52A1\u8C03\u5EA6\u4E2D\u5FC3
admin_name_full=\u5206\u5E03\u5F0F\u4EFB\u52A1\u8C03\u5EA6\u5E73\u53F0\uFF5CXXL-JOB
admin_version=3.4.1-SNAPSHOT
admin_version=3.4.1
admin_i18n=
## system
@@ -1,6 +1,6 @@
admin_name=\u4EFB\u52D9\u8ABF\u5EA6\u4E2D\u5FC3
admin_name_full=\u5206\u5E03\u5F0F\u4EFB\u52D9\u8ABF\u5EA6\u5E73\u81FA\uFF5CXXL-JOB
admin_version=3.4.1-SNAPSHOT
admin_version=3.4.1
admin_i18n=
## system
@@ -1,102 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.xxl.job.admin.mapper.XxlJobUserMapper">
<resultMap id="XxlJobUser" type="com.xxl.job.admin.model.XxlJobUser">
<result column="id" property="id" />
<result column="username" property="username" />
<result column="password" property="password" />
<result column="token" property="token"/>
<result column="role" property="role" />
<result column="permission" property="permission" />
</resultMap>
<sql id="Base_Column_List">
t.id,
t.username,
t.password,
t.token,
t.role,
t.permission
</sql>
<select id="pageList" parameterType="java.util.HashMap" resultMap="XxlJobUser">
SELECT <include refid="Base_Column_List" />
FROM xxl_job_user AS t
<trim prefix="WHERE" prefixOverrides="AND | OR" >
<if test="username != null and username != ''">
AND t.username like CONCAT(CONCAT('%', #{username}), '%')
</if>
<if test="role gt -1">
AND t.role = #{role}
</if>
</trim>
ORDER BY username ASC
LIMIT #{offset}, #{pagesize}
</select>
<select id="pageListCount" parameterType="java.util.HashMap" resultType="int">
SELECT count(1)
FROM xxl_job_user AS t
<trim prefix="WHERE" prefixOverrides="AND | OR" >
<if test="username != null and username != ''">
AND t.username like CONCAT(CONCAT('%', #{username}), '%')
</if>
<if test="role gt -1">
AND t.role = #{role}
</if>
</trim>
</select>
<select id="loadByUserName" parameterType="java.util.HashMap" resultMap="XxlJobUser">
SELECT <include refid="Base_Column_List" />
FROM xxl_job_user AS t
WHERE t.username = #{username}
</select>
<select id="loadById" parameterType="java.util.HashMap" resultMap="XxlJobUser">
SELECT
<include refid="Base_Column_List"/>
FROM xxl_job_user AS t
WHERE t.id = #{id}
</select>
<insert id="save" parameterType="com.xxl.job.admin.model.XxlJobUser" useGeneratedKeys="true" keyProperty="id">
INSERT INTO xxl_job_user (
username,
password,
role,
permission
) VALUES (
#{username},
#{password},
#{role},
#{permission}
);
</insert>
<update id="update" parameterType="com.xxl.job.admin.model.XxlJobUser">
UPDATE xxl_job_user
SET
<if test="password != null and password != ''">
password = #{password},
</if>
role = #{role},
permission = #{permission}
WHERE id = #{id}
</update>
<delete id="delete" parameterType="java.util.HashMap">
DELETE
FROM xxl_job_user
WHERE id = #{id}
</delete>
<update id="updateToken" parameterType="java.util.HashMap">
UPDATE xxl_job_user
SET token = #{token}
WHERE id = #{id}
</update>
</mapper>
@@ -1,9 +1,9 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.xxl.job.admin.mapper.XxlJobGroupMapper">
<mapper namespace="com.xxl.job.admin.business.mapper.XxlJobGroupMapper">
<resultMap id="XxlJobGroup" type="com.xxl.job.admin.model.XxlJobGroup">
<resultMap id="XxlJobGroup" type="com.xxl.job.admin.business.model.XxlJobGroup">
<result column="id" property="id"/>
<result column="app_name" property="appname"/>
<result column="title" property="title"/>
@@ -36,12 +36,13 @@
ORDER BY t.app_name, t.title, t.id ASC
</select>
<insert id="save" parameterType="com.xxl.job.admin.model.XxlJobGroup" useGeneratedKeys="true" keyProperty="id">
<insert id="save" parameterType="com.xxl.job.admin.business.model.XxlJobGroup" useGeneratedKeys="true"
keyProperty="id">
INSERT INTO xxl_job_group ( `app_name`, `title`, `address_type`, `address_list`, `update_time`)
values ( #{appname}, #{title}, #{addressType}, #{addressList}, #{updateTime} );
</insert>
<update id="update" parameterType="com.xxl.job.admin.model.XxlJobGroup">
<update id="update" parameterType="com.xxl.job.admin.business.model.XxlJobGroup">
UPDATE xxl_job_group
SET `app_name` = #{appname},
`title` = #{title},
Loaded 100 of 126 files, more files were not shown because too many files have changed in this diff. Show more