瀏覽代碼

Merge branch 'master' into master

许雪里 5 年之前
父節點
當前提交
d0f5c1f6ee
共有 20 個文件被更改,包括 874 次插入439 次删除
  1. 18 0
      README.md
  2. 71 38
      doc/XXL-JOB官方文档.md
  3. 1 0
      doc/db/tables_xxl_job.sql
  4. 142 141
      pom.xml
  5. 113 113
      xxl-job-admin/pom.xml
  6. 20 0
      xxl-job-admin/src/main/java/com/xxl/job/admin/core/alarm/JobAlarm.java
  7. 65 0
      xxl-job-admin/src/main/java/com/xxl/job/admin/core/alarm/JobAlarmer.java
  8. 117 0
      xxl-job-admin/src/main/java/com/xxl/job/admin/core/alarm/impl/EmailJobAlarm.java
  9. 11 0
      xxl-job-admin/src/main/java/com/xxl/job/admin/core/conf/XxlJobAdminConfig.java
  10. 1 100
      xxl-job-admin/src/main/java/com/xxl/job/admin/core/thread/JobFailMonitorHelper.java
  11. 1 2
      xxl-job-admin/src/main/java/com/xxl/job/admin/core/util/I18nUtil.java
  12. 17 12
      xxl-job-admin/src/main/resources/application.properties
  13. 0 0
      xxl-job-admin/src/main/resources/i18n/message_zh_CN.properties
  14. 262 0
      xxl-job-admin/src/main/resources/i18n/message_zh_TC.properties
  15. 0 1
      xxl-job-admin/src/main/resources/static/adminlte/bower_components/bootstrap/css/bootstrap.css.map
  16. 1 0
      xxl-job-admin/src/main/resources/static/adminlte/bower_components/bootstrap/css/bootstrap.min.css.map
  17. 2 2
      xxl-job-admin/src/main/resources/templates/jobcode/jobcode.index.ftl
  18. 7 5
      xxl-job-core/src/main/java/com/xxl/job/core/executor/impl/XxlJobSpringExecutor.java
  19. 24 24
      xxl-job-core/src/main/java/com/xxl/job/core/handler/annotation/JobHandler.java
  20. 1 1
      xxl-job-core/src/main/java/com/xxl/job/core/handler/annotation/XxlJob.java

+ 18 - 0
README.md

@@ -408,6 +408,24 @@ XXL-JOB是一个分布式任务调度平台,其核心设计目标是开发迅
     - 287、F5未来商店
     - 288、深圳环阳通信息技术有限公司
     - 289、遠傳電信
