refactor(xxl-job): replace xxl-job-admin for boot4

This commit is contained in:
PIGCLOUD committed 2026-05-25 17:31:39 +08:00
1 parent 3132cc3784
commit 124a2547dd
261 files changed
+20881 -13050

No files matched your search

@@ -13,4 +13,4 @@ public class PigxJobAdminApplication {
SpringApplication.run(PigxJobAdminApplication.class, args);
}
}
}
@@ -0,0 +1,7 @@
package com.xxl.job.admin.constant;
public class Consts {
public static final String ADMIN_ROLE = "ADMIN";
}
@@ -0,0 +1,32 @@
package com.xxl.job.admin.constant;
public enum TriggerStatus {
STOPPED(0, "stopped"),
RUNNING(1, "running");
private int value;
private String desc;
TriggerStatus(int value, String desc) {
this.value = value;
this.desc = desc;
}
public int getValue() {
return value;
}
public void setValue(int value) {
this.value = value;
}
public String getDesc() {
return desc;
}
public void setDesc(String desc) {
this.desc = desc;
}
}
@@ -1,99 +0,0 @@
package com.xxl.job.admin.controller;
import com.xxl.job.admin.controller.annotation.PermissionLimit;
import com.xxl.job.admin.service.XxlJobService;
import com.xxl.job.admin.service.impl.LoginService;
import com.xxl.job.core.biz.model.ReturnT;
import jakarta.annotation.Resource;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import org.springframework.beans.propertyeditors.CustomDateEditor;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.WebDataBinder;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.servlet.ModelAndView;
import org.springframework.web.servlet.view.RedirectView;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Map;
/**
* index controller
*
* @author xuxueli 2015-12-19 16:13:16
*/
@Controller
public class IndexController {
@Resource
private XxlJobService xxlJobService;
@Resource
private LoginService loginService;
@RequestMapping("/")
public String index(Model model) {
Map<String, Object> dashboardMap = xxlJobService.dashboardInfo();
model.addAllAttributes(dashboardMap);
return "index";
}
@RequestMapping("/chartInfo")
@ResponseBody
public ReturnT<Map<String, Object>> chartInfo(@RequestParam("startDate") Date startDate,
@RequestParam("endDate") Date endDate) {
ReturnT<Map<String, Object>> chartInfo = xxlJobService.chartInfo(startDate, endDate);
return chartInfo;
}
@RequestMapping("/toLogin")
@PermissionLimit(limit = false)
public ModelAndView toLogin(HttpServletRequest request, HttpServletResponse response, ModelAndView modelAndView) {
if (loginService.ifLogin(request, response) != null) {
modelAndView.setView(new RedirectView("/", true, false));
return modelAndView;
}
return new ModelAndView("login");
}
@RequestMapping(value = "login", method = RequestMethod.POST)
@ResponseBody
@PermissionLimit(limit = false)
public ReturnT<String> loginDo(HttpServletRequest request, HttpServletResponse response,
@RequestParam("userName") String userName, @RequestParam("password") String password,
@RequestParam(value = "ifRemember", required = false) String ifRemember) {
boolean ifRem = (ifRemember != null && ifRemember.trim().length() > 0 && "on".equals(ifRemember)) ? true
: false;
return loginService.login(request, response, userName, password, ifRem);
}
@RequestMapping(value = "logout", method = RequestMethod.POST)
@ResponseBody
@PermissionLimit(limit = false)
public ReturnT<String> logout(HttpServletRequest request, HttpServletResponse response) {
return loginService.logout(request, response);
}
@RequestMapping("/help")
public String help() {
/*
* if (!PermissionInterceptor.ifLogin(request)) { return "redirect:/toLogin"; }
*/
return "help";
}
@InitBinder
public void initBinder(WebDataBinder binder) {
SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
dateFormat.setLenient(false);
binder.registerCustomEditor(Date.class, new CustomDateEditor(dateFormat, true));
}
}
@@ -1,78 +0,0 @@
package com.xxl.job.admin.controller;
import com.xxl.job.admin.controller.annotation.PermissionLimit;
import com.xxl.job.admin.core.conf.XxlJobAdminConfig;
import com.xxl.job.core.biz.AdminBiz;
import com.xxl.job.core.biz.model.HandleCallbackParam;
import com.xxl.job.core.biz.model.RegistryParam;
import com.xxl.job.core.biz.model.ReturnT;
import com.xxl.job.core.util.GsonTool;
import com.xxl.job.core.util.XxlJobRemotingUtil;
import jakarta.annotation.Resource;
import jakarta.servlet.http.HttpServletRequest;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;
import java.util.List;
/**
* Created by xuxueli on 17/5/10.
*/
@Controller
@RequestMapping("/api")
public class JobApiController {
@Resource
private AdminBiz adminBiz;
/**
* api
* @param uri
* @param data
* @return
*/
@RequestMapping("/{uri}")
@ResponseBody
@PermissionLimit(limit = false)
public ReturnT<String> api(HttpServletRequest request, @PathVariable("uri") String uri,
@RequestBody(required = false) String data) {
// valid
if (!"POST".equalsIgnoreCase(request.getMethod())) {
return new ReturnT<String>(ReturnT.FAIL_CODE, "invalid request, HttpMethod not support.");
}
if (uri == null || uri.trim().length() == 0) {
return new ReturnT<String>(ReturnT.FAIL_CODE, "invalid request, uri-mapping empty.");
}
if (XxlJobAdminConfig.getAdminConfig().getAccessToken() != null
&& XxlJobAdminConfig.getAdminConfig().getAccessToken().trim().length() > 0
&& !XxlJobAdminConfig.getAdminConfig()
.getAccessToken()
.equals(request.getHeader(XxlJobRemotingUtil.XXL_JOB_ACCESS_TOKEN))) {
return new ReturnT<String>(ReturnT.FAIL_CODE, "The access token is wrong.");
}
// services mapping
if ("callback".equals(uri)) {
List<HandleCallbackParam> callbackParamList = GsonTool.fromJson(data, List.class,
HandleCallbackParam.class);
return adminBiz.callback(callbackParamList);
}
else if ("registry".equals(uri)) {
RegistryParam registryParam = GsonTool.fromJson(data, RegistryParam.class);
return adminBiz.registry(registryParam);
}
else if ("registryRemove".equals(uri)) {
RegistryParam registryParam = GsonTool.fromJson(data, RegistryParam.class);
return adminBiz.registryRemove(registryParam);
}
else {
return new ReturnT<String>(ReturnT.FAIL_CODE, "invalid request, uri-mapping(" + uri + ") not found.");
}
}
}
@@ -1,106 +0,0 @@
package com.xxl.job.admin.controller;
import com.xxl.job.admin.controller.interceptor.PermissionInterceptor;
import com.xxl.job.admin.core.model.XxlJobInfo;
import com.xxl.job.admin.core.model.XxlJobLogGlue;
import com.xxl.job.admin.core.util.I18nUtil;
import com.xxl.job.admin.dao.XxlJobInfoDao;
import com.xxl.job.admin.dao.XxlJobLogGlueDao;
import com.xxl.job.core.biz.model.ReturnT;
import com.xxl.job.core.glue.GlueTypeEnum;
import jakarta.annotation.Resource;
import jakarta.servlet.http.HttpServletRequest;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.ResponseBody;
import java.util.Date;
import java.util.List;
/**
* job code controller
*
* @author xuxueli 2015-12-19 16:13:16
*/
@Controller
@RequestMapping("/jobcode")
public class JobCodeController {
@Resource
private XxlJobInfoDao xxlJobInfoDao;
@Resource
private XxlJobLogGlueDao xxlJobLogGlueDao;
@RequestMapping
public String index(HttpServletRequest request, Model model, @RequestParam("jobId") int jobId) {
XxlJobInfo jobInfo = xxlJobInfoDao.loadById(jobId);
List<XxlJobLogGlue> jobLogGlues = xxlJobLogGlueDao.findByJobId(jobId);
if (jobInfo == null) {
throw new RuntimeException(I18nUtil.getString("jobinfo_glue_jobid_unvalid"));
}
if (GlueTypeEnum.BEAN == GlueTypeEnum.match(jobInfo.getGlueType())) {
throw new RuntimeException(I18nUtil.getString("jobinfo_glue_gluetype_unvalid"));
}
// valid permission
PermissionInterceptor.validJobGroupPermission(request, jobInfo.getJobGroup());
// Glue类型-字典
model.addAttribute("GlueTypeEnum", GlueTypeEnum.values());
model.addAttribute("jobInfo", jobInfo);
model.addAttribute("jobLogGlues", jobLogGlues);
return "jobcode/jobcode.index";
}
@RequestMapping("/save")
@ResponseBody
public ReturnT<String> save(HttpServletRequest request, @RequestParam("id") int id,
@RequestParam("glueSource") String glueSource, @RequestParam("glueRemark") String glueRemark) {
// valid
if (glueRemark == null) {
return new ReturnT<String>(500,
(I18nUtil.getString("system_please_input") + I18nUtil.getString("jobinfo_glue_remark")));
}
if (glueRemark.length() < 4 || glueRemark.length() > 100) {
return new ReturnT<String>(500, I18nUtil.getString("jobinfo_glue_remark_limit"));
}
XxlJobInfo existsJobInfo = xxlJobInfoDao.loadById(id);
if (existsJobInfo == null) {
return new ReturnT<String>(500, I18nUtil.getString("jobinfo_glue_jobid_unvalid"));
}
// valid permission
PermissionInterceptor.validJobGroupPermission(request, existsJobInfo.getJobGroup());
// update new code
existsJobInfo.setGlueSource(glueSource);
existsJobInfo.setGlueRemark(glueRemark);
existsJobInfo.setGlueUpdatetime(new Date());
existsJobInfo.setUpdateTime(new Date());
xxlJobInfoDao.update(existsJobInfo);
// log old code
XxlJobLogGlue xxlJobLogGlue = new XxlJobLogGlue();
xxlJobLogGlue.setJobId(existsJobInfo.getId());
xxlJobLogGlue.setGlueType(existsJobInfo.getGlueType());
xxlJobLogGlue.setGlueSource(glueSource);
xxlJobLogGlue.setGlueRemark(glueRemark);
xxlJobLogGlue.setAddTime(new Date());
xxlJobLogGlue.setUpdateTime(new Date());
xxlJobLogGlueDao.save(xxlJobLogGlue);
// remove code backup more than 30
xxlJobLogGlueDao.removeOld(existsJobInfo.getId(), 30);
return ReturnT.SUCCESS;
}
}
@@ -1,213 +0,0 @@
package com.xxl.job.admin.controller;
import com.xxl.job.admin.controller.annotation.PermissionLimit;
import com.xxl.job.admin.core.model.XxlJobGroup;
import com.xxl.job.admin.core.model.XxlJobRegistry;
import com.xxl.job.admin.core.util.I18nUtil;
import com.xxl.job.admin.dao.XxlJobGroupDao;
import com.xxl.job.admin.dao.XxlJobInfoDao;
import com.xxl.job.admin.dao.XxlJobRegistryDao;
import com.xxl.job.core.biz.model.ReturnT;
import com.xxl.job.core.enums.RegistryConfig;
import jakarta.annotation.Resource;
import jakarta.servlet.http.HttpServletRequest;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.ResponseBody;
import java.util.*;
/**
* job group controller
*
* @author xuxueli 2016-10-02 20:52:56
*/
@Controller
@RequestMapping("/jobgroup")
public class JobGroupController {
@Resource
public XxlJobInfoDao xxlJobInfoDao;
@Resource
public XxlJobGroupDao xxlJobGroupDao;
@Resource
private XxlJobRegistryDao xxlJobRegistryDao;
@RequestMapping
@PermissionLimit(adminuser = true)
public String index(Model model) {
return "jobgroup/jobgroup.index";
}
@RequestMapping("/pageList")
@ResponseBody
@PermissionLimit(adminuser = true)
public Map<String, Object> pageList(HttpServletRequest request,
@RequestParam(value = "start", required = false, defaultValue = "0") int start,
@RequestParam(value = "length", required = false, defaultValue = "10") int length,
@RequestParam("appname") String appname, @RequestParam("title") String title) {
// page query
List<XxlJobGroup> list = xxlJobGroupDao.pageList(start, length, appname, title);
int list_count = xxlJobGroupDao.pageListCount(start, length, appname, title);
// package result
Map<String, Object> maps = new HashMap<String, Object>();
maps.put("recordsTotal", list_count); // 总记录数
maps.put("recordsFiltered", list_count); // 过滤后的总记录数
maps.put("data", list); // 分页列表
return maps;
}
@RequestMapping("/save")
@ResponseBody
@PermissionLimit(adminuser = true)
public ReturnT<String> save(XxlJobGroup xxlJobGroup) {
// valid
if (xxlJobGroup.getAppname() == null || xxlJobGroup.getAppname().trim().length() == 0) {
return new ReturnT<String>(500, (I18nUtil.getString("system_please_input") + "AppName"));
}
if (xxlJobGroup.getAppname().length() < 4 || xxlJobGroup.getAppname().length() > 64) {
return new ReturnT<String>(500, I18nUtil.getString("jobgroup_field_appname_length"));
}
if (xxlJobGroup.getAppname().contains(">") || xxlJobGroup.getAppname().contains("<")) {
return new ReturnT<String>(500, "AppName" + I18nUtil.getString("system_unvalid"));
}
if (xxlJobGroup.getTitle() == null || xxlJobGroup.getTitle().trim().length() == 0) {
return new ReturnT<String>(500,
(I18nUtil.getString("system_please_input") + I18nUtil.getString("jobgroup_field_title")));
}
if (xxlJobGroup.getTitle().contains(">") || xxlJobGroup.getTitle().contains("<")) {
return new ReturnT<String>(500,
I18nUtil.getString("jobgroup_field_title") + I18nUtil.getString("system_unvalid"));
}
if (xxlJobGroup.getAddressType() != 0) {
if (xxlJobGroup.getAddressList() == null || xxlJobGroup.getAddressList().trim().length() == 0) {
return new ReturnT<String>(500, I18nUtil.getString("jobgroup_field_addressType_limit"));
}
if (xxlJobGroup.getAddressList().contains(">") || xxlJobGroup.getAddressList().contains("<")) {
return new ReturnT<String>(500,
I18nUtil.getString("jobgroup_field_registryList") + I18nUtil.getString("system_unvalid"));
}
String[] addresss = xxlJobGroup.getAddressList().split(",");
for (String item : addresss) {
if (item == null || item.trim().length() == 0) {
return new ReturnT<String>(500, I18nUtil.getString("jobgroup_field_registryList_unvalid"));
}
}
}
// process
xxlJobGroup.setUpdateTime(new Date());
int ret = xxlJobGroupDao.save(xxlJobGroup);
return (ret > 0) ? ReturnT.SUCCESS : ReturnT.FAIL;
}
@RequestMapping("/update")
@ResponseBody
@PermissionLimit(adminuser = true)
public ReturnT<String> update(XxlJobGroup xxlJobGroup) {
// valid
if (xxlJobGroup.getAppname() == null || xxlJobGroup.getAppname().trim().length() == 0) {
return new ReturnT<String>(500, (I18nUtil.getString("system_please_input") + "AppName"));
}
if (xxlJobGroup.getAppname().length() < 4 || xxlJobGroup.getAppname().length() > 64) {
return new ReturnT<String>(500, I18nUtil.getString("jobgroup_field_appname_length"));
}
if (xxlJobGroup.getTitle() == null || xxlJobGroup.getTitle().trim().length() == 0) {
return new ReturnT<String>(500,
(I18nUtil.getString("system_please_input") + I18nUtil.getString("jobgroup_field_title")));
}
if (xxlJobGroup.getAddressType() == 0) {
// 0=自动注册
List<String> registryList = findRegistryByAppName(xxlJobGroup.getAppname());
String addressListStr = null;
if (registryList != null && !registryList.isEmpty()) {
Collections.sort(registryList);
addressListStr = "";
for (String item : registryList) {
addressListStr += item + ",";
}
addressListStr = addressListStr.substring(0, addressListStr.length() - 1);
}
xxlJobGroup.setAddressList(addressListStr);
}
else {
// 1=手动录入
if (xxlJobGroup.getAddressList() == null || xxlJobGroup.getAddressList().trim().length() == 0) {
return new ReturnT<String>(500, I18nUtil.getString("jobgroup_field_addressType_limit"));
}
String[] addresss = xxlJobGroup.getAddressList().split(",");
for (String item : addresss) {
if (item == null || item.trim().length() == 0) {
return new ReturnT<String>(500, I18nUtil.getString("jobgroup_field_registryList_unvalid"));
}
}
}
// process
xxlJobGroup.setUpdateTime(new Date());
int ret = xxlJobGroupDao.update(xxlJobGroup);
return (ret > 0) ? ReturnT.SUCCESS : ReturnT.FAIL;
}
private List<String> findRegistryByAppName(String appnameParam) {
HashMap<String, List<String>> appAddressMap = new HashMap<String, List<String>>();
List<XxlJobRegistry> list = xxlJobRegistryDao.findAll(RegistryConfig.DEAD_TIMEOUT, new Date());
if (list != null) {
for (XxlJobRegistry item : list) {
if (RegistryConfig.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);
}
}
}
return appAddressMap.get(appnameParam);
}
@RequestMapping("/remove")
@ResponseBody
@PermissionLimit(adminuser = true)
public ReturnT<String> remove(@RequestParam("id") int id) {
// valid
int count = xxlJobInfoDao.pageListCount(0, 10, id, -1, null, null, null);
if (count > 0) {
return new ReturnT<String>(500, I18nUtil.getString("jobgroup_del_limit_0"));
}
List<XxlJobGroup> allList = xxlJobGroupDao.findAll();
if (allList.size() == 1) {
return new ReturnT<String>(500, I18nUtil.getString("jobgroup_del_limit_1"));
}
int ret = xxlJobGroupDao.remove(id);
return (ret > 0) ? ReturnT.SUCCESS : ReturnT.FAIL;
}
@RequestMapping("/loadById")
@ResponseBody
@PermissionLimit(adminuser = true)
public ReturnT<XxlJobGroup> loadById(@RequestParam("id") int id) {
XxlJobGroup jobGroup = xxlJobGroupDao.load(id);
return jobGroup != null ? new ReturnT<XxlJobGroup>(jobGroup)
: new ReturnT<XxlJobGroup>(ReturnT.FAIL_CODE, null);
}
}
@@ -1,170 +0,0 @@
package com.xxl.job.admin.controller;
import com.xxl.job.admin.controller.interceptor.PermissionInterceptor;
import com.xxl.job.admin.core.exception.XxlJobException;
import com.xxl.job.admin.core.model.XxlJobGroup;
import com.xxl.job.admin.core.model.XxlJobInfo;
import com.xxl.job.admin.core.model.XxlJobUser;
import com.xxl.job.admin.core.route.ExecutorRouteStrategyEnum;
import com.xxl.job.admin.core.scheduler.MisfireStrategyEnum;
import com.xxl.job.admin.core.scheduler.ScheduleTypeEnum;
import com.xxl.job.admin.core.thread.JobScheduleHelper;
import com.xxl.job.admin.core.util.I18nUtil;
import com.xxl.job.admin.dao.XxlJobGroupDao;
import com.xxl.job.admin.service.XxlJobService;
import com.xxl.job.core.biz.model.ReturnT;
import com.xxl.job.core.enums.ExecutorBlockStrategyEnum;
import com.xxl.job.core.glue.GlueTypeEnum;
import com.xxl.job.core.util.DateUtil;
import jakarta.annotation.Resource;
import jakarta.servlet.http.HttpServletRequest;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.ResponseBody;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.Map;
/**
* index controller
*
* @author xuxueli 2015-12-19 16:13:16
*/
@Controller
@RequestMapping("/jobinfo")
public class JobInfoController {
private static Logger logger = LoggerFactory.getLogger(JobInfoController.class);
@Resource
private XxlJobGroupDao xxlJobGroupDao;
@Resource
private XxlJobService xxlJobService;
@RequestMapping
public String index(HttpServletRequest request, Model model,
@RequestParam(value = "jobGroup", required = false, defaultValue = "-1") int jobGroup) {
// 枚举-字典
model.addAttribute("ExecutorRouteStrategyEnum", ExecutorRouteStrategyEnum.values()); // 路由策略-列表
model.addAttribute("GlueTypeEnum", GlueTypeEnum.values()); // Glue类型-字典
model.addAttribute("ExecutorBlockStrategyEnum", ExecutorBlockStrategyEnum.values()); // 阻塞处理策略-字典
model.addAttribute("ScheduleTypeEnum", ScheduleTypeEnum.values()); // 调度类型
model.addAttribute("MisfireStrategyEnum", MisfireStrategyEnum.values()); // 调度过期策略
// 执行器列表
List<XxlJobGroup> jobGroupList_all = xxlJobGroupDao.findAll();
// filter group
List<XxlJobGroup> jobGroupList = PermissionInterceptor.filterJobGroupByRole(request, jobGroupList_all);
if (jobGroupList == null || jobGroupList.size() == 0) {
throw new XxlJobException(I18nUtil.getString("jobgroup_empty"));
}
model.addAttribute("JobGroupList", jobGroupList);
model.addAttribute("jobGroup", jobGroup);
return "jobinfo/jobinfo.index";
}
@RequestMapping("/pageList")
@ResponseBody
public Map<String, Object> pageList(@RequestParam(value = "start", required = false, defaultValue = "0") int start,
@RequestParam(value = "length", required = false, defaultValue = "10") int length,
@RequestParam("jobGroup") int jobGroup, @RequestParam("triggerStatus") int triggerStatus,
@RequestParam("jobDesc") String jobDesc, @RequestParam("executorHandler") String executorHandler,
@RequestParam("author") String author) {
return xxlJobService.pageList(start, length, jobGroup, triggerStatus, jobDesc, executorHandler, author);
}
@RequestMapping("/add")
@ResponseBody
public ReturnT<String> add(HttpServletRequest request, XxlJobInfo jobInfo) {
// valid permission
PermissionInterceptor.validJobGroupPermission(request, jobInfo.getJobGroup());
// opt
XxlJobUser loginUser = PermissionInterceptor.getLoginUser(request);
return xxlJobService.add(jobInfo, loginUser);
}
@RequestMapping("/update")
@ResponseBody
public ReturnT<String> update(HttpServletRequest request, XxlJobInfo jobInfo) {
// valid permission
PermissionInterceptor.validJobGroupPermission(request, jobInfo.getJobGroup());
// opt
XxlJobUser loginUser = PermissionInterceptor.getLoginUser(request);
return xxlJobService.update(jobInfo, loginUser);
}
@RequestMapping("/remove")
@ResponseBody
public ReturnT<String> remove(@RequestParam("id") int id) {
return xxlJobService.remove(id);
}
@RequestMapping("/stop")
@ResponseBody
public ReturnT<String> pause(@RequestParam("id") int id) {
return xxlJobService.stop(id);
}
@RequestMapping("/start")
@ResponseBody
public ReturnT<String> start(@RequestParam("id") int id) {
return xxlJobService.start(id);
}
@RequestMapping("/trigger")
@ResponseBody
public ReturnT<String> triggerJob(HttpServletRequest request, @RequestParam("id") int id,
@RequestParam("executorParam") String executorParam, @RequestParam("addressList") String addressList) {
// login user
XxlJobUser loginUser = PermissionInterceptor.getLoginUser(request);
// trigger
return xxlJobService.trigger(loginUser, id, executorParam, addressList);
}
@RequestMapping("/nextTriggerTime")
@ResponseBody
public ReturnT<List<String>> nextTriggerTime(@RequestParam("scheduleType") String scheduleType,
@RequestParam("scheduleConf") String scheduleConf) {
XxlJobInfo paramXxlJobInfo = new XxlJobInfo();
paramXxlJobInfo.setScheduleType(scheduleType);
paramXxlJobInfo.setScheduleConf(scheduleConf);
List<String> result = new ArrayList<>();
try {
Date lastTime = new Date();
for (int i = 0; i < 5; i++) {
lastTime = JobScheduleHelper.generateNextValidTime(paramXxlJobInfo, lastTime);
if (lastTime != null) {
result.add(DateUtil.formatDateTime(lastTime));
}
else {
break;
}
}
}
catch (Exception e) {
logger.error("nextTriggerTime error. scheduleType = {}, scheduleConf= {}", scheduleType, scheduleConf, e);
return new ReturnT<List<String>>(ReturnT.FAIL_CODE,
(I18nUtil.getString("schedule_type") + I18nUtil.getString("system_unvalid")) + e.getMessage());
}
return new ReturnT<List<String>>(result);
}
}
@@ -1,312 +0,0 @@
package com.xxl.job.admin.controller;
import com.xxl.job.admin.controller.interceptor.PermissionInterceptor;
import com.xxl.job.admin.core.complete.XxlJobCompleter;
import com.xxl.job.admin.core.exception.XxlJobException;
import com.xxl.job.admin.core.model.XxlJobGroup;
import com.xxl.job.admin.core.model.XxlJobInfo;
import com.xxl.job.admin.core.model.XxlJobLog;
import com.xxl.job.admin.core.scheduler.XxlJobScheduler;
import com.xxl.job.admin.core.util.I18nUtil;
import com.xxl.job.admin.dao.XxlJobGroupDao;
import com.xxl.job.admin.dao.XxlJobInfoDao;
import com.xxl.job.admin.dao.XxlJobLogDao;
import com.xxl.job.core.biz.ExecutorBiz;
import com.xxl.job.core.biz.model.KillParam;
import com.xxl.job.core.biz.model.LogParam;
import com.xxl.job.core.biz.model.LogResult;
import com.xxl.job.core.biz.model.ReturnT;
import com.xxl.job.core.util.DateUtil;
import jakarta.annotation.Resource;
import jakarta.servlet.http.HttpServletRequest;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.util.StringUtils;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.util.HtmlUtils;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
/**
* index controller
*
* @author xuxueli 2015-12-19 16:13:16
*/
@Controller
@RequestMapping("/joblog")
public class JobLogController {
private static Logger logger = LoggerFactory.getLogger(JobLogController.class);
@Resource
private XxlJobGroupDao xxlJobGroupDao;
@Resource
public XxlJobInfoDao xxlJobInfoDao;
@Resource
public XxlJobLogDao xxlJobLogDao;
@RequestMapping
public String index(HttpServletRequest request, Model model,
@RequestParam(value = "jobId", required = false, defaultValue = "0") Integer jobId) {
// 执行器列表
List<XxlJobGroup> jobGroupList_all = xxlJobGroupDao.findAll();
// filter group
List<XxlJobGroup> jobGroupList = PermissionInterceptor.filterJobGroupByRole(request, jobGroupList_all);
if (jobGroupList == null || jobGroupList.size() == 0) {
throw new XxlJobException(I18nUtil.getString("jobgroup_empty"));
}
model.addAttribute("JobGroupList", jobGroupList);
// 任务
if (jobId > 0) {
XxlJobInfo jobInfo = xxlJobInfoDao.loadById(jobId);
if (jobInfo == null) {
throw new RuntimeException(
I18nUtil.getString("jobinfo_field_id") + I18nUtil.getString("system_unvalid"));
}
model.addAttribute("jobInfo", jobInfo);
// valid permission
PermissionInterceptor.validJobGroupPermission(request, jobInfo.getJobGroup());
}
return "joblog/joblog.index";
}
@RequestMapping("/getJobsByGroup")
@ResponseBody
public ReturnT<List<XxlJobInfo>> getJobsByGroup(@RequestParam("jobGroup") int jobGroup) {
List<XxlJobInfo> list = xxlJobInfoDao.getJobsByGroup(jobGroup);
return new ReturnT<List<XxlJobInfo>>(list);
}
@RequestMapping("/pageList")
@ResponseBody
public Map<String, Object> pageList(HttpServletRequest request,
@RequestParam(value = "start", required = false, defaultValue = "0") int start,
@RequestParam(value = "length", required = false, defaultValue = "10") int length,
@RequestParam("jobGroup") int jobGroup, @RequestParam("jobId") int jobId,
@RequestParam("logStatus") int logStatus, @RequestParam("filterTime") String filterTime) {
// valid permission
PermissionInterceptor.validJobGroupPermission(request, jobGroup); // 仅管理员支持查询全部;普通用户仅支持查询有权限的
// jobGroup
// parse param
Date triggerTimeStart = null;
Date triggerTimeEnd = null;
if (filterTime != null && filterTime.trim().length() > 0) {
String[] temp = filterTime.split(" - ");
if (temp.length == 2) {
triggerTimeStart = DateUtil.parseDateTime(temp[0]);
triggerTimeEnd = DateUtil.parseDateTime(temp[1]);
}
}
// page query
List<XxlJobLog> list = xxlJobLogDao.pageList(start, length, jobGroup, jobId, triggerTimeStart, triggerTimeEnd,
logStatus);
int list_count = xxlJobLogDao.pageListCount(start, length, jobGroup, jobId, triggerTimeStart, triggerTimeEnd,
logStatus);
// package result
Map<String, Object> maps = new HashMap<String, Object>();
maps.put("recordsTotal", list_count); // 总记录数
maps.put("recordsFiltered", list_count); // 过滤后的总记录数
maps.put("data", list); // 分页列表
return maps;
}
@RequestMapping("/logDetailPage")
public String logDetailPage(HttpServletRequest request, @RequestParam("id") int id, Model model) {
// base check
XxlJobLog jobLog = xxlJobLogDao.load(id);
if (jobLog == null) {
throw new RuntimeException(I18nUtil.getString("joblog_logid_unvalid"));
}
// valid permission
PermissionInterceptor.validJobGroupPermission(request, jobLog.getJobGroup());
// data
model.addAttribute("triggerCode", jobLog.getTriggerCode());
model.addAttribute("handleCode", jobLog.getHandleCode());
model.addAttribute("logId", jobLog.getId());
return "joblog/joblog.detail";
}
@RequestMapping("/logDetailCat")
@ResponseBody
public ReturnT<LogResult> logDetailCat(@RequestParam("logId") long logId,
@RequestParam("fromLineNum") int fromLineNum) {
try {
// valid
XxlJobLog jobLog = xxlJobLogDao.load(logId); // todo, need to improve
// performance
if (jobLog == null) {
return new ReturnT<LogResult>(ReturnT.FAIL_CODE, I18nUtil.getString("joblog_logid_unvalid"));
}
// log cat
ExecutorBiz executorBiz = XxlJobScheduler.getExecutorBiz(jobLog.getExecutorAddress());
ReturnT<LogResult> logResult = executorBiz
.log(new LogParam(jobLog.getTriggerTime().getTime(), logId, fromLineNum));
// is end
if (logResult.getContent() != null
&& logResult.getContent().getFromLineNum() > logResult.getContent().getToLineNum()) {
if (jobLog.getHandleCode() > 0) {
logResult.getContent().setEnd(true);
}
}
// fix xss
if (logResult.getContent() != null && StringUtils.hasText(logResult.getContent().getLogContent())) {
String newLogContent = filter(logResult.getContent().getLogContent());
logResult.getContent().setLogContent(newLogContent);
}
return logResult;
}
catch (Exception e) {
logger.error(e.getMessage(), e);
return new ReturnT<LogResult>(ReturnT.FAIL_CODE, e.getMessage());
}
}
/**
* filter xss tag
* @param originData
* @return
*/
private String filter(String originData) {
// exclude tag
Map<String, String> excludeTagMap = new HashMap<String, String>();
excludeTagMap.put("<br>", "###TAG_BR###");
excludeTagMap.put("<b>", "###TAG_BOLD###");
excludeTagMap.put("</b>", "###TAG_BOLD_END###");
// replace
for (String key : excludeTagMap.keySet()) {
String value = excludeTagMap.get(key);
originData = originData.replaceAll(key, value);
}
// htmlEscape
originData = HtmlUtils.htmlEscape(originData, "UTF-8");
// replace back
for (String key : excludeTagMap.keySet()) {
String value = excludeTagMap.get(key);
originData = originData.replaceAll(value, key);
}
return originData;
}
@RequestMapping("/logKill")
@ResponseBody
public ReturnT<String> logKill(@RequestParam("id") int id) {
// base check
XxlJobLog log = xxlJobLogDao.load(id);
XxlJobInfo jobInfo = xxlJobInfoDao.loadById(log.getJobId());
if (jobInfo == null) {
return new ReturnT<String>(500, I18nUtil.getString("jobinfo_glue_jobid_unvalid"));
}
if (ReturnT.SUCCESS_CODE != log.getTriggerCode()) {
return new ReturnT<String>(500, I18nUtil.getString("joblog_kill_log_limit"));
}
// request of kill
ReturnT<String> runResult = null;
try {
ExecutorBiz executorBiz = XxlJobScheduler.getExecutorBiz(log.getExecutorAddress());
runResult = executorBiz.kill(new KillParam(jobInfo.getId()));
}
catch (Exception e) {
logger.error(e.getMessage(), e);
runResult = new ReturnT<String>(500, e.getMessage());
}
if (ReturnT.SUCCESS_CODE == runResult.getCode()) {
log.setHandleCode(ReturnT.FAIL_CODE);
log.setHandleMsg(I18nUtil.getString("joblog_kill_log_byman") + ":"
+ (runResult.getMsg() != null ? runResult.getMsg() : ""));
log.setHandleTime(new Date());
XxlJobCompleter.updateHandleInfoAndFinish(log);
return new ReturnT<String>(runResult.getMsg());
}
else {
return new ReturnT<String>(500, runResult.getMsg());
}
}
@RequestMapping("/clearLog")
@ResponseBody
public ReturnT<String> clearLog(HttpServletRequest request, @RequestParam("jobGroup") int jobGroup,
@RequestParam("jobId") int jobId, @RequestParam("type") int type) {
// valid permission
PermissionInterceptor.validJobGroupPermission(request, jobGroup);
// opt
Date clearBeforeTime = null;
int clearBeforeNum = 0;
if (type == 1) {
clearBeforeTime = DateUtil.addMonths(new Date(), -1); // 清理一个月之前日志数据
}
else if (type == 2) {
clearBeforeTime = DateUtil.addMonths(new Date(), -3); // 清理三个月之前日志数据
}
else if (type == 3) {
clearBeforeTime = DateUtil.addMonths(new Date(), -6); // 清理六个月之前日志数据
}
else if (type == 4) {
clearBeforeTime = DateUtil.addYears(new Date(), -1); // 清理一年之前日志数据
}
else if (type == 5) {
clearBeforeNum = 1000; // 清理一千条以前日志数据
}
else if (type == 6) {
clearBeforeNum = 10000; // 清理一万条以前日志数据
}
else if (type == 7) {
clearBeforeNum = 30000; // 清理三万条以前日志数据
}
else if (type == 8) {
clearBeforeNum = 100000; // 清理十万条以前日志数据
}
else if (type == 9) {
clearBeforeNum = 0; // 清理所有日志数据
}
else {
return new ReturnT<String>(ReturnT.FAIL_CODE, I18nUtil.getString("joblog_clean_type_unvalid"));
}
List<Long> logIds = null;
do {
logIds = xxlJobLogDao.findClearLogIds(jobGroup, jobId, clearBeforeTime, clearBeforeNum, 1000);
if (logIds != null && logIds.size() > 0) {
xxlJobLogDao.clearLog(logIds);
}
}
while (logIds != null && logIds.size() > 0);
return ReturnT.SUCCESS;
}
}
@@ -1,194 +0,0 @@
package com.xxl.job.admin.controller;
import com.xxl.job.admin.controller.annotation.PermissionLimit;
import com.xxl.job.admin.controller.interceptor.PermissionInterceptor;
import com.xxl.job.admin.core.model.XxlJobGroup;
import com.xxl.job.admin.core.model.XxlJobUser;
import com.xxl.job.admin.core.util.I18nUtil;
import com.xxl.job.admin.dao.XxlJobGroupDao;
import com.xxl.job.admin.dao.XxlJobUserDao;
import com.xxl.job.core.biz.model.ReturnT;
import jakarta.annotation.Resource;
import jakarta.servlet.http.HttpServletRequest;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.util.DigestUtils;
import org.springframework.util.StringUtils;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.ResponseBody;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
/**
* @author xuxueli 2019-05-04 16:39:50
*/
@Controller
@RequestMapping("/user")
public class JobUserController {
@Resource
private XxlJobUserDao xxlJobUserDao;
@Resource
private XxlJobGroupDao xxlJobGroupDao;
@RequestMapping
@PermissionLimit(adminuser = true)
public String index(Model model) {
// 执行器列表
List<XxlJobGroup> groupList = xxlJobGroupDao.findAll();
model.addAttribute("groupList", groupList);
return "user/user.index";
}
@RequestMapping("/pageList")
@ResponseBody
@PermissionLimit(adminuser = true)
public Map<String, Object> pageList(@RequestParam(value = "start", required = false, defaultValue = "0") int start,
@RequestParam(value = "length", required = false, defaultValue = "10") int length,
@RequestParam("username") String username, @RequestParam("role") int role) {
// page list
List<XxlJobUser> list = xxlJobUserDao.pageList(start, length, username, role);
int list_count = xxlJobUserDao.pageListCount(start, length, username, role);
// filter
if (list != null && list.size() > 0) {
for (XxlJobUser item : list) {
item.setPassword(null);
}
}
// package result
Map<String, Object> maps = new HashMap<String, Object>();
maps.put("recordsTotal", list_count); // 总记录数
maps.put("recordsFiltered", list_count); // 过滤后的总记录数
maps.put("data", list); // 分页列表
return maps;
}
@RequestMapping("/add")
@ResponseBody
@PermissionLimit(adminuser = true)
public ReturnT<String> add(XxlJobUser xxlJobUser) {
// valid username
if (!StringUtils.hasText(xxlJobUser.getUsername())) {
return new ReturnT<String>(ReturnT.FAIL_CODE,
I18nUtil.getString("system_please_input") + I18nUtil.getString("user_username"));
}
xxlJobUser.setUsername(xxlJobUser.getUsername().trim());
if (!(xxlJobUser.getUsername().length() >= 4 && xxlJobUser.getUsername().length() <= 20)) {
return new ReturnT<String>(ReturnT.FAIL_CODE, I18nUtil.getString("system_lengh_limit") + "[4-20]");
}
// valid password
if (!StringUtils.hasText(xxlJobUser.getPassword())) {
return new ReturnT<String>(ReturnT.FAIL_CODE,
I18nUtil.getString("system_please_input") + I18nUtil.getString("user_password"));
}
xxlJobUser.setPassword(xxlJobUser.getPassword().trim());
if (!(xxlJobUser.getPassword().length() >= 4 && xxlJobUser.getPassword().length() <= 20)) {
return new ReturnT<String>(ReturnT.FAIL_CODE, I18nUtil.getString("system_lengh_limit") + "[4-20]");
}
// md5 password
xxlJobUser.setPassword(DigestUtils.md5DigestAsHex(xxlJobUser.getPassword().getBytes()));
// check repeat
XxlJobUser existUser = xxlJobUserDao.loadByUserName(xxlJobUser.getUsername());
if (existUser != null) {
return new ReturnT<String>(ReturnT.FAIL_CODE, I18nUtil.getString("user_username_repeat"));
}
// write
xxlJobUserDao.save(xxlJobUser);
return ReturnT.SUCCESS;
}
@RequestMapping("/update")
@ResponseBody
@PermissionLimit(adminuser = true)
public ReturnT<String> update(HttpServletRequest request, XxlJobUser xxlJobUser) {
// avoid opt login seft
XxlJobUser loginUser = PermissionInterceptor.getLoginUser(request);
if (loginUser.getUsername().equals(xxlJobUser.getUsername())) {
return new ReturnT<String>(ReturnT.FAIL.getCode(), I18nUtil.getString("user_update_loginuser_limit"));
}
// valid password
if (StringUtils.hasText(xxlJobUser.getPassword())) {
xxlJobUser.setPassword(xxlJobUser.getPassword().trim());
if (!(xxlJobUser.getPassword().length() >= 4 && xxlJobUser.getPassword().length() <= 20)) {
return new ReturnT<String>(ReturnT.FAIL_CODE, I18nUtil.getString("system_lengh_limit") + "[4-20]");
}
// md5 password
xxlJobUser.setPassword(DigestUtils.md5DigestAsHex(xxlJobUser.getPassword().getBytes()));
}
else {
xxlJobUser.setPassword(null);
}
// write
xxlJobUserDao.update(xxlJobUser);
return ReturnT.SUCCESS;
}
@RequestMapping("/remove")
@ResponseBody
@PermissionLimit(adminuser = true)
public ReturnT<String> remove(HttpServletRequest request, @RequestParam("id") int id) {
// avoid opt login seft
XxlJobUser loginUser = PermissionInterceptor.getLoginUser(request);
if (loginUser.getId() == id) {
return new ReturnT<String>(ReturnT.FAIL.getCode(), I18nUtil.getString("user_update_loginuser_limit"));
}
xxlJobUserDao.delete(id);
return ReturnT.SUCCESS;
}
@RequestMapping("/updatePwd")
@ResponseBody
public ReturnT<String> updatePwd(HttpServletRequest request, @RequestParam("password") String password,
@RequestParam("oldPassword") String oldPassword) {
// valid
if (oldPassword == null || oldPassword.trim().length() == 0) {
return new ReturnT<String>(ReturnT.FAIL.getCode(),
I18nUtil.getString("system_please_input") + I18nUtil.getString("change_pwd_field_oldpwd"));
}
if (password == null || password.trim().length() == 0) {
return new ReturnT<String>(ReturnT.FAIL.getCode(),
I18nUtil.getString("system_please_input") + I18nUtil.getString("change_pwd_field_oldpwd"));
}
password = password.trim();
if (!(password.length() >= 4 && password.length() <= 20)) {
return new ReturnT<String>(ReturnT.FAIL_CODE, I18nUtil.getString("system_lengh_limit") + "[4-20]");
}
// md5 password
String md5OldPassword = DigestUtils.md5DigestAsHex(oldPassword.getBytes());
String md5Password = DigestUtils.md5DigestAsHex(password.getBytes());
// valid old pwd
XxlJobUser loginUser = PermissionInterceptor.getLoginUser(request);
XxlJobUser existUser = xxlJobUserDao.loadByUserName(loginUser.getUsername());
if (!md5OldPassword.equals(existUser.getPassword())) {
return new ReturnT<String>(ReturnT.FAIL.getCode(),
I18nUtil.getString("change_pwd_field_oldpwd") + I18nUtil.getString("system_unvalid"));
}
// write new
existUser.setPassword(md5Password);
xxlJobUserDao.update(existUser);
return ReturnT.SUCCESS;
}
}
@@ -1,28 +0,0 @@
package com.xxl.job.admin.controller.annotation;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* 权限限制
*
* @author xuxueli 2015-12-12 18:29:02
*/
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface PermissionLimit {
/**
* 登录拦截 (默认拦截)
*/
boolean limit() default true;
/**
* 要求管理员权限
* @return
*/
boolean adminuser() default false;
}
@@ -0,0 +1,127 @@
package com.xxl.job.admin.controller.base;
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.sso.core.annotation.XxlSso;
import com.xxl.sso.core.helper.XxlSsoHelper;
import com.xxl.sso.core.model.LoginInfo;
import com.xxl.tool.core.StringTool;
import com.xxl.tool.response.Response;
import jakarta.annotation.Resource;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import org.springframework.beans.propertyeditors.CustomDateEditor;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.WebDataBinder;
import org.springframework.web.bind.annotation.InitBinder;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.servlet.ModelAndView;
import java.text.SimpleDateFormat;
import java.util.*;
import java.util.stream.Collectors;
/**
* index controller
*
* @author xuxueli 2015-12-19 16:13:16
*/
@Controller
public class IndexController {
@Resource
private XxlJobService xxlJobService;
/**
* index
*/
@RequestMapping("/")
@XxlSso
public String index(HttpServletRequest request, Model model) {
// menu resource
List<XxlBootResourceDTO> resourceList = findResourceList(request);
model.addAttribute("resourceList", resourceList);
return "base/index";
}
/**
* fill menu data
*/
private List<XxlBootResourceDTO> findResourceList(HttpServletRequest request) {
// login check
Response<LoginInfo> loginInfoResponse = XxlSsoHelper.loginCheckWithAttr(request);
// init menu-list
List<XxlBootResourceDTO> resourceDTOList = Arrays.asList(
new XxlBootResourceDTO(1, 0, I18nUtil.getString("job_dashboard_name"), 1, "", "/dashboard", "fa-home", 1, 0, null),
new XxlBootResourceDTO(2, 0, I18nUtil.getString("jobinfo_name"), 1, "", "/jobinfo", " fa-clock-o", 2, 0, null),
new XxlBootResourceDTO(3, 0, I18nUtil.getString("joblog_name"), 1, "", "/joblog", " fa-database", 3, 0, null),
new XxlBootResourceDTO(4, 0, I18nUtil.getString("jobgroup_name"), 1, Consts.ADMIN_ROLE, "/jobgroup", " fa-cloud", 4, 0, null),
new XxlBootResourceDTO(5, 0, I18nUtil.getString("user_manage"), 1, Consts.ADMIN_ROLE, "/user", "fa-users", 5, 0, null),
new XxlBootResourceDTO(9, 0, I18nUtil.getString("admin_help"), 1, "", "/help", "fa-book", 6, 0, null)
);
// filter by role
if (!XxlSsoHelper.hasRole(loginInfoResponse.getData(), Consts.ADMIN_ROLE).isSuccess()) {
resourceDTOList = resourceDTOList.stream()
.filter(resourceDTO -> StringTool.isBlank(resourceDTO.getPermission())) // normal user had no permission
.collect(Collectors.toList());
}
resourceDTOList.stream().sorted(Comparator.comparing(XxlBootResourceDTO::getOrder)).toList();
return resourceDTOList;
}
/**
* dashboard
*/
@RequestMapping("/dashboard")
@XxlSso
public String dashboard(HttpServletRequest request, Model model) {
Map<String, Object> dashboardMap = xxlJobService.dashboardInfo();
model.addAllAttributes(dashboardMap);
return "base/dashboard";
}
@RequestMapping("/chartInfo")
@ResponseBody
public Response<Map<String, Object>> chartInfo(@RequestParam("startDate") Date startDate, @RequestParam("endDate") Date endDate) {
Response<Map<String, Object>> chartInfo = xxlJobService.chartInfo(startDate, endDate);
return chartInfo;
}
/**
* help
*/
@RequestMapping("/help")
@XxlSso
public String help() {
return "base/help";
}
@RequestMapping(value = "/errorpage")
@XxlSso(login = false)
public ModelAndView errorPage(HttpServletRequest request, HttpServletResponse response, ModelAndView mv) {
String exceptionMsg = "HTTP Status Code: " + response.getStatus();
mv.addObject("exceptionMsg", exceptionMsg);
mv.setViewName("common/common.errorpage");
return mv;
}
@InitBinder
public void initBinder(WebDataBinder binder) {
SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
dateFormat.setLenient(false);
binder.registerCustomEditor(Date.class, new CustomDateEditor(dateFormat, true));
}
}
@@ -0,0 +1,125 @@
package com.xxl.job.admin.controller.base;
import com.xxl.job.admin.mapper.XxlJobUserMapper;
import com.xxl.job.admin.model.XxlJobUser;
import com.xxl.job.admin.util.I18nUtil;
import com.xxl.sso.core.annotation.XxlSso;
import com.xxl.sso.core.helper.XxlSsoHelper;
import com.xxl.sso.core.model.LoginInfo;
import com.xxl.tool.core.StringTool;
import com.xxl.tool.crypto.Sha256Tool;
import com.xxl.tool.id.UUIDTool;
import com.xxl.tool.response.Response;
import jakarta.annotation.Resource;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.servlet.ModelAndView;
import org.springframework.web.servlet.view.RedirectView;
/**
* index controller
*
* @author xuxueli 2015-12-19 16:13:16
*/
@Controller
@RequestMapping("/auth")
public class LoginController {
@Resource
private XxlJobUserMapper xxlJobUserMapper;
@RequestMapping("/login")
@XxlSso(login = false)
public ModelAndView login(HttpServletRequest request, HttpServletResponse response, ModelAndView modelAndView) {
// xxl-sso, logincheck
Response<LoginInfo> loginInfoResponse = XxlSsoHelper.loginCheckWithCookie(request, response);
if (loginInfoResponse.isSuccess()) {
modelAndView.setView(new RedirectView("/", true, false));
return modelAndView;
}
return new ModelAndView("base/login");
}
@RequestMapping(value = "/doLogin", method = RequestMethod.POST)
@ResponseBody
@XxlSso(login = false)
public Response<String> doLogin(HttpServletRequest request, HttpServletResponse response, String userName, String password, String ifRemember) {
// param
boolean ifRem = StringTool.isNotBlank(ifRemember) && "on".equals(ifRemember);
if (StringTool.isBlank(userName) || StringTool.isBlank(password)) {
return Response.ofFail(I18nUtil.getString("login_param_empty"));
}
// valid user、status
XxlJobUser xxlJobUser = xxlJobUserMapper.loadByUserName(userName);
if (xxlJobUser == null) {
return Response.ofFail(I18nUtil.getString("login_param_invalid"));
}
// valid passowrd
String passwordHash = Sha256Tool.sha256(password);
if (!passwordHash.equals(xxlJobUser.getPassword())) {
return Response.ofFail(I18nUtil.getString("login_param_invalid"));
}
// xxl-sso, do login
LoginInfo loginInfo = new LoginInfo(String.valueOf(xxlJobUser.getId()), UUIDTool.getSimpleUUID());
Response<String> result = XxlSsoHelper.loginWithCookie(loginInfo, response, ifRem);
return Response.of(result.getCode(), result.getMsg());
}
@RequestMapping(value = "/logout", method = RequestMethod.POST)
@ResponseBody
@XxlSso(login = false)
public Response<String> logout(HttpServletRequest request, HttpServletResponse response) {
// xxl-sso, do logout
Response<String> result = XxlSsoHelper.logoutWithCookie(request, response);
return Response.of(result.getCode(), result.getMsg());
}
@RequestMapping("/updatePwd")
@ResponseBody
@XxlSso
public Response<String> updatePwd(HttpServletRequest request, String oldPassword, String password) {
// valid
if (oldPassword == null || oldPassword.trim().isEmpty()) {
return Response.ofFail(I18nUtil.getString("system_please_input") + I18nUtil.getString("change_pwd_field_oldpwd"));
}
if (password == null || password.trim().isEmpty()) {
return Response.ofFail(I18nUtil.getString("system_please_input") + I18nUtil.getString("change_pwd_field_oldpwd"));
}
password = password.trim();
if (!(password.length() >= 4 && password.length() <= 20)) {
return Response.ofFail(I18nUtil.getString("system_length_limit") + "[4-20]");
}
// md5 password
String oldPasswordHash = Sha256Tool.sha256(oldPassword);
String passwordHash = Sha256Tool.sha256(password);
// valid old pwd
Response<LoginInfo> loginInfoResponse = XxlSsoHelper.loginCheckWithAttr(request);
XxlJobUser existUser = xxlJobUserMapper.loadByUserName(loginInfoResponse.getData().getUserName());
if (!oldPasswordHash.equals(existUser.getPassword())) {
return Response.ofFail(I18nUtil.getString("change_pwd_field_oldpwd") + I18nUtil.getString("system_invalid"));
}
// write new
existUser.setPassword(passwordHash);
xxlJobUserMapper.update(existUser);
return Response.ofSuccess();
}
}
@@ -0,0 +1,118 @@
package com.xxl.job.admin.controller.biz;
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.core.glue.GlueTypeEnum;
import com.xxl.sso.core.model.LoginInfo;
import com.xxl.tool.core.StringTool;
import com.xxl.tool.json.GsonTool;
import com.xxl.tool.response.Response;
import jakarta.annotation.Resource;
import jakarta.servlet.http.HttpServletRequest;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.ResponseBody;
import java.util.Date;
import java.util.List;
/**
* job code controller
*
* @author xuxueli 2015-12-19 16:13:16
*/
@Controller
@RequestMapping("/jobcode")
public class JobCodeController {
private static final Logger logger = LoggerFactory.getLogger(JobCodeController.class);
@Resource
private XxlJobInfoMapper xxlJobInfoMapper;
@Resource
private XxlJobLogGlueMapper xxlJobLogGlueMapper;
@RequestMapping
public String index(HttpServletRequest request, Model model, @RequestParam("jobId") int jobId) {
XxlJobInfo jobInfo = xxlJobInfoMapper.loadById(jobId);
List<XxlJobLogGlue> jobLogGlues = xxlJobLogGlueMapper.findByJobId(jobId);
if (jobInfo == null) {
throw new RuntimeException(I18nUtil.getString("jobinfo_glue_jobid_invalid"));
}
if (GlueTypeEnum.BEAN == GlueTypeEnum.match(jobInfo.getGlueType())) {
throw new RuntimeException(I18nUtil.getString("jobinfo_glue_gluetype_invalid"));
}
// valid jobGroup permission
JobGroupPermissionUtil.validJobGroupPermission(request, jobInfo.getJobGroup());
// Glue类型-字典
model.addAttribute("GlueTypeEnum", GlueTypeEnum.values());
model.addAttribute("jobInfo", jobInfo);
model.addAttribute("jobLogGlues", jobLogGlues);
return "biz/job.code";
}
@RequestMapping("/save")
@ResponseBody
public Response<String> save(HttpServletRequest request,
@RequestParam("id") int id,
@RequestParam("glueSource") String glueSource,
@RequestParam("glueRemark") String glueRemark) {
// valid
if (StringTool.isBlank(glueSource)) {
return Response.ofFail((I18nUtil.getString("system_please_input") + I18nUtil.getString("jobinfo_glue_source")));
}
if (glueRemark == null) {
return Response.ofFail((I18nUtil.getString("system_please_input") + I18nUtil.getString("jobinfo_glue_remark")));
}
if (glueRemark.length() < 4 || glueRemark.length() > 100) {
return Response.ofFail(I18nUtil.getString("jobinfo_glue_remark_limit"));
}
XxlJobInfo existsJobInfo = xxlJobInfoMapper.loadById(id);
if (existsJobInfo == null) {
return Response.ofFail(I18nUtil.getString("jobinfo_glue_jobid_invalid"));
}
// valid jobGroup permission
LoginInfo loginInfo = JobGroupPermissionUtil.validJobGroupPermission(request, existsJobInfo.getJobGroup());
// update new code
existsJobInfo.setGlueSource(glueSource);
existsJobInfo.setGlueRemark(glueRemark);
existsJobInfo.setGlueUpdatetime(new Date());
existsJobInfo.setUpdateTime(new Date());
xxlJobInfoMapper.update(existsJobInfo);
// log old code
XxlJobLogGlue xxlJobLogGlue = new XxlJobLogGlue();
xxlJobLogGlue.setJobId(existsJobInfo.getId());
xxlJobLogGlue.setGlueType(existsJobInfo.getGlueType());
xxlJobLogGlue.setGlueSource(glueSource);
xxlJobLogGlue.setGlueRemark(glueRemark);
xxlJobLogGlue.setAddTime(new Date());
xxlJobLogGlue.setUpdateTime(new Date());
xxlJobLogGlueMapper.save(xxlJobLogGlue);
// remove code backup more than 30
xxlJobLogGlueMapper.removeOld(existsJobInfo.getId(), 30);
// write operation log
logger.info(">>>>>>>>>>> xxl-job operation log: operator = {}, type = {}, content = {}",
loginInfo.getUserName(), "jobcode-update", GsonTool.toJson(xxlJobLogGlue));
return Response.ofSuccess();
}
}
@@ -0,0 +1,226 @@
package com.xxl.job.admin.controller.biz;
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.core.constant.Const;
import com.xxl.job.core.constant.RegistType;
import com.xxl.sso.core.annotation.XxlSso;
import com.xxl.tool.core.CollectionTool;
import com.xxl.tool.core.StringTool;
import com.xxl.tool.http.HttpTool;
import com.xxl.tool.response.PageModel;
import com.xxl.tool.response.Response;
import jakarta.annotation.Resource;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.ResponseBody;
import java.util.*;
/**
* job group controller
*
* @author xuxueli 2016-10-02 20:52:56
*/
@Controller
@RequestMapping("/jobgroup")
public class JobGroupController {
@Resource
public XxlJobInfoMapper xxlJobInfoMapper;
@Resource
public XxlJobGroupMapper xxlJobGroupMapper;
@Resource
private XxlJobRegistryMapper xxlJobRegistryMapper;
@RequestMapping
@XxlSso(role = Consts.ADMIN_ROLE)
public String index(Model model) {
return "biz/group.list";
}
@RequestMapping("/pageList")
@ResponseBody
@XxlSso(role = Consts.ADMIN_ROLE)
public Response<PageModel<XxlJobGroup>> pageList(@RequestParam(required = false, defaultValue = "0") int offset,
@RequestParam(required = false, defaultValue = "10") int pagesize,
String appname,
String title) {
// page query
List<XxlJobGroup> list = xxlJobGroupMapper.pageList(offset, pagesize, appname, title);
int list_count = xxlJobGroupMapper.pageListCount(offset, pagesize, appname, title);
// package result
PageModel<XxlJobGroup> pageModel = new PageModel<>();
pageModel.setData(list);
pageModel.setTotal(list_count);
return Response.ofSuccess(pageModel);
}
@RequestMapping("/insert")
@ResponseBody
@XxlSso(role = Consts.ADMIN_ROLE)
public Response<String> insert(XxlJobGroup xxlJobGroup) {
// valid
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("<")) {
return Response.ofFail("AppName" + I18nUtil.getString("system_invalid"));
}
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("<")) {
return Response.ofFail(I18nUtil.getString("jobgroup_field_title") + I18nUtil.getString("system_invalid"));
}
if (xxlJobGroup.getAddressType() != 0) {
if (StringTool.isBlank(xxlJobGroup.getAddressList())) {
return Response.ofFail(I18nUtil.getString("jobgroup_field_addressType_limit"));
}
if (xxlJobGroup.getAddressList().contains(">") || xxlJobGroup.getAddressList().contains("<")) {
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)) {
return Response.ofFail(I18nUtil.getString("jobgroup_field_registryList_invalid"));
}
if (!(HttpTool.isHttp(item) || HttpTool.isHttps(item))) {
return Response.ofFail(I18nUtil.getString("jobgroup_field_registryList_invalid") + "[2]");
}
}
}
// process
xxlJobGroup.setUpdateTime(new Date());
int ret = xxlJobGroupMapper.save(xxlJobGroup);
return (ret > 0) ? Response.ofSuccess() : Response.ofFail();
}
@RequestMapping("/update")
@ResponseBody
@XxlSso(role = Consts.ADMIN_ROLE)
public Response<String> update(XxlJobGroup xxlJobGroup) {
// valid
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 (StringTool.isBlank(xxlJobGroup.getTitle())) {
return Response.ofFail((I18nUtil.getString("system_please_input") + I18nUtil.getString("jobgroup_field_title")));
}
if (xxlJobGroup.getAddressType() == 0) {
// 0=自动注册
List<String> registryList = findRegistryByAppName(xxlJobGroup.getAppname());
String addressListStr = null;
if (CollectionTool.isNotEmpty(registryList)) {
Collections.sort(registryList);
addressListStr = String.join(",", registryList);
}
xxlJobGroup.setAddressList(addressListStr);
} else {
// 1=手动录入
if (StringTool.isBlank(xxlJobGroup.getAddressList())) {
return Response.ofFail(I18nUtil.getString("jobgroup_field_addressType_limit"));
}
String[] addresss = xxlJobGroup.getAddressList().split(",");
for (String item : addresss) {
if (StringTool.isBlank(item)) {
return Response.ofFail(I18nUtil.getString("jobgroup_field_registryList_invalid"));
}
if (!(HttpTool.isHttp(item) || HttpTool.isHttps(item))) {
return Response.ofFail(I18nUtil.getString("jobgroup_field_registryList_invalid") + "[2]");
}
}
}
// process
xxlJobGroup.setUpdateTime(new Date());
int ret = xxlJobGroupMapper.update(xxlJobGroup);
return (ret > 0) ? Response.ofSuccess() : Response.ofFail();
}
private List<String> findRegistryByAppName(String appnameParam) {
HashMap<String, List<String>> appAddressMap = new HashMap<>();
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())) {
continue;
}
String appname = item.getRegistryKey();
List<String> registryList = appAddressMap.computeIfAbsent(appname, k -> new ArrayList<>());
if (!registryList.contains(item.getRegistryValue())) {
registryList.add(item.getRegistryValue());
}
}
}
return appAddressMap.get(appnameParam);
}
@RequestMapping("/delete")
@ResponseBody
@XxlSso(role = Consts.ADMIN_ROLE)
public Response<String> delete(@RequestParam("ids[]") List<Integer> ids) {
// parse id
if (CollectionTool.isEmpty(ids) || ids.size() != 1) {
return Response.ofFail(I18nUtil.getString("system_please_choose") + I18nUtil.getString("system_one") + I18nUtil.getString("system_data"));
}
int id = ids.get(0);
// valid repeat operation
XxlJobGroup xxlJobGroup = xxlJobGroupMapper.load(id);
if (xxlJobGroup == null) {
return Response.ofSuccess();
}
// whether exists job
int count = xxlJobInfoMapper.pageListCount(0, 10, id, -1, null, null, null);
if (count > 0) {
return Response.ofFail(I18nUtil.getString("jobgroup_del_limit_0"));
}
// whether only exists one group
List<XxlJobGroup> allList = xxlJobGroupMapper.findAll();
if (allList.size() == 1) {
return Response.ofFail(I18nUtil.getString("jobgroup_del_limit_1"));
}
// remove group
int ret = xxlJobGroupMapper.remove(id);
// remove registry-data
xxlJobRegistryMapper.removeByRegistryGroupAndKey(RegistType.EXECUTOR.name(), xxlJobGroup.getAppname());
return (ret > 0) ? Response.ofSuccess() : Response.ofFail();
}
@RequestMapping("/loadById")
@ResponseBody
//@XxlSso(role = Consts.ADMIN_ROLE) // open to default user, support show registry nodes
public Response<XxlJobGroup> loadById(@RequestParam("id") int id) {
XxlJobGroup jobGroup = xxlJobGroupMapper.load(id);
return jobGroup != null ? Response.ofSuccess(jobGroup) : Response.ofFail();
}
}
@@ -0,0 +1,212 @@
package com.xxl.job.admin.controller.biz;
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.core.constant.ExecutorBlockStrategyEnum;
import com.xxl.job.core.glue.GlueTypeEnum;
import com.xxl.sso.core.helper.XxlSsoHelper;
import com.xxl.sso.core.model.LoginInfo;
import com.xxl.tool.core.CollectionTool;
import com.xxl.tool.core.DateTool;
import com.xxl.tool.core.StringTool;
import com.xxl.tool.response.PageModel;
import com.xxl.tool.response.Response;
import jakarta.annotation.Resource;
import jakarta.servlet.http.HttpServletRequest;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.ResponseBody;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
/**
* index controller
*
* @author xuxueli 2015-12-19 16:13:16
*/
@Controller
@RequestMapping("/jobinfo")
public class JobInfoController {
private static Logger logger = LoggerFactory.getLogger(JobInfoController.class);
@Resource
private XxlJobGroupMapper xxlJobGroupMapper;
@Resource
private XxlJobService xxlJobService;
@RequestMapping
public String index(HttpServletRequest request, Model model, @RequestParam(value = "jobGroup", required = false, defaultValue = "-1") int jobGroup) {
// 枚举-字典
model.addAttribute("ExecutorRouteStrategyEnum", ExecutorRouteStrategyEnum.values()); // 路由策略-列表
model.addAttribute("GlueTypeEnum", GlueTypeEnum.values()); // Glue类型-字典
model.addAttribute("ExecutorBlockStrategyEnum", ExecutorBlockStrategyEnum.values()); // 阻塞处理策略-字典
model.addAttribute("ScheduleTypeEnum", ScheduleTypeEnum.values()); // 调度类型
model.addAttribute("MisfireStrategyEnum", MisfireStrategyEnum.values()); // 调度过期策略
// 执行器列表
List<XxlJobGroup> jobGroupListTotal = xxlJobGroupMapper.findAll();
// filter group
List<XxlJobGroup> jobGroupList = JobGroupPermissionUtil.filterJobGroupByPermission(request, jobGroupListTotal);
if (CollectionTool.isEmpty(jobGroupList)) {
throw new XxlJobException(I18nUtil.getString("jobgroup_empty"));
}
// parse jobGroup
if (!(CollectionTool.isNotEmpty(jobGroupList)
&& jobGroupList.stream().map(XxlJobGroup::getId).toList().contains(jobGroup))) {
jobGroup = -1;
}
model.addAttribute("JobGroupList", jobGroupList);
model.addAttribute("jobGroup", jobGroup);
return "biz/job.list";
}
@RequestMapping("/pageList")
@ResponseBody
public Response<PageModel<XxlJobInfo>> pageList(HttpServletRequest request,
@RequestParam(required = false, defaultValue = "0") int offset,
@RequestParam(required = false, defaultValue = "10") int pagesize,
@RequestParam int jobGroup,
@RequestParam int triggerStatus,
@RequestParam String jobDesc,
@RequestParam String executorHandler,
@RequestParam String author) {
// valid jobGroup permission
JobGroupPermissionUtil.validJobGroupPermission(request, jobGroup);
// page
return xxlJobService.pageList(offset, pagesize, jobGroup, triggerStatus, jobDesc, executorHandler, author);
}
@RequestMapping("/insert")
@ResponseBody
public Response<String> add(HttpServletRequest request, XxlJobInfo jobInfo) {
// valid permission
LoginInfo loginInfo = JobGroupPermissionUtil.validJobGroupPermission(request, jobInfo.getJobGroup());
// opt
return xxlJobService.add(jobInfo, loginInfo);
}
@RequestMapping("/update")
@ResponseBody
public Response<String> update(HttpServletRequest request, XxlJobInfo jobInfo) {
// valid permission
LoginInfo loginInfo = JobGroupPermissionUtil.validJobGroupPermission(request, jobInfo.getJobGroup());
// opt
return xxlJobService.update(jobInfo, loginInfo);
}
@RequestMapping("/delete")
@ResponseBody
public Response<String> delete(HttpServletRequest request, @RequestParam("ids[]") List<Integer> ids) {
// valid
if (CollectionTool.isEmpty(ids) || ids.size() != 1) {
return Response.ofFail(I18nUtil.getString("system_please_choose") + I18nUtil.getString("system_one") + I18nUtil.getString("system_data"));
}
// invoke
Response<LoginInfo> loginInfoResponse = XxlSsoHelper.loginCheckWithAttr(request);
return xxlJobService.remove(ids.get(0), loginInfoResponse.getData());
}
@RequestMapping("/stop")
@ResponseBody
public Response<String> pause(HttpServletRequest request, @RequestParam("ids[]") List<Integer> ids) {
// valid
if (CollectionTool.isEmpty(ids) || ids.size() != 1) {
return Response.ofFail(I18nUtil.getString("system_please_choose") + I18nUtil.getString("system_one") + I18nUtil.getString("system_data"));
}
// invoke
Response<LoginInfo> loginInfoResponse = XxlSsoHelper.loginCheckWithAttr(request);
return xxlJobService.stop(ids.get(0), loginInfoResponse.getData());
}
@RequestMapping("/start")
@ResponseBody
public Response<String> start(HttpServletRequest request, @RequestParam("ids[]") List<Integer> ids) {
// valid
if (CollectionTool.isEmpty(ids) || ids.size() != 1) {
return Response.ofFail(I18nUtil.getString("system_please_choose") + I18nUtil.getString("system_one") + I18nUtil.getString("system_data"));
}
// invoke
Response<LoginInfo> loginInfoResponse = XxlSsoHelper.loginCheckWithAttr(request);
return xxlJobService.start(ids.get(0), loginInfoResponse.getData());
}
@RequestMapping("/trigger")
@ResponseBody
public Response<String> triggerJob(HttpServletRequest request,
@RequestParam("id") int id,
@RequestParam("executorParam") String executorParam,
@RequestParam("addressList") String addressList) {
Response<LoginInfo> loginInfoResponse = XxlSsoHelper.loginCheckWithAttr(request);
return xxlJobService.trigger(loginInfoResponse.getData(), id, executorParam, addressList);
}
@RequestMapping("/nextTriggerTime")
@ResponseBody
public Response<List<String>> nextTriggerTime(@RequestParam("scheduleType") String scheduleType,
@RequestParam("scheduleConf") String scheduleConf) {
// valid
if (StringTool.isBlank(scheduleType) || StringTool.isBlank(scheduleConf)) {
return Response.ofSuccess(new ArrayList<>());
}
// param
XxlJobInfo paramXxlJobInfo = new XxlJobInfo();
paramXxlJobInfo.setScheduleType(scheduleType);
paramXxlJobInfo.setScheduleConf(scheduleConf);
// generate
List<String> result = new ArrayList<>();
try {
Date lastTime = new Date();
for (int i = 0; i < 5; i++) {
// generate next trigger time
ScheduleTypeEnum scheduleTypeEnum = ScheduleTypeEnum.match(paramXxlJobInfo.getScheduleType(), ScheduleTypeEnum.NONE);
lastTime = scheduleTypeEnum.getScheduleType().generateNextTriggerTime(paramXxlJobInfo, lastTime);
// collect data
if (lastTime != null) {
result.add(DateTool.formatDateTime(lastTime));
} else {
break;
}
}
} catch (Exception e) {
logger.error(">>>>>>>>>>> nextTriggerTime error. scheduleType = {}, scheduleConf= {}, error:{} ", scheduleType, scheduleConf, e.getMessage());
return Response.ofFail((I18nUtil.getString("schedule_type") + I18nUtil.getString("system_invalid")) + e.getMessage());
}
return Response.ofSuccess(result);
}
}
@@ -0,0 +1,325 @@
package com.xxl.job.admin.controller.biz;
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.core.context.XxlJobContext;
import com.xxl.job.core.openapi.ExecutorBiz;
import com.xxl.job.core.openapi.model.KillRequest;
import com.xxl.job.core.openapi.model.LogRequest;
import com.xxl.job.core.openapi.model.LogResult;
import com.xxl.tool.core.CollectionTool;
import com.xxl.tool.core.DateTool;
import com.xxl.tool.core.StringTool;
import com.xxl.tool.response.PageModel;
import com.xxl.tool.response.Response;
import jakarta.annotation.Resource;
import jakarta.servlet.http.HttpServletRequest;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.util.HtmlUtils;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
/**
* index controller
*
* @author xuxueli 2015-12-19 16:13:16
*/
@Controller
@RequestMapping("/joblog")
public class JobLogController {
private static final Logger logger = LoggerFactory.getLogger(JobLogController.class);
@Resource
private XxlJobGroupMapper xxlJobGroupMapper;
@Resource
public XxlJobInfoMapper xxlJobInfoMapper;
@Resource
public XxlJobLogMapper xxlJobLogMapper;
@Autowired
private XxlJobService xxlJobService;
@RequestMapping
public String index(HttpServletRequest request,
Model model,
@RequestParam(value = "jobGroup", required = false, defaultValue = "0") Integer jobGroup,
@RequestParam(value = "jobId", required = false, defaultValue = "0") Integer jobId) {
// 1、init JobGroupList
// find all jobGroup
List<XxlJobGroup> jobGroupListTotal = xxlJobGroupMapper.findAll();
// filter JobGroupList
List<XxlJobGroup> jobGroupList = JobGroupPermissionUtil.filterJobGroupByPermission(request, jobGroupListTotal);
if (CollectionTool.isEmpty(jobGroupList)) {
throw new XxlJobException(I18nUtil.getString("jobgroup_empty"));
}
List<Integer> jobGroupIds = jobGroupList.stream().map(XxlJobGroup::getId).toList();
// 2、check jobId
if (jobId > 0) {
// valid jobId
XxlJobInfo jobInfo = xxlJobInfoMapper.loadById(jobId);
if (jobInfo == null) {
throw new RuntimeException(I18nUtil.getString("jobinfo_field_id") + I18nUtil.getString("system_invalid"));
}
// valid jobGroup
jobGroup = jobInfo.getJobGroup();
}
// 3、init jobGroup, default first 1
if (!jobGroupIds.contains(jobGroup)) {
jobGroup = jobGroupList.get(0).getId();
}
// 4、init jobInfoList
List<XxlJobInfo> jobInfoList = xxlJobInfoMapper.getJobsByGroup(jobGroup);
List<Integer> jobIds = jobInfoList.stream().map(XxlJobInfo::getId).toList();
// 5、init JobId, default 0
if (!jobIds.contains(jobId)) {
jobId = 0;
}
// write
model.addAttribute("JobGroupList", jobGroupList);
model.addAttribute("jobInfoList", jobInfoList);
model.addAttribute("jobGroup", jobGroup);
model.addAttribute("jobId", jobId);
return "biz/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) {
// valid jobGroup permission
JobGroupPermissionUtil.validJobGroupPermission(request, jobGroup);
// valid jobId
/*if (jobId < 1) {
return Response.ofFail(I18nUtil.getString("system_please_choose") + I18nUtil.getString("jobinfo_job"));
}*/
// parse param
Date triggerTimeStart = null;
Date triggerTimeEnd = null;
if (StringTool.isNotBlank(filterTime)) {
String[] temp = filterTime.split(" - ");
if (temp.length == 2) {
triggerTimeStart = DateTool.parseDateTime(temp[0]);
triggerTimeEnd = DateTool.parseDateTime(temp[1]);
}
}
// page query
List<XxlJobLog> list = xxlJobLogMapper.pageList(offset, pagesize, jobGroup, jobId, triggerTimeStart, triggerTimeEnd, logStatus);
int list_count = xxlJobLogMapper.pageListCount(offset, pagesize, jobGroup, jobId, triggerTimeStart, triggerTimeEnd, logStatus);
// package result
PageModel<XxlJobLog> pageModel = new PageModel<>();
pageModel.setData(list);
pageModel.setTotal(list_count);
return Response.ofSuccess(pageModel);
}
/**
* filter xss tag
*/
private String filter(String originData) {
// exclude tag
Map<String, String> excludeTagMap = new HashMap<String, String>();
excludeTagMap.put("<br>", "###TAG_BR###");
excludeTagMap.put("<b>", "###TAG_BOLD###");
excludeTagMap.put("</b>", "###TAG_BOLD_END###");
// replace
for (String key : excludeTagMap.keySet()) {
String value = excludeTagMap.get(key);
originData = originData.replaceAll(key, value);
}
// htmlEscape
originData = HtmlUtils.htmlEscape(originData, "UTF-8");
// replace back
for (String key : excludeTagMap.keySet()) {
String value = excludeTagMap.get(key);
originData = originData.replaceAll(value, key);
}
return originData;
}
@RequestMapping("/logKill")
@ResponseBody
public Response<String> logKill(HttpServletRequest request, @RequestParam("id") long id) {
// base check
XxlJobLog log = xxlJobLogMapper.load(id);
XxlJobInfo jobInfo = xxlJobInfoMapper.loadById(log.getJobId());
if (jobInfo == null) {
return Response.ofFail(I18nUtil.getString("jobinfo_glue_jobid_invalid"));
}
if (XxlJobContext.HANDLE_CODE_SUCCESS != log.getTriggerCode()) {
return Response.ofFail(I18nUtil.getString("joblog_kill_log_limit"));
}
// valid JobGroup permission
JobGroupPermissionUtil.validJobGroupPermission(request, jobInfo.getJobGroup());
// request of kill
Response<String> runResult = null;
try {
ExecutorBiz executorBiz = XxlJobAdminBootstrap.getExecutorBiz(log.getExecutorAddress());
runResult = executorBiz.kill(new KillRequest(jobInfo.getId()));
} catch (Exception e) {
logger.error(e.getMessage(), e);
runResult = Response.ofFail(e.getMessage());
}
if (XxlJobContext.HANDLE_CODE_SUCCESS == runResult.getCode()) {
log.setHandleCode(XxlJobContext.HANDLE_CODE_FAIL);
log.setHandleMsg(I18nUtil.getString("joblog_kill_log_byman") + ":" + (runResult.getMsg() != null ? runResult.getMsg() : ""));
log.setHandleTime(new Date());
XxlJobAdminBootstrap.getInstance().getJobCompleter().complete(log);
return Response.ofSuccess(runResult.getMsg());
} else {
return Response.ofFail(runResult.getMsg());
}
}
@RequestMapping("/clearLog")
@ResponseBody
public Response<String> clearLog(HttpServletRequest request,
@RequestParam("jobGroup") int jobGroup,
@RequestParam("jobId") int jobId,
@RequestParam("type") int type) {
// valid JobGroup permission
JobGroupPermissionUtil.validJobGroupPermission(request, jobGroup);
// valid jobId
/*if (jobId < 1) {
return Response.ofFail(I18nUtil.getString("system_please_choose") + I18nUtil.getString("jobinfo_job"));
}*/
// opt
Date clearBeforeTime = null;
int clearBeforeNum = 0;
if (type == 1) {
clearBeforeTime = DateTool.addMonths(new Date(), -1); // 清理一个月之前日志数据
} else if (type == 2) {
clearBeforeTime = DateTool.addMonths(new Date(), -3); // 清理三个月之前日志数据
} else if (type == 3) {
clearBeforeTime = DateTool.addMonths(new Date(), -6); // 清理六个月之前日志数据
} else if (type == 4) {
clearBeforeTime = DateTool.addYears(new Date(), -1); // 清理一年之前日志数据
} else if (type == 5) {
clearBeforeNum = 1000; // 清理一千条以前日志数据
} else if (type == 6) {
clearBeforeNum = 10000; // 清理一万条以前日志数据
} else if (type == 7) {
clearBeforeNum = 30000; // 清理三万条以前日志数据
} else if (type == 8) {
clearBeforeNum = 100000; // 清理十万条以前日志数据
} else if (type == 9) {
clearBeforeNum = 0; // 清理所有日志数据
} else {
return Response.ofFail(I18nUtil.getString("joblog_clean_type_invalid"));
}
List<Long> logIds = null;
do {
logIds = xxlJobLogMapper.findClearLogIds(jobGroup, jobId, clearBeforeTime, clearBeforeNum, 1000);
if (logIds != null && !logIds.isEmpty()) {
xxlJobLogMapper.clearLog(logIds);
}
} while (logIds != null && !logIds.isEmpty());
return Response.ofSuccess();
}
@RequestMapping("/logDetailPage")
public String logDetailPage(HttpServletRequest request, @RequestParam("id") long id, Model model) {
// base check
XxlJobLog jobLog = xxlJobLogMapper.load(id);
if (jobLog == null) {
throw new RuntimeException(I18nUtil.getString("joblog_logid_invalid"));
}
// valid permission
JobGroupPermissionUtil.validJobGroupPermission(request, jobLog.getJobGroup());
// load jobInfo
XxlJobInfo jobInfo = xxlJobInfoMapper.loadById(jobLog.getJobId());
// data
model.addAttribute("triggerCode", jobLog.getTriggerCode());
model.addAttribute("handleCode", jobLog.getHandleCode());
model.addAttribute("logId", jobLog.getId());
model.addAttribute("jobInfo", jobInfo);
return "biz/log.detail";
}
@RequestMapping("/logDetailCat")
@ResponseBody
public Response<LogResult> logDetailCat(@RequestParam("logId") long logId, @RequestParam("fromLineNum") int fromLineNum) {
try {
// valid
XxlJobLog jobLog = xxlJobLogMapper.load(logId); // todo, need to improve performance
if (jobLog == null) {
return Response.ofFail(I18nUtil.getString("joblog_logid_invalid"));
}
// log cat
ExecutorBiz executorBiz = XxlJobAdminBootstrap.getExecutorBiz(jobLog.getExecutorAddress());
Response<LogResult> logResult = executorBiz.log(new LogRequest(jobLog.getTriggerTime().getTime(), logId, fromLineNum));
// is end
if (logResult.getData() != null && logResult.getData().getFromLineNum() > logResult.getData().getToLineNum()) {
if (jobLog.getHandleCode() > 0) {
logResult.getData().setEnd(true);
}
}
// fix xss
if (logResult.getData() != null && StringTool.isNotBlank(logResult.getData().getLogContent())) {
String newLogContent = filter(logResult.getData().getLogContent());
logResult.getData().setLogContent(newLogContent);
}
return logResult;
} catch (Exception e) {
logger.error("logId({}) logDetailCat error: {}", logId, e.getMessage(), e);
return Response.ofFail(e.getMessage());
}
}
}
@@ -0,0 +1,198 @@
package com.xxl.job.admin.controller.biz;
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.sso.core.annotation.XxlSso;
import com.xxl.sso.core.helper.XxlSsoHelper;
import com.xxl.sso.core.model.LoginInfo;
import com.xxl.tool.core.CollectionTool;
import com.xxl.tool.core.StringTool;
import com.xxl.tool.crypto.Sha256Tool;
import com.xxl.tool.response.PageModel;
import com.xxl.tool.response.Response;
import jakarta.annotation.Resource;
import jakarta.servlet.http.HttpServletRequest;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.ResponseBody;
import java.util.List;
/**
* @author xuxueli 2019-05-04 16:39:50
*/
@Controller
@RequestMapping("/user")
public class JobUserController {
@Resource
private XxlJobUserMapper xxlJobUserMapper;
@Resource
private XxlJobGroupMapper xxlJobGroupMapper;
@RequestMapping
@XxlSso(role = Consts.ADMIN_ROLE)
public String index(Model model) {
// 执行器列表
List<XxlJobGroup> groupList = xxlJobGroupMapper.findAll();
model.addAttribute("groupList", groupList);
return "biz/user.list";
}
@RequestMapping("/pageList")
@ResponseBody
@XxlSso(role = Consts.ADMIN_ROLE)
public Response<PageModel<XxlJobUser>> pageList(@RequestParam(required = false, defaultValue = "0") int offset,
@RequestParam(required = false, defaultValue = "10") int pagesize,
@RequestParam String username,
@RequestParam int role) {
// page list
List<XxlJobUser> list = xxlJobUserMapper.pageList(offset, pagesize, username, role);
int list_count = xxlJobUserMapper.pageListCount(offset, pagesize, username, role);
// filter
if (list != null && !list.isEmpty()) {
for (XxlJobUser item : list) {
item.setPassword(null);
}
}
// package result
PageModel<XxlJobUser> pageModel = new PageModel<>();
pageModel.setData(list);
pageModel.setTotal(list_count);
return Response.ofSuccess(pageModel);
}
@RequestMapping("/insert")
@ResponseBody
@XxlSso(role = Consts.ADMIN_ROLE)
public Response<String> insert(XxlJobUser xxlJobUser) {
// valid username
if (StringTool.isBlank(xxlJobUser.getUsername())) {
return Response.ofFail(I18nUtil.getString("system_please_input") + I18nUtil.getString("user_username"));
}
xxlJobUser.setUsername(xxlJobUser.getUsername().trim());
if (!(xxlJobUser.getUsername().length() >= 4 && xxlJobUser.getUsername().length() <= 20)) {
return Response.ofFail(I18nUtil.getString("system_length_limit") + "[4-20]");
}
// valid password
if (StringTool.isBlank(xxlJobUser.getPassword())) {
return Response.ofFail(I18nUtil.getString("system_please_input") + I18nUtil.getString("user_password"));
}
xxlJobUser.setPassword(xxlJobUser.getPassword().trim());
if (!(xxlJobUser.getPassword().length() >= 4 && xxlJobUser.getPassword().length() <= 20)) {
return Response.ofFail(I18nUtil.getString("system_length_limit") + "[4-20]");
}
// md5 password
String passwordHash = Sha256Tool.sha256(xxlJobUser.getPassword());
xxlJobUser.setPassword(passwordHash);
// check repeat
XxlJobUser existUser = xxlJobUserMapper.loadByUserName(xxlJobUser.getUsername());
if (existUser != null) {
return Response.ofFail(I18nUtil.getString("user_username_repeat"));
}
// write
xxlJobUserMapper.save(xxlJobUser);
return Response.ofSuccess();
}
@RequestMapping("/update")
@ResponseBody
@XxlSso(role = Consts.ADMIN_ROLE)
public Response<String> update(HttpServletRequest request, XxlJobUser xxlJobUser) {
// avoid opt login seft
Response<LoginInfo> loginInfoResponse = XxlSsoHelper.loginCheckWithAttr(request);
if (loginInfoResponse.getData().getUserName().equals(xxlJobUser.getUsername())) {
return Response.ofFail(I18nUtil.getString("user_update_loginuser_limit"));
}
// valid password
if (StringTool.isNotBlank(xxlJobUser.getPassword())) {
xxlJobUser.setPassword(xxlJobUser.getPassword().trim());
if (!(xxlJobUser.getPassword().length() >= 4 && xxlJobUser.getPassword().length() <= 20)) {
return Response.ofFail(I18nUtil.getString("system_length_limit") + "[4-20]");
}
// md5 password
String passwordHash = Sha256Tool.sha256(xxlJobUser.getPassword());
xxlJobUser.setPassword(passwordHash);
} else {
xxlJobUser.setPassword(null);
}
// write
xxlJobUserMapper.update(xxlJobUser);
return Response.ofSuccess();
}
@RequestMapping("/delete")
@ResponseBody
@XxlSso(role = Consts.ADMIN_ROLE)
public Response<String> delete(HttpServletRequest request, @RequestParam("ids[]") List<Integer> ids) {
// valid
if (CollectionTool.isEmpty(ids) || ids.size() != 1) {
return Response.ofFail(I18nUtil.getString("system_please_choose") + I18nUtil.getString("system_one") + I18nUtil.getString("system_data"));
}
// avoid opt login seft
Response<LoginInfo> loginInfoResponse = XxlSsoHelper.loginCheckWithAttr(request);
if (ids.contains(Integer.parseInt(loginInfoResponse.getData().getUserId()))) {
return Response.ofFail(I18nUtil.getString("user_update_loginuser_limit"));
}
xxlJobUserMapper.delete(ids.get(0));
return Response.ofSuccess();
}
/*@RequestMapping("/updatePwd")
@ResponseBody
public Response<String> updatePwd(HttpServletRequest request,
@RequestParam("password") String password,
@RequestParam("oldPassword") String oldPassword){
// valid
if (oldPassword==null || oldPassword.trim().isEmpty()){
return Response.ofFail(I18nUtil.getString("system_please_input") + I18nUtil.getString("change_pwd_field_oldpwd"));
}
if (password==null || password.trim().isEmpty()){
return Response.ofFail(I18nUtil.getString("system_please_input") + I18nUtil.getString("change_pwd_field_oldpwd"));
}
password = password.trim();
if (!(password.length()>=4 && password.length()<=20)) {
return Response.ofFail(I18nUtil.getString("system_length_limit")+"[4-20]" );
}
// md5 password
String oldPasswordHash = Sha256Tool.sha256(oldPassword);
String passwordHash = Sha256Tool.sha256(password);
// valid old pwd
Response<LoginInfo> loginInfoResponse = XxlSsoHelper.loginCheckWithAttr(request);
XxlJobUser existUser = xxlJobUserMapper.loadByUserName(loginInfoResponse.getData().getUserName());
if (!oldPasswordHash.equals(existUser.getPassword())) {
return Response.ofFail(I18nUtil.getString("change_pwd_field_oldpwd") + I18nUtil.getString("system_invalid"));
}
// write new
existUser.setPassword(passwordHash);
xxlJobUserMapper.update(existUser);
return Response.ofSuccess();
}*/
}
@@ -1,42 +0,0 @@
package com.xxl.job.admin.controller.interceptor;
import com.xxl.job.admin.core.util.FtlUtil;
import com.xxl.job.admin.core.util.I18nUtil;
import jakarta.servlet.http.Cookie;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import org.springframework.stereotype.Component;
import org.springframework.web.servlet.AsyncHandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;
import java.util.HashMap;
/**
* push cookies to model as cookieMap
*
* @author xuxueli 2015-12-12 18:09:04
*/
@Component
public class CookieInterceptor implements AsyncHandlerInterceptor {
@Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler,
ModelAndView modelAndView) throws Exception {
// cookie
if (modelAndView != null && request.getCookies() != null && request.getCookies().length > 0) {
HashMap<String, Cookie> cookieMap = new HashMap<String, Cookie>();
for (Cookie ck : request.getCookies()) {
cookieMap.put(ck.getName(), ck);
}
modelAndView.addObject("cookieMap", cookieMap);
}
// static method
if (modelAndView != null) {
modelAndView.addObject("I18nUtil", FtlUtil.generateStaticModel(I18nUtil.class.getName()));
}
}
}
@@ -1,132 +0,0 @@
package com.xxl.job.admin.controller.interceptor;
import com.xxl.job.admin.controller.annotation.PermissionLimit;
import com.xxl.job.admin.core.model.XxlJobGroup;
import com.xxl.job.admin.core.model.XxlJobUser;
import com.xxl.job.admin.core.util.I18nUtil;
import com.xxl.job.admin.service.impl.LoginService;
import jakarta.annotation.Resource;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import org.springframework.stereotype.Component;
import org.springframework.web.method.HandlerMethod;
import org.springframework.web.servlet.AsyncHandlerInterceptor;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
/**
* 权限拦截
*
* @author xuxueli 2015-12-12 18:09:04
*/
@Component
public class PermissionInterceptor implements AsyncHandlerInterceptor {
@Resource
private LoginService loginService;
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)
throws Exception {
if (!(handler instanceof HandlerMethod)) {
return true; // proceed with the next interceptor
}
// if need login
boolean needLogin = true;
boolean needAdminuser = false;
HandlerMethod method = (HandlerMethod) handler;
PermissionLimit permission = method.getMethodAnnotation(PermissionLimit.class);
if (permission != null) {
needLogin = permission.limit();
needAdminuser = permission.adminuser();
}
if (needLogin) {
XxlJobUser loginUser = loginService.ifLogin(request, response);
if (loginUser == null) {
response.setStatus(302);
response.setHeader("location", request.getContextPath() + "/toLogin");
return false;
}
if (needAdminuser && loginUser.getRole() != 1) {
throw new RuntimeException(I18nUtil.getString("system_permission_limit"));
}
// set loginUser, with request
setLoginUser(request, loginUser);
}
return true; // proceed with the next interceptor
}
// -------------------- permission tool --------------------
/**
* set loginUser
* @param request
* @param loginUser
*/
private static void setLoginUser(HttpServletRequest request, XxlJobUser loginUser) {
request.setAttribute("loginUser", loginUser);
}
/**
* get loginUser
* @param request
* @return
*/
public static XxlJobUser getLoginUser(HttpServletRequest request) {
XxlJobUser loginUser = (XxlJobUser) request.getAttribute("loginUser"); // get
// loginUser,
// with
// request
return loginUser;
}
/**
* valid permission by JobGroup
* @param request
* @param jobGroup
*/
public static void validJobGroupPermission(HttpServletRequest request, int jobGroup) {
XxlJobUser loginUser = getLoginUser(request);
if (!loginUser.validPermission(jobGroup)) {
throw new RuntimeException(
I18nUtil.getString("system_permission_limit") + "[username=" + loginUser.getUsername() + "]");
}
}
/**
* filter XxlJobGroup by role
* @param request
* @param jobGroupList_all
* @return
*/
public static List<XxlJobGroup> filterJobGroupByRole(HttpServletRequest request,
List<XxlJobGroup> jobGroupList_all) {
List<XxlJobGroup> jobGroupList = new ArrayList<>();
if (jobGroupList_all != null && jobGroupList_all.size() > 0) {
XxlJobUser loginUser = PermissionInterceptor.getLoginUser(request);
if (loginUser.getRole() == 1) {
jobGroupList = jobGroupList_all;
}
else {
List<String> groupIdStrs = new ArrayList<>();
if (loginUser.getPermission() != null && loginUser.getPermission().trim().length() > 0) {
groupIdStrs = Arrays.asList(loginUser.getPermission().trim().split(","));
}
for (XxlJobGroup groupItem : jobGroupList_all) {
if (groupIdStrs.contains(String.valueOf(groupItem.getId()))) {
jobGroupList.add(groupItem);
}
}
}
}
return jobGroupList;
}
}
@@ -1,28 +0,0 @@
package com.xxl.job.admin.controller.interceptor;
import jakarta.annotation.Resource;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
/**
* web mvc config
*
* @author xuxueli 2018-04-02 20:48:20
*/
@Configuration
public class WebMvcConfig implements WebMvcConfigurer {
@Resource
private PermissionInterceptor permissionInterceptor;
@Resource
private CookieInterceptor cookieInterceptor;
@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(permissionInterceptor).addPathPatterns("/**");
registry.addInterceptor(cookieInterceptor).addPathPatterns("/**");
}
}
@@ -1,19 +0,0 @@
package com.xxl.job.admin.core.alarm;
import com.xxl.job.admin.core.model.XxlJobInfo;
import com.xxl.job.admin.core.model.XxlJobLog;
/**
* @author xuxueli 2020-01-19
*/
public interface JobAlarm {
/**
* job alarm
* @param info
* @param jobLog
* @return
*/
public boolean doAlarm(XxlJobInfo info, XxlJobLog jobLog);
}
@@ -1,67 +0,0 @@
package com.xxl.job.admin.core.alarm;
import com.xxl.job.admin.core.model.XxlJobInfo;
import com.xxl.job.admin.core.model.XxlJobLog;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.stereotype.Component;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
@Component
public class JobAlarmer implements ApplicationContextAware, InitializingBean {
private static Logger logger = LoggerFactory.getLogger(JobAlarmer.class);
private ApplicationContext applicationContext;
private List<JobAlarm> jobAlarmList;
@Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
this.applicationContext = applicationContext;
}
@Override
public void afterPropertiesSet() throws Exception {
Map<String, JobAlarm> serviceBeanMap = applicationContext.getBeansOfType(JobAlarm.class);
if (serviceBeanMap != null && serviceBeanMap.size() > 0) {
jobAlarmList = new ArrayList<JobAlarm>(serviceBeanMap.values());
}
}
/**
* job alarm
* @param info
* @param jobLog
* @return
*/
public boolean alarm(XxlJobInfo info, XxlJobLog jobLog) {
boolean result = false;
if (jobAlarmList != null && jobAlarmList.size() > 0) {
result = true; // success means all-success
for (JobAlarm alarm : jobAlarmList) {
boolean resultItem = false;
try {
resultItem = alarm.doAlarm(info, jobLog);
}
catch (Exception e) {
logger.error(e.getMessage(), e);
}
if (!resultItem) {
result = false;
}
}
}
return result;
}
}
@@ -1,109 +0,0 @@
package com.xxl.job.admin.core.alarm.impl;
import com.xxl.job.admin.core.alarm.JobAlarm;
import com.xxl.job.admin.core.conf.XxlJobAdminConfig;
import com.xxl.job.admin.core.model.XxlJobGroup;
import com.xxl.job.admin.core.model.XxlJobInfo;
import com.xxl.job.admin.core.model.XxlJobLog;
import com.xxl.job.admin.core.util.I18nUtil;
import com.xxl.job.core.biz.model.ReturnT;
import jakarta.mail.internet.MimeMessage;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.mail.javamail.MimeMessageHelper;
import org.springframework.stereotype.Component;
import java.text.MessageFormat;
import java.util.Arrays;
import java.util.HashSet;
import java.util.Set;
/**
* job alarm by email
*
* @author xuxueli 2020-01-19
*/
@Component
public class EmailJobAlarm implements JobAlarm {
private static Logger logger = LoggerFactory.getLogger(EmailJobAlarm.class);
/**
* fail alarm
* @param jobLog
*/
@Override
public boolean doAlarm(XxlJobInfo info, XxlJobLog jobLog) {
boolean alarmResult = true;
// send monitor email
if (info != null && info.getAlarmEmail() != null && info.getAlarmEmail().trim().length() > 0) {
// alarmContent
String alarmContent = "Alarm Job LogId=" + jobLog.getId();
if (jobLog.getTriggerCode() != ReturnT.SUCCESS_CODE) {
alarmContent += "<br>TriggerMsg=<br>" + jobLog.getTriggerMsg();
}
if (jobLog.getHandleCode() > 0 && jobLog.getHandleCode() != ReturnT.SUCCESS_CODE) {
alarmContent += "<br>HandleCode=" + jobLog.getHandleMsg();
}
// email info
XxlJobGroup group = XxlJobAdminConfig.getAdminConfig()
.getXxlJobGroupDao()
.load(Integer.valueOf(info.getJobGroup()));
String personal = I18nUtil.getString("admin_name_full");
String title = I18nUtil.getString("jobconf_monitor");
String content = MessageFormat.format(loadEmailJobAlarmTemplate(),
group != null ? group.getTitle() : "null", info.getId(), info.getJobDesc(), alarmContent);
Set<String> emailSet = new HashSet<String>(Arrays.asList(info.getAlarmEmail().split(",")));
for (String email : emailSet) {
// make mail
try {
MimeMessage mimeMessage = XxlJobAdminConfig.getAdminConfig().getMailSender().createMimeMessage();
MimeMessageHelper helper = new MimeMessageHelper(mimeMessage, true);
helper.setFrom(XxlJobAdminConfig.getAdminConfig().getEmailFrom(), personal);
helper.setTo(email);
helper.setSubject(title);
helper.setText(content, true);
XxlJobAdminConfig.getAdminConfig().getMailSender().send(mimeMessage);
}
catch (Exception e) {
logger.error(">>>>>>>>>>> xxl-job, job fail alarm email send error, JobLogId:{}", jobLog.getId(),
e);
alarmResult = false;
}
}
}
return alarmResult;
}
/**
* load email job alarm template
* @return
*/
private static final String loadEmailJobAlarmTemplate() {
String mailBodyTemplate = "<h5>" + I18nUtil.getString("jobconf_monitor_detail") + ":</span>"
+ "<table border=\"1\" cellpadding=\"3\" style=\"border-collapse:collapse; width:80%;\" >\n"
+ " <thead style=\"font-weight: bold;color: #ffffff;background-color: #ff8c00;\" >" + " <tr>\n"
+ " <td width=\"20%\" >" + I18nUtil.getString("jobinfo_field_jobgroup") + "</td>\n"
+ " <td width=\"10%\" >" + I18nUtil.getString("jobinfo_field_id") + "</td>\n"
+ " <td width=\"20%\" >" + I18nUtil.getString("jobinfo_field_jobdesc") + "</td>\n"
+ " <td width=\"10%\" >" + I18nUtil.getString("jobconf_monitor_alarm_title") + "</td>\n"
+ " <td width=\"40%\" >" + I18nUtil.getString("jobconf_monitor_alarm_content") + "</td>\n"
+ " </tr>\n" + " </thead>\n" + " <tbody>\n" + " <tr>\n" + " <td>{0}</td>\n"
+ " <td>{1}</td>\n" + " <td>{2}</td>\n" + " <td>"
+ I18nUtil.getString("jobconf_monitor_alarm_type") + "</td>\n" + " <td>{3}</td>\n"
+ " </tr>\n" + " </tbody>\n" + "</table>";
return mailBodyTemplate;
}
}
@@ -1,110 +0,0 @@
package com.xxl.job.admin.core.complete;
import com.xxl.job.admin.core.conf.XxlJobAdminConfig;
import com.xxl.job.admin.core.model.XxlJobInfo;
import com.xxl.job.admin.core.model.XxlJobLog;
import com.xxl.job.admin.core.thread.JobTriggerPoolHelper;
import com.xxl.job.admin.core.trigger.TriggerTypeEnum;
import com.xxl.job.admin.core.util.I18nUtil;
import com.xxl.job.core.biz.model.ReturnT;
import com.xxl.job.core.context.XxlJobContext;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.text.MessageFormat;
/**
* @author xuxueli 2020-10-30 20:43:10
*/
public class XxlJobCompleter {
private static Logger logger = LoggerFactory.getLogger(XxlJobCompleter.class);
/**
* common fresh handle entrance (limit only once)
* @param xxlJobLog
* @return
*/
public static int updateHandleInfoAndFinish(XxlJobLog xxlJobLog) {
// finish
finishJob(xxlJobLog);
// text最大64kb 避免长度过长
if (xxlJobLog.getHandleMsg().length() > 15000) {
xxlJobLog.setHandleMsg(xxlJobLog.getHandleMsg().substring(0, 15000));
}
// fresh handle
return XxlJobAdminConfig.getAdminConfig().getXxlJobLogDao().updateHandleInfo(xxlJobLog);
}
/**
* do somethind to finish job
*/
private static void finishJob(XxlJobLog xxlJobLog) {
// 1、handle success, to trigger child job
String triggerChildMsg = null;
if (XxlJobContext.HANDLE_CODE_SUCCESS == xxlJobLog.getHandleCode()) {
XxlJobInfo xxlJobInfo = XxlJobAdminConfig.getAdminConfig()
.getXxlJobInfoDao()
.loadById(xxlJobLog.getJobId());
if (xxlJobInfo != null && xxlJobInfo.getChildJobId() != null
&& xxlJobInfo.getChildJobId().trim().length() > 0) {
triggerChildMsg = "<br><br><span style=\"color:#00c0ef;\" > >>>>>>>>>>>"
+ I18nUtil.getString("jobconf_trigger_child_run") + "<<<<<<<<<<< </span><br>";
String[] childJobIds = xxlJobInfo.getChildJobId().split(",");
for (int i = 0; i < childJobIds.length; i++) {
int childJobId = (childJobIds[i] != null && childJobIds[i].trim().length() > 0
&& isNumeric(childJobIds[i])) ? Integer.valueOf(childJobIds[i]) : -1;
if (childJobId > 0) {
// valid
if (childJobId == xxlJobLog.getJobId()) {
logger.debug(
">>>>>>>>>>> xxl-job, XxlJobCompleter-finishJob ignore childJobId, childJobId {} is self.",
childJobId);
continue;
}
// trigger child job
JobTriggerPoolHelper.trigger(childJobId, TriggerTypeEnum.PARENT, -1, null, null, null);
ReturnT<String> triggerChildResult = ReturnT.SUCCESS;
// add msg
triggerChildMsg += MessageFormat.format(I18nUtil.getString("jobconf_callback_child_msg1"),
(i + 1), childJobIds.length, childJobIds[i],
(triggerChildResult.getCode() == ReturnT.SUCCESS_CODE
? I18nUtil.getString("system_success") : I18nUtil.getString("system_fail")),
triggerChildResult.getMsg());
}
else {
triggerChildMsg += MessageFormat.format(I18nUtil.getString("jobconf_callback_child_msg2"),
(i + 1), childJobIds.length, childJobIds[i]);
}
}
}
}
if (triggerChildMsg != null) {
xxlJobLog.setHandleMsg(xxlJobLog.getHandleMsg() + triggerChildMsg);
}
// 2、fix_delay trigger next
// on the way
}
private static boolean isNumeric(String str) {
try {
int result = Integer.valueOf(str);
return true;
}
catch (NumberFormatException e) {
return false;
}
}
}
@@ -1,170 +0,0 @@
package com.xxl.job.admin.core.conf;
import com.xxl.job.admin.core.alarm.JobAlarmer;
import com.xxl.job.admin.core.scheduler.XxlJobScheduler;
import com.xxl.job.admin.dao.*;
import jakarta.annotation.Resource;
import org.springframework.beans.factory.DisposableBean;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.mail.javamail.JavaMailSender;
import org.springframework.stereotype.Component;
import javax.sql.DataSource;
import java.util.Arrays;
/**
* xxl-job config
*
* @author xuxueli 2017-04-28
*/
@Component
public class XxlJobAdminConfig implements InitializingBean, DisposableBean {
private static XxlJobAdminConfig adminConfig = null;
public static XxlJobAdminConfig getAdminConfig() {
return adminConfig;
}
// ---------------------- XxlJobScheduler ----------------------
private XxlJobScheduler xxlJobScheduler;
@Override
public void afterPropertiesSet() throws Exception {
adminConfig = this;
xxlJobScheduler = new XxlJobScheduler();
xxlJobScheduler.init();
}
@Override
public void destroy() throws Exception {
xxlJobScheduler.destroy();
}
// ---------------------- XxlJobScheduler ----------------------
// conf
@Value("${xxl.job.i18n}")
private String i18n;
@Value("${xxl.job.accessToken:}")
private String accessToken;
@Value("${xxl.job.timeout:3}")
private int timeout;
@Value("${spring.mail.from}")
private String emailFrom;
@Value("${xxl.job.triggerpool.fast.max}")
private int triggerPoolFastMax;
@Value("${xxl.job.triggerpool.slow.max}")
private int triggerPoolSlowMax;
@Value("${xxl.job.logretentiondays}")
private int logretentiondays;
// dao, service
@Resource
private XxlJobLogDao xxlJobLogDao;
@Resource
private XxlJobInfoDao xxlJobInfoDao;
@Resource
private XxlJobRegistryDao xxlJobRegistryDao;
@Resource
private XxlJobGroupDao xxlJobGroupDao;
@Resource
private XxlJobLogReportDao xxlJobLogReportDao;
@Resource
private JavaMailSender mailSender;
@Resource
private DataSource dataSource;
@Resource
private JobAlarmer jobAlarmer;
public String getI18n() {
if (!Arrays.asList("zh_CN", "zh_TC", "en").contains(i18n)) {
return "zh_CN";
}
return i18n;
}
public String getAccessToken() {
return accessToken;
}
public int getTimeout() {
return timeout;
}
public String getEmailFrom() {
return emailFrom;
}
public int getTriggerPoolFastMax() {
if (triggerPoolFastMax < 200) {
return 200;
}
return triggerPoolFastMax;
}
public int getTriggerPoolSlowMax() {
if (triggerPoolSlowMax < 100) {
return 100;
}
return triggerPoolSlowMax;
}
public int getLogretentiondays() {
if (logretentiondays < 3) {
return -1; // Limit greater than or equal to 3, otherwise close
}
return logretentiondays;
}
public XxlJobLogDao getXxlJobLogDao() {
return xxlJobLogDao;
}
public XxlJobInfoDao getXxlJobInfoDao() {
return xxlJobInfoDao;
}
public XxlJobRegistryDao getXxlJobRegistryDao() {
return xxlJobRegistryDao;
}
public XxlJobGroupDao getXxlJobGroupDao() {
return xxlJobGroupDao;
}
public XxlJobLogReportDao getXxlJobLogReportDao() {
return xxlJobLogReportDao;
}
public JavaMailSender getMailSender() {
return mailSender;
}
public DataSource getDataSource() {
return dataSource;
}
public JobAlarmer getJobAlarmer() {
return jobAlarmer;
}
}
@@ -1,15 +0,0 @@
package com.xxl.job.admin.core.exception;
/**
* @author xuxueli 2019-05-04 23:19:29
*/
public class XxlJobException extends RuntimeException {
public XxlJobException() {
}
public XxlJobException(String message) {
super(message);
}
}
@@ -1,83 +0,0 @@
package com.xxl.job.admin.core.model;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Date;
import java.util.List;
/**
* Created by xuxueli on 16/9/30.
*/
public class XxlJobGroup {
private int id;
private String appname;
private String title;
private int addressType; // 执行器地址类型:0=自动注册、1=手动录入
private String addressList; // 执行器地址列表,多地址逗号分隔(手动录入)
private Date updateTime;
// registry list
private List<String> registryList; // 执行器地址列表(系统注册)
public List<String> getRegistryList() {
if (addressList != null && addressList.trim().length() > 0) {
registryList = new ArrayList<String>(Arrays.asList(addressList.split(",")));
}
return registryList;
}
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getAppname() {
return appname;
}
public void setAppname(String appname) {
this.appname = appname;
}
public String getTitle() {
return title;
}
public void setTitle(String title) {
this.title = title;
}
public int getAddressType() {
return addressType;
}
public void setAddressType(int addressType) {
this.addressType = addressType;
}
public String getAddressList() {
return addressList;
}
public Date getUpdateTime() {
return updateTime;
}
public void setUpdateTime(Date updateTime) {
this.updateTime = updateTime;
}
public void setAddressList(String addressList) {
this.addressList = addressList;
}
}
@@ -1,57 +0,0 @@
package com.xxl.job.admin.core.model;
import java.util.Date;
public class XxlJobLogReport {
private int id;
private Date triggerDay;
private int runningCount;
private int sucCount;
private int failCount;
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public Date getTriggerDay() {
return triggerDay;
}
public void setTriggerDay(Date triggerDay) {
this.triggerDay = triggerDay;
}
public int getRunningCount() {
return runningCount;
}
public void setRunningCount(int runningCount) {
this.runningCount = runningCount;
}
public int getSucCount() {
return sucCount;
}
public void setSucCount(int sucCount) {
this.sucCount = sucCount;
}
public int getFailCount() {
return failCount;
}
public void setFailCount(int failCount) {
this.failCount = failCount;
}
}
@@ -1,60 +0,0 @@
package com.xxl.job.admin.core.model;
import java.util.Date;
/**
* Created by xuxueli on 16/9/30.
*/
public class XxlJobRegistry {
private int id;
private String registryGroup;
private String registryKey;
private String registryValue;
private Date updateTime;
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getRegistryGroup() {
return registryGroup;
}
public void setRegistryGroup(String registryGroup) {
this.registryGroup = registryGroup;
}
public String getRegistryKey() {
return registryKey;
}
public void setRegistryKey(String registryKey) {
this.registryKey = registryKey;
}
public String getRegistryValue() {
return registryValue;
}
public void setRegistryValue(String registryValue) {
this.registryValue = registryValue;
}
public Date getUpdateTime() {
return updateTime;
}
public void setUpdateTime(Date updateTime) {
this.updateTime = updateTime;
}
}
@@ -1,33 +0,0 @@
// package com.xxl.job.admin.core.jobbean;
//
// import com.xxl.job.admin.core.thread.JobTriggerPoolHelper;
// import com.xxl.job.admin.core.trigger.TriggerTypeEnum;
// import org.quartz.JobExecutionContext;
// import org.quartz.JobExecutionException;
// import org.quartz.JobKey;
// import org.slf4j.Logger;
// import org.slf4j.LoggerFactory;
// import org.springframework.scheduling.quartz.QuartzJobBean;
//
/// **
// * http job bean
// * “@DisallowConcurrentExecution” disable concurrent, thread size can not be only one,
// better given more
// * @author xuxueli 2015-12-17 18:20:34
// */
//// @DisallowConcurrentExecution
// public class RemoteHttpJobBean extends QuartzJobBean {
// private static Logger logger = LoggerFactory.getLogger(RemoteHttpJobBean.class);
//
// @Override
// protected void executeInternal(JobExecutionContext context)
// throws JobExecutionException {
//
// // load jobId
// JobKey jobKey = context.getTrigger().getJobKey();
// Integer jobId = Integer.valueOf(jobKey.getName());
//
//
// }
//
// }
@@ -1,430 +0,0 @@
// package com.xxl.job.admin.core.schedule;
//
// import com.xxl.job.admin.core.conf.XxlJobAdminConfig;
// import com.xxl.job.admin.core.jobbean.RemoteHttpJobBean;
// import com.xxl.job.admin.core.model.XxlJobInfo;
// import com.xxl.job.admin.core.thread.JobFailMonitorHelper;
// import com.xxl.job.admin.core.thread.JobRegistryMonitorHelper;
// import com.xxl.job.admin.core.thread.JobTriggerPoolHelper;
// import com.xxl.job.admin.core.util.I18nUtil;
// import com.xxl.job.core.biz.AdminBiz;
// import com.xxl.job.core.biz.ExecutorBiz;
// import com.xxl.job.core.enums.ExecutorBlockStrategyEnum;
// import com.xxl.rpc.remoting.invoker.XxlRpcInvokerFactory;
// import com.xxl.rpc.remoting.invoker.call.CallType;
// import com.xxl.rpc.remoting.invoker.reference.XxlRpcReferenceBean;
// import com.xxl.rpc.remoting.invoker.route.LoadBalance;
// import com.xxl.rpc.remoting.net.NetEnum;
// import com.xxl.rpc.remoting.net.impl.servlet.server.ServletServerHandler;
// import com.xxl.rpc.remoting.provider.XxlRpcProviderFactory;
// import com.xxl.rpc.serialize.Serializer;
// import org.quartz.*;
// import org.quartz.Trigger.TriggerState;
// import org.quartz.impl.triggers.CronTriggerImpl;
// import org.slf4j.Logger;
// import org.slf4j.LoggerFactory;
// import org.springframework.util.Assert;
//
// import javax.servlet.ServletException;
// import javax.servlet.http.HttpServletRequest;
// import javax.servlet.http.HttpServletResponse;
// import java.io.IOException;
// import java.util.Date;
// import java.util.concurrent.ConcurrentHashMap;
//
/// **
// * base quartz scheduler util
// * @author xuxueli 2015-12-19 16:13:53
// */
// public final class XxlJobDynamicScheduler {
// private static final Logger logger =
// LoggerFactory.getLogger(XxlJobDynamicScheduler_old.class);
//
// // ---------------------- param ----------------------
//
// // scheduler
// private static Scheduler scheduler;
// public void setScheduler(Scheduler scheduler) {
// XxlJobDynamicScheduler_old.scheduler = scheduler;
// }
//
//
// // ---------------------- init + destroy ----------------------
// public void start() throws Exception {
// // valid
// Assert.notNull(scheduler, "quartz scheduler is null");
//
// // init i18n
// initI18n();
//
// // admin registry monitor run
// JobRegistryMonitorHelper.getInstance().start();
//
// // admin monitor run
// JobFailMonitorHelper.getInstance().start();
//
// // admin-server
// initRpcProvider();
//
// logger.info(">>>>>>>>> init xxl-job admin success.");
// }
//
//
// public void destroy() throws Exception {
// // admin trigger pool stop
// JobTriggerPoolHelper.toStop();
//
// // admin registry stop
// JobRegistryMonitorHelper.getInstance().toStop();
//
// // admin monitor stop
// JobFailMonitorHelper.getInstance().toStop();
//
// // admin-server
// stopRpcProvider();
// }
//
//
// // ---------------------- I18n ----------------------
//
// private void initI18n(){
// for (ExecutorBlockStrategyEnum item:ExecutorBlockStrategyEnum.values()) {
// item.setTitle(I18nUtil.getString("jobconf_block_".concat(item.name())));
// }
// }
//
//
// // ---------------------- admin rpc provider (no server version) ----------------------
// private static ServletServerHandler servletServerHandler;
// private void initRpcProvider(){
// // init
// XxlRpcProviderFactory xxlRpcProviderFactory = new XxlRpcProviderFactory();
// xxlRpcProviderFactory.initConfig(
// NetEnum.NETTY_HTTP,
// Serializer.SerializeEnum.HESSIAN.getSerializer(),
// null,
// 0,
// XxlJobAdminConfig.getAdminConfig().getAccessToken(),
// null,
// null);
//
// // add services
// xxlRpcProviderFactory.addService(AdminBiz.class.getName(), null,
// XxlJobAdminConfig.getAdminConfig().getAdminBiz());
//
// // servlet handler
// servletServerHandler = new ServletServerHandler(xxlRpcProviderFactory);
// }
// private void stopRpcProvider() throws Exception {
// XxlRpcInvokerFactory.getInstance().stop();
// }
// public static void invokeAdminService(HttpServletRequest request, HttpServletResponse
// response) throws IOException, ServletException {
// servletServerHandler.handle(null, request, response);
// }
//
//
// // ---------------------- executor-client ----------------------
// private static ConcurrentHashMap<String, ExecutorBiz> executorBizRepository = new
// ConcurrentHashMap<String, ExecutorBiz>();
// public static ExecutorBiz getExecutorBiz(String address) throws Exception {
// // valid
// if (address==null || address.trim().length()==0) {
// return null;
// }
//
// // load-cache
// address = address.trim();
// ExecutorBiz executorBiz = executorBizRepository.get(address);
// if (executorBiz != null) {
// return executorBiz;
// }
//
// // set-cache
// executorBiz = (ExecutorBiz) new XxlRpcReferenceBean(
// NetEnum.NETTY_HTTP,
// Serializer.SerializeEnum.HESSIAN.getSerializer(),
// CallType.SYNC,
// LoadBalance.ROUND,
// ExecutorBiz.class,
// null,
// 5000,
// address,
// XxlJobAdminConfig.getAdminConfig().getAccessToken(),
// null,
// null).getObject();
//
// executorBizRepository.put(address, executorBiz);
// return executorBiz;
// }
//
//
// // ---------------------- schedule util ----------------------
//
// /**
// * fill job info
// *
// * @param jobInfo
// */
// public static void fillJobInfo(XxlJobInfo jobInfo) {
//
// String name = String.valueOf(jobInfo.getId());
//
// // trigger key
// TriggerKey triggerKey = TriggerKey.triggerKey(name);
// try {
//
// // trigger cron
// Trigger trigger = scheduler.getTrigger(triggerKey);
// if (trigger!=null && trigger instanceof CronTriggerImpl) {
// String cronExpression = ((CronTriggerImpl) trigger).getCronExpression();
// jobInfo.setJobCron(cronExpression);
// }
//
// // trigger state
// TriggerState triggerState = scheduler.getTriggerState(triggerKey);
// if (triggerState!=null) {
// jobInfo.setJobStatus(triggerState.name());
// }
//
// //JobKey jobKey = new JobKey(jobInfo.getJobName(),
// String.valueOf(jobInfo.getJobGroup()));
// //JobDetail jobDetail = scheduler.getJobDetail(jobKey);
// //String jobClass = jobDetail.getJobClass().getName();
//
// } catch (SchedulerException e) {
// logger.error(e.getMessage(), e);
// }
// }
//
//
// /**
// * add trigger + job
// *
// * @param jobName
// * @param cronExpression
// * @return
// * @throws SchedulerException
// */
// public static boolean addJob(String jobName, String cronExpression) throws
// SchedulerException {
// // 1、job key
// TriggerKey triggerKey = TriggerKey.triggerKey(jobName);
// JobKey jobKey = new JobKey(jobName);
//
// // 2、valid
// if (scheduler.checkExists(triggerKey)) {
// return true; // PASS
// }
//
// // 3、corn trigger
// CronScheduleBuilder cronScheduleBuilder =
// CronScheduleBuilder.cronSchedule(cronExpression).withMisfireHandlingInstructionDoNothing();
// // withMisfireHandlingInstructionDoNothing 忽略掉调度终止过程中忽略的调度
// CronTrigger cronTrigger =
// TriggerBuilder.newTrigger().withIdentity(triggerKey).withSchedule(cronScheduleBuilder).build();
//
// // 4、job detail
// Class<? extends Job> jobClass_ = RemoteHttpJobBean.class; //
// Class.forName(jobInfo.getJobClass());
// JobDetail jobDetail = JobBuilder.newJob(jobClass_).withIdentity(jobKey).build();
//
// /*if (jobInfo.getJobData()!=null) {
// JobDataMap jobDataMap = jobDetail.getJobDataMap();
// jobDataMap.putAll(JacksonUtil.readValue(jobInfo.getJobData(), Map.class));
// // JobExecutionContext context.getMergedJobDataMap().get("mailGuid");
// }*/
//
// // 5、schedule job
// Date date = scheduler.scheduleJob(jobDetail, cronTrigger);
//
// logger.info(">>>>>>>>>>> addJob success(quartz), jobDetail:{}, cronTrigger:{},
// date:{}", jobDetail, cronTrigger, date);
// return true;
// }
//
//
// /**
// * remove trigger + job
// *
// * @param jobName
// * @return
// * @throws SchedulerException
// */
// public static boolean removeJob(String jobName) throws SchedulerException {
//
// JobKey jobKey = new JobKey(jobName);
// scheduler.deleteJob(jobKey);
//
// /*TriggerKey triggerKey = TriggerKey.triggerKey(jobName);
// if (scheduler.checkExists(triggerKey)) {
// scheduler.unscheduleJob(triggerKey); // trigger + job
// }*/
//
// logger.info(">>>>>>>>>>> removeJob success(quartz), jobKey:{}", jobKey);
// return true;
// }
//
//
// /**
// * updateJobCron
// *
// * @param jobName
// * @param cronExpression
// * @return
// * @throws SchedulerException
// */
// public static boolean updateJobCron(String jobName, String cronExpression) throws
// SchedulerException {
//
// // 1、job key
// TriggerKey triggerKey = TriggerKey.triggerKey(jobName);
//
// // 2、valid
// if (!scheduler.checkExists(triggerKey)) {
// return true; // PASS
// }
//
// CronTrigger oldTrigger = (CronTrigger) scheduler.getTrigger(triggerKey);
//
// // 3、avoid repeat cron
// String oldCron = oldTrigger.getCronExpression();
// if (oldCron.equals(cronExpression)){
// return true; // PASS
// }
//
// // 4、new cron trigger
// CronScheduleBuilder cronScheduleBuilder =
// CronScheduleBuilder.cronSchedule(cronExpression).withMisfireHandlingInstructionDoNothing();
// oldTrigger =
// oldTrigger.getTriggerBuilder().withIdentity(triggerKey).withSchedule(cronScheduleBuilder).build();
//
// // 5、rescheduleJob
// scheduler.rescheduleJob(triggerKey, oldTrigger);
//
// /*
// JobKey jobKey = new JobKey(jobName);
//
// // old job detail
// JobDetail jobDetail = scheduler.getJobDetail(jobKey);
//
// // new trigger
// HashSet<Trigger> triggerSet = new HashSet<Trigger>();
// triggerSet.add(cronTrigger);
// // cover trigger of job detail
// scheduler.scheduleJob(jobDetail, triggerSet, true);*/
//
// logger.info(">>>>>>>>>>> resumeJob success, JobName:{}", jobName);
// return true;
// }
//
//
// /**
// * pause
// *
// * @param jobName
// * @return
// * @throws SchedulerException
// */
// /*public static boolean pauseJob(String jobName) throws SchedulerException {
//
// TriggerKey triggerKey = TriggerKey.triggerKey(jobName);
//
// boolean result = false;
// if (scheduler.checkExists(triggerKey)) {
// scheduler.pauseTrigger(triggerKey);
// result = true;
// }
//
// logger.info(">>>>>>>>>>> pauseJob {}, triggerKey:{}",
// (result?"success":"fail"),triggerKey);
// return result;
// }*/
//
//
// /**
// * resume
// *
// * @param jobName
// * @return
// * @throws SchedulerException
// */
// /*public static boolean resumeJob(String jobName) throws SchedulerException {
//
// TriggerKey triggerKey = TriggerKey.triggerKey(jobName);
//
// boolean result = false;
// if (scheduler.checkExists(triggerKey)) {
// scheduler.resumeTrigger(triggerKey);
// result = true;
// }
//
// logger.info(">>>>>>>>>>> resumeJob {}, triggerKey:{}", (result?"success":"fail"),
// triggerKey);
// return result;
// }*/
//
//
// /**
// * run
// *
// * @param jobName
// * @return
// * @throws SchedulerException
// */
// /*public static boolean triggerJob(String jobName) throws SchedulerException {
// // TriggerKey : name + group
// JobKey jobKey = new JobKey(jobName);
// TriggerKey triggerKey = TriggerKey.triggerKey(jobName);
//
// boolean result = false;
// if (scheduler.checkExists(triggerKey)) {
// scheduler.triggerJob(jobKey);
// result = true;
// logger.info(">>>>>>>>>>> runJob success, jobKey:{}", jobKey);
// } else {
// logger.info(">>>>>>>>>>> runJob fail, jobKey:{}", jobKey);
// }
// return result;
// }*/
//
//
// /**
// * finaAllJobList
// *
// * @return
// *//*
// @Deprecated
// public static List<Map<String, Object>> finaAllJobList(){
// List<Map<String, Object>> jobList = new ArrayList<Map<String,Object>>();
//
// try {
// if (scheduler.getJobGroupNames()==null || scheduler.getJobGroupNames().size()==0) {
// return null;
// }
// String groupName = scheduler.getJobGroupNames().get(0);
// Set<JobKey> jobKeys = scheduler.getJobKeys(GroupMatcher.jobGroupEquals(groupName));
// if (jobKeys!=null && jobKeys.size()>0) {
// for (JobKey jobKey : jobKeys) {
// TriggerKey triggerKey = TriggerKey.triggerKey(jobKey.getName(),
// Scheduler.DEFAULT_GROUP);
// Trigger trigger = scheduler.getTrigger(triggerKey);
// JobDetail jobDetail = scheduler.getJobDetail(jobKey);
// TriggerState triggerState = scheduler.getTriggerState(triggerKey);
// Map<String, Object> jobMap = new HashMap<String, Object>();
// jobMap.put("TriggerKey", triggerKey);
// jobMap.put("Trigger", trigger);
// jobMap.put("JobDetail", jobDetail);
// jobMap.put("TriggerState", triggerState);
// jobList.add(jobMap);
// }
// }
//
// } catch (SchedulerException e) {
// logger.error(e.getMessage(), e);
// return null;
// }
// return jobList;
// }*/
//
// }
@@ -1,58 +0,0 @@
// package com.xxl.job.admin.core.quartz;
//
// import org.quartz.SchedulerConfigException;
// import org.quartz.spi.ThreadPool;
//
/// **
// * single thread pool, for async trigger
// *
// * @author xuxueli 2019-03-06
// */
// public class XxlJobThreadPool implements ThreadPool {
//
// @Override
// public boolean runInThread(Runnable runnable) {
//
// // async run
// runnable.run();
// return true;
//
// //return false;
// }
//
// @Override
// public int blockForAvailableThreads() {
// return 1;
// }
//
// @Override
// public void initialize() throws SchedulerConfigException {
//
// }
//
// @Override
// public void shutdown(boolean waitForJobsToComplete) {
//
// }
//
// @Override
// public int getPoolSize() {
// return 1;
// }
//
// @Override
// public void setInstanceId(String schedInstId) {
//
// }
//
// @Override
// public void setInstanceName(String schedName) {
//
// }
//
// // support
// public void setThreadCount(int count) {
// //
// }
//
// }
@@ -1,50 +0,0 @@
package com.xxl.job.admin.core.route;
import com.xxl.job.admin.core.route.strategy.*;
import com.xxl.job.admin.core.util.I18nUtil;
/**
* Created by xuxueli on 17/3/10.
*/
public enum ExecutorRouteStrategyEnum {
FIRST(I18nUtil.getString("jobconf_route_first"), new ExecutorRouteFirst()),
LAST(I18nUtil.getString("jobconf_route_last"), new ExecutorRouteLast()),
ROUND(I18nUtil.getString("jobconf_route_round"), new ExecutorRouteRound()),
RANDOM(I18nUtil.getString("jobconf_route_random"), new ExecutorRouteRandom()),
CONSISTENT_HASH(I18nUtil.getString("jobconf_route_consistenthash"), new ExecutorRouteConsistentHash()),
LEAST_FREQUENTLY_USED(I18nUtil.getString("jobconf_route_lfu"), new ExecutorRouteLFU()),
LEAST_RECENTLY_USED(I18nUtil.getString("jobconf_route_lru"), new ExecutorRouteLRU()),
FAILOVER(I18nUtil.getString("jobconf_route_failover"), new ExecutorRouteFailover()),
BUSYOVER(I18nUtil.getString("jobconf_route_busyover"), new ExecutorRouteBusyover()),
SHARDING_BROADCAST(I18nUtil.getString("jobconf_route_shard"), null);
ExecutorRouteStrategyEnum(String title, ExecutorRouter router) {
this.title = title;
this.router = router;
}
private String title;
private ExecutorRouter router;
public String getTitle() {
return title;
}
public ExecutorRouter getRouter() {
return router;
}
public static ExecutorRouteStrategyEnum match(String name, ExecutorRouteStrategyEnum defaultItem) {
if (name != null) {
for (ExecutorRouteStrategyEnum item : ExecutorRouteStrategyEnum.values()) {
if (item.name().equals(name)) {
return item;
}
}
}
return defaultItem;
}
}
@@ -1,24 +0,0 @@
package com.xxl.job.admin.core.route;
import com.xxl.job.core.biz.model.ReturnT;
import com.xxl.job.core.biz.model.TriggerParam;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.List;
/**
* Created by xuxueli on 17/3/10.
*/
public abstract class ExecutorRouter {
protected static Logger logger = LoggerFactory.getLogger(ExecutorRouter.class);
/**
* route address
* @param addressList
* @return ReturnT.content=address
*/
public abstract ReturnT<String> route(TriggerParam triggerParam, List<String> addressList);
}
@@ -1,52 +0,0 @@
package com.xxl.job.admin.core.route.strategy;
import com.xxl.job.admin.core.route.ExecutorRouter;
import com.xxl.job.admin.core.scheduler.XxlJobScheduler;
import com.xxl.job.admin.core.util.I18nUtil;
import com.xxl.job.core.biz.ExecutorBiz;
import com.xxl.job.core.biz.model.IdleBeatParam;
import com.xxl.job.core.biz.model.ReturnT;
import com.xxl.job.core.biz.model.TriggerParam;
import java.util.List;
/**
* Created by xuxueli on 17/3/10.
*/
public class ExecutorRouteBusyover extends ExecutorRouter {
@Override
public ReturnT<String> route(TriggerParam triggerParam, List<String> addressList) {
StringBuffer idleBeatResultSB = new StringBuffer();
for (String address : addressList) {
// beat
ReturnT<String> idleBeatResult = null;
try {
ExecutorBiz executorBiz = XxlJobScheduler.getExecutorBiz(address);
idleBeatResult = executorBiz.idleBeat(new IdleBeatParam(triggerParam.getJobId()));
}
catch (Exception e) {
logger.error(e.getMessage(), e);
idleBeatResult = new ReturnT<String>(ReturnT.FAIL_CODE, "" + e);
}
idleBeatResultSB.append((idleBeatResultSB.length() > 0) ? "<br><br>" : "")
.append(I18nUtil.getString("jobconf_idleBeat") + ":")
.append("<br>address:")
.append(address)
.append("<br>code:")
.append(idleBeatResult.getCode())
.append("<br>msg:")
.append(idleBeatResult.getMsg());
// beat success
if (idleBeatResult.getCode() == ReturnT.SUCCESS_CODE) {
idleBeatResult.setMsg(idleBeatResultSB.toString());
idleBeatResult.setContent(address);
return idleBeatResult;
}
}
return new ReturnT<String>(ReturnT.FAIL_CODE, idleBeatResultSB.toString());
}
}
@@ -1,84 +0,0 @@
package com.xxl.job.admin.core.route.strategy;
import com.xxl.job.admin.core.route.ExecutorRouter;
import com.xxl.job.core.biz.model.ReturnT;
import com.xxl.job.core.biz.model.TriggerParam;
import java.io.UnsupportedEncodingException;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.List;
import java.util.SortedMap;
import java.util.TreeMap;
/**
* 分组下机器地址相同,不同JOB均匀散列在不同机器上,保证分组下机器分配JOB平均;且每个JOB固定调度其中一台机器; a、virtual node:解决不均衡问题
* b、hash method replace hashCode:String的hashCode可能重复,需要进一步扩大hashCode的取值范围 Created by
* xuxueli on 17/3/10.
*/
public class ExecutorRouteConsistentHash extends ExecutorRouter {
private static int VIRTUAL_NODE_NUM = 100;
/**
* get hash code on 2^32 ring (md5散列的方式计算hash值)
* @param key
* @return
*/
private static long hash(String key) {
// md5 byte
MessageDigest md5;
try {
md5 = MessageDigest.getInstance("MD5");
}
catch (NoSuchAlgorithmException e) {
throw new RuntimeException("MD5 not supported", e);
}
md5.reset();
byte[] keyBytes = null;
try {
keyBytes = key.getBytes("UTF-8");
}
catch (UnsupportedEncodingException e) {
throw new RuntimeException("Unknown string :" + key, e);
}
md5.update(keyBytes);
byte[] digest = md5.digest();
// hash code, Truncate to 32-bits
long hashCode = ((long) (digest[3] & 0xFF) << 24) | ((long) (digest[2] & 0xFF) << 16)
| ((long) (digest[1] & 0xFF) << 8) | (digest[0] & 0xFF);
long truncateHashCode = hashCode & 0xffffffffL;
return truncateHashCode;
}
public String hashJob(int jobId, List<String> addressList) {
// ------A1------A2-------A3------
// -----------J1------------------
TreeMap<Long, String> addressRing = new TreeMap<Long, String>();
for (String address : addressList) {
for (int i = 0; i < VIRTUAL_NODE_NUM; i++) {
long addressHash = hash("SHARD-" + address + "-NODE-" + i);
addressRing.put(addressHash, address);
}
}
long jobHash = hash(String.valueOf(jobId));
SortedMap<Long, String> lastRing = addressRing.tailMap(jobHash);
if (!lastRing.isEmpty()) {
return lastRing.get(lastRing.firstKey());
}
return addressRing.firstEntry().getValue();
}
@Override
public ReturnT<String> route(TriggerParam triggerParam, List<String> addressList) {
String address = hashJob(triggerParam.getJobId(), addressList);
return new ReturnT<String>(address);
}
}
@@ -1,53 +0,0 @@
package com.xxl.job.admin.core.route.strategy;
import com.xxl.job.admin.core.route.ExecutorRouter;
import com.xxl.job.admin.core.scheduler.XxlJobScheduler;
import com.xxl.job.admin.core.util.I18nUtil;
import com.xxl.job.core.biz.ExecutorBiz;
import com.xxl.job.core.biz.model.ReturnT;
import com.xxl.job.core.biz.model.TriggerParam;
import java.util.List;
/**
* Created by xuxueli on 17/3/10.
*/
public class ExecutorRouteFailover extends ExecutorRouter {
@Override
public ReturnT<String> route(TriggerParam triggerParam, List<String> addressList) {
StringBuffer beatResultSB = new StringBuffer();
for (String address : addressList) {
// beat
ReturnT<String> beatResult = null;
try {
ExecutorBiz executorBiz = XxlJobScheduler.getExecutorBiz(address);
beatResult = executorBiz.beat();
}
catch (Exception e) {
logger.error(e.getMessage(), e);
beatResult = new ReturnT<String>(ReturnT.FAIL_CODE, "" + e);
}
beatResultSB.append((beatResultSB.length() > 0) ? "<br><br>" : "")
.append(I18nUtil.getString("jobconf_beat") + ":")
.append("<br>address:")
.append(address)
.append("<br>code:")
.append(beatResult.getCode())
.append("<br>msg:")
.append(beatResult.getMsg());
// beat success
if (beatResult.getCode() == ReturnT.SUCCESS_CODE) {
beatResult.setMsg(beatResultSB.toString());
beatResult.setContent(address);
return beatResult;
}
}
return new ReturnT<String>(ReturnT.FAIL_CODE, beatResultSB.toString());
}
}
@@ -1,19 +0,0 @@
package com.xxl.job.admin.core.route.strategy;
import com.xxl.job.admin.core.route.ExecutorRouter;
import com.xxl.job.core.biz.model.ReturnT;
import com.xxl.job.core.biz.model.TriggerParam;
import java.util.List;
/**
* Created by xuxueli on 17/3/10.
*/
public class ExecutorRouteFirst extends ExecutorRouter {
@Override
public ReturnT<String> route(TriggerParam triggerParam, List<String> addressList) {
return new ReturnT<String>(addressList.get(0));
}
}
@@ -1,79 +0,0 @@
package com.xxl.job.admin.core.route.strategy;
import com.xxl.job.admin.core.route.ExecutorRouter;
import com.xxl.job.core.biz.model.ReturnT;
import com.xxl.job.core.biz.model.TriggerParam;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
/**
* 单个JOB对应的每个执行器,使用频率最低的优先被选举 a(*)、LFU(Least Frequently Used):最不经常使用,频率/次数 b、LRU(Least
* Recently Used):最近最久未使用,时间
*
* Created by xuxueli on 17/3/10.
*/
public class ExecutorRouteLFU extends ExecutorRouter {
private static ConcurrentMap<Integer, HashMap<String, Integer>> jobLfuMap = new ConcurrentHashMap<Integer, HashMap<String, Integer>>();
private static long CACHE_VALID_TIME = 0;
public String route(int jobId, List<String> addressList) {
// cache clear
if (System.currentTimeMillis() > CACHE_VALID_TIME) {
jobLfuMap.clear();
CACHE_VALID_TIME = System.currentTimeMillis() + 1000 * 60 * 60 * 24;
}
// lfu item init
HashMap<String, Integer> lfuItemMap = jobLfuMap.get(jobId); // Key排序可以用TreeMap+构造入参Compare;Value排序暂时只能通过ArrayList;
if (lfuItemMap == null) {
lfuItemMap = new HashMap<String, Integer>();
jobLfuMap.putIfAbsent(jobId, lfuItemMap); // 避免重复覆盖
}
// put new
for (String address : addressList) {
if (!lfuItemMap.containsKey(address) || lfuItemMap.get(address) > 1000000) {
lfuItemMap.put(address, new Random().nextInt(addressList.size())); // 初始化时主动Random一次,缓解首次压力
}
}
// remove old
List<String> delKeys = new ArrayList<>();
for (String existKey : lfuItemMap.keySet()) {
if (!addressList.contains(existKey)) {
delKeys.add(existKey);
}
}
if (delKeys.size() > 0) {
for (String delKey : delKeys) {
lfuItemMap.remove(delKey);
}
}
// load least userd count address
List<Map.Entry<String, Integer>> lfuItemList = new ArrayList<Map.Entry<String, Integer>>(lfuItemMap.entrySet());
Collections.sort(lfuItemList, new Comparator<Map.Entry<String, Integer>>() {
@Override
public int compare(Map.Entry<String, Integer> o1, Map.Entry<String, Integer> o2) {
return o1.getValue().compareTo(o2.getValue());
}
});
Map.Entry<String, Integer> addressItem = lfuItemList.get(0);
String minAddress = addressItem.getKey();
addressItem.setValue(addressItem.getValue() + 1);
return addressItem.getKey();
}
@Override
public ReturnT<String> route(TriggerParam triggerParam, List<String> addressList) {
String address = route(triggerParam.getJobId(), addressList);
return new ReturnT<String>(address);
}
}
@@ -1,75 +0,0 @@
package com.xxl.job.admin.core.route.strategy;
import com.xxl.job.admin.core.route.ExecutorRouter;
import com.xxl.job.core.biz.model.ReturnT;
import com.xxl.job.core.biz.model.TriggerParam;
import java.util.ArrayList;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
/**
* 单个JOB对应的每个执行器,最久为使用的优先被选举 a、LFU(Least Frequently Used):最不经常使用,频率/次数 b(*)、LRU(Least
* Recently Used):最近最久未使用,时间
*
* Created by xuxueli on 17/3/10.
*/
public class ExecutorRouteLRU extends ExecutorRouter {
private static ConcurrentMap<Integer, LinkedHashMap<String, String>> jobLRUMap = new ConcurrentHashMap<Integer, LinkedHashMap<String, String>>();
private static long CACHE_VALID_TIME = 0;
public String route(int jobId, List<String> addressList) {
// cache clear
if (System.currentTimeMillis() > CACHE_VALID_TIME) {
jobLRUMap.clear();
CACHE_VALID_TIME = System.currentTimeMillis() + 1000 * 60 * 60 * 24;
}
// init lru
LinkedHashMap<String, String> lruItem = jobLRUMap.get(jobId);
if (lruItem == null) {
/**
* LinkedHashMap a、accessOrder:true=访问顺序排序(get/put时排序);false=插入顺序排期;
* b、removeEldestEntry:新增元素时将会调用,返回true时会删除最老元素;可封装LinkedHashMap并重写该方法,比如定义最大容量,超出是返回true即可实现固定长度的LRU算法;
*/
lruItem = new LinkedHashMap<String, String>(16, 0.75f, true);
jobLRUMap.putIfAbsent(jobId, lruItem);
}
// put new
for (String address : addressList) {
if (!lruItem.containsKey(address)) {
lruItem.put(address, address);
}
}
// remove old
List<String> delKeys = new ArrayList<>();
for (String existKey : lruItem.keySet()) {
if (!addressList.contains(existKey)) {
delKeys.add(existKey);
}
}
if (delKeys.size() > 0) {
for (String delKey : delKeys) {
lruItem.remove(delKey);
}
}
// load
String eldestKey = lruItem.entrySet().iterator().next().getKey();
String eldestValue = lruItem.get(eldestKey);
return eldestValue;
}
@Override
public ReturnT<String> route(TriggerParam triggerParam, List<String> addressList) {
String address = route(triggerParam.getJobId(), addressList);
return new ReturnT<String>(address);
}
}
@@ -1,19 +0,0 @@
package com.xxl.job.admin.core.route.strategy;
import com.xxl.job.admin.core.route.ExecutorRouter;
import com.xxl.job.core.biz.model.ReturnT;
import com.xxl.job.core.biz.model.TriggerParam;
import java.util.List;
/**
* Created by xuxueli on 17/3/10.
*/
public class ExecutorRouteLast extends ExecutorRouter {
@Override
public ReturnT<String> route(TriggerParam triggerParam, List<String> addressList) {
return new ReturnT<String>(addressList.get(addressList.size() - 1));
}
}
@@ -1,23 +0,0 @@
package com.xxl.job.admin.core.route.strategy;
import com.xxl.job.admin.core.route.ExecutorRouter;
import com.xxl.job.core.biz.model.ReturnT;
import com.xxl.job.core.biz.model.TriggerParam;
import java.util.List;
import java.util.Random;
/**
* Created by xuxueli on 17/3/10.
*/
public class ExecutorRouteRandom extends ExecutorRouter {
private static Random localRandom = new Random();
@Override
public ReturnT<String> route(TriggerParam triggerParam, List<String> addressList) {
String address = addressList.get(localRandom.nextInt(addressList.size()));
return new ReturnT<String>(address);
}
}
@@ -1,48 +0,0 @@
package com.xxl.job.admin.core.route.strategy;
import com.xxl.job.admin.core.route.ExecutorRouter;
import com.xxl.job.core.biz.model.ReturnT;
import com.xxl.job.core.biz.model.TriggerParam;
import java.util.List;
import java.util.Random;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
import java.util.concurrent.atomic.AtomicInteger;
/**
* Created by xuxueli on 17/3/10.
*/
public class ExecutorRouteRound extends ExecutorRouter {
private static ConcurrentMap<Integer, AtomicInteger> routeCountEachJob = new ConcurrentHashMap<>();
private static long CACHE_VALID_TIME = 0;
private static int count(int jobId) {
// cache clear
if (System.currentTimeMillis() > CACHE_VALID_TIME) {
routeCountEachJob.clear();
CACHE_VALID_TIME = System.currentTimeMillis() + 1000 * 60 * 60 * 24;
}
AtomicInteger count = routeCountEachJob.get(jobId);
if (count == null || count.get() > 1000000) {
// 初始化时主动Random一次,缓解首次压力
count = new AtomicInteger(new Random().nextInt(100));
}
else {
// count++
count.addAndGet(1);
}
routeCountEachJob.put(jobId, count);
return count.get();
}
@Override
public ReturnT<String> route(TriggerParam triggerParam, List<String> addressList) {
String address = addressList.get(count(triggerParam.getJobId()) % addressList.size());
return new ReturnT<String>(address);
}
}
@@ -1,39 +0,0 @@
package com.xxl.job.admin.core.scheduler;
import com.xxl.job.admin.core.util.I18nUtil;
/**
* @author xuxueli 2020-10-29 21:11:23
*/
public enum MisfireStrategyEnum {
/**
* do nothing
*/
DO_NOTHING(I18nUtil.getString("misfire_strategy_do_nothing")),
/**
* fire once now
*/
FIRE_ONCE_NOW(I18nUtil.getString("misfire_strategy_fire_once_now"));
private String title;
MisfireStrategyEnum(String title) {
this.title = title;
}
public String getTitle() {
return title;
}
public static MisfireStrategyEnum match(String name, MisfireStrategyEnum defaultItem) {
for (MisfireStrategyEnum item : MisfireStrategyEnum.values()) {
if (item.name().equals(name)) {
return item;
}
}
return defaultItem;
}
}
@@ -1,46 +0,0 @@
package com.xxl.job.admin.core.scheduler;
import com.xxl.job.admin.core.util.I18nUtil;
/**
* @author xuxueli 2020-10-29 21:11:23
*/
public enum ScheduleTypeEnum {
NONE(I18nUtil.getString("schedule_type_none")),
/**
* schedule by cron
*/
CRON(I18nUtil.getString("schedule_type_cron")),
/**
* schedule by fixed rate (in seconds)
*/
FIX_RATE(I18nUtil.getString("schedule_type_fix_rate")),
/**
* schedule by fix delay (in seconds), after the last time
*/
/* FIX_DELAY(I18nUtil.getString("schedule_type_fix_delay")) */;
private String title;
ScheduleTypeEnum(String title) {
this.title = title;
}
public String getTitle() {
return title;
}
public static ScheduleTypeEnum match(String name, ScheduleTypeEnum defaultItem) {
for (ScheduleTypeEnum item : ScheduleTypeEnum.values()) {
if (item.name().equals(name)) {
return item;
}
}
return defaultItem;
}
}
@@ -1,102 +0,0 @@
package com.xxl.job.admin.core.scheduler;
import com.xxl.job.admin.core.conf.XxlJobAdminConfig;
import com.xxl.job.admin.core.thread.*;
import com.xxl.job.admin.core.util.I18nUtil;
import com.xxl.job.core.biz.ExecutorBiz;
import com.xxl.job.core.biz.client.ExecutorBizClient;
import com.xxl.job.core.enums.ExecutorBlockStrategyEnum;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
/**
* @author xuxueli 2018-10-28 00:18:17
*/
public class XxlJobScheduler {
private static final Logger logger = LoggerFactory.getLogger(XxlJobScheduler.class);
public void init() throws Exception {
// init i18n
initI18n();
// admin trigger pool start
JobTriggerPoolHelper.toStart();
// admin registry monitor run
JobRegistryHelper.getInstance().start();
// admin fail-monitor run
JobFailMonitorHelper.getInstance().start();
// admin lose-monitor run ( depend on JobTriggerPoolHelper )
JobCompleteHelper.getInstance().start();
// admin log report start
JobLogReportHelper.getInstance().start();
// start-schedule ( depend on JobTriggerPoolHelper )
JobScheduleHelper.getInstance().start();
logger.info(">>>>>>>>> init xxl-job admin success.");
}
public void destroy() throws Exception {
// stop-schedule
JobScheduleHelper.getInstance().toStop();
// admin log report stop
JobLogReportHelper.getInstance().toStop();
// admin lose-monitor stop
JobCompleteHelper.getInstance().toStop();
// admin fail-monitor stop
JobFailMonitorHelper.getInstance().toStop();
// admin registry stop
JobRegistryHelper.getInstance().toStop();
// admin trigger pool stop
JobTriggerPoolHelper.toStop();
}
// ---------------------- I18n ----------------------
private void initI18n() {
for (ExecutorBlockStrategyEnum item : ExecutorBlockStrategyEnum.values()) {
item.setTitle(I18nUtil.getString("jobconf_block_".concat(item.name())));
}
}
// ---------------------- executor-client ----------------------
private static ConcurrentMap<String, ExecutorBiz> executorBizRepository = new ConcurrentHashMap<String, ExecutorBiz>();
public static ExecutorBiz getExecutorBiz(String address) throws Exception {
// valid
if (address == null || address.trim().length() == 0) {
return null;
}
// load-cache
address = address.trim();
ExecutorBiz executorBiz = executorBizRepository.get(address);
if (executorBiz != null) {
return executorBiz;
}
// set-cache
executorBiz = new ExecutorBizClient(address, XxlJobAdminConfig.getAdminConfig().getAccessToken(),
XxlJobAdminConfig.getAdminConfig().getTimeout());
executorBizRepository.put(address, executorBiz);
return executorBiz;
}
}
@@ -1,193 +0,0 @@
package com.xxl.job.admin.core.thread;
import com.xxl.job.admin.core.complete.XxlJobCompleter;
import com.xxl.job.admin.core.conf.XxlJobAdminConfig;
import com.xxl.job.admin.core.model.XxlJobLog;
import com.xxl.job.admin.core.util.I18nUtil;
import com.xxl.job.core.biz.model.HandleCallbackParam;
import com.xxl.job.core.biz.model.ReturnT;
import com.xxl.job.core.util.DateUtil;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.Date;
import java.util.List;
import java.util.concurrent.*;
/**
* job lose-monitor instance
*
* @author xuxueli 2015-9-1 18:05:56
*/
public class JobCompleteHelper {
private static Logger logger = LoggerFactory.getLogger(JobCompleteHelper.class);
private static JobCompleteHelper instance = new JobCompleteHelper();
public static JobCompleteHelper getInstance() {
return instance;
}
// ---------------------- monitor ----------------------
private ThreadPoolExecutor callbackThreadPool = null;
private Thread monitorThread;
private volatile boolean toStop = false;
public void start() {
// for callback
callbackThreadPool = new ThreadPoolExecutor(2, 20, 30L, TimeUnit.SECONDS,
new LinkedBlockingQueue<Runnable>(3000), new ThreadFactory() {
@Override
public Thread newThread(Runnable r) {
return new Thread(r, "xxl-job, admin JobLosedMonitorHelper-callbackThreadPool-" + r.hashCode());
}
}, new RejectedExecutionHandler() {
@Override
public void rejectedExecution(Runnable r, ThreadPoolExecutor executor) {
r.run();
logger.warn(
">>>>>>>>>>> xxl-job, callback too fast, match threadpool rejected handler(run now).");
}
});
// for monitor
monitorThread = new Thread(new Runnable() {
@Override
public void run() {
// wait for JobTriggerPoolHelper-init
try {
TimeUnit.MILLISECONDS.sleep(50);
}
catch (Throwable e) {
if (!toStop) {
logger.error(e.getMessage(), e);
}
}
// monitor
while (!toStop) {
try {
// 任务结果丢失处理:调度记录停留在 "运行中" 状态超过10min,且对应执行器心跳注册失败不在线,则将本地调度主动标记失败;
Date losedTime = DateUtil.addMinutes(new Date(), -10);
List<Long> losedJobIds = XxlJobAdminConfig.getAdminConfig()
.getXxlJobLogDao()
.findLostJobIds(losedTime);
if (losedJobIds != null && losedJobIds.size() > 0) {
for (Long logId : losedJobIds) {
XxlJobLog jobLog = new XxlJobLog();
jobLog.setId(logId);
jobLog.setHandleTime(new Date());
jobLog.setHandleCode(ReturnT.FAIL_CODE);
jobLog.setHandleMsg(I18nUtil.getString("joblog_lost_fail"));
XxlJobCompleter.updateHandleInfoAndFinish(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);
}
}
}
logger.info(">>>>>>>>>>> xxl-job, JobLosedMonitorHelper stop");
}
});
monitorThread.setDaemon(true);
monitorThread.setName("xxl-job, admin JobLosedMonitorHelper");
monitorThread.start();
}
public void toStop() {
toStop = true;
// stop registryOrRemoveThreadPool
callbackThreadPool.shutdownNow();
// stop monitorThread (interrupt and wait)
monitorThread.interrupt();
try {
monitorThread.join();
}
catch (Throwable e) {
logger.error(e.getMessage(), e);
}
}
// ---------------------- helper ----------------------
public ReturnT<String> callback(List<HandleCallbackParam> callbackParamList) {
callbackThreadPool.execute(new Runnable() {
@Override
public void run() {
for (HandleCallbackParam handleCallbackParam : callbackParamList) {
ReturnT<String> callbackResult = callback(handleCallbackParam);
logger.debug(">>>>>>>>> JobApiController.callback {}, handleCallbackParam={}, callbackResult={}",
(callbackResult.getCode() == ReturnT.SUCCESS_CODE ? "success" : "fail"),
handleCallbackParam, callbackResult);
}
}
});
return ReturnT.SUCCESS;
}
private ReturnT<String> callback(HandleCallbackParam handleCallbackParam) {
// valid log item
XxlJobLog log = XxlJobAdminConfig.getAdminConfig().getXxlJobLogDao().load(handleCallbackParam.getLogId());
if (log == null) {
return new ReturnT<String>(ReturnT.FAIL_CODE, "log item not found.");
}
if (log.getHandleCode() > 0) {
return new ReturnT<String>(ReturnT.FAIL_CODE, "log repeate callback."); // avoid
// repeat
// callback,
// trigger
// child
// job
// etc
}
// handle msg
StringBuffer handleMsg = new StringBuffer();
if (log.getHandleMsg() != null) {
handleMsg.append(log.getHandleMsg()).append("<br>");
}
if (handleCallbackParam.getHandleMsg() != null) {
handleMsg.append(handleCallbackParam.getHandleMsg());
}
// success, save log
log.setHandleTime(new Date());
log.setHandleCode(handleCallbackParam.getHandleCode());
log.setHandleMsg(handleMsg.toString());
XxlJobCompleter.updateHandleInfoAndFinish(log);
return ReturnT.SUCCESS;
}
}
@@ -1,132 +0,0 @@
package com.xxl.job.admin.core.thread;
import com.xxl.job.admin.core.conf.XxlJobAdminConfig;
import com.xxl.job.admin.core.model.XxlJobInfo;
import com.xxl.job.admin.core.model.XxlJobLog;
import com.xxl.job.admin.core.trigger.TriggerTypeEnum;
import com.xxl.job.admin.core.util.I18nUtil;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.List;
import java.util.concurrent.TimeUnit;
/**
* job monitor instance
*
* @author xuxueli 2015-9-1 18:05:56
*/
public class JobFailMonitorHelper {
private static Logger logger = LoggerFactory.getLogger(JobFailMonitorHelper.class);
private static JobFailMonitorHelper instance = new JobFailMonitorHelper();
public static JobFailMonitorHelper getInstance() {
return instance;
}
// ---------------------- monitor ----------------------
private Thread monitorThread;
private volatile boolean toStop = false;
public void start() {
monitorThread = new Thread(new Runnable() {
@Override
public void run() {
// monitor
while (!toStop) {
try {
List<Long> failLogIds = XxlJobAdminConfig.getAdminConfig()
.getXxlJobLogDao()
.findFailJobLogIds(1000);
if (failLogIds != null && !failLogIds.isEmpty()) {
for (long failLogId : failLogIds) {
// lock log
int lockRet = XxlJobAdminConfig.getAdminConfig()
.getXxlJobLogDao()
.updateAlarmStatus(failLogId, 0, -1);
if (lockRet < 1) {
continue;
}
XxlJobLog log = XxlJobAdminConfig.getAdminConfig().getXxlJobLogDao().load(failLogId);
XxlJobInfo info = XxlJobAdminConfig.getAdminConfig()
.getXxlJobInfoDao()
.loadById(log.getJobId());
// 1、fail retry monitor
if (log.getExecutorFailRetryCount() > 0) {
JobTriggerPoolHelper.trigger(log.getJobId(), TriggerTypeEnum.RETRY,
(log.getExecutorFailRetryCount() - 1), log.getExecutorShardingParam(),
log.getExecutorParam(), null);
String retryMsg = "<br><br><span style=\"color:#F39C12;\" > >>>>>>>>>>>"
+ I18nUtil.getString("jobconf_trigger_type_retry")
+ "<<<<<<<<<<< </span><br>";
log.setTriggerMsg(log.getTriggerMsg() + retryMsg);
XxlJobAdminConfig.getAdminConfig().getXxlJobLogDao().updateTriggerInfo(log);
}
// 2、fail alarm monitor
int newAlarmStatus = 0; // 告警状态:0-默认、-1=锁定状态、1-无需告警、2-告警成功、3-告警失败
if (info != null) {
boolean alarmResult = XxlJobAdminConfig.getAdminConfig()
.getJobAlarmer()
.alarm(info, log);
newAlarmStatus = alarmResult ? 2 : 3;
}
else {
newAlarmStatus = 1;
}
XxlJobAdminConfig.getAdminConfig()
.getXxlJobLogDao()
.updateAlarmStatus(failLogId, -1, newAlarmStatus);
}
}
}
catch (Throwable e) {
if (!toStop) {
logger.error(">>>>>>>>>>> xxl-job, job fail monitor thread error:{}", 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();
}
public void toStop() {
toStop = true;
// interrupt and wait
monitorThread.interrupt();
try {
monitorThread.join();
}
catch (Throwable e) {
logger.error(e.getMessage(), e);
}
}
}
@@ -1,170 +0,0 @@
package com.xxl.job.admin.core.thread;
import com.xxl.job.admin.core.conf.XxlJobAdminConfig;
import com.xxl.job.admin.core.model.XxlJobLogReport;
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 Logger logger = LoggerFactory.getLogger(JobLogReportHelper.class);
private static JobLogReportHelper instance = new JobLogReportHelper();
public static JobLogReportHelper getInstance() {
return instance;
}
private Thread logrThread;
private volatile boolean toStop = false;
public void start() {
logrThread = 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);
Map<String, Object> triggerCountMap = XxlJobAdminConfig.getAdminConfig()
.getXxlJobLogDao()
.findLogReport(todayFrom, todayTo);
if (triggerCountMap != null && triggerCountMap.size() > 0) {
int triggerDayCount = triggerCountMap.containsKey("triggerDayCount")
? Integer.valueOf(String.valueOf(triggerCountMap.get("triggerDayCount"))) : 0;
int triggerDayCountRunning = triggerCountMap.containsKey("triggerDayCountRunning")
? Integer.valueOf(String.valueOf(triggerCountMap.get("triggerDayCountRunning")))
: 0;
int triggerDayCountSuc = triggerCountMap.containsKey("triggerDayCountSuc")
? Integer.valueOf(String.valueOf(triggerCountMap.get("triggerDayCountSuc")))
: 0;
int triggerDayCountFail = triggerDayCount - triggerDayCountRunning - triggerDayCountSuc;
xxlJobLogReport.setRunningCount(triggerDayCountRunning);
xxlJobLogReport.setSucCount(triggerDayCountSuc);
xxlJobLogReport.setFailCount(triggerDayCountFail);
}
// do refresh
int ret = XxlJobAdminConfig.getAdminConfig()
.getXxlJobLogReportDao()
.update(xxlJobLogReport);
if (ret < 1) {
XxlJobAdminConfig.getAdminConfig().getXxlJobLogReportDao().save(xxlJobLogReport);
}
}
}
catch (Throwable e) {
if (!toStop) {
logger.error(">>>>>>>>>>> xxl-job, job log report thread error:{}", e);
}
}
// 2、log-clean: switch open & once each day
if (XxlJobAdminConfig.getAdminConfig().getLogretentiondays() > 0
&& System.currentTimeMillis() - lastCleanLogTime > 24 * 60 * 60 * 1000) {
// expire-time
Calendar expiredDay = Calendar.getInstance();
expiredDay.add(Calendar.DAY_OF_MONTH,
-1 * XxlJobAdminConfig.getAdminConfig().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 = XxlJobAdminConfig.getAdminConfig()
.getXxlJobLogDao()
.findClearLogIds(0, 0, clearBeforeTime, 0, 1000);
if (logIds != null && logIds.size() > 0) {
XxlJobAdminConfig.getAdminConfig().getXxlJobLogDao().clearLog(logIds);
}
}
while (logIds != null && logIds.size() > 0);
// update clean time
lastCleanLogTime = System.currentTimeMillis();
}
try {
TimeUnit.MINUTES.sleep(1);
}
catch (Throwable e) {
if (!toStop) {
logger.error(e.getMessage(), e);
}
}
}
logger.info(">>>>>>>>>>> xxl-job, job log report thread stop");
}
});
logrThread.setDaemon(true);
logrThread.setName("xxl-job, admin JobLogReportHelper");
logrThread.start();
}
public void toStop() {
toStop = true;
// interrupt and wait
logrThread.interrupt();
try {
logrThread.join();
}
catch (Throwable e) {
logger.error(e.getMessage(), e);
}
}
}
@@ -1,228 +0,0 @@
package com.xxl.job.admin.core.thread;
import com.xxl.job.admin.core.conf.XxlJobAdminConfig;
import com.xxl.job.admin.core.model.XxlJobGroup;
import com.xxl.job.admin.core.model.XxlJobRegistry;
import com.xxl.job.core.biz.model.RegistryParam;
import com.xxl.job.core.biz.model.ReturnT;
import com.xxl.job.core.enums.RegistryConfig;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.util.StringUtils;
import java.util.*;
import java.util.concurrent.*;
/**
* job registry instance
*
* @author xuxueli 2016-10-02 19:10:24
*/
public class JobRegistryHelper {
private static Logger logger = LoggerFactory.getLogger(JobRegistryHelper.class);
private static JobRegistryHelper instance = new JobRegistryHelper();
private ThreadPoolExecutor registryOrRemoveThreadPool = null;
private Thread registryMonitorThread;
private volatile boolean toStop = false;
public static JobRegistryHelper getInstance() {
return instance;
}
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 = XxlJobAdminConfig.getAdminConfig()
.getXxlJobGroupDao()
.findByAddressType(0);
if (groupList != null && !groupList.isEmpty()) {
// remove dead address (admin/executor)
List<Integer> ids = XxlJobAdminConfig.getAdminConfig()
.getXxlJobRegistryDao()
.findDead(RegistryConfig.DEAD_TIMEOUT, new Date());
if (ids != null && ids.size() > 0) {
XxlJobAdminConfig.getAdminConfig().getXxlJobRegistryDao().removeDead(ids);
}
// fresh online address (admin/executor)
HashMap<String, List<String>> appAddressMap = new HashMap<String, List<String>>();
List<XxlJobRegistry> list = XxlJobAdminConfig.getAdminConfig()
.getXxlJobRegistryDao()
.findAll(RegistryConfig.DEAD_TIMEOUT, new Date());
if (list != null) {
for (XxlJobRegistry item : list) {
if (RegistryConfig.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());
XxlJobAdminConfig.getAdminConfig().getXxlJobGroupDao().update(group);
}
}
}
catch (Throwable e) {
if (!toStop) {
logger.error(">>>>>>>>>>> xxl-job, job registry monitor thread error:{}", e);
}
}
try {
TimeUnit.SECONDS.sleep(RegistryConfig.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();
}
public void toStop() {
toStop = true;
// stop registryOrRemoveThreadPool
registryOrRemoveThreadPool.shutdownNow();
// stop monitir (interrupt and wait)
registryMonitorThread.interrupt();
try {
registryMonitorThread.join();
}
catch (Throwable e) {
logger.error(e.getMessage(), e);
}
}
// ---------------------- helper ----------------------
public ReturnT<String> registry(RegistryParam registryParam) {
// valid
if (!StringUtils.hasText(registryParam.getRegistryGroup())
|| !StringUtils.hasText(registryParam.getRegistryKey())
|| !StringUtils.hasText(registryParam.getRegistryValue())) {
return new ReturnT<String>(ReturnT.FAIL_CODE, "Illegal Argument.");
}
// async execute
registryOrRemoveThreadPool.execute(new Runnable() {
@Override
public void run() {
// 0-fail; 1-save suc; 2-update suc;
int ret = XxlJobAdminConfig.getAdminConfig()
.getXxlJobRegistryDao()
.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 ReturnT.SUCCESS;
}
public ReturnT<String> registryRemove(RegistryParam registryParam) {
// valid
if (!StringUtils.hasText(registryParam.getRegistryGroup())
|| !StringUtils.hasText(registryParam.getRegistryKey())
|| !StringUtils.hasText(registryParam.getRegistryValue())) {
return new ReturnT<String>(ReturnT.FAIL_CODE, "Illegal Argument.");
}
// async execute
registryOrRemoveThreadPool.execute(new Runnable() {
@Override
public void run() {
int ret = XxlJobAdminConfig.getAdminConfig()
.getXxlJobRegistryDao()
.registryDelete(registryParam.getRegistryGroup(), registryParam.getRegistryKey(),
registryParam.getRegistryValue());
if (ret > 0) {
// fresh (delete)
freshGroupRegistryInfo(registryParam);
}
}
});
return ReturnT.SUCCESS;
}
private void freshGroupRegistryInfo(RegistryParam registryParam) {
// Under consideration, prevent affecting core tables
}
}
@@ -1,427 +0,0 @@
package com.xxl.job.admin.core.thread;
import com.xxl.job.admin.core.conf.XxlJobAdminConfig;
import com.xxl.job.admin.core.cron.CronExpression;
import com.xxl.job.admin.core.model.XxlJobInfo;
import com.xxl.job.admin.core.scheduler.MisfireStrategyEnum;
import com.xxl.job.admin.core.scheduler.ScheduleTypeEnum;
import com.xxl.job.admin.core.trigger.TriggerTypeEnum;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.TimeUnit;
/**
* @author xuxueli 2019-05-21
*/
public class JobScheduleHelper {
private static Logger logger = LoggerFactory.getLogger(JobScheduleHelper.class);
private static JobScheduleHelper instance = new JobScheduleHelper();
public static JobScheduleHelper getInstance() {
return instance;
}
public static final long PRE_READ_MS = 5000; // pre read
private Thread scheduleThread;
private Thread ringThread;
private volatile boolean scheduleThreadToStop = false;
private volatile boolean ringThreadToStop = false;
private volatile static Map<Integer, List<Integer>> ringData = new ConcurrentHashMap<>();
public void start() {
// schedule thread
scheduleThread = new Thread(new Runnable() {
@Override
public void run() {
try {
TimeUnit.MILLISECONDS.sleep(5000 - System.currentTimeMillis() % 1000);
}
catch (Throwable e) {
if (!scheduleThreadToStop) {
logger.error(e.getMessage(), e);
}
}
logger.info(">>>>>>>>> init xxl-job admin scheduler success.");
// pre-read count: treadpool-size * trigger-qps (each trigger cost 50ms,
// qps = 1000/50 = 20)
int preReadCount = (XxlJobAdminConfig.getAdminConfig().getTriggerPoolFastMax()
+ XxlJobAdminConfig.getAdminConfig().getTriggerPoolSlowMax()) * 20;
while (!scheduleThreadToStop) {
// Scan Job
long start = System.currentTimeMillis();
Connection conn = null;
Boolean connAutoCommit = null;
PreparedStatement preparedStatement = null;
boolean preReadSuc = true;
try {
conn = XxlJobAdminConfig.getAdminConfig().getDataSource().getConnection();
connAutoCommit = conn.getAutoCommit();
conn.setAutoCommit(false);
preparedStatement = conn.prepareStatement(
"select * from xxl_job_lock where lock_name = 'schedule_lock' for update");
preparedStatement.execute();
// tx start
// 1、pre read
long nowTime = System.currentTimeMillis();
List<XxlJobInfo> scheduleList = XxlJobAdminConfig.getAdminConfig()
.getXxlJobInfoDao()
.scheduleJobQuery(nowTime + PRE_READ_MS, preReadCount);
if (scheduleList != null && scheduleList.size() > 0) {
// 2、push time-ring
for (XxlJobInfo jobInfo : scheduleList) {
// time-ring jump
if (nowTime > jobInfo.getTriggerNextTime() + PRE_READ_MS) {
// 2.1、trigger-expire > 5s:pass && make
// next-trigger-time
logger.warn(">>>>>>>>>>> xxl-job, schedule misfire, jobId = " + jobInfo.getId());
// 1、misfire match
MisfireStrategyEnum misfireStrategyEnum = MisfireStrategyEnum
.match(jobInfo.getMisfireStrategy(), MisfireStrategyEnum.DO_NOTHING);
if (MisfireStrategyEnum.FIRE_ONCE_NOW == misfireStrategyEnum) {
// FIRE_ONCE_NOW 》 trigger
JobTriggerPoolHelper.trigger(jobInfo.getId(), TriggerTypeEnum.MISFIRE, -1, null,
null, null);
logger.debug(">>>>>>>>>>> xxl-job, schedule push trigger : jobId = "
+ jobInfo.getId());
}
// 2、fresh next
refreshNextValidTime(jobInfo, new Date());
}
else if (nowTime > jobInfo.getTriggerNextTime()) {
// 2.2、trigger-expire < 5s:direct-trigger && make
// next-trigger-time
// 1、trigger
JobTriggerPoolHelper.trigger(jobInfo.getId(), TriggerTypeEnum.CRON, -1, null, null,
null);
logger.debug(
">>>>>>>>>>> xxl-job, schedule push trigger : jobId = " + jobInfo.getId());
// 2、fresh next
refreshNextValidTime(jobInfo, new Date());
// next-trigger-time in 5s, pre-read again
if (jobInfo.getTriggerStatus() == 1
&& nowTime + PRE_READ_MS > jobInfo.getTriggerNextTime()) {
// 1、make ring second
int ringSecond = (int) ((jobInfo.getTriggerNextTime() / 1000) % 60);
// 2、push time ring
pushTimeRing(ringSecond, jobInfo.getId());
// 3、fresh next
refreshNextValidTime(jobInfo, new Date(jobInfo.getTriggerNextTime()));
}
}
else {
// 2.3、trigger-pre-read:time-ring trigger && make
// next-trigger-time
// 1、make ring second
int ringSecond = (int) ((jobInfo.getTriggerNextTime() / 1000) % 60);
// 2、push time ring
pushTimeRing(ringSecond, jobInfo.getId());
// 3、fresh next
refreshNextValidTime(jobInfo, new Date(jobInfo.getTriggerNextTime()));
}
}
// 3、update trigger info
for (XxlJobInfo jobInfo : scheduleList) {
XxlJobAdminConfig.getAdminConfig().getXxlJobInfoDao().scheduleUpdate(jobInfo);
}
}
else {
preReadSuc = false;
}
// tx stop
}
catch (Throwable e) {
if (!scheduleThreadToStop) {
logger.error(">>>>>>>>>>> xxl-job, JobScheduleHelper#scheduleThread error:{}", e);
}
}
finally {
// commit
if (conn != null) {
try {
conn.commit();
}
catch (Throwable e) {
if (!scheduleThreadToStop) {
logger.error(e.getMessage(), e);
}
}
try {
conn.setAutoCommit(connAutoCommit);
}
catch (Throwable e) {
if (!scheduleThreadToStop) {
logger.error(e.getMessage(), e);
}
}
try {
conn.close();
}
catch (Throwable e) {
if (!scheduleThreadToStop) {
logger.error(e.getMessage(), e);
}
}
}
// close PreparedStatement
if (null != preparedStatement) {
try {
preparedStatement.close();
}
catch (Throwable e) {
if (!scheduleThreadToStop) {
logger.error(e.getMessage(), e);
}
}
}
}
long cost = System.currentTimeMillis() - start;
// Wait seconds, align second
if (cost < 1000) { // scan-overtime, not wait
try {
// pre-read period: success > scan each second; fail > skip
// this period;
TimeUnit.MILLISECONDS
.sleep((preReadSuc ? 1000 : PRE_READ_MS) - System.currentTimeMillis() % 1000);
}
catch (Throwable e) {
if (!scheduleThreadToStop) {
logger.error(e.getMessage(), e);
}
}
}
}
logger.info(">>>>>>>>>>> xxl-job, JobScheduleHelper#scheduleThread stop");
}
});
scheduleThread.setDaemon(true);
scheduleThread.setName("xxl-job, admin JobScheduleHelper#scheduleThread");
scheduleThread.start();
// ring thread
ringThread = new Thread(new Runnable() {
@Override
public void run() {
while (!ringThreadToStop) {
// align second
try {
TimeUnit.MILLISECONDS.sleep(1000 - System.currentTimeMillis() % 1000);
}
catch (Throwable e) {
if (!ringThreadToStop) {
logger.error(e.getMessage(), e);
}
}
try {
// second data
List<Integer> ringItemData = new ArrayList<>();
int nowSecond = Calendar.getInstance().get(Calendar.SECOND); // 避免处理耗时太长,跨过刻度,向前校验一个刻度;
for (int i = 0; i < 2; i++) {
List<Integer> tmpData = ringData.remove((nowSecond + 60 - i) % 60);
if (tmpData != null) {
ringItemData.addAll(tmpData);
}
}
// ring trigger
logger.debug(">>>>>>>>>>> xxl-job, time-ring beat : " + nowSecond + " = "
+ Arrays.asList(ringItemData));
if (ringItemData.size() > 0) {
// do trigger
for (int jobId : ringItemData) {
// do trigger
JobTriggerPoolHelper.trigger(jobId, TriggerTypeEnum.CRON, -1, null, null, null);
}
// clear
ringItemData.clear();
}
}
catch (Throwable e) {
if (!ringThreadToStop) {
logger.error(">>>>>>>>>>> xxl-job, JobScheduleHelper#ringThread error:{}", e);
}
}
}
logger.info(">>>>>>>>>>> xxl-job, JobScheduleHelper#ringThread stop");
}
});
ringThread.setDaemon(true);
ringThread.setName("xxl-job, admin JobScheduleHelper#ringThread");
ringThread.start();
}
private void refreshNextValidTime(XxlJobInfo jobInfo, Date fromTime) {
try {
Date nextValidTime = generateNextValidTime(jobInfo, fromTime);
if (nextValidTime != null) {
jobInfo.setTriggerStatus(-1); // pass, may be Inaccurate
jobInfo.setTriggerLastTime(jobInfo.getTriggerNextTime());
jobInfo.setTriggerNextTime(nextValidTime.getTime());
}
else {
// generateNextValidTime fail, stop job
jobInfo.setTriggerStatus(0);
jobInfo.setTriggerLastTime(0);
jobInfo.setTriggerNextTime(0);
logger.error(
">>>>>>>>>>> xxl-job, refreshNextValidTime fail for job: jobId={}, scheduleType={}, scheduleConf={}",
jobInfo.getId(), jobInfo.getScheduleType(), jobInfo.getScheduleConf());
}
}
catch (Throwable e) {
// generateNextValidTime error, stop job
jobInfo.setTriggerStatus(0);
jobInfo.setTriggerLastTime(0);
jobInfo.setTriggerNextTime(0);
logger.error(
">>>>>>>>>>> xxl-job, refreshNextValidTime error for job: jobId={}, scheduleType={}, scheduleConf={}",
jobInfo.getId(), jobInfo.getScheduleType(), jobInfo.getScheduleConf(), e);
}
}
private void pushTimeRing(int ringSecond, int jobId) {
// push async ring
List<Integer> ringItemData = ringData.get(ringSecond);
if (ringItemData == null) {
ringItemData = new ArrayList<Integer>();
ringData.put(ringSecond, ringItemData);
}
ringItemData.add(jobId);
logger.debug(
">>>>>>>>>>> xxl-job, schedule push time-ring : " + ringSecond + " = " + Arrays.asList(ringItemData));
}
public void toStop() {
// 1、stop schedule
scheduleThreadToStop = true;
try {
TimeUnit.SECONDS.sleep(1); // wait
}
catch (Throwable e) {
logger.error(e.getMessage(), e);
}
if (scheduleThread.getState() != Thread.State.TERMINATED) {
// interrupt and wait
scheduleThread.interrupt();
try {
scheduleThread.join();
}
catch (Throwable e) {
logger.error(e.getMessage(), e);
}
}
// if has ring data
boolean hasRingData = false;
if (!ringData.isEmpty()) {
for (int second : ringData.keySet()) {
List<Integer> tmpData = ringData.get(second);
if (tmpData != null && tmpData.size() > 0) {
hasRingData = true;
break;
}
}
}
if (hasRingData) {
try {
TimeUnit.SECONDS.sleep(8);
}
catch (Throwable e) {
logger.error(e.getMessage(), e);
}
}
// stop ring (wait job-in-memory stop)
ringThreadToStop = true;
try {
TimeUnit.SECONDS.sleep(1);
}
catch (Throwable e) {
logger.error(e.getMessage(), e);
}
if (ringThread.getState() != Thread.State.TERMINATED) {
// interrupt and wait
ringThread.interrupt();
try {
ringThread.join();
}
catch (Throwable e) {
logger.error(e.getMessage(), e);
}
}
logger.info(">>>>>>>>>>> xxl-job, JobScheduleHelper stop");
}
// ---------------------- tools ----------------------
public static Date generateNextValidTime(XxlJobInfo jobInfo, Date fromTime) throws Exception {
ScheduleTypeEnum scheduleTypeEnum = ScheduleTypeEnum.match(jobInfo.getScheduleType(), null);
if (ScheduleTypeEnum.CRON == scheduleTypeEnum) {
Date nextValidTime = new CronExpression(jobInfo.getScheduleConf()).getNextValidTimeAfter(fromTime);
return nextValidTime;
}
else if (ScheduleTypeEnum.FIX_RATE == scheduleTypeEnum /*
* || ScheduleTypeEnum.
* FIX_DELAY ==
* scheduleTypeEnum
*/) {
return new Date(fromTime.getTime() + Long.parseLong(jobInfo.getScheduleConf()) * 1000L);
}
return null;
}
}
@@ -1,154 +0,0 @@
package com.xxl.job.admin.core.thread;
import com.xxl.job.admin.core.conf.XxlJobAdminConfig;
import com.xxl.job.admin.core.trigger.TriggerTypeEnum;
import com.xxl.job.admin.core.trigger.XxlJobTrigger;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.concurrent.*;
import java.util.concurrent.atomic.AtomicInteger;
/**
* job trigger thread pool helper
*
* @author xuxueli 2018-07-03 21:08:07
*/
public class JobTriggerPoolHelper {
private static Logger logger = LoggerFactory.getLogger(JobTriggerPoolHelper.class);
// ---------------------- trigger pool ----------------------
// fast/slow thread pool
private ThreadPoolExecutor fastTriggerPool = null;
private ThreadPoolExecutor slowTriggerPool = null;
public void start() {
fastTriggerPool = new ThreadPoolExecutor(10, XxlJobAdminConfig.getAdminConfig().getTriggerPoolFastMax(), 60L,
TimeUnit.SECONDS, new LinkedBlockingQueue<Runnable>(2000), new ThreadFactory() {
@Override
public Thread newThread(Runnable r) {
return new Thread(r, "xxl-job, admin JobTriggerPoolHelper-fastTriggerPool-" + r.hashCode());
}
}, new RejectedExecutionHandler() {
@Override
public void rejectedExecution(Runnable r, ThreadPoolExecutor executor) {
logger.error(
">>>>>>>>>>> xxl-job, admin JobTriggerPoolHelper-fastTriggerPool execute too fast, Runnable="
+ r.toString());
}
});
slowTriggerPool = new ThreadPoolExecutor(10, XxlJobAdminConfig.getAdminConfig().getTriggerPoolSlowMax(), 60L,
TimeUnit.SECONDS, new LinkedBlockingQueue<Runnable>(5000), new ThreadFactory() {
@Override
public Thread newThread(Runnable r) {
return new Thread(r, "xxl-job, admin JobTriggerPoolHelper-slowTriggerPool-" + r.hashCode());
}
}, new RejectedExecutionHandler() {
@Override
public void rejectedExecution(Runnable r, ThreadPoolExecutor executor) {
logger.error(
">>>>>>>>>>> xxl-job, admin JobTriggerPoolHelper-slowTriggerPool execute too fast, Runnable="
+ r.toString());
}
});
}
public void stop() {
// triggerPool.shutdown();
fastTriggerPool.shutdownNow();
slowTriggerPool.shutdownNow();
logger.info(">>>>>>>>> xxl-job trigger thread pool shutdown success.");
}
// job timeout count
private volatile long minTim = System.currentTimeMillis() / 60000; // ms > min
private volatile ConcurrentMap<Integer, AtomicInteger> jobTimeoutCountMap = new ConcurrentHashMap<>();
/**
* add trigger
*/
public void addTrigger(final int jobId, final TriggerTypeEnum triggerType, final int failRetryCount,
final String executorShardingParam, final String executorParam, final String addressList) {
// choose thread pool
ThreadPoolExecutor triggerPool_ = fastTriggerPool;
AtomicInteger jobTimeoutCount = jobTimeoutCountMap.get(jobId);
if (jobTimeoutCount != null && jobTimeoutCount.get() > 10) { // job-timeout 10
// times in 1 min
triggerPool_ = slowTriggerPool;
}
// trigger
triggerPool_.execute(new Runnable() {
@Override
public void run() {
long start = System.currentTimeMillis();
try {
// do trigger
XxlJobTrigger.trigger(jobId, triggerType, failRetryCount, executorShardingParam, executorParam,
addressList);
}
catch (Throwable e) {
logger.error(e.getMessage(), e);
}
finally {
// check timeout-count-map
long minTim_now = System.currentTimeMillis() / 60000;
if (minTim != minTim_now) {
minTim = minTim_now;
jobTimeoutCountMap.clear();
}
// incr timeout-count-map
long cost = System.currentTimeMillis() - start;
if (cost > 500) { // ob-timeout threshold 500ms
AtomicInteger timeoutCount = jobTimeoutCountMap.putIfAbsent(jobId, new AtomicInteger(1));
if (timeoutCount != null) {
timeoutCount.incrementAndGet();
}
}
}
}
@Override
public String toString() {
return "Job Runnable, jobId:" + jobId;
}
});
}
// ---------------------- helper ----------------------
private static JobTriggerPoolHelper helper = new JobTriggerPoolHelper();
public static void toStart() {
helper.start();
}
public static void toStop() {
helper.stop();
}
/**
* @param jobId
* @param triggerType
* @param failRetryCount >=0: use this param <0: use param from job info config
* @param executorShardingParam
* @param executorParam null: use job param not null: cover job param
*/
public static void trigger(int jobId, TriggerTypeEnum triggerType, int failRetryCount, String executorShardingParam,
String executorParam, String addressList) {
helper.addTrigger(jobId, triggerType, failRetryCount, executorShardingParam, executorParam, addressList);
}
}
@@ -1,26 +0,0 @@
package com.xxl.job.admin.core.trigger;
import com.xxl.job.admin.core.util.I18nUtil;
/**
* trigger type enum
*
* @author xuxueli 2018-09-16 04:56:41
*/
public enum TriggerTypeEnum {
MANUAL(I18nUtil.getString("jobconf_trigger_type_manual")), CRON(I18nUtil.getString("jobconf_trigger_type_cron")),
RETRY(I18nUtil.getString("jobconf_trigger_type_retry")), PARENT(I18nUtil.getString("jobconf_trigger_type_parent")),
API(I18nUtil.getString("jobconf_trigger_type_api")), MISFIRE(I18nUtil.getString("jobconf_trigger_type_misfire"));
private TriggerTypeEnum(String title) {
this.title = title;
}
private String title;
public String getTitle() {
return title;
}
}
@@ -1,252 +0,0 @@
package com.xxl.job.admin.core.trigger;
import com.xxl.job.admin.core.conf.XxlJobAdminConfig;
import com.xxl.job.admin.core.model.XxlJobGroup;
import com.xxl.job.admin.core.model.XxlJobInfo;
import com.xxl.job.admin.core.model.XxlJobLog;
import com.xxl.job.admin.core.route.ExecutorRouteStrategyEnum;
import com.xxl.job.admin.core.scheduler.XxlJobScheduler;
import com.xxl.job.admin.core.util.I18nUtil;
import com.xxl.job.core.biz.ExecutorBiz;
import com.xxl.job.core.biz.model.ReturnT;
import com.xxl.job.core.biz.model.TriggerParam;
import com.xxl.job.core.enums.ExecutorBlockStrategyEnum;
import com.xxl.job.core.util.IpUtil;
import com.xxl.job.core.util.ThrowableUtil;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.Date;
/**
* xxl-job trigger Created by xuxueli on 17/7/13.
*/
public class XxlJobTrigger {
private static Logger logger = LoggerFactory.getLogger(XxlJobTrigger.class);
/**
* trigger job
* @param jobId
* @param triggerType
* @param failRetryCount >=0: use this param <0: use param from job info config
* @param executorShardingParam
* @param executorParam null: use job param not null: cover job param
* @param addressList null: use executor addressList not null: cover
*/
public static void trigger(int jobId, TriggerTypeEnum triggerType, int failRetryCount, String executorShardingParam,
String executorParam, String addressList) {
// load data
XxlJobInfo jobInfo = XxlJobAdminConfig.getAdminConfig().getXxlJobInfoDao().loadById(jobId);
if (jobInfo == null) {
logger.warn(">>>>>>>>>>>> trigger fail, jobId invalid,jobId={}", jobId);
return;
}
if (executorParam != null) {
jobInfo.setExecutorParam(executorParam);
}
int finalFailRetryCount = failRetryCount >= 0 ? failRetryCount : jobInfo.getExecutorFailRetryCount();
XxlJobGroup group = XxlJobAdminConfig.getAdminConfig().getXxlJobGroupDao().load(jobInfo.getJobGroup());
// cover addressList
if (addressList != null && addressList.trim().length() > 0) {
group.setAddressType(1);
group.setAddressList(addressList.trim());
}
// sharding param
int[] shardingParam = null;
if (executorShardingParam != null) {
String[] shardingArr = executorShardingParam.split("/");
if (shardingArr.length == 2 && isNumeric(shardingArr[0]) && isNumeric(shardingArr[1])) {
shardingParam = new int[2];
shardingParam[0] = Integer.valueOf(shardingArr[0]);
shardingParam[1] = Integer.valueOf(shardingArr[1]);
}
}
if (ExecutorRouteStrategyEnum.SHARDING_BROADCAST == ExecutorRouteStrategyEnum
.match(jobInfo.getExecutorRouteStrategy(), null) && group.getRegistryList() != null
&& !group.getRegistryList().isEmpty() && shardingParam == null) {
for (int i = 0; i < group.getRegistryList().size(); i++) {
processTrigger(group, jobInfo, finalFailRetryCount, triggerType, i, group.getRegistryList().size());
}
}
else {
if (shardingParam == null) {
shardingParam = new int[] { 0, 1 };
}
processTrigger(group, jobInfo, finalFailRetryCount, triggerType, shardingParam[0], shardingParam[1]);
}
}
private static boolean isNumeric(String str) {
try {
int result = Integer.valueOf(str);
return true;
}
catch (NumberFormatException e) {
return false;
}
}
/**
* @param group job group, registry list may be empty
* @param jobInfo
* @param finalFailRetryCount
* @param triggerType
* @param index sharding index
* @param total sharding index
*/
private static void processTrigger(XxlJobGroup group, XxlJobInfo jobInfo, int finalFailRetryCount,
TriggerTypeEnum triggerType, int index, int total) {
// param
ExecutorBlockStrategyEnum blockStrategy = ExecutorBlockStrategyEnum.match(jobInfo.getExecutorBlockStrategy(),
ExecutorBlockStrategyEnum.SERIAL_EXECUTION); // block strategy
ExecutorRouteStrategyEnum executorRouteStrategyEnum = ExecutorRouteStrategyEnum
.match(jobInfo.getExecutorRouteStrategy(), null); // route strategy
String shardingParam = (ExecutorRouteStrategyEnum.SHARDING_BROADCAST == executorRouteStrategyEnum)
? String.valueOf(index).concat("/").concat(String.valueOf(total)) : null;
// 1、save log-id
XxlJobLog jobLog = new XxlJobLog();
jobLog.setJobGroup(jobInfo.getJobGroup());
jobLog.setJobId(jobInfo.getId());
jobLog.setTriggerTime(new Date());
XxlJobAdminConfig.getAdminConfig().getXxlJobLogDao().save(jobLog);
logger.debug(">>>>>>>>>>> xxl-job trigger start, jobId:{}", jobLog.getId());
// 2、init trigger-param
TriggerParam triggerParam = new TriggerParam();
triggerParam.setJobId(jobInfo.getId());
triggerParam.setExecutorHandler(jobInfo.getExecutorHandler());
triggerParam.setExecutorParams(jobInfo.getExecutorParam());
triggerParam.setExecutorBlockStrategy(jobInfo.getExecutorBlockStrategy());
triggerParam.setExecutorTimeout(jobInfo.getExecutorTimeout());
triggerParam.setLogId(jobLog.getId());
triggerParam.setLogDateTime(jobLog.getTriggerTime().getTime());
triggerParam.setGlueType(jobInfo.getGlueType());
triggerParam.setGlueSource(jobInfo.getGlueSource());
triggerParam.setGlueUpdatetime(jobInfo.getGlueUpdatetime().getTime());
triggerParam.setBroadcastIndex(index);
triggerParam.setBroadcastTotal(total);
// 3、init address
String address = null;
ReturnT<String> routeAddressResult = null;
if (group.getRegistryList() != null && !group.getRegistryList().isEmpty()) {
if (ExecutorRouteStrategyEnum.SHARDING_BROADCAST == executorRouteStrategyEnum) {
if (index < group.getRegistryList().size()) {
address = group.getRegistryList().get(index);
}
else {
address = group.getRegistryList().get(0);
}
}
else {
routeAddressResult = executorRouteStrategyEnum.getRouter().route(triggerParam, group.getRegistryList());
if (routeAddressResult.getCode() == ReturnT.SUCCESS_CODE) {
address = routeAddressResult.getContent();
}
}
}
else {
routeAddressResult = new ReturnT<String>(ReturnT.FAIL_CODE,
I18nUtil.getString("jobconf_trigger_address_empty"));
}
// 4、trigger remote executor
ReturnT<String> triggerResult = null;
if (address != null) {
triggerResult = runExecutor(triggerParam, address);
}
else {
triggerResult = new ReturnT<String>(ReturnT.FAIL_CODE, null);
}
// 5、collection trigger info
StringBuffer triggerMsgSb = new StringBuffer();
triggerMsgSb.append(I18nUtil.getString("jobconf_trigger_type")).append(":").append(triggerType.getTitle());
triggerMsgSb.append("<br>")
.append(I18nUtil.getString("jobconf_trigger_admin_adress"))
.append(":")
.append(IpUtil.getIp());
triggerMsgSb.append("<br>")
.append(I18nUtil.getString("jobconf_trigger_exe_regtype"))
.append(":")
.append((group.getAddressType() == 0) ? I18nUtil.getString("jobgroup_field_addressType_0")
: I18nUtil.getString("jobgroup_field_addressType_1"));
triggerMsgSb.append("<br>")
.append(I18nUtil.getString("jobconf_trigger_exe_regaddress"))
.append(":")
.append(group.getRegistryList());
triggerMsgSb.append("<br>")
.append(I18nUtil.getString("jobinfo_field_executorRouteStrategy"))
.append(":")
.append(executorRouteStrategyEnum.getTitle());
if (shardingParam != null) {
triggerMsgSb.append("(" + shardingParam + ")");
}
triggerMsgSb.append("<br>")
.append(I18nUtil.getString("jobinfo_field_executorBlockStrategy"))
.append(":")
.append(blockStrategy.getTitle());
triggerMsgSb.append("<br>")
.append(I18nUtil.getString("jobinfo_field_timeout"))
.append(":")
.append(jobInfo.getExecutorTimeout());
triggerMsgSb.append("<br>")
.append(I18nUtil.getString("jobinfo_field_executorFailRetryCount"))
.append(":")
.append(finalFailRetryCount);
triggerMsgSb
.append("<br><br><span style=\"color:#00c0ef;\" > >>>>>>>>>>>" + I18nUtil.getString("jobconf_trigger_run")
+ "<<<<<<<<<<< </span><br>")
.append((routeAddressResult != null && routeAddressResult.getMsg() != null)
? routeAddressResult.getMsg() + "<br><br>" : "")
.append(triggerResult.getMsg() != null ? triggerResult.getMsg() : "");
// 6、save log trigger-info
jobLog.setExecutorAddress(address);
jobLog.setExecutorHandler(jobInfo.getExecutorHandler());
jobLog.setExecutorParam(jobInfo.getExecutorParam());
jobLog.setExecutorShardingParam(shardingParam);
jobLog.setExecutorFailRetryCount(finalFailRetryCount);
// jobLog.setTriggerTime();
jobLog.setTriggerCode(triggerResult.getCode());
jobLog.setTriggerMsg(triggerMsgSb.toString());
XxlJobAdminConfig.getAdminConfig().getXxlJobLogDao().updateTriggerInfo(jobLog);
logger.debug(">>>>>>>>>>> xxl-job trigger end, jobId:{}", jobLog.getId());
}
/**
* run executor
* @param triggerParam
* @param address
* @return
*/
public static ReturnT<String> runExecutor(TriggerParam triggerParam, String address) {
ReturnT<String> runResult = null;
try {
ExecutorBiz executorBiz = XxlJobScheduler.getExecutorBiz(address);
runResult = executorBiz.run(triggerParam);
}
catch (Exception e) {
logger.error(">>>>>>>>>>> xxl-job trigger error, please check if the executor[{}] is running.", address, e);
runResult = new ReturnT<String>(ReturnT.FAIL_CODE, ThrowableUtil.toString(e));
}
StringBuffer runResultSB = new StringBuffer(I18nUtil.getString("jobconf_trigger_run") + ":");
runResultSB.append("<br>address:").append(address);
runResultSB.append("<br>code:").append(runResult.getCode());
runResultSB.append("<br>msg:").append(runResult.getMsg());
runResult.setMsg(runResultSB.toString());
return runResult;
}
}
@@ -1,95 +0,0 @@
package com.xxl.job.admin.core.util;
import jakarta.servlet.http.Cookie;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
/**
* Cookie.Util
*
* @author xuxueli 2015-12-12 18:01:06
*/
public class CookieUtil {
// 默认缓存时间,单位/秒, 2H
private static final int COOKIE_MAX_AGE = Integer.MAX_VALUE;
// 保存路径,根路径
private static final String COOKIE_PATH = "/";
/**
* 保存
* @param response
* @param key
* @param value
* @param ifRemember
*/
public static void set(HttpServletResponse response, String key, String value, boolean ifRemember) {
int age = ifRemember ? COOKIE_MAX_AGE : -1;
set(response, key, value, null, COOKIE_PATH, age, true);
}
/**
* 保存
* @param response
* @param key
* @param value
* @param maxAge
*/
private static void set(HttpServletResponse response, String key, String value, String domain, String path,
int maxAge, boolean isHttpOnly) {
Cookie cookie = new Cookie(key, value);
if (domain != null) {
cookie.setDomain(domain);
}
cookie.setPath(path);
cookie.setMaxAge(maxAge);
cookie.setHttpOnly(isHttpOnly);
response.addCookie(cookie);
}
/**
* 查询value
* @param request
* @param key
* @return
*/
public static String getValue(HttpServletRequest request, String key) {
Cookie cookie = get(request, key);
if (cookie != null) {
return cookie.getValue();
}
return null;
}
/**
* 查询Cookie
* @param request
* @param key
*/
private static Cookie get(HttpServletRequest request, String key) {
Cookie[] arr_cookie = request.getCookies();
if (arr_cookie != null && arr_cookie.length > 0) {
for (Cookie cookie : arr_cookie) {
if (cookie.getName().equals(key)) {
return cookie;
}
}
}
return null;
}
/**
* 删除Cookie
* @param request
* @param response
* @param key
*/
public static void remove(HttpServletRequest request, HttpServletResponse response, String key) {
Cookie cookie = get(request, key);
if (cookie != null) {
set(response, key, "", null, COOKIE_PATH, 0, true);
}
}
}
@@ -1,34 +0,0 @@
package com.xxl.job.admin.core.util;
import freemarker.ext.beans.BeansWrapper;
import freemarker.ext.beans.BeansWrapperBuilder;
import freemarker.template.Configuration;
import freemarker.template.TemplateHashModel;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* ftl util
*
* @author xuxueli 2018-01-17 20:37:48
*/
public class FtlUtil {
private static Logger logger = LoggerFactory.getLogger(FtlUtil.class);
private static BeansWrapper wrapper = new BeansWrapperBuilder(Configuration.DEFAULT_INCOMPATIBLE_IMPROVEMENTS)
.build(); // BeansWrapper.getDefaultInstance();
public static TemplateHashModel generateStaticModel(String packageName) {
try {
TemplateHashModel staticModels = wrapper.getStaticModels();
TemplateHashModel fileStatics = (TemplateHashModel) staticModels.get(packageName);
return fileStatics;
}
catch (Exception e) {
logger.error(e.getMessage(), e);
}
return null;
}
}
@@ -1,81 +0,0 @@
package com.xxl.job.admin.core.util;
import com.xxl.job.admin.core.conf.XxlJobAdminConfig;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.core.io.ClassPathResource;
import org.springframework.core.io.Resource;
import org.springframework.core.io.support.EncodedResource;
import org.springframework.core.io.support.PropertiesLoaderUtils;
import java.io.IOException;
import java.text.MessageFormat;
import java.util.HashMap;
import java.util.Map;
import java.util.Properties;
/**
* i18n util
*
* @author xuxueli 2018-01-17 20:39:06
*/
public class I18nUtil {
private static Logger logger = LoggerFactory.getLogger(I18nUtil.class);
private static Properties prop = null;
public static Properties loadI18nProp() {
if (prop != null) {
return prop;
}
try {
// build i18n prop
String i18n = XxlJobAdminConfig.getAdminConfig().getI18n();
String i18nFile = MessageFormat.format("i18n/message_{0}.properties", i18n);
// load prop
Resource resource = new ClassPathResource(i18nFile);
EncodedResource encodedResource = new EncodedResource(resource, "UTF-8");
prop = PropertiesLoaderUtils.loadProperties(encodedResource);
}
catch (IOException e) {
logger.error(e.getMessage(), e);
}
return prop;
}
/**
* get val of i18n key
* @param key
* @return
*/
public static String getString(String key) {
return loadI18nProp().getProperty(key);
}
/**
* get mult val of i18n mult key, as json
* @param keys
* @return
*/
public static String getMultString(String... keys) {
Map<String, String> map = new HashMap<String, String>();
Properties prop = loadI18nProp();
if (keys != null && keys.length > 0) {
for (String key : keys) {
map.put(key, prop.getProperty(key));
}
}
else {
for (String key : prop.stringPropertyNames()) {
map.put(key, prop.getProperty(key));
}
}
String json = JacksonUtil.writeValueAsString(map);
return json;
}
}
@@ -1,100 +0,0 @@
package com.xxl.job.admin.core.util;
import com.fasterxml.jackson.core.JsonGenerationException;
import com.fasterxml.jackson.core.JsonParseException;
import com.fasterxml.jackson.databind.JavaType;
import com.fasterxml.jackson.databind.JsonMappingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.io.IOException;
/**
* Jackson util
*
* 1、obj need private and set/get; 2、do not support inner class;
*
* @author xuxueli 2015-9-25 18:02:56
*/
public class JacksonUtil {
private static Logger logger = LoggerFactory.getLogger(JacksonUtil.class);
private final static ObjectMapper objectMapper = new ObjectMapper();
public static ObjectMapper getInstance() {
return objectMapper;
}
/**
* bean、array、List、Map --> json
* @param obj
* @return json string
* @throws Exception
*/
public static String writeValueAsString(Object obj) {
try {
return getInstance().writeValueAsString(obj);
}
catch (JsonGenerationException e) {
logger.error(e.getMessage(), e);
}
catch (JsonMappingException e) {
logger.error(e.getMessage(), e);
}
catch (IOException e) {
logger.error(e.getMessage(), e);
}
return null;
}
/**
* string --> bean、Map、List(array)
* @param jsonStr
* @param clazz
* @return obj
* @throws Exception
*/
public static <T> T readValue(String jsonStr, Class<T> clazz) {
try {
return getInstance().readValue(jsonStr, clazz);
}
catch (JsonParseException e) {
logger.error(e.getMessage(), e);
}
catch (JsonMappingException e) {
logger.error(e.getMessage(), e);
}
catch (IOException e) {
logger.error(e.getMessage(), e);
}
return null;
}
/**
* string --> List<Bean>...
* @param jsonStr
* @param parametrized
* @param parameterClasses
* @param <T>
* @return
*/
public static <T> T readValue(String jsonStr, Class<?> parametrized, Class<?>... parameterClasses) {
try {
JavaType javaType = getInstance().getTypeFactory().constructParametricType(parametrized, parameterClasses);
return getInstance().readValue(jsonStr, javaType);
}
catch (JsonParseException e) {
logger.error(e.getMessage(), e);
}
catch (JsonMappingException e) {
logger.error(e.getMessage(), e);
}
catch (IOException e) {
logger.error(e.getMessage(), e);
}
return null;
}
}
@@ -1,134 +0,0 @@
package com.xxl.job.admin.core.util;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
/**
* local cache tool
*
* @author xuxueli 2018-01-22 21:37:34
*/
public class LocalCacheUtil {
private static ConcurrentMap<String, LocalCacheData> cacheRepository = new ConcurrentHashMap<String, LocalCacheData>(); // 类型建议用抽象父类,兼容性更好;
private static class LocalCacheData {
private String key;
private Object val;
private long timeoutTime;
public LocalCacheData() {
}
public LocalCacheData(String key, Object val, long timeoutTime) {
this.key = key;
this.val = val;
this.timeoutTime = timeoutTime;
}
public String getKey() {
return key;
}
public void setKey(String key) {
this.key = key;
}
public Object getVal() {
return val;
}
public void setVal(Object val) {
this.val = val;
}
public long getTimeoutTime() {
return timeoutTime;
}
public void setTimeoutTime(long timeoutTime) {
this.timeoutTime = timeoutTime;
}
}
/**
* set cache
* @param key
* @param val
* @param cacheTime
* @return
*/
public static boolean set(String key, Object val, long cacheTime) {
// clean timeout cache, before set new cache (avoid cache too much)
cleanTimeoutCache();
// set new cache
if (key == null || key.trim().length() == 0) {
return false;
}
if (val == null) {
remove(key);
}
if (cacheTime <= 0) {
remove(key);
}
long timeoutTime = System.currentTimeMillis() + cacheTime;
LocalCacheData localCacheData = new LocalCacheData(key, val, timeoutTime);
cacheRepository.put(localCacheData.getKey(), localCacheData);
return true;
}
/**
* remove cache
* @param key
* @return
*/
public static boolean remove(String key) {
if (key == null || key.trim().length() == 0) {
return false;
}
cacheRepository.remove(key);
return true;
}
/**
* get cache
* @param key
* @return
*/
public static Object get(String key) {
if (key == null || key.trim().length() == 0) {
return null;
}
LocalCacheData localCacheData = cacheRepository.get(key);
if (localCacheData != null && System.currentTimeMillis() < localCacheData.getTimeoutTime()) {
return localCacheData.getVal();
}
else {
remove(key);
return null;
}
}
/**
* clean timeout cache
* @return
*/
public static boolean cleanTimeoutCache() {
if (!cacheRepository.keySet().isEmpty()) {
for (String key : cacheRepository.keySet()) {
LocalCacheData localCacheData = cacheRepository.get(key);
if (localCacheData != null && System.currentTimeMillis() >= localCacheData.getTimeoutTime()) {
cacheRepository.remove(key);
}
}
}
return true;
}
}
@@ -1,33 +0,0 @@
package com.xxl.job.admin.dao;
import com.xxl.job.admin.core.model.XxlJobGroup;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Param;
import java.util.List;
/**
* Created by xuxueli on 16/9/30.
*/
@Mapper
public interface XxlJobGroupDao {
public List<XxlJobGroup> findAll();
public List<XxlJobGroup> findByAddressType(@Param("addressType") int addressType);
public int save(XxlJobGroup xxlJobGroup);
public int update(XxlJobGroup xxlJobGroup);
public int remove(@Param("id") int id);
public XxlJobGroup load(@Param("id") int id);
public List<XxlJobGroup> pageList(@Param("offset") int offset, @Param("pagesize") int pagesize,
@Param("appname") String appname, @Param("title") String title);
public int pageListCount(@Param("offset") int offset, @Param("pagesize") int pagesize,
@Param("appname") String appname, @Param("title") String title);
}
@@ -1,57 +0,0 @@
package com.xxl.job.admin.dao;
import com.xxl.job.admin.core.model.XxlJobInfo;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Param;
import java.util.List;
/**
* job info
*
* @author xuxueli 2016-1-12 18:03:45
*/
@Mapper
public interface XxlJobInfoDao {
public List<XxlJobInfo> pageList(@Param("offset") int offset, @Param("pagesize") int pagesize,
@Param("jobGroup") int jobGroup, @Param("triggerStatus") int triggerStatus,
@Param("jobDesc") String jobDesc, @Param("executorHandler") String executorHandler,
@Param("author") String author);
public int pageListCount(@Param("offset") int offset, @Param("pagesize") int pagesize,
@Param("jobGroup") int jobGroup, @Param("triggerStatus") int triggerStatus,
@Param("jobDesc") String jobDesc, @Param("executorHandler") String executorHandler,
@Param("author") String author);
public int save(XxlJobInfo info);
public XxlJobInfo loadById(@Param("id") int id);
public int update(XxlJobInfo xxlJobInfo);
public int delete(@Param("id") long id);
public List<XxlJobInfo> getJobsByGroup(@Param("jobGroup") int jobGroup);
public int findAllCount();
/**
* find schedule job, limit "trigger_status = 1"
* @param maxNextTime
* @param pagesize
* @return
*/
public List<XxlJobInfo> scheduleJobQuery(@Param("maxNextTime") long maxNextTime, @Param("pagesize") int pagesize);
/**
* update schedule job
*
* 1、can only update "trigger_status = 1", Avoid stopping tasks from being opened
* 2、valid "triggerStatus gte 0", filter illegal state
* @param xxlJobInfo
* @return
*/
public int scheduleUpdate(XxlJobInfo xxlJobInfo);
}
@@ -1,55 +0,0 @@
package com.xxl.job.admin.dao;
import com.xxl.job.admin.core.model.XxlJobLog;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Param;
import java.util.Date;
import java.util.List;
import java.util.Map;
/**
* job log
*
* @author xuxueli 2016-1-12 18:03:06
*/
@Mapper
public interface XxlJobLogDao {
// exist jobId not use jobGroup, not exist use jobGroup
public List<XxlJobLog> pageList(@Param("offset") int offset, @Param("pagesize") int pagesize,
@Param("jobGroup") int jobGroup, @Param("jobId") int jobId,
@Param("triggerTimeStart") Date triggerTimeStart, @Param("triggerTimeEnd") Date triggerTimeEnd,
@Param("logStatus") int logStatus);
public int pageListCount(@Param("offset") int offset, @Param("pagesize") int pagesize,
@Param("jobGroup") int jobGroup, @Param("jobId") int jobId,
@Param("triggerTimeStart") Date triggerTimeStart, @Param("triggerTimeEnd") Date triggerTimeEnd,
@Param("logStatus") int logStatus);
public XxlJobLog load(@Param("id") long id);
public long save(XxlJobLog xxlJobLog);
public int updateTriggerInfo(XxlJobLog xxlJobLog);
public int updateHandleInfo(XxlJobLog xxlJobLog);
public int delete(@Param("jobId") int jobId);
public Map<String, Object> findLogReport(@Param("from") Date from, @Param("to") Date to);
public List<Long> findClearLogIds(@Param("jobGroup") int jobGroup, @Param("jobId") int jobId,
@Param("clearBeforeTime") Date clearBeforeTime, @Param("clearBeforeNum") int clearBeforeNum,
@Param("pagesize") int pagesize);
public int clearLog(@Param("logIds") List<Long> logIds);
public List<Long> findFailJobLogIds(@Param("pagesize") int pagesize);
public int updateAlarmStatus(@Param("logId") long logId, @Param("oldAlarmStatus") int oldAlarmStatus,
@Param("newAlarmStatus") int newAlarmStatus);
public List<Long> findLostJobIds(@Param("losedTime") Date losedTime);
}
@@ -1,27 +0,0 @@
package com.xxl.job.admin.dao;
import com.xxl.job.admin.core.model.XxlJobLogReport;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Param;
import java.util.Date;
import java.util.List;
/**
* job log
*
* @author xuxueli 2019-11-22
*/
@Mapper
public interface XxlJobLogReportDao {
public int save(XxlJobLogReport xxlJobLogReport);
public int update(XxlJobLogReport xxlJobLogReport);
public List<XxlJobLogReport> queryLogReport(@Param("triggerDayFrom") Date triggerDayFrom,
@Param("triggerDayTo") Date triggerDayTo);
public XxlJobLogReport queryLogReportTotal();
}
@@ -1,47 +0,0 @@
package com.xxl.job.admin.dao;
import com.xxl.job.admin.core.model.XxlJobRegistry;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Param;
import java.util.Date;
import java.util.List;
/**
* Created by xuxueli on 16/9/30.
*/
@Mapper
public interface XxlJobRegistryDao {
public List<Integer> findDead(@Param("timeout") int timeout, @Param("nowTime") Date nowTime);
public int removeDead(@Param("ids") List<Integer> ids);
public List<XxlJobRegistry> findAll(@Param("timeout") int timeout, @Param("nowTime") Date nowTime);
public int registrySaveOrUpdate(@Param("registryGroup") String registryGroup,
@Param("registryKey") String registryKey, @Param("registryValue") String registryValue,
@Param("updateTime") Date updateTime);
/*
* public int registryUpdate(@Param("registryGroup") String registryGroup,
*
* @Param("registryKey") String registryKey,
*
* @Param("registryValue") String registryValue,
*
* @Param("updateTime") Date updateTime);
*
* public int registrySave(@Param("registryGroup") String registryGroup,
*
* @Param("registryKey") String registryKey,
*
* @Param("registryValue") String registryValue,
*
* @Param("updateTime") Date updateTime);
*/
public int registryDelete(@Param("registryGroup") String registryGroup, @Param("registryKey") String registryKey,
@Param("registryValue") String registryValue);
}
@@ -1,28 +0,0 @@
package com.xxl.job.admin.dao;
import com.xxl.job.admin.core.model.XxlJobUser;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Param;
import java.util.List;
/**
* @author xuxueli 2019-05-04 16:44:59
*/
@Mapper
public interface XxlJobUserDao {
public List<XxlJobUser> pageList(@Param("offset") int offset, @Param("pagesize") int pagesize,
@Param("username") String username, @Param("role") int role);
public int pageListCount(@Param("offset") int offset, @Param("pagesize") int pagesize,
@Param("username") String username, @Param("role") int role);
public XxlJobUser loadByUserName(@Param("username") String username);
public int save(XxlJobUser xxlJobUser);
public int update(XxlJobUser xxlJobUser);
public int delete(@Param("id") int id);
}
@@ -0,0 +1,37 @@
package com.xxl.job.admin.mapper;
import com.xxl.job.admin.model.XxlJobGroup;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Param;
import java.util.List;
/**
* Created by xuxueli on 16/9/30.
*/
@Mapper
public interface XxlJobGroupMapper {
public List<XxlJobGroup> findAll();
public List<XxlJobGroup> findByAddressType(@Param("addressType") int addressType);
public int save(XxlJobGroup xxlJobGroup);
public int update(XxlJobGroup xxlJobGroup);
public int remove(@Param("id") int id);
public XxlJobGroup load(@Param("id") int id);
public List<XxlJobGroup> pageList(@Param("offset") int offset,
@Param("pagesize") int pagesize,
@Param("appname") String appname,
@Param("title") String title);
public int pageListCount(@Param("offset") int offset,
@Param("pagesize") int pagesize,
@Param("appname") String appname,
@Param("title") String title);
}
@@ -0,0 +1,74 @@
package com.xxl.job.admin.mapper;
import com.xxl.job.admin.model.XxlJobInfo;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Param;
import java.util.List;
/**
* job info
*
* @author xuxueli 2016-1-12 18:03:45
*/
@Mapper
public interface XxlJobInfoMapper {
public List<XxlJobInfo> pageList(@Param("offset") int offset,
@Param("pagesize") int pagesize,
@Param("jobGroup") int jobGroup,
@Param("triggerStatus") int triggerStatus,
@Param("jobDesc") String jobDesc,
@Param("executorHandler") String executorHandler,
@Param("author") String author);
public int pageListCount(@Param("offset") int offset,
@Param("pagesize") int pagesize,
@Param("jobGroup") int jobGroup,
@Param("triggerStatus") int triggerStatus,
@Param("jobDesc") String jobDesc,
@Param("executorHandler") String executorHandler,
@Param("author") String author);
public int save(XxlJobInfo info);
public XxlJobInfo loadById(@Param("id") int id);
public int update(XxlJobInfo xxlJobInfo);
public int delete(@Param("id") long id);
public List<XxlJobInfo> getJobsByGroup(@Param("jobGroup") int jobGroup);
public int findAllCount();
/**
* find schedule job, limit "trigger_status = 1"
*
* @param maxNextTime
* @param pagesize
* @return
*/
public List<XxlJobInfo> scheduleJobQuery(@Param("maxNextTime") long maxNextTime, @Param("pagesize") int pagesize);
/**
* update schedule job
* <p>
* 1、can only update "trigger_status = 1", Avoid stopping tasks from being opened
* 2、valid "triggerStatus gte 0", filter illegal state
*
* @param xxlJobInfo
* @return
*/
public int scheduleUpdate(XxlJobInfo xxlJobInfo);
/**
* batch update job info
*
* @param jobInfoList
* @return
*/
public int scheduleBatchUpdate(@Param("list") List<XxlJobInfo> jobInfoList);
}
@@ -0,0 +1,18 @@
package com.xxl.job.admin.mapper;
import org.apache.ibatis.annotations.Mapper;
/**
* job lock
*
* @author xuxueli 2016-1-12 18:03:45
*/
@Mapper
public interface XxlJobLockMapper {
/**
* get schedule lock
*/
String scheduleLock();
}
@@ -1,6 +1,6 @@
package com.xxl.job.admin.dao;
package com.xxl.job.admin.mapper;
import com.xxl.job.admin.core.model.XxlJobLogGlue;
import com.xxl.job.admin.model.XxlJobLogGlue;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Param;
@@ -8,12 +8,11 @@ import java.util.List;
/**
* job log for glue
*
* @author xuxueli 2016-5-19 18:04:56
*/
@Mapper
public interface XxlJobLogGlueDao {
public interface XxlJobLogGlueMapper {
public int save(XxlJobLogGlue xxlJobLogGlue);
public List<XxlJobLogGlue> findByJobId(@Param("jobId") int jobId);
@@ -0,0 +1,65 @@
package com.xxl.job.admin.mapper;
import com.xxl.job.admin.model.XxlJobLog;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Param;
import java.util.Date;
import java.util.List;
import java.util.Map;
/**
* job log
*
* @author xuxueli 2016-1-12 18:03:06
*/
@Mapper
public interface XxlJobLogMapper {
// exist jobId not use jobGroup, not exist use jobGroup
public List<XxlJobLog> pageList(@Param("offset") int offset,
@Param("pagesize") int pagesize,
@Param("jobGroup") int jobGroup,
@Param("jobId") int jobId,
@Param("triggerTimeStart") Date triggerTimeStart,
@Param("triggerTimeEnd") Date triggerTimeEnd,
@Param("logStatus") int logStatus);
public int pageListCount(@Param("offset") int offset,
@Param("pagesize") int pagesize,
@Param("jobGroup") int jobGroup,
@Param("jobId") int jobId,
@Param("triggerTimeStart") Date triggerTimeStart,
@Param("triggerTimeEnd") Date triggerTimeEnd,
@Param("logStatus") int logStatus);
public XxlJobLog load(@Param("id") long id);
public long save(XxlJobLog xxlJobLog);
public int updateTriggerInfo(XxlJobLog xxlJobLog);
public int updateHandleInfo(XxlJobLog xxlJobLog);
public int delete(@Param("jobId") int jobId);
public Map<String, Object> findLogReport(@Param("from") Date from,
@Param("to") Date to);
public List<Long> findClearLogIds(@Param("jobGroup") int jobGroup,
@Param("jobId") int jobId,
@Param("clearBeforeTime") Date clearBeforeTime,
@Param("clearBeforeNum") int clearBeforeNum,
@Param("pagesize") int pagesize);
public int clearLog(@Param("logIds") List<Long> logIds);
public List<Long> findFailJobLogIds(@Param("pagesize") int pagesize);
public int updateAlarmStatus(@Param("logId") long logId,
@Param("oldAlarmStatus") int oldAlarmStatus,
@Param("newAlarmStatus") int newAlarmStatus);
public List<Long> findLostJobIds(@Param("losedTime") Date losedTime);
}
@@ -0,0 +1,29 @@
package com.xxl.job.admin.mapper;
import com.xxl.job.admin.model.XxlJobLogReport;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Param;
import java.util.Date;
import java.util.List;
/**
* job log
*
* @author xuxueli 2019-11-22
*/
@Mapper
public interface XxlJobLogReportMapper {
/*public int save(XxlJobLogReport xxlJobLogReport);
public int update(XxlJobLogReport xxlJobLogReport);*/
public int saveOrUpdate(XxlJobLogReport xxlJobLogReport);
public List<XxlJobLogReport> queryLogReport(@Param("triggerDayFrom") Date triggerDayFrom,
@Param("triggerDayTo") Date triggerDayTo);
public XxlJobLogReport queryLogReportTotal();
}
@@ -0,0 +1,46 @@
package com.xxl.job.admin.mapper;
import com.xxl.job.admin.model.XxlJobRegistry;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Param;
import java.util.Date;
import java.util.List;
/**
* Created by xuxueli on 16/9/30.
*/
@Mapper
public interface XxlJobRegistryMapper {
public List<Integer> findDead(@Param("timeout") int timeout,
@Param("nowTime") Date nowTime);
public int removeDead(@Param("ids") List<Integer> ids);
public List<XxlJobRegistry> findAll(@Param("timeout") int timeout,
@Param("nowTime") Date nowTime);
public int registrySaveOrUpdate(@Param("registryGroup") String registryGroup,
@Param("registryKey") String registryKey,
@Param("registryValue") String registryValue,
@Param("updateTime") Date updateTime);
/*public int registryUpdate(@Param("registryGroup") String registryGroup,
@Param("registryKey") String registryKey,
@Param("registryValue") String registryValue,
@Param("updateTime") Date updateTime);
public int registrySave(@Param("registryGroup") String registryGroup,
@Param("registryKey") String registryKey,
@Param("registryValue") String registryValue,
@Param("updateTime") Date updateTime);*/
public int registryDelete(@Param("registryGroup") String registryGroup,
@Param("registryKey") String registryKey,
@Param("registryValue") String registryValue);
public int removeByRegistryGroupAndKey(@Param("registryGroup") String registryGroup,
@Param("registryKey") String registryKey);
}
@@ -0,0 +1,37 @@
package com.xxl.job.admin.mapper;
import com.xxl.job.admin.model.XxlJobUser;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Param;
import java.util.List;
/**
* @author xuxueli 2019-05-04 16:44:59
*/
@Mapper
public interface XxlJobUserMapper {
public List<XxlJobUser> pageList(@Param("offset") int offset,
@Param("pagesize") int pagesize,
@Param("username") String username,
@Param("role") int role);
public int pageListCount(@Param("offset") int offset,
@Param("pagesize") int pagesize,
@Param("username") String username,
@Param("role") int role);
public XxlJobUser loadByUserName(@Param("username") String username);
public XxlJobUser loadById(@Param("id") int id);
public int save(XxlJobUser xxlJobUser);
public int update(XxlJobUser xxlJobUser);
public int delete(@Param("id") int id);
public int updateToken(@Param("id") int id, @Param("token") String token);
}
@@ -0,0 +1,80 @@
package com.xxl.job.admin.model;
import com.xxl.tool.core.StringTool;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Date;
import java.util.List;
/**
* Created by xuxueli on 16/9/30.
*/
public class XxlJobGroup {
private int id;
private String appname;
private String title;
private int addressType; // 执行器地址类型:0=自动注册、1=手动录入
private String addressList; // 执行器地址列表,多地址逗号分隔(手动录入)
private Date updateTime;
// registry list
private List<String> registryList; // 执行器地址列表(系统注册)
public List<String> getRegistryList() {
if (StringTool.isNotBlank(addressList)) {
registryList = new ArrayList<>(Arrays.asList(addressList.split(",")));
}
return registryList;
}
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getAppname() {
return appname;
}
public void setAppname(String appname) {
this.appname = appname;
}
public String getTitle() {
return title;
}
public void setTitle(String title) {
this.title = title;
}
public int getAddressType() {
return addressType;
}
public void setAddressType(int addressType) {
this.addressType = addressType;
}
public String getAddressList() {
return addressList;
}
public Date getUpdateTime() {
return updateTime;
}
public void setUpdateTime(Date updateTime) {
this.updateTime = updateTime;
}
public void setAddressList(String addressList) {
this.addressList = addressList;
}
}
@@ -1,61 +1,47 @@
package com.xxl.job.admin.core.model;
package com.xxl.job.admin.model;
import java.util.Date;
/**
* xxl-job info
*
* @author xuxueli 2016-1-12 18:25:49
* @author xuxueli 2016-1-12 18:25:49
*/
public class XxlJobInfo {
private int id; // 主键ID
private int jobGroup; // 执行器主键ID
private int id; // 主键ID
private int jobGroup; // 执行器主键ID
private String jobDesc;
private Date addTime;
private Date updateTime;
private String author; // 负责人
private String author; // 负责人
private String alarmEmail; // 报警邮件
private String alarmEmail; // 报警邮件
private String scheduleType; // 调度类型:ScheduleTypeEnum
private String scheduleConf; // 调度配置,值含义取决于调度类型
private String misfireStrategy; // 调度过期策略:MisfireStrategyEnum
private String scheduleType; // 调度类型
private String executorRouteStrategy; // 执行器路由策略:ExecutorRouteStrategyEnum
private String executorHandler; // 执行器,任务Handler名称
private String executorParam; // 执行器,任务参数
private String executorBlockStrategy; // 阻塞处理策略:ExecutorBlockStrategyEnum
private int executorTimeout; // 任务执行超时时间,单位秒
private int executorFailRetryCount; // 失败重试次数
private String scheduleConf; // 调度配置,值含义取决于调度类型
private String glueType; // GLUE类型:GlueTypeEnum
private String glueSource; // GLUE源代码
private String glueRemark; // GLUE备注
private Date glueUpdatetime; // GLUE更新时间
private String misfireStrategy; // 调度过期策略
private String childJobId; // 子任务ID,多个逗号分隔
private String executorRouteStrategy; // 执行器路由策略
private int triggerStatus; // 调度状态:TriggerStatus
private long triggerLastTime; // 上次调度时间
private long triggerNextTime; // 下次调度时间
private String executorHandler; // 执行器,任务Handler名称
private String executorParam; // 执行器,任务参数
private String executorBlockStrategy; // 阻塞处理策略
private int executorTimeout; // 任务执行超时时间,单位秒
private int executorFailRetryCount; // 失败重试次数
private String glueType; // GLUE类型 #com.xxl.job.core.glue.GlueTypeEnum
private String glueSource; // GLUE源代码
private String glueRemark; // GLUE备注
private Date glueUpdatetime; // GLUE更新时间
private String childJobId; // 子任务ID,多个逗号分隔
private int triggerStatus; // 调度状态:0-停止,1-运行
private long triggerLastTime; // 上次调度时间
private long triggerNextTime; // 下次调度时间
public int getId() {
return id;
@@ -248,5 +234,4 @@ public class XxlJobInfo {
public void setTriggerNextTime(long triggerNextTime) {
this.triggerNextTime = triggerNextTime;
}
}
@@ -1,11 +1,10 @@
package com.xxl.job.admin.core.model;
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
* @author xuxueli 2015-12-19 23:19:09
*/
public class XxlJobLog {
@@ -13,32 +12,23 @@ public class XxlJobLog {
// 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
@@ -1,26 +1,19 @@
package com.xxl.job.admin.core.model;
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 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() {
@@ -0,0 +1,64 @@
package com.xxl.job.admin.model;
import java.util.Date;
public class XxlJobLogReport {
private int id;
private Date triggerDay;
private int runningCount;
private int sucCount;
private int failCount;
private Date updateTime;
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public Date getTriggerDay() {
return triggerDay;
}
public void setTriggerDay(Date triggerDay) {
this.triggerDay = triggerDay;
}
public int getRunningCount() {
return runningCount;
}
public void setRunningCount(int runningCount) {
this.runningCount = runningCount;
}
public int getSucCount() {
return sucCount;
}
public void setSucCount(int sucCount) {
this.sucCount = sucCount;
}
public int getFailCount() {
return failCount;
}
public void setFailCount(int failCount) {
this.failCount = failCount;
}
public Date getUpdateTime() {
return updateTime;
}
public void setUpdateTime(Date updateTime) {
this.updateTime = updateTime;
}
}
@@ -0,0 +1,55 @@
package com.xxl.job.admin.model;
import java.util.Date;
/**
* Created by xuxueli on 16/9/30.
*/
public class XxlJobRegistry {
private long id;
private String registryGroup;
private String registryKey;
private String registryValue;
private Date updateTime;
public long getId() {
return id;
}
public void setId(long id) {
this.id = id;
}
public String getRegistryGroup() {
return registryGroup;
}
public void setRegistryGroup(String registryGroup) {
this.registryGroup = registryGroup;
}
public String getRegistryKey() {
return registryKey;
}
public void setRegistryKey(String registryKey) {
this.registryKey = registryKey;
}
public String getRegistryValue() {
return registryValue;
}
public void setRegistryValue(String registryValue) {
this.registryValue = registryValue;
}
public Date getUpdateTime() {
return updateTime;
}
public void setUpdateTime(Date updateTime) {
this.updateTime = updateTime;
}
}
@@ -1,6 +1,4 @@
package com.xxl.job.admin.core.model;
import org.springframework.util.StringUtils;
package com.xxl.job.admin.model;
/**
* @author xuxueli 2019-05-04 16:43:12
@@ -8,14 +6,11 @@ import org.springframework.util.StringUtils;
public class XxlJobUser {
private int id;
private String username; // 账号
private String password; // 密码
private int role; // 角色:0-普通用户、1-管理员
private String permission; // 权限:执行器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;
@@ -41,6 +36,14 @@ public class XxlJobUser {
this.password = password;
}
public String getToken() {
return token;
}
public void setToken(String token) {
this.token = token;
}
public int getRole() {
return role;
}
@@ -57,22 +60,4 @@ public class XxlJobUser {
this.permission = permission;
}
// plugin
public boolean validPermission(int jobGroup) {
if (this.role == 1) {
return true;
}
else {
if (StringUtils.hasText(this.permission)) {
for (String permissionItem : this.permission.split(",")) {
if (String.valueOf(jobGroup).equals(permissionItem)) {
return true;
}
}
}
return false;
}
}
}
@@ -0,0 +1,186 @@
package com.xxl.job.admin.model.dto;
import java.io.Serializable;
import java.util.Date;
import java.util.List;
/**
* XxlBootResource DTO
* <p>
* Created by xuxueli on 2024-08-04
*/
public class XxlBootResourceDTO implements Serializable {
private static final long serialVersionUID = 42L;
/**
* 资源ID
*/
private int id;
/**
* 父节点ID
*/
private int parentId;
/**
* 名称
*/
private String name;
/**
* 类型:0-目录, 1-菜单, 2-按钮
*/
private int type;
/**
* 权限标识
*/
private String permission;
/**
* 菜单地址
*/
private String url;
/**
* ICON
*/
private String icon;
/**
* 顺序
*/
private int order;
/**
* 状态:0-正常、1-禁用
*/
private int status;
/**
* 新增时间
*/
private Date addTime;
/**
* 更新时间
*/
private Date updateTime;
/**
* child data
*/
private List<XxlBootResourceDTO> children;
public XxlBootResourceDTO() {
}
public XxlBootResourceDTO(int id, int parentId, String name, int type, String permission, String url, String icon, int order, int status, List<XxlBootResourceDTO> children) {
this.id = id;
this.parentId = parentId;
this.name = name;
this.type = type;
this.permission = permission;
this.url = url;
this.icon = icon;
this.order = order;
this.status = status;
this.children = children;
}
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public int getParentId() {
return parentId;
}
public void setParentId(int parentId) {
this.parentId = parentId;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getType() {
return type;
}
public void setType(int type) {
this.type = type;
}
public String getPermission() {
return permission;
}
public void setPermission(String permission) {
this.permission = permission;
}
public String getUrl() {
return url;
}
public void setUrl(String url) {
this.url = url;
}
public String getIcon() {
return icon;
}
public void setIcon(String icon) {
this.icon = icon;
}
public int getOrder() {
return order;
}
public void setOrder(int order) {
this.order = order;
}
public int getStatus() {
return status;
}
public void setStatus(int status) {
this.status = status;
}
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 List<XxlBootResourceDTO> getChildren() {
return children;
}
public void setChildren(List<XxlBootResourceDTO> children) {
this.children = children;
}
}
@@ -0,0 +1,20 @@
package com.xxl.job.admin.scheduler.alarm;
import com.xxl.job.admin.model.XxlJobInfo;
import com.xxl.job.admin.model.XxlJobLog;
/**
* @author xuxueli 2020-01-19
*/
public interface JobAlarm {
/**
* job alarm
*
* @param info
* @param jobLog
* @return
*/
public boolean doAlarm(XxlJobInfo info, XxlJobLog jobLog);
}
@@ -0,0 +1,67 @@
package com.xxl.job.admin.scheduler.alarm;
import com.xxl.job.admin.model.XxlJobInfo;
import com.xxl.job.admin.model.XxlJobLog;
import com.xxl.tool.core.CollectionTool;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import java.util.List;
/**
* xxl-job alarmer
*
* @author xuxueli 17/7/13.
*/
@Component
public class JobAlarmer {
private static final Logger logger = LoggerFactory.getLogger(JobAlarmer.class);
@Autowired
private List<JobAlarm> jobAlarmList;
/**
* job alarm
*/
public boolean alarm(XxlJobInfo info, XxlJobLog jobLog) {
boolean result = false;
if (CollectionTool.isNotEmpty(jobAlarmList)) {
result = true; // success means all-success
for (JobAlarm alarm : jobAlarmList) {
boolean resultItem = false;
try {
resultItem = alarm.doAlarm(info, jobLog);
} catch (Exception e) {
logger.error(e.getMessage(), e);
}
if (!resultItem) {
result = false;
}
}
}
return result;
}
/*
// implements SmartInitializingSingleton
// implements ApplicationContextAware, InitializingBean
private ApplicationContext applicationContext;
@Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
this.applicationContext = applicationContext;
}
@Override
public void afterPropertiesSet() throws Exception {
Map<String, JobAlarm> serviceBeanMap = applicationContext.getBeansOfType(JobAlarm.class);
if (MapTool.isNotEmpty(serviceBeanMap)) {
jobAlarmList = new ArrayList<>(serviceBeanMap.values());
}
}*/
}
@@ -0,0 +1,118 @@
package com.xxl.job.admin.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.core.context.XxlJobContext;
import jakarta.mail.internet.MimeMessage;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.mail.javamail.MimeMessageHelper;
import org.springframework.stereotype.Component;
import java.text.MessageFormat;
import java.util.Arrays;
import java.util.HashSet;
import java.util.Set;
/**
* job alarm by email
*
* @author xuxueli 2020-01-19
*/
@Component
public class EmailJobAlarm implements JobAlarm {
private static Logger logger = LoggerFactory.getLogger(EmailJobAlarm.class);
/**
* fail alarm
*
* @param jobLog
*/
@Override
public boolean doAlarm(XxlJobInfo info, XxlJobLog jobLog) {
boolean alarmResult = true;
// send monitor email
if (info != null && info.getAlarmEmail() != null && !info.getAlarmEmail().trim().isEmpty()) {
// alarmContent
String alarmContent = "Alarm Job LogId=" + jobLog.getId();
if (jobLog.getTriggerCode() != XxlJobContext.HANDLE_CODE_SUCCESS) {
alarmContent += "<br>TriggerMsg=<br>" + jobLog.getTriggerMsg();
}
if (jobLog.getHandleCode() > 0 && jobLog.getHandleCode() != XxlJobContext.HANDLE_CODE_SUCCESS) {
alarmContent += "<br>HandleCode=" + jobLog.getHandleMsg();
}
// email info
XxlJobGroup group = XxlJobAdminBootstrap.getInstance().getXxlJobGroupMapper().load(Integer.valueOf(info.getJobGroup()));
String personal = I18nUtil.getString("admin_name_full");
String title = I18nUtil.getString("jobconf_monitor");
String content = MessageFormat.format(loadEmailJobAlarmTemplate(),
group != null ? group.getTitle() : "null",
info.getId(),
info.getJobDesc(),
alarmContent);
Set<String> emailSet = new HashSet<String>(Arrays.asList(info.getAlarmEmail().split(",")));
for (String email : emailSet) {
// make mail
try {
MimeMessage mimeMessage = XxlJobAdminBootstrap.getInstance().getMailSender().createMimeMessage();
MimeMessageHelper helper = new MimeMessageHelper(mimeMessage, true);
helper.setFrom(XxlJobAdminBootstrap.getInstance().getEmailFrom(), personal);
helper.setTo(email);
helper.setSubject(title);
helper.setText(content, true);
XxlJobAdminBootstrap.getInstance().getMailSender().send(mimeMessage);
} catch (Exception e) {
logger.error(">>>>>>>>>>> xxl-job, job fail alarm email send error, JobLogId:{}", jobLog.getId(), e);
alarmResult = false;
}
}
}
return alarmResult;
}
/**
* load email job alarm template
*
* @return
*/
private static final String loadEmailJobAlarmTemplate() {
String mailBodyTemplate = "<h5>" + I18nUtil.getString("jobconf_monitor_detail") + ":</span>" +
"<table border=\"1\" cellpadding=\"3\" style=\"border-collapse:collapse; width:80%;\" >\n" +
" <thead style=\"font-weight: bold;color: #ffffff;background-color: #ff8c00;\" >" +
" <tr>\n" +
" <td width=\"20%\" >" + I18nUtil.getString("jobinfo_field_jobgroup") + "</td>\n" +
" <td width=\"10%\" >" + I18nUtil.getString("jobinfo_field_id") + "</td>\n" +
" <td width=\"20%\" >" + I18nUtil.getString("jobinfo_field_jobdesc") + "</td>\n" +
" <td width=\"10%\" >" + I18nUtil.getString("jobconf_monitor_alarm_title") + "</td>\n" +
" <td width=\"40%\" >" + I18nUtil.getString("jobconf_monitor_alarm_content") + "</td>\n" +
" </tr>\n" +
" </thead>\n" +
" <tbody>\n" +
" <tr>\n" +
" <td>{0}</td>\n" +
" <td>{1}</td>\n" +
" <td>{2}</td>\n" +
" <td>" + I18nUtil.getString("jobconf_monitor_alarm_type") + "</td>\n" +
" <td>{3}</td>\n" +
" </tr>\n" +
" </tbody>\n" +
"</table>";
return mailBodyTemplate;
}
}
@@ -0,0 +1,122 @@
package com.xxl.job.admin.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.core.context.XxlJobContext;
import com.xxl.tool.core.StringTool;
import com.xxl.tool.response.Response;
import jakarta.annotation.Resource;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;
import java.text.MessageFormat;
/**
* xxl-job job log complete
*
* @author xuxueli 2020-10-30 20:43:10
*/
@Component
public class JobCompleter {
private static final Logger logger = LoggerFactory.getLogger(JobCompleter.class);
@Resource
private XxlJobInfoMapper xxlJobInfoMapper;
@Resource
private XxlJobLogMapper xxlJobLogMapper;
/**
* complate job (limit only once)
*/
public int complete(XxlJobLog xxlJobLog) {
// 1、process child-job
processChildJob(xxlJobLog);
// text最大64kb 避免长度过长
if (xxlJobLog.getHandleMsg().length() > 15000) {
xxlJobLog.setHandleMsg(xxlJobLog.getHandleMsg().substring(0, 15000));
}
// 2、fix_delay trigger next
// on the way
// 3、update job handle-info
return xxlJobLogMapper.updateHandleInfo(xxlJobLog);
}
/**
* do somethind to finish job
*/
private void processChildJob(XxlJobLog xxlJobLog) {
// 1、handle success, to trigger child job
String triggerChildMsg = null;
if (XxlJobContext.HANDLE_CODE_SUCCESS == xxlJobLog.getHandleCode()) {
XxlJobInfo xxlJobInfo = xxlJobInfoMapper.loadById(xxlJobLog.getJobId());
// process child job
if (xxlJobInfo != null && StringTool.isNotBlank(xxlJobInfo.getChildJobId())) {
triggerChildMsg = "<br><br><span style=\"color:#00c0ef;\" > >>>>>>>>>>>" + I18nUtil.getString("jobconf_trigger_child_run") + "<<<<<<<<<<< </span><br>";
String[] childJobIds = xxlJobInfo.getChildJobId().split(",");
for (int i = 0; i < childJobIds.length; i++) {
// process eath child
int childJobId = (StringTool.isNotBlank(childJobIds[i]) && StringTool.isNumeric(childJobIds[i]))
? Integer.parseInt(childJobIds[i])
: -1;
if (childJobId > 0) {
// valid
if (childJobId == xxlJobLog.getJobId()) {
logger.debug(">>>>>>>>>>> xxl-job, XxlJobCompleter-finishJob ignore childJobId, childJobId {} is self.", childJobId);
continue;
}
// trigger child job
XxlJobAdminBootstrap.getInstance().getJobTriggerPoolHelper().trigger(childJobId, TriggerTypeEnum.PARENT, -1, null, null, null);
Response<String> triggerChildResult = Response.ofSuccess();
// add msg
triggerChildMsg += MessageFormat.format(I18nUtil.getString("jobconf_callback_child_msg1"),
(i + 1),
childJobIds.length,
childJobIds[i],
(triggerChildResult.isSuccess() ? I18nUtil.getString("system_success") : I18nUtil.getString("system_fail")),
triggerChildResult.getMsg());
} else {
triggerChildMsg += MessageFormat.format(I18nUtil.getString("jobconf_callback_child_msg2"),
(i + 1),
childJobIds.length,
childJobIds[i]);
}
}
}
}
// 2、append trigger-child message
if (StringTool.isNotBlank(triggerChildMsg)) {
xxlJobLog.setHandleMsg(xxlJobLog.getHandleMsg() + triggerChildMsg);
}
}
/*private static boolean isNumeric(String str){
try {
int result = Integer.valueOf(str);
return true;
} catch (NumberFormatException e) {
return false;
}
}*/
}
@@ -0,0 +1,316 @@
package com.xxl.job.admin.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.core.constant.Const;
import com.xxl.job.core.openapi.ExecutorBiz;
import com.xxl.tool.core.StringTool;
import com.xxl.tool.http.HttpTool;
import jakarta.annotation.Resource;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.DisposableBean;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.mail.javamail.JavaMailSender;
import org.springframework.stereotype.Component;
import org.springframework.transaction.PlatformTransactionManager;
import java.util.Arrays;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
/**
* xxl-job config
*
* @author xuxueli 2017-04-28
*/
@Component
public class XxlJobAdminBootstrap implements InitializingBean, DisposableBean {
private static final Logger logger = LoggerFactory.getLogger(XxlJobAdminBootstrap.class);
// ---------------------- instance ----------------------
private static XxlJobAdminBootstrap adminConfig = null;
public static XxlJobAdminBootstrap getInstance() {
return adminConfig;
}
// ---------------------- start / stop ----------------------
@Override
public void afterPropertiesSet() throws Exception {
// init instance
adminConfig = this;
// start
doStart();
}
@Override
public void destroy() throws Exception {
// stop
doStop();
}
// job module
private JobTriggerPoolHelper jobTriggerPoolHelper;
private JobRegistryHelper jobRegistryHelper;
private JobFailAlarmMonitorHelper jobFailAlarmMonitorHelper;
private JobCompleteHelper jobCompleteHelper;
private JobLogReportHelper jobLogReportHelper;
private JobScheduleHelper jobScheduleHelper;
public JobTriggerPoolHelper getJobTriggerPoolHelper() {
return jobTriggerPoolHelper;
}
public JobRegistryHelper getJobRegistryHelper() {
return jobRegistryHelper;
}
public JobCompleteHelper getJobCompleteHelper() {
return jobCompleteHelper;
}
/**
* do start
*/
private void doStart() throws Exception {
// trigger-pool start
jobTriggerPoolHelper = new JobTriggerPoolHelper();
jobTriggerPoolHelper.start();
// registry monitor start
jobRegistryHelper = new JobRegistryHelper();
jobRegistryHelper.start();
// fail-alarm monitor start
jobFailAlarmMonitorHelper = new JobFailAlarmMonitorHelper();
jobFailAlarmMonitorHelper.start();
// job complate start ( depend on JobTriggerPoolHelper ) for callback and result-lost
jobCompleteHelper = new JobCompleteHelper();
jobCompleteHelper.start();
// log-report start
jobLogReportHelper = new JobLogReportHelper();
jobLogReportHelper.start();
// job-schedule start ( depend on JobTriggerPoolHelper )
jobScheduleHelper = new JobScheduleHelper();
jobScheduleHelper.start();
logger.info(">>>>>>>>> xxl-job admin start success.");
}
/**
* do stop
*/
private void doStop() {
// job-schedule stop
jobScheduleHelper.stop();
// log-report stop
jobLogReportHelper.stop();
// job complate stop
jobCompleteHelper.stop();
// fail-alarm monitor stop
jobFailAlarmMonitorHelper.stop();
// registry monitor stop
jobRegistryHelper.stop();
// trigger-pool stop
jobTriggerPoolHelper.stop();
logger.info(">>>>>>>>> xxl-job admin stopped.");
}
// ---------------------- executor-client ----------------------
private static ConcurrentMap<String, ExecutorBiz> executorBizRepository = new ConcurrentHashMap<String, ExecutorBiz>();
public static ExecutorBiz getExecutorBiz(String address) throws Exception {
// valid
if (StringTool.isBlank(address)) {
return null;
}
// load-cache
address = address.trim();
ExecutorBiz executorBiz = executorBizRepository.get(address);
if (executorBiz != null) {
return executorBiz;
}
// set-cache
executorBiz = HttpTool.createClient()
.url(address)
.timeout(XxlJobAdminBootstrap.getInstance().getTimeout() * 1000)
.header(Const.XXL_JOB_ACCESS_TOKEN, XxlJobAdminBootstrap.getInstance().getAccessToken())
.proxy(ExecutorBiz.class);
executorBizRepository.put(address, executorBiz);
return executorBiz;
}
// ---------------------- field ----------------------
// conf
@Value("${xxl.job.i18n}")
private String i18n;
@Value("${xxl.job.accessToken:}")
private String accessToken;
@Value("${xxl.job.timeout}")
private int timeout;
@Value("${spring.mail.from}")
private String emailFrom;
@Value("${xxl.job.triggerpool.fast.max}")
private int triggerPoolFastMax;
@Value("${xxl.job.triggerpool.slow.max}")
private int triggerPoolSlowMax;
@Value("${xxl.job.schedule.batchsize}")
private int scheduleBatchSize;
@Value("${xxl.job.logretentiondays}")
private int logretentiondays;
// service, mapper
@Resource
private XxlJobLogMapper xxlJobLogMapper;
@Resource
private XxlJobInfoMapper xxlJobInfoMapper;
@Resource
private XxlJobRegistryMapper xxlJobRegistryMapper;
@Resource
private XxlJobGroupMapper xxlJobGroupMapper;
@Resource
private XxlJobLogReportMapper xxlJobLogReportMapper;
@Resource
private XxlJobLockMapper xxlJobLockMapper;
@Resource
private JavaMailSender mailSender;
/*@Resource
private DataSource dataSource;*/
@Resource
private PlatformTransactionManager transactionManager;
@Resource
private JobAlarmer jobAlarmer;
@Resource
private JobTrigger jobTrigger;
@Resource
private JobCompleter jobCompleter;
public String getI18n() {
if (!Arrays.asList("zh_CN", "zh_TC", "en").contains(i18n)) {
return "zh_CN";
}
return i18n;
}
public String getAccessToken() {
return accessToken;
}
public int getTimeout() {
return timeout;
}
public String getEmailFrom() {
return emailFrom;
}
public int getTriggerPoolFastMax() {
if (triggerPoolFastMax < 200) {
return 200;
}
return triggerPoolFastMax;
}
public int getTriggerPoolSlowMax() {
if (triggerPoolSlowMax < 100) {
return 100;
}
return triggerPoolSlowMax;
}
public int getScheduleBatchSize() {
if (!(scheduleBatchSize >= 50 && scheduleBatchSize <= 500)) {
return 100;
}
return scheduleBatchSize;
}
public int getLogretentiondays() {
if (logretentiondays < 3) {
return -1; // Limit greater than or equal to 3, otherwise close
}
return logretentiondays;
}
public XxlJobLogMapper getXxlJobLogMapper() {
return xxlJobLogMapper;
}
public XxlJobInfoMapper getXxlJobInfoMapper() {
return xxlJobInfoMapper;
}
public XxlJobRegistryMapper getXxlJobRegistryMapper() {
return xxlJobRegistryMapper;
}
public XxlJobGroupMapper getXxlJobGroupMapper() {
return xxlJobGroupMapper;
}
public XxlJobLogReportMapper getXxlJobLogReportMapper() {
return xxlJobLogReportMapper;
}
public XxlJobLockMapper getXxlJobLockMapper() {
return xxlJobLockMapper;
}
public JavaMailSender getMailSender() {
return mailSender;
}
/*public DataSource getDataSource() {
return dataSource;
}*/
public PlatformTransactionManager getTransactionManager() {
return transactionManager;
}
public JobAlarmer getJobAlarmer() {
return jobAlarmer;
}
public JobTrigger getJobTrigger() {
return jobTrigger;
}
public JobCompleter getJobCompleter() {
return jobCompleter;
}
}
@@ -0,0 +1,15 @@
package com.xxl.job.admin.scheduler.exception;
/**
* @author xuxueli 2019-05-04 23:19:29
*/
public class XxlJobException extends RuntimeException {
public XxlJobException() {
}
public XxlJobException(String message) {
super(message);
}
}
@@ -0,0 +1,17 @@
package com.xxl.job.admin.scheduler.misfire;
/**
* Misfire Handler
*
* @author xuxueli 2020-10-29
*/
public abstract class MisfireHandler {
/**
* misfire handle
*
* @param jobId jobId
*/
public abstract void handle(final int jobId);
}
@@ -0,0 +1,54 @@
package com.xxl.job.admin.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;
/**
* @author xuxueli 2020-10-29 21:11:23
*/
public enum MisfireStrategyEnum {
/**
* do nothing
*/
DO_NOTHING(I18nUtil.getString("misfire_strategy_do_nothing"), new MisfireDoNothing()),
/**
* fire once now
*/
FIRE_ONCE_NOW(I18nUtil.getString("misfire_strategy_fire_once_now"), new MisfireFireOnceNow());
private final String title;
private final MisfireHandler misfireHandler;
MisfireStrategyEnum(String title, MisfireHandler misfireHandler) {
this.title = title;
this.misfireHandler = misfireHandler;
}
public String getTitle() {
return title;
}
public MisfireHandler getMisfireHandler() {
return misfireHandler;
}
/**
* match misfire strategy
*
* @param name name of misfire strategy
* @param defaultItem default misfire strategy
* @return misfire strategy
*/
public static MisfireStrategyEnum match(String name, MisfireStrategyEnum defaultItem) {
for (MisfireStrategyEnum item : MisfireStrategyEnum.values()) {
if (item.name().equals(name)) {
return item;
}
}
return defaultItem;
}
}
@@ -0,0 +1,15 @@
package com.xxl.job.admin.scheduler.misfire.strategy;
import com.xxl.job.admin.scheduler.misfire.MisfireHandler;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class MisfireDoNothing extends MisfireHandler {
private static final Logger logger = LoggerFactory.getLogger(MisfireDoNothing.class);
@Override
public void handle(int jobId) {
logger.warn(">>>>>>>>>>> xxl-job, schedule MisfireDoNothing: jobId = " + jobId);
}
}
@@ -0,0 +1,19 @@
package com.xxl.job.admin.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 org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class MisfireFireOnceNow extends MisfireHandler {
protected static Logger logger = LoggerFactory.getLogger(MisfireFireOnceNow.class);
@Override
public void handle(int jobId) {
// FIRE_ONCE_NOW 》 trigger
XxlJobAdminBootstrap.getInstance().getJobTriggerPoolHelper().trigger(jobId, TriggerTypeEnum.MISFIRE, -1, null, null, null);
logger.warn(">>>>>>>>>>> xxl-job, schedule MisfireFireOnceNow: jobId = " + jobId);
}
}
@@ -0,0 +1,80 @@
package com.xxl.job.admin.scheduler.openapi;
import com.xxl.job.admin.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;
import com.xxl.job.core.openapi.model.RegistryRequest;
import com.xxl.sso.core.annotation.XxlSso;
import com.xxl.tool.core.StringTool;
import com.xxl.tool.json.GsonTool;
import com.xxl.tool.response.Response;
import jakarta.annotation.Resource;
import jakarta.servlet.http.HttpServletRequest;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.*;
import java.util.List;
/**
* Created by xuxueli on 17/5/10.
*/
@Controller
public class OpenApiController {
@Resource
private AdminBiz adminBiz;
/**
* api
*/
@RequestMapping("/api/{uri}")
@ResponseBody
@XxlSso(login = false)
public Object api(HttpServletRequest request,
@PathVariable("uri") String uri,
@RequestHeader(value = Const.XXL_JOB_ACCESS_TOKEN, required = false) String accesstoken,
@RequestBody(required = false) String requestBody) {
// valid
if (!"POST".equalsIgnoreCase(request.getMethod())) {
return Response.ofFail("invalid request, HttpMethod not support.");
}
if (StringTool.isBlank(uri)) {
return Response.ofFail("invalid request, uri-mapping empty.");
}
if (StringTool.isBlank(requestBody)) {
return Response.ofFail("invalid request, requestBody empty.");
}
// valid token
if (StringTool.isNotBlank(XxlJobAdminBootstrap.getInstance().getAccessToken())
&& !XxlJobAdminBootstrap.getInstance().getAccessToken().equals(accesstoken)) {
return Response.ofFail("The access token is wrong.");
}
// dispatch request
try {
switch (uri) {
case "callback": {
List<CallbackRequest> callbackParamList = GsonTool.fromJson(requestBody, List.class, CallbackRequest.class);
return adminBiz.callback(callbackParamList);
}
case "registry": {
RegistryRequest registryParam = GsonTool.fromJson(requestBody, RegistryRequest.class);
return adminBiz.registry(registryParam);
}
case "registryRemove": {
RegistryRequest registryParam = GsonTool.fromJson(requestBody, RegistryRequest.class);
return adminBiz.registryRemove(registryParam);
}
default:
return Response.ofFail("invalid request, uri-mapping(" + uri + ") not found.");
}
} catch (Exception e) {
return Response.ofFail("openapi invoke error: " + e.getMessage());
}
}
}
@@ -0,0 +1,52 @@
package com.xxl.job.admin.scheduler.route;
import com.xxl.job.admin.scheduler.route.strategy.*;
import com.xxl.job.admin.util.I18nUtil;
/**
* Created by xuxueli on 17/3/10.
*/
public enum ExecutorRouteStrategyEnum {
FIRST(I18nUtil.getString("jobconf_route_first"), new ExecutorRouteFirst()),
LAST(I18nUtil.getString("jobconf_route_last"), new ExecutorRouteLast()),
ROUND(I18nUtil.getString("jobconf_route_round"), new ExecutorRouteRound()),
RANDOM(I18nUtil.getString("jobconf_route_random"), new ExecutorRouteRandom()),
CONSISTENT_HASH(I18nUtil.getString("jobconf_route_consistenthash"), new ExecutorRouteConsistentHash()),
LEAST_FREQUENTLY_USED(I18nUtil.getString("jobconf_route_lfu"), new ExecutorRouteLFU()),
LEAST_RECENTLY_USED(I18nUtil.getString("jobconf_route_lru"), new ExecutorRouteLRU()),
FAILOVER(I18nUtil.getString("jobconf_route_failover"), new ExecutorRouteFailover()),
BUSYOVER(I18nUtil.getString("jobconf_route_busyover"), new ExecutorRouteBusyover()),
SHARDING_BROADCAST(I18nUtil.getString("jobconf_route_shard"), null);
ExecutorRouteStrategyEnum(String title, ExecutorRouter router) {
this.title = title;
this.router = router;
}
private String title;
private ExecutorRouter router;
public String getTitle() {
return title;
}
public ExecutorRouter getRouter() {
return router;
}
/**
* match router
*/
public static ExecutorRouteStrategyEnum match(String name, ExecutorRouteStrategyEnum defaultItem) {
if (name != null) {
for (ExecutorRouteStrategyEnum item : ExecutorRouteStrategyEnum.values()) {
if (item.name().equals(name)) {
return item;
}
}
}
return defaultItem;
}
}
@@ -0,0 +1,24 @@
package com.xxl.job.admin.scheduler.route;
import com.xxl.job.core.openapi.model.TriggerRequest;
import com.xxl.tool.response.Response;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.List;
/**
* Created by xuxueli on 17/3/10.
*/
public abstract class ExecutorRouter {
protected static Logger logger = LoggerFactory.getLogger(ExecutorRouter.class);
/**
* route address
*
* @param addressList executor address list
* @return ReturnT.content=address
*/
public abstract Response<String> route(TriggerRequest triggerParam, List<String> addressList);
}
@@ -0,0 +1,48 @@
package com.xxl.job.admin.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.core.openapi.ExecutorBiz;
import com.xxl.job.core.openapi.model.IdleBeatRequest;
import com.xxl.job.core.openapi.model.TriggerRequest;
import com.xxl.tool.response.Response;
import java.util.List;
/**
* Created by xuxueli on 17/3/10.
*/
public class ExecutorRouteBusyover extends ExecutorRouter {
@Override
public Response<String> route(TriggerRequest triggerParam, List<String> addressList) {
StringBuffer idleBeatResultSB = new StringBuffer();
for (String address : addressList) {
// beat
Response<String> idleBeatResult = null;
try {
ExecutorBiz executorBiz = XxlJobAdminBootstrap.getExecutorBiz(address);
idleBeatResult = executorBiz.idleBeat(new IdleBeatRequest(triggerParam.getJobId()));
} catch (Exception e) {
logger.error(e.getMessage(), e);
idleBeatResult = Response.ofFail("" + e);
}
idleBeatResultSB.append((idleBeatResultSB.length() > 0) ? "<br><br>" : "")
.append(I18nUtil.getString("jobconf_idleBeat") + ":")
.append("<br>address:").append(address)
.append("<br>code:").append(idleBeatResult.getCode())
.append("<br>msg:").append(idleBeatResult.getMsg());
// beat success
if (idleBeatResult.isSuccess()) {
idleBeatResult.setMsg(idleBeatResultSB.toString());
idleBeatResult.setData(address);
return idleBeatResult;
}
}
return Response.ofFail(idleBeatResultSB.toString());
}
}
@@ -0,0 +1,99 @@
package com.xxl.job.admin.scheduler.route.strategy;
import com.xxl.job.admin.scheduler.route.ExecutorRouter;
import com.xxl.job.core.openapi.model.TriggerRequest;
import com.xxl.tool.response.Response;
import java.nio.charset.StandardCharsets;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.List;
import java.util.Map;
import java.util.TreeMap;
/**
* 分组下机器地址相同,不同JOB均匀散列在不同机器上,保证分组下机器分配JOB平均;且每个JOB固定调度其中一台机器;
* a、virtual node:解决不均衡问题
* b、hash method replace hashCode:String的hashCode可能重复,需要进一步扩大hashCode的取值范围
* <p>
* Created by xuxueli on 17/3/10.
*/
public class ExecutorRouteConsistentHash extends ExecutorRouter {
private static final int VIRTUAL_NODE_NUM = 100;
/**
* get hash code on 2^32 ring (md5散列的方式计算hash值)
*
* @param key key
* @return hash code
*/
private static long hash(String key) {
// md5 byte
MessageDigest md5;
try {
md5 = MessageDigest.getInstance("MD5");
} catch (NoSuchAlgorithmException e) {
throw new RuntimeException("MD5 not supported", e);
}
md5.reset();
byte[] keyBytes = null;
keyBytes = key.getBytes(StandardCharsets.UTF_8);
md5.update(keyBytes);
byte[] digest = md5.digest();
// hash code, Truncate to 32-bits
long hashCode = ((long) (digest[3] & 0xFF) << 24)
| ((long) (digest[2] & 0xFF) << 16)
| ((long) (digest[1] & 0xFF) << 8)
| (digest[0] & 0xFF);
return hashCode & 0xffffffffL;
}
/**
* get address by jobId
*
* @param jobId job id
* @param addressList address list
* @return address
*/
public String hashJob(int jobId, List<String> addressList) {
// 1、hash ring
// ------A1------A2-------A3------
// -----------J1------------------
TreeMap<Long, String> addressRing = new TreeMap<>();
for (String address : addressList) {
for (int i = 0; i < VIRTUAL_NODE_NUM; i++) {
long addressHash = hash("SHARD-" + address + "-NODE-" + i);
addressRing.put(addressHash, address);
}
}
// 2、generate job-hash
long jobHash = hash(String.valueOf(jobId));
// 3、route job-node
Map.Entry<Long, String> ceilingEntry = addressRing.ceilingEntry(jobHash);
if (ceilingEntry != null) {
return ceilingEntry.getValue();
}
/*SortedMap<Long, String> lastRing = addressRing.tailMap(jobHash);
if (!lastRing.isEmpty()) {
return lastRing.get(lastRing.firstKey());
}*/
// 4、default first node
return addressRing.firstEntry().getValue();
}
@Override
public Response<String> route(TriggerRequest triggerParam, List<String> addressList) {
String address = hashJob(triggerParam.getJobId(), addressList);
return Response.ofSuccess(address);
}
}
@@ -0,0 +1,48 @@
package com.xxl.job.admin.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.core.openapi.ExecutorBiz;
import com.xxl.job.core.openapi.model.TriggerRequest;
import com.xxl.tool.response.Response;
import java.util.List;
/**
* Created by xuxueli on 17/3/10.
*/
public class ExecutorRouteFailover extends ExecutorRouter {
@Override
public Response<String> route(TriggerRequest triggerParam, List<String> addressList) {
StringBuffer beatResultSB = new StringBuffer();
for (String address : addressList) {
// beat
Response<String> beatResult = null;
try {
ExecutorBiz executorBiz = XxlJobAdminBootstrap.getExecutorBiz(address);
beatResult = executorBiz.beat();
} catch (Exception e) {
logger.error(e.getMessage(), e);
beatResult = Response.ofFail(e.getMessage());
}
beatResultSB.append((beatResultSB.length() > 0) ? "<br><br>" : "")
.append(I18nUtil.getString("jobconf_beat") + ":")
.append("<br>address:").append(address)
.append("<br>code:").append(beatResult.getCode())
.append("<br>msg:").append(beatResult.getMsg());
// beat success
if (beatResult.isSuccess()) {
beatResult.setMsg(beatResultSB.toString());
beatResult.setData(address);
return beatResult;
}
}
return Response.ofFail(beatResultSB.toString());
}
}
Loaded 100 of 261 files, more files were not shown because too many files have changed in this diff. Show more