Sfoglia il codice sorgente

Merge pull request #1 from xuxueli/master

pull xuxueli/xxl-job
thinking_fioa 7 anni fa
parent
commit
8ed4d055a3
83 ha cambiato i file con 1402 aggiunte e 860 eliminazioni
  1. 28 23
      README.md
  2. 116 79
      doc/XXL-JOB官方文档.md
  3. 6 4
      doc/db/tables_xxl_job.sql
  4. 16 16
      pom.xml
  5. 33 37
      xxl-job-admin/pom.xml
  6. 16 14
      xxl-job-admin/src/main/java/com/xxl/job/admin/controller/IndexController.java
  7. 1 1
      xxl-job-admin/src/main/java/com/xxl/job/admin/controller/JobGroupController.java
  8. 2 2
      xxl-job-admin/src/main/java/com/xxl/job/admin/controller/JobLogController.java
  9. 5 5
      xxl-job-admin/src/main/java/com/xxl/job/admin/controller/interceptor/CookieInterceptor.java
  10. 27 6
      xxl-job-admin/src/main/java/com/xxl/job/admin/controller/interceptor/PermissionInterceptor.java
  11. 1 1
      xxl-job-admin/src/main/java/com/xxl/job/admin/core/model/XxlJobGroup.java
  12. 5 5
      xxl-job-admin/src/main/java/com/xxl/job/admin/core/model/XxlJobInfo.java
  13. 54 23
      xxl-job-admin/src/main/java/com/xxl/job/admin/core/thread/JobFailMonitorHelper.java
  14. 2 2
      xxl-job-admin/src/main/java/com/xxl/job/admin/core/thread/JobRegistryMonitorHelper.java
  15. 69 66
      xxl-job-admin/src/main/java/com/xxl/job/admin/core/trigger/XxlJobTrigger.java
  16. 17 18
      xxl-job-admin/src/main/java/com/xxl/job/admin/core/util/CookieUtil.java
  17. 32 147
      xxl-job-admin/src/main/java/com/xxl/job/admin/core/util/MailUtil.java
  18. 1 4
      xxl-job-admin/src/main/java/com/xxl/job/admin/core/util/PropertiesUtil.java
  19. 30 22
      xxl-job-admin/src/main/java/com/xxl/job/admin/service/impl/AdminBizImpl.java
  20. 33 27
      xxl-job-admin/src/main/java/com/xxl/job/admin/service/impl/XxlJobServiceImpl.java
  21. 5 5
      xxl-job-admin/src/main/resources/mybatis-mapper/XxlJobInfoMapper.xml
  22. 2 2
      xxl-job-admin/src/main/resources/spring/applicationcontext-base.xml
  23. 2 2
      xxl-job-admin/src/main/resources/spring/applicationcontext-xxl-job-admin.xml
  24. 3 3
      xxl-job-admin/src/main/resources/spring/springmvc-context.xml
  25. 20 3
      xxl-job-admin/src/main/webapp/WEB-INF/template/jobgroup/jobgroup.index.ftl
  26. 27 17
      xxl-job-admin/src/main/webapp/WEB-INF/template/jobinfo/jobinfo.index.ftl
  27. 1 3
      xxl-job-admin/src/main/webapp/WEB-INF/template/joblog/joblog.index.ftl
  28. 6 2
      xxl-job-admin/src/main/webapp/static/js/common.1.js
  29. 5 3
      xxl-job-admin/src/main/webapp/static/js/jobgroup.index.1.js
  30. 2 11
      xxl-job-admin/src/main/webapp/static/js/jobinfo.index.1.js
  31. 39 44
      xxl-job-admin/src/main/webapp/static/js/joblog.index.1.js
  32. 6 2
      xxl-job-admin/src/main/webapp/static/js/login.1.js
  33. 27 0
      xxl-job-admin/src/test/java/com/xxl/job/admin/controller/AbstractSpringMvcTest.java
  34. 47 0
      xxl-job-admin/src/test/java/com/xxl/job/admin/controller/JobInfoControllerTest.java
  35. 1 1
      xxl-job-admin/src/test/java/com/xxl/job/admin/dao/XxlJobGroupDaoTest.java
  36. 78 78
      xxl-job-admin/src/test/java/com/xxl/job/admin/dao/XxlJobInfoDaoTest.java
  37. 2 2
      xxl-job-admin/src/test/java/com/xxl/job/admin/dao/XxlJobLogDaoTest.java
  38. 1 1
      xxl-job-admin/src/test/java/com/xxl/job/admin/dao/XxlJobLogGlueDaoTest.java
  39. 1 1
      xxl-job-admin/src/test/java/com/xxl/job/admin/dao/XxlJobRegistryDaoTest.java
  40. 45 0
      xxl-job-admin/src/test/java/com/xxl/job/admin/util/MailUtilTest.java
  41. 18 0
      xxl-job-admin/src/test/java/com/xxl/job/admin/util/PropertiesUtilTest.java
  42. 1 1
      xxl-job-admin/src/test/java/com/xxl/job/adminbiz/AdminBizTest.java
  43. 4 4
      xxl-job-core/pom.xml
  44. 5 6
      xxl-job-core/src/main/java/com/xxl/job/core/biz/impl/ExecutorBizImpl.java
  45. 6 6
      xxl-job-core/src/main/java/com/xxl/job/core/executor/XxlJobExecutor.java
  46. 27 6
      xxl-job-core/src/main/java/com/xxl/job/core/glue/GlueTypeEnum.java
  47. 33 5
      xxl-job-core/src/main/java/com/xxl/job/core/handler/IJobHandler.java
  48. 2 2
      xxl-job-core/src/main/java/com/xxl/job/core/handler/impl/GlueJobHandler.java
  49. 24 17
      xxl-job-core/src/main/java/com/xxl/job/core/handler/impl/ScriptJobHandler.java
  50. 45 25
      xxl-job-core/src/main/java/com/xxl/job/core/log/XxlJobFileAppender.java
  51. 36 17
      xxl-job-core/src/main/java/com/xxl/job/core/log/XxlJobLogger.java
  52. 19 9
      xxl-job-core/src/main/java/com/xxl/job/core/thread/JobThread.java
  53. 5 6
      xxl-job-core/src/main/java/com/xxl/job/core/util/JacksonUtil.java
  54. 0 14
      xxl-job-core/src/main/java/com/xxl/job/core/util/ScriptUtil.java
  55. 1 0
      xxl-job-executor-samples/pom.xml
  56. 1 1
      xxl-job-executor-samples/xxl-job-executor-sample-jfinal/pom.xml
  57. 2 1
      xxl-job-executor-samples/xxl-job-executor-sample-jfinal/src/main/java/com/xuxueli/executor/sample/jfinal/config/JFinalCoreConfig.java
  58. 1 0
      xxl-job-executor-samples/xxl-job-executor-sample-jfinal/src/main/java/com/xuxueli/executor/sample/jfinal/controller/IndexController.java
  59. 6 7
      xxl-job-executor-samples/xxl-job-executor-sample-jfinal/src/main/java/com/xuxueli/executor/sample/jfinal/jobhandler/DemoJobHandler.java
  60. 2 2
      xxl-job-executor-samples/xxl-job-executor-sample-jfinal/src/main/java/com/xuxueli/executor/sample/jfinal/jobhandler/ShardingJobHandler.java
  61. 2 2
      xxl-job-executor-samples/xxl-job-executor-sample-jfinal/src/main/resources/xxl-job-executor.properties
  62. 6 0
      xxl-job-executor-samples/xxl-job-executor-sample-jfinal/src/main/webapp/WEB-INF/web.xml
  63. 1 0
      xxl-job-executor-samples/xxl-job-executor-sample-jfinal/src/main/webapp/index.html
  64. 38 0
      xxl-job-executor-samples/xxl-job-executor-sample-nutz/pom.xml
  65. 23 0
      xxl-job-executor-samples/xxl-job-executor-sample-nutz/src/main/java/com/xuxueli/executor/sample/nutz/MainModule.java
  66. 64 0
      xxl-job-executor-samples/xxl-job-executor-sample-nutz/src/main/java/com/xuxueli/executor/sample/nutz/config/NutzSetup.java
  67. 38 0
      xxl-job-executor-samples/xxl-job-executor-sample-nutz/src/main/java/com/xuxueli/executor/sample/nutz/jobhandler/DemoJobHandler.java
  68. 39 0
      xxl-job-executor-samples/xxl-job-executor-sample-nutz/src/main/java/com/xuxueli/executor/sample/nutz/jobhandler/ShardingJobHandler.java
  69. 16 0
      xxl-job-executor-samples/xxl-job-executor-sample-nutz/src/main/java/com/xuxueli/executor/sample/nutz/module/IndexModule.java
  70. 27 0
      xxl-job-executor-samples/xxl-job-executor-sample-nutz/src/main/resources/log4j.xml
  71. 13 0
      xxl-job-executor-samples/xxl-job-executor-sample-nutz/src/main/resources/xxl-job-executor.properties
  72. 32 0
      xxl-job-executor-samples/xxl-job-executor-sample-nutz/src/main/webapp/WEB-INF/web.xml
  73. 1 0
      xxl-job-executor-samples/xxl-job-executor-sample-nutz/src/main/webapp/index.html
  74. 6 6
      xxl-job-executor-samples/xxl-job-executor-sample-spring/src/main/java/com/xxl/job/executor/service/jobhandler/DemoJobHandler.java
  75. 2 2
      xxl-job-executor-samples/xxl-job-executor-sample-spring/src/main/java/com/xxl/job/executor/service/jobhandler/ShardingJobHandler.java
  76. 5 5
      xxl-job-executor-samples/xxl-job-executor-sample-spring/src/main/resources/applicationcontext-xxl-job.xml
  77. 1 1
      xxl-job-executor-samples/xxl-job-executor-sample-spring/src/main/resources/xxl-job-executor.properties
  78. 5 2
      xxl-job-executor-samples/xxl-job-executor-sample-spring/src/main/webapp/WEB-INF/web.xml
  79. 1 1
      xxl-job-executor-samples/xxl-job-executor-sample-spring/src/main/webapp/index.html
  80. 18 18
      xxl-job-executor-samples/xxl-job-executor-sample-springboot/src/main/java/com/xxl/job/executor/mvc/controller/IndexController.java
  81. 8 8
      xxl-job-executor-samples/xxl-job-executor-sample-springboot/src/main/java/com/xxl/job/executor/service/jobhandler/DemoJobHandler.java
  82. 2 2
      xxl-job-executor-samples/xxl-job-executor-sample-springboot/src/main/java/com/xxl/job/executor/service/jobhandler/ShardingJobHandler.java
  83. 2 2
      xxl-job-executor-samples/xxl-job-executor-sample-springboot/src/main/resources/application.properties

+ 28 - 23
README.md

@@ -47,29 +47,30 @@ XXL-JOB是一个轻量级分布式任务调度框架,其核心设计目标是
 ## Features
 - 1、简单:支持通过Web页面对任务进行CRUD操作,操作简单,一分钟上手;
 - 2、动态:支持动态修改任务状态、暂停/恢复任务,以及终止运行中任务,即时生效;
-- 3、调度中心HA(中心式):调度采用中心式设计,“调度中心”基于集群Quartz实现,可保证调度中心HA;
+- 3、调度中心HA(中心式):调度采用中心式设计,“调度中心”基于集群Quartz实现并支持集群部署,可保证调度中心HA;
 - 4、执行器HA(分布式):任务分布式执行,任务"执行器"支持集群部署,可保证任务执行HA;
-- 5、任务Failover:执行器集群部署时,任务路由策略选择"故障转移"情况下调度失败时将会平滑切换执行器进行Failover;
-- 6、一致性:“调度中心”通过DB锁保证集群分布式调度的一致性, 一次任务调度只会触发一次执行;
-- 7、自定义任务参数:支持在线配置调度任务入参,即时生效;
-- 8、调度线程池:调度系统多线程触发调度运行,确保调度精确执行,不被堵塞;
-- 9、弹性扩容缩容:一旦有新执行器机器上线或者下线,下次调度时将会重新分配任务;
-- 10、邮件报警:任务失败时支持邮件报警,支持配置多邮件地址群发报警邮件;
-- 11、状态监控:支持实时监控任务进度;
-- 12、Rolling执行日志:支持在线查看调度结果,并且支持以Rolling方式实时查看执行器输出的完整的执行日志;
-- 13、GLUE:提供Web IDE,支持在线开发任务逻辑代码,动态发布,实时编译生效,省略部署上线的过程。支持30个版本的历史版本回溯。
-- 14、数据加密:调度中心和执行器之间的通讯进行数据加密,提升调度信息安全性;
-- 15、任务依赖:支持配置子任务依赖,当父任务执行结束且执行成功后将会主动触发一次子任务的执行, 多个子任务用逗号分隔;
-- 16、推送maven中央仓库: 将会把最新稳定版推送到maven中央仓库, 方便用户接入和使用;
-- 17、任务注册: 执行器会周期性自动注册任务, 调度中心将会自动发现注册的任务并触发执行。同时,也支持手动录入执行器地址;
-- 18、路由策略:执行器集群部署时提供丰富的路由策略,包括:第一个、最后一个、轮询、随机、一致性HASH、最不经常使用、最近最久未使用、故障转移、忙碌转移等;
-- 19、运行报表:支持实时查看运行数据,如任务数量、调度次数、执行器数量等;以及调度报表,如调度日期分布图,调度成功分布图等;
-- 20、脚本任务:支持以GLUE模式开发和运行脚本任务,包括Shell、Python等类型脚本;
-- 21、阻塞处理策略:调度过于密集执行器来不及处理时的处理策略,策略包括:单机串行(默认)、丢弃后续调度、覆盖之前调度;
-- 22、失败处理策略;调度失败时的处理策略,策略包括:失败告警(默认)、失败重试;
-- 23、分片广播任务:执行器集群部署时,任务路由策略选择"分片广播"情况下,一次任务调度将会广播触发对应集群中所有执行器执行一次任务,同时传递分片参数;可根据分片参数开发分片任务;
-- 24、动态分片:分片广播任务以执行器为维度进行分片,支持动态扩容执行器集群从而动态增加分片数量,协同进行业务处理;在进行大数据量业务操作时可显著提升任务处理能力和速度。
-- 25、事件触发:除了"Cron方式"和"任务依赖方式"触发任务执行之外,支持基于事件的触发任务方式。调度中心提供触发任务单次执行的API服务,可根据业务事件灵活触发。
+- 5、注册中心: 执行器会周期性自动注册任务, 调度中心将会自动发现注册的任务并触发执行。同时,也支持手动录入执行器地址;
+- 6、弹性扩容缩容:一旦有新执行器机器上线或者下线,下次调度时将会重新分配任务;
+- 7、路由策略:执行器集群部署时提供丰富的路由策略,包括:第一个、最后一个、轮询、随机、一致性HASH、最不经常使用、最近最久未使用、故障转移、忙碌转移等;
+- 8、故障转移:任务路由策略选择"故障转移"情况下,如果执行器集群中某一台机器故障,将会自动Failover切换到一台正常的执行器发送调度请求。
+- 9、失败处理策略;调度失败时的处理策略,策略包括:失败告警(默认)、失败重试;
+- 10、失败重试:调度中心调度失败且启用"失败重试"策略时,将会自动重试一次;执行器执行失败且回调失败重试状态时,也将会自动重试一次;
+- 11、阻塞处理策略:调度过于密集执行器来不及处理时的处理策略,策略包括:单机串行(默认)、丢弃后续调度、覆盖之前调度;
+- 12、分片广播任务:执行器集群部署时,任务路由策略选择"分片广播"情况下,一次任务调度将会广播触发集群中所有执行器执行一次任务,可根据分片参数开发分片任务;
+- 13、动态分片:分片广播任务以执行器为维度进行分片,支持动态扩容执行器集群从而动态增加分片数量,协同进行业务处理;在进行大数据量业务操作时可显著提升任务处理能力和速度。
+- 14、事件触发:除了"Cron方式"和"任务依赖方式"触发任务执行之外,支持基于事件的触发任务方式。调度中心提供触发任务单次执行的API服务,可根据业务事件灵活触发。
+- 15、任务进度监控:支持实时监控任务进度;
+- 16、Rolling实时日志:支持在线查看调度结果,并且支持以Rolling方式实时查看执行器输出的完整的执行日志;
+- 17、GLUE:提供Web IDE,支持在线开发任务逻辑代码,动态发布,实时编译生效,省略部署上线的过程。支持30个版本的历史版本回溯。
+- 18、脚本任务:支持以GLUE模式开发和运行脚本任务,包括Shell、Python、NodeJS等类型脚本;
+- 19、任务依赖:支持配置子任务依赖,当父任务执行结束且执行成功后将会主动触发一次子任务的执行, 多个子任务用逗号分隔;
+- 20、一致性:“调度中心”通过DB锁保证集群分布式调度的一致性, 一次任务调度只会触发一次执行;
+- 21、自定义任务参数:支持在线配置调度任务入参,即时生效;
+- 22、调度线程池:调度系统多线程触发调度运行,确保调度精确执行,不被堵塞;
+- 23、数据加密:调度中心和执行器之间的通讯进行数据加密,提升调度信息安全性;
+- 24、邮件报警:任务失败时支持邮件报警,支持配置多邮件地址群发报警邮件;
+- 25、推送maven中央仓库: 将会把最新稳定版推送到maven中央仓库, 方便用户接入和使用;
+- 26、运行报表:支持实时查看运行数据,如任务数量、调度次数、执行器数量等;以及调度报表,如调度日期分布图,调度成功分布图等;
 
 
 ## Development
@@ -83,8 +84,10 @@ XXL-JOB是一个轻量级分布式任务调度框架,其核心设计目标是
 
 于2017-05-13,在上海举办的 "[第62期开源中国源创会](https://www.oschina.net/event/2236961)" 的 "放码过来" 环节,我登台对XXL-JOB做了演讲,台下五百位在场观众反响热烈([图文回顾](https://www.oschina.net/question/2686220_2242120) )。
 
+于2017-12-11,XXL-JOB有幸参会《[InfoQ ArchSummit全球架构师峰会](http://bj2017.archsummit.com/)》,并被拍拍贷架构总监"杨波老师"在专题 "[微服务原理、基础架构和开源实践](http://bj2017.archsummit.com/training/2)" 中现场介绍。
+
 > 我司大众点评目前已接入XXL-JOB,内部别名《Ferrari》(Ferrari基于XXL-JOB的V1.1版本定制而成,新接入应用推荐升级最新版本)。**
-据最新统计, 自2016-01-21接入至2017-07-07期间,该系统已调度约60万余次,表现优异。新接入应用推荐使用最新版本,因为经过数个大版本的更新,系统的任务模型、UI交互模型以及底层调度通讯模型都有了较大的优化和提升,核心功能更加稳定高效。
+据最新统计, 自2016-01-21接入至2017-12-01期间,该系统已调度约100万次,表现优异。新接入应用推荐使用最新版本,因为经过数个大版本的更新,系统的任务模型、UI交互模型以及底层调度通讯模型都有了较大的优化和提升,核心功能更加稳定高效。
 
 至今,XXL-JOB已接入多家公司的线上产品线,接入场景如电商业务,O2O业务和大数据作业等,截止2016-07-19为止,XXL-JOB已接入的公司包括不限于:
     
@@ -145,6 +148,8 @@ XXL-JOB是一个轻量级分布式任务调度框架,其核心设计目标是
     - 55、中商惠民(北京)电子商务有限公司
     - 56、凯京集团
     - 57、华夏票联(北京)科技有限公司
+    - 58、拍拍贷
+    - 59、北京尚德机构在线教育有限公司
 	- ……
 
 > 更多接入的公司,欢迎在 [登记地址](https://github.com/xuxueli/xxl-job/issues/1 ) 登记,登记仅仅为了产品推广。

+ 116 - 79
doc/XXL-JOB官方文档.md

@@ -16,29 +16,30 @@ XXL-JOB是一个轻量级分布式任务调度框架,其核心设计目标是
 ### 1.2 特性
 - 1、简单:支持通过Web页面对任务进行CRUD操作,操作简单,一分钟上手;
 - 2、动态:支持动态修改任务状态、暂停/恢复任务,以及终止运行中任务,即时生效;
-- 3、调度中心HA(中心式):调度采用中心式设计,“调度中心”基于集群Quartz实现,可保证调度中心HA;
+- 3、调度中心HA(中心式):调度采用中心式设计,“调度中心”基于集群Quartz实现并支持集群部署,可保证调度中心HA;
 - 4、执行器HA(分布式):任务分布式执行,任务"执行器"支持集群部署,可保证任务执行HA;
-- 5、任务Failover:执行器集群部署时,任务路由策略选择"故障转移"情况下调度失败时将会平滑切换执行器进行Failover;
-- 6、一致性:“调度中心”通过DB锁保证集群分布式调度的一致性, 一次任务调度只会触发一次执行;
-- 7、自定义任务参数:支持在线配置调度任务入参,即时生效;
-- 8、调度线程池:调度系统多线程触发调度运行,确保调度精确执行,不被堵塞;
-- 9、弹性扩容缩容:一旦有新执行器机器上线或者下线,下次调度时将会重新分配任务;
-- 10、邮件报警:任务失败时支持邮件报警,支持配置多邮件地址群发报警邮件;
-- 11、状态监控:支持实时监控任务进度;
-- 12、Rolling执行日志:支持在线查看调度结果,并且支持以Rolling方式实时查看执行器输出的完整的执行日志;
-- 13、GLUE:提供Web IDE,支持在线开发任务逻辑代码,动态发布,实时编译生效,省略部署上线的过程。支持30个版本的历史版本回溯。
-- 14、数据加密:调度中心和执行器之间的通讯进行数据加密,提升调度信息安全性;
-- 15、任务依赖:支持配置子任务依赖,当父任务执行结束且执行成功后将会主动触发一次子任务的执行, 多个子任务用逗号分隔;
-- 16、推送maven中央仓库: 将会把最新稳定版推送到maven中央仓库, 方便用户接入和使用;
-- 17、任务注册: 执行器会周期性自动注册任务, 调度中心将会自动发现注册的任务并触发执行。同时,也支持手动录入执行器地址;
-- 18、路由策略:执行器集群部署时提供丰富的路由策略,包括:第一个、最后一个、轮询、随机、一致性HASH、最不经常使用、最近最久未使用、故障转移、忙碌转移等;
-- 19、运行报表:支持实时查看运行数据,如任务数量、调度次数、执行器数量等;以及调度报表,如调度日期分布图,调度成功分布图等;
-- 20、脚本任务:支持以GLUE模式开发和运行脚本任务,包括Shell、Python等类型脚本;
-- 21、阻塞处理策略:调度过于密集执行器来不及处理时的处理策略,策略包括:单机串行(默认)、丢弃后续调度、覆盖之前调度;
-- 22、失败处理策略;调度失败时的处理策略,策略包括:失败告警(默认)、失败重试;
-- 23、分片广播任务:执行器集群部署时,任务路由策略选择"分片广播"情况下,一次任务调度将会广播触发集群中所有执行器执行一次任务,可根据分片参数开发分片任务;
-- 24、动态分片:分片广播任务以执行器为维度进行分片,支持动态扩容执行器集群从而动态增加分片数量,协同进行业务处理;在进行大数据量业务操作时可显著提升任务处理能力和速度。
-- 25、事件触发:除了"Cron方式"和"任务依赖方式"触发任务执行之外,支持基于事件的触发任务方式。调度中心提供触发任务单次执行的API服务,可根据业务事件灵活触发。
+- 5、注册中心: 执行器会周期性自动注册任务, 调度中心将会自动发现注册的任务并触发执行。同时,也支持手动录入执行器地址;
+- 6、弹性扩容缩容:一旦有新执行器机器上线或者下线,下次调度时将会重新分配任务;
+- 7、路由策略:执行器集群部署时提供丰富的路由策略,包括:第一个、最后一个、轮询、随机、一致性HASH、最不经常使用、最近最久未使用、故障转移、忙碌转移等;
+- 8、故障转移:任务路由策略选择"故障转移"情况下,如果执行器集群中某一台机器故障,将会自动Failover切换到一台正常的执行器发送调度请求。
+- 9、失败处理策略;调度失败时的处理策略,策略包括:失败告警(默认)、失败重试;
+- 10、失败重试:调度中心调度失败且启用"失败重试"策略时,将会自动重试一次;执行器执行失败且回调失败重试状态时,也将会自动重试一次;
+- 11、阻塞处理策略:调度过于密集执行器来不及处理时的处理策略,策略包括:单机串行(默认)、丢弃后续调度、覆盖之前调度;
+- 12、分片广播任务:执行器集群部署时,任务路由策略选择"分片广播"情况下,一次任务调度将会广播触发集群中所有执行器执行一次任务,可根据分片参数开发分片任务;
+- 13、动态分片:分片广播任务以执行器为维度进行分片,支持动态扩容执行器集群从而动态增加分片数量,协同进行业务处理;在进行大数据量业务操作时可显著提升任务处理能力和速度。
+- 14、事件触发:除了"Cron方式"和"任务依赖方式"触发任务执行之外,支持基于事件的触发任务方式。调度中心提供触发任务单次执行的API服务,可根据业务事件灵活触发。
+- 15、任务进度监控:支持实时监控任务进度;
+- 16、Rolling实时日志:支持在线查看调度结果,并且支持以Rolling方式实时查看执行器输出的完整的执行日志;
+- 17、GLUE:提供Web IDE,支持在线开发任务逻辑代码,动态发布,实时编译生效,省略部署上线的过程。支持30个版本的历史版本回溯。
+- 18、脚本任务:支持以GLUE模式开发和运行脚本任务,包括Shell、Python、NodeJS等类型脚本;
+- 19、任务依赖:支持配置子任务依赖,当父任务执行结束且执行成功后将会主动触发一次子任务的执行, 多个子任务用逗号分隔;
+- 20、一致性:“调度中心”通过DB锁保证集群分布式调度的一致性, 一次任务调度只会触发一次执行;
+- 21、自定义任务参数:支持在线配置调度任务入参,即时生效;
+- 22、调度线程池:调度系统多线程触发调度运行,确保调度精确执行,不被堵塞;
+- 23、数据加密:调度中心和执行器之间的通讯进行数据加密,提升调度信息安全性;
+- 24、邮件报警:任务失败时支持邮件报警,支持配置多邮件地址群发报警邮件;
+- 25、推送maven中央仓库: 将会把最新稳定版推送到maven中央仓库, 方便用户接入和使用;
+- 26、运行报表:支持实时查看运行数据,如任务数量、调度次数、执行器数量等;以及调度报表,如调度日期分布图,调度成功分布图等;
 
 ### 1.3 发展
 于2015年中,我在github上创建XXL-JOB项目仓库并提交第一个commit,随之进行系统结构设计,UI选型,交互设计……
@@ -51,8 +52,10 @@ XXL-JOB是一个轻量级分布式任务调度框架,其核心设计目标是
 
 于2017-05-13,在上海举办的 "[第62期开源中国源创会](https://www.oschina.net/event/2236961)" 的 "放码过来" 环节,我登台对XXL-JOB做了演讲,台下五百位在场观众反响热烈([图文回顾](https://www.oschina.net/question/2686220_2242120) )。
 
+于2017-12-11,XXL-JOB有幸参会《[InfoQ ArchSummit全球架构师峰会](http://bj2017.archsummit.com/)》,并被拍拍贷架构总监"杨波老师"在专题 "[微服务原理、基础架构和开源实践](http://bj2017.archsummit.com/training/2)" 中现场介绍。
+
 > 我司大众点评目前已接入XXL-JOB,内部别名《Ferrari》(Ferrari基于XXL-JOB的V1.1版本定制而成,新接入应用推荐升级最新版本)。
-据最新统计, 自2016-01-21接入至2017-07-07期间,该系统已调度约60万余次,表现优异。新接入应用推荐使用最新版本,因为经过数个大版本的更新,系统的任务模型、UI交互模型以及底层调度通讯模型都有了较大的优化和提升,核心功能更加稳定高效。
+据最新统计, 自2016-01-21接入至2017-12-01期间,该系统已调度约100万次,表现优异。新接入应用推荐使用最新版本,因为经过数个大版本的更新,系统的任务模型、UI交互模型以及底层调度通讯模型都有了较大的优化和提升,核心功能更加稳定高效。
 
 至今,XXL-JOB已接入多家公司的线上产品线,接入场景如电商业务,O2O业务和大数据作业等,截止2016-07-19为止,XXL-JOB已接入的公司包括不限于:
     
@@ -113,6 +116,8 @@ XXL-JOB是一个轻量级分布式任务调度框架,其核心设计目标是
     - 55、中商惠民(北京)电子商务有限公司
     - 56、凯京集团
     - 57、华夏票联(北京)科技有限公司
+    - 58、拍拍贷
+    - 59、北京尚德机构在线教育有限公司
 	- ……
 
 > 更多接入的公司,欢迎在 [登记地址](https://github.com/xuxueli/xxl-job/issues/1 ) 登记,登记仅仅为了产品推广。
@@ -181,6 +186,7 @@ XXL-JOB是一个轻量级分布式任务调度框架,其核心设计目标是
         :xxl-job-executor-sample-spring:Spring版本,通过Spring容器管理执行器,比较通用,推荐这种方式;
         :xxl-job-executor-sample-springboot:Springboot版本,通过Springboot管理执行器;
         :xxl-job-executor-sample-jfinal:JFinal版本,通过JFinal管理执行器;
+        :xxl-job-executor-sample-nutz:Nutz版本,通过Nutz管理执行器;
         
 
 ### 2.3 配置部署“调度中心”
@@ -226,15 +232,17 @@ XXL-JOB是一个轻量级分布式任务调度框架,其核心设计目标是
 至此“调度中心”项目已经部署成功。
 
 #### 步骤三:调度中心集群(可选):
-调度中心支持集群部署,提升调度系统可用性。
-
-集群部署唯一要求为:保证每个集群节点配置(db和登陆账号等)保持一致。调度中心通过db配置区分不同集群。
+调度中心支持集群部署,提升调度系统容灾和可用性。
 
-调度中心在集群部署时可通过nginx负载均衡,此时可以为集群分配一个域名。该域名一方面可以用于访问,另一方面也可以用于配置执行器回调地址。
+调度中心集群部署时,几点要求和建议:
+- DB配置保持一致;
+- 登陆账号配置保持一致;
+- 集群机器时钟保持一致(单机集群忽视);
+- 建议:推荐通过nginx为调度中心集群做负载均衡,分配域名。调度中心访问、执行器回调配置、调用API服务等操作均通过该域名进行。
 
 ### 2.4 配置部署“执行器项目”
 
-    “执行器”项目:xxl-job-executor-sample-spring (如新建执行器项目,可参考该Sample示例执行器项目的配置步骤;)
+    “执行器”项目:xxl-job-executor-sample-spring (提供多种版本执行器供选择,现以Spring版本为例,可直接使用,也可以参考其并将现有项目改造成执行器)
     作用:负责接收“调度中心”的调度并执行;可直接部署执行器,也可以将执行器集成到现有业务项目中。
     
 #### 步骤一:maven依赖
@@ -278,15 +286,15 @@ XXL-JOB是一个轻量级分布式任务调度框架,其核心设计目标是
 <bean id="xxlJobExecutor" class="com.xxl.job.core.executor.XxlJobExecutor" init-method="start" destroy-method="destroy" >
     <!-- 执行器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}" />
 </bean>
 ```
@@ -316,7 +324,8 @@ XXL-JOB是一个轻量级分布式任务调度框架,其核心设计目标是
 
 ![输入图片说明](https://static.oschina.net/uploads/img/201704/27205910_o8HQ.png "在这里输入图片标题")
 
-![输入图片说明](https://static.oschina.net/uploads/img/201704/27210202_SE2u.png "在这里输入图片标题")
+![输入图片说明](https://static.oschina.net/uploads/img/201712/25183654_ZAsz.png "在这里输入图片标题")
+
 
 #### 步骤二:“GLUE模式(Java)” 任务开发:
 请点击任务右侧 “GLUE” 按钮,进入 “GLUE编辑器开发界面” ,见下图。“GLUE模式(Java)” 运行模式的任务默认已经初始化了示例任务代码,即打印Hello World。
@@ -365,14 +374,14 @@ XXL-JOB是一个轻量级分布式任务调度框架,其核心设计目标是
         GLUE模式(Python):任务以源码方式维护在调度中心;该模式的任务实际上是一段 "python" 脚本;
         GLUE模式(NodeJS):任务以源码方式维护在调度中心;该模式的任务实际上是一段 "nodejs" 脚本;
     - JobHandler:运行模式为 "BEAN模式" 时生效,对应执行器中新开发的JobHandler类“@JobHandler”注解自定义的value值;
-    - 子任务Key:每个任务都拥有一个唯一的任务Key(任务Key可以从任务列表获取),当本任务执行结束并且执行成功时,将会触发子任务Key所对应的任务的一次主动调度。
+    - 子任务:每个任务都拥有一个唯一的任务ID(任务ID可以从任务列表获取),当本任务执行结束并且执行成功时,将会触发子任务ID所对应的任务的一次主动调度。
     - 阻塞处理策略:调度过于密集执行器来不及处理时的处理策略;
         单机串行(默认):调度请求进入单机执行器后,调度请求进入FIFO队列并以串行方式运行;
         丢弃后续调度:调度请求进入单机执行器后,发现执行器存在运行的调度任务,本次请求将会被丢弃并标记为失败;
         覆盖之前调度:调度请求进入单机执行器后,发现执行器存在运行的调度任务,将会终止运行中的调度任务并清空队列,然后运行本地调度任务;
     - 失败处理策略;调度失败时的处理策略;
-        失败告警(默认):调度失败时,将会触发失败报警,如发送报警邮件;
-        失败重试:调度失败时,将会主动进行一次失败重试调度,重试调度后仍然失败将会触发一失败告警。注意当任务以failover方式路由时,每次失败重试将会触发新一轮路由。
+        失败告警(默认):调度失败和执行失败时,都将会触发失败报警,默认会发送报警邮件;
+        失败重试:调度失败时,除了进行失败告警之外,将会自动重试一次;注意在执行失败时不会重试,而是根据回调返回值判断是否重试;
     - 执行参数:任务执行所需的参数,多个参数时用逗号分隔,任务执行时将会把多个参数转换成数组传入;
     - 报警邮件:任务调度失败时邮件通知的邮箱地址,支持配置多邮箱地址,配置多个邮箱地址时用逗号分隔;
     - 负责人:任务的负责人;
@@ -381,9 +390,11 @@ XXL-JOB是一个轻量级分布式任务调度框架,其核心设计目标是
 任务逻辑以JobHandler的形式存在于“执行器”所在项目中,开发流程如下:
 
 #### 步骤一:执行器项目中,开发JobHandler:
-    - 1、 新建一个继承com.xxl.job.core.handler.IJobHandler的Java类;
-    - 2、 该类被Spring容器扫描为Bean实例,如加“@Component”注解;
-    - 3、 添加 “@JobHandler(value="自定义jobhandler名称")”注解,注解的value值为自定义的JobHandler名称,该名称对应的是调度中心新建任务的JobHandler属性的值。
+
+     - 1、继承"IJobHandler":“com.xxl.job.core.handler.IJobHandler”;
+     - 2、注册到Spring容器:添加“@Component”注解,被Spring容器扫描为Bean实例;
+     - 3、注册到执行器工厂:添加“@JobHandler(value="自定义jobhandler名称")”注解,注解value值对应的是调度中心新建任务的JobHandler属性的值。
+     - 4、执行日志:需要通过 "XxlJobLogger.log" 打印执行日志;
     (可参考Sample示例执行器中的DemoJobHandler,见下图)
 
 ![输入图片说明](https://static.oschina.net/uploads/img/201607/23232347_oLlM.png "在这里输入图片标题")
@@ -391,7 +402,7 @@ XXL-JOB是一个轻量级分布式任务调度框架,其核心设计目标是
 #### 步骤二:调度中心,新建调度任务
 参考上文“配置属性详细说明”对新建的任务进行参数配置,运行模式选中 "BEAN模式",JobHandler属性填写任务注解“@JobHandler”中定义的值;
 
-![输入图片说明](https://static.oschina.net/uploads/img/201704/27225124_yrcO.png "在这里输入图片标题")
+![输入图片说明](https://static.oschina.net/uploads/img/201712/25183654_ZAsz.png "在这里输入图片标题")
 
 ### 3.2 GLUE模式(Java)
 任务以源码方式维护在调度中心,支持通过Web IDE在线更新,实时编译和生效,因此不需要指定JobHandler。开发流程如下:
@@ -399,7 +410,7 @@ XXL-JOB是一个轻量级分布式任务调度框架,其核心设计目标是
 #### 步骤一:调度中心,新建调度任务:
 参考上文“配置属性详细说明”对新建的任务进行参数配置,运行模式选中 "GLUE模式(Java)";
 
-![输入图片说明](https://static.oschina.net/uploads/img/201704/27210202_SE2u.png "在这里输入图片标题")
+![输入图片说明](https://static.oschina.net/uploads/img/201712/25183837_tJOq.png "在这里输入图片标题")
 
 #### 步骤二:开发任务代码:
 选中指定任务,点击该任务右侧“GLUE”按钮,将会前往GLUE任务的Web IDE界面,在该界面支持对任务代码进行开发(也可以在IDE中开发完成后,复制粘贴到编辑中)。
@@ -453,7 +464,8 @@ XXL-JOB是一个轻量级分布式任务调度框架,其核心设计目标是
     2、"执行器列表" 中显示在线的执行器列表, 可通过"OnLine 机器"查看对应执行器的集群机器。
 
 点击按钮 "+新增执行器" 弹框如下图, 可新增执行器配置:
-![输入图片说明](https://static.oschina.net/uploads/img/201703/12223617_g3Im.png "在这里输入图片标题")
+
+![输入图片说明](https://static.oschina.net/uploads/img/201712/25183958_V3vF.png "在这里输入图片标题")
 
 ### 执行器属性说明
 
@@ -493,7 +505,7 @@ XXL-JOB是一个轻量级分布式任务调度框架,其核心设计目标是
 
 ![输入图片说明](https://static.oschina.net/uploads/img/201607/24133500_9235.png "在这里输入图片标题")
 
-![输入图片说明](https://static.oschina.net/uploads/img/201704/27232850_inc8.png "在这里输入图片标题")
+![输入图片说明](https://static.oschina.net/uploads/img/201712/25184206_UDSo.png "在这里输入图片标题")
 
     调度时间:"调度中心"触发本次调度并向"执行器"发送任务执行信号的时间;
     调度结果:"调度中心"触发本次调度的结果,200表示成功,500或其他表示失败;
@@ -677,7 +689,6 @@ xxl-job-admin#com.xxl.job.admin.controller.JobApiController.callback
 执行器如若集群部署,调度中心将会感知到在线的所有执行器,如“127.0.0.1:9997, 127.0.0.1:9998, 127.0.0.1:9999”。
 
 当任务"路由策略"选择"故障转移(FAILOVER)"时,当调度中心每次发起调度请求时,会按照顺序对执行器发出心跳检测请求,第一个检测为存活状态的执行器将会被选定并发送调度请求。
-![输入图片说明](https://static.oschina.net/uploads/img/201705/11221144_P128.png "在这里输入图片标题")
 
 调度成功后,可在日志监控界面查看“调度备注”,如下;
 ![输入图片说明](https://static.oschina.net/uploads/img/201703/12230733_jrdI.png "在这里输入图片标题")
@@ -688,12 +699,10 @@ xxl-job-admin#com.xxl.job.admin.controller.JobApiController.callback
 #### 5.4.9 调度日志
 调度中心每次进行任务调度,都会记录一条任务日志,任务日志主要包括以下三部分内容:
 
-- 任务信息:包括“执行器地址”、“JobHandler”和“执行参数”等属性,根据这些参数,可以精确的定位任务执行的具体机器和任务代码;
+- 任务信息:包括“执行器地址”、“JobHandler”和“执行参数”等属性,点击任务ID按钮可查看,根据这些参数,可以精确的定位任务执行的具体机器和任务代码;
 - 调度信息:包括“调度时间”、“调度结果”和“调度日志”等,根据这些参数,可以了解“调度中心”发起调度请求时具体情况。
 - 执行信息:包括“执行时间”、“执行结果”和“执行日志”等,根据这些参数,可以了解在“执行器”端任务执行的具体情况;
 
-![输入图片说明](https://static.oschina.net/uploads/img/201703/12221436_c8Ru.png "在这里输入图片标题")
-
 调度日志,针对单次调度,属性说明如下:
 - 执行器地址:任务执行的机器地址;
 - JobHandler:Bean模式表示任务执行的JobHandler名称;
@@ -707,9 +716,9 @@ xxl-job-admin#com.xxl.job.admin.controller.JobApiController.callback
 - 执行日志:任务执行过程中,业务代码中打印的完整执行日志,见“4.7 查看执行日志”;
 
 #### 5.4.10 任务依赖
-原理:XXL-JOB中每个任务都对应有一个任务Key,同时,每个任务支持设置属性“子任务Key”,因此,通过“任务Key”可以匹配任务依赖关系。
+原理:XXL-JOB中每个任务都对应有一个任务ID,同时,每个任务支持设置属性“子任务ID”,因此,通过“任务ID”可以匹配任务依赖关系。
 
-当父任务执行结束并且执行成功时,将会根据“子任务Key”匹配子任务依赖,如果匹配到子任务,将会主动触发一次子任务的执行。
+当父任务执行结束并且执行成功时,将会根据“子任务ID”匹配子任务依赖,如果匹配到子任务,将会主动触发一次子任务的执行。
 
 在任务日志界面,点击任务的“执行备注”的“查看”按钮,可以看到匹配子任务以及触发子任务执行的日志信息,如无信息则表示未触发子任务执行,可参考下图。
 
@@ -738,9 +747,7 @@ xxl-job-admin#com.xxl.job.admin.controller.JobApiController.callback
     
 
 #### 5.5.4 执行器
-执行器实际上是一个内嵌的Jetty服务器,默认端口9999,如下图配置文件所示(参数:xxl.job.executor.port)。
-
-![输入图片说明](https://static.oschina.net/uploads/img/201703/10174923_TgNO.png "在这里输入图片标题")
+执行器实际上是一个内嵌的Jetty服务器,默认端口9999(配置项:xxl.job.executor.port)。
 
 在项目启动时,执行器会通过“@JobHandler”识别Spring容器中“Bean模式任务”,以注解的value属性为key管理起来。
 
@@ -786,11 +793,21 @@ XXL-JOB会为每次调度请求生成一个单独的日志文件,需要通过
 
 "分片广播" 以执行器为维度进行分片,支持动态扩容执行器集群从而动态增加分片数量,协同进行业务处理;在进行大数据量业务操作时可显著提升任务处理能力和速度。
 
-"分片广播" 和普通任务开发流程一致,不同之处在于可以可以获取分片参数,获取分片参数对象的代码如下(可参考Sample示例执行器中的示例任务"ShardingJobHandler" ):
+"分片广播" 和普通任务开发流程一致,不同之处在于可以可以获取分片参数,获取分片参数进行分片业务处理。
 
-    ShardingUtil.ShardingVO shardingVO = ShardingUtil.getShardingVo();
+- Java语言任务获取分片参数方式:BEAN、GLUE模式(Java)
+```
+// 可参考Sample示例执行器中的示例任务"ShardingJobHandler"了解试用 
+ShardingUtil.ShardingVO shardingVO = ShardingUtil.getShardingVo();
+```
+- 脚本语言任务获取分片参数方式:GLUE模式(Shell)、GLUE模式(Python)、GLUE模式(Nodejs)
+```
+// 脚本任务入参固定为三个,依次为:任务传参、分片序号、分片总数。以Shell模式任务为例,获取分片参数代码如下
+echo "分片序号 index = $2"
+echo "分片总数 total = $3"
+```  
     
-该分片参数对象拥有两个属性:
+分片参数属性说明
 
     index:当前分片序号(从0开始),执行器集群列表中当前执行器的序号;
     total:总分片数,执行器集群的总机器数量;
@@ -819,7 +836,7 @@ XXL-JOB会为每次调度请求生成一个单独的日志文件,需要通过
     
 调度中心API服务位置:com.xxl.job.core.biz.AdminBiz.java
 
-调度中心API服务请求参考代码:com.xxl.job.dao.impl.AdminBizTest.java
+调度中心API服务请求参考代码:com.xxl.job.adminbiz.AdminBizTest.java
 
 ### 5.12 执行器API服务
 执行器提供了API服务,供调度中心选择使用,目前提供的API服务有:
@@ -834,6 +851,15 @@ XXL-JOB会为每次调度请求生成一个单独的日志文件,需要通过
 
 执行器API服务请求参考代码:com.xxl.executor.test.DemoJobHandlerTest
 
+### 5.13 故障转移 & 失败重试
+一次完整任务流程包括"调度(调度中心) + 执行(执行器)"两个阶段。
+    
+- "故障转移"发生在调度阶段,在执行器集群部署时,如果某一台执行器发生故障,该策略支持自动进行Failover切换到一台正常的执行器机器并且完成调度请求流程。
+- "失败重试"发生在"调度 + 执行"两个阶段,如下:
+    - 调度中心调度失败时,任务失败处理策略选择"失败重试",将会自动重试一次;
+    - 执行器运行失败时,任务执行结果返回"失败重试(IJobHandler.FAIL_RETRY)"回调,将会自动重试一次;
+
+
 ## 六、版本更新日志
 ### 6.1 版本 V1.1.x,新特性[2015-12-05]
 **【于V1.1.x版本,XXL-JOB正式应用于我司,内部定制别名为 “Ferrari”,新接入应用推荐使用最新版本】**
@@ -1049,40 +1075,51 @@ Tips: 历史版本(V1.3.x)目前已经Release至稳定版本, 进入维护阶段
 - 10、任务日志文件路径时间戳格式化时SimpleDateFormat并发问题解决;
 
 ### 6.20 版本 V1.9.0 特性[迭代中]
-- 1、新增任务运行模式 "GLUE模式(NodeJS) ",支持NodeJS脚本任务;
-- 2、失败告警策略扩展:默认提供邮件失败告警,可扩展短信等,扩展代码位置为 "JobFailMonitorHelper.failAlarm";
-- 3、修复任务监控线程被耗时任务阻塞的问题;
-- 4、修复任务监控线程无法监控任务触发和执行状态均未0的问题;
-- 5、调度中心项目日志配置改为xml文件格式;
-- 6、执行器动态代理对象,拦截非业务方法的执行;
-- 7、底层系统日志级别规范调整,清理遗留代码;
-- 8、修改JobThread捕获Error错误不更新JobLog的问题; 
-- 9、任务注解调整为 “@JobHandler”,与任务注解统一;
-- 10、执行器端口支持随机生成(小于等于0时),避免端口定义冲突;
-- 11、任务Cron长度扩展支持至128位;
-- 12、调度报表优化,支持时间区间筛选;
+- 1、新增Nutz执行器Sample示例项目;
+- 2、新增任务运行模式 "GLUE模式(NodeJS) ",支持NodeJS脚本任务;
+- 3、脚本任务Shell、Python和Nodejs等支持获取分片参数;
+- 4、失败重试,完整支持:调度中心调度失败且启用"失败重试"策略时,将会自动重试一次;执行器执行失败且回调失败重试状态(新增失败重试状态返回值)时,也将会自动重试一次;
+- 5、失败告警策略扩展:默认提供邮件失败告警,可扩展短信等,扩展代码位置为 "JobFailMonitorHelper.failAlarm";
+- 6、执行器端口支持自动生成(小于等于0时),避免端口定义冲突;
+- 7、调度报表优化,支持时间区间筛选;
+- 8、Log组件支持输出异常栈信息,底层实现优化;
+- 9、告警邮件样式优化,调整为表格形式,邮件组件调整为commons-email简化邮件操作;
+- 10、项目依赖升级,如spring、jackson等;
+- 11、任务日志,记录发起调度的机器信息;
+- 12、交互优化,如登陆注销;
+- 13、任务Cron长度扩展支持至128位,支持负责类型Cron设置;
+- 14、执行器地址录入交互优化,地址长度扩展支持至512位,支持大规模执行器集群配置;
+- 15、任务参数“IJobHandler.execute”入参改为“String params”,增强入参通用性。
+- 16、JobHandler提供init/destroy方法,支持在JobHandler初始化和销毁时进行附加操作;
+- 17、任务注解调整为 “@JobHandler”,与任务抽象接口统一;
+- 18、修复任务监控线程被耗时任务阻塞的问题;
+- 19、修复任务监控线程无法监控任务触发和执行状态均未0的问题;
+- 20、执行器动态代理对象,拦截非业务方法的执行;
+- 21、修复JobThread捕获Error错误不更新JobLog的问题;
+- 22、修复任务列表界面左侧菜单合并时样式错乱问题;
+- 23、调度中心项目日志配置改为xml文件格式;
+- 24、Log地址格式兼容,支持非"/"结尾路径配置;
+- 25、底层系统日志级别规范调整,清理遗留代码;
+- 26、建表SQL优化,支持同步创建制定编码的库和表;
+- 27、系统安全性优化,登陆Token写Cookie时进行MD5加密,同时Cookie启用HttpOnly;
+- 28、新增"任务ID"属性,移除"JobKey"属性,前者承担所有功能,方便后续增强任务依赖功能。
+- 29、任务循环依赖问题修复,避免子任务与父任务重复导致的调度死循环;
 
 ### TODO LIST
 - 1、任务权限管理:执行器为粒度分配权限,核心操作校验权限;
 - 2、任务分片路由:分片采用一致性Hash算法计算出尽量稳定的分片顺序,即使注册机器存在波动也不会引起分批分片顺序大的波动;目前采用IP自然排序,可以满足需求,待定;
-- 3、失败重试完整支持:任务流程分为触发和执行,目前仅支持触发失败的重试;后续支持任务执行失败的重试,通过任务返回值判断。
+- 3、任务单机多线程:提升任务单机并行处理能力;
 - 4、回调失败丢包问题:执行器回调失败写文件,重启或周期性回调重试;调度中心周期性请求并同步未回调的执行结果;
 - 5、任务依赖,流程图,子任务+会签任务,各节点日志;
 - 6、调度任务优先级;
 - 7、移除quartz依赖,重写调度模块:新增或恢复任务时将下次执行记录插入delayqueue,调度中心集群竞争分布式锁,成功节点批量加载到期delayqueue数据,批量执行。
 - 8、springboot 和 docker镜像,并且推送docker镜像到中央仓库,更进一步实现产品开箱即用;
 - 9、国际化:调度中心界面。
-- 10、任务类方法"IJobHandler.execute"的参数类型改为"string",进一步方便参数传递;任务注解和任务类统一并改为"JobHandler"";
-- 11、任务日志,记录发起调度的机器信息;
-- 12、任务告警逻辑调整:任务调度,以及任务回调失败时,均推送监控队列。后期考虑通过任务Log字段控制告警状态;
-- 13、执行器Log清理功能:调度中心Log删除时同步删除执行器中的Log文件;
-- 14、脚本任务 Shell、Python和Nodejs,如何友好获取分片参数;
-- 15、Bean模式任务,JobHandler自动从执行器中查询展示为下拉框,选择后自动填充任务名称等属性;
-- 16、任务告警邮件优化,调整为表格形式;
-- 17、JobHandler提供 init/destroy 方法,支持自定义任务线程销毁逻辑;
-- 18、cron表达式的最大长度调整,兼容复杂类型cron;
-- 19、执行器回调地址/日志地址格式兼容,是否已"/"结尾均支持;
-- 20、任务单机多线程:提升任务单机并行处理能力;
+- 10、任务告警逻辑调整:任务调度,以及任务回调失败时,均推送监控队列。后期考虑通过任务Log字段控制告警状态;
+- 11、执行器Log清理功能:调度中心Log删除时同步删除执行器中的Log文件;
+- 12、Bean模式任务,JobHandler自动从执行器中查询展示为下拉框,选择后自动填充任务名称等属性;
+- 13、API事件触发类型任务(更类似MQ消息)支持"动态传参、延时消费";该类型任务不走Quartz,单独建立MQ消息表,调度中心竞争触发;
+- 14、任务依赖增强,新增任务类型 "流程任务",流程节点可挂载普通类型任务,承担任务依赖功能。现有子任务模型取消;需要考虑任务依赖死循环问题;
 
 
 ## 七、其他

+ 6 - 4
doc/db/tables_xxl_job.sql

@@ -1,6 +1,8 @@
 CREATE database if NOT EXISTS `xxl-job` default character set utf8 collate utf8_general_ci;
 use `xxl-job`;
 
+
+
 CREATE TABLE XXL_JOB_QRTZ_JOB_DETAILS
   (
     SCHED_NAME VARCHAR(120) NOT NULL,
@@ -158,14 +160,14 @@ CREATE TABLE `XXL_JOB_QRTZ_TRIGGER_INFO` (
   `alarm_email` varchar(255) DEFAULT NULL COMMENT '报警邮件',
   `executor_route_strategy` varchar(50) DEFAULT NULL COMMENT '执行器路由策略',
   `executor_handler` varchar(255) DEFAULT NULL COMMENT '执行器任务handler',
-  `executor_param` varchar(255) DEFAULT NULL COMMENT '执行器任务参数',
+  `executor_param` varchar(512) DEFAULT NULL COMMENT '执行器任务参数',
   `executor_block_strategy` varchar(50) DEFAULT NULL COMMENT '阻塞处理策略',
   `executor_fail_strategy` varchar(50) DEFAULT NULL COMMENT '失败处理策略',
   `glue_type` varchar(50) NOT NULL COMMENT 'GLUE类型',
   `glue_source` text COMMENT 'GLUE源代码',
   `glue_remark` varchar(128) DEFAULT NULL COMMENT 'GLUE备注',
   `glue_updatetime` datetime DEFAULT NULL COMMENT 'GLUE更新时间',
-  `child_jobkey` varchar(255) DEFAULT NULL COMMENT '子任务Key',
+  `child_jobid` varchar(255) DEFAULT NULL COMMENT '子任务ID,多个逗号分隔',
   PRIMARY KEY (`id`)
 ) ENGINE=InnoDB DEFAULT CHARSET=utf8;
 
@@ -176,7 +178,7 @@ CREATE TABLE `XXL_JOB_QRTZ_TRIGGER_LOG` (
   `glue_type` varchar(50) DEFAULT NULL COMMENT 'GLUE类型',
   `executor_address` varchar(255) DEFAULT NULL COMMENT '执行器地址,本次执行的地址',
   `executor_handler` varchar(255) DEFAULT NULL COMMENT '执行器任务handler',
-  `executor_param` varchar(255) DEFAULT NULL COMMENT 'executor_param',
+  `executor_param` varchar(512) DEFAULT NULL COMMENT '执行器任务参数',
   `trigger_time` datetime DEFAULT NULL COMMENT '调度-时间',
   `trigger_code` varchar(255) NOT NULL DEFAULT '0' COMMENT '调度-结果',
   `trigger_msg` varchar(2048) DEFAULT NULL COMMENT '调度-日志',
@@ -212,7 +214,7 @@ CREATE TABLE `XXL_JOB_QRTZ_TRIGGER_GROUP` (
   `title` varchar(12) NOT NULL COMMENT '执行器名称',
   `order` tinyint(4) NOT NULL DEFAULT '0' COMMENT '排序',
   `address_type` tinyint(4) NOT NULL DEFAULT '0' COMMENT '执行器地址类型:0=自动注册、1=手动录入',
-  `address_list` varchar(200) DEFAULT NULL COMMENT '执行器地址列表,多地址逗号分隔',
+  `address_list` varchar(512) DEFAULT NULL COMMENT '执行器地址列表,多地址逗号分隔',
   PRIMARY KEY (`id`)
 ) ENGINE=InnoDB DEFAULT CHARSET=utf8;
 

+ 16 - 16
pom.xml

@@ -20,31 +20,31 @@
 		<javax.servlet-api.version>3.0.1</javax.servlet-api.version>
 		<jsp-api.version>2.2</jsp-api.version>
 
-		<spring.version>3.2.18.RELEASE</spring.version>
-		<jackson-mapper-asl.version>1.9.13</jackson-mapper-asl.version>
-		<aspectjweaver.version>1.8.7</aspectjweaver.version>
+		<spring.version>4.3.13.RELEASE</spring.version>
+		<jackson.version>2.9.3</jackson.version>
+		<aspectjweaver.version>1.8.13</aspectjweaver.version>
 		<slf4j-api.version>1.7.25</slf4j-api.version>
-		<freemarker.version>2.3.20</freemarker.version>
-		<junit.version>4.11</junit.version>
+		<freemarker.version>2.3.23</freemarker.version>
+		<junit.version>4.12</junit.version>
 
 		<jetty-server.version>9.2.22.v20170606</jetty-server.version>
-		<hessian.version>4.0.38</hessian.version>
-		<httpclient.version>4.3.6</httpclient.version>
-
+		<hessian.version>4.0.51</hessian.version>
+		<httpclient.version>4.5.4</httpclient.version>
+		
 		<commons-exec.version>1.3</commons-exec.version>
-		<commons-beanutils.version>1.9.2</commons-beanutils.version>
-		<commons-lang.version>2.6</commons-lang.version>
+		<commons-collections4.version>4.1</commons-collections4.version>
+		<commons-lang3.version>3.7</commons-lang3.version>
+		<commons-email.version>1.5</commons-email.version>
 
 		<c3p0.version>0.9.5.2</c3p0.version>
-		<mysql-connector-java.version>5.1.29</mysql-connector-java.version>
-		<mybatis-spring.version>1.2.2</mybatis-spring.version>
-		<mybatis.version>3.2.8</mybatis.version>
+		<mysql-connector-java.version>5.1.45</mysql-connector-java.version>
+		<mybatis-spring.version>1.3.1</mybatis-spring.version>
+		<mybatis.version>3.4.5</mybatis.version>
 
-		<groovy-all.version>2.4.5</groovy-all.version>
-		<mail.version>1.4.6</mail.version>
+		<groovy-all.version>2.4.13</groovy-all.version>
 		<quartz.version>2.3.0</quartz.version>
 
-		<spring-boot.version>1.5.6.RELEASE</spring-boot.version>
+		<spring-boot.version>1.5.9.RELEASE</spring-boot.version>
 	</properties>
 
 	<build>

+ 33 - 37
xxl-job-admin/pom.xml

@@ -40,18 +40,22 @@
 		</dependency>
 		<!-- jackson (support spring json) -->
 		<dependency>
-			<groupId>org.codehaus.jackson</groupId>
-			<artifactId>jackson-mapper-asl</artifactId>
-			<version>${jackson-mapper-asl.version}</version>
+			<groupId>com.fasterxml.jackson.core</groupId>
+			<artifactId>jackson-databind</artifactId>
+			<version>${jackson.version}</version>
 		</dependency>
 
-		<!-- slf4j -->
+		<!-- servlet -->
 		<dependency>
-			<groupId>org.slf4j</groupId>
-			<artifactId>slf4j-log4j12</artifactId>
-			<version>${slf4j-api.version}</version>
+			<groupId>javax.servlet</groupId>
+			<artifactId>javax.servlet-api</artifactId>
+			<version>${javax.servlet-api.version}</version>
+		</dependency>
+		<dependency>
+			<groupId>javax.servlet.jsp</groupId>
+			<artifactId>jsp-api</artifactId>
+			<version>${jsp-api.version}</version>
 		</dependency>
-
 		<!-- freemarker -->
 		<dependency>
 			<groupId>org.freemarker</groupId>
@@ -59,37 +63,37 @@
 			<version>${freemarker.version}</version>
 		</dependency>
 
-		<!-- commons-beanutils -->
+		<!-- slf4j -->
 		<dependency>
-			<groupId>commons-beanutils</groupId>
-			<artifactId>commons-beanutils</artifactId>
-			<version>${commons-beanutils.version}</version>
+			<groupId>org.slf4j</groupId>
+			<artifactId>slf4j-log4j12</artifactId>
+			<version>${slf4j-api.version}</version>
 		</dependency>
-		<!-- commons-lang -->
+		<!-- junit -->
 		<dependency>
-			<groupId>commons-lang</groupId>
-			<artifactId>commons-lang</artifactId>
-			<version>${commons-lang.version}</version>
+			<groupId>junit</groupId>
+			<artifactId>junit</artifactId>
+			<version>${junit.version}</version>
+			<scope>test</scope>
 		</dependency>
 
-		<!-- servlet -->
+		<!-- commons-collections4 -->
 		<dependency>
-			<groupId>javax.servlet</groupId>
-			<artifactId>javax.servlet-api</artifactId>
-			<version>${javax.servlet-api.version}</version>
+			<groupId>org.apache.commons</groupId>
+			<artifactId>commons-collections4</artifactId>
+			<version>${commons-collections4.version}</version>
 		</dependency>
+		<!-- commons-lang3 -->
 		<dependency>
-			<groupId>javax.servlet.jsp</groupId>
-			<artifactId>jsp-api</artifactId>
-			<version>${jsp-api.version}</version>
+			<groupId>org.apache.commons</groupId>
+			<artifactId>commons-lang3</artifactId>
+			<version>${commons-lang3.version}</version>
 		</dependency>
-
-		<!-- junit -->
+		<!-- commons-email -->
 		<dependency>
-			<groupId>junit</groupId>
-			<artifactId>junit</artifactId>
-			<version>${junit.version}</version>
-			<scope>test</scope>
+			<groupId>org.apache.commons</groupId>
+			<artifactId>commons-email</artifactId>
+			<version>${commons-email.version}</version>
 		</dependency>
 
 		<!-- c3p0 -->
@@ -116,7 +120,6 @@
 			<version>${mybatis.version}</version>
 		</dependency>
 
-
 		<!-- httpclient -->
 		<dependency>
 			<groupId>org.apache.httpcomponents</groupId>
@@ -124,13 +127,6 @@
 			<version>${httpclient.version}</version>
 		</dependency>
 
-		<!-- javax.mail -->
-		<dependency>
-			<groupId>javax.mail</groupId>
-			<artifactId>mail</artifactId>
-			<version>${mail.version}</version>
-		</dependency>
-
 		<!-- quartz :quartz-2.2.3/c3p0-0.9.1.1/slf4j-api-1.6.6 -->
 		<dependency>
 			<groupId>org.quartz-scheduler</groupId>

+ 16 - 14
xxl-job-admin/src/main/java/com/xxl/job/admin/controller/IndexController.java

@@ -2,10 +2,9 @@ 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.PropertiesUtil;
 import com.xxl.job.admin.service.XxlJobService;
 import com.xxl.job.core.biz.model.ReturnT;
-import org.apache.commons.lang.StringUtils;
+import org.apache.commons.lang3.StringUtils;
 import org.springframework.beans.propertyeditors.CustomDateEditor;
 import org.springframework.stereotype.Controller;
 import org.springframework.ui.Model;
@@ -61,18 +60,21 @@ public class IndexController {
 	@ResponseBody
 	@PermessionLimit(limit=false)
 	public ReturnT<String> loginDo(HttpServletRequest request, HttpServletResponse response, String userName, String password, String ifRemember){
-		if (!PermissionInterceptor.ifLogin(request)) {
-			if (StringUtils.isNotBlank(userName) && StringUtils.isNotBlank(password)
-					&& PropertiesUtil.getString("xxl.job.login.username").equals(userName)
-					&& PropertiesUtil.getString("xxl.job.login.password").equals(password)) {
-				boolean ifRem = false;
-				if (StringUtils.isNotBlank(ifRemember) && "on".equals(ifRemember)) {
-					ifRem = true;
-				}
-				PermissionInterceptor.login(response, ifRem);
-			} else {
-				return new ReturnT<String>(500, "账号或密码错误");
-			}
+		// valid
+		if (PermissionInterceptor.ifLogin(request)) {
+			return ReturnT.SUCCESS;
+		}
+
+		// param
+		if (StringUtils.isBlank(userName) || StringUtils.isBlank(password)){
+			return new ReturnT<String>(500, "账号或密码为空");
+		}
+		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 ReturnT.SUCCESS;
 	}

+ 1 - 1
xxl-job-admin/src/main/java/com/xxl/job/admin/controller/JobGroupController.java

@@ -4,7 +4,7 @@ import com.xxl.job.admin.core.model.XxlJobGroup;
 import com.xxl.job.admin.dao.XxlJobGroupDao;
 import com.xxl.job.admin.dao.XxlJobInfoDao;
 import com.xxl.job.core.biz.model.ReturnT;
-import org.apache.commons.lang.StringUtils;
+import org.apache.commons.lang3.StringUtils;
 import org.springframework.stereotype.Controller;
 import org.springframework.ui.Model;
 import org.springframework.web.bind.annotation.RequestMapping;

+ 2 - 2
xxl-job-admin/src/main/java/com/xxl/job/admin/controller/JobLogController.java

@@ -11,8 +11,8 @@ 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.rpc.netcom.NetComClientProxy;
-import org.apache.commons.lang.StringUtils;
-import org.apache.commons.lang.time.DateUtils;
+import org.apache.commons.lang3.StringUtils;
+import org.apache.commons.lang3.time.DateUtils;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 import org.springframework.stereotype.Controller;

+ 5 - 5
xxl-job-admin/src/main/java/com/xxl/job/admin/controller/interceptor/CookieInterceptor.java

@@ -1,17 +1,17 @@
 package com.xxl.job.admin.controller.interceptor;
 
-import java.util.HashMap;
+import org.apache.commons.lang3.ArrayUtils;
+import org.springframework.web.servlet.ModelAndView;
+import org.springframework.web.servlet.handler.HandlerInterceptorAdapter;
 
 import javax.servlet.http.Cookie;
 import javax.servlet.http.HttpServletRequest;
 import javax.servlet.http.HttpServletResponse;
-
-import org.apache.commons.lang.ArrayUtils;
-import org.springframework.web.servlet.ModelAndView;
-import org.springframework.web.servlet.handler.HandlerInterceptorAdapter;
+import java.util.HashMap;
 
 /**
  * push cookies to model as cookieMap
+ *
  * @author xuxueli 2015-12-12 18:09:04
  */
 public class CookieInterceptor extends HandlerInterceptorAdapter {

+ 27 - 6
xxl-job-admin/src/main/java/com/xxl/job/admin/controller/interceptor/PermissionInterceptor.java

@@ -3,6 +3,7 @@ package com.xxl.job.admin.controller.interceptor;
 import com.xxl.job.admin.controller.annotation.PermessionLimit;
 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;
 
@@ -12,20 +13,38 @@ import java.math.BigInteger;
 
 /**
  * 权限拦截, 简易版
+ *
  * @author xuxueli 2015-12-12 18:09:04
  */
 public class PermissionInterceptor extends HandlerInterceptorAdapter {
-	
-	public static final String LOGIN_IDENTITY_KEY = "LOGIN_IDENTITY";
+
+
+	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 temp = username + "_" + password;
-        LOGIN_IDENTITY_TOKEN = new BigInteger(1, temp.getBytes()).toString(16);
+
+        // login token
+        String tokenTmp = DigestUtils.md5Hex(username + "_" + password);
+		tokenTmp = new BigInteger(1, tokenTmp.getBytes()).toString(16);
+
+		LOGIN_IDENTITY_TOKEN = tokenTmp;
     }
-	
-	public static boolean login(HttpServletResponse response, boolean ifRemember){
+
+
+
+	public static boolean login(HttpServletResponse response, String username, String password, boolean ifRemember){
+
+    	// login token
+		String tokenTmp = DigestUtils.md5Hex(username + "_" + password);
+		tokenTmp = new BigInteger(1, tokenTmp.getBytes()).toString(16);
+
+		if (!LOGIN_IDENTITY_TOKEN.equals(tokenTmp)){
+			return false;
+		}
+
+		// do login
 		CookieUtil.set(response, LOGIN_IDENTITY_KEY, LOGIN_IDENTITY_TOKEN, ifRemember);
 		return true;
 	}
@@ -40,6 +59,8 @@ public class PermissionInterceptor extends HandlerInterceptorAdapter {
 		return true;
 	}
 
+
+
 	@Override
 	public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
 		

+ 1 - 1
xxl-job-admin/src/main/java/com/xxl/job/admin/core/model/XxlJobGroup.java

@@ -1,6 +1,6 @@
 package com.xxl.job.admin.core.model;
 
-import org.apache.commons.lang.StringUtils;
+import org.apache.commons.lang3.StringUtils;
 
 import java.util.ArrayList;
 import java.util.Arrays;

+ 5 - 5
xxl-job-admin/src/main/java/com/xxl/job/admin/core/model/XxlJobInfo.java

@@ -31,7 +31,7 @@ public class XxlJobInfo {
 	private String glueRemark;		// GLUE备注
 	private Date glueUpdatetime;	// GLUE更新时间
 
-	private String childJobKey;		// 子任务Key
+	private String childJobId;		// 子任务ID,多个逗号分隔
 	
 	// copy from quartz
 	private String jobStatus;		// 任务状态 【base on quartz】
@@ -172,12 +172,12 @@ public class XxlJobInfo {
 		this.glueUpdatetime = glueUpdatetime;
 	}
 
-	public String getChildJobKey() {
-		return childJobKey;
+	public String getChildJobId() {
+		return childJobId;
 	}
 
-	public void setChildJobKey(String childJobKey) {
-		this.childJobKey = childJobKey;
+	public void setChildJobId(String childJobId) {
+		this.childJobId = childJobId;
 	}
 
 	public String getJobStatus() {

+ 54 - 23
xxl-job-admin/src/main/java/com/xxl/job/admin/core/thread/JobFailMonitorHelper.java

@@ -6,7 +6,8 @@ import com.xxl.job.admin.core.model.XxlJobLog;
 import com.xxl.job.admin.core.schedule.XxlJobDynamicScheduler;
 import com.xxl.job.admin.core.util.MailUtil;
 import com.xxl.job.core.biz.model.ReturnT;
-import org.apache.commons.collections.CollectionUtils;
+import com.xxl.job.core.handler.IJobHandler;
+import org.apache.commons.collections4.CollectionUtils;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
@@ -27,6 +28,8 @@ public class JobFailMonitorHelper {
 		return instance;
 	}
 
+	// ---------------------- monitor ----------------------
+
 	private LinkedBlockingQueue<Integer> queue = new LinkedBlockingQueue<Integer>(0xfff8);
 
 	private Thread monitorThread;
@@ -51,13 +54,15 @@ public class JobFailMonitorHelper {
 								if (log == null) {
 									continue;
 								}
-								if (ReturnT.SUCCESS_CODE == log.getTriggerCode() && log.getHandleCode() == 0) {
+								if (IJobHandler.SUCCESS.getCode() == log.getTriggerCode() && log.getHandleCode() == 0) {
 									JobFailMonitorHelper.monitor(jobLogId);
 									logger.info(">>>>>>>>>>> job monitor, job running, JobLogId:{}", jobLogId);
-								} else if (ReturnT.SUCCESS_CODE == log.getTriggerCode() && ReturnT.SUCCESS_CODE == log.getHandleCode()) {
+								} else if (IJobHandler.SUCCESS.getCode() == log.getHandleCode()) {
 									// job success, pass
 									logger.info(">>>>>>>>>>> job monitor, job success, JobLogId:{}", jobLogId);
-								} else if (ReturnT.FAIL_CODE == log.getTriggerCode() || ReturnT.FAIL_CODE == log.getHandleCode()) {
+								} else if (IJobHandler.FAIL.getCode() == log.getTriggerCode()
+										|| IJobHandler.FAIL.getCode() == log.getHandleCode()
+										|| IJobHandler.FAIL_RETRY.getCode() == log.getHandleCode() ) {
 									// job fail,
 									failAlarm(log);
 									logger.info(">>>>>>>>>>> job monitor, job fail, JobLogId:{}", jobLogId);
@@ -94,6 +99,46 @@ public class JobFailMonitorHelper {
 		monitorThread.start();
 	}
 
+	public void toStop(){
+		toStop = true;
+		// interrupt and wait
+		monitorThread.interrupt();
+		try {
+			monitorThread.join();
+		} catch (InterruptedException e) {
+			logger.error(e.getMessage(), e);
+		}
+	}
+	
+	// producer
+	public static void monitor(int jobLogId){
+		getInstance().queue.offer(jobLogId);
+	}
+
+
+	// ---------------------- alarm ----------------------
+
+	// email alarm template
+	private static final String mailBodyTemplate = "<h5>监控告警明细:</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" +
+			"      </tr>\n" +
+			"   <thead/>\n" +
+			"   <tbody>\n" +
+			"      <tr>\n" +
+			"         <td>{0}</td>\n" +
+			"         <td>{1}</td>\n" +
+			"         <td>{2}</td>\n" +
+			"         <td>调度失败</td>\n" +
+			"      </tr>\n" +
+			"   <tbody>\n" +
+			"</table>";
+
 	/**
 	 * fail alarm
 	 *
@@ -107,10 +152,12 @@ public class JobFailMonitorHelper {
 
 			Set<String> emailSet = new HashSet<String>(Arrays.asList(info.getAlarmEmail().split(",")));
 			for (String email: emailSet) {
-				String title = "《调度监控报警》(任务调度中心XXL-JOB)";
 				XxlJobGroup group = XxlJobDynamicScheduler.xxlJobGroupDao.load(Integer.valueOf(info.getJobGroup()));
-				String content = MessageFormat.format("任务调度失败, 执行器名称:{0}, 任务描述:{1}.", group!=null?group.getTitle():"null", info.getJobDesc());
-				MailUtil.sendMail(email, title, content, false, null);
+
+				String title = "调度中心监控报警";
+				String content = MessageFormat.format(mailBodyTemplate, group!=null?group.getTitle():"null", info.getId(), info.getJobDesc());
+
+				MailUtil.sendMail(email, title, content);
 			}
 		}
 
@@ -118,20 +165,4 @@ public class JobFailMonitorHelper {
 
 	}
 
-	public void toStop(){
-		toStop = true;
-		// interrupt and wait
-		monitorThread.interrupt();
-		try {
-			monitorThread.join();
-		} catch (InterruptedException e) {
-			logger.error(e.getMessage(), e);
-		}
-	}
-	
-	// producer
-	public static void monitor(int jobLogId){
-		getInstance().queue.offer(jobLogId);
-	}
-	
 }

+ 2 - 2
xxl-job-admin/src/main/java/com/xxl/job/admin/core/thread/JobRegistryMonitorHelper.java

@@ -4,8 +4,8 @@ import com.xxl.job.admin.core.model.XxlJobGroup;
 import com.xxl.job.admin.core.model.XxlJobRegistry;
 import com.xxl.job.admin.core.schedule.XxlJobDynamicScheduler;
 import com.xxl.job.core.enums.RegistryConfig;
-import org.apache.commons.collections.CollectionUtils;
-import org.apache.commons.lang.StringUtils;
+import org.apache.commons.collections4.CollectionUtils;
+import org.apache.commons.lang3.StringUtils;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 

+ 69 - 66
xxl-job-admin/src/main/java/com/xxl/job/admin/core/trigger/XxlJobTrigger.java

@@ -11,7 +11,8 @@ import com.xxl.job.core.biz.ExecutorBiz;
 import com.xxl.job.core.biz.model.ReturnT;
 import com.xxl.job.core.biz.model.TriggerParam;
 import com.xxl.job.core.enums.ExecutorBlockStrategyEnum;
-import org.apache.commons.collections.CollectionUtils;
+import com.xxl.job.core.util.IpUtil;
+import org.apache.commons.collections4.CollectionUtils;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
@@ -66,11 +67,12 @@ public class XxlJobTrigger {
 
                 ReturnT<String> triggerResult = new ReturnT<String>(null);
                 StringBuffer triggerMsgSb = new StringBuffer();
-                triggerMsgSb.append("注册方式:").append( (group.getAddressType() == 0)?"自动注册":"手动录入" );
+                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("<br>地址列表:").append(group.getRegistryList());
-                triggerMsgSb.append("<br>路由策略:").append(executorRouteStrategyEnum.getTitle()).append("("+i+"/"+addressList.size()+")"); // update01
 
                 // 3、trigger-valid
                 if (triggerResult.getCode()==ReturnT.SUCCESS_CODE && CollectionUtils.isEmpty(addressList)) {
@@ -115,72 +117,73 @@ public class XxlJobTrigger {
                 logger.debug(">>>>>>>>>>> xxl-job trigger end, jobId:{}", jobLog.getId());
 
             }
-            return;
-        }
-
-        // 1、save log-id
-        XxlJobLog jobLog = new XxlJobLog();
-        jobLog.setJobGroup(jobInfo.getJobGroup());
-        jobLog.setJobId(jobInfo.getId());
-        XxlJobDynamicScheduler.xxlJobLogDao.save(jobLog);
-        logger.debug(">>>>>>>>>>> xxl-job trigger start, jobId:{}", jobLog.getId());
-
-        // 2、prepare trigger-info
-        //jobLog.setExecutorAddress(executorAddress);
-        jobLog.setGlueType(jobInfo.getGlueType());
-        jobLog.setExecutorHandler(jobInfo.getExecutorHandler());
-        jobLog.setExecutorParam(jobInfo.getExecutorParam());
-        jobLog.setTriggerTime(new Date());
-
-        ReturnT<String> triggerResult = new ReturnT<String>(null);
-        StringBuffer triggerMsgSb = new StringBuffer();
-        triggerMsgSb.append("注册方式:").append( (group.getAddressType() == 0)?"自动注册":"手动录入" );
-        triggerMsgSb.append("<br>阻塞处理策略:").append(blockStrategy.getTitle());
-        triggerMsgSb.append("<br>失败处理策略:").append(failStrategy.getTitle());
-        triggerMsgSb.append("<br>地址列表:").append(group.getRegistryList());
-        triggerMsgSb.append("<br>路由策略:").append(executorRouteStrategyEnum.getTitle());
-
-        // 3、trigger-valid
-        if (triggerResult.getCode()==ReturnT.SUCCESS_CODE && CollectionUtils.isEmpty(addressList)) {
-            triggerResult.setCode(ReturnT.FAIL_CODE);
-            triggerMsgSb.append("<br>----------------------<br>").append("调度失败:").append("执行器地址为空");
-        }
+        } else {
+            // 1、save log-id
+            XxlJobLog jobLog = new XxlJobLog();
+            jobLog.setJobGroup(jobInfo.getJobGroup());
+            jobLog.setJobId(jobInfo.getId());
+            XxlJobDynamicScheduler.xxlJobLogDao.save(jobLog);
+            logger.debug(">>>>>>>>>>> xxl-job trigger start, jobId:{}", jobLog.getId());
+
+            // 2、prepare trigger-info
+            //jobLog.setExecutorAddress(executorAddress);
+            jobLog.setGlueType(jobInfo.getGlueType());
+            jobLog.setExecutorHandler(jobInfo.getExecutorHandler());
+            jobLog.setExecutorParam(jobInfo.getExecutorParam());
+            jobLog.setTriggerTime(new Date());
+
+            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());
+
+            // 3、trigger-valid
+            if (triggerResult.getCode()==ReturnT.SUCCESS_CODE && CollectionUtils.isEmpty(addressList)) {
+                triggerResult.setCode(ReturnT.FAIL_CODE);
+                triggerMsgSb.append("<br>----------------------<br>").append("调度失败:").append("执行器地址为空");
+            }
 
-        if (triggerResult.getCode() == ReturnT.SUCCESS_CODE) {
-            // 4.1、trigger-param
-            TriggerParam triggerParam = new TriggerParam();
-            triggerParam.setJobId(jobInfo.getId());
-            triggerParam.setExecutorHandler(jobInfo.getExecutorHandler());
-            triggerParam.setExecutorParams(jobInfo.getExecutorParam());
-            triggerParam.setExecutorBlockStrategy(jobInfo.getExecutorBlockStrategy());
-            triggerParam.setLogId(jobLog.getId());
-            triggerParam.setLogDateTim(jobLog.getTriggerTime().getTime());
-            triggerParam.setGlueType(jobInfo.getGlueType());
-            triggerParam.setGlueSource(jobInfo.getGlueSource());
-            triggerParam.setGlueUpdatetime(jobInfo.getGlueUpdatetime().getTime());
-            triggerParam.setBroadcastIndex(0);
-            triggerParam.setBroadcastTotal(1);
-
-            // 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());
-
-            // 4.3、trigger (fail retry)
-            if (triggerResult.getCode()!=ReturnT.SUCCESS_CODE && failStrategy == ExecutorFailStrategyEnum.FAIL_RETRY) {
+            if (triggerResult.getCode() == ReturnT.SUCCESS_CODE) {
+                // 4.1、trigger-param
+                TriggerParam triggerParam = new TriggerParam();
+                triggerParam.setJobId(jobInfo.getId());
+                triggerParam.setExecutorHandler(jobInfo.getExecutorHandler());
+                triggerParam.setExecutorParams(jobInfo.getExecutorParam());
+                triggerParam.setExecutorBlockStrategy(jobInfo.getExecutorBlockStrategy());
+                triggerParam.setLogId(jobLog.getId());
+                triggerParam.setLogDateTim(jobLog.getTriggerTime().getTime());
+                triggerParam.setGlueType(jobInfo.getGlueType());
+                triggerParam.setGlueSource(jobInfo.getGlueSource());
+                triggerParam.setGlueUpdatetime(jobInfo.getGlueUpdatetime().getTime());
+                triggerParam.setBroadcastIndex(0);
+                triggerParam.setBroadcastTotal(1);
+
+                // 4.2、trigger-run (route run / trigger remote executor)
                 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:#00c0ef;\" > >>>>>>>>>>>触发调度<<<<<<<<<<< </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());
+                }
             }
-        }
 
-        // 5、save trigger-info
-        jobLog.setExecutorAddress(triggerResult.getContent());
-        jobLog.setTriggerCode(triggerResult.getCode());
-        jobLog.setTriggerMsg(triggerMsgSb.toString());
-        XxlJobDynamicScheduler.xxlJobLogDao.updateTriggerInfo(jobLog);
+            // 5、save trigger-info
+            jobLog.setExecutorAddress(triggerResult.getContent());
+            jobLog.setTriggerCode(triggerResult.getCode());
+            jobLog.setTriggerMsg(triggerMsgSb.toString());
+            XxlJobDynamicScheduler.xxlJobLogDao.updateTriggerInfo(jobLog);
+
+            // 6、monitor triger
+            JobFailMonitorHelper.monitor(jobLog.getId());
+            logger.debug(">>>>>>>>>>> xxl-job trigger end, jobId:{}", jobLog.getId());
+        }
 
-        // 6、monitor triger
-        JobFailMonitorHelper.monitor(jobLog.getId());
-        logger.debug(">>>>>>>>>>> xxl-job trigger end, jobId:{}", jobLog.getId());
     }
 
     /**
@@ -195,7 +198,7 @@ public class XxlJobTrigger {
             ExecutorBiz executorBiz = XxlJobDynamicScheduler.getExecutorBiz(address);
             runResult = executorBiz.run(triggerParam);
         } catch (Exception e) {
-            logger.error(e.getMessage(), e);
+            logger.error(">>>>>>>>>>> xxl-job trigger error, please check if the executor[{}] is running.", address, e);
             runResult = new ReturnT<String>(ReturnT.FAIL_CODE, ""+e );
         }
 

+ 17 - 18
xxl-job-admin/src/main/java/com/xxl/job/admin/core/util/CookieUtil.java

@@ -6,9 +6,11 @@ import javax.servlet.http.HttpServletResponse;
 
 /**
  * Cookie.Util
+ *
  * @author xuxueli 2015-12-12 18:01:06
  */
 public class CookieUtil {
+
 	// 默认缓存时间,单位/秒, 2H
 	private static final int COOKIE_MAX_AGE = 60 * 60 * 2;
 	// 保存路径,根路径
@@ -16,43 +18,39 @@ public class CookieUtil {
 	
 	/**
 	 * 保存
+	 *
 	 * @param response
 	 * @param key
 	 * @param value
 	 * @param ifRemember 
 	 */
 	public static void set(HttpServletResponse response, String key, String value, boolean ifRemember) {
-		
-		int age = COOKIE_MAX_AGE;
-		if (ifRemember) {
-			age = COOKIE_MAX_AGE;
-		} else {
-			age = -1;
-		}
-		
-		Cookie cookie = new Cookie(key, value);
-		cookie.setMaxAge(age);				// Cookie过期时间,单位/秒
-		cookie.setPath(COOKIE_PATH);		// Cookie适用的路径
-		response.addCookie(cookie);
+		int age = ifRemember?COOKIE_MAX_AGE:-1;
+		set(response, key, value, null, COOKIE_PATH, age, true);
 	}
 
 	/**
 	 * 保存
+	 *
 	 * @param response
 	 * @param key
 	 * @param value
 	 * @param maxAge
 	 */
-	private static void set(HttpServletResponse response, 
-			String key, String value, int maxAge, String path) {
+	private static void set(HttpServletResponse response, String key, String value, String domain, String path, int maxAge, boolean isHttpOnly) {
 		Cookie cookie = new Cookie(key, value);
-		cookie.setMaxAge(maxAge);	// Cookie过期时间,单位/秒
-		cookie.setPath(path);		// Cookie适用的路径
+		if (domain != null) {
+			cookie.setDomain(domain);
+		}
+		cookie.setPath(path);
+		cookie.setMaxAge(maxAge);
+		cookie.setHttpOnly(isHttpOnly);
 		response.addCookie(cookie);
 	}
 	
 	/**
 	 * 查询value
+	 *
 	 * @param request
 	 * @param key
 	 * @return
@@ -67,6 +65,7 @@ public class CookieUtil {
 
 	/**
 	 * 查询Cookie
+	 *
 	 * @param request
 	 * @param key
 	 */
@@ -84,15 +83,15 @@ public class CookieUtil {
 	
 	/**
 	 * 删除Cookie
+	 *
 	 * @param request
 	 * @param response
 	 * @param key
-	 * @param domainName
 	 */
 	public static void remove(HttpServletRequest request, HttpServletResponse response, String key) {
 		Cookie cookie = get(request, key);
 		if (cookie != null) {
-			set(response, key, "", 0, COOKIE_PATH);
+			set(response, key, "", null, COOKIE_PATH, 0, true);
 		}
 	}
 

+ 32 - 147
xxl-job-admin/src/main/java/com/xxl/job/admin/core/util/MailUtil.java

@@ -1,21 +1,16 @@
 package com.xxl.job.admin.core.util;
 
-import org.apache.commons.lang.ArrayUtils;
+import org.apache.commons.mail.DefaultAuthenticator;
+import org.apache.commons.mail.EmailException;
+import org.apache.commons.mail.HtmlEmail;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
-import org.springframework.mail.javamail.JavaMailSender;
-import org.springframework.mail.javamail.JavaMailSenderImpl;
-import org.springframework.mail.javamail.MimeMessageHelper;
 
-import javax.mail.internet.MimeMessage;
-import javax.mail.internet.MimeUtility;
-import java.io.File;
-import java.util.Properties;
-import java.util.concurrent.ExecutorService;
-import java.util.concurrent.Executors;
+import java.nio.charset.Charset;
 
 /**
  * 邮件发送.Util
+ *
  * @author xuxueli 2016-3-12 15:06:20
  */
 public class MailUtil {
@@ -33,154 +28,44 @@ public class MailUtil {
 		password = PropertiesUtil.getString("xxl.job.mail.password");
 		sendNick = PropertiesUtil.getString("xxl.job.mail.sendNick");
 	}
-	
-	/**
-	<!-- spring mail sender -->
-	<bean id="javaMailSender" class="org.springframework.mail.javamail.JavaMailSenderImpl"  scope="singleton" >
-		<property name="host" value="${mail.host}" />			<!-- SMTP发送邮件的服务器的IP和端口 -->
-		<property name="port" value="${mail.port}" />
-		<property name="username" value="${mail.username}" />	<!-- 登录SMTP邮件发送服务器的用户名和密码 -->
-		<property name="password" value="${mail.password}" />
-		<property name="javaMailProperties">					<!-- 获得邮件会话属性,验证登录邮件服务器是否成功 -->
-			<props>
-				<prop key="mail.smtp.auth">true</prop>
-				<prop key="prop">true</prop>
-				<!-- <prop key="mail.smtp.timeout">25000</prop> -->
-			</props>
-		</property>
-	</bean>
-	 */
+
 	/**
-	 * 发送邮件 (完整版)(结合Spring)
-	 * 
-	 * //@param javaMailSender: 发送Bean
-	 * //@param sendFrom		: 发送人邮箱
-	 * //@param sendNick		: 发送人昵称
-	 * @param toAddress		: 收件人邮箱
-	 * @param mailSubject	: 邮件主题
-	 * @param mailBody		: 邮件正文
-	 * @param mailBodyIsHtml: 邮件正文格式,true:HTML格式;false:文本格式
-	 * @param attachments	: 附件
+	 *
+	 * @param toAddress		收件人邮箱
+	 * @param mailSubject	邮件主题
+	 * @param mailBody		邮件正文
+	 * @return
 	 */
-	@SuppressWarnings("null")
-	public static boolean sendMailSpring(String toAddress, String mailSubject, String mailBody, boolean mailBodyIsHtml,File[] attachments) {
-		JavaMailSender javaMailSender = null;//ResourceBundle.getInstance().getJavaMailSender();
+	public static boolean sendMail(String toAddress, String mailSubject, String mailBody){
+
 		try {
-			MimeMessage mimeMessage = javaMailSender.createMimeMessage();
-			MimeMessageHelper helper = new MimeMessageHelper(mimeMessage, ArrayUtils.isNotEmpty(attachments), "UTF-8"); // 设置utf-8或GBK编码,否则邮件会有乱码;multipart,true表示文件上传
+			// Create the email message
+			HtmlEmail email = new HtmlEmail();
 
+			//email.setDebug(true);		// 将会打印一些log
+			//email.setTLS(true);		// 是否TLS校验,,某些邮箱需要TLS安全校验,同理有SSL校验
+			//email.setSSL(true);
 
-			helper.setFrom(username, sendNick);
-			helper.setTo(toAddress);
+			email.setHostName(host);
+			email.setSmtpPort(Integer.valueOf(port));
+			//email.setSslSmtpPort(port);
+			email.setAuthenticator(new DefaultAuthenticator(username, password));
+			email.setCharset(Charset.defaultCharset().name());
 
-			// 设置收件人抄送的名片和地址(相当于群发了)
-			//helper.setCc(InternetAddress.parse(MimeUtility.encodeText("邮箱001") + " <@163.com>," + MimeUtility.encodeText("邮箱002") + " <@foxmail.com>"));
+			email.setFrom(username, sendNick);
+			email.addTo(toAddress);
+			email.setSubject(mailSubject);
+			email.setMsg(mailBody);
 
-			helper.setSubject(mailSubject);
-			helper.setText(mailBody, mailBodyIsHtml);
-			
-			// 添加附件
-			if (ArrayUtils.isNotEmpty(attachments)) {
-				for (File file : attachments) {
-					helper.addAttachment(MimeUtility.encodeText(file.getName()), file);	
-				}
-			}
-			
-			// 群发
-			//MimeMessage[] mailMessages = { mimeMessage };
-			
-			javaMailSender.send(mimeMessage);
-			return true;
-		} catch (Exception e) {
-			logger.error(e.getMessage(), e);
-		}
-		return false;
-	}
-	
-	/**
-	 * 发送邮件 (完整版) (纯JavaMail)
-	 * 
-	 * @param toAddress		: 收件人邮箱
-	 * @param mailSubject	: 邮件主题
-	 * @param mailBody		: 邮件正文
-	 * @param mailBodyIsHtml: 邮件正文格式,true:HTML格式;false:文本格式
-	 * //@param inLineFile	: 内嵌文件
-	 * @param attachments	: 附件
-	 */
-	public static boolean sendMail (String toAddress, String mailSubject, String mailBody, 
-			boolean mailBodyIsHtml, File[] attachments){
-        try {
-			// 创建邮件发送类 JavaMailSender (用于发送多元化邮件,包括附件,图片,html 等)
-        	JavaMailSenderImpl mailSender = new JavaMailSenderImpl();
-        	mailSender.setHost(host); 			// 设置邮件服务主机    
-        	mailSender.setUsername(username); 	// 发送者邮箱的用户名    
-        	mailSender.setPassword(password); 	// 发送者邮箱的密码    
-        	
-			// 配置文件,用于实例化java.mail.session
-			Properties pro = new Properties();
-			pro.put("mail.transport.protocol", "smtp");
-			pro.put("mail.smtp.auth", "true");		// 登录SMTP服务器,需要获得授权 (网易163邮箱新近注册的邮箱均不能授权,测试 sohu 的邮箱可以获得授权)
-			pro.put("mail.smtp.socketFactory.port", port);
-			pro.put("mail.smtp.socketFactory.fallback", "false");
-			mailSender.setJavaMailProperties(pro);
-			
-			// 创建多元化邮件 (创建 mimeMessage 帮助类,用于封装信息至 mimeMessage)
-			MimeMessage mimeMessage = mailSender.createMimeMessage();						
-			MimeMessageHelper helper = new MimeMessageHelper(mimeMessage, ArrayUtils.isNotEmpty(attachments), "UTF-8");
-			
-			helper.setFrom(username, sendNick);
-			helper.setTo(toAddress);
-
-			helper.setSubject(mailSubject);
-			helper.setText(mailBody, mailBodyIsHtml); 
-			
-			// 设置收件人抄送的名片和地址(相当于群发)
-			//helper.setCc(InternetAddress.parse(MimeUtility.encodeText("邮箱001") + " <@163.com>," + MimeUtility.encodeText("邮箱002") + " <@foxmail.com>"));
+			//email.attach(attachment);	// add the attachment
 
-			// 内嵌文件,第1个参数为cid标识这个文件,第2个参数为资源
-			//helper.addInline(MimeUtility.encodeText(inLineFile.getName()), inLineFile);
-			
-			// 添加附件
-			/*if (ArrayUtils.isNotEmpty(attachments)) {
-				for (File file : attachments) {
-					helper.addAttachment(MimeUtility.encodeText(file.getName()), file);	
-				}
-			}*/
-
-			// 群发
-			//MimeMessage[] mailMessages = { mimeMessage };
-			
-			mailSender.send(mimeMessage);
+			email.send();				// send the email
 			return true;
-		} catch (Exception e) {
+		} catch (EmailException e) {
 			logger.error(e.getMessage(), e);
+
 		}
 		return false;
 	}
-	
-	static int total = 0;
-	public static void main(String[] args) {
-		
-		ExecutorService exec = Executors.newCachedThreadPool();
-		for (int i = 0; i < 1; i++) {
-			exec.execute(new Thread(new Runnable() {
-				@Override
-				public void run() {
-					while(total < 1){
-						String mailBody = "<html><head><meta http-equiv="
-								+ "Content-Type"
-								+ " content="
-								+ "text/html; charset=gb2312"
-								+ "></head><body><h1>新书快递通知</h1>你的新书快递申请已推送新书,请到<a href=''>空间"
-								+ "</a>中查看</body></html>";
-						
-						sendMail("931591021@qq.com", "测试邮件", mailBody, true, null);
-						System.out.println(total);
-						total++;
-					}
-				}
-			}));
-		}
-	}
-	
+
 }

+ 1 - 4
xxl-job-admin/src/main/java/com/xxl/job/admin/core/util/PropertiesUtil.java

@@ -12,6 +12,7 @@ import java.util.Properties;
 
 /**
  * properties util
+ *
  * @author xuxueli 2015-8-28 10:35:53
  */
 public class PropertiesUtil {
@@ -33,9 +34,5 @@ public class PropertiesUtil {
 		}
 		return null;
 	}
-	
-	public static void main(String[] args) {
-		System.out.println(getString("xxl.job.login.username"));
-	}
 
 }

+ 30 - 22
xxl-job-admin/src/main/java/com/xxl/job/admin/service/impl/AdminBizImpl.java

@@ -1,10 +1,7 @@
 package com.xxl.job.admin.service.impl;
 
-import com.xxl.job.admin.controller.JobApiController;
 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.trigger.XxlJobTrigger;
 import com.xxl.job.admin.dao.XxlJobInfoDao;
 import com.xxl.job.admin.dao.XxlJobLogDao;
 import com.xxl.job.admin.dao.XxlJobRegistryDao;
@@ -13,8 +10,8 @@ import com.xxl.job.core.biz.AdminBiz;
 import com.xxl.job.core.biz.model.HandleCallbackParam;
 import com.xxl.job.core.biz.model.RegistryParam;
 import com.xxl.job.core.biz.model.ReturnT;
-import org.apache.commons.lang.StringUtils;
-import org.quartz.SchedulerException;
+import com.xxl.job.core.handler.IJobHandler;
+import org.apache.commons.lang3.StringUtils;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 import org.springframework.stereotype.Service;
@@ -46,7 +43,7 @@ public class AdminBizImpl implements AdminBiz {
         for (HandleCallbackParam handleCallbackParam: callbackParamList) {
             ReturnT<String> callbackResult = callback(handleCallbackParam);
             logger.info(">>>>>>>>> JobApiController.callback {}, handleCallbackParam={}, callbackResult={}",
-                    (callbackResult.getCode()==ReturnT.SUCCESS_CODE?"success":"fail"), handleCallbackParam, callbackResult);
+                    (callbackResult.getCode()==IJobHandler.SUCCESS.getCode()?"success":"fail"), handleCallbackParam, callbackResult);
         }
 
         return ReturnT.SUCCESS;
@@ -58,28 +55,39 @@ public class AdminBizImpl implements AdminBiz {
         if (log == null) {
             return new ReturnT<String>(ReturnT.FAIL_CODE, "log item not found.");
         }
+        if (log.getHandleCode() > 0) {
+            return new ReturnT<String>(ReturnT.FAIL_CODE, "log repeate callback.");     // avoid repeat callback, trigger child job etc
+        }
 
-        // trigger success, to trigger child job, and avoid repeat trigger child job
-        String childTriggerMsg = null;
-        if (ReturnT.SUCCESS_CODE==handleCallbackParam.getExecuteResult().getCode() && ReturnT.SUCCESS_CODE!=log.getHandleCode()) {
+        // trigger success, to trigger child job
+        String callbackMsg = null;
+        if (IJobHandler.SUCCESS.getCode() == handleCallbackParam.getExecuteResult().getCode()) {
             XxlJobInfo xxlJobInfo = xxlJobInfoDao.loadById(log.getJobId());
-            if (xxlJobInfo!=null && StringUtils.isNotBlank(xxlJobInfo.getChildJobKey())) {
-                childTriggerMsg = "<hr>";
-                String[] childJobKeys = xxlJobInfo.getChildJobKey().split(",");
-                for (int i = 0; i < childJobKeys.length; i++) {
-                    String[] jobKeyArr = childJobKeys[i].split("_");
-                    if (jobKeyArr!=null && jobKeyArr.length==2) {
-                        ReturnT<String> triggerChildResult = xxlJobService.triggerJob(Integer.valueOf(jobKeyArr[1]));
+            if (xxlJobInfo!=null && StringUtils.isNotBlank(xxlJobInfo.getChildJobId())) {
+                callbackMsg = "<br><br><span style=\"color:#00c0ef;\" > >>>>>>>>>>>触发子任务<<<<<<<<<<< </span><br>";
+
+                String[] childJobIds = xxlJobInfo.getChildJobId().split(",");
+                for (int i = 0; i < childJobIds.length; i++) {
+                    int childJobId = (StringUtils.isNotBlank(childJobIds[i]) && StringUtils.isNumeric(childJobIds[i]))?Integer.valueOf(childJobIds[i]):-1;
+                    if (childJobId > 0) {
+                        ReturnT<String> triggerChildResult = xxlJobService.triggerJob(childJobId);
+
                         // add msg
-                        childTriggerMsg += MessageFormat.format("<br> {0}/{1} 触发子任务{2}, 子任务Key: {3}, 子任务触发备注: {4}",
-                                (i+1), childJobKeys.length, (triggerChildResult.getCode()==ReturnT.SUCCESS_CODE?"成功":"失败"), childJobKeys[i], triggerChildResult.getMsg());
+                        callbackMsg += MessageFormat.format("{0}/{1} [任务ID={2}], 触发{3}, 触发备注: {4} <br>",
+                                (i+1), childJobIds.length, childJobIds[i], (triggerChildResult.getCode()==ReturnT.SUCCESS_CODE?"成功":"失败"), triggerChildResult.getMsg());
                     } else {
-                        childTriggerMsg += MessageFormat.format("<br> {0}/{1} 触发子任务失败, 子任务Key格式错误, 子任务Key: {2}",
-                                (i+1), childJobKeys.length, childJobKeys[i]);
+                        callbackMsg += MessageFormat.format(" {0}/{1} [任务ID={2}], 触发失败, 触发备注: 任务ID格式错误 <br>",
+                                (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 += MessageFormat.format("触发{0}, 触发备注: {1}",
+                   (retryTriggerResult.getCode()==ReturnT.SUCCESS_CODE?"成功":"失败"), retryTriggerResult.getMsg());
         }
 
         // handle msg
@@ -90,8 +98,8 @@ public class AdminBizImpl implements AdminBiz {
         if (handleCallbackParam.getExecuteResult().getMsg() != null) {
             handleMsg.append(handleCallbackParam.getExecuteResult().getMsg());
         }
-        if (childTriggerMsg !=null) {
-            handleMsg.append("<br>子任务触发备注:").append(childTriggerMsg);
+        if (callbackMsg != null) {
+            handleMsg.append(callbackMsg);
         }
 
         // success, save log

+ 33 - 27
xxl-job-admin/src/main/java/com/xxl/job/admin/service/impl/XxlJobServiceImpl.java

@@ -13,10 +13,10 @@ import com.xxl.job.admin.service.XxlJobService;
 import com.xxl.job.core.biz.model.ReturnT;
 import com.xxl.job.core.enums.ExecutorBlockStrategyEnum;
 import com.xxl.job.core.glue.GlueTypeEnum;
-import org.apache.commons.collections.CollectionUtils;
-import org.apache.commons.lang.StringUtils;
-import org.apache.commons.lang.time.DateUtils;
-import org.apache.commons.lang.time.FastDateFormat;
+import org.apache.commons.collections4.CollectionUtils;
+import org.apache.commons.lang3.StringUtils;
+import org.apache.commons.lang3.time.DateUtils;
+import org.apache.commons.lang3.time.FastDateFormat;
 import org.quartz.CronExpression;
 import org.quartz.SchedulerException;
 import org.slf4j.Logger;
@@ -103,19 +103,20 @@ public class XxlJobServiceImpl implements XxlJobService {
 			jobInfo.setGlueSource(jobInfo.getGlueSource().replaceAll("\r", ""));
 		}
 
-		// childJobKey valid
-		if (StringUtils.isNotBlank(jobInfo.getChildJobKey())) {
-			String[] childJobKeys = jobInfo.getChildJobKey().split(",");
-			for (String childJobKeyItem: childJobKeys) {
-				String[] childJobKeyArr = childJobKeyItem.split("_");
-				if (childJobKeyArr.length!=2) {
-					return new ReturnT<String>(ReturnT.FAIL_CODE, MessageFormat.format("子任务Key({0})格式错误", childJobKeyItem));
-				}
-				XxlJobInfo childJobInfo = xxlJobInfoDao.loadById(Integer.valueOf(childJobKeyArr[1]));
-				if (childJobInfo==null) {
-					return new ReturnT<String>(ReturnT.FAIL_CODE, MessageFormat.format("子任务Key({0})无效", childJobKeyItem));
+		// ChildJobId valid
+		if (StringUtils.isNotBlank(jobInfo.getChildJobId())) {
+			String[] childJobIds = StringUtils.split(jobInfo.getChildJobId(), ",");
+			for (String childJobIdItem: childJobIds) {
+				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));
+					}
+				} else {
+					return new ReturnT<String>(ReturnT.FAIL_CODE, MessageFormat.format("子任务ID({0})格式错误", childJobIdItem));
 				}
 			}
+			jobInfo.setChildJobId(StringUtils.join(childJobIds, ","));
 		}
 
 		// add in db
@@ -166,19 +167,24 @@ public class XxlJobServiceImpl implements XxlJobService {
 			return new ReturnT<String>(ReturnT.FAIL_CODE, "失败处理策略非法");
 		}
 
-		// childJobKey valid
-		if (StringUtils.isNotBlank(jobInfo.getChildJobKey())) {
-			String[] childJobKeys = jobInfo.getChildJobKey().split(",");
-			for (String childJobKeyItem: childJobKeys) {
-				String[] childJobKeyArr = childJobKeyItem.split("_");
-				if (childJobKeyArr.length!=2) {
-					return new ReturnT<String>(ReturnT.FAIL_CODE, MessageFormat.format("子任务Key({0})格式错误", childJobKeyItem));
-				}
-                XxlJobInfo childJobInfo = xxlJobInfoDao.loadById(Integer.valueOf(childJobKeyArr[1]));
-				if (childJobInfo==null) {
-					return new ReturnT<String>(ReturnT.FAIL_CODE, MessageFormat.format("子任务Key({0})无效", childJobKeyItem));
+		// ChildJobId valid
+		if (StringUtils.isNotBlank(jobInfo.getChildJobId())) {
+			String[] childJobIds = StringUtils.split(jobInfo.getChildJobId(), ",");
+			for (String childJobIdItem: childJobIds) {
+				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));
+					}
+					// avoid cycle relate
+					if (childJobInfo.getId() == jobInfo.getId()) {
+						return new ReturnT<String>(ReturnT.FAIL_CODE, MessageFormat.format("子任务ID({0})不可与父任务重复", childJobIdItem));
+					}
+				} else {
+					return new ReturnT<String>(ReturnT.FAIL_CODE, MessageFormat.format("子任务ID({0})格式错误", childJobIdItem));
 				}
 			}
+			jobInfo.setChildJobId(StringUtils.join(childJobIds, ","));
 		}
 
 		// stage job info
@@ -197,7 +203,7 @@ public class XxlJobServiceImpl implements XxlJobService {
 		exists_jobInfo.setExecutorParam(jobInfo.getExecutorParam());
 		exists_jobInfo.setExecutorBlockStrategy(jobInfo.getExecutorBlockStrategy());
 		exists_jobInfo.setExecutorFailStrategy(jobInfo.getExecutorFailStrategy());
-		exists_jobInfo.setChildJobKey(jobInfo.getChildJobKey());
+		exists_jobInfo.setChildJobId(jobInfo.getChildJobId());
         xxlJobInfoDao.update(exists_jobInfo);
 
 		// fresh quartz

+ 5 - 5
xxl-job-admin/src/main/resources/mybatis-mapper/XxlJobInfoMapper.xml

@@ -27,7 +27,7 @@
 	    <result column="glue_remark" property="glueRemark" />
 		<result column="glue_updatetime" property="glueUpdatetime" />
 
-		<result column="child_jobkey" property="childJobKey" />
+		<result column="child_jobid" property="childJobId" />
 	</resultMap>
 
 	<sql id="Base_Column_List">
@@ -48,7 +48,7 @@
 		t.glue_source,
 		t.glue_remark,
 		t.glue_updatetime,
-		t.child_jobkey
+		t.child_jobid
 	</sql>
 	
 	<select id="pageList" parameterType="java.util.HashMap" resultMap="XxlJobInfo">
@@ -97,7 +97,7 @@
 			glue_source,
 			glue_remark,
 			glue_updatetime,
-			child_jobkey
+			child_jobid
 		) VALUES (
 			#{jobGroup},
 			#{jobCron}, 
@@ -115,7 +115,7 @@
 			#{glueSource},
 			#{glueRemark},
 			NOW(),
-			#{childJobKey}
+			#{childJobId}
 		);
 		<!--<selectKey resultType="java.lang.Integer" order="AFTER" keyProperty="id">
 			SELECT LAST_INSERT_ID()
@@ -146,7 +146,7 @@
 			glue_source = #{glueSource},
 			glue_remark = #{glueRemark},
 			glue_updatetime = #{glueUpdatetime},
-			child_jobkey = #{childJobKey}
+			child_jobid = #{childJobId}
 		WHERE id = #{id}
 	</update>
 	

+ 2 - 2
xxl-job-admin/src/main/resources/spring/applicationcontext-base.xml

@@ -3,9 +3,9 @@
 	   xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
 	   xmlns:context="http://www.springframework.org/schema/context"
 	   xsi:schemaLocation="http://www.springframework.org/schema/beans
-           http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
+           http://www.springframework.org/schema/beans/spring-beans.xsd
            http://www.springframework.org/schema/context
-           http://www.springframework.org/schema/context/spring-context-3.0.xsd">
+           http://www.springframework.org/schema/context/spring-context.xsd">
 
 	<context:component-scan base-package="com.xxl.job.admin.service, com.xxl.job.admin.dao" />
 

+ 2 - 2
xxl-job-admin/src/main/resources/spring/applicationcontext-xxl-job-admin.xml

@@ -3,9 +3,9 @@
 	   xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
 	   xmlns:tx="http://www.springframework.org/schema/tx"
 	   xsi:schemaLocation="http://www.springframework.org/schema/beans
-		http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
+		http://www.springframework.org/schema/beans/spring-beans.xsd
 		http://www.springframework.org/schema/tx 
-		http://www.springframework.org/schema/tx/spring-tx-3.0.xsd">
+		http://www.springframework.org/schema/tx/spring-tx.xsd">
 	
 	
 	<bean id="propertyConfigurer" class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">

+ 3 - 3
xxl-job-admin/src/main/resources/spring/springmvc-context.xml

@@ -4,11 +4,11 @@
 	   xmlns:mvc="http://www.springframework.org/schema/mvc"
 	   xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
 	   xsi:schemaLocation="http://www.springframework.org/schema/beans
-		http://www.springframework.org/schema/beans/spring-beans-3.0.xsd 
+		http://www.springframework.org/schema/beans/spring-beans.xsd
 		http://www.springframework.org/schema/context 
-		http://www.springframework.org/schema/context/spring-context-3.0.xsd
+		http://www.springframework.org/schema/context/spring-context.xsd
 		http://www.springframework.org/schema/mvc
-		http://www.springframework.org/schema/mvc/spring-mvc-3.0.xsd">
+		http://www.springframework.org/schema/mvc/spring-mvc.xsd">
 
 	<mvc:annotation-driven />
 	<context:component-scan base-package="com.xxl.job.admin.controller" />

+ 20 - 3
xxl-job-admin/src/main/webapp/WEB-INF/template/jobgroup/jobgroup.index.ftl

@@ -53,7 +53,20 @@
                                         <td>${group.appName}</td>
                                         <td>${group.title}</td>
                                         <td><#if group.addressType==0>自动注册<#else>手动录入</#if></td>
-                                        <td><#if group.registryList?exists><#list group.registryList as item><span class="badge bg-green">${item}</span><br></#list></#if></td>
+                                        <td>
+                                            <#if group.registryList?exists>
+                                                <#list group.registryList as item>
+                                                    <span class="badge bg-green" title="${item}" >
+                                                        <#if item?length gt 35>
+                                                            ${item?substring(0, 35)}...
+                                                        <#else>
+                                                            ${item}
+                                                        </#if>
+                                                    </span>
+                                                    <br>
+                                                </#list>
+                                            </#if>
+                                        </td>
 										<td>
                                             <button class="btn btn-warning btn-xs update"
                                                     id="${group.id}"
@@ -107,7 +120,9 @@
                         </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="addressList" placeholder="请输入执行器地址列表,多地址逗号分隔" maxlength="200" readonly="readonly" ></div>
+                            <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>
+                            </div>
                         </div>
                         <hr>
                         <div class="form-group">
@@ -153,7 +168,9 @@
                         </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="addressList" placeholder="请输入执行器地址列表,多地址逗号分隔" maxlength="200" readonly="readonly" ></div>
+                            <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>
+                            </div>
                         </div>
                         <hr>
                         <div class="form-group">

+ 27 - 17
xxl-job-admin/src/main/webapp/WEB-INF/template/jobinfo/jobinfo.index.ftl

@@ -63,12 +63,11 @@
 			            	<h3 class="box-title">调度列表</h3>
 			            </div>
 			            <div class="box-body" >
-			              	<table id="job_list" class="table table-bordered table-striped">
+			              	<table id="job_list" class="table table-bordered table-striped" width="100%" >
 				                <thead>
 					            	<tr>
-					            		<th name="id" >id</th>
+					            		<th name="id" >任务ID</th>
 					                	<th name="jobGroup" >jobGroup</th>
-                                        <th name="childJobKey" >JobKey</th>
 					                  	<th name="jobDesc" >描述</th>
                                         <th name="glueType" >运行模式</th>
 					                  	<th name="executorParam" >任务参数</th>
@@ -143,9 +142,9 @@
                     </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="100" ></div>
-                        <label for="lastname" class="col-sm-2 control-label">子任务Key<font color="black">*</font></label>
-                        <div class="col-sm-4"><input type="text" class="form-control" name="childJobKey" placeholder="请输入子任务的任务Key,如存在多个逗号分隔" maxlength="100" ></div>
+                        <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>
                     </div>
                     <div class="form-group">
                         <label for="firstname" class="col-sm-2 control-label">阻塞处理策略<font color="red">*</font></label>
@@ -192,7 +191,7 @@ import com.xxl.job.core.handler.IJobHandler;
 public class DemoGlueJobHandler extends IJobHandler {
 
 	@Override
-	public ReturnT<String> execute(String... params) throws Exception {
+	public ReturnT<String> execute(String param) throws Exception {
 		XxlJobLogger.log("XXL-JOB, Hello World.");
 		return ReturnT.SUCCESS;
 	}
@@ -204,12 +203,15 @@ public class DemoGlueJobHandler extends IJobHandler {
 echo "xxl-job: hello shell"
 
 echo "脚本位置:$0"
-echo "参数数量:$#"
+echo "任务参数:$1"
+echo "分片序号 = $2"
+echo "分片总数 = $3"
+<#--echo "参数数量:$#"
 for param in $*
 do
     echo "参数 : $param"
     sleep 1s
-done
+done-->
 
 echo "Good bye!"
 exit 0
@@ -221,13 +223,18 @@ import time
 import sys
 
 print "xxl-job: hello python"
+
 print "脚本文件:", sys.argv[0]
-for i in range(1, len(sys.argv)):
+print "任务参数:", sys.argv[1]
+print "分片序号:", sys.argv[2]
+print "分片总数:", sys.argv[3]
+<#--for i in range(1, len(sys.argv)):
 	time.sleep(1)
-	print "参数", i, sys.argv[i]
+	print "参数", i, sys.argv[i]-->
 
 print "Good bye!"
-exit(0)<#--
+exit(0)
+<#--
 import logging
 logging.basicConfig(level=logging.DEBUG)
 logging.info("脚本文件:" + sys.argv[0])
@@ -240,9 +247,12 @@ console.log("xxl-job: hello nodejs")
 var arguments = process.argv
 
 console.log("脚本文件: " + arguments[1])
-for (var i = 2; i < arguments.length; i++){
+console.log("任务参数: " + arguments[2])
+console.log("分片序号: " + arguments[3])
+console.log("分片总数: " + arguments[4])
+<#--for (var i = 2; i < arguments.length; i++){
 	console.log("参数 %s = %s", (i-1), arguments[i]);
-}
+}-->
 
 console.log("Good bye!")
 process.exit(0)
@@ -300,9 +310,9 @@ process.exit(0)
                     </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="100" ></div>
-                        <label for="lastname" class="col-sm-2 control-label">子任务Key<font color="black">*</font></label>
-                        <div class="col-sm-4"><input type="text" class="form-control" name="childJobKey" placeholder="请输入子任务的任务Key,如存在多个逗号分隔" maxlength="100" ></div>
+                        <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>
                     </div>
                     <div class="form-group">
                         <label for="firstname" class="col-sm-2 control-label">阻塞处理策略<font color="red">*</font></label>

+ 1 - 3
xxl-job-admin/src/main/webapp/WEB-INF/template/joblog/joblog.index.ftl

@@ -90,10 +90,8 @@
 			              	<table id="joblog_list" class="table table-bordered table-striped display" width="100%" >
 				                <thead>
 					            	<tr>
-					                	<th name="id" >id</th>
+                                        <th name="jobId" >任务ID</th>
                                         <th name="jobGroup" >执行器ID</th>
-					                	<th name="jobId" >任务ID</th>
-                                        <th name="JobKey" >JobKey</th>
 										<#--<th name="executorAddress" >执行器地址</th>
 										<th name="glueType" >运行模式</th>
                                       	<th name="executorParam" >任务参数</th>-->

+ 6 - 2
xxl-job-admin/src/main/webapp/static/js/common.1.js

@@ -7,14 +7,18 @@ $(function(){
 
 			$.post(base_url + "/logout", function(data, status) {
 				if (data.code == "200") {
-					layer.open({
+                    layer.msg('注销成功');
+                    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: '系统提示',

+ 5 - 3
xxl-job-admin/src/main/webapp/static/js/jobgroup.index.1.js

@@ -122,11 +122,13 @@ $(function() {
 	// 注册方式,切换
 	$("#addModal input[name=addressType], #updateModal input[name=addressType]").click(function(){
 		var addressType = $(this).val();
-		var $addressList = $(this).parents("form").find("input[name=addressList]");
+		var $addressList = $(this).parents("form").find("textarea[name=addressList]");
 		if (addressType == 0) {
+            $addressList.css("background-color", "#eee");	// 自动注册
+            $addressList.attr("readonly","readonly");
 			$addressList.val("");
-			$addressList.attr("readonly","readonly");
 		} else {
+            $addressList.css("background-color", "white");
 			$addressList.removeAttr("readonly");
 		}
 	});
@@ -144,7 +146,7 @@ $(function() {
 		//$("#updateModal .form input[name='addressType'][value='"+ addressType +"']").attr('checked', 'true');
 		$("#updateModal .form input[name='addressType'][value='"+ addressType +"']").click();
 		// 机器地址
-		$("#updateModal .form input[name='addressList']").val($(this).attr("addressList"));
+		$("#updateModal .form textarea[name='addressList']").val($(this).attr("addressList"));
 
 		$('#updateModal').modal({backdrop: false, keyboard: false}).modal('show');
 	});

+ 2 - 11
xxl-job-admin/src/main/webapp/static/js/jobinfo.index.1.js

@@ -20,7 +20,7 @@ $(function() {
 	    "ordering": false,
 	    //"scrollX": true,	// X轴滚动条,取消自适应
 	    "columns": [
-	                { "data": 'id', "bSortable": false, "visible" : false},
+	                { "data": 'id', "bSortable": false, "visible" : true},
 	                { 
 	                	"data": 'jobGroup', 
 	                	"visible" : false,
@@ -34,15 +34,6 @@ $(function() {
 	            			return data;
 	            		}
             		},
-					{
-						"data": 'childJobKey',
-						"width":'10%',
-						"visible" : true,
-						"render": function ( data, type, row ) {
-							var jobKey = row.jobGroup + "_" + row.id;
-							return jobKey;
-						}
-					},
 	                { "data": 'jobDesc', "visible" : true,"width":'20%'},
 					{
 						"data": 'glueType',
@@ -372,7 +363,7 @@ $(function() {
 		$('#updateModal .form select[name=executorRouteStrategy] option[value='+ row.executorRouteStrategy +']').prop('selected', true);
 		$("#updateModal .form input[name='executorHandler']").val( row.executorHandler );
 		$("#updateModal .form input[name='executorParam']").val( row.executorParam );
-        $("#updateModal .form input[name='childJobKey']").val( row.childJobKey );
+        $("#updateModal .form input[name='childJobId']").val( row.childJobId );
 		$('#updateModal .form select[name=executorBlockStrategy] option[value='+ row.executorBlockStrategy +']').prop('selected', true);
 		$('#updateModal .form select[name=executorFailStrategy] option[value='+ row.executorFailStrategy +']').prop('selected', true);
 		$('#updateModal .form select[name=glueType] option[value='+ row.glueType +']').prop('selected', true);

+ 39 - 44
xxl-job-admin/src/main/webapp/static/js/joblog.index.1.js

@@ -89,54 +89,32 @@ $(function() {
 	    "ordering": false,
 	    //"scrollX": false,
 	    "columns": [
-	                { "data": 'id', "bSortable": false, "visible" : false},
-					{ "data": 'jobGroup', "visible" : false},
-	                { "data": 'jobId', "visible" : false},
 					{
-						"data": 'JobKey',
+						"data": 'jobId',
 						"visible" : true,
 						"render": function ( data, type, row ) {
-							var jobKey = row.jobGroup + "_" + row.jobId;
-
-                            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 = 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 temp = '';
-                            temp += '执行器地址:' + (row.executorAddress?row.executorAddress:'');
-                            temp += '<br>运行模式:' + glueTypeTitle;
-                            temp += '<br>任务参数:' + row.executorParam;
+							var temp = '';
+							temp += '执行器地址:' + (row.executorAddress?row.executorAddress:'');
+							temp += '<br>运行模式:' + glueTypeTitle;
+							temp += '<br>任务参数:' + row.executorParam;
 
-                            return '<a class="logTips" href="javascript:;" >'+ jobKey +'<span style="display:none;">'+ temp +'</span></a>';
+							return '<a class="logTips" href="javascript:;" >'+ row.jobId +'<span style="display:none;">'+ temp +'</span></a>';
 						}
 					},
-					// { "data": 'executorAddress', "visible" : true},
-					// {
-					// 	"data": 'glueType',
-					//  	"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 ('BEAN'==row.glueType) {
-					// 		 	return "BEAN模式:" + row.executorHandler;
-					// 		}
-					// 		return row.executorHandler;
-					// 	 }
-					// },
-					// { "data": 'executorParam', "visible" : true},
+					{ "data": 'jobGroup', "visible" : false},
 					{
 						"data": 'triggerTime',
 						"render": function ( data, type, row ) {
@@ -146,9 +124,16 @@ $(function() {
 					{
 						"data": 'triggerCode',
 						"render": function ( data, type, row ) {
-							return (data==200)?'<span style="color: green">成功</span>':(data==500)?'<span style="color: red">失败</span>':(data==0)?'':data;
+							var html = data;
+							if (data == 200) {
+								html = '<span style="color: green">成功</span>';
+							} else if (data == 500) {
+								html = '<span style="color: red">失败</span>';
+							} else if (data == 0) {
+                                html = '';
+							}
+                            return html;
 						}
-
 					},
 					{
 						"data": 'triggerMsg',
@@ -165,7 +150,17 @@ $(function() {
 	                {
 						"data": 'handleCode',
 						"render": function ( data, type, row ) {
-							return (data==200)?'<span style="color: green">成功</span>':(data==500)?'<span style="color: red">失败</span>':(data==0)?'':data;
+                            var html = data;
+                            if (data == 200) {
+                                html = '<span style="color: green">成功</span>';
+                            } else if (data == 500) {
+                                html = '<span style="color: red">失败</span>';
+                            } else if (data == 501) {
+                                html = '<span style="color: red">失败重试</span>';
+                            } else if (data == 0) {
+                                html = '';
+                            }
+                            return html;
 						}
 	                },
 	                { 

+ 6 - 2
xxl-job-admin/src/main/webapp/static/js/login.1.js

@@ -48,14 +48,18 @@ $(function(){
         submitHandler : function(form) {
 			$.post(base_url + "/login", $("#loginForm").serialize(), function(data, status) {
 				if (data.code == "200") {
-                    layer.open({
+                    layer.msg('登录成功');
+                    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: '系统提示',

+ 27 - 0
xxl-job-admin/src/test/java/com/xxl/job/admin/controller/AbstractSpringMvcTest.java

@@ -0,0 +1,27 @@
+package com.xxl.job.admin.controller;
+
+import org.junit.Before;
+import org.junit.runner.RunWith;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.test.context.ContextConfiguration;
+import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
+import org.springframework.test.context.web.WebAppConfiguration;
+import org.springframework.test.web.servlet.MockMvc;
+import org.springframework.test.web.servlet.setup.MockMvcBuilders;
+import org.springframework.web.context.WebApplicationContext;
+
+@WebAppConfiguration
+@RunWith(SpringJUnit4ClassRunner.class)
+@ContextConfiguration(locations = {"classpath*:spring/*.xml"})
+public class AbstractSpringMvcTest {
+
+  @Autowired
+  private WebApplicationContext applicationContext;
+  protected MockMvc mockMvc;
+
+  @Before
+  public void setup() {
+    this.mockMvc = MockMvcBuilders.webAppContextSetup(this.applicationContext).build();
+  }
+
+}

+ 47 - 0
xxl-job-admin/src/test/java/com/xxl/job/admin/controller/JobInfoControllerTest.java

@@ -0,0 +1,47 @@
+package com.xxl.job.admin.controller;
+
+import com.xxl.job.admin.controller.interceptor.PermissionInterceptor;
+import com.xxl.job.admin.core.util.PropertiesUtil;
+import org.junit.Before;
+import org.junit.Test;
+import org.springframework.http.MediaType;
+import org.springframework.test.web.servlet.MvcResult;
+import org.springframework.util.LinkedMultiValueMap;
+import org.springframework.util.MultiValueMap;
+
+import javax.servlet.http.Cookie;
+
+import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post;
+
+public class JobInfoControllerTest extends AbstractSpringMvcTest {
+
+  private Cookie cookie;
+
+  @Before
+  public void login() throws Exception {
+    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"))
+    ).andReturn();
+    cookie = ret.getResponse().getCookie(PermissionInterceptor.LOGIN_IDENTITY_KEY);
+  }
+
+  @Test
+  public void testAdd() throws Exception {
+    MultiValueMap<String, String> parameters = new LinkedMultiValueMap<String, String>();
+    parameters.add("jobGroup", "1");
+
+    MvcResult ret = mockMvc.perform(
+        post("/jobinfo/pageList")
+            .contentType(MediaType.APPLICATION_FORM_URLENCODED)
+            //.content(paramsJson)
+            .params(parameters)
+            .cookie(cookie)
+    ).andReturn();
+
+    System.out.println(ret.getResponse().getContentAsString());
+  }
+
+}

+ 1 - 1
xxl-job-admin/src/test/java/com/xxl/job/dao/impl/XxlJobGroupDaoTest.java → xxl-job-admin/src/test/java/com/xxl/job/admin/dao/XxlJobGroupDaoTest.java

@@ -1,4 +1,4 @@
-package com.xxl.job.dao.impl;
+package com.xxl.job.admin.dao;
 
 import com.xxl.job.admin.core.model.XxlJobGroup;
 import com.xxl.job.admin.dao.XxlJobGroupDao;

+ 78 - 78
xxl-job-admin/src/test/java/com/xxl/job/dao/impl/XxlJobInfoTest.java → xxl-job-admin/src/test/java/com/xxl/job/admin/dao/XxlJobInfoDaoTest.java

@@ -1,78 +1,78 @@
-package com.xxl.job.dao.impl;
-
-import com.xxl.job.admin.core.model.XxlJobInfo;
-import com.xxl.job.admin.dao.XxlJobInfoDao;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.springframework.test.context.ContextConfiguration;
-import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
-
-import javax.annotation.Resource;
-import java.util.Date;
-import java.util.List;
-
-@RunWith(SpringJUnit4ClassRunner.class)
-@ContextConfiguration(locations = "classpath*:spring/applicationcontext-*.xml")
-public class XxlJobInfoTest {
-	
-	@Resource
-	private XxlJobInfoDao xxlJobInfoDao;
-	
-	@Test
-	public void pageList(){
-		List<XxlJobInfo> list = xxlJobInfoDao.pageList(0, 20, 0, null);
-		int list_count = xxlJobInfoDao.pageListCount(0, 20, 0, null);
-		
-		System.out.println(list);
-		System.out.println(list_count);
-
-		List<XxlJobInfo> list2 = xxlJobInfoDao.getJobsByGroup(1);
-	}
-	
-	@Test
-	public void save_load(){
-		XxlJobInfo info = new XxlJobInfo();
-		info.setJobGroup(1);
-		info.setJobCron("jobCron");
-		info.setJobDesc("desc");
-		info.setAuthor("setAuthor");
-		info.setAlarmEmail("setAlarmEmail");
-		info.setExecutorRouteStrategy("setExecutorRouteStrategy");
-		info.setExecutorHandler("setExecutorHandler");
-		info.setExecutorParam("setExecutorParam");
-		info.setExecutorBlockStrategy("setExecutorBlockStrategy");
-		info.setExecutorFailStrategy("setExecutorFailStrategy");
-		info.setGlueType("setGlueType");
-		info.setGlueSource("setGlueSource");
-		info.setGlueRemark("setGlueRemark");
-		info.setChildJobKey("setChildJobKey");
-
-		int count = xxlJobInfoDao.save(info);
-
-		XxlJobInfo info2 = xxlJobInfoDao.loadById(info.getId());
-		info2.setJobCron("jobCron2");
-		info2.setJobDesc("desc2");
-		info2.setAuthor("setAuthor2");
-		info2.setAlarmEmail("setAlarmEmail2");
-		info2.setExecutorRouteStrategy("setExecutorRouteStrategy2");
-		info2.setExecutorHandler("setExecutorHandler2");
-		info2.setExecutorParam("setExecutorParam2");
-		info2.setExecutorBlockStrategy("setExecutorBlockStrategy2");
-		info2.setExecutorFailStrategy("setExecutorFailStrategy2");
-		info2.setGlueType("setGlueType2");
-		info2.setGlueSource("setGlueSource2");
-		info2.setGlueRemark("setGlueRemark2");
-		info2.setGlueUpdatetime(new Date());
-		info2.setChildJobKey("setChildJobKey2");
-
-		int item2 = xxlJobInfoDao.update(info2);
-
-		xxlJobInfoDao.delete(info2.getId());
-
-		List<XxlJobInfo> list2 = xxlJobInfoDao.getJobsByGroup(1);
-
-		int ret3 = xxlJobInfoDao.findAllCount();
-
-	}
-
-}
+package com.xxl.job.admin.dao;
+
+import com.xxl.job.admin.core.model.XxlJobInfo;
+import com.xxl.job.admin.dao.XxlJobInfoDao;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.springframework.test.context.ContextConfiguration;
+import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
+
+import javax.annotation.Resource;
+import java.util.Date;
+import java.util.List;
+
+@RunWith(SpringJUnit4ClassRunner.class)
+@ContextConfiguration(locations = "classpath*:spring/applicationcontext-*.xml")
+public class XxlJobInfoDaoTest {
+	
+	@Resource
+	private XxlJobInfoDao xxlJobInfoDao;
+	
+	@Test
+	public void pageList(){
+		List<XxlJobInfo> list = xxlJobInfoDao.pageList(0, 20, 0, null);
+		int list_count = xxlJobInfoDao.pageListCount(0, 20, 0, null);
+		
+		System.out.println(list);
+		System.out.println(list_count);
+
+		List<XxlJobInfo> list2 = xxlJobInfoDao.getJobsByGroup(1);
+	}
+	
+	@Test
+	public void save_load(){
+		XxlJobInfo info = new XxlJobInfo();
+		info.setJobGroup(1);
+		info.setJobCron("jobCron");
+		info.setJobDesc("desc");
+		info.setAuthor("setAuthor");
+		info.setAlarmEmail("setAlarmEmail");
+		info.setExecutorRouteStrategy("setExecutorRouteStrategy");
+		info.setExecutorHandler("setExecutorHandler");
+		info.setExecutorParam("setExecutorParam");
+		info.setExecutorBlockStrategy("setExecutorBlockStrategy");
+		info.setExecutorFailStrategy("setExecutorFailStrategy");
+		info.setGlueType("setGlueType");
+		info.setGlueSource("setGlueSource");
+		info.setGlueRemark("setGlueRemark");
+		info.setChildJobId("1");
+
+		int count = xxlJobInfoDao.save(info);
+
+		XxlJobInfo info2 = xxlJobInfoDao.loadById(info.getId());
+		info2.setJobCron("jobCron2");
+		info2.setJobDesc("desc2");
+		info2.setAuthor("setAuthor2");
+		info2.setAlarmEmail("setAlarmEmail2");
+		info2.setExecutorRouteStrategy("setExecutorRouteStrategy2");
+		info2.setExecutorHandler("setExecutorHandler2");
+		info2.setExecutorParam("setExecutorParam2");
+		info2.setExecutorBlockStrategy("setExecutorBlockStrategy2");
+		info2.setExecutorFailStrategy("setExecutorFailStrategy2");
+		info2.setGlueType("setGlueType2");
+		info2.setGlueSource("setGlueSource2");
+		info2.setGlueRemark("setGlueRemark2");
+		info2.setGlueUpdatetime(new Date());
+		info2.setChildJobId("1");
+
+		int item2 = xxlJobInfoDao.update(info2);
+
+		xxlJobInfoDao.delete(info2.getId());
+
+		List<XxlJobInfo> list2 = xxlJobInfoDao.getJobsByGroup(1);
+
+		int ret3 = xxlJobInfoDao.findAllCount();
+
+	}
+
+}

+ 2 - 2
xxl-job-admin/src/test/java/com/xxl/job/dao/impl/XxlJobLogDaoTest.java → xxl-job-admin/src/test/java/com/xxl/job/admin/dao/XxlJobLogDaoTest.java

@@ -1,8 +1,8 @@
-package com.xxl.job.dao.impl;
+package com.xxl.job.admin.dao;
 
 import com.xxl.job.admin.core.model.XxlJobLog;
 import com.xxl.job.admin.dao.XxlJobLogDao;
-import org.apache.commons.lang.time.DateUtils;
+import org.apache.commons.lang3.time.DateUtils;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 import org.springframework.test.context.ContextConfiguration;

+ 1 - 1
xxl-job-admin/src/test/java/com/xxl/job/dao/impl/XxlJobLogGlueDaoTest.java → xxl-job-admin/src/test/java/com/xxl/job/admin/dao/XxlJobLogGlueDaoTest.java

@@ -1,4 +1,4 @@
-package com.xxl.job.dao.impl;
+package com.xxl.job.admin.dao;
 
 import com.xxl.job.admin.core.model.XxlJobLogGlue;
 import com.xxl.job.admin.dao.XxlJobLogGlueDao;

+ 1 - 1
xxl-job-admin/src/test/java/com/xxl/job/dao/impl/XxlJobRegistryDaoTest.java → xxl-job-admin/src/test/java/com/xxl/job/admin/dao/XxlJobRegistryDaoTest.java

@@ -1,4 +1,4 @@
-package com.xxl.job.dao.impl;
+package com.xxl.job.admin.dao;
 
 import com.xxl.job.admin.core.model.XxlJobRegistry;
 import com.xxl.job.admin.dao.XxlJobRegistryDao;

+ 45 - 0
xxl-job-admin/src/test/java/com/xxl/job/admin/util/MailUtilTest.java

@@ -0,0 +1,45 @@
+package com.xxl.job.admin.util;
+
+import com.xxl.job.admin.core.util.MailUtil;
+import org.junit.Test;
+
+import java.text.MessageFormat;
+
+/**
+ * email util test
+ *
+ * @author xuxueli 2017-12-22 17:16:23
+ */
+public class MailUtilTest {
+
+    @Test
+    public void registryTest() throws Exception {
+
+        String mailBodyTemplate = "<h5>监控告警明细:</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" +
+                "      </tr>\n" +
+                "   <thead/>\n" +
+                "   <tbody>\n" +
+                "      <tr>\n" +
+                "         <td>{0}</td>\n" +
+                "         <td>{1}</td>\n" +
+                "         <td>{2}</td>\n" +
+                "         <td>调度失败</td>\n" +
+                "      </tr>\n" +
+                "   <tbody>\n" +
+                "</table>";
+
+        String title = "调度中心监控报警";
+        String content = MessageFormat.format(mailBodyTemplate, "执行器A", "01", "任务A1");
+
+        boolean ret = MailUtil.sendMail("931591021@qq.com", title, content);
+        System.out.println(ret);
+    }
+
+}

+ 18 - 0
xxl-job-admin/src/test/java/com/xxl/job/admin/util/PropertiesUtilTest.java

@@ -0,0 +1,18 @@
+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"));
+    }
+
+}

+ 1 - 1
xxl-job-admin/src/test/java/com/xxl/job/dao/impl/AdminBizTest.java → xxl-job-admin/src/test/java/com/xxl/job/adminbiz/AdminBizTest.java

@@ -1,4 +1,4 @@
-package com.xxl.job.dao.impl;
+package com.xxl.job.adminbiz;
 
 import com.xxl.job.core.biz.AdminBiz;
 import com.xxl.job.core.biz.model.RegistryParam;

+ 4 - 4
xxl-job-core/pom.xml

@@ -49,11 +49,11 @@
 
 		<!-- jackson -->
 		<dependency>
-			<groupId>org.codehaus.jackson</groupId>
-			<artifactId>jackson-mapper-asl</artifactId>
-			<version>${jackson-mapper-asl.version}</version>
+			<groupId>com.fasterxml.jackson.core</groupId>
+			<artifactId>jackson-databind</artifactId>
+			<version>${jackson.version}</version>
 		</dependency>
-		
+
 		<!-- httpclient -->
 		<dependency>
 			<groupId>org.apache.httpcomponents</groupId>

+ 5 - 6
xxl-job-core/src/main/java/com/xxl/job/core/biz/impl/ExecutorBizImpl.java

@@ -59,7 +59,7 @@ public class ExecutorBizImpl implements ExecutorBiz {
 
     @Override
     public ReturnT<LogResult> log(long logDateTim, int logId, int fromLineNum) {
-        // log filename: yyyy-MM-dd/9999.log
+        // log filename: logPath/yyyy-MM-dd/9999.log
         String logFileName = XxlJobFileAppender.makeLogFileName(new Date(logDateTim), logId);
 
         LogResult logResult = XxlJobFileAppender.readLog(logFileName, fromLineNum);
@@ -74,7 +74,8 @@ public class ExecutorBizImpl implements ExecutorBiz {
         String removeOldReason = null;
 
         // valid:jobHandler + jobThread
-        if (GlueTypeEnum.BEAN==GlueTypeEnum.match(triggerParam.getGlueType())) {
+        GlueTypeEnum glueTypeEnum = GlueTypeEnum.match(triggerParam.getGlueType());
+        if (GlueTypeEnum.BEAN == glueTypeEnum) {
 
             // new jobhandler
             IJobHandler newJobHandler = XxlJobExecutor.loadJobHandler(triggerParam.getExecutorHandler());
@@ -96,7 +97,7 @@ public class ExecutorBizImpl implements ExecutorBiz {
                 }
             }
 
-        } else if (GlueTypeEnum.GLUE_GROOVY==GlueTypeEnum.match(triggerParam.getGlueType())) {
+        } else if (GlueTypeEnum.GLUE_GROOVY == glueTypeEnum) {
 
             // valid old jobThread
             if (jobThread != null &&
@@ -119,9 +120,7 @@ public class ExecutorBizImpl implements ExecutorBiz {
                     return new ReturnT<String>(ReturnT.FAIL_CODE, e.getMessage());
                 }
             }
-        } else if (GlueTypeEnum.GLUE_SHELL==GlueTypeEnum.match(triggerParam.getGlueType())
-                || GlueTypeEnum.GLUE_PYTHON==GlueTypeEnum.match(triggerParam.getGlueType())
-                || GlueTypeEnum.GLUE_NODEJS==GlueTypeEnum.match(triggerParam.getGlueType())) {
+        } else if (glueTypeEnum!=null && glueTypeEnum.isScript()) {
 
             // valid old jobThread
             if (jobThread != null &&

+ 6 - 6
xxl-job-core/src/main/java/com/xxl/job/core/executor/XxlJobExecutor.java

@@ -72,14 +72,10 @@ public class XxlJobExecutor implements ApplicationContextAware {
         initAdminBizList(adminAddresses, accessToken);
 
         // init executor-jobHandlerRepository
-        if (applicationContext != null) {
-            initJobHandlerRepository(applicationContext);
-        }
+        initJobHandlerRepository(applicationContext);
 
         // init logpath
-        if (logPath!=null && logPath.trim().length()>0) {
-            XxlJobFileAppender.logPath = logPath;
-        }
+        XxlJobFileAppender.initLogPath(logPath);
 
         // init executor-server
         initExecutorServer(port, ip, appName, accessToken);
@@ -145,6 +141,10 @@ public class XxlJobExecutor implements ApplicationContextAware {
         return jobHandlerRepository.get(name);
     }
     private static void initJobHandlerRepository(ApplicationContext applicationContext){
+        if (applicationContext == null) {
+            return;
+        }
+
         // init job handler action
         Map<String, Object> serviceBeanMap = applicationContext.getBeansWithAnnotation(JobHandler.class);
 

+ 27 - 6
xxl-job-core/src/main/java/com/xxl/job/core/glue/GlueTypeEnum.java

@@ -5,20 +5,40 @@ package com.xxl.job.core.glue;
  */
 public enum GlueTypeEnum {
 
-    BEAN("BEAN模式"),
-    GLUE_GROOVY("GLUE模式(Java)"),
-    GLUE_SHELL("GLUE模式(Shell)"),
-    GLUE_PYTHON("GLUE模式(Python)"),
-    GLUE_NODEJS("GLUE模式(Nodejs)");
+    BEAN("BEAN模式", false, null, null),
+    GLUE_GROOVY("GLUE模式(Java)", false, null, null),
+    GLUE_SHELL("GLUE模式(Shell)", true, "bash", ".sh"),
+    GLUE_PYTHON("GLUE模式(Python)", true, "python", ".py"),
+    GLUE_NODEJS("GLUE模式(Nodejs)", true, "node", ".js");
 
     private String desc;
-    private GlueTypeEnum(String desc) {
+    private boolean isScript;
+    private String cmd;
+    private String suffix;
+
+    private GlueTypeEnum(String desc, boolean isScript, String cmd, String suffix) {
         this.desc = desc;
+        this.isScript = isScript;
+        this.cmd = cmd;
+        this.suffix = suffix;
     }
+
     public String getDesc() {
         return desc;
     }
 
+    public boolean isScript() {
+        return isScript;
+    }
+
+    public String getCmd() {
+        return cmd;
+    }
+
+    public String getSuffix() {
+        return suffix;
+    }
+
     public static GlueTypeEnum match(String name){
         for (GlueTypeEnum item: GlueTypeEnum.values()) {
             if (item.name().equals(name)) {
@@ -27,4 +47,5 @@ public enum GlueTypeEnum {
         }
         return null;
     }
+
 }

+ 33 - 5
xxl-job-core/src/main/java/com/xxl/job/core/handler/IJobHandler.java

@@ -3,17 +3,45 @@ package com.xxl.job.core.handler;
 import com.xxl.job.core.biz.model.ReturnT;
 
 /**
- * remote job handler
+ * job handler
+ *
  * @author xuxueli 2015-12-19 19:06:38
  */
 public abstract class IJobHandler {
 
+
+	/** success */
+	public static final ReturnT<String> SUCCESS = new ReturnT<String>(200, null);
+	/** fail */
+	public static final ReturnT<String> FAIL = new ReturnT<String>(500, null);
+	/** fail retry */
+	public static final ReturnT<String> FAIL_RETRY = new ReturnT<String>(501, null);
+
+
 	/**
-	 * job handler
-	 * @param params
+	 * execute handler, invoked when executor receives a scheduling request
+	 *
+	 * @param param
 	 * @return
 	 * @throws Exception
 	 */
-	public abstract ReturnT<String> execute(String... params) throws Exception;
-	
+	public abstract ReturnT<String> execute(String param) throws Exception;
+
+
+	/**
+	 * init handler, invoked when JobThread init
+	 */
+	public void init() {
+		// TODO
+	}
+
+
+	/**
+	 * destroy handler, invoked when JobThread destroy
+	 */
+	public void destroy() {
+		// TODO
+	}
+
+
 }

+ 2 - 2
xxl-job-core/src/main/java/com/xxl/job/core/handler/impl/GlueJobHandler.java

@@ -21,9 +21,9 @@ public class GlueJobHandler extends IJobHandler {
 	}
 
 	@Override
-	public ReturnT<String> execute(String... params) throws Exception {
+	public ReturnT<String> execute(String param) throws Exception {
 		XxlJobLogger.log("----------- glue.version:"+ glueUpdatetime +" -----------");
-		return jobHandler.execute(params);
+		return jobHandler.execute(param);
 	}
 
 }

+ 24 - 17
xxl-job-core/src/main/java/com/xxl/job/core/handler/impl/ScriptJobHandler.java

@@ -6,6 +6,7 @@ import com.xxl.job.core.handler.IJobHandler;
 import com.xxl.job.core.log.XxlJobFileAppender;
 import com.xxl.job.core.log.XxlJobLogger;
 import com.xxl.job.core.util.ScriptUtil;
+import com.xxl.job.core.util.ShardingUtil;
 
 /**
  * Created by xuxueli on 17/4/27.
@@ -29,32 +30,38 @@ public class ScriptJobHandler extends IJobHandler {
     }
 
     @Override
-    public ReturnT<String> execute(String... params) throws Exception {
-
-        // cmd + script-file-name
-        String cmd = "bash";
-        String scriptFileName = null;
-        if (GlueTypeEnum.GLUE_SHELL == glueType) {
-            cmd = "bash";
-            scriptFileName = XxlJobFileAppender.logPath.concat("gluesource/").concat(String.valueOf(jobId)).concat("_").concat(String.valueOf(glueUpdatetime)).concat(".sh");
-        } else if (GlueTypeEnum.GLUE_PYTHON == glueType) {
-            cmd = "python";
-            scriptFileName = XxlJobFileAppender.logPath.concat("gluesource/").concat(String.valueOf(jobId)).concat("_").concat(String.valueOf(glueUpdatetime)).concat(".py");
-        } else if (GlueTypeEnum.GLUE_NODEJS == glueType) {
-            cmd = "node";
-            scriptFileName = XxlJobFileAppender.logPath.concat("gluesource/").concat(String.valueOf(jobId)).concat("_").concat(String.valueOf(glueUpdatetime)).concat(".js");
+    public ReturnT<String> execute(String param) throws Exception {
+
+        if (!glueType.isScript()) {
+            return new ReturnT<String>(IJobHandler.FAIL.getCode(), "glueType["+ glueType +"] invalid.");
         }
 
+        // cmd
+        String cmd = glueType.getCmd();
+
         // make script file
+        String scriptFileName = XxlJobFileAppender.getLogPath()
+                .concat("/gluesource/")
+                .concat(String.valueOf(jobId))
+                .concat("_")
+                .concat(String.valueOf(glueUpdatetime))
+                .concat(glueType.getSuffix());
         ScriptUtil.markScriptFile(scriptFileName, gluesource);
 
         // log file
-        String logFileName = XxlJobFileAppender.logPath.concat(XxlJobFileAppender.contextHolder.get());
+        String logFileName = XxlJobFileAppender.contextHolder.get();
+
+        // script params:0=param、1=分片序号、2=分片总数
+        ShardingUtil.ShardingVO shardingVO = ShardingUtil.getShardingVo();
+        String[] scriptParams = new String[3];
+        scriptParams[0] = param;
+        scriptParams[1] = String.valueOf(shardingVO.getIndex());
+        scriptParams[2] = String.valueOf(shardingVO.getTotal());
 
         // invoke
         XxlJobLogger.log("----------- script file:"+ scriptFileName +" -----------");
-        int exitValue = ScriptUtil.execToFile(cmd, scriptFileName, logFileName, params);
-        ReturnT<String> result = (exitValue==0)?ReturnT.SUCCESS:new ReturnT<String>(ReturnT.FAIL_CODE, "script exit value("+exitValue+") is failed");
+        int exitValue = ScriptUtil.execToFile(cmd, scriptFileName, logFileName, scriptParams);
+        ReturnT<String> result = (exitValue==0)?IJobHandler.SUCCESS:new ReturnT<String>(IJobHandler.FAIL.getCode(), "script exit value("+exitValue+") is failed");
         return result;
     }
 

+ 45 - 25
xxl-job-core/src/main/java/com/xxl/job/core/log/XxlJobFileAppender.java

@@ -18,10 +18,35 @@ public class XxlJobFileAppender {
 	// for JobThread (support log for child thread of job handler)
 	//public static ThreadLocal<String> contextHolder = new ThreadLocal<String>();
 	public static final InheritableThreadLocal<String> contextHolder = new InheritableThreadLocal<String>();
-	public static String logPath = "/data/applogs/xxl-job/jobhandler/";
+
+
+	// log base path
+	private static String logBasePath = "/data/applogs/xxl-job/jobhandler";
+	public static void initLogPath(String logPath){
+		// init
+		if (logPath!=null && logPath.trim().length()>0) {
+			logBasePath = logPath;
+		}
+		// mk base dir
+		File logPathDir = new File(logBasePath);
+		if (!logPathDir.exists()) {
+			logPathDir.mkdirs();
+		}
+		logBasePath = logPathDir.getPath();
+
+		// mk glue dir
+		File glueBaseDir = new File(logPathDir, "gluesource");
+		if (!glueBaseDir.exists()) {
+			glueBaseDir.mkdirs();
+		}
+	}
+	public static String getLogPath() {
+		return logBasePath;
+	}
+
 
 	/**
-	 * log filename: yyyy-MM-dd/9999.log
+	 * log filename, like "logPath/yyyy-MM-dd/9999.log"
 	 *
 	 * @param triggerDate
 	 * @param logId
@@ -29,23 +54,18 @@ public class XxlJobFileAppender {
 	 */
 	public static String makeLogFileName(Date triggerDate, int logId) {
 
-        // filePath/
-        File filePathDir = new File(logPath);
-        if (!filePathDir.exists()) {
-            filePathDir.mkdirs();
-        }
-
-        // filePath/yyyy-MM-dd/
+		// filePath/yyyy-MM-dd
 		SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");	// avoid concurrent problem, can not be static
+		File logFilePath = new File(getLogPath(), sdf.format(triggerDate));
+		if (!logFilePath.exists()) {
+			logFilePath.mkdir();
+		}
 
-        String nowFormat = sdf.format(new Date());
-        File filePathDateDir = new File(filePathDir, nowFormat);
-        if (!filePathDateDir.exists()) {
-            filePathDateDir.mkdirs();
-        }
-
-        // filePath/yyyy-MM-dd/9999.log
-		String logFileName = sdf.format(triggerDate).concat("/").concat(String.valueOf(logId)).concat(".log");
+		// filePath/yyyy-MM-dd/9999.log
+		String logFileName = logFilePath.getPath()
+				.concat("/")
+				.concat(String.valueOf(logId))
+				.concat(".log");
 		return logFileName;
 	}
 
@@ -57,17 +77,11 @@ public class XxlJobFileAppender {
 	 */
 	public static void appendLog(String logFileName, String appendLog) {
 
-		// log
-		if (appendLog == null) {
-			appendLog = "";
-		}
-		appendLog += "\r\n";
-
 		// log file
 		if (logFileName==null || logFileName.trim().length()==0) {
 			return;
 		}
-		File logFile = new File(logPath, logFileName);
+		File logFile = new File(logFileName);
 
 		if (!logFile.exists()) {
 			try {
@@ -77,6 +91,12 @@ public class XxlJobFileAppender {
 				return;
 			}
 		}
+
+		// log
+		if (appendLog == null) {
+			appendLog = "";
+		}
+		appendLog += "\r\n";
 		
 		// append file content
 		try {
@@ -112,7 +132,7 @@ public class XxlJobFileAppender {
 		if (logFileName==null || logFileName.trim().length()==0) {
             return new LogResult(fromLineNum, 0, "readLog fail, logFile not found", true);
 		}
-		File logFile = new File(logPath, logFileName);
+		File logFile = new File(logFileName);
 
 		if (!logFile.exists()) {
             return new LogResult(fromLineNum, 0, "readLog fail, logFile not exists", true);

+ 36 - 17
xxl-job-core/src/main/java/com/xxl/job/core/log/XxlJobLogger.java

@@ -3,6 +3,8 @@ package com.xxl.job.core.log;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
+import java.io.PrintWriter;
+import java.io.StringWriter;
 import java.text.MessageFormat;
 import java.text.SimpleDateFormat;
 import java.util.Date;
@@ -17,46 +19,63 @@ public class XxlJobLogger {
     /**
      * append log
      *
+     * @param callInfo
      * @param appendLog
      */
-    public static void log(String appendLog) {
+    private static void logDetail(StackTraceElement callInfo, String appendLog) {
 
-        // logFileName
-        String logFileName = XxlJobFileAppender.contextHolder.get();
-        if (logFileName==null || logFileName.trim().length()==0) {
-            return;
-        }
 
-        // "yyyy-MM-dd HH:mm:ss [ClassName]-[MethodName]-[LineNumber]-[ThreadName] log";
+        /*// "yyyy-MM-dd HH:mm:ss [ClassName]-[MethodName]-[LineNumber]-[ThreadName] log";
         StackTraceElement[] stackTraceElements = new Throwable().getStackTrace();
-        StackTraceElement callInfo = stackTraceElements[1];
+        StackTraceElement callInfo = stackTraceElements[1];*/
 
         StringBuffer stringBuffer = new StringBuffer();
         stringBuffer.append(xxlJobLoggerFormat.format(new Date())).append(" ")
-            .append("["+ callInfo.getClassName() +"]").append("-")
-            .append("["+ callInfo.getMethodName() +"]").append("-")
+            .append("["+ callInfo.getClassName() + "#" + callInfo.getMethodName() +"]").append("-")
             .append("["+ callInfo.getLineNumber() +"]").append("-")
             .append("["+ Thread.currentThread().getName() +"]").append(" ")
             .append(appendLog!=null?appendLog:"");
         String formatAppendLog = stringBuffer.toString();
 
         // appendlog
-        XxlJobFileAppender.appendLog(logFileName, formatAppendLog);
-
-        logger.debug(">>>>>>>>>>> [{}]: {}", logFileName, formatAppendLog);
+        String logFileName = XxlJobFileAppender.contextHolder.get();
+        if (logFileName!=null && logFileName.trim().length()>0) {
+            XxlJobFileAppender.appendLog(logFileName, formatAppendLog);
+        } else {
+            logger.info(">>>>>>>>>>> {}", formatAppendLog);
+        }
     }
 
     /**
      * append log with pattern
      *
-     * @
-     *
      * @param appendLogPattern  like "aaa {0} bbb {1} ccc"
      * @param appendLogArguments    like "111, true"
      */
     public static void log(String appendLogPattern, Object ... appendLogArguments) {
-        String appendLog = MessageFormat.format(appendLogPattern, appendLogArguments);
-        log(appendLog);
+
+        String appendLog = appendLogPattern;
+        if (appendLogArguments!=null && appendLogArguments.length>0) {
+            appendLog = MessageFormat.format(appendLogPattern, appendLogArguments);
+        }
+
+        StackTraceElement callInfo = new Throwable().getStackTrace()[1];
+        logDetail(callInfo, appendLog);
+    }
+
+    /**
+     * append exception stack
+     *
+     * @param e
+     */
+    public static void log(Throwable e) {
+
+        StringWriter stringWriter = new StringWriter();
+        e.printStackTrace(new PrintWriter(stringWriter));
+        String appendLog = stringWriter.toString();
+
+        StackTraceElement callInfo = new Throwable().getStackTrace()[1];
+        logDetail(callInfo, appendLog);
     }
 
 }

+ 19 - 9
xxl-job-core/src/main/java/com/xxl/job/core/thread/JobThread.java

@@ -92,6 +92,14 @@ public class JobThread extends Thread{
     @Override
 	public void run() {
 
+    	// init
+    	try {
+			handler.init();
+		} catch (Throwable e) {
+    		logger.error(e.getMessage(), e);
+		}
+
+		// execute
 		while(!toStop){
 			running = false;
 			idleTimes++;
@@ -106,21 +114,16 @@ public class JobThread extends Thread{
 					idleTimes = 0;
 					triggerLogIdSet.remove(triggerParam.getLogId());
 
-					// parse param
-					String[] handlerParams = (triggerParam.getExecutorParams()!=null && triggerParam.getExecutorParams().trim().length()>0)
-							? (String[])(Arrays.asList(triggerParam.getExecutorParams().split(",")).toArray()) : null;
-
-
-					// log filename: yyyy-MM-dd/9999.log
+					// log filename, like "logPath/yyyy-MM-dd/9999.log"
 					String logFileName = XxlJobFileAppender.makeLogFileName(new Date(triggerParam.getLogDateTim()), triggerParam.getLogId());
 					XxlJobFileAppender.contextHolder.set(logFileName);
 					ShardingUtil.setShardingVo(new ShardingUtil.ShardingVO(triggerParam.getBroadcastIndex(), triggerParam.getBroadcastTotal()));
 
 					// execute
-					XxlJobLogger.log("<br>----------- xxl-job job execute start -----------<br>----------- Params:" + Arrays.toString(handlerParams));
-					executeResult = handler.execute(handlerParams);
+					XxlJobLogger.log("<br>----------- xxl-job job execute start -----------<br>----------- Param:" + triggerParam.getExecutorParams());
+					executeResult = handler.execute(triggerParam.getExecutorParams());
 					if (executeResult == null) {
-						executeResult = ReturnT.FAIL;
+						executeResult = IJobHandler.FAIL;
 					}
 					XxlJobLogger.log("<br>----------- xxl-job job execute end(finish) -----------<br>----------- ReturnT:" + executeResult);
 
@@ -165,6 +168,13 @@ public class JobThread extends Thread{
 			}
 		}
 
+		// destroy
+		try {
+			handler.destroy();
+		} catch (Throwable e) {
+			logger.error(e.getMessage(), e);
+		}
+
 		logger.info(">>>>>>>>>>> xxl-job JobThread stoped, hashCode:{}", Thread.currentThread());
 	}
 }

+ 5 - 6
xxl-job-core/src/main/java/com/xxl/job/core/util/JacksonUtil.java

@@ -1,11 +1,10 @@
 package com.xxl.job.core.util;
 
-
-import org.codehaus.jackson.JsonGenerationException;
-import org.codehaus.jackson.JsonParseException;
-import org.codehaus.jackson.map.JsonMappingException;
-import org.codehaus.jackson.map.ObjectMapper;
-import org.codehaus.jackson.type.TypeReference;
+import com.fasterxml.jackson.core.JsonGenerationException;
+import com.fasterxml.jackson.core.JsonParseException;
+import com.fasterxml.jackson.core.type.TypeReference;
+import com.fasterxml.jackson.databind.JsonMappingException;
+import com.fasterxml.jackson.databind.ObjectMapper;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 

+ 0 - 14
xxl-job-core/src/main/java/com/xxl/job/core/util/ScriptUtil.java

@@ -1,11 +1,9 @@
 package com.xxl.job.core.util;
 
-import com.xxl.job.core.log.XxlJobFileAppender;
 import org.apache.commons.exec.CommandLine;
 import org.apache.commons.exec.DefaultExecutor;
 import org.apache.commons.exec.PumpStreamHandler;
 
-import java.io.File;
 import java.io.FileOutputStream;
 import java.io.IOException;
 
@@ -27,18 +25,6 @@ public class ScriptUtil {
      * @throws IOException
      */
     public static void markScriptFile(String scriptFileName, String content) throws IOException {
-        // filePath/
-        File filePathDir = new File(XxlJobFileAppender.logPath);
-        if (!filePathDir.exists()) {
-            filePathDir.mkdirs();
-        }
-
-        // filePath/gluesource/
-        File filePathSourceDir = new File(filePathDir, "gluesource");
-        if (!filePathSourceDir.exists()) {
-            filePathSourceDir.mkdirs();
-        }
-
         // make file,   filePath/gluesource/666-123456789.py
         FileOutputStream fileOutputStream = null;
         try {

+ 1 - 0
xxl-job-executor-samples/pom.xml

@@ -14,6 +14,7 @@
         <module>xxl-job-executor-sample-spring</module>
         <module>xxl-job-executor-sample-springboot</module>
         <module>xxl-job-executor-sample-jfinal</module>
+        <module>xxl-job-executor-sample-nutz</module>
     </modules>
 
 </project>

+ 1 - 1
xxl-job-executor-samples/xxl-job-executor-sample-jfinal/pom.xml

@@ -16,7 +16,7 @@
         <dependency>
             <groupId>com.jfinal</groupId>
             <artifactId>jfinal</artifactId>
-            <version>2.0</version>
+            <version>2.2</version>
         </dependency>
 
         <!-- slf4j -->

+ 2 - 1
xxl-job-executor-samples/xxl-job-executor-sample-jfinal/src/main/java/com/xuxueli/executor/sample/jfinal/config/JFinalCoreConfig.java

@@ -17,8 +17,9 @@ public class JFinalCoreConfig extends JFinalConfig {
 	private Logger logger = LoggerFactory.getLogger(JFinalCoreConfig.class);
 
 	// ---------------------- xxl-job executor ----------------------
-	XxlJobExecutor xxlJobExecutor = null;
+	private XxlJobExecutor xxlJobExecutor = null;
 	private void initXxlJobExecutor() {
+
 		// registry jobhandler
 		XxlJobExecutor.registJobHandler("demoJobHandler", new DemoJobHandler());
 		XxlJobExecutor.registJobHandler("shardingJobHandler", new ShardingJobHandler());

+ 1 - 0
xxl-job-executor-samples/xxl-job-executor-sample-jfinal/src/main/java/com/xuxueli/executor/sample/jfinal/controller/IndexController.java

@@ -7,4 +7,5 @@ public class IndexController extends Controller {
 	public void index(){
 		renderText("xxl job executor running.");
 	}
+
 }

+ 6 - 7
xxl-job-executor-samples/xxl-job-executor-sample-jfinal/src/main/java/com/xuxueli/executor/sample/jfinal/jobhandler/DemoJobHandler.java

@@ -6,28 +6,27 @@ import com.xxl.job.core.log.XxlJobLogger;
 
 import java.util.concurrent.TimeUnit;
 
-
 /**
- * 任务Handler的一个Demo(Bean模式)
+ * 任务Handler示例(Bean模式)
  *
  * 开发步骤:
- * 1、继承 “IJobHandler” 
- * 2、执行日志:需要通过 "XxlJobLogger.log" 打印执行日志;
- * 3、在 "JFinalCoreConfig" 中注册,执行Jobhandler名称
+ * 1、继承"IJobHandler":“com.xxl.job.core.handler.IJobHandler”
+ * 2、注册到执行器工厂:在 "JFinalCoreConfig.initXxlJobExecutor" 中手动注册,注解key值对应的是调度中心新建任务的JobHandler属性的值。
+ * 3、执行日志:需要通过 "XxlJobLogger.log" 打印执行日志
  *
  * @author xuxueli 2015-12-19 19:43:36
  */
 public class DemoJobHandler extends IJobHandler {
 
 	@Override
-	public ReturnT<String> execute(String... params) throws Exception {
+	public ReturnT<String> execute(String param) throws Exception {
 		XxlJobLogger.log("XXL-JOB, Hello World.");
 
 		for (int i = 0; i < 5; i++) {
 			XxlJobLogger.log("beat at:" + i);
 			TimeUnit.SECONDS.sleep(2);
 		}
-		return ReturnT.SUCCESS;
+		return SUCCESS;
 	}
 
 }

+ 2 - 2
xxl-job-executor-samples/xxl-job-executor-sample-jfinal/src/main/java/com/xuxueli/executor/sample/jfinal/jobhandler/ShardingJobHandler.java

@@ -14,7 +14,7 @@ import com.xxl.job.core.util.ShardingUtil;
 public class ShardingJobHandler extends IJobHandler {
 
 	@Override
-	public ReturnT<String> execute(String... params) throws Exception {
+	public ReturnT<String> execute(String param) throws Exception {
 
 		// 分片参数
 		ShardingUtil.ShardingVO shardingVO = ShardingUtil.getShardingVo();
@@ -29,7 +29,7 @@ public class ShardingJobHandler extends IJobHandler {
 			}
 		}
 
-		return ReturnT.SUCCESS;
+		return SUCCESS;
 	}
 	
 }

+ 2 - 2
xxl-job-executor-samples/xxl-job-executor-sample-jfinal/src/main/resources/xxl-job-executor.properties

@@ -4,10 +4,10 @@ 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=9997
+xxl.job.executor.port=9996
 
 ### xxl-job log path
-xxl.job.executor.logpath=/data/applogs/xxl-job/jobhandler/
+xxl.job.executor.logpath=/data/applogs/xxl-job/jobhandler
 
 ### xxl-job, access token
 xxl.job.accessToken=

+ 6 - 0
xxl-job-executor-samples/xxl-job-executor-sample-jfinal/src/main/webapp/WEB-INF/web.xml

@@ -3,6 +3,7 @@
 	xmlns="http://java.sun.com/xml/ns/javaee" xmlns:web="http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd"
 	xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd"
 	id="WebApp_ID" version="2.5">
+
 	<display-name>xxl-job-executor-sample-jfinal</display-name>
 	<context-param>
 		<param-name>webAppRootKey</param-name>
@@ -23,4 +24,9 @@
 		<url-pattern>/*</url-pattern>
 	</filter-mapping>
 
+
+	<welcome-file-list>
+		<welcome-file>index.html</welcome-file>
+	</welcome-file-list>
+
 </web-app>

+ 1 - 0
xxl-job-executor-samples/xxl-job-executor-sample-jfinal/src/main/webapp/index.html

@@ -0,0 +1 @@
+i am alive.

+ 38 - 0
xxl-job-executor-samples/xxl-job-executor-sample-nutz/pom.xml

@@ -0,0 +1,38 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project xmlns="http://maven.apache.org/POM/4.0.0"
+         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+    <parent>
+        <groupId>com.xuxueli</groupId>
+        <artifactId>xxl-job-executor-samples</artifactId>
+        <version>1.9.0-SNAPSHOT</version>
+    </parent>
+    <modelVersion>4.0.0</modelVersion>
+    <artifactId>xxl-job-executor-sample-nutz</artifactId>
+    <packaging>war</packaging>
+
+    <dependencies>
+        <!-- nutz -->
+        <dependency>
+			<groupId>org.nutz</groupId>
+			<artifactId>nutz</artifactId>
+			<version>1.r.62</version>
+		</dependency>
+
+        <!-- slf4j -->
+        <dependency>
+            <groupId>org.slf4j</groupId>
+            <artifactId>slf4j-log4j12</artifactId>
+            <version>${slf4j-api.version}</version>
+        </dependency>
+
+        <!-- xxl-job -->
+        <dependency>
+            <groupId>com.xuxueli</groupId>
+            <artifactId>xxl-job-core</artifactId>
+            <version>${project.parent.version}</version>
+        </dependency>
+
+    </dependencies>
+
+</project>

+ 23 - 0
xxl-job-executor-samples/xxl-job-executor-sample-nutz/src/main/java/com/xuxueli/executor/sample/nutz/MainModule.java

@@ -0,0 +1,23 @@
+package com.xuxueli.executor.sample.nutz;
+
+import com.xuxueli.executor.sample.nutz.config.NutzSetup;
+import org.nutz.mvc.annotation.*;
+import org.nutz.mvc.ioc.provider.ComboIocProvider;
+
+/**
+ * nutz module
+ *
+ * @author xuxueli 2017-12-25 17:58:43
+ */
+@IocBy(type = ComboIocProvider.class,
+        args = {"*org.nutz.ioc.loader.annotation.AnnotationIocLoader",
+                "com.xuxueli.executor.sample.nutz"})
+@Encoding(input = "utf-8", output = "utf-8")
+@Modules(scanPackage = true)
+@Localization("msg")
+@Ok("json")
+@Fail("json")
+@SetupBy(NutzSetup.class)
+public class MainModule {
+
+}

+ 64 - 0
xxl-job-executor-samples/xxl-job-executor-sample-nutz/src/main/java/com/xuxueli/executor/sample/nutz/config/NutzSetup.java

@@ -0,0 +1,64 @@
+package com.xuxueli.executor.sample.nutz.config;
+
+import com.xxl.job.core.executor.XxlJobExecutor;
+import com.xxl.job.core.handler.IJobHandler;
+import com.xxl.job.core.handler.annotation.JobHandler;
+import org.nutz.ioc.impl.PropertiesProxy;
+import org.nutz.mvc.NutConfig;
+import org.nutz.mvc.Setup;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * nutz setup
+ *
+ * @author xuxueli 2017-12-25 17:58:43
+ */
+public class NutzSetup implements Setup {
+	private Logger logger = LoggerFactory.getLogger(NutzSetup.class);
+	//public static final Log logger = Logs.get();
+
+	private XxlJobExecutor xxlJobExecutor = null;
+
+	@Override
+	public void init(NutConfig cfg) {
+
+		// regist JobHandler
+		String[] beanNames = cfg.getIoc().getNamesByType(IJobHandler.class);
+		if (beanNames==null || beanNames.length==0) {
+			return;
+		}
+		for (String beanName : beanNames) {
+			IJobHandler jobHandler = cfg.getIoc().get(IJobHandler.class, beanName);
+			String name = jobHandler.getClass().getAnnotation(JobHandler.class).value();
+			XxlJobExecutor.registJobHandler(name, jobHandler);
+		}
+
+		// load executor prop
+		PropertiesProxy xxlJobProp = new PropertiesProxy("xxl-job-executor.properties");
+
+		// init executor
+		xxlJobExecutor = new XxlJobExecutor();
+		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"));
+
+		// start executor
+		try {
+			xxlJobExecutor.start();
+		} catch (Exception e) {
+			logger.error(e.getMessage(), e);
+		}
+	}
+
+	@Override
+	public void destroy(NutConfig cfg) {
+		if (xxlJobExecutor != null) {
+			xxlJobExecutor.destroy();
+		}
+	}
+
+}

+ 38 - 0
xxl-job-executor-samples/xxl-job-executor-sample-nutz/src/main/java/com/xuxueli/executor/sample/nutz/jobhandler/DemoJobHandler.java

@@ -0,0 +1,38 @@
+package com.xuxueli.executor.sample.nutz.jobhandler;
+
+import com.xxl.job.core.biz.model.ReturnT;
+import com.xxl.job.core.handler.IJobHandler;
+import com.xxl.job.core.handler.annotation.JobHandler;
+import com.xxl.job.core.log.XxlJobLogger;
+import org.nutz.ioc.loader.annotation.IocBean;
+
+import java.util.concurrent.TimeUnit;
+
+/**
+ * 任务Handler示例(Bean模式)
+ *
+ * 开发步骤:
+ * 1、继承"IJobHandler":“com.xxl.job.core.handler.IJobHandler”;
+ * 2、注册到Nutz容器:添加“@IocBean”注解,被Nutz容器扫描为Bean实例;
+ * 3、注册到执行器工厂:添加“@JobHandler(value="自定义jobhandler名称")”注解,注解value值对应的是调度中心新建任务的JobHandler属性的值。
+ * 4、执行日志:需要通过 "XxlJobLogger.log" 打印执行日志;
+ *
+ * @author xuxueli 2015-12-19 19:43:36
+ */
+@JobHandler(value="demoJobHandler")
+@IocBean
+public class DemoJobHandler extends IJobHandler {
+
+	@Override
+	public ReturnT<String> execute(String param) throws Exception {
+		XxlJobLogger.log("XXL-JOB, Hello World.");
+
+		for (int i = 0; i < 5; i++) {
+			XxlJobLogger.log("beat at:" + i);
+			TimeUnit.SECONDS.sleep(2);
+		}
+		return SUCCESS;
+	}
+
+}
+

+ 39 - 0
xxl-job-executor-samples/xxl-job-executor-sample-nutz/src/main/java/com/xuxueli/executor/sample/nutz/jobhandler/ShardingJobHandler.java

@@ -0,0 +1,39 @@
+package com.xuxueli.executor.sample.nutz.jobhandler;
+
+import com.xxl.job.core.handler.annotation.JobHandler;
+import org.nutz.ioc.loader.annotation.IocBean;
+
+import com.xxl.job.core.biz.model.ReturnT;
+import com.xxl.job.core.handler.IJobHandler;
+import com.xxl.job.core.log.XxlJobLogger;
+import com.xxl.job.core.util.ShardingUtil;
+
+/**
+ * 分片广播任务
+ *
+ * @author xuxueli 2017-07-25 20:56:50
+ */
+@JobHandler(value="shardingJobHandler")
+@IocBean
+public class ShardingJobHandler extends IJobHandler {
+
+	@Override
+	public ReturnT<String> execute(String param) throws Exception {
+
+		// 分片参数
+		ShardingUtil.ShardingVO shardingVO = ShardingUtil.getShardingVo();
+		XxlJobLogger.log("分片参数:当前分片序号 = {0}, 总分片数 = {1}", shardingVO.getIndex(), shardingVO.getTotal());
+
+		// 业务逻辑
+		for (int i = 0; i < shardingVO.getTotal(); i++) {
+			if (i == shardingVO.getIndex()) {
+				XxlJobLogger.log("第 {0} 片, 命中分片开始处理", i);
+			} else {
+				XxlJobLogger.log("第 {0} 片, 忽略", i);
+			}
+		}
+
+		return SUCCESS;
+	}
+
+}

+ 16 - 0
xxl-job-executor-samples/xxl-job-executor-sample-nutz/src/main/java/com/xuxueli/executor/sample/nutz/module/IndexModule.java

@@ -0,0 +1,16 @@
+package com.xuxueli.executor.sample.nutz.module;
+
+import org.nutz.ioc.loader.annotation.IocBean;
+import org.nutz.mvc.annotation.At;
+import org.nutz.mvc.annotation.Ok;
+
+@IocBean
+public class IndexModule {
+	
+	@At("/")
+	@Ok("json")
+	public String index() {
+		return "xxl job executor running.";
+	}
+
+}

+ 27 - 0
xxl-job-executor-samples/xxl-job-executor-sample-nutz/src/main/resources/log4j.xml

@@ -0,0 +1,27 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE log4j:configuration PUBLIC "-//log4j/log4j Configuration//EN" "log4j.dtd">
+<log4j:configuration xmlns:log4j="http://jakarta.apache.org/log4j/" threshold="null" debug="null">
+
+    <appender name="CONSOLE" class="org.apache.log4j.ConsoleAppender">
+        <param name="Target" value="System.out" />
+        <layout class="org.apache.log4j.PatternLayout">
+            <param name="ConversionPattern" value="%-d{yyyy-MM-dd HH:mm:ss} xxl-job-executor-sample-nutz [%c]-[%t]-[%M]-[%L]-[%p] %m%n"/>
+        </layout>
+    </appender>
+
+    <appender name="FILE" class="org.apache.log4j.DailyRollingFileAppender">
+        <param name="file" value="/data/applogs/xxl-job/xxl-job-executor-sample-nutz.log"/>
+        <param name="append" value="true"/>
+        <param name="encoding" value="UTF-8"/>
+        <layout class="org.apache.log4j.PatternLayout">
+            <param name="ConversionPattern" value="%-d{yyyy-MM-dd HH:mm:ss} xxl-job-executor-sample-nutz [%c]-[%t]-[%M]-[%L]-[%p] %m%n"/>
+        </layout>
+    </appender>
+
+    <root>
+        <level value="INFO" />
+        <appender-ref ref="CONSOLE" />
+        <appender-ref ref="FILE" />
+    </root>
+
+</log4j:configuration>

+ 13 - 0
xxl-job-executor-samples/xxl-job-executor-sample-nutz/src/main/resources/xxl-job-executor.properties

@@ -0,0 +1,13 @@
+### 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=9997
+
+### xxl-job log path
+xxl.job.executor.logpath=/data/applogs/xxl-job/jobhandler
+
+### xxl-job, access token
+xxl.job.accessToken=

+ 32 - 0
xxl-job-executor-samples/xxl-job-executor-sample-nutz/src/main/webapp/WEB-INF/web.xml

@@ -0,0 +1,32 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+	xmlns="http://java.sun.com/xml/ns/javaee" xmlns:web="http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd"
+	xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd"
+	id="WebApp_ID" version="2.5">
+
+	<display-name>xxl-job-executor-sample-nutz</display-name>
+	<context-param>
+		<param-name>webAppRootKey</param-name>
+		<param-value>xxl-job-executor-sample-nutz</param-value>
+	</context-param>
+
+	<!-- nutz -->
+	<filter>
+		<filter-name>nutz</filter-name>
+		<filter-class>org.nutz.mvc.NutFilter</filter-class>
+		<init-param>
+			<param-name>modules</param-name>
+			<param-value>com.xuxueli.executor.sample.nutz.MainModule</param-value>
+		</init-param>
+	</filter>
+	<filter-mapping>
+		<filter-name>nutz</filter-name>
+		<url-pattern>/*</url-pattern>
+	</filter-mapping>
+
+
+	<welcome-file-list>
+		<welcome-file>index.html</welcome-file>
+	</welcome-file-list>
+
+</web-app>

+ 1 - 0
xxl-job-executor-samples/xxl-job-executor-sample-nutz/src/main/webapp/index.html

@@ -0,0 +1 @@
+i am alive.

+ 6 - 6
xxl-job-executor-samples/xxl-job-executor-sample-spring/src/main/java/com/xxl/job/executor/service/jobhandler/DemoJobHandler.java

@@ -10,12 +10,12 @@ import java.util.concurrent.TimeUnit;
 
 
 /**
- * 任务Handler的一个Demo(Bean模式)
+ * 任务Handler示例(Bean模式)
  * 
  * 开发步骤:
- * 1、新建一个继承com.xxl.job.core.handler.IJobHandler的Java类
- * 2、该类被Spring容器扫描为Bean实例,如加“@Component”注解
- * 3、添加 “@JobHandler(value="自定义jobhandler名称")”注解,注解value值为自定义的JobHandler名称,该名称对应的是调度中心新建任务的JobHandler属性的值。
+ * 1、继承"IJobHandler":“com.xxl.job.core.handler.IJobHandler”
+ * 2、注册到Spring容器:添加“@Component”注解,被Spring容器扫描为Bean实例
+ * 3、注册到执行器工厂:添加“@JobHandler(value="自定义jobhandler名称")”注解,注解value值对应的是调度中心新建任务的JobHandler属性的值。
  * 4、执行日志:需要通过 "XxlJobLogger.log" 打印执行日志;
  * 
  * @author xuxueli 2015-12-19 19:43:36
@@ -25,14 +25,14 @@ import java.util.concurrent.TimeUnit;
 public class DemoJobHandler extends IJobHandler {
 
 	@Override
-	public ReturnT<String> execute(String... params) throws Exception {
+	public ReturnT<String> execute(String param) throws Exception {
 		XxlJobLogger.log("XXL-JOB, Hello World.");
 		
 		for (int i = 0; i < 5; i++) {
 			XxlJobLogger.log("beat at:" + i);
 			TimeUnit.SECONDS.sleep(2);
 		}
-		return ReturnT.SUCCESS;
+		return SUCCESS;
 	}
 	
 }

+ 2 - 2
xxl-job-executor-samples/xxl-job-executor-sample-spring/src/main/java/com/xxl/job/executor/service/jobhandler/ShardingJobHandler.java

@@ -18,7 +18,7 @@ import org.springframework.stereotype.Service;
 public class ShardingJobHandler extends IJobHandler {
 
 	@Override
-	public ReturnT<String> execute(String... params) throws Exception {
+	public ReturnT<String> execute(String param) throws Exception {
 
 		// 分片参数
 		ShardingUtil.ShardingVO shardingVO = ShardingUtil.getShardingVo();
@@ -33,7 +33,7 @@ public class ShardingJobHandler extends IJobHandler {
 			}
 		}
 
-		return ReturnT.SUCCESS;
+		return SUCCESS;
 	}
 	
 }

+ 5 - 5
xxl-job-executor-samples/xxl-job-executor-sample-spring/src/main/resources/applicationcontext-xxl-job.xml

@@ -3,9 +3,9 @@
        xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
        xmlns:context="http://www.springframework.org/schema/context"
        xsi:schemaLocation="http://www.springframework.org/schema/beans
-           http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
+           http://www.springframework.org/schema/beans/spring-beans.xsd
            http://www.springframework.org/schema/context
-           http://www.springframework.org/schema/context/spring-context-3.0.xsd">
+           http://www.springframework.org/schema/context/spring-context.xsd">
 
 	<bean id="propertyConfigurer" class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
 		<property name="fileEncoding" value="utf-8" />
@@ -25,15 +25,15 @@
 	<bean id="xxlJobExecutor" class="com.xxl.job.core.executor.XxlJobExecutor" init-method="start" destroy-method="destroy" >
 		<!-- 执行器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}" />
 	</bean>
 

+ 1 - 1
xxl-job-executor-samples/xxl-job-executor-sample-spring/src/main/resources/xxl-job-executor.properties

@@ -7,7 +7,7 @@ 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.logpath=/data/applogs/xxl-job/jobhandler
 
 ### xxl-job, access token
 xxl.job.accessToken=

+ 5 - 2
xxl-job-executor-samples/xxl-job-executor-sample-spring/src/main/webapp/WEB-INF/web.xml

@@ -3,12 +3,14 @@
 	xmlns="http://java.sun.com/xml/ns/javaee" xmlns:web="http://java.sun.com/xml/ns/javaee"
 	xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd"
 	id="WebApp_ID" version="2.5">
+
 	<display-name>xxl-job-executor-sample-spring</display-name>
 	<context-param>
 	    <param-name>webAppRootKey</param-name>
 	    <param-value>xxl-job-executor-sample-spring</param-value>
 	</context-param>
-	
+
+	<!-- spring -->
 	<context-param>
 		<param-name>contextConfigLocation</param-name>
 		<param-value>classpath*:applicationcontext-*.xml</param-value>
@@ -20,7 +22,8 @@
 	<listener>
 		<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
 	</listener>
-	
+
+
 	<welcome-file-list>
 		<welcome-file>index.html</welcome-file>
 	</welcome-file-list>

+ 1 - 1
xxl-job-executor-samples/xxl-job-executor-sample-spring/src/main/webapp/index.html

@@ -1 +1 @@
-200
+i am alive.

+ 18 - 18
xxl-job-executor-samples/xxl-job-executor-sample-springboot/src/main/java/com/xxl/job/executor/mvc/controller/IndexController.java

@@ -1,18 +1,18 @@
-package com.xxl.job.executor.mvc.controller;
-
-import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
-import org.springframework.stereotype.Controller;
-import org.springframework.web.bind.annotation.RequestMapping;
-import org.springframework.web.bind.annotation.ResponseBody;
-
-@Controller
-@EnableAutoConfiguration
-public class IndexController {
-
-    @RequestMapping("/")
-    @ResponseBody
-    String index() {
-        return "xxl job executor running.";
-    }
-
-}
+//package com.xxl.job.executor.mvc.controller;
+//
+//import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
+//import org.springframework.stereotype.Controller;
+//import org.springframework.web.bind.annotation.RequestMapping;
+//import org.springframework.web.bind.annotation.ResponseBody;
+//
+//@Controller
+//@EnableAutoConfiguration
+//public class IndexController {
+//
+//    @RequestMapping("/")
+//    @ResponseBody
+//    String index() {
+//        return "xxl job executor running.";
+//    }
+//
+//}

+ 8 - 8
xxl-job-executor-samples/xxl-job-executor-sample-springboot/src/main/java/com/xxl/job/executor/service/jobhandler/DemoJobHandler.java

@@ -4,35 +4,35 @@ import com.xxl.job.core.biz.model.ReturnT;
 import com.xxl.job.core.handler.IJobHandler;
 import com.xxl.job.core.handler.annotation.JobHandler;
 import com.xxl.job.core.log.XxlJobLogger;
-import org.springframework.stereotype.Service;
+import org.springframework.stereotype.Component;
 
 import java.util.concurrent.TimeUnit;
 
 
 /**
- * 任务Handler的一个Demo(Bean模式)
+ * 任务Handler示例(Bean模式)
  *
  * 开发步骤:
- * 1、继承 “IJobHandler” 
- * 2、装配到Spring,例如加 “@Service” 注解
- * 3、加 “@JobHandler” 注解,注解value值为新增任务生成的JobKey的值;多个JobKey用逗号分割;
+ * 1、继承"IJobHandler":“com.xxl.job.core.handler.IJobHandler”
+ * 2、注册到Spring容器:添加“@Component”注解,被Spring容器扫描为Bean实例
+ * 3、注册到执行器工厂:添加“@JobHandler(value="自定义jobhandler名称")”注解,注解value值对应的是调度中心新建任务的JobHandler属性的值。
  * 4、执行日志:需要通过 "XxlJobLogger.log" 打印执行日志;
  *
  * @author xuxueli 2015-12-19 19:43:36
  */
 @JobHandler(value="demoJobHandler")
-@Service
+@Component
 public class DemoJobHandler extends IJobHandler {
 
 	@Override
-	public ReturnT<String> execute(String... params) throws Exception {
+	public ReturnT<String> execute(String param) throws Exception {
 		XxlJobLogger.log("XXL-JOB, Hello World.");
 
 		for (int i = 0; i < 5; i++) {
 			XxlJobLogger.log("beat at:" + i);
 			TimeUnit.SECONDS.sleep(2);
 		}
-		return ReturnT.SUCCESS;
+		return SUCCESS;
 	}
 
 }

+ 2 - 2
xxl-job-executor-samples/xxl-job-executor-sample-springboot/src/main/java/com/xxl/job/executor/service/jobhandler/ShardingJobHandler.java

@@ -18,7 +18,7 @@ import org.springframework.stereotype.Service;
 public class ShardingJobHandler extends IJobHandler {
 
 	@Override
-	public ReturnT<String> execute(String... params) throws Exception {
+	public ReturnT<String> execute(String param) throws Exception {
 
 		// 分片参数
 		ShardingUtil.ShardingVO shardingVO = ShardingUtil.getShardingVo();
@@ -33,7 +33,7 @@ public class ShardingJobHandler extends IJobHandler {
 			}
 		}
 
-		return ReturnT.SUCCESS;
+		return SUCCESS;
 	}
 
 }

+ 2 - 2
xxl-job-executor-samples/xxl-job-executor-sample-springboot/src/main/resources/application.properties

@@ -11,10 +11,10 @@ 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=-1
+xxl.job.executor.port=9998
 
 ### xxl-job log path
-xxl.job.executor.logpath=/data/applogs/xxl-job/jobhandler/
+xxl.job.executor.logpath=/data/applogs/xxl-job/jobhandler
 
 ### xxl-job, access token
 xxl.job.accessToken=