+    - 290、作业帮(北京)教育科技有限公司【作业帮】
+    - 291、成都科鸿智信科技有限公司
+    - 292、北京木屋时代科技有限公司
+    - 293、大学通(哈尔滨)科技有限责任公司
+    - 294、浙江华坤道威数据科技有限公司
+    - 295、吉祥航空【吉祥航空】
+    - 296、南京圆周网络科技有限公司
+    - 297、广州市洋葱omall电子商务
+    - 298、天津联物科技有限公司
+    - 299、跑哪儿科技(北京)有限公司
+    - 300、深圳市美西西餐饮有限公司(喜茶)
+    - 301、平安不动产有限公司【平安】
+    - 302、江苏中海昇物联科技有限公司
+    - 303、湖南牙医帮科技有限公司
+    - 304、重庆民航凯亚信息技术有限公司(易通航)
+    - 305、递易(上海)智能科技有限公司
+    - 306、亚朵
+    - 307、浙江新课堂教育股份有限公司
 	- ……
 
 > 更多接入的公司,欢迎在 [登记地址](https://github.com/xuxueli/xxl-job/issues/1 ) 登记,登记仅仅为了产品推广。

+ 71 - 38
doc/XXL-JOB官方文档.md

@@ -379,6 +379,24 @@ XXL-JOB是一个分布式任务调度平台,其核心设计目标是开发迅
     - 287、F5未来商店
     - 288、深圳环阳通信息技术有限公司
     - 289、遠傳電信
+    - 290、作业帮(北京)教育科技有限公司【作业帮】
+    - 291、成都科鸿智信科技有限公司
+    - 292、北京木屋时代科技有限公司
+    - 293、大学通(哈尔滨)科技有限责任公司
+    - 294、浙江华坤道威数据科技有限公司
+    - 295、吉祥航空【吉祥航空】
+    - 296、南京圆周网络科技有限公司
+    - 297、广州市洋葱omall电子商务
+    - 298、天津联物科技有限公司
+    - 299、跑哪儿科技(北京)有限公司
+    - 300、深圳市美西西餐饮有限公司(喜茶)
+    - 301、平安不动产有限公司【平安】
+    - 302、江苏中海昇物联科技有限公司
+    - 303、湖南牙医帮科技有限公司
+    - 304、重庆民航凯亚信息技术有限公司(易通航)
+    - 305、递易(上海)智能科技有限公司
+    - 306、亚朵
+    - 307、浙江新课堂教育股份有限公司
 	- ……
 
 > 更多接入的公司,欢迎在 [登记地址](https://github.com/xuxueli/xxl-job/issues/1 ) 登记,登记仅仅为了产品推广。
@@ -415,7 +433,7 @@ XXL-JOB是一个分布式任务调度平台,其核心设计目标是开发迅
 
 ### 1.6 环境
 - Maven3+
-- Jdk1.7+
+- Jdk1.8+
 - Mysql5.7+
 
 
@@ -478,8 +496,8 @@ XXL-JOB是一个分布式任务调度平台,其核心设计目标是开发迅
     ### 调度中心通讯TOKEN [选填]:非空时启用;
     xxl.job.accessToken=
     
-    ### 调度中心国际化配置 [选填]: 默认为空,表示中文; "en" 表示英文;
-    xxl.job.i18n=
+    ### 调度中心国际化配置 [必填]: 默认为 "zh_CN"/中文简体, 可选范围为 "zh_CN"/中文简体, "zh_TC"/中文繁体 and "en"/英文;
+    xxl.job.i18n=zh_CN
     
     ## 调度线程池最大线程配置【必填】
     xxl.job.triggerpool.fast.max=200
@@ -1245,7 +1263,7 @@ API服务请求参考代码:com.xxl.job.executor.ExecutorBizTest
 - 2、提供基于HTTP的任务Handler(Bean任务,JobHandler="HttpJobHandler");业务方只需要提供HTTP链接即可,不限制语言、平台;
 
 ### 5.18 任务失败告警
-默认提供邮件失败告警,可扩展短信、钉钉等方式,扩展代码位置为 "JobFailMonitorHelper.failAlarm";
+默认提供邮件失败告警,可扩展短信、钉钉等方式。如果需要新增一种告警方式,只需要新增一个实现 "com.xxl.job.admin.core.alarm.JobAlarm" 接口的告警实现即可。可以参考默认提供邮箱告警实现 "EmailJobAlarm"。
 
 ### 5.19 调度中心Docker镜像构建
 可以通过以下命令快速构建调度中心,并启动运行;
@@ -1686,46 +1704,61 @@ public ReturnT<String> execute(String param) {
 - 注意:最新版本 "XxlJobSpringExecutor" 逻辑有调整,历史项目中该组件的配置方式请参考Sample示例项目进行调整,尤其注意需要移除组件的init和destroy方法;
 
 ### 6.28 版本 v2.2.0 Release Notes[迭代中]
-- 1、[迭代中]调度中心升级springboot2.x;因此,系统要求JDK8+;
+- 1、调度中心升级springboot2.x;因此,系统要求JDK8+;
+- 2、执行器注册默认值优化;
+- 3、SQL脚本编码默认utf8mb4执行,避免小概率下容器环境中乱码问题;
+- 4、多个项目依赖升级至较新稳定版本,如mybatis、groovy和mysql驱动等;
+- 5、默认数据库连接池调整为hikari,移除tomcat-jdbc依赖;
+- 6、任务告警组件模块化:如果需要新增一种告警方式,只需要新增一个实现 "com.xxl.job.admin.core.alarm.JobAlarm" 接口的告警实现即可,更加灵活、方便定制;
+- 7、执行器XxlJob注解扫描逻辑优化,修复任务为空时小概率NPE问题;
+- 8、Web IDE交互问题修复:输入源码备注之后按回车跳转error问题处理;
+- 9、调度中心国际化完善:新增 "中文繁体" 支持。默认为 "zh_CN"/中文简体, 可选范围为 "zh_CN"/中文简体, "zh_TC"/中文繁体 and "en"/英文;
+- 10、移除旧类注解JobHandler,推荐使用基于方法注解 "@XxlJob" 的方式进行任务开发;(如需保留类注解JobHandler使用方式,可以参考旧版逻辑定制开发);
+- 11、修复bootstrap.min.css.map 404问题;
+- 12、[迭代中]自定义失败重试时间间隔;
+- 13、[迭代中]任务复制功能;点击复制是弹出新建任务弹框,并初始化被复制任务信息;
+- 14、[迭代中]新增执行器描述、任务描述属性;
+- 15、[迭代中]任务执行一次的时候指定IP;
+- 16、[迭代中]任务日志支持单个清理和状态转移,方便触发子任务;
+- 17、[迭代中]任务结果丢失处理:针对长期处于运行中的任务(设置过期时间时,运行超过"过期时间+1min";未设置超时时间时,运行超过"30min"),主动检测该执行器是否在线,如果不在线主动标记失败;
+- 18、[迭代中]优雅停机回调丢失问题修复;
 
 
 ### TODO LIST
 - 1、任务分片路由:分片采用一致性Hash算法计算出尽量稳定的分片顺序,即使注册机器存在波动也不会引起分批分片顺序大的波动;目前采用IP自然排序,可以满足需求,待定;
-- 2、任务单机多线程:提升任务单机并行处理能力;
+- 2、调度隔离:调度中心针对不同执行器,各自维护不同的调度和远程触发组件。
 - 3、调度任务优先级;
-- 4、多数据库支持,在重写并移除Quartz的基础上,DAO层通过JPA实现,不限制数据库类型;
+- 4、多数据库支持,DAO层通过JPA实现,不限制数据库类型;
 - 5、执行器Log清理功能:调度中心Log删除时同步删除执行器中的Log文件;
-- 6、任务自动注册:Bean模式任务,JobHandler自动从执行器中查询展示为下拉框,选择后自动填充任务名称等属性;待考虑,因为任务自动注册将会导致任务难以管理控制;
-- 7、API事件触发类型任务(更类似MQ消息)支持"动态传参、延时消费";该类型任务不走调度组件,单独建立MQ消息表,调度中心竞争触发;待定,该功能与 XXL-MQ 冲突,该场景建议用后者;
-- 8、调度线程池改为协程方式实现,大幅降低系统内存消耗;
-- 9、任务、执行器数据全量本地缓存;新增消息表广播通知;
-- 10、忙碌转移优化,全部机器忙碌时不再直接失败;
-- 11、失败重试间隔;
-- 12、SimpleTrigger:除Cron外,支持设置固定时间间隔触发;
-- 13、调度日志列表加上执行时长列,并支持排序;
-- 14、DAG流程任务:替换子任务,支持参数传递:配置并列的"a-b、b-c"路径列表,构成串行、并行、dag任务流程,"dagre-d3"绘图;任务依赖,流程图,子任务+会签任务,各节点日志;支持根据成功、失败选择分支;
-- 15、日期过滤:支持多个时间段排除;
-- 16、告警邮件内容,支持自定义模板配置;
-- 17、暂停状态,支持Cron 为空;
-- 18、新增任务运行模式 "GLUE模式(GO) ",支持GO任务;
-- 19、注册中心优化,实时性注册发现:心跳注册间隔10s,refresh失败则首次注册并立即更新注册信息,心跳类似;30s过期销毁;
-- 20、提供执行器Docker镜像;
-- 21、脚本任务,支持数据参数,新版本仅支持单参数不支持需要兼容;
-- 22、GLUE 模式 Web Ide 版本对比功能;
-- 23、批量调度:调度请求入queue,调度线程批量获取调度请求并发起远程调度;提高线程效率;
-- 24、多语言执行器:约定跨语言通讯方案,以及通讯接口;
-- 25、移除commons-exec,采用原生实现;
-- 26、调度中心JDK版本调整为JDK8,从而升级至最新版本SpringBoot;
-- 27、执行器服务端口与注册端口分离,支持docker动态随机端口;
-- 28、执行器端口复用,复用容器端口提供通讯服务;
-- 29、自定义失败重试时间间隔;
-- 30、分片任务全部成功后触发子任务;
-- 31、任务复制功能;点击复制是弹出新建任务弹框,并初始化被复制任务信息;
-- 32、AccessToken按照执行器维度设置;控制调度、回调;
-- 33、任务执行一次的时候指定IP;
-- 34、通讯调整;双向HTTP,回调和其他API自定义AccessToken,Restful,执行器复用容器端口;
-- 35、父子任务参数传递;流程任务等,透传动态参数;
-- 36、新增执行器描述、任务描述属性;
+- 6、延时任务:API触发,支持"动态传参、延时消费";该功能与 XXL-MQ 冲突,该场景建议用后者;
+- 7、调度线程池改为协程方式实现,大幅降低系统内存消耗;
+- 8、任务、执行器数据全量本地缓存;新增消息表广播通知;
+- 9、忙碌转移优化,全部机器忙碌时不再直接失败;
+- 10、任务触发参数优化:支持选择 "Cron触发"、"固定间隔时间触发"、"指定时间点触发"、"不选择" 等;
+- 11、调度日志列表加上执行时长列,并支持排序;
+- 12、DAG流程任务:替换子任务,支持参数传递,共享数据:配置并列的"a-b、b-c"路径列表,构成串行、并行、dag任务流程,"dagre-d3"绘图;任务依赖,流程图,子任务+会签任务,各节点日志;支持根据成功、失败选择分支;
+- 13、日期过滤:支持多个时间段排除;
+- 14、告警增强:
+    - 邮件告警:支持自定义标题、模板格式;
+    - webhook告警:支持自定义告警URL、请求体格式;
+- 15、新增任务运行模式 "GLUE模式(GO) ",支持GO任务;
+- 16、GLUE 模式 Web Ide 版本对比功能;
+- 17、注册中心优化,实时性注册发现:心跳注册间隔10s,refresh失败则首次注册并立即更新注册信息,心跳类似;30s过期销毁;
+- 18、提供执行器Docker镜像;
+- 19、脚本任务,支持数据参数,新版本仅支持单参数不支持需要兼容;
+- 20、批量调度:调度请求入queue,调度线程批量获取调度请求并发起远程调度;提高线程效率;
+- 21、多语言执行器:约定跨语言通讯方案,以及通讯接口;
+- 22、架构升级;XXL-JOB演进成一种任务调度规范和协议。默认双向Restful通讯。
+    - 调度中心API:
+        - 说明:提供给执行器使用,不局限于官方执行器;第三方可据此开发业务自己的执行器;
+        - API列表:执行器注册相关、任务回调相关;
+    - 执行器API:
+        - 说明:官方执行器,接收调度中心请求,异步回调任务结果;
+        - API列表:心跳服务、任务触发、任务终止、任务日志查询;
+- 23、执行器服务端口与注册端口分离,支持docker动态随机端口;
+- 24、执行器端口复用,复用容器端口提供通讯服务;
+- 25、分片任务全部成功后触发子任务;
+- 26、AccessToken按照执行器维度设置;控制调度、回调;
 
 
 ## 七、其他

+ 1 - 0
doc/db/tables_xxl_job.sql

@@ -5,6 +5,7 @@
 CREATE database if NOT EXISTS `xxl_job` default character set utf8mb4 collate utf8mb4_unicode_ci;
 use `xxl_job`;
 
+SET NAMES utf8mb4;
 
 CREATE TABLE `xxl_job_info` (
   `id` int(11) NOT NULL AUTO_INCREMENT,

+ 142 - 141
pom.xml

@@ -1,142 +1,143 @@
-<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
-	xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
-	<modelVersion>4.0.0</modelVersion>
-	<groupId>com.xuxueli</groupId>
-	<artifactId>xxl-job</artifactId>
-	<version>2.2.0-SNAPSHOT</version>
-	<packaging>pom</packaging>
-
-	<name>${project.artifactId}</name>
-	<description>A distributed task scheduling framework.</description>
-	<url>https://www.xuxueli.com/</url>
-
-	<modules>
-		<module>xxl-job-core</module>
-		<module>xxl-job-admin</module>
-		<module>xxl-job-executor-samples</module>
-    </modules>
-
-	<properties>
-		<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
-		<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
-		<maven.compiler.encoding>UTF-8</maven.compiler.encoding>
-		<maven.compiler.source>1.7</maven.compiler.source>
-		<maven.compiler.target>1.7</maven.compiler.target>
-		<maven.test.skip>true</maven.test.skip>
-
-		<xxl-rpc.version>1.5.0</xxl-rpc.version>
-
-		<spring.version>4.3.25.RELEASE</spring.version>
-		<spring-boot.version>1.5.22.RELEASE</spring-boot.version>
-		<mybatis-spring-boot-starter.version>1.3.5</mybatis-spring-boot-starter.version>
-		<mysql-connector-java.version>5.1.48</mysql-connector-java.version>
-
-		<slf4j-api.version>1.7.29</slf4j-api.version>
-		<junit.version>4.12</junit.version>
-
-		<groovy.version>2.5.8</groovy.version>
-
-		<maven-source-plugin.version>3.2.0</maven-source-plugin.version>
-		<maven-javadoc-plugin.version>3.1.1</maven-javadoc-plugin.version>
-		<maven-gpg-plugin.version>1.6</maven-gpg-plugin.version>
-		<maven-war-plugin.version>3.2.3</maven-war-plugin.version>
-	</properties>
-
-	<build>
-		<plugins>
-		</plugins>
-	</build>
-
-
-	<licenses>
-		<license>
-			<name>GNU General Public License version 3</name>
-			<url>https://opensource.org/licenses/GPL-3.0</url>
-		</license>
-	</licenses>
-
-	<scm>
-		<tag>master</tag>
-		<url>https://github.com/xuxueli/xxl-job.git</url>
-		<connection>scm:git:https://github.com/xuxueli/xxl-job.git</connection>
-		<developerConnection>scm:git:git@github.com:xuxueli/xxl-job.git</developerConnection>
-	</scm>
-	<developers>
-		<developer>
-			<id>XXL</id>
-			<name>xuxueli</name>
-			<email>931591021@qq.com</email>
-			<url>https://github.com/xuxueli</url>
-		</developer>
-	</developers>
-
-	<profiles>
-
-		<profile>
-			<id>release</id>
-			<build>
-				<plugins>
-					<!-- Source -->
-					<plugin>
-						<groupId>org.apache.maven.plugins</groupId>
-						<artifactId>maven-source-plugin</artifactId>
-						<version>${maven-source-plugin.version}</version>
-						<executions>
-							<execution>
-								<phase>package</phase>
-								<goals>
-									<goal>jar-no-fork</goal>
-								</goals>
-							</execution>
-						</executions>
-					</plugin>
-					<!-- Javadoc -->
-					<plugin>
-						<groupId>org.apache.maven.plugins</groupId>
-						<artifactId>maven-javadoc-plugin</artifactId>
-						<version>${maven-javadoc-plugin.version}</version>
-						<executions>
-							<execution>
-								<phase>package</phase>
-								<goals>
-									<goal>jar</goal>
-								</goals>
-								<configuration>
-									<doclint>none</doclint>
-								</configuration>
-							</execution>
-						</executions>
-					</plugin>
-					<!-- GPG -->
-                    <plugin>
-                        <groupId>org.apache.maven.plugins</groupId>
-                        <artifactId>maven-gpg-plugin</artifactId>
-                        <version>${maven-gpg-plugin.version}</version>
-						<configuration>
-							<useAgent>false</useAgent>
-						</configuration>
-                        <executions>
-                            <execution>
-                                <phase>verify</phase>
-                                <goals>
-                                    <goal>sign</goal>
-                                </goals>
-                            </execution>
-                        </executions>
-                    </plugin>
-				</plugins>
-			</build>
-			<distributionManagement>
-				<snapshotRepository>
-					<id>oss</id>
-					<url>https://oss.sonatype.org/content/repositories/snapshots/</url>
-				</snapshotRepository>
-				<repository>
-					<id>oss</id>
-					<url>https://oss.sonatype.org/service/local/staging/deploy/maven2/</url>
-				</repository>
-			</distributionManagement>
-		</profile>
-	</profiles>
-
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+	xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+	<modelVersion>4.0.0</modelVersion>
+	<groupId>com.xuxueli</groupId>
+	<artifactId>xxl-job</artifactId>
+	<version>2.2.0-SNAPSHOT</version>
+	<packaging>pom</packaging>
+
+	<name>${project.artifactId}</name>
+	<description>A distributed task scheduling framework.</description>
+	<url>https://www.xuxueli.com/</url>
+
+	<modules>
+		<module>xxl-job-core</module>
+		<module>xxl-job-admin</module>
+		<module>xxl-job-executor-samples</module>
+    </modules>
+
+	<properties>
+		<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
+		<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
+		<maven.compiler.encoding>UTF-8</maven.compiler.encoding>
+		<maven.compiler.source>1.8</maven.compiler.source>
+		<maven.compiler.target>1.8</maven.compiler.target>
+		<maven.test.skip>true</maven.test.skip>
+
+		<xxl-rpc.version>1.5.0</xxl-rpc.version>
+
+		<spring.version>5.2.5.RELEASE</spring.version>
+		<spring-boot.version>2.2.5.RELEASE</spring-boot.version>
+
+		<mybatis-spring-boot-starter.version>2.1.2</mybatis-spring-boot-starter.version>
+		<mysql-connector-java.version>8.0.19</mysql-connector-java.version>
+
+		<slf4j-api.version>1.7.30</slf4j-api.version>
+		<junit.version>4.13</junit.version>
+
+		<groovy.version>3.0.2</groovy.version>
+
+		<maven-source-plugin.version>3.2.1</maven-source-plugin.version>
+		<maven-javadoc-plugin.version>3.2.0</maven-javadoc-plugin.version>
+		<maven-gpg-plugin.version>1.6</maven-gpg-plugin.version>
+		<maven-war-plugin.version>3.2.3</maven-war-plugin.version>
+	</properties>
+
+	<build>
+		<plugins>
+		</plugins>
+	</build>
+
+
+	<licenses>
+		<license>
+			<name>GNU General Public License version 3</name>
+			<url>https://opensource.org/licenses/GPL-3.0</url>
+		</license>
+	</licenses>
+
+	<scm>
+		<tag>master</tag>
+		<url>https://github.com/xuxueli/xxl-job.git</url>
+		<connection>scm:git:https://github.com/xuxueli/xxl-job.git</connection>
+		<developerConnection>scm:git:git@github.com:xuxueli/xxl-job.git</developerConnection>
+	</scm>
+	<developers>
+		<developer>
+			<id>XXL</id>
+			<name>xuxueli</name>
+			<email>931591021@qq.com</email>
+			<url>https://github.com/xuxueli</url>
+		</developer>
+	</developers>
+
+	<profiles>
+
+		<profile>
+			<id>release</id>
+			<build>
+				<plugins>
+					<!-- Source -->
+					<plugin>
+						<groupId>org.apache.maven.plugins</groupId>
+						<artifactId>maven-source-plugin</artifactId>
+						<version>${maven-source-plugin.version}</version>
+						<executions>
+							<execution>
+								<phase>package</phase>
+								<goals>
+									<goal>jar-no-fork</goal>
+								</goals>
+							</execution>
+						</executions>
+					</plugin>
+					<!-- Javadoc -->
+					<plugin>
+						<groupId>org.apache.maven.plugins</groupId>
+						<artifactId>maven-javadoc-plugin</artifactId>
+						<version>${maven-javadoc-plugin.version}</version>
+						<executions>
+							<execution>
+								<phase>package</phase>
+								<goals>
+									<goal>jar</goal>
+								</goals>
+								<configuration>
+									<doclint>none</doclint>
+								</configuration>
+							</execution>
+						</executions>
+					</plugin>
+					<!-- GPG -->
+                    <plugin>
+                        <groupId>org.apache.maven.plugins</groupId>
+                        <artifactId>maven-gpg-plugin</artifactId>
+                        <version>${maven-gpg-plugin.version}</version>
+						<configuration>
+							<useAgent>false</useAgent>
+						</configuration>
+                        <executions>
+                            <execution>
+                                <phase>verify</phase>
+                                <goals>
+                                    <goal>sign</goal>
+                                </goals>
+                            </execution>
+                        </executions>
+                    </plugin>
+				</plugins>
+			</build>
+			<distributionManagement>
+				<snapshotRepository>
+					<id>oss</id>
+					<url>https://oss.sonatype.org/content/repositories/snapshots/</url>
+				</snapshotRepository>
+				<repository>
+					<id>oss</id>
+					<url>https://oss.sonatype.org/service/local/staging/deploy/maven2/</url>
+				</repository>
+			</distributionManagement>
+		</profile>
+	</profiles>
+
 </project>

+ 113 - 113
xxl-job-admin/pom.xml

@@ -1,113 +1,113 @@
-<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
-	xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
-	<modelVersion>4.0.0</modelVersion>
-	<parent>
-		<groupId>com.xuxueli</groupId>
-		<artifactId>xxl-job</artifactId>
-		<version>2.2.0-SNAPSHOT</version>
-	</parent>
-	<artifactId>xxl-job-admin</artifactId>
-	<packaging>jar</packaging>
-
-	<dependencyManagement>
-		<dependencies>
-			<dependency>
-				<groupId>org.springframework.boot</groupId>
-				<artifactId>spring-boot-starter-parent</artifactId>
-				<version>${spring-boot.version}</version>
-				<type>pom</type>
-				<scope>import</scope>
-			</dependency>
-		</dependencies>
-	</dependencyManagement>
-
-	<dependencies>
-
-		<!-- starter-web:spring-webmvc + autoconfigure + logback + yaml + tomcat -->
-		<dependency>
-			<groupId>org.springframework.boot</groupId>
-			<artifactId>spring-boot-starter-web</artifactId>
-		</dependency>
-		<!-- starter-test:junit + spring-test + mockito -->
-		<dependency>
-			<groupId>org.springframework.boot</groupId>
-			<artifactId>spring-boot-starter-test</artifactId>
-			<scope>test</scope>
-		</dependency>
-
-		<!-- freemarker-starter -->
-		<dependency>
-			<groupId>org.springframework.boot</groupId>
-			<artifactId>spring-boot-starter-freemarker</artifactId>
-		</dependency>
-
-		<!-- mail-starter -->
-		<dependency>
-			<groupId>org.springframework.boot</groupId>
-			<artifactId>spring-boot-starter-mail</artifactId>
-		</dependency>
-
-		<!-- starter-actuator -->
-		<dependency>
-			<groupId>org.springframework.boot</groupId>
-			<artifactId>spring-boot-starter-actuator</artifactId>
-		</dependency>
-
-		<!-- mybatis-starter:mybatis + mybatis-spring + tomcat-jdbc(default) -->
-		<dependency>
-			<groupId>org.mybatis.spring.boot</groupId>
-			<artifactId>mybatis-spring-boot-starter</artifactId>
-			<version>${mybatis-spring-boot-starter.version}</version>
-		</dependency>
-		<!-- mysql -->
-		<dependency>
-			<groupId>mysql</groupId>
-			<artifactId>mysql-connector-java</artifactId>
-			<version>${mysql-connector-java.version}</version>
-		</dependency>
-
-		<!-- xxl-job-core -->
-		<dependency>
-			<groupId>com.xuxueli</groupId>
-			<artifactId>xxl-job-core</artifactId>
-			<version>${project.parent.version}</version>
-		</dependency>
-
-	</dependencies>
-
-	<build>
-		<plugins>
-			<plugin>
-				<groupId>org.springframework.boot</groupId>
-				<artifactId>spring-boot-maven-plugin</artifactId>
-				<version>${spring-boot.version}</version>
-				<executions>
-					<execution>
-						<goals>
-							<goal>repackage</goal>
-						</goals>
-					</execution>
-				</executions>
-			</plugin>
-			<!-- docker -->
-			<plugin>
-				<groupId>com.spotify</groupId>
-				<artifactId>docker-maven-plugin</artifactId>
-				<version>0.4.13</version>
-				<configuration>
-					<!-- made of '[a-z0-9-_.]' -->
-					<imageName>${project.artifactId}:${project.version}</imageName>
-					<dockerDirectory>${project.basedir}</dockerDirectory>
-					<resources>
-						<resource>
-							<targetPath>/</targetPath>
-							<directory>${project.build.directory}</directory>
-							<include>${project.build.finalName}.jar</include>
-						</resource>
-					</resources>
-				</configuration>
-			</plugin>
-		</plugins>
-	</build>
-
-</project>
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+	xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+	<modelVersion>4.0.0</modelVersion>
+	<parent>
+		<groupId>com.xuxueli</groupId>
+		<artifactId>xxl-job</artifactId>
+		<version>2.2.0-SNAPSHOT</version>
+	</parent>
+	<artifactId>xxl-job-admin</artifactId>
+	<packaging>jar</packaging>
+
+	<dependencyManagement>
+		<dependencies>
+			<dependency>
+				<groupId>org.springframework.boot</groupId>
+				<artifactId>spring-boot-starter-parent</artifactId>
+				<version>${spring-boot.version}</version>
+				<type>pom</type>
+				<scope>import</scope>
+			</dependency>
+		</dependencies>
+	</dependencyManagement>
+
+	<dependencies>
+
+		<!-- starter-web:spring-webmvc + autoconfigure + logback + yaml + tomcat -->
+		<dependency>
+			<groupId>org.springframework.boot</groupId>
+			<artifactId>spring-boot-starter-web</artifactId>
+		</dependency>
+		<!-- starter-test:junit + spring-test + mockito -->
+		<dependency>
+			<groupId>org.springframework.boot</groupId>
+			<artifactId>spring-boot-starter-test</artifactId>
+			<scope>test</scope>
+		</dependency>
+
+		<!-- freemarker-starter -->
+		<dependency>
+			<groupId>org.springframework.boot</groupId>
+			<artifactId>spring-boot-starter-freemarker</artifactId>
+		</dependency>
+
+		<!-- mail-starter -->
+		<dependency>
+			<groupId>org.springframework.boot</groupId>
+			<artifactId>spring-boot-starter-mail</artifactId>
+		</dependency>
+
+		<!-- starter-actuator -->
+		<dependency>
+			<groupId>org.springframework.boot</groupId>
+			<artifactId>spring-boot-starter-actuator</artifactId>
+		</dependency>
+
+		<!-- mybatis-starter:mybatis + mybatis-spring + hikari(default) -->
+		<dependency>
+			<groupId>org.mybatis.spring.boot</groupId>
+			<artifactId>mybatis-spring-boot-starter</artifactId>
+			<version>${mybatis-spring-boot-starter.version}</version>
+		</dependency>
+		<!-- mysql -->
+		<dependency>
+			<groupId>mysql</groupId>
+			<artifactId>mysql-connector-java</artifactId>
+			<version>${mysql-connector-java.version}</version>
+		</dependency>
+
+		<!-- xxl-job-core -->
+		<dependency>
+			<groupId>com.xuxueli</groupId>
+			<artifactId>xxl-job-core</artifactId>
+			<version>${project.parent.version}</version>
+		</dependency>
+
+	</dependencies>
+
+	<build>
+		<plugins>
+			<plugin>
+				<groupId>org.springframework.boot</groupId>
+				<artifactId>spring-boot-maven-plugin</artifactId>
+				<version>${spring-boot.version}</version>
+				<executions>
+					<execution>
+						<goals>
+							<goal>repackage</goal>
+						</goals>
+					</execution>
+				</executions>
+			</plugin>
+			<!-- docker -->
+			<plugin>
+				<groupId>com.spotify</groupId>
+				<artifactId>docker-maven-plugin</artifactId>
+				<version>0.4.13</version>
+				<configuration>
+					<!-- made of '[a-z0-9-_.]' -->
+					<imageName>${project.artifactId}:${project.version}</imageName>
+					<dockerDirectory>${project.basedir}</dockerDirectory>
+					<resources>
+						<resource>
+							<targetPath>/</targetPath>
+							<directory>${project.build.directory}</directory>
+							<include>${project.build.finalName}.jar</include>
+						</resource>
+					</resources>
+				</configuration>
+			</plugin>
+		</plugins>
+	</build>
+
+</project>

+ 20 - 0
xxl-job-admin/src/main/java/com/xxl/job/admin/core/alarm/JobAlarm.java

@@ -0,0 +1,20 @@
+package com.xxl.job.admin.core.alarm;
+
+import com.xxl.job.admin.core.model.XxlJobInfo;
+import com.xxl.job.admin.core.model.XxlJobLog;
+
+/**
+ * @author xuxueli 2020-01-19
+ */
+public interface JobAlarm {
+
+    /**
+     * job alarm
+     *
+     * @param info
+     * @param jobLog
+     * @return
+     */
+    public boolean doAlarm(XxlJobInfo info, XxlJobLog jobLog);
+
+}

+ 65 - 0
xxl-job-admin/src/main/java/com/xxl/job/admin/core/alarm/JobAlarmer.java

@@ -0,0 +1,65 @@
+package com.xxl.job.admin.core.alarm;
+
+import com.xxl.job.admin.core.model.XxlJobInfo;
+import com.xxl.job.admin.core.model.XxlJobLog;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.beans.BeansException;
+import org.springframework.beans.factory.InitializingBean;
+import org.springframework.context.ApplicationContext;
+import org.springframework.context.ApplicationContextAware;
+import org.springframework.stereotype.Component;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
+
+@Component
+public class JobAlarmer implements ApplicationContextAware, InitializingBean {
+    private static Logger logger = LoggerFactory.getLogger(JobAlarmer.class);
+
+    private ApplicationContext applicationContext;
+    private List<JobAlarm> jobAlarmList;
+
+    @Override
+    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
+        this.applicationContext = applicationContext;
+    }
+
+    @Override
+    public void afterPropertiesSet() throws Exception {
+        Map<String, JobAlarm> serviceBeanMap = applicationContext.getBeansOfType(JobAlarm.class);
+        if (serviceBeanMap != null && serviceBeanMap.size() > 0) {
+            jobAlarmList = new ArrayList<JobAlarm>(serviceBeanMap.values());
+        }
+    }
+
+    /**
+     * job alarm
+     *
+     * @param info
+     * @param jobLog
+     * @return
+     */
+    public boolean alarm(XxlJobInfo info, XxlJobLog jobLog) {
+
+        boolean result = false;
+        if (jobAlarmList!=null && jobAlarmList.size()>0) {
+            result = true;  // success means all-success
+            for (JobAlarm alarm: jobAlarmList) {
+                boolean resultItem = false;
+                try {
+                    resultItem = alarm.doAlarm(info, jobLog);
+                } catch (Exception e) {
+                    logger.error(e.getMessage(), e);
+                }
+                if (!resultItem) {
+                    result = false;
+                }
+            }
+        }
+
+        return result;
+    }
+
+}

+ 117 - 0
xxl-job-admin/src/main/java/com/xxl/job/admin/core/alarm/impl/EmailJobAlarm.java

@@ -0,0 +1,117 @@
+package com.xxl.job.admin.core.alarm.impl;
+
+import com.xxl.job.admin.core.alarm.JobAlarm;
+import com.xxl.job.admin.core.conf.XxlJobAdminConfig;
+import com.xxl.job.admin.core.model.XxlJobGroup;
+import com.xxl.job.admin.core.model.XxlJobInfo;
+import com.xxl.job.admin.core.model.XxlJobLog;
+import com.xxl.job.admin.core.util.I18nUtil;
+import com.xxl.job.core.biz.model.ReturnT;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.mail.javamail.MimeMessageHelper;
+import org.springframework.stereotype.Component;
+
+import javax.mail.internet.MimeMessage;
+import java.text.MessageFormat;
+import java.util.Arrays;
+import java.util.HashSet;
+import java.util.Set;
+
+/**
+ * job alarm by email
+ *
+ * @author xuxueli 2020-01-19
+ */
+@Component
+public class EmailJobAlarm implements JobAlarm {
+    private static Logger logger = LoggerFactory.getLogger(EmailJobAlarm.class);
+
+    /**
+     * fail alarm
+     *
+     * @param jobLog
+     */
+    public boolean doAlarm(XxlJobInfo info, XxlJobLog jobLog){
+        boolean alarmResult = true;
+
+        // send monitor email
+        if (info!=null && info.getAlarmEmail()!=null && info.getAlarmEmail().trim().length()>0) {
+
+            // alarmContent
+            String alarmContent = "Alarm Job LogId=" + jobLog.getId();
+            if (jobLog.getTriggerCode() != ReturnT.SUCCESS_CODE) {
+                alarmContent += "<br>TriggerMsg=<br>" + jobLog.getTriggerMsg();
+            }
+            if (jobLog.getHandleCode()>0 && jobLog.getHandleCode() != ReturnT.SUCCESS_CODE) {
+                alarmContent += "<br>HandleCode=" + jobLog.getHandleMsg();
+            }
+
+            // email info
+            XxlJobGroup group = XxlJobAdminConfig.getAdminConfig().getXxlJobGroupDao().load(Integer.valueOf(info.getJobGroup()));
+            String personal = I18nUtil.getString("admin_name_full");
+            String title = I18nUtil.getString("jobconf_monitor");
+            String content = MessageFormat.format(loadEmailJobAlarmTemplate(),
+                    group!=null?group.getTitle():"null",
+                    info.getId(),
+                    info.getJobDesc(),
+                    alarmContent);
+
+            Set<String> emailSet = new HashSet<String>(Arrays.asList(info.getAlarmEmail().split(",")));
+            for (String email: emailSet) {
+
+                // make mail
+                try {
+                    MimeMessage mimeMessage = XxlJobAdminConfig.getAdminConfig().getMailSender().createMimeMessage();
+
+                    MimeMessageHelper helper = new MimeMessageHelper(mimeMessage, true);
+                    helper.setFrom(XxlJobAdminConfig.getAdminConfig().getEmailUserName(), personal);
+                    helper.setTo(email);
+                    helper.setSubject(title);
+                    helper.setText(content, true);
+
+                    XxlJobAdminConfig.getAdminConfig().getMailSender().send(mimeMessage);
+                } catch (Exception e) {
+                    logger.error(">>>>>>>>>>> xxl-job, job fail alarm email send error, JobLogId:{}", jobLog.getId(), e);
+
+                    alarmResult = false;
+                }
+
+            }
+        }
+
+        return alarmResult;
+    }
+
+    /**
+     * load email job alarm template
+     *
+     * @return
+     */
+    private static final String loadEmailJobAlarmTemplate(){
+        String mailBodyTemplate = "<h5>" + I18nUtil.getString("jobconf_monitor_detail") + ":</span>" +
+                "<table border=\"1\" cellpadding=\"3\" style=\"border-collapse:collapse; width:80%;\" >\n" +
+                "   <thead style=\"font-weight: bold;color: #ffffff;background-color: #ff8c00;\" >" +
+                "      <tr>\n" +
+                "         <td width=\"20%\" >"+ I18nUtil.getString("jobinfo_field_jobgroup") +"</td>\n" +
+                "         <td width=\"10%\" >"+ I18nUtil.getString("jobinfo_field_id") +"</td>\n" +
+                "         <td width=\"20%\" >"+ I18nUtil.getString("jobinfo_field_jobdesc") +"</td>\n" +
+                "         <td width=\"10%\" >"+ I18nUtil.getString("jobconf_monitor_alarm_title") +"</td>\n" +
+                "         <td width=\"40%\" >"+ I18nUtil.getString("jobconf_monitor_alarm_content") +"</td>\n" +
+                "      </tr>\n" +
+                "   </thead>\n" +
+                "   <tbody>\n" +
+                "      <tr>\n" +
+                "         <td>{0}</td>\n" +
+                "         <td>{1}</td>\n" +
+                "         <td>{2}</td>\n" +
+                "         <td>"+ I18nUtil.getString("jobconf_monitor_alarm_type") +"</td>\n" +
+                "         <td>{3}</td>\n" +
+                "      </tr>\n" +
+                "   </tbody>\n" +
+                "</table>";
+
+        return mailBodyTemplate;
+    }
+
+}

+ 11 - 0
xxl-job-admin/src/main/java/com/xxl/job/admin/core/conf/XxlJobAdminConfig.java

@@ -1,5 +1,6 @@
 package com.xxl.job.admin.core.conf;
 
+import com.xxl.job.admin.core.alarm.JobAlarmer;
 import com.xxl.job.admin.core.scheduler.XxlJobScheduler;
 import com.xxl.job.admin.dao.*;
 import org.springframework.beans.factory.DisposableBean;
@@ -10,6 +11,7 @@ import org.springframework.stereotype.Component;
 
 import javax.annotation.Resource;
 import javax.sql.DataSource;
+import java.util.Arrays;
 
 /**
  * xxl-job config
@@ -81,9 +83,14 @@ public class XxlJobAdminConfig implements InitializingBean, DisposableBean {
     private JavaMailSender mailSender;
     @Resource
     private DataSource dataSource;
+    @Resource
+    private JobAlarmer jobAlarmer;
 
 
     public String getI18n() {
+        if (!Arrays.asList("zh_CN", "zh_TC", "en").contains(i18n)) {
+            return "zh_CN";
+        }
         return i18n;
     }
 
@@ -144,4 +151,8 @@ public class XxlJobAdminConfig implements InitializingBean, DisposableBean {
         return dataSource;
     }
 
+    public JobAlarmer getJobAlarmer() {
+        return jobAlarmer;
+    }
+
 }

+ 1 - 100
xxl-job-admin/src/main/java/com/xxl/job/admin/core/thread/JobFailMonitorHelper.java

@@ -1,22 +1,14 @@
 package com.xxl.job.admin.core.thread;
 
 import com.xxl.job.admin.core.conf.XxlJobAdminConfig;
-import com.xxl.job.admin.core.model.XxlJobGroup;
 import com.xxl.job.admin.core.model.XxlJobInfo;
 import com.xxl.job.admin.core.model.XxlJobLog;
 import com.xxl.job.admin.core.trigger.TriggerTypeEnum;
 import com.xxl.job.admin.core.util.I18nUtil;
-import com.xxl.job.core.biz.model.ReturnT;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
-import org.springframework.mail.javamail.MimeMessageHelper;
 
-import javax.mail.internet.MimeMessage;
-import java.text.MessageFormat;
-import java.util.Arrays;
-import java.util.HashSet;
 import java.util.List;
-import java.util.Set;
 import java.util.concurrent.TimeUnit;
 
 /**
@@ -69,13 +61,7 @@ public class JobFailMonitorHelper {
 								// 2、fail alarm monitor
 								int newAlarmStatus = 0;		// 告警状态:0-默认、-1=锁定状态、1-无需告警、2-告警成功、3-告警失败
 								if (info!=null && info.getAlarmEmail()!=null && info.getAlarmEmail().trim().length()>0) {
-									boolean alarmResult = true;
-									try {
-										alarmResult = failAlarm(info, log);
-									} catch (Exception e) {
-										alarmResult = false;
-										logger.error(e.getMessage(), e);
-									}
+									boolean alarmResult = XxlJobAdminConfig.getAdminConfig().getJobAlarmer().alarm(info, log);
 									newAlarmStatus = alarmResult?2:3;
 								} else {
 									newAlarmStatus = 1;
@@ -121,89 +107,4 @@ public class JobFailMonitorHelper {
 		}
 	}
 
-
-	// ---------------------- alarm ----------------------
-
-	// email alarm template
-	private static final String mailBodyTemplate = "<h5>" + I18nUtil.getString("jobconf_monitor_detail") + ":</span>" +
-			"<table border=\"1\" cellpadding=\"3\" style=\"border-collapse:collapse; width:80%;\" >\n" +
-			"   <thead style=\"font-weight: bold;color: #ffffff;background-color: #ff8c00;\" >" +
-			"      <tr>\n" +
-			"         <td width=\"20%\" >"+ I18nUtil.getString("jobinfo_field_jobgroup") +"</td>\n" +
-			"         <td width=\"10%\" >"+ I18nUtil.getString("jobinfo_field_id") +"</td>\n" +
-			"         <td width=\"20%\" >"+ I18nUtil.getString("jobinfo_field_jobdesc") +"</td>\n" +
-			"         <td width=\"10%\" >"+ I18nUtil.getString("jobconf_monitor_alarm_title") +"</td>\n" +
-			"         <td width=\"40%\" >"+ I18nUtil.getString("jobconf_monitor_alarm_content") +"</td>\n" +
-			"      </tr>\n" +
-			"   </thead>\n" +
-			"   <tbody>\n" +
-			"      <tr>\n" +
-			"         <td>{0}</td>\n" +
-			"         <td>{1}</td>\n" +
-			"         <td>{2}</td>\n" +
-			"         <td>"+ I18nUtil.getString("jobconf_monitor_alarm_type") +"</td>\n" +
-			"         <td>{3}</td>\n" +
-			"      </tr>\n" +
-			"   </tbody>\n" +
-			"</table>";
-
-	/**
-	 * fail alarm
-	 *
-	 * @param jobLog
-	 */
-	private boolean failAlarm(XxlJobInfo info, XxlJobLog jobLog){
-		boolean alarmResult = true;
-
-		// send monitor email
-		if (info!=null && info.getAlarmEmail()!=null && info.getAlarmEmail().trim().length()>0) {
-
-			// alarmContent
-			String alarmContent = "Alarm Job LogId=" + jobLog.getId();
-			if (jobLog.getTriggerCode() != ReturnT.SUCCESS_CODE) {
-				alarmContent += "<br>TriggerMsg=<br>" + jobLog.getTriggerMsg();
-			}
-			if (jobLog.getHandleCode()>0 && jobLog.getHandleCode() != ReturnT.SUCCESS_CODE) {
-				alarmContent += "<br>HandleCode=" + jobLog.getHandleMsg();
-			}
-
-			// email info
-			XxlJobGroup group = XxlJobAdminConfig.getAdminConfig().getXxlJobGroupDao().load(Integer.valueOf(info.getJobGroup()));
-			String personal = I18nUtil.getString("admin_name_full");
-			String title = I18nUtil.getString("jobconf_monitor");
-			String content = MessageFormat.format(mailBodyTemplate,
-					group!=null?group.getTitle():"null",
-					info.getId(),
-					info.getJobDesc(),
-					alarmContent);
-
-			Set<String> emailSet = new HashSet<String>(Arrays.asList(info.getAlarmEmail().split(",")));
-			for (String email: emailSet) {
-
-				// make mail
-				try {
-					MimeMessage mimeMessage = XxlJobAdminConfig.getAdminConfig().getMailSender().createMimeMessage();
-
-					MimeMessageHelper helper = new MimeMessageHelper(mimeMessage, true);
-					helper.setFrom(XxlJobAdminConfig.getAdminConfig().getEmailUserName(), personal);
-					helper.setTo(email);
-					helper.setSubject(title);
-					helper.setText(content, true);
-
-					XxlJobAdminConfig.getAdminConfig().getMailSender().send(mimeMessage);
-				} catch (Exception e) {
-					logger.error(">>>>>>>>>>> xxl-job, job fail alarm email send error, JobLogId:{}", jobLog.getId(), e);
-
-					alarmResult = false;
-				}
-
-			}
-		}
-
-		// do something, custom alarm strategy, such as sms
-
-
-		return alarmResult;
-	}
-
 }

+ 1 - 2
xxl-job-admin/src/main/java/com/xxl/job/admin/core/util/I18nUtil.java

@@ -30,8 +30,7 @@ public class I18nUtil {
         try {
             // build i18n prop
             String i18n = XxlJobAdminConfig.getAdminConfig().getI18n();
-            i18n = (i18n!=null && i18n.trim().length()>0)?("_"+i18n):i18n;
-            String i18nFile = MessageFormat.format("i18n/message{0}.properties", i18n);
+            String i18nFile = MessageFormat.format("i18n/message_{0}.properties", i18n);
 
             // load prop
             Resource resource = new ClassPathResource(i18nFile);

+ 17 - 12
xxl-job-admin/src/main/resources/application.properties

@@ -1,9 +1,9 @@
 ### web
 server.port=8080
-server.context-path=/xxl-job-admin
+server.servlet.context-path=/xxl-job-admin
 
 ### actuator
-management.context-path=/actuator
+management.server.servlet.context-path=/actuator
 management.health.mail.enabled=false
 
 ### resources
@@ -20,6 +20,7 @@ spring.freemarker.settings.number_format=0.##########
 
 ### mybatis
 mybatis.mapper-locations=classpath:/mybatis-mapper/*Mapper.xml
+#mybatis.type-aliases-package=com.xxl.job.admin.core.model
 
 ### xxl-job, datasource
 spring.datasource.url=jdbc:mysql://127.0.0.1:3306/xxl_job?Unicode=true&characterEncoding=UTF-8
@@ -27,14 +28,18 @@ spring.datasource.username=root
 spring.datasource.password=root_pwd
 spring.datasource.driver-class-name=com.mysql.jdbc.Driver
 
-spring.datasource.type=org.apache.tomcat.jdbc.pool.DataSource
-spring.datasource.tomcat.max-wait=10000
-spring.datasource.tomcat.max-active=30
-spring.datasource.tomcat.test-on-borrow=true
-spring.datasource.tomcat.validation-query=SELECT 1
-spring.datasource.tomcat.validation-interval=30000
-
-### xxl-job email
+### datasource-pool
+spring.datasource.type=com.zaxxer.hikari.HikariDataSource
+spring.datasource.hikari.minimum-idle=10
+spring.datasource.hikari.maximum-pool-size=30
+spring.datasource.hikari.auto-commit=true
+spring.datasource.hikari.idle-timeout=30000
+spring.datasource.hikari.pool-name=HikariCP
+spring.datasource.hikari.max-lifetime=900000
+spring.datasource.hikari.connection-timeout=10000
+spring.datasource.hikari.connection-test-query=SELECT 1
+
+### xxl-job, email
 spring.mail.host=smtp.qq.com
 spring.mail.port=25
 spring.mail.username=xxx@qq.com
@@ -47,8 +52,8 @@ spring.mail.properties.mail.smtp.socketFactory.class=javax.net.ssl.SSLSocketFact
 ### xxl-job, access token
 xxl.job.accessToken=
 
-### xxl-job, i18n (default empty as chinese, "en" as english)
-xxl.job.i18n=
+### xxl-job, i18n (default is zh_CN, and you can choose "zh_CN", "zh_TC" and "en")
+xxl.job.i18n=zh_CN
 
 ## xxl-job, triggerpool max size
 xxl.job.triggerpool.fast.max=200

xxl-job-admin/src/main/resources/i18n/message.properties → xxl-job-admin/src/main/resources/i18n/message_zh_CN.properties


+ 262 - 0
xxl-job-admin/src/main/resources/i18n/message_zh_TC.properties

@@ -0,0 +1,262 @@
+admin_name=任務調度中心
+admin_name_full=分布式任務調度平臺XXL-JOB
+admin_version=2.1.1
+admin_i18n=
+
+## system
+system_tips=系統提示
+system_ok=確定
+system_close=關閉
+system_save=儲存
+system_cancel=取消
+system_search=搜尋
+system_status=狀態
+system_opt=操作
+system_please_input=請輸入
+system_please_choose=请選擇
+system_success=成功
+system_fail=失敗
+system_add_suc=新增成功
+system_add_fail=新增失敗
+system_update_suc=更新成功
+system_update_fail=更新失敗
+system_all=全部
+system_api_error=API錯誤
+system_show=查看
+system_empty=無
+system_opt_suc=操作成功
+system_opt_fail=操作失敗
+system_opt_edit=編輯
+system_opt_del=刪除
+system_unvalid=非法
+system_not_found=不存在
+system_nav=導航
+system_digits=整數
+system_lengh_limit=長度限制
+system_permission_limit=權限控管
+system_welcome=歡迎
+
+## daterangepicker
+daterangepicker_ranges_recent_hour=最近一小時
+daterangepicker_ranges_today=今日
+daterangepicker_ranges_yesterday=昨日
+daterangepicker_ranges_this_month=本月
+daterangepicker_ranges_last_month=上個月
+daterangepicker_ranges_recent_week=最近一周
+daterangepicker_ranges_recent_month=最近一月
+daterangepicker_custom_name=自定義
+daterangepicker_custom_starttime=起始時間
+daterangepicker_custom_endtime=結束時間
+daterangepicker_custom_daysofweek=日,一,二,三,四,五,六
+daterangepicker_custom_monthnames=一月,二月,三月,四月,五月,六月,七月,八月,九月,十月,十一月,十二月
+
+## dataTable
+dataTable_sProcessing=處理中...
+dataTable_sLengthMenu=每頁 _MENU_ 條記錄
+dataTable_sZeroRecords=沒有相符合記錄
+dataTable_sInfo=第 _PAGE_ 頁 ( 總共 _PAGES_ 頁,_TOTAL_ 條記錄 )
+dataTable_sInfoEmpty=無記錄
+dataTable_sInfoFiltered=(由 _MAX_ 項結果過濾)
+dataTable_sSearch=搜尋
+dataTable_sEmptyTable=表中資料為空
+dataTable_sLoadingRecords=載入中...
+dataTable_sFirst=首頁
+dataTable_sPrevious=上頁
+dataTable_sNext=下頁
+dataTable_sLast=末頁
+dataTable_sSortAscending=: 以升幂排序此列
+dataTable_sSortDescending=: 以降幂排序此列
+
+## login
+login_btn=登入
+login_remember_me=記住密碼
+login_username_placeholder=請輸入登入帳號
+login_password_placeholder=請輸入登入密碼
+login_username_empty=請輸入登入帳號
+login_username_lt_4=登入帳號不應低於4位數
+login_password_empty=請輸入登入密碼
+login_password_lt_4=登入密碼不應低於4位數
+login_success=登入成功
+login_fail=登入失敗
+login_param_empty=帳號或密碼為空值
+login_param_unvalid=帳號或密碼錯誤
+
+## logout
+logout_btn=登出
+logout_confirm=確認登出?
+logout_success=登出成功
+logout_fail=登出失敗
+
+## change pwd
+change_pwd=修改密碼
+change_pwd_suc_to_logout=修改密碼成功,即將登出
+change_pwd_field_newpwd=新密碼
+
+## dashboard
+job_dashboard_name=運行報表
+job_dashboard_job_num=任務數量
+job_dashboard_job_num_tip=調度中心運行的任務數量
+job_dashboard_trigger_num=調度次數
+job_dashboard_trigger_num_tip=調度中心觸發的調度次數
+job_dashboard_jobgroup_num=執行器數量
+job_dashboard_jobgroup_num_tip=調度中心在線的執行器機器數量
+job_dashboard_report=調度報表
+job_dashboard_report_loaddata_fail=調度報表資料加載異常
+job_dashboard_date_report=日期分布圖
+job_dashboard_rate_report=成功比例圖
+
+## job info
+jobinfo_name=任務管理
+jobinfo_job=任務
+jobinfo_field_add=新增
+jobinfo_field_update=更新任務
+jobinfo_field_id=任務ID
+jobinfo_field_jobgroup=執行器
+jobinfo_field_jobdesc=任務描述
+jobinfo_field_gluetype=運行模式
+jobinfo_field_executorparam=任務參數
+jobinfo_field_cron_unvalid=Cron 格式非法
+jobinfo_field_cron_never_fire=Cron 格式非法,永遠不會觸發
+jobinfo_field_author=負責人
+jobinfo_field_timeout=任務超時秒數
+jobinfo_field_alarmemail=告警郵件
+jobinfo_field_alarmemail_placeholder=輸入多個告警郵件地址,請以逗號分隔
+jobinfo_field_executorRouteStrategy=路由策略
+jobinfo_field_childJobId=子任務ID
+jobinfo_field_childJobId_placeholder=輸入子任務ID,如有多個請以逗號分隔
+jobinfo_field_executorBlockStrategy=阻塞處理策略
+jobinfo_field_executorFailRetryCount=失敗重試次數
+jobinfo_field_executorFailRetryCount_placeholder=失敗重試次數,大於零時生效
+jobinfo_script_location=腳本位置
+jobinfo_shard_index=分片序號
+jobinfo_shard_total=分片總數
+jobinfo_opt_stop=停止
+jobinfo_opt_start=啟動
+jobinfo_opt_log=查詢日誌
+jobinfo_opt_run=執行一次
+jobinfo_opt_registryinfo=注冊節點
+jobinfo_opt_next_time=下次執行時間
+jobinfo_glue_remark=源碼備註
+jobinfo_glue_remark_limit=源碼備註長度限制為4~100
+jobinfo_glue_rollback=版本回復
+jobinfo_glue_jobid_unvalid=任務ID非法
+jobinfo_glue_gluetype_unvalid=該任務非GLUE模式
+jobinfo_field_executorTimeout_placeholder=任務超時時間,單位秒,大於零時生效
+
+## job log
+joblog_name=調度日誌
+joblog_status=狀態
+joblog_status_all=全部
+joblog_status_suc=成功
+joblog_status_fail=失敗
+joblog_status_running=進行中
+joblog_field_triggerTime=調度時間
+joblog_field_triggerCode=調度結果
+joblog_field_triggerMsg=調度備註
+joblog_field_handleTime=執行時間
+joblog_field_handleCode=執行结果
+joblog_field_handleMsg=執行備註
+joblog_field_executorAddress=執行器地址
+joblog_clean=清理
+joblog_clean_log=日誌清理
+joblog_clean_type=清理方式
+joblog_clean_type_1=清理一個月之前日誌資料
+joblog_clean_type_2=清理三個月之前日誌資料
+joblog_clean_type_3=清理六個月之前日誌資料
+joblog_clean_type_4=清理一年之前日誌資料
+joblog_clean_type_5=清理一千條以前日誌資料
+joblog_clean_type_6=清理一萬條以前日誌資料
+joblog_clean_type_7=清理三萬條以前日誌資料
+joblog_clean_type_8=清理十萬條以前日誌資料
+joblog_clean_type_9=清理所有日誌資料
+joblog_clean_type_unvalid=清理類型參数異常
+joblog_handleCode_200=成功
+joblog_handleCode_500=失敗
+joblog_handleCode_502=失敗(超時)
+joblog_kill_log=终止任務
+joblog_kill_log_limit=調度失敗,無法终止日誌
+joblog_kill_log_byman=人為操作主動終止
+joblog_rolling_log=執行日誌
+joblog_rolling_log_refresh=更新
+joblog_rolling_log_triggerfail=任務發起調度失敗,無法查看執行日誌
+joblog_rolling_log_failoften=終止請求Rolling日誌,請求失敗次數超上限,可刷新頁面重新加載日誌
+joblog_logid_unvalid=日誌ID非法
+
+## job group
+jobgroup_name=執行器管理
+jobgroup_list=執行器列表
+jobgroup_add=新增執行器
+jobgroup_edit=編輯執行器
+jobgroup_del=刪除執行器
+jobgroup_field_order=排序
+jobgroup_field_title=名稱
+jobgroup_field_addressType=注冊方式
+jobgroup_field_addressType_0=自動注冊
+jobgroup_field_addressType_1=手動登錄
+jobgroup_field_addressType_limit=手動登錄注冊方式,機器地址不可為空
+jobgroup_field_registryList=機器地址
+jobgroup_field_registryList_unvalid=機器地址格式非法
+jobgroup_field_registryList_placeholder=請輸入執行器地址列表,多個地址請以逗號分隔
+jobgroup_field_appName_limit=限制以小寫字母開頭,由小寫字母、數字和中划線組成
+jobgroup_field_appName_length=AppName長度限制為4~64
+jobgroup_field_title_length=名稱長度限制為4~12
+jobgroup_field_order_digits=請輸入整數
+jobgroup_field_orderrange=取值範圍為1~1000
+jobgroup_del_limit_0=拒絕刪除,該執行器使用中
+jobgroup_del_limit_1=拒絕删除,系统至少保留一個執行器
+jobgroup_empty=不存在有效執行器,請聯絡系統管理員
+
+## job conf
+jobconf_block_SERIAL_EXECUTION=單機串行
+jobconf_block_DISCARD_LATER=丢棄后續調度
+jobconf_block_COVER_EARLY=覆蓋之前調度
+jobconf_route_first=第一個
+jobconf_route_last=最後一個
+jobconf_route_round=輪詢
+jobconf_route_random=隨機
+jobconf_route_consistenthash=一致性HASH
+jobconf_route_lfu=最不經常使用
+jobconf_route_lru=最近最久未使用
+jobconf_route_failover=故障轉移
+jobconf_route_busyover=忙碌轉移
+jobconf_route_shard=分片廣播
+jobconf_idleBeat=空閒檢測
+jobconf_beat=心跳檢測
+jobconf_monitor=任務調度中心監控告警
+jobconf_monitor_detail=監控告警明细
+jobconf_monitor_alarm_title=告警類型
+jobconf_monitor_alarm_type=調度失敗
+jobconf_monitor_alarm_content=告警内容
+jobconf_trigger_admin_adress=調度機器
+jobconf_trigger_exe_regtype=執行器-注冊方式
+jobconf_trigger_exe_regaddress=執行器-地址列表
+jobconf_trigger_address_empty=調度失敗:執行器地址為空
+jobconf_trigger_run=觸發調度
+jobconf_trigger_child_run=觸發子任務
+jobconf_callback_child_msg1={0}/{1} [任務ID={2}], 觸發{3}, 觸發備註: {4} <br>
+jobconf_callback_child_msg2={0}/{1} [任務ID={2}], 觸發失败, 觸發備註: 任務ID格式錯誤 <br>
+jobconf_trigger_type=任務觸發類型
+jobconf_trigger_type_cron=Cron觸發
+jobconf_trigger_type_manual=手動觸發
+jobconf_trigger_type_parent=父任務觸發
+jobconf_trigger_type_api=API觸發
+jobconf_trigger_type_retry=失敗重試觸發
+
+## user
+user_manage=用户管理
+user_username=帳號
+user_password=密碼
+user_role=角色
+user_role_admin=管理員
+user_role_normal=普通用戶
+user_permission=權限
+user_add=新增用戶
+user_update=更新用戶
+user_username_repeat=帳號重複
+user_username_valid=限制以小寫字母開頭,由小寫字母、數字組成
+user_password_update_placeholder=請輸入新密碼,為空則不更新密碼
+user_update_loginuser_limit=禁止操作當前登入帳號
+
+## help
+job_help=使用教程
+job_help_document=官方文件

文件差異過大導致無法顯示
+ 0 - 1
xxl-job-admin/src/main/resources/static/adminlte/bower_components/bootstrap/css/bootstrap.css.map


文件差異過大導致無法顯示
+ 1 - 0
xxl-job-admin/src/main/resources/static/adminlte/bower_components/bootstrap/css/bootstrap.min.css.map


+ 2 - 2
xxl-job-admin/src/main/resources/templates/jobcode/jobcode.index.ftl

@@ -100,7 +100,7 @@
                     <h4 class="modal-title" ><i class="fa fa-fw fa-save"></i>${I18n.system_save}</h4>
                 </div>
                 <div class="modal-body">
-                    <form class="form-horizontal form" role="form" >
+                    <div class="form-horizontal form" role="form" >
                         <div class="form-group">
                             <label for="lastname" class="col-sm-2 control-label">${I18n.jobinfo_glue_remark}<font color="red">*</font></label>
                             <div class="col-sm-10"><input type="text" class="form-control" id="glueRemark" placeholder="${I18n.system_please_input}${I18n.jobinfo_glue_remark}" maxlength="64" ></div>
@@ -112,7 +112,7 @@
                                 <button type="button" class="btn btn-default" data-dismiss="modal">${I18n.system_cancel}</button>
                             </div>
                         </div>
-                    </form>
+                    </div>
                 </div>
             </div>
         </div>

+ 7 - 5
xxl-job-core/src/main/java/com/xxl/job/core/executor/impl/XxlJobSpringExecutor.java

@@ -3,8 +3,6 @@ package com.xxl.job.core.executor.impl;
 import com.xxl.job.core.biz.model.ReturnT;
 import com.xxl.job.core.executor.XxlJobExecutor;
 import com.xxl.job.core.glue.GlueFactory;
-import com.xxl.job.core.handler.IJobHandler;
-import com.xxl.job.core.handler.annotation.JobHandler;
 import com.xxl.job.core.handler.annotation.XxlJob;
 import com.xxl.job.core.handler.impl.MethodJobHandler;
 import org.slf4j.Logger;
@@ -18,9 +16,11 @@ import org.springframework.core.MethodIntrospector;
 import org.springframework.core.annotation.AnnotatedElementUtils;
 
 import java.lang.reflect.Method;
+
 import java.util.HashMap;
 import java.util.Map;
 
+
 /**
  * xxl-job executor (for spring)
  *
@@ -35,7 +35,7 @@ public class XxlJobSpringExecutor extends XxlJobExecutor implements ApplicationC
     public void afterPropertiesSet() throws Exception {
 
         // init JobHandler Repository
-        initJobHandlerRepository(applicationContext);
+        /*initJobHandlerRepository(applicationContext);*/
 
         // init JobHandler Repository (for method)
         initJobHandlerMethodRepository(applicationContext);
@@ -54,7 +54,7 @@ public class XxlJobSpringExecutor extends XxlJobExecutor implements ApplicationC
     }
 
 
-    private void initJobHandlerRepository(ApplicationContext applicationContext) {
+    /*private void initJobHandlerRepository(ApplicationContext applicationContext) {
         if (applicationContext == null) {
             return;
         }
@@ -74,13 +74,14 @@ public class XxlJobSpringExecutor extends XxlJobExecutor implements ApplicationC
                 }
             }
         }
-    }
+    }*/
 
     private void initJobHandlerMethodRepository(ApplicationContext applicationContext) {
         if (applicationContext == null) {
             return;
         }
         // init job handler from method
+
         String[] beanDefinitionNames = applicationContext.getBeanNamesForType(Object.class, false, true);
         for (String beanDefinitionName : beanDefinitionNames) {
             Object bean = applicationContext.getBean(beanDefinitionName);
@@ -150,6 +151,7 @@ public class XxlJobSpringExecutor extends XxlJobExecutor implements ApplicationC
                 registJobHandler(name, new MethodJobHandler(bean, method, initMethod, destroyMethod));
             }
         }
+
     }
 
     // ---------------------- applicationContext ----------------------

+ 24 - 24
xxl-job-core/src/main/java/com/xxl/job/core/handler/annotation/JobHandler.java

@@ -1,24 +1,24 @@
-package com.xxl.job.core.handler.annotation;
-
-import java.lang.annotation.ElementType;
-import java.lang.annotation.Inherited;
-import java.lang.annotation.Retention;
-import java.lang.annotation.RetentionPolicy;
-import java.lang.annotation.Target;
-
-/**
- * annotation for job handler
- *
- * will be replaced by {@link com.xxl.job.core.handler.annotation.XxlJob}
- *
- * @author 2016-5-17 21:06:49
- */
-@Target({ElementType.TYPE})
-@Retention(RetentionPolicy.RUNTIME)
-@Inherited
-@Deprecated
-public @interface JobHandler {
-
-    String value() default "";
-
-}
+//package com.xxl.job.core.handler.annotation;
+//
+//import java.lang.annotation.ElementType;
+//import java.lang.annotation.Inherited;
+//import java.lang.annotation.Retention;
+//import java.lang.annotation.RetentionPolicy;
+//import java.lang.annotation.Target;
+//
+///**
+// * annotation for job handler
+// *
+// * will be replaced by {@link com.xxl.job.core.handler.annotation.XxlJob}
+// *
+// * @author 2016-5-17 21:06:49
+// */
+//@Target({ElementType.TYPE})
+//@Retention(RetentionPolicy.RUNTIME)
+//@Inherited
+//@Deprecated
+//public @interface JobHandler {
+//
+//    String value();
+//
+//}

+ 1 - 1
xxl-job-core/src/main/java/com/xxl/job/core/handler/annotation/XxlJob.java

@@ -15,7 +15,7 @@ public @interface XxlJob {
     /**
      * jobhandler name
      */
-    String value() default "";
+    String value();
 
     /**
      * init handler, invoked when JobThread init