diff --git a/README.md b/README.md index 802d8ddb..a393bdb8 100644 --- a/README.md +++ b/README.md @@ -1,12 +1,10 @@

- - - +

XXL-JOB

XXL-JOB, a lightweight distributed task scheduling framework.
- -- Browse website. -- + -- Home Page --

@@ -71,7 +69,8 @@ XXL-JOB是一个轻量级分布式任务调度框架,其核心设计目标是 - 24、邮件报警:任务失败时支持邮件报警,支持配置多邮件地址群发报警邮件; - 25、推送maven中央仓库: 将会把最新稳定版推送到maven中央仓库, 方便用户接入和使用; - 26、运行报表:支持实时查看运行数据,如任务数量、调度次数、执行器数量等;以及调度报表,如调度日期分布图,调度成功分布图等; - +- 27、全异步:系统底层实现全部异步化,针对密集调度进行流量削峰,理论上支持任意时长任务的运行; +- 28、国际化:调度中心支持国际化设置,提供中文、英文两种可选语言,默认为中文; ## Development 于2015年中,我在github上创建XXL-JOB项目仓库并提交第一个commit,随之进行系统结构设计,UI选型,交互设计…… @@ -150,6 +149,13 @@ XXL-JOB是一个轻量级分布式任务调度框架,其核心设计目标是 - 57、华夏票联(北京)科技有限公司 - 58、拍拍贷 - 59、北京尚德机构在线教育有限公司 + - 60、任子行股份有限公司 + - 61、北京时态电子商务有限公司 + - 62、深圳卷皮网络科技有限公司 + - 63、北京安博通科技股份有限公司 + - 64、未来无线网 + - 65、厦门瓷禧网络有限公司 + - 66、北京递蓝科软件股份有限公司 - …… > 更多接入的公司,欢迎在 [登记地址](https://github.com/xuxueli/xxl-job/issues/1 ) 登记,登记仅仅为了产品推广。 @@ -179,6 +185,6 @@ This product is open source and free, and will continue to provide free communit ## Donate -No matter how much the amount is enough to express your thought, thank you very much :) [To donate](http://www.xuxueli.com/page/donate.html ) +No matter how much the donation amount is enough to express your thought, thank you very much :) [To donate](http://www.xuxueli.com/page/donate.html ) -无论金额多少都足够表达您这份心意,非常感谢 :) [前往捐赠](http://www.xuxueli.com/page/donate.html ) +无论捐赠金额多少都足够表达您这份心意,非常感谢 :) [前往捐赠](http://www.xuxueli.com/page/donate.html ) diff --git a/doc/XXL-JOB-English-Documentation.md b/doc/XXL-JOB-English-Documentation.md index 0835599a..f3e903e7 100644 --- a/doc/XXL-JOB-English-Documentation.md +++ b/doc/XXL-JOB-English-Documentation.md @@ -12,6 +12,9 @@ ### 1.1 Overview XXL-JOB is a lightweight distributed task scheduling framework, the core design goal is to develop quickly, learning simple, lightweight, easy to expand. Is now open source and access to a number of companies online product line, download and use it now. + +> English document update slightly delayed, Please check the Chinese version for the latest document. + ### 1.2 Features - 1.Simple: support through the Web page on the task CRUD operation, simple operation, a minute to get started; - 2.Dynamic: support dynamic modification of task status, pause / resume tasks, and termination of running tasks,immediate effect; @@ -105,6 +108,19 @@ So far, XXL-JOB has access to a number of companies online product line, access - 52、聚金资本 - 53、北京父母邦网络科技有限公司 - 54、中山元赫软件科技有限公司 + - 55、中商惠民(北京)电子商务有限公司 + - 56、凯京集团 + - 57、华夏票联(北京)科技有限公司 + - 58、拍拍贷 + - 59、北京尚德机构在线教育有限公司 + - 60、任子行股份有限公司 + - 61、北京时态电子商务有限公司 + - 62、深圳卷皮网络科技有限公司 + - 63、北京安博通科技股份有限公司 + - 64、未来无线网 + - 65、厦门瓷禧网络有限公司 + - 66、北京递蓝科软件股份有限公司 + - 67、郑州创海软件科技公司 - …… > The company that access and use this product is welcome to register at the [address](https://github.com/xuxueli/xxl-job/issues/1 ), only for product promotion. @@ -122,7 +138,7 @@ Welcome everyone's attention and use, XXL-JOB will also embrace changes, sustain Source repository address | Release Download --- | --- [https://github.com/xuxueli/xxl-job](https://github.com/xuxueli/xxl-job) | [Download](https://github.com/xuxueli/xxl-job/releases) -[http://git.oschina.net/xuxueli0323/xxl-job](http://git.oschina.net/xuxueli0323/xxl-job) | [Download](http://git.oschina.net/xuxueli0323/xxl-job/releases) +[http://gitee.com/xuxueli0323/xxl-job](http://gitee.com/xuxueli0323/xxl-job) | [Download](http://gitee.com/xuxueli0323/xxl-job/releases) #### Center repository address (The latest Release version:1.8.1) ``` @@ -203,6 +219,9 @@ The concrete contet describe as follows: ### TOKEN used for communication between the executor and schedule center, enabled if it’s not null xxl.job.accessToken= + + ### Internationalized Settings, the default is Chinese version,Switch to English when the value is "en". + xxl.job.i18n=en #### Step 2:Deploy: If you has finished step 1,then you can compile the project in maven and deploy the war package to tomcat. diff --git a/doc/XXL-JOB官方文档.md b/doc/XXL-JOB官方文档.md index d2aaa475..7b1d7e30 100644 --- a/doc/XXL-JOB官方文档.md +++ b/doc/XXL-JOB官方文档.md @@ -40,6 +40,8 @@ XXL-JOB是一个轻量级分布式任务调度框架,其核心设计目标是 - 24、邮件报警:任务失败时支持邮件报警,支持配置多邮件地址群发报警邮件; - 25、推送maven中央仓库: 将会把最新稳定版推送到maven中央仓库, 方便用户接入和使用; - 26、运行报表:支持实时查看运行数据,如任务数量、调度次数、执行器数量等;以及调度报表,如调度日期分布图,调度成功分布图等; +- 27、全异步:系统底层实现全部异步化,针对密集调度进行流量削峰,理论上支持任意时长任务的运行; +- 28、国际化:调度中心支持国际化设置,提供中文、英文两种可选语言,默认为中文; ### 1.3 发展 于2015年中,我在github上创建XXL-JOB项目仓库并提交第一个commit,随之进行系统结构设计,UI选型,交互设计…… @@ -52,12 +54,18 @@ XXL-JOB是一个轻量级分布式任务调度框架,其核心设计目标是 于2017-05-13,在上海举办的 "[第62期开源中国源创会](https://www.oschina.net/event/2236961)" 的 "放码过来" 环节,我登台对XXL-JOB做了演讲,台下五百位在场观众反响热烈([图文回顾](https://www.oschina.net/question/2686220_2242120) )。 +于2017-10-22,又拍云 Open Talk 联合 Spring Cloud 中国社区举办的 "[进击的微服务实战派上海站](https://opentalk.upyun.com/303.html)",我登台对XXL-JOB做了演讲,现场观众反响热烈并在会后与XXL-JOB用户热烈讨论交流 + 于2017-12-11,XXL-JOB有幸参会《[InfoQ ArchSummit全球架构师峰会](http://bj2017.archsummit.com/)》,并被拍拍贷架构总监"杨波老师"在专题 "[微服务原理、基础架构和开源实践](http://bj2017.archsummit.com/training/2)" 中现场介绍。 +于2017-12-18,XXL-JOB参与"[2017年度最受欢迎中国开源软件](http://www.oschina.net/project/top_cn_2017?sort=1)"评比,在当时已录入的约九千个国产开源项目中角逐,最终进入了前30强。 + +于2018-01-15,XXL-JOB参与"[2017码云最火开源项目](https://www.oschina.net/news/92438/2017-mayun-top-50)"评比,在当时已录入的约六千五百个码云项目中角逐,最终进去了前20强。 + > 我司大众点评目前已接入XXL-JOB,内部别名《Ferrari》(Ferrari基于XXL-JOB的V1.1版本定制而成,新接入应用推荐升级最新版本)。 据最新统计, 自2016-01-21接入至2017-12-01期间,该系统已调度约100万次,表现优异。新接入应用推荐使用最新版本,因为经过数个大版本的更新,系统的任务模型、UI交互模型以及底层调度通讯模型都有了较大的优化和提升,核心功能更加稳定高效。 -至今,XXL-JOB已接入多家公司的线上产品线,接入场景如电商业务,O2O业务和大数据作业等,截止2016-07-19为止,XXL-JOB已接入的公司包括不限于: +至今,XXL-JOB已接入多家公司的线上产品线,接入场景如电商业务,O2O业务和大数据作业等,截止最新统计时间为止,XXL-JOB已接入的公司包括不限于: - 1、大众点评; - 2、山东学而网络科技有限公司; @@ -118,6 +126,14 @@ XXL-JOB是一个轻量级分布式任务调度框架,其核心设计目标是 - 57、华夏票联(北京)科技有限公司 - 58、拍拍贷 - 59、北京尚德机构在线教育有限公司 + - 60、任子行股份有限公司 + - 61、北京时态电子商务有限公司 + - 62、深圳卷皮网络科技有限公司 + - 63、北京安博通科技股份有限公司 + - 64、未来无线网 + - 65、厦门瓷禧网络有限公司 + - 66、北京递蓝科软件股份有限公司 + - 67、郑州创海软件科技公司 - …… > 更多接入的公司,欢迎在 [登记地址](https://github.com/xuxueli/xxl-job/issues/1 ) 登记,登记仅仅为了产品推广。 @@ -137,7 +153,7 @@ XXL-JOB是一个轻量级分布式任务调度框架,其核心设计目标是 源码仓库地址 | Release Download --- | --- [https://github.com/xuxueli/xxl-job](https://github.com/xuxueli/xxl-job) | [Download](https://github.com/xuxueli/xxl-job/releases) -[http://git.oschina.net/xuxueli0323/xxl-job](http://git.oschina.net/xuxueli0323/xxl-job) | [Download](http://git.oschina.net/xuxueli0323/xxl-job/releases) +[http://gitee.com/xuxueli0323/xxl-job](http://gitee.com/xuxueli0323/xxl-job) | [Download](http://gitee.com/xuxueli0323/xxl-job/releases) #### 中央仓库地址 @@ -147,7 +163,7 @@ XXL-JOB是一个轻量级分布式任务调度框架,其核心设计目标是 com.xuxueli xxl-job-core - 1.8.2 + ${最新稳定版本} ``` @@ -222,6 +238,9 @@ XXL-JOB是一个轻量级分布式任务调度框架,其核心设计目标是 ### 调度中心通讯TOKEN,非空时启用 xxl.job.accessToken= + + ### 调度中心国际化设置,默认为中文版本,值设置为“en”时切换为英文版本 + xxl.job.i18n= #### 步骤二:部署项目: 如果已经正确进行上述配置,可将项目编译打war包并部署到tomcat中。 @@ -263,12 +282,15 @@ XXL-JOB是一个轻量级分布式任务调度框架,其核心设计目标是 xxl.job.executor.ip= xxl.job.executor.port=9999 + ### xxl-job, access token:执行器通讯TOKEN,非空时启用 + xxl.job.accessToken= + ### xxl-job log path:执行器运行日志文件存储的磁盘位置,需要对该路径拥有读写权限 xxl.job.executor.logpath=/data/applogs/xxl-job/jobhandler/ - ### xxl-job, access token:执行器通讯TOKEN,非空时启用 - xxl.job.accessToken= - + ### xxl-job log retention days:执行器Log文件定期清理功能,指定日志保存天数,日志文件过期自动删除。限制至少保持3天,否则功能不生效; + xxl.job.executor.logretentiondays=-1 + #### 步骤三:执行器组件配置 @@ -282,35 +304,42 @@ XXL-JOB是一个轻量级分布式任务调度框架,其核心设计目标是 - + + + + + - - - - - - + + + + ``` #### 步骤四:部署执行器项目: -如果已经正确进行上述配置,可将执行器项目编译打部署,系统提供三个执行器Sample示例项目,选择其中一个即可,各自的部署方式如下。 +如果已经正确进行上述配置,可将执行器项目编译打部署,系统提供多种执行器Sample示例项目,选择其中一个即可,各自的部署方式如下。 - xxl-job-executor-sample-spring:项目编译打包成WAR包,并部署到tomcat中。 xxl-job-executor-sample-springboot:项目编译打包成springboot类型的可执行JAR包,命令启动即可; + xxl-job-executor-sample-spring:项目编译打包成WAR包,并部署到tomcat中。 + xxl-job-executor-sample-jfinal:同上 + xxl-job-executor-sample-nutz:同上 + 至此“执行器”项目已经部署结束。 #### 步骤五:执行器集群(可选): 执行器支持集群部署,提升调度系统可用性,同时提升任务处理能力。 -集群部署唯一要求为:保证集群中每个执行器的配置项 "xxl.job.admin.addresses/调度中心地址" 保持一致,执行器根据该配置进行执行器自动注册等操作。 +执行器集群部署时,几点要求和建议: +- 执行器回调地址(xxl.job.admin.addresses)需要保持一致;执行器根据该配置进行执行器自动注册等操作。 +- 同一个执行器集群内AppName(xxl.job.executor.appname)需要保持一致;调度中心根据该配置动态发现不同集群的在线执行器列表。 ### 2.5 开发第一个任务“Hello World” @@ -602,7 +631,7 @@ XXL-JOB首先定制了Quartz原生表结构前缀(XXL_JOB_QRTZ_)。 #### 5.3.3 架构图 -![输入图片说明](https://static.oschina.net/uploads/img/201707/17190028_aEE2.png "在这里输入图片标题") +![输入图片说明](https://static.oschina.net/uploads/img/201801/03103007_Qohm.png "在这里输入图片标题") ### 5.4 调度模块剖析 #### 5.4.1 quartz的不足 @@ -639,15 +668,22 @@ org.quartz.threadPool.threadPriority: 5 org.quartz.threadPool.threadsInheritContextClassLoaderOfInitializingThread: true ``` -XXL-JOB系统中业务逻辑在远程执行器执行,调度中心每次触发调度时仅发送一次调度请求,执行器会将请求存入执行队列并且立即响应调度中心;相比直接在quartz的QuartzJobBean中执行业务逻辑,极大的降低了调度线程占用; +XXL-JOB系统中业务逻辑在远程执行器执行,全异步化设计,调度中心每次触发调度时仅发送一次调度请求,执行器会将请求存入执行队列并且立即响应调度中心,异步运行;相比直接在quartz的QuartzJobBean中执行业务逻辑,极大的降低了调度线程占用时间; XXL-JOB调度中心中每个JOB逻辑非常 “轻”,单个JOB一次运行平均耗时基本在 "10ms" 之内(基本为一次请求的网络开销);因此,可以保证使用有限的线程支撑大量的JOB并发运行; -理论上采用推荐机器配置 "4核4G内存"情况下,单线程可以承担 100(quartz最小时间粒度1000ms/触发一次任务耗时10ms)个密集任务(每秒执行一次)的正常调度触发。因此,默认配置的15个线程理论上可以承担起1500个密集任务的正常运行。 +理论支撑任务量公式如下: + + 理论支撑任务量 = 线程数配置 / 平均调度频率(每秒) * 平均触发耗时(单位s) + +理论上采用推荐机器配置 "4核4G内存" + "配置1s运行1次密集任务" + "调度中心与执行器ping延迟10ms(0.01s)" 的情况下, + + - 单线程支撑任务量 :1 / 1 * 0.01 = 100个任务 + - 15个线程支撑任务量:15 / 1 * 0.01 = 1500个任务 -实际场景中,调度请求网络耗时不同、DB读写耗时不同、任务密集或稀疏调度情况不同,会导致任务量上限会上下波动。 +实际场景中,由于调度中心与执行器ping延迟不同、DB读写耗时不同、任务调度密集程度不同,会导致任务量上限会上下波动。 -如若需要支撑更多的任务量,可以通过 "调大调度线程数" 和 "提升机器配置" 两种方式实现。 +如若需要支撑更多的任务量,可以通过 "调大调度线程数" 、"降低调度中心与执行器ping延迟" 和 "提升机器配置" 几种方式实现。 #### 5.4.5 @DisallowConcurrentExecution XXL-JOB调度模块的“调度中心”默认不使用该注解,即默认开启并行机制,因为RemoteHttpJobBean为公共QuartzJobBean,这样在多线程调度的情况下,调度模块被阻塞的几率很低,大大提高了调度系统的承载量。 @@ -1074,7 +1110,7 @@ Tips: 历史版本(V1.3.x)目前已经Release至稳定版本, 进入维护阶段 - 9、调度中心任务监控线程销毁时,批量对失败任务告警,防止告警信息丢失; - 10、任务日志文件路径时间戳格式化时SimpleDateFormat并发问题解决; -### 6.20 版本 V1.9.0 特性[迭代中] +### 6.20 版本 V1.9.0 特性[2017-12-29] - 1、新增Nutz执行器Sample示例项目; - 2、新增任务运行模式 "GLUE模式(NodeJS) ",支持NodeJS脚本任务; - 3、脚本任务Shell、Python和Nodejs等支持获取分片参数; @@ -1084,13 +1120,13 @@ Tips: 历史版本(V1.3.x)目前已经Release至稳定版本, 进入维护阶段 - 7、调度报表优化,支持时间区间筛选; - 8、Log组件支持输出异常栈信息,底层实现优化; - 9、告警邮件样式优化,调整为表格形式,邮件组件调整为commons-email简化邮件操作; -- 10、项目依赖升级,如spring、jackson等; +- 10、项目依赖全量升级至较新稳定版本,如spring、jackson等等; - 11、任务日志,记录发起调度的机器信息; - 12、交互优化,如登陆注销; - 13、任务Cron长度扩展支持至128位,支持负责类型Cron设置; - 14、执行器地址录入交互优化,地址长度扩展支持至512位,支持大规模执行器集群配置; - 15、任务参数“IJobHandler.execute”入参改为“String params”,增强入参通用性。 -- 16、JobHandler提供init/destroy方法,支持在JobHandler初始化和销毁时进行附加操作; +- 16、IJobHandler提供init/destroy方法,支持在相应任务线程初始化和销毁时进行附加操作; - 17、任务注解调整为 “@JobHandler”,与任务抽象接口统一; - 18、修复任务监控线程被耗时任务阻塞的问题; - 19、修复任务监控线程无法监控任务触发和执行状态均未0的问题; @@ -1104,6 +1140,24 @@ Tips: 历史版本(V1.3.x)目前已经Release至稳定版本, 进入维护阶段 - 27、系统安全性优化,登陆Token写Cookie时进行MD5加密,同时Cookie启用HttpOnly; - 28、新增"任务ID"属性,移除"JobKey"属性,前者承担所有功能,方便后续增强任务依赖功能。 - 29、任务循环依赖问题修复,避免子任务与父任务重复导致的调度死循环; +- 30、任务列表新增筛选条件 "任务描述",快速检索任务; +- 31、执行器Log文件定期清理功能:执行器新增配置项("xxl.job.executor.logretentiondays")日志保存天数,日志文件过期自动删除。 + +### 6.21 版本 V1.9.1 特性[2018-02-22] +- 1、国际化:调度中心实现国际化,支持中文、英文两种语言,默认为中文。 +- 2、调度报表新增"运行中"中状态项; +- 3、调度报表优化,报表SQL调优并且新增LocalCache缓存(缓存时间60s),提高大数据量下报表加载速度; +- 4、修复打包部署时资源文件乱码问题; +- 5、修复新版本chrome滚动到顶部失效问题; +- 6、调度中心配置加载优化,取消对配置文件名的强依赖,支持加载磁盘配置; +- 7、修复脚本任务Log文件未正常close的问题; +- 8、项目依赖全量升级至较新稳定版本,如spring、jackson等等; + +### 6.22 版本 V1.9.2 特性[迭代中] +- 1、[迭代中]支持通过API服务操作任务信息; +- 2、[迭代中]任务告警逻辑调整:任务调度,以及任务回调失败时,均推送监控队列。后期考虑通过任务Log字段控制告警状态; +- 3、[迭代中]任务超时设置,超时任务主动终止; + ### TODO LIST - 1、任务权限管理:执行器为粒度分配权限,核心操作校验权限; @@ -1114,12 +1168,18 @@ Tips: 历史版本(V1.3.x)目前已经Release至稳定版本, 进入维护阶段 - 6、调度任务优先级; - 7、移除quartz依赖,重写调度模块:新增或恢复任务时将下次执行记录插入delayqueue,调度中心集群竞争分布式锁,成功节点批量加载到期delayqueue数据,批量执行。 - 8、springboot 和 docker镜像,并且推送docker镜像到中央仓库,更进一步实现产品开箱即用; -- 9、国际化:调度中心界面。 -- 10、任务告警逻辑调整:任务调度,以及任务回调失败时,均推送监控队列。后期考虑通过任务Log字段控制告警状态; -- 11、执行器Log清理功能:调度中心Log删除时同步删除执行器中的Log文件; -- 12、Bean模式任务,JobHandler自动从执行器中查询展示为下拉框,选择后自动填充任务名称等属性; -- 13、API事件触发类型任务(更类似MQ消息)支持"动态传参、延时消费";该类型任务不走Quartz,单独建立MQ消息表,调度中心竞争触发; -- 14、任务依赖增强,新增任务类型 "流程任务",流程节点可挂载普通类型任务,承担任务依赖功能。现有子任务模型取消;需要考虑任务依赖死循环问题; +- 9、多数据库支持; +- 10、执行器Log清理功能:调度中心Log删除时同步删除执行器中的Log文件; +- 11、Bean模式任务,JobHandler自动从执行器中查询展示为下拉框,选择后自动填充任务名称等属性; +- 12、API事件触发类型任务(更类似MQ消息)支持"动态传参、延时消费";该类型任务不走Quartz,单独建立MQ消息表,调度中心竞争触发; +- 13、任务依赖增强,新增任务类型 "流程任务",流程节点可挂载普通类型任务,承担任务依赖功能。现有子任务模型取消;需要考虑任务依赖死循环问题; +- 14、分片任务某一分片失败,支持分片转移; +- 15、调度中心触发任务后,先推送触发队列,异步触发,然后立即返回。降低quartz线程占用时长。 +- 16、新增API服务 "XxlJobService" ,支持通过API服务来维护管理任务信息; +- 17、新增任务默认运行状态,任务更新时运行状态保持不变; +- 18、告警邮件中展示失败告警信息; +- 19、提供多版本执行器:不依赖容器版本、不内嵌Jetty版本(通过配置executoraddress替换jetty通讯)等; + ## 七、其他 @@ -1138,4 +1198,4 @@ Tips: 历史版本(V1.3.x)目前已经Release至稳定版本, 进入维护阶段 --- ### 捐赠 -无论金额多少都足够表达您这份心意,非常感谢 :) [前往捐赠](http://www.xuxueli.com/page/donate.html ) +无论捐赠金额多少都足够表达您这份心意,非常感谢 :) [前往捐赠](http://www.xuxueli.com/page/donate.html ) diff --git a/doc/XXL-JOB架构图.pptx b/doc/XXL-JOB架构图.pptx index de490c44..2f9c9520 100644 Binary files a/doc/XXL-JOB架构图.pptx and b/doc/XXL-JOB架构图.pptx differ diff --git a/pom.xml b/pom.xml index f7f3d33c..4a6c432e 100644 --- a/pom.xml +++ b/pom.xml @@ -3,7 +3,7 @@ 4.0.0 com.xuxueli xxl-job - 1.9.0-SNAPSHOT + 1.9.2-SNAPSHOT pom ${project.artifactId} @@ -20,16 +20,16 @@ 3.0.1 2.2 - 4.3.13.RELEASE - 2.9.3 + 4.3.14.RELEASE + 2.9.4 1.8.13 1.7.25 2.3.23 4.12 - 9.2.22.v20170606 + 9.2.24.v20180105 4.0.51 - 4.5.4 + 4.5.5 1.3 4.1 @@ -44,7 +44,7 @@ 2.4.13 2.3.0 - 1.5.9.RELEASE + 1.5.10.RELEASE @@ -59,6 +59,14 @@ UTF8 + + org.apache.maven.plugins + maven-resources-plugin + 2.6 + + UTF-8 + + org.apache.maven.plugins maven-war-plugin diff --git a/xxl-job-admin/pom.xml b/xxl-job-admin/pom.xml index 2498c234..3e0f4c34 100644 --- a/xxl-job-admin/pom.xml +++ b/xxl-job-admin/pom.xml @@ -4,7 +4,7 @@ com.xuxueli xxl-job - 1.9.0-SNAPSHOT + 1.9.2-SNAPSHOT xxl-job-admin war diff --git a/xxl-job-admin/src/main/java/com/xxl/job/admin/controller/IndexController.java b/xxl-job-admin/src/main/java/com/xxl/job/admin/controller/IndexController.java index f4b1cadd..8dc6b754 100644 --- a/xxl-job-admin/src/main/java/com/xxl/job/admin/controller/IndexController.java +++ b/xxl-job-admin/src/main/java/com/xxl/job/admin/controller/IndexController.java @@ -2,6 +2,7 @@ package com.xxl.job.admin.controller; import com.xxl.job.admin.controller.annotation.PermessionLimit; import com.xxl.job.admin.controller.interceptor.PermissionInterceptor; +import com.xxl.job.admin.core.util.I18nUtil; import com.xxl.job.admin.service.XxlJobService; import com.xxl.job.core.biz.model.ReturnT; import org.apache.commons.lang3.StringUtils; @@ -40,11 +41,11 @@ public class IndexController { return "index"; } - @RequestMapping("/triggerChartDate") + @RequestMapping("/chartInfo") @ResponseBody - public ReturnT> triggerChartDate(Date startDate, Date endDate) { - ReturnT> triggerChartDate = xxlJobService.triggerChartDate(startDate, endDate); - return triggerChartDate; + public ReturnT> chartInfo(Date startDate, Date endDate) { + ReturnT> chartInfo = xxlJobService.chartInfo(startDate, endDate); + return chartInfo; } @RequestMapping("/toLogin") @@ -67,14 +68,14 @@ public class IndexController { // param if (StringUtils.isBlank(userName) || StringUtils.isBlank(password)){ - return new ReturnT(500, "账号或密码为空"); + return new ReturnT(500, I18nUtil.getString("login_param_empty")); } boolean ifRem = (StringUtils.isNotBlank(ifRemember) && "on".equals(ifRemember))?true:false; // do login boolean loginRet = PermissionInterceptor.login(response, userName, password, ifRem); if (!loginRet) { - return new ReturnT(500, "账号或密码错误"); + return new ReturnT(500, I18nUtil.getString("login_param_unvalid")); } return ReturnT.SUCCESS; } diff --git a/xxl-job-admin/src/main/java/com/xxl/job/admin/controller/JobCodeController.java b/xxl-job-admin/src/main/java/com/xxl/job/admin/controller/JobCodeController.java index e41f8e8d..2cb59de7 100644 --- a/xxl-job-admin/src/main/java/com/xxl/job/admin/controller/JobCodeController.java +++ b/xxl-job-admin/src/main/java/com/xxl/job/admin/controller/JobCodeController.java @@ -2,6 +2,7 @@ package com.xxl.job.admin.controller; import com.xxl.job.admin.core.model.XxlJobInfo; import com.xxl.job.admin.core.model.XxlJobLogGlue; +import com.xxl.job.admin.core.util.I18nUtil; import com.xxl.job.admin.dao.XxlJobInfoDao; import com.xxl.job.admin.dao.XxlJobLogGlueDao; import com.xxl.job.core.biz.model.ReturnT; @@ -34,10 +35,10 @@ public class JobCodeController { List jobLogGlues = xxlJobLogGlueDao.findByJobId(jobId); if (jobInfo == null) { - throw new RuntimeException("抱歉,任务不存在."); + throw new RuntimeException(I18nUtil.getString("jobinfo_glue_jobid_unvalid")); } if (GlueTypeEnum.BEAN == GlueTypeEnum.match(jobInfo.getGlueType())) { - throw new RuntimeException("该任务非GLUE模式."); + throw new RuntimeException(I18nUtil.getString("jobinfo_glue_gluetype_unvalid")); } // Glue类型-字典 @@ -53,14 +54,14 @@ public class JobCodeController { public ReturnT save(Model model, int id, String glueSource, String glueRemark) { // valid if (glueRemark==null) { - return new ReturnT(500, "请输入备注"); + return new ReturnT(500, (I18nUtil.getString("system_please_input") + I18nUtil.getString("jobinfo_glue_remark")) ); } if (glueRemark.length()<4 || glueRemark.length()>100) { - return new ReturnT(500, "备注长度应该在4至100之间"); + return new ReturnT(500, I18nUtil.getString("jobinfo_glue_remark_limit")); } XxlJobInfo exists_jobInfo = xxlJobInfoDao.loadById(id); if (exists_jobInfo == null) { - return new ReturnT(500, "参数异常"); + return new ReturnT(500, I18nUtil.getString("jobinfo_glue_jobid_unvalid")); } // update new code diff --git a/xxl-job-admin/src/main/java/com/xxl/job/admin/controller/JobGroupController.java b/xxl-job-admin/src/main/java/com/xxl/job/admin/controller/JobGroupController.java index 6211f3c7..c7a86cf0 100644 --- a/xxl-job-admin/src/main/java/com/xxl/job/admin/controller/JobGroupController.java +++ b/xxl-job-admin/src/main/java/com/xxl/job/admin/controller/JobGroupController.java @@ -1,6 +1,7 @@ package com.xxl.job.admin.controller; import com.xxl.job.admin.core.model.XxlJobGroup; +import com.xxl.job.admin.core.util.I18nUtil; import com.xxl.job.admin.dao.XxlJobGroupDao; import com.xxl.job.admin.dao.XxlJobInfoDao; import com.xxl.job.core.biz.model.ReturnT; @@ -42,22 +43,22 @@ public class JobGroupController { // valid if (xxlJobGroup.getAppName()==null || StringUtils.isBlank(xxlJobGroup.getAppName())) { - return new ReturnT(500, "请输入AppName"); + return new ReturnT(500, (I18nUtil.getString("system_please_input")+"AppName") ); } - if (xxlJobGroup.getAppName().length()>64) { - return new ReturnT(500, "AppName长度限制为4~64"); + if (xxlJobGroup.getAppName().length()<4 || xxlJobGroup.getAppName().length()>64) { + return new ReturnT(500, I18nUtil.getString("jobgroup_field_appName_length") ); } if (xxlJobGroup.getTitle()==null || StringUtils.isBlank(xxlJobGroup.getTitle())) { - return new ReturnT(500, "请输入名称"); + return new ReturnT(500, (I18nUtil.getString("system_please_input") + I18nUtil.getString("jobgroup_field_title")) ); } if (xxlJobGroup.getAddressType()!=0) { if (StringUtils.isBlank(xxlJobGroup.getAddressList())) { - return new ReturnT(500, "手动录入注册方式,机器地址不可为空"); + return new ReturnT(500, I18nUtil.getString("jobgroup_field_addressType_limit") ); } String[] addresss = xxlJobGroup.getAddressList().split(","); for (String item: addresss) { if (StringUtils.isBlank(item)) { - return new ReturnT(500, "机器地址非法"); + return new ReturnT(500, I18nUtil.getString("jobgroup_field_registryList_unvalid") ); } } } @@ -71,22 +72,22 @@ public class JobGroupController { public ReturnT update(XxlJobGroup xxlJobGroup){ // valid if (xxlJobGroup.getAppName()==null || StringUtils.isBlank(xxlJobGroup.getAppName())) { - return new ReturnT(500, "请输入AppName"); + return new ReturnT(500, (I18nUtil.getString("system_please_input")+"AppName") ); } - if (xxlJobGroup.getAppName().length()>64) { - return new ReturnT(500, "AppName长度限制为4~64"); + if (xxlJobGroup.getAppName().length()<4 || xxlJobGroup.getAppName().length()>64) { + return new ReturnT(500, I18nUtil.getString("jobgroup_field_appName_length") ); } if (xxlJobGroup.getTitle()==null || StringUtils.isBlank(xxlJobGroup.getTitle())) { - return new ReturnT(500, "请输入名称"); + return new ReturnT(500, (I18nUtil.getString("system_please_input") + I18nUtil.getString("jobgroup_field_title")) ); } if (xxlJobGroup.getAddressType()!=0) { if (StringUtils.isBlank(xxlJobGroup.getAddressList())) { - return new ReturnT(500, "手动录入注册方式,机器地址不可为空"); + return new ReturnT(500, I18nUtil.getString("jobgroup_field_addressType_limit") ); } String[] addresss = xxlJobGroup.getAddressList().split(","); for (String item: addresss) { if (StringUtils.isBlank(item)) { - return new ReturnT(500, "机器地址非法"); + return new ReturnT(500, I18nUtil.getString("jobgroup_field_registryList_unvalid") ); } } } @@ -100,14 +101,14 @@ public class JobGroupController { public ReturnT remove(int id){ // valid - int count = xxlJobInfoDao.pageListCount(0, 10, id, null); + int count = xxlJobInfoDao.pageListCount(0, 10, id, null, null); if (count > 0) { - return new ReturnT(500, "该分组使用中, 不可删除"); + return new ReturnT(500, I18nUtil.getString("jobgroup_del_limit_0") ); } List allList = xxlJobGroupDao.findAll(); if (allList.size() == 1) { - return new ReturnT(500, "删除失败, 系统需要至少预留一个默认分组"); + return new ReturnT(500, I18nUtil.getString("jobgroup_del_limit_1") ); } int ret = xxlJobGroupDao.remove(id); diff --git a/xxl-job-admin/src/main/java/com/xxl/job/admin/controller/JobInfoController.java b/xxl-job-admin/src/main/java/com/xxl/job/admin/controller/JobInfoController.java index 0962d397..09eedb60 100644 --- a/xxl-job-admin/src/main/java/com/xxl/job/admin/controller/JobInfoController.java +++ b/xxl-job-admin/src/main/java/com/xxl/job/admin/controller/JobInfoController.java @@ -53,9 +53,9 @@ public class JobInfoController { @ResponseBody public Map pageList(@RequestParam(required = false, defaultValue = "0") int start, @RequestParam(required = false, defaultValue = "10") int length, - int jobGroup, String executorHandler, String filterTime) { + int jobGroup, String jobDesc, String executorHandler, String filterTime) { - return xxlJobService.pageList(start, length, jobGroup, executorHandler, filterTime); + return xxlJobService.pageList(start, length, jobGroup, jobDesc, executorHandler, filterTime); } @RequestMapping("/add") @@ -64,10 +64,10 @@ public class JobInfoController { return xxlJobService.add(jobInfo); } - @RequestMapping("/reschedule") + @RequestMapping("/update") @ResponseBody - public ReturnT reschedule(XxlJobInfo jobInfo) { - return xxlJobService.reschedule(jobInfo); + public ReturnT update(XxlJobInfo jobInfo) { + return xxlJobService.update(jobInfo); } @RequestMapping("/remove") diff --git a/xxl-job-admin/src/main/java/com/xxl/job/admin/controller/JobLogController.java b/xxl-job-admin/src/main/java/com/xxl/job/admin/controller/JobLogController.java index a31536cf..0b856df2 100644 --- a/xxl-job-admin/src/main/java/com/xxl/job/admin/controller/JobLogController.java +++ b/xxl-job-admin/src/main/java/com/xxl/job/admin/controller/JobLogController.java @@ -4,12 +4,14 @@ import com.xxl.job.admin.core.model.XxlJobGroup; import com.xxl.job.admin.core.model.XxlJobInfo; import com.xxl.job.admin.core.model.XxlJobLog; import com.xxl.job.admin.core.schedule.XxlJobDynamicScheduler; +import com.xxl.job.admin.core.util.I18nUtil; import com.xxl.job.admin.dao.XxlJobGroupDao; import com.xxl.job.admin.dao.XxlJobInfoDao; import com.xxl.job.admin.dao.XxlJobLogDao; import com.xxl.job.core.biz.ExecutorBiz; import com.xxl.job.core.biz.model.LogResult; import com.xxl.job.core.biz.model.ReturnT; +import com.xxl.job.core.glue.GlueTypeEnum; import com.xxl.job.core.rpc.netcom.NetComClientProxy; import org.apache.commons.lang3.StringUtils; import org.apache.commons.lang3.time.DateUtils; @@ -50,6 +52,7 @@ public class JobLogController { // 执行器列表 List jobGroupList = xxlJobGroupDao.findAll(); model.addAttribute("JobGroupList", jobGroupList); + model.addAttribute("GlueTypeEnum", GlueTypeEnum.values()); // 任务 if (jobId > 0) { @@ -105,7 +108,7 @@ public class JobLogController { ReturnT logStatue = ReturnT.SUCCESS; XxlJobLog jobLog = xxlJobLogDao.load(id); if (jobLog == null) { - throw new RuntimeException("抱歉,日志ID非法."); + throw new RuntimeException(I18nUtil.getString("joblog_logid_unvalid")); } model.addAttribute("triggerCode", jobLog.getTriggerCode()); @@ -145,10 +148,10 @@ public class JobLogController { XxlJobLog log = xxlJobLogDao.load(id); XxlJobInfo jobInfo = xxlJobInfoDao.loadById(log.getJobId()); if (jobInfo==null) { - return new ReturnT(500, "参数异常"); + return new ReturnT(500, I18nUtil.getString("jobinfo_glue_jobid_unvalid")); } if (ReturnT.SUCCESS_CODE != log.getTriggerCode()) { - return new ReturnT(500, "调度失败,无法终止日志"); + return new ReturnT(500, I18nUtil.getString("joblog_kill_log_limit")); } // request of kill @@ -163,7 +166,7 @@ public class JobLogController { if (ReturnT.SUCCESS_CODE == runResult.getCode()) { log.setHandleCode(ReturnT.FAIL_CODE); - log.setHandleMsg("人为操作主动终止:" + (runResult.getMsg()!=null?runResult.getMsg():"")); + log.setHandleMsg( I18nUtil.getString("joblog_kill_log_byman")+":" + (runResult.getMsg()!=null?runResult.getMsg():"")); log.setHandleTime(new Date()); xxlJobLogDao.updateHandleInfo(log); return new ReturnT(runResult.getMsg()); @@ -197,7 +200,7 @@ public class JobLogController { } else if (type == 9) { clearBeforeNum = 0; // 清理所有日志数据 } else { - return new ReturnT(ReturnT.FAIL_CODE, "清理类型参数异常"); + return new ReturnT(ReturnT.FAIL_CODE, I18nUtil.getString("joblog_clean_type_unvalid")); } xxlJobLogDao.clearLog(jobGroup, jobId, clearBeforeTime, clearBeforeNum); diff --git a/xxl-job-admin/src/main/java/com/xxl/job/admin/controller/interceptor/CookieInterceptor.java b/xxl-job-admin/src/main/java/com/xxl/job/admin/controller/interceptor/CookieInterceptor.java index bd84534f..9337d1b0 100644 --- a/xxl-job-admin/src/main/java/com/xxl/job/admin/controller/interceptor/CookieInterceptor.java +++ b/xxl-job-admin/src/main/java/com/xxl/job/admin/controller/interceptor/CookieInterceptor.java @@ -1,5 +1,7 @@ package com.xxl.job.admin.controller.interceptor; +import com.xxl.job.admin.core.util.FtlUtil; +import com.xxl.job.admin.core.util.I18nUtil; import org.apache.commons.lang3.ArrayUtils; import org.springframework.web.servlet.ModelAndView; import org.springframework.web.servlet.handler.HandlerInterceptorAdapter; @@ -19,7 +21,8 @@ public class CookieInterceptor extends HandlerInterceptorAdapter { @Override public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception { - + + // cookie if (modelAndView!=null && ArrayUtils.isNotEmpty(request.getCookies())) { HashMap cookieMap = new HashMap(); for (Cookie ck : request.getCookies()) { @@ -27,6 +30,11 @@ public class CookieInterceptor extends HandlerInterceptorAdapter { } modelAndView.addObject("cookieMap", cookieMap); } + + // static method + if (modelAndView != null) { + modelAndView.addObject("I18nUtil", FtlUtil.generateStaticModel(I18nUtil.class.getName())); + } super.postHandle(request, response, handler, modelAndView); } diff --git a/xxl-job-admin/src/main/java/com/xxl/job/admin/controller/interceptor/PermissionInterceptor.java b/xxl-job-admin/src/main/java/com/xxl/job/admin/controller/interceptor/PermissionInterceptor.java index 1d6facf5..b72cf99e 100644 --- a/xxl-job-admin/src/main/java/com/xxl/job/admin/controller/interceptor/PermissionInterceptor.java +++ b/xxl-job-admin/src/main/java/com/xxl/job/admin/controller/interceptor/PermissionInterceptor.java @@ -1,8 +1,8 @@ package com.xxl.job.admin.controller.interceptor; import com.xxl.job.admin.controller.annotation.PermessionLimit; +import com.xxl.job.admin.core.conf.XxlJobAdminConfig; import com.xxl.job.admin.core.util.CookieUtil; -import com.xxl.job.admin.core.util.PropertiesUtil; import org.apache.commons.codec.digest.DigestUtils; import org.springframework.web.method.HandlerMethod; import org.springframework.web.servlet.handler.HandlerInterceptorAdapter; @@ -22,8 +22,8 @@ public class PermissionInterceptor extends HandlerInterceptorAdapter { public static final String LOGIN_IDENTITY_KEY = "XXL_JOB_LOGIN_IDENTITY"; public static final String LOGIN_IDENTITY_TOKEN; static { - String username = PropertiesUtil.getString("xxl.job.login.username"); - String password = PropertiesUtil.getString("xxl.job.login.password"); + String username = XxlJobAdminConfig.getAdminConfig().getLoginUsername(); + String password = XxlJobAdminConfig.getAdminConfig().getLoginPassword(); // login token String tokenTmp = DigestUtils.md5Hex(username + "_" + password); diff --git a/xxl-job-admin/src/main/java/com/xxl/job/admin/controller/resolver/WebExceptionResolver.java b/xxl-job-admin/src/main/java/com/xxl/job/admin/controller/resolver/WebExceptionResolver.java index e8928f5f..0ceb9089 100644 --- a/xxl-job-admin/src/main/java/com/xxl/job/admin/controller/resolver/WebExceptionResolver.java +++ b/xxl-job-admin/src/main/java/com/xxl/job/admin/controller/resolver/WebExceptionResolver.java @@ -11,6 +11,7 @@ import org.springframework.web.servlet.ModelAndView; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; +import java.io.IOException; /** * common exception resolver @@ -23,19 +24,34 @@ public class WebExceptionResolver implements HandlerExceptionResolver { public ModelAndView resolveException(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) { logger.error("WebExceptionResolver:{}", ex); - - ModelAndView mv = new ModelAndView(); + + // if json + boolean isJson = false; HandlerMethod method = (HandlerMethod)handler; ResponseBody responseBody = method.getMethodAnnotation(ResponseBody.class); if (responseBody != null) { - response.setContentType("application/json;charset=UTF-8"); - mv.addObject("result", JacksonUtil.writeValueAsString(new ReturnT(500, ex.toString().replaceAll("\n", "
")))); - mv.setViewName("/common/common.result"); + isJson = true; + } + + // error result + ReturnT errorResult = new ReturnT(ReturnT.FAIL_CODE, ex.toString().replaceAll("\n", "
")); + + // response + ModelAndView mv = new ModelAndView(); + if (isJson) { + try { + response.setContentType("application/json;charset=utf-8"); + response.getWriter().print(JacksonUtil.writeValueAsString(errorResult)); + } catch (IOException e) { + logger.error(e.getMessage(), e); + } + return mv; } else { - mv.addObject("exceptionMsg", ex.toString().replaceAll("\n", "
")); + + mv.addObject("exceptionMsg", errorResult.getMsg()); mv.setViewName("/common/common.exception"); + return mv; } - return mv; } } \ No newline at end of file diff --git a/xxl-job-admin/src/main/java/com/xxl/job/admin/core/conf/XxlJobAdminConfig.java b/xxl-job-admin/src/main/java/com/xxl/job/admin/core/conf/XxlJobAdminConfig.java new file mode 100644 index 00000000..adcd321c --- /dev/null +++ b/xxl-job-admin/src/main/java/com/xxl/job/admin/core/conf/XxlJobAdminConfig.java @@ -0,0 +1,81 @@ +package com.xxl.job.admin.core.conf; + +import org.springframework.beans.factory.InitializingBean; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.context.annotation.Configuration; + +/** + * xxl-job config + * + * @author xuxueli 2017-04-28 + */ +@Configuration +public class XxlJobAdminConfig implements InitializingBean{ + private static XxlJobAdminConfig adminConfig = null; + public static XxlJobAdminConfig getAdminConfig() { + return adminConfig; + } + + @Override + public void afterPropertiesSet() throws Exception { + adminConfig = this; + } + + @Value("${xxl.job.mail.host}") + private String mailHost; + + @Value("${xxl.job.mail.port}") + private String mailPort; + + @Value("${xxl.job.mail.username}") + private String mailUsername; + + @Value("${xxl.job.mail.password}") + private String mailPassword; + + @Value("${xxl.job.mail.sendNick}") + private String mailSendNick; + + @Value("${xxl.job.login.username}") + private String loginUsername; + + @Value("${xxl.job.login.password}") + private String loginPassword; + + @Value("${xxl.job.i18n}") + private String i18n; + + + public String getMailHost() { + return mailHost; + } + + public String getMailPort() { + return mailPort; + } + + public String getMailUsername() { + return mailUsername; + } + + public String getMailPassword() { + return mailPassword; + } + + public String getMailSendNick() { + return mailSendNick; + } + + public String getLoginUsername() { + return loginUsername; + } + + public String getLoginPassword() { + return loginPassword; + } + + public String getI18n() { + return i18n; + } + +} diff --git a/xxl-job-admin/src/main/java/com/xxl/job/admin/core/enums/ExecutorFailStrategyEnum.java b/xxl-job-admin/src/main/java/com/xxl/job/admin/core/enums/ExecutorFailStrategyEnum.java index ea715233..c6db0407 100644 --- a/xxl-job-admin/src/main/java/com/xxl/job/admin/core/enums/ExecutorFailStrategyEnum.java +++ b/xxl-job-admin/src/main/java/com/xxl/job/admin/core/enums/ExecutorFailStrategyEnum.java @@ -1,13 +1,15 @@ package com.xxl.job.admin.core.enums; +import com.xxl.job.admin.core.util.I18nUtil; + /** * Created by xuxueli on 17/5/9. */ public enum ExecutorFailStrategyEnum { - FAIL_ALARM("失败告警"), + FAIL_ALARM(I18nUtil.getString("jobconf_fail_alarm")), - FAIL_RETRY("失败重试"); + FAIL_RETRY(I18nUtil.getString("jobconf_fail_retry")); private final String title; private ExecutorFailStrategyEnum(String title) { diff --git a/xxl-job-admin/src/main/java/com/xxl/job/admin/core/route/ExecutorRouteStrategyEnum.java b/xxl-job-admin/src/main/java/com/xxl/job/admin/core/route/ExecutorRouteStrategyEnum.java index 9e82ef56..7fff93a9 100644 --- a/xxl-job-admin/src/main/java/com/xxl/job/admin/core/route/ExecutorRouteStrategyEnum.java +++ b/xxl-job-admin/src/main/java/com/xxl/job/admin/core/route/ExecutorRouteStrategyEnum.java @@ -1,22 +1,23 @@ package com.xxl.job.admin.core.route; import com.xxl.job.admin.core.route.strategy.*; +import com.xxl.job.admin.core.util.I18nUtil; /** * Created by xuxueli on 17/3/10. */ public enum ExecutorRouteStrategyEnum { - FIRST("第一个", new ExecutorRouteFirst()), - LAST("最后一个", new ExecutorRouteLast()), - ROUND("轮询", new ExecutorRouteRound()), - RANDOM("随机", new ExecutorRouteRandom()), - CONSISTENT_HASH("一致性HASH", new ExecutorRouteConsistentHash()), - LEAST_FREQUENTLY_USED("最不经常使用", new ExecutorRouteLFU()), - LEAST_RECENTLY_USED("最近最久未使用", new ExecutorRouteLRU()), - FAILOVER("故障转移", new ExecutorRouteFailover()), - BUSYOVER("忙碌转移", new ExecutorRouteBusyover()), - SHARDING_BROADCAST("分片广播", null); + FIRST(I18nUtil.getString("jobconf_route_first"), new ExecutorRouteFirst()), + LAST(I18nUtil.getString("jobconf_route_last"), new ExecutorRouteLast()), + ROUND(I18nUtil.getString("jobconf_route_round"), new ExecutorRouteRound()), + RANDOM(I18nUtil.getString("jobconf_route_random"), new ExecutorRouteRandom()), + CONSISTENT_HASH(I18nUtil.getString("jobconf_route_consistenthash"), new ExecutorRouteConsistentHash()), + LEAST_FREQUENTLY_USED(I18nUtil.getString("jobconf_route_lfu"), new ExecutorRouteLFU()), + LEAST_RECENTLY_USED(I18nUtil.getString("jobconf_route_lru"), new ExecutorRouteLRU()), + FAILOVER(I18nUtil.getString("jobconf_route_failover"), new ExecutorRouteFailover()), + BUSYOVER(I18nUtil.getString("jobconf_route_busyover"), new ExecutorRouteBusyover()), + SHARDING_BROADCAST(I18nUtil.getString("jobconf_route_shard"), null); ExecutorRouteStrategyEnum(String title, ExecutorRouter router) { this.title = title; diff --git a/xxl-job-admin/src/main/java/com/xxl/job/admin/core/route/strategy/ExecutorRouteBusyover.java b/xxl-job-admin/src/main/java/com/xxl/job/admin/core/route/strategy/ExecutorRouteBusyover.java index f3840a02..7f355248 100644 --- a/xxl-job-admin/src/main/java/com/xxl/job/admin/core/route/strategy/ExecutorRouteBusyover.java +++ b/xxl-job-admin/src/main/java/com/xxl/job/admin/core/route/strategy/ExecutorRouteBusyover.java @@ -3,6 +3,7 @@ package com.xxl.job.admin.core.route.strategy; import com.xxl.job.admin.core.route.ExecutorRouter; import com.xxl.job.admin.core.schedule.XxlJobDynamicScheduler; import com.xxl.job.admin.core.trigger.XxlJobTrigger; +import com.xxl.job.admin.core.util.I18nUtil; import com.xxl.job.core.biz.ExecutorBiz; import com.xxl.job.core.biz.model.ReturnT; import com.xxl.job.core.biz.model.TriggerParam; @@ -33,7 +34,7 @@ public class ExecutorRouteBusyover extends ExecutorRouter { idleBeatResult = new ReturnT(ReturnT.FAIL_CODE, ""+e ); } idleBeatResultSB.append( (idleBeatResultSB.length()>0)?"

":"") - .append("空闲检测:") + .append(I18nUtil.getString("jobconf_idleBeat") + ":") .append("
address:").append(address) .append("
code:").append(idleBeatResult.getCode()) .append("
msg:").append(idleBeatResult.getMsg()); diff --git a/xxl-job-admin/src/main/java/com/xxl/job/admin/core/route/strategy/ExecutorRouteFailover.java b/xxl-job-admin/src/main/java/com/xxl/job/admin/core/route/strategy/ExecutorRouteFailover.java index d78cbaca..663c7f2f 100644 --- a/xxl-job-admin/src/main/java/com/xxl/job/admin/core/route/strategy/ExecutorRouteFailover.java +++ b/xxl-job-admin/src/main/java/com/xxl/job/admin/core/route/strategy/ExecutorRouteFailover.java @@ -3,6 +3,7 @@ package com.xxl.job.admin.core.route.strategy; import com.xxl.job.admin.core.route.ExecutorRouter; import com.xxl.job.admin.core.schedule.XxlJobDynamicScheduler; import com.xxl.job.admin.core.trigger.XxlJobTrigger; +import com.xxl.job.admin.core.util.I18nUtil; import com.xxl.job.core.biz.ExecutorBiz; import com.xxl.job.core.biz.model.ReturnT; import com.xxl.job.core.biz.model.TriggerParam; @@ -33,7 +34,7 @@ public class ExecutorRouteFailover extends ExecutorRouter { beatResult = new ReturnT(ReturnT.FAIL_CODE, ""+e ); } beatResultSB.append( (beatResultSB.length()>0)?"

":"") - .append("心跳检测:") + .append(I18nUtil.getString("jobconf_beat") + ":") .append("
address:").append(address) .append("
code:").append(beatResult.getCode()) .append("
msg:").append(beatResult.getMsg()); diff --git a/xxl-job-admin/src/main/java/com/xxl/job/admin/core/thread/JobFailMonitorHelper.java b/xxl-job-admin/src/main/java/com/xxl/job/admin/core/thread/JobFailMonitorHelper.java index c9f7f04d..66a77346 100644 --- a/xxl-job-admin/src/main/java/com/xxl/job/admin/core/thread/JobFailMonitorHelper.java +++ b/xxl-job-admin/src/main/java/com/xxl/job/admin/core/thread/JobFailMonitorHelper.java @@ -4,6 +4,7 @@ import com.xxl.job.admin.core.model.XxlJobGroup; import com.xxl.job.admin.core.model.XxlJobInfo; import com.xxl.job.admin.core.model.XxlJobLog; import com.xxl.job.admin.core.schedule.XxlJobDynamicScheduler; +import com.xxl.job.admin.core.util.I18nUtil; import com.xxl.job.admin.core.util.MailUtil; import com.xxl.job.core.biz.model.ReturnT; import com.xxl.job.core.handler.IJobHandler; @@ -119,14 +120,14 @@ public class JobFailMonitorHelper { // ---------------------- alarm ---------------------- // email alarm template - private static final String mailBodyTemplate = "

监控告警明细:" + + private static final String mailBodyTemplate = "
" + I18nUtil.getString("jobconf_monitor_detail") + ":" + "\n" + " " + " \n" + - " \n" + - " \n" + - " \n" + - " \n" + + " \n" + + " \n" + + " \n" + + " \n" + " \n" + " \n" + " \n" + @@ -134,7 +135,7 @@ public class JobFailMonitorHelper { " \n" + " \n" + " \n" + - " \n" + + " \n" + " \n" + " \n" + "
执行器任务ID任务描述告警类型"+ I18nUtil.getString("jobinfo_field_jobgroup") +""+ I18nUtil.getString("jobinfo_field_id") +""+ I18nUtil.getString("jobinfo_field_jobdesc") +""+ I18nUtil.getString("jobconf_monitor_alarm_title") +"
{0}{1}{2}调度失败"+ I18nUtil.getString("jobconf_monitor_alarm_type") +"
"; @@ -154,7 +155,7 @@ public class JobFailMonitorHelper { for (String email: emailSet) { XxlJobGroup group = XxlJobDynamicScheduler.xxlJobGroupDao.load(Integer.valueOf(info.getJobGroup())); - String title = "调度中心监控报警"; + String title = I18nUtil.getString("jobconf_monitor"); String content = MessageFormat.format(mailBodyTemplate, group!=null?group.getTitle():"null", info.getId(), info.getJobDesc()); MailUtil.sendMail(email, title, content); diff --git a/xxl-job-admin/src/main/java/com/xxl/job/admin/core/trigger/XxlJobTrigger.java b/xxl-job-admin/src/main/java/com/xxl/job/admin/core/trigger/XxlJobTrigger.java index bf3c8567..a03d393c 100644 --- a/xxl-job-admin/src/main/java/com/xxl/job/admin/core/trigger/XxlJobTrigger.java +++ b/xxl-job-admin/src/main/java/com/xxl/job/admin/core/trigger/XxlJobTrigger.java @@ -7,6 +7,7 @@ import com.xxl.job.admin.core.model.XxlJobLog; import com.xxl.job.admin.core.route.ExecutorRouteStrategyEnum; import com.xxl.job.admin.core.schedule.XxlJobDynamicScheduler; import com.xxl.job.admin.core.thread.JobFailMonitorHelper; +import com.xxl.job.admin.core.util.I18nUtil; import com.xxl.job.core.biz.ExecutorBiz; import com.xxl.job.core.biz.model.ReturnT; import com.xxl.job.core.biz.model.TriggerParam; @@ -67,17 +68,18 @@ public class XxlJobTrigger { ReturnT triggerResult = new ReturnT(null); StringBuffer triggerMsgSb = new StringBuffer(); - triggerMsgSb.append("调度机器:").append(IpUtil.getIp()); - triggerMsgSb.append("
执行器-注册方式:").append( (group.getAddressType() == 0)?"自动注册":"手动录入" ); - triggerMsgSb.append("
执行器-地址列表:").append(group.getRegistryList()); - triggerMsgSb.append("
路由策略:").append(executorRouteStrategyEnum.getTitle()).append("("+i+"/"+addressList.size()+")"); // update01 - triggerMsgSb.append("
阻塞处理策略:").append(blockStrategy.getTitle()); - triggerMsgSb.append("
失败处理策略:").append(failStrategy.getTitle()); + triggerMsgSb.append(I18nUtil.getString("jobconf_trigger_admin_adress")).append(":").append(IpUtil.getIp()); + triggerMsgSb.append("
").append(I18nUtil.getString("jobconf_trigger_exe_regtype")).append(":") + .append( (group.getAddressType() == 0)?I18nUtil.getString("jobgroup_field_addressType_0"):I18nUtil.getString("jobgroup_field_addressType_1") ); + triggerMsgSb.append("
").append(I18nUtil.getString("jobconf_trigger_exe_regaddress")).append(":").append(group.getRegistryList()); + triggerMsgSb.append("
").append(I18nUtil.getString("jobinfo_field_executorRouteStrategy")).append(":").append(executorRouteStrategyEnum.getTitle()).append("("+i+"/"+addressList.size()+")"); // update01 + triggerMsgSb.append("
").append(I18nUtil.getString("jobinfo_field_executorBlockStrategy")).append(":").append(blockStrategy.getTitle()); + triggerMsgSb.append("
").append(I18nUtil.getString("jobinfo_field_executorFailStrategy")).append(":").append(failStrategy.getTitle()); // 3、trigger-valid if (triggerResult.getCode()==ReturnT.SUCCESS_CODE && CollectionUtils.isEmpty(addressList)) { triggerResult.setCode(ReturnT.FAIL_CODE); - triggerMsgSb.append("
----------------------
").append("调度失败:").append("执行器地址为空"); + triggerMsgSb.append("
----------------------
").append(I18nUtil.getString("jobconf_trigger_address_empty")); } if (triggerResult.getCode() == ReturnT.SUCCESS_CODE) { @@ -97,12 +99,12 @@ public class XxlJobTrigger { // 4.2、trigger-run (route run / trigger remote executor) triggerResult = runExecutor(triggerParam, address); // update03 - triggerMsgSb.append("

>>>>>>>>>>>触发调度<<<<<<<<<<<
").append(triggerResult.getMsg()); + triggerMsgSb.append("

>>>>>>>>>>>"+ I18nUtil.getString("jobconf_trigger_run") +"<<<<<<<<<<<
").append(triggerResult.getMsg()); // 4.3、trigger (fail retry) if (triggerResult.getCode()!=ReturnT.SUCCESS_CODE && failStrategy == ExecutorFailStrategyEnum.FAIL_RETRY) { triggerResult = runExecutor(triggerParam, address); // update04 - triggerMsgSb.append("

>>>>>>>>>>>失败重试<<<<<<<<<<<
").append(triggerResult.getMsg()); + triggerMsgSb.append("

>>>>>>>>>>>"+ I18nUtil.getString("jobconf_trigger_fail_retry") +"<<<<<<<<<<<
").append(triggerResult.getMsg()); } } @@ -112,7 +114,7 @@ public class XxlJobTrigger { jobLog.setTriggerMsg(triggerMsgSb.toString()); XxlJobDynamicScheduler.xxlJobLogDao.updateTriggerInfo(jobLog); - // 6、monitor triger + // 6、monitor trigger JobFailMonitorHelper.monitor(jobLog.getId()); logger.debug(">>>>>>>>>>> xxl-job trigger end, jobId:{}", jobLog.getId()); @@ -134,17 +136,18 @@ public class XxlJobTrigger { ReturnT triggerResult = new ReturnT(null); StringBuffer triggerMsgSb = new StringBuffer(); - triggerMsgSb.append("调度机器:").append(IpUtil.getIp()); - triggerMsgSb.append("
执行器-注册方式:").append( (group.getAddressType() == 0)?"自动注册":"手动录入" ); - triggerMsgSb.append("
执行器-地址列表:").append(group.getRegistryList()); - triggerMsgSb.append("
路由策略:").append(executorRouteStrategyEnum.getTitle()); - triggerMsgSb.append("
阻塞处理策略:").append(blockStrategy.getTitle()); - triggerMsgSb.append("
失败处理策略:").append(failStrategy.getTitle()); + triggerMsgSb.append(I18nUtil.getString("jobconf_trigger_admin_adress")).append(":").append(IpUtil.getIp()); + triggerMsgSb.append("
").append(I18nUtil.getString("jobconf_trigger_exe_regtype")).append(":") + .append( (group.getAddressType() == 0)?I18nUtil.getString("jobgroup_field_addressType_0"):I18nUtil.getString("jobgroup_field_addressType_1") ); + triggerMsgSb.append("
").append(I18nUtil.getString("jobconf_trigger_exe_regaddress")).append(":").append(group.getRegistryList()); + triggerMsgSb.append("
").append(I18nUtil.getString("jobinfo_field_executorRouteStrategy")).append(":").append(executorRouteStrategyEnum.getTitle()); + triggerMsgSb.append("
").append(I18nUtil.getString("jobinfo_field_executorBlockStrategy")).append(":").append(blockStrategy.getTitle()); + triggerMsgSb.append("
").append(I18nUtil.getString("jobinfo_field_executorFailStrategy")).append(":").append(failStrategy.getTitle()); // 3、trigger-valid if (triggerResult.getCode()==ReturnT.SUCCESS_CODE && CollectionUtils.isEmpty(addressList)) { triggerResult.setCode(ReturnT.FAIL_CODE); - triggerMsgSb.append("
----------------------
").append("调度失败:").append("执行器地址为空"); + triggerMsgSb.append("
----------------------
").append(I18nUtil.getString("jobconf_trigger_address_empty")); } if (triggerResult.getCode() == ReturnT.SUCCESS_CODE) { @@ -164,12 +167,12 @@ public class XxlJobTrigger { // 4.2、trigger-run (route run / trigger remote executor) triggerResult = executorRouteStrategyEnum.getRouter().routeRun(triggerParam, addressList); - triggerMsgSb.append("

>>>>>>>>>>>触发调度<<<<<<<<<<<
").append(triggerResult.getMsg()); + triggerMsgSb.append("

>>>>>>>>>>>"+ I18nUtil.getString("jobconf_trigger_run") +"<<<<<<<<<<<
").append(triggerResult.getMsg()); // 4.3、trigger (fail retry) if (triggerResult.getCode()!=ReturnT.SUCCESS_CODE && failStrategy == ExecutorFailStrategyEnum.FAIL_RETRY) { triggerResult = executorRouteStrategyEnum.getRouter().routeRun(triggerParam, addressList); - triggerMsgSb.append("

>>>>>>>>>>>调度失败重试<<<<<<<<<<<
").append(triggerResult.getMsg()); + triggerMsgSb.append("

>>>>>>>>>>>"+ I18nUtil.getString("jobconf_trigger_fail_retry") +"<<<<<<<<<<<
").append(triggerResult.getMsg()); } } @@ -179,7 +182,7 @@ public class XxlJobTrigger { jobLog.setTriggerMsg(triggerMsgSb.toString()); XxlJobDynamicScheduler.xxlJobLogDao.updateTriggerInfo(jobLog); - // 6、monitor triger + // 6、monitor trigger JobFailMonitorHelper.monitor(jobLog.getId()); logger.debug(">>>>>>>>>>> xxl-job trigger end, jobId:{}", jobLog.getId()); } @@ -202,7 +205,7 @@ public class XxlJobTrigger { runResult = new ReturnT(ReturnT.FAIL_CODE, ""+e ); } - StringBuffer runResultSB = new StringBuffer("触发调度:"); + StringBuffer runResultSB = new StringBuffer(I18nUtil.getString("jobconf_trigger_run") + ":"); runResultSB.append("
address:").append(address); runResultSB.append("
code:").append(runResult.getCode()); runResultSB.append("
msg:").append(runResult.getMsg()); diff --git a/xxl-job-admin/src/main/java/com/xxl/job/admin/core/util/FtlUtil.java b/xxl-job-admin/src/main/java/com/xxl/job/admin/core/util/FtlUtil.java new file mode 100644 index 00000000..fe888a1c --- /dev/null +++ b/xxl-job-admin/src/main/java/com/xxl/job/admin/core/util/FtlUtil.java @@ -0,0 +1,25 @@ +package com.xxl.job.admin.core.util; + +import freemarker.ext.beans.BeansWrapper; +import freemarker.template.TemplateHashModel; + +/** + * ftl util + * + * @author xuxueli 2018-01-17 20:37:48 + */ +public class FtlUtil { + + public static TemplateHashModel generateStaticModel(String packageName) { + try { + BeansWrapper wrapper = BeansWrapper.getDefaultInstance(); + TemplateHashModel staticModels = wrapper.getStaticModels(); + TemplateHashModel fileStatics = (TemplateHashModel) staticModels.get(packageName); + return fileStatics; + } catch (Exception e) { + e.printStackTrace(); + } + return null; + } + +} diff --git a/xxl-job-admin/src/main/java/com/xxl/job/admin/core/util/I18nUtil.java b/xxl-job-admin/src/main/java/com/xxl/job/admin/core/util/I18nUtil.java new file mode 100644 index 00000000..93b255c9 --- /dev/null +++ b/xxl-job-admin/src/main/java/com/xxl/job/admin/core/util/I18nUtil.java @@ -0,0 +1,82 @@ +package com.xxl.job.admin.core.util; + +import com.xxl.job.admin.core.conf.XxlJobAdminConfig; +import com.xxl.job.core.util.JacksonUtil; +import org.apache.commons.lang3.StringUtils; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.core.io.ClassPathResource; +import org.springframework.core.io.Resource; +import org.springframework.core.io.support.EncodedResource; +import org.springframework.core.io.support.PropertiesLoaderUtils; + +import java.io.IOException; +import java.text.MessageFormat; +import java.util.HashMap; +import java.util.Map; +import java.util.Properties; + +/** + * i18n util + * + * @author xuxueli 2018-01-17 20:39:06 + */ +public class I18nUtil { + private static Logger logger = LoggerFactory.getLogger(I18nUtil.class); + + private static Properties prop = null; + public static Properties loadI18nProp(){ + if (prop != null) { + return prop; + } + try { + // bild i18n prop + String i18n = XxlJobAdminConfig.getAdminConfig().getI18n(); + i18n = StringUtils.isNotBlank(i18n)?("_"+i18n):i18n; + String i18nFile = MessageFormat.format("i18n/message{0}.properties", i18n); + + // load prop + Resource resource = new ClassPathResource(i18nFile); + EncodedResource encodedResource = new EncodedResource(resource,"UTF-8"); + prop = PropertiesLoaderUtils.loadProperties(encodedResource); + } catch (IOException e) { + logger.error(e.getMessage(), e); + } + return prop; + } + + /** + * get val of i18n key + * + * @param key + * @return + */ + public static String getString(String key) { + return loadI18nProp().getProperty(key); + } + + /** + * get mult val of i18n mult key, as json + * + * @param keys + * @return + */ + public static String getMultString(String... keys) { + Map map = new HashMap<>(); + + Properties prop = loadI18nProp(); + if (keys!=null && keys.length>0) { + for (String key: keys) { + map.put(key, prop.getProperty(key)); + } + } else { + for (String key: prop.stringPropertyNames()) { + map.put(key, prop.getProperty(key)); + } + } + + String json = JacksonUtil.writeValueAsString(map); + return json; + } + +} diff --git a/xxl-job-admin/src/main/java/com/xxl/job/admin/core/util/LocalCacheUtil.java b/xxl-job-admin/src/main/java/com/xxl/job/admin/core/util/LocalCacheUtil.java new file mode 100644 index 00000000..3a0ec942 --- /dev/null +++ b/xxl-job-admin/src/main/java/com/xxl/job/admin/core/util/LocalCacheUtil.java @@ -0,0 +1,134 @@ +package com.xxl.job.admin.core.util; + +import org.apache.commons.lang3.StringUtils; + +import java.util.concurrent.ConcurrentHashMap; + +/** + * local cache tool + * + * @author xuxueli 2018-01-22 21:37:34 + */ +public class LocalCacheUtil { + + private static ConcurrentHashMap cacheRepository = new ConcurrentHashMap<>(); + private static class LocalCacheData{ + private String key; + private Object val; + private long timeoutTime; + + public LocalCacheData() { + } + + public LocalCacheData(String key, Object val, long timeoutTime) { + this.key = key; + this.val = val; + this.timeoutTime = timeoutTime; + } + + public String getKey() { + return key; + } + + public void setKey(String key) { + this.key = key; + } + + public Object getVal() { + return val; + } + + public void setVal(Object val) { + this.val = val; + } + + public long getTimeoutTime() { + return timeoutTime; + } + + public void setTimeoutTime(long timeoutTime) { + this.timeoutTime = timeoutTime; + } + } + + + /** + * set cache + * + * @param key + * @param val + * @param cacheTime + * @return + */ + public static boolean set(String key, Object val, long cacheTime){ + + // clean timeout cache, before set new cache (avoid cache too much) + cleanTimeutCache(); + + // set new cache + if (StringUtils.isBlank(key)) { + return false; + } + if (val == null) { + remove(key); + } + if (cacheTime <= 0) { + remove(key); + } + long timeoutTime = System.currentTimeMillis() + cacheTime; + LocalCacheData localCacheData = new LocalCacheData(key, val, timeoutTime); + cacheRepository.put(localCacheData.getKey(), localCacheData); + return true; + } + + /** + * remove cache + * + * @param key + * @return + */ + public static boolean remove(String key){ + if (StringUtils.isBlank(key)) { + return false; + } + cacheRepository.remove(key); + return true; + } + + /** + * get cache + * + * @param key + * @return + */ + public static Object get(String key){ + if (StringUtils.isBlank(key)) { + return null; + } + LocalCacheData localCacheData = cacheRepository.get(key); + if (localCacheData!=null && System.currentTimeMillis()=localCacheData.getTimeoutTime()) { + cacheRepository.remove(key); + } + } + } + return true; + } + +} diff --git a/xxl-job-admin/src/main/java/com/xxl/job/admin/core/util/MailUtil.java b/xxl-job-admin/src/main/java/com/xxl/job/admin/core/util/MailUtil.java index e000b157..8e469bc7 100644 --- a/xxl-job-admin/src/main/java/com/xxl/job/admin/core/util/MailUtil.java +++ b/xxl-job-admin/src/main/java/com/xxl/job/admin/core/util/MailUtil.java @@ -1,5 +1,6 @@ package com.xxl.job.admin.core.util; +import com.xxl.job.admin.core.conf.XxlJobAdminConfig; import org.apache.commons.mail.DefaultAuthenticator; import org.apache.commons.mail.EmailException; import org.apache.commons.mail.HtmlEmail; @@ -16,19 +17,6 @@ import java.nio.charset.Charset; public class MailUtil { private static Logger logger = LoggerFactory.getLogger(MailUtil.class); - private static String host; - private static String port; - private static String username; - private static String password; - private static String sendNick; - static{ - host = PropertiesUtil.getString("xxl.job.mail.host"); - port = PropertiesUtil.getString("xxl.job.mail.port"); - username = PropertiesUtil.getString("xxl.job.mail.username"); - password = PropertiesUtil.getString("xxl.job.mail.password"); - sendNick = PropertiesUtil.getString("xxl.job.mail.sendNick"); - } - /** * * @param toAddress 收件人邮箱 @@ -46,13 +34,13 @@ public class MailUtil { //email.setTLS(true); // 是否TLS校验,,某些邮箱需要TLS安全校验,同理有SSL校验 //email.setSSL(true); - email.setHostName(host); - email.setSmtpPort(Integer.valueOf(port)); + email.setHostName(XxlJobAdminConfig.getAdminConfig().getMailHost()); + email.setSmtpPort(Integer.valueOf(XxlJobAdminConfig.getAdminConfig().getMailPort())); //email.setSslSmtpPort(port); - email.setAuthenticator(new DefaultAuthenticator(username, password)); + email.setAuthenticator(new DefaultAuthenticator(XxlJobAdminConfig.getAdminConfig().getMailUsername(), XxlJobAdminConfig.getAdminConfig().getMailPassword())); email.setCharset(Charset.defaultCharset().name()); - email.setFrom(username, sendNick); + email.setFrom(XxlJobAdminConfig.getAdminConfig().getMailUsername(), XxlJobAdminConfig.getAdminConfig().getMailSendNick()); email.addTo(toAddress); email.setSubject(mailSubject); email.setMsg(mailBody); diff --git a/xxl-job-admin/src/main/java/com/xxl/job/admin/core/util/PropertiesUtil.java b/xxl-job-admin/src/main/java/com/xxl/job/admin/core/util/PropertiesUtil.java deleted file mode 100644 index a3c60077..00000000 --- a/xxl-job-admin/src/main/java/com/xxl/job/admin/core/util/PropertiesUtil.java +++ /dev/null @@ -1,38 +0,0 @@ -package com.xxl.job.admin.core.util; - -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import org.springframework.core.io.ClassPathResource; -import org.springframework.core.io.Resource; -import org.springframework.core.io.support.EncodedResource; -import org.springframework.core.io.support.PropertiesLoaderUtils; - -import java.io.IOException; -import java.util.Properties; - -/** - * properties util - * - * @author xuxueli 2015-8-28 10:35:53 - */ -public class PropertiesUtil { - private static Logger logger = LoggerFactory.getLogger(PropertiesUtil.class); - private static final String file_name = "xxl-job-admin.properties"; - - - public static String getString(String key) { - Properties prop = null; - try { - Resource resource = new ClassPathResource(file_name); - EncodedResource encodedResource = new EncodedResource(resource,"UTF-8"); - prop = PropertiesLoaderUtils.loadProperties(encodedResource); - } catch (IOException e) { - logger.error(e.getMessage(), e); - } - if (prop!=null) { - return prop.getProperty(key); - } - return null; - } - -} diff --git a/xxl-job-admin/src/main/java/com/xxl/job/admin/dao/XxlJobInfoDao.java b/xxl-job-admin/src/main/java/com/xxl/job/admin/dao/XxlJobInfoDao.java index 69b556c3..92fbba4e 100644 --- a/xxl-job-admin/src/main/java/com/xxl/job/admin/dao/XxlJobInfoDao.java +++ b/xxl-job-admin/src/main/java/com/xxl/job/admin/dao/XxlJobInfoDao.java @@ -12,8 +12,16 @@ import java.util.List; */ public interface XxlJobInfoDao { - public List pageList(@Param("offset") int offset, @Param("pagesize") int pagesize, @Param("jobGroup") int jobGroup, @Param("executorHandler") String executorHandler); - public int pageListCount(@Param("offset") int offset, @Param("pagesize") int pagesize, @Param("jobGroup") int jobGroup, @Param("executorHandler") String executorHandler); + public List pageList(@Param("offset") int offset, + @Param("pagesize") int pagesize, + @Param("jobGroup") int jobGroup, + @Param("jobDesc") String jobDesc, + @Param("executorHandler") String executorHandler); + public int pageListCount(@Param("offset") int offset, + @Param("pagesize") int pagesize, + @Param("jobGroup") int jobGroup, + @Param("jobDesc") String jobDesc, + @Param("executorHandler") String executorHandler); public int save(XxlJobInfo info); diff --git a/xxl-job-admin/src/main/java/com/xxl/job/admin/dao/XxlJobLogDao.java b/xxl-job-admin/src/main/java/com/xxl/job/admin/dao/XxlJobLogDao.java index 0f91c79e..8263811b 100644 --- a/xxl-job-admin/src/main/java/com/xxl/job/admin/dao/XxlJobLogDao.java +++ b/xxl-job-admin/src/main/java/com/xxl/job/admin/dao/XxlJobLogDao.java @@ -41,8 +41,7 @@ public interface XxlJobLogDao { public int triggerCountByHandleCode(@Param("handleCode") int handleCode); public List> triggerCountByDay(@Param("from") Date from, - @Param("to") Date to, - @Param("handleCode") int handleCode); + @Param("to") Date to); public int clearLog(@Param("jobGroup") int jobGroup, @Param("jobId") int jobId, diff --git a/xxl-job-admin/src/main/java/com/xxl/job/admin/service/XxlJobService.java b/xxl-job-admin/src/main/java/com/xxl/job/admin/service/XxlJobService.java index 7a0c1607..b51e9288 100644 --- a/xxl-job-admin/src/main/java/com/xxl/job/admin/service/XxlJobService.java +++ b/xxl-job-admin/src/main/java/com/xxl/job/admin/service/XxlJobService.java @@ -13,23 +13,82 @@ import java.util.Map; * @author xuxueli 2016-5-28 15:30:33 */ public interface XxlJobService { - - public Map pageList(int start, int length, int jobGroup, String executorHandler, String filterTime); - + + /** + * page list + * + * @param start + * @param length + * @param jobGroup + * @param jobDesc + * @param executorHandler + * @param filterTime + * @return + */ + public Map pageList(int start, int length, int jobGroup, String jobDesc, String executorHandler, String filterTime); + + /** + * add job + * + * @param jobInfo + * @return + */ public ReturnT add(XxlJobInfo jobInfo); - - public ReturnT reschedule(XxlJobInfo jobInfo); - + + /** + * update job + * + * @param jobInfo + * @return + */ + public ReturnT update(XxlJobInfo jobInfo); + + /** + * remove job + * + * @param id + * @return + */ public ReturnT remove(int id); - + + /** + * pause job + * + * @param id + * @return + */ public ReturnT pause(int id); - + + /** + * resume job + * + * @param id + * @return + */ public ReturnT resume(int id); - + + /** + * trigger job + * + * @param id + * @return + */ public ReturnT triggerJob(int id); + /** + * dashboard info + * + * @return + */ public Map dashboardInfo(); - public ReturnT> triggerChartDate(Date startDate, Date endDate); + /** + * chart info + * + * @param startDate + * @param endDate + * @return + */ + public ReturnT> chartInfo(Date startDate, Date endDate); } diff --git a/xxl-job-admin/src/main/java/com/xxl/job/admin/service/impl/AdminBizImpl.java b/xxl-job-admin/src/main/java/com/xxl/job/admin/service/impl/AdminBizImpl.java index 352b4c95..ddaa6bd0 100644 --- a/xxl-job-admin/src/main/java/com/xxl/job/admin/service/impl/AdminBizImpl.java +++ b/xxl-job-admin/src/main/java/com/xxl/job/admin/service/impl/AdminBizImpl.java @@ -2,6 +2,7 @@ package com.xxl.job.admin.service.impl; import com.xxl.job.admin.core.model.XxlJobInfo; import com.xxl.job.admin.core.model.XxlJobLog; +import com.xxl.job.admin.core.util.I18nUtil; import com.xxl.job.admin.dao.XxlJobInfoDao; import com.xxl.job.admin.dao.XxlJobLogDao; import com.xxl.job.admin.dao.XxlJobRegistryDao; @@ -64,7 +65,7 @@ public class AdminBizImpl implements AdminBiz { if (IJobHandler.SUCCESS.getCode() == handleCallbackParam.getExecuteResult().getCode()) { XxlJobInfo xxlJobInfo = xxlJobInfoDao.loadById(log.getJobId()); if (xxlJobInfo!=null && StringUtils.isNotBlank(xxlJobInfo.getChildJobId())) { - callbackMsg = "

>>>>>>>>>>>触发子任务<<<<<<<<<<<
"; + callbackMsg = "

>>>>>>>>>>>"+ I18nUtil.getString("jobconf_trigger_child_run") +"<<<<<<<<<<<
"; String[] childJobIds = xxlJobInfo.getChildJobId().split(","); for (int i = 0; i < childJobIds.length; i++) { @@ -73,21 +74,27 @@ public class AdminBizImpl implements AdminBiz { ReturnT triggerChildResult = xxlJobService.triggerJob(childJobId); // add msg - callbackMsg += MessageFormat.format("{0}/{1} [任务ID={2}], 触发{3}, 触发备注: {4}
", - (i+1), childJobIds.length, childJobIds[i], (triggerChildResult.getCode()==ReturnT.SUCCESS_CODE?"成功":"失败"), triggerChildResult.getMsg()); + callbackMsg += MessageFormat.format(I18nUtil.getString("jobconf_callback_child_msg1"), + (i+1), + childJobIds.length, + childJobIds[i], + (triggerChildResult.getCode()==ReturnT.SUCCESS_CODE?I18nUtil.getString("system_success"):I18nUtil.getString("system_fail")), + triggerChildResult.getMsg()); } else { - callbackMsg += MessageFormat.format(" {0}/{1} [任务ID={2}], 触发失败, 触发备注: 任务ID格式错误
", - (i+1), childJobIds.length, childJobIds[i]); + callbackMsg += MessageFormat.format(I18nUtil.getString("jobconf_callback_child_msg2"), + (i+1), + childJobIds.length, + childJobIds[i]); } } } } else if (IJobHandler.FAIL_RETRY.getCode() == handleCallbackParam.getExecuteResult().getCode()){ ReturnT retryTriggerResult = xxlJobService.triggerJob(log.getJobId()); - callbackMsg = "

>>>>>>>>>>>执行失败重试<<<<<<<<<<<
"; + callbackMsg = "

>>>>>>>>>>>"+ I18nUtil.getString("jobconf_exe_fail_retry") +"<<<<<<<<<<<
"; - callbackMsg += MessageFormat.format("触发{0}, 触发备注: {1}", - (retryTriggerResult.getCode()==ReturnT.SUCCESS_CODE?"成功":"失败"), retryTriggerResult.getMsg()); + callbackMsg += MessageFormat.format(I18nUtil.getString("jobconf_callback_msg1"), + (retryTriggerResult.getCode()==ReturnT.SUCCESS_CODE?I18nUtil.getString("system_success"):I18nUtil.getString("system_fail")), retryTriggerResult.getMsg()); } // handle msg diff --git a/xxl-job-admin/src/main/java/com/xxl/job/admin/service/impl/XxlJobServiceImpl.java b/xxl-job-admin/src/main/java/com/xxl/job/admin/service/impl/XxlJobServiceImpl.java index eecef342..406c74b0 100644 --- a/xxl-job-admin/src/main/java/com/xxl/job/admin/service/impl/XxlJobServiceImpl.java +++ b/xxl-job-admin/src/main/java/com/xxl/job/admin/service/impl/XxlJobServiceImpl.java @@ -5,6 +5,8 @@ import com.xxl.job.admin.core.model.XxlJobGroup; import com.xxl.job.admin.core.model.XxlJobInfo; import com.xxl.job.admin.core.route.ExecutorRouteStrategyEnum; import com.xxl.job.admin.core.schedule.XxlJobDynamicScheduler; +import com.xxl.job.admin.core.util.I18nUtil; +import com.xxl.job.admin.core.util.LocalCacheUtil; import com.xxl.job.admin.dao.XxlJobGroupDao; import com.xxl.job.admin.dao.XxlJobInfoDao; import com.xxl.job.admin.dao.XxlJobLogDao; @@ -45,11 +47,11 @@ public class XxlJobServiceImpl implements XxlJobService { private XxlJobLogGlueDao xxlJobLogGlueDao; @Override - public Map pageList(int start, int length, int jobGroup, String executorHandler, String filterTime) { + public Map pageList(int start, int length, int jobGroup, String jobDesc, String executorHandler, String filterTime) { // page list - List list = xxlJobInfoDao.pageList(start, length, jobGroup, executorHandler); - int list_count = xxlJobInfoDao.pageListCount(start, length, jobGroup, executorHandler); + List list = xxlJobInfoDao.pageList(start, length, jobGroup, jobDesc, executorHandler); + int list_count = xxlJobInfoDao.pageListCount(start, length, jobGroup, jobDesc, executorHandler); // fill job info if (list!=null && list.size()>0) { @@ -71,31 +73,31 @@ public class XxlJobServiceImpl implements XxlJobService { // valid XxlJobGroup group = xxlJobGroupDao.load(jobInfo.getJobGroup()); if (group == null) { - return new ReturnT(ReturnT.FAIL_CODE, "请选择“执行器”"); + return new ReturnT(ReturnT.FAIL_CODE, (I18nUtil.getString("system_please_choose")+I18nUtil.getString("jobinfo_field_jobgroup")) ); } if (!CronExpression.isValidExpression(jobInfo.getJobCron())) { - return new ReturnT(ReturnT.FAIL_CODE, "请输入格式正确的“Cron”"); + return new ReturnT(ReturnT.FAIL_CODE, I18nUtil.getString("jobinfo_field_cron_unvalid") ); } if (StringUtils.isBlank(jobInfo.getJobDesc())) { - return new ReturnT(ReturnT.FAIL_CODE, "请输入“任务描述”"); + return new ReturnT(ReturnT.FAIL_CODE, (I18nUtil.getString("system_please_input")+I18nUtil.getString("jobinfo_field_jobdesc")) ); } if (StringUtils.isBlank(jobInfo.getAuthor())) { - return new ReturnT(ReturnT.FAIL_CODE, "请输入“负责人”"); + return new ReturnT(ReturnT.FAIL_CODE, (I18nUtil.getString("system_please_input")+I18nUtil.getString("jobinfo_field_author")) ); } if (ExecutorRouteStrategyEnum.match(jobInfo.getExecutorRouteStrategy(), null) == null) { - return new ReturnT(ReturnT.FAIL_CODE, "路由策略非法"); + return new ReturnT(ReturnT.FAIL_CODE, (I18nUtil.getString("jobinfo_field_executorRouteStrategy")+I18nUtil.getString("system_unvalid")) ); } if (ExecutorBlockStrategyEnum.match(jobInfo.getExecutorBlockStrategy(), null) == null) { - return new ReturnT(ReturnT.FAIL_CODE, "阻塞处理策略非法"); + return new ReturnT(ReturnT.FAIL_CODE, (I18nUtil.getString("jobinfo_field_executorBlockStrategy")+I18nUtil.getString("system_unvalid")) ); } if (ExecutorFailStrategyEnum.match(jobInfo.getExecutorFailStrategy(), null) == null) { - return new ReturnT(ReturnT.FAIL_CODE, "失败处理策略非法"); + return new ReturnT(ReturnT.FAIL_CODE, (I18nUtil.getString("jobinfo_field_executorFailStrategy")+I18nUtil.getString("system_unvalid")) ); } if (GlueTypeEnum.match(jobInfo.getGlueType()) == null) { - return new ReturnT(ReturnT.FAIL_CODE, "运行模式非法非法"); + return new ReturnT(ReturnT.FAIL_CODE, (I18nUtil.getString("jobinfo_field_gluetype")+I18nUtil.getString("system_unvalid")) ); } if (GlueTypeEnum.BEAN==GlueTypeEnum.match(jobInfo.getGlueType()) && StringUtils.isBlank(jobInfo.getExecutorHandler())) { - return new ReturnT(ReturnT.FAIL_CODE, "请输入“JobHandler”"); + return new ReturnT(ReturnT.FAIL_CODE, (I18nUtil.getString("system_please_input")+"JobHandler") ); } // fix "\r" in shell @@ -110,10 +112,12 @@ public class XxlJobServiceImpl implements XxlJobService { if (StringUtils.isNotBlank(childJobIdItem) && StringUtils.isNumeric(childJobIdItem)) { XxlJobInfo childJobInfo = xxlJobInfoDao.loadById(Integer.valueOf(childJobIdItem)); if (childJobInfo==null) { - return new ReturnT(ReturnT.FAIL_CODE, MessageFormat.format("子任务ID({0})无效", childJobIdItem)); + return new ReturnT(ReturnT.FAIL_CODE, + MessageFormat.format((I18nUtil.getString("jobinfo_field_childJobId")+"({0})"+I18nUtil.getString("system_not_found")), childJobIdItem)); } } else { - return new ReturnT(ReturnT.FAIL_CODE, MessageFormat.format("子任务ID({0})格式错误", childJobIdItem)); + return new ReturnT(ReturnT.FAIL_CODE, + MessageFormat.format((I18nUtil.getString("jobinfo_field_childJobId")+"({0})"+I18nUtil.getString("system_unvalid")), childJobIdItem)); } } jobInfo.setChildJobId(StringUtils.join(childJobIds, ",")); @@ -122,7 +126,7 @@ public class XxlJobServiceImpl implements XxlJobService { // add in db xxlJobInfoDao.save(jobInfo); if (jobInfo.getId() < 1) { - return new ReturnT(ReturnT.FAIL_CODE, "新增任务失败"); + return new ReturnT(ReturnT.FAIL_CODE, (I18nUtil.getString("jobinfo_field_add")+I18nUtil.getString("system_fail")) ); } // add in quartz @@ -140,31 +144,31 @@ public class XxlJobServiceImpl implements XxlJobService { } catch (SchedulerException e1) { logger.error(e.getMessage(), e1); } - return new ReturnT(ReturnT.FAIL_CODE, "新增任务失败:" + e.getMessage()); + return new ReturnT(ReturnT.FAIL_CODE, (I18nUtil.getString("jobinfo_field_add")+I18nUtil.getString("system_fail"))+":" + e.getMessage()); } } @Override - public ReturnT reschedule(XxlJobInfo jobInfo) { + public ReturnT update(XxlJobInfo jobInfo) { // valid if (!CronExpression.isValidExpression(jobInfo.getJobCron())) { - return new ReturnT(ReturnT.FAIL_CODE, "请输入格式正确的“Cron”"); + return new ReturnT(ReturnT.FAIL_CODE, I18nUtil.getString("jobinfo_field_cron_unvalid") ); } if (StringUtils.isBlank(jobInfo.getJobDesc())) { - return new ReturnT(ReturnT.FAIL_CODE, "请输入“任务描述”"); + return new ReturnT(ReturnT.FAIL_CODE, (I18nUtil.getString("system_please_input")+I18nUtil.getString("jobinfo_field_jobdesc")) ); } if (StringUtils.isBlank(jobInfo.getAuthor())) { - return new ReturnT(ReturnT.FAIL_CODE, "请输入“负责人”"); + return new ReturnT(ReturnT.FAIL_CODE, (I18nUtil.getString("system_please_input")+I18nUtil.getString("jobinfo_field_author")) ); } if (ExecutorRouteStrategyEnum.match(jobInfo.getExecutorRouteStrategy(), null) == null) { - return new ReturnT(ReturnT.FAIL_CODE, "路由策略非法"); + return new ReturnT(ReturnT.FAIL_CODE, (I18nUtil.getString("jobinfo_field_executorRouteStrategy")+I18nUtil.getString("system_unvalid")) ); } if (ExecutorBlockStrategyEnum.match(jobInfo.getExecutorBlockStrategy(), null) == null) { - return new ReturnT(ReturnT.FAIL_CODE, "阻塞处理策略非法"); + return new ReturnT(ReturnT.FAIL_CODE, (I18nUtil.getString("jobinfo_field_executorBlockStrategy")+I18nUtil.getString("system_unvalid")) ); } if (ExecutorFailStrategyEnum.match(jobInfo.getExecutorFailStrategy(), null) == null) { - return new ReturnT(ReturnT.FAIL_CODE, "失败处理策略非法"); + return new ReturnT(ReturnT.FAIL_CODE, (I18nUtil.getString("jobinfo_field_executorFailStrategy")+I18nUtil.getString("system_unvalid"))); } // ChildJobId valid @@ -174,14 +178,16 @@ public class XxlJobServiceImpl implements XxlJobService { if (StringUtils.isNotBlank(childJobIdItem) && StringUtils.isNumeric(childJobIdItem)) { XxlJobInfo childJobInfo = xxlJobInfoDao.loadById(Integer.valueOf(childJobIdItem)); if (childJobInfo==null) { - return new ReturnT(ReturnT.FAIL_CODE, MessageFormat.format("子任务ID({0})无效", childJobIdItem)); + return new ReturnT(ReturnT.FAIL_CODE, + MessageFormat.format((I18nUtil.getString("jobinfo_field_childJobId")+"({0})"+I18nUtil.getString("system_not_found")), childJobIdItem)); } // avoid cycle relate if (childJobInfo.getId() == jobInfo.getId()) { - return new ReturnT(ReturnT.FAIL_CODE, MessageFormat.format("子任务ID({0})不可与父任务重复", childJobIdItem)); + return new ReturnT(ReturnT.FAIL_CODE, MessageFormat.format(I18nUtil.getString("jobinfo_field_childJobId_limit"), childJobIdItem)); } } else { - return new ReturnT(ReturnT.FAIL_CODE, MessageFormat.format("子任务ID({0})格式错误", childJobIdItem)); + return new ReturnT(ReturnT.FAIL_CODE, + MessageFormat.format((I18nUtil.getString("jobinfo_field_childJobId")+"({0})"+I18nUtil.getString("system_unvalid")), childJobIdItem)); } } jobInfo.setChildJobId(StringUtils.join(childJobIds, ",")); @@ -190,7 +196,7 @@ public class XxlJobServiceImpl implements XxlJobService { // stage job info XxlJobInfo exists_jobInfo = xxlJobInfoDao.loadById(jobInfo.getId()); if (exists_jobInfo == null) { - return new ReturnT(ReturnT.FAIL_CODE, "参数异常"); + return new ReturnT(ReturnT.FAIL_CODE, (I18nUtil.getString("jobinfo_field_id")+I18nUtil.getString("system_not_found")) ); } //String old_cron = exists_jobInfo.getJobCron(); @@ -271,7 +277,7 @@ public class XxlJobServiceImpl implements XxlJobService { public ReturnT triggerJob(int id) { XxlJobInfo xxlJobInfo = xxlJobInfoDao.loadById(id); if (xxlJobInfo == null) { - return new ReturnT(ReturnT.FAIL_CODE, "任务ID非法"); + return new ReturnT(ReturnT.FAIL_CODE, (I18nUtil.getString("jobinfo_field_id")+I18nUtil.getString("system_unvalid")) ); } String group = String.valueOf(xxlJobInfo.getJobGroup()); @@ -315,38 +321,42 @@ public class XxlJobServiceImpl implements XxlJobService { return dashboardMap; } + private static final String TRIGGER_CHART_DATA_CACHE = "trigger_chart_data_cache"; @Override - public ReturnT> triggerChartDate(Date startDate, Date endDate) { + public ReturnT> chartInfo(Date startDate, Date endDate) { + // get cache + String cacheKey = TRIGGER_CHART_DATA_CACHE + "_" + startDate.getTime() + "_" + endDate.getTime(); + Map chartInfo = (Map) LocalCacheUtil.get(cacheKey); + if (chartInfo != null) { + return new ReturnT>(chartInfo); + } + + // process List triggerDayList = new ArrayList(); + List triggerDayCountRunningList = new ArrayList(); List triggerDayCountSucList = new ArrayList(); List triggerDayCountFailList = new ArrayList(); + int triggerCountRunningTotal = 0; int triggerCountSucTotal = 0; int triggerCountFailTotal = 0; - List> triggerCountMapAll = xxlJobLogDao.triggerCountByDay(startDate, endDate, -1); - List> triggerCountMapSuc = xxlJobLogDao.triggerCountByDay(startDate, endDate, ReturnT.SUCCESS_CODE); + List> triggerCountMapAll = xxlJobLogDao.triggerCountByDay(startDate, endDate); if (CollectionUtils.isNotEmpty(triggerCountMapAll)) { for (Map item: triggerCountMapAll) { String day = String.valueOf(item.get("triggerDay")); - int dayAllCount = Integer.valueOf(String.valueOf(item.get("triggerCount"))); - int daySucCount = 0; - int dayFailCount = dayAllCount - daySucCount; - - if (CollectionUtils.isNotEmpty(triggerCountMapSuc)) { - for (Map sucItem: triggerCountMapSuc) { - String daySuc = String.valueOf(sucItem.get("triggerDay")); - if (day.equals(daySuc)) { - daySucCount = Integer.valueOf(String.valueOf(sucItem.get("triggerCount"))); - dayFailCount = dayAllCount - daySucCount; - } - } - } + int triggerDayCount = Integer.valueOf(String.valueOf(item.get("triggerDayCount"))); + int triggerDayCountRunning = Integer.valueOf(String.valueOf(item.get("triggerDayCountRunning"))); + int triggerDayCountSuc = Integer.valueOf(String.valueOf(item.get("triggerDayCountSuc"))); + int triggerDayCountFail = triggerDayCount - triggerDayCountRunning - triggerDayCountSuc; triggerDayList.add(day); - triggerDayCountSucList.add(daySucCount); - triggerDayCountFailList.add(dayFailCount); - triggerCountSucTotal += daySucCount; - triggerCountFailTotal += dayFailCount; + triggerDayCountRunningList.add(triggerDayCountRunning); + triggerDayCountSucList.add(triggerDayCountSuc); + triggerDayCountFailList.add(triggerDayCountFail); + + triggerCountRunningTotal += triggerDayCountRunning; + triggerCountSucTotal += triggerDayCountSuc; + triggerCountFailTotal += triggerDayCountFail; } } else { for (int i = 4; i > -1; i--) { @@ -358,10 +368,17 @@ public class XxlJobServiceImpl implements XxlJobService { Map result = new HashMap(); result.put("triggerDayList", triggerDayList); + result.put("triggerDayCountRunningList", triggerDayCountRunningList); result.put("triggerDayCountSucList", triggerDayCountSucList); result.put("triggerDayCountFailList", triggerDayCountFailList); + + result.put("triggerCountRunningTotal", triggerCountRunningTotal); result.put("triggerCountSucTotal", triggerCountSucTotal); result.put("triggerCountFailTotal", triggerCountFailTotal); + + // set cache + LocalCacheUtil.set(cacheKey, result, 60*1000); // cache 60s + return new ReturnT>(result); } diff --git a/xxl-job-admin/src/main/resources/i18n/message.properties b/xxl-job-admin/src/main/resources/i18n/message.properties new file mode 100644 index 00000000..66c7aa9d --- /dev/null +++ b/xxl-job-admin/src/main/resources/i18n/message.properties @@ -0,0 +1,226 @@ +admin_name=任务调度中心 +admin_name_full=分布式任务调度平台XXL-JOB +admin_version=1.9.2 (快照版) + +## system +system_tips=系统提示 +system_ok=确定 +system_close=关闭 +system_save=保存 +system_cancel=取消 +system_search=搜索 +system_status=状态 +system_opt=操作 +system_please_input=请输入 +system_please_choose=请选择 +system_success=成功 +system_fail=失败 +system_add_suc=新增成功 +system_add_fail=新增失败 +system_update_suc=更新成功 +system_update_fail=更新失败 +system_all=全部 +system_api_error=接口异常 +system_show=查看 +system_empty=无 +system_opt_suc=操作成功 +system_opt_fail=操作失败 +system_opt_edit=编辑 +system_opt_del=删除 +system_unvalid=非法 +system_not_found=不存在 +system_nav=导航 + +## daterangepicker +daterangepicker_ranges_recent_hour=最近一小时 +daterangepicker_ranges_today=今日 +daterangepicker_ranges_yesterday=昨日 +daterangepicker_ranges_this_month=本月 +daterangepicker_ranges_last_month=上个月 +daterangepicker_ranges_recent_week=最近一周 +daterangepicker_ranges_recent_month=最近一月 +daterangepicker_custom_name=自定义 +daterangepicker_custom_starttime=起始时间 +daterangepicker_custom_endtime=结束时间 +daterangepicker_custom_daysofweek=日,一,二,三,四,五,六 +daterangepicker_custom_monthnames=一月,二月,三月,四月,五月,六月,七月,八月,九月,十月,十一月,十二月 + +## dataTable +dataTable_sProcessing=处理中... +dataTable_sLengthMenu=每页 _MENU_ 条记录 +dataTable_sZeroRecords=没有匹配结果 +dataTable_sInfo=第 _PAGE_ 页 ( 总共 _PAGES_ 页,_TOTAL_ 条记录 ) +dataTable_sInfoEmpty=无记录 +dataTable_sInfoFiltered=(由 _MAX_ 项结果过滤) +dataTable_sSearch=搜索 +dataTable_sEmptyTable=表中数据为空 +dataTable_sLoadingRecords=载入中... +dataTable_sFirst=首页 +dataTable_sPrevious=上页 +dataTable_sNext=下页 +dataTable_sLast=末页 +dataTable_sSortAscending=: 以升序排列此列 +dataTable_sSortDescending=: 以降序排列此列 + +## login +login_btn=登录 +login_remember_me=记住密码 +login_username_placeholder=请输入登录账号 +login_password_placeholder=请输入登录密码 +login_username_empty=请输入登录账号 +login_username_lt_5=登录账号不应低于5位 +login_password_empty=请输入登录密码 +login_password_lt_5=登录密码不应低于5位 +login_success=登录成功 +login_fail=登录失败 +login_param_empty=账号或密码为空 +login_param_unvalid=账号或密码错误 + +## logout +logout_btn=注销 +logout_confirm=确认注销登录? +logout_success=注销成功 +logout_fail=注销失败 + +## dashboard +job_dashboard_name=运行报表 +job_dashboard_job_num=任务数量 +job_dashboard_job_num_tip=调度中心运行的任务数量 +job_dashboard_trigger_num=调度次数 +job_dashboard_trigger_num_tip=调度中心触发的调度次数 +job_dashboard_jobgroup_num=执行器数量 +job_dashboard_jobgroup_num_tip=调度中心在线的执行器机器数量 +job_dashboard_report=调度报表 +job_dashboard_report_loaddata_fail=调度报表数据加载异常 +job_dashboard_date_report=日期分布图 +job_dashboard_rate_report=成功比例图 + +## job info +jobinfo_name=任务管理 +jobinfo_job=任务 +jobinfo_field_add=新增任务 +jobinfo_field_update=更新任务 +jobinfo_field_id=任务ID +jobinfo_field_jobgroup=执行器 +jobinfo_field_jobdesc=任务描述 +jobinfo_field_gluetype=运行模式 +jobinfo_field_executorparam=任务参数 +jobinfo_field_cron_unvalid=Cron格式非法 +jobinfo_field_author=负责人 +jobinfo_field_alarmemail=报警邮件 +jobinfo_field_alarmemail_placeholder=请输入报警邮件,多个邮件地址则逗号分隔 +jobinfo_field_executorRouteStrategy=路由策略 +jobinfo_field_childJobId=子任务ID +jobinfo_field_childJobId_limit=子任务ID({0})不可与父任务重复 +jobinfo_field_childJobId_placeholder=请输入子任务的任务ID,如存在多个则逗号分隔 +jobinfo_field_executorBlockStrategy=阻塞处理策略 +jobinfo_field_executorFailStrategy=失败处理策略 +jobinfo_script_location=脚本位置 +jobinfo_shard_index=分片序号 +jobinfo_shard_total=分片总数 +jobinfo_opt_pause=暂停 +jobinfo_opt_resume=恢复 +jobinfo_opt_log=日志 +jobinfo_opt_run=执行 +jobinfo_glue_remark=源码备注 +jobinfo_glue_remark_limit=源码备注长度限制为4~100 +jobinfo_glue_rollback=版本回溯 +jobinfo_glue_jobid_unvalid=任务ID非法 +jobinfo_glue_gluetype_unvalid=该任务非GLUE模式 + +## job log +joblog_name=调度日志 +joblog_status=状态 +joblog_status_all=全部 +joblog_status_suc=成功 +joblog_status_fail=失败 +joblog_status_running=进行中 +joblog_field_triggerTime=调度时间 +joblog_field_triggerCode=调度结果 +joblog_field_triggerMsg=调度备注 +joblog_field_handleTime=执行时间 +joblog_field_handleCode=执行结果 +joblog_field_handleMsg=执行备注 +joblog_field_executorAddress=执行器地址 +joblog_clean=清理 +joblog_clean_log=日志清理 +joblog_clean_type=清理方式 +joblog_clean_type_1=清理一个月之前日志数据 +joblog_clean_type_2=清理三个月之前日志数据 +joblog_clean_type_3=清理六个月之前日志数据 +joblog_clean_type_4=清理一年之前日志数据 +joblog_clean_type_5=清理一千条以前日志数据 +joblog_clean_type_6=清理一万条以前日志数据 +joblog_clean_type_7=清理三万条以前日志数据 +joblog_clean_type_8=清理十万条以前日志数据 +joblog_clean_type_9=清理所有日志数据 +joblog_clean_type_unvalid=清理类型参数异常 +joblog_handleCode_200=成功 +joblog_handleCode_500=失败 +joblog_handleCode_501=失败重试 +joblog_kill_log=终止任务 +joblog_kill_log_limit=调度失败,无法终止日志 +joblog_kill_log_byman=人为操作主动终止 +joblog_rolling_log=执行日志 +joblog_rolling_log_refresh=刷新 +joblog_rolling_log_triggerfail=任务发起调度失败,无法查看执行日志 +joblog_rolling_log_failoften=终止请求Rolling日志,请求失败次数超上限,可刷新页面重新加载日志 +joblog_logid_unvalid=日志ID非法 + +## job group +jobgroup_name=执行器管理 +jobgroup_list=执行器列表 +jobgroup_add=新增执行器 +jobgroup_edit=编辑执行器 +jobgroup_del=删除执行器 +jobgroup_field_order=排序 +jobgroup_field_title=名称 +jobgroup_field_addressType=注册方式 +jobgroup_field_addressType_0=自动注册 +jobgroup_field_addressType_1=手动录入 +jobgroup_field_addressType_limit=手动录入注册方式,机器地址不可为空 +jobgroup_field_registryList=机器地址 +jobgroup_field_registryList_unvalid=机器地址格式非法 +jobgroup_field_registryList_placeholder=请输入执行器地址列表,多地址逗号分隔 +jobgroup_field_appName_limit=限制以小写字母开头,由小写字母、数字和中划线组成 +jobgroup_field_appName_length=AppName长度限制为4~64 +jobgroup_field_title_length=名称长度限制为4~12 +jobgroup_field_order_digits=请输入整数 +jobgroup_field_orderrange=取值范围为1~1000 +jobgroup_del_limit_0=拒绝删除,该执行器使用中 +jobgroup_del_limit_1=拒绝删除, 系统至少保留一个执行器 + +## job conf +jobconf_fail_alarm=失败告警 +jobconf_fail_retry=失败重试 +jobconf_route_first=第一个 +jobconf_route_last=最后一个 +jobconf_route_round=轮询 +jobconf_route_random=随机 +jobconf_route_consistenthash=一致性HASH +jobconf_route_lfu=最不经常使用 +jobconf_route_lru=最近最久未使用 +jobconf_route_failover=故障转移 +jobconf_route_busyover=忙碌转移 +jobconf_route_shard=分片广播 +jobconf_idleBeat=空闲检测 +jobconf_beat=心跳检测 +jobconf_monitor=调度中心监控报警 +jobconf_monitor_detail=监控告警明细 +jobconf_monitor_alarm_title=告警类型 +jobconf_monitor_alarm_type=调度失败 +jobconf_trigger_admin_adress=调度机器 +jobconf_trigger_exe_regtype=执行器-注册方式 +jobconf_trigger_exe_regaddress=执行器-地址列表 +jobconf_trigger_address_empty=调度失败:执行器地址为空 +jobconf_trigger_run=触发调度 +jobconf_trigger_child_run=触发子任务 +jobconf_trigger_fail_retry=调度失败重试 +jobconf_exe_fail_retry=执行失败重试 +jobconf_callback_child_msg1={0}/{1} [任务ID={2}], 触发{3}, 触发备注: {4}
+jobconf_callback_child_msg2={0}/{1} [任务ID={2}], 触发失败, 触发备注: 任务ID格式错误
+jobconf_callback_msg1=触发{0}, 触发备注: {1}
+ +## help +job_help=使用教程 +job_help_document=官方文档 \ No newline at end of file diff --git a/xxl-job-admin/src/main/resources/i18n/message_en.properties b/xxl-job-admin/src/main/resources/i18n/message_en.properties new file mode 100644 index 00000000..9a8d14bd --- /dev/null +++ b/xxl-job-admin/src/main/resources/i18n/message_en.properties @@ -0,0 +1,226 @@ +admin_name=Scheduling Center +admin_name_full=Distributed Task Scheduling Platform XXL-JOB +admin_version=1.9.2 (SNAPSHOT) + +## system +system_tips=System message +system_ok=Confirm +system_close=Close +system_save=Save +system_cancel=Cancel +system_search=Search +system_status=Status +system_opt=Operate +system_please_input=please input +system_please_choose=please choose +system_success=success +system_fail=fail +system_add_suc=add success +system_add_fail=add fail +system_update_suc=update success +system_update_fail=update fail +system_all=All +system_api_error=net error +system_show=Show +system_empty=Empty +system_opt_suc=operate success +system_opt_fail=operate fail +system_opt_edit=Edit +system_opt_del=Delete +system_unvalid=illegal +system_not_found=not exist +system_nav=Navigation + +## daterangepicker +daterangepicker_ranges_recent_hour=recent one hour +daterangepicker_ranges_today=today +daterangepicker_ranges_yesterday=yesterday +daterangepicker_ranges_this_month=this month +daterangepicker_ranges_last_month=last month +daterangepicker_ranges_recent_week=recent one week +daterangepicker_ranges_recent_month=recent one month +daterangepicker_custom_name=custom +daterangepicker_custom_starttime=start time +daterangepicker_custom_endtime=end time +daterangepicker_custom_daysofweek=Sun,Mon,Tue,Wed,Thu,Fri,Sat +daterangepicker_custom_monthnames=Jan,Feb,Mar,Apr,May,Jun,Jul,Aug,Sep,Oct,Nov,Dec + +## dataTable +dataTable_sProcessing=processing... +dataTable_sLengthMenu= _MENU_ records per page +dataTable_sZeroRecords=No matching results +dataTable_sInfo=page _PAGE_ ( Total _PAGES_ pages,_TOTAL_ records ) +dataTable_sInfoEmpty=No Record +dataTable_sInfoFiltered=(Filtered by _MAX_ results) +dataTable_sSearch=Search +dataTable_sEmptyTable=Table data is empty +dataTable_sLoadingRecords=Loading... +dataTable_sFirst=FIRST PAGE +dataTable_sPrevious=Previous Page +dataTable_sNext=Next Page +dataTable_sLast=LAST PAGE +dataTable_sSortAscending=: Rank this column in ascending order +dataTable_sSortDescending=: Rank this column in descending order + +## login +login_btn=Login +login_remember_me=Remember Me +login_username_placeholder=Please enter username +login_password_placeholder=Please enter password +login_username_empty=Please enter username +login_username_lt_5=Username length should not be less than 5 +login_password_empty=Please enter password +login_password_lt_5=Password length should not be less than 5 +login_success=Login success +login_fail=Login fail +login_param_empty=Username or password is empty +login_param_unvalid=Username or password error + +## logout +logout_btn=Logout +logout_confirm=Confirm logout? +logout_success=Logout success +logout_fail=Logout fail + +## dashboard +job_dashboard_name=Run report +job_dashboard_job_num=Job number +job_dashboard_job_num_tip=The number of tasks running in the scheduling center +job_dashboard_trigger_num=trigger number +job_dashboard_trigger_num_tip=The number of trigger record scheduled by the scheduling center +job_dashboard_jobgroup_num=Executor number +job_dashboard_jobgroup_num_tip=The number of online executor machines perceived by the scheduling center +job_dashboard_report=Scheduling report +job_dashboard_report_loaddata_fail=Scheduling report load data error +job_dashboard_date_report=Date distribution +job_dashboard_rate_report=Percentage distribution + +## job info +jobinfo_name=Job Manage +jobinfo_job=Job +jobinfo_field_add=Add Job +jobinfo_field_update=Edit Job +jobinfo_field_id=Job ID +jobinfo_field_jobgroup=Executor +jobinfo_field_jobdesc=Job description +jobinfo_field_gluetype=GLUE Type +jobinfo_field_executorparam=Param +jobinfo_field_cron_unvalid=The Cron is illegal +jobinfo_field_author=Author +jobinfo_field_alarmemail=Alarm email +jobinfo_field_alarmemail_placeholder=Please enter alarm mail, if there are more than one comma separated +jobinfo_field_executorRouteStrategy=Route Strategy +jobinfo_field_childJobId=Child Job ID +jobinfo_field_childJobId_limit=Child job ID({0}) cannot be duplicated with the parent job. +jobinfo_field_childJobId_placeholder=Please enter the Child job ID, if there are more than one comma separated +jobinfo_field_executorBlockStrategy=Block Strategy +jobinfo_field_executorFailStrategy=Fail Strategy +jobinfo_script_location=Script location +jobinfo_shard_index=Shard index +jobinfo_shard_total=Shard total +jobinfo_opt_pause=Pause +jobinfo_opt_resume=Resume +jobinfo_opt_log=Log +jobinfo_opt_run=Run +jobinfo_glue_remark=Resource Remark +jobinfo_glue_remark_limit=Resource Remark length is limited to 4~100 +jobinfo_glue_rollback=Version Backtrack +jobinfo_glue_jobid_unvalid=Job ID is illegal +jobinfo_glue_gluetype_unvalid=The job is not GLUE Type + +## job log +joblog_name=Trigger Log +joblog_status=Status +joblog_status_all=All +joblog_status_suc=Success +joblog_status_fail=Fail +joblog_status_running=Running +joblog_field_triggerTime=Trigger Time +joblog_field_triggerCode=Trigger Result +joblog_field_triggerMsg=Trigger Msg +joblog_field_handleTime=Handle Time +joblog_field_handleCode=Handle Result +joblog_field_handleMsg=Trigger Msg +joblog_field_executorAddress=Executor Address +joblog_clean=Clean +joblog_clean_log=Clean Log +joblog_clean_type=Clean Type +joblog_clean_type_1=Clean up log data a month ago +joblog_clean_type_2=Clean up log data three month ago +joblog_clean_type_3=Clean up log data six month ago +joblog_clean_type_4=Clean up log data a year ago +joblog_clean_type_5=Clean up log data a thousand record ago +joblog_clean_type_6=Clean up log data ten thousand record ago +joblog_clean_type_7=Clean up log data thirty thousand record ago +joblog_clean_type_8=Clean up log data hundred thousand record ago +joblog_clean_type_9=Clean up all log data +joblog_clean_type_unvalid=Clean type is illegal +joblog_handleCode_200=Success +joblog_handleCode_500=Fail +joblog_handleCode_501=Fail retry +joblog_kill_log=Kill Job +joblog_kill_log_limit=Trigger Fail, can not kill job +joblog_kill_log_byman=Manual operation to active kill job +joblog_rolling_log=Rolling log +joblog_rolling_log_refresh=Refresh +joblog_rolling_log_triggerfail=The job trigger fail, can not view the rolling log +joblog_rolling_log_failoften=The request for the Rolling log is terminated, the number of failed requests exceeds the limit, Reload the log on the refresh page +joblog_logid_unvalid=Log ID is illegal + +## job group +jobgroup_name=Executor Manage +jobgroup_list=Executor List +jobgroup_add=Add Executor +jobgroup_edit=Edit Executor +jobgroup_del=Delete Executor +jobgroup_field_order=Order +jobgroup_field_title=Title +jobgroup_field_addressType=Registry Type +jobgroup_field_addressType_0=Automatic registration +jobgroup_field_addressType_1=Manual registration +jobgroup_field_addressType_limit=Manually registration type, the machine address must not be empty +jobgroup_field_registryList=machine address +jobgroup_field_registryList_unvalid=registry machine address is illegal +jobgroup_field_registryList_placeholder=Please enter the machine address, if there are more than one comma separated +jobgroup_field_appName_limit=Limit the beginning of a lowercase letter, consists of lowercase letters、number and underscores. +jobgroup_field_appName_length=AppName length is limited to 4~64 +jobgroup_field_title_length=Title length is limited to 4~12 +jobgroup_field_order_digits=Please enter a positive integer +jobgroup_field_orderrange=Order is limited to 1~1000 +jobgroup_del_limit_0=Refuse to delete, the executor is being used +jobgroup_del_limit_1=Refuses to delete, the system retains at least one executor + +## job conf +jobconf_fail_alarm=Fail Alarm +jobconf_fail_retry=Fail Retry +jobconf_route_first=First +jobconf_route_last=Last +jobconf_route_round=Round +jobconf_route_random=Random +jobconf_route_consistenthash=Consistent Hash +jobconf_route_lfu=Least Frequently Used +jobconf_route_lru=Least Recently Used +jobconf_route_failover=Failover +jobconf_route_busyover=Busyover +jobconf_route_shard=Sharding Broadcast +jobconf_idleBeat=Idle check +jobconf_beat=Heartbeats +jobconf_monitor=Scheduling Center monitor alarm +jobconf_monitor_detail=monitor alarm details +jobconf_monitor_alarm_title=Alarm Type +jobconf_monitor_alarm_type=Trigger Fail +jobconf_trigger_admin_adress=Trigger machine address +jobconf_trigger_exe_regtype=Execotor-Registry Type +jobconf_trigger_exe_regaddress=Execotor-Registry Address +jobconf_trigger_address_empty=Trigger Fail:registry address is empty +jobconf_trigger_run=Trigger Job +jobconf_trigger_child_run=Trigger child job +jobconf_trigger_fail_retry=Trigger fail retry +jobconf_exe_fail_retry=Handle fail retry +jobconf_callback_child_msg1={0}/{1} [Job ID={2}], Trigger {3}, Trigger msg: {4}
+jobconf_callback_child_msg2={0}/{1} [Job ID={2}], Trigger Fail, Trigger msg: Job ID is illegal
+jobconf_callback_msg1=Trigger {0}, Trigger msg: {1}
+ +## help +job_help=Tutorial +job_help_document=Official Document \ No newline at end of file diff --git a/xxl-job-admin/src/main/resources/mybatis-mapper/XxlJobInfoMapper.xml b/xxl-job-admin/src/main/resources/mybatis-mapper/XxlJobInfoMapper.xml index a5c3abd0..b1fc6302 100644 --- a/xxl-job-admin/src/main/resources/mybatis-mapper/XxlJobInfoMapper.xml +++ b/xxl-job-admin/src/main/resources/mybatis-mapper/XxlJobInfoMapper.xml @@ -58,6 +58,9 @@ AND t.job_group = #{jobGroup} + + AND t.job_desc like CONCAT(CONCAT('%', #{jobDesc}), '%') + AND t.executor_handler like CONCAT(CONCAT('%', #{executorHandler}), '%') @@ -73,6 +76,9 @@ AND t.job_group = #{jobGroup} + + AND t.job_desc like CONCAT(CONCAT('%', #{jobDesc}), '%') + AND t.executor_handler like CONCAT(CONCAT('%', #{executorHandler}), '%') diff --git a/xxl-job-admin/src/main/resources/mybatis-mapper/XxlJobLogMapper.xml b/xxl-job-admin/src/main/resources/mybatis-mapper/XxlJobLogMapper.xml index a767aebd..6298a6da 100644 --- a/xxl-job-admin/src/main/resources/mybatis-mapper/XxlJobLogMapper.xml +++ b/xxl-job-admin/src/main/resources/mybatis-mapper/XxlJobLogMapper.xml @@ -163,13 +163,14 @@ diff --git a/xxl-job-admin/src/main/resources/quartz.properties b/xxl-job-admin/src/main/resources/quartz.properties index cc6ac0ec..368f172e 100644 --- a/xxl-job-admin/src/main/resources/quartz.properties +++ b/xxl-job-admin/src/main/resources/quartz.properties @@ -20,7 +20,7 @@ org.quartz.jobStore.maxMisfiresToHandleAtATime: 1 #org.quartz.jobStore.class: org.quartz.simpl.RAMJobStore # for cluster -org.quartz.jobStore.tablePrefix = XXL_JOB_QRTZ_ +org.quartz.jobStore.tablePrefix: XXL_JOB_QRTZ_ org.quartz.jobStore.class: org.quartz.impl.jdbcjobstore.JobStoreTX org.quartz.jobStore.isClustered: true org.quartz.jobStore.clusterCheckinInterval: 5000 diff --git a/xxl-job-admin/src/main/resources/spring/applicationcontext-base.xml b/xxl-job-admin/src/main/resources/spring/applicationcontext-base.xml index af486d7d..003649cf 100644 --- a/xxl-job-admin/src/main/resources/spring/applicationcontext-base.xml +++ b/xxl-job-admin/src/main/resources/spring/applicationcontext-base.xml @@ -7,7 +7,7 @@ http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd"> - + diff --git a/xxl-job-admin/src/main/resources/xxl-job-admin.properties b/xxl-job-admin/src/main/resources/xxl-job-admin.properties index 9c425aba..f038062c 100644 --- a/xxl-job-admin/src/main/resources/xxl-job-admin.properties +++ b/xxl-job-admin/src/main/resources/xxl-job-admin.properties @@ -16,4 +16,7 @@ xxl.job.login.username=admin xxl.job.login.password=123456 ### xxl-job, access token -xxl.job.accessToken= \ No newline at end of file +xxl.job.accessToken= + +### xxl-job, i18n (default empty as chinese, "en" as english) +xxl.job.i18n= \ No newline at end of file diff --git a/xxl-job-admin/src/main/webapp/500.html b/xxl-job-admin/src/main/webapp/500.html index ed32d54f..5cf0a73c 100644 --- a/xxl-job-admin/src/main/webapp/500.html +++ b/xxl-job-admin/src/main/webapp/500.html @@ -2,7 +2,7 @@ - 应用程序异常 (500) + Error