Merge pull request #2 from xuxueli/master

同步xuxueli/xxl-job最新代码
pull/MERGE
thinking_fioa 7 years ago committed by GitHub
commit 60eb19094b
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

@ -1,12 +1,10 @@
<p align="center">
<a href="http://www.xuxueli.com/xxl-job/">
<img src="https://raw.githubusercontent.com/xuxueli/xxl-job/master/doc/images/xxl-logo.jpg" width="150">
</a>
<h3 align="center">XXL-JOB</h3>
<p align="center">
XXL-JOB, a lightweight distributed task scheduling framework.
<br>
<a href="http://www.xuxueli.com/xxl-job/"><strong>-- Browse website. --</strong></a>
<a href="http://www.xuxueli.com/xxl-job/"><strong>-- Home Page --</strong></a>
<br>
<br>
<a href="https://travis-ci.org/xuxueli/xxl-job">
@ -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 )

@ -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 version1.8.1)
```
@ -204,6 +220,9 @@ The concrete contet describe as follows:
### TOKEN used for communication between the executor and schedule center, enabled if its not null
xxl.job.accessToken=
### Internationalized Settings, the default is Chinese versionSwitch 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.
the url to visit is :http://localhost:8080/xxl-job-admin (this address will be used by executor and use it as callback url),the index page after login in is as follow

@ -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-11XXL-JOB有幸参会《[InfoQ ArchSummit全球架构师峰会](http://bj2017.archsummit.com/)》,并被拍拍贷架构总监"杨波老师"在专题 "[微服务原理、基础架构和开源实践](http://bj2017.archsummit.com/training/2)" 中现场介绍。
于2017-12-18XXL-JOB参与"[2017年度最受欢迎中国开源软件](http://www.oschina.net/project/top_cn_2017?sort=1)"评比在当时已录入的约九千个国产开源项目中角逐最终进入了前30强。
于2018-01-15XXL-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是一个轻量级分布式任务调度框架其核心设计目标是
<dependency>
<groupId>com.xuxueli</groupId>
<artifactId>xxl-job-core</artifactId>
<version>1.8.2</version>
<version>${最新稳定版本}</version>
</dependency>
```
@ -223,6 +239,9 @@ XXL-JOB是一个轻量级分布式任务调度框架其核心设计目标是
### 调度中心通讯TOKEN非空时启用
xxl.job.accessToken=
### 调度中心国际化设置默认为中文版本值设置为“en”时切换为英文版本
xxl.job.i18n=
#### 步骤二:部署项目:
如果已经正确进行上述配置可将项目编译打war包并部署到tomcat中。
调度中心访问地址http://localhost:8080/xxl-job-admin (该地址执行器将会使用到,作为回调地址),登录后运行界面如下图所示
@ -263,11 +282,14 @@ 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是一个轻量级分布式任务调度框架其核心设计目标是
<!-- 配置01、JobHandler 扫描路径自动扫描容器中JobHandler -->
<context:component-scan base-package="com.xxl.job.executor.service.jobhandler" />
<!-- 配置02、执行器Excutor配置执行器核心配置 -->
<!-- 配置02、执行器 -->
<bean id="xxlJobExecutor" class="com.xxl.job.core.executor.XxlJobExecutor" init-method="start" destroy-method="destroy" >
<!-- 执行器注册中心地址[选填],为空则关闭自动注册 -->
<property name="adminAddresses" value="${xxl.job.admin.addresses}" />
<!-- 执行器AppName[选填],为空则关闭自动注册 -->
<property name="appName" value="${xxl.job.executor.appname}" />
<!-- 执行器IP[选填],为空则自动获取 -->
<property name="ip" value="${xxl.job.executor.ip}" />
<!-- 执行器端口号[选填],为空则自动获取 -->
<property name="port" value="${xxl.job.executor.port}" />
<!-- 执行器AppName[选填],为空则关闭自动注册 -->
<property name="appName" value="${xxl.job.executor.appname}" />
<!-- 执行器注册中心地址[选填],为空则关闭自动注册 -->
<property name="adminAddresses" value="${xxl.job.admin.addresses}" />
<!-- 执行器日志路径[选填],为空则使用默认路径 -->
<property name="logPath" value="${xxl.job.executor.logpath}" />
<!-- 访问令牌[选填],非空则进行匹配校验 -->
<property name="accessToken" value="${xxl.job.accessToken}" />
<!-- 执行器日志路径[选填],为空则使用默认路径 -->
<property name="logPath" value="${xxl.job.executor.logpath}" />
<!-- 日志保存天数[选填]值大于3时生效 -->
<property name="logRetentionDays" value="${xxl.job.executor.logretentiondays}" />
</bean>
```
#### 步骤四:部署执行器项目:
如果已经正确进行上述配置,可将执行器项目编译打部署,系统提供三个执行器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需要保持一致执行器根据该配置进行执行器自动注册等操作。
- 同一个执行器集群内AppNamexxl.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内存"情况下,单线程可以承担 100quartz最小时间粒度1000ms/触发一次任务耗时10ms个密集任务每秒执行一次的正常调度触发。因此默认配置的15个线程理论上可以承担起1500个密集任务的正常运行。
理论支撑任务量公式如下:
实际场景中调度请求网络耗时不同、DB读写耗时不同、任务密集或稀疏调度情况不同会导致任务量上限会上下波动。
理论支撑任务量 = 线程数配置 / 平均调度频率(每秒) * 平均触发耗时单位s
如若需要支撑更多的任务量,可以通过 "调大调度线程数" 和 "提升机器配置" 两种方式实现。
理论上采用推荐机器配置 "4核4G内存" + "配置1s运行1次密集任务" + "调度中心与执行器ping延迟10ms0.01s" 的情况下,
- 单线程支撑任务量 1 / 1 * 0.01 = 100个任务
- 15个线程支撑任务量15 / 1 * 0.01 = 1500个任务
实际场景中由于调度中心与执行器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 )

Binary file not shown.

@ -3,7 +3,7 @@
<modelVersion>4.0.0</modelVersion>
<groupId>com.xuxueli</groupId>
<artifactId>xxl-job</artifactId>
<version>1.9.0-SNAPSHOT</version>
<version>1.9.2-SNAPSHOT</version>
<packaging>pom</packaging>
<name>${project.artifactId}</name>
@ -20,16 +20,16 @@
<javax.servlet-api.version>3.0.1</javax.servlet-api.version>
<jsp-api.version>2.2</jsp-api.version>
<spring.version>4.3.13.RELEASE</spring.version>
<jackson.version>2.9.3</jackson.version>
<spring.version>4.3.14.RELEASE</spring.version>
<jackson.version>2.9.4</jackson.version>
<aspectjweaver.version>1.8.13</aspectjweaver.version>
<slf4j-api.version>1.7.25</slf4j-api.version>
<freemarker.version>2.3.23</freemarker.version>
<junit.version>4.12</junit.version>
<jetty-server.version>9.2.22.v20170606</jetty-server.version>
<jetty-server.version>9.2.24.v20180105</jetty-server.version>
<hessian.version>4.0.51</hessian.version>
<httpclient.version>4.5.4</httpclient.version>
<httpclient.version>4.5.5</httpclient.version>
<commons-exec.version>1.3</commons-exec.version>
<commons-collections4.version>4.1</commons-collections4.version>
@ -44,7 +44,7 @@
<groovy-all.version>2.4.13</groovy-all.version>
<quartz.version>2.3.0</quartz.version>
<spring-boot.version>1.5.9.RELEASE</spring-boot.version>
<spring-boot.version>1.5.10.RELEASE</spring-boot.version>
</properties>
<build>
@ -59,6 +59,14 @@
<encoding>UTF8</encoding>
</configuration>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-resources-plugin</artifactId>
<version>2.6</version>
<configuration>
<encoding>UTF-8</encoding>
</configuration>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-war-plugin</artifactId>

@ -4,7 +4,7 @@
<parent>
<groupId>com.xuxueli</groupId>
<artifactId>xxl-job</artifactId>
<version>1.9.0-SNAPSHOT</version>
<version>1.9.2-SNAPSHOT</version>
</parent>
<artifactId>xxl-job-admin</artifactId>
<packaging>war</packaging>

@ -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<Map<String, Object>> triggerChartDate(Date startDate, Date endDate) {
ReturnT<Map<String, Object>> triggerChartDate = xxlJobService.triggerChartDate(startDate, endDate);
return triggerChartDate;
public ReturnT<Map<String, Object>> chartInfo(Date startDate, Date endDate) {
ReturnT<Map<String, Object>> 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<String>(500, "账号或密码为空");
return new ReturnT<String>(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<String>(500, "账号或密码错误");
return new ReturnT<String>(500, I18nUtil.getString("login_param_unvalid"));
}
return ReturnT.SUCCESS;
}

@ -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<XxlJobLogGlue> 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<String> save(Model model, int id, String glueSource, String glueRemark) {
// valid
if (glueRemark==null) {
return new ReturnT<String>(500, "请输入备注");
return new ReturnT<String>(500, (I18nUtil.getString("system_please_input") + I18nUtil.getString("jobinfo_glue_remark")) );
}
if (glueRemark.length()<4 || glueRemark.length()>100) {
return new ReturnT<String>(500, "备注长度应该在4至100之间");
return new ReturnT<String>(500, I18nUtil.getString("jobinfo_glue_remark_limit"));
}
XxlJobInfo exists_jobInfo = xxlJobInfoDao.loadById(id);
if (exists_jobInfo == null) {
return new ReturnT<String>(500, "参数异常");
return new ReturnT<String>(500, I18nUtil.getString("jobinfo_glue_jobid_unvalid"));
}
// update new code

@ -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<String>(500, "请输入AppName");
return new ReturnT<String>(500, (I18nUtil.getString("system_please_input")+"AppName") );
}
if (xxlJobGroup.getAppName().length()>64) {
return new ReturnT<String>(500, "AppName长度限制为4~64");
if (xxlJobGroup.getAppName().length()<4 || xxlJobGroup.getAppName().length()>64) {
return new ReturnT<String>(500, I18nUtil.getString("jobgroup_field_appName_length") );
}
if (xxlJobGroup.getTitle()==null || StringUtils.isBlank(xxlJobGroup.getTitle())) {
return new ReturnT<String>(500, "请输入名称");
return new ReturnT<String>(500, (I18nUtil.getString("system_please_input") + I18nUtil.getString("jobgroup_field_title")) );
}
if (xxlJobGroup.getAddressType()!=0) {
if (StringUtils.isBlank(xxlJobGroup.getAddressList())) {
return new ReturnT<String>(500, "手动录入注册方式,机器地址不可为空");
return new ReturnT<String>(500, I18nUtil.getString("jobgroup_field_addressType_limit") );
}
String[] addresss = xxlJobGroup.getAddressList().split(",");
for (String item: addresss) {
if (StringUtils.isBlank(item)) {
return new ReturnT<String>(500, "机器地址非法");
return new ReturnT<String>(500, I18nUtil.getString("jobgroup_field_registryList_unvalid") );
}
}
}
@ -71,22 +72,22 @@ public class JobGroupController {
public ReturnT<String> update(XxlJobGroup xxlJobGroup){
// valid
if (xxlJobGroup.getAppName()==null || StringUtils.isBlank(xxlJobGroup.getAppName())) {
return new ReturnT<String>(500, "请输入AppName");
return new ReturnT<String>(500, (I18nUtil.getString("system_please_input")+"AppName") );
}
if (xxlJobGroup.getAppName().length()>64) {
return new ReturnT<String>(500, "AppName长度限制为4~64");
if (xxlJobGroup.getAppName().length()<4 || xxlJobGroup.getAppName().length()>64) {
return new ReturnT<String>(500, I18nUtil.getString("jobgroup_field_appName_length") );
}
if (xxlJobGroup.getTitle()==null || StringUtils.isBlank(xxlJobGroup.getTitle())) {
return new ReturnT<String>(500, "请输入名称");
return new ReturnT<String>(500, (I18nUtil.getString("system_please_input") + I18nUtil.getString("jobgroup_field_title")) );
}
if (xxlJobGroup.getAddressType()!=0) {
if (StringUtils.isBlank(xxlJobGroup.getAddressList())) {
return new ReturnT<String>(500, "手动录入注册方式,机器地址不可为空");
return new ReturnT<String>(500, I18nUtil.getString("jobgroup_field_addressType_limit") );
}
String[] addresss = xxlJobGroup.getAddressList().split(",");
for (String item: addresss) {
if (StringUtils.isBlank(item)) {
return new ReturnT<String>(500, "机器地址非法");
return new ReturnT<String>(500, I18nUtil.getString("jobgroup_field_registryList_unvalid") );
}
}
}
@ -100,14 +101,14 @@ public class JobGroupController {
public ReturnT<String> 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<String>(500, "该分组使用中, 不可删除");
return new ReturnT<String>(500, I18nUtil.getString("jobgroup_del_limit_0") );
}
List<XxlJobGroup> allList = xxlJobGroupDao.findAll();
if (allList.size() == 1) {
return new ReturnT<String>(500, "删除失败, 系统需要至少预留一个默认分组");
return new ReturnT<String>(500, I18nUtil.getString("jobgroup_del_limit_1") );
}
int ret = xxlJobGroupDao.remove(id);

@ -53,9 +53,9 @@ public class JobInfoController {
@ResponseBody
public Map<String, Object> 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<String> reschedule(XxlJobInfo jobInfo) {
return xxlJobService.reschedule(jobInfo);
public ReturnT<String> update(XxlJobInfo jobInfo) {
return xxlJobService.update(jobInfo);
}
@RequestMapping("/remove")

@ -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<XxlJobGroup> jobGroupList = xxlJobGroupDao.findAll();
model.addAttribute("JobGroupList", jobGroupList);
model.addAttribute("GlueTypeEnum", GlueTypeEnum.values());
// 任务
if (jobId > 0) {
@ -105,7 +108,7 @@ public class JobLogController {
ReturnT<String> 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<String>(500, "参数异常");
return new ReturnT<String>(500, I18nUtil.getString("jobinfo_glue_jobid_unvalid"));
}
if (ReturnT.SUCCESS_CODE != log.getTriggerCode()) {
return new ReturnT<String>(500, "调度失败,无法终止日志");
return new ReturnT<String>(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<String>(runResult.getMsg());
@ -197,7 +200,7 @@ public class JobLogController {
} else if (type == 9) {
clearBeforeNum = 0; // 清理所有日志数据
} else {
return new ReturnT<String>(ReturnT.FAIL_CODE, "清理类型参数异常");
return new ReturnT<String>(ReturnT.FAIL_CODE, I18nUtil.getString("joblog_clean_type_unvalid"));
}
xxlJobLogDao.clearLog(jobGroup, jobId, clearBeforeTime, clearBeforeNum);

@ -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;
@ -20,6 +22,7 @@ public class CookieInterceptor extends HandlerInterceptorAdapter {
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler,
ModelAndView modelAndView) throws Exception {
// cookie
if (modelAndView!=null && ArrayUtils.isNotEmpty(request.getCookies())) {
HashMap<String, Cookie> cookieMap = new HashMap<String, Cookie>();
for (Cookie ck : request.getCookies()) {
@ -28,6 +31,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);
}

@ -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);

@ -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
@ -24,18 +25,33 @@ public class WebExceptionResolver implements HandlerExceptionResolver {
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<String>(500, ex.toString().replaceAll("\n", "<br/>"))));
mv.setViewName("/common/common.result");
isJson = true;
}
// error result
ReturnT<String> errorResult = new ReturnT<String>(ReturnT.FAIL_CODE, ex.toString().replaceAll("\n", "<br/>"));
// 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", "<br/>"));
mv.addObject("exceptionMsg", errorResult.getMsg());
mv.setViewName("/common/common.exception");
}
return mv;
}
}
}

@ -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;
}
}

@ -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) {

@ -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;

@ -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<String>(ReturnT.FAIL_CODE, ""+e );
}
idleBeatResultSB.append( (idleBeatResultSB.length()>0)?"<br><br>":"")
.append("空闲检测")
.append(I18nUtil.getString("jobconf_idleBeat") + "")
.append("<br>address").append(address)
.append("<br>code").append(idleBeatResult.getCode())
.append("<br>msg").append(idleBeatResult.getMsg());

@ -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<String>(ReturnT.FAIL_CODE, ""+e );
}
beatResultSB.append( (beatResultSB.length()>0)?"<br><br>":"")
.append("心跳检测")
.append(I18nUtil.getString("jobconf_beat") + "")
.append("<br>address").append(address)
.append("<br>code").append(beatResult.getCode())
.append("<br>msg").append(beatResult.getMsg());

@ -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 = "<h5>监控告警明细</span>" +
private static final String mailBodyTemplate = "<h5>" + I18nUtil.getString("jobconf_monitor_detail") + "</span>" +
"<table border=\"1\" cellpadding=\"3\" style=\"border-collapse:collapse; width:80%;\" >\n" +
" <thead style=\"font-weight: bold;color: #ffffff;background-color: #ff8c00;\" >" +
" <tr>\n" +
" <td>执行器</td>\n" +
" <td>任务ID</td>\n" +
" <td>任务描述</td>\n" +
" <td>告警类型</td>\n" +
" <td>"+ I18nUtil.getString("jobinfo_field_jobgroup") +"</td>\n" +
" <td>"+ I18nUtil.getString("jobinfo_field_id") +"</td>\n" +
" <td>"+ I18nUtil.getString("jobinfo_field_jobdesc") +"</td>\n" +
" <td>"+ I18nUtil.getString("jobconf_monitor_alarm_title") +"</td>\n" +
" </tr>\n" +
" <thead/>\n" +
" <tbody>\n" +
@ -134,7 +135,7 @@ public class JobFailMonitorHelper {
" <td>{0}</td>\n" +
" <td>{1}</td>\n" +
" <td>{2}</td>\n" +
" <td>调度失败</td>\n" +
" <td>"+ I18nUtil.getString("jobconf_monitor_alarm_type") +"</td>\n" +
" </tr>\n" +
" <tbody>\n" +
"</table>";
@ -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);

@ -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<String> triggerResult = new ReturnT<String>(null);
StringBuffer triggerMsgSb = new StringBuffer();
triggerMsgSb.append("调度机器:").append(IpUtil.getIp());
triggerMsgSb.append("<br>执行器-注册方式:").append( (group.getAddressType() == 0)?"自动注册":"手动录入" );
triggerMsgSb.append("<br>执行器-地址列表:").append(group.getRegistryList());
triggerMsgSb.append("<br>路由策略:").append(executorRouteStrategyEnum.getTitle()).append("("+i+"/"+addressList.size()+")"); // update01
triggerMsgSb.append("<br>阻塞处理策略:").append(blockStrategy.getTitle());
triggerMsgSb.append("<br>失败处理策略:").append(failStrategy.getTitle());
triggerMsgSb.append(I18nUtil.getString("jobconf_trigger_admin_adress")).append("").append(IpUtil.getIp());
triggerMsgSb.append("<br>").append(I18nUtil.getString("jobconf_trigger_exe_regtype")).append("")
.append( (group.getAddressType() == 0)?I18nUtil.getString("jobgroup_field_addressType_0"):I18nUtil.getString("jobgroup_field_addressType_1") );
triggerMsgSb.append("<br>").append(I18nUtil.getString("jobconf_trigger_exe_regaddress")).append("").append(group.getRegistryList());
triggerMsgSb.append("<br>").append(I18nUtil.getString("jobinfo_field_executorRouteStrategy")).append("").append(executorRouteStrategyEnum.getTitle()).append("("+i+"/"+addressList.size()+")"); // update01
triggerMsgSb.append("<br>").append(I18nUtil.getString("jobinfo_field_executorBlockStrategy")).append("").append(blockStrategy.getTitle());
triggerMsgSb.append("<br>").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("<br>----------------------<br>").append("调度失败:").append("执行器地址为空");
triggerMsgSb.append("<br>----------------------<br>").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("<br><br><span style=\"color:#00c0ef;\" > >>>>>>>>>>>触发调度<<<<<<<<<<< </span><br>").append(triggerResult.getMsg());
triggerMsgSb.append("<br><br><span style=\"color:#00c0ef;\" > >>>>>>>>>>>"+ I18nUtil.getString("jobconf_trigger_run") +"<<<<<<<<<<< </span><br>").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("<br><br><span style=\"color:#F39C12;\" > >>>>>>>>>>>失败重试<<<<<<<<<<< </span><br>").append(triggerResult.getMsg());
triggerMsgSb.append("<br><br><span style=\"color:#F39C12;\" > >>>>>>>>>>>"+ I18nUtil.getString("jobconf_trigger_fail_retry") +"<<<<<<<<<<< </span><br>").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<String> triggerResult = new ReturnT<String>(null);
StringBuffer triggerMsgSb = new StringBuffer();
triggerMsgSb.append("调度机器:").append(IpUtil.getIp());
triggerMsgSb.append("<br>执行器-注册方式:").append( (group.getAddressType() == 0)?"自动注册":"手动录入" );
triggerMsgSb.append("<br>执行器-地址列表:").append(group.getRegistryList());
triggerMsgSb.append("<br>路由策略:").append(executorRouteStrategyEnum.getTitle());
triggerMsgSb.append("<br>阻塞处理策略:").append(blockStrategy.getTitle());
triggerMsgSb.append("<br>失败处理策略:").append(failStrategy.getTitle());
triggerMsgSb.append(I18nUtil.getString("jobconf_trigger_admin_adress")).append("").append(IpUtil.getIp());
triggerMsgSb.append("<br>").append(I18nUtil.getString("jobconf_trigger_exe_regtype")).append("")
.append( (group.getAddressType() == 0)?I18nUtil.getString("jobgroup_field_addressType_0"):I18nUtil.getString("jobgroup_field_addressType_1") );
triggerMsgSb.append("<br>").append(I18nUtil.getString("jobconf_trigger_exe_regaddress")).append("").append(group.getRegistryList());
triggerMsgSb.append("<br>").append(I18nUtil.getString("jobinfo_field_executorRouteStrategy")).append("").append(executorRouteStrategyEnum.getTitle());
triggerMsgSb.append("<br>").append(I18nUtil.getString("jobinfo_field_executorBlockStrategy")).append("").append(blockStrategy.getTitle());
triggerMsgSb.append("<br>").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("<br>----------------------<br>").append("调度失败:").append("执行器地址为空");
triggerMsgSb.append("<br>----------------------<br>").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("<br><br><span style=\"color:#00c0ef;\" > >>>>>>>>>>>触发调度<<<<<<<<<<< </span><br>").append(triggerResult.getMsg());
triggerMsgSb.append("<br><br><span style=\"color:#00c0ef;\" > >>>>>>>>>>>"+ I18nUtil.getString("jobconf_trigger_run") +"<<<<<<<<<<< </span><br>").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("<br><br><span style=\"color:#F39C12;\" > >>>>>>>>>>>调度失败重试<<<<<<<<<<< </span><br>").append(triggerResult.getMsg());
triggerMsgSb.append("<br><br><span style=\"color:#F39C12;\" > >>>>>>>>>>>"+ I18nUtil.getString("jobconf_trigger_fail_retry") +"<<<<<<<<<<< </span><br>").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<String>(ReturnT.FAIL_CODE, ""+e );
}
StringBuffer runResultSB = new StringBuffer("触发调度");
StringBuffer runResultSB = new StringBuffer(I18nUtil.getString("jobconf_trigger_run") + "");
runResultSB.append("<br>address").append(address);
runResultSB.append("<br>code").append(runResult.getCode());
runResultSB.append("<br>msg").append(runResult.getMsg());

@ -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;
}
}

@ -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<String, String> 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;
}
}

@ -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<String, LocalCacheData> 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()) {
return localCacheData.getVal();
} else {
remove(key);
return null;
}
}
/**
* clean timeout cache
*
* @return
*/
public static boolean cleanTimeutCache(){
if (!cacheRepository.keySet().isEmpty()) {
for (String key: cacheRepository.keySet()) {
LocalCacheData localCacheData = cacheRepository.get(key);
if (localCacheData!=null && System.currentTimeMillis()>=localCacheData.getTimeoutTime()) {
cacheRepository.remove(key);
}
}
}
return true;
}
}

@ -1,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);

@ -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;
}
}

@ -12,8 +12,16 @@ import java.util.List;
*/
public interface XxlJobInfoDao {
public List<XxlJobInfo> 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<XxlJobInfo> 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);

@ -41,8 +41,7 @@ public interface XxlJobLogDao {
public int triggerCountByHandleCode(@Param("handleCode") int handleCode);
public List<Map<String, Object>> 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,

@ -14,22 +14,81 @@ import java.util.Map;
*/
public interface XxlJobService {
public Map<String, Object> 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<String, Object> pageList(int start, int length, int jobGroup, String jobDesc, String executorHandler, String filterTime);
/**
* add job
*
* @param jobInfo
* @return
*/
public ReturnT<String> add(XxlJobInfo jobInfo);
public ReturnT<String> reschedule(XxlJobInfo jobInfo);
/**
* update job
*
* @param jobInfo
* @return
*/
public ReturnT<String> update(XxlJobInfo jobInfo);
/**
* remove job
*
* @param id
* @return
*/
public ReturnT<String> remove(int id);
/**
* pause job
*
* @param id
* @return
*/
public ReturnT<String> pause(int id);
/**
* resume job
*
* @param id
* @return
*/
public ReturnT<String> resume(int id);
/**
* trigger job
*
* @param id
* @return
*/
public ReturnT<String> triggerJob(int id);
/**
* dashboard info
*
* @return
*/
public Map<String,Object> dashboardInfo();
public ReturnT<Map<String,Object>> triggerChartDate(Date startDate, Date endDate);
/**
* chart info
*
* @param startDate
* @param endDate
* @return
*/
public ReturnT<Map<String,Object>> chartInfo(Date startDate, Date endDate);
}

@ -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 = "<br><br><span style=\"color:#00c0ef;\" > >>>>>>>>>>>触发子任务<<<<<<<<<<< </span><br>";
callbackMsg = "<br><br><span style=\"color:#00c0ef;\" > >>>>>>>>>>>"+ I18nUtil.getString("jobconf_trigger_child_run") +"<<<<<<<<<<< </span><br>";
String[] childJobIds = xxlJobInfo.getChildJobId().split(",");
for (int i = 0; i < childJobIds.length; i++) {
@ -73,21 +74,27 @@ public class AdminBizImpl implements AdminBiz {
ReturnT<String> triggerChildResult = xxlJobService.triggerJob(childJobId);
// add msg
callbackMsg += MessageFormat.format("{0}/{1} [任务ID={2}], 触发{3}, 触发备注: {4} <br>",
(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格式错误 <br>",
(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<String> retryTriggerResult = xxlJobService.triggerJob(log.getJobId());
callbackMsg = "<br><br><span style=\"color:#F39C12;\" > >>>>>>>>>>>执行失败重试<<<<<<<<<<< </span><br>";
callbackMsg = "<br><br><span style=\"color:#F39C12;\" > >>>>>>>>>>>"+ I18nUtil.getString("jobconf_exe_fail_retry") +"<<<<<<<<<<< </span><br>";
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

@ -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<String, Object> pageList(int start, int length, int jobGroup, String executorHandler, String filterTime) {
public Map<String, Object> pageList(int start, int length, int jobGroup, String jobDesc, String executorHandler, String filterTime) {
// page list
List<XxlJobInfo> list = xxlJobInfoDao.pageList(start, length, jobGroup, executorHandler);
int list_count = xxlJobInfoDao.pageListCount(start, length, jobGroup, executorHandler);
List<XxlJobInfo> 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<String>(ReturnT.FAIL_CODE, "请选择“执行器”");
return new ReturnT<String>(ReturnT.FAIL_CODE, (I18nUtil.getString("system_please_choose")+I18nUtil.getString("jobinfo_field_jobgroup")) );
}
if (!CronExpression.isValidExpression(jobInfo.getJobCron())) {
return new ReturnT<String>(ReturnT.FAIL_CODE, "请输入格式正确的“Cron”");
return new ReturnT<String>(ReturnT.FAIL_CODE, I18nUtil.getString("jobinfo_field_cron_unvalid") );
}
if (StringUtils.isBlank(jobInfo.getJobDesc())) {
return new ReturnT<String>(ReturnT.FAIL_CODE, "请输入“任务描述”");
return new ReturnT<String>(ReturnT.FAIL_CODE, (I18nUtil.getString("system_please_input")+I18nUtil.getString("jobinfo_field_jobdesc")) );
}
if (StringUtils.isBlank(jobInfo.getAuthor())) {
return new ReturnT<String>(ReturnT.FAIL_CODE, "请输入“负责人”");
return new ReturnT<String>(ReturnT.FAIL_CODE, (I18nUtil.getString("system_please_input")+I18nUtil.getString("jobinfo_field_author")) );
}
if (ExecutorRouteStrategyEnum.match(jobInfo.getExecutorRouteStrategy(), null) == null) {
return new ReturnT<String>(ReturnT.FAIL_CODE, "路由策略非法");
return new ReturnT<String>(ReturnT.FAIL_CODE, (I18nUtil.getString("jobinfo_field_executorRouteStrategy")+I18nUtil.getString("system_unvalid")) );
}
if (ExecutorBlockStrategyEnum.match(jobInfo.getExecutorBlockStrategy(), null) == null) {
return new ReturnT<String>(ReturnT.FAIL_CODE, "阻塞处理策略非法");
return new ReturnT<String>(ReturnT.FAIL_CODE, (I18nUtil.getString("jobinfo_field_executorBlockStrategy")+I18nUtil.getString("system_unvalid")) );
}
if (ExecutorFailStrategyEnum.match(jobInfo.getExecutorFailStrategy(), null) == null) {
return new ReturnT<String>(ReturnT.FAIL_CODE, "失败处理策略非法");
return new ReturnT<String>(ReturnT.FAIL_CODE, (I18nUtil.getString("jobinfo_field_executorFailStrategy")+I18nUtil.getString("system_unvalid")) );
}
if (GlueTypeEnum.match(jobInfo.getGlueType()) == null) {
return new ReturnT<String>(ReturnT.FAIL_CODE, "运行模式非法非法");
return new ReturnT<String>(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<String>(ReturnT.FAIL_CODE, "请输入“JobHandler”");
return new ReturnT<String>(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<String>(ReturnT.FAIL_CODE, MessageFormat.format("子任务ID({0})无效", childJobIdItem));
return new ReturnT<String>(ReturnT.FAIL_CODE,
MessageFormat.format((I18nUtil.getString("jobinfo_field_childJobId")+"({0})"+I18nUtil.getString("system_not_found")), childJobIdItem));
}
} else {
return new ReturnT<String>(ReturnT.FAIL_CODE, MessageFormat.format("子任务ID({0})格式错误", childJobIdItem));
return new ReturnT<String>(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<String>(ReturnT.FAIL_CODE, "新增任务失败");
return new ReturnT<String>(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<String>(ReturnT.FAIL_CODE, "新增任务失败:" + e.getMessage());
return new ReturnT<String>(ReturnT.FAIL_CODE, (I18nUtil.getString("jobinfo_field_add")+I18nUtil.getString("system_fail"))+":" + e.getMessage());
}
}
@Override
public ReturnT<String> reschedule(XxlJobInfo jobInfo) {
public ReturnT<String> update(XxlJobInfo jobInfo) {
// valid
if (!CronExpression.isValidExpression(jobInfo.getJobCron())) {
return new ReturnT<String>(ReturnT.FAIL_CODE, "请输入格式正确的“Cron”");
return new ReturnT<String>(ReturnT.FAIL_CODE, I18nUtil.getString("jobinfo_field_cron_unvalid") );
}
if (StringUtils.isBlank(jobInfo.getJobDesc())) {
return new ReturnT<String>(ReturnT.FAIL_CODE, "请输入“任务描述”");
return new ReturnT<String>(ReturnT.FAIL_CODE, (I18nUtil.getString("system_please_input")+I18nUtil.getString("jobinfo_field_jobdesc")) );
}
if (StringUtils.isBlank(jobInfo.getAuthor())) {
return new ReturnT<String>(ReturnT.FAIL_CODE, "请输入“负责人”");
return new ReturnT<String>(ReturnT.FAIL_CODE, (I18nUtil.getString("system_please_input")+I18nUtil.getString("jobinfo_field_author")) );
}
if (ExecutorRouteStrategyEnum.match(jobInfo.getExecutorRouteStrategy(), null) == null) {
return new ReturnT<String>(ReturnT.FAIL_CODE, "路由策略非法");
return new ReturnT<String>(ReturnT.FAIL_CODE, (I18nUtil.getString("jobinfo_field_executorRouteStrategy")+I18nUtil.getString("system_unvalid")) );
}
if (ExecutorBlockStrategyEnum.match(jobInfo.getExecutorBlockStrategy(), null) == null) {
return new ReturnT<String>(ReturnT.FAIL_CODE, "阻塞处理策略非法");
return new ReturnT<String>(ReturnT.FAIL_CODE, (I18nUtil.getString("jobinfo_field_executorBlockStrategy")+I18nUtil.getString("system_unvalid")) );
}
if (ExecutorFailStrategyEnum.match(jobInfo.getExecutorFailStrategy(), null) == null) {
return new ReturnT<String>(ReturnT.FAIL_CODE, "失败处理策略非法");
return new ReturnT<String>(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<String>(ReturnT.FAIL_CODE, MessageFormat.format("子任务ID({0})无效", childJobIdItem));
return new ReturnT<String>(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<String>(ReturnT.FAIL_CODE, MessageFormat.format("子任务ID({0})不可与父任务重复", childJobIdItem));
return new ReturnT<String>(ReturnT.FAIL_CODE, MessageFormat.format(I18nUtil.getString("jobinfo_field_childJobId_limit"), childJobIdItem));
}
} else {
return new ReturnT<String>(ReturnT.FAIL_CODE, MessageFormat.format("子任务ID({0})格式错误", childJobIdItem));
return new ReturnT<String>(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<String>(ReturnT.FAIL_CODE, "参数异常");
return new ReturnT<String>(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<String> triggerJob(int id) {
XxlJobInfo xxlJobInfo = xxlJobInfoDao.loadById(id);
if (xxlJobInfo == null) {
return new ReturnT<String>(ReturnT.FAIL_CODE, "任务ID非法");
return new ReturnT<String>(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<Map<String, Object>> triggerChartDate(Date startDate, Date endDate) {
public ReturnT<Map<String, Object>> chartInfo(Date startDate, Date endDate) {
// get cache
String cacheKey = TRIGGER_CHART_DATA_CACHE + "_" + startDate.getTime() + "_" + endDate.getTime();
Map<String, Object> chartInfo = (Map<String, Object>) LocalCacheUtil.get(cacheKey);
if (chartInfo != null) {
return new ReturnT<Map<String, Object>>(chartInfo);
}
// process
List<String> triggerDayList = new ArrayList<String>();
List<Integer> triggerDayCountRunningList = new ArrayList<Integer>();
List<Integer> triggerDayCountSucList = new ArrayList<Integer>();
List<Integer> triggerDayCountFailList = new ArrayList<Integer>();
int triggerCountRunningTotal = 0;
int triggerCountSucTotal = 0;
int triggerCountFailTotal = 0;
List<Map<String, Object>> triggerCountMapAll = xxlJobLogDao.triggerCountByDay(startDate, endDate, -1);
List<Map<String, Object>> triggerCountMapSuc = xxlJobLogDao.triggerCountByDay(startDate, endDate, ReturnT.SUCCESS_CODE);
List<Map<String, Object>> triggerCountMapAll = xxlJobLogDao.triggerCountByDay(startDate, endDate);
if (CollectionUtils.isNotEmpty(triggerCountMapAll)) {
for (Map<String, Object> 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<String, Object> 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<String, Object> result = new HashMap<String, Object>();
result.put("triggerDayList", triggerDayList);
result.put("triggerDayCountRunningList", triggerDayCountRunningList);
result.put("triggerDayCountSucList", triggerDayCountSucList);
result.put("triggerDayCountFailList", triggerDayCountFailList);
result.put("triggerCountRunningTotal", triggerCountRunningTotal);
result.put("triggerCountSucTotal", triggerCountSucTotal);
result.put("triggerCountFailTotal", triggerCountFailTotal);
// set cache
LocalCacheUtil.set(cacheKey, result, 60*1000); // cache 60s
return new ReturnT<Map<String, Object>>(result);
}

@ -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} <br>
jobconf_callback_child_msg2={0}/{1} [任务ID={2}], 触发失败, 触发备注: 任务ID格式错误 <br>
jobconf_callback_msg1=触发{0}, 触发备注: {1} <br>
## help
job_help=使用教程
job_help_document=官方文档

@ -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 Failregistry 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} <br>
jobconf_callback_child_msg2={0}/{1} [Job ID={2}], Trigger Fail, Trigger msg: Job ID is illegal <br>
jobconf_callback_msg1=Trigger {0}, Trigger msg: {1} <br>
## help
job_help=Tutorial
job_help_document=Official Document

@ -58,6 +58,9 @@
<if test="jobGroup gt 0">
AND t.job_group = #{jobGroup}
</if>
<if test="jobDesc != null and jobDesc != ''">
AND t.job_desc like CONCAT(CONCAT('%', #{jobDesc}), '%')
</if>
<if test="executorHandler != null and executorHandler != ''">
AND t.executor_handler like CONCAT(CONCAT('%', #{executorHandler}), '%')
</if>
@ -73,6 +76,9 @@
<if test="jobGroup gt 0">
AND t.job_group = #{jobGroup}
</if>
<if test="jobDesc != null and jobDesc != ''">
AND t.job_desc like CONCAT(CONCAT('%', #{jobDesc}), '%')
</if>
<if test="executorHandler != null and executorHandler != ''">
AND t.executor_handler like CONCAT(CONCAT('%', #{executorHandler}), '%')
</if>

@ -163,12 +163,13 @@
</select>
<select id="triggerCountByDay" resultType="java.util.Map" >
SELECT DATE_FORMAT(trigger_time,'%Y-%m-%d') triggerDay, COUNT(id) triggerCount
SELECT
DATE_FORMAT(trigger_time,'%Y-%m-%d') triggerDay,
COUNT(handle_code) triggerDayCount,
SUM(CASE WHEN handle_code = 0 then 1 else 0 end) as triggerDayCountRunning,
SUM(CASE WHEN handle_code = 200 then 1 else 0 end) as triggerDayCountSuc
FROM XXL_JOB_QRTZ_TRIGGER_LOG
WHERE trigger_time BETWEEN #{from} and #{to}
<if test="handleCode gt 0">
AND handle_code = #{handleCode}
</if>
GROUP BY triggerDay;
</select>

@ -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

@ -7,7 +7,7 @@
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd">
<context:component-scan base-package="com.xxl.job.admin.service, com.xxl.job.admin.dao" />
<context:component-scan base-package="com.xxl.job.admin.core.conf, com.xxl.job.admin.service, com.xxl.job.admin.dao" />
<bean id="freemarkerConfig" class="org.springframework.web.servlet.view.freemarker.FreeMarkerConfigurer">
<property name="templateLoaderPath" value="/WEB-INF/template/" />

@ -17,3 +17,6 @@ xxl.job.login.password=123456
### xxl-job, access token
xxl.job.accessToken=
### xxl-job, i18n (default empty as chinese, "en" as english)
xxl.job.i18n=

@ -2,7 +2,7 @@
<html>
<head>
<meta charset="UTF-8">
<title> (500)</title>
<title>Error</title>
<style type="text/css">
body { background-color: #fff; color: #666; text-align: center; font-family: arial, sans-serif; }
div.dialog {
@ -21,8 +21,9 @@
<body>
<div class="dialog">
<h1></h1>
<p>访</p>
<h1>System Error</h1>
<p>Oops! Page not found.</p>
<a href="javascript:window.location.href='${request.contextPath}/'">Back</a>
</div>
</body>

@ -2,7 +2,7 @@
<html>
<head>
<meta charset="UTF-8">
<title> (error)</title>
<title>Error</title>
<style type="text/css">
body { background-color: #fff; color: #666; text-align: center; font-family: arial, sans-serif; }
div.dialog {
@ -21,9 +21,9 @@
<body>
<div class="dialog">
<h1></h1>
<h1>System Error</h1>
<p>${exceptionMsg}</p>
<a href="javascript:window.location.href='${request.contextPath}/'"> </a>
<a href="javascript:window.location.href='${request.contextPath}/'">Back</a>
</p>
</div>

@ -29,6 +29,10 @@
<!-- pace -->
<link rel="stylesheet" href="${request.contextPath}/static/plugins/pace/themes/pace-theme-flash.css">
<#-- i18n -->
<#global I18n = I18nUtil.getMultString()?eval />
</#macro>
<#macro commonScript>
@ -53,7 +57,10 @@
<#-- common -->
<script src="${request.contextPath}/static/js/common.1.js"></script>
<script>var base_url = '${request.contextPath}';</script>
<script>
var base_url = '${request.contextPath}';
var I18n = ${I18nUtil.getMultString()};
</script>
</#macro>
@ -61,7 +68,7 @@
<header class="main-header">
<a href="${request.contextPath}/" class="logo">
<span class="logo-mini"><b>XXL</b></span>
<span class="logo-lg"><b></b></span>
<span class="logo-lg"><b>${I18n.admin_name}</b></span>
</a>
<nav class="navbar navbar-static-top" role="navigation">
<a href="#" class="sidebar-toggle" data-toggle="offcanvas" role="button"><span class="sr-only"></span></a>
@ -69,7 +76,7 @@
<ul class="nav navbar-nav">
<li class="dropdown user user-menu">
<a href=";" id="logoutBtn" class="dropdown-toggle" data-toggle="dropdown" aria-expanded="false">
<span class="hidden-xs"></span>
<span class="hidden-xs">${I18n.logout_btn}</span>
</a>
</li>
</ul>
@ -85,11 +92,11 @@
<section class="sidebar">
<!-- sidebar menu: : style can be found in sidebar.less -->
<ul class="sidebar-menu">
<li class="header"></li>
<li class="nav-click <#if pageName == "jobinfo">active</#if>" ><a href="${request.contextPath}/jobinfo"><i class="fa fa-circle-o text-aqua"></i><span></span></a></li>
<li class="nav-click <#if pageName == "joblog">active</#if>" ><a href="${request.contextPath}/joblog"><i class="fa fa-circle-o text-yellow"></i><span></span></a></li>
<li class="nav-click <#if pageName == "jobgroup">active</#if>" ><a href="${request.contextPath}/jobgroup"><i class="fa fa-circle-o text-green"></i><span></span></a></li>
<li class="nav-click <#if pageName == "help">active</#if>" ><a href="${request.contextPath}/help"><i class="fa fa-circle-o text-gray"></i><span>使</span></a></li>
<li class="header">${I18n.system_nav}</li>
<li class="nav-click <#if pageName == "jobinfo">active</#if>" ><a href="${request.contextPath}/jobinfo"><i class="fa fa-circle-o text-aqua"></i><span>${I18n.jobinfo_name}</span></a></li>
<li class="nav-click <#if pageName == "joblog">active</#if>" ><a href="${request.contextPath}/joblog"><i class="fa fa-circle-o text-yellow"></i><span>${I18n.joblog_name}</span></a></li>
<li class="nav-click <#if pageName == "jobgroup">active</#if>" ><a href="${request.contextPath}/jobgroup"><i class="fa fa-circle-o text-green"></i><span>${I18n.jobgroup_name}</span></a></li>
<li class="nav-click <#if pageName == "help">active</#if>" ><a href="${request.contextPath}/help"><i class="fa fa-circle-o text-gray"></i><span>${I18n.job_help}</span></a></li>
</ul>
</section>
<!-- /.sidebar -->
@ -175,7 +182,7 @@
<#macro commonFooter >
<footer class="main-footer">
Powered by <b>XXL-JOB</b> 1.9.0
Powered by <b>XXL-JOB</b> ${I18n.admin_version}
<div class="pull-right hidden-xs">
<strong>Copyright &copy; 2015-${.now?string('yyyy')} &nbsp;
<a href="http://www.xuxueli.com/" target="_blank" >xuxueli</a>

@ -1,9 +1,9 @@
<!DOCTYPE html>
<html>
<head>
<title></title>
<#import "/common/common.macro.ftl" as netCommon>
<@netCommon.commonStyle />
<title>${I18n.admin_name}</title>
</head>
<body class="hold-transition skin-blue sidebar-mini <#if cookieMap?exists && "off" == cookieMap["xxljob_adminlte_settings"].value >sidebar-collapse</#if> ">
<div class="wrapper">
@ -16,25 +16,19 @@
<div class="content-wrapper">
<!-- Content Header (Page header) -->
<section class="content-header">
<h1>使<small></small></h1>
<!--
<ol class="breadcrumb">
<li><a><i class="fa fa-dashboard"></i></a></li>
<li class="active">使</li>
</ol>
-->
<h1>${I18n.job_help}</h1>
</section>
<!-- Main content -->
<section class="content">
<div class="callout callout-info">
<h4>XXL-JOB</h4>
<h4>${I18n.admin_name_full}</h4>
<br>
<p>
<a target="_blank" href="https://github.com/xuxueli/xxl-job">github</a>&nbsp;&nbsp;&nbsp;&nbsp;
<a target="_blank" href="https://github.com/xuxueli/xxl-job">Github</a>&nbsp;&nbsp;&nbsp;&nbsp;
<iframe src="https://ghbtns.com/github-btn.html?user=xuxueli&repo=xxl-job&type=star&count=true" frameborder="0" scrolling="0" width="170px" height="20px" style="margin-bottom:-5px;"></iframe>
<br><br>
<a target="_blank" href="http://www.xuxueli.com/xxl-job/"></a>
<a target="_blank" href="http://www.xuxueli.com/xxl-job/">${I18n.job_help_document}</a>
<br><br>
</p>

@ -1,11 +1,11 @@
<!DOCTYPE html>
<html>
<head>
<title></title>
<#import "/common/common.macro.ftl" as netCommon>
<@netCommon.commonStyle />
<!-- daterangepicker -->
<link rel="stylesheet" href="${request.contextPath}/static/adminlte/plugins/daterangepicker/daterangepicker.css">
<title>${I18n.admin_name}</title>
</head>
<body class="hold-transition skin-blue sidebar-mini <#if cookieMap?exists && "off" == cookieMap["xxljob_adminlte_settings"].value >sidebar-collapse</#if> ">
<div class="wrapper">
@ -18,8 +18,9 @@
<div class="content-wrapper">
<!-- Content Header (Page header) -->
<section class="content-header">
<h1><small></small></h1>
<h1>${I18n.job_dashboard_name}</h1>
<!--
<h1><small></small></h1>
<ol class="breadcrumb">
<li><a><i class="fa fa-dashboard"></i></a></li>
<li class="active">使</li>
@ -39,13 +40,13 @@
<span class="info-box-icon"><i class="fa fa-flag-o"></i></span>
<div class="info-box-content">
<span class="info-box-text"></span>
<span class="info-box-text">${I18n.job_dashboard_job_num}</span>
<span class="info-box-number">${jobInfoCount}</span>
<div class="progress">
<div class="progress-bar" style="width: 100%"></div>
</div>
<span class="progress-description"></span>
<span class="progress-description">${I18n.job_dashboard_job_num_tip}</span>
</div>
</div>
</div>
@ -56,14 +57,14 @@
<span class="info-box-icon"><i class="fa fa-calendar"></i></span>
<div class="info-box-content">
<span class="info-box-text"></span>
<span class="info-box-text">${I18n.job_dashboard_trigger_num}</span>
<span class="info-box-number">${jobLogCount}</span>
<div class="progress">
<div class="progress-bar" style="width: 100%" ></div>
</div>
<span class="progress-description">
${I18n.job_dashboard_trigger_num_tip}
<#--<#if jobLogCount gt 0>
${(jobLogSuccessCount*100/jobLogCount)?string("0.00")}<small>%</small>
</#if>-->
@ -78,13 +79,13 @@
<span class="info-box-icon"><i class="fa ion-ios-settings-strong"></i></span>
<div class="info-box-content">
<span class="info-box-text"></span>
<span class="info-box-text">${I18n.job_dashboard_jobgroup_num}</span>
<span class="info-box-number">${executorCount}</span>
<div class="progress">
<div class="progress-bar" style="width: 100%"></div>
</div>
<span class="progress-description">线</span>
<span class="progress-description">${I18n.job_dashboard_jobgroup_num_tip}</span>
</div>
</div>
</div>
@ -96,7 +97,7 @@
<div class="col-md-12">
<div class="box">
<div class="box-header with-border">
<h3 class="box-title"></h3>
<h3 class="box-title">${I18n.job_dashboard_report}</h3>
<#--<input type="text" class="form-control" id="filterTime" readonly >-->
<!-- tools box -->

@ -1,11 +1,11 @@
<!DOCTYPE html>
<html>
<head>
<title></title>
<#import "/common/common.macro.ftl" as netCommon>
<@netCommon.commonStyle />
<link rel="stylesheet" href="${request.contextPath}/static/plugins/codemirror/lib/codemirror.css">
<link rel="stylesheet" href="${request.contextPath}/static/plugins/codemirror/addon/hint/show-hint.css">
<title>${I18n.admin_name}</title>
<style type="text/css">
.CodeMirror {
font-size:16px;
@ -35,7 +35,11 @@
<#-- left nav -->
<div class="collapse navbar-collapse pull-left" id="navbar-collapse">
<ul class="nav navbar-nav">
<li class="active" ><a href="javascript:;"><#list GlueTypeEnum as item><#if item == jobInfo.glueType>${item.desc}</#if></#list> ${jobInfo.jobDesc}<span class="sr-only">(current)</span></a></li>
<li class="active" ><a href="javascript:;">
<span class="sr-only">(current)</span>
<#list GlueTypeEnum as item><#if item == jobInfo.glueType>${item.desc}</#if></#list>
${jobInfo.jobDesc}
</a></li>
</ul>
</div>
@ -43,7 +47,7 @@
<div class="navbar-custom-menu">
<ul class="nav navbar-nav">
<li class="dropdown">
<a href="#" class="dropdown-toggle" data-toggle="dropdown" aria-expanded="false"> <span class="caret"></span></a>
<a href="#" class="dropdown-toggle" data-toggle="dropdown" aria-expanded="false">${I18n.jobinfo_glue_rollback} <span class="caret"></span></a>
<ul class="dropdown-menu" role="menu">
<li <#if jobLogGlues?exists && jobLogGlues?size gt 0 >style="display: none;"</#if> >
<a href="javascript:;" class="source_version" version="version_now" glueType="${jobInfo.glueType}" >
@ -66,7 +70,7 @@
<li id="save" >
<a href="javascript:;" >
<i class="fa fa-fw fa-save" ></i>
${I18n.system_save}
</a>
</li>
</ul>
@ -87,19 +91,19 @@
<div class="modal-dialog ">
<div class="modal-content">
<div class="modal-header">
<h4 class="modal-title" ><i class="fa fa-fw fa-save"></i></h4>
<h4 class="modal-title" ><i class="fa fa-fw fa-save"></i>${I18n.system_save}</h4>
</div>
<div class="modal-body">
<form class="form-horizontal form" role="form" >
<div class="form-group">
<label for="lastname" class="col-sm-2 control-label"><font color="red">*</font></label>
<div class="col-sm-10"><input type="text" class="form-control" id="glueRemark" placeholder="请输入备注信息" maxlength="64" ></div>
<label for="lastname" class="col-sm-2 control-label">${I18n.jobinfo_glue_remark}<font color="red">*</font></label>
<div class="col-sm-10"><input type="text" class="form-control" id="glueRemark" placeholder="${I18n.system_please_input}${I18n.jobinfo_glue_remark}" maxlength="64" ></div>
</div>
<hr>
<div class="form-group">
<div class="col-sm-offset-3 col-sm-6">
<button type="button" class="btn btn-primary ok" ></button>
<button type="button" class="btn btn-default" data-dismiss="modal"></button>
<button type="button" class="btn btn-primary ok" >${I18n.system_save}</button>
<button type="button" class="btn btn-default" data-dismiss="modal">${I18n.system_cancel}</button>
</div>
</div>
</form>

@ -1,11 +1,11 @@
<!DOCTYPE html>
<html>
<head>
<title></title>
<#import "/common/common.macro.ftl" as netCommon>
<@netCommon.commonStyle />
<!-- DataTables -->
<link rel="stylesheet" href="${request.contextPath}/static/adminlte/plugins/datatables/dataTables.bootstrap.css">
<title>${I18n.admin_name}</title>
</head>
<body class="hold-transition skin-blue sidebar-mini <#if cookieMap?exists && "off" == cookieMap["xxljob_adminlte_settings"].value >sidebar-collapse</#if> ">
<div class="wrapper">
@ -18,7 +18,7 @@
<div class="content-wrapper">
<!-- Content Header (Page header) -->
<section class="content-header">
<h1><small></small></h1>
<h1>${I18n.jobgroup_name}</h1>
</section>
<!-- Main content -->
@ -28,20 +28,20 @@
<div class="col-xs-12">
<div class="box">
<div class="box-header">
<h3 class="box-title"></h3>&nbsp;&nbsp;
<button class="btn btn-info btn-xs pull-left2 add" >+</button>
<h3 class="box-title">${I18n.jobgroup_list}</h3>&nbsp;&nbsp;
<button class="btn btn-info btn-xs pull-left2 add" >${I18n.jobgroup_add}</button>
</div>
<div class="box-body">
<table id="joblog_list" class="table table-bordered table-striped display" width="100%" >
<thead>
<tr>
<#--<th name="id" >ID</th>-->
<th name="order" ></th>
<th name="order" >${I18n.jobgroup_field_order}</th>
<th name="appName" >AppName</th>
<th name="title" ></th>
<th name="addressType" ></th>
<th name="registryList" >OnLine </th>
<th name="operate" ></th>
<th name="title" >${I18n.jobgroup_field_title}</th>
<th name="addressType" >${I18n.jobgroup_field_addressType}</th>
<th name="registryList" >OnLine ${I18n.jobgroup_field_registryList}</th>
<th name="operate" >${I18n.system_opt}</th>
</tr>
</thead>
<tbody>
@ -52,7 +52,7 @@
<td>${group.order}</td>
<td>${group.appName}</td>
<td>${group.title}</td>
<td><#if group.addressType==0><#else></#if></td>
<td><#if group.addressType==0>${I18n.jobgroup_field_addressType_0}<#else>${I18n.jobgroup_field_addressType_1}</#if></td>
<td>
<#if group.registryList?exists>
<#list group.registryList as item>
@ -74,8 +74,8 @@
title="${group.title}"
order="${group.order}"
addressType="${group.addressType}"
addressList="${group.addressList}" ></button>
<button class="btn btn-danger btn-xs remove" id="${group.id}" ></button>
addressList="${group.addressList}" >${I18n.system_opt_edit}</button>
<button class="btn btn-danger btn-xs remove" id="${group.id}" >${I18n.system_opt_del}</button>
</td>
</tr>
</#list>
@ -94,41 +94,41 @@
<div class="modal-dialog ">
<div class="modal-content">
<div class="modal-header">
<h4 class="modal-title" ></h4>
<h4 class="modal-title" >${I18n.jobgroup_add}</h4>
</div>
<div class="modal-body">
<form class="form-horizontal form" role="form" >
<div class="form-group">
<label for="lastname" class="col-sm-2 control-label">AppName<font color="red">*</font></label>
<div class="col-sm-10"><input type="text" class="form-control" name="appName" placeholder="请输入“AppName”" maxlength="64" ></div>
<div class="col-sm-10"><input type="text" class="form-control" name="appName" placeholder="${I18n.system_please_input}AppName" maxlength="64" ></div>
</div>
<div class="form-group">
<label for="lastname" class="col-sm-2 control-label"><font color="red">*</font></label>
<div class="col-sm-10"><input type="text" class="form-control" name="title" placeholder="请输入“名称”" maxlength="12" ></div>
<label for="lastname" class="col-sm-2 control-label">${I18n.jobgroup_field_title}<font color="red">*</font></label>
<div class="col-sm-10"><input type="text" class="form-control" name="title" placeholder="${I18n.system_please_input}${I18n.jobgroup_field_title}" maxlength="12" ></div>
</div>
<div class="form-group">
<label for="lastname" class="col-sm-2 control-label"><font color="red">*</font></label>
<div class="col-sm-10"><input type="text" class="form-control" name="order" placeholder="请输入“排序”" maxlength="50" ></div>
<label for="lastname" class="col-sm-2 control-label">${I18n.jobgroup_field_order}<font color="red">*</font></label>
<div class="col-sm-10"><input type="text" class="form-control" name="order" placeholder="${I18n.system_please_input}${I18n.jobgroup_field_order}" maxlength="50" ></div>
</div>
<div class="form-group">
<label for="lastname" class="col-sm-2 control-label"><font color="red">*</font></label>
<label for="lastname" class="col-sm-2 control-label">${I18n.jobgroup_field_addressType}<font color="red">*</font></label>
<div class="col-sm-10">
<input type="radio" name="addressType" value="0" checked />
<input type="radio" name="addressType" value="0" checked />${I18n.jobgroup_field_addressType_0}
&nbsp;&nbsp;&nbsp;&nbsp;
<input type="radio" name="addressType" value="1" />
<input type="radio" name="addressType" value="1" />${I18n.jobgroup_field_addressType_1}
</div>
</div>
<div class="form-group">
<label for="lastname" class="col-sm-2 control-label"><font color="red">*</font></label>
<label for="lastname" class="col-sm-2 control-label">${I18n.jobgroup_field_registryList}<font color="red">*</font></label>
<div class="col-sm-10">
<textarea class="textarea" name="addressList" maxlength="512" placeholder="请输入执行器地址列表,多地址逗号分隔" readonly="readonly" style="background-color:#eee; width: 100%; height: 100px; font-size: 14px; line-height: 10px; border: 1px solid #dddddd; padding: 10px;"></textarea>
<textarea class="textarea" name="addressList" maxlength="512" placeholder="${I18n.jobgroup_field_registryList_placeholder}" readonly="readonly" style="background-color:#eee; width: 100%; height: 100px; font-size: 14px; line-height: 10px; border: 1px solid #dddddd; padding: 10px;"></textarea>
</div>
</div>
<hr>
<div class="form-group">
<div class="col-sm-offset-3 col-sm-6">
<button type="submit" class="btn btn-primary" ></button>
<button type="button" class="btn btn-default" data-dismiss="modal"></button>
<button type="submit" class="btn btn-primary" >${I18n.system_save}</button>
<button type="button" class="btn btn-default" data-dismiss="modal">${I18n.system_cancel}</button>
</div>
</div>
</form>
@ -142,41 +142,41 @@
<div class="modal-dialog ">
<div class="modal-content">
<div class="modal-header">
<h4 class="modal-title" ></h4>
<h4 class="modal-title" >${I18n.jobgroup_edit}</h4>
</div>
<div class="modal-body">
<form class="form-horizontal form" role="form" >
<div class="form-group">
<label for="lastname" class="col-sm-2 control-label">AppName<font color="red">*</font></label>
<div class="col-sm-10"><input type="text" class="form-control" name="appName" placeholder="请输入“AppName”" maxlength="64" ></div>
<div class="col-sm-10"><input type="text" class="form-control" name="appName" placeholder="${I18n.system_please_input}AppName" maxlength="64" ></div>
</div>
<div class="form-group">
<label for="lastname" class="col-sm-2 control-label"><font color="red">*</font></label>
<div class="col-sm-10"><input type="text" class="form-control" name="title" placeholder="请输入“名称”" maxlength="12" ></div>
<label for="lastname" class="col-sm-2 control-label">${I18n.jobgroup_field_title}<font color="red">*</font></label>
<div class="col-sm-10"><input type="text" class="form-control" name="title" placeholder="${I18n.system_please_input}${I18n.jobgroup_field_title}" maxlength="12" ></div>
</div>
<div class="form-group">
<label for="lastname" class="col-sm-2 control-label"><font color="red">*</font></label>
<div class="col-sm-10"><input type="text" class="form-control" name="order" placeholder="请输入“排序”" maxlength="50" ></div>
<label for="lastname" class="col-sm-2 control-label">${I18n.jobgroup_field_order}<font color="red">*</font></label>
<div class="col-sm-10"><input type="text" class="form-control" name="order" placeholder="${I18n.system_please_input}${I18n.jobgroup_field_order}" maxlength="50" ></div>
</div>
<div class="form-group">
<label for="lastname" class="col-sm-2 control-label"><font color="red">*</font></label>
<label for="lastname" class="col-sm-2 control-label">${I18n.jobgroup_field_addressType}<font color="red">*</font></label>
<div class="col-sm-10">
<input type="radio" name="addressType" value="0" />
<input type="radio" name="addressType" value="0" />${I18n.jobgroup_field_addressType_0}
&nbsp;&nbsp;&nbsp;&nbsp;
<input type="radio" name="addressType" value="1" />
<input type="radio" name="addressType" value="1" />${I18n.jobgroup_field_addressType_1}
</div>
</div>
<div class="form-group">
<label for="lastname" class="col-sm-2 control-label"><font color="red">*</font></label>
<label for="lastname" class="col-sm-2 control-label">${I18n.jobgroup_field_registryList}<font color="red">*</font></label>
<div class="col-sm-10">
<textarea class="textarea" name="addressList" maxlength="512" placeholder="请输入执行器地址列表,多地址逗号分隔" readonly="readonly" style="background-color:#eee; width: 100%; height: 100px; font-size: 14px; line-height: 10px; border: 1px solid #dddddd; padding: 10px;"></textarea>
<textarea class="textarea" name="addressList" maxlength="512" placeholder="${I18n.jobgroup_field_registryList_placeholder}" readonly="readonly" style="background-color:#eee; width: 100%; height: 100px; font-size: 14px; line-height: 10px; border: 1px solid #dddddd; padding: 10px;"></textarea>
</div>
</div>
<hr>
<div class="form-group">
<div class="col-sm-offset-3 col-sm-6">
<button type="submit" class="btn btn-primary" ></button>
<button type="button" class="btn btn-default" data-dismiss="modal"></button>
<button type="submit" class="btn btn-primary" >${I18n.system_save}</button>
<button type="button" class="btn btn-default" data-dismiss="modal">${I18n.system_cancel}</button>
<input type="hidden" name="id" >
</div>
</div>

@ -1,12 +1,11 @@
<!DOCTYPE html>
<html>
<head>
<title></title>
<#import "/common/common.macro.ftl" as netCommon>
<@netCommon.commonStyle />
<!-- DataTables -->
<link rel="stylesheet" href="${request.contextPath}/static/adminlte/plugins/datatables/dataTables.bootstrap.css">
<title>${I18n.admin_name}</title>
</head>
<body class="hold-transition skin-blue sidebar-mini <#if cookieMap?exists && "off" == cookieMap["xxljob_adminlte_settings"].value >sidebar-collapse</#if>">
<div class="wrapper">
@ -19,22 +18,16 @@
<div class="content-wrapper">
<!-- Content Header (Page header) -->
<section class="content-header">
<h1><small></small></h1>
<!--
<ol class="breadcrumb">
<li><a><i class="fa fa-dashboard"></i></a></li>
<li class="active"></li>
</ol>
-->
<h1>${I18n.jobinfo_name}</h1>
</section>
<!-- Main content -->
<section class="content">
<div class="row">
<div class="col-xs-4">
<div class="col-xs-3">
<div class="input-group">
<span class="input-group-addon"></span>
<span class="input-group-addon">${I18n.jobinfo_field_jobgroup}</span>
<select class="form-control" id="jobGroup" >
<#list JobGroupList as group>
<option value="${group.id}" <#if jobGroup==group.id>selected</#if> >${group.title}</option>
@ -42,43 +35,48 @@
</select>
</div>
</div>
<div class="col-xs-4">
<div class="col-xs-3">
<div class="input-group">
<span class="input-group-addon">${I18n.jobinfo_field_jobdesc}</span>
<input type="text" class="form-control" id="jobDesc" autocomplete="on" >
</div>
</div>
<div class="col-xs-3">
<div class="input-group">
<span class="input-group-addon">JobHandler</span>
<input type="text" class="form-control" id="executorHandler" autocomplete="on" >
</div>
</div>
<div class="col-xs-2">
<button class="btn btn-block btn-info" id="searchBtn"></button>
<div class="col-xs-1">
<button class="btn btn-block btn-info" id="searchBtn">${I18n.system_search}</button>
</div>
<div class="col-xs-2">
<button class="btn btn-block btn-success add" type="button">+</button>
<button class="btn btn-block btn-success add" type="button">${I18n.jobinfo_field_add}</button>
</div>
</div>
<div class="row">
<div class="col-xs-12">
<div class="box">
<div class="box-header hide">
<#--<div class="box-header hide">
<h3 class="box-title"></h3>
</div>
</div>-->
<div class="box-body" >
<table id="job_list" class="table table-bordered table-striped" width="100%" >
<thead>
<tr>
<th name="id" >ID</th>
<th name="jobGroup" >jobGroup</th>
<th name="jobDesc" ></th>
<th name="glueType" ></th>
<th name="executorParam" ></th>
<th name="id" >${I18n.jobinfo_field_id}</th>
<th name="jobGroup" >${I18n.jobinfo_field_jobgroup}</th>
<th name="jobDesc" >${I18n.jobinfo_field_jobdesc}</th>
<th name="glueType" >${I18n.jobinfo_field_gluetype}</th>
<th name="executorParam" >${I18n.jobinfo_field_executorparam}</th>
<th name="jobCron" >Cron</th>
<th name="addTime" ></th>
<th name="updateTime" ></th>
<th name="author" ></th>
<th name="alarmEmail" ></th>
<th name="glueType" ></th>
<th name="jobStatus" ></th>
<th></th>
<th name="addTime" >addTime</th>
<th name="updateTime" >updateTime</th>
<th name="author" >${I18n.jobinfo_field_author}</th>
<th name="alarmEmail" >${I18n.jobinfo_field_alarmemail}</th>
<th name="jobStatus" >${I18n.system_status}</th>
<th>${I18n.system_opt}</th>
</tr>
</thead>
<tbody></tbody>
@ -100,12 +98,12 @@
<div class="modal-dialog modal-lg">
<div class="modal-content">
<div class="modal-header">
<h4 class="modal-title" ></h4>
<h4 class="modal-title" >${I18n.jobinfo_field_add}</h4>
</div>
<div class="modal-body">
<form class="form-horizontal form" role="form" >
<div class="form-group">
<label for="firstname" class="col-sm-2 control-label"><font color="red">*</font></label>
<label for="firstname" class="col-sm-2 control-label">${I18n.jobinfo_field_jobgroup}<font color="red">*</font></label>
<div class="col-sm-4">
<select class="form-control" name="jobGroup" >
<#list JobGroupList as group>
@ -113,11 +111,11 @@
</#list>
</select>
</div>
<label for="lastname" class="col-sm-2 control-label"><font color="red">*</font></label>
<div class="col-sm-4"><input type="text" class="form-control" name="jobDesc" placeholder="请输入“描述”" maxlength="50" ></div>
<label for="lastname" class="col-sm-2 control-label">${I18n.jobinfo_field_jobdesc}<font color="red">*</font></label>
<div class="col-sm-4"><input type="text" class="form-control" name="jobDesc" placeholder="${I18n.system_please_input}${I18n.jobinfo_field_jobdesc}" maxlength="50" ></div>
</div>
<div class="form-group">
<label for="firstname" class="col-sm-2 control-label"><font color="red">*</font></label>
<label for="firstname" class="col-sm-2 control-label">${I18n.jobinfo_field_executorRouteStrategy}<font color="red">*</font></label>
<div class="col-sm-4">
<select class="form-control" name="executorRouteStrategy" >
<#list ExecutorRouteStrategyEnum as item>
@ -126,10 +124,10 @@
</select>
</div>
<label for="lastname" class="col-sm-2 control-label">Cron<font color="red">*</font></label>
<div class="col-sm-4"><input type="text" class="form-control" name="jobCron" placeholder="请输入“Cron”" maxlength="128" ></div>
<div class="col-sm-4"><input type="text" class="form-control" name="jobCron" placeholder="${I18n.system_please_input}Cron" maxlength="128" ></div>
</div>
<div class="form-group">
<label for="firstname" class="col-sm-2 control-label"><font color="red">*</font></label>
<label for="firstname" class="col-sm-2 control-label">${I18n.jobinfo_field_gluetype}<font color="red">*</font></label>
<div class="col-sm-4">
<select class="form-control glueType" name="glueType" >
<#list GlueTypeEnum as item>
@ -138,16 +136,16 @@
</select>
</div>
<label for="firstname" class="col-sm-2 control-label">JobHandler<font color="black">*</font></label>
<div class="col-sm-4"><input type="text" class="form-control" name="executorHandler" placeholder="请输入“JobHandler”" maxlength="100" ></div>
<div class="col-sm-4"><input type="text" class="form-control" name="executorHandler" placeholder="${I18n.system_please_input}JobHandler" maxlength="100" ></div>
</div>
<div class="form-group">
<label for="firstname" class="col-sm-2 control-label"><font color="black">*</font></label>
<div class="col-sm-4"><input type="text" class="form-control" name="executorParam" placeholder="请输入“执行参数”" maxlength="512" ></div>
<label for="lastname" class="col-sm-2 control-label">ID<font color="black">*</font></label>
<div class="col-sm-4"><input type="text" class="form-control" name="childJobId" placeholder="请输入子任务的任务ID,如存在多个逗号分隔" maxlength="100" ></div>
<label for="firstname" class="col-sm-2 control-label">${I18n.jobinfo_field_executorparam}<font color="black">*</font></label>
<div class="col-sm-4"><input type="text" class="form-control" name="executorParam" placeholder="${I18n.system_please_input}${I18n.jobinfo_field_executorparam}" maxlength="512" ></div>
<label for="lastname" class="col-sm-2 control-label">${I18n.jobinfo_field_childJobId}<font color="black">*</font></label>
<div class="col-sm-4"><input type="text" class="form-control" name="childJobId" placeholder="${I18n.jobinfo_field_childJobId_placeholder}" maxlength="100" ></div>
</div>
<div class="form-group">
<label for="firstname" class="col-sm-2 control-label"><font color="red">*</font></label>
<label for="firstname" class="col-sm-2 control-label">${I18n.jobinfo_field_executorBlockStrategy}<font color="red">*</font></label>
<div class="col-sm-4">
<select class="form-control" name="executorBlockStrategy" >
<#list ExecutorBlockStrategyEnum as item>
@ -155,7 +153,7 @@
</#list>
</select>
</div>
<label for="lastname" class="col-sm-2 control-label"><font color="red">*</font></label>
<label for="lastname" class="col-sm-2 control-label">${I18n.jobinfo_field_executorFailStrategy}<font color="red">*</font></label>
<div class="col-sm-4">
<select class="form-control" name="executorFailStrategy" >
<#list ExecutorFailStrategyEnum as item>
@ -165,17 +163,17 @@
</div>
</div>
<div class="form-group">
<label for="lastname" class="col-sm-2 control-label"><font color="red">*</font></label>
<div class="col-sm-4"><input type="text" class="form-control" name="author" placeholder="请输入“负责人”" maxlength="50" ></div>
<label for="lastname" class="col-sm-2 control-label"><font color="black">*</font></label>
<div class="col-sm-4"><input type="text" class="form-control" name="alarmEmail" placeholder="请输入“报警邮件”,多个邮件地址逗号分隔" maxlength="100" ></div>
<label for="lastname" class="col-sm-2 control-label">${I18n.jobinfo_field_author}<font color="red">*</font></label>
<div class="col-sm-4"><input type="text" class="form-control" name="author" placeholder="${I18n.system_please_input}${I18n.jobinfo_field_author}" maxlength="50" ></div>
<label for="lastname" class="col-sm-2 control-label">${I18n.jobinfo_field_alarmemail}<font color="black">*</font></label>
<div class="col-sm-4"><input type="text" class="form-control" name="alarmEmail" placeholder="${I18n.jobinfo_field_alarmemail_placeholder}" maxlength="100" ></div>
</div>
<hr>
<div class="form-group">
<div class="col-sm-offset-3 col-sm-6">
<button type="submit" class="btn btn-primary" ></button>
<button type="button" class="btn btn-default" data-dismiss="modal"></button>
<button type="submit" class="btn btn-primary" >${I18n.system_save}</button>
<button type="button" class="btn btn-default" data-dismiss="modal">${I18n.system_cancel}</button>
</div>
</div>
@ -202,10 +200,10 @@ public class DemoGlueJobHandler extends IJobHandler {
#!/bin/bash
echo "xxl-job: hello shell"
echo "脚本位置$0"
echo "任务参数$1"
echo "分片序号 = $2"
echo "分片总数 = $3"
echo "${I18n.jobinfo_script_location}$0"
echo "${I18n.jobinfo_field_executorparam}$1"
echo "${I18n.jobinfo_shard_index} = $2"
echo "${I18n.jobinfo_shard_total} = $3"
<#--echo "参数数量:$#"
for param in $*
do
@ -224,10 +222,10 @@ import sys
print "xxl-job: hello python"
print "脚本文件", sys.argv[0]
print "任务参数", sys.argv[1]
print "分片序号", sys.argv[2]
print "分片总数", sys.argv[3]
print "${I18n.jobinfo_script_location}", sys.argv[0]
print "${I18n.jobinfo_field_executorparam}", sys.argv[1]
print "${I18n.jobinfo_shard_index}", sys.argv[2]
print "${I18n.jobinfo_shard_total}", sys.argv[3]
<#--for i in range(1, len(sys.argv)):
time.sleep(1)
print "参数", i, sys.argv[i]-->
@ -246,10 +244,10 @@ console.log("xxl-job: hello nodejs")
var arguments = process.argv
console.log("脚本文件: " + arguments[1])
console.log("任务参数: " + arguments[2])
console.log("分片序号: " + arguments[3])
console.log("分片总数: " + arguments[4])
console.log("${I18n.jobinfo_script_location}: " + arguments[1])
console.log("${I18n.jobinfo_field_executorparam}: " + arguments[2])
console.log("${I18n.jobinfo_shard_index}: " + arguments[3])
console.log("${I18n.jobinfo_shard_total}: " + arguments[4])
<#--for (var i = 2; i < arguments.length; i++){
console.log("参数 %s = %s", (i-1), arguments[i]);
}-->
@ -268,12 +266,12 @@ process.exit(0)
<div class="modal-dialog modal-lg">
<div class="modal-content">
<div class="modal-header">
<h4 class="modal-title" ></h4>
<h4 class="modal-title" >${I18n.jobinfo_field_update}</h4>
</div>
<div class="modal-body">
<form class="form-horizontal form" role="form" >
<div class="form-group">
<label for="firstname" class="col-sm-2 control-label"><font color="red">*</font></label>
<label for="firstname" class="col-sm-2 control-label">${I18n.jobinfo_field_jobgroup}<font color="red">*</font></label>
<div class="col-sm-4">
<select class="form-control" name="jobGroup" disabled >
<#list JobGroupList as group>
@ -281,11 +279,11 @@ process.exit(0)
</#list>
</select>
</div>
<label for="lastname" class="col-sm-2 control-label"><font color="red">*</font></label>
<div class="col-sm-4"><input type="text" class="form-control" name="jobDesc" placeholder="请输入“描述”" maxlength="50" ></div>
<label for="lastname" class="col-sm-2 control-label">${I18n.jobinfo_field_jobdesc}<font color="red">*</font></label>
<div class="col-sm-4"><input type="text" class="form-control" name="jobDesc" placeholder="${I18n.system_please_input}${I18n.jobinfo_field_jobdesc}" maxlength="50" ></div>
</div>
<div class="form-group">
<label for="firstname" class="col-sm-2 control-label"><font color="red">*</font></label>
<label for="firstname" class="col-sm-2 control-label">${I18n.jobinfo_field_executorRouteStrategy}<font color="red">*</font></label>
<div class="col-sm-4">
<select class="form-control" name="executorRouteStrategy" >
<#list ExecutorRouteStrategyEnum as item>
@ -294,10 +292,10 @@ process.exit(0)
</select>
</div>
<label for="lastname" class="col-sm-2 control-label">Cron<font color="red">*</font></label>
<div class="col-sm-4"><input type="text" class="form-control" name="jobCron" placeholder="请输入“Cron”" maxlength="128" ></div>
<div class="col-sm-4"><input type="text" class="form-control" name="jobCron" placeholder="${I18n.system_please_input}Cron" maxlength="128" ></div>
</div>
<div class="form-group">
<label for="firstname" class="col-sm-2 control-label"><font color="red">*</font></label>
<label for="firstname" class="col-sm-2 control-label">${I18n.jobinfo_field_gluetype}<font color="red">*</font></label>
<div class="col-sm-4">
<select class="form-control glueType" name="glueType" disabled >
<#list GlueTypeEnum as item>
@ -306,16 +304,16 @@ process.exit(0)
</select>
</div>
<label for="firstname" class="col-sm-2 control-label">JobHandler<font color="black">*</font></label>
<div class="col-sm-4"><input type="text" class="form-control" name="executorHandler" placeholder="请输入“JobHandler”" maxlength="100" ></div>
<div class="col-sm-4"><input type="text" class="form-control" name="executorHandler" placeholder="${I18n.system_please_input}JobHandler" maxlength="100" ></div>
</div>
<div class="form-group">
<label for="firstname" class="col-sm-2 control-label"><font color="black">*</font></label>
<div class="col-sm-4"><input type="text" class="form-control" name="executorParam" placeholder="请输入“执行参数”" maxlength="512" ></div>
<label for="lastname" class="col-sm-2 control-label">ID<font color="black">*</font></label>
<div class="col-sm-4"><input type="text" class="form-control" name="childJobId" placeholder="请输入子任务的任务ID,如存在多个逗号分隔" maxlength="100" ></div>
<label for="firstname" class="col-sm-2 control-label">${I18n.jobinfo_field_executorparam}<font color="black">*</font></label>
<div class="col-sm-4"><input type="text" class="form-control" name="executorParam" placeholder="${I18n.system_please_input}${I18n.jobinfo_field_executorparam}" maxlength="512" ></div>
<label for="lastname" class="col-sm-2 control-label">${I18n.jobinfo_field_childJobId}<font color="black">*</font></label>
<div class="col-sm-4"><input type="text" class="form-control" name="childJobId" placeholder="${I18n.jobinfo_field_childJobId_placeholder}" maxlength="100" ></div>
</div>
<div class="form-group">
<label for="firstname" class="col-sm-2 control-label"><font color="red">*</font></label>
<label for="firstname" class="col-sm-2 control-label">${I18n.jobinfo_field_executorBlockStrategy}<font color="red">*</font></label>
<div class="col-sm-4">
<select class="form-control" name="executorBlockStrategy" >
<#list ExecutorBlockStrategyEnum as item>
@ -323,7 +321,7 @@ process.exit(0)
</#list>
</select>
</div>
<label for="lastname" class="col-sm-2 control-label"><font color="red">*</font></label>
<label for="lastname" class="col-sm-2 control-label">${I18n.jobinfo_field_executorFailStrategy}<font color="red">*</font></label>
<div class="col-sm-4">
<select class="form-control" name="executorFailStrategy" >
<#list ExecutorFailStrategyEnum as item>
@ -333,20 +331,21 @@ process.exit(0)
</div>
</div>
<div class="form-group">
<label for="lastname" class="col-sm-2 control-label"><font color="red">*</font></label>
<div class="col-sm-4"><input type="text" class="form-control" name="author" placeholder="请输入“负责人”" maxlength="50" ></div>
<label for="lastname" class="col-sm-2 control-label"><font color="black">*</font></label>
<div class="col-sm-4"><input type="text" class="form-control" name="alarmEmail" placeholder="请输入“报警邮件”,多个邮件地址逗号分隔" maxlength="100" ></div>
<label for="lastname" class="col-sm-2 control-label">${I18n.jobinfo_field_author}<font color="red">*</font></label>
<div class="col-sm-4"><input type="text" class="form-control" name="author" placeholder="${I18n.system_please_input}${I18n.jobinfo_field_author}" maxlength="50" ></div>
<label for="lastname" class="col-sm-2 control-label">${I18n.jobinfo_field_alarmemail}<font color="black">*</font></label>
<div class="col-sm-4"><input type="text" class="form-control" name="alarmEmail" placeholder="${I18n.jobinfo_field_alarmemail_placeholder}" maxlength="100" ></div>
</div>
<hr>
<div class="form-group">
<div class="col-sm-offset-3 col-sm-6">
<button type="submit" class="btn btn-primary" ></button>
<button type="button" class="btn btn-default" data-dismiss="modal"></button>
<button type="submit" class="btn btn-primary" >${I18n.system_save}</button>
<button type="button" class="btn btn-default" data-dismiss="modal">${I18n.system_cancel}</button>
<input type="hidden" name="id" >
</div>
</div>
</form>
</div>
</div>

@ -1,9 +1,9 @@
<!DOCTYPE html>
<html>
<head>
<title></title>
<#import "/common/common.macro.ftl" as netCommon>
<@netCommon.commonStyle />
<#import "/common/common.macro.ftl" as netCommon>
<@netCommon.commonStyle />
<title>${I18n.admin_name}</title>
</head>
<body class="hold-transition skin-blue layout-top-nav">
@ -14,7 +14,7 @@
<div class="container">
<#-- icon -->
<div class="navbar-header">
<a class="navbar-brand"><b></b>Console</a>
<a class="navbar-brand"><b>${I18n.joblog_rolling_log}</b> Console</a>
<button type="button" class="navbar-toggle collapsed" data-toggle="collapse" data-target="#navbar-collapse">
<i class="fa fa-bars"></i>
</button>
@ -33,7 +33,7 @@
<li>
<a href="javascript:window.location.reload();" >
<i class="fa fa-fw fa-refresh" ></i>
${I18n.joblog_rolling_log_refresh}
</a>
</li>
</ul>

@ -1,13 +1,13 @@
<!DOCTYPE html>
<html>
<head>
<title></title>
<#import "/common/common.macro.ftl" as netCommon>
<@netCommon.commonStyle />
<!-- DataTables -->
<link rel="stylesheet" href="${request.contextPath}/static/adminlte/plugins/datatables/dataTables.bootstrap.css">
<!-- daterangepicker -->
<link rel="stylesheet" href="${request.contextPath}/static/adminlte/plugins/daterangepicker/daterangepicker.css">
<title>${I18n.admin_name}</title>
</head>
<body class="hold-transition skin-blue sidebar-mini <#if cookieMap?exists && "off" == cookieMap["xxljob_adminlte_settings"].value >sidebar-collapse</#if> ">
<div class="wrapper">
@ -20,13 +20,7 @@
<div class="content-wrapper">
<!-- Content Header (Page header) -->
<section class="content-header">
<h1><small></small></h1>
<!--
<ol class="breadcrumb">
<li><a><i class="fa fa-dashboard"></i></a></li>
<li class="active"></li>
</ol>
-->
<h1>${I18n.joblog_name}</h1>
</section>
<!-- Main content -->
@ -34,9 +28,9 @@
<div class="row">
<div class="col-xs-2">
<div class="input-group">
<span class="input-group-addon"></span>
<span class="input-group-addon">${I18n.jobinfo_field_jobgroup}</span>
<select class="form-control" id="jobGroup" paramVal="<#if jobInfo?exists>${jobInfo.jobGroup}</#if>" >
<option value="0" ></option>
<option value="0" >${I18n.system_all}</option>
<#list JobGroupList as group>
<option value="${group.id}" >${group.title}</option>
</#list>
@ -45,21 +39,21 @@
</div>
<div class="col-xs-2">
<div class="input-group">
<span class="input-group-addon"></span>
<span class="input-group-addon">${I18n.jobinfo_job}</span>
<select class="form-control" id="jobId" paramVal="<#if jobInfo?exists>${jobInfo.id}</#if>" >
<option value="0" ></option>
<option value="0" >${I18n.system_all}</option>
</select>
</div>
</div>
<div class="col-xs-2">
<div class="input-group">
<span class="input-group-addon"></span>
<span class="input-group-addon">${I18n.joblog_status}</span>
<select class="form-control" id="logStatus" >
<option value="-1" ></option>
<option value="1" ></option>
<option value="2" ></option>
<option value="3" ></option>
<option value="-1" >${I18n.joblog_status_all}</option>
<option value="1" >${I18n.joblog_status_suc}</option>
<option value="2" >${I18n.joblog_status_fail}</option>
<option value="3" >${I18n.joblog_status_running}</option>
</select>
</div>
</div>
@ -67,41 +61,41 @@
<div class="col-xs-4">
<div class="input-group">
<span class="input-group-addon">
${I18n.joblog_field_triggerTime}
</span>
<input type="text" class="form-control" id="filterTime" readonly >
</div>
</div>
<div class="col-xs-1">
<button class="btn btn-block btn-info" id="searchBtn"></button>
<button class="btn btn-block btn-info" id="searchBtn">${I18n.system_search}</button>
</div>
<div class="col-xs-1">
<button class="btn btn-block btn-nomal" id="clearLog"></button>
<button class="btn btn-block btn-nomal" id="clearLog">${I18n.joblog_clean}</button>
</div>
</div>
<div class="row">
<div class="col-xs-12">
<div class="box">
<div class="box-header hide"><h3 class="box-title"></h3></div>
<#--<div class="box-header hide"><h3 class="box-title"></h3></div>-->
<div class="box-body">
<table id="joblog_list" class="table table-bordered table-striped display" width="100%" >
<thead>
<tr>
<th name="jobId" >ID</th>
<th name="jobGroup" >ID</th>
<th name="jobId" >${I18n.jobinfo_field_id}</th>
<th name="jobGroup" >jobGroup</th>
<#--<th name="executorAddress" ></th>
<th name="glueType" ></th>
<th name="executorParam" ></th>-->
<th name="triggerTime" ></th>
<th name="triggerCode" ></th>
<th name="triggerMsg" ></th>
<th name="handleTime" ></th>
<th name="handleCode" ></th>
<th name="handleMsg" ></th>
<th name="handleMsg" ></th>
<th name="triggerTime" >${I18n.joblog_field_triggerTime}</th>
<th name="triggerCode" >${I18n.joblog_field_triggerCode}</th>
<th name="triggerMsg" >${I18n.joblog_field_triggerMsg}</th>
<th name="handleTime" >${I18n.joblog_field_handleTime}</th>
<th name="handleCode" >${I18n.joblog_field_handleCode}</th>
<th name="handleMsg" >${I18n.joblog_field_handleMsg}</th>
<th name="handleMsg" >${I18n.system_opt}</th>
</tr>
</thead>
<tbody></tbody>
@ -122,12 +116,12 @@
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-header">
<h4 class="modal-title" ></h4>
<h4 class="modal-title" >${I18n.joblog_clean_log}</h4>
</div>
<div class="modal-body">
<form class="form-horizontal form" role="form" >
<div class="form-group">
<label class="col-sm-3 control-label""></label>
<label class="col-sm-3 control-label"">${I18n.jobinfo_field_jobgroup}</label>
<div class="col-sm-9">
<input type="text" class="form-control jobGroupText" readonly >
<input type="hidden" name="jobGroup" >
@ -135,7 +129,7 @@
</div>
<div class="form-group">
<label class="col-sm-3 control-label""></label>
<label class="col-sm-3 control-label"">${I18n.jobinfo_job}</label>
<div class="col-sm-9">
<input type="text" class="form-control jobIdText" readonly >
<input type="hidden" name="jobId" >
@ -143,18 +137,18 @@
</div>
<div class="form-group">
<label class="col-sm-3 control-label""></label>
<label class="col-sm-3 control-label"">${I18n.joblog_clean_type}</label>
<div class="col-sm-9">
<select class="form-control" name="type" >
<option value="1" ></option>
<option value="2" ></option>
<option value="3" ></option>
<option value="4" ></option>
<option value="5" ></option>
<option value="6" ></option>
<option value="7" ></option>
<option value="8" ></option>
<option value="9" ></option>
<option value="1" >${I18n.joblog_clean_type_1}</option>
<option value="2" >${I18n.joblog_clean_type_2}</option>
<option value="3" >${I18n.joblog_clean_type_3}</option>
<option value="4" >${I18n.joblog_clean_type_4}</option>
<option value="5" >${I18n.joblog_clean_type_5}</option>
<option value="6" >${I18n.joblog_clean_type_6}</option>
<option value="7" >${I18n.joblog_clean_type_7}</option>
<option value="8" >${I18n.joblog_clean_type_8}</option>
<option value="9" >${I18n.joblog_clean_type_9}</option>
</select>
</div>
</div>
@ -162,8 +156,8 @@
<hr>
<div class="form-group">
<div class="col-sm-offset-3 col-sm-6">
<button type="button" class="btn btn-primary ok" ></button>
<button type="button" class="btn btn-default" data-dismiss="modal"></button>
<button type="button" class="btn btn-primary ok" >${I18n.system_ok}</button>
<button type="button" class="btn btn-default" data-dismiss="modal">${I18n.system_cancel}</button>
</div>
</div>
</form>
@ -173,6 +167,12 @@
</div>
<@netCommon.commonScript />
<script>
var GlueTypeEnum = {};
<#list GlueTypeEnum as item>
GlueTypeEnum['${item}'] = '${item.desc}';
</#list>
</script>
<!-- DataTables -->
<script src="${request.contextPath}/static/adminlte/plugins/datatables/jquery.dataTables.min.js"></script>
<script src="${request.contextPath}/static/adminlte/plugins/datatables/dataTables.bootstrap.min.js"></script>

@ -1,10 +1,10 @@
<!DOCTYPE html>
<html>
<head>
<title></title>
<#import "/common/common.macro.ftl" as netCommon>
<@netCommon.commonStyle />
<link rel="stylesheet" href="${request.contextPath}/static/adminlte/plugins/iCheck/square/blue.css">
<title>${I18n.admin_name}</title>
</head>
<body class="hold-transition login-page">
<div class="login-box">
@ -13,25 +13,25 @@
</div>
<form id="loginForm" method="post" >
<div class="login-box-body">
<p class="login-box-msg"></p>
<p class="login-box-msg">${I18n.admin_name}</p>
<div class="form-group has-feedback">
<input type="text" name="userName" class="form-control" placeholder="请输入登录账号" value="admin" >
<input type="text" name="userName" class="form-control" placeholder="${I18n.login_username_placeholder}" value="admin" maxlength="18" >
<span class="glyphicon glyphicon-envelope form-control-feedback"></span>
</div>
<div class="form-group has-feedback">
<input type="password" name="password" class="form-control" placeholder="请输入登录密码" value="123456" >
<input type="password" name="password" class="form-control" placeholder="${I18n.login_password_placeholder}" value="123456" maxlength="18" >
<span class="glyphicon glyphicon-lock form-control-feedback"></span>
</div>
<div class="row">
<div class="col-xs-8">
<div class="checkbox icheck">
<label>
<input type="checkbox" name="ifRemember" > Remember Me
<input type="checkbox" name="ifRemember" >${I18n.login_remember_me}
</label>
</div>
</div><!-- /.col -->
<div class="col-xs-4">
<button type="submit" class="btn btn-primary btn-block btn-flat"></button>
<button type="submit" class="btn btn-primary btn-block btn-flat">${I18n.login_btn}</button>
</div>
</div>
</div>

@ -2,27 +2,24 @@ $(function(){
// logout
$("#logoutBtn").click(function(){
layer.confirm('?', {icon: 3, title:''}, function(index){
layer.confirm( I18n.logout_confirm , {
icon: 3,
title: I18n.system_tips ,
btn: [ I18n.system_ok, I18n.system_cancel ]
}, function(index){
layer.close(index);
$.post(base_url + "/logout", function(data, status) {
if (data.code == "200") {
layer.msg('');
layer.msg( I18n.logout_success );
setTimeout(function(){
window.location.href = base_url + "/";
}, 500);
/*layer.open({
title: '',
content: '',
icon: '1',
end: function(layero, index){
window.location.href = base_url + "/";
}
});*/
} else {
layer.open({
title: '',
content: (data.msg || "操作失败"),
title: I18n.system_tips ,
btn: [ I18n.system_ok ],
content: (data.msg || I18n.logout_fail),
icon: '2'
});
}
@ -68,14 +65,14 @@ $(function(){
}
});
$(slideToTop).click(function () {
$("body").animate({
$("html,body").animate({ // firefox ie not support body, chrome support body. but found that new version chrome not support body too.
scrollTop: 0
}, 100);
});
// 左侧菜单状态js + 后端 + cookie方式
// left menu status v: js + server + cookie
$('.sidebar-toggle').click(function(){
var xxljob_adminlte_settings = $.cookie('xxljob_adminlte_settings'); // 左侧菜单展开状态[xxljob_adminlte_settings]on=展开off=折叠
var xxljob_adminlte_settings = $.cookie('xxljob_adminlte_settings'); // on=openoff=close
if ('off' == xxljob_adminlte_settings) {
xxljob_adminlte_settings = 'on';
} else {
@ -83,7 +80,8 @@ $(function(){
}
$.cookie('xxljob_adminlte_settings', xxljob_adminlte_settings, { expires: 7 }); //$.cookie('the_cookie', '', { expires: -1 });
});
// 左侧菜单状态js + cookie方式遗弃
// left menu status v1: js + cookie
/*
var xxljob_adminlte_settings = $.cookie('xxljob_adminlte_settings');
if (xxljob_adminlte_settings == 'off') {

@ -1,13 +1,17 @@
/**
* Created by xuxueli on 17/4/24.
*/
$(function () {
// 过滤时间
var _startDate = moment().subtract(1, 'months'); // 默认,最近一月
var _endDate = moment();
// filter Time
var rangesConf = {};
rangesConf[I18n.daterangepicker_ranges_today] = [moment().startOf('day'), moment().endOf('day')];
rangesConf[I18n.daterangepicker_ranges_yesterday] = [moment().subtract(1, 'days').startOf('day'), moment().subtract(1, 'days').endOf('day')];
rangesConf[I18n.daterangepicker_ranges_this_month] = [moment().startOf('month'), moment().endOf('month')];
rangesConf[I18n.daterangepicker_ranges_last_month] = [moment().subtract(1, 'months').startOf('month'), moment().subtract(1, 'months').endOf('month')];
rangesConf[I18n.daterangepicker_ranges_recent_week] = [moment().subtract(1, 'weeks').startOf('day'), moment().endOf('day')];
rangesConf[I18n.daterangepicker_ranges_recent_month] = [moment().subtract(1, 'months').startOf('day'), moment().endOf('day')];
$('#filterTime').daterangepicker({
autoApply:false,
singleDatePicker:false,
@ -16,36 +20,28 @@ $(function () {
timePickerIncrement: 10, // 时间的增量,单位为分钟
timePicker24Hour : true,
opens : 'left', //日期选择框的弹出位置
ranges: {
//'最近1小时': [moment().subtract(1, 'hours'), moment()],
'': [moment().startOf('day'), moment().endOf('day')],
'': [moment().subtract(1, 'days').startOf('day'), moment().subtract(1, 'days').endOf('day')],
'': [moment().startOf('month'), moment().endOf('month')],
'': [moment().subtract(1, 'months').startOf('month'), moment().subtract(1, 'months').endOf('month')],
'1': [moment().subtract(1, 'weeks'), moment()],
'1': [_startDate, _endDate]
},
ranges: rangesConf,
locale : {
format: 'YYYY-MM-DD HH:mm:ss',
separator : ' - ',
customRangeLabel : '',
applyLabel : '',
cancelLabel : '',
fromLabel : '',
toLabel : '',
daysOfWeek : [ '日', '一', '二', '三', '四', '五', '六' ],
monthNames : [ '', '', '', '', '', '', '', '', '', '', '', '' ],
customRangeLabel : I18n.daterangepicker_custom_name ,
applyLabel : I18n.system_ok ,
cancelLabel : I18n.system_cancel ,
fromLabel : I18n.daterangepicker_custom_starttime ,
toLabel : I18n.daterangepicker_custom_endtime ,
daysOfWeek : I18n.daterangepicker_custom_daysofweek.split(',') , // '日', '一', '二', '三', '四', '五', '六'
monthNames : I18n.daterangepicker_custom_monthnames.split(',') , // '一月', '二月', '三月', '四月', '五月', '六月', '七月', '八月', '九月', '十月', '十一月', '十二月'
firstDay : 1
},
startDate:_startDate,
endDate: _endDate
startDate: rangesConf[I18n.daterangepicker_ranges_recent_month][0] ,
endDate: rangesConf[I18n.daterangepicker_ranges_recent_month][1]
}, function (start, end, label) {
freshChartDate(start, end);
});
freshChartDate(_startDate, _endDate);
freshChartDate(rangesConf[I18n.daterangepicker_ranges_recent_month][0], rangesConf[I18n.daterangepicker_ranges_recent_month][1]);
/**
*
* fresh Chart Date
*
* @param startDate
* @param endDate
@ -53,7 +49,7 @@ $(function () {
function freshChartDate(startDate, endDate) {
$.ajax({
type : 'POST',
url : base_url + '/triggerChartDate',
url : base_url + '/chartInfo',
data : {
'startDate':startDate.format('YYYY-MM-DD HH:mm:ss'),
'endDate':endDate.format('YYYY-MM-DD HH:mm:ss')
@ -65,8 +61,9 @@ $(function () {
pieChartInit(data);
} else {
layer.open({
title: '',
content: (data.msg || ''),
title: I18n.system_tips ,
btn: [ I18n.system_ok ],
content: (data.msg || I18n.job_dashboard_report_loaddata_fail ),
icon: '2'
});
}
@ -75,12 +72,12 @@ $(function () {
}
/**
* 线
* line Chart Init
*/
function lineChartInit(data) {
var option = {
title: {
text: ''
text: I18n.job_dashboard_date_report
},
tooltip : {
trigger: 'axis',
@ -92,7 +89,7 @@ $(function () {
}
},
legend: {
data:['','']
data:[I18n.joblog_status_suc, I18n.joblog_status_fail, I18n.joblog_status_running]
},
toolbox: {
feature: {
@ -119,16 +116,16 @@ $(function () {
],
series : [
{
name:'',
name:I18n.joblog_status_suc,
type:'line',
stack: '',
stack: 'Total',
areaStyle: {normal: {}},
data: data.content.triggerDayCountSucList
},
{
name:'',
name:I18n.joblog_status_fail,
type:'line',
stack: '',
stack: 'Total',
label: {
normal: {
show: true,
@ -137,9 +134,16 @@ $(function () {
},
areaStyle: {normal: {}},
data: data.content.triggerDayCountFailList
},
{
name:I18n.joblog_status_running,
type:'line',
stack: 'Total',
areaStyle: {normal: {}},
data: data.content.triggerDayCountRunningList
}
],
color:['#00A65A', '#F39C12']
color:['#00A65A', '#c23632', '#F39C12']
};
var lineChart = echarts.init(document.getElementById('lineChart'));
@ -147,38 +151,42 @@ $(function () {
}
/**
*
* pie Chart Init
*/
function pieChartInit(data) {
var option = {
title : {
text: '',
text: I18n.job_dashboard_rate_report ,
/*subtext: 'subtext',*/
x:'center'
},
tooltip : {
trigger: 'item',
formatter: "{a} <br/>{b} : {c} ({d}%)"
formatter: "{b} : {c} ({d}%)"
},
legend: {
orient: 'vertical',
left: 'left',
data: ['','']
data: [I18n.joblog_status_suc, I18n.joblog_status_fail, I18n.joblog_status_running ]
},
series : [
{
name: '',
//name: '分布比例',
type: 'pie',
radius : '55%',
center: ['50%', '60%'],
data:[
{
value:data.content.triggerCountSucTotal,
name:''
name:I18n.joblog_status_suc,
value:data.content.triggerCountSucTotal
},
{
name:I18n.joblog_status_fail,
value:data.content.triggerCountFailTotal
},
{
value:data.content.triggerCountFailTotal,
name:''
name:I18n.joblog_status_running,
value:data.content.triggerCountRunningTotal
}
],
itemStyle: {
@ -190,7 +198,7 @@ $(function () {
}
}
],
color:['#00A65A', '#F39C12']
color:['#00A65A', '#c23632', '#F39C12']
};
var pieChart = echarts.init(document.getElementById('pieChart'));
pieChart.setOption(option);

@ -1,12 +1,6 @@
$(function() {
// init code editor
/*var codeEditor = CodeMirror.fromTextArea(document.getElementById("glueSource"), {
mode : "text/x-java",
lineNumbers : true,
matchBrackets : true
});*/
var codeEditor;
function initIde(glueSource) {
if (codeEditor == null) {
@ -44,16 +38,18 @@ $(function() {
if (!glueRemark) {
layer.open({
title: '',
content: '',
title: I18n.system_tips,
btn: [ I18n.system_ok],
content: I18n.system_please_input + I18n.jobinfo_glue_remark ,
icon: '2'
});
return;
}
if (glueRemark.length <4 || glueRemark.length > 100) {
layer.open({
title: '',
content: '4100',
title: I18n.system_tips ,
btn: [ I18n.system_ok ],
content: I18n.jobinfo_glue_remark_limit ,
icon: '2'
});
return;
@ -71,8 +67,9 @@ $(function() {
success : function(data){
if (data.code == 200) {
layer.open({
title: '',
content: '',
title: I18n.system_tips,
btn: [ I18n.system_ok ],
content: (I18n.system_save + I18n.system_success) ,
icon: '1',
end: function(layero, index){
//$(window).unbind('beforeunload');
@ -81,8 +78,9 @@ $(function() {
});
} else {
layer.open({
title: '',
content: (data.msg || "保存失败"),
title: I18n.system_tips,
btn: [ I18n.system_ok ],
content: (data.msg || (I18n.system_save + I18n.system_fail) ),
icon: '2'
});
}

@ -4,7 +4,11 @@ $(function() {
$('.remove').on('click', function(){
var id = $(this).attr('id');
layer.confirm('?', {icon: 3, title:''}, function(index){
layer.confirm( (I18n.system_ok + I18n.jobgroup_del + '') , {
icon: 3,
title: I18n.system_tips ,
btn: [ I18n.system_ok, I18n.system_cancel ]
}, function(index){
layer.close(index);
$.ajax({
@ -15,8 +19,9 @@ $(function() {
success : function(data){
if (data.code == 200) {
layer.open({
title: '',
content: '',
title: I18n.system_tips ,
btn: [ I18n.system_ok ],
content: (I18n.jobgroup_del + I18n.system_success),
icon: '1',
end: function(layero, index){
window.location.reload();
@ -24,8 +29,9 @@ $(function() {
});
} else {
layer.open({
title: '',
content: (data.msg || "删除失败"),
title: I18n.system_tips,
btn: [ I18n.system_ok ],
content: (data.msg || (I18n.jobgroup_del + I18n.system_fail)),
icon: '2'
});
}
@ -35,12 +41,12 @@ $(function() {
});
// jquery.validate 自定义校验 “英文字母开头,只含有英文字母、数字和下划线
// jquery.validate “low letters start, limit contants、 letters、numbers and line-through.
jQuery.validator.addMethod("myValid01", function(value, element) {
var length = value.length;
var valid = /^[a-z][a-zA-Z0-9-]*$/;
return this.optional(element) || valid.test(value);
}, "限制以小写字母开头,由小写字母、数字和下划线组成");
}, I18n.jobgroup_field_appName_limit );
$('.add').on('click', function(){
$('#addModal').modal({backdrop: false, keyboard: false}).modal('show');
@ -67,18 +73,18 @@ $(function() {
},
messages : {
appName : {
required :"请输入“AppName",
rangelength:"AppName长度限制为4~64",
myValid01: "限制以小写字母开头,由小写字母、数字和中划线组成"
required : I18n.system_please_input+"AppName",
rangelength: I18n.jobgroup_field_appName_length ,
myValid01: I18n.jobgroup_field_appName_limit
},
title : {
required :"请输入“执行器名称”",
rangelength:"长度限制为4~12"
required : I18n.system_please_input + I18n.jobgroup_field_title ,
rangelength: I18n.jobgroup_field_title_length
},
order : {
required :"请输入“排序”",
digits: "请输入整数",
range: "取值范围为1~1000"
required : I18n.system_please_input + I18n.jobgroup_field_order ,
digits: I18n.jobgroup_field_order_digits ,
range: I18n.jobgroup_field_orderrange
}
},
highlight : function(element) {
@ -96,8 +102,9 @@ $(function() {
if (data.code == "200") {
$('#addModal').modal('hide');
layer.open({
title: '',
content: '',
title: I18n.system_tips ,
btn: [ I18n.system_ok ],
content: I18n.system_add_suc ,
icon: '1',
end: function(layero, index){
window.location.reload();
@ -105,8 +112,9 @@ $(function() {
});
} else {
layer.open({
title: '',
content: (data.msg || "新增失败"),
title: I18n.system_tips,
btn: [ I18n.system_ok ],
content: (data.msg || I18n.system_add_fail ),
icon: '2'
});
}
@ -119,7 +127,7 @@ $(function() {
$("#addModal .form .form-group").removeClass("has-error");
});
// 注册方式,切换
// addressType change
$("#addModal input[name=addressType], #updateModal input[name=addressType]").click(function(){
var addressType = $(this).val();
var $addressList = $(this).parents("form").find("textarea[name=addressList]");
@ -172,18 +180,18 @@ $(function() {
},
messages : {
appName : {
required :"请输入“AppName”",
rangelength:"AppName长度限制为4~64",
myValid01: "限制以小写字母开头,由小写字母、数字和中划线组成"
required : I18n.system_please_input+"AppName",
rangelength: I18n.jobgroup_field_appName_length ,
myValid01: I18n.jobgroup_field_appName_limit
},
title : {
required :"请输入“执行器名称”",
rangelength:"长度限制为4~12"
required : I18n.system_please_input + I18n.jobgroup_field_title ,
rangelength: I18n.jobgroup_field_title_length
},
order : {
required :"请输入“排序”",
digits: "请输入整数",
range: "取值范围为1~1000"
required : I18n.system_please_input + I18n.jobgroup_field_order ,
digits: I18n.jobgroup_field_order_digits ,
range: I18n.jobgroup_field_orderrange
}
},
highlight : function(element) {
@ -202,8 +210,9 @@ $(function() {
$('#addModal').modal('hide');
layer.open({
title: '',
content: '',
title: I18n.system_tips ,
btn: [ I18n.system_ok ],
content: I18n.system_update_suc ,
icon: '1',
end: function(layero, index){
window.location.reload();
@ -211,8 +220,9 @@ $(function() {
});
} else {
layer.open({
title: '',
content: (data.msg || "更新失败"),
title: I18n.system_tips,
btn: [ I18n.system_ok ],
content: (data.msg || I18n.system_update_fail ),
icon: '2'
});
}

@ -1,4 +1,5 @@
$(function() {
// init date tables
var jobTable = $("#job_list").dataTable({
"deferRender": true,
@ -10,6 +11,7 @@ $(function() {
data : function ( d ) {
var obj = {};
obj.jobGroup = $('#jobGroup').val();
obj.jobDesc = $('#jobDesc').val();
obj.executorHandler = $('#executorHandler').val();
obj.start = d.start;
obj.length = d.length;
@ -18,12 +20,18 @@ $(function() {
},
"searching": false,
"ordering": false,
//"scrollX": true, // X轴滚动条取消自适应
//"scrollX": true, // scroll xclose self-adaption
"columns": [
{ "data": 'id', "bSortable": false, "visible" : true},
{
"data": 'id',
"bSortable": false,
"visible" : true,
"width":'10%'
},
{
"data": 'jobGroup',
"visible" : false,
"width":'20%',
"render": function ( data, type, row ) {
var groupMenu = $("#jobGroup").find("option");
for ( var index in $("#jobGroup").find("option")) {
@ -34,28 +42,30 @@ $(function() {
return data;
}
},
{ "data": 'jobDesc', "visible" : true,"width":'20%'},
{
"data": 'jobDesc',
"visible" : true,
"width":'20%'
},
{
"data": 'glueType',
"width":'20%',
"visible" : true,
"render": function ( data, type, row ) {
if ('GLUE_GROOVY'==row.glueType) {
return "GLUE模式(Java)";
} else if ('GLUE_SHELL'==row.glueType) {
return "GLUE模式(Shell)";
} else if ('GLUE_PYTHON'==row.glueType) {
return "GLUE模式(Python)";
}else if ('GLUE_NODEJS'==row.glueType){
return "GLUE模式(Nodejs)";
} else if ('BEAN'==row.glueType) {
return "BEAN模式" + row.executorHandler;
}
return row.executorHandler;
var glueTypeTitle = findGlueTypeTitle(row.glueType);
if (row.executorHandler) {
return glueTypeTitle +"" + row.executorHandler;
} else {
return glueTypeTitle;
}
}
},
{ "data": 'executorParam', "visible" : false},
{ "data": 'jobCron', "visible" : true,"width":'10%'},
{
"data": 'jobCron',
"visible" : true,
"width":'10%'
},
{
"data": 'addTime',
"visible" : false,
@ -72,7 +82,6 @@ $(function() {
},
{ "data": 'author', "visible" : true, "width":'10%'},
{ "data": 'alarmEmail', "visible" : false},
{ "data": 'glueType', "visible" : false},
{
"data": 'jobStatus',
"width":'10%',
@ -81,24 +90,24 @@ $(function() {
if ('NORMAL' == data) {
return '<small class="label label-success" ><i class="fa fa-clock-o"></i>'+ data +'</small>';
} else if ('PAUSED' == data){
return '<small class="label label-default" title="暂停" ><i class="fa fa-clock-o"></i>'+ data +'</small>';
return '<small class="label label-default" ><i class="fa fa-clock-o"></i>'+ data +'</small>';
} else if ('BLOCKED' == data){
return '<small class="label label-default" title="阻塞[串行]" ><i class="fa fa-clock-o"></i>'+ data +'</small>';
return '<small class="label label-default" ><i class="fa fa-clock-o"></i>'+ data +'</small>';
}
return data;
}
},
{
"data": '' ,
"data": I18n.system_opt ,
"width":'15%',
"render": function ( data, type, row ) {
return function(){
// status
var pause_resume = "";
if ('NORMAL' == row.jobStatus) {
pause_resume = '<button class="btn btn-primary btn-xs job_operate" _type="job_pause" type="button"></button> ';
pause_resume = '<button class="btn btn-primary btn-xs job_operate" _type="job_pause" type="button">'+ I18n.jobinfo_opt_pause +'</button> ';
} else if ('PAUSED' == row.jobStatus){
pause_resume = '<button class="btn btn-primary btn-xs job_operate" _type="job_resume" type="button"></button> ';
pause_resume = '<button class="btn btn-primary btn-xs job_operate" _type="job_resume" type="button">'+ I18n.jobinfo_opt_resume +'</button> ';
}
// log url
var logUrl = base_url +'/joblog?jobId='+ row.id;
@ -113,12 +122,12 @@ $(function() {
// html
tableData['key'+row.id] = row;
var html = '<p id="'+ row.id +'" >'+
'<button class="btn btn-primary btn-xs job_operate" _type="job_trigger" type="button"></button> '+
'<button class="btn btn-primary btn-xs job_operate" _type="job_trigger" type="button">'+ I18n.jobinfo_opt_run +'</button> '+
pause_resume +
'<button class="btn btn-primary btn-xs" type="job_del" type="button" onclick="javascript:window.open(\'' + logUrl + '\')" ></button><br> '+
'<button class="btn btn-warning btn-xs update" type="button"></button> '+
'<button class="btn btn-primary btn-xs" type="job_del" type="button" onclick="javascript:window.open(\'' + logUrl + '\')" >'+ I18n.jobinfo_opt_log +'</button><br> '+
'<button class="btn btn-warning btn-xs update" type="button">'+ I18n.system_opt_edit +'</button> '+
codeBtn +
'<button class="btn btn-danger btn-xs job_operate" _type="job_del" type="button"></button> '+
'<button class="btn btn-danger btn-xs job_operate" _type="job_del" type="button">'+ I18n.system_opt_del +'</button> '+
'</p>';
return html;
@ -127,27 +136,27 @@ $(function() {
}
],
"language" : {
"sProcessing" : "处理中...",
"sLengthMenu" : "每页 _MENU_ 条记录",
"sZeroRecords" : "没有匹配结果",
"sInfo" : "第 _PAGE_ 页 ( 总共 _PAGES_ 页_TOTAL_ 条记录 )",
"sInfoEmpty" : "无记录",
"sInfoFiltered" : "(由 _MAX_ 项结果过滤)",
"sProcessing" : I18n.dataTable_sProcessing ,
"sLengthMenu" : I18n.dataTable_sLengthMenu ,
"sZeroRecords" : I18n.dataTable_sZeroRecords ,
"sInfo" : I18n.dataTable_sInfo ,
"sInfoEmpty" : I18n.dataTable_sInfoEmpty ,
"sInfoFiltered" : I18n.dataTable_sInfoFiltered ,
"sInfoPostFix" : "",
"sSearch" : "搜索:",
"sSearch" : I18n.dataTable_sSearch ,
"sUrl" : "",
"sEmptyTable" : "表中数据为空",
"sLoadingRecords" : "载入中...",
"sEmptyTable" : I18n.dataTable_sEmptyTable ,
"sLoadingRecords" : I18n.dataTable_sLoadingRecords ,
"sInfoThousands" : ",",
"oPaginate" : {
"sFirst" : "首页",
"sPrevious" : "上页",
"sNext" : "下页",
"sLast" : "末页"
"sFirst" : I18n.dataTable_sFirst ,
"sPrevious" : I18n.dataTable_sPrevious ,
"sNext" : I18n.dataTable_sNext ,
"sLast" : I18n.dataTable_sLast
},
"oAria" : {
"sSortAscending" : ": 以升序排列此列",
"sSortDescending" : ": 以降序排列此列"
"sSortAscending" : I18n.dataTable_sSortAscending ,
"sSortDescending" : I18n.dataTable_sSortDescending
}
}
});
@ -155,7 +164,7 @@ $(function() {
// table data
var tableData = {};
// 搜索按钮
// search btn
$('#searchBtn').on('click', function(){
jobTable.fnDraw();
});
@ -175,19 +184,19 @@ $(function() {
var type = $(this).attr("_type");
if ("job_pause" == type) {
typeName = "暂停";
typeName = I18n.jobinfo_opt_pause ;
url = base_url + "/jobinfo/pause";
needFresh = true;
} else if ("job_resume" == type) {
typeName = "恢复";
typeName = I18n.jobinfo_opt_resume ;
url = base_url + "/jobinfo/resume";
needFresh = true;
} else if ("job_del" == type) {
typeName = "删除";
typeName = I18n.system_opt_del ;
url = base_url + "/jobinfo/remove";
needFresh = true;
} else if ("job_trigger" == type) {
typeName = "执行";
typeName = I18n.jobinfo_opt_run ;
url = base_url + "/jobinfo/trigger";
} else {
return;
@ -195,7 +204,11 @@ $(function() {
var id = $(this).parent('p').attr("id");
layer.confirm('' + typeName + '?', {icon: 3, title:''}, function(index){
layer.confirm( I18n.system_ok + typeName + '?', {
icon: 3,
title: I18n.system_tips ,
btn: [ I18n.system_ok, I18n.system_cancel ]
}, function(index){
layer.close(index);
$.ajax({
@ -209,8 +222,9 @@ $(function() {
if (data.code == 200) {
layer.open({
title: '',
content: typeName + "成功",
title: I18n.system_tips,
btn: [ I18n.system_ok ],
content: typeName + I18n.system_success ,
icon: '1',
end: function(layero, index){
if (needFresh) {
@ -221,8 +235,9 @@ $(function() {
});
} else {
layer.open({
title: '',
content: (data.msg || typeName + "失败"),
title: I18n.system_tips,
btn: [ I18n.system_ok ],
content: (data.msg || typeName + I18n.system_fail ),
icon: '2'
});
}
@ -231,14 +246,7 @@ $(function() {
});
});
// jquery.validate 自定义校验 “英文字母开头,只含有英文字母、数字和下划线”
jQuery.validator.addMethod("myValid01", function(value, element) {
var length = value.length;
var valid = /^[a-zA-Z][a-zA-Z0-9_]*$/;
return this.optional(element) || valid.test(value);
}, "只支持英文字母开头,只含有英文字母、数字和下划线");
// 新增
// add
$(".add").click(function(){
$('#addModal').modal({backdrop: false, keyboard: false}).modal('show');
});
@ -260,13 +268,13 @@ $(function() {
},
messages : {
jobDesc : {
required :"请输入“描述”."
required : I18n.system_please_input + I18n.jobinfo_field_jobdesc
},
jobCron : {
required :"请输入“Cron”."
required : I18n.system_please_input + "Cron"
},
author : {
required : "请输入“负责人”."
required : I18n.system_please_input + I18n.jobinfo_field_author
}
},
highlight : function(element) {
@ -284,8 +292,9 @@ $(function() {
if (data.code == "200") {
$('#addModal').modal('hide');
layer.open({
title: '',
content: '',
title: I18n.system_tips ,
btn: [ I18n.system_ok ],
content: I18n.system_add_suc ,
icon: '1',
end: function(layero, index){
jobTable.fnDraw();
@ -294,8 +303,9 @@ $(function() {
});
} else {
layer.open({
title: '',
content: (data.msg || "新增失败"),
title: I18n.system_tips ,
btn: [ I18n.system_ok ],
content: (data.msg || I18n.system_add_fail),
icon: '2'
});
}
@ -312,7 +322,7 @@ $(function() {
});
// 运行模式
// glueType change
$(".glueType").change(function(){
// executorHandler
var $executorHandler = $(this).parents("form").find("input[name='executorHandler']");
@ -339,19 +349,11 @@ $(function() {
}
});
// 更新
// update
$("#job_list").on('click', '.update',function() {
var id = $(this).parent('p').attr("id");
var row = tableData['key'+id];
if (!row) {
layer.open({
title: '',
content: ("任务信息加载失败,请刷新页面"),
icon: '2'
});
return;
}
// base data
$("#updateModal .form input[name='id']").val( row.id );
@ -392,13 +394,13 @@ $(function() {
},
messages : {
jobDesc : {
required :"请输入“描述”."
required : I18n.system_please_input + I18n.jobinfo_field_jobdesc
},
jobCron : {
required :"请输入“Cron”."
required : I18n.system_please_input + "Cron"
},
author : {
required : "请输入“负责人”."
required : I18n.system_please_input + I18n.jobinfo_field_author
}
},
highlight : function(element) {
@ -413,12 +415,13 @@ $(function() {
},
submitHandler : function(form) {
// post
$.post(base_url + "/jobinfo/reschedule", $("#updateModal .form").serialize(), function(data, status) {
$.post(base_url + "/jobinfo/update", $("#updateModal .form").serialize(), function(data, status) {
if (data.code == "200") {
$('#updateModal').modal('hide');
layer.open({
title: '',
content: '',
title: I18n.system_tips ,
btn: [ I18n.system_ok ],
content: I18n.system_update_suc ,
icon: '1',
end: function(layero, index){
//window.location.reload();
@ -427,8 +430,9 @@ $(function() {
});
} else {
layer.open({
title: '',
content: (data.msg || "更新失败"),
title: I18n.system_tips ,
btn: [ I18n.system_ok ],
content: (data.msg || I18n.system_update_fail ),
icon: '2'
});
}
@ -439,4 +443,20 @@ $(function() {
$("#updateModal .form")[0].reset()
});
/**
* find title by name, GlueType
*/
function findGlueTypeTitle(glueType) {
var glueTypeTitle;
$("#addModal .form select[name=glueType] option").each(function () {
var name = $(this).val();
var title = $(this).text();
if (glueType == name) {
glueTypeTitle = title;
return false
}
});
return glueTypeTitle;
}
});

@ -3,7 +3,7 @@ $(function() {
// trigger fail, end
if (triggerCode != 200) {
$('#logConsoleRunning').hide();
$('#logConsole').append('<span style="color: red;"></span>');
$('#logConsole').append('<span style="color: red;">'+ I18n.joblog_rolling_log_triggerfail +'</span>');
return;
}
@ -13,7 +13,7 @@ $(function() {
function pullLog() {
// pullFailCount, max=20
if (pullFailCount++ > 20) {
logRunStop('<span style="color: red;">Rolling,,</span>');
logRunStop('<span style="color: red;">'+ I18n.joblog_rolling_log_failoften +'</span>');
return;
}

@ -1,6 +1,6 @@
$(function() {
// 任务组列表选中, 任务列表初始化和选中
// jobGroup change, job list init and select
$("#jobGroup").on("change", function () {
var jobGroup = $(this).children('option:selected').val();
$.ajax({
@ -11,7 +11,7 @@ $(function() {
dataType : "json",
success : function(data){
if (data.code == 200) {
$("#jobId").html('<option value="0" ></option>');
$("#jobId").html( '<option value="0" >'+ I18n.system_all +'</option>' );
$.each(data.content, function (n, value) {
$("#jobId").append('<option value="' + value.id + '" >' + value.jobDesc + '</option>');
});
@ -20,8 +20,9 @@ $(function() {
}
} else {
layer.open({
title: '',
content: (data.msg || "接口异常"),
title: I18n.system_tips ,
btn: [ I18n.system_ok ],
content: (data.msg || I18n.system_api_error ),
icon: '2'
});
}
@ -33,7 +34,16 @@ $(function() {
$("#jobGroup").change();
}
// 过滤时间
// filter Time
var rangesConf = {};
rangesConf[I18n.daterangepicker_ranges_recent_hour] = [moment().subtract(1, 'hours'), moment()];
rangesConf[I18n.daterangepicker_ranges_today] = [moment().startOf('day'), moment().endOf('day')];
rangesConf[I18n.daterangepicker_ranges_yesterday] = [moment().subtract(1, 'days').startOf('day'), moment().subtract(1, 'days').endOf('day')];
rangesConf[I18n.daterangepicker_ranges_this_month] = [moment().startOf('month'), moment().endOf('month')];
rangesConf[I18n.daterangepicker_ranges_last_month] = [moment().subtract(1, 'months').startOf('month'), moment().subtract(1, 'months').endOf('month')];
rangesConf[I18n.daterangepicker_ranges_recent_week] = [moment().subtract(1, 'weeks').startOf('day'), moment().endOf('day')];
rangesConf[I18n.daterangepicker_ranges_recent_month] = [moment().subtract(1, 'months').startOf('day'), moment().endOf('day')];
$('#filterTime').daterangepicker({
autoApply:false,
singleDatePicker:false,
@ -42,29 +52,21 @@ $(function() {
timePickerIncrement: 10, // 时间的增量,单位为分钟
timePicker24Hour : true,
opens : 'left', //日期选择框的弹出位置
ranges: {
'1': [moment().subtract(1, 'hours'), moment()],
'': [moment().startOf('day'), moment().endOf('day')],
'': [moment().subtract(1, 'days').startOf('day'), moment().subtract(1, 'days').endOf('day')],
'7': [moment().subtract(6, 'days'), moment()],
'30': [moment().subtract(29, 'days'), moment()],
'': [moment().startOf('month'), moment().endOf('month')],
'': [moment().subtract(1, 'month').startOf('month'), moment().subtract(1, 'month').endOf('month')]
},
ranges: rangesConf,
locale : {
format: 'YYYY-MM-DD HH:mm:ss',
separator : ' - ',
customRangeLabel : '',
applyLabel : '',
cancelLabel : '',
fromLabel : '',
toLabel : '',
daysOfWeek : [ '日', '一', '二', '三', '四', '五', '六' ],
monthNames : [ '', '', '', '', '', '', '', '', '', '', '', '' ],
firstDay : 1,
startDate: moment().startOf('day'),
endDate: moment().endOf('day')
}
customRangeLabel : I18n.daterangepicker_custom_name ,
applyLabel : I18n.system_ok ,
cancelLabel : I18n.system_cancel ,
fromLabel : I18n.daterangepicker_custom_starttime ,
toLabel : I18n.daterangepicker_custom_endtime ,
daysOfWeek : I18n.daterangepicker_custom_daysofweek.split(',') , // '日', '一', '二', '三', '四', '五', '六'
monthNames : I18n.daterangepicker_custom_monthnames.split(',') , // '一月', '二月', '三月', '四月', '五月', '六月', '七月', '八月', '九月', '十月', '十一月', '十二月'
firstDay : 1
},
startDate: rangesConf[I18n.daterangepicker_ranges_today][0],
endDate: rangesConf[I18n.daterangepicker_ranges_today][1]
});
// init date tables
@ -74,6 +76,7 @@ $(function() {
"serverSide": true,
"ajax": {
url: base_url + "/joblog/pageList" ,
type:"post",
data : function ( d ) {
var obj = {};
obj.jobGroup = $('#jobGroup').val();
@ -92,24 +95,18 @@ $(function() {
{
"data": 'jobId',
"visible" : true,
"width":'10%',
"render": function ( data, type, row ) {
var glueTypeTitle = row.glueType;
if ('GLUE_GROOVY'==row.glueType) {
glueTypeTitle = "GLUE模式(Java)";
} else if ('GLUE_SHELL'==row.glueType) {
glueTypeTitle = "GLUE模式(Shell)";
} else if ('GLUE_PYTHON'==row.glueType) {
glueTypeTitle = "GLUE模式(Python)";
}else if ('GLUE_NODEJS'==row.glueType) {
glueTypeTitle = "GLUE模式(Nodejs)";
} else if ('BEAN'==row.glueType) {
glueTypeTitle = "BEAN模式" + row.executorHandler;
var glueTypeTitle = GlueTypeEnum[row.glueType];
if (row.executorHandler) {
glueTypeTitle = glueTypeTitle +"" + row.executorHandler;
}
var temp = '';
temp += '' + (row.executorAddress?row.executorAddress:'');
temp += '<br>' + glueTypeTitle;
temp += '<br>' + row.executorParam;
temp += I18n.joblog_field_executorAddress + '' + (row.executorAddress?row.executorAddress:'');
temp += '<br>'+ I18n.jobinfo_field_gluetype +'' + glueTypeTitle;
temp += '<br>'+ I18n.jobinfo_field_executorparam +'' + row.executorParam;
return '<a class="logTips" href="javascript:;" >'+ row.jobId +'<span style="display:none;">'+ temp +'</span></a>';
}
@ -117,18 +114,20 @@ $(function() {
{ "data": 'jobGroup', "visible" : false},
{
"data": 'triggerTime',
"width":'16%',
"render": function ( data, type, row ) {
return data?moment(new Date(data)).format("YYYY-MM-DD HH:mm:ss"):"";
}
},
{
"data": 'triggerCode',
"width":'12%',
"render": function ( data, type, row ) {
var html = data;
if (data == 200) {
html = '<span style="color: green"></span>';
html = '<span style="color: green">'+ I18n.system_success +'</span>';
} else if (data == 500) {
html = '<span style="color: red"></span>';
html = '<span style="color: red">'+ I18n.system_fail +'</span>';
} else if (data == 0) {
html = '';
}
@ -137,26 +136,29 @@ $(function() {
},
{
"data": 'triggerMsg',
"width":'12%',
"render": function ( data, type, row ) {
return data?'<a class="logTips" href="javascript:;" ><span style="display:none;">'+ data +'</span></a>':"无";
return data?'<a class="logTips" href="javascript:;" >'+ I18n.system_show +'<span style="display:none;">'+ data +'</span></a>':I18n.system_empty;
}
},
{
"data": 'handleTime',
"width":'16%',
"render": function ( data, type, row ) {
return data?moment(new Date(data)).format("YYYY-MM-DD HH:mm:ss"):"";
}
},
{
"data": 'handleCode',
"width":'12%',
"render": function ( data, type, row ) {
var html = data;
if (data == 200) {
html = '<span style="color: green"></span>';
html = '<span style="color: green">'+ I18n.joblog_handleCode_200 +'</span>';
} else if (data == 500) {
html = '<span style="color: red"></span>';
html = '<span style="color: red">'+ I18n.joblog_handleCode_500 +'</span>';
} else if (data == 501) {
html = '<span style="color: red"></span>';
html = '<span style="color: red">'+ I18n.joblog_handleCode_501 +'</span>';
} else if (data == 0) {
html = '';
}
@ -165,21 +167,22 @@ $(function() {
},
{
"data": 'handleMsg',
"width":'12%',
"render": function ( data, type, row ) {
return data?'<a class="logTips" href="javascript:;" ><span style="display:none;">'+ data +'</span></a>':"无";
return data?'<a class="logTips" href="javascript:;" >'+ I18n.system_show +'<span style="display:none;">'+ data +'</span></a>':I18n.system_empty;
}
},
{
"data": 'handleMsg' ,
"bSortable": false,
"width": "8%" ,
"width":'10%',
"render": function ( data, type, row ) {
// better support expression or string, not function
return function () {
if (row.triggerCode == 200){
var temp = '<a href="javascript:;" class="logDetail" _id="'+ row.id +'"></a>';
var temp = '<a href="javascript:;" class="logDetail" _id="'+ row.id +'">'+ I18n.joblog_rolling_log +'</a>';
if(row.handleCode == 0){
temp += '<br><a href="javascript:;" class="logKill" _id="'+ row.id +'" style="color: red;" ></a>';
temp += '<br><a href="javascript:;" class="logKill" _id="'+ row.id +'" style="color: red;" >'+ I18n.joblog_kill_log +'</a>';
}
return temp;
}
@ -189,43 +192,43 @@ $(function() {
}
],
"language" : {
"sProcessing" : "处理中...",
"sLengthMenu" : "每页 _MENU_ 条记录",
"sZeroRecords" : "没有匹配结果",
"sInfo" : "第 _PAGE_ 页 ( 总共 _PAGES_ 页_TOTAL_ 条记录 )",
"sInfoEmpty" : "无记录",
"sInfoFiltered" : "(由 _MAX_ 项结果过滤)",
"sProcessing" : I18n.dataTable_sProcessing ,
"sLengthMenu" : I18n.dataTable_sLengthMenu ,
"sZeroRecords" : I18n.dataTable_sZeroRecords ,
"sInfo" : I18n.dataTable_sInfo ,
"sInfoEmpty" : I18n.dataTable_sInfoEmpty ,
"sInfoFiltered" : I18n.dataTable_sInfoFiltered ,
"sInfoPostFix" : "",
"sSearch" : "搜索:",
"sSearch" : I18n.dataTable_sSearch ,
"sUrl" : "",
"sEmptyTable" : "表中数据为空",
"sLoadingRecords" : "载入中...",
"sEmptyTable" : I18n.dataTable_sEmptyTable ,
"sLoadingRecords" : I18n.dataTable_sLoadingRecords ,
"sInfoThousands" : ",",
"oPaginate" : {
"sFirst" : "首页",
"sPrevious" : "上页",
"sNext" : "下页",
"sLast" : "末页"
"sFirst" : I18n.dataTable_sFirst ,
"sPrevious" : I18n.dataTable_sPrevious ,
"sNext" : I18n.dataTable_sNext ,
"sLast" : I18n.dataTable_sLast
},
"oAria" : {
"sSortAscending" : ": 以升序排列此列",
"sSortDescending" : ": 以降序排列此列"
"sSortAscending" : I18n.dataTable_sSortAscending ,
"sSortDescending" : I18n.dataTable_sSortDescending
}
}
});
// 日志弹框提示
// logTips alert
$('#joblog_list').on('click', '.logTips', function(){
var msg = $(this).find('span').html();
ComAlertTec.show(msg);
});
// 搜索按钮
// search Btn
$('#searchBtn').on('click', function(){
logTable.fnDraw();
});
// 查看执行器详细执行日志
// logDetail look
$('#joblog_list').on('click', '.logDetail', function(){
var _id = $(this).attr('_id');
@ -234,12 +237,16 @@ $(function() {
});
/**
*
* log Kill
*/
$('#joblog_list').on('click', '.logKill', function(){
var _id = $(this).attr('_id');
layer.confirm('?', {icon: 3, title:''}, function(index){
layer.confirm( (I18n.system_ok + I18n.joblog_kill_log + '?'), {
icon: 3,
title: I18n.system_tips ,
btn: [ I18n.system_ok, I18n.system_cancel ]
}, function(index){
layer.close(index);
$.ajax({
@ -250,8 +257,9 @@ $(function() {
success : function(data){
if (data.code == 200) {
layer.open({
title: '',
content: '',
title: I18n.system_tips,
btn: [ I18n.system_ok ],
content: I18n.system_opt_suc ,
icon: '1',
end: function(layero, index){
logTable.fnDraw();
@ -259,8 +267,9 @@ $(function() {
});
} else {
layer.open({
title: '',
content: (data.msg || "操作失败"),
title: I18n.system_tips,
btn: [ I18n.system_ok ],
content: (data.msg || I18n.system_opt_fail ),
icon: '2'
});
}
@ -271,7 +280,7 @@ $(function() {
});
/**
* Log
* clear Log
*/
$('#clearLog').on('click', function(){
@ -295,8 +304,9 @@ $(function() {
if (data.code == "200") {
$('#clearLogModal').modal('hide');
layer.open({
title: '',
content: '',
title: I18n.system_tips ,
btn: [ I18n.system_ok ],
content: (I18n.joblog_clean_log + I18n.system_success) ,
icon: '1',
end: function(layero, index){
logTable.fnDraw();
@ -304,8 +314,9 @@ $(function() {
});
} else {
layer.open({
title: '',
content: (data.msg || "日志清理失败"),
title: I18n.system_tips ,
btn: [ I18n.system_ok ],
content: (data.msg || (I18n.joblog_clean_log + I18n.system_fail) ),
icon: '2'
});
}
@ -318,7 +329,7 @@ $(function() {
});
// 提示-科技主题
// Com Alert by Tec theme
var ComAlertTec = {
html:function(){
var html =
@ -328,7 +339,7 @@ var ComAlertTec = {
'<div class="modal-body"><div class="alert" style="color:#fff;"></div></div>' +
'<div class="modal-footer">' +
'<div class="text-center" >' +
'<button type="button" class="btn btn-info ok" data-dismiss="modal" ></button>' +
'<button type="button" class="btn btn-info ok" data-dismiss="modal" >'+ I18n.system_ok +'</button>' +
'</div>' +
'</div>' +
'</div>' +
@ -342,7 +353,7 @@ var ComAlertTec = {
$('body').append(ComAlertTec.html());
}
// 弹框初始
// init com alert
$('#ComAlertTec .alert').html(msg);
$('#ComAlertTec').modal('show');

@ -1,12 +1,13 @@
$(function(){
// 复选框
// input iCheck
$('input').iCheck({
checkboxClass: 'icheckbox_square-blue',
radioClass: 'iradio_square-blue',
increaseArea: '20%' // optional
});
// 登录.规则校验
// login Form Valid
var loginFormValid = $("#loginForm").validate({
errorElement : 'span',
errorClass : 'help-block',
@ -25,14 +26,13 @@ $(function(){
},
messages : {
userName : {
required :"请输入登录账号." ,
minlength:"登录账号不应低于5位",
maxlength:"登录账号不应超过18位"
required : I18n.login_username_empty,
minlength : I18n.login_username_lt_5
},
password : {
required :"请输入登录密码." ,
minlength:"登录密码不应低于5位",
maxlength:"登录密码不应超过18位"
required : I18n.login_password_empty ,
minlength : I18n.login_password_lt_5
/*,maxlength:"登录密码不应超过18位"*/
}
},
highlight : function(element) {
@ -48,22 +48,15 @@ $(function(){
submitHandler : function(form) {
$.post(base_url + "/login", $("#loginForm").serialize(), function(data, status) {
if (data.code == "200") {
layer.msg('');
layer.msg( I18n.login_success );
setTimeout(function(){
window.location.href = base_url;
}, 500);
/*layer.open({
title: '',
content: '',
icon: '1',
end: function(layero, index){
window.location.href = base_url;
}
});*/
} else {
layer.open({
title: '',
content: (data.msg || "登录失败"),
title: I18n.system_tips,
btn: [ I18n.system_ok ],
content: (data.msg || I18n.login_fail ),
icon: '2'
});
}

@ -1,2 +0,0 @@
/*! layer mobile-v2.0.0 Web弹层组件 MIT License http://layer.layui.com/mobile By 贤心 */
;!function(e){"use strict";var t=document,n="querySelectorAll",i="getElementsByClassName",a=function(e){return t[n](e)},s={type:0,shade:!0,shadeClose:!0,fixed:!0,anim:"scale"},l={extend:function(e){var t=JSON.parse(JSON.stringify(s));for(var n in e)t[n]=e[n];return t},timer:{},end:{}};l.touch=function(e,t){e.addEventListener("click",function(e){t.call(this,e)},!1)};var r=0,o=["layui-m-layer"],c=function(e){var t=this;t.config=l.extend(e),t.view()};c.prototype.view=function(){var e=this,n=e.config,s=t.createElement("div");e.id=s.id=o[0]+r,s.setAttribute("class",o[0]+" "+o[0]+(n.type||0)),s.setAttribute("index",r);var l=function(){var e="object"==typeof n.title;return n.title?'<h3 style="'+(e?n.title[1]:"")+'">'+(e?n.title[0]:n.title)+"</h3>":""}(),c=function(){"string"==typeof n.btn&&(n.btn=[n.btn]);var e,t=(n.btn||[]).length;return 0!==t&&n.btn?(e='<span yes type="1">'+n.btn[0]+"</span>",2===t&&(e='<span no type="0">'+n.btn[1]+"</span>"+e),'<div class="layui-m-layerbtn">'+e+"</div>"):""}();if(n.fixed||(n.top=n.hasOwnProperty("top")?n.top:100,n.style=n.style||"",n.style+=" top:"+(t.body.scrollTop+n.top)+"px"),2===n.type&&(n.content='<i></i><i class="layui-m-layerload"></i><i></i><p>'+(n.content||"")+"</p>"),n.skin&&(n.anim="up"),"msg"===n.skin&&(n.shade=!1),s.innerHTML=(n.shade?"<div "+("string"==typeof n.shade?'style="'+n.shade+'"':"")+' class="layui-m-layershade"></div>':"")+'<div class="layui-m-layermain" '+(n.fixed?"":'style="position:static;"')+'><div class="layui-m-layersection"><div class="layui-m-layerchild '+(n.skin?"layui-m-layer-"+n.skin+" ":"")+(n.className?n.className:"")+" "+(n.anim?"layui-m-anim-"+n.anim:"")+'" '+(n.style?'style="'+n.style+'"':"")+">"+l+'<div class="layui-m-layercont">'+n.content+"</div>"+c+"</div></div></div>",!n.type||2===n.type){var d=t[i](o[0]+n.type),y=d.length;y>=1&&layer.close(d[0].getAttribute("index"))}document.body.appendChild(s);var u=e.elem=a("#"+e.id)[0];n.success&&n.success(u),e.index=r++,e.action(n,u)},c.prototype.action=function(e,t){var n=this;e.time&&(l.timer[n.index]=setTimeout(function(){layer.close(n.index)},1e3*e.time));var a=function(){var t=this.getAttribute("type");0==t?(e.no&&e.no(),layer.close(n.index)):e.yes?e.yes(n.index):layer.close(n.index)};if(e.btn)for(var s=t[i]("layui-m-layerbtn")[0].children,r=s.length,o=0;o<r;o++)l.touch(s[o],a);if(e.shade&&e.shadeClose){var c=t[i]("layui-m-layershade")[0];l.touch(c,function(){layer.close(n.index,e.end)})}e.end&&(l.end[n.index]=e.end)},e.layer={v:"2.0",index:r,open:function(e){var t=new c(e||{});return t.index},close:function(e){var n=a("#"+o[0]+e)[0];n&&(n.innerHTML="",t.body.removeChild(n),clearTimeout(l.timer[e]),delete l.timer[e],"function"==typeof l.end[e]&&l.end[e](),delete l.end[e])},closeAll:function(){for(var e=t[i](o[0]),n=0,a=e.length;n<a;n++)layer.close(0|e[0].getAttribute("index"))}},"function"==typeof define?define(function(){return layer}):function(){var e=document.scripts,n=e[e.length-1],i=n.src,a=i.substring(0,i.lastIndexOf("/")+1);n.getAttribute("merge")||document.head.appendChild(function(){var e=t.createElement("link");return e.href=a+"need/layer.css?2.0",e.type="text/css",e.rel="styleSheet",e.id="layermcss",e}())}()}(window);

File diff suppressed because one or more lines are too long

@ -1,7 +1,7 @@
package com.xxl.job.admin.controller;
import com.xxl.job.admin.controller.interceptor.PermissionInterceptor;
import com.xxl.job.admin.core.util.PropertiesUtil;
import com.xxl.job.admin.core.conf.XxlJobAdminConfig;
import org.junit.Before;
import org.junit.Test;
import org.springframework.http.MediaType;
@ -22,8 +22,8 @@ public class JobInfoControllerTest extends AbstractSpringMvcTest {
MvcResult ret = mockMvc.perform(
post("/login")
.contentType(MediaType.APPLICATION_FORM_URLENCODED)
.param("userName", PropertiesUtil.getString("xxl.job.login.username"))
.param("password", PropertiesUtil.getString("xxl.job.login.password"))
.param("userName", XxlJobAdminConfig.getAdminConfig().getLoginUsername())
.param("password", XxlJobAdminConfig.getAdminConfig().getLoginPassword())
).andReturn();
cookie = ret.getResponse().getCookie(PermissionInterceptor.LOGIN_IDENTITY_KEY);
}

@ -20,8 +20,8 @@ public class XxlJobInfoDaoTest {
@Test
public void pageList(){
List<XxlJobInfo> list = xxlJobInfoDao.pageList(0, 20, 0, null);
int list_count = xxlJobInfoDao.pageListCount(0, 20, 0, null);
List<XxlJobInfo> list = xxlJobInfoDao.pageList(0, 20, 0, null, null);
int list_count = xxlJobInfoDao.pageListCount(0, 20, 0, null, null);
System.out.println(list);
System.out.println(list_count);

@ -50,7 +50,7 @@ public class XxlJobLogDaoTest {
dto = xxlJobLogDao.load(log.getId());
List<Map<String, Object>> list2 = xxlJobLogDao.triggerCountByDay(DateUtils.addDays(new Date(), 30), new Date(), 200);
List<Map<String, Object>> list2 = xxlJobLogDao.triggerCountByDay(DateUtils.addDays(new Date(), 30), new Date());
int ret4 = xxlJobLogDao.clearLog(1, 1, new Date(), 100);

@ -0,0 +1,25 @@
package com.xxl.job.admin.util;
import com.xxl.job.admin.core.util.I18nUtil;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
/**
* email util test
*
* @author xuxueli 2017-12-22 17:16:23
*/
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations = "classpath*:spring/applicationcontext-*.xml")
public class I18nUtilTest {
@Test
public void test(){
System.out.println(I18nUtil.getString("admin_name"));
System.out.println(I18nUtil.getMultString("admin_name", "admin_name_full"));
System.out.println(I18nUtil.getMultString());
}
}

@ -2,6 +2,9 @@ package com.xxl.job.admin.util;
import com.xxl.job.admin.core.util.MailUtil;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import java.text.MessageFormat;
@ -10,6 +13,8 @@ import java.text.MessageFormat;
*
* @author xuxueli 2017-12-22 17:16:23
*/
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations = "classpath*:spring/applicationcontext-*.xml")
public class MailUtilTest {
@Test

@ -1,18 +0,0 @@
package com.xxl.job.admin.util;
import com.xxl.job.admin.core.util.PropertiesUtil;
import org.junit.Test;
/**
* prop util test
*
* @author xuxueli 2017-12-25 15:17:36
*/
public class PropertiesUtilTest {
@Test
public void registryTest() throws Exception {
System.out.println(PropertiesUtil.getString("xxl.job.login.username"));
}
}

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

@ -8,6 +8,7 @@ import com.xxl.job.core.handler.annotation.JobHandler;
import com.xxl.job.core.log.XxlJobFileAppender;
import com.xxl.job.core.rpc.netcom.NetComClientProxy;
import com.xxl.job.core.rpc.netcom.NetComServerFactory;
import com.xxl.job.core.thread.JobLogFileCleanThread;
import com.xxl.job.core.thread.JobThread;
import com.xxl.job.core.util.NetUtil;
import org.slf4j.Logger;
@ -28,32 +29,35 @@ public class XxlJobExecutor implements ApplicationContextAware {
private static final Logger logger = LoggerFactory.getLogger(XxlJobExecutor.class);
// ---------------------- param ----------------------
private String adminAddresses;
private String appName;
private String ip;
private int port;
private String appName;
private String adminAddresses;
private String accessToken;
private String logPath;
private int logRetentionDays;
public void setAdminAddresses(String adminAddresses) {
this.adminAddresses = adminAddresses;
}
public void setAppName(String appName) {
this.appName = appName;
}
public void setIp(String ip) {
this.ip = ip;
}
public void setPort(int port) {
this.port = port;
}
public void setAppName(String appName) {
this.appName = appName;
}
public void setAdminAddresses(String adminAddresses) {
this.adminAddresses = adminAddresses;
}
public void setAccessToken(String accessToken) {
this.accessToken = accessToken;
}
public void setLogPath(String logPath) {
this.logPath = logPath;
}
public void setLogRetentionDays(int logRetentionDays) {
this.logRetentionDays = logRetentionDays;
}
// ---------------------- applicationContext ----------------------
private static ApplicationContext applicationContext;
@ -79,6 +83,9 @@ public class XxlJobExecutor implements ApplicationContextAware {
// init executor-server
initExecutorServer(port, ip, appName, accessToken);
// init JobLogFileCleanThread
JobLogFileCleanThread.getInstance().start(logRetentionDays);
}
public void destroy(){
// destory JobThreadRepository
@ -91,6 +98,9 @@ public class XxlJobExecutor implements ApplicationContextAware {
// destory executor-server
stopExecutorServer();
// destory JobLogFileCleanThread
JobLogFileCleanThread.getInstance().toStop();
}

@ -40,8 +40,8 @@ public class ScriptJobHandler extends IJobHandler {
String cmd = glueType.getCmd();
// make script file
String scriptFileName = XxlJobFileAppender.getLogPath()
.concat("/gluesource/")
String scriptFileName = XxlJobFileAppender.getGlueSrcPath()
.concat("/")
.concat(String.valueOf(jobId))
.concat("_")
.concat(String.valueOf(glueUpdatetime))

@ -20,8 +20,21 @@ public class XxlJobFileAppender {
public static final InheritableThreadLocal<String> contextHolder = new InheritableThreadLocal<String>();
// log base path
/**
* log base path
*
* strut like:
* ---/
* ---/gluesource/
* ---/gluesource/10_1514171108000.js
* ---/gluesource/10_1514171108000.js
* ---/2017-12-25/
* ---/2017-12-25/639.log
* ---/2017-12-25/821.log
*
*/
private static String logBasePath = "/data/applogs/xxl-job/jobhandler";
private static String glueSrcPath = logBasePath.concat("/gluesource");
public static void initLogPath(String logPath){
// init
if (logPath!=null && logPath.trim().length()>0) {
@ -39,11 +52,14 @@ public class XxlJobFileAppender {
if (!glueBaseDir.exists()) {
glueBaseDir.mkdirs();
}
glueSrcPath = glueBaseDir.getPath();
}
public static String getLogPath() {
return logBasePath;
}
public static String getGlueSrcPath() {
return glueSrcPath;
}
/**
* log filename, like "logPath/yyyy-MM-dd/9999.log"

@ -0,0 +1,118 @@
package com.xxl.job.core.thread;
import com.xxl.job.core.log.XxlJobFileAppender;
import com.xxl.job.core.util.FileUtil;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.io.File;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Calendar;
import java.util.Date;
import java.util.concurrent.TimeUnit;
/**
* job file clean thread
*
* @author xuxueli 2017-12-29 16:23:43
*/
public class JobLogFileCleanThread extends Thread {
private static Logger logger = LoggerFactory.getLogger(JobLogFileCleanThread.class);
private static JobLogFileCleanThread instance = new JobLogFileCleanThread();
public static JobLogFileCleanThread getInstance(){
return instance;
}
private Thread localThread;
private volatile boolean toStop = false;
public void start(final long logRetentionDays){
// limit min value
if (logRetentionDays < 3 ) {
return;
}
localThread = new Thread(new Runnable() {
@Override
public void run() {
while (!toStop) {
try {
// clean log dir, over logRetentionDays
File[] childDirs = new File(XxlJobFileAppender.getLogPath()).listFiles();
if (childDirs!=null && childDirs.length>0) {
// today
Calendar todayCal = Calendar.getInstance();
todayCal.set(Calendar.HOUR_OF_DAY,0);
todayCal.set(Calendar.MINUTE,0);
todayCal.set(Calendar.SECOND,0);
todayCal.set(Calendar.MILLISECOND,0);
Date todayDate = todayCal.getTime();
for (File childFile: childDirs) {
// valid
if (!childFile.isDirectory()) {
continue;
}
if (childFile.getName().indexOf("-") == -1) {
continue;
}
// file create date
Date logFileCreateDate = null;
try {
SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd");
logFileCreateDate = simpleDateFormat.parse(childFile.getName());
} catch (ParseException e) {
logger.error(e.getMessage(), e);
}
if (logFileCreateDate == null) {
continue;
}
if ((todayDate.getTime()-logFileCreateDate.getTime()) >= logRetentionDays * (24 * 60 * 60 * 1000) ) {
FileUtil.deleteRecursively(childFile);
}
}
}
} catch (Exception e) {
logger.error(e.getMessage(), e);
}
try {
TimeUnit.DAYS.sleep(1);
} catch (InterruptedException e) {
logger.error(e.getMessage(), e);
}
}
logger.info(">>>>>>>>>>> xxl-job, executor JobLogFileCleanThread thread destory.");
}
});
localThread.setDaemon(true);
localThread.start();
}
public void toStop() {
toStop = true;
if (localThread == null) {
return;
}
// interrupt and wait
localThread.interrupt();
try {
localThread.join();
} catch (InterruptedException e) {
logger.error(e.getMessage(), e);
}
}
}

@ -0,0 +1,27 @@
package com.xxl.job.core.util;
import java.io.File;
/**
* file tool
*
* @author xuxueli 2017-12-29 17:56:48
*/
public class FileUtil {
public static boolean deleteRecursively(File root) {
if (root != null && root.exists()) {
if (root.isDirectory()) {
File[] children = root.listFiles();
if (children != null) {
for (File child : children) {
deleteRecursively(child);
}
}
}
return root.delete();
}
return false;
}
}

@ -59,7 +59,7 @@ public class ScriptUtil {
// 标准输出print null if watchdog timeout
// 错误输出logging + 异常 still exists if watchdog timeout
// 标准输入
FileOutputStream fileOutputStream = new FileOutputStream(logFile, true);
try (FileOutputStream fileOutputStream = new FileOutputStream(logFile, true)) {
PumpStreamHandler streamHandler = new PumpStreamHandler(fileOutputStream, fileOutputStream, null);
// command
@ -76,5 +76,6 @@ public class ScriptUtil {
int exitValue = exec.execute(commandline); // exit code: 0=success, 1=error
return exitValue;
}
}
}

@ -5,7 +5,7 @@
<parent>
<groupId>com.xuxueli</groupId>
<artifactId>xxl-job</artifactId>
<version>1.9.0-SNAPSHOT</version>
<version>1.9.2-SNAPSHOT</version>
</parent>
<artifactId>xxl-job-executor-samples</artifactId>
<packaging>pom</packaging>

@ -5,7 +5,7 @@
<parent>
<artifactId>xxl-job-executor-samples</artifactId>
<groupId>com.xuxueli</groupId>
<version>1.9.0-SNAPSHOT</version>
<version>1.9.2-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>xxl-job-executor-sample-jfinal</artifactId>

@ -29,12 +29,13 @@ public class JFinalCoreConfig extends JFinalConfig {
// init executor
xxlJobExecutor = new XxlJobExecutor();
xxlJobExecutor.setAdminAddresses(xxlJobProp.get("xxl.job.admin.addresses"));
xxlJobExecutor.setAppName(xxlJobProp.get("xxl.job.executor.appname"));
xxlJobExecutor.setIp(xxlJobProp.get("xxl.job.executor.ip"));
xxlJobExecutor.setPort(xxlJobProp.getInt("xxl.job.executor.port"));
xxlJobExecutor.setAppName(xxlJobProp.get("xxl.job.executor.appname"));
xxlJobExecutor.setAdminAddresses(xxlJobProp.get("xxl.job.admin.addresses"));
xxlJobExecutor.setLogPath(xxlJobProp.get("xxl.job.executor.logpath"));
xxlJobExecutor.setAccessToken(xxlJobProp.get("xxl.job.accessToken"));
xxlJobExecutor.setLogPath(xxlJobProp.get("xxl.job.executor.logpath"));
xxlJobExecutor.setLogRetentionDays(xxlJobProp.getInt("xxl.job.executor.logretentiondays"));
// start executor
try {

@ -6,8 +6,10 @@ xxl.job.executor.appname=xxl-job-executor-sample
xxl.job.executor.ip=
xxl.job.executor.port=9996
### xxl-job log path
xxl.job.executor.logpath=/data/applogs/xxl-job/jobhandler
### xxl-job, access token
xxl.job.accessToken=
### xxl-job log path
xxl.job.executor.logpath=/data/applogs/xxl-job/jobhandler
### xxl-job log retention days
xxl.job.executor.logretentiondays=-1

@ -5,7 +5,7 @@
<parent>
<groupId>com.xuxueli</groupId>
<artifactId>xxl-job-executor-samples</artifactId>
<version>1.9.0-SNAPSHOT</version>
<version>1.9.2-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>xxl-job-executor-sample-nutz</artifactId>

@ -39,12 +39,13 @@ public class NutzSetup implements Setup {
// init executor
xxlJobExecutor = new XxlJobExecutor();
xxlJobExecutor.setAdminAddresses(xxlJobProp.get("xxl.job.admin.addresses"));
xxlJobExecutor.setAppName(xxlJobProp.get("xxl.job.executor.appname"));
xxlJobExecutor.setIp(xxlJobProp.get("xxl.job.executor.ip"));
xxlJobExecutor.setPort(xxlJobProp.getInt("xxl.job.executor.port"));
xxlJobExecutor.setAppName(xxlJobProp.get("xxl.job.executor.appname"));
xxlJobExecutor.setAdminAddresses(xxlJobProp.get("xxl.job.admin.addresses"));
xxlJobExecutor.setLogPath(xxlJobProp.get("xxl.job.executor.logpath"));
xxlJobExecutor.setAccessToken(xxlJobProp.get("xxl.job.accessToken"));
xxlJobExecutor.setLogPath(xxlJobProp.get("xxl.job.executor.logpath"));
xxlJobExecutor.setLogRetentionDays(xxlJobProp.getInt("xxl.job.executor.logretentiondays"));
// start executor
try {

@ -6,8 +6,10 @@ xxl.job.executor.appname=xxl-job-executor-sample
xxl.job.executor.ip=
xxl.job.executor.port=9997
### xxl-job log path
xxl.job.executor.logpath=/data/applogs/xxl-job/jobhandler
### xxl-job, access token
xxl.job.accessToken=
### xxl-job log path
xxl.job.executor.logpath=/data/applogs/xxl-job/jobhandler
### xxl-job log retention days
xxl.job.executor.logretentiondays=-1

@ -4,7 +4,7 @@
<parent>
<groupId>com.xuxueli</groupId>
<artifactId>xxl-job-executor-samples</artifactId>
<version>1.9.0-SNAPSHOT</version>
<version>1.9.2-SNAPSHOT</version>
</parent>
<artifactId>xxl-job-executor-sample-spring</artifactId>
<packaging>war</packaging>

@ -23,18 +23,20 @@
<!-- 配置02、执行器 -->
<bean id="xxlJobExecutor" class="com.xxl.job.core.executor.XxlJobExecutor" init-method="start" destroy-method="destroy" >
<!-- 执行器注册中心地址[选填],为空则关闭自动注册 -->
<property name="adminAddresses" value="${xxl.job.admin.addresses}" />
<!-- 执行器AppName[选填],为空则关闭自动注册 -->
<property name="appName" value="${xxl.job.executor.appname}" />
<!-- 执行器IP[选填],为空则自动获取 -->
<property name="ip" value="${xxl.job.executor.ip}" />
<!-- 执行器端口号[选填],为空则自动获取 -->
<property name="port" value="${xxl.job.executor.port}" />
<!-- 执行器AppName[选填],为空则关闭自动注册 -->
<property name="appName" value="${xxl.job.executor.appname}" />
<!-- 执行器注册中心地址[选填],为空则关闭自动注册 -->
<property name="adminAddresses" value="${xxl.job.admin.addresses}" />
<!-- 执行器日志路径[选填],为空则使用默认路径 -->
<property name="logPath" value="${xxl.job.executor.logpath}" />
<!-- 访问令牌[选填],非空则进行匹配校验 -->
<property name="accessToken" value="${xxl.job.accessToken}" />
<!-- 执行器日志路径[选填],为空则使用默认路径 -->
<property name="logPath" value="${xxl.job.executor.logpath}" />
<!-- 日志保存天数[选填]值大于3时生效 -->
<property name="logRetentionDays" value="${xxl.job.executor.logretentiondays}" />
</bean>

@ -4,10 +4,12 @@ xxl.job.admin.addresses=http://127.0.0.1:8080/xxl-job-admin
### xxl-job executor address
xxl.job.executor.appname=xxl-job-executor-sample
xxl.job.executor.ip=
xxl.job.executor.port=9999
### xxl-job log path
xxl.job.executor.logpath=/data/applogs/xxl-job/jobhandler
xxl.job.executor.port=9998
### xxl-job, access token
xxl.job.accessToken=
### xxl-job log path
xxl.job.executor.logpath=/data/applogs/xxl-job/jobhandler
### xxl-job log retention days
xxl.job.executor.logretentiondays=-1

@ -6,7 +6,7 @@
<parent>
<groupId>com.xuxueli</groupId>
<artifactId>xxl-job-executor-samples</artifactId>
<version>1.9.0-SNAPSHOT</version>
<version>1.9.2-SNAPSHOT</version>
</parent>
<artifactId>xxl-job-executor-sample-springboot</artifactId>
<packaging>jar</packaging>

@ -18,12 +18,11 @@ import org.springframework.context.annotation.Configuration;
public class XxlJobConfig {
private Logger logger = LoggerFactory.getLogger(XxlJobConfig.class);
@Value("${xxl.job.admin.addresses}")
private String addresses;
private String adminAddresses;
@Value("${xxl.job.executor.appname}")
private String appname;
private String appName;
@Value("${xxl.job.executor.ip}")
private String ip;
@ -31,22 +30,28 @@ public class XxlJobConfig {
@Value("${xxl.job.executor.port}")
private int port;
@Value("${xxl.job.executor.logpath}")
private String logpath;
@Value("${xxl.job.accessToken}")
private String accessToken;
@Value("${xxl.job.executor.logpath}")
private String logPath;
@Value("${xxl.job.executor.logretentiondays}")
private int logRetentionDays;
@Bean(initMethod = "start", destroyMethod = "destroy")
public XxlJobExecutor xxlJobExecutor() {
logger.info(">>>>>>>>>>> xxl-job config init.");
XxlJobExecutor xxlJobExecutor = new XxlJobExecutor();
xxlJobExecutor.setAdminAddresses(adminAddresses);
xxlJobExecutor.setAppName(appName);
xxlJobExecutor.setIp(ip);
xxlJobExecutor.setPort(port);
xxlJobExecutor.setAppName(appname);
xxlJobExecutor.setAdminAddresses(addresses);
xxlJobExecutor.setLogPath(logpath);
xxlJobExecutor.setAccessToken(accessToken);
xxlJobExecutor.setLogPath(logPath);
xxlJobExecutor.setLogRetentionDays(logRetentionDays);
return xxlJobExecutor;
}

@ -4,17 +4,19 @@ server.port=8081
# log config
logging.config=classpath:logback.xml
# xxl-job
### xxl-job admin address list, such as "http://address" or "http://address01,http://address02"
xxl.job.admin.addresses=http://127.0.0.1:8080/xxl-job-admin
### xxl-job executor address
xxl.job.executor.appname=xxl-job-executor-sample
xxl.job.executor.ip=
xxl.job.executor.port=9998
### xxl-job log path
xxl.job.executor.logpath=/data/applogs/xxl-job/jobhandler
xxl.job.executor.port=9999
### xxl-job, access token
xxl.job.accessToken=
### xxl-job log path
xxl.job.executor.logpath=/data/applogs/xxl-job/jobhandler
### xxl-job log retention days
xxl.job.executor.logretentiondays=-1

Loading…
Cancel
Save