Merge branch 'master' into master

2.5.0
许雪里 2 years ago committed by GitHub
commit 66e0d56724
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194

@ -40,6 +40,19 @@ Now, it's already open source, and many companies use it in production environme
XXL-JOB是一个分布式任务调度平台其核心设计目标是开发迅速、学习简单、轻量级、易扩展。现已开放源代码并接入多家公司线上产品线开箱即用。 XXL-JOB是一个分布式任务调度平台其核心设计目标是开发迅速、学习简单、轻量级、易扩展。现已开放源代码并接入多家公司线上产品线开箱即用。
## Sponsor
XXL-JOB is an open source and free project, with its ongoing development made possible entirely by the support of these awesome backers.
XXL-JOB 是一个开源且免费项目,其正在进行的开发完全得益于支持者的支持。开源不易,[前往赞助项目开发](https://www.xuxueli.com/page/donate.html )
<!-- supporter start -->
<h5 style="color: silver;" >金牌赞助方</h5>
<a href="https://www.jetbrains.com/idea/?from=xuxueli.com" title="jetbrains" target="_blank" >
<img width="55px" src="http://www.xuxueli.com/page/static/images/logo_intellij.jpeg" >
</a>
<!-- supporter end -->
## Documentation ## Documentation
- [中文文档](https://www.xuxueli.com/xxl-job/) - [中文文档](https://www.xuxueli.com/xxl-job/)
- [English Documentation](https://www.xuxueli.com/xxl-job/en/) - [English Documentation](https://www.xuxueli.com/xxl-job/en/)
@ -119,6 +132,8 @@ XXL-JOB是一个分布式任务调度平台其核心设计目标是开发迅
于2021-12-06XXL-JOB参与"[2021年度OSC中国开源项目评选](https://www.oschina.net/project/top_cn_2021) "评比,在当时已录入的一万多个开源项目中角逐,最终当选"最受欢迎项目"。 于2021-12-06XXL-JOB参与"[2021年度OSC中国开源项目评选](https://www.oschina.net/project/top_cn_2021) "评比,在当时已录入的一万多个开源项目中角逐,最终当选"最受欢迎项目"。
于2024-11-06XXL-JOB经 GitCode 官方评审,获得 “G-Star项目毕业认证”。
> 我司大众点评目前已接入XXL-JOB内部别名《Ferrari》Ferrari基于XXL-JOB的V1.1版本定制而成,新接入应用推荐升级最新版本)。 > 我司大众点评目前已接入XXL-JOB内部别名《Ferrari》Ferrari基于XXL-JOB的V1.1版本定制而成,新接入应用推荐升级最新版本)。
据最新统计, 自2016-01-21接入至2017-12-01期间该系统已调度约100万次表现优异。新接入应用推荐使用最新版本因为经过数十个版本的更新系统的任务模型、UI交互模型以及底层调度通讯模型都有了较大的优化和提升核心功能更加稳定高效。 据最新统计, 自2016-01-21接入至2017-12-01期间该系统已调度约100万次表现优异。新接入应用推荐使用最新版本因为经过数十个版本的更新系统的任务模型、UI交互模型以及底层调度通讯模型都有了较大的优化和提升核心功能更加稳定高效。
@ -766,6 +781,41 @@ XXL-JOB是一个分布式任务调度平台其核心设计目标是开发迅
- 640、再造再生健康科技有限公司 - 640、再造再生健康科技有限公司
- 641、华宝证券 - 641、华宝证券
- 642、卓正医疗 - 642、卓正医疗
- 643、深圳湛信科技
- 644、陕西鑫众为软件有限公司
- 645、深圳市润农科技有限公司
- 646、庚商教育智能科技有限公司
- 647、杭州祎声科技
- 648、四川久远银海软件股份有限公司
- 649、GeeFox极狐低代码
- 650、浙江和仁科技股份有限公司
- 651、宁波聚臻智能科技有限公司
- 652、福建福昕软件开发股份有限公司【福昕】
- 653、广州中长康达信息技术有限公司
- 654、武汉趣改信息科技有限公司
- 655、北京华夏思源科技发展有限公司
- 656、宁波关关通科技有限公司
- 657、青岛吕氏餐饮有限公司
- 658、杭州乐刻网络科技有限公司
- 659、上海红瓦信息科技有限公司
- 660、陕西旅小宝信息科技有限公司
- 661、中科卓恒(大连)科技有限公司
- 662、北京华益精点生物技术有限公司
- 663、马士基中国航运有限公司【马士基】
- 664、陕西美咚网络科技有限公司
- 665、山东新北洋信息技术股份有限公司
- 666、福建中瑞文化发展集团有限公司
- 667、黑龙江省建工集团有限责任公司【黑龙江省建工】
- 668、志信能达安全科技(广州)有限公司
- 669、重庆开源共创科技有限公司
- 670、华泰人寿保险股份有限公司【华泰人寿】
- 671、成都盘古纵横集团
- 672、北京果果乐学科技有限公司
- 673、北京凌云空间科技有限公司
- 674、临工重机股份有限公司
- 675、上海热风时尚管理集团【热风】
- 676、HashKey Exchange
- 677、傲基深圳跨境商务股份有限公司
- …… - ……
> 更多接入的公司,欢迎在 [登记地址](https://github.com/xuxueli/xxl-job/issues/1 ) 登记,登记仅仅为了产品推广。 > 更多接入的公司,欢迎在 [登记地址](https://github.com/xuxueli/xxl-job/issues/1 ) 登记,登记仅仅为了产品推广。
@ -785,10 +835,4 @@ This product is open source and free, and will continue to provide free communit
- Licensed under the GNU General Public License (GPL) v3. - Licensed under the GNU General Public License (GPL) v3.
- Copyright (c) 2015-present, xuxueli. - Copyright (c) 2015-present, xuxueli.
产品开源免费,并且将持续提供免费的社区技术支持。个人或企业内部可自由的接入和使用。如有需要可邮件联系作者免费获取项目授权。 产品开源免费,并且将持续提供免费的社区技术支持。个人或企业内部可自由的接入和使用。如有需要可 [邮件联系](https://www.xuxueli.com/page/community.html) 作者免费获取项目授权。
## Donate
No matter how much the donation amount is enough to express your thought, thank you very much [To donate](https://www.xuxueli.com/page/donate.html )
无论捐赠金额多少都足够表达您这份心意,非常感谢 [前往捐赠](https://www.xuxueli.com/page/donate.html )

@ -736,6 +736,41 @@ XXL-JOB是一个分布式任务调度平台其核心设计目标是开发迅
- 640、再造再生健康科技有限公司 - 640、再造再生健康科技有限公司
- 641、华宝证券 - 641、华宝证券
- 642、卓正医疗 - 642、卓正医疗
- 643、深圳湛信科技
- 644、陕西鑫众为软件有限公司
- 645、深圳市润农科技有限公司
- 646、庚商教育智能科技有限公司
- 647、杭州祎声科技
- 648、四川久远银海软件股份有限公司
- 649、GeeFox极狐低代码
- 650、浙江和仁科技股份有限公司
- 651、宁波聚臻智能科技有限公司
- 652、福建福昕软件开发股份有限公司【福昕】
- 653、广州中长康达信息技术有限公司
- 654、武汉趣改信息科技有限公司
- 655、北京华夏思源科技发展有限公司
- 656、宁波关关通科技有限公司
- 657、青岛吕氏餐饮有限公司
- 658、杭州乐刻网络科技有限公司
- 659、上海红瓦信息科技有限公司
- 660、陕西旅小宝信息科技有限公司
- 661、中科卓恒(大连)科技有限公司
- 662、北京华益精点生物技术有限公司
- 663、马士基中国航运有限公司【马士基】
- 664、陕西美咚网络科技有限公司
- 665、山东新北洋信息技术股份有限公司
- 666、福建中瑞文化发展集团有限公司
- 667、黑龙江省建工集团有限责任公司【黑龙江省建工】
- 668、志信能达安全科技(广州)有限公司
- 669、重庆开源共创科技有限公司
- 670、华泰人寿保险股份有限公司【华泰人寿】
- 671、成都盘古纵横集团
- 672、北京果果乐学科技有限公司
- 673、北京凌云空间科技有限公司
- 674、临工重机股份有限公司
- 675、上海热风时尚管理集团【热风】
- 676、HashKey Exchange
- 677、傲基深圳跨境商务股份有限公司
- …… - ……
> 更多接入的公司,欢迎在 [登记地址](https://github.com/xuxueli/xxl-job/issues/1 ) 登记,登记仅仅为了产品推广。 > 更多接入的公司,欢迎在 [登记地址](https://github.com/xuxueli/xxl-job/issues/1 ) 登记,登记仅仅为了产品推广。
@ -756,6 +791,7 @@ XXL-JOB是一个分布式任务调度平台其核心设计目标是开发迅
--- | --- --- | ---
[https://github.com/xuxueli/xxl-job](https://github.com/xuxueli/xxl-job) | [Download](https://github.com/xuxueli/xxl-job/releases) [https://github.com/xuxueli/xxl-job](https://github.com/xuxueli/xxl-job) | [Download](https://github.com/xuxueli/xxl-job/releases)
[http://gitee.com/xuxueli0323/xxl-job](http://gitee.com/xuxueli0323/xxl-job) | [Download](http://gitee.com/xuxueli0323/xxl-job/releases) [http://gitee.com/xuxueli0323/xxl-job](http://gitee.com/xuxueli0323/xxl-job) | [Download](http://gitee.com/xuxueli0323/xxl-job/releases)
[https://gitcode.com/xuxueli0323/xxl-job](https://gitcode.com/xuxueli0323/xxl-job) | [Download](https://gitcode.com/xuxueli0323/xxl-job/tags)
#### 中央仓库地址 #### 中央仓库地址
@ -875,8 +911,6 @@ docker pull xuxueli/xxl-job-admin
- 创建容器并运行 - 创建容器并运行
``` ```
docker run -p 8080:8080 -v /tmp:/data/applogs --name xxl-job-admin -d xuxueli/xxl-job-admin:{指定版本}
/** /**
* 如需自定义 mysql 等配置,可通过 "-e PARAMS" 指定,参数格式 PARAMS="--key=value --key2=value2" * 如需自定义 mysql 等配置,可通过 "-e PARAMS" 指定,参数格式 PARAMS="--key=value --key2=value2"
* 配置项参考文件:/xxl-job/xxl-job-admin/src/main/resources/application.properties * 配置项参考文件:/xxl-job/xxl-job-admin/src/main/resources/application.properties
@ -1027,7 +1061,7 @@ public XxlJobSpringExecutor xxlJobExecutor() {
GLUE模式(PHP):任务以源码方式维护在调度中心;该模式的任务实际上是一段 "php" 脚本; GLUE模式(PHP):任务以源码方式维护在调度中心;该模式的任务实际上是一段 "php" 脚本;
GLUE模式(NodeJS):任务以源码方式维护在调度中心;该模式的任务实际上是一段 "nodejs" 脚本; GLUE模式(NodeJS):任务以源码方式维护在调度中心;该模式的任务实际上是一段 "nodejs" 脚本;
GLUE模式(PowerShell):任务以源码方式维护在调度中心;该模式的任务实际上是一段 "PowerShell" 脚本; GLUE模式(PowerShell):任务以源码方式维护在调度中心;该模式的任务实际上是一段 "PowerShell" 脚本;
- JobHandler运行模式为 "BEAN模式" 时生效对应执行器中新开发的JobHandler类“@JobHandler”注解自定义的value值 - JobHandler运行模式为 "BEAN模式" 时生效对应执行器中新开发的JobHandler类“@XxlJob”注解自定义的value值
- 执行参数:任务执行所需的参数; - 执行参数:任务执行所需的参数;
高级配置: 高级配置:
@ -1330,11 +1364,11 @@ XXL-JOB调度模块基于自研调度组件并支持集群部署调度数据
#### 5.3.2 系统组成 #### 5.3.2 系统组成
- **调度模块(调度中心)** - **调度模块(调度中心)**
负责管理调度信息,按照调度配置发出调度请求,自身不承担业务代码。调度系统与任务解耦,提高了系统可用性和稳定性,同时调度系统性能不再受限于任务模块; 负责管理调度信息,按照调度配置发出调度请求,自身不承担业务代码。调度系统与任务解耦,提高了系统可用性和稳定性,同时调度系统性能不再受限于任务模块;
支持可视化、简单且动态的管理调度信息包括任务新建更新删除GLUE开发和任务报警等所有上述操作都会实时生效同时支持监控调度结果以及执行日志支持执行器Failover。 支持可视化、简单且动态的管理调度信息包括任务新建更新删除GLUE开发和任务报警等所有上述操作都会实时生效同时支持监控调度结果以及执行日志支持执行器Failover。
- **执行模块(执行器)** - **执行模块(执行器)**
负责接收调度请求并执行任务逻辑。任务模块专注于任务的执行等操作,开发和维护更加简单和高效; 负责接收调度请求并执行任务逻辑。任务模块专注于任务的执行等操作,开发和维护更加简单和高效;
接收“调度中心”的执行请求、终止请求和日志请求等。 接收“调度中心”的执行请求、终止请求和日志请求等。
#### 5.3.3 架构图 #### 5.3.3 架构图
@ -1469,7 +1503,7 @@ xxl-job-admin#com.xxl.job.admin.controller.JobApiController.callback
#### 5.5.4 执行器 #### 5.5.4 执行器
执行器实际上是一个内嵌的Server默认端口9999配置项xxl.job.executor.port 执行器实际上是一个内嵌的Server默认端口9999配置项xxl.job.executor.port
在项目启动时,执行器会通过“@JobHandler”识别Spring容器中“Bean模式任务”以注解的value属性为key管理起来。 在项目启动时,执行器会通过“@XxlJob”识别Spring容器中“Bean模式任务”以注解的value属性为key管理起来。
“执行器”接收到“调度中心”的调度请求时如果任务类型为“Bean模式”将会匹配Spring容器中的“Bean模式任务”然后调用其execute方法执行任务逻辑。如果任务类型为“GLUE模式”将会加载GLue代码实例化Java对象注入依赖的Spring服务注意Glue代码中注入的Spring服务必须存在与该“执行器”项目的Spring容器中然后调用execute方法执行任务逻辑。 “执行器”接收到“调度中心”的调度请求时如果任务类型为“Bean模式”将会匹配Spring容器中的“Bean模式任务”然后调用其execute方法执行任务逻辑。如果任务类型为“GLUE模式”将会加载GLue代码实例化Java对象注入依赖的Spring服务注意Glue代码中注入的Spring服务必须存在与该“执行器”项目的Spring容器中然后调用execute方法执行任务逻辑。
@ -1561,7 +1595,7 @@ echo "分片总数 total = $3"
- 1、执行器改为手动注册下线一半机器列表A组线上运行另一半机器列表B组 - 1、执行器改为手动注册下线一半机器列表A组线上运行另一半机器列表B组
- 2、等待A组机器任务运行结束并编译上线执行器注册地址替换为A组 - 2、等待A组机器任务运行结束并编译上线执行器注册地址替换为A组
- 3、等待B组机器任务运行结束并编译上线执行器注册地址替换为A组+B组 - 3、等待B组机器任务运行结束并编译上线执行器注册地址替换为A组+B组
操作结束; 操作结束;
### 5.13 任务执行结果说明 ### 5.13 任务执行结果说明
系统根据以下标准判断任务执行结果,可参考之。 系统根据以下标准判断任务执行结果,可参考之。
@ -1589,7 +1623,7 @@ XXL-JOB是一个跨语言的任务调度平台主要体现在如下几个方
可以通过以下命令快速构建调度中心,并启动运行; 可以通过以下命令快速构建调度中心,并启动运行;
``` ```
mvn clean package mvn clean package
docker build -t xuxueli/xxl-job-admin ./xxl-job-admin docker build -t xuxueli/xxl-job-admin:{version} ./xxl-job-admin
docker run --name xxl-job-admin -p 8080:8080 -d xuxueli/xxl-job-admin docker run --name xxl-job-admin -p 8080:8080 -d xuxueli/xxl-job-admin
``` ```
@ -1818,7 +1852,7 @@ Header
#### d、查看执行日志 #### d、查看执行日志
``` ```
说明:终止任务,滚动方式加载 说明:查看任务日志,滚动方式加载
------ ------
@ -1868,16 +1902,16 @@ Header
- 4、支持“任务日志” - 4、支持“任务日志”
- 5、支持“串行执行”并行执行 - 5、支持“串行执行”并行执行
说明V1.2版本将系统架构按功能拆分为: 说明V1.2版本将系统架构按功能拆分为:
- 调度模块(调度中心):负责管理调度信息,按照调度配置发出调度请求; - 调度模块(调度中心):负责管理调度信息,按照调度配置发出调度请求;
- 执行模块(执行器):负责接收调度请求并执行任务逻辑; - 执行模块(执行器):负责接收调度请求并执行任务逻辑;
- 通讯模块:负责调度模块和任务模块之间的信息通讯; - 通讯模块:负责调度模块和任务模块之间的信息通讯;
优点: 优点:
- 解耦:任务模块提供任务接口,调度模块维护调度信息,业务相互独立; - 解耦:任务模块提供任务接口,调度模块维护调度信息,业务相互独立;
- 高扩展性; - 高扩展性;
- 稳定性; - 稳定性;
### 7.3 版本 V1.3.0,新特性[2016-05-19] ### 7.3 版本 V1.3.0,新特性[2016-05-19]
- 1、遗弃“本地任务”模式推荐使用“远程任务”易于系统解耦任务对应的JobHandler统称为“执行器” - 1、遗弃“本地任务”模式推荐使用“远程任务”易于系统解耦任务对应的JobHandler统称为“执行器”
@ -1885,17 +1919,17 @@ Header
- 3、UI交互优化左侧菜单展开状态优化菜单项选中状态优化任务列表打开表格有压缩优化 - 3、UI交互优化左侧菜单展开状态优化菜单项选中状态优化任务列表打开表格有压缩优化
- 4、【重要】“执行器”细分为BEAN、GLUE两种开发模式简介见下文 - 4、【重要】“执行器”细分为BEAN、GLUE两种开发模式简介见下文
“执行器” 模式简介: “执行器” 模式简介:
- BEAN模式执行器每个执行器都是Spring的一个Bean实例XXL-JOB通过注解@JobHandler识别和调度执行器 - BEAN模式执行器每个执行器都是Spring的一个Bean实例XXL-JOB通过注解@JobHandler识别和调度执行器
-GLUE模式执行器每个执行器对应一段代码在线Web编辑和维护动态编译生效执行器负责加载GLUE代码和执行 -GLUE模式执行器每个执行器对应一段代码在线Web编辑和维护动态编译生效执行器负责加载GLUE代码和执行
### 7.4 版本 V1.3.1,新特性[2016-05-23] ### 7.4 版本 V1.3.1,新特性[2016-05-23]
- 1、更新项目目录结构 - 1、更新项目目录结构
- /xxl-job-admin -------------------- 【调度中心】:负责管理调度信息,按照调度配置发出调度请求; - /xxl-job-admin -------------------- 【调度中心】:负责管理调度信息,按照调度配置发出调度请求;
- /xxl-job-core ----------------------- 公共依赖 - /xxl-job-core ----------------------- 公共依赖
- /xxl-job-executor-example ------ 【执行器】:负责接收调度请求并执行任务逻辑; - /xxl-job-executor-example ------ 【执行器】:负责接收调度请求并执行任务逻辑;
- /db ---------------------------------- 建表脚本 - /db ---------------------------------- 建表脚本
- /doc --------------------------------- 用户手册 - /doc --------------------------------- 用户手册
- 2、在新的目录结构上升级了用户手册 - 2、在新的目录结构上升级了用户手册
- 3、优化了一些交互和UI - 3、优化了一些交互和UI
@ -2341,17 +2375,39 @@ public void execute() {
- 5、【优化】任务线程名优化提升可读性与问题定位效率(ISSUE-2527)。 - 5、【优化】任务线程名优化提升可读性与问题定位效率(ISSUE-2527)。
### 7.33 版本 v2.4.0 Release Notes[2023-03-23] ### 7.33 版本 v2.4.0 Release Notes[2023-03-23]
- 1、【优化】执行器任务Bean扫描逻辑优化解决懒加载注解失效问题 - 1、【优化】执行器任务Bean扫描逻辑优化解决懒加载注解失效问题
- 2、【优化】多个项目依赖升级至较新稳定版本涉及netty、groovy、spring、springboot、mybatis等 - 2、【优化】多个项目依赖升级至较新稳定版本涉及netty、groovy、spring、springboot、mybatis等
- 3、【修复】"CVE-2022-36157" 授权漏洞修复。 - 3、【修复】漏洞修复包括"CVE-2022-36157" 授权漏洞修复;"CVE-2022-43183" SSRF漏洞修复
- 4、【修复】"CVE-2022-43183" SSRF漏洞修复。
### 7.34 版本 v2.4.1 Release Notes[规划中] ### 7.34 版本 v2.4.1 Release Notes[2024-04-17]
- 1、【优化】多个项目依赖升级至较新稳定版本涉及netty、groovy、springboot、mybatis等 - 1、【优化】多个项目依赖升级至较新稳定版本涉及netty、groovy、springboot、mybatis等
- 2、【修复】"CVE-2022-43402" groovy低版本漏洞修复。 - 2、【优化】执行器注册节点显示交互调整优化注册节点过多时展示不全体验
- 3、【修复】调度日志页面XSS漏洞修复(ISSUE-3360)。 - 3、【修复】漏洞修复包括"CVE-2022-43402" groovy低版本漏洞修复"CVE-2024-29025" netty低版本漏洞修复"CVE-2024-3366" freemarker模板注入漏洞修复"CVE-2022-43183" 越权漏洞增强修复;
- 4、[规划中]注册节点,弹框分页展示;解决注册节点过多时无法展示问题; - 4、【修复】调度日志页面XSS问题修复(ISSUE-3360)。
### 7.35 版本 v2.4.2 Release Notes[2024-11-16]
- 1、【优化】调度中心任务Next计算逻辑调整避免Cron解析失败导致重复执行问题。
- 2、【优化】Cron解析组件代码重构微调健壮性提升
- 3、【优化】修改密码交互调整避免CSRF隐患
- 4、【优化】JdkSerializeTool流关闭逻辑优化
- 5、【优化】任务信息、执行日志API非功能设计完善避免越权隐患
- 6、【修复】漏洞修复包括 "CVE-2024-42681" 子任务越权漏洞修复、"CVE-2023-33779" 任务API越权问题修复
- 7、【升级】多个项目依赖升级至较新稳定版本涉及netty、groovy、gson、springboot、mybatis等
**备注:**
- a、“CVE-2024-38820”漏洞源自spring当前使用spring5.x及springboot2.x软件普遍受该问题影响。 该问题修复需要升级至spring6.x与springboot3.x同时需要升级JDK17如有诉求可自行升级。计划下个大版本升级spring及jdk版本解决该问题。
- b、本次升级数据模型及通讯协议向前兼容v2.4.*可无缝升级。
### 7.36 版本 v2.5.0 Release Notes[规划中]
- 1、【优化】框架基础守护线程异常处理逻辑优化避免极端情况下因Error导致调度终止问题
- 2、【优化】部分系统日志优化提升可读性
- 3、【重构】调度线程任务信息更新逻辑优化避免极端情况下已关闭任务被启动问题
- 5、【重构】执行器注册逻辑重构降低多调度中心地址时并发注册问题注册表“xxl_job_registry”新增唯一索引避免冗余注册信息存储
- 4、[规划中]升级springboot3.x解决2.x老版本漏洞类问题。注意springboot3.x依赖jdk17
- 5、[规划中]安全功能增强通讯加密参数改用加密数据避免AccessToken明文 降低token泄漏风险
- 6、[规划中]登陆态Token声称逻辑优化混淆登陆时间属性降低token泄漏风险
### TODO LIST ### TODO LIST
- 1、调度隔离调度中心针对不同执行器各自维护不同的调度和远程触发组件。 - 1、调度隔离调度中心针对不同执行器各自维护不同的调度和远程触发组件。

Binary file not shown.

Binary file not shown.

@ -1,5 +1,5 @@
# #
# XXL-JOB v2.4.1-SNAPSHOT # XXL-JOB
# Copyright (c) 2015-present, xuxueli. # Copyright (c) 2015-present, xuxueli.
CREATE database if NOT EXISTS `xxl_job` default character set utf8mb4 collate utf8mb4_unicode_ci; CREATE database if NOT EXISTS `xxl_job` default character set utf8mb4 collate utf8mb4_unicode_ci;
@ -54,7 +54,8 @@ CREATE TABLE `xxl_job_log` (
PRIMARY KEY (`id`), PRIMARY KEY (`id`),
KEY `I_trigger_time` (`trigger_time`), KEY `I_trigger_time` (`trigger_time`),
KEY `I_handle_code` (`handle_code`), KEY `I_handle_code` (`handle_code`),
KEY `idx_jobid_jobgroup` (`job_id`,`job_group`) KEY `I_jobid_jobgroup` (`job_id`,`job_group`),
KEY `I_job_id` (`job_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4; ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
CREATE TABLE `xxl_job_log_report` ( CREATE TABLE `xxl_job_log_report` (
@ -86,7 +87,7 @@ CREATE TABLE `xxl_job_registry` (
`registry_value` varchar(255) NOT NULL, `registry_value` varchar(255) NOT NULL,
`update_time` datetime DEFAULT NULL, `update_time` datetime DEFAULT NULL,
PRIMARY KEY (`id`), PRIMARY KEY (`id`),
KEY `i_g_k_v` (`registry_group`,`registry_key`,`registry_value`) UNIQUE KEY `i_g_k_v` (`registry_group`,`registry_key`,`registry_value`) USING BTREE
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4; ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
CREATE TABLE `xxl_job_group` ( CREATE TABLE `xxl_job_group` (

Binary file not shown.

After

Width:  |  Height:  |  Size: 221 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 367 KiB

After

Width:  |  Height:  |  Size: 286 KiB

@ -3,7 +3,7 @@
<modelVersion>4.0.0</modelVersion> <modelVersion>4.0.0</modelVersion>
<groupId>com.xuxueli</groupId> <groupId>com.xuxueli</groupId>
<artifactId>xxl-job</artifactId> <artifactId>xxl-job</artifactId>
<version>2.4.1-SNAPSHOT</version> <version>2.5.0-SNAPSHOT</version>
<packaging>pom</packaging> <packaging>pom</packaging>
<name>${project.artifactId}</name> <name>${project.artifactId}</name>
@ -17,31 +17,32 @@
</modules> </modules>
<properties> <properties>
<!-- env -->
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding> <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
<maven.compiler.encoding>UTF-8</maven.compiler.encoding> <maven.compiler.encoding>UTF-8</maven.compiler.encoding>
<maven.compiler.source>1.8</maven.compiler.source> <maven.compiler.source>1.8</maven.compiler.source>
<maven.compiler.target>1.8</maven.compiler.target> <maven.compiler.target>1.8</maven.compiler.target>
<maven.test.skip>true</maven.test.skip> <maven.test.skip>true</maven.test.skip>
<!-- plugin -->
<netty.version>4.1.106.Final</netty.version> <maven-source-plugin.version>3.3.1</maven-source-plugin.version>
<gson.version>2.10.1</gson.version> <maven-javadoc-plugin.version>3.11.1</maven-javadoc-plugin.version>
<maven-gpg-plugin.version>3.2.7</maven-gpg-plugin.version>
<spring.version>5.3.31</spring.version> <!-- base -->
<slf4j-api.version>2.0.16</slf4j-api.version>
<junit-jupiter.version>5.11.3</junit-jupiter.version>
<javax.annotation-api.version>1.3.2</javax.annotation-api.version>
<!-- net -->
<netty.version>4.1.115.Final</netty.version>
<gson.version>2.11.0</gson.version>
<!-- spring -->
<spring.version>5.3.39</spring.version>
<spring-boot.version>2.7.18</spring-boot.version> <spring-boot.version>2.7.18</spring-boot.version>
<!-- db -->
<mybatis-spring-boot-starter.version>2.3.2</mybatis-spring-boot-starter.version> <mybatis-spring-boot-starter.version>2.3.2</mybatis-spring-boot-starter.version>
<mysql-connector-j.version>8.3.0</mysql-connector-j.version> <mysql-connector-j.version>9.1.0</mysql-connector-j.version>
<!-- dynamic language -->
<slf4j-api.version>2.0.11</slf4j-api.version> <groovy.version>4.0.24</groovy.version>
<junit-jupiter.version>5.10.1</junit-jupiter.version>
<javax.annotation-api.version>1.3.2</javax.annotation-api.version>
<groovy.version>4.0.18</groovy.version>
<maven-source-plugin.version>3.3.0</maven-source-plugin.version>
<maven-javadoc-plugin.version>3.6.3</maven-javadoc-plugin.version>
<maven-gpg-plugin.version>3.1.0</maven-gpg-plugin.version>
</properties> </properties>
<build> <build>
@ -76,6 +77,12 @@
<profile> <profile>
<id>release</id> <id>release</id>
<activation>
<activeByDefault>true</activeByDefault>
</activation>
<modules>
<module>xxl-job-core</module>
</modules>
<build> <build>
<plugins> <plugins>
<!-- Source --> <!-- Source -->

@ -4,11 +4,15 @@
<parent> <parent>
<groupId>com.xuxueli</groupId> <groupId>com.xuxueli</groupId>
<artifactId>xxl-job</artifactId> <artifactId>xxl-job</artifactId>
<version>2.4.1-SNAPSHOT</version> <version>2.5.0-SNAPSHOT</version>
</parent> </parent>
<artifactId>xxl-job-admin</artifactId> <artifactId>xxl-job-admin</artifactId>
<packaging>jar</packaging> <packaging>jar</packaging>
<properties>
<maven.deploy.skip>true</maven.deploy.skip>
</properties>
<dependencyManagement> <dependencyManagement>
<dependencies> <dependencies>
<dependency> <dependency>
@ -89,13 +93,14 @@
</execution> </execution>
</executions> </executions>
</plugin> </plugin>
<!-- docker --> <!-- docker -->
<plugin> <!--<plugin>
<groupId>com.spotify</groupId> <groupId>com.spotify</groupId>
<artifactId>docker-maven-plugin</artifactId> <artifactId>docker-maven-plugin</artifactId>
<version>0.4.13</version> <version>0.4.13</version>
<configuration> <configuration>
<!-- made of '[a-z0-9-_.]' --> &lt;!&ndash; made of '[a-z0-9-_.]' &ndash;&gt;
<imageName>${project.artifactId}:${project.version}</imageName> <imageName>${project.artifactId}:${project.version}</imageName>
<dockerDirectory>${project.basedir}</dockerDirectory> <dockerDirectory>${project.basedir}</dockerDirectory>
<resources> <resources>
@ -106,7 +111,8 @@
</resource> </resource>
</resources> </resources>
</configuration> </configuration>
</plugin> </plugin>-->
</plugins> </plugins>
</build> </build>

@ -1,7 +1,7 @@
package com.xxl.job.admin.controller; package com.xxl.job.admin.controller;
import com.xxl.job.admin.controller.annotation.PermissionLimit; import com.xxl.job.admin.controller.annotation.PermissionLimit;
import com.xxl.job.admin.service.LoginService; import com.xxl.job.admin.service.impl.LoginService;
import com.xxl.job.admin.service.XxlJobService; import com.xxl.job.admin.service.XxlJobService;
import com.xxl.job.core.biz.model.ReturnT; import com.xxl.job.core.biz.model.ReturnT;
import org.springframework.beans.propertyeditors.CustomDateEditor; import org.springframework.beans.propertyeditors.CustomDateEditor;

@ -1,5 +1,6 @@
package com.xxl.job.admin.controller; package com.xxl.job.admin.controller;
import com.xxl.job.admin.controller.interceptor.PermissionInterceptor;
import com.xxl.job.admin.core.model.XxlJobInfo; import com.xxl.job.admin.core.model.XxlJobInfo;
import com.xxl.job.admin.core.model.XxlJobLogGlue; import com.xxl.job.admin.core.model.XxlJobLogGlue;
import com.xxl.job.admin.core.util.I18nUtil; import com.xxl.job.admin.core.util.I18nUtil;
@ -43,7 +44,7 @@ public class JobCodeController {
} }
// valid permission // valid permission
JobInfoController.validPermission(request, jobInfo.getJobGroup()); PermissionInterceptor.validJobGroupPermission(request, jobInfo.getJobGroup());
// Glue类型-字典 // Glue类型-字典
model.addAttribute("GlueTypeEnum", GlueTypeEnum.values()); model.addAttribute("GlueTypeEnum", GlueTypeEnum.values());
@ -55,7 +56,7 @@ public class JobCodeController {
@RequestMapping("/save") @RequestMapping("/save")
@ResponseBody @ResponseBody
public ReturnT<String> save(Model model, int id, String glueSource, String glueRemark) { public ReturnT<String> save(HttpServletRequest request, int id, String glueSource, String glueRemark) {
// valid // valid
if (glueRemark==null) { if (glueRemark==null) {
return new ReturnT<String>(500, (I18nUtil.getString("system_please_input") + I18nUtil.getString("jobinfo_glue_remark")) ); return new ReturnT<String>(500, (I18nUtil.getString("system_please_input") + I18nUtil.getString("jobinfo_glue_remark")) );
@ -63,23 +64,26 @@ public class JobCodeController {
if (glueRemark.length()<4 || glueRemark.length()>100) { if (glueRemark.length()<4 || glueRemark.length()>100) {
return new ReturnT<String>(500, I18nUtil.getString("jobinfo_glue_remark_limit")); return new ReturnT<String>(500, I18nUtil.getString("jobinfo_glue_remark_limit"));
} }
XxlJobInfo exists_jobInfo = xxlJobInfoDao.loadById(id); XxlJobInfo existsJobInfo = xxlJobInfoDao.loadById(id);
if (exists_jobInfo == null) { if (existsJobInfo == null) {
return new ReturnT<String>(500, I18nUtil.getString("jobinfo_glue_jobid_unvalid")); return new ReturnT<String>(500, I18nUtil.getString("jobinfo_glue_jobid_unvalid"));
} }
// valid permission
PermissionInterceptor.validJobGroupPermission(request, existsJobInfo.getJobGroup());
// update new code // update new code
exists_jobInfo.setGlueSource(glueSource); existsJobInfo.setGlueSource(glueSource);
exists_jobInfo.setGlueRemark(glueRemark); existsJobInfo.setGlueRemark(glueRemark);
exists_jobInfo.setGlueUpdatetime(new Date()); existsJobInfo.setGlueUpdatetime(new Date());
exists_jobInfo.setUpdateTime(new Date()); existsJobInfo.setUpdateTime(new Date());
xxlJobInfoDao.update(exists_jobInfo); xxlJobInfoDao.update(existsJobInfo);
// log old code // log old code
XxlJobLogGlue xxlJobLogGlue = new XxlJobLogGlue(); XxlJobLogGlue xxlJobLogGlue = new XxlJobLogGlue();
xxlJobLogGlue.setJobId(exists_jobInfo.getId()); xxlJobLogGlue.setJobId(existsJobInfo.getId());
xxlJobLogGlue.setGlueType(exists_jobInfo.getGlueType()); xxlJobLogGlue.setGlueType(existsJobInfo.getGlueType());
xxlJobLogGlue.setGlueSource(glueSource); xxlJobLogGlue.setGlueSource(glueSource);
xxlJobLogGlue.setGlueRemark(glueRemark); xxlJobLogGlue.setGlueRemark(glueRemark);
@ -88,7 +92,7 @@ public class JobCodeController {
xxlJobLogGlueDao.save(xxlJobLogGlue); xxlJobLogGlueDao.save(xxlJobLogGlue);
// remove code backup more than 30 // remove code backup more than 30
xxlJobLogGlueDao.removeOld(exists_jobInfo.getId(), 30); xxlJobLogGlueDao.removeOld(existsJobInfo.getId(), 30);
return ReturnT.SUCCESS; return ReturnT.SUCCESS;
} }

@ -1,6 +1,6 @@
package com.xxl.job.admin.controller; package com.xxl.job.admin.controller;
import com.xxl.job.admin.core.cron.CronExpression; import com.xxl.job.admin.controller.interceptor.PermissionInterceptor;
import com.xxl.job.admin.core.exception.XxlJobException; import com.xxl.job.admin.core.exception.XxlJobException;
import com.xxl.job.admin.core.model.XxlJobGroup; import com.xxl.job.admin.core.model.XxlJobGroup;
import com.xxl.job.admin.core.model.XxlJobInfo; import com.xxl.job.admin.core.model.XxlJobInfo;
@ -9,11 +9,8 @@ import com.xxl.job.admin.core.route.ExecutorRouteStrategyEnum;
import com.xxl.job.admin.core.scheduler.MisfireStrategyEnum; import com.xxl.job.admin.core.scheduler.MisfireStrategyEnum;
import com.xxl.job.admin.core.scheduler.ScheduleTypeEnum; import com.xxl.job.admin.core.scheduler.ScheduleTypeEnum;
import com.xxl.job.admin.core.thread.JobScheduleHelper; import com.xxl.job.admin.core.thread.JobScheduleHelper;
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.admin.core.util.I18nUtil;
import com.xxl.job.admin.dao.XxlJobGroupDao; 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.admin.service.XxlJobService;
import com.xxl.job.core.biz.model.ReturnT; import com.xxl.job.core.biz.model.ReturnT;
import com.xxl.job.core.enums.ExecutorBlockStrategyEnum; import com.xxl.job.core.enums.ExecutorBlockStrategyEnum;
@ -29,7 +26,6 @@ import org.springframework.web.bind.annotation.ResponseBody;
import javax.annotation.Resource; import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletRequest;
import java.text.ParseException;
import java.util.*; import java.util.*;
/** /**
@ -60,7 +56,7 @@ public class JobInfoController {
List<XxlJobGroup> jobGroupList_all = xxlJobGroupDao.findAll(); List<XxlJobGroup> jobGroupList_all = xxlJobGroupDao.findAll();
// filter group // filter group
List<XxlJobGroup> jobGroupList = filterJobGroupByRole(request, jobGroupList_all); List<XxlJobGroup> jobGroupList = PermissionInterceptor.filterJobGroupByRole(request, jobGroupList_all);
if (jobGroupList==null || jobGroupList.size()==0) { if (jobGroupList==null || jobGroupList.size()==0) {
throw new XxlJobException(I18nUtil.getString("jobgroup_empty")); throw new XxlJobException(I18nUtil.getString("jobgroup_empty"));
} }
@ -71,33 +67,6 @@ public class JobInfoController {
return "jobinfo/jobinfo.index"; 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") @RequestMapping("/pageList")
@ResponseBody @ResponseBody
public Map<String, Object> pageList(@RequestParam(required = false, defaultValue = "0") int start, public Map<String, Object> pageList(@RequestParam(required = false, defaultValue = "0") int start,
@ -109,14 +78,24 @@ public class JobInfoController {
@RequestMapping("/add") @RequestMapping("/add")
@ResponseBody @ResponseBody
public ReturnT<String> add(XxlJobInfo jobInfo) { public ReturnT<String> add(HttpServletRequest request, XxlJobInfo jobInfo) {
return xxlJobService.add(jobInfo); // valid permission
PermissionInterceptor.validJobGroupPermission(request, jobInfo.getJobGroup());
// opt
XxlJobUser loginUser = PermissionInterceptor.getLoginUser(request);
return xxlJobService.add(jobInfo, loginUser);
} }
@RequestMapping("/update") @RequestMapping("/update")
@ResponseBody @ResponseBody
public ReturnT<String> update(XxlJobInfo jobInfo) { public ReturnT<String> update(HttpServletRequest request, XxlJobInfo jobInfo) {
return xxlJobService.update(jobInfo); // valid permission
PermissionInterceptor.validJobGroupPermission(request, jobInfo.getJobGroup());
// opt
XxlJobUser loginUser = PermissionInterceptor.getLoginUser(request);
return xxlJobService.update(jobInfo, loginUser);
} }
@RequestMapping("/remove") @RequestMapping("/remove")
@ -139,15 +118,11 @@ public class JobInfoController {
@RequestMapping("/trigger") @RequestMapping("/trigger")
@ResponseBody @ResponseBody
//@PermissionLimit(limit = false) public ReturnT<String> triggerJob(HttpServletRequest request, int id, String executorParam, String addressList) {
public ReturnT<String> triggerJob(int id, String executorParam, String addressList) { // login user
// force cover job param XxlJobUser loginUser = PermissionInterceptor.getLoginUser(request);
if (executorParam == null) { // trigger
executorParam = ""; return xxlJobService.trigger(loginUser, id, executorParam, addressList);
}
JobTriggerPoolHelper.trigger(id, TriggerTypeEnum.MANUAL, -1, null, executorParam, addressList);
return ReturnT.SUCCESS;
} }
@RequestMapping("/nextTriggerTime") @RequestMapping("/nextTriggerTime")
@ -170,7 +145,7 @@ public class JobInfoController {
} }
} }
} catch (Exception e) { } catch (Exception e) {
logger.error(e.getMessage(), e); logger.error("nextTriggerTime error. scheduleType = {}, scheduleConf= {}", scheduleType, scheduleConf, e);
return new ReturnT<List<String>>(ReturnT.FAIL_CODE, (I18nUtil.getString("schedule_type")+I18nUtil.getString("system_unvalid")) + e.getMessage()); return new ReturnT<List<String>>(ReturnT.FAIL_CODE, (I18nUtil.getString("schedule_type")+I18nUtil.getString("system_unvalid")) + e.getMessage());
} }
return new ReturnT<List<String>>(result); return new ReturnT<List<String>>(result);

@ -1,5 +1,6 @@
package com.xxl.job.admin.controller; package com.xxl.job.admin.controller;
import com.xxl.job.admin.controller.interceptor.PermissionInterceptor;
import com.xxl.job.admin.core.complete.XxlJobCompleter; import com.xxl.job.admin.core.complete.XxlJobCompleter;
import com.xxl.job.admin.core.exception.XxlJobException; import com.xxl.job.admin.core.exception.XxlJobException;
import com.xxl.job.admin.core.model.XxlJobGroup; import com.xxl.job.admin.core.model.XxlJobGroup;
@ -56,7 +57,7 @@ public class JobLogController {
List<XxlJobGroup> jobGroupList_all = xxlJobGroupDao.findAll(); List<XxlJobGroup> jobGroupList_all = xxlJobGroupDao.findAll();
// filter group // filter group
List<XxlJobGroup> jobGroupList = JobInfoController.filterJobGroupByRole(request, jobGroupList_all); List<XxlJobGroup> jobGroupList = PermissionInterceptor.filterJobGroupByRole(request, jobGroupList_all);
if (jobGroupList==null || jobGroupList.size()==0) { if (jobGroupList==null || jobGroupList.size()==0) {
throw new XxlJobException(I18nUtil.getString("jobgroup_empty")); throw new XxlJobException(I18nUtil.getString("jobgroup_empty"));
} }
@ -73,7 +74,7 @@ public class JobLogController {
model.addAttribute("jobInfo", jobInfo); model.addAttribute("jobInfo", jobInfo);
// valid permission // valid permission
JobInfoController.validPermission(request, jobInfo.getJobGroup()); PermissionInterceptor.validJobGroupPermission(request, jobInfo.getJobGroup());
} }
return "joblog/joblog.index"; return "joblog/joblog.index";
@ -94,7 +95,7 @@ public class JobLogController {
int jobGroup, int jobId, int logStatus, String filterTime) { int jobGroup, int jobId, int logStatus, String filterTime) {
// valid permission // valid permission
JobInfoController.validPermission(request, jobGroup); // 仅管理员支持查询全部;普通用户仅支持查询有权限的 jobGroup PermissionInterceptor.validJobGroupPermission(request, jobGroup); // 仅管理员支持查询全部;普通用户仅支持查询有权限的 jobGroup
// parse param // parse param
Date triggerTimeStart = null; Date triggerTimeStart = null;
@ -206,8 +207,11 @@ public class JobLogController {
@RequestMapping("/clearLog") @RequestMapping("/clearLog")
@ResponseBody @ResponseBody
public ReturnT<String> clearLog(int jobGroup, int jobId, int type){ public ReturnT<String> clearLog(HttpServletRequest request, int jobGroup, int jobId, int type){
// valid permission
PermissionInterceptor.validJobGroupPermission(request, jobGroup);
// opt
Date clearBeforeTime = null; Date clearBeforeTime = null;
int clearBeforeNum = 0; int clearBeforeNum = 0;
if (type == 1) { if (type == 1) {

@ -1,12 +1,12 @@
package com.xxl.job.admin.controller; package com.xxl.job.admin.controller;
import com.xxl.job.admin.controller.annotation.PermissionLimit; import com.xxl.job.admin.controller.annotation.PermissionLimit;
import com.xxl.job.admin.controller.interceptor.PermissionInterceptor;
import com.xxl.job.admin.core.model.XxlJobGroup; import com.xxl.job.admin.core.model.XxlJobGroup;
import com.xxl.job.admin.core.model.XxlJobUser; import com.xxl.job.admin.core.model.XxlJobUser;
import com.xxl.job.admin.core.util.I18nUtil; import com.xxl.job.admin.core.util.I18nUtil;
import com.xxl.job.admin.dao.XxlJobGroupDao; import com.xxl.job.admin.dao.XxlJobGroupDao;
import com.xxl.job.admin.dao.XxlJobUserDao; import com.xxl.job.admin.dao.XxlJobUserDao;
import com.xxl.job.admin.service.LoginService;
import com.xxl.job.core.biz.model.ReturnT; import com.xxl.job.core.biz.model.ReturnT;
import org.springframework.stereotype.Controller; import org.springframework.stereotype.Controller;
import org.springframework.ui.Model; import org.springframework.ui.Model;
@ -27,7 +27,7 @@ import java.util.Map;
*/ */
@Controller @Controller
@RequestMapping("/user") @RequestMapping("/user")
public class UserController { public class JobUserController {
@Resource @Resource
private XxlJobUserDao xxlJobUserDao; private XxlJobUserDao xxlJobUserDao;
@ -112,7 +112,7 @@ public class UserController {
public ReturnT<String> update(HttpServletRequest request, XxlJobUser xxlJobUser) { public ReturnT<String> update(HttpServletRequest request, XxlJobUser xxlJobUser) {
// avoid opt login seft // avoid opt login seft
XxlJobUser loginUser = (XxlJobUser) request.getAttribute(LoginService.LOGIN_IDENTITY_KEY); XxlJobUser loginUser = PermissionInterceptor.getLoginUser(request);
if (loginUser.getUsername().equals(xxlJobUser.getUsername())) { if (loginUser.getUsername().equals(xxlJobUser.getUsername())) {
return new ReturnT<String>(ReturnT.FAIL.getCode(), I18nUtil.getString("user_update_loginuser_limit")); return new ReturnT<String>(ReturnT.FAIL.getCode(), I18nUtil.getString("user_update_loginuser_limit"));
} }
@ -140,7 +140,7 @@ public class UserController {
public ReturnT<String> remove(HttpServletRequest request, int id) { public ReturnT<String> remove(HttpServletRequest request, int id) {
// avoid opt login seft // avoid opt login seft
XxlJobUser loginUser = (XxlJobUser) request.getAttribute(LoginService.LOGIN_IDENTITY_KEY); XxlJobUser loginUser = PermissionInterceptor.getLoginUser(request);
if (loginUser.getId() == id) { if (loginUser.getId() == id) {
return new ReturnT<String>(ReturnT.FAIL.getCode(), I18nUtil.getString("user_update_loginuser_limit")); return new ReturnT<String>(ReturnT.FAIL.getCode(), I18nUtil.getString("user_update_loginuser_limit"));
} }
@ -151,11 +151,14 @@ public class UserController {
@RequestMapping("/updatePwd") @RequestMapping("/updatePwd")
@ResponseBody @ResponseBody
public ReturnT<String> updatePwd(HttpServletRequest request, String password){ public ReturnT<String> updatePwd(HttpServletRequest request, String password, String oldPassword){
// valid password // valid
if (oldPassword==null || oldPassword.trim().length()==0){
return new ReturnT<String>(ReturnT.FAIL.getCode(), I18nUtil.getString("system_please_input") + I18nUtil.getString("change_pwd_field_oldpwd"));
}
if (password==null || password.trim().length()==0){ if (password==null || password.trim().length()==0){
return new ReturnT<String>(ReturnT.FAIL.getCode(), "密码不可为空"); return new ReturnT<String>(ReturnT.FAIL.getCode(), I18nUtil.getString("system_please_input") + I18nUtil.getString("change_pwd_field_oldpwd"));
} }
password = password.trim(); password = password.trim();
if (!(password.length()>=4 && password.length()<=20)) { if (!(password.length()>=4 && password.length()<=20)) {
@ -163,13 +166,17 @@ public class UserController {
} }
// md5 password // md5 password
String md5OldPassword = DigestUtils.md5DigestAsHex(oldPassword.getBytes());
String md5Password = DigestUtils.md5DigestAsHex(password.getBytes()); String md5Password = DigestUtils.md5DigestAsHex(password.getBytes());
// update pwd // valid old pwd
XxlJobUser loginUser = (XxlJobUser) request.getAttribute(LoginService.LOGIN_IDENTITY_KEY); XxlJobUser loginUser = PermissionInterceptor.getLoginUser(request);
// do write
XxlJobUser existUser = xxlJobUserDao.loadByUserName(loginUser.getUsername()); XxlJobUser existUser = xxlJobUserDao.loadByUserName(loginUser.getUsername());
if (!md5OldPassword.equals(existUser.getPassword())) {
return new ReturnT<String>(ReturnT.FAIL.getCode(), I18nUtil.getString("change_pwd_field_oldpwd") + I18nUtil.getString("system_unvalid"));
}
// write new
existUser.setPassword(md5Password); existUser.setPassword(md5Password);
xxlJobUserDao.update(existUser); xxlJobUserDao.update(existUser);

@ -1,9 +1,10 @@
package com.xxl.job.admin.controller.interceptor; package com.xxl.job.admin.controller.interceptor;
import com.xxl.job.admin.controller.annotation.PermissionLimit; 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.model.XxlJobUser;
import com.xxl.job.admin.core.util.I18nUtil; import com.xxl.job.admin.core.util.I18nUtil;
import com.xxl.job.admin.service.LoginService; import com.xxl.job.admin.service.impl.LoginService;
import org.springframework.stereotype.Component; import org.springframework.stereotype.Component;
import org.springframework.web.method.HandlerMethod; import org.springframework.web.method.HandlerMethod;
import org.springframework.web.servlet.AsyncHandlerInterceptor; import org.springframework.web.servlet.AsyncHandlerInterceptor;
@ -11,6 +12,9 @@ import org.springframework.web.servlet.AsyncHandlerInterceptor;
import javax.annotation.Resource; import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse; import javax.servlet.http.HttpServletResponse;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
/** /**
* *
@ -50,10 +54,66 @@ public class PermissionInterceptor implements AsyncHandlerInterceptor {
if (needAdminuser && loginUser.getRole()!=1) { if (needAdminuser && loginUser.getRole()!=1) {
throw new RuntimeException(I18nUtil.getString("system_permission_limit")); throw new RuntimeException(I18nUtil.getString("system_permission_limit"));
} }
request.setAttribute(LoginService.LOGIN_IDENTITY_KEY, loginUser); request.setAttribute(LoginService.LOGIN_IDENTITY_KEY, loginUser); // set loginUser, with request
} }
return true; // proceed with the next interceptor return true; // proceed with the next interceptor
} }
// -------------------- permission tool --------------------
/**
* get loginUser
*
* @param request
* @return
*/
public static XxlJobUser getLoginUser(HttpServletRequest request){
XxlJobUser loginUser = (XxlJobUser) request.getAttribute(LoginService.LOGIN_IDENTITY_KEY); // get loginUser, with request
return loginUser;
}
/**
* valid permission by JobGroup
*
* @param request
* @param jobGroup
*/
public static void validJobGroupPermission(HttpServletRequest request, int jobGroup) {
XxlJobUser loginUser = getLoginUser(request);
if (!loginUser.validPermission(jobGroup)) {
throw new RuntimeException(I18nUtil.getString("system_permission_limit") + "[username="+ loginUser.getUsername() +"]");
}
}
/**
* filter XxlJobGroup by role
*
* @param request
* @param jobGroupList_all
* @return
*/
public static List<XxlJobGroup> filterJobGroupByRole(HttpServletRequest request, List<XxlJobGroup> jobGroupList_all){
List<XxlJobGroup> jobGroupList = new ArrayList<>();
if (jobGroupList_all!=null && jobGroupList_all.size()>0) {
XxlJobUser loginUser = PermissionInterceptor.getLoginUser(request);
if (loginUser.getRole() == 1) {
jobGroupList = jobGroupList_all;
} else {
List<String> groupIdStrs = new ArrayList<>();
if (loginUser.getPermission()!=null && loginUser.getPermission().trim().length()>0) {
groupIdStrs = Arrays.asList(loginUser.getPermission().trim().split(","));
}
for (XxlJobGroup groupItem:jobGroupList_all) {
if (groupIdStrs.contains(String.valueOf(groupItem.getId()))) {
jobGroupList.add(groupItem);
}
}
}
}
return jobGroupList;
}
} }

@ -55,6 +55,10 @@ public class XxlJobCompleter {
String[] childJobIds = xxlJobInfo.getChildJobId().split(","); String[] childJobIds = xxlJobInfo.getChildJobId().split(",");
for (int i = 0; i < childJobIds.length; i++) { 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; int childJobId = (childJobIds[i]!=null && childJobIds[i].trim().length()>0 && isNumeric(childJobIds[i]))?Integer.valueOf(childJobIds[i]):-1;
if (childJobId == xxlJobLog.getJobId()) {
logger.info("jobid {} is self, ignore it.", childJobId);
continue;
}
if (childJobId > 0) { if (childJobId > 0) {
JobTriggerPoolHelper.trigger(childJobId, TriggerTypeEnum.PARENT, -1, null, null, null); JobTriggerPoolHelper.trigger(childJobId, TriggerTypeEnum.PARENT, -1, null, null, null);

@ -363,9 +363,9 @@ public final class CronExpression implements Serializable, Cloneable {
// the second immediately following it. // the second immediately following it.
while (difference == 1000) { while (difference == 1000) {
newDate = getTimeAfter(lastDate); newDate = getTimeAfter(lastDate);
if(newDate == null) if(newDate == null) {
break; break;
}
difference = newDate.getTime() - lastDate.getTime(); difference = newDate.getTime() - lastDate.getTime();
if (difference == 1000) { if (difference == 1000) {
@ -668,8 +668,9 @@ public final class CronExpression implements Serializable, Cloneable {
if(c == '-') { if(c == '-') {
ValueSet vs = getValue(0, s, i+1); ValueSet vs = getValue(0, s, i+1);
lastdayOffset = vs.value; lastdayOffset = vs.value;
if(lastdayOffset > 30) if(lastdayOffset > 30) {
throw new ParseException("Offset from last day must be <= 30", i+1); throw new ParseException("Offset from last day must be <= 30", i + 1);
}
i = vs.pos; i = vs.pos;
} }
if(s.length() > i) { if(s.length() > i) {
@ -732,8 +733,9 @@ public final class CronExpression implements Serializable, Cloneable {
if (c == 'L') { if (c == 'L') {
if (type == DAY_OF_WEEK) { if (type == DAY_OF_WEEK) {
if(val < 1 || val > 7) if(val < 1 || val > 7) {
throw new ParseException("Day-of-Week values must be between 1 and 7", -1); throw new ParseException("Day-of-Week values must be between 1 and 7", -1);
}
lastdayOfWeek = true; lastdayOfWeek = true;
} else { } else {
throw new ParseException("'L' option is not valid here. (pos=" + i + ")", i); throw new ParseException("'L' option is not valid here. (pos=" + i + ")", i);
@ -750,8 +752,9 @@ public final class CronExpression implements Serializable, Cloneable {
} else { } else {
throw new ParseException("'W' option is not valid here. (pos=" + i + ")", i); throw new ParseException("'W' option is not valid here. (pos=" + i + ")", i);
} }
if(val > 31) if(val > 31) {
throw new ParseException("The 'W' option does not make sense with values larger than 31 (max number of days in a month)", i); throw new ParseException("The 'W' option does not make sense with values larger than 31 (max number of days in a month)", i);
}
TreeSet<Integer> set = getSet(type); TreeSet<Integer> set = getSet(type);
set.add(val); set.add(val);
i++; i++;

@ -65,7 +65,7 @@ public class JobCompleteHelper {
// wait for JobTriggerPoolHelper-init // wait for JobTriggerPoolHelper-init
try { try {
TimeUnit.MILLISECONDS.sleep(50); TimeUnit.MILLISECONDS.sleep(50);
} catch (InterruptedException e) { } catch (Throwable e) {
if (!toStop) { if (!toStop) {
logger.error(e.getMessage(), e); logger.error(e.getMessage(), e);
} }
@ -92,7 +92,7 @@ public class JobCompleteHelper {
} }
} }
} catch (Exception e) { } catch (Throwable e) {
if (!toStop) { if (!toStop) {
logger.error(">>>>>>>>>>> xxl-job, job fail monitor thread error:{}", e); logger.error(">>>>>>>>>>> xxl-job, job fail monitor thread error:{}", e);
} }
@ -100,7 +100,7 @@ public class JobCompleteHelper {
try { try {
TimeUnit.SECONDS.sleep(60); TimeUnit.SECONDS.sleep(60);
} catch (Exception e) { } catch (Throwable e) {
if (!toStop) { if (!toStop) {
logger.error(e.getMessage(), e); logger.error(e.getMessage(), e);
} }
@ -127,7 +127,7 @@ public class JobCompleteHelper {
monitorThread.interrupt(); monitorThread.interrupt();
try { try {
monitorThread.join(); monitorThread.join();
} catch (InterruptedException e) { } catch (Throwable e) {
logger.error(e.getMessage(), e); logger.error(e.getMessage(), e);
} }
} }

@ -71,7 +71,7 @@ public class JobFailMonitorHelper {
} }
} }
} catch (Exception e) { } catch (Throwable e) {
if (!toStop) { if (!toStop) {
logger.error(">>>>>>>>>>> xxl-job, job fail monitor thread error:{}", e); logger.error(">>>>>>>>>>> xxl-job, job fail monitor thread error:{}", e);
} }
@ -79,7 +79,7 @@ public class JobFailMonitorHelper {
try { try {
TimeUnit.SECONDS.sleep(10); TimeUnit.SECONDS.sleep(10);
} catch (Exception e) { } catch (Throwable e) {
if (!toStop) { if (!toStop) {
logger.error(e.getMessage(), e); logger.error(e.getMessage(), e);
} }
@ -102,7 +102,7 @@ public class JobFailMonitorHelper {
monitorThread.interrupt(); monitorThread.interrupt();
try { try {
monitorThread.join(); monitorThread.join();
} catch (InterruptedException e) { } catch (Throwable e) {
logger.error(e.getMessage(), e); logger.error(e.getMessage(), e);
} }
} }

@ -87,7 +87,7 @@ public class JobLogReportHelper {
} }
} }
} catch (Exception e) { } catch (Throwable e) {
if (!toStop) { if (!toStop) {
logger.error(">>>>>>>>>>> xxl-job, job log report thread error:{}", e); logger.error(">>>>>>>>>>> xxl-job, job log report thread error:{}", e);
} }
@ -121,7 +121,7 @@ public class JobLogReportHelper {
try { try {
TimeUnit.MINUTES.sleep(1); TimeUnit.MINUTES.sleep(1);
} catch (Exception e) { } catch (Throwable e) {
if (!toStop) { if (!toStop) {
logger.error(e.getMessage(), e); logger.error(e.getMessage(), e);
} }
@ -144,7 +144,7 @@ public class JobLogReportHelper {
logrThread.interrupt(); logrThread.interrupt();
try { try {
logrThread.join(); logrThread.join();
} catch (InterruptedException e) { } catch (Throwable e) {
logger.error(e.getMessage(), e); logger.error(e.getMessage(), e);
} }
} }

@ -107,14 +107,14 @@ public class JobRegistryHelper {
XxlJobAdminConfig.getAdminConfig().getXxlJobGroupDao().update(group); XxlJobAdminConfig.getAdminConfig().getXxlJobGroupDao().update(group);
} }
} }
} catch (Exception e) { } catch (Throwable e) {
if (!toStop) { if (!toStop) {
logger.error(">>>>>>>>>>> xxl-job, job registry monitor thread error:{}", e); logger.error(">>>>>>>>>>> xxl-job, job registry monitor thread error:{}", e);
} }
} }
try { try {
TimeUnit.SECONDS.sleep(RegistryConfig.BEAT_TIMEOUT); TimeUnit.SECONDS.sleep(RegistryConfig.BEAT_TIMEOUT);
} catch (InterruptedException e) { } catch (Throwable e) {
if (!toStop) { if (!toStop) {
logger.error(">>>>>>>>>>> xxl-job, job registry monitor thread error:{}", e); logger.error(">>>>>>>>>>> xxl-job, job registry monitor thread error:{}", e);
} }
@ -138,7 +138,7 @@ public class JobRegistryHelper {
registryMonitorThread.interrupt(); registryMonitorThread.interrupt();
try { try {
registryMonitorThread.join(); registryMonitorThread.join();
} catch (InterruptedException e) { } catch (Throwable e) {
logger.error(e.getMessage(), e); logger.error(e.getMessage(), e);
} }
} }
@ -159,13 +159,19 @@ public class JobRegistryHelper {
registryOrRemoveThreadPool.execute(new Runnable() { registryOrRemoveThreadPool.execute(new Runnable() {
@Override @Override
public void run() { public void run() {
int ret = XxlJobAdminConfig.getAdminConfig().getXxlJobRegistryDao().registryUpdate(registryParam.getRegistryGroup(), registryParam.getRegistryKey(), registryParam.getRegistryValue(), new Date()); // 0-fail; 1-save suc; 2-update suc;
int ret = XxlJobAdminConfig.getAdminConfig().getXxlJobRegistryDao().registrySaveOrUpdate(registryParam.getRegistryGroup(), registryParam.getRegistryKey(), registryParam.getRegistryValue(), new Date());
if (ret == 1) {
// fresh (add)
freshGroupRegistryInfo(registryParam);
}
/*int ret = XxlJobAdminConfig.getAdminConfig().getXxlJobRegistryDao().registryUpdate(registryParam.getRegistryGroup(), registryParam.getRegistryKey(), registryParam.getRegistryValue(), new Date());
if (ret < 1) { if (ret < 1) {
XxlJobAdminConfig.getAdminConfig().getXxlJobRegistryDao().registrySave(registryParam.getRegistryGroup(), registryParam.getRegistryKey(), registryParam.getRegistryValue(), new Date()); XxlJobAdminConfig.getAdminConfig().getXxlJobRegistryDao().registrySave(registryParam.getRegistryGroup(), registryParam.getRegistryKey(), registryParam.getRegistryValue(), new Date());
// fresh // fresh
freshGroupRegistryInfo(registryParam); freshGroupRegistryInfo(registryParam);
} }*/
} }
}); });
@ -187,7 +193,7 @@ public class JobRegistryHelper {
public void run() { public void run() {
int ret = XxlJobAdminConfig.getAdminConfig().getXxlJobRegistryDao().registryDelete(registryParam.getRegistryGroup(), registryParam.getRegistryKey(), registryParam.getRegistryValue()); int ret = XxlJobAdminConfig.getAdminConfig().getXxlJobRegistryDao().registryDelete(registryParam.getRegistryGroup(), registryParam.getRegistryKey(), registryParam.getRegistryValue());
if (ret > 0) { if (ret > 0) {
// fresh // fresh (delete)
freshGroupRegistryInfo(registryParam); freshGroupRegistryInfo(registryParam);
} }
} }

@ -11,7 +11,6 @@ import org.slf4j.LoggerFactory;
import java.sql.Connection; import java.sql.Connection;
import java.sql.PreparedStatement; import java.sql.PreparedStatement;
import java.sql.SQLException;
import java.util.*; import java.util.*;
import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeUnit;
@ -44,7 +43,7 @@ public class JobScheduleHelper {
try { try {
TimeUnit.MILLISECONDS.sleep(5000 - System.currentTimeMillis()%1000 ); TimeUnit.MILLISECONDS.sleep(5000 - System.currentTimeMillis()%1000 );
} catch (InterruptedException e) { } catch (Throwable e) {
if (!scheduleThreadToStop) { if (!scheduleThreadToStop) {
logger.error(e.getMessage(), e); logger.error(e.getMessage(), e);
} }
@ -150,7 +149,7 @@ public class JobScheduleHelper {
// tx stop // tx stop
} catch (Exception e) { } catch (Throwable e) {
if (!scheduleThreadToStop) { if (!scheduleThreadToStop) {
logger.error(">>>>>>>>>>> xxl-job, JobScheduleHelper#scheduleThread error:{}", e); logger.error(">>>>>>>>>>> xxl-job, JobScheduleHelper#scheduleThread error:{}", e);
} }
@ -160,21 +159,21 @@ public class JobScheduleHelper {
if (conn != null) { if (conn != null) {
try { try {
conn.commit(); conn.commit();
} catch (SQLException e) { } catch (Throwable e) {
if (!scheduleThreadToStop) { if (!scheduleThreadToStop) {
logger.error(e.getMessage(), e); logger.error(e.getMessage(), e);
} }
} }
try { try {
conn.setAutoCommit(connAutoCommit); conn.setAutoCommit(connAutoCommit);
} catch (SQLException e) { } catch (Throwable e) {
if (!scheduleThreadToStop) { if (!scheduleThreadToStop) {
logger.error(e.getMessage(), e); logger.error(e.getMessage(), e);
} }
} }
try { try {
conn.close(); conn.close();
} catch (SQLException e) { } catch (Throwable e) {
if (!scheduleThreadToStop) { if (!scheduleThreadToStop) {
logger.error(e.getMessage(), e); logger.error(e.getMessage(), e);
} }
@ -185,7 +184,7 @@ public class JobScheduleHelper {
if (null != preparedStatement) { if (null != preparedStatement) {
try { try {
preparedStatement.close(); preparedStatement.close();
} catch (SQLException e) { } catch (Throwable e) {
if (!scheduleThreadToStop) { if (!scheduleThreadToStop) {
logger.error(e.getMessage(), e); logger.error(e.getMessage(), e);
} }
@ -200,7 +199,7 @@ public class JobScheduleHelper {
try { try {
// pre-read period: success > scan each second; fail > skip this period; // pre-read period: success > scan each second; fail > skip this period;
TimeUnit.MILLISECONDS.sleep((preReadSuc?1000:PRE_READ_MS) - System.currentTimeMillis()%1000); TimeUnit.MILLISECONDS.sleep((preReadSuc?1000:PRE_READ_MS) - System.currentTimeMillis()%1000);
} catch (InterruptedException e) { } catch (Throwable e) {
if (!scheduleThreadToStop) { if (!scheduleThreadToStop) {
logger.error(e.getMessage(), e); logger.error(e.getMessage(), e);
} }
@ -227,7 +226,7 @@ public class JobScheduleHelper {
// align second // align second
try { try {
TimeUnit.MILLISECONDS.sleep(1000 - System.currentTimeMillis() % 1000); TimeUnit.MILLISECONDS.sleep(1000 - System.currentTimeMillis() % 1000);
} catch (InterruptedException e) { } catch (Throwable e) {
if (!ringThreadToStop) { if (!ringThreadToStop) {
logger.error(e.getMessage(), e); logger.error(e.getMessage(), e);
} }
@ -255,7 +254,7 @@ public class JobScheduleHelper {
// clear // clear
ringItemData.clear(); ringItemData.clear();
} }
} catch (Exception e) { } catch (Throwable e) {
if (!ringThreadToStop) { if (!ringThreadToStop) {
logger.error(">>>>>>>>>>> xxl-job, JobScheduleHelper#ringThread error:{}", e); logger.error(">>>>>>>>>>> xxl-job, JobScheduleHelper#ringThread error:{}", e);
} }
@ -269,17 +268,28 @@ public class JobScheduleHelper {
ringThread.start(); ringThread.start();
} }
private void refreshNextValidTime(XxlJobInfo jobInfo, Date fromTime) throws Exception { private void refreshNextValidTime(XxlJobInfo jobInfo, Date fromTime) {
Date nextValidTime = generateNextValidTime(jobInfo, fromTime); try {
if (nextValidTime != null) { Date nextValidTime = generateNextValidTime(jobInfo, fromTime);
jobInfo.setTriggerLastTime(jobInfo.getTriggerNextTime()); if (nextValidTime != null) {
jobInfo.setTriggerNextTime(nextValidTime.getTime()); jobInfo.setTriggerLastTime(jobInfo.getTriggerNextTime());
} else { jobInfo.setTriggerNextTime(nextValidTime.getTime());
} else {
// generateNextValidTime fail, stop job
jobInfo.setTriggerStatus(0);
jobInfo.setTriggerLastTime(0);
jobInfo.setTriggerNextTime(0);
logger.error(">>>>>>>>>>> xxl-job, refreshNextValidTime fail for job: jobId={}, scheduleType={}, scheduleConf={}",
jobInfo.getId(), jobInfo.getScheduleType(), jobInfo.getScheduleConf());
}
} catch (Throwable e) {
// generateNextValidTime error, stop job
jobInfo.setTriggerStatus(0); jobInfo.setTriggerStatus(0);
jobInfo.setTriggerLastTime(0); jobInfo.setTriggerLastTime(0);
jobInfo.setTriggerNextTime(0); jobInfo.setTriggerNextTime(0);
logger.warn(">>>>>>>>>>> xxl-job, refreshNextValidTime fail for job: jobId={}, scheduleType={}, scheduleConf={}",
jobInfo.getId(), jobInfo.getScheduleType(), jobInfo.getScheduleConf()); logger.error(">>>>>>>>>>> xxl-job, refreshNextValidTime error for job: jobId={}, scheduleType={}, scheduleConf={}",
jobInfo.getId(), jobInfo.getScheduleType(), jobInfo.getScheduleConf(), e);
} }
} }
@ -301,7 +311,7 @@ public class JobScheduleHelper {
scheduleThreadToStop = true; scheduleThreadToStop = true;
try { try {
TimeUnit.SECONDS.sleep(1); // wait TimeUnit.SECONDS.sleep(1); // wait
} catch (InterruptedException e) { } catch (Throwable e) {
logger.error(e.getMessage(), e); logger.error(e.getMessage(), e);
} }
if (scheduleThread.getState() != Thread.State.TERMINATED){ if (scheduleThread.getState() != Thread.State.TERMINATED){
@ -309,7 +319,7 @@ public class JobScheduleHelper {
scheduleThread.interrupt(); scheduleThread.interrupt();
try { try {
scheduleThread.join(); scheduleThread.join();
} catch (InterruptedException e) { } catch (Throwable e) {
logger.error(e.getMessage(), e); logger.error(e.getMessage(), e);
} }
} }
@ -328,7 +338,7 @@ public class JobScheduleHelper {
if (hasRingData) { if (hasRingData) {
try { try {
TimeUnit.SECONDS.sleep(8); TimeUnit.SECONDS.sleep(8);
} catch (InterruptedException e) { } catch (Throwable e) {
logger.error(e.getMessage(), e); logger.error(e.getMessage(), e);
} }
} }
@ -337,7 +347,7 @@ public class JobScheduleHelper {
ringThreadToStop = true; ringThreadToStop = true;
try { try {
TimeUnit.SECONDS.sleep(1); TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e) { } catch (Throwable e) {
logger.error(e.getMessage(), e); logger.error(e.getMessage(), e);
} }
if (ringThread.getState() != Thread.State.TERMINATED){ if (ringThread.getState() != Thread.State.TERMINATED){
@ -345,7 +355,7 @@ public class JobScheduleHelper {
ringThread.interrupt(); ringThread.interrupt();
try { try {
ringThread.join(); ringThread.join();
} catch (InterruptedException e) { } catch (Throwable e) {
logger.error(e.getMessage(), e); logger.error(e.getMessage(), e);
} }
} }

@ -93,7 +93,7 @@ public class JobTriggerPoolHelper {
try { try {
// do trigger // do trigger
XxlJobTrigger.trigger(jobId, triggerType, failRetryCount, executorShardingParam, executorParam, addressList); XxlJobTrigger.trigger(jobId, triggerType, failRetryCount, executorShardingParam, executorParam, addressList);
} catch (Exception e) { } catch (Throwable e) {
logger.error(e.getMessage(), e); logger.error(e.getMessage(), e);
} finally { } finally {

@ -41,8 +41,21 @@ public interface XxlJobInfoDao {
public int findAllCount(); 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 ); public List<XxlJobInfo> scheduleJobQuery(@Param("maxNextTime") long maxNextTime, @Param("pagesize") int pagesize );
/**
* update schedule job, limit "trigger_status = 1"avoid stopping tasks from being opened
*
* @param xxlJobInfo
* @return
*/
public int scheduleUpdate(XxlJobInfo xxlJobInfo); public int scheduleUpdate(XxlJobInfo xxlJobInfo);

@ -21,7 +21,12 @@ public interface XxlJobRegistryDao {
public List<XxlJobRegistry> findAll(@Param("timeout") int timeout, public List<XxlJobRegistry> findAll(@Param("timeout") int timeout,
@Param("nowTime") Date nowTime); @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("registryKey") String registryKey,
@Param("registryValue") String registryValue, @Param("registryValue") String registryValue,
@Param("updateTime") Date updateTime); @Param("updateTime") Date updateTime);
@ -29,7 +34,7 @@ public interface XxlJobRegistryDao {
public int registrySave(@Param("registryGroup") String registryGroup, public int registrySave(@Param("registryGroup") String registryGroup,
@Param("registryKey") String registryKey, @Param("registryKey") String registryKey,
@Param("registryValue") String registryValue, @Param("registryValue") String registryValue,
@Param("updateTime") Date updateTime); @Param("updateTime") Date updateTime);*/
public int registryDelete(@Param("registryGroup") String registryGroup, public int registryDelete(@Param("registryGroup") String registryGroup,
@Param("registryKey") String registryKey, @Param("registryKey") String registryKey,

@ -2,6 +2,7 @@ package com.xxl.job.admin.service;
import com.xxl.job.admin.core.model.XxlJobInfo; import com.xxl.job.admin.core.model.XxlJobInfo;
import com.xxl.job.admin.core.model.XxlJobUser;
import com.xxl.job.core.biz.model.ReturnT; import com.xxl.job.core.biz.model.ReturnT;
import java.util.Date; import java.util.Date;
@ -33,7 +34,7 @@ public interface XxlJobService {
* @param jobInfo * @param jobInfo
* @return * @return
*/ */
public ReturnT<String> add(XxlJobInfo jobInfo); public ReturnT<String> add(XxlJobInfo jobInfo, XxlJobUser loginUser);
/** /**
* update job * update job
@ -41,7 +42,7 @@ public interface XxlJobService {
* @param jobInfo * @param jobInfo
* @return * @return
*/ */
public ReturnT<String> update(XxlJobInfo jobInfo); public ReturnT<String> update(XxlJobInfo jobInfo, XxlJobUser loginUser);
/** /**
* remove job * remove job
@ -67,6 +68,17 @@ public interface XxlJobService {
*/ */
public ReturnT<String> stop(int id); public ReturnT<String> stop(int id);
/**
* trigger
*
* @param loginUser
* @param jobId
* @param executorParam
* @param addressList
* @return
*/
public ReturnT<String> trigger(XxlJobUser loginUser, int jobId, String executorParam, String addressList);
/** /**
* dashboard info * dashboard info
* *

@ -1,4 +1,4 @@
package com.xxl.job.admin.service; package com.xxl.job.admin.service.impl;
import com.xxl.job.admin.core.model.XxlJobUser; import com.xxl.job.admin.core.model.XxlJobUser;
import com.xxl.job.admin.core.util.CookieUtil; import com.xxl.job.admin.core.util.CookieUtil;
@ -6,7 +6,7 @@ import com.xxl.job.admin.core.util.I18nUtil;
import com.xxl.job.admin.core.util.JacksonUtil; import com.xxl.job.admin.core.util.JacksonUtil;
import com.xxl.job.admin.dao.XxlJobUserDao; import com.xxl.job.admin.dao.XxlJobUserDao;
import com.xxl.job.core.biz.model.ReturnT; import com.xxl.job.core.biz.model.ReturnT;
import org.springframework.context.annotation.Configuration; import org.springframework.stereotype.Service;
import org.springframework.util.DigestUtils; import org.springframework.util.DigestUtils;
import javax.annotation.Resource; import javax.annotation.Resource;
@ -17,7 +17,7 @@ import java.math.BigInteger;
/** /**
* @author xuxueli 2019-05-04 22:13:264 * @author xuxueli 2019-05-04 22:13:264
*/ */
@Configuration @Service
public class LoginService { public class LoginService {
public static final String LOGIN_IDENTITY_KEY = "XXL_JOB_LOGIN_IDENTITY"; public static final String LOGIN_IDENTITY_KEY = "XXL_JOB_LOGIN_IDENTITY";
@ -26,6 +26,8 @@ public class LoginService {
private XxlJobUserDao xxlJobUserDao; private XxlJobUserDao xxlJobUserDao;
// ---------------------- token tool ----------------------
private String makeToken(XxlJobUser xxlJobUser){ private String makeToken(XxlJobUser xxlJobUser){
String tokenJson = JacksonUtil.writeValueAsString(xxlJobUser); String tokenJson = JacksonUtil.writeValueAsString(xxlJobUser);
String tokenHex = new BigInteger(tokenJson.getBytes()).toString(16); String tokenHex = new BigInteger(tokenJson.getBytes()).toString(16);
@ -41,6 +43,8 @@ public class LoginService {
} }
// ---------------------- login tool, with cookie and db ----------------------
public ReturnT<String> login(HttpServletRequest request, HttpServletResponse response, String username, String password, boolean ifRemember){ public ReturnT<String> login(HttpServletRequest request, HttpServletResponse response, String username, String password, boolean ifRemember){
// param // param

@ -4,10 +4,13 @@ import com.xxl.job.admin.core.cron.CronExpression;
import com.xxl.job.admin.core.model.XxlJobGroup; import com.xxl.job.admin.core.model.XxlJobGroup;
import com.xxl.job.admin.core.model.XxlJobInfo; import com.xxl.job.admin.core.model.XxlJobInfo;
import com.xxl.job.admin.core.model.XxlJobLogReport; import com.xxl.job.admin.core.model.XxlJobLogReport;
import com.xxl.job.admin.core.model.XxlJobUser;
import com.xxl.job.admin.core.route.ExecutorRouteStrategyEnum; import com.xxl.job.admin.core.route.ExecutorRouteStrategyEnum;
import com.xxl.job.admin.core.scheduler.MisfireStrategyEnum; import com.xxl.job.admin.core.scheduler.MisfireStrategyEnum;
import com.xxl.job.admin.core.scheduler.ScheduleTypeEnum; import com.xxl.job.admin.core.scheduler.ScheduleTypeEnum;
import com.xxl.job.admin.core.thread.JobScheduleHelper; import com.xxl.job.admin.core.thread.JobScheduleHelper;
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.admin.core.util.I18nUtil;
import com.xxl.job.admin.dao.*; import com.xxl.job.admin.dao.*;
import com.xxl.job.admin.service.XxlJobService; import com.xxl.job.admin.service.XxlJobService;
@ -58,7 +61,7 @@ public class XxlJobServiceImpl implements XxlJobService {
} }
@Override @Override
public ReturnT<String> add(XxlJobInfo jobInfo) { public ReturnT<String> add(XxlJobInfo jobInfo, XxlJobUser loginUser) {
// valid base // valid base
XxlJobGroup group = xxlJobGroupDao.load(jobInfo.getJobGroup()); XxlJobGroup group = xxlJobGroupDao.load(jobInfo.getJobGroup());
@ -128,6 +131,10 @@ public class XxlJobServiceImpl implements XxlJobService {
return new ReturnT<String>(ReturnT.FAIL_CODE, return new ReturnT<String>(ReturnT.FAIL_CODE,
MessageFormat.format((I18nUtil.getString("jobinfo_field_childJobId")+"({0})"+I18nUtil.getString("system_not_found")), childJobIdItem)); MessageFormat.format((I18nUtil.getString("jobinfo_field_childJobId")+"({0})"+I18nUtil.getString("system_not_found")), childJobIdItem));
} }
if (!loginUser.validPermission(childJobInfo.getJobGroup())) {
return new ReturnT<String>(ReturnT.FAIL_CODE,
MessageFormat.format((I18nUtil.getString("jobinfo_field_childJobId")+"({0})"+I18nUtil.getString("system_permission_limit")), childJobIdItem));
}
} else { } else {
return new ReturnT<String>(ReturnT.FAIL_CODE, return new ReturnT<String>(ReturnT.FAIL_CODE,
MessageFormat.format((I18nUtil.getString("jobinfo_field_childJobId")+"({0})"+I18nUtil.getString("system_unvalid")), childJobIdItem)); MessageFormat.format((I18nUtil.getString("jobinfo_field_childJobId")+"({0})"+I18nUtil.getString("system_unvalid")), childJobIdItem));
@ -166,7 +173,7 @@ public class XxlJobServiceImpl implements XxlJobService {
} }
@Override @Override
public ReturnT<String> update(XxlJobInfo jobInfo) { public ReturnT<String> update(XxlJobInfo jobInfo, XxlJobUser loginUser) {
// valid base // valid base
if (jobInfo.getJobDesc()==null || jobInfo.getJobDesc().trim().length()==0) { if (jobInfo.getJobDesc()==null || jobInfo.getJobDesc().trim().length()==0) {
@ -215,11 +222,19 @@ public class XxlJobServiceImpl implements XxlJobService {
String[] childJobIds = jobInfo.getChildJobId().split(","); String[] childJobIds = jobInfo.getChildJobId().split(",");
for (String childJobIdItem: childJobIds) { for (String childJobIdItem: childJobIds) {
if (childJobIdItem!=null && childJobIdItem.trim().length()>0 && isNumeric(childJobIdItem)) { if (childJobIdItem!=null && childJobIdItem.trim().length()>0 && isNumeric(childJobIdItem)) {
XxlJobInfo childJobInfo = xxlJobInfoDao.loadById(Integer.parseInt(childJobIdItem)); int childJobId = Integer.parseInt(childJobIdItem);
XxlJobInfo childJobInfo = xxlJobInfoDao.loadById(childJobId);
if (childJobId == jobInfo.getId()) {
return new ReturnT<String>(ReturnT.FAIL_CODE, (I18nUtil.getString("jobinfo_field_childJobId")+"("+childJobId+")"+I18nUtil.getString("system_unvalid")) );
}
if (childJobInfo==null) { if (childJobInfo==null) {
return new ReturnT<String>(ReturnT.FAIL_CODE, return new ReturnT<String>(ReturnT.FAIL_CODE,
MessageFormat.format((I18nUtil.getString("jobinfo_field_childJobId")+"({0})"+I18nUtil.getString("system_not_found")), childJobIdItem)); MessageFormat.format((I18nUtil.getString("jobinfo_field_childJobId")+"({0})"+I18nUtil.getString("system_not_found")), childJobIdItem));
} }
if (!loginUser.validPermission(childJobInfo.getJobGroup())) {
return new ReturnT<String>(ReturnT.FAIL_CODE,
MessageFormat.format((I18nUtil.getString("jobinfo_field_childJobId")+"({0})"+I18nUtil.getString("system_permission_limit")), childJobIdItem));
}
} else { } else {
return new ReturnT<String>(ReturnT.FAIL_CODE, return new ReturnT<String>(ReturnT.FAIL_CODE,
MessageFormat.format((I18nUtil.getString("jobinfo_field_childJobId")+"({0})"+I18nUtil.getString("system_unvalid")), childJobIdItem)); MessageFormat.format((I18nUtil.getString("jobinfo_field_childJobId")+"({0})"+I18nUtil.getString("system_unvalid")), childJobIdItem));
@ -345,6 +360,42 @@ public class XxlJobServiceImpl implements XxlJobService {
return ReturnT.SUCCESS; return ReturnT.SUCCESS;
} }
@Override
public ReturnT<String> trigger(XxlJobUser loginUser, int jobId, String executorParam, String addressList) {
// permission
if (loginUser == null) {
return new ReturnT<String>(ReturnT.FAIL.getCode(), I18nUtil.getString("system_permission_limit"));
}
XxlJobInfo xxlJobInfo = xxlJobInfoDao.loadById(jobId);
if (xxlJobInfo == null) {
return new ReturnT<String>(ReturnT.FAIL.getCode(), I18nUtil.getString("jobinfo_glue_jobid_unvalid"));
}
if (!hasPermission(loginUser, xxlJobInfo.getJobGroup())) {
return new ReturnT<String>(ReturnT.FAIL.getCode(), I18nUtil.getString("system_permission_limit"));
}
// force cover job param
if (executorParam == null) {
executorParam = "";
}
JobTriggerPoolHelper.trigger(jobId, TriggerTypeEnum.MANUAL, -1, null, executorParam, addressList);
return ReturnT.SUCCESS;
}
private boolean hasPermission(XxlJobUser loginUser, int jobGroup){
if (loginUser.getRole() == 1) {
return true;
}
List<String> groupIdStrs = new ArrayList<>();
if (loginUser.getPermission()!=null && loginUser.getPermission().trim().length()>0) {
groupIdStrs = Arrays.asList(loginUser.getPermission().trim().split(","));
}
return groupIdStrs.contains(String.valueOf(jobGroup));
}
@Override @Override
public Map<String, Object> dashboardInfo() { public Map<String, Object> dashboardInfo() {

@ -17,6 +17,7 @@ spring.freemarker.suffix=.ftl
spring.freemarker.charset=UTF-8 spring.freemarker.charset=UTF-8
spring.freemarker.request-context-attribute=request spring.freemarker.request-context-attribute=request
spring.freemarker.settings.number_format=0.########## spring.freemarker.settings.number_format=0.##########
spring.freemarker.settings.new_builtin_class_resolver=safer
### mybatis ### mybatis
mybatis.mapper-locations=classpath:/mybatis-mapper/*Mapper.xml mybatis.mapper-locations=classpath:/mybatis-mapper/*Mapper.xml

@ -1,6 +1,6 @@
admin_name=Scheduling Center admin_name=Scheduling Center
admin_name_full=Distributed Task Scheduling Platform XXL-JOB admin_name_full=Distributed Task Scheduling Platform XXL-JOB
admin_version=2.4.1-SNAPSHOT admin_version=2.5.0-SNAPSHOT
admin_i18n=en admin_i18n=en
## system ## system
@ -91,6 +91,7 @@ logout_fail=Logout fail
## change pwd ## change pwd
change_pwd=Change password change_pwd=Change password
change_pwd_suc_to_logout=Change password successful, about to log out login change_pwd_suc_to_logout=Change password successful, about to log out login
change_pwd_field_oldpwd=old password
change_pwd_field_newpwd=new password change_pwd_field_newpwd=new password
## dashboard ## dashboard

@ -1,6 +1,6 @@
admin_name=任务调度中心 admin_name=任务调度中心
admin_name_full=分布式任务调度平台XXL-JOB admin_name_full=分布式任务调度平台XXL-JOB
admin_version=2.4.1-SNAPSHOT admin_version=2.5.0-SNAPSHOT
admin_i18n= admin_i18n=
## system ## system
@ -91,6 +91,7 @@ logout_fail=注销失败
## change pwd ## change pwd
change_pwd=修改密码 change_pwd=修改密码
change_pwd_suc_to_logout=修改密码成功,即将注销登陆 change_pwd_suc_to_logout=修改密码成功,即将注销登陆
change_pwd_field_oldpwd=旧密码
change_pwd_field_newpwd=新密码 change_pwd_field_newpwd=新密码
## dashboard ## dashboard

@ -1,6 +1,6 @@
admin_name=任務調度中心 admin_name=任務調度中心
admin_name_full=分布式任務調度平臺XXL-JOB admin_name_full=分布式任務調度平臺XXL-JOB
admin_version=2.4.1-SNAPSHOT admin_version=2.5.0-SNAPSHOT
admin_i18n= admin_i18n=
## system ## system
@ -91,6 +91,7 @@ logout_fail=登出失敗
## change pwd ## change pwd
change_pwd=修改密碼 change_pwd=修改密碼
change_pwd_suc_to_logout=修改密碼成功,即將登出 change_pwd_suc_to_logout=修改密碼成功,即將登出
change_pwd_field_oldpwd=舊密碼
change_pwd_field_newpwd=新密碼 change_pwd_field_newpwd=新密碼
## dashboard ## dashboard

@ -232,9 +232,12 @@
UPDATE xxl_job_info UPDATE xxl_job_info
SET SET
trigger_last_time = #{triggerLastTime}, trigger_last_time = #{triggerLastTime},
trigger_next_time = #{triggerNextTime}, trigger_next_time = #{triggerNextTime}
trigger_status = #{triggerStatus} <if test="triggerStatus == 0">
, trigger_status = #{triggerStatus}
</if>
WHERE id = #{id} WHERE id = #{id}
AND trigger_status = 1
</update> </update>
</mapper> </mapper>

@ -178,9 +178,9 @@
<select id="findLogReport" resultType="java.util.Map" > <select id="findLogReport" resultType="java.util.Map" >
SELECT SELECT
COUNT(handle_code) triggerDayCount, COUNT(handle_code) "triggerDayCount",
SUM(CASE WHEN (trigger_code in (0, 200) and handle_code = 0) then 1 else 0 end) as triggerDayCountRunning, SUM(CASE WHEN (trigger_code in (0, 200) and handle_code = 0) then 1 else 0 end) as "triggerDayCountRunning",
SUM(CASE WHEN handle_code = 200 then 1 else 0 end) as triggerDayCountSuc SUM(CASE WHEN handle_code = 200 then 1 else 0 end) as "triggerDayCountSuc"
FROM xxl_job_log FROM xxl_job_log
WHERE trigger_time BETWEEN #{from} and #{to} WHERE trigger_time BETWEEN #{from} and #{to}
</select> </select>

@ -39,6 +39,14 @@
WHERE t.update_time <![CDATA[ > ]]> DATE_ADD(#{nowTime},INTERVAL -#{timeout} SECOND) WHERE t.update_time <![CDATA[ > ]]> DATE_ADD(#{nowTime},INTERVAL -#{timeout} SECOND)
</select> </select>
<insert id="registrySaveOrUpdate" >
INSERT INTO xxl_job_registry( `registry_group` , `registry_key` , `registry_value`, `update_time`)
VALUES( #{registryGroup} , #{registryKey} , #{registryValue}, #{updateTime})
ON DUPLICATE KEY UPDATE
`update_time` = #{updateTime}
</insert>
<!--
<update id="registryUpdate" > <update id="registryUpdate" >
UPDATE xxl_job_registry UPDATE xxl_job_registry
SET `update_time` = #{updateTime} SET `update_time` = #{updateTime}
@ -51,6 +59,7 @@
INSERT INTO xxl_job_registry( `registry_group` , `registry_key` , `registry_value`, `update_time`) INSERT INTO xxl_job_registry( `registry_group` , `registry_key` , `registry_value`, `update_time`)
VALUES( #{registryGroup} , #{registryKey} , #{registryValue}, #{updateTime}) VALUES( #{registryGroup} , #{registryKey} , #{registryValue}, #{updateTime})
</insert> </insert>
-->
<delete id="registryDelete" > <delete id="registryDelete" >
DELETE FROM xxl_job_registry DELETE FROM xxl_job_registry

@ -99,15 +99,23 @@ $(function(){
errorClass : 'help-block', errorClass : 'help-block',
focusInvalid : true, focusInvalid : true,
rules : { rules : {
oldPassword : {
required : true ,
rangelength:[4,20]
},
password : { password : {
required : true , required : true ,
rangelength:[4,50] rangelength:[4,20]
} }
}, },
messages : { messages : {
oldPassword : {
required : I18n.system_please_input +I18n.change_pwd_field_oldpwd,
rangelength : "密码长度限制为4~20"
},
password : { password : {
required : '' , required : I18n.system_please_input +I18n.change_pwd_field_newpwd,
rangelength : "密码长度限制为4~50" rangelength : "密码长度限制为4~20"
} }
}, },
highlight : function(element) { highlight : function(element) {

@ -123,7 +123,7 @@ $(function() {
var id = $(this).attr("_id"); var id = $(this).attr("_id");
var row = tableData['key'+id]; var row = tableData['key'+id];
var html = '<div>'; /*var html = '<div>';
if (row.registryList) { if (row.registryList) {
for (var index in row.registryList) { for (var index in row.registryList) {
html += (parseInt(index)+1) + '. <span class="badge bg-green" >' + row.registryList[index] + '</span><br>'; html += (parseInt(index)+1) + '. <span class="badge bg-green" >' + row.registryList[index] + '</span><br>';
@ -135,8 +135,19 @@ $(function() {
title: I18n.jobinfo_opt_registryinfo , title: I18n.jobinfo_opt_registryinfo ,
btn: [ I18n.system_ok ], btn: [ I18n.system_ok ],
content: html content: html
}); });*/
var html = '<table class="table table-bordered"><tbody>';
if (row.registryList) {
for (var index in row.registryList) {
html += '<tr><th>' + (parseInt(index)+1) + '</th>';
html += '<th><span class="badge bg-green" >' + row.registryList[index] + '</span></th><tr>';
}
}
html += '</tbody></table>';
$('#showRegistryListModal .data').html(html);
$('#showRegistryListModal').modal({backdrop: false, keyboard: false}).modal('show');
}); });

@ -16,12 +16,12 @@ $(function(){
userName : { userName : {
required : true , required : true ,
minlength: 4, minlength: 4,
maxlength: 18 maxlength: 20
}, },
password : { password : {
required : true , required : true ,
minlength: 4, minlength: 4,
maxlength: 18 maxlength: 20
} }
}, },
messages : { messages : {
@ -32,7 +32,7 @@ $(function(){
password : { password : {
required : I18n.login_password_empty , required : I18n.login_password_empty ,
minlength : I18n.login_password_lt_4 minlength : I18n.login_password_lt_4
/*,maxlength:"登录密码不应超过18位"*/ /*,maxlength:"登录密码不应超过20位"*/
} }
}, },
highlight : function(element) { highlight : function(element) {

@ -107,9 +107,13 @@
</div> </div>
<div class="modal-body"> <div class="modal-body">
<form class="form-horizontal form" role="form" > <form class="form-horizontal form" role="form" >
<div class="form-group">
<label for="lastname" class="col-sm-2 control-label">${I18n.change_pwd_field_oldpwd}<font color="red">*</font></label>
<div class="col-sm-10"><input type="text" class="form-control" name="oldPassword" placeholder="${I18n.system_please_input} ${I18n.change_pwd_field_oldpwd}" maxlength="20" ></div>
</div>
<div class="form-group"> <div class="form-group">
<label for="lastname" class="col-sm-2 control-label">${I18n.change_pwd_field_newpwd}<font color="red">*</font></label> <label for="lastname" class="col-sm-2 control-label">${I18n.change_pwd_field_newpwd}<font color="red">*</font></label>
<div class="col-sm-10"><input type="text" class="form-control" name="password" placeholder="${I18n.system_please_input} ${I18n.change_pwd_field_newpwd}" maxlength="18" ></div> <div class="col-sm-10"><input type="text" class="form-control" name="password" placeholder="${I18n.system_please_input} ${I18n.change_pwd_field_newpwd}" maxlength="20" ></div>
</div> </div>
<hr> <hr>
<div class="form-group"> <div class="form-group">

@ -70,6 +70,25 @@
</section> </section>
</div> </div>
<!-- . -->
<div class="modal fade" id="showRegistryListModal" tabindex="-1" role="dialog" aria-hidden="true">
<div class="modal-dialog modal-sm">
<div class="modal-content">
<div class="modal-header">
<h4 class="modal-title" >${I18n.jobinfo_opt_registryinfo}</h4>
</div>
<div class="modal-body">
<div class="data" style="word-wrap: break-word;"></div>
</div>
<div class="modal-footer">
<div class="text-center" >
<button type="button" class="btn btn-info ok" data-dismiss="modal" >${I18n.system_ok}</button>
</div>
</div>
</div>
</div>
</div>
<!-- . --> <!-- . -->
<div class="modal fade" id="addModal" tabindex="-1" role="dialog" aria-hidden="true"> <div class="modal fade" id="addModal" tabindex="-1" role="dialog" aria-hidden="true">
<div class="modal-dialog "> <div class="modal-dialog ">

@ -15,11 +15,11 @@
<div class="login-box-body"> <div class="login-box-body">
<p class="login-box-msg">${I18n.admin_name}</p> <p class="login-box-msg">${I18n.admin_name}</p>
<div class="form-group has-feedback"> <div class="form-group has-feedback">
<input type="text" name="userName" class="form-control" placeholder="${I18n.login_username_placeholder}" maxlength="18" > <input type="text" name="userName" class="form-control" placeholder="${I18n.login_username_placeholder}" maxlength="20" >
<span class="glyphicon glyphicon-envelope form-control-feedback"></span> <span class="glyphicon glyphicon-envelope form-control-feedback"></span>
</div> </div>
<div class="form-group has-feedback"> <div class="form-group has-feedback">
<input type="password" name="password" class="form-control" placeholder="${I18n.login_password_placeholder}" maxlength="18" > <input type="password" name="password" class="form-control" placeholder="${I18n.login_password_placeholder}" maxlength="20" >
<span class="glyphicon glyphicon-lock form-control-feedback"></span> <span class="glyphicon glyphicon-lock form-control-feedback"></span>
</div> </div>
<div class="row"> <div class="row">

@ -1,6 +1,6 @@
package com.xxl.job.admin.controller; package com.xxl.job.admin.controller;
import com.xxl.job.admin.service.LoginService; import com.xxl.job.admin.service.impl.LoginService;
import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Test;
import org.slf4j.Logger; import org.slf4j.Logger;

@ -8,6 +8,7 @@ import javax.annotation.Resource;
import java.util.Arrays; import java.util.Arrays;
import java.util.Date; import java.util.Date;
import java.util.List; import java.util.List;
import java.util.concurrent.TimeUnit;
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT) @SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
public class XxlJobRegistryDaoTest { public class XxlJobRegistryDaoTest {
@ -17,14 +18,32 @@ public class XxlJobRegistryDaoTest {
@Test @Test
public void test(){ public void test(){
int ret = xxlJobRegistryDao.registryUpdate("g1", "k1", "v1", new Date()); int ret = xxlJobRegistryDao.registrySaveOrUpdate("g1", "k1", "v1", new Date());
/*int ret = xxlJobRegistryDao.registryUpdate("g1", "k1", "v1", new Date());
if (ret < 1) { if (ret < 1) {
ret = xxlJobRegistryDao.registrySave("g1", "k1", "v1", new Date()); ret = xxlJobRegistryDao.registrySave("g1", "k1", "v1", new Date());
} }*/
List<XxlJobRegistry> list = xxlJobRegistryDao.findAll(1, new Date()); List<XxlJobRegistry> list = xxlJobRegistryDao.findAll(1, new Date());
int ret2 = xxlJobRegistryDao.removeDead(Arrays.asList(1)); int ret2 = xxlJobRegistryDao.removeDead(Arrays.asList(1));
} }
@Test
public void test2() throws InterruptedException {
for (int i = 0; i < 100; i++) {
new Thread(()->{
int ret = xxlJobRegistryDao.registrySaveOrUpdate("g1", "k1", "v1", new Date());
System.out.println(ret);
/*int ret = xxlJobRegistryDao.registryUpdate("g1", "k1", "v1", new Date());
if (ret < 1) {
ret = xxlJobRegistryDao.registrySave("g1", "k1", "v1", new Date());
}*/
}).start();
}
TimeUnit.SECONDS.sleep(10);
}
} }

@ -4,7 +4,7 @@
<parent> <parent>
<groupId>com.xuxueli</groupId> <groupId>com.xuxueli</groupId>
<artifactId>xxl-job</artifactId> <artifactId>xxl-job</artifactId>
<version>2.4.1-SNAPSHOT</version> <version>2.5.0-SNAPSHOT</version>
</parent> </parent>
<artifactId>xxl-job-core</artifactId> <artifactId>xxl-job-core</artifactId>
<packaging>jar</packaging> <packaging>jar</packaging>

@ -89,14 +89,14 @@ public class EmbedServer {
} catch (InterruptedException e) { } catch (InterruptedException e) {
logger.info(">>>>>>>>>>> xxl-job remoting server stop."); logger.info(">>>>>>>>>>> xxl-job remoting server stop.");
} catch (Exception e) { } catch (Throwable e) {
logger.error(">>>>>>>>>>> xxl-job remoting server error.", e); logger.error(">>>>>>>>>>> xxl-job remoting server error.", e);
} finally { } finally {
// stop // stop
try { try {
workerGroup.shutdownGracefully(); workerGroup.shutdownGracefully();
bossGroup.shutdownGracefully(); bossGroup.shutdownGracefully();
} catch (Exception e) { } catch (Throwable e) {
logger.error(e.getMessage(), e); logger.error(e.getMessage(), e);
} }
} }
@ -200,7 +200,7 @@ public class EmbedServer {
default: default:
return new ReturnT<String>(ReturnT.FAIL_CODE, "invalid request, uri-mapping(" + uri + ") not found."); return new ReturnT<String>(ReturnT.FAIL_CODE, "invalid request, uri-mapping(" + uri + ") not found.");
} }
} catch (Exception e) { } catch (Throwable e) {
logger.error(e.getMessage(), e); logger.error(e.getMessage(), e);
return new ReturnT<String>(ReturnT.FAIL_CODE, "request error:" + ThrowableUtil.toString(e)); return new ReturnT<String>(ReturnT.FAIL_CODE, "request error:" + ThrowableUtil.toString(e));
} }

@ -53,12 +53,12 @@ public class ExecutorRegistryThread {
} else { } else {
logger.info(">>>>>>>>>>> xxl-job registry fail, registryParam:{}, registryResult:{}", new Object[]{registryParam, registryResult}); logger.info(">>>>>>>>>>> xxl-job registry fail, registryParam:{}, registryResult:{}", new Object[]{registryParam, registryResult});
} }
} catch (Exception e) { } catch (Throwable e) {
logger.info(">>>>>>>>>>> xxl-job registry error, registryParam:{}", registryParam, e); logger.info(">>>>>>>>>>> xxl-job registry error, registryParam:{}", registryParam, e);
} }
} }
} catch (Exception e) { } catch (Throwable e) {
if (!toStop) { if (!toStop) {
logger.error(e.getMessage(), e); logger.error(e.getMessage(), e);
} }
@ -69,7 +69,7 @@ public class ExecutorRegistryThread {
if (!toStop) { if (!toStop) {
TimeUnit.SECONDS.sleep(RegistryConfig.BEAT_TIMEOUT); TimeUnit.SECONDS.sleep(RegistryConfig.BEAT_TIMEOUT);
} }
} catch (InterruptedException e) { } catch (Throwable e) {
if (!toStop) { if (!toStop) {
logger.warn(">>>>>>>>>>> xxl-job, executor registry thread interrupted, error msg:{}", e.getMessage()); logger.warn(">>>>>>>>>>> xxl-job, executor registry thread interrupted, error msg:{}", e.getMessage());
} }
@ -89,7 +89,7 @@ public class ExecutorRegistryThread {
} else { } else {
logger.info(">>>>>>>>>>> xxl-job registry-remove fail, registryParam:{}, registryResult:{}", new Object[]{registryParam, registryResult}); logger.info(">>>>>>>>>>> xxl-job registry-remove fail, registryParam:{}, registryResult:{}", new Object[]{registryParam, registryResult});
} }
} catch (Exception e) { } catch (Throwable e) {
if (!toStop) { if (!toStop) {
logger.info(">>>>>>>>>>> xxl-job registry-remove error, registryParam:{}", registryParam, e); logger.info(">>>>>>>>>>> xxl-job registry-remove error, registryParam:{}", registryParam, e);
} }
@ -97,7 +97,7 @@ public class ExecutorRegistryThread {
} }
} }
} catch (Exception e) { } catch (Throwable e) {
if (!toStop) { if (!toStop) {
logger.error(e.getMessage(), e); logger.error(e.getMessage(), e);
} }
@ -119,7 +119,7 @@ public class ExecutorRegistryThread {
registryThread.interrupt(); registryThread.interrupt();
try { try {
registryThread.join(); registryThread.join();
} catch (InterruptedException e) { } catch (Throwable e) {
logger.error(e.getMessage(), e); logger.error(e.getMessage(), e);
} }
} }

@ -81,7 +81,7 @@ public class JobLogFileCleanThread {
} }
} }
} catch (Exception e) { } catch (Throwable e) {
if (!toStop) { if (!toStop) {
logger.error(e.getMessage(), e); logger.error(e.getMessage(), e);
} }
@ -90,7 +90,7 @@ public class JobLogFileCleanThread {
try { try {
TimeUnit.DAYS.sleep(1); TimeUnit.DAYS.sleep(1);
} catch (InterruptedException e) { } catch (Throwable e) {
if (!toStop) { if (!toStop) {
logger.error(e.getMessage(), e); logger.error(e.getMessage(), e);
} }

@ -36,7 +36,7 @@ public class JobThread extends Thread{
private String stopReason; private String stopReason;
private boolean running = false; // if running job private boolean running = false; // if running job
private int idleTimes = 0; // idel times private int idleTimes = 0; // idle times
public JobThread(int jobId, IJobHandler handler) { public JobThread(int jobId, IJobHandler handler) {
@ -185,7 +185,7 @@ public class JobThread extends Thread{
} else { } else {
if (idleTimes > 30) { if (idleTimes > 30) {
if(triggerQueue.size() == 0) { // avoid concurrent trigger causes jobId-lost if(triggerQueue.size() == 0) { // avoid concurrent trigger causes jobId-lost
XxlJobExecutor.removeJobThread(jobId, "excutor idel times over limit."); XxlJobExecutor.removeJobThread(jobId, "excutor idle times over limit.");
} }
} }
} }

@ -76,7 +76,7 @@ public class TriggerCallbackThread {
doCallback(callbackParamList); doCallback(callbackParamList);
} }
} }
} catch (Exception e) { } catch (Throwable e) {
if (!toStop) { if (!toStop) {
logger.error(e.getMessage(), e); logger.error(e.getMessage(), e);
} }
@ -90,7 +90,7 @@ public class TriggerCallbackThread {
if (callbackParamList!=null && callbackParamList.size()>0) { if (callbackParamList!=null && callbackParamList.size()>0) {
doCallback(callbackParamList); doCallback(callbackParamList);
} }
} catch (Exception e) { } catch (Throwable e) {
if (!toStop) { if (!toStop) {
logger.error(e.getMessage(), e); logger.error(e.getMessage(), e);
} }
@ -111,7 +111,7 @@ public class TriggerCallbackThread {
while(!toStop){ while(!toStop){
try { try {
retryFailCallbackFile(); retryFailCallbackFile();
} catch (Exception e) { } catch (Throwable e) {
if (!toStop) { if (!toStop) {
logger.error(e.getMessage(), e); logger.error(e.getMessage(), e);
} }
@ -119,7 +119,7 @@ public class TriggerCallbackThread {
} }
try { try {
TimeUnit.SECONDS.sleep(RegistryConfig.BEAT_TIMEOUT); TimeUnit.SECONDS.sleep(RegistryConfig.BEAT_TIMEOUT);
} catch (InterruptedException e) { } catch (Throwable e) {
if (!toStop) { if (!toStop) {
logger.error(e.getMessage(), e); logger.error(e.getMessage(), e);
} }
@ -139,7 +139,7 @@ public class TriggerCallbackThread {
triggerCallbackThread.interrupt(); triggerCallbackThread.interrupt();
try { try {
triggerCallbackThread.join(); triggerCallbackThread.join();
} catch (InterruptedException e) { } catch (Throwable e) {
logger.error(e.getMessage(), e); logger.error(e.getMessage(), e);
} }
} }
@ -149,7 +149,7 @@ public class TriggerCallbackThread {
triggerRetryCallbackThread.interrupt(); triggerRetryCallbackThread.interrupt();
try { try {
triggerRetryCallbackThread.join(); triggerRetryCallbackThread.join();
} catch (InterruptedException e) { } catch (Throwable e) {
logger.error(e.getMessage(), e); logger.error(e.getMessage(), e);
} }
} }
@ -173,7 +173,7 @@ public class TriggerCallbackThread {
} else { } else {
callbackLog(callbackParamList, "<br>----------- xxl-job job callback fail, callbackResult:" + callbackResult); callbackLog(callbackParamList, "<br>----------- xxl-job job callback fail, callbackResult:" + callbackResult);
} }
} catch (Exception e) { } catch (Throwable e) {
callbackLog(callbackParamList, "<br>----------- xxl-job job callback error, errorMsg:" + e.getMessage()); callbackLog(callbackParamList, "<br>----------- xxl-job job callback error, errorMsg:" + e.getMessage());
} }
} }

@ -51,16 +51,18 @@ public class JdkSerializeTool {
* @return * @return
*/ */
public static <T> Object deserialize(byte[] bytes, Class<T> clazz) { public static <T> Object deserialize(byte[] bytes, Class<T> clazz) {
ObjectInputStream ois = null;
ByteArrayInputStream bais = null; ByteArrayInputStream bais = null;
try { try {
// 反序列化 // 反序列化
bais = new ByteArrayInputStream(bytes); bais = new ByteArrayInputStream(bytes);
ObjectInputStream ois = new ObjectInputStream(bais); ois = new ObjectInputStream(bais);
return ois.readObject(); return ois.readObject();
} catch (Exception e) { } catch (Exception e) {
logger.error(e.getMessage(), e); logger.error(e.getMessage(), e);
} finally { } finally {
try { try {
ois.close();
bais.close(); bais.close();
} catch (IOException e) { } catch (IOException e) {
logger.error(e.getMessage(), e); logger.error(e.getMessage(), e);

@ -1,11 +1,10 @@
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" <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"> 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> <modelVersion>4.0.0</modelVersion>
<parent> <parent>
<groupId>com.xuxueli</groupId> <groupId>com.xuxueli</groupId>
<artifactId>xxl-job</artifactId> <artifactId>xxl-job</artifactId>
<version>2.4.1-SNAPSHOT</version> <version>2.5.0-SNAPSHOT</version>
</parent> </parent>
<artifactId>xxl-job-executor-samples</artifactId> <artifactId>xxl-job-executor-samples</artifactId>
<packaging>pom</packaging> <packaging>pom</packaging>
@ -15,4 +14,8 @@
<module>xxl-job-executor-sample-springboot</module> <module>xxl-job-executor-sample-springboot</module>
</modules> </modules>
<properties>
<maven.deploy.skip>true</maven.deploy.skip>
</properties>
</project> </project>

@ -6,7 +6,7 @@
<parent> <parent>
<groupId>com.xuxueli</groupId> <groupId>com.xuxueli</groupId>
<artifactId>xxl-job-executor-samples</artifactId> <artifactId>xxl-job-executor-samples</artifactId>
<version>2.4.1-SNAPSHOT</version> <version>2.5.0-SNAPSHOT</version>
</parent> </parent>
<artifactId>xxl-job-executor-sample-frameless</artifactId> <artifactId>xxl-job-executor-sample-frameless</artifactId>
<packaging>jar</packaging> <packaging>jar</packaging>
@ -21,7 +21,7 @@
<!-- slf4j --> <!-- slf4j -->
<dependency> <dependency>
<groupId>org.slf4j</groupId> <groupId>org.slf4j</groupId>
<artifactId>slf4j-log4j12</artifactId> <artifactId>slf4j-reload4j</artifactId>
<version>${slf4j-api.version}</version> <version>${slf4j-api.version}</version>
</dependency> </dependency>
<!-- junit --> <!-- junit -->

@ -6,7 +6,7 @@
<parent> <parent>
<groupId>com.xuxueli</groupId> <groupId>com.xuxueli</groupId>
<artifactId>xxl-job-executor-samples</artifactId> <artifactId>xxl-job-executor-samples</artifactId>
<version>2.4.1-SNAPSHOT</version> <version>2.5.0-SNAPSHOT</version>
</parent> </parent>
<artifactId>xxl-job-executor-sample-springboot</artifactId> <artifactId>xxl-job-executor-sample-springboot</artifactId>
<packaging>jar</packaging> <packaging>jar</packaging>
@ -66,6 +66,9 @@
</goals> </goals>
</execution> </execution>
</executions> </executions>
<configuration>
<fork>true</fork>
</configuration>
</plugin> </plugin>
</plugins> </plugins>
</build> </build>

Loading…
Cancel
Save