mirror of
https://github.com/jeecgboot/JeecgBoot.git
synced 2026-08-07 13:28:41 +00:00
升级xxl-job 3.4.2
This commit is contained in:
parent
3a33fc7964
commit
dfcf9ebea3
File diff suppressed because it is too large
Load Diff
@ -335,11 +335,14 @@
|
||||
<groupId>com.squareup.okhttp3</groupId>
|
||||
<artifactId>okhttp</artifactId>
|
||||
</dependency>
|
||||
<!-- 2026-07-06 for:【SpringBoot4】jackson-module-kotlin Jackson2不兼容Jackson3,spring-cloud-function 5.0 JsonMapper初始化时 ClassCastException: KotlinModule cannot be cast to tools.jackson.databind.JacksonModule,暂时注释 -->
|
||||
<!-- 解决okhttp引用了kotlin,应用启动有警告日志问题 -->
|
||||
<!--
|
||||
<dependency>
|
||||
<groupId>com.fasterxml.jackson.module</groupId>
|
||||
<artifactId>jackson-module-kotlin</artifactId>
|
||||
</dependency>
|
||||
-->
|
||||
<dependency>
|
||||
<groupId>commons-fileupload</groupId>
|
||||
<artifactId>commons-fileupload</artifactId>
|
||||
|
||||
@ -39,6 +39,12 @@
|
||||
<groupId>org.jeecgframework.boot3</groupId>
|
||||
<artifactId>jeecg-boot-starter-lock</artifactId>
|
||||
</dependency>
|
||||
<!-- spring-cloud-function 5.0 需要 joda-time 但声明为 optional,显式引入 -->
|
||||
<dependency>
|
||||
<groupId>joda-time</groupId>
|
||||
<artifactId>joda-time</artifactId>
|
||||
<version>2.12.7</version>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
</project>
|
||||
@ -2,9 +2,8 @@
|
||||
package org.jeecg.modules.test.xxljob;
|
||||
|
||||
|
||||
import com.xxl.job.core.biz.model.ReturnT;
|
||||
import com.xxl.tool.response.Response;
|
||||
import com.xxl.job.core.context.XxlJobHelper;
|
||||
import com.xxl.job.core.handler.IJobHandler;
|
||||
import com.xxl.job.core.handler.annotation.XxlJob;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.stereotype.Component;
|
||||
@ -15,7 +14,6 @@ import java.io.InputStreamReader;
|
||||
import java.net.HttpURLConnection;
|
||||
import java.net.URL;
|
||||
import java.util.Arrays;
|
||||
import com.xxl.job.core.context.XxlJobHelper;
|
||||
|
||||
/**
|
||||
* xxl-job定时任务测试
|
||||
@ -34,16 +32,16 @@ public class DemoJobHandler {
|
||||
* @return
|
||||
*/
|
||||
@XxlJob(value = "demoJob")
|
||||
public ReturnT<String> demoJobHandler(String params) {
|
||||
public Response<String> demoJobHandler(String params) {
|
||||
log.info("我是 jeecg-system 服务里的定时任务 demoJob,我执行了...............................");
|
||||
return ReturnT.SUCCESS;
|
||||
return Response.ofSuccess();
|
||||
}
|
||||
|
||||
/**
|
||||
* 2、分片广播任务
|
||||
*/
|
||||
@XxlJob("shardingJobHandler")
|
||||
public ReturnT<String> shardingJobHandler(String param) throws Exception {
|
||||
public Response<String> shardingJobHandler(String param) throws Exception {
|
||||
|
||||
// 获取分片序号和总分片数
|
||||
int shardIndex = XxlJobHelper.getShardIndex();
|
||||
@ -59,7 +57,7 @@ public class DemoJobHandler {
|
||||
}
|
||||
}
|
||||
|
||||
return ReturnT.SUCCESS;
|
||||
return Response.ofSuccess();
|
||||
}
|
||||
|
||||
|
||||
@ -69,7 +67,7 @@ public class DemoJobHandler {
|
||||
* 输入参数:ipconfig /all
|
||||
*/
|
||||
@XxlJob("commandJobHandler")
|
||||
public ReturnT<String> commandJobHandler(String param) throws Exception {
|
||||
public Response<String> commandJobHandler(String param) throws Exception {
|
||||
String command = param;
|
||||
int exitValue = -1;
|
||||
|
||||
@ -98,9 +96,9 @@ public class DemoJobHandler {
|
||||
}
|
||||
|
||||
if (exitValue == 0) {
|
||||
return ReturnT.SUCCESS;
|
||||
return Response.ofSuccess();
|
||||
} else {
|
||||
return new ReturnT<String>(ReturnT.FAIL_CODE, "command exit value(" + exitValue + ") is failed");
|
||||
return Response.ofFail("command exit value(" + exitValue + ") is failed");
|
||||
}
|
||||
}
|
||||
|
||||
@ -114,13 +112,13 @@ public class DemoJobHandler {
|
||||
* data: content
|
||||
*/
|
||||
@XxlJob("httpJobHandler")
|
||||
public ReturnT<String> httpJobHandler(String param) throws Exception {
|
||||
public Response<String> httpJobHandler(String param) throws Exception {
|
||||
String[] methodArray=new String[]{"GET","POST"};
|
||||
int okState=200;
|
||||
// param parse
|
||||
if (param == null || param.trim().length() == 0) {
|
||||
log.info("param[" + param + "] invalid.");
|
||||
return ReturnT.FAIL;
|
||||
return Response.ofFail();
|
||||
}
|
||||
String[] httpParams = param.split("\n");
|
||||
String url = null;
|
||||
@ -141,11 +139,11 @@ public class DemoJobHandler {
|
||||
// param valid
|
||||
if (url == null || url.trim().length() == 0) {
|
||||
log.info("url[" + url + "] invalid.");
|
||||
return ReturnT.FAIL;
|
||||
return Response.ofFail();
|
||||
}
|
||||
if (method == null || !Arrays.asList(methodArray).contains(method)) {
|
||||
log.info("method[" + method + "] invalid.");
|
||||
return ReturnT.FAIL;
|
||||
return Response.ofFail();
|
||||
}
|
||||
|
||||
// request
|
||||
@ -194,10 +192,10 @@ public class DemoJobHandler {
|
||||
String responseMsg = result.toString();
|
||||
|
||||
log.info(responseMsg);
|
||||
return ReturnT.SUCCESS;
|
||||
return Response.ofSuccess();
|
||||
} catch (Exception e) {
|
||||
log.info(e.getMessage(),e);
|
||||
return ReturnT.FAIL;
|
||||
return Response.ofFail();
|
||||
} finally {
|
||||
try {
|
||||
if (bufferedReader != null) {
|
||||
@ -218,9 +216,9 @@ public class DemoJobHandler {
|
||||
* 5、生命周期任务示例:任务初始化与销毁时,支持自定义相关逻辑;
|
||||
*/
|
||||
@XxlJob(value = "demoJobHandler2", init = "init", destroy = "destroy")
|
||||
public ReturnT<String> demoJobHandler2(String param) throws Exception {
|
||||
public Response<String> demoJobHandler2(String param) throws Exception {
|
||||
log.info("XXL-JOB, Hello World.");
|
||||
return ReturnT.SUCCESS;
|
||||
return Response.ofSuccess();
|
||||
}
|
||||
|
||||
public void init() {
|
||||
|
||||
@ -1,8 +1,8 @@
|
||||
|
||||
package org.jeecg.modules.test.xxljob;
|
||||
|
||||
import com.xxl.job.core.biz.model.ReturnT;
|
||||
import com.xxl.job.core.handler.annotation.XxlJob;
|
||||
import com.xxl.tool.response.Response;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
@ -24,9 +24,9 @@ public class XxclJobTest {
|
||||
*/
|
||||
|
||||
@XxlJob(value = "xxclJobTest")
|
||||
public ReturnT<String> demoJobHandler(String params) {
|
||||
public Response<String> demoJobHandler(String params) {
|
||||
log.info("我是 jeecg-system 服务里的定时任务 xxclJobTest , 我执行了...............................");
|
||||
return ReturnT.SUCCESS;
|
||||
return Response.ofSuccess();
|
||||
}
|
||||
|
||||
public void init() {
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@ -1,102 +1,110 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<parent>
|
||||
<artifactId>jeecg-visual</artifactId>
|
||||
<groupId>org.jeecgframework.boot3</groupId>
|
||||
<version>3.9.2</version>
|
||||
</parent>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
<parent>
|
||||
<artifactId>jeecg-visual</artifactId>
|
||||
<groupId>org.jeecgframework.boot3</groupId>
|
||||
<version>3.9.2</version>
|
||||
</parent>
|
||||
<packaging>jar</packaging>
|
||||
<artifactId>jeecg-cloud-xxljob</artifactId>
|
||||
|
||||
<properties>
|
||||
<maven.deploy.skip>true</maven.deploy.skip>
|
||||
<xxl-tool.version>2.5.0</xxl-tool.version>
|
||||
<xxl-sso-core.version>2.4.0</xxl-sso-core.version>
|
||||
</properties>
|
||||
|
||||
<artifactId>jeecg-cloud-xxljob</artifactId>
|
||||
<dependencies>
|
||||
|
||||
<dependencies>
|
||||
<!-- starter-web -->
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-web</artifactId>
|
||||
</dependency>
|
||||
<!-- starter-test -->
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-test</artifactId>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-web</artifactId>
|
||||
</dependency>
|
||||
<!-- starter-test:junit + spring-test + mockito -->
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-test</artifactId>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<!-- freemarker-starter -->
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-freemarker</artifactId>
|
||||
</dependency>
|
||||
<!-- freemarker-starter -->
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-freemarker</artifactId>
|
||||
</dependency>
|
||||
<!-- mail-starter -->
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-mail</artifactId>
|
||||
</dependency>
|
||||
<!-- starter-actuator -->
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-actuator</artifactId>
|
||||
</dependency>
|
||||
|
||||
<!-- starter-actuator -->
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-actuator</artifactId>
|
||||
</dependency>
|
||||
<!-- mybatis-starter -->
|
||||
<dependency>
|
||||
<groupId>org.mybatis.spring.boot</groupId>
|
||||
<artifactId>mybatis-spring-boot-starter</artifactId>
|
||||
<version>4.0.1</version>
|
||||
</dependency>
|
||||
<!-- mysql -->
|
||||
<dependency>
|
||||
<groupId>com.mysql</groupId>
|
||||
<artifactId>mysql-connector-j</artifactId>
|
||||
</dependency>
|
||||
|
||||
<!-- mybatis-starter:mybatis + mybatis-spring + hikari(default) -->
|
||||
<dependency>
|
||||
<groupId>org.mybatis.spring.boot</groupId>
|
||||
<artifactId>mybatis-spring-boot-starter</artifactId>
|
||||
<version>3.0.3</version>
|
||||
</dependency>
|
||||
<!-- mysql -->
|
||||
<dependency>
|
||||
<groupId>mysql</groupId>
|
||||
<artifactId>mysql-connector-java</artifactId>
|
||||
<version>${mysql-connector-java.version}</version>
|
||||
<scope>runtime</scope>
|
||||
</dependency>
|
||||
<!-- xxl-job-core -->
|
||||
<dependency>
|
||||
<groupId>com.xuxueli</groupId>
|
||||
<artifactId>xxl-job-core</artifactId>
|
||||
<version>${xxl-job-core.version}</version>
|
||||
</dependency>
|
||||
|
||||
<!-- xxl-sso -->
|
||||
<dependency>
|
||||
<groupId>com.xuxueli</groupId>
|
||||
<artifactId>xxl-sso-core</artifactId>
|
||||
<version>${xxl-sso-core.version}</version>
|
||||
</dependency>
|
||||
|
||||
<!-- mail-starter -->
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-mail</artifactId>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
<dependency>
|
||||
<groupId>com.xuxueli</groupId>
|
||||
<artifactId>xxl-job-core</artifactId>
|
||||
<version>${xxl-job-core.version}</version>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
<!-- update-begin-author:taoyan date:20210226 for:docker部署报错:no main manifest attribute -->
|
||||
<build>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-maven-plugin</artifactId>
|
||||
|
||||
<executions>
|
||||
<execution>
|
||||
<build>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-maven-plugin</artifactId>
|
||||
<executions>
|
||||
<execution>
|
||||
<phase>package</phase>
|
||||
<goals>
|
||||
<goal>repackage</goal>
|
||||
</goals>
|
||||
</execution>
|
||||
</executions>
|
||||
<goals>
|
||||
<goal>repackage</goal>
|
||||
</goals>
|
||||
</execution>
|
||||
</executions>
|
||||
<configuration>
|
||||
<includeSystemScope>true</includeSystemScope>
|
||||
<mainClass>com.xxl.job.admin.XxlJobAdminApplication</mainClass>
|
||||
</configuration>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-resources-plugin</artifactId>
|
||||
<configuration>
|
||||
<nonFilteredFileExtensions>
|
||||
<nonFilteredFileExtension>otf</nonFilteredFileExtension>
|
||||
<nonFilteredFileExtension>ttf</nonFilteredFileExtension>
|
||||
<nonFilteredFileExtension>woff</nonFilteredFileExtension>
|
||||
<nonFilteredFileExtension>woff2</nonFilteredFileExtension>
|
||||
<nonFilteredFileExtension>eot</nonFilteredFileExtension>
|
||||
</nonFilteredFileExtensions>
|
||||
</configuration>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</build>
|
||||
<!-- update-end-author:taoyan date:20210226 for:docker部署报错:no main manifest attribute -->
|
||||
</project>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-resources-plugin</artifactId>
|
||||
<configuration>
|
||||
<nonFilteredFileExtensions>
|
||||
<nonFilteredFileExtension>otf</nonFilteredFileExtension>
|
||||
<nonFilteredFileExtension>ttf</nonFilteredFileExtension>
|
||||
<nonFilteredFileExtension>woff</nonFilteredFileExtension>
|
||||
<nonFilteredFileExtension>woff2</nonFilteredFileExtension>
|
||||
<nonFilteredFileExtension>eot</nonFilteredFileExtension>
|
||||
</nonFilteredFileExtensions>
|
||||
</configuration>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</build>
|
||||
|
||||
</project>
|
||||
|
||||
@ -17,10 +17,9 @@ public class XxlJobAdminApplication {
|
||||
ConfigurableApplicationContext application = SpringApplication.run(XxlJobAdminApplication.class, args);
|
||||
Environment env = application.getEnvironment();
|
||||
String port = env.getProperty("server.port");
|
||||
String path = env.getProperty("server.servlet.context-path");
|
||||
log.info("\n----------------------------------------------------------\n\t" +
|
||||
"Application XxlJobAdmin is running! Access URLs:\n\t" +
|
||||
"Local: \t\thttp://localhost:" + port + path + "/\n\t" +
|
||||
"Local: \t\thttp://localhost:" + port + "\n\t" +
|
||||
"----------------------------------------------------------");
|
||||
}
|
||||
|
||||
|
||||
@ -0,0 +1,44 @@
|
||||
package com.xxl.job.admin.business.constant;
|
||||
|
||||
/**
|
||||
* Trigger Status
|
||||
*
|
||||
* @author xuxueli 2019-05-04
|
||||
*/
|
||||
public enum TriggerStatus {
|
||||
|
||||
/**
|
||||
* Stopped
|
||||
*/
|
||||
STOPPED(0, "stopped"),
|
||||
|
||||
/**
|
||||
* Running
|
||||
*/
|
||||
RUNNING(1, "running");
|
||||
|
||||
private int value;
|
||||
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;
|
||||
}
|
||||
|
||||
}
|
||||
@ -0,0 +1,122 @@
|
||||
package com.xxl.job.admin.business.controller;
|
||||
|
||||
import com.xxl.job.admin.business.mapper.XxlJobInfoMapper;
|
||||
import com.xxl.job.admin.business.mapper.XxlJobLogGlueMapper;
|
||||
import com.xxl.job.admin.business.model.XxlJobInfo;
|
||||
import com.xxl.job.admin.business.model.XxlJobLogGlue;
|
||||
import com.xxl.job.admin.framework.util.I18nUtil;
|
||||
import com.xxl.job.admin.framework.util.JobGroupPermissionUtil;
|
||||
import com.xxl.job.admin.framework.util.XssUtil;
|
||||
import com.xxl.job.core.glue.GlueTypeEnum;
|
||||
import com.xxl.sso.core.model.LoginInfo;
|
||||
import com.xxl.tool.core.StringTool;
|
||||
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 "business/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"));
|
||||
}
|
||||
if (XssUtil.hasXss(glueRemark)) {
|
||||
return Response.ofFail(I18nUtil.getString("jobinfo_glue_remark") + I18nUtil.getString("system_invalid"));
|
||||
}
|
||||
XxlJobInfo existsJobInfo = xxlJobInfoMapper.loadById(id);
|
||||
if (existsJobInfo == null) {
|
||||
return Response.ofFail( I18nUtil.getString("jobinfo_glue_jobid_invalid"));
|
||||
}
|
||||
|
||||
// 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,247 @@
|
||||
package com.xxl.job.admin.business.controller;
|
||||
|
||||
import com.xxl.job.admin.framework.constant.Consts;
|
||||
import com.xxl.job.admin.business.model.XxlJobGroup;
|
||||
import com.xxl.job.admin.business.model.XxlJobRegistry;
|
||||
import com.xxl.job.admin.framework.util.I18nUtil;
|
||||
import com.xxl.job.admin.business.mapper.XxlJobGroupMapper;
|
||||
import com.xxl.job.admin.business.mapper.XxlJobInfoMapper;
|
||||
import com.xxl.job.admin.business.mapper.XxlJobRegistryMapper;
|
||||
import com.xxl.job.admin.framework.util.XssUtil;
|
||||
import com.xxl.job.core.constant.Const;
|
||||
import com.xxl.job.core.constant.RegistTypeEnum;
|
||||
import com.xxl.sso.core.annotation.XxlSso;
|
||||
import com.xxl.tool.core.CollectionTool;
|
||||
import com.xxl.tool.core.StringTool;
|
||||
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 "business/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 appname
|
||||
if (StringTool.isBlank(xxlJobGroup.getAppname())) {
|
||||
return Response.ofFail((I18nUtil.getString("system_please_input")+"AppName") );
|
||||
}
|
||||
if (xxlJobGroup.getAppname().length()<4 || xxlJobGroup.getAppname().length()>64) {
|
||||
return Response.ofFail( I18nUtil.getString("jobgroup_field_appname_length") );
|
||||
}
|
||||
if (XssUtil.hasXss(xxlJobGroup.getAppname())) {
|
||||
return Response.ofFail( "AppName"+I18nUtil.getString("system_invalid") );
|
||||
}
|
||||
|
||||
// valid title
|
||||
if (StringTool.isBlank(xxlJobGroup.getTitle())) {
|
||||
return Response.ofFail((I18nUtil.getString("system_please_input") + I18nUtil.getString("jobgroup_field_title")) );
|
||||
}
|
||||
if (XssUtil.hasXss(xxlJobGroup.getTitle())) {
|
||||
return Response.ofFail(I18nUtil.getString("jobgroup_field_title") + I18nUtil.getString("system_invalid"));
|
||||
}
|
||||
if (xxlJobGroup.getAddressType() != 0) {
|
||||
|
||||
// valid addressList
|
||||
if (StringTool.isBlank(xxlJobGroup.getAddressList())) {
|
||||
return Response.ofFail( I18nUtil.getString("jobgroup_field_addressType_limit") );
|
||||
}
|
||||
if (XssUtil.hasXss(xxlJobGroup.getAddressList())) {
|
||||
return Response.ofFail(I18nUtil.getString("jobgroup_field_registryList")+I18nUtil.getString("system_invalid") );
|
||||
}
|
||||
|
||||
String[] addresss = xxlJobGroup.getAddressList().split(",");
|
||||
for (String item: addresss) {
|
||||
if (StringTool.isBlank(item)) {
|
||||
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 appname
|
||||
if (StringTool.isBlank(xxlJobGroup.getAppname())) {
|
||||
return Response.ofFail((I18nUtil.getString("system_please_input")+"AppName") );
|
||||
}
|
||||
if (xxlJobGroup.getAppname().length()<4 || xxlJobGroup.getAppname().length()>64) {
|
||||
return Response.ofFail( I18nUtil.getString("jobgroup_field_appname_length") );
|
||||
}
|
||||
if (XssUtil.hasXss(xxlJobGroup.getAppname())) {
|
||||
return Response.ofFail( "AppName"+I18nUtil.getString("system_invalid") );
|
||||
}
|
||||
|
||||
// valid title
|
||||
if (StringTool.isBlank(xxlJobGroup.getTitle())) {
|
||||
return Response.ofFail((I18nUtil.getString("system_please_input") + I18nUtil.getString("jobgroup_field_title")) );
|
||||
}
|
||||
if (XssUtil.hasXss(xxlJobGroup.getTitle())) {
|
||||
return Response.ofFail(I18nUtil.getString("jobgroup_field_title") + I18nUtil.getString("system_invalid"));
|
||||
}
|
||||
|
||||
if (xxlJobGroup.getAddressType() == 0) {
|
||||
// 0=自动注册
|
||||
List<String> registryList = findRegistryByAppName(xxlJobGroup.getAppname());
|
||||
String addressListStr = null;
|
||||
if (CollectionTool.isNotEmpty(registryList)) {
|
||||
Collections.sort(registryList);
|
||||
addressListStr = String.join(",", registryList);
|
||||
}
|
||||
xxlJobGroup.setAddressList(addressListStr);
|
||||
} else {
|
||||
// 1=手动录入
|
||||
|
||||
// valid addressList
|
||||
if (StringTool.isBlank(xxlJobGroup.getAddressList())) {
|
||||
return Response.ofFail( I18nUtil.getString("jobgroup_field_addressType_limit") );
|
||||
}
|
||||
if (XssUtil.hasXss(xxlJobGroup.getAddressList())) {
|
||||
return Response.ofFail(I18nUtil.getString("jobgroup_field_registryList")+I18nUtil.getString("system_invalid") );
|
||||
}
|
||||
|
||||
String[] addresss = xxlJobGroup.getAddressList().split(",");
|
||||
for (String item: addresss) {
|
||||
if (StringTool.isBlank(item)) {
|
||||
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 (!RegistTypeEnum.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(RegistTypeEnum.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,211 @@
|
||||
package com.xxl.job.admin.business.controller;
|
||||
|
||||
import com.xxl.job.admin.business.mapper.XxlJobGroupMapper;
|
||||
import com.xxl.job.admin.business.model.XxlJobGroup;
|
||||
import com.xxl.job.admin.business.model.XxlJobInfo;
|
||||
import com.xxl.job.admin.business.scheduler.exception.XxlJobException;
|
||||
import com.xxl.job.admin.business.scheduler.misfire.MisfireStrategyEnum;
|
||||
import com.xxl.job.admin.business.scheduler.route.ExecutorRouteStrategyEnum;
|
||||
import com.xxl.job.admin.business.scheduler.type.ScheduleTypeEnum;
|
||||
import com.xxl.job.admin.business.service.XxlJobService;
|
||||
import com.xxl.job.admin.framework.util.I18nUtil;
|
||||
import com.xxl.job.admin.framework.util.JobGroupPermissionUtil;
|
||||
import com.xxl.job.core.constant.ExecutorBlockStrategyEnum;
|
||||
import com.xxl.job.core.glue.GlueTypeEnum;
|
||||
import com.xxl.sso.core.helper.XxlSsoHelper;
|
||||
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 "business/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,324 @@
|
||||
package com.xxl.job.admin.business.controller;
|
||||
|
||||
import com.xxl.job.admin.business.mapper.XxlJobGroupMapper;
|
||||
import com.xxl.job.admin.business.mapper.XxlJobInfoMapper;
|
||||
import com.xxl.job.admin.business.mapper.XxlJobLogMapper;
|
||||
import com.xxl.job.admin.business.model.XxlJobGroup;
|
||||
import com.xxl.job.admin.business.model.XxlJobInfo;
|
||||
import com.xxl.job.admin.business.model.XxlJobLog;
|
||||
import com.xxl.job.admin.business.model.dto.XxlJobLogDTO;
|
||||
import com.xxl.job.admin.business.scheduler.config.XxlJobAdminBootstrap;
|
||||
import com.xxl.job.admin.business.scheduler.exception.XxlJobException;
|
||||
import com.xxl.job.admin.business.service.XxlJobService;
|
||||
import com.xxl.job.admin.framework.util.I18nUtil;
|
||||
import com.xxl.job.admin.framework.util.JobGroupPermissionUtil;
|
||||
import com.xxl.job.core.context.XxlJobContext;
|
||||
import com.xxl.job.core.openapi.ExecutorBiz;
|
||||
import com.xxl.job.core.openapi.model.KillRequest;
|
||||
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 "business/log.list";
|
||||
}
|
||||
|
||||
@RequestMapping("/pageList")
|
||||
@ResponseBody
|
||||
public Response<PageModel<XxlJobLogDTO>> pageList(HttpServletRequest request,
|
||||
@RequestParam(required = false, defaultValue = "0") int offset,
|
||||
@RequestParam(required = false, defaultValue = "10") int pagesize,
|
||||
@RequestParam int jobGroup,
|
||||
@RequestParam int jobId,
|
||||
@RequestParam int logStatus,
|
||||
@RequestParam String filterTime) {
|
||||
|
||||
// valid jobGroup permission
|
||||
JobGroupPermissionUtil.validJobGroupPermission(request, jobGroup);
|
||||
|
||||
// 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);
|
||||
|
||||
// model > dto
|
||||
List<XxlJobLogDTO> listDTO = list.stream().map(XxlJobLogDTO::new).toList();
|
||||
|
||||
// package result
|
||||
PageModel<XxlJobLogDTO> pageModel = new PageModel<>();
|
||||
pageModel.setData(listDTO);
|
||||
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);
|
||||
|
||||
// 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 "business/log.detail";
|
||||
}
|
||||
|
||||
@RequestMapping("/logDetailCat")
|
||||
@ResponseBody
|
||||
public Response<LogResult> logDetailCat(HttpServletRequest request,
|
||||
@RequestParam("logId") long logId,
|
||||
@RequestParam("fromLineNum") int fromLineNum){
|
||||
try {
|
||||
// valid
|
||||
XxlJobLog jobLog = xxlJobLogMapper.load(logId);
|
||||
if (jobLog == null) {
|
||||
return Response.ofFail(I18nUtil.getString("joblog_logid_invalid"));
|
||||
}
|
||||
|
||||
// valid permission
|
||||
JobGroupPermissionUtil.validJobGroupPermission(request, jobLog.getJobGroup());
|
||||
|
||||
// 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());
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@ -1,6 +1,6 @@
|
||||
package com.xxl.job.admin.dao;
|
||||
package com.xxl.job.admin.business.mapper;
|
||||
|
||||
import com.xxl.job.admin.core.model.XxlJobGroup;
|
||||
import com.xxl.job.admin.business.model.XxlJobGroup;
|
||||
import org.apache.ibatis.annotations.Mapper;
|
||||
import org.apache.ibatis.annotations.Param;
|
||||
|
||||
@ -10,7 +10,7 @@ import java.util.List;
|
||||
* Created by xuxueli on 16/9/30.
|
||||
*/
|
||||
@Mapper
|
||||
public interface XxlJobGroupDao {
|
||||
public interface XxlJobGroupMapper {
|
||||
|
||||
public List<XxlJobGroup> findAll();
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
package com.xxl.job.admin.dao;
|
||||
package com.xxl.job.admin.business.mapper;
|
||||
|
||||
import com.xxl.job.admin.core.model.XxlJobInfo;
|
||||
import com.xxl.job.admin.business.model.XxlJobInfo;
|
||||
import org.apache.ibatis.annotations.Mapper;
|
||||
import org.apache.ibatis.annotations.Param;
|
||||
|
||||
@ -12,7 +12,7 @@ import java.util.List;
|
||||
* @author xuxueli 2016-1-12 18:03:45
|
||||
*/
|
||||
@Mapper
|
||||
public interface XxlJobInfoDao {
|
||||
public interface XxlJobInfoMapper {
|
||||
|
||||
public List<XxlJobInfo> pageList(@Param("offset") int offset,
|
||||
@Param("pagesize") int pagesize,
|
||||
@ -41,9 +41,32 @@ public interface XxlJobInfoDao {
|
||||
|
||||
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);
|
||||
|
||||
/**
|
||||
* batch update job info
|
||||
*
|
||||
* @param jobInfoList
|
||||
* @return
|
||||
*/
|
||||
public int scheduleBatchUpdate(@Param("list") List<XxlJobInfo> jobInfoList);
|
||||
|
||||
}
|
||||
@ -0,0 +1,18 @@
|
||||
package com.xxl.job.admin.business.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.business.mapper;
|
||||
|
||||
import com.xxl.job.admin.core.model.XxlJobLogGlue;
|
||||
import com.xxl.job.admin.business.model.XxlJobLogGlue;
|
||||
import org.apache.ibatis.annotations.Mapper;
|
||||
import org.apache.ibatis.annotations.Param;
|
||||
|
||||
@ -11,7 +11,7 @@ import java.util.List;
|
||||
* @author xuxueli 2016-5-19 18:04:56
|
||||
*/
|
||||
@Mapper
|
||||
public interface XxlJobLogGlueDao {
|
||||
public interface XxlJobLogGlueMapper {
|
||||
|
||||
public int save(XxlJobLogGlue xxlJobLogGlue);
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
package com.xxl.job.admin.dao;
|
||||
package com.xxl.job.admin.business.mapper;
|
||||
|
||||
import com.xxl.job.admin.core.model.XxlJobLog;
|
||||
import com.xxl.job.admin.business.model.XxlJobLog;
|
||||
import org.apache.ibatis.annotations.Mapper;
|
||||
import org.apache.ibatis.annotations.Param;
|
||||
|
||||
@ -13,7 +13,7 @@ import java.util.Map;
|
||||
* @author xuxueli 2016-1-12 18:03:06
|
||||
*/
|
||||
@Mapper
|
||||
public interface XxlJobLogDao {
|
||||
public interface XxlJobLogMapper {
|
||||
|
||||
// exist jobId not use jobGroup, not exist use jobGroup
|
||||
public List<XxlJobLog> pageList(@Param("offset") int offset,
|
||||
@ -1,6 +1,6 @@
|
||||
package com.xxl.job.admin.dao;
|
||||
package com.xxl.job.admin.business.mapper;
|
||||
|
||||
import com.xxl.job.admin.core.model.XxlJobLogReport;
|
||||
import com.xxl.job.admin.business.model.XxlJobLogReport;
|
||||
import org.apache.ibatis.annotations.Mapper;
|
||||
import org.apache.ibatis.annotations.Param;
|
||||
|
||||
@ -12,11 +12,13 @@ import java.util.List;
|
||||
* @author xuxueli 2019-11-22
|
||||
*/
|
||||
@Mapper
|
||||
public interface XxlJobLogReportDao {
|
||||
public interface XxlJobLogReportMapper {
|
||||
|
||||
public int save(XxlJobLogReport xxlJobLogReport);
|
||||
/*public int save(XxlJobLogReport xxlJobLogReport);
|
||||
|
||||
public int update(XxlJobLogReport xxlJobLogReport);
|
||||
public int update(XxlJobLogReport xxlJobLogReport);*/
|
||||
|
||||
public int saveOrUpdate(XxlJobLogReport xxlJobLogReport);
|
||||
|
||||
public List<XxlJobLogReport> queryLogReport(@Param("triggerDayFrom") Date triggerDayFrom,
|
||||
@Param("triggerDayTo") Date triggerDayTo);
|
||||
@ -1,6 +1,6 @@
|
||||
package com.xxl.job.admin.dao;
|
||||
package com.xxl.job.admin.business.mapper;
|
||||
|
||||
import com.xxl.job.admin.core.model.XxlJobRegistry;
|
||||
import com.xxl.job.admin.business.model.XxlJobRegistry;
|
||||
import org.apache.ibatis.annotations.Mapper;
|
||||
import org.apache.ibatis.annotations.Param;
|
||||
|
||||
@ -11,7 +11,7 @@ import java.util.List;
|
||||
* Created by xuxueli on 16/9/30.
|
||||
*/
|
||||
@Mapper
|
||||
public interface XxlJobRegistryDao {
|
||||
public interface XxlJobRegistryMapper {
|
||||
|
||||
public List<Integer> findDead(@Param("timeout") int timeout,
|
||||
@Param("nowTime") Date nowTime);
|
||||
@ -21,7 +21,12 @@ public interface XxlJobRegistryDao {
|
||||
public List<XxlJobRegistry> findAll(@Param("timeout") int timeout,
|
||||
@Param("nowTime") Date nowTime);
|
||||
|
||||
public int registryUpdate(@Param("registryGroup") String registryGroup,
|
||||
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);
|
||||
@ -29,10 +34,13 @@ public interface XxlJobRegistryDao {
|
||||
public int registrySave(@Param("registryGroup") String registryGroup,
|
||||
@Param("registryKey") String registryKey,
|
||||
@Param("registryValue") String registryValue,
|
||||
@Param("updateTime") Date updateTime);
|
||||
@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);
|
||||
|
||||
}
|
||||
@ -1,4 +1,6 @@
|
||||
package com.xxl.job.admin.core.model;
|
||||
package com.xxl.job.admin.business.model;
|
||||
|
||||
import com.xxl.tool.core.StringTool;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
@ -20,8 +22,8 @@ public class XxlJobGroup {
|
||||
// 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(",")));
|
||||
if (StringTool.isNotBlank(addressList)) {
|
||||
registryList = new ArrayList<>(Arrays.asList(addressList.split(",")));
|
||||
}
|
||||
return registryList;
|
||||
}
|
||||
@ -1,4 +1,4 @@
|
||||
package com.xxl.job.admin.core.model;
|
||||
package com.xxl.job.admin.business.model;
|
||||
|
||||
import java.util.Date;
|
||||
|
||||
@ -20,25 +20,25 @@ public class XxlJobInfo {
|
||||
private String author; // 负责人
|
||||
private String alarmEmail; // 报警邮件
|
||||
|
||||
private String scheduleType; // 调度类型
|
||||
private String scheduleType; // 调度类型:ScheduleTypeEnum
|
||||
private String scheduleConf; // 调度配置,值含义取决于调度类型
|
||||
private String misfireStrategy; // 调度过期策略
|
||||
private String misfireStrategy; // 调度过期策略:MisfireStrategyEnum
|
||||
|
||||
private String executorRouteStrategy; // 执行器路由策略
|
||||
private String executorRouteStrategy; // 执行器路由策略:ExecutorRouteStrategyEnum
|
||||
private String executorHandler; // 执行器,任务Handler名称
|
||||
private String executorParam; // 执行器,任务参数
|
||||
private String executorBlockStrategy; // 阻塞处理策略
|
||||
private String executorBlockStrategy; // 阻塞处理策略:ExecutorBlockStrategyEnum
|
||||
private int executorTimeout; // 任务执行超时时间,单位秒
|
||||
private int executorFailRetryCount; // 失败重试次数
|
||||
|
||||
private String glueType; // GLUE类型 #com.xxl.job.core.glue.GlueTypeEnum
|
||||
private String glueType; // GLUE类型:GlueTypeEnum
|
||||
private String glueSource; // GLUE源代码
|
||||
private String glueRemark; // GLUE备注
|
||||
private Date glueUpdatetime; // GLUE更新时间
|
||||
|
||||
private String childJobId; // 子任务ID,多个逗号分隔
|
||||
|
||||
private int triggerStatus; // 调度状态:0-停止,1-运行
|
||||
private int triggerStatus; // 调度状态:TriggerStatus
|
||||
private long triggerLastTime; // 上次调度时间
|
||||
private long triggerNextTime; // 下次调度时间
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
package com.xxl.job.admin.core.model;
|
||||
package com.xxl.job.admin.business.model;
|
||||
|
||||
import java.util.Date;
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
package com.xxl.job.admin.core.model;
|
||||
package com.xxl.job.admin.business.model;
|
||||
|
||||
import java.util.Date;
|
||||
|
||||
@ -1,17 +1,18 @@
|
||||
package com.xxl.job.admin.core.model;
|
||||
package com.xxl.job.admin.business.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;
|
||||
}
|
||||
@ -51,4 +52,13 @@ public class XxlJobLogReport {
|
||||
public void setFailCount(int failCount) {
|
||||
this.failCount = failCount;
|
||||
}
|
||||
|
||||
public Date getUpdateTime() {
|
||||
return updateTime;
|
||||
}
|
||||
|
||||
public void setUpdateTime(Date updateTime) {
|
||||
this.updateTime = updateTime;
|
||||
}
|
||||
|
||||
}
|
||||
@ -1,4 +1,4 @@
|
||||
package com.xxl.job.admin.core.model;
|
||||
package com.xxl.job.admin.business.model;
|
||||
|
||||
import java.util.Date;
|
||||
|
||||
@ -7,17 +7,17 @@ import java.util.Date;
|
||||
*/
|
||||
public class XxlJobRegistry {
|
||||
|
||||
private int id;
|
||||
private long id;
|
||||
private String registryGroup;
|
||||
private String registryKey;
|
||||
private String registryValue;
|
||||
private Date updateTime;
|
||||
|
||||
public int getId() {
|
||||
public long getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public void setId(int id) {
|
||||
public void setId(long id) {
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
@ -0,0 +1,172 @@
|
||||
package com.xxl.job.admin.business.model.dto;
|
||||
|
||||
import com.xxl.job.admin.business.model.XxlJobLog;
|
||||
import com.xxl.tool.core.DateTool;
|
||||
|
||||
public class XxlJobLogDTO {
|
||||
|
||||
private long id;
|
||||
|
||||
// job info
|
||||
private int jobGroup;
|
||||
private int jobId;
|
||||
|
||||
// execute info
|
||||
private String executorAddress;
|
||||
private String executorHandler;
|
||||
private String executorParam;
|
||||
private String executorShardingParam;
|
||||
private int executorFailRetryCount;
|
||||
|
||||
// trigger info
|
||||
private String triggerTime;
|
||||
private int triggerCode;
|
||||
private String triggerMsg;
|
||||
|
||||
// handle info
|
||||
private String handleTime;
|
||||
private int handleCode;
|
||||
private String handleMsg;
|
||||
|
||||
// alarm info
|
||||
private int alarmStatus;
|
||||
|
||||
public XxlJobLogDTO(XxlJobLog xxlJobLog) {
|
||||
this.id = xxlJobLog.getId();
|
||||
this.jobGroup = xxlJobLog.getJobGroup();
|
||||
this.jobId = xxlJobLog.getJobId();
|
||||
this.executorAddress = xxlJobLog.getExecutorAddress();
|
||||
this.executorHandler = xxlJobLog.getExecutorHandler();
|
||||
this.executorParam = xxlJobLog.getExecutorParam();
|
||||
this.executorShardingParam = xxlJobLog.getExecutorShardingParam();
|
||||
this.executorFailRetryCount = xxlJobLog.getExecutorFailRetryCount();
|
||||
this.triggerTime = xxlJobLog.getTriggerTime() != null ? DateTool.formatDateTime(xxlJobLog.getTriggerTime()) : null;
|
||||
this.triggerCode = xxlJobLog.getTriggerCode();
|
||||
this.triggerMsg = xxlJobLog.getTriggerMsg();
|
||||
this.handleTime = xxlJobLog.getHandleTime() != null ? DateTool.formatDateTime(xxlJobLog.getHandleTime()) : null;
|
||||
this.handleCode = xxlJobLog.getHandleCode();
|
||||
this.handleMsg = xxlJobLog.getHandleMsg();
|
||||
this.alarmStatus = xxlJobLog.getAlarmStatus();
|
||||
}
|
||||
|
||||
public long getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public void setId(long id) {
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
public int getJobGroup() {
|
||||
return jobGroup;
|
||||
}
|
||||
|
||||
public void setJobGroup(int jobGroup) {
|
||||
this.jobGroup = jobGroup;
|
||||
}
|
||||
|
||||
public int getJobId() {
|
||||
return jobId;
|
||||
}
|
||||
|
||||
public void setJobId(int jobId) {
|
||||
this.jobId = jobId;
|
||||
}
|
||||
|
||||
public String getExecutorAddress() {
|
||||
return executorAddress;
|
||||
}
|
||||
|
||||
public void setExecutorAddress(String executorAddress) {
|
||||
this.executorAddress = executorAddress;
|
||||
}
|
||||
|
||||
public String getExecutorHandler() {
|
||||
return executorHandler;
|
||||
}
|
||||
|
||||
public void setExecutorHandler(String executorHandler) {
|
||||
this.executorHandler = executorHandler;
|
||||
}
|
||||
|
||||
public String getExecutorParam() {
|
||||
return executorParam;
|
||||
}
|
||||
|
||||
public void setExecutorParam(String executorParam) {
|
||||
this.executorParam = executorParam;
|
||||
}
|
||||
|
||||
public String getExecutorShardingParam() {
|
||||
return executorShardingParam;
|
||||
}
|
||||
|
||||
public void setExecutorShardingParam(String executorShardingParam) {
|
||||
this.executorShardingParam = executorShardingParam;
|
||||
}
|
||||
|
||||
public int getExecutorFailRetryCount() {
|
||||
return executorFailRetryCount;
|
||||
}
|
||||
|
||||
public void setExecutorFailRetryCount(int executorFailRetryCount) {
|
||||
this.executorFailRetryCount = executorFailRetryCount;
|
||||
}
|
||||
|
||||
public String getTriggerTime() {
|
||||
return triggerTime;
|
||||
}
|
||||
|
||||
public void setTriggerTime(String triggerTime) {
|
||||
this.triggerTime = triggerTime;
|
||||
}
|
||||
|
||||
public int getTriggerCode() {
|
||||
return triggerCode;
|
||||
}
|
||||
|
||||
public void setTriggerCode(int triggerCode) {
|
||||
this.triggerCode = triggerCode;
|
||||
}
|
||||
|
||||
public String getTriggerMsg() {
|
||||
return triggerMsg;
|
||||
}
|
||||
|
||||
public void setTriggerMsg(String triggerMsg) {
|
||||
this.triggerMsg = triggerMsg;
|
||||
}
|
||||
|
||||
public String getHandleTime() {
|
||||
return handleTime;
|
||||
}
|
||||
|
||||
public void setHandleTime(String handleTime) {
|
||||
this.handleTime = handleTime;
|
||||
}
|
||||
|
||||
public int getHandleCode() {
|
||||
return handleCode;
|
||||
}
|
||||
|
||||
public void setHandleCode(int handleCode) {
|
||||
this.handleCode = handleCode;
|
||||
}
|
||||
|
||||
public String getHandleMsg() {
|
||||
return handleMsg;
|
||||
}
|
||||
|
||||
public void setHandleMsg(String handleMsg) {
|
||||
this.handleMsg = handleMsg;
|
||||
}
|
||||
|
||||
public int getAlarmStatus() {
|
||||
return alarmStatus;
|
||||
}
|
||||
|
||||
public void setAlarmStatus(int alarmStatus) {
|
||||
this.alarmStatus = alarmStatus;
|
||||
}
|
||||
|
||||
}
|
||||
@ -1,7 +1,7 @@
|
||||
package com.xxl.job.admin.core.alarm;
|
||||
package com.xxl.job.admin.business.scheduler.alarm;
|
||||
|
||||
import com.xxl.job.admin.core.model.XxlJobInfo;
|
||||
import com.xxl.job.admin.core.model.XxlJobLog;
|
||||
import com.xxl.job.admin.business.model.XxlJobInfo;
|
||||
import com.xxl.job.admin.business.model.XxlJobLog;
|
||||
|
||||
/**
|
||||
* @author xuxueli 2020-01-19
|
||||
@ -1,50 +1,34 @@
|
||||
package com.xxl.job.admin.core.alarm;
|
||||
package com.xxl.job.admin.business.scheduler.alarm;
|
||||
|
||||
import com.xxl.job.admin.core.model.XxlJobInfo;
|
||||
import com.xxl.job.admin.core.model.XxlJobLog;
|
||||
import com.xxl.job.admin.business.model.XxlJobInfo;
|
||||
import com.xxl.job.admin.business.model.XxlJobLog;
|
||||
import com.xxl.tool.core.CollectionTool;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.beans.BeansException;
|
||||
import org.springframework.beans.factory.InitializingBean;
|
||||
import org.springframework.context.ApplicationContext;
|
||||
import org.springframework.context.ApplicationContextAware;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* xxl-job alarmer
|
||||
*
|
||||
* @author xuxueli 17/7/13.
|
||||
*/
|
||||
@Component
|
||||
public class JobAlarmer implements ApplicationContextAware, InitializingBean {
|
||||
private static Logger logger = LoggerFactory.getLogger(JobAlarmer.class);
|
||||
public class JobAlarmer {
|
||||
private static final Logger logger = LoggerFactory.getLogger(JobAlarmer.class);
|
||||
|
||||
private ApplicationContext applicationContext;
|
||||
@Autowired
|
||||
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) {
|
||||
if (CollectionTool.isNotEmpty(jobAlarmList)) {
|
||||
result = true; // success means all-success
|
||||
for (JobAlarm alarm: jobAlarmList) {
|
||||
boolean resultItem = false;
|
||||
@ -62,4 +46,22 @@ public class JobAlarmer implements ApplicationContextAware, InitializingBean {
|
||||
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());
|
||||
}
|
||||
}*/
|
||||
|
||||
}
|
||||
@ -1,18 +1,18 @@
|
||||
package com.xxl.job.admin.core.alarm.impl;
|
||||
package com.xxl.job.admin.business.scheduler.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 com.xxl.job.admin.business.scheduler.alarm.JobAlarm;
|
||||
import com.xxl.job.admin.business.scheduler.config.XxlJobAdminBootstrap;
|
||||
import com.xxl.job.admin.business.model.XxlJobGroup;
|
||||
import com.xxl.job.admin.business.model.XxlJobInfo;
|
||||
import com.xxl.job.admin.business.model.XxlJobLog;
|
||||
import com.xxl.job.admin.framework.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 jakarta.mail.internet.MimeMessage;
|
||||
import java.text.MessageFormat;
|
||||
import java.util.Arrays;
|
||||
import java.util.HashSet;
|
||||
@ -37,19 +37,19 @@ public class EmailJobAlarm implements JobAlarm {
|
||||
boolean alarmResult = true;
|
||||
|
||||
// send monitor email
|
||||
if (info!=null && info.getAlarmEmail()!=null && info.getAlarmEmail().trim().length()>0) {
|
||||
if (info!=null && info.getAlarmEmail()!=null && !info.getAlarmEmail().trim().isEmpty()) {
|
||||
|
||||
// alarmContent
|
||||
String alarmContent = "Alarm Job LogId=" + jobLog.getId();
|
||||
if (jobLog.getTriggerCode() != ReturnT.SUCCESS_CODE) {
|
||||
if (jobLog.getTriggerCode() != XxlJobContext.HANDLE_CODE_SUCCESS) {
|
||||
alarmContent += "<br>TriggerMsg=<br>" + jobLog.getTriggerMsg();
|
||||
}
|
||||
if (jobLog.getHandleCode()>0 && jobLog.getHandleCode() != ReturnT.SUCCESS_CODE) {
|
||||
if (jobLog.getHandleCode()>0 && jobLog.getHandleCode() != XxlJobContext.HANDLE_CODE_SUCCESS) {
|
||||
alarmContent += "<br>HandleCode=" + jobLog.getHandleMsg();
|
||||
}
|
||||
|
||||
// email info
|
||||
XxlJobGroup group = XxlJobAdminConfig.getAdminConfig().getXxlJobGroupDao().load(Integer.valueOf(info.getJobGroup()));
|
||||
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(),
|
||||
@ -63,15 +63,15 @@ public class EmailJobAlarm implements JobAlarm {
|
||||
|
||||
// make mail
|
||||
try {
|
||||
MimeMessage mimeMessage = XxlJobAdminConfig.getAdminConfig().getMailSender().createMimeMessage();
|
||||
MimeMessage mimeMessage = XxlJobAdminBootstrap.getInstance().getMailSender().createMimeMessage();
|
||||
|
||||
MimeMessageHelper helper = new MimeMessageHelper(mimeMessage, true);
|
||||
helper.setFrom(XxlJobAdminConfig.getAdminConfig().getEmailFrom(), personal);
|
||||
helper.setFrom(XxlJobAdminBootstrap.getInstance().getEmailFrom(), personal);
|
||||
helper.setTo(email);
|
||||
helper.setSubject(title);
|
||||
helper.setText(content, true);
|
||||
|
||||
XxlJobAdminConfig.getAdminConfig().getMailSender().send(mimeMessage);
|
||||
XxlJobAdminBootstrap.getInstance().getMailSender().send(mimeMessage);
|
||||
} catch (Exception e) {
|
||||
logger.error(">>>>>>>>>>> xxl-job, job fail alarm email send error, JobLogId:{}", jobLog.getId(), e);
|
||||
|
||||
@ -0,0 +1,122 @@
|
||||
package com.xxl.job.admin.business.scheduler.complete;
|
||||
|
||||
import com.xxl.job.admin.business.mapper.XxlJobInfoMapper;
|
||||
import com.xxl.job.admin.business.mapper.XxlJobLogMapper;
|
||||
import com.xxl.job.admin.business.model.XxlJobInfo;
|
||||
import com.xxl.job.admin.business.model.XxlJobLog;
|
||||
import com.xxl.job.admin.business.scheduler.config.XxlJobAdminBootstrap;
|
||||
import com.xxl.job.admin.business.scheduler.trigger.TriggerTypeEnum;
|
||||
import com.xxl.job.admin.framework.util.I18nUtil;
|
||||
import com.xxl.job.core.context.XxlJobContext;
|
||||
import com.xxl.tool.core.StringTool;
|
||||
import com.xxl.tool.response.Response;
|
||||
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,312 @@
|
||||
package com.xxl.job.admin.business.scheduler.config;
|
||||
|
||||
import com.xxl.job.admin.business.mapper.*;
|
||||
import com.xxl.job.admin.business.scheduler.alarm.JobAlarmer;
|
||||
import com.xxl.job.admin.business.scheduler.complete.JobCompleter;
|
||||
import com.xxl.job.admin.business.scheduler.thread.*;
|
||||
import com.xxl.job.admin.business.scheduler.trigger.JobTrigger;
|
||||
import com.xxl.job.core.constant.Const;
|
||||
import com.xxl.job.core.openapi.ExecutorBiz;
|
||||
import com.xxl.tool.core.StringTool;
|
||||
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;
|
||||
}
|
||||
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@ -1,4 +1,4 @@
|
||||
package com.xxl.job.admin.core.exception;
|
||||
package com.xxl.job.admin.business.scheduler.exception;
|
||||
|
||||
/**
|
||||
* @author xuxueli 2019-05-04 23:19:29
|
||||
@ -0,0 +1,17 @@
|
||||
package com.xxl.job.admin.business.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.business.scheduler.misfire;
|
||||
|
||||
import com.xxl.job.admin.business.scheduler.misfire.strategy.MisfireDoNothing;
|
||||
import com.xxl.job.admin.business.scheduler.misfire.strategy.MisfireFireOnceNow;
|
||||
import com.xxl.job.admin.framework.util.I18nUtil;
|
||||
|
||||
/**
|
||||
* @author xuxueli 2020-10-29 21:11:23
|
||||
*/
|
||||
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.business.scheduler.misfire.strategy;
|
||||
|
||||
import com.xxl.job.admin.business.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.business.scheduler.misfire.strategy;
|
||||
|
||||
import com.xxl.job.admin.business.scheduler.config.XxlJobAdminBootstrap;
|
||||
import com.xxl.job.admin.business.scheduler.misfire.MisfireHandler;
|
||||
import com.xxl.job.admin.business.scheduler.trigger.TriggerTypeEnum;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
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.business.scheduler.openapi;
|
||||
|
||||
import com.xxl.job.admin.business.scheduler.config.XxlJobAdminBootstrap;
|
||||
import com.xxl.job.core.constant.Const;
|
||||
import com.xxl.job.core.openapi.AdminBiz;
|
||||
import com.xxl.job.core.openapi.model.CallbackRequest;
|
||||
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());
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@ -1,7 +1,7 @@
|
||||
package com.xxl.job.admin.core.route;
|
||||
package com.xxl.job.admin.business.scheduler.route;
|
||||
|
||||
import com.xxl.job.admin.core.route.strategy.*;
|
||||
import com.xxl.job.admin.core.util.I18nUtil;
|
||||
import com.xxl.job.admin.business.scheduler.route.strategy.*;
|
||||
import com.xxl.job.admin.framework.util.I18nUtil;
|
||||
|
||||
/**
|
||||
* Created by xuxueli on 17/3/10.
|
||||
@ -34,6 +34,9 @@ public enum ExecutorRouteStrategyEnum {
|
||||
return router;
|
||||
}
|
||||
|
||||
/**
|
||||
* match router
|
||||
*/
|
||||
public static ExecutorRouteStrategyEnum match(String name, ExecutorRouteStrategyEnum defaultItem){
|
||||
if (name != null) {
|
||||
for (ExecutorRouteStrategyEnum item: ExecutorRouteStrategyEnum.values()) {
|
||||
@ -1,7 +1,7 @@
|
||||
package com.xxl.job.admin.core.route;
|
||||
package com.xxl.job.admin.business.scheduler.route;
|
||||
|
||||
import com.xxl.job.core.biz.model.ReturnT;
|
||||
import com.xxl.job.core.biz.model.TriggerParam;
|
||||
import com.xxl.job.core.openapi.model.TriggerRequest;
|
||||
import com.xxl.tool.response.Response;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
@ -16,9 +16,9 @@ public abstract class ExecutorRouter {
|
||||
/**
|
||||
* route address
|
||||
*
|
||||
* @param addressList
|
||||
* @param addressList executor address list
|
||||
* @return ReturnT.content=address
|
||||
*/
|
||||
public abstract ReturnT<String> route(TriggerParam triggerParam, List<String> addressList);
|
||||
public abstract Response<String> route(TriggerRequest triggerParam, List<String> addressList);
|
||||
|
||||
}
|
||||
@ -1,12 +1,12 @@
|
||||
package com.xxl.job.admin.core.route.strategy;
|
||||
package com.xxl.job.admin.business.scheduler.route.strategy;
|
||||
|
||||
import com.xxl.job.admin.core.scheduler.XxlJobScheduler;
|
||||
import com.xxl.job.admin.core.route.ExecutorRouter;
|
||||
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 com.xxl.job.admin.business.scheduler.config.XxlJobAdminBootstrap;
|
||||
import com.xxl.job.admin.business.scheduler.route.ExecutorRouter;
|
||||
import com.xxl.job.admin.framework.util.I18nUtil;
|
||||
import com.xxl.job.core.openapi.ExecutorBiz;
|
||||
import com.xxl.job.core.openapi.model.IdleBeatRequest;
|
||||
import com.xxl.job.core.openapi.model.TriggerRequest;
|
||||
import com.xxl.tool.response.Response;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
@ -16,17 +16,17 @@ import java.util.List;
|
||||
public class ExecutorRouteBusyover extends ExecutorRouter {
|
||||
|
||||
@Override
|
||||
public ReturnT<String> route(TriggerParam triggerParam, List<String> addressList) {
|
||||
public Response<String> route(TriggerRequest triggerParam, List<String> addressList) {
|
||||
StringBuffer idleBeatResultSB = new StringBuffer();
|
||||
for (String address : addressList) {
|
||||
// beat
|
||||
ReturnT<String> idleBeatResult = null;
|
||||
Response<String> idleBeatResult = null;
|
||||
try {
|
||||
ExecutorBiz executorBiz = XxlJobScheduler.getExecutorBiz(address);
|
||||
idleBeatResult = executorBiz.idleBeat(new IdleBeatParam(triggerParam.getJobId()));
|
||||
ExecutorBiz executorBiz = XxlJobAdminBootstrap.getExecutorBiz(address);
|
||||
idleBeatResult = executorBiz.idleBeat(new IdleBeatRequest(triggerParam.getJobId()));
|
||||
} catch (Exception e) {
|
||||
logger.error(e.getMessage(), e);
|
||||
idleBeatResult = new ReturnT<String>(ReturnT.FAIL_CODE, ""+e );
|
||||
idleBeatResult = Response.ofFail( ""+e );
|
||||
}
|
||||
idleBeatResultSB.append( (idleBeatResultSB.length()>0)?"<br><br>":"")
|
||||
.append(I18nUtil.getString("jobconf_idleBeat") + ":")
|
||||
@ -35,14 +35,14 @@ public class ExecutorRouteBusyover extends ExecutorRouter {
|
||||
.append("<br>msg:").append(idleBeatResult.getMsg());
|
||||
|
||||
// beat success
|
||||
if (idleBeatResult.getCode() == ReturnT.SUCCESS_CODE) {
|
||||
if (idleBeatResult.isSuccess()) {
|
||||
idleBeatResult.setMsg(idleBeatResultSB.toString());
|
||||
idleBeatResult.setContent(address);
|
||||
idleBeatResult.setData(address);
|
||||
return idleBeatResult;
|
||||
}
|
||||
}
|
||||
|
||||
return new ReturnT<String>(ReturnT.FAIL_CODE, idleBeatResultSB.toString());
|
||||
return Response.ofFail( idleBeatResultSB.toString());
|
||||
}
|
||||
|
||||
}
|
||||
@ -1,30 +1,32 @@
|
||||
package com.xxl.job.admin.core.route.strategy;
|
||||
package com.xxl.job.admin.business.scheduler.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 com.xxl.job.admin.business.scheduler.route.ExecutorRouter;
|
||||
import com.xxl.job.core.openapi.model.TriggerRequest;
|
||||
import com.xxl.tool.response.Response;
|
||||
|
||||
import java.io.UnsupportedEncodingException;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.security.MessageDigest;
|
||||
import java.security.NoSuchAlgorithmException;
|
||||
import java.util.List;
|
||||
import java.util.SortedMap;
|
||||
import java.util.Map;
|
||||
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;
|
||||
private static final int VIRTUAL_NODE_NUM = 100;
|
||||
|
||||
/**
|
||||
* get hash code on 2^32 ring (md5散列的方式计算hash值)
|
||||
* @param key
|
||||
* @return
|
||||
*
|
||||
* @param key key
|
||||
* @return hash code
|
||||
*/
|
||||
private static long hash(String key) {
|
||||
|
||||
@ -37,11 +39,7 @@ public class ExecutorRouteConsistentHash extends ExecutorRouter {
|
||||
}
|
||||
md5.reset();
|
||||
byte[] keyBytes = null;
|
||||
try {
|
||||
keyBytes = key.getBytes("UTF-8");
|
||||
} catch (UnsupportedEncodingException e) {
|
||||
throw new RuntimeException("Unknown string :" + key, e);
|
||||
}
|
||||
keyBytes = key.getBytes(StandardCharsets.UTF_8);
|
||||
|
||||
md5.update(keyBytes);
|
||||
byte[] digest = md5.digest();
|
||||
@ -52,15 +50,22 @@ public class ExecutorRouteConsistentHash extends ExecutorRouter {
|
||||
| ((long) (digest[1] & 0xFF) << 8)
|
||||
| (digest[0] & 0xFF);
|
||||
|
||||
long truncateHashCode = hashCode & 0xffffffffL;
|
||||
return truncateHashCode;
|
||||
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<Long, String>();
|
||||
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);
|
||||
@ -68,18 +73,27 @@ public class ExecutorRouteConsistentHash extends ExecutorRouter {
|
||||
}
|
||||
}
|
||||
|
||||
// 2、generate job-hash
|
||||
long jobHash = hash(String.valueOf(jobId));
|
||||
SortedMap<Long, String> lastRing = addressRing.tailMap(jobHash);
|
||||
|
||||
// 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 ReturnT<String> route(TriggerParam triggerParam, List<String> addressList) {
|
||||
public Response<String> route(TriggerRequest triggerParam, List<String> addressList) {
|
||||
String address = hashJob(triggerParam.getJobId(), addressList);
|
||||
return new ReturnT<String>(address);
|
||||
return Response.ofSuccess(address);
|
||||
}
|
||||
|
||||
}
|
||||
@ -1,11 +1,11 @@
|
||||
package com.xxl.job.admin.core.route.strategy;
|
||||
package com.xxl.job.admin.business.scheduler.route.strategy;
|
||||
|
||||
import com.xxl.job.admin.core.scheduler.XxlJobScheduler;
|
||||
import com.xxl.job.admin.core.route.ExecutorRouter;
|
||||
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.admin.business.scheduler.config.XxlJobAdminBootstrap;
|
||||
import com.xxl.job.admin.business.scheduler.route.ExecutorRouter;
|
||||
import com.xxl.job.admin.framework.util.I18nUtil;
|
||||
import com.xxl.job.core.openapi.ExecutorBiz;
|
||||
import com.xxl.job.core.openapi.model.TriggerRequest;
|
||||
import com.xxl.tool.response.Response;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
@ -15,18 +15,18 @@ import java.util.List;
|
||||
public class ExecutorRouteFailover extends ExecutorRouter {
|
||||
|
||||
@Override
|
||||
public ReturnT<String> route(TriggerParam triggerParam, List<String> addressList) {
|
||||
public Response<String> route(TriggerRequest triggerParam, List<String> addressList) {
|
||||
|
||||
StringBuffer beatResultSB = new StringBuffer();
|
||||
for (String address : addressList) {
|
||||
// beat
|
||||
ReturnT<String> beatResult = null;
|
||||
Response<String> beatResult = null;
|
||||
try {
|
||||
ExecutorBiz executorBiz = XxlJobScheduler.getExecutorBiz(address);
|
||||
ExecutorBiz executorBiz = XxlJobAdminBootstrap.getExecutorBiz(address);
|
||||
beatResult = executorBiz.beat();
|
||||
} catch (Exception e) {
|
||||
logger.error(e.getMessage(), e);
|
||||
beatResult = new ReturnT<String>(ReturnT.FAIL_CODE, ""+e );
|
||||
beatResult = Response.ofFail(e.getMessage() );
|
||||
}
|
||||
beatResultSB.append( (beatResultSB.length()>0)?"<br><br>":"")
|
||||
.append(I18nUtil.getString("jobconf_beat") + ":")
|
||||
@ -35,14 +35,14 @@ public class ExecutorRouteFailover extends ExecutorRouter {
|
||||
.append("<br>msg:").append(beatResult.getMsg());
|
||||
|
||||
// beat success
|
||||
if (beatResult.getCode() == ReturnT.SUCCESS_CODE) {
|
||||
if (beatResult.isSuccess()) {
|
||||
|
||||
beatResult.setMsg(beatResultSB.toString());
|
||||
beatResult.setContent(address);
|
||||
beatResult.setData(address);
|
||||
return beatResult;
|
||||
}
|
||||
}
|
||||
return new ReturnT<String>(ReturnT.FAIL_CODE, beatResultSB.toString());
|
||||
return Response.ofFail( beatResultSB.toString());
|
||||
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,19 @@
|
||||
package com.xxl.job.admin.business.scheduler.route.strategy;
|
||||
|
||||
import com.xxl.job.admin.business.scheduler.route.ExecutorRouter;
|
||||
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 ExecutorRouteFirst extends ExecutorRouter {
|
||||
|
||||
@Override
|
||||
public Response<String> route(TriggerRequest triggerParam, List<String> addressList){
|
||||
return Response.ofSuccess(addressList.get(0));
|
||||
}
|
||||
|
||||
}
|
||||
@ -1,8 +1,8 @@
|
||||
package com.xxl.job.admin.core.route.strategy;
|
||||
package com.xxl.job.admin.business.scheduler.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 com.xxl.job.admin.business.scheduler.route.ExecutorRouter;
|
||||
import com.xxl.job.core.openapi.model.TriggerRequest;
|
||||
import com.xxl.tool.response.Response;
|
||||
|
||||
import java.util.*;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
@ -17,6 +17,11 @@ import java.util.concurrent.ConcurrentMap;
|
||||
*/
|
||||
public class ExecutorRouteLFU extends ExecutorRouter {
|
||||
|
||||
/**
|
||||
* job lfu map
|
||||
*
|
||||
* <jobId, <address, count>>
|
||||
*/
|
||||
private static ConcurrentMap<Integer, HashMap<String, Integer>> jobLfuMap = new ConcurrentHashMap<Integer, HashMap<String, Integer>>();
|
||||
private static long CACHE_VALID_TIME = 0;
|
||||
|
||||
@ -31,7 +36,7 @@ public class ExecutorRouteLFU extends ExecutorRouter {
|
||||
// lfu item init
|
||||
HashMap<String, Integer> lfuItemMap = jobLfuMap.get(jobId); // Key排序可以用TreeMap+构造入参Compare;Value排序暂时只能通过ArrayList;
|
||||
if (lfuItemMap == null) {
|
||||
lfuItemMap = new HashMap<String, Integer>();
|
||||
lfuItemMap = new HashMap<>();
|
||||
jobLfuMap.putIfAbsent(jobId, lfuItemMap); // 避免重复覆盖
|
||||
}
|
||||
|
||||
@ -48,32 +53,26 @@ public class ExecutorRouteLFU extends ExecutorRouter {
|
||||
delKeys.add(existKey);
|
||||
}
|
||||
}
|
||||
if (delKeys.size() > 0) {
|
||||
if (!delKeys.isEmpty()) {
|
||||
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());
|
||||
}
|
||||
});
|
||||
List<Map.Entry<String, Integer>> lfuItemList = new ArrayList<>(lfuItemMap.entrySet());
|
||||
lfuItemList.sort(Map.Entry.comparingByValue()); // 默认升序, 获取 Value 最小值
|
||||
|
||||
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) {
|
||||
public Response<String> route(TriggerRequest triggerParam, List<String> addressList) {
|
||||
String address = route(triggerParam.getJobId(), addressList);
|
||||
return new ReturnT<String>(address);
|
||||
return Response.ofSuccess(address);
|
||||
}
|
||||
|
||||
}
|
||||
@ -1,8 +1,8 @@
|
||||
package com.xxl.job.admin.core.route.strategy;
|
||||
package com.xxl.job.admin.business.scheduler.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 com.xxl.job.admin.business.scheduler.route.ExecutorRouter;
|
||||
import com.xxl.job.core.openapi.model.TriggerRequest;
|
||||
import com.xxl.tool.response.Response;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.LinkedHashMap;
|
||||
@ -19,6 +19,11 @@ import java.util.concurrent.ConcurrentMap;
|
||||
*/
|
||||
public class ExecutorRouteLRU extends ExecutorRouter {
|
||||
|
||||
/**
|
||||
* job lru map
|
||||
*
|
||||
* <jobId, <address, address>>
|
||||
*/
|
||||
private static ConcurrentMap<Integer, LinkedHashMap<String, String>> jobLRUMap = new ConcurrentHashMap<Integer, LinkedHashMap<String, String>>();
|
||||
private static long CACHE_VALID_TIME = 0;
|
||||
|
||||
@ -38,7 +43,7 @@ public class ExecutorRouteLRU extends ExecutorRouter {
|
||||
* a、accessOrder:true=访问顺序排序(get/put时排序);false=插入顺序排期;
|
||||
* b、removeEldestEntry:新增元素时将会调用,返回true时会删除最老元素;可封装LinkedHashMap并重写该方法,比如定义最大容量,超出是返回true即可实现固定长度的LRU算法;
|
||||
*/
|
||||
lruItem = new LinkedHashMap<String, String>(16, 0.75f, true);
|
||||
lruItem = new LinkedHashMap<>(16, 0.75f, true);
|
||||
jobLRUMap.putIfAbsent(jobId, lruItem);
|
||||
}
|
||||
|
||||
@ -55,22 +60,21 @@ public class ExecutorRouteLRU extends ExecutorRouter {
|
||||
delKeys.add(existKey);
|
||||
}
|
||||
}
|
||||
if (delKeys.size() > 0) {
|
||||
if (!delKeys.isEmpty()) {
|
||||
for (String delKey: delKeys) {
|
||||
lruItem.remove(delKey);
|
||||
}
|
||||
}
|
||||
|
||||
// load
|
||||
// load first elment, eldest entry
|
||||
String eldestKey = lruItem.entrySet().iterator().next().getKey();
|
||||
String eldestValue = lruItem.get(eldestKey);
|
||||
return eldestValue;
|
||||
return lruItem.get(eldestKey);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ReturnT<String> route(TriggerParam triggerParam, List<String> addressList) {
|
||||
public Response<String> route(TriggerRequest triggerParam, List<String> addressList) {
|
||||
String address = route(triggerParam.getJobId(), addressList);
|
||||
return new ReturnT<String>(address);
|
||||
return Response.ofSuccess(address);
|
||||
}
|
||||
|
||||
}
|
||||
@ -0,0 +1,19 @@
|
||||
package com.xxl.job.admin.business.scheduler.route.strategy;
|
||||
|
||||
import com.xxl.job.admin.business.scheduler.route.ExecutorRouter;
|
||||
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 ExecutorRouteLast extends ExecutorRouter {
|
||||
|
||||
@Override
|
||||
public Response<String> route(TriggerRequest triggerParam, List<String> addressList) {
|
||||
return Response.ofSuccess(addressList.get(addressList.size()-1));
|
||||
}
|
||||
|
||||
}
|
||||
@ -0,0 +1,23 @@
|
||||
package com.xxl.job.admin.business.scheduler.route.strategy;
|
||||
|
||||
import com.xxl.job.admin.business.scheduler.route.ExecutorRouter;
|
||||
import com.xxl.job.core.openapi.model.TriggerRequest;
|
||||
import com.xxl.tool.response.Response;
|
||||
|
||||
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 Response<String> route(TriggerRequest triggerParam, List<String> addressList) {
|
||||
String address = addressList.get(localRandom.nextInt(addressList.size()));
|
||||
return Response.ofSuccess(address);
|
||||
}
|
||||
|
||||
}
|
||||
@ -1,8 +1,8 @@
|
||||
package com.xxl.job.admin.core.route.strategy;
|
||||
package com.xxl.job.admin.business.scheduler.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 com.xxl.job.admin.business.scheduler.route.ExecutorRouter;
|
||||
import com.xxl.job.core.openapi.model.TriggerRequest;
|
||||
import com.xxl.tool.response.Response;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Random;
|
||||
@ -38,9 +38,9 @@ public class ExecutorRouteRound extends ExecutorRouter {
|
||||
}
|
||||
|
||||
@Override
|
||||
public ReturnT<String> route(TriggerParam triggerParam, List<String> addressList) {
|
||||
public Response<String> route(TriggerRequest triggerParam, List<String> addressList) {
|
||||
String address = addressList.get(count(triggerParam.getJobId())%addressList.size());
|
||||
return new ReturnT<String>(address);
|
||||
return Response.ofSuccess(address);
|
||||
}
|
||||
|
||||
}
|
||||
@ -0,0 +1,153 @@
|
||||
package com.xxl.job.admin.business.scheduler.thread;
|
||||
|
||||
import com.xxl.job.admin.business.model.XxlJobLog;
|
||||
import com.xxl.job.admin.business.scheduler.config.XxlJobAdminBootstrap;
|
||||
import com.xxl.job.admin.framework.util.I18nUtil;
|
||||
import com.xxl.job.core.context.XxlJobContext;
|
||||
import com.xxl.job.core.openapi.model.CallbackRequest;
|
||||
import com.xxl.tool.concurrent.CyclicThread;
|
||||
import com.xxl.tool.core.DateTool;
|
||||
import com.xxl.tool.response.Response;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import java.util.Date;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.*;
|
||||
|
||||
/**
|
||||
* job complate, for callback and result-lost
|
||||
*
|
||||
* @author xuxueli 2015-9-1 18:05:56
|
||||
*/
|
||||
public class JobCompleteHelper {
|
||||
private static final Logger logger = LoggerFactory.getLogger(JobCompleteHelper.class);
|
||||
|
||||
// ---------------------- monitor ----------------------
|
||||
|
||||
private ThreadPoolExecutor callbackThreadPool = null;
|
||||
private CyclicThread jobMonitorThread;
|
||||
|
||||
/**
|
||||
* start
|
||||
*/
|
||||
public void start(){
|
||||
|
||||
// 1、callbackThreadPool
|
||||
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).");
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
// 2、jobMonitorThread
|
||||
jobMonitorThread = new CyclicThread("JobCompleteHelper#jobMonitorThread", true, new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
// 任务结果丢失处理:调度记录停留在 "运行中" 状态超过10min,且对应执行器心跳注册失败不在线,则将本地调度主动标记失败;
|
||||
Date losedTime = DateTool.addMinutes(new Date(), -10);
|
||||
List<Long> losedJobIds = XxlJobAdminBootstrap.getInstance().getXxlJobLogMapper().findLostJobIds(losedTime);
|
||||
|
||||
if (losedJobIds!=null && losedJobIds.size()>0) {
|
||||
for (Long logId: losedJobIds) {
|
||||
|
||||
XxlJobLog jobLog = new XxlJobLog();
|
||||
jobLog.setId(logId);
|
||||
|
||||
jobLog.setHandleTime(new Date());
|
||||
jobLog.setHandleCode(XxlJobContext.HANDLE_CODE_FAIL);
|
||||
jobLog.setHandleMsg( I18nUtil.getString("joblog_lost_fail") );
|
||||
|
||||
XxlJobAdminBootstrap.getInstance().getJobCompleter().complete(jobLog);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}, 60 * 1000L, true);
|
||||
jobMonitorThread.start();
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* stop
|
||||
*/
|
||||
public void stop(){
|
||||
|
||||
// 1、callbackThreadPool
|
||||
callbackThreadPool.shutdownNow();
|
||||
|
||||
// 2、jobMonitorThread
|
||||
jobMonitorThread.stop();
|
||||
}
|
||||
|
||||
|
||||
// ---------------------- helper ----------------------
|
||||
|
||||
/**
|
||||
* callback
|
||||
*
|
||||
* @param callbackParamList callback param
|
||||
* @return callback result
|
||||
*/
|
||||
public Response<String> callback(List<CallbackRequest> callbackParamList) {
|
||||
|
||||
callbackThreadPool.execute(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
for (CallbackRequest callbackRequest: callbackParamList) {
|
||||
Response<String> callbackResult = doCallback(callbackRequest);
|
||||
logger.debug(">>>>>>>>> JobApiController.callback {}, callbackRequest={}, callbackResult={}",
|
||||
(callbackResult.isSuccess()?"success":"fail"), callbackRequest, callbackResult);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
return Response.ofSuccess();
|
||||
}
|
||||
|
||||
private Response<String> doCallback(CallbackRequest handleCallbackParam) {
|
||||
// valid log item
|
||||
XxlJobLog log = XxlJobAdminBootstrap.getInstance().getXxlJobLogMapper().load(handleCallbackParam.getLogId());
|
||||
if (log == null) {
|
||||
return Response.ofFail( "log item not found.");
|
||||
}
|
||||
if (log.getHandleCode() > 0) {
|
||||
return Response.ofFail("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());
|
||||
XxlJobAdminBootstrap.getInstance().getJobCompleter().complete(log);
|
||||
|
||||
return Response.ofSuccess();
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
||||
@ -0,0 +1,82 @@
|
||||
package com.xxl.job.admin.business.scheduler.thread;
|
||||
|
||||
import com.xxl.job.admin.business.model.XxlJobInfo;
|
||||
import com.xxl.job.admin.business.model.XxlJobLog;
|
||||
import com.xxl.job.admin.business.scheduler.config.XxlJobAdminBootstrap;
|
||||
import com.xxl.job.admin.business.scheduler.trigger.TriggerTypeEnum;
|
||||
import com.xxl.job.admin.framework.util.I18nUtil;
|
||||
import com.xxl.tool.concurrent.CyclicThread;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* job fail-monitor helper
|
||||
*
|
||||
* @author xuxueli 2015-9-1 18:05:56
|
||||
*/
|
||||
public class JobFailAlarmMonitorHelper {
|
||||
private static Logger logger = LoggerFactory.getLogger(JobFailAlarmMonitorHelper.class);
|
||||
|
||||
|
||||
// ---------------------- monitor ----------------------
|
||||
|
||||
/**
|
||||
* monitor thread
|
||||
*/
|
||||
private CyclicThread monitorThread;
|
||||
|
||||
/**
|
||||
* start
|
||||
*/
|
||||
public void start(){
|
||||
|
||||
monitorThread = new CyclicThread("JobFailAlarmMonitorHelper#monitorThread", true, new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
List<Long> failLogIds = XxlJobAdminBootstrap.getInstance().getXxlJobLogMapper().findFailJobLogIds(1000);
|
||||
if (failLogIds!=null && !failLogIds.isEmpty()) {
|
||||
for (long failLogId: failLogIds) {
|
||||
|
||||
// lock log
|
||||
int lockRet = XxlJobAdminBootstrap.getInstance().getXxlJobLogMapper().updateAlarmStatus(failLogId, 0, -1);
|
||||
if (lockRet < 1) {
|
||||
continue;
|
||||
}
|
||||
XxlJobLog log = XxlJobAdminBootstrap.getInstance().getXxlJobLogMapper().load(failLogId);
|
||||
XxlJobInfo info = XxlJobAdminBootstrap.getInstance().getXxlJobInfoMapper().loadById(log.getJobId());
|
||||
|
||||
// 1、fail retry monitor
|
||||
if (log.getExecutorFailRetryCount() > 0) {
|
||||
XxlJobAdminBootstrap.getInstance().getJobTriggerPoolHelper().trigger(log.getJobId(), TriggerTypeEnum.RETRY, (log.getExecutorFailRetryCount()-1), log.getExecutorShardingParam(), log.getExecutorParam(), null);
|
||||
String retryMsg = "<br><br><span style=\"color:#00c0ef;\" > >>>>>>>>>>>"+ I18nUtil.getString("jobconf_trigger_type_retry") +"<<<<<<<<<<< </span><br>";
|
||||
log.setTriggerMsg(log.getTriggerMsg() + retryMsg);
|
||||
XxlJobAdminBootstrap.getInstance().getXxlJobLogMapper().updateTriggerInfo(log);
|
||||
}
|
||||
|
||||
// 2、fail alarm monitor
|
||||
int newAlarmStatus = 0; // 告警状态:0-默认、-1=锁定状态、1-无需告警、2-告警成功、3-告警失败
|
||||
if (info != null) {
|
||||
boolean alarmResult = XxlJobAdminBootstrap.getInstance().getJobAlarmer().alarm(info, log);
|
||||
newAlarmStatus = alarmResult?2:3;
|
||||
} else {
|
||||
newAlarmStatus = 1;
|
||||
}
|
||||
|
||||
XxlJobAdminBootstrap.getInstance().getXxlJobLogMapper().updateAlarmStatus(failLogId, -1, newAlarmStatus);
|
||||
}
|
||||
}
|
||||
}
|
||||
}, 10 * 1000L, true);
|
||||
monitorThread.start();
|
||||
}
|
||||
|
||||
/**
|
||||
* stop
|
||||
*/
|
||||
public void stop(){
|
||||
monitorThread.stop();
|
||||
}
|
||||
|
||||
}
|
||||
@ -0,0 +1,128 @@
|
||||
package com.xxl.job.admin.business.scheduler.thread;
|
||||
|
||||
import com.xxl.job.admin.business.model.XxlJobLogReport;
|
||||
import com.xxl.job.admin.business.scheduler.config.XxlJobAdminBootstrap;
|
||||
import com.xxl.tool.concurrent.CyclicThread;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import java.util.Calendar;
|
||||
import java.util.Date;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.atomic.AtomicLong;
|
||||
|
||||
/**
|
||||
* job log report helper
|
||||
*
|
||||
* @author xuxueli 2019-11-22
|
||||
*/
|
||||
public class JobLogReportHelper {
|
||||
private static final Logger logger = LoggerFactory.getLogger(JobLogReportHelper.class);
|
||||
|
||||
private CyclicThread logReportThread;
|
||||
private AtomicLong lastCleanLogTime;
|
||||
|
||||
/**
|
||||
* start
|
||||
*/
|
||||
public void start(){
|
||||
|
||||
/**
|
||||
* last clean log time ( Thread-safe concurrent reading and writing )
|
||||
*/
|
||||
lastCleanLogTime = new AtomicLong(0);
|
||||
|
||||
// log report thread
|
||||
logReportThread = new CyclicThread("JobLogReportHelper#logReportThread", true, new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
|
||||
// 1、log-report refresh: refresh log report in 3 days
|
||||
for (int i = 0; i < 3; i++) {
|
||||
|
||||
// today
|
||||
Calendar itemDay = Calendar.getInstance();
|
||||
itemDay.add(Calendar.DAY_OF_MONTH, -i);
|
||||
itemDay.set(Calendar.HOUR_OF_DAY, 0);
|
||||
itemDay.set(Calendar.MINUTE, 0);
|
||||
itemDay.set(Calendar.SECOND, 0);
|
||||
itemDay.set(Calendar.MILLISECOND, 0);
|
||||
|
||||
Date todayFrom = itemDay.getTime();
|
||||
|
||||
itemDay.set(Calendar.HOUR_OF_DAY, 23);
|
||||
itemDay.set(Calendar.MINUTE, 59);
|
||||
itemDay.set(Calendar.SECOND, 59);
|
||||
itemDay.set(Calendar.MILLISECOND, 999);
|
||||
|
||||
Date todayTo = itemDay.getTime();
|
||||
|
||||
// refresh log-report every minute
|
||||
XxlJobLogReport xxlJobLogReport = new XxlJobLogReport();
|
||||
xxlJobLogReport.setTriggerDay(todayFrom);
|
||||
xxlJobLogReport.setRunningCount(0);
|
||||
xxlJobLogReport.setSucCount(0);
|
||||
xxlJobLogReport.setFailCount(0);
|
||||
xxlJobLogReport.setUpdateTime(new Date());
|
||||
|
||||
// fill count-data
|
||||
Map<String, Object> triggerCountMap = XxlJobAdminBootstrap.getInstance().getXxlJobLogMapper().findLogReport(todayFrom, todayTo);
|
||||
if (triggerCountMap!=null && !triggerCountMap.isEmpty()) {
|
||||
int triggerDayCount = triggerCountMap.containsKey("triggerDayCount")?Integer.parseInt(String.valueOf(triggerCountMap.get("triggerDayCount"))):0;
|
||||
int triggerDayCountRunning = triggerCountMap.containsKey("triggerDayCountRunning")?Integer.parseInt(String.valueOf(triggerCountMap.get("triggerDayCountRunning"))):0;
|
||||
int triggerDayCountSuc = triggerCountMap.containsKey("triggerDayCountSuc")?Integer.parseInt(String.valueOf(triggerCountMap.get("triggerDayCountSuc"))):0;
|
||||
int triggerDayCountFail = triggerDayCount - triggerDayCountRunning - triggerDayCountSuc;
|
||||
|
||||
xxlJobLogReport.setRunningCount(triggerDayCountRunning);
|
||||
xxlJobLogReport.setSucCount(triggerDayCountSuc);
|
||||
xxlJobLogReport.setFailCount(triggerDayCountFail);
|
||||
}
|
||||
|
||||
// do refresh:
|
||||
XxlJobAdminBootstrap.getInstance().getXxlJobLogReportMapper().saveOrUpdate(xxlJobLogReport); // 0-fail; 1-save suc; 2-update suc;
|
||||
/*if (ret < 1) {
|
||||
XxlJobAdminBootstrap.getInstance().getXxlJobLogReportMapper().save(xxlJobLogReport);
|
||||
}*/
|
||||
}
|
||||
|
||||
// 2、log-clean: switch open & once each day
|
||||
if (XxlJobAdminBootstrap.getInstance().getLogretentiondays()>0
|
||||
&& System.currentTimeMillis() - lastCleanLogTime.longValue() > 24*60*60*1000) {
|
||||
|
||||
// expire-time
|
||||
Calendar expiredDay = Calendar.getInstance();
|
||||
expiredDay.add(Calendar.DAY_OF_MONTH, -1 * XxlJobAdminBootstrap.getInstance().getLogretentiondays());
|
||||
expiredDay.set(Calendar.HOUR_OF_DAY, 0);
|
||||
expiredDay.set(Calendar.MINUTE, 0);
|
||||
expiredDay.set(Calendar.SECOND, 0);
|
||||
expiredDay.set(Calendar.MILLISECOND, 0);
|
||||
Date clearBeforeTime = expiredDay.getTime();
|
||||
|
||||
// clean expired log
|
||||
List<Long> logIds = null;
|
||||
do {
|
||||
logIds = XxlJobAdminBootstrap.getInstance().getXxlJobLogMapper().findClearLogIds(0, 0, clearBeforeTime, 0, 1000);
|
||||
if (logIds!=null && !logIds.isEmpty()) {
|
||||
XxlJobAdminBootstrap.getInstance().getXxlJobLogMapper().clearLog(logIds);
|
||||
}
|
||||
} while (logIds!=null && !logIds.isEmpty());
|
||||
|
||||
// update clean time
|
||||
lastCleanLogTime.set(System.currentTimeMillis());
|
||||
}
|
||||
|
||||
}
|
||||
}, 60 * 1000L, true);
|
||||
logReportThread.start();
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* stop
|
||||
*/
|
||||
public void stop(){
|
||||
logReportThread.stop();
|
||||
}
|
||||
|
||||
}
|
||||
@ -0,0 +1,204 @@
|
||||
package com.xxl.job.admin.business.scheduler.thread;
|
||||
|
||||
import com.xxl.job.admin.business.model.XxlJobGroup;
|
||||
import com.xxl.job.admin.business.model.XxlJobRegistry;
|
||||
import com.xxl.job.admin.business.scheduler.config.XxlJobAdminBootstrap;
|
||||
import com.xxl.job.core.constant.Const;
|
||||
import com.xxl.job.core.constant.RegistTypeEnum;
|
||||
import com.xxl.job.core.openapi.model.RegistryRequest;
|
||||
import com.xxl.tool.concurrent.CyclicThread;
|
||||
import com.xxl.tool.core.StringTool;
|
||||
import com.xxl.tool.response.Response;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import java.util.*;
|
||||
import java.util.concurrent.*;
|
||||
|
||||
/**
|
||||
* job registry instance helper
|
||||
*
|
||||
* @author xuxueli 2016-10-02 19:10:24
|
||||
*/
|
||||
public class JobRegistryHelper {
|
||||
private static final Logger logger = LoggerFactory.getLogger(JobRegistryHelper.class);
|
||||
|
||||
|
||||
/**
|
||||
* registry or remove thread pool
|
||||
*/
|
||||
private ThreadPoolExecutor registryOrRemoveThreadPool = null;
|
||||
|
||||
/**
|
||||
* registry monitor thread
|
||||
*/
|
||||
private CyclicThread registryMonitorThread;
|
||||
|
||||
/**
|
||||
* start
|
||||
*/
|
||||
public void start(){
|
||||
|
||||
// 1、for registry or remove
|
||||
registryOrRemoveThreadPool = new ThreadPoolExecutor(
|
||||
2,
|
||||
10,
|
||||
30L,
|
||||
TimeUnit.SECONDS,
|
||||
new LinkedBlockingQueue<Runnable>(2000),
|
||||
new ThreadFactory() {
|
||||
@Override
|
||||
public Thread newThread(Runnable r) {
|
||||
return new Thread(r, "xxl-job, admin JobRegistryMonitorHelper-registryOrRemoveThreadPool-" + r.hashCode());
|
||||
}
|
||||
},
|
||||
new RejectedExecutionHandler() {
|
||||
@Override
|
||||
public void rejectedExecution(Runnable r, ThreadPoolExecutor executor) {
|
||||
r.run();
|
||||
logger.warn(">>>>>>>>>>> xxl-job, registry or remove too fast, match threadpool rejected handler(run now).");
|
||||
}
|
||||
});
|
||||
|
||||
// 2、for registry monitor
|
||||
registryMonitorThread = new CyclicThread("JobRegistryHelper#registryMonitorThread", true, new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
// auto registry group
|
||||
List<XxlJobGroup> groupList = XxlJobAdminBootstrap.getInstance().getXxlJobGroupMapper().findByAddressType(0);
|
||||
if (groupList!=null && !groupList.isEmpty()) {
|
||||
|
||||
// remove dead address (admin/executor)
|
||||
List<Integer> ids = XxlJobAdminBootstrap.getInstance().getXxlJobRegistryMapper().findDead(Const.DEAD_TIMEOUT, new Date());
|
||||
if (ids!=null && !ids.isEmpty()) {
|
||||
XxlJobAdminBootstrap.getInstance().getXxlJobRegistryMapper().removeDead(ids);
|
||||
}
|
||||
|
||||
// fresh online address (admin/executor)
|
||||
HashMap<String, List<String>> appAddressMap = new HashMap<String, List<String>>();
|
||||
List<XxlJobRegistry> list = XxlJobAdminBootstrap.getInstance().getXxlJobRegistryMapper().findAll(Const.DEAD_TIMEOUT, new Date());
|
||||
if (list != null) {
|
||||
for (XxlJobRegistry item: list) {
|
||||
if (RegistTypeEnum.EXECUTOR.name().equals(item.getRegistryGroup())) {
|
||||
String appname = item.getRegistryKey();
|
||||
List<String> registryList = appAddressMap.get(appname);
|
||||
if (registryList == null) {
|
||||
registryList = new ArrayList<String>();
|
||||
}
|
||||
|
||||
if (!registryList.contains(item.getRegistryValue())) {
|
||||
registryList.add(item.getRegistryValue());
|
||||
}
|
||||
appAddressMap.put(appname, registryList);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// fresh group address
|
||||
for (XxlJobGroup group: groupList) {
|
||||
List<String> registryList = appAddressMap.get(group.getAppname());
|
||||
String addressListStr = null;
|
||||
if (registryList!=null && !registryList.isEmpty()) {
|
||||
Collections.sort(registryList);
|
||||
StringBuilder addressListSB = new StringBuilder();
|
||||
for (String item:registryList) {
|
||||
addressListSB.append(item).append(",");
|
||||
}
|
||||
addressListStr = addressListSB.toString();
|
||||
addressListStr = addressListStr.substring(0, addressListStr.length()-1);
|
||||
}
|
||||
group.setAddressList(addressListStr);
|
||||
group.setUpdateTime(new Date());
|
||||
|
||||
XxlJobAdminBootstrap.getInstance().getXxlJobGroupMapper().update(group);
|
||||
}
|
||||
}
|
||||
}
|
||||
}, Const.BEAT_TIMEOUT * 1000L, true);
|
||||
registryMonitorThread.start();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* stop
|
||||
*/
|
||||
public void stop(){
|
||||
|
||||
// 1、registryOrRemoveThreadPool
|
||||
registryOrRemoveThreadPool.shutdownNow();
|
||||
|
||||
// 2、registryMonitorThread
|
||||
registryMonitorThread.stop();
|
||||
}
|
||||
|
||||
|
||||
// ---------------------- tool ----------------------
|
||||
|
||||
/**
|
||||
* registry
|
||||
*/
|
||||
public Response<String> registry(RegistryRequest registryParam) {
|
||||
|
||||
// valid
|
||||
if (StringTool.isBlank(registryParam.getRegistryGroup())
|
||||
|| StringTool.isBlank(registryParam.getRegistryKey())
|
||||
|| StringTool.isBlank(registryParam.getRegistryValue())) {
|
||||
return Response.ofFail("Illegal Argument.");
|
||||
}
|
||||
|
||||
// async execute
|
||||
registryOrRemoveThreadPool.execute(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
// 0-fail; 1-save suc; 2-update suc;
|
||||
int ret = XxlJobAdminBootstrap.getInstance().getXxlJobRegistryMapper().registrySaveOrUpdate(registryParam.getRegistryGroup(), registryParam.getRegistryKey(), registryParam.getRegistryValue(), new Date());
|
||||
if (ret == 1) {
|
||||
// fresh (add)
|
||||
freshGroupRegistryInfo(registryParam);
|
||||
}
|
||||
/*int ret = XxlJobAdminConfig.getAdminConfig().getXxlJobRegistryDao().registryUpdate(registryParam.getRegistryGroup(), registryParam.getRegistryKey(), registryParam.getRegistryValue(), new Date());
|
||||
if (ret < 1) {
|
||||
XxlJobAdminConfig.getAdminConfig().getXxlJobRegistryDao().registrySave(registryParam.getRegistryGroup(), registryParam.getRegistryKey(), registryParam.getRegistryValue(), new Date());
|
||||
|
||||
// fresh
|
||||
freshGroupRegistryInfo(registryParam);
|
||||
}*/
|
||||
}
|
||||
});
|
||||
|
||||
return Response.ofSuccess();
|
||||
}
|
||||
|
||||
/**
|
||||
* registry remove
|
||||
*/
|
||||
public Response<String> registryRemove(RegistryRequest registryParam) {
|
||||
|
||||
// valid
|
||||
if (StringTool.isBlank(registryParam.getRegistryGroup())
|
||||
|| StringTool.isBlank(registryParam.getRegistryKey())
|
||||
|| StringTool.isBlank(registryParam.getRegistryValue())) {
|
||||
return Response.ofFail("Illegal Argument.");
|
||||
}
|
||||
|
||||
// async execute
|
||||
registryOrRemoveThreadPool.execute(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
int ret = XxlJobAdminBootstrap.getInstance().getXxlJobRegistryMapper().registryDelete(registryParam.getRegistryGroup(), registryParam.getRegistryKey(), registryParam.getRegistryValue());
|
||||
if (ret > 0) {
|
||||
// fresh (delete)
|
||||
freshGroupRegistryInfo(registryParam);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
return Response.ofSuccess();
|
||||
}
|
||||
|
||||
private void freshGroupRegistryInfo(RegistryRequest registryParam){
|
||||
// Under consideration, prevent affecting core tables
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@ -0,0 +1,374 @@
|
||||
package com.xxl.job.admin.business.scheduler.thread;
|
||||
|
||||
import com.xxl.job.admin.business.constant.TriggerStatus;
|
||||
import com.xxl.job.admin.business.model.XxlJobInfo;
|
||||
import com.xxl.job.admin.business.scheduler.config.XxlJobAdminBootstrap;
|
||||
import com.xxl.job.admin.business.scheduler.misfire.MisfireStrategyEnum;
|
||||
import com.xxl.job.admin.business.scheduler.trigger.TriggerTypeEnum;
|
||||
import com.xxl.job.admin.business.scheduler.type.ScheduleTypeEnum;
|
||||
import com.xxl.tool.core.CollectionTool;
|
||||
import com.xxl.tool.core.MapTool;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.transaction.TransactionStatus;
|
||||
import org.springframework.transaction.support.DefaultTransactionDefinition;
|
||||
|
||||
import java.util.*;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
/**
|
||||
* @author xuxueli 2019-05-21
|
||||
*/
|
||||
public class JobScheduleHelper {
|
||||
private static final Logger logger = LoggerFactory.getLogger(JobScheduleHelper.class);
|
||||
|
||||
|
||||
/**
|
||||
* pre-read time for scheduler, increase efficiency
|
||||
*/
|
||||
public static final long PRE_READ_MS = 5000;
|
||||
/*
|
||||
* elegant shutdown wait seconds
|
||||
*/
|
||||
private static final long ELEGANT_SHUTDOWN_WAITING_SECONDS = 10;
|
||||
|
||||
private Thread scheduleThread;
|
||||
private Thread ringThread;
|
||||
private volatile boolean scheduleThreadToStop = false;
|
||||
private volatile boolean ringThreadToStop = false;
|
||||
private final Map<Integer, List<Integer>> ringData = new ConcurrentHashMap<>();
|
||||
|
||||
/**
|
||||
* start
|
||||
*/
|
||||
public void start(){
|
||||
|
||||
// init thread flag
|
||||
scheduleThreadToStop = false;
|
||||
ringThreadToStop = false;
|
||||
|
||||
// 1、schedule thread
|
||||
scheduleThread = new Thread(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
|
||||
// align time
|
||||
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 * 10 (trigger-qps: 1000ms / 100ms each trigger cost)
|
||||
int preReadCount = (XxlJobAdminBootstrap.getInstance().getTriggerPoolFastMax() + XxlJobAdminBootstrap.getInstance().getTriggerPoolSlowMax()) * 10;
|
||||
|
||||
// do schedule
|
||||
while (!scheduleThreadToStop) {
|
||||
|
||||
// param
|
||||
long start = System.currentTimeMillis();
|
||||
boolean preReadSuc = true;
|
||||
|
||||
// transaction start
|
||||
TransactionStatus transactionStatus = null;
|
||||
try {
|
||||
transactionStatus = XxlJobAdminBootstrap.getInstance().getTransactionManager().getTransaction(new DefaultTransactionDefinition());
|
||||
// 1、job lock
|
||||
String lockedRecord = XxlJobAdminBootstrap.getInstance().getXxlJobLockMapper().scheduleLock();
|
||||
long nowTime = System.currentTimeMillis();
|
||||
|
||||
// scan and process job
|
||||
List<XxlJobInfo> scheduleList = XxlJobAdminBootstrap.getInstance().getXxlJobInfoMapper().scheduleJobQuery(nowTime + PRE_READ_MS, preReadCount);
|
||||
if (CollectionTool.isNotEmpty(scheduleList)) {
|
||||
|
||||
// 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
|
||||
|
||||
// 1、misfire handle
|
||||
MisfireStrategyEnum misfireStrategyEnum = MisfireStrategyEnum.match(jobInfo.getMisfireStrategy(), MisfireStrategyEnum.DO_NOTHING);
|
||||
misfireStrategyEnum.getMisfireHandler().handle(jobInfo.getId());
|
||||
|
||||
// 2、fresh next
|
||||
refreshNextTriggerTime(jobInfo, new Date());
|
||||
|
||||
} else if (nowTime >= jobInfo.getTriggerNextTime()) {
|
||||
// 2.2、trigger-expire < 5s:direct-trigger && make next-trigger-time
|
||||
|
||||
// 1、trigger direct
|
||||
XxlJobAdminBootstrap.getInstance().getJobTriggerPoolHelper().trigger(jobInfo.getId(), TriggerTypeEnum.CRON, -1, null, null, null);
|
||||
logger.debug(">>>>>>>>>>> xxl-job, schedule expire, direct trigger : jobId = " + jobInfo.getId() );
|
||||
|
||||
// 2、fresh next
|
||||
refreshNextTriggerTime(jobInfo, new Date());
|
||||
|
||||
// next-trigger-time in 5s, pre-read again
|
||||
if (jobInfo.getTriggerStatus()== TriggerStatus.RUNNING.getValue() && nowTime + PRE_READ_MS > jobInfo.getTriggerNextTime()) {
|
||||
|
||||
// 1、make ring second
|
||||
int ringSecond = (int)((jobInfo.getTriggerNextTime()/1000)%60);
|
||||
|
||||
// 2、push time ring (pre read)
|
||||
pushTimeRing(ringSecond, jobInfo.getId());
|
||||
logger.debug(">>>>>>>>>>> xxl-job, schedule pre-read, push trigger : jobId = " + jobInfo.getId() );
|
||||
|
||||
// 3、fresh next
|
||||
refreshNextTriggerTime(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());
|
||||
logger.debug(">>>>>>>>>>> xxl-job, schedule normal, push trigger : jobId = " + jobInfo.getId() );
|
||||
|
||||
// 3、fresh next
|
||||
refreshNextTriggerTime(jobInfo, new Date(jobInfo.getTriggerNextTime()));
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// 3、update trigger info
|
||||
/*for (XxlJobInfo jobInfo: scheduleList) {
|
||||
XxlJobAdminBootstrap.getInstance().getXxlJobInfoMapper().scheduleUpdate(jobInfo);
|
||||
}*/
|
||||
int batchSize = XxlJobAdminBootstrap.getInstance().getScheduleBatchSize();
|
||||
List<List<XxlJobInfo>> scheduleListBatches = CollectionTool.split(scheduleList, batchSize);
|
||||
for (List<XxlJobInfo> scheduleListBatch : scheduleListBatches) {
|
||||
int totalAffected = XxlJobAdminBootstrap.getInstance().getXxlJobInfoMapper().scheduleBatchUpdate(scheduleListBatch);
|
||||
logger.debug(">>>>>>>>>>> xxl-job, JobScheduleHelper scheduleBatchUpdate records:" + totalAffected);
|
||||
}
|
||||
|
||||
} else {
|
||||
preReadSuc = false;
|
||||
}
|
||||
|
||||
} catch (Throwable e) {
|
||||
if (!scheduleThreadToStop) {
|
||||
logger.error(">>>>>>>>>>> xxl-job, JobScheduleHelper#scheduleThread error:{}", e.getMessage(), e);
|
||||
}
|
||||
} finally {
|
||||
// transaction commit
|
||||
try {
|
||||
if (transactionStatus != null) {
|
||||
XxlJobAdminBootstrap.getInstance().getTransactionManager().commit(transactionStatus); // avlid schedule repeat
|
||||
}
|
||||
} catch (Throwable e) {
|
||||
logger.error(">>>>>>>>>>> xxl-job, JobScheduleHelper#scheduleThread transaction commit error:{}", e.getMessage(), e);
|
||||
}
|
||||
}
|
||||
// transaction end
|
||||
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();
|
||||
|
||||
// 2、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<>();
|
||||
|
||||
// collect rind data, by second
|
||||
int nowSecond = Calendar.getInstance().get(Calendar.SECOND);
|
||||
for (int i = 0; i <= 2; i++) { // 避免调度遗漏:处理耗时太长、跨过刻度,除当前刻度外 + 向前校验2个刻度;
|
||||
List<Integer> ringItemList = ringData.remove( (nowSecond+60-i)%60 );
|
||||
if (CollectionTool.isNotEmpty(ringItemList)) {
|
||||
// distinct for each second
|
||||
List<Integer> ringItemListDistinct = ringItemList.stream().distinct().toList(); // 避免调度重复:重复推送时间轮刻度,去重只保留一个;;
|
||||
if (ringItemListDistinct.size() < ringItemList.size()) {
|
||||
logger.warn(">>>>>>>>>>> xxl-job, time-ring found job repeat beat : " + nowSecond + " = " + ringItemData);
|
||||
}
|
||||
|
||||
// collect ring item
|
||||
ringItemData.addAll(ringItemListDistinct);
|
||||
}
|
||||
}
|
||||
|
||||
// ring trigger
|
||||
logger.debug(">>>>>>>>>>> xxl-job, time-ring beat : " + nowSecond + " = " + ringItemData);
|
||||
if (CollectionTool.isNotEmpty(ringItemData)) {
|
||||
// do trigger
|
||||
for (int jobId: ringItemData) {
|
||||
// do trigger
|
||||
XxlJobAdminBootstrap.getInstance().getJobTriggerPoolHelper().trigger(jobId, TriggerTypeEnum.CRON, -1, null, null, null);
|
||||
}
|
||||
// clear
|
||||
ringItemData.clear();
|
||||
}
|
||||
} catch (Throwable e) {
|
||||
if (!ringThreadToStop) {
|
||||
logger.error(">>>>>>>>>>> xxl-job, JobScheduleHelper#ringThread error:{}", e.getMessage(), e);
|
||||
}
|
||||
}
|
||||
}
|
||||
logger.info(">>>>>>>>>>> xxl-job, JobScheduleHelper#ringThread stop");
|
||||
}
|
||||
});
|
||||
ringThread.setDaemon(true);
|
||||
ringThread.setName("xxl-job, admin JobScheduleHelper#ringThread");
|
||||
ringThread.start();
|
||||
}
|
||||
|
||||
/**
|
||||
* refresh next trigger time of job
|
||||
*
|
||||
* @param jobInfo job info
|
||||
* @param fromTime from time
|
||||
*/
|
||||
private void refreshNextTriggerTime(XxlJobInfo jobInfo, Date fromTime) {
|
||||
try {
|
||||
// generate next trigger time
|
||||
ScheduleTypeEnum scheduleTypeEnum = ScheduleTypeEnum.match(jobInfo.getScheduleType(), ScheduleTypeEnum.NONE);
|
||||
Date nextTriggerTime = scheduleTypeEnum.getScheduleType().generateNextTriggerTime(jobInfo, fromTime);
|
||||
|
||||
// refresh next trigger-time + status
|
||||
if (nextTriggerTime != null) {
|
||||
// generate success
|
||||
jobInfo.setTriggerStatus(-1); // pass, may be Inaccurate
|
||||
jobInfo.setTriggerLastTime(jobInfo.getTriggerNextTime());
|
||||
jobInfo.setTriggerNextTime(nextTriggerTime.getTime());
|
||||
} else {
|
||||
// generate fail, stop job
|
||||
jobInfo.setTriggerStatus(TriggerStatus.STOPPED.getValue());
|
||||
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) {
|
||||
// generate error, stop job
|
||||
jobInfo.setTriggerStatus(TriggerStatus.STOPPED.getValue());
|
||||
jobInfo.setTriggerLastTime(0);
|
||||
jobInfo.setTriggerNextTime(0);
|
||||
|
||||
logger.error(">>>>>>>>>>> xxl-job, refreshNextValidTime error for job: jobId={}, scheduleType={}, scheduleConf={}",
|
||||
jobInfo.getId(), jobInfo.getScheduleType(), jobInfo.getScheduleConf(), e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* push time ring
|
||||
*
|
||||
* @param ringSecond ring second
|
||||
* @param jobId job id
|
||||
*/
|
||||
private void pushTimeRing(int ringSecond, int jobId){
|
||||
// get ringItemData, init when not exists
|
||||
List<Integer> ringItemList = ringData.computeIfAbsent(
|
||||
ringSecond,
|
||||
k -> new ArrayList<>());
|
||||
|
||||
// push async rind
|
||||
ringItemList.add(jobId);
|
||||
logger.debug(">>>>>>>>>>> xxl-job, schedule push time-ring : " + ringSecond + " = " + List.of(ringItemList));
|
||||
}
|
||||
|
||||
/**
|
||||
* stop
|
||||
*/
|
||||
public void stop(){
|
||||
|
||||
// 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, wait for elegent shutdown
|
||||
boolean hasRingData = false;
|
||||
if (MapTool.isNotEmpty(ringData)) {
|
||||
for (int second : ringData.keySet()) {
|
||||
List<Integer> ringItemList = ringData.get(second);
|
||||
if (CollectionTool.isNotEmpty(ringItemList)) {
|
||||
hasRingData = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (hasRingData) {
|
||||
try {
|
||||
TimeUnit.SECONDS.sleep(ELEGANT_SHUTDOWN_WAITING_SECONDS);
|
||||
} catch (Throwable e) {
|
||||
logger.error(e.getMessage(), e);
|
||||
}
|
||||
}
|
||||
|
||||
// 2、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");
|
||||
}
|
||||
|
||||
}
|
||||
@ -1,8 +1,7 @@
|
||||
package com.xxl.job.admin.core.thread;
|
||||
package com.xxl.job.admin.business.scheduler.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 com.xxl.job.admin.business.scheduler.config.XxlJobAdminBootstrap;
|
||||
import com.xxl.job.admin.business.scheduler.trigger.TriggerTypeEnum;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
@ -15,7 +14,7 @@ import java.util.concurrent.atomic.AtomicInteger;
|
||||
* @author xuxueli 2018-07-03 21:08:07
|
||||
*/
|
||||
public class JobTriggerPoolHelper {
|
||||
private static Logger logger = LoggerFactory.getLogger(JobTriggerPoolHelper.class);
|
||||
private static final Logger logger = LoggerFactory.getLogger(JobTriggerPoolHelper.class);
|
||||
|
||||
|
||||
// ---------------------- trigger pool ----------------------
|
||||
@ -24,35 +23,52 @@ public class JobTriggerPoolHelper {
|
||||
private ThreadPoolExecutor fastTriggerPool = null;
|
||||
private ThreadPoolExecutor slowTriggerPool = null;
|
||||
|
||||
/**
|
||||
* start
|
||||
*/
|
||||
public void start(){
|
||||
fastTriggerPool = new ThreadPoolExecutor(
|
||||
10,
|
||||
XxlJobAdminConfig.getAdminConfig().getTriggerPoolFastMax(),
|
||||
60L,
|
||||
TimeUnit.SECONDS,
|
||||
new LinkedBlockingQueue<Runnable>(1000),
|
||||
new ThreadFactory() {
|
||||
@Override
|
||||
public Thread newThread(Runnable r) {
|
||||
return new Thread(r, "xxl-job, admin JobTriggerPoolHelper-fastTriggerPool-" + r.hashCode());
|
||||
}
|
||||
});
|
||||
|
||||
slowTriggerPool = new ThreadPoolExecutor(
|
||||
10,
|
||||
XxlJobAdminConfig.getAdminConfig().getTriggerPoolSlowMax(),
|
||||
XxlJobAdminBootstrap.getInstance().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,
|
||||
XxlJobAdminBootstrap.getInstance().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() );
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* stop
|
||||
*/
|
||||
public void stop() {
|
||||
//triggerPool.shutdown();
|
||||
fastTriggerPool.shutdownNow();
|
||||
@ -66,15 +82,27 @@ public class JobTriggerPoolHelper {
|
||||
private volatile ConcurrentMap<Integer, AtomicInteger> jobTimeoutCountMap = new ConcurrentHashMap<>();
|
||||
|
||||
|
||||
// ---------------------- tool ----------------------
|
||||
|
||||
/**
|
||||
* add trigger
|
||||
* 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
|
||||
*/
|
||||
public void addTrigger(final int jobId,
|
||||
final TriggerTypeEnum triggerType,
|
||||
final int failRetryCount,
|
||||
final String executorShardingParam,
|
||||
final String executorParam,
|
||||
final String addressList) {
|
||||
public void trigger(final int jobId,
|
||||
final TriggerTypeEnum triggerType,
|
||||
final int failRetryCount,
|
||||
final String executorShardingParam,
|
||||
final String executorParam,
|
||||
final String addressList) {
|
||||
|
||||
// choose thread pool
|
||||
ThreadPoolExecutor triggerPool_ = fastTriggerPool;
|
||||
@ -92,8 +120,8 @@ public class JobTriggerPoolHelper {
|
||||
|
||||
try {
|
||||
// do trigger
|
||||
XxlJobTrigger.trigger(jobId, triggerType, failRetryCount, executorShardingParam, executorParam, addressList);
|
||||
} catch (Exception e) {
|
||||
XxlJobAdminBootstrap.getInstance().getJobTrigger().trigger(jobId, triggerType, failRetryCount, executorShardingParam, executorParam, addressList);
|
||||
} catch (Throwable e) {
|
||||
logger.error(e.getMessage(), e);
|
||||
} finally {
|
||||
|
||||
@ -116,35 +144,11 @@ public class JobTriggerPoolHelper {
|
||||
}
|
||||
|
||||
}
|
||||
@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,29 +1,46 @@
|
||||
package com.xxl.job.admin.core.trigger;
|
||||
package com.xxl.job.admin.business.scheduler.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 com.xxl.job.admin.business.mapper.XxlJobGroupMapper;
|
||||
import com.xxl.job.admin.business.mapper.XxlJobInfoMapper;
|
||||
import com.xxl.job.admin.business.mapper.XxlJobLogMapper;
|
||||
import com.xxl.job.admin.business.model.XxlJobGroup;
|
||||
import com.xxl.job.admin.business.model.XxlJobInfo;
|
||||
import com.xxl.job.admin.business.model.XxlJobLog;
|
||||
import com.xxl.job.admin.business.scheduler.config.XxlJobAdminBootstrap;
|
||||
import com.xxl.job.admin.business.scheduler.route.ExecutorRouteStrategyEnum;
|
||||
import com.xxl.job.admin.framework.util.I18nUtil;
|
||||
import com.xxl.job.core.constant.ExecutorBlockStrategyEnum;
|
||||
import com.xxl.job.core.context.XxlJobContext;
|
||||
import com.xxl.job.core.openapi.ExecutorBiz;
|
||||
import com.xxl.job.core.openapi.model.TriggerRequest;
|
||||
import com.xxl.tool.core.StringTool;
|
||||
import com.xxl.tool.error.ThrowableTool;
|
||||
import com.xxl.tool.http.IPTool;
|
||||
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.util.Date;
|
||||
|
||||
/**
|
||||
* xxl-job trigger
|
||||
* Created by xuxueli on 17/7/13.
|
||||
*
|
||||
* @author xuxueli 17/7/13.
|
||||
*/
|
||||
public class XxlJobTrigger {
|
||||
private static Logger logger = LoggerFactory.getLogger(XxlJobTrigger.class);
|
||||
@Component
|
||||
public class JobTrigger {
|
||||
private static final Logger logger = LoggerFactory.getLogger(JobTrigger.class);
|
||||
|
||||
|
||||
@Resource
|
||||
private XxlJobInfoMapper xxlJobInfoMapper;
|
||||
@Resource
|
||||
private XxlJobGroupMapper xxlJobGroupMapper;
|
||||
@Resource
|
||||
private XxlJobLogMapper xxlJobLogMapper;
|
||||
|
||||
|
||||
/**
|
||||
* trigger job
|
||||
@ -34,6 +51,8 @@ public class XxlJobTrigger {
|
||||
* >=0: use this param
|
||||
* <0: use param from job info config
|
||||
* @param executorShardingParam
|
||||
* null: new sharding, all nodes
|
||||
* not null: for retry, only one node
|
||||
* @param executorParam
|
||||
* null: use job param
|
||||
* not null: cover job param
|
||||
@ -41,7 +60,7 @@ public class XxlJobTrigger {
|
||||
* null: use executor addressList
|
||||
* not null: cover
|
||||
*/
|
||||
public static void trigger(int jobId,
|
||||
public void trigger(int jobId,
|
||||
TriggerTypeEnum triggerType,
|
||||
int failRetryCount,
|
||||
String executorShardingParam,
|
||||
@ -49,7 +68,7 @@ public class XxlJobTrigger {
|
||||
String addressList) {
|
||||
|
||||
// load data
|
||||
XxlJobInfo jobInfo = XxlJobAdminConfig.getAdminConfig().getXxlJobInfoDao().loadById(jobId);
|
||||
XxlJobInfo jobInfo = xxlJobInfoMapper.loadById(jobId);
|
||||
if (jobInfo == null) {
|
||||
logger.warn(">>>>>>>>>>>> trigger fail, jobId invalid,jobId={}", jobId);
|
||||
return;
|
||||
@ -58,57 +77,67 @@ public class XxlJobTrigger {
|
||||
jobInfo.setExecutorParam(executorParam);
|
||||
}
|
||||
int finalFailRetryCount = failRetryCount>=0?failRetryCount:jobInfo.getExecutorFailRetryCount();
|
||||
XxlJobGroup group = XxlJobAdminConfig.getAdminConfig().getXxlJobGroupDao().load(jobInfo.getJobGroup());
|
||||
XxlJobGroup group = xxlJobGroupMapper.load(jobInfo.getJobGroup());
|
||||
|
||||
// cover addressList
|
||||
if (addressList!=null && addressList.trim().length()>0) {
|
||||
if (StringTool.isNotBlank(addressList)) {
|
||||
group.setAddressType(1);
|
||||
group.setAddressList(addressList.trim());
|
||||
}
|
||||
|
||||
// sharding param
|
||||
int[] shardingParam = null;
|
||||
Date triggerTime = new Date();
|
||||
if (executorShardingParam!=null){
|
||||
String[] shardingArr = executorShardingParam.split("/");
|
||||
if (shardingArr.length==2 && isNumeric(shardingArr[0]) && isNumeric(shardingArr[1])) {
|
||||
if (shardingArr.length==2 && StringTool.isNumeric(shardingArr[0]) && StringTool.isNumeric(shardingArr[1])) {
|
||||
shardingParam = new int[2];
|
||||
shardingParam[0] = Integer.valueOf(shardingArr[0]);
|
||||
shardingParam[1] = Integer.valueOf(shardingArr[1]);
|
||||
shardingParam[0] = Integer.parseInt(shardingArr[0]);
|
||||
shardingParam[1] = Integer.parseInt(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());
|
||||
processTrigger(group, jobInfo, finalFailRetryCount, triggerType, triggerTime, i, group.getRegistryList().size());
|
||||
}
|
||||
} else {
|
||||
if (shardingParam == null) {
|
||||
shardingParam = new int[]{0, 1};
|
||||
}
|
||||
processTrigger(group, jobInfo, finalFailRetryCount, triggerType, shardingParam[0], shardingParam[1]);
|
||||
processTrigger(group, jobInfo, finalFailRetryCount, triggerType, triggerTime, shardingParam[0], shardingParam[1]);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private static boolean isNumeric(String str){
|
||||
/*private static boolean isNumeric(String str){
|
||||
try {
|
||||
int result = Integer.valueOf(str);
|
||||
return true;
|
||||
} catch (NumberFormatException e) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}*/
|
||||
|
||||
/**
|
||||
* process trigger with log
|
||||
*
|
||||
* @param group job group, registry list may be empty
|
||||
* @param jobInfo
|
||||
* @param finalFailRetryCount
|
||||
* @param triggerType
|
||||
* @param jobInfo job info
|
||||
* @param finalFailRetryCount the fail-retry count
|
||||
* @param triggerType trigger type
|
||||
* @param triggerTime trigger time
|
||||
* @param index sharding index
|
||||
* @param total sharding index
|
||||
*/
|
||||
private static void processTrigger(XxlJobGroup group, XxlJobInfo jobInfo, int finalFailRetryCount, TriggerTypeEnum triggerType, int index, int total){
|
||||
private void processTrigger(XxlJobGroup group,
|
||||
XxlJobInfo jobInfo,
|
||||
int finalFailRetryCount,
|
||||
TriggerTypeEnum triggerType,
|
||||
Date triggerTime,
|
||||
int index,
|
||||
int total){
|
||||
|
||||
// param
|
||||
ExecutorBlockStrategyEnum blockStrategy = ExecutorBlockStrategyEnum.match(jobInfo.getExecutorBlockStrategy(), ExecutorBlockStrategyEnum.SERIAL_EXECUTION); // block strategy
|
||||
@ -119,12 +148,12 @@ public class XxlJobTrigger {
|
||||
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());
|
||||
jobLog.setTriggerTime(triggerTime);
|
||||
xxlJobLogMapper.save(jobLog);
|
||||
logger.debug(">>>>>>>>>>> xxl-job trigger start, jobId:{}", jobLog.getJobId());
|
||||
|
||||
// 2、init trigger-param
|
||||
TriggerParam triggerParam = new TriggerParam();
|
||||
TriggerRequest triggerParam = new TriggerRequest();
|
||||
triggerParam.setJobId(jobInfo.getId());
|
||||
triggerParam.setExecutorHandler(jobInfo.getExecutorHandler());
|
||||
triggerParam.setExecutorParams(jobInfo.getExecutorParam());
|
||||
@ -140,7 +169,7 @@ public class XxlJobTrigger {
|
||||
|
||||
// 3、init address
|
||||
String address = null;
|
||||
ReturnT<String> routeAddressResult = null;
|
||||
Response<String> routeAddressResult = null;
|
||||
if (group.getRegistryList()!=null && !group.getRegistryList().isEmpty()) {
|
||||
if (ExecutorRouteStrategyEnum.SHARDING_BROADCAST == executorRouteStrategyEnum) {
|
||||
if (index < group.getRegistryList().size()) {
|
||||
@ -150,39 +179,60 @@ public class XxlJobTrigger {
|
||||
}
|
||||
} else {
|
||||
routeAddressResult = executorRouteStrategyEnum.getRouter().route(triggerParam, group.getRegistryList());
|
||||
if (routeAddressResult.getCode() == ReturnT.SUCCESS_CODE) {
|
||||
address = routeAddressResult.getContent();
|
||||
if (routeAddressResult.isSuccess()) {
|
||||
address = routeAddressResult.getData();
|
||||
}
|
||||
}
|
||||
} else {
|
||||
routeAddressResult = new ReturnT<String>(ReturnT.FAIL_CODE, I18nUtil.getString("jobconf_trigger_address_empty"));
|
||||
routeAddressResult = Response.of(XxlJobContext.HANDLE_CODE_FAIL, I18nUtil.getString("jobconf_trigger_address_empty"));
|
||||
}
|
||||
|
||||
// 4、trigger remote executor
|
||||
ReturnT<String> triggerResult = null;
|
||||
Response<String> triggerResult = null;
|
||||
if (address != null) {
|
||||
triggerResult = runExecutor(triggerParam, address);
|
||||
triggerResult = doTrigger(triggerParam, address);
|
||||
} else {
|
||||
triggerResult = new ReturnT<String>(ReturnT.FAIL_CODE, null);
|
||||
triggerResult = Response.of(XxlJobContext.HANDLE_CODE_FAIL, "Address Router Fail.");
|
||||
}
|
||||
|
||||
// 5、collection trigger info
|
||||
StringBuffer triggerMsgSb = new StringBuffer();
|
||||
// trigger config
|
||||
StringBuilder triggerMsgSb = new StringBuilder();
|
||||
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_admin_adress")).append(":").append(IPTool.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("(").append(shardingParam).append(")");
|
||||
}
|
||||
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():"");
|
||||
// trigger data
|
||||
triggerMsgSb.append("<br><br><span style=\"color:#00c0ef;\" > >>>>>>>>>>>").append(I18nUtil.getString("jobconf_trigger_run")).append("<<<<<<<<<<< </span><br>");
|
||||
triggerMsgSb.append("<br>").append(I18nUtil.getString("joblog_field_executorAddress")).append(":");
|
||||
if (StringTool.isNotBlank(address)) {
|
||||
triggerMsgSb.append(address);
|
||||
} else if (routeAddressResult!=null && !routeAddressResult.isSuccess() && routeAddressResult.getMsg()!=null) {
|
||||
triggerMsgSb.append("address route fail, ").append(routeAddressResult.getMsg());
|
||||
} else {
|
||||
triggerMsgSb.append("address route fail.");
|
||||
}
|
||||
if (StringTool.isNotBlank(jobInfo.getExecutorHandler())) {
|
||||
triggerMsgSb.append("<br>").append("JobHandler").append(":").append(jobInfo.getExecutorHandler());
|
||||
}
|
||||
triggerMsgSb.append("<br>").append(I18nUtil.getString("jobinfo_field_executorparam")).append(":").append(jobInfo.getExecutorParam());
|
||||
triggerMsgSb.append("<br>").append(I18nUtil.getString("joblog_field_triggerMsg")).append(":");
|
||||
if (triggerResult.isSuccess()) {
|
||||
triggerMsgSb.append("success");
|
||||
} else if (triggerResult.getMsg()!=null) {
|
||||
triggerMsgSb.append("error, ").append(triggerResult.getMsg());
|
||||
} else {
|
||||
triggerMsgSb.append("fail");
|
||||
}
|
||||
|
||||
// 6、save log trigger-info
|
||||
jobLog.setExecutorAddress(address);
|
||||
@ -193,34 +243,39 @@ public class XxlJobTrigger {
|
||||
//jobLog.setTriggerTime();
|
||||
jobLog.setTriggerCode(triggerResult.getCode());
|
||||
jobLog.setTriggerMsg(triggerMsgSb.toString());
|
||||
XxlJobAdminConfig.getAdminConfig().getXxlJobLogDao().updateTriggerInfo(jobLog);
|
||||
xxlJobLogMapper.updateTriggerInfo(jobLog);
|
||||
|
||||
logger.debug(">>>>>>>>>>> xxl-job trigger end, jobId:{}", jobLog.getId());
|
||||
logger.debug(">>>>>>>>>>> xxl-job trigger end, jobId:{}", jobLog.getJobId());
|
||||
}
|
||||
|
||||
/**
|
||||
* run executor
|
||||
* @param triggerParam
|
||||
* @param address
|
||||
* @return
|
||||
* do trigger with address
|
||||
*
|
||||
* @param triggerParam trigger param
|
||||
* @param address the address
|
||||
* @return return
|
||||
*/
|
||||
public static ReturnT<String> runExecutor(TriggerParam triggerParam, String address){
|
||||
ReturnT<String> runResult = null;
|
||||
private Response<String> doTrigger(TriggerRequest triggerParam, String address){
|
||||
try {
|
||||
ExecutorBiz executorBiz = XxlJobScheduler.getExecutorBiz(address);
|
||||
runResult = executorBiz.run(triggerParam);
|
||||
// build client
|
||||
ExecutorBiz executorBiz = XxlJobAdminBootstrap.getExecutorBiz(address);
|
||||
|
||||
// invoke
|
||||
Response<String> runResult = executorBiz.run(triggerParam);
|
||||
|
||||
// build result
|
||||
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());
|
||||
|
||||
// return
|
||||
runResult.setMsg(runResultSB.toString());
|
||||
return runResult;
|
||||
} 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));
|
||||
return Response.of(XxlJobContext.HANDLE_CODE_FAIL, ThrowableTool.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,6 +1,6 @@
|
||||
package com.xxl.job.admin.core.trigger;
|
||||
package com.xxl.job.admin.business.scheduler.trigger;
|
||||
|
||||
import com.xxl.job.admin.core.util.I18nUtil;
|
||||
import com.xxl.job.admin.framework.util.I18nUtil;
|
||||
|
||||
/**
|
||||
* trigger type enum
|
||||
@ -0,0 +1,22 @@
|
||||
package com.xxl.job.admin.business.scheduler.type;
|
||||
|
||||
import com.xxl.job.admin.business.model.XxlJobInfo;
|
||||
|
||||
import java.util.Date;
|
||||
|
||||
/**
|
||||
* Schedule Type
|
||||
*
|
||||
* @author xuxueli 2020-10-29
|
||||
*/
|
||||
public abstract class ScheduleType {
|
||||
|
||||
/**
|
||||
* generate next trigger time
|
||||
*
|
||||
* @param jobInfo job info
|
||||
* @param fromTime from time
|
||||
*/
|
||||
public abstract Date generateNextTriggerTime(XxlJobInfo jobInfo, Date fromTime) throws Exception;
|
||||
|
||||
}
|
||||
@ -0,0 +1,62 @@
|
||||
package com.xxl.job.admin.business.scheduler.type;
|
||||
|
||||
import com.xxl.job.admin.business.scheduler.type.strategy.CronScheduleType;
|
||||
import com.xxl.job.admin.business.scheduler.type.strategy.FixRateScheduleType;
|
||||
import com.xxl.job.admin.business.scheduler.type.strategy.NoneScheduleType;
|
||||
import com.xxl.job.admin.framework.util.I18nUtil;
|
||||
|
||||
/**
|
||||
* @author xuxueli 2020-10-29 21:11:23
|
||||
*/
|
||||
public enum ScheduleTypeEnum {
|
||||
|
||||
NONE(I18nUtil.getString("schedule_type_none"), new NoneScheduleType()),
|
||||
|
||||
/**
|
||||
* schedule by cron
|
||||
*/
|
||||
CRON(I18nUtil.getString("schedule_type_cron"), new CronScheduleType()),
|
||||
|
||||
/**
|
||||
* schedule by fixed rate (in seconds)
|
||||
*/
|
||||
FIX_RATE(I18nUtil.getString("schedule_type_fix_rate"), new FixRateScheduleType()),
|
||||
|
||||
/**
|
||||
* schedule by fix delay (in seconds), after the last time
|
||||
*/
|
||||
/*FIX_DELAY(I18nUtil.getString("schedule_type_fix_delay"))*/;
|
||||
|
||||
private final String title;
|
||||
private final ScheduleType scheduleType;;
|
||||
|
||||
ScheduleTypeEnum(String title, ScheduleType scheduleType) {
|
||||
this.title = title;
|
||||
this.scheduleType = scheduleType;
|
||||
}
|
||||
|
||||
public String getTitle() {
|
||||
return title;
|
||||
}
|
||||
|
||||
public ScheduleType getScheduleType() {
|
||||
return scheduleType;
|
||||
}
|
||||
|
||||
/**
|
||||
* match by name
|
||||
*
|
||||
* @param name name of ScheduleTypeEnum
|
||||
* @param defaultItem default item
|
||||
* @return ScheduleTypeEnum
|
||||
*/
|
||||
public static ScheduleTypeEnum match(String name, ScheduleTypeEnum defaultItem){
|
||||
for (ScheduleTypeEnum item: ScheduleTypeEnum.values()) {
|
||||
if (item.name().equals(name)) {
|
||||
return item;
|
||||
}
|
||||
}
|
||||
return defaultItem;
|
||||
}
|
||||
|
||||
}
|
||||
@ -0,0 +1,17 @@
|
||||
package com.xxl.job.admin.business.scheduler.type.strategy;
|
||||
|
||||
import com.xxl.job.admin.business.model.XxlJobInfo;
|
||||
import com.xxl.job.admin.business.scheduler.cron.CronExpression;
|
||||
import com.xxl.job.admin.business.scheduler.type.ScheduleType;
|
||||
|
||||
import java.util.Date;
|
||||
|
||||
public class CronScheduleType extends ScheduleType {
|
||||
|
||||
@Override
|
||||
public Date generateNextTriggerTime(XxlJobInfo jobInfo, Date fromTime) throws Exception {
|
||||
// generate next trigger time, with cron
|
||||
return new CronExpression(jobInfo.getScheduleConf()).getNextValidTimeAfter(fromTime);
|
||||
}
|
||||
|
||||
}
|
||||
@ -0,0 +1,26 @@
|
||||
package com.xxl.job.admin.business.scheduler.type.strategy;
|
||||
|
||||
import com.xxl.job.admin.business.model.XxlJobInfo;
|
||||
import com.xxl.job.admin.business.scheduler.type.ScheduleType;
|
||||
import com.xxl.tool.core.DateTool;
|
||||
|
||||
import java.util.Date;
|
||||
|
||||
public class FixRateScheduleType extends ScheduleType {
|
||||
|
||||
@Override
|
||||
public Date generateNextTriggerTime(XxlJobInfo jobInfo, Date fromTime) throws Exception {
|
||||
|
||||
// generate next trigger time, fix rate delay
|
||||
Date nextTriggerTime = new Date(fromTime.getTime() + Long.parseLong(jobInfo.getScheduleConf()) * 1000L);
|
||||
|
||||
// assign second:
|
||||
if (nextTriggerTime.getTime() % 1000 != 0) {
|
||||
nextTriggerTime = DateTool.addSeconds(DateTool.setMilliseconds(nextTriggerTime, 0), 1);
|
||||
}
|
||||
|
||||
return nextTriggerTime;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@ -0,0 +1,16 @@
|
||||
package com.xxl.job.admin.business.scheduler.type.strategy;
|
||||
|
||||
import com.xxl.job.admin.business.model.XxlJobInfo;
|
||||
import com.xxl.job.admin.business.scheduler.type.ScheduleType;
|
||||
|
||||
import java.util.Date;
|
||||
|
||||
public class NoneScheduleType extends ScheduleType {
|
||||
|
||||
@Override
|
||||
public Date generateNextTriggerTime(XxlJobInfo jobInfo, Date fromTime) throws Exception {
|
||||
// generate none trigger-time
|
||||
return null;
|
||||
}
|
||||
|
||||
}
|
||||
@ -0,0 +1,63 @@
|
||||
package com.xxl.job.admin.business.service;
|
||||
|
||||
import com.xxl.job.admin.business.model.XxlJobInfo;
|
||||
import com.xxl.sso.core.model.LoginInfo;
|
||||
import com.xxl.tool.response.PageModel;
|
||||
import com.xxl.tool.response.Response;
|
||||
|
||||
import java.util.Date;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* core job action for xxl-job
|
||||
*
|
||||
* @author xuxueli 2016-5-28 15:30:33
|
||||
*/
|
||||
public interface XxlJobService {
|
||||
|
||||
/**
|
||||
* page list
|
||||
*/
|
||||
public Response<PageModel<XxlJobInfo>> pageList(int offset, int pagesize, int jobGroup, int triggerStatus, String jobDesc, String executorHandler, String author);
|
||||
|
||||
/**
|
||||
* add job
|
||||
*/
|
||||
public Response<String> add(XxlJobInfo jobInfo, LoginInfo loginInfo);
|
||||
|
||||
/**
|
||||
* update job
|
||||
*/
|
||||
public Response<String> update(XxlJobInfo jobInfo, LoginInfo loginInfo);
|
||||
|
||||
/**
|
||||
* remove job
|
||||
*/
|
||||
public Response<String> remove(int id, LoginInfo loginInfo);
|
||||
|
||||
/**
|
||||
* start job
|
||||
*/
|
||||
public Response<String> start(int id, LoginInfo loginInfo);
|
||||
|
||||
/**
|
||||
* stop job
|
||||
*/
|
||||
public Response<String> stop(int id, LoginInfo loginInfo);
|
||||
|
||||
/**
|
||||
* trigger
|
||||
*/
|
||||
public Response<String> trigger(LoginInfo loginInfo, int jobId, String executorParam, String addressList);
|
||||
|
||||
/**
|
||||
* dashboard info
|
||||
*/
|
||||
public Map<String,Object> dashboardInfo();
|
||||
|
||||
/**
|
||||
* chart info
|
||||
*/
|
||||
public Response<Map<String,Object>> chartInfo(Date startDate, Date endDate);
|
||||
|
||||
}
|
||||
@ -0,0 +1,33 @@
|
||||
package com.xxl.job.admin.business.service.impl;
|
||||
|
||||
import com.xxl.job.admin.business.scheduler.config.XxlJobAdminBootstrap;
|
||||
import com.xxl.job.core.openapi.AdminBiz;
|
||||
import com.xxl.job.core.openapi.model.CallbackRequest;
|
||||
import com.xxl.job.core.openapi.model.RegistryRequest;
|
||||
import com.xxl.tool.response.Response;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* @author xuxueli 2017-07-27 21:54:20
|
||||
*/
|
||||
@Service
|
||||
public class AdminBizImpl implements AdminBiz {
|
||||
|
||||
@Override
|
||||
public Response<String> callback(List<CallbackRequest> callbackRequestList) {
|
||||
return XxlJobAdminBootstrap.getInstance().getJobCompleteHelper().callback(callbackRequestList);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Response<String> registry(RegistryRequest registryRequest) {
|
||||
return XxlJobAdminBootstrap.getInstance().getJobRegistryHelper().registry(registryRequest);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Response<String> registryRemove(RegistryRequest registryRequest) {
|
||||
return XxlJobAdminBootstrap.getInstance().getJobRegistryHelper().registryRemove(registryRequest);
|
||||
}
|
||||
|
||||
}
|
||||
@ -0,0 +1,531 @@
|
||||
package com.xxl.job.admin.business.service.impl;
|
||||
|
||||
import com.xxl.job.admin.business.constant.TriggerStatus;
|
||||
import com.xxl.job.admin.business.mapper.*;
|
||||
import com.xxl.job.admin.business.model.XxlJobGroup;
|
||||
import com.xxl.job.admin.business.model.XxlJobInfo;
|
||||
import com.xxl.job.admin.business.model.XxlJobLogReport;
|
||||
import com.xxl.job.admin.business.scheduler.config.XxlJobAdminBootstrap;
|
||||
import com.xxl.job.admin.business.scheduler.cron.CronExpression;
|
||||
import com.xxl.job.admin.business.scheduler.misfire.MisfireStrategyEnum;
|
||||
import com.xxl.job.admin.business.scheduler.route.ExecutorRouteStrategyEnum;
|
||||
import com.xxl.job.admin.business.scheduler.thread.JobScheduleHelper;
|
||||
import com.xxl.job.admin.business.scheduler.trigger.TriggerTypeEnum;
|
||||
import com.xxl.job.admin.business.scheduler.type.ScheduleTypeEnum;
|
||||
import com.xxl.job.admin.business.service.XxlJobService;
|
||||
import com.xxl.job.admin.framework.util.I18nUtil;
|
||||
import com.xxl.job.admin.framework.util.JobGroupPermissionUtil;
|
||||
import com.xxl.job.core.constant.ExecutorBlockStrategyEnum;
|
||||
import com.xxl.job.core.glue.GlueTypeEnum;
|
||||
import com.xxl.sso.core.model.LoginInfo;
|
||||
import com.xxl.tool.core.DateTool;
|
||||
import com.xxl.tool.core.StringTool;
|
||||
import com.xxl.tool.json.GsonTool;
|
||||
import com.xxl.tool.response.PageModel;
|
||||
import com.xxl.tool.response.Response;
|
||||
import jakarta.annotation.Resource;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import java.text.MessageFormat;
|
||||
import java.util.*;
|
||||
|
||||
/**
|
||||
* core job action for xxl-job
|
||||
* @author xuxueli 2016-5-28 15:30:33
|
||||
*/
|
||||
@Service
|
||||
public class XxlJobServiceImpl implements XxlJobService {
|
||||
private static Logger logger = LoggerFactory.getLogger(XxlJobServiceImpl.class);
|
||||
|
||||
@Resource
|
||||
private XxlJobGroupMapper xxlJobGroupMapper;
|
||||
@Resource
|
||||
private XxlJobInfoMapper xxlJobInfoMapper;
|
||||
@Resource
|
||||
public XxlJobLogMapper xxlJobLogMapper;
|
||||
@Resource
|
||||
private XxlJobLogGlueMapper xxlJobLogGlueMapper;
|
||||
@Resource
|
||||
private XxlJobLogReportMapper xxlJobLogReportMapper;
|
||||
|
||||
@Override
|
||||
public Response<PageModel<XxlJobInfo>> pageList(int offset, int pagesize, int jobGroup, int triggerStatus, String jobDesc, String executorHandler, String author) {
|
||||
|
||||
// page list
|
||||
List<XxlJobInfo> list = xxlJobInfoMapper.pageList(offset, pagesize, jobGroup, triggerStatus, jobDesc, executorHandler, author);
|
||||
int list_count = xxlJobInfoMapper.pageListCount(offset, pagesize, jobGroup, triggerStatus, jobDesc, executorHandler, author);
|
||||
|
||||
// package result
|
||||
PageModel<XxlJobInfo> pageModel = new PageModel<>();
|
||||
pageModel.setData(list);
|
||||
pageModel.setTotal(list_count);
|
||||
|
||||
return Response.ofSuccess(pageModel);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Response<String> add(XxlJobInfo jobInfo, LoginInfo loginInfo) {
|
||||
|
||||
// valid base
|
||||
XxlJobGroup group = xxlJobGroupMapper.load(jobInfo.getJobGroup());
|
||||
if (group == null) {
|
||||
return Response.ofFail (I18nUtil.getString("system_please_choose")+I18nUtil.getString("jobinfo_field_jobgroup"));
|
||||
}
|
||||
if (StringTool.isBlank(jobInfo.getJobDesc())) {
|
||||
return Response.ofFail ( (I18nUtil.getString("system_please_input")+I18nUtil.getString("jobinfo_field_jobdesc")) );
|
||||
}
|
||||
if (StringTool.isBlank(jobInfo.getAuthor())) {
|
||||
return Response.ofFail ( (I18nUtil.getString("system_please_input")+I18nUtil.getString("jobinfo_field_author")) );
|
||||
}
|
||||
|
||||
// valid trigger
|
||||
ScheduleTypeEnum scheduleTypeEnum = ScheduleTypeEnum.match(jobInfo.getScheduleType(), null);
|
||||
if (scheduleTypeEnum == null) {
|
||||
return Response.ofFail ( (I18nUtil.getString("schedule_type")+I18nUtil.getString("system_invalid")) );
|
||||
}
|
||||
if (scheduleTypeEnum == ScheduleTypeEnum.CRON) {
|
||||
if (jobInfo.getScheduleConf()==null || !CronExpression.isValidExpression(jobInfo.getScheduleConf())) {
|
||||
return Response.ofFail ( "Cron"+I18nUtil.getString("system_invalid"));
|
||||
}
|
||||
} else if (scheduleTypeEnum == ScheduleTypeEnum.FIX_RATE/* || scheduleTypeEnum == ScheduleTypeEnum.FIX_DELAY*/) {
|
||||
if (jobInfo.getScheduleConf() == null) {
|
||||
return Response.ofFail ( (I18nUtil.getString("schedule_type")) );
|
||||
}
|
||||
try {
|
||||
int fixSecond = Integer.parseInt(jobInfo.getScheduleConf());
|
||||
if (fixSecond < 1) {
|
||||
return Response.ofFail ( (I18nUtil.getString("schedule_type")+I18nUtil.getString("system_invalid")) );
|
||||
}
|
||||
} catch (Exception e) {
|
||||
return Response.ofFail ( (I18nUtil.getString("schedule_type")+I18nUtil.getString("system_invalid")) );
|
||||
}
|
||||
}
|
||||
|
||||
// valid job
|
||||
if (GlueTypeEnum.match(jobInfo.getGlueType()) == null) {
|
||||
return Response.ofFail ( (I18nUtil.getString("jobinfo_field_gluetype")+I18nUtil.getString("system_invalid")) );
|
||||
}
|
||||
if (GlueTypeEnum.BEAN==GlueTypeEnum.match(jobInfo.getGlueType()) && StringTool.isBlank(jobInfo.getExecutorHandler()) ) {
|
||||
return Response.ofFail ( (I18nUtil.getString("system_please_input")+"JobHandler") );
|
||||
}
|
||||
// 》fix "\r" in shell
|
||||
if (GlueTypeEnum.GLUE_SHELL==GlueTypeEnum.match(jobInfo.getGlueType()) && jobInfo.getGlueSource()!=null) {
|
||||
jobInfo.setGlueSource(jobInfo.getGlueSource().replaceAll("\r", ""));
|
||||
}
|
||||
|
||||
// valid advanced
|
||||
if (ExecutorRouteStrategyEnum.match(jobInfo.getExecutorRouteStrategy(), null) == null) {
|
||||
return Response.ofFail ( (I18nUtil.getString("jobinfo_field_executorRouteStrategy")+I18nUtil.getString("system_invalid")) );
|
||||
}
|
||||
if (MisfireStrategyEnum.match(jobInfo.getMisfireStrategy(), null) == null) {
|
||||
return Response.ofFail ( (I18nUtil.getString("misfire_strategy")+I18nUtil.getString("system_invalid")) );
|
||||
}
|
||||
if (ExecutorBlockStrategyEnum.match(jobInfo.getExecutorBlockStrategy(), null) == null) {
|
||||
return Response.ofFail ( (I18nUtil.getString("jobinfo_field_executorBlockStrategy")+I18nUtil.getString("system_invalid")) );
|
||||
}
|
||||
|
||||
// 》ChildJobId valid
|
||||
if (StringTool.isNotBlank(jobInfo.getChildJobId())) {
|
||||
String[] childJobIds = jobInfo.getChildJobId().split(",");
|
||||
for (String childJobIdItem: childJobIds) {
|
||||
if (StringTool.isNotBlank(childJobIdItem) && StringTool.isNumeric(childJobIdItem)) {
|
||||
XxlJobInfo childJobInfo = xxlJobInfoMapper.loadById(Integer.parseInt(childJobIdItem));
|
||||
if (childJobInfo==null) {
|
||||
return Response.ofFail (
|
||||
MessageFormat.format((I18nUtil.getString("jobinfo_field_childJobId")+"({0})"+I18nUtil.getString("system_not_found")), childJobIdItem));
|
||||
}
|
||||
// valid jobGroup permission
|
||||
if (!JobGroupPermissionUtil.hasJobGroupPermission(loginInfo, childJobInfo.getJobGroup())) {
|
||||
return Response.ofFail (
|
||||
MessageFormat.format((I18nUtil.getString("jobinfo_field_childJobId")+"({0})"+I18nUtil.getString("system_permission_limit")), childJobIdItem));
|
||||
}
|
||||
} else {
|
||||
return Response.ofFail (
|
||||
MessageFormat.format((I18nUtil.getString("jobinfo_field_childJobId")+"({0})"+I18nUtil.getString("system_invalid")), childJobIdItem));
|
||||
}
|
||||
}
|
||||
|
||||
// join , avoid "xxx,,"
|
||||
String temp = "";
|
||||
for (String item:childJobIds) {
|
||||
temp += item + ",";
|
||||
}
|
||||
temp = temp.substring(0, temp.length()-1);
|
||||
|
||||
jobInfo.setChildJobId(temp);
|
||||
}
|
||||
|
||||
// add in db
|
||||
jobInfo.setAddTime(new Date());
|
||||
jobInfo.setUpdateTime(new Date());
|
||||
jobInfo.setGlueUpdatetime(new Date());
|
||||
// remove the whitespace
|
||||
jobInfo.setExecutorHandler(jobInfo.getExecutorHandler().trim());
|
||||
xxlJobInfoMapper.save(jobInfo);
|
||||
if (jobInfo.getId() < 1) {
|
||||
return Response.ofFail ( (I18nUtil.getString("jobinfo_field_add")+I18nUtil.getString("system_fail")) );
|
||||
}
|
||||
|
||||
// write operation log
|
||||
logger.info(">>>>>>>>>>> xxl-job operation log: operator = {}, type = {}, content = {}",
|
||||
loginInfo.getUserName(), "jobinfo-save", GsonTool.toJson(jobInfo));
|
||||
|
||||
return Response.ofSuccess(String.valueOf(jobInfo.getId()));
|
||||
}
|
||||
|
||||
@Override
|
||||
public Response<String> update(XxlJobInfo jobInfo, LoginInfo loginInfo) {
|
||||
|
||||
// valid base
|
||||
if (StringTool.isBlank(jobInfo.getJobDesc())) {
|
||||
return Response.ofFail ( (I18nUtil.getString("system_please_input")+I18nUtil.getString("jobinfo_field_jobdesc")) );
|
||||
}
|
||||
if (StringTool.isBlank(jobInfo.getAuthor())) {
|
||||
return Response.ofFail ( (I18nUtil.getString("system_please_input")+I18nUtil.getString("jobinfo_field_author")) );
|
||||
}
|
||||
|
||||
// valid trigger
|
||||
ScheduleTypeEnum scheduleTypeEnum = ScheduleTypeEnum.match(jobInfo.getScheduleType(), null);
|
||||
if (scheduleTypeEnum == null) {
|
||||
return Response.ofFail ( (I18nUtil.getString("schedule_type")+I18nUtil.getString("system_invalid")) );
|
||||
}
|
||||
if (scheduleTypeEnum == ScheduleTypeEnum.CRON) {
|
||||
if (jobInfo.getScheduleConf()==null || !CronExpression.isValidExpression(jobInfo.getScheduleConf())) {
|
||||
return Response.ofFail ( "Cron"+I18nUtil.getString("system_invalid") );
|
||||
}
|
||||
} else if (scheduleTypeEnum == ScheduleTypeEnum.FIX_RATE /*|| scheduleTypeEnum == ScheduleTypeEnum.FIX_DELAY*/) {
|
||||
if (jobInfo.getScheduleConf() == null) {
|
||||
return Response.ofFail ( (I18nUtil.getString("schedule_type")+I18nUtil.getString("system_invalid")) );
|
||||
}
|
||||
try {
|
||||
int fixSecond = Integer.parseInt(jobInfo.getScheduleConf());
|
||||
if (fixSecond < 1) {
|
||||
return Response.ofFail ( (I18nUtil.getString("schedule_type")+I18nUtil.getString("system_invalid")) );
|
||||
}
|
||||
} catch (Exception e) {
|
||||
return Response.ofFail ( (I18nUtil.getString("schedule_type")+I18nUtil.getString("system_invalid")) );
|
||||
}
|
||||
}
|
||||
|
||||
// valid advanced
|
||||
if (ExecutorRouteStrategyEnum.match(jobInfo.getExecutorRouteStrategy(), null) == null) {
|
||||
return Response.ofFail ( (I18nUtil.getString("jobinfo_field_executorRouteStrategy")+I18nUtil.getString("system_invalid")) );
|
||||
}
|
||||
if (MisfireStrategyEnum.match(jobInfo.getMisfireStrategy(), null) == null) {
|
||||
return Response.ofFail ( (I18nUtil.getString("misfire_strategy")+I18nUtil.getString("system_invalid")) );
|
||||
}
|
||||
if (ExecutorBlockStrategyEnum.match(jobInfo.getExecutorBlockStrategy(), null) == null) {
|
||||
return Response.ofFail ( (I18nUtil.getString("jobinfo_field_executorBlockStrategy")+I18nUtil.getString("system_invalid")) );
|
||||
}
|
||||
|
||||
// 》ChildJobId valid
|
||||
if (StringTool.isNotBlank(jobInfo.getChildJobId())) {
|
||||
String[] childJobIds = jobInfo.getChildJobId().split(",");
|
||||
for (String childJobIdItem: childJobIds) {
|
||||
if (StringTool.isNotBlank(childJobIdItem) && StringTool.isNumeric(childJobIdItem)) {
|
||||
// parse child
|
||||
int childJobId = Integer.parseInt(childJobIdItem);
|
||||
if (childJobId == jobInfo.getId()) {
|
||||
return Response.ofFail ( (I18nUtil.getString("jobinfo_field_childJobId")+"("+childJobId+")"+I18nUtil.getString("system_invalid")) );
|
||||
}
|
||||
|
||||
// valid child
|
||||
XxlJobInfo childJobInfo = xxlJobInfoMapper.loadById(childJobId);
|
||||
if (childJobInfo==null) {
|
||||
return Response.ofFail (
|
||||
MessageFormat.format((I18nUtil.getString("jobinfo_field_childJobId")+"({0})"+I18nUtil.getString("system_not_found")), childJobIdItem));
|
||||
}
|
||||
// valid jobGroup permission
|
||||
if (!JobGroupPermissionUtil.hasJobGroupPermission(loginInfo, childJobInfo.getJobGroup())) {
|
||||
return Response.ofFail (
|
||||
MessageFormat.format((I18nUtil.getString("jobinfo_field_childJobId")+"({0})"+I18nUtil.getString("system_permission_limit")), childJobIdItem));
|
||||
}
|
||||
} else {
|
||||
return Response.ofFail (
|
||||
MessageFormat.format((I18nUtil.getString("jobinfo_field_childJobId")+"({0})"+I18nUtil.getString("system_invalid")), childJobIdItem));
|
||||
}
|
||||
}
|
||||
|
||||
// join , avoid "xxx,,"
|
||||
String temp = "";
|
||||
for (String item:childJobIds) {
|
||||
temp += item + ",";
|
||||
}
|
||||
temp = temp.substring(0, temp.length()-1);
|
||||
|
||||
jobInfo.setChildJobId(temp);
|
||||
}
|
||||
|
||||
// group valid
|
||||
XxlJobGroup jobGroup = xxlJobGroupMapper.load(jobInfo.getJobGroup());
|
||||
if (jobGroup == null) {
|
||||
return Response.ofFail ( (I18nUtil.getString("jobinfo_field_jobgroup")+I18nUtil.getString("system_invalid")) );
|
||||
}
|
||||
|
||||
// stage job info
|
||||
XxlJobInfo exists_jobInfo = xxlJobInfoMapper.loadById(jobInfo.getId());
|
||||
if (exists_jobInfo == null) {
|
||||
return Response.ofFail ( (I18nUtil.getString("jobinfo_field_id")+I18nUtil.getString("system_not_found")) );
|
||||
}
|
||||
|
||||
// next trigger time (5s后生效,避开预读周期)
|
||||
long nextTriggerTime = exists_jobInfo.getTriggerNextTime();
|
||||
boolean scheduleDataNotChanged = jobInfo.getScheduleType().equals(exists_jobInfo.getScheduleType())
|
||||
&& jobInfo.getScheduleConf().equals(exists_jobInfo.getScheduleConf()); // 触发配置如果不变,避免重复计算;
|
||||
if (exists_jobInfo.getTriggerStatus() == TriggerStatus.RUNNING.getValue() && !scheduleDataNotChanged) {
|
||||
try {
|
||||
// generate next trigger time
|
||||
Date nextValidTime = scheduleTypeEnum.getScheduleType().generateNextTriggerTime(jobInfo, new Date(System.currentTimeMillis() + JobScheduleHelper.PRE_READ_MS));
|
||||
if (nextValidTime == null) {
|
||||
return Response.ofFail ( (I18nUtil.getString("schedule_type")+I18nUtil.getString("system_invalid")) );
|
||||
}
|
||||
nextTriggerTime = nextValidTime.getTime();
|
||||
} catch (Exception e) {
|
||||
logger.error(e.getMessage(), e);
|
||||
return Response.ofFail ( (I18nUtil.getString("schedule_type")+I18nUtil.getString("system_invalid")) );
|
||||
}
|
||||
}
|
||||
|
||||
exists_jobInfo.setJobGroup(jobInfo.getJobGroup());
|
||||
exists_jobInfo.setJobDesc(jobInfo.getJobDesc());
|
||||
exists_jobInfo.setAuthor(jobInfo.getAuthor());
|
||||
exists_jobInfo.setAlarmEmail(jobInfo.getAlarmEmail());
|
||||
exists_jobInfo.setScheduleType(jobInfo.getScheduleType());
|
||||
exists_jobInfo.setScheduleConf(jobInfo.getScheduleConf());
|
||||
exists_jobInfo.setMisfireStrategy(jobInfo.getMisfireStrategy());
|
||||
exists_jobInfo.setExecutorRouteStrategy(jobInfo.getExecutorRouteStrategy());
|
||||
// remove the whitespace
|
||||
exists_jobInfo.setExecutorHandler(jobInfo.getExecutorHandler().trim());
|
||||
exists_jobInfo.setExecutorParam(jobInfo.getExecutorParam());
|
||||
exists_jobInfo.setExecutorBlockStrategy(jobInfo.getExecutorBlockStrategy());
|
||||
exists_jobInfo.setExecutorTimeout(jobInfo.getExecutorTimeout());
|
||||
exists_jobInfo.setExecutorFailRetryCount(jobInfo.getExecutorFailRetryCount());
|
||||
exists_jobInfo.setChildJobId(jobInfo.getChildJobId());
|
||||
exists_jobInfo.setTriggerNextTime(nextTriggerTime);
|
||||
|
||||
exists_jobInfo.setUpdateTime(new Date());
|
||||
xxlJobInfoMapper.update(exists_jobInfo);
|
||||
|
||||
// write operation log
|
||||
logger.info(">>>>>>>>>>> xxl-job operation log: operator = {}, type = {}, content = {}",
|
||||
loginInfo.getUserName(), "jobinfo-update", GsonTool.toJson(exists_jobInfo));
|
||||
|
||||
return Response.ofSuccess();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Response<String> remove(int id, LoginInfo loginInfo) {
|
||||
// valid job
|
||||
XxlJobInfo xxlJobInfo = xxlJobInfoMapper.loadById(id);
|
||||
if (xxlJobInfo == null) {
|
||||
return Response.ofSuccess();
|
||||
}
|
||||
|
||||
// valid jobGroup permission
|
||||
if (!JobGroupPermissionUtil.hasJobGroupPermission(loginInfo, xxlJobInfo.getJobGroup())) {
|
||||
return Response.ofFail(I18nUtil.getString("system_permission_limit"));
|
||||
}
|
||||
|
||||
xxlJobInfoMapper.delete(id);
|
||||
xxlJobLogMapper.delete(id);
|
||||
xxlJobLogGlueMapper.deleteByJobId(id);
|
||||
|
||||
// write operation log
|
||||
logger.info(">>>>>>>>>>> xxl-job operation log: operator = {}, type = {}, content = {}",
|
||||
loginInfo.getUserName(), "jobinfo-remove", id);
|
||||
|
||||
return Response.ofSuccess();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Response<String> start(int id, LoginInfo loginInfo) {
|
||||
// load and valid
|
||||
XxlJobInfo xxlJobInfo = xxlJobInfoMapper.loadById(id);
|
||||
if (xxlJobInfo == null) {
|
||||
return Response.ofFail(I18nUtil.getString("jobinfo_glue_jobid_invalid"));
|
||||
}
|
||||
|
||||
// valid jobGroup permission
|
||||
if (!JobGroupPermissionUtil.hasJobGroupPermission(loginInfo, xxlJobInfo.getJobGroup())) {
|
||||
return Response.ofFail(I18nUtil.getString("system_permission_limit"));
|
||||
}
|
||||
|
||||
// valid ScheduleType: can not be none
|
||||
ScheduleTypeEnum scheduleTypeEnum = ScheduleTypeEnum.match(xxlJobInfo.getScheduleType(), ScheduleTypeEnum.NONE);
|
||||
if (ScheduleTypeEnum.NONE == scheduleTypeEnum) {
|
||||
return Response.ofFail(I18nUtil.getString("schedule_type_none_limit_start"));
|
||||
}
|
||||
|
||||
// next trigger time (5s后生效,避开预读周期)
|
||||
long nextTriggerTime = 0;
|
||||
try {
|
||||
// generate next trigger time
|
||||
Date nextValidTime = scheduleTypeEnum.getScheduleType().generateNextTriggerTime(xxlJobInfo, new Date(System.currentTimeMillis() + JobScheduleHelper.PRE_READ_MS));
|
||||
|
||||
if (nextValidTime == null) {
|
||||
return Response.ofFail ( (I18nUtil.getString("schedule_type")+I18nUtil.getString("system_invalid")) );
|
||||
}
|
||||
nextTriggerTime = nextValidTime.getTime();
|
||||
} catch (Exception e) {
|
||||
logger.error(e.getMessage(), e);
|
||||
return Response.ofFail ( (I18nUtil.getString("schedule_type")+I18nUtil.getString("system_invalid")) );
|
||||
}
|
||||
|
||||
xxlJobInfo.setTriggerStatus(TriggerStatus.RUNNING.getValue());
|
||||
xxlJobInfo.setTriggerLastTime(0);
|
||||
xxlJobInfo.setTriggerNextTime(nextTriggerTime);
|
||||
|
||||
xxlJobInfo.setUpdateTime(new Date());
|
||||
xxlJobInfoMapper.update(xxlJobInfo);
|
||||
|
||||
// write operation log
|
||||
logger.info(">>>>>>>>>>> xxl-job operation log: operator = {}, type = {}, content = {}",
|
||||
loginInfo.getUserName(), "jobinfo-start", id);
|
||||
|
||||
return Response.ofSuccess();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Response<String> stop(int id, LoginInfo loginInfo) {
|
||||
// load and valid
|
||||
XxlJobInfo xxlJobInfo = xxlJobInfoMapper.loadById(id);
|
||||
if (xxlJobInfo == null) {
|
||||
return Response.ofFail(I18nUtil.getString("jobinfo_glue_jobid_invalid"));
|
||||
}
|
||||
|
||||
// valid jobGroup permission
|
||||
if (!JobGroupPermissionUtil.hasJobGroupPermission(loginInfo, xxlJobInfo.getJobGroup())) {
|
||||
return Response.ofFail(I18nUtil.getString("system_permission_limit"));
|
||||
}
|
||||
|
||||
// stop
|
||||
xxlJobInfo.setTriggerStatus(TriggerStatus.STOPPED.getValue());
|
||||
xxlJobInfo.setTriggerLastTime(0);
|
||||
xxlJobInfo.setTriggerNextTime(0);
|
||||
|
||||
xxlJobInfo.setUpdateTime(new Date());
|
||||
xxlJobInfoMapper.update(xxlJobInfo);
|
||||
|
||||
// write operation log
|
||||
logger.info(">>>>>>>>>>> xxl-job operation log: operator = {}, type = {}, content = {}",
|
||||
loginInfo.getUserName(), "jobinfo-stop", id);
|
||||
|
||||
return Response.ofSuccess();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Response<String> trigger(LoginInfo loginInfo, int jobId, String executorParam, String addressList) {
|
||||
// valid job
|
||||
XxlJobInfo xxlJobInfo = xxlJobInfoMapper.loadById(jobId);
|
||||
if (xxlJobInfo == null) {
|
||||
return Response.ofFail(I18nUtil.getString("jobinfo_glue_jobid_invalid"));
|
||||
}
|
||||
|
||||
// valid jobGroup permission
|
||||
if (!JobGroupPermissionUtil.hasJobGroupPermission(loginInfo, xxlJobInfo.getJobGroup())) {
|
||||
return Response.ofFail(I18nUtil.getString("system_permission_limit"));
|
||||
}
|
||||
|
||||
// force cover job param
|
||||
if (executorParam == null) {
|
||||
executorParam = "";
|
||||
}
|
||||
|
||||
XxlJobAdminBootstrap.getInstance().getJobTriggerPoolHelper().trigger(jobId, TriggerTypeEnum.MANUAL, -1, null, executorParam, addressList);
|
||||
|
||||
// write operation log
|
||||
logger.info(">>>>>>>>>>> xxl-job operation log: operator = {}, type = {}, content = {}",
|
||||
loginInfo.getUserName(), "jobinfo-trigger", jobId);
|
||||
|
||||
return Response.ofSuccess();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Map<String, Object> dashboardInfo() {
|
||||
|
||||
int jobInfoCount = xxlJobInfoMapper.findAllCount();
|
||||
int jobLogCount = 0;
|
||||
int jobLogSuccessCount = 0;
|
||||
XxlJobLogReport xxlJobLogReport = xxlJobLogReportMapper.queryLogReportTotal();
|
||||
if (xxlJobLogReport != null) {
|
||||
jobLogCount = xxlJobLogReport.getRunningCount() + xxlJobLogReport.getSucCount() + xxlJobLogReport.getFailCount();
|
||||
jobLogSuccessCount = xxlJobLogReport.getSucCount();
|
||||
}
|
||||
|
||||
// executor count
|
||||
Set<String> executorAddressSet = new HashSet<String>();
|
||||
List<XxlJobGroup> groupList = xxlJobGroupMapper.findAll();
|
||||
|
||||
if (groupList!=null && !groupList.isEmpty()) {
|
||||
for (XxlJobGroup group: groupList) {
|
||||
if (group.getRegistryList()!=null && !group.getRegistryList().isEmpty()) {
|
||||
executorAddressSet.addAll(group.getRegistryList());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int executorCount = executorAddressSet.size();
|
||||
|
||||
Map<String, Object> dashboardMap = new HashMap<String, Object>();
|
||||
dashboardMap.put("jobInfoCount", jobInfoCount);
|
||||
dashboardMap.put("jobLogCount", jobLogCount);
|
||||
dashboardMap.put("jobLogSuccessCount", jobLogSuccessCount);
|
||||
dashboardMap.put("executorCount", executorCount);
|
||||
return dashboardMap;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Response<Map<String, Object>> chartInfo(Date startDate, Date endDate) {
|
||||
|
||||
// process
|
||||
List<String> triggerDayList = new ArrayList<String>();
|
||||
List<Integer> triggerDayCountRunningList = new ArrayList<Integer>();
|
||||
List<Integer> triggerDayCountSucList = new ArrayList<Integer>();
|
||||
List<Integer> triggerDayCountFailList = new ArrayList<Integer>();
|
||||
int triggerCountRunningTotal = 0;
|
||||
int triggerCountSucTotal = 0;
|
||||
int triggerCountFailTotal = 0;
|
||||
|
||||
List<XxlJobLogReport> logReportList = xxlJobLogReportMapper.queryLogReport(startDate, endDate);
|
||||
|
||||
if (logReportList!=null && !logReportList.isEmpty()) {
|
||||
for (XxlJobLogReport item: logReportList) {
|
||||
String day = DateTool.formatDate(item.getTriggerDay());
|
||||
int triggerDayCountRunning = item.getRunningCount();
|
||||
int triggerDayCountSuc = item.getSucCount();
|
||||
int triggerDayCountFail = item.getFailCount();
|
||||
|
||||
triggerDayList.add(day);
|
||||
triggerDayCountRunningList.add(triggerDayCountRunning);
|
||||
triggerDayCountSucList.add(triggerDayCountSuc);
|
||||
triggerDayCountFailList.add(triggerDayCountFail);
|
||||
|
||||
triggerCountRunningTotal += triggerDayCountRunning;
|
||||
triggerCountSucTotal += triggerDayCountSuc;
|
||||
triggerCountFailTotal += triggerDayCountFail;
|
||||
}
|
||||
} else {
|
||||
for (int i = -6; i <= 0; i++) {
|
||||
triggerDayList.add(DateTool.formatDate(DateTool.addDays(new Date(), i)));
|
||||
triggerDayCountRunningList.add(0);
|
||||
triggerDayCountSucList.add(0);
|
||||
triggerDayCountFailList.add(0);
|
||||
}
|
||||
}
|
||||
|
||||
Map<String, Object> result = new HashMap<String, Object>();
|
||||
result.put("triggerDayList", triggerDayList);
|
||||
result.put("triggerDayCountRunningList", triggerDayCountRunningList);
|
||||
result.put("triggerDayCountSucList", triggerDayCountSucList);
|
||||
result.put("triggerDayCountFailList", triggerDayCountFailList);
|
||||
|
||||
result.put("triggerCountRunningTotal", triggerCountRunningTotal);
|
||||
result.put("triggerCountSucTotal", triggerCountSucTotal);
|
||||
result.put("triggerCountFailTotal", triggerCountFailTotal);
|
||||
|
||||
return Response.ofSuccess(result);
|
||||
}
|
||||
|
||||
}
|
||||
@ -1,96 +0,0 @@
|
||||
package com.xxl.job.admin.controller;
|
||||
|
||||
import com.xxl.job.admin.controller.annotation.PermissionLimit;
|
||||
import com.xxl.job.admin.service.LoginService;
|
||||
import com.xxl.job.admin.service.XxlJobService;
|
||||
import com.xxl.job.core.biz.model.ReturnT;
|
||||
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.RequestMethod;
|
||||
import org.springframework.web.bind.annotation.ResponseBody;
|
||||
import org.springframework.web.servlet.ModelAndView;
|
||||
import org.springframework.web.servlet.view.RedirectView;
|
||||
|
||||
import jakarta.annotation.Resource;
|
||||
import jakarta.servlet.http.HttpServletRequest;
|
||||
import jakarta.servlet.http.HttpServletResponse;
|
||||
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(Date startDate, 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, String userName, String password, 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,72 +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 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 jakarta.annotation.Resource;
|
||||
import jakarta.servlet.http.HttpServletRequest;
|
||||
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,96 +0,0 @@
|
||||
package com.xxl.job.admin.controller;
|
||||
|
||||
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 org.springframework.stereotype.Controller;
|
||||
import org.springframework.ui.Model;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.ResponseBody;
|
||||
|
||||
import jakarta.annotation.Resource;
|
||||
import jakarta.servlet.http.HttpServletRequest;
|
||||
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, 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
|
||||
JobInfoController.validPermission(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(Model model, int id, String glueSource, 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 exists_jobInfo = xxlJobInfoDao.loadById(id);
|
||||
if (exists_jobInfo == null) {
|
||||
return new ReturnT<String>(500, I18nUtil.getString("jobinfo_glue_jobid_unvalid"));
|
||||
}
|
||||
|
||||
// update new code
|
||||
exists_jobInfo.setGlueSource(glueSource);
|
||||
exists_jobInfo.setGlueRemark(glueRemark);
|
||||
exists_jobInfo.setGlueUpdatetime(new Date());
|
||||
|
||||
exists_jobInfo.setUpdateTime(new Date());
|
||||
xxlJobInfoDao.update(exists_jobInfo);
|
||||
|
||||
// log old code
|
||||
XxlJobLogGlue xxlJobLogGlue = new XxlJobLogGlue();
|
||||
xxlJobLogGlue.setJobId(exists_jobInfo.getId());
|
||||
xxlJobLogGlue.setGlueType(exists_jobInfo.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(exists_jobInfo.getId(), 30);
|
||||
|
||||
return ReturnT.SUCCESS;
|
||||
}
|
||||
|
||||
}
|
||||
@ -1,204 +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 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 jakarta.annotation.Resource;
|
||||
import jakarta.servlet.http.HttpServletRequest;
|
||||
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(required = false, defaultValue = "0") int start,
|
||||
@RequestParam(required = false, defaultValue = "10") int length,
|
||||
String appname, 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(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(int id){
|
||||
XxlJobGroup jobGroup = xxlJobGroupDao.load(id);
|
||||
return jobGroup!=null?new ReturnT<XxlJobGroup>(jobGroup):new ReturnT<XxlJobGroup>(ReturnT.FAIL_CODE, null);
|
||||
}
|
||||
|
||||
}
|
||||
@ -1,172 +0,0 @@
|
||||
package com.xxl.job.admin.controller;
|
||||
|
||||
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.LoginService;
|
||||
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.*;
|
||||
|
||||
/**
|
||||
* 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(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 = 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";
|
||||
}
|
||||
|
||||
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 = (XxlJobUser) request.getAttribute(LoginService.LOGIN_IDENTITY_KEY);
|
||||
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;
|
||||
}
|
||||
public static void validPermission(HttpServletRequest request, int jobGroup) {
|
||||
XxlJobUser loginUser = (XxlJobUser) request.getAttribute(LoginService.LOGIN_IDENTITY_KEY);
|
||||
if (!loginUser.validPermission(jobGroup)) {
|
||||
throw new RuntimeException(I18nUtil.getString("system_permission_limit") + "[username="+ loginUser.getUsername() +"]");
|
||||
}
|
||||
}
|
||||
|
||||
@RequestMapping("/pageList")
|
||||
@ResponseBody
|
||||
public Map<String, Object> pageList(@RequestParam(required = false, defaultValue = "0") int start,
|
||||
@RequestParam(required = false, defaultValue = "10") int length,
|
||||
int jobGroup, int triggerStatus, String jobDesc, String executorHandler, String author) {
|
||||
|
||||
return xxlJobService.pageList(start, length, jobGroup, triggerStatus, jobDesc, executorHandler, author);
|
||||
}
|
||||
|
||||
@RequestMapping("/add")
|
||||
@ResponseBody
|
||||
public ReturnT<String> add(XxlJobInfo jobInfo) {
|
||||
return xxlJobService.add(jobInfo);
|
||||
}
|
||||
|
||||
@RequestMapping("/update")
|
||||
@ResponseBody
|
||||
public ReturnT<String> update(XxlJobInfo jobInfo) {
|
||||
return xxlJobService.update(jobInfo);
|
||||
}
|
||||
|
||||
@RequestMapping("/remove")
|
||||
@ResponseBody
|
||||
public ReturnT<String> remove(int id) {
|
||||
return xxlJobService.remove(id);
|
||||
}
|
||||
|
||||
@RequestMapping("/stop")
|
||||
@ResponseBody
|
||||
public ReturnT<String> pause(int id) {
|
||||
return xxlJobService.stop(id);
|
||||
}
|
||||
|
||||
@RequestMapping("/start")
|
||||
@ResponseBody
|
||||
public ReturnT<String> start(int id) {
|
||||
return xxlJobService.start(id);
|
||||
}
|
||||
|
||||
@RequestMapping("/trigger")
|
||||
@ResponseBody
|
||||
public ReturnT<String> triggerJob(HttpServletRequest request, int id, String executorParam, String addressList) {
|
||||
// login user
|
||||
XxlJobUser loginUser = (XxlJobUser) request.getAttribute(LoginService.LOGIN_IDENTITY_KEY);
|
||||
// trigger
|
||||
return xxlJobService.trigger(loginUser, id, executorParam, addressList);
|
||||
}
|
||||
|
||||
@RequestMapping("/nextTriggerTime")
|
||||
@ResponseBody
|
||||
public ReturnT<List<String>> nextTriggerTime(String scheduleType, 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(e.getMessage(), 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,246 +0,0 @@
|
||||
package com.xxl.job.admin.controller;
|
||||
|
||||
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 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 jakarta.annotation.Resource;
|
||||
import jakarta.servlet.http.HttpServletRequest;
|
||||
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(required = false, defaultValue = "0") Integer jobId) {
|
||||
|
||||
// 执行器列表
|
||||
List<XxlJobGroup> jobGroupList_all = xxlJobGroupDao.findAll();
|
||||
|
||||
// filter group
|
||||
List<XxlJobGroup> jobGroupList = JobInfoController.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
|
||||
JobInfoController.validPermission(request, jobInfo.getJobGroup());
|
||||
}
|
||||
|
||||
return "joblog/joblog.index";
|
||||
}
|
||||
|
||||
@RequestMapping("/getJobsByGroup")
|
||||
@ResponseBody
|
||||
public ReturnT<List<XxlJobInfo>> getJobsByGroup(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(required = false, defaultValue = "0") int start,
|
||||
@RequestParam(required = false, defaultValue = "10") int length,
|
||||
int jobGroup, int jobId, int logStatus, String filterTime) {
|
||||
|
||||
// valid permission
|
||||
JobInfoController.validPermission(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(int id, Model model){
|
||||
|
||||
// base check
|
||||
ReturnT<String> logStatue = ReturnT.SUCCESS;
|
||||
XxlJobLog jobLog = xxlJobLogDao.load(id);
|
||||
if (jobLog == null) {
|
||||
throw new RuntimeException(I18nUtil.getString("joblog_logid_unvalid"));
|
||||
}
|
||||
|
||||
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(long logId, 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 = logResult.getContent().getLogContent();
|
||||
newLogContent = HtmlUtils.htmlEscape(newLogContent, "UTF-8");
|
||||
logResult.getContent().setLogContent(newLogContent);
|
||||
}
|
||||
|
||||
return logResult;
|
||||
} catch (Exception e) {
|
||||
logger.error(e.getMessage(), e);
|
||||
return new ReturnT<LogResult>(ReturnT.FAIL_CODE, e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
@RequestMapping("/logKill")
|
||||
@ResponseBody
|
||||
public ReturnT<String> logKill(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(int jobGroup, int jobId, int type){
|
||||
|
||||
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,179 +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.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.admin.service.LoginService;
|
||||
import com.xxl.job.core.biz.model.ReturnT;
|
||||
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 jakarta.annotation.Resource;
|
||||
import jakarta.servlet.http.HttpServletRequest;
|
||||
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 UserController {
|
||||
|
||||
@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(required = false, defaultValue = "0") int start,
|
||||
@RequestParam(required = false, defaultValue = "10") int length,
|
||||
String username, 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 = (XxlJobUser) request.getAttribute(LoginService.LOGIN_IDENTITY_KEY);
|
||||
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, int id) {
|
||||
|
||||
// avoid opt login seft
|
||||
XxlJobUser loginUser = (XxlJobUser) request.getAttribute(LoginService.LOGIN_IDENTITY_KEY);
|
||||
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, String password){
|
||||
|
||||
// valid password
|
||||
if (password==null || password.trim().length()==0){
|
||||
return new ReturnT<String>(ReturnT.FAIL.getCode(), "密码不可为空");
|
||||
}
|
||||
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 md5Password = DigestUtils.md5DigestAsHex(password.getBytes());
|
||||
|
||||
// update pwd
|
||||
XxlJobUser loginUser = (XxlJobUser) request.getAttribute(LoginService.LOGIN_IDENTITY_KEY);
|
||||
|
||||
// do write
|
||||
XxlJobUser existUser = xxlJobUserDao.loadByUserName(loginUser.getUsername());
|
||||
existUser.setPassword(md5Password);
|
||||
xxlJobUserDao.update(existUser);
|
||||
|
||||
return ReturnT.SUCCESS;
|
||||
}
|
||||
|
||||
}
|
||||
@ -1,29 +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;
|
||||
|
||||
}
|
||||
@ -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,59 +0,0 @@
|
||||
package com.xxl.job.admin.controller.interceptor;
|
||||
|
||||
import com.xxl.job.admin.controller.annotation.PermissionLimit;
|
||||
import com.xxl.job.admin.core.model.XxlJobUser;
|
||||
import com.xxl.job.admin.core.util.I18nUtil;
|
||||
import com.xxl.job.admin.service.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;
|
||||
|
||||
|
||||
/**
|
||||
* 权限拦截
|
||||
*
|
||||
* @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"));
|
||||
}
|
||||
request.setAttribute(LoginService.LOGIN_IDENTITY_KEY, loginUser);
|
||||
}
|
||||
|
||||
return true; // proceed with the next interceptor
|
||||
}
|
||||
|
||||
}
|
||||
@ -1,28 +0,0 @@
|
||||
package com.xxl.job.admin.controller.interceptor;
|
||||
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
|
||||
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
|
||||
|
||||
import jakarta.annotation.Resource;
|
||||
|
||||
/**
|
||||
* 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,99 +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) {
|
||||
|
||||
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,158 +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 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 jakarta.annotation.Resource;
|
||||
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("${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 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 < 7) {
|
||||
return -1; // Limit greater than or equal to 7, 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,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,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,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,101 +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());
|
||||
|
||||
executorBizRepository.put(address, executorBiz);
|
||||
return executorBiz;
|
||||
}
|
||||
|
||||
}
|
||||
@ -1,184 +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 (InterruptedException 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 (Exception e) {
|
||||
if (!toStop) {
|
||||
logger.error(">>>>>>>>>>> xxl-job, job fail monitor thread error:{}", e);
|
||||
}
|
||||
}
|
||||
|
||||
try {
|
||||
TimeUnit.SECONDS.sleep(60);
|
||||
} catch (Exception 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 (InterruptedException 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,110 +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 (Exception e) {
|
||||
if (!toStop) {
|
||||
logger.error(">>>>>>>>>>> xxl-job, job fail monitor thread error:{}", e);
|
||||
}
|
||||
}
|
||||
|
||||
try {
|
||||
TimeUnit.SECONDS.sleep(10);
|
||||
} catch (Exception 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 (InterruptedException e) {
|
||||
logger.error(e.getMessage(), e);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@ -1,152 +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 (Exception 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 (Exception 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 (InterruptedException e) {
|
||||
logger.error(e.getMessage(), e);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@ -1,204 +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();
|
||||
public static JobRegistryHelper getInstance(){
|
||||
return instance;
|
||||
}
|
||||
|
||||
private ThreadPoolExecutor registryOrRemoveThreadPool = null;
|
||||
private Thread registryMonitorThread;
|
||||
private volatile boolean toStop = false;
|
||||
|
||||
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 (Exception e) {
|
||||
if (!toStop) {
|
||||
logger.error(">>>>>>>>>>> xxl-job, job registry monitor thread error:{}", e);
|
||||
}
|
||||
}
|
||||
try {
|
||||
TimeUnit.SECONDS.sleep(RegistryConfig.BEAT_TIMEOUT);
|
||||
} catch (InterruptedException 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 (InterruptedException 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() {
|
||||
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
|
||||
freshGroupRegistryInfo(registryParam);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
return ReturnT.SUCCESS;
|
||||
}
|
||||
|
||||
private void freshGroupRegistryInfo(RegistryParam registryParam){
|
||||
// Under consideration, prevent affecting core tables
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@ -1,369 +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.sql.SQLException;
|
||||
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 (InterruptedException 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 (Exception e) {
|
||||
if (!scheduleThreadToStop) {
|
||||
logger.error(">>>>>>>>>>> xxl-job, JobScheduleHelper#scheduleThread error:{}", e);
|
||||
}
|
||||
} finally {
|
||||
|
||||
// commit
|
||||
if (conn != null) {
|
||||
try {
|
||||
conn.commit();
|
||||
} catch (SQLException e) {
|
||||
if (!scheduleThreadToStop) {
|
||||
logger.error(e.getMessage(), e);
|
||||
}
|
||||
}
|
||||
try {
|
||||
conn.setAutoCommit(connAutoCommit);
|
||||
} catch (SQLException e) {
|
||||
if (!scheduleThreadToStop) {
|
||||
logger.error(e.getMessage(), e);
|
||||
}
|
||||
}
|
||||
try {
|
||||
conn.close();
|
||||
} catch (SQLException e) {
|
||||
if (!scheduleThreadToStop) {
|
||||
logger.error(e.getMessage(), e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// close PreparedStatement
|
||||
if (null != preparedStatement) {
|
||||
try {
|
||||
preparedStatement.close();
|
||||
} catch (SQLException 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 (InterruptedException 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 (InterruptedException 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 (Exception 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) throws Exception {
|
||||
Date nextValidTime = generateNextValidTime(jobInfo, fromTime);
|
||||
if (nextValidTime != null) {
|
||||
jobInfo.setTriggerLastTime(jobInfo.getTriggerNextTime());
|
||||
jobInfo.setTriggerNextTime(nextValidTime.getTime());
|
||||
} else {
|
||||
jobInfo.setTriggerStatus(0);
|
||||
jobInfo.setTriggerLastTime(0);
|
||||
jobInfo.setTriggerNextTime(0);
|
||||
logger.warn(">>>>>>>>>>> xxl-job, refreshNextValidTime fail for job: jobId={}, scheduleType={}, scheduleConf={}",
|
||||
jobInfo.getId(), jobInfo.getScheduleType(), jobInfo.getScheduleConf());
|
||||
}
|
||||
}
|
||||
|
||||
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 (InterruptedException e) {
|
||||
logger.error(e.getMessage(), e);
|
||||
}
|
||||
if (scheduleThread.getState() != Thread.State.TERMINATED){
|
||||
// interrupt and wait
|
||||
scheduleThread.interrupt();
|
||||
try {
|
||||
scheduleThread.join();
|
||||
} catch (InterruptedException 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 (InterruptedException e) {
|
||||
logger.error(e.getMessage(), e);
|
||||
}
|
||||
}
|
||||
|
||||
// stop ring (wait job-in-memory stop)
|
||||
ringThreadToStop = true;
|
||||
try {
|
||||
TimeUnit.SECONDS.sleep(1);
|
||||
} catch (InterruptedException e) {
|
||||
logger.error(e.getMessage(), e);
|
||||
}
|
||||
if (ringThread.getState() != Thread.State.TERMINATED){
|
||||
// interrupt and wait
|
||||
ringThread.interrupt();
|
||||
try {
|
||||
ringThread.join();
|
||||
} catch (InterruptedException 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() + Integer.valueOf(jobInfo.getScheduleConf())*1000 );
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
}
|
||||
@ -1,98 +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,31 +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,79 +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,133 +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;
|
||||
}
|
||||
|
||||
}
|
||||
@ -0,0 +1,15 @@
|
||||
package com.xxl.job.admin.framework.constant;
|
||||
|
||||
/**
|
||||
* System Constants
|
||||
*
|
||||
* @author xuxueli 2018-08-24 03:43
|
||||
*/
|
||||
public class Consts {
|
||||
|
||||
/**
|
||||
* Admin Role
|
||||
*/
|
||||
public static final String ADMIN_ROLE = "ADMIN";
|
||||
|
||||
}
|
||||
@ -0,0 +1,127 @@
|
||||
package com.xxl.job.admin.framework.controller;
|
||||
|
||||
import com.xxl.job.admin.framework.constant.Consts;
|
||||
import com.xxl.job.admin.framework.model.dto.XxlBootResourceDTO;
|
||||
import com.xxl.job.admin.business.service.XxlJobService;
|
||||
import com.xxl.job.admin.framework.util.I18nUtil;
|
||||
import com.xxl.sso.core.annotation.XxlSso;
|
||||
import com.xxl.sso.core.helper.XxlSsoHelper;
|
||||
import com.xxl.sso.core.model.LoginInfo;
|
||||
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 "framework/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 "framework/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 "framework/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("framework/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,200 @@
|
||||
package com.xxl.job.admin.framework.controller;
|
||||
|
||||
import com.xxl.job.admin.framework.constant.Consts;
|
||||
import com.xxl.job.admin.business.mapper.XxlJobGroupMapper;
|
||||
import com.xxl.job.admin.framework.mapper.XxlJobUserMapper;
|
||||
import com.xxl.job.admin.business.model.XxlJobGroup;
|
||||
import com.xxl.job.admin.framework.model.XxlJobUser;
|
||||
import com.xxl.job.admin.framework.util.I18nUtil;
|
||||
import com.xxl.sso.core.annotation.XxlSso;
|
||||
import com.xxl.sso.core.helper.XxlSsoHelper;
|
||||
import com.xxl.sso.core.model.LoginInfo;
|
||||
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;
|
||||
|
||||
/**
|
||||
* job user controller
|
||||
*
|
||||
* @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 "business/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();
|
||||
}*/
|
||||
|
||||
}
|
||||
@ -0,0 +1,125 @@
|
||||
package com.xxl.job.admin.framework.controller;
|
||||
|
||||
import com.xxl.job.admin.framework.mapper.XxlJobUserMapper;
|
||||
import com.xxl.job.admin.framework.model.XxlJobUser;
|
||||
import com.xxl.job.admin.framework.util.I18nUtil;
|
||||
import com.xxl.sso.core.annotation.XxlSso;
|
||||
import com.xxl.sso.core.helper.XxlSsoHelper;
|
||||
import com.xxl.sso.core.model.LoginInfo;
|
||||
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("framework/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();
|
||||
}
|
||||
|
||||
}
|
||||
@ -1,6 +1,7 @@
|
||||
package com.xxl.job.admin.dao;
|
||||
package com.xxl.job.admin.framework.mapper;
|
||||
|
||||
import com.xxl.job.admin.core.model.XxlJobUser;
|
||||
import com.xxl.job.admin.framework.model.XxlJobUser;
|
||||
import com.xxl.tool.response.Response;
|
||||
import org.apache.ibatis.annotations.Mapper;
|
||||
import org.apache.ibatis.annotations.Param;
|
||||
import java.util.List;
|
||||
@ -9,7 +10,7 @@ import java.util.List;
|
||||
* @author xuxueli 2019-05-04 16:44:59
|
||||
*/
|
||||
@Mapper
|
||||
public interface XxlJobUserDao {
|
||||
public interface XxlJobUserMapper {
|
||||
|
||||
public List<XxlJobUser> pageList(@Param("offset") int offset,
|
||||
@Param("pagesize") int pagesize,
|
||||
@ -22,10 +23,14 @@ public interface XxlJobUserDao {
|
||||
|
||||
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);
|
||||
|
||||
}
|
||||
@ -1,8 +1,8 @@
|
||||
package com.xxl.job.admin.core.model;
|
||||
|
||||
import org.springframework.util.StringUtils;
|
||||
package com.xxl.job.admin.framework.model;
|
||||
|
||||
/**
|
||||
* xxl job user
|
||||
*
|
||||
* @author xuxueli 2019-05-04 16:43:12
|
||||
*/
|
||||
public class XxlJobUser {
|
||||
@ -10,8 +10,9 @@ public class XxlJobUser {
|
||||
private int id;
|
||||
private String username; // 账号
|
||||
private String password; // 密码
|
||||
private String token; // 登录token
|
||||
private int role; // 角色:0-普通用户、1-管理员
|
||||
private String permission; // 权限:执行器ID列表,多个逗号分割
|
||||
private String permission; // 权限:执行器ID列表,多个逗号分割
|
||||
|
||||
public int getId() {
|
||||
return id;
|
||||
@ -37,6 +38,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;
|
||||
}
|
||||
@ -53,21 +62,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;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Loading…
x
Reference in New Issue
Block a user