Merge pull request #561 from analysys/branch-1.0.2

fix bug that the task instance of MR cannot stop in some cases. #516
diff --git a/README.md b/README.md
index 23fbd6e..ffd8dcf 100644
--- a/README.md
+++ b/README.md
@@ -43,18 +43,14 @@
 
 - [**升级文档**](https://analysys.github.io/easyscheduler_docs_cn/升级文档.html?_blank "升级文档") 
 
-- [**接口文档**](http://52.82.13.76:8888/easyscheduler/doc.html?language=zh_CN&lang=cn?_blank "接口文档") 
-
-- <a href="http://52.82.13.76:8888" target="_blank">我要体验</a>
-
-- [**FAQ**](https://analysys.github.io/easyscheduler_docs_cn/EasyScheduler-FAQ".html?_blank "EasyScheduler-FAQ") 
+- <a href="http://52.82.13.76:8888" target="_blank">我要体验</a> 
 
 更多文档请参考 <a href="https://analysys.github.io/easyscheduler_docs_cn/" target="_blank">easyscheduler中文在线文档</a>
 
 
 ### 近期研发计划
 
-EasyScheduler的工作计划:<a href="https://github.com/analysys/EasyScheduler/projects/1" target="_blank">研发计划</a> ,其中 In Develop卡片下是1.0.2版本的功能,TODO卡片是待做事项(包括 feature ideas)
+EasyScheduler的工作计划:<a href="https://github.com/analysys/EasyScheduler/projects/1" target="_blank">研发计划</a> ,其中 In Develop卡片下是1.1.0版本的功能,TODO卡片是待做事项(包括 feature ideas)
 
 ### 贡献代码
 
diff --git a/docs/zh_CN/EasyScheduler-FAQ.md b/docs/zh_CN/EasyScheduler-FAQ.md
index e9f9d5d..360565a 100644
--- a/docs/zh_CN/EasyScheduler-FAQ.md
+++ b/docs/zh_CN/EasyScheduler-FAQ.md
@@ -1,96 +1,287 @@
-Q:单机运行服务老挂,应该是内存不够,测试机器4核8G。生产环境需要分布式,如果单机的话建议的配置是?
+## Q:EasyScheduler服务介绍及建议运行内存
 
-A: Easy Scheduler有5个服务组成,这些服务本身需要的内存和cpu不多,
+A: EasyScheduler由5个服务组成,MasterServer、WorkerServer、ApiServer、AlertServer、LoggerServer和UI。
 
-| 服务         | 内存 | cpu核数 |
-| ------------ | ---- | ------- |
-| MasterServer | 2G   | 2核     |
-| WorkerServer | 2G   | 2核     |
-| ApiServer    | 512M | 1核     |
-| AlertServer  | 512M | 1核     |
-| LoggerServer | 512M | 1核     |
+| 服务                      | 说明                                                         |
+| ------------------------- | ------------------------------------------------------------ |
+| MasterServer              | 主要负责 **DAG** 的切分和任务状态的监控                      |
+| WorkerServer/LoggerServer | 主要负责任务的提交、执行和任务状态的更新。LoggerServer用于Rest Api通过 **RPC** 查看日志 |
+| ApiServer                 | 提供Rest Api服务,供UI进行调用                               |
+| AlertServer               | 提供告警服务                                                 |
+| UI                        | 前端页面展示                                                 |
 
-注意:由于如果任务较多,WorkServer所在机器建议物理内存在16G以上
-
-
+注意:**由于服务比较多,建议单机部署最好是4核16G以上**
 
 ---
 
-Q: 管理员为什么不能创建项目?
+## Q: 管理员为什么不能创建项目
 
-A: 管理员目前属于"纯管理", 没有租户,即没有linux上对应的用户,所以没有执行权限,  但是有所有的查看权限。如果需要创建项目等业务操作,请使用管理员创建租户和普通用户,然后使用普通用户登录进行操作
+A:管理员目前属于"**纯管理**", 没有租户,即没有linux上对应的用户,所以没有执行权限,  **故没有所属的项目、资源及数据源**,所以没有创建权限。**但是有所有的查看权限**。如果需要创建项目等业务操作,**请使用管理员创建租户和普通用户,然后使用普通用户登录进行操作**。我们将会在1.1.0版本中将管理员的创建和执行权限放开,管理员将会有所有的权限
 
 ---
 
-Q: 系统支持哪些邮箱?
+## Q:系统支持哪些邮箱?
 
-A: 支持绝大多数邮箱,qq、163、126、139、outlook、aliyun等皆可支持
+A:支持绝大多数邮箱,qq、163、126、139、outlook、aliyun等皆支持。支持**TLS和SSL**协议,可以在alert.properties中选择性配置
 
 ---
 
-Q:常用的系统变量时间参数有哪些,如何使用?
+## Q:常用的系统变量时间参数有哪些,如何使用?
 
-A: 请参考使用手册中的系统参数
+A:请参考 https://analysys.github.io/easyscheduler_docs_cn/%E7%B3%BB%E7%BB%9F%E4%BD%BF%E7%94%A8%E6%89%8B%E5%86%8C.html#%E7%B3%BB%E7%BB%9F%E5%8F%82%E6%95%B0
 
 ---
 
-Q:pip install kazoo 这个安装报错。是必须安装的吗?
+## Q:pip install kazoo 这个安装报错。是必须安装的吗?
 
-A: 这个是python连接zookeeper需要使用到的
+A: 这个是python连接zookeeper需要使用到的,必须要安装
 
 ---
 
-Q: 如果alert、api、logger服务任意一个宕机,任何还会正常执行吧
+## Q: 怎么指定机器运行任务
 
-A:   不影响,影响正在运行中的任务的服务有Master和Worker服务
+A:使用 **管理员** 创建Worker分组,在 **流程定义启动** 的时候可**指定Worker分组**或者在**任务节点上指定Worker分组**。如果不指定,则使用Default,**Default默认是使用的集群里所有的Worker中随机选取一台来进行任务提交、执行**
 
 ---
 
-Q: 这个怎么指定机器运行任务的啊 」
+## Q:任务的优先级
 
-A: 通过worker分组: 这个流程只能在指定的机器组里执行。默认是Default,可以在任一worker上执行。
-
----
-
-Q: 跨用户的任务依赖怎么实现呢, 比如A用户写了一个任务,B用户需要依赖这个任务
-
-就比如说 我们数仓组 写了一个 中间宽表的任务, 其他业务部门想要使用这个中间表的时候,他们应该是另外一个用户,怎么依赖这个中间表呢
-
-A: 有两种情况,一个是要运行这个宽表任务,可以使用子工作流把宽表任务放到自己的工作流里面。另一个是检查这个宽表任务有没有完成,可以使用依赖节点来检查这个宽表任务在指定的时间周期有没有完成。
-
----
-
-Q: 启动WorkerServer服务时不能正常启动,报以下信息是什么原因?
-
-```
-[INFO] 2019-05-06 16:39:31.492 cn.escheduler.server.zk.ZKWorkerClient:[155] - register failure , worker already started on : 127.0.0.1, please wait for a moment and try again
-```
-
-A:Worker/Master Server在启动时,会向Zookeeper注册自己的启动信息,是Zookeeper的临时节点,如果两次启动时间间隔较短的情况,上次启动的Worker/Master Server在Zookeeper的会话还未过期,会出现上述信息,处理办法是等待session过期,一般是1分钟左右
+A:我们同时 **支持流程和任务的优先级**。优先级我们有 **HIGHEST、HIGH、MEDIUM、LOW和LOWEST** 五种级别。**可以设置不同流程实例之间的优先级,也可以设置同一个流程实例中不同任务实例的优先级**。详细内容请参考任务优先级设计 https://analysys.github.io/easyscheduler_docs_cn/%E7%B3%BB%E7%BB%9F%E6%9E%B6%E6%9E%84%E8%AE%BE%E8%AE%A1.html#%E7%B3%BB%E7%BB%9F%E6%9E%B6%E6%9E%84%E8%AE%BE%E8%AE%A1
 
 ----
 
-Q: 编译时escheduler-grpc模块一直报错:Information:java: Errors occurred while compiling module 'escheduler-rpc', 找不到LogParameter、RetStrInfo、RetByteInfo等class类
+## Q:escheduler-grpc报错
 
-A: 这是因为rpc源码包是google Grpc实现的,需要使用maven进行编译,在根目录下执行:mvn -U clean package assembly:assembly -Dmaven.test.skip=true , 然后刷新下整个项目
+A:在根目录下执行:mvn -U clean package assembly:assembly -Dmaven.test.skip=true , 然后刷新下整个项目
 
 ----
 
-Q:EasyScheduler支持windows上运行么?
+## Q:EasyScheduler支持windows上运行么
 
-A:  建议在Ubuntu、Centos上运行,暂不支持windows上运行,不过windows上可以进行编译。开发调试的话建议Ubuntu或者mac上进行。
+A: 理论上只有**Worker是需要在Linux上运行的**,其它的服务都是可以在windows上正常运行的。但是还是建议最好能在linux上部署使用
 
 -----
 
-Q:任务为什么不执行?
+## Q:UI 在 linux 编译node-sass提示:Error:EACCESS:permission denied,mkdir xxxx
 
-A: 不执行的原因:
+A:单独安装 **npm install node-sass --unsafe-perm**,之后再 **npm install**
 
-查看command表里有没有内容?
+---
 
-查看Master server的运行日志:
+## Q:UI 不能正常登陆访问
 
-查看Worker Server的运行日志
+A: 1,如果是node启动的查看escheduler-ui下的.env API_BASE配置是否是Api Server服务地址
+
+    2,如果是nginx启动的并且是通过 **install-escheduler-ui.sh** 安装的,查看             											**/etc/nginx/conf.d/escheduler.conf** 中的proxy_pass配置是否是Api Server服务地址
+
+    3,如果以上配置都是正确的,那么请查看Api Server服务是否是正常的,curl http://192.168.xx.xx:12345/escheduler/users/get-user-info,查看Api Server日志,如果提示 cn.escheduler.api.interceptor.LoginHandlerInterceptor:[76] - session info is null,则证明Api Server服务是正常的
+
+    4,如果以上都没有问题,需要查看一下 **application.properties** 中的 **server.context-path 和 server.port 配置**是否正确
+
+---
+
+## Q: 流程定义手动启动或调度启动之后,没有流程实例生成
+
+A: 1,首先通过**jps 查看MasterServer服务是否存在**,或者从服务监控直接查看zk中是否存在master服务
+
+​	2,如果存在master服务,查看 **命令状态统计** 或者 **t_escheduler_error_command** 中是否增加的新记录,如果增加了,**请查看 message 字段定位启动异常原因**
+
+---
+
+## Q : 任务状态一直处于提交成功状态
+
+A: 1,首先通过**jps 查看WorkerServer服务是否存在**,或者从服务监控直接查看zk中是否存在worker服务
+
+​       2,如果 **WorkerServer** 服务正常,需要 **查看MasterServer是否把task任务放到zk队列中** ,**需要查看MasterServer日志及zk队列中是否有任务阻塞**
+
+​	3,如果以上都没有问题,需要定位是否指定了Worker分组,但是 **Worker分组的机器不是在线状态**
+
+---
+
+## Q: 是否提供Docker镜像及Dockerfile
+
+A: 提供Docker镜像及Dockerfile。
+
+Docker镜像地址:https://hub.docker.com/r/escheduler/escheduler_images
+
+Dockerfile地址:https://github.com/qiaozhanwei/escheduler_dockerfile/tree/master/docker_escheduler
+
+---
+
+## Q : install.sh 中需要注意问题
+
+A:  1,如果替换变量中包含特殊字符,**请用 \ 转移符进行转移**
+
+​	2,installPath="/data1_1T/escheduler",**这个目录不能和当前要一键安装的install.sh目录是一样的**
+
+​	3,deployUser="escheduler",**部署用户必须具有sudo权限**,因为worker是通过sudo -u 租户 sh xxx.command进行执行的
+
+​	4,monitorServerState="false",服务监控脚本是否启动,默认是不启动服务监控脚本的。**如果启动服务监控脚本,则每5分钟定时来监控master和worker的服务是否down机,如果down机则会自动重启**
+
+​	5,hdfsStartupSate="false",是否开启HDFS资源上传功能。默认是不开启的,**如果不开启则资源中心是不能使用的**。如果开启,需要conf/common/hadoop/hadoop.properties中配置fs.defaultFS和yarn的相关配置,如果使用namenode HA,需要将core-site.xml和hdfs-site.xml复制到conf根目录下
+
+​	注意:**1.0.x版本是不会自动创建hdfs根目录的,需要自行创建,并且需要部署用户有hdfs的操作权限**
+
+---
+
+## Q : 流程定义和流程实例下线异常
+
+A : 对于 **1.0.4 以前的版本中**,修改escheduler-api cn.escheduler.api.quartz包下的代码即可
+
+```
+public boolean deleteJob(String jobName, String jobGroupName) {
+    lock.writeLock().lock();
+    try {
+      JobKey jobKey = new JobKey(jobName,jobGroupName);
+      if(scheduler.checkExists(jobKey)){
+        logger.info("try to delete job, job name: {}, job group name: {},", jobName, jobGroupName);
+        return scheduler.deleteJob(jobKey);
+      }else {
+        return true;
+      }
+
+    } catch (SchedulerException e) {
+      logger.error(String.format("delete job : %s failed",jobName), e);
+    } finally {
+      lock.writeLock().unlock();
+    }
+    return false;
+  }
+```
+
+---
+
+## Q : HDFS启动之前创建的租户,能正常使用资源中心吗
+
+A: 不能。因为在未启动HDFS创建的租户,不会在HDFS中注册租户目录。所以上次资源会报错
+
+## Q :  多Master和多Worker状态下,服务掉了,怎么容错
+
+A:  **注意:Master监控Master及Worker服务。**
+
+​	1,如果Master服务掉了,其它的Master会接管挂掉的Master的流程,继续监控Worker task状态
+
+​	2,如果Worker服务掉,Master会监控到Worker服务掉了,如果存在Yarn任务,Kill Yarn任务之后走重试
+
+具体请看容错设计:https://analysys.github.io/easyscheduler_docs_cn/%E7%B3%BB%E7%BB%9F%E6%9E%B6%E6%9E%84%E8%AE%BE%E8%AE%A1.html#%E7%B3%BB%E7%BB%9F%E6%9E%B6%E6%9E%84%E8%AE%BE%E8%AE%A1
+
+---
+
+## Q : 对于Master和Worker一台机器伪分布式下的容错
+
+A : 1.0.3 版本只实现了Master启动流程容错,不走Worker容错。也就是说如果Worker挂掉的时候,没有Master存在。这流程将会出现问题。我们会在 **1.1.0** 版本中增加Master和Worker启动自容错,修复这个问题。如果想手动修改这个问题,需要针对 **跨重启正在运行流程** **并且已经掉的正在运行的Worker任务,需要修改为失败**,**同时跨重启正在运行流程设置为失败状态**。然后从失败节点进行流程恢复即可
+
+---
+
+## Q : 定时容易设置成每秒执行
+
+A : 设置定时的时候需要注意,如果第一位(* * * * * ? *)设置成 \* ,则表示每秒执行。**我们将会在1.1.0版本中加入显示最近调度的时间列表** ,使用http://cron.qqe2.com/ 可以在线看近5次运行时间
 
 
 
+## Q: 定时有有效时间范围吗
+
+A:有的,**如果定时的起止时间是同一个时间,那么此定时将是无效的定时**。**如果起止时间的结束时间比当前的时间小,很有可能定时会被自动删除**
+
+
+
+## Q : 任务依赖有几种实现
+
+A:	1,**DAG** 之间的任务依赖关系,是从 **入度为零** 进行DAG切分的
+
+​	2,有 **任务依赖节点** ,可以实现跨流程的任务或者流程依赖,具体请参考 依赖(DEPENDENT)节点:https://analysys.github.io/easyscheduler_docs_cn/%E7%B3%BB%E7%BB%9F%E4%BD%BF%E7%94%A8%E6%89%8B%E5%86%8C.html#%E4%BB%BB%E5%8A%A1%E8%8A%82%E7%82%B9%E7%B1%BB%E5%9E%8B%E5%92%8C%E5%8F%82%E6%95%B0%E8%AE%BE%E7%BD%AE
+
+​	注意:**不支持跨项目的流程或任务依赖**
+
+## Q: 流程定义有几种启动方式
+
+A: 1,在 **流程定义列表**,点击 **启动** 按钮
+
+​	2,**流程定义列表添加定时器**,调度启动流程定义
+
+​	3,流程定义 **查看或编辑** DAG 页面,任意 **任务节点右击** 启动流程定义
+
+​	4,可以对流程定义 DAG 编辑,设置某些任务的运行标志位 **禁止运行**,则在启动流程定义的时候,将该节点的连线将从DAG中去掉
+
+## Q : Python任务设置Python版本
+
+A:  1,对于1**.0.3之后的版本**只需要修改 conf/env/.escheduler_env.sh中的PYTHON_HOME
+
+```
+export PYTHON_HOME=/bin/python
+```
+
+注意:这了 **PYTHON_HOME** ,是python命令的绝对路径,而不是单纯的 PYTHON_HOME,还需要注意的是 export PATH 的时候,需要直接
+
+```
+export PATH=$HADOOP_HOME/bin:$SPARK_HOME1/bin:$SPARK_HOME2/bin:$PYTHON_HOME:$JAVA_HOME/bin:$HIVE_HOME/bin:$PATH
+```
+
+​	2,对 1.0.3 之前的版本,Python任务只能支持系统的Python版本,不支持指定Python版本
+
+## Q: Worker Task 通过sudo -u 租户 sh xxx.command会产生子进程,在kill的时候,是否会杀掉
+
+A: 我们会在1.0.4中增加kill任务同时,kill掉任务产生的各种所有子进程
+
+
+
+## Q : EasyScheduler中的队列怎么用,用户队列和租户队列是什么意思
+
+A : EasyScheduler 中的队列可以在用户或者租户上指定队列,**用户指定的队列优先级是高于租户队列的优先级的。**,例如:对MR任务指定队列,是通过 mapreduce.job.queuename 来指定队列的。
+
+注意:MR在用以上方法指定队列的时候,传递参数请使用如下方式:
+
+```
+	Configuration conf = new Configuration();
+        GenericOptionsParser optionParser = new GenericOptionsParser(conf, args);
+        String[] remainingArgs = optionParser.getRemainingArgs();
+```
+
+
+
+如果是Spark任务 --queue 方式指定队列
+
+
+
+## Q : Master 或者 Worker报如下告警
+
+<p align="center">
+   <img src="https://analysys.github.io/easyscheduler_docs_cn/images/master_worker_lack_res.png" width="60%" />
+ </p>
+
+
+
+A : 修改conf下的 master.properties **master.reserved.memory** 的值为更小的值,比如说0.1 或者
+
+worker.properties **worker.reserved.memory** 的值为更小的值,比如说0.1
+
+
+
+## Q : hive版本是1.1.0+cdh5.15.0,SQL hive任务连接报错
+
+<p align="center">
+   <img src="https://analysys.github.io/easyscheduler_docs_cn/images/cdh_hive_error.png" width="60%" />
+ </p>
+
+
+
+A : 将 hive pom
+
+```
+<dependency>
+    <groupId>org.apache.hive</groupId>
+    <artifactId>hive-jdbc</artifactId>
+    <version>2.1.0</version>
+</dependency>
+```
+
+修改为
+
+```
+<dependency>
+    <groupId>org.apache.hive</groupId>
+    <artifactId>hive-jdbc</artifactId>
+    <version>1.1.0</version>
+</dependency>
+```
+
diff --git a/docs/zh_CN/README.md b/docs/zh_CN/README.md
index 3295e1f..5337e58 100644
--- a/docs/zh_CN/README.md
+++ b/docs/zh_CN/README.md
@@ -43,9 +43,7 @@
 
 - [**升级文档**](https://analysys.github.io/easyscheduler_docs_cn/升级文档.html?_blank "升级文档") 
 
-- [**FAQ**](https://analysys.github.io/easyscheduler_docs_cn/EasyScheduler-FAQ.html?_blank "EasyScheduler-FAQ") 
-
-- <a href="http://52.82.13.76:8888" target="_blank">我要体验</a> 普通用户登录:demo/demo123
+- <a href="http://52.82.13.76:8888" target="_blank">我要体验</a> 
 
 更多文档请参考 <a href="https://analysys.github.io/easyscheduler_docs_cn/" target="_blank">easyscheduler中文在线文档</a>
 
@@ -57,8 +55,7 @@
 
 ### 帮助
 The fastest way to get response from our developers is to submit issues,   or add our wechat : 510570367
-
-
+ 
  
 
 
diff --git a/docs/zh_CN/images/cdh_hive_error.png b/docs/zh_CN/images/cdh_hive_error.png
new file mode 100644
index 0000000..ad1db4c
--- /dev/null
+++ b/docs/zh_CN/images/cdh_hive_error.png
Binary files differ
diff --git a/docs/zh_CN/images/master_worker_lack_res.png b/docs/zh_CN/images/master_worker_lack_res.png
new file mode 100644
index 0000000..1b26714
--- /dev/null
+++ b/docs/zh_CN/images/master_worker_lack_res.png
Binary files differ
diff --git "a/docs/zh_CN/\347\263\273\347\273\237\346\236\266\346\236\204\350\256\276\350\256\241.md" "b/docs/zh_CN/\347\263\273\347\273\237\346\236\266\346\236\204\350\256\276\350\256\241.md"
index a6e1645..1346841 100644
--- "a/docs/zh_CN/\347\263\273\347\273\237\346\236\266\346\236\204\350\256\276\350\256\241.md"
+++ "b/docs/zh_CN/\347\263\273\347\273\237\346\236\266\346\236\204\350\256\276\350\256\241.md"
@@ -13,13 +13,13 @@
 
 **流程定义**:通过拖拽任务节点并建立任务节点的关联所形成的可视化**DAG**
 
-**流程实例**:流程实例是流程定义的实例化,可以通过手动启动或定时调度生成
+**流程实例**:流程实例是流程定义的实例化,可以通过手动启动或定时调度生成,流程定义每运行一次,产生一个流程实例
 
 **任务实例**:任务实例是流程定义中任务节点的实例化,标识着具体的任务执行状态
 
-**任务类型**: 目前支持有SHELL、SQL、SUB_PROCESS、PROCEDURE、MR、SPARK、PYTHON、DEPENDENT,同时计划支持动态插件扩展,注意:其中子 **SUB_PROCESS**  也是一个单独的流程定义,是可以单独启动执行的
+**任务类型**: 目前支持有SHELL、SQL、SUB_PROCESS(子流程)、PROCEDURE、MR、SPARK、PYTHON、DEPENDENT(依赖),同时计划支持动态插件扩展,注意:其中子 **SUB_PROCESS**  也是一个单独的流程定义,是可以单独启动执行的
 
-**调度方式:** 系统支持基于cron表达式的定时调度和手动调度。命令类型支持:启动工作流、从当前节点开始执行、恢复被容错的工作流、恢复暂停流程、从失败节点开始执行、补数、调度、重跑、暂停、停止、恢复等待线程。其中 **恢复被容错的工作流** 和 **恢复等待线程** 两种命令类型是由调度内部控制使用,外部无法调用
+**调度方式:** 系统支持基于cron表达式的定时调度和手动调度。命令类型支持:启动工作流、从当前节点开始执行、恢复被容错的工作流、恢复暂停流程、从失败节点开始执行、补数、定时、重跑、暂停、停止、恢复等待线程。其中 **恢复被容错的工作流** 和 **恢复等待线程** 两种命令类型是由调度内部控制使用,外部无法调用
 
 **定时调度**:系统采用 **quartz** 分布式调度器,并同时支持cron表达式可视化的生成
 
diff --git a/escheduler-alert/pom.xml b/escheduler-alert/pom.xml
index 58c9df4..be8b5fb 100644
--- a/escheduler-alert/pom.xml
+++ b/escheduler-alert/pom.xml
@@ -4,7 +4,7 @@
     <parent>
         <groupId>cn.analysys</groupId>
         <artifactId>escheduler</artifactId>
-        <version>1.0.4-SNAPSHOT</version>
+        <version>1.1.0-SNAPSHOT</version>
     </parent>
     <artifactId>escheduler-alert</artifactId>
     <packaging>jar</packaging>
@@ -90,10 +90,6 @@
             <artifactId>commons-io</artifactId>
         </dependency>
 
-        <dependency>
-            <groupId>org.apache.commons</groupId>
-            <artifactId>commons-collections4</artifactId>
-        </dependency>
 
         <!--excel poi-->
         <dependency>
diff --git a/escheduler-alert/src/main/java/cn/escheduler/alert/manager/EnterpriseWeChatManager.java b/escheduler-alert/src/main/java/cn/escheduler/alert/manager/EnterpriseWeChatManager.java
new file mode 100644
index 0000000..cf16f3a
--- /dev/null
+++ b/escheduler-alert/src/main/java/cn/escheduler/alert/manager/EnterpriseWeChatManager.java
@@ -0,0 +1,57 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *    http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package cn.escheduler.alert.manager;
+
+import cn.escheduler.alert.utils.Constants;
+import cn.escheduler.alert.utils.EnterpriseWeChatUtils;
+import cn.escheduler.dao.model.Alert;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.io.IOException;
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * Enterprise WeChat Manager
+ */
+public class EnterpriseWeChatManager {
+    private static final Logger logger = LoggerFactory.getLogger(MsgManager.class);
+    /**
+     * Enterprise We Chat send
+     * @param alert
+     */
+    public Map<String,Object> send(Alert alert, String token){
+        Map<String,Object> retMap = new HashMap<>();
+        retMap.put(Constants.STATUS, false);
+        String agentId = EnterpriseWeChatUtils.enterpriseWeChatAgentId;
+        String users = EnterpriseWeChatUtils.enterpriseWeChatUsers;
+        List<String> userList = Arrays.asList(users.split(","));
+        logger.info("send message {}",alert);
+        String msg = EnterpriseWeChatUtils.makeUserSendMsg(userList, agentId,EnterpriseWeChatUtils.markdownByAlert(alert));
+        try {
+            EnterpriseWeChatUtils.sendEnterpriseWeChat(Constants.UTF_8, msg, token);
+        } catch (IOException e) {
+            logger.error(e.getMessage(),e);
+        }
+        retMap.put(Constants.STATUS, true);
+        return retMap;
+    }
+
+}
diff --git a/escheduler-alert/src/main/java/cn/escheduler/alert/runner/AlertSender.java b/escheduler-alert/src/main/java/cn/escheduler/alert/runner/AlertSender.java
index fbaf512..bc6566e 100644
--- a/escheduler-alert/src/main/java/cn/escheduler/alert/runner/AlertSender.java
+++ b/escheduler-alert/src/main/java/cn/escheduler/alert/runner/AlertSender.java
@@ -17,7 +17,9 @@
 package cn.escheduler.alert.runner;
 
 import cn.escheduler.alert.manager.EmailManager;
+import cn.escheduler.alert.manager.EnterpriseWeChatManager;
 import cn.escheduler.alert.utils.Constants;
+import cn.escheduler.alert.utils.EnterpriseWeChatUtils;
 import cn.escheduler.common.enums.AlertStatus;
 import cn.escheduler.common.enums.AlertType;
 import cn.escheduler.dao.AlertDao;
@@ -40,6 +42,7 @@
     private static final Logger logger = LoggerFactory.getLogger(AlertSender.class);
 
     private static final EmailManager emailManager= new EmailManager();
+    private static final EnterpriseWeChatManager weChatManager= new EnterpriseWeChatManager();
 
 
     private List<Alert> alertList;
@@ -109,6 +112,12 @@
             if (flag){
                 alertDao.updateAlert(AlertStatus.EXECUTION_SUCCESS, "execution success", alert.getId());
                 logger.info("alert send success");
+                try {
+                    String token = EnterpriseWeChatUtils.getToken();
+                    weChatManager.send(alert,token);
+                } catch (Exception e) {
+                    logger.error(e.getMessage(),e);
+                }
             }else {
                 alertDao.updateAlert(AlertStatus.EXECUTION_FAILURE,String.valueOf(retMaps.get(Constants.MESSAGE)),alert.getId());
                 logger.info("alert send error : {}" , String.valueOf(retMaps.get(Constants.MESSAGE)));
diff --git a/escheduler-alert/src/main/java/cn/escheduler/alert/utils/Constants.java b/escheduler-alert/src/main/java/cn/escheduler/alert/utils/Constants.java
index 1e1a767..d077dcf 100644
--- a/escheduler-alert/src/main/java/cn/escheduler/alert/utils/Constants.java
+++ b/escheduler-alert/src/main/java/cn/escheduler/alert/utils/Constants.java
@@ -128,4 +128,24 @@
     public static final String TH_END = "</th>";
 
     public static final int ALERT_SCAN_INTERVEL = 5000;
+
+    public static final String MARKDOWN_QUOTE = ">";
+
+    public static final String MARKDOWN_ENTER = "\n";
+
+    public static final String ENTERPRISE_WECHAT_CORP_ID = "enterprise.wechat.corp.id";
+
+    public static final String ENTERPRISE_WECHAT_SECRET = "enterprise.wechat.secret";
+
+    public static final String ENTERPRISE_WECHAT_TOKEN_URL = "enterprise.wechat.token.url";
+
+    public static final String ENTERPRISE_WECHAT_PUSH_URL = "enterprise.wechat.push.url";
+
+    public static final String ENTERPRISE_WECHAT_TEAM_SEND_MSG = "enterprise.wechat.team.send.msg";
+
+    public static final String ENTERPRISE_WECHAT_USER_SEND_MSG = "enterprise.wechat.user.send.msg";
+
+    public static final String ENTERPRISE_WECHAT_AGENT_ID = "enterprise.wechat.agent.id";
+
+    public static final String ENTERPRISE_WECHAT_USERS = "enterprise.wechat.users";
 }
diff --git a/escheduler-alert/src/main/java/cn/escheduler/alert/utils/EnterpriseWeChatUtils.java b/escheduler-alert/src/main/java/cn/escheduler/alert/utils/EnterpriseWeChatUtils.java
new file mode 100644
index 0000000..eab8149
--- /dev/null
+++ b/escheduler-alert/src/main/java/cn/escheduler/alert/utils/EnterpriseWeChatUtils.java
@@ -0,0 +1,248 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *    http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package cn.escheduler.alert.utils;
+
+import cn.escheduler.common.enums.ShowType;
+import cn.escheduler.dao.model.Alert;
+import com.alibaba.fastjson.JSON;
+
+import com.google.common.reflect.TypeToken;
+import org.apache.commons.lang3.StringUtils;
+import org.apache.http.HttpEntity;
+import org.apache.http.client.methods.CloseableHttpResponse;
+import org.apache.http.client.methods.HttpGet;
+import org.apache.http.client.methods.HttpPost;
+import org.apache.http.entity.StringEntity;
+import org.apache.http.impl.client.CloseableHttpClient;
+import org.apache.http.impl.client.HttpClients;
+import org.apache.http.util.EntityUtils;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.io.IOException;
+import java.util.*;
+
+import static cn.escheduler.alert.utils.PropertyUtils.getString;
+
+/**
+ * Enterprise WeChat utils
+ */
+public class EnterpriseWeChatUtils {
+
+    public static final Logger logger = LoggerFactory.getLogger(EnterpriseWeChatUtils.class);
+
+    private static final String enterpriseWeChatCorpId = getString(Constants.ENTERPRISE_WECHAT_CORP_ID);
+
+    private static final String enterpriseWeChatSecret = getString(Constants.ENTERPRISE_WECHAT_SECRET);
+
+    private static final String enterpriseWeChatTokenUrl = getString(Constants.ENTERPRISE_WECHAT_TOKEN_URL);
+    private static String enterpriseWeChatTokenUrlReplace = enterpriseWeChatTokenUrl
+            .replaceAll("\\$corpId", enterpriseWeChatCorpId)
+            .replaceAll("\\$secret", enterpriseWeChatSecret);
+
+    private static final String enterpriseWeChatPushUrl = getString(Constants.ENTERPRISE_WECHAT_PUSH_URL);
+
+    private static final String enterpriseWeChatTeamSendMsg = getString(Constants.ENTERPRISE_WECHAT_TEAM_SEND_MSG);
+
+    private static final String enterpriseWeChatUserSendMsg = getString(Constants.ENTERPRISE_WECHAT_USER_SEND_MSG);
+
+    public static final String enterpriseWeChatAgentId = getString(Constants.ENTERPRISE_WECHAT_AGENT_ID);
+
+    public static final String enterpriseWeChatUsers = getString(Constants.ENTERPRISE_WECHAT_USERS);
+
+    /**
+     * get Enterprise WeChat token info
+     * @return token string info
+     * @throws IOException
+     */
+    public static String getToken() throws IOException {
+        String resp;
+
+        CloseableHttpClient httpClient = HttpClients.createDefault();
+        HttpGet httpGet = new HttpGet(enterpriseWeChatTokenUrlReplace);
+        CloseableHttpResponse response = httpClient.execute(httpGet);
+        try {
+            HttpEntity entity = response.getEntity();
+            resp = EntityUtils.toString(entity, Constants.UTF_8);
+            EntityUtils.consume(entity);
+        } finally {
+            response.close();
+        }
+
+        Map<String, Object> map = JSON.parseObject(resp,
+                new TypeToken<Map<String, Object>>() {
+                }.getType());
+        return map.get("access_token").toString();
+    }
+
+    /**
+     * make team single Enterprise WeChat message
+     * @param toParty
+     * @param agentId
+     * @param msg
+     * @return Enterprise WeChat send message
+     */
+    public static String makeTeamSendMsg(String toParty, String agentId, String msg) {
+        return enterpriseWeChatTeamSendMsg.replaceAll("\\$toParty", toParty)
+                .replaceAll("\\$agentId", agentId)
+                .replaceAll("\\$msg", msg);
+    }
+
+    /**
+     * make team multi Enterprise WeChat message
+     * @param toParty
+     * @param agentId
+     * @param msg
+     * @return Enterprise WeChat send message
+     */
+    public static String makeTeamSendMsg(Collection<String> toParty, String agentId, String msg) {
+        String listParty = FuncUtils.mkString(toParty, "|");
+        return enterpriseWeChatTeamSendMsg.replaceAll("\\$toParty", listParty)
+                .replaceAll("\\$agentId", agentId)
+                .replaceAll("\\$msg", msg);
+    }
+
+    /**
+     * make team single user message
+     * @param toUser
+     * @param agentId
+     * @param msg
+     * @return Enterprise WeChat send message
+     */
+    public static String makeUserSendMsg(String toUser, String agentId, String msg) {
+        return enterpriseWeChatUserSendMsg.replaceAll("\\$toUser", toUser)
+                .replaceAll("\\$agentId", agentId)
+                .replaceAll("\\$msg", msg);
+    }
+
+    /**
+     * make team multi user message
+     * @param toUser
+     * @param agentId
+     * @param msg
+     * @return Enterprise WeChat send message
+     */
+    public static String makeUserSendMsg(Collection<String> toUser, String agentId, String msg) {
+        String listUser = FuncUtils.mkString(toUser, "|");
+        return enterpriseWeChatUserSendMsg.replaceAll("\\$toUser", listUser)
+                .replaceAll("\\$agentId", agentId)
+                .replaceAll("\\$msg", msg);
+    }
+
+    /**
+     * send Enterprise WeChat
+     * @param charset
+     * @param data
+     * @param token
+     * @return Enterprise WeChat resp, demo: {"errcode":0,"errmsg":"ok","invaliduser":""}
+     * @throws IOException
+     */
+    public static String sendEnterpriseWeChat(String charset, String data, String token) throws IOException {
+        String enterpriseWeChatPushUrlReplace = enterpriseWeChatPushUrl.replaceAll("\\$token", token);
+
+        CloseableHttpClient httpclient = HttpClients.createDefault();
+        HttpPost httpPost = new HttpPost(enterpriseWeChatPushUrlReplace);
+        httpPost.setEntity(new StringEntity(data, charset));
+        CloseableHttpResponse response = httpclient.execute(httpPost);
+        String resp;
+        try {
+            HttpEntity entity = response.getEntity();
+            resp = EntityUtils.toString(entity, charset);
+            EntityUtils.consume(entity);
+        } finally {
+            response.close();
+        }
+        logger.info("Enterprise WeChat send [{}], param:{}, resp:{}", enterpriseWeChatPushUrl, data, resp);
+        return resp;
+    }
+
+    /**
+     * convert table to markdown style
+     * @param title
+     * @param content
+     * @return
+     */
+    public static String markdownTable(String title,String content){
+        List<LinkedHashMap> mapItemsList = JSONUtils.toList(content, LinkedHashMap.class);
+        StringBuilder contents = new StringBuilder(200);
+        for (LinkedHashMap mapItems : mapItemsList){
+
+            Set<Map.Entry<String, String>> entries = mapItems.entrySet();
+
+            Iterator<Map.Entry<String, String>> iterator = entries.iterator();
+
+            StringBuilder t = new StringBuilder(String.format("`%s`%s",title,Constants.MARKDOWN_ENTER));
+            while (iterator.hasNext()){
+
+                Map.Entry<String, String> entry = iterator.next();
+                t.append(Constants.MARKDOWN_QUOTE);
+                t.append(entry.getKey()).append(":").append(entry.getValue());
+                t.append(Constants.MARKDOWN_ENTER);
+            }
+
+            contents.append(t);
+        }
+        return contents.toString();
+    }
+
+    /**
+     * convert text to markdown style
+     * @param title
+     * @param content
+     * @return
+     */
+    public static String markdownText(String title,String content){
+        if (StringUtils.isNotEmpty(content)){
+            List<String> list;
+            try {
+                list = JSONUtils.toList(content,String.class);
+            }catch (Exception e){
+                logger.error("json format exception",e);
+                return null;
+            }
+
+            StringBuilder contents = new StringBuilder(100);
+            contents.append(String.format("`%s`\n",title));
+            for (String str : list){
+                contents.append(Constants.MARKDOWN_QUOTE);
+                contents.append(str);
+                contents.append(Constants.MARKDOWN_ENTER);
+            }
+
+            return contents.toString();
+
+        }
+        return null;
+    }
+
+    /**
+     * Determine the mardown style based on the show type of the alert
+     * @param alert
+     * @return
+     */
+    public static String markdownByAlert(Alert alert){
+        String result = "";
+        if (alert.getShowType() == ShowType.TABLE) {
+            result = markdownTable(alert.getTitle(),alert.getContent());
+        }else if(alert.getShowType() == ShowType.TEXT){
+            result = markdownText(alert.getTitle(),alert.getContent());
+        }
+        return result;
+
+    }
+
+}
diff --git a/escheduler-alert/src/main/java/cn/escheduler/alert/utils/FuncUtils.java b/escheduler-alert/src/main/java/cn/escheduler/alert/utils/FuncUtils.java
new file mode 100644
index 0000000..c9dbe1d
--- /dev/null
+++ b/escheduler-alert/src/main/java/cn/escheduler/alert/utils/FuncUtils.java
@@ -0,0 +1,35 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *    http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package cn.escheduler.alert.utils;
+
+public class FuncUtils {
+
+    static public String mkString(Iterable<String> list, String split) {
+        StringBuilder sb = new StringBuilder();
+        boolean first = true;
+        for (String item : list) {
+            if (first) {
+                first = false;
+            } else {
+                sb.append(split);
+            }
+            sb.append(item);
+        }
+        return sb.toString();
+    }
+
+}
diff --git a/escheduler-alert/src/main/java/cn/escheduler/alert/utils/MailUtils.java b/escheduler-alert/src/main/java/cn/escheduler/alert/utils/MailUtils.java
index bd918fc..50d161f 100644
--- a/escheduler-alert/src/main/java/cn/escheduler/alert/utils/MailUtils.java
+++ b/escheduler-alert/src/main/java/cn/escheduler/alert/utils/MailUtils.java
@@ -36,6 +36,7 @@
 import java.security.Security;
 import java.util.*;
 
+import static cn.escheduler.alert.utils.PropertyUtils.getBoolean;
 import static cn.escheduler.alert.utils.PropertyUtils.getInt;
 import static cn.escheduler.alert.utils.PropertyUtils.getString;
 
@@ -57,6 +58,10 @@
 
     public static final String mailPasswd = getString(Constants.MAIL_PASSWD);
 
+    public static final Boolean mailUseStartTLS = getBoolean(Constants.MAIL_SMTP_STARTTLS_ENABLE);
+
+    public static final Boolean mailUseSSL = getBoolean(Constants.MAIL_SMTP_SSL_ENABLE);
+
     public static final String xlsFilePath = getString(Constants.XLS_FILE_PATH);
 
     public static final String starttlsEnable = getString(Constants.MAIL_SMTP_STARTTLS_ENABLE);
diff --git a/escheduler-alert/src/main/resources/alert.properties b/escheduler-alert/src/main/resources/alert.properties
index 602384a..87ccae6 100644
--- a/escheduler-alert/src/main/resources/alert.properties
+++ b/escheduler-alert/src/main/resources/alert.properties
@@ -16,6 +16,15 @@
 #xls file path,need create if not exist
 xls.file.path=/tmp/xls
 
+# Enterprise WeChat configuration
+enterprise.wechat.corp.id=xxxxxxx
+enterprise.wechat.secret=xxxxxxx
+enterprise.wechat.agent.id=xxxxxxx
+enterprise.wechat.users=xxxxxxx
+enterprise.wechat.token.url=https://qyapi.weixin.qq.com/cgi-bin/gettoken?corpid=$corpId&corpsecret=$secret
+enterprise.wechat.push.url=https://qyapi.weixin.qq.com/cgi-bin/message/send?access_token=$token
+enterprise.wechat.team.send.msg={\"toparty\":\"$toParty\",\"agentid\":\"$agentId\",\"msgtype\":\"text\",\"text\":{\"content\":\"$msg\"},\"safe\":\"0\"}
+enterprise.wechat.user.send.msg={\"touser\":\"$toUser\",\"agentid\":\"$agentId\",\"msgtype\":\"markdown\",\"markdown\":{\"content\":\"$msg\"}}
 
 
 
diff --git a/escheduler-alert/src/test/java/cn/escheduler/alert/utils/EnterpriseWeChatUtilsTest.java b/escheduler-alert/src/test/java/cn/escheduler/alert/utils/EnterpriseWeChatUtilsTest.java
new file mode 100644
index 0000000..646d34e
--- /dev/null
+++ b/escheduler-alert/src/test/java/cn/escheduler/alert/utils/EnterpriseWeChatUtilsTest.java
@@ -0,0 +1,117 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *    http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package cn.escheduler.alert.utils;
+
+import com.alibaba.fastjson.JSON;
+import org.junit.Assert;
+import org.junit.Test;
+
+import java.io.IOException;
+import java.util.Arrays;
+import java.util.Collection;
+
+/**
+ * Please manually modify the configuration file before testing.
+ * file: alert.properties
+ *   enterprise.wechat.corp.id
+ *   enterprise.wechat.secret
+ *   enterprise.wechat.token.url
+ *   enterprise.wechat.push.url
+ *   enterprise.wechat.send.msg
+ *   enterprise.wechat.agent.id
+ *   enterprise.wechat.users
+ */
+public class EnterpriseWeChatUtilsTest {
+
+    private String agentId = PropertyUtils.getString(Constants.ENTERPRISE_WECHAT_AGENT_ID); // app id
+    private Collection<String> listUserId = Arrays.asList(PropertyUtils.getString(Constants.ENTERPRISE_WECHAT_USERS).split(","));
+
+    // Please change
+    private String partyId = "2";
+    private Collection<String> listPartyId = Arrays.asList("2","4");
+    @Test
+    public void testSendSingleTeamWeChat() {
+        try {
+            String token = EnterpriseWeChatUtils.getToken();
+            String msg = EnterpriseWeChatUtils.makeTeamSendMsg(partyId, agentId, "hello world");
+            String resp = EnterpriseWeChatUtils.sendEnterpriseWeChat("utf-8", msg, token);
+
+            String errmsg = JSON.parseObject(resp).getString("errmsg");
+            Assert.assertEquals(errmsg, "ok");
+        } catch (IOException e) {
+            e.printStackTrace();
+        }
+    }
+
+    @Test
+    public void testSendMultiTeamWeChat() {
+
+        try {
+            String token = EnterpriseWeChatUtils.getToken();
+            String msg = EnterpriseWeChatUtils.makeTeamSendMsg(listPartyId, agentId, "hello world");
+            String resp = EnterpriseWeChatUtils.sendEnterpriseWeChat("utf-8", msg, token);
+
+            String errmsg = JSON.parseObject(resp).getString("errmsg");
+            Assert.assertEquals(errmsg, "ok");
+        } catch (IOException e) {
+            e.printStackTrace();
+        }
+    }
+
+    @Test
+    public void testSendSingleUserWeChat() {
+        try {
+            String token = EnterpriseWeChatUtils.getToken();
+            String msg = EnterpriseWeChatUtils.makeUserSendMsg(listUserId.stream().findFirst().get(), agentId, "您的会议室已经预定,稍后会同步到`邮箱` \n" +
+                    ">**事项详情** \n" +
+                    ">事 项:<font color='info'>开会</font> <br>" +
+                    ">组织者:@miglioguan \n" +
+                    ">参与者:@miglioguan、@kunliu、@jamdeezhou、@kanexiong、@kisonwang \n" +
+                    "> \n" +
+                    ">会议室:<font color='info'>广州TIT 1楼 301</font> \n" +
+                    ">日 期:<font color='warning'>2018年5月18日</font> \n" +
+                    ">时 间:<font color='comment'>上午9:00-11:00</font> \n" +
+                    "> \n" +
+                    ">请准时参加会议。 \n" +
+                    "> \n" +
+                    ">如需修改会议信息,请点击:[修改会议信息](https://work.weixin.qq.com)\"");
+
+            String resp = EnterpriseWeChatUtils.sendEnterpriseWeChat("utf-8", msg, token);
+
+            String errmsg = JSON.parseObject(resp).getString("errmsg");
+            Assert.assertEquals(errmsg, "ok");
+        } catch (IOException e) {
+            e.printStackTrace();
+        }
+    }
+
+    @Test
+    public void testSendMultiUserWeChat() {
+        try {
+            String token = EnterpriseWeChatUtils.getToken();
+
+            String msg = EnterpriseWeChatUtils.makeUserSendMsg(listUserId, agentId, "hello world");
+            String resp = EnterpriseWeChatUtils.sendEnterpriseWeChat("utf-8", msg, token);
+
+            String errmsg = JSON.parseObject(resp).getString("errmsg");
+            Assert.assertEquals(errmsg, "ok");
+        } catch (IOException e) {
+            e.printStackTrace();
+        }
+    }
+
+}
diff --git a/escheduler-api/pom.xml b/escheduler-api/pom.xml
index 1d0431e..b0d92d5 100644
--- a/escheduler-api/pom.xml
+++ b/escheduler-api/pom.xml
@@ -3,20 +3,17 @@
   <parent>
     <groupId>cn.analysys</groupId>
     <artifactId>escheduler</artifactId>
-    <version>1.0.4-SNAPSHOT</version>
+    <version>1.1.0-SNAPSHOT</version>
   </parent>
   <artifactId>escheduler-api</artifactId>
   <packaging>jar</packaging>
 
   <dependencies>
 
+
     <dependency>
       <groupId>cn.analysys</groupId>
-      <artifactId>escheduler-dao</artifactId>
-    </dependency>
-    <dependency>
-      <groupId>cn.analysys</groupId>
-      <artifactId>escheduler-common</artifactId>
+      <artifactId>escheduler-server</artifactId>
       <exclusions>
         <exclusion>
           <groupId>io.netty</groupId>
@@ -44,14 +41,6 @@
     <!--springboot-->
     <dependency>
       <groupId>org.springframework.boot</groupId>
-      <artifactId>spring-boot-starter-parent</artifactId>
-      <version>${spring.boot.version}</version>
-      <type>pom</type>
-      <scope>import</scope>
-    </dependency>
-
-    <dependency>
-      <groupId>org.springframework.boot</groupId>
       <artifactId>spring-boot-starter-web</artifactId>
       <exclusions>
         <exclusion>
@@ -59,8 +48,8 @@
           <artifactId>spring-boot-starter-tomcat</artifactId>
         </exclusion>
         <exclusion>
-          <groupId>org.springframework.boot</groupId>
-          <artifactId>spring-boot-starter</artifactId>
+          <artifactId>log4j-to-slf4j</artifactId>
+          <groupId>org.apache.logging.log4j</groupId>
         </exclusion>
       </exclusions>
     </dependency>
@@ -158,6 +147,24 @@
     </dependency>
 
     <dependency>
+      <groupId>io.springfox</groupId>
+      <artifactId>springfox-swagger2</artifactId>
+      <version>2.9.2</version>
+    </dependency>
+
+    <dependency>
+      <groupId>io.springfox</groupId>
+      <artifactId>springfox-swagger-ui</artifactId>
+      <version>2.9.2</version>
+    </dependency>
+
+    <dependency>
+      <groupId>com.github.xiaoymin</groupId>
+      <artifactId>swagger-bootstrap-ui</artifactId>
+      <version>1.9.3</version>
+    </dependency>
+
+    <dependency>
       <groupId>cn.analysys</groupId>
       <artifactId>escheduler-rpc</artifactId>
     </dependency>
@@ -168,7 +175,6 @@
       <version>4.12</version>
       <scope>test</scope>
     </dependency>
-
   </dependencies>
   <build>
     <plugins>
@@ -202,4 +208,4 @@
       </plugin>
     </plugins>
   </build>
-</project>
+</project>
\ No newline at end of file
diff --git a/escheduler-api/src/main/java/cn/escheduler/api/ApiApplicationServer.java b/escheduler-api/src/main/java/cn/escheduler/api/ApiApplicationServer.java
index 1c66e2d..9b84c7f 100644
--- a/escheduler-api/src/main/java/cn/escheduler/api/ApiApplicationServer.java
+++ b/escheduler-api/src/main/java/cn/escheduler/api/ApiApplicationServer.java
@@ -19,13 +19,19 @@
 import org.springframework.boot.SpringApplication;
 import org.springframework.boot.autoconfigure.SpringBootApplication;
 import org.springframework.boot.web.servlet.ServletComponentScan;
+import org.springframework.boot.web.servlet.support.SpringBootServletInitializer;
 import org.springframework.context.annotation.ComponentScan;
+import springfox.documentation.swagger2.annotations.EnableSwagger2;
 
 @SpringBootApplication
 @ServletComponentScan
 @ComponentScan("cn.escheduler")
-public class ApiApplicationServer {
+@EnableSwagger2
+public class ApiApplicationServer extends SpringBootServletInitializer {
+
   public static void main(String[] args) {
     SpringApplication.run(ApiApplicationServer.class, args);
   }
+
+
 }
diff --git a/escheduler-api/src/main/java/cn/escheduler/api/configuration/AppConfiguration.java b/escheduler-api/src/main/java/cn/escheduler/api/configuration/AppConfiguration.java
index 491da08..b9b69c0 100644
--- a/escheduler-api/src/main/java/cn/escheduler/api/configuration/AppConfiguration.java
+++ b/escheduler-api/src/main/java/cn/escheduler/api/configuration/AppConfiguration.java
@@ -19,31 +19,73 @@
 import cn.escheduler.api.interceptor.LoginHandlerInterceptor;
 import org.springframework.context.annotation.Bean;
 import org.springframework.context.annotation.Configuration;
-import org.springframework.web.servlet.config.annotation.ContentNegotiationConfigurer;
-import org.springframework.web.servlet.config.annotation.CorsRegistry;
-import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
-import org.springframework.web.servlet.config.annotation.WebMvcConfigurerAdapter;
+import org.springframework.web.servlet.LocaleResolver;
+import org.springframework.web.servlet.config.annotation.*;
+import org.springframework.web.servlet.i18n.CookieLocaleResolver;
+import org.springframework.web.servlet.i18n.LocaleChangeInterceptor;
+
+import java.util.Locale;
+
 
 /**
  * application configuration
  */
 @Configuration
-public class AppConfiguration extends WebMvcConfigurerAdapter {
+public class AppConfiguration implements WebMvcConfigurer {
 
   public static final String LOGIN_INTERCEPTOR_PATH_PATTERN = "/**/*";
   public static final String LOGIN_PATH_PATTERN = "/login";
   public static final String PATH_PATTERN = "/**";
+  public static final String LOCALE_LANGUAGE_COOKIE = "language";
+  public static final int COOKIE_MAX_AGE = 3600;
 
-  @Override
-  public void addInterceptors(InterceptorRegistry registry) {
-    registry.addInterceptor(loginInterceptor()).addPathPatterns(LOGIN_INTERCEPTOR_PATH_PATTERN).excludePathPatterns(LOGIN_PATH_PATTERN);
-  }
 
   @Bean
   public LoginHandlerInterceptor loginInterceptor() {
     return new LoginHandlerInterceptor();
   }
 
+
+  /**
+   * Cookie
+   */
+  @Bean(name = "localeResolver")
+  public LocaleResolver localeResolver() {
+    CookieLocaleResolver localeResolver = new CookieLocaleResolver();
+    localeResolver.setCookieName(LOCALE_LANGUAGE_COOKIE);
+    /** set default locale **/
+    localeResolver.setDefaultLocale(Locale.US);
+    /** set cookie max age **/
+    localeResolver.setCookieMaxAge(COOKIE_MAX_AGE);
+    return localeResolver;
+  }
+
+  @Bean
+  public LocaleChangeInterceptor localeChangeInterceptor() {
+    LocaleChangeInterceptor lci = new LocaleChangeInterceptor();
+    /**  **/
+    lci.setParamName("language");
+
+    return lci;
+  }
+
+
+  @Override
+  public void addInterceptors(InterceptorRegistry registry) {
+    //i18n
+    registry.addInterceptor(localeChangeInterceptor());
+
+    registry.addInterceptor(loginInterceptor()).addPathPatterns(LOGIN_INTERCEPTOR_PATH_PATTERN).excludePathPatterns(LOGIN_PATH_PATTERN,"/swagger-resources/**", "/webjars/**", "/v2/**", "/doc.html", "*.html");
+  }
+
+
+  @Override
+  public void addResourceHandlers(ResourceHandlerRegistry registry) {
+    registry.addResourceHandler("/static/**").addResourceLocations("classpath:/static/");
+    registry.addResourceHandler("doc.html").addResourceLocations("classpath:/META-INF/resources/");
+    registry.addResourceHandler("/webjars/**").addResourceLocations("classpath:/META-INF/resources/webjars/");
+  }
+
   @Override
   public void addCorsMappings(CorsRegistry registry) {
     registry.addMapping(PATH_PATTERN).allowedOrigins("*").allowedMethods("*");
@@ -59,4 +101,8 @@
   public void configureContentNegotiation(final ContentNegotiationConfigurer configurer) {
     configurer.favorPathExtension(false);
   }
+
+
+
+
 }
diff --git a/escheduler-api/src/main/java/cn/escheduler/api/configuration/ServiceModelToSwagger2MapperImpl.java b/escheduler-api/src/main/java/cn/escheduler/api/configuration/ServiceModelToSwagger2MapperImpl.java
new file mode 100644
index 0000000..e9c001c
--- /dev/null
+++ b/escheduler-api/src/main/java/cn/escheduler/api/configuration/ServiceModelToSwagger2MapperImpl.java
@@ -0,0 +1,509 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *    http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package cn.escheduler.api.configuration;
+
+import com.google.common.collect.ArrayListMultimap;
+import com.google.common.collect.Multimap;
+import io.swagger.models.*;
+import io.swagger.models.parameters.Parameter;
+import org.apache.commons.lang3.StringUtils;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.context.MessageSource;
+import org.springframework.context.annotation.Primary;
+import org.springframework.context.i18n.LocaleContextHolder;
+import org.springframework.stereotype.Component;
+import springfox.documentation.service.ApiInfo;
+import springfox.documentation.service.ApiListing;
+import springfox.documentation.service.Documentation;
+import springfox.documentation.service.ResourceListing;
+import springfox.documentation.swagger2.mappers.*;
+
+import java.util.*;
+
+import static com.google.common.collect.Maps.newTreeMap;
+
+/**
+ * application configuration
+ */
+@Component(value = "ServiceModelToSwagger2Mapper")
+@Primary
+public class ServiceModelToSwagger2MapperImpl extends ServiceModelToSwagger2Mapper {
+
+
+    @Autowired
+    private ModelMapper modelMapper;
+    @Autowired
+    private ParameterMapper parameterMapper;
+    @Autowired
+    private SecurityMapper securityMapper;
+    @Autowired
+    private LicenseMapper licenseMapper;
+    @Autowired
+    private VendorExtensionsMapper vendorExtensionsMapper;
+
+    @Autowired
+    private MessageSource messageSource;
+
+    @Override
+    public Swagger mapDocumentation(Documentation from) {
+
+        if (from == null) {
+            return null;
+        }
+
+        Swagger swagger = new Swagger();
+
+        swagger.setVendorExtensions(vendorExtensionsMapper.mapExtensions(from.getVendorExtensions()));
+        swagger.setSchemes(mapSchemes(from.getSchemes()));
+        swagger.setPaths(mapApiListings(from.getApiListings()));
+        swagger.setHost(from.getHost());
+        swagger.setDefinitions(modelsFromApiListings( from.getApiListings() ) );
+        swagger.setSecurityDefinitions(securityMapper.toSecuritySchemeDefinitions(from.getResourceListing()));
+        ApiInfo info = fromResourceListingInfo(from);
+        if (info != null) {
+            swagger.setInfo(mapApiInfo(info));
+        }
+        swagger.setBasePath(from.getBasePath());
+        swagger.setTags(tagSetToTagList(from.getTags()));
+        List<String> list2 = from.getConsumes();
+        if (list2 != null) {
+            swagger.setConsumes(new ArrayList<String>(list2));
+        } else {
+            swagger.setConsumes(null);
+        }
+        List<String> list3 = from.getProduces();
+        if (list3 != null) {
+            swagger.setProduces(new ArrayList<String>(list3));
+        } else {
+            swagger.setProduces(null);
+        }
+
+        return swagger;
+    }
+
+
+    @Override
+    protected Info mapApiInfo(ApiInfo from) {
+
+        if (from == null) {
+            return null;
+        }
+
+        Info info = new Info();
+
+        info.setLicense(licenseMapper.apiInfoToLicense(from));
+        info.setVendorExtensions(vendorExtensionsMapper.mapExtensions(from.getVendorExtensions()));
+        info.setTermsOfService(from.getTermsOfServiceUrl());
+        info.setContact(map(from.getContact()));
+        info.setDescription(from.getDescription());
+        info.setVersion(from.getVersion());
+        info.setTitle(from.getTitle());
+
+        return info;
+    }
+
+    @Override
+    protected Contact map(springfox.documentation.service.Contact from) {
+
+        if (from == null) {
+            return null;
+        }
+
+        Contact contact = new Contact();
+
+        contact.setName(from.getName());
+        contact.setUrl(from.getUrl());
+        contact.setEmail(from.getEmail());
+
+        return contact;
+    }
+
+    @Override
+    protected io.swagger.models.Operation mapOperation(springfox.documentation.service.Operation from) {
+
+        if (from == null) {
+            return null;
+        }
+
+        Locale locale = LocaleContextHolder.getLocale();
+
+        io.swagger.models.Operation operation = new io.swagger.models.Operation();
+
+        operation.setSecurity(mapAuthorizations(from.getSecurityReferences()));
+        operation.setVendorExtensions(vendorExtensionsMapper.mapExtensions(from.getVendorExtensions()));
+        operation.setDescription(messageSource.getMessage(from.getNotes(), null, from.getNotes(), locale));
+        operation.setOperationId(from.getUniqueId());
+        operation.setResponses(mapResponseMessages(from.getResponseMessages()));
+        operation.setSchemes(stringSetToSchemeList(from.getProtocol()));
+        Set<String> tagsSet = new HashSet<>(1);
+
+        if(from.getTags() != null && from.getTags().size() > 0){
+
+            List<String> list = new ArrayList<String>(tagsSet.size());
+
+            Iterator<String> it = from.getTags().iterator();
+            while(it.hasNext())
+            {
+               String tag = it.next();
+               list.add(StringUtils.isNotBlank(tag) ? messageSource.getMessage(tag, null, tag, locale) : " ");
+            }
+
+            operation.setTags(list);
+        }else {
+            operation.setTags(null);
+        }
+
+        operation.setSummary(from.getSummary());
+        Set<String> set1 = from.getConsumes();
+        if (set1 != null) {
+            operation.setConsumes(new ArrayList<String>(set1));
+        } else {
+            operation.setConsumes(null);
+        }
+
+        Set<String> set2 = from.getProduces();
+        if (set2 != null) {
+            operation.setProduces(new ArrayList<String>(set2));
+        } else {
+            operation.setProduces(null);
+        }
+
+
+        operation.setParameters(parameterListToParameterList(from.getParameters()));
+        if (from.getDeprecated() != null) {
+            operation.setDeprecated(Boolean.parseBoolean(from.getDeprecated()));
+        }
+
+        return operation;
+    }
+
+    @Override
+    protected Tag mapTag(springfox.documentation.service.Tag from) {
+
+        if (from == null) {
+            return null;
+        }
+
+        Locale locale = LocaleContextHolder.getLocale();
+
+        Tag tag = new Tag();
+
+        tag.setVendorExtensions(vendorExtensionsMapper.mapExtensions(from.getVendorExtensions()));
+        tag.setName(messageSource.getMessage(from.getName(), null, from.getName(), locale));
+        tag.setDescription(from.getDescription());
+
+        return tag;
+    }
+
+
+    private ApiInfo fromResourceListingInfo(Documentation documentation) {
+
+        if (documentation == null) {
+            return null;
+        }
+        ResourceListing resourceListing = documentation.getResourceListing();
+        if (resourceListing == null) {
+            return null;
+        }
+        ApiInfo info = resourceListing.getInfo();
+        if (info == null) {
+            return null;
+        }
+        return info;
+    }
+
+    protected List<Tag> tagSetToTagList(Set<springfox.documentation.service.Tag> set) {
+
+        if (set == null) {
+            return null;
+        }
+
+        List<Tag> list = new ArrayList<Tag>(set.size());
+        for (springfox.documentation.service.Tag tag : set) {
+            list.add(mapTag(tag));
+        }
+
+        return list;
+    }
+
+    protected List<Scheme> stringSetToSchemeList(Set<String> set) {
+        if (set == null) {
+            return null;
+        }
+
+        List<Scheme> list = new ArrayList<Scheme>(set.size());
+        for (String string : set) {
+            list.add(Enum.valueOf(Scheme.class, string));
+        }
+
+        return list;
+    }
+
+    protected List<Parameter> parameterListToParameterList(List<springfox.documentation.service.Parameter> list) {
+        if (list == null) {
+            return null;
+        }
+
+        List<Parameter> list1 = new ArrayList<Parameter>(list.size());
+
+        Locale locale = LocaleContextHolder.getLocale();
+
+        for (springfox.documentation.service.Parameter param : list) {
+            String description = messageSource.getMessage(param.getDescription(), null, param.getDescription(), locale);
+
+            springfox.documentation.service.Parameter parameter = new springfox.documentation.service.Parameter(param.getName(),description,param.getDefaultValue(),param.isRequired(),param.isAllowMultiple(),param.isAllowEmptyValue(),param.getModelRef(),param.getType(),param.getAllowableValues(),param.getParamType(),param.getParamAccess(),param.isHidden(),param.getPattern(),param.getCollectionFormat(),param.getOrder(),param.getScalarExample(),param.getExamples() ,param.getVendorExtentions());
+            list1.add(parameterMapper.mapParameter(parameter));
+        }
+
+        return list1;
+    }
+
+
+    Map<String, Model> modelsFromApiListings(Multimap<String, ApiListing> apiListings) {
+        Map<String, springfox.documentation.schema.Model> definitions = newTreeMap();
+        for (ApiListing each : apiListings.values()) {
+            definitions.putAll(each.getModels());
+        }
+        return modelMapper.mapModels(definitions);
+    }
+
+
+
+
+
+
+//
+//
+//
+//    private static final VendorExtensionsMapper vendorMapper = new VendorExtensionsMapper();
+//
+//
+//
+//    public Parameter mapParameter(springfox.documentation.service.Parameter source) {
+//        Parameter bodyParameter = bodyParameter(source);
+//        return SerializableParameterFactories.create(source).or(bodyParameter);
+//    }
+//
+//    private Parameter bodyParameter(springfox.documentation.service.Parameter source) {
+//        BodyParameter parameter = new BodyParameter()
+//                .description(source.getDescription())
+//                .name(source.getName())
+//                .schema(fromModelRef(source.getModelRef()));
+//        parameter.setIn(source.getParamType());
+//        parameter.setAccess(source.getParamAccess());
+//        parameter.setPattern(source.getPattern());
+//        parameter.setRequired(source.isRequired());
+//        parameter.getVendorExtensions().putAll(vendorMapper.mapExtensions(source.getVendorExtentions()));
+//        for (Map.Entry<String, Collection<Example>> each : source.getExamples().asMap().entrySet()) {
+//            Optional<Example> example = FluentIterable.from(each.getValue()).first();
+//            if (example.isPresent() && example.get().getValue() != null) {
+//                parameter.addExample(each.getKey(), String.valueOf(example.get().getValue()));
+//            }
+//        }
+//
+//        //TODO: swagger-core Body parameter does not have an enum property
+//        return parameter;
+//    }
+//
+//    Model fromModelRef(ModelReference modelRef) {
+//        if (modelRef.isCollection()) {
+//            if (modelRef.getItemType().equals("byte")) {
+//                ModelImpl baseModel = new ModelImpl();
+//                baseModel.setType("string");
+//                baseModel.setFormat("byte");
+//                return maybeAddAllowableValuesToParameter(baseModel, modelRef.getAllowableValues());
+//            } else if (modelRef.getItemType().equals("file")) {
+//                ArrayModel files = new ArrayModel();
+//                files.items(new FileProperty());
+//                return files;
+//            }
+//            ModelReference itemModel = modelRef.itemModel().get();
+//            return new ArrayModel()
+//                    .items(maybeAddAllowableValues(itemTypeProperty(itemModel), itemModel.getAllowableValues()));
+//        }
+//        if (modelRef.isMap()) {
+//            ModelImpl baseModel = new ModelImpl();
+//            ModelReference itemModel = modelRef.itemModel().get();
+//            baseModel.additionalProperties(
+//                    maybeAddAllowableValues(
+//                            itemTypeProperty(itemModel),
+//                            itemModel.getAllowableValues()));
+//            return baseModel;
+//        }
+//        if (isBaseType(modelRef.getType())) {
+//            Property property = property(modelRef.getType());
+//            ModelImpl baseModel = new ModelImpl();
+//            baseModel.setType(property.getType());
+//            baseModel.setFormat(property.getFormat());
+//            return maybeAddAllowableValuesToParameter(baseModel, modelRef.getAllowableValues());
+//
+//        }
+//        return new RefModel(modelRef.getType());
+//    }
+//
+//
+//   private static class Properties {
+//        private static final Map<String, Function<String, ? extends Property>> typeFactory
+//                = ImmutableMap.<String, Function<String, ? extends Property>>builder()
+//                .put("int", newInstanceOf(IntegerProperty.class))
+//                .put("long", newInstanceOf(LongProperty.class))
+//                .put("float", newInstanceOf(FloatProperty.class))
+//                .put("double", newInstanceOf(DoubleProperty.class))
+//                .put("string", newInstanceOf(StringProperty.class))
+//                .put("boolean", newInstanceOf(BooleanProperty.class))
+//                .put("date", newInstanceOf(DateProperty.class))
+//                .put("date-time", newInstanceOf(DateTimeProperty.class))
+//                .put("bigdecimal", newInstanceOf(DecimalProperty.class))
+//                .put("biginteger", newInstanceOf(BaseIntegerProperty.class))
+//                .put("uuid", newInstanceOf(UUIDProperty.class))
+//                .put("object", newInstanceOf(ObjectProperty.class))
+//                .put("byte", bytePropertyFactory())
+//                .put("__file", filePropertyFactory())
+//                .build();
+//
+//        private Properties() {
+//            throw new UnsupportedOperationException();
+//        }
+//
+//        public static Property property(final String typeName) {
+//            String safeTypeName = nullToEmpty(typeName);
+//            Function<String, Function<String, ? extends Property>> propertyLookup
+//                    = forMap(typeFactory, voidOrRef(safeTypeName));
+//            return propertyLookup.apply(safeTypeName.toLowerCase()).apply(safeTypeName);
+//        }
+//
+//        public static Property property(final ModelReference modelRef) {
+//            if (modelRef.isMap()) {
+//                return new MapProperty(property(modelRef.itemModel().get()));
+//            } else if (modelRef.isCollection()) {
+//                if ("byte".equals(modelRef.itemModel().transform(toTypeName()).or(""))) {
+//                    return new ByteArrayProperty();
+//                }
+//                return new ArrayProperty(
+//                        maybeAddAllowableValues(itemTypeProperty(modelRef.itemModel().get()), modelRef.getAllowableValues()));
+//            }
+//            return property(modelRef.getType());
+//        }
+//
+//        private static Function<? super ModelReference, String> toTypeName() {
+//            return new Function<ModelReference, String>() {
+//                @Override
+//                public String apply(ModelReference input) {
+//                    return input.getType();
+//                }
+//            };
+//        }
+//
+//        public static Property itemTypeProperty(ModelReference paramModel) {
+//            if (paramModel.isCollection()) {
+//                return new ArrayProperty(
+//                        maybeAddAllowableValues(itemTypeProperty(paramModel.itemModel().get()), paramModel.getAllowableValues()));
+//            }
+//            return property(paramModel.getType());
+//        }
+//
+//        private static <T extends Property> Function<String, T> newInstanceOf(final Class<T> clazz) {
+//            return new Function<String, T>() {
+//                @Override
+//                public T apply(String input) {
+//                    try {
+//                        return clazz.newInstance();
+//                    } catch (Exception e) {
+//                        //This is bad! should never come here
+//                        throw new IllegalStateException(e);
+//                    }
+//                }
+//            };
+//        }
+//
+//        static Ordering<String> defaultOrdering(Map<String, ModelProperty> properties) {
+//            return Ordering.from(byPosition(properties)).compound(byName());
+//        }
+//
+//        private static Function<String, ? extends Property> voidOrRef(final String typeName) {
+//            return new Function<String, Property>() {
+//                @Override
+//                public Property apply(String input) {
+//                    if (typeName.equalsIgnoreCase("void")) {
+//                        return null;
+//                    }
+//                    return new RefProperty(typeName);
+//                }
+//            };
+//        }
+//
+//        private static Function<String, ? extends Property> bytePropertyFactory() {
+//            return new Function<String, Property>() {
+//                @Override
+//                public Property apply(String input) {
+//                    final IntegerProperty integerProperty = new IntegerProperty();
+//                    integerProperty.setFormat("int32");
+//                    integerProperty.setMaximum(BigDecimal.valueOf(Byte.MAX_VALUE));
+//                    integerProperty.setMinimum(BigDecimal.valueOf(Byte.MIN_VALUE));
+//                    return integerProperty;
+//                }
+//            };
+//        }
+//
+//        private static Function<String, ? extends Property> filePropertyFactory() {
+//            return new Function<String, Property>() {
+//                @Override
+//                public Property apply(String input) {
+//                    return new FileProperty();
+//                }
+//            };
+//        }
+//
+//        private static Comparator<String> byName() {
+//            return new Comparator<String>() {
+//                @Override
+//                public int compare(String first, String second) {
+//                    return first.compareTo(second);
+//                }
+//            };
+//        }
+//
+//        private static Comparator<String> byPosition(final Map<String, ModelProperty> modelProperties) {
+//            return new Comparator<String>() {
+//                @Override
+//                public int compare(String first, String second) {
+//                    ModelProperty p1 = modelProperties.get(first);
+//                    ModelProperty p2 = modelProperties.get(second);
+//                    return Ints.compare(p1.getPosition(), p2.getPosition());
+//                }
+//            };
+//        }
+//
+//        static Predicate<Map.Entry<String, ModelProperty>> voidProperties() {
+//            return new Predicate<Map.Entry<String, ModelProperty>>() {
+//                @Override
+//                public boolean apply(Map.Entry<String, ModelProperty> input) {
+//                    return isVoid(input.getValue().getType())
+//                            || collectionOfVoid(input.getValue().getType())
+//                            || arrayTypeOfVoid(input.getValue().getType().getArrayElementType());
+//                }
+//            };
+//        }
+//
+//        private static boolean arrayTypeOfVoid(ResolvedType arrayElementType) {
+//            return arrayElementType != null && isVoid(arrayElementType);
+//        }
+//
+//        private static boolean collectionOfVoid(ResolvedType type) {
+//            return isContainerType(type) && isVoid(collectionElementType(type));
+//        }
+}
diff --git a/escheduler-api/src/main/java/cn/escheduler/api/configuration/SwaggerConfig.java b/escheduler-api/src/main/java/cn/escheduler/api/configuration/SwaggerConfig.java
new file mode 100644
index 0000000..fb332c5
--- /dev/null
+++ b/escheduler-api/src/main/java/cn/escheduler/api/configuration/SwaggerConfig.java
@@ -0,0 +1,55 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *    http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package cn.escheduler.api.configuration;
+
+import com.github.xiaoymin.swaggerbootstrapui.annotations.EnableSwaggerBootstrapUI;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+
+import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
+import springfox.documentation.builders.ApiInfoBuilder;
+import springfox.documentation.builders.PathSelectors;
+import springfox.documentation.builders.RequestHandlerSelectors;
+import springfox.documentation.service.ApiInfo;
+import springfox.documentation.spi.DocumentationType;
+import springfox.documentation.spring.web.plugins.Docket;
+import springfox.documentation.swagger2.annotations.EnableSwagger2;
+
+/**
+ *
+ * swager2 config class <br/>
+ *
+ */
+@Configuration
+@EnableSwagger2
+@EnableSwaggerBootstrapUI
+public class SwaggerConfig implements WebMvcConfigurer {
+
+    @Bean
+    public Docket createRestApi() {
+        return new Docket(DocumentationType.SWAGGER_2).apiInfo(apiInfo()).select()
+                .apis(RequestHandlerSelectors.basePackage("cn.escheduler.api.controller")).paths(PathSelectors.any())
+                .build();
+    }
+
+    private ApiInfo apiInfo() {
+        return new ApiInfoBuilder().title("Easy Scheduler Api Docs").description("Easy Scheduler Api Docs")
+                .build();
+    }
+
+
+}
diff --git a/escheduler-api/src/main/java/cn/escheduler/api/controller/AccessTokenController.java b/escheduler-api/src/main/java/cn/escheduler/api/controller/AccessTokenController.java
index 103709c..4b60807 100644
--- a/escheduler-api/src/main/java/cn/escheduler/api/controller/AccessTokenController.java
+++ b/escheduler-api/src/main/java/cn/escheduler/api/controller/AccessTokenController.java
@@ -24,11 +24,16 @@
 import cn.escheduler.api.utils.Result;
 import cn.escheduler.common.utils.ParameterUtils;
 import cn.escheduler.dao.model.User;
+import io.swagger.annotations.Api;
+import io.swagger.annotations.ApiImplicitParam;
+import io.swagger.annotations.ApiImplicitParams;
+import io.swagger.annotations.ApiOperation;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.http.HttpStatus;
 import org.springframework.web.bind.annotation.*;
+import springfox.documentation.annotations.ApiIgnore;
 
 import java.util.Map;
 
@@ -36,8 +41,9 @@
 
 
 /**
- * user controller
+ * access token controller
  */
+@Api(tags = "ACCESS_TOKEN_TAG", position = 1)
 @RestController
 @RequestMapping("/access-token")
 public class AccessTokenController extends BaseController{
@@ -54,9 +60,10 @@
      * @param loginUser
      * @return
      */
+    @ApiIgnore
     @PostMapping(value = "/create")
     @ResponseStatus(HttpStatus.CREATED)
-    public Result createToken(@RequestAttribute(value = Constants.SESSION_USER) User loginUser,
+    public Result createToken(@ApiIgnore @RequestAttribute(value = Constants.SESSION_USER) User loginUser,
                                                   @RequestParam(value = "userId") int userId,
                                                   @RequestParam(value = "expireTime") String expireTime,
                                                   @RequestParam(value = "token") String token){
@@ -77,6 +84,7 @@
      * @param loginUser
      * @return
      */
+    @ApiIgnore
     @PostMapping(value = "/generate")
     @ResponseStatus(HttpStatus.CREATED)
     public Result generateToken(@RequestAttribute(value = Constants.SESSION_USER) User loginUser,
@@ -101,9 +109,15 @@
      * @param pageSize
      * @return
      */
+    @ApiOperation(value = "queryAccessTokenList", notes= "QUERY_ACCESS_TOKEN_LIST_NOTES")
+    @ApiImplicitParams({
+            @ApiImplicitParam(name = "searchVal", value = "SEARCH_VAL", dataType ="String"),
+            @ApiImplicitParam(name = "pageNo", value = "PAGE_NO", dataType = "Int", example = "1"),
+            @ApiImplicitParam(name = "pageSize", value = "PAGE_SIZE", dataType ="Int",example = "20")
+    })
     @GetMapping(value="/list-paging")
     @ResponseStatus(HttpStatus.OK)
-    public Result queryAccessTokenList(@RequestAttribute(value = Constants.SESSION_USER) User loginUser,
+    public Result queryAccessTokenList(@ApiIgnore @RequestAttribute(value = Constants.SESSION_USER) User loginUser,
                                 @RequestParam("pageNo") Integer pageNo,
                                 @RequestParam(value = "searchVal", required = false) String searchVal,
                                 @RequestParam("pageSize") Integer pageSize){
@@ -129,6 +143,7 @@
      * @param id
      * @return
      */
+    @ApiIgnore
     @PostMapping(value = "/delete")
     @ResponseStatus(HttpStatus.OK)
     public Result delAccessTokenById(@RequestAttribute(value = Constants.SESSION_USER) User loginUser,
@@ -149,6 +164,7 @@
      * @param loginUser
      * @return
      */
+    @ApiIgnore
     @PostMapping(value = "/update")
     @ResponseStatus(HttpStatus.CREATED)
     public Result updateToken(@RequestAttribute(value = Constants.SESSION_USER) User loginUser,
diff --git a/escheduler-api/src/main/java/cn/escheduler/api/controller/AlertGroupController.java b/escheduler-api/src/main/java/cn/escheduler/api/controller/AlertGroupController.java
index bf61cf7..f678cb5 100644
--- a/escheduler-api/src/main/java/cn/escheduler/api/controller/AlertGroupController.java
+++ b/escheduler-api/src/main/java/cn/escheduler/api/controller/AlertGroupController.java
@@ -22,17 +22,26 @@
 import cn.escheduler.common.enums.AlertType;
 import cn.escheduler.common.utils.ParameterUtils;
 import cn.escheduler.dao.model.User;
+import io.swagger.annotations.Api;
+import io.swagger.annotations.ApiImplicitParam;
+import io.swagger.annotations.ApiImplicitParams;
+import io.swagger.annotations.ApiOperation;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.http.HttpStatus;
 import org.springframework.web.bind.annotation.*;
+import springfox.documentation.annotations.ApiIgnore;
 
 import java.util.HashMap;
 import java.util.Map;
 
 import static cn.escheduler.api.enums.Status.*;
 
+/**
+ * alert group controller
+ */
+@Api(tags = "ALERT_GROUP_TAG", position = 1)
 @RestController
 @RequestMapping("alert-group")
 public class AlertGroupController extends  BaseController{
@@ -51,9 +60,15 @@
      * @param desc
      * @return
      */
+    @ApiOperation(value = "createAlertgroup", notes= "CREATE_ALERT_GROUP_NOTES")
+    @ApiImplicitParams({
+            @ApiImplicitParam(name = "groupName", value = "GROUP_NAME", required = true, dataType = "String"),
+            @ApiImplicitParam(name = "groupType", value = "GROUP_TYPE", required = true, dataType ="AlertType"),
+            @ApiImplicitParam(name = "desc", value = "DESC",  dataType ="String")
+    })
     @PostMapping(value = "/create")
     @ResponseStatus(HttpStatus.CREATED)
-    public Result createAlertgroup(@RequestAttribute(value = Constants.SESSION_USER) User loginUser,
+    public Result createAlertgroup(@ApiIgnore @RequestAttribute(value = Constants.SESSION_USER) User loginUser,
                                @RequestParam(value = "groupName") String groupName,
                                @RequestParam(value = "groupType") AlertType groupType,
                                @RequestParam(value = "desc",required = false) String desc) {
@@ -73,9 +88,10 @@
      * @param loginUser
      * @return
      */
+    @ApiOperation(value = "list", notes= "QUERY_ALERT_GROUP_LIST_NOTES")
     @GetMapping(value = "/list")
     @ResponseStatus(HttpStatus.OK)
-    public Result list(@RequestAttribute(value = Constants.SESSION_USER) User loginUser) {
+    public Result list(@ApiIgnore @RequestAttribute(value = Constants.SESSION_USER) User loginUser) {
         logger.info("login  user {}, query all alertGroup",
                 loginUser.getUserName());
         try{
@@ -96,9 +112,15 @@
      * @param pageSize
      * @return
      */
+    @ApiOperation(value = "queryTaskListPaging", notes= "QUERY_TASK_INSTANCE_LIST_PAGING_NOTES")
+    @ApiImplicitParams({
+            @ApiImplicitParam(name = "searchVal", value = "SEARCH_VAL", type ="String"),
+            @ApiImplicitParam(name = "pageNo", value = "PAGE_NO", dataType = "Int", example = "1"),
+            @ApiImplicitParam(name = "pageSize", value = "PAGE_SIZE", dataType = "Int", example = "20")
+    })
     @GetMapping(value="/list-paging")
     @ResponseStatus(HttpStatus.OK)
-    public Result listPaging(@RequestAttribute(value = Constants.SESSION_USER) User loginUser,
+    public Result listPaging(@ApiIgnore @RequestAttribute(value = Constants.SESSION_USER) User loginUser,
                              @RequestParam("pageNo") Integer pageNo,
                              @RequestParam(value = "searchVal", required = false) String searchVal,
                              @RequestParam("pageSize") Integer pageSize){
@@ -128,9 +150,16 @@
      * @param desc
      * @return
      */
+    @ApiOperation(value = "updateAlertgroup", notes= "UPDATE_ALERT_GROUP_NOTES")
+    @ApiImplicitParams({
+            @ApiImplicitParam(name = "id", value = "ALERT_GROUP_ID", required = true, dataType = "Int",example = "100"),
+            @ApiImplicitParam(name = "groupName", value = "GROUP_NAME", required = true, dataType = "String"),
+            @ApiImplicitParam(name = "groupType", value = "GROUP_TYPE", required = true, dataType ="AlertType"),
+            @ApiImplicitParam(name = "desc", value = "DESC",  dataType ="String")
+    })
     @PostMapping(value = "/update")
     @ResponseStatus(HttpStatus.OK)
-    public Result updateAlertgroup(@RequestAttribute(value = Constants.SESSION_USER) User loginUser,
+    public Result updateAlertgroup(@ApiIgnore @RequestAttribute(value = Constants.SESSION_USER) User loginUser,
                                    @RequestParam(value = "id") int id,
                                    @RequestParam(value = "groupName") String groupName,
                                    @RequestParam(value = "groupType") AlertType groupType,
@@ -153,9 +182,13 @@
      * @param id
      * @return
      */
+    @ApiOperation(value = "delAlertgroupById", notes= "DELETE_ALERT_GROUP_BY_ID_NOTES")
+    @ApiImplicitParams({
+            @ApiImplicitParam(name = "id", value = "ALERT_GROUP_ID", required = true, dataType = "Int",example = "100")
+    })
     @PostMapping(value = "/delete")
     @ResponseStatus(HttpStatus.OK)
-    public Result delAlertgroupById(@RequestAttribute(value = Constants.SESSION_USER) User loginUser,
+    public Result delAlertgroupById(@ApiIgnore @RequestAttribute(value = Constants.SESSION_USER) User loginUser,
                                   @RequestParam(value = "id") int id) {
         logger.info("login user {}, delete AlertGroup, id: {},", loginUser.getUserName(), id);
         try {
@@ -175,9 +208,13 @@
      * @param groupName
      * @return
      */
+    @ApiOperation(value = "verifyGroupName", notes= "VERIFY_ALERT_GROUP_NAME_NOTES")
+    @ApiImplicitParams({
+            @ApiImplicitParam(name = "groupName", value = "GROUP_NAME", required = true, dataType = "String"),
+    })
     @GetMapping(value = "/verify-group-name")
     @ResponseStatus(HttpStatus.OK)
-    public Result verifyGroupName(@RequestAttribute(value = Constants.SESSION_USER) User loginUser,
+    public Result verifyGroupName(@ApiIgnore @RequestAttribute(value = Constants.SESSION_USER) User loginUser,
                                  @RequestParam(value ="groupName") String groupName
     ) {
         logger.info("login user {}, verfiy group name: {}",
@@ -193,9 +230,14 @@
      * @param userIds
      * @return
      */
+    @ApiOperation(value = "grantUser", notes= "GRANT_ALERT_GROUP_NOTES")
+    @ApiImplicitParams({
+            @ApiImplicitParam(name = "id", value = "ALERT_GROUP_ID", required = true, dataType = "Int",example = "100"),
+            @ApiImplicitParam(name = "userIds", value = "USER_IDS", required = true, dataType = "String")
+    })
     @PostMapping(value = "/grant-user")
     @ResponseStatus(HttpStatus.OK)
-    public Result grantUser(@RequestAttribute(value = Constants.SESSION_USER) User loginUser,
+    public Result grantUser(@ApiIgnore @RequestAttribute(value = Constants.SESSION_USER) User loginUser,
                                   @RequestParam(value = "alertgroupId") int  alertgroupId,
                                   @RequestParam(value = "userIds") String userIds) {
         logger.info("login user {}, grant user, alertGroupId: {},userIds : {}", loginUser.getUserName(), alertgroupId,userIds);
diff --git a/escheduler-api/src/main/java/cn/escheduler/api/controller/DataAnalysisController.java b/escheduler-api/src/main/java/cn/escheduler/api/controller/DataAnalysisController.java
index 568af1f..be256a9 100644
--- a/escheduler-api/src/main/java/cn/escheduler/api/controller/DataAnalysisController.java
+++ b/escheduler-api/src/main/java/cn/escheduler/api/controller/DataAnalysisController.java
@@ -21,11 +21,16 @@
 import cn.escheduler.api.utils.Constants;
 import cn.escheduler.api.utils.Result;
 import cn.escheduler.dao.model.User;
+import io.swagger.annotations.Api;
+import io.swagger.annotations.ApiImplicitParam;
+import io.swagger.annotations.ApiImplicitParams;
+import io.swagger.annotations.ApiOperation;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.http.HttpStatus;
 import org.springframework.web.bind.annotation.*;
+import springfox.documentation.annotations.ApiIgnore;
 
 import java.util.Map;
 
@@ -34,6 +39,7 @@
 /**
  * data analysis controller
  */
+@Api(tags = "DATA_ANALYSIS_TAG", position = 1)
 @RestController
 @RequestMapping("projects/analysis")
 public class DataAnalysisController extends BaseController{
@@ -52,9 +58,15 @@
      * @param projectId
      * @return
      */
+    @ApiOperation(value = "countTaskState", notes= "COUNT_TASK_STATE_NOTES")
+    @ApiImplicitParams({
+            @ApiImplicitParam(name = "startDate", value = "START_DATE", dataType = "String"),
+            @ApiImplicitParam(name = "endDate", value = "END_DATE", dataType ="String"),
+            @ApiImplicitParam(name = "projectId", value = "PROJECT_ID",  dataType ="Int", example = "100")
+    })
     @GetMapping(value="/task-state-count")
     @ResponseStatus(HttpStatus.OK)
-    public Result countTaskState(@RequestAttribute(value = Constants.SESSION_USER) User loginUser,
+    public Result countTaskState(@ApiIgnore @RequestAttribute(value = Constants.SESSION_USER) User loginUser,
                                                          @RequestParam(value="startDate", required=false) String startDate,
                                                          @RequestParam(value="endDate", required=false) String endDate,
                                                          @RequestParam(value="projectId", required=false, defaultValue = "0") int projectId){
@@ -76,9 +88,15 @@
      * @param projectId
      * @return
      */
+    @ApiOperation(value = "countProcessInstanceState", notes= "COUNT_PROCESS_INSTANCE_NOTES")
+    @ApiImplicitParams({
+            @ApiImplicitParam(name = "startDate", value = "START_DATE", dataType = "String"),
+            @ApiImplicitParam(name = "endDate", value = "END_DATE",  dataType ="String"),
+            @ApiImplicitParam(name = "projectId", value = "PROJECT_ID",  dataType ="Int", example = "100")
+    })
     @GetMapping(value="/process-state-count")
     @ResponseStatus(HttpStatus.OK)
-    public Result countProcessInstanceState(@RequestAttribute(value = Constants.SESSION_USER) User loginUser,
+    public Result countProcessInstanceState(@ApiIgnore @RequestAttribute(value = Constants.SESSION_USER) User loginUser,
                                             @RequestParam(value="startDate", required=false) String startDate,
                                             @RequestParam(value="endDate", required=false) String endDate,
                                             @RequestParam(value="projectId", required=false, defaultValue = "0") int projectId){
@@ -100,9 +118,13 @@
      * @param projectId
      * @return
      */
+    @ApiOperation(value = "countDefinitionByUser", notes= "COUNT_PROCESS_DEFINITION_BY_USER_NOTES")
+    @ApiImplicitParams({
+            @ApiImplicitParam(name = "projectId", value = "PROJECT_ID", dataType ="Int", example = "100")
+    })
     @GetMapping(value="/define-user-count")
     @ResponseStatus(HttpStatus.OK)
-    public Result countDefinitionByUser(@RequestAttribute(value = Constants.SESSION_USER) User loginUser,
+    public Result countDefinitionByUser(@ApiIgnore @RequestAttribute(value = Constants.SESSION_USER) User loginUser,
                                         @RequestParam(value="projectId", required=false, defaultValue = "0") int projectId){
         try{
             logger.info("count process definition , user:{}, project id",
@@ -123,9 +145,15 @@
      * @param projectId
      * @return
      */
+    @ApiOperation(value = "countCommandState", notes= "COUNT_COMMAND_STATE_NOTES")
+    @ApiImplicitParams({
+            @ApiImplicitParam(name = "startDate", value = "START_DATE", dataType = "String"),
+            @ApiImplicitParam(name = "endDate", value = "END_DATE",  dataType ="String"),
+            @ApiImplicitParam(name = "projectId", value = "PROJECT_ID",  dataType ="Int", example = "100")
+    })
     @GetMapping(value="/command-state-count")
     @ResponseStatus(HttpStatus.OK)
-    public Result countCommandState(@RequestAttribute(value = Constants.SESSION_USER) User loginUser,
+    public Result countCommandState(@ApiIgnore @RequestAttribute(value = Constants.SESSION_USER) User loginUser,
                                  @RequestParam(value="startDate", required=false) String startDate,
                                  @RequestParam(value="endDate", required=false) String endDate,
                                  @RequestParam(value="projectId", required=false, defaultValue = "0") int projectId){
@@ -147,9 +175,15 @@
      * @param projectId
      * @return
      */
+    @ApiOperation(value = "countQueueState", notes= "COUNT_QUEUE_STATE_NOTES")
+    @ApiImplicitParams({
+            @ApiImplicitParam(name = "startDate", value = "START_DATE", dataType = "String"),
+            @ApiImplicitParam(name = "endDate", value = "END_DATE",  dataType ="String"),
+            @ApiImplicitParam(name = "projectId", value = "PROJECT_ID",  dataType ="Int", example = "100")
+    })
     @GetMapping(value="/queue-count")
     @ResponseStatus(HttpStatus.OK)
-    public Result countQueueState(@RequestAttribute(value = Constants.SESSION_USER) User loginUser,
+    public Result countQueueState(@ApiIgnore @RequestAttribute(value = Constants.SESSION_USER) User loginUser,
                                     @RequestParam(value="projectId", required=false, defaultValue = "0") int projectId){
         try{
             logger.info("count command state, user:{}, start date: {}, end date:{}, project id {}",
diff --git a/escheduler-api/src/main/java/cn/escheduler/api/controller/DataSourceController.java b/escheduler-api/src/main/java/cn/escheduler/api/controller/DataSourceController.java
index fcaf2be..7301f41 100644
--- a/escheduler-api/src/main/java/cn/escheduler/api/controller/DataSourceController.java
+++ b/escheduler-api/src/main/java/cn/escheduler/api/controller/DataSourceController.java
@@ -18,25 +18,37 @@
 
 import cn.escheduler.api.enums.Status;
 import cn.escheduler.api.service.DataSourceService;
+import cn.escheduler.api.utils.CheckUtils;
 import cn.escheduler.api.utils.Constants;
 import cn.escheduler.api.utils.Result;
 import cn.escheduler.common.enums.DbType;
+import cn.escheduler.common.enums.ResUploadType;
+import cn.escheduler.common.utils.CommonUtils;
 import cn.escheduler.common.utils.ParameterUtils;
+import cn.escheduler.common.utils.PropertyUtils;
 import cn.escheduler.dao.model.User;
+import io.swagger.annotations.Api;
+import io.swagger.annotations.ApiImplicitParam;
+import io.swagger.annotations.ApiImplicitParams;
+import io.swagger.annotations.ApiOperation;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.http.HttpStatus;
 import org.springframework.web.bind.annotation.*;
+import springfox.documentation.annotations.ApiIgnore;
 
+import java.util.HashMap;
 import java.util.Map;
 
 import static cn.escheduler.api.enums.Status.*;
+import static cn.escheduler.common.utils.PropertyUtils.getBoolean;
 
 
 /**
  * data source controller
  */
+@Api(tags = "DATA_SOURCE_TAG", position = 3)
 @RestController
 @RequestMapping("datasources")
 public class DataSourceController extends BaseController {
@@ -48,31 +60,48 @@
 
     /**
      * create data source
-     * 创建数据源
-     *
      * @param loginUser
      * @param name
      * @param note
      * @param type
+     * @param host
+     * @param port
+     * @param database
+     * @param principal
+     * @param userName
+     * @param password
      * @param other
      * @return
      */
+    @ApiOperation(value = "createDataSource", notes= "CREATE_DATA_SOURCE_NOTES")
+    @ApiImplicitParams({
+            @ApiImplicitParam(name = "name", value = "DATA_SOURCE_NAME", required = true, dataType ="String"),
+            @ApiImplicitParam(name = "note", value = "DATA_SOURCE_NOTE", dataType = "String"),
+            @ApiImplicitParam(name = "type", value = "DB_TYPE", required = true,dataType ="DbType"),
+            @ApiImplicitParam(name = "host", value = "DATA_SOURCE_HOST",required = true, dataType ="String"),
+            @ApiImplicitParam(name = "port", value = "DATA_SOURCE_PORT",required = true, dataType ="String"),
+            @ApiImplicitParam(name = "database", value = "DATABASE_NAME",required = true, dataType ="String"),
+            @ApiImplicitParam(name = "userName", value = "USER_NAME",required = true, dataType ="String"),
+            @ApiImplicitParam(name = "password", value = "PASSWORD", dataType ="String"),
+            @ApiImplicitParam(name = "other", value = "DATA_SOURCE_OTHER", dataType ="String")
+    })
     @PostMapping(value = "/create")
     @ResponseStatus(HttpStatus.CREATED)
-    public Result createDataSource(@RequestAttribute(value = Constants.SESSION_USER) User loginUser,
+    public Result createDataSource(@ApiIgnore @RequestAttribute(value = Constants.SESSION_USER) User loginUser,
                                    @RequestParam("name") String name,
                                    @RequestParam(value = "note", required = false) String note,
                                    @RequestParam(value = "type") DbType type,
                                    @RequestParam(value = "host") String host,
                                    @RequestParam(value = "port") String port,
                                    @RequestParam(value = "database") String database,
+                                   @RequestParam(value = "principal") String principal,
                                    @RequestParam(value = "userName") String userName,
                                    @RequestParam(value = "password") String password,
                                    @RequestParam(value = "other") String other) {
-        logger.info("login user {} create datasource ame: {}, note: {}, type: {}, other: {}",
-                loginUser.getUserName(), name, note, type, other);
+        logger.info("login user {} create datasource name: {}, note: {}, type: {}, host: {},port: {},database : {},principal: {},userName : {} other: {}",
+                loginUser.getUserName(), name, note, type, host,port,database,principal,userName,other);
         try {
-            String parameter = dataSourceService.buildParameter(name, note, type, host, port, database, userName, password, other);
+            String parameter = dataSourceService.buildParameter(name, note, type, host, port, database,principal,userName, password, other);
             Map<String, Object> result = dataSourceService.createDataSource(loginUser, name, note, type, parameter);
             return returnDataList(result);
 
@@ -93,9 +122,22 @@
      * @param other
      * @return
      */
+    @ApiOperation(value = "updateDataSource", notes= "UPDATE_DATA_SOURCE_NOTES")
+    @ApiImplicitParams({
+            @ApiImplicitParam(name = "id", value = "DATA_SOURCE_ID", required = true, dataType ="Int", example = "100"),
+            @ApiImplicitParam(name = "name", value = "DATA_SOURCE_NAME", required = true, dataType ="String"),
+            @ApiImplicitParam(name = "note", value = "DATA_SOURCE_NOTE", dataType = "String"),
+            @ApiImplicitParam(name = "type", value = "DB_TYPE", required = true,dataType ="DbType"),
+            @ApiImplicitParam(name = "host", value = "DATA_SOURCE_HOST",required = true, dataType ="String"),
+            @ApiImplicitParam(name = "port", value = "DATA_SOURCE_PORT",required = true, dataType ="String"),
+            @ApiImplicitParam(name = "database", value = "DATABASE_NAME",required = true, dataType ="String"),
+            @ApiImplicitParam(name = "userName", value = "USER_NAME",required = true, dataType ="String"),
+            @ApiImplicitParam(name = "password", value = "PASSWORD", dataType ="String"),
+            @ApiImplicitParam(name = "other", value = "DATA_SOURCE_OTHER", dataType ="String")
+    })
     @PostMapping(value = "/update")
     @ResponseStatus(HttpStatus.OK)
-    public Result updateDataSource(@RequestAttribute(value = Constants.SESSION_USER) User loginUser,
+    public Result updateDataSource(@ApiIgnore @RequestAttribute(value = Constants.SESSION_USER) User loginUser,
                                    @RequestParam("id") int id,
                                    @RequestParam("name") String name,
                                    @RequestParam(value = "note", required = false) String note,
@@ -103,13 +145,14 @@
                                    @RequestParam(value = "host") String host,
                                    @RequestParam(value = "port") String port,
                                    @RequestParam(value = "database") String database,
+                                   @RequestParam(value = "principal") String principal,
                                    @RequestParam(value = "userName") String userName,
                                    @RequestParam(value = "password") String password,
                                    @RequestParam(value = "other") String other) {
         logger.info("login user {} updateProcessInstance datasource name: {}, note: {}, type: {}, other: {}",
                 loginUser.getUserName(), name, note, type, other);
         try {
-            String parameter = dataSourceService.buildParameter(name, note, type, host, port, database, userName, password, other);
+            String parameter = dataSourceService.buildParameter(name, note, type, host, port, database,principal, userName, password, other);
             Map<String, Object> dataSource = dataSourceService.updateDataSource(id, loginUser, name, note, type, parameter);
             return returnDataList(dataSource);
         } catch (Exception e) {
@@ -127,9 +170,14 @@
      * @param id
      * @return
      */
+    @ApiOperation(value = "queryDataSource", notes= "QUERY_DATA_SOURCE_NOTES")
+    @ApiImplicitParams({
+            @ApiImplicitParam(name = "id", value = "DATA_SOURCE_ID", required = true, dataType ="Int", example = "100")
+
+    })
     @PostMapping(value = "/update-ui")
     @ResponseStatus(HttpStatus.OK)
-    public Result queryDataSource(@RequestAttribute(value = Constants.SESSION_USER) User loginUser,
+    public Result queryDataSource(@ApiIgnore @RequestAttribute(value = Constants.SESSION_USER) User loginUser,
                                   @RequestParam("id") int id) {
         logger.info("login user {}, query datasource: {}",
                 loginUser.getUserName(), id);
@@ -150,9 +198,13 @@
      * @param loginUser
      * @return
      */
+    @ApiOperation(value = "queryDataSourceList", notes= "QUERY_DATA_SOURCE_LIST_BY_TYPE_NOTES")
+    @ApiImplicitParams({
+            @ApiImplicitParam(name = "type", value = "DB_TYPE", required = true,dataType ="DbType")
+    })
     @GetMapping(value = "/list")
     @ResponseStatus(HttpStatus.OK)
-    public Result queryDataSourceList(@RequestAttribute(value = Constants.SESSION_USER) User loginUser,
+    public Result queryDataSourceList(@ApiIgnore @RequestAttribute(value = Constants.SESSION_USER) User loginUser,
                                       @RequestParam("type") DbType type) {
         try {
             Map<String, Object> result = dataSourceService.queryDataSourceList(loginUser, type.ordinal());
@@ -172,9 +224,15 @@
      * @param pageSize
      * @return
      */
+    @ApiOperation(value = "queryDataSourceListPaging", notes= "QUERY_DATA_SOURCE_LIST_PAGING_NOTES")
+    @ApiImplicitParams({
+            @ApiImplicitParam(name = "searchVal", value = "SEARCH_VAL", dataType ="String"),
+            @ApiImplicitParam(name = "pageNo", value = "PAGE_NO", dataType = "Int", example = "1"),
+            @ApiImplicitParam(name = "pageSize", value = "PAGE_SIZE", dataType ="Int",example = "20")
+    })
     @GetMapping(value = "/list-paging")
     @ResponseStatus(HttpStatus.OK)
-    public Result queryDataSourceListPaging(@RequestAttribute(value = Constants.SESSION_USER) User loginUser,
+    public Result queryDataSourceListPaging(@ApiIgnore @RequestAttribute(value = Constants.SESSION_USER) User loginUser,
                                             @RequestParam(value = "searchVal", required = false) String searchVal,
                                             @RequestParam("pageNo") Integer pageNo,
                                             @RequestParam("pageSize") Integer pageSize) {
@@ -202,22 +260,35 @@
      * @param other
      * @return
      */
+    @ApiOperation(value = "connectDataSource", notes= "CONNECT_DATA_SOURCE_NOTES")
+    @ApiImplicitParams({
+            @ApiImplicitParam(name = "name", value = "DATA_SOURCE_NAME", required = true, dataType ="String"),
+            @ApiImplicitParam(name = "note", value = "DATA_SOURCE_NOTE", dataType = "String"),
+            @ApiImplicitParam(name = "type", value = "DB_TYPE", required = true,dataType ="DbType"),
+            @ApiImplicitParam(name = "host", value = "DATA_SOURCE_HOST",required = true, dataType ="String"),
+            @ApiImplicitParam(name = "port", value = "DATA_SOURCE_PORT",required = true, dataType ="String"),
+            @ApiImplicitParam(name = "database", value = "DATABASE_NAME",required = true, dataType ="String"),
+            @ApiImplicitParam(name = "userName", value = "USER_NAME",required = true, dataType ="String"),
+            @ApiImplicitParam(name = "password", value = "PASSWORD", dataType ="String"),
+            @ApiImplicitParam(name = "other", value = "DATA_SOURCE_OTHER", dataType ="String")
+    })
     @PostMapping(value = "/connect")
     @ResponseStatus(HttpStatus.OK)
-    public Result connectDataSource(@RequestAttribute(value = Constants.SESSION_USER) User loginUser,
+    public Result connectDataSource(@ApiIgnore @RequestAttribute(value = Constants.SESSION_USER) User loginUser,
                                     @RequestParam("name") String name,
                                     @RequestParam(value = "note", required = false) String note,
                                     @RequestParam(value = "type") DbType type,
                                     @RequestParam(value = "host") String host,
                                     @RequestParam(value = "port") String port,
                                     @RequestParam(value = "database") String database,
+                                    @RequestParam(value = "principal") String principal,
                                     @RequestParam(value = "userName") String userName,
                                     @RequestParam(value = "password") String password,
                                     @RequestParam(value = "other") String other) {
         logger.info("login user {}, connect datasource: {} failure, note: {}, type: {}, other: {}",
                 loginUser.getUserName(), name, note, type, other);
         try {
-            String parameter = dataSourceService.buildParameter(name, note, type, host, port, database, userName, password, other);
+            String parameter = dataSourceService.buildParameter(name, note, type, host, port, database,principal,userName, password, other);
             Boolean isConnection = dataSourceService.checkConnection(type, parameter);
             Result result = new Result();
 
@@ -239,9 +310,13 @@
      * @param loginUser
      * @return
      */
+    @ApiOperation(value = "connectionTest", notes= "CONNECT_DATA_SOURCE_TEST_NOTES")
+    @ApiImplicitParams({
+            @ApiImplicitParam(name = "id", value = "DATA_SOURCE_ID", required = true, dataType ="Int", example = "100")
+    })
     @GetMapping(value = "/connect-by-id")
     @ResponseStatus(HttpStatus.OK)
-    public Result connectionTest(@RequestAttribute(value = Constants.SESSION_USER) User loginUser,
+    public Result connectionTest(@ApiIgnore @RequestAttribute(value = Constants.SESSION_USER) User loginUser,
                                  @RequestParam("id") int id) {
         logger.info("connection test, login user:{}, id:{}", loginUser.getUserName(), id);
 
@@ -269,9 +344,13 @@
      * @param id datasource id
      * @return
      */
+    @ApiOperation(value = "delete", notes= "DELETE_DATA_SOURCE_NOTES")
+    @ApiImplicitParams({
+            @ApiImplicitParam(name = "id", value = "DATA_SOURCE_ID", required = true, dataType ="Int", example = "100")
+    })
     @GetMapping(value = "/delete")
     @ResponseStatus(HttpStatus.OK)
-    public Result delete(@RequestAttribute(value = Constants.SESSION_USER) User loginUser,
+    public Result delete(@ApiIgnore @RequestAttribute(value = Constants.SESSION_USER) User loginUser,
                          @RequestParam("id") int id) {
         try {
             logger.info("delete datasource,login user:{}, id:{}", loginUser.getUserName(), id);
@@ -289,9 +368,13 @@
      * @param name
      * @return
      */
+    @ApiOperation(value = "verifyDataSourceName", notes= "VERIFY_DATA_SOURCE_NOTES")
+    @ApiImplicitParams({
+            @ApiImplicitParam(name = "name", value = "DATA_SOURCE_NAME", required = true, dataType ="String")
+    })
     @GetMapping(value = "/verify-name")
     @ResponseStatus(HttpStatus.OK)
-    public Result verifyDataSourceName(@RequestAttribute(value = Constants.SESSION_USER) User loginUser,
+    public Result verifyDataSourceName(@ApiIgnore @RequestAttribute(value = Constants.SESSION_USER) User loginUser,
                                        @RequestParam(value = "name") String name
     ) {
         logger.info("login user {}, verfiy datasource name: {}",
@@ -314,9 +397,13 @@
      * @param userId
      * @return
      */
+    @ApiOperation(value = "unauthDatasource", notes= "UNAUTHORIZED_DATA_SOURCE_NOTES")
+    @ApiImplicitParams({
+            @ApiImplicitParam(name = "userId", value = "USER_ID", required = true, dataType ="Int", example = "100")
+    })
     @GetMapping(value = "/unauth-datasource")
     @ResponseStatus(HttpStatus.OK)
-    public Result unauthDatasource(@RequestAttribute(value = Constants.SESSION_USER) User loginUser,
+    public Result unauthDatasource(@ApiIgnore @RequestAttribute(value = Constants.SESSION_USER) User loginUser,
                                    @RequestParam("userId") Integer userId) {
         try {
             logger.info("unauthorized datasource, login user:{}, unauthorized userId:{}",
@@ -337,9 +424,13 @@
      * @param userId
      * @return
      */
+    @ApiOperation(value = "authedDatasource", notes= "AUTHORIZED_DATA_SOURCE_NOTES")
+    @ApiImplicitParams({
+            @ApiImplicitParam(name = "userId", value = "USER_ID", required = true, dataType ="Int", example = "100")
+    })
     @GetMapping(value = "/authed-datasource")
     @ResponseStatus(HttpStatus.OK)
-    public Result authedDatasource(@RequestAttribute(value = Constants.SESSION_USER) User loginUser,
+    public Result authedDatasource(@ApiIgnore @RequestAttribute(value = Constants.SESSION_USER) User loginUser,
                                    @RequestParam("userId") Integer userId) {
         try {
             logger.info("authorized data source, login user:{}, authorized useId:{}",
@@ -351,4 +442,24 @@
             return error(AUTHORIZED_DATA_SOURCE.getCode(), AUTHORIZED_DATA_SOURCE.getMsg());
         }
     }
+
+    /**
+     * get user info
+     *
+     * @param loginUser
+     * @return
+     */
+    @ApiOperation(value = "getKerberosStartupState", notes= "GET_USER_INFO_NOTES")
+    @GetMapping(value="/kerberos-startup-state")
+    @ResponseStatus(HttpStatus.OK)
+    public Result getKerberosStartupState(@ApiIgnore @RequestAttribute(value = Constants.SESSION_USER) User loginUser){
+        logger.info("login user {},get kerberos startup state : {}", loginUser.getUserName());
+        try{
+            // if upload resource is HDFS and kerberos startup is true , else false
+            return success(Status.SUCCESS.getMsg(), CommonUtils.getKerberosStartupState());
+        }catch (Exception e){
+            logger.error(KERBEROS_STARTUP_STATE.getMsg(),e);
+            return error(Status.KERBEROS_STARTUP_STATE.getCode(), Status.KERBEROS_STARTUP_STATE.getMsg());
+        }
+    }
 }
diff --git a/escheduler-api/src/main/java/cn/escheduler/api/controller/ExecutorController.java b/escheduler-api/src/main/java/cn/escheduler/api/controller/ExecutorController.java
index a3c1e40..1938644 100644
--- a/escheduler-api/src/main/java/cn/escheduler/api/controller/ExecutorController.java
+++ b/escheduler-api/src/main/java/cn/escheduler/api/controller/ExecutorController.java
@@ -24,11 +24,13 @@
 import cn.escheduler.api.utils.Result;
 import cn.escheduler.common.enums.*;
 import cn.escheduler.dao.model.User;
+import io.swagger.annotations.*;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.http.HttpStatus;
 import org.springframework.web.bind.annotation.*;
+import springfox.documentation.annotations.ApiIgnore;
 
 import java.util.Map;
 
@@ -36,8 +38,9 @@
 
 
 /**
- * execute task controller
+ * execute process controller
  */
+@Api(tags = "PROCESS_INSTANCE_EXECUTOR_TAG", position = 1)
 @RestController
 @RequestMapping("projects/{projectName}/executors")
 public class ExecutorController extends BaseController {
@@ -50,10 +53,27 @@
     /**
      * execute process instance
      */
+    @ApiOperation(value = "startProcessInstance", notes= "RUN_PROCESS_INSTANCE_NOTES")
+    @ApiImplicitParams({
+            @ApiImplicitParam(name = "processDefinitionId", value = "PROCESS_DEFINITION_ID", required = true, dataType = "Int", example = "100"),
+            @ApiImplicitParam(name = "scheduleTime", value = "SCHEDULE_TIME", required = true, dataType = "String"),
+            @ApiImplicitParam(name = "failureStrategy", value = "FAILURE_STRATEGY", required = true, dataType ="FailureStrategy"),
+            @ApiImplicitParam(name = "startNodeList", value = "START_NODE_LIST", dataType ="String"),
+            @ApiImplicitParam(name = "taskDependType", value = "TASK_DEPEND_TYPE", dataType ="TaskDependType"),
+            @ApiImplicitParam(name = "execType", value = "COMMAND_TYPE", dataType ="CommandType"),
+            @ApiImplicitParam(name = "warningType", value = "WARNING_TYPE",required = true, dataType ="WarningType"),
+            @ApiImplicitParam(name = "warningGroupId", value = "WARNING_GROUP_ID",required = true, dataType ="Int", example = "100"),
+            @ApiImplicitParam(name = "receivers", value = "RECEIVERS",dataType ="String" ),
+            @ApiImplicitParam(name = "receiversCc", value = "RECEIVERS_CC",dataType ="String" ),
+            @ApiImplicitParam(name = "runMode", value = "RUN_MODE",dataType ="RunMode" ),
+            @ApiImplicitParam(name = "processInstancePriority", value = "PROCESS_INSTANCE_PRIORITY", required = true, dataType = "Priority" ),
+            @ApiImplicitParam(name = "workerGroupId", value = "WORKER_GROUP_ID", dataType = "Int",example = "100"),
+            @ApiImplicitParam(name = "timeout", value = "TIMEOUT", dataType = "Int",example = "100"),
+    })
     @PostMapping(value = "start-process-instance")
     @ResponseStatus(HttpStatus.OK)
-    public Result startProcessInstance(@RequestAttribute(value = Constants.SESSION_USER) User loginUser,
-                                       @PathVariable String projectName,
+    public Result startProcessInstance(@ApiIgnore @RequestAttribute(value = Constants.SESSION_USER) User loginUser,
+                                       @ApiParam(name = "projectName", value = "PROJECT_NAME", required = true) @PathVariable String projectName,
                                        @RequestParam(value = "processDefinitionId") int processDefinitionId,
                                        @RequestParam(value = "scheduleTime", required = false) String scheduleTime,
                                        @RequestParam(value = "failureStrategy", required = true) FailureStrategy failureStrategy,
@@ -99,10 +119,15 @@
      * @param processInstanceId
      * @return
      */
+    @ApiOperation(value = "execute", notes= "EXECUTE_ACTION_TO_PROCESS_INSTANCE_NOTES")
+    @ApiImplicitParams({
+            @ApiImplicitParam(name = "processInstanceId", value = "PROCESS_INSTANCE_ID", required = true, dataType = "Int", example = "100"),
+            @ApiImplicitParam(name = "executeType", value = "EXECUTE_TYPE", required = true, dataType = "ExecuteType")
+    })
     @PostMapping(value = "/execute")
     @ResponseStatus(HttpStatus.OK)
-    public Result execute(@RequestAttribute(value = Constants.SESSION_USER) User loginUser,
-                                                  @PathVariable String projectName,
+    public Result execute(@ApiIgnore @RequestAttribute(value = Constants.SESSION_USER) User loginUser,
+                          @ApiParam(name = "projectName", value = "PROJECT_NAME", required = true) @PathVariable String projectName,
                                                   @RequestParam("processInstanceId") Integer processInstanceId,
                                                   @RequestParam("executeType") ExecuteType executeType
     ) {
@@ -124,9 +149,13 @@
      * @param processDefinitionId
      * @return
      */
+    @ApiOperation(value = "startCheckProcessDefinition", notes= "START_CHECK_PROCESS_DEFINITION_NOTES")
+    @ApiImplicitParams({
+            @ApiImplicitParam(name = "processDefinitionId", value = "PROCESS_DEFINITION_ID", required = true, dataType = "Int", example = "100")
+    })
     @PostMapping(value = "/start-check")
     @ResponseStatus(HttpStatus.OK)
-    public Result startCheckProcessDefinition(@RequestAttribute(value = Constants.SESSION_USER) User loginUser,
+    public Result startCheckProcessDefinition(@ApiIgnore @RequestAttribute(value = Constants.SESSION_USER) User loginUser,
                              @RequestParam(value = "processDefinitionId") int processDefinitionId) {
         logger.info("login user {}, check process definition", loginUser.getUserName(), processDefinitionId);
         try {
@@ -146,9 +175,16 @@
      * @param processDefinitionId
      * @return
      */
+    @ApiIgnore
+    @ApiOperation(value = "getReceiverCc", notes= "GET_RECEIVER_CC_NOTES")
+    @ApiImplicitParams({
+            @ApiImplicitParam(name = "processDefinitionId", value = "PROCESS_DEFINITION_ID", required = true, dataType = "Int", example = "100"),
+            @ApiImplicitParam(name = "processInstanceId", value = "PROCESS_INSTANCE_ID", required = true, dataType = "Int", example = "100")
+
+    })
     @GetMapping(value = "/get-receiver-cc")
     @ResponseStatus(HttpStatus.OK)
-    public Result getReceiverCc(@RequestAttribute(value = Constants.SESSION_USER) User loginUser,
+    public Result getReceiverCc(@ApiIgnore @RequestAttribute(value = Constants.SESSION_USER) User loginUser,
                                 @RequestParam(value = "processDefinitionId",required = false) Integer processDefinitionId,
                                 @RequestParam(value = "processInstanceId",required = false) Integer processInstanceId) {
         logger.info("login user {}, get process definition receiver and cc", loginUser.getUserName());
diff --git a/escheduler-api/src/main/java/cn/escheduler/api/controller/LoggerController.java b/escheduler-api/src/main/java/cn/escheduler/api/controller/LoggerController.java
index 603dced..24d28b5 100644
--- a/escheduler-api/src/main/java/cn/escheduler/api/controller/LoggerController.java
+++ b/escheduler-api/src/main/java/cn/escheduler/api/controller/LoggerController.java
@@ -21,6 +21,10 @@
 import cn.escheduler.api.utils.Constants;
 import cn.escheduler.api.utils.Result;
 import cn.escheduler.dao.model.User;
+import io.swagger.annotations.Api;
+import io.swagger.annotations.ApiImplicitParam;
+import io.swagger.annotations.ApiImplicitParams;
+import io.swagger.annotations.ApiOperation;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 import org.springframework.beans.factory.annotation.Autowired;
@@ -28,6 +32,7 @@
 import org.springframework.http.HttpStatus;
 import org.springframework.http.ResponseEntity;
 import org.springframework.web.bind.annotation.*;
+import springfox.documentation.annotations.ApiIgnore;
 
 import static cn.escheduler.api.enums.Status.DOWNLOAD_TASK_INSTANCE_LOG_FILE_ERROR;
 import static cn.escheduler.api.enums.Status.QUERY_TASK_INSTANCE_LOG_ERROR;
@@ -36,6 +41,7 @@
 /**
  * log controller
  */
+@Api(tags = "LOGGER_TAG", position = 13)
 @RestController
 @RequestMapping("/log")
 public class LoggerController extends BaseController {
@@ -49,9 +55,15 @@
     /**
      * query task log
      */
+    @ApiOperation(value = "queryLog", notes= "QUERY_TASK_INSTANCE_LOG_NOTES")
+    @ApiImplicitParams({
+            @ApiImplicitParam(name = "taskInstId", value = "TASK_ID", dataType = "Int", example = "100"),
+            @ApiImplicitParam(name = "skipLineNum", value = "SKIP_LINE_NUM", dataType ="Int", example = "100"),
+            @ApiImplicitParam(name = "limit", value = "LIMIT", dataType ="Int", example = "100")
+    })
     @GetMapping(value = "/detail")
     @ResponseStatus(HttpStatus.OK)
-    public Result queryLog(@RequestAttribute(value = Constants.SESSION_USER) User loginUser,
+    public Result queryLog(@ApiIgnore @RequestAttribute(value = Constants.SESSION_USER) User loginUser,
                            @RequestParam(value = "taskInstId") int taskInstanceId,
                            @RequestParam(value = "skipLineNum") int skipNum,
                            @RequestParam(value = "limit") int limit) {
@@ -73,9 +85,13 @@
      * @param loginUser
      * @param taskInstanceId
      */
+    @ApiOperation(value = "downloadTaskLog", notes= "DOWNLOAD_TASK_INSTANCE_LOG_NOTES")
+    @ApiImplicitParams({
+            @ApiImplicitParam(name = "taskInstId", value = "TASK_ID",dataType = "Int", example = "100")
+    })
     @GetMapping(value = "/download-log")
     @ResponseBody
-    public ResponseEntity downloadTaskLog(@RequestAttribute(value = Constants.SESSION_USER) User loginUser,
+    public ResponseEntity downloadTaskLog(@ApiIgnore @RequestAttribute(value = Constants.SESSION_USER) User loginUser,
                                           @RequestParam(value = "taskInstId") int taskInstanceId) {
         try {
             byte[] logBytes = loggerService.getLogBytes(taskInstanceId);
diff --git a/escheduler-api/src/main/java/cn/escheduler/api/controller/LoginController.java b/escheduler-api/src/main/java/cn/escheduler/api/controller/LoginController.java
index 60530c5..f5521c6 100644
--- a/escheduler-api/src/main/java/cn/escheduler/api/controller/LoginController.java
+++ b/escheduler-api/src/main/java/cn/escheduler/api/controller/LoginController.java
@@ -23,34 +23,44 @@
 import cn.escheduler.api.utils.Constants;
 import cn.escheduler.api.utils.Result;
 import cn.escheduler.dao.model.User;
+import io.swagger.annotations.*;
 import org.apache.commons.httpclient.HttpStatus;
 import org.apache.commons.lang3.StringUtils;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.context.i18n.LocaleContextHolder;
 import org.springframework.web.bind.annotation.*;
+import springfox.documentation.annotations.ApiIgnore;
 
 import javax.servlet.http.Cookie;
 import javax.servlet.http.HttpServletRequest;
 import javax.servlet.http.HttpServletResponse;
 
+import java.util.Locale;
+
 import static cn.escheduler.api.enums.Status.*;
 
 /**
  * user login controller
+ *
+ * swagger bootstrap ui docs refer : https://doc.xiaominfo.com/guide/enh-func.html
  */
+@Api(tags = "LOGIN_TAG", position = 1)
 @RestController
 @RequestMapping("")
 public class LoginController extends BaseController {
 
     private static final Logger logger = LoggerFactory.getLogger(LoginController.class);
 
+
     @Autowired
     private SessionService sessionService;
 
     @Autowired
     private UsersService userService;
 
+
     /**
      * login
      *
@@ -60,13 +70,17 @@
      * @param response
      * @return
      */
-    @RequestMapping(value = "/login")
+    @ApiOperation(value = "login", notes= "LOGIN_NOTES")
+    @ApiImplicitParams({
+            @ApiImplicitParam(name = "userName", value = "USER_NAME", required = true, dataType = "String"),
+            @ApiImplicitParam(name = "userPassword", value = "USER_PASSWORD", required = true, dataType ="String")
+    })
+    @PostMapping(value = "/login")
     public Result login(@RequestParam(value = "userName") String userName,
                         @RequestParam(value = "userPassword") String userPassword,
                         HttpServletRequest request,
                         HttpServletResponse response) {
 
-
         try {
             logger.info("login user name: {} ", userName);
 
@@ -76,7 +90,6 @@
                         Status.USER_NAME_NULL.getMsg());
             }
 
-
             // user ip check
             String ip = getClientIpAddress(request);
             if (StringUtils.isEmpty(ip)) {
@@ -117,8 +130,9 @@
      * @param loginUser
      * @return
      */
+    @ApiOperation(value = "signOut", notes = "SIGNOUT_NOTES")
     @PostMapping(value = "/signOut")
-    public Result signOut(@RequestAttribute(value = Constants.SESSION_USER) User loginUser,
+    public Result signOut(@ApiIgnore @RequestAttribute(value = Constants.SESSION_USER) User loginUser,
                           HttpServletRequest request) {
 
         try {
diff --git a/escheduler-api/src/main/java/cn/escheduler/api/controller/MonitorController.java b/escheduler-api/src/main/java/cn/escheduler/api/controller/MonitorController.java
index 602dd4f..cba39d5 100644
--- a/escheduler-api/src/main/java/cn/escheduler/api/controller/MonitorController.java
+++ b/escheduler-api/src/main/java/cn/escheduler/api/controller/MonitorController.java
@@ -22,11 +22,16 @@
 import cn.escheduler.api.utils.Constants;
 import cn.escheduler.api.utils.Result;
 import cn.escheduler.dao.model.User;
+import io.swagger.annotations.Api;
+import io.swagger.annotations.ApiImplicitParam;
+import io.swagger.annotations.ApiImplicitParams;
+import io.swagger.annotations.ApiOperation;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.http.HttpStatus;
 import org.springframework.web.bind.annotation.*;
+import springfox.documentation.annotations.ApiIgnore;
 
 import java.util.Map;
 
@@ -36,6 +41,7 @@
 /**
  * monitor controller
  */
+@Api(tags = "MONITOR_TAG", position = 1)
 @RestController
 @RequestMapping("/monitor")
 public class MonitorController extends BaseController{
@@ -53,13 +59,14 @@
      * @param loginUser
      * @return
      */
+    @ApiOperation(value = "listMaster", notes= "MASTER_LIST_NOTES")
     @GetMapping(value = "/master/list")
     @ResponseStatus(HttpStatus.OK)
-    public Result listMaster(@RequestAttribute(value = Constants.SESSION_USER) User loginUser) {
+    public Result listMaster(@ApiIgnore @RequestAttribute(value = Constants.SESSION_USER) User loginUser) {
         logger.info("login user: {}, query all master", loginUser.getUserName());
         try{
             logger.info("list master, user:{}", loginUser.getUserName());
-            Map<String, Object> result = serverService.queryMaster(loginUser);
+            Map<String, Object> result = monitorService.queryMaster(loginUser);
             return returnDataList(result);
         }catch (Exception e){
             logger.error(LIST_MASTERS_ERROR.getMsg(),e);
@@ -73,12 +80,13 @@
      * @param loginUser
      * @return
      */
+    @ApiOperation(value = "listWorker", notes= "WORKER_LIST_NOTES")
     @GetMapping(value = "/worker/list")
     @ResponseStatus(HttpStatus.OK)
-    public Result listWorker(@RequestAttribute(value = Constants.SESSION_USER) User loginUser) {
+    public Result listWorker(@ApiIgnore @RequestAttribute(value = Constants.SESSION_USER) User loginUser) {
         logger.info("login user: {}, query all workers", loginUser.getUserName());
         try{
-            Map<String, Object> result = serverService.queryWorker(loginUser);
+            Map<String, Object> result = monitorService.queryWorker(loginUser);
             return returnDataList(result);
         }catch (Exception e){
             logger.error(LIST_WORKERS_ERROR.getMsg(),e);
@@ -92,9 +100,10 @@
      * @param loginUser
      * @return
      */
+    @ApiOperation(value = "queryDatabaseState", notes= "QUERY_DATABASE_STATE_NOTES")
     @GetMapping(value = "/database")
     @ResponseStatus(HttpStatus.OK)
-    public Result queryDatabaseState(@RequestAttribute(value = Constants.SESSION_USER) User loginUser) {
+    public Result queryDatabaseState(@ApiIgnore @RequestAttribute(value = Constants.SESSION_USER) User loginUser) {
         logger.info("login user: {}, query database state", loginUser.getUserName());
         try{
 
@@ -112,9 +121,10 @@
      * @param loginUser
      * @return
      */
+    @ApiOperation(value = "queryZookeeperState", notes= "QUERY_ZOOKEEPER_STATE_NOTES")
     @GetMapping(value = "/zookeeper/list")
     @ResponseStatus(HttpStatus.OK)
-    public Result queryZookeeperState(@RequestAttribute(value = Constants.SESSION_USER) User loginUser) {
+    public Result queryZookeeperState(@ApiIgnore @RequestAttribute(value = Constants.SESSION_USER) User loginUser) {
         logger.info("login user: {}, query zookeeper state", loginUser.getUserName());
         try{
             Map<String, Object> result = monitorService.queryZookeeperState(loginUser);
diff --git a/escheduler-api/src/main/java/cn/escheduler/api/controller/ProcessDefinitionController.java b/escheduler-api/src/main/java/cn/escheduler/api/controller/ProcessDefinitionController.java
index 8b28925..aa7615f 100644
--- a/escheduler-api/src/main/java/cn/escheduler/api/controller/ProcessDefinitionController.java
+++ b/escheduler-api/src/main/java/cn/escheduler/api/controller/ProcessDefinitionController.java
@@ -22,11 +22,13 @@
 import cn.escheduler.api.utils.Result;
 import cn.escheduler.common.utils.ParameterUtils;
 import cn.escheduler.dao.model.User;
+import io.swagger.annotations.*;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.http.HttpStatus;
 import org.springframework.web.bind.annotation.*;
+import springfox.documentation.annotations.ApiIgnore;
 
 import java.util.Map;
 
@@ -36,6 +38,7 @@
 /**
  * process definition controller
  */
+@Api(tags = "PROCESS_DEFINITION_TAG", position = 2)
 @RestController
 @RequestMapping("projects/{projectName}/process")
 public class ProcessDefinitionController extends BaseController{
@@ -55,28 +58,36 @@
      * @param desc
      * @return
      */
-  @PostMapping(value = "/save")
-  @ResponseStatus(HttpStatus.CREATED)
-  public Result createProcessDefinition(@RequestAttribute(value = Constants.SESSION_USER) User loginUser,
-                                         @PathVariable String projectName,
-                                         @RequestParam(value = "name", required = true) String name,
-                                         @RequestParam(value = "processDefinitionJson", required = true) String json,
-                                         @RequestParam(value = "locations", required = false) String locations,
-                                         @RequestParam(value = "connects", required = false) String connects,
-                                         @RequestParam(value = "desc", required = false) String desc) {
+    @ApiOperation(value = "save", notes= "CREATE_PROCESS_DEFINITION_NOTES")
+    @ApiImplicitParams({
+            @ApiImplicitParam(name = "name", value = "PROCESS_DEFINITION_NAME", required = true, type = "String"),
+            @ApiImplicitParam(name = "processDefinitionJson", value = "PROCESS_DEFINITION_JSON", required = true, type ="String"),
+            @ApiImplicitParam(name = "locations", value = "PROCESS_DEFINITION_LOCATIONS", required = true, type ="String"),
+            @ApiImplicitParam(name = "connects", value = "PROCESS_DEFINITION_CONNECTS", required = true, type ="String"),
+            @ApiImplicitParam(name = "desc", value = "PROCESS_DEFINITION_DESC", required = false, type ="String"),
+    })
+    @PostMapping(value = "/save")
+    @ResponseStatus(HttpStatus.CREATED)
+    public Result createProcessDefinition(@ApiIgnore @RequestAttribute(value = Constants.SESSION_USER) User loginUser,
+                                          @ApiParam(name = "projectName", value = "PROJECT_NAME", required = true) @PathVariable String projectName,
+                                          @RequestParam(value = "name", required = true) String name,
+                                          @RequestParam(value = "processDefinitionJson", required = true) String json,
+                                          @RequestParam(value = "locations", required = true) String locations,
+                                          @RequestParam(value = "connects", required = true) String connects,
+                                          @RequestParam(value = "desc", required = false) String desc) {
 
-    try {
-        logger.info("login user {}, create  process definition, project name: {}, process definition name: {}, " +
-                        "process_definition_json: {}, desc: {} locations:{}, connects:{}",
-                loginUser.getUserName(), projectName, name, json,desc, locations, connects);
-        Map<String, Object> result = processDefinitionService.createProcessDefinition(loginUser, projectName, name, json,
-                desc, locations, connects );
-        return returnDataList(result);
-    }catch (Exception e){
-        logger.error(CREATE_PROCESS_DEFINITION.getMsg(),e);
-        return error(CREATE_PROCESS_DEFINITION.getCode(), CREATE_PROCESS_DEFINITION.getMsg());
+        try {
+            logger.info("login user {}, create  process definition, project name: {}, process definition name: {}, " +
+                            "process_definition_json: {}, desc: {} locations:{}, connects:{}",
+                    loginUser.getUserName(), projectName, name, json, desc, locations, connects);
+            Map<String, Object> result = processDefinitionService.createProcessDefinition(loginUser, projectName, name, json,
+                    desc, locations, connects);
+            return returnDataList(result);
+        } catch (Exception e) {
+            logger.error(CREATE_PROCESS_DEFINITION.getMsg(), e);
+            return error(CREATE_PROCESS_DEFINITION.getCode(), CREATE_PROCESS_DEFINITION.getMsg());
+        }
     }
-  }
 
     /**
      * verify process definition name unique
@@ -86,10 +97,14 @@
      * @param name
      * @return
      */
+    @ApiOperation(value = "verify-name", notes = "VERIFY_PROCCESS_DEFINITION_NAME_NOTES")
+    @ApiImplicitParams({
+            @ApiImplicitParam(name = "name", value = "PROCESS_DEFINITION_NAME", required = true, type = "String")
+    })
     @GetMapping(value = "/verify-name")
     @ResponseStatus(HttpStatus.OK)
-    public Result verifyProccessDefinitionName(@RequestAttribute(value = Constants.SESSION_USER) User loginUser,
-                                                             @PathVariable String projectName,
+    public Result verifyProccessDefinitionName(@ApiIgnore @RequestAttribute(value = Constants.SESSION_USER) User loginUser,
+                                               @ApiParam(name = "projectName", value = "PROJECT_NAME",required = true) @PathVariable String projectName,
                                                              @RequestParam(value = "name", required = true) String name){
         try {
             logger.info("verify process definition name unique, user:{}, project name:{}, process definition name:{}",
@@ -103,7 +118,7 @@
     }
 
     /**
-     * update  process definition
+     * update process definition
      *
      * @param loginUser
      * @param projectName
@@ -113,10 +128,19 @@
      * @param desc
      * @return
      */
+    @ApiOperation(value = "updateProccessDefinition", notes= "UPDATE_PROCCESS_DEFINITION_NOTES")
+    @ApiImplicitParams({
+            @ApiImplicitParam(name = "name", value = "PROCESS_DEFINITION_NAME", required = true, type = "String"),
+            @ApiImplicitParam(name = "id", value = "PROCESS_DEFINITION_ID", required = true, dataType = "Int", example = "100"),
+            @ApiImplicitParam(name = "processDefinitionJson", value = "PROCESS_DEFINITION_JSON", required = true, type ="String"),
+            @ApiImplicitParam(name = "locations", value = "PROCESS_DEFINITION_LOCATIONS", required = true, type ="String"),
+            @ApiImplicitParam(name = "connects", value = "PROCESS_DEFINITION_CONNECTS", required = true, type ="String"),
+            @ApiImplicitParam(name = "desc", value = "PROCESS_DEFINITION_DESC", required = false, type ="String"),
+    })
     @PostMapping(value = "/update")
     @ResponseStatus(HttpStatus.OK)
-    public Result updateProccessDefinition(@RequestAttribute(value = Constants.SESSION_USER) User loginUser,
-                                                         @PathVariable String projectName,
+    public Result updateProccessDefinition(@ApiIgnore @RequestAttribute(value = Constants.SESSION_USER) User loginUser,
+                                           @ApiParam(name = "projectName", value = "PROJECT_NAME",required = true) @PathVariable String projectName,
                                                          @RequestParam(value = "name", required = true) String name,
                                                          @RequestParam(value = "id", required = true) int id,
                                                          @RequestParam(value = "processDefinitionJson", required = true) String processDefinitionJson,
@@ -146,10 +170,16 @@
      * @param releaseState
      * @return
      */
+    @ApiOperation(value = "releaseProccessDefinition", notes= "RELEASE_PROCCESS_DEFINITION_NOTES")
+    @ApiImplicitParams({
+            @ApiImplicitParam(name = "name", value = "PROCESS_DEFINITION_NAME", required = true, type = "String"),
+            @ApiImplicitParam(name = "processId", value = "PROCESS_DEFINITION_ID", required = true, dataType = "Int", example = "100"),
+            @ApiImplicitParam(name = "releaseState", value = "PROCESS_DEFINITION_CONNECTS", required = true, dataType = "Int", example = "100"),
+    })
     @PostMapping(value = "/release")
     @ResponseStatus(HttpStatus.OK)
-    public Result releaseProccessDefinition(@RequestAttribute(value = Constants.SESSION_USER) User loginUser,
-                                                          @PathVariable String projectName,
+    public Result releaseProccessDefinition(@ApiIgnore @RequestAttribute(value = Constants.SESSION_USER) User loginUser,
+                                            @ApiParam(name = "projectName", value = "PROJECT_NAME",required = true) @PathVariable String projectName,
                                                           @RequestParam(value = "processId", required = true) int processId,
                                                           @RequestParam(value = "releaseState", required = true) int releaseState) {
 
@@ -173,10 +203,14 @@
      * @param processId
      * @return
      */
+    @ApiOperation(value = "queryProccessDefinitionById", notes= "QUERY_PROCCESS_DEFINITION_BY_ID_NOTES")
+    @ApiImplicitParams({
+            @ApiImplicitParam(name = "processId", value = "PROCESS_DEFINITION_ID", required = true, dataType = "Int", example = "100")
+    })
     @GetMapping(value="/select-by-id")
     @ResponseStatus(HttpStatus.OK)
-    public Result queryProccessDefinitionById(@RequestAttribute(value = Constants.SESSION_USER) User loginUser,
-                                              @PathVariable String projectName,
+    public Result queryProccessDefinitionById(@ApiIgnore @RequestAttribute(value = Constants.SESSION_USER) User loginUser,
+                                              @ApiParam(name = "projectName", value = "PROJECT_NAME",required = true) @PathVariable String projectName,
                                               @RequestParam("processId") Integer processId
     ){
         try{
@@ -198,10 +232,11 @@
      * @param projectName
      * @return
      */
+    @ApiOperation(value = "queryProccessDefinitionList", notes= "QUERY_PROCCESS_DEFINITION_LIST_NOTES")
     @GetMapping(value="/list")
     @ResponseStatus(HttpStatus.OK)
-    public Result queryProccessDefinitionList(@RequestAttribute(value = Constants.SESSION_USER) User loginUser,
-                                              @PathVariable String projectName
+    public Result queryProccessDefinitionList(@ApiIgnore @RequestAttribute(value = Constants.SESSION_USER) User loginUser,
+                                              @ApiParam(name = "projectName", value = "PROJECT_NAME",required = true) @PathVariable String projectName
     ){
         try{
             logger.info("query proccess definition list, login user:{}, project name:{}",
@@ -222,10 +257,17 @@
      * @param pageSize
      * @return
      */
+    @ApiOperation(value = "queryProcessDefinitionListPaging", notes= "QUERY_PROCCESS_DEFINITION_LIST_PAGING_NOTES")
+    @ApiImplicitParams({
+            @ApiImplicitParam(name = "pageNo", value = "PAGE_NO", required = true, dataType = "Int", example = "100"),
+            @ApiImplicitParam(name = "searchVal", value = "SEARCH_VAL", required = false, type = "String"),
+            @ApiImplicitParam(name = "userId", value = "USER_ID", required = false, dataType = "Int", example = "100"),
+            @ApiImplicitParam(name = "pageSize", value = "PAGE_SIZE", required = true, dataType = "Int", example = "100")
+    })
     @GetMapping(value="/list-paging")
     @ResponseStatus(HttpStatus.OK)
-    public Result queryProcessDefinitionListPaging(@RequestAttribute(value = Constants.SESSION_USER) User loginUser,
-                                                   @PathVariable String projectName,
+    public Result queryProcessDefinitionListPaging(@ApiIgnore @RequestAttribute(value = Constants.SESSION_USER) User loginUser,
+                                                   @ApiParam(name = "projectName", value = "PROJECT_NAME",required = true) @PathVariable String projectName,
                                                    @RequestParam("pageNo") Integer pageNo,
                                                    @RequestParam(value = "searchVal", required = false) String searchVal,
                                                    @RequestParam(value = "userId", required = false, defaultValue = "0") Integer userId,
@@ -254,10 +296,15 @@
      * @param id
      * @return
      */
+    @ApiOperation(value = "viewTree", notes= "VIEW_TREE_NOTES")
+    @ApiImplicitParams({
+            @ApiImplicitParam(name = "processId", value = "PROCESS_DEFINITION_ID", required = true, dataType = "Int", example = "100"),
+            @ApiImplicitParam(name = "limit", value = "LIMIT", required = true, dataType = "Int", example = "100")
+    })
     @GetMapping(value="/view-tree")
     @ResponseStatus(HttpStatus.OK)
-    public Result viewTree(@RequestAttribute(value = Constants.SESSION_USER) User loginUser,
-                                                   @PathVariable String projectName,
+    public Result viewTree(@ApiIgnore @RequestAttribute(value = Constants.SESSION_USER) User loginUser,
+                           @ApiParam(name = "projectName", value = "PROJECT_NAME",required = true) @PathVariable String projectName,
                                                    @RequestParam("processId") Integer id,
                                                    @RequestParam("limit") Integer limit){
         try{
@@ -280,11 +327,15 @@
      * @param processDefinitionId
      * @return
      */
+    @ApiOperation(value = "getNodeListByDefinitionId", notes= "GET_NODE_LIST_BY_DEFINITION_ID_NOTES")
+    @ApiImplicitParams({
+            @ApiImplicitParam(name = "processDefinitionId", value = "PROCESS_DEFINITION_ID", required = true, dataType = "Int", example = "100")
+    })
     @GetMapping(value="gen-task-list")
     @ResponseStatus(HttpStatus.OK)
     public Result getNodeListByDefinitionId(
-            @RequestAttribute(value = Constants.SESSION_USER) User loginUser,
-            @PathVariable String projectName,
+            @ApiIgnore @RequestAttribute(value = Constants.SESSION_USER) User loginUser,
+            @ApiParam(name = "projectName", value = "PROJECT_NAME",required = true) @PathVariable String projectName,
             @RequestParam("processDefinitionId") Integer processDefinitionId){
         try {
             logger.info("query task node name list by definitionId, login user:{}, project name:{}, id : {}",
@@ -307,11 +358,15 @@
      * @param processDefinitionIdList
      * @return
      */
+    @ApiOperation(value = "getNodeListByDefinitionIdList", notes= "GET_NODE_LIST_BY_DEFINITION_ID_NOTES")
+    @ApiImplicitParams({
+            @ApiImplicitParam(name = "processDefinitionIdList", value = "PROCESS_DEFINITION_ID_LIST", required = true, type = "String")
+    })
     @GetMapping(value="get-task-list")
     @ResponseStatus(HttpStatus.OK)
     public Result getNodeListByDefinitionIdList(
-            @RequestAttribute(value = Constants.SESSION_USER) User loginUser,
-            @PathVariable String projectName,
+            @ApiIgnore @RequestAttribute(value = Constants.SESSION_USER) User loginUser,
+            @ApiParam(name = "projectName", value = "PROJECT_NAME",required = true) @PathVariable String projectName,
             @RequestParam("processDefinitionIdList") String processDefinitionIdList){
 
         try {
diff --git a/escheduler-api/src/main/java/cn/escheduler/api/controller/ProcessInstanceController.java b/escheduler-api/src/main/java/cn/escheduler/api/controller/ProcessInstanceController.java
index cea47be..2a08eac 100644
--- a/escheduler-api/src/main/java/cn/escheduler/api/controller/ProcessInstanceController.java
+++ b/escheduler-api/src/main/java/cn/escheduler/api/controller/ProcessInstanceController.java
@@ -26,11 +26,13 @@
 import cn.escheduler.common.queue.TaskQueueFactory;
 import cn.escheduler.common.utils.ParameterUtils;
 import cn.escheduler.dao.model.User;
+import io.swagger.annotations.*;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.http.HttpStatus;
 import org.springframework.web.bind.annotation.*;
+import springfox.documentation.annotations.ApiIgnore;
 
 import java.util.Map;
 
@@ -39,6 +41,7 @@
 /**
  * process instance controller
  */
+@Api(tags = "PROCESS_INSTANCE_TAG", position = 10)
 @RestController
 @RequestMapping("projects/{projectName}/instance")
 public class ProcessInstanceController extends BaseController{
@@ -58,10 +61,21 @@
      * @param pageSize
      * @return
      */
+    @ApiOperation(value = "queryProcessInstanceList", notes= "QUERY_PROCESS_INSTANCE_LIST_NOTES")
+    @ApiImplicitParams({
+            @ApiImplicitParam(name = "processDefinitionId", value = "PROCESS_DEFINITION_ID", dataType = "Int", example = "100"),
+            @ApiImplicitParam(name = "searchVal", value = "SEARCH_VAL", type ="String"),
+            @ApiImplicitParam(name = "stateType", value = "EXECUTION_STATUS", type ="ExecutionStatus"),
+            @ApiImplicitParam(name = "host", value = "HOST", type ="String"),
+            @ApiImplicitParam(name = "startDate", value = "START_DATE", type ="String"),
+            @ApiImplicitParam(name = "endDate", value = "END_DATE", type ="String"),
+            @ApiImplicitParam(name = "pageNo", value = "PAGE_NO", dataType = "Int", example = "100"),
+            @ApiImplicitParam(name = "pageSize", value = "PAGE_SIZE", dataType = "Int", example = "100")
+    })
     @GetMapping(value="list-paging")
     @ResponseStatus(HttpStatus.OK)
-    public Result queryProcessInstanceList(@RequestAttribute(value = Constants.SESSION_USER) User loginUser,
-                                                                   @PathVariable String projectName,
+    public Result queryProcessInstanceList(@ApiIgnore @RequestAttribute(value = Constants.SESSION_USER) User loginUser,
+                                           @ApiParam(name = "projectName", value = "PROJECT_NAME", required = true) @PathVariable String projectName,
                                                                    @RequestParam(value = "processDefinitionId", required = false, defaultValue = "0") Integer processDefinitionId,
                                                                    @RequestParam(value = "searchVal", required = false) String searchVal,
                                                                    @RequestParam(value = "stateType", required = false) ExecutionStatus stateType,
@@ -90,19 +104,23 @@
      *
      * @param loginUser
      * @param projectName
-     * @param workflowId
+     * @param processInstanceId
      * @return
      */
+    @ApiOperation(value = "queryTaskListByProcessId", notes= "QUERY_TASK_LIST_BY_PROCESS_INSTANCE_ID_NOTES")
+    @ApiImplicitParams({
+            @ApiImplicitParam(name = "processInstanceId", value = "PROCESS_INSTANCE_ID", dataType = "Int", example = "100")
+    })
     @GetMapping(value="/task-list-by-process-id")
     @ResponseStatus(HttpStatus.OK)
-    public Result queryTaskListByProcessId(@RequestAttribute(value = Constants.SESSION_USER) User loginUser,
-                                           @PathVariable String projectName,
-                                           @RequestParam("processInstanceId") Integer workflowId
+    public Result queryTaskListByProcessId(@ApiIgnore @RequestAttribute(value = Constants.SESSION_USER) User loginUser,
+                                           @ApiParam(name = "projectName", value = "PROJECT_NAME", required = true) @PathVariable String projectName,
+                                           @RequestParam("processInstanceId") Integer processInstanceId
     ) {
         try{
-            logger.info("query task instance list by process instance id, login user:{}, project name:{}, work instance id:{}",
-                    loginUser.getUserName(), projectName, workflowId);
-            Map<String, Object> result = processInstanceService.queryTaskListByProcessId(loginUser, projectName, workflowId);
+            logger.info("query task instance list by process instance id, login user:{}, project name:{}, process instance id:{}",
+                    loginUser.getUserName(), projectName, processInstanceId);
+            Map<String, Object> result = processInstanceService.queryTaskListByProcessId(loginUser, projectName, processInstanceId);
             return returnDataList(result);
         }catch (Exception e){
             logger.error(QUERY_TASK_LIST_BY_PROCESS_INSTANCE_ID_ERROR.getMsg(),e);
@@ -122,10 +140,20 @@
      * @param flag
      * @return
      */
+    @ApiOperation(value = "updateProcessInstance", notes= "UPDATE_PROCESS_INSTANCE_NOTES")
+    @ApiImplicitParams({
+            @ApiImplicitParam(name = "processInstanceJson", value = "PROCESS_INSTANCE_JSON", type = "String"),
+            @ApiImplicitParam(name = "processInstanceId", value = "PROCESS_INSTANCE_ID", dataType = "Int", example = "100"),
+            @ApiImplicitParam(name = "scheduleTime", value = "SCHEDULE_TIME", type = "String"),
+            @ApiImplicitParam(name = "syncDefine", value = "SYNC_DEFINE", type = "Boolean"),
+            @ApiImplicitParam(name = "locations", value = "PROCESS_INSTANCE_LOCATIONS", type = "String"),
+            @ApiImplicitParam(name = "connects", value = "PROCESS_INSTANCE_CONNECTS", type = "String"),
+            @ApiImplicitParam(name = "flag", value = "RECOVERY_PROCESS_INSTANCE_FLAG", type = "Flag"),
+    })
     @PostMapping(value="/update")
     @ResponseStatus(HttpStatus.OK)
-    public Result updateProcessInstance(@RequestAttribute(value = Constants.SESSION_USER) User loginUser,
-                                        @PathVariable String projectName,
+    public Result updateProcessInstance(@ApiIgnore @RequestAttribute(value = Constants.SESSION_USER) User loginUser,
+                                        @ApiParam(name = "projectName", value = "PROJECT_NAME", required = true) @PathVariable String projectName,
                                         @RequestParam( value = "processInstanceJson", required = false) String processInstanceJson,
                                         @RequestParam( value = "processInstanceId") Integer processInstanceId,
                                         @RequestParam( value = "scheduleTime", required = false) String scheduleTime,
@@ -156,10 +184,14 @@
      * @param processInstanceId
      * @return
      */
+    @ApiOperation(value = "queryProcessInstanceById", notes= "QUERY_PROCESS_INSTANCE_BY_ID_NOTES")
+    @ApiImplicitParams({
+            @ApiImplicitParam(name = "processInstanceId", value = "PROCESS_INSTANCE_ID", dataType = "Int", example = "100")
+    })
     @GetMapping(value="/select-by-id")
     @ResponseStatus(HttpStatus.OK)
-    public Result queryProcessInstanceById(@RequestAttribute(value = Constants.SESSION_USER) User loginUser,
-                                                                     @PathVariable String projectName,
+    public Result queryProcessInstanceById(@ApiIgnore @RequestAttribute(value = Constants.SESSION_USER) User loginUser,
+                                                                     @ApiParam(name = "projectName", value = "PROJECT_NAME", required = true) @PathVariable String projectName,
                                                                      @RequestParam("processInstanceId") Integer processInstanceId
     ){
         try{
@@ -182,10 +214,14 @@
      * @param processInstanceId
      * @return
      */
+    @ApiOperation(value = "deleteProcessInstanceById", notes= "DELETE_PROCESS_INSTANCE_BY_ID_NOTES")
+    @ApiImplicitParams({
+            @ApiImplicitParam(name = "processInstanceId", value = "PROCESS_INSTANCE_ID", dataType = "Int", example = "100")
+    })
     @GetMapping(value="/delete")
     @ResponseStatus(HttpStatus.OK)
-    public Result deleteProcessInstanceById(@RequestAttribute(value = Constants.SESSION_USER) User loginUser,
-                                                                     @PathVariable String projectName,
+    public Result deleteProcessInstanceById(@ApiIgnore @RequestAttribute(value = Constants.SESSION_USER) User loginUser,
+                                                                     @ApiParam(name = "projectName", value = "PROJECT_NAME", required = true) @PathVariable String projectName,
                                                                      @RequestParam("processInstanceId") Integer processInstanceId
     ){
         try{
@@ -209,10 +245,14 @@
      * @param taskId
      * @return
      */
+    @ApiOperation(value = "querySubProcessInstanceByTaskId", notes= "QUERY_SUBPROCESS_INSTANCE_BY_TASK_ID_NOTES")
+    @ApiImplicitParams({
+            @ApiImplicitParam(name = "taskId", value = "TASK_ID", dataType = "Int", example = "100")
+    })
     @GetMapping(value="/select-sub-process")
     @ResponseStatus(HttpStatus.OK)
-    public Result querySubProcessInstanceByTaskId(@RequestAttribute(value = Constants.SESSION_USER) User loginUser,
-                                                  @PathVariable String projectName,
+    public Result querySubProcessInstanceByTaskId(@ApiIgnore @RequestAttribute(value = Constants.SESSION_USER) User loginUser,
+                                                  @ApiParam(name = "projectName", value = "PROJECT_NAME", required = true) @PathVariable String projectName,
                                                   @RequestParam("taskId") Integer taskId){
         try{
             Map<String, Object> result = processInstanceService.querySubProcessInstanceByTaskId(loginUser, projectName, taskId);
@@ -231,10 +271,14 @@
      * @param subId
      * @return
      */
+    @ApiOperation(value = "queryParentInstanceBySubId", notes= "QUERY_PARENT_PROCESS_INSTANCE_BY_SUB_PROCESS_INSTANCE_ID_NOTES")
+    @ApiImplicitParams({
+            @ApiImplicitParam(name = "subId", value = "SUB_PROCESS_INSTANCE_ID", dataType = "Int", example = "100")
+    })
     @GetMapping(value="/select-parent-process")
     @ResponseStatus(HttpStatus.OK)
-    public Result queryParentInstanceBySubId(@RequestAttribute(value = Constants.SESSION_USER) User loginUser,
-                                             @PathVariable String projectName,
+    public Result queryParentInstanceBySubId(@ApiIgnore @RequestAttribute(value = Constants.SESSION_USER) User loginUser,
+                                             @ApiParam(name = "projectName", value = "PROJECT_NAME", required = true) @PathVariable String projectName,
                                              @RequestParam("subId") Integer subId){
         try{
             Map<String, Object> result = processInstanceService.queryParentInstanceBySubId(loginUser, projectName, subId);
@@ -252,9 +296,13 @@
      * @param processInstanceId
      * @return
      */
+    @ApiOperation(value = "viewVariables", notes= "QUERY_PROCESS_INSTANCE_GLOBAL_VARIABLES_AND_LOCAL_VARIABLES_NOTES")
+    @ApiImplicitParams({
+            @ApiImplicitParam(name = "processInstanceId", value = "PROCESS_INSTANCE_ID", dataType = "Int", example = "100")
+    })
     @GetMapping(value="/view-variables")
     @ResponseStatus(HttpStatus.OK)
-    public Result viewVariables(@RequestAttribute(value = Constants.SESSION_USER) User loginUser
+    public Result viewVariables(@ApiIgnore @RequestAttribute(value = Constants.SESSION_USER) User loginUser
             , @RequestParam("processInstanceId") Integer processInstanceId){
         try{
             Map<String, Object> result = processInstanceService.viewVariables(processInstanceId);
@@ -273,10 +321,14 @@
      * @param processInstanceId
      * @return
      */
+    @ApiOperation(value = "vieGanttTree", notes= "VIEW_GANTT_NOTES")
+    @ApiImplicitParams({
+            @ApiImplicitParam(name = "processInstanceId", value = "PROCESS_INSTANCE_ID", dataType = "Int", example = "100")
+    })
     @GetMapping(value="/view-gantt")
     @ResponseStatus(HttpStatus.OK)
-    public Result viewTree(@RequestAttribute(value = Constants.SESSION_USER) User loginUser,
-                                                   @PathVariable String projectName,
+    public Result viewTree(@ApiIgnore @RequestAttribute(value = Constants.SESSION_USER) User loginUser,
+                                                   @ApiParam(name = "projectName", value = "PROJECT_NAME", required = true) @PathVariable String projectName,
                                                    @RequestParam("processInstanceId") Integer processInstanceId){
         try{
             Map<String, Object> result = processInstanceService.viewGantt(processInstanceId);
diff --git a/escheduler-api/src/main/java/cn/escheduler/api/controller/ProjectController.java b/escheduler-api/src/main/java/cn/escheduler/api/controller/ProjectController.java
index 1a4b30c..fbb650c 100644
--- a/escheduler-api/src/main/java/cn/escheduler/api/controller/ProjectController.java
+++ b/escheduler-api/src/main/java/cn/escheduler/api/controller/ProjectController.java
@@ -23,11 +23,16 @@
 import cn.escheduler.api.utils.Result;
 import cn.escheduler.common.utils.ParameterUtils;
 import cn.escheduler.dao.model.User;
+import io.swagger.annotations.Api;
+import io.swagger.annotations.ApiImplicitParam;
+import io.swagger.annotations.ApiImplicitParams;
+import io.swagger.annotations.ApiOperation;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.http.HttpStatus;
 import org.springframework.web.bind.annotation.*;
+import springfox.documentation.annotations.ApiIgnore;
 
 import java.util.Map;
 
@@ -36,6 +41,7 @@
 /**
  * project controller
  */
+@Api(tags = "PROJECT_TAG", position = 1)
 @RestController
 @RequestMapping("projects")
 public class ProjectController extends BaseController {
@@ -53,9 +59,14 @@
      * @param desc
      * @return returns an error if it exists
      */
+    @ApiOperation(value = "createProject", notes= "CREATE_PROJECT_NOTES")
+    @ApiImplicitParams({
+            @ApiImplicitParam(name = "projectName", value = "PROJECT_NAME", dataType ="String"),
+            @ApiImplicitParam(name = "desc", value = "PROJECT_DESC", dataType = "String")
+    })
     @PostMapping(value = "/create")
     @ResponseStatus(HttpStatus.CREATED)
-    public Result createProject(@RequestAttribute(value = Constants.SESSION_USER) User loginUser,
+    public Result createProject(@ApiIgnore @RequestAttribute(value = Constants.SESSION_USER) User loginUser,
                                 @RequestParam("projectName") String projectName,
                                 @RequestParam(value = "desc", required = false) String desc) {
 
@@ -78,9 +89,15 @@
      * @param desc
      * @return
      */
+    @ApiOperation(value = "updateProject", notes= "UPDATE_PROJECT_NOTES")
+    @ApiImplicitParams({
+            @ApiImplicitParam(name = "projectId", value = "PROJECT_ID", dataType ="Int", example = "100"),
+            @ApiImplicitParam(name = "projectName",value = "PROJECT_NAME",dataType = "String"),
+            @ApiImplicitParam(name = "desc", value = "PROJECT_DESC", dataType = "String")
+    })
     @PostMapping(value = "/update")
     @ResponseStatus(HttpStatus.OK)
-    public Result updateProject(@RequestAttribute(value = Constants.SESSION_USER) User loginUser,
+    public Result updateProject(@ApiIgnore @RequestAttribute(value = Constants.SESSION_USER) User loginUser,
                                 @RequestParam("projectId") Integer projectId,
                                 @RequestParam("projectName") String projectName,
                                 @RequestParam(value = "desc", required = false) String desc) {
@@ -101,9 +118,13 @@
      * @param projectId
      * @return
      */
+    @ApiOperation(value = "queryProjectById", notes= "QUERY_PROJECT_BY_ID_NOTES")
+    @ApiImplicitParams({
+            @ApiImplicitParam(name = "projectId", value = "PROJECT_ID", dataType ="Int", example = "100")
+    })
     @GetMapping(value = "/query-by-id")
     @ResponseStatus(HttpStatus.OK)
-    public Result queryProjectById(@RequestAttribute(value = Constants.SESSION_USER) User loginUser,
+    public Result queryProjectById(@ApiIgnore @RequestAttribute(value = Constants.SESSION_USER) User loginUser,
                                    @RequestParam("projectId") Integer projectId) {
         logger.info("login user {}, query project by id: {}", loginUser.getUserName(), projectId);
 
@@ -125,9 +146,15 @@
      * @param pageNo
      * @return
      */
+    @ApiOperation(value = "queryProjectListPaging", notes= "QUERY_PROJECT_LIST_PAGING_NOTES")
+    @ApiImplicitParams({
+            @ApiImplicitParam(name = "searchVal", value = "SEARCH_VAL", dataType ="String"),
+            @ApiImplicitParam(name = "projectId", value = "PAGE_SIZE", dataType ="Int", example = "20"),
+            @ApiImplicitParam(name = "projectId", value = "PAGE_NO", dataType ="Int", example = "1")
+    })
     @GetMapping(value = "/list-paging")
     @ResponseStatus(HttpStatus.OK)
-    public Result queryProjectListPaging(@RequestAttribute(value = Constants.SESSION_USER) User loginUser,
+    public Result queryProjectListPaging(@ApiIgnore @RequestAttribute(value = Constants.SESSION_USER) User loginUser,
                                          @RequestParam(value = "searchVal", required = false) String searchVal,
                                          @RequestParam("pageSize") Integer pageSize,
                                          @RequestParam("pageNo") Integer pageNo
@@ -151,9 +178,13 @@
      * @param projectId
      * @return
      */
+    @ApiOperation(value = "deleteProjectById", notes= "DELETE_PROJECT_BY_ID_NOTES")
+    @ApiImplicitParams({
+            @ApiImplicitParam(name = "projectId", value = "PROJECT_ID", dataType ="Int", example = "100")
+    })
     @GetMapping(value = "/delete")
     @ResponseStatus(HttpStatus.OK)
-    public Result deleteProject(@RequestAttribute(value = Constants.SESSION_USER) User loginUser,
+    public Result deleteProject(@ApiIgnore @RequestAttribute(value = Constants.SESSION_USER) User loginUser,
                                 @RequestParam("projectId") Integer projectId
     ) {
 
@@ -174,9 +205,13 @@
      * @param userId
      * @return
      */
+    @ApiOperation(value = "queryUnauthorizedProject", notes= "QUERY_UNAUTHORIZED_PROJECT_NOTES")
+    @ApiImplicitParams({
+            @ApiImplicitParam(name = "userId", value = "USER_ID", dataType ="Int", example = "100")
+    })
     @GetMapping(value = "/unauth-project")
     @ResponseStatus(HttpStatus.OK)
-    public Result queryUnauthorizedProject(@RequestAttribute(value = Constants.SESSION_USER) User loginUser,
+    public Result queryUnauthorizedProject(@ApiIgnore @RequestAttribute(value = Constants.SESSION_USER) User loginUser,
                                            @RequestParam("userId") Integer userId) {
         try {
             logger.info("login user {}, query unauthorized project by user id: {}.", loginUser.getUserName(), userId);
@@ -196,9 +231,13 @@
      * @param userId
      * @return
      */
+    @ApiOperation(value = "queryAuthorizedProject", notes= "QUERY_AUTHORIZED_PROJECT_NOTES")
+    @ApiImplicitParams({
+            @ApiImplicitParam(name = "userId", value = "USER_ID", dataType ="Int", example = "100")
+    })
     @GetMapping(value = "/authed-project")
     @ResponseStatus(HttpStatus.OK)
-    public Result queryAuthorizedProject(@RequestAttribute(value = Constants.SESSION_USER) User loginUser,
+    public Result queryAuthorizedProject(@ApiIgnore @RequestAttribute(value = Constants.SESSION_USER) User loginUser,
                                          @RequestParam("userId") Integer userId) {
         try {
             logger.info("login user {}, query authorized project by user id: {}.", loginUser.getUserName(), userId);
diff --git a/escheduler-api/src/main/java/cn/escheduler/api/controller/QueueController.java b/escheduler-api/src/main/java/cn/escheduler/api/controller/QueueController.java
index 89ac8f0..d2b7c14 100644
--- a/escheduler-api/src/main/java/cn/escheduler/api/controller/QueueController.java
+++ b/escheduler-api/src/main/java/cn/escheduler/api/controller/QueueController.java
@@ -23,11 +23,16 @@
 import cn.escheduler.api.utils.Result;
 import cn.escheduler.common.utils.ParameterUtils;
 import cn.escheduler.dao.model.User;
+import io.swagger.annotations.Api;
+import io.swagger.annotations.ApiImplicitParam;
+import io.swagger.annotations.ApiImplicitParams;
+import io.swagger.annotations.ApiOperation;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.http.HttpStatus;
 import org.springframework.web.bind.annotation.*;
+import springfox.documentation.annotations.ApiIgnore;
 
 import java.util.Map;
 
@@ -37,6 +42,7 @@
 /**
  * queue controller
  */
+@Api(tags = "QUEUE_TAG", position = 1)
 @RestController
 @RequestMapping("/queue")
 public class QueueController extends BaseController{
@@ -52,9 +58,10 @@
      * @param loginUser
      * @return
      */
+    @ApiOperation(value = "queryList", notes= "QUERY_QUEUE_LIST_NOTES")
     @GetMapping(value="/list")
     @ResponseStatus(HttpStatus.OK)
-    public Result queryList(@RequestAttribute(value = Constants.SESSION_USER) User loginUser){
+    public Result queryList(@ApiIgnore @RequestAttribute(value = Constants.SESSION_USER) User loginUser){
         try{
             logger.info("login user {}, query queue list", loginUser.getUserName());
             Map<String, Object> result = queueService.queryList(loginUser);
@@ -70,9 +77,15 @@
      * @param loginUser
      * @return
      */
+    @ApiOperation(value = "queryQueueListPaging", notes= "QUERY_QUEUE_LIST_PAGING_NOTES")
+    @ApiImplicitParams({
+            @ApiImplicitParam(name = "searchVal", value = "SEARCH_VAL", dataType ="String"),
+            @ApiImplicitParam(name = "pageNo", value = "PAGE_NO", dataType = "Int", example = "1"),
+            @ApiImplicitParam(name = "pageSize", value = "PAGE_SIZE", dataType ="Int",example = "20")
+    })
     @GetMapping(value="/list-paging")
     @ResponseStatus(HttpStatus.OK)
-    public Result queryQueueListPaging(@RequestAttribute(value = Constants.SESSION_USER) User loginUser,
+    public Result queryQueueListPaging(@ApiIgnore @RequestAttribute(value = Constants.SESSION_USER) User loginUser,
                                   @RequestParam("pageNo") Integer pageNo,
                                   @RequestParam(value = "searchVal", required = false) String searchVal,
                                   @RequestParam("pageSize") Integer pageSize){
@@ -100,9 +113,14 @@
      * @param queueName
      * @return
      */
+    @ApiOperation(value = "createQueue", notes= "CREATE_QUEUE_NOTES")
+    @ApiImplicitParams({
+            @ApiImplicitParam(name = "queue", value = "YARN_QUEUE_NAME", required = true,dataType ="String"),
+            @ApiImplicitParam(name = "queueName", value = "QUEUE_NAME",required = true, dataType ="String")
+    })
     @PostMapping(value = "/create")
     @ResponseStatus(HttpStatus.CREATED)
-    public Result createQueue(@RequestAttribute(value = Constants.SESSION_USER) User loginUser,
+    public Result createQueue(@ApiIgnore @RequestAttribute(value = Constants.SESSION_USER) User loginUser,
                                @RequestParam(value = "queue") String queue,
                                @RequestParam(value = "queueName") String queueName) {
         logger.info("login user {}, create queue, queue: {}, queueName: {}",
@@ -125,9 +143,15 @@
      * @param queueName
      * @return
      */
+    @ApiOperation(value = "updateQueue", notes= "UPDATE_QUEUE_NOTES")
+    @ApiImplicitParams({
+            @ApiImplicitParam(name = "id", value = "QUEUE_ID", required = true, dataType ="Int", example = "100"),
+            @ApiImplicitParam(name = "queue", value = "YARN_QUEUE_NAME",required = true, dataType ="String"),
+            @ApiImplicitParam(name = "queueName", value = "QUEUE_NAME",required = true, dataType ="String")
+    })
     @PostMapping(value = "/update")
     @ResponseStatus(HttpStatus.CREATED)
-    public Result updateQueue(@RequestAttribute(value = Constants.SESSION_USER) User loginUser,
+    public Result updateQueue(@ApiIgnore @RequestAttribute(value = Constants.SESSION_USER) User loginUser,
                               @RequestParam(value = "id") int id,
                               @RequestParam(value = "queue") String queue,
                               @RequestParam(value = "queueName") String queueName) {
@@ -151,9 +175,15 @@
      * @param queueName
      * @return
      */
+    @ApiOperation(value = "verifyQueue", notes= "VERIFY_QUEUE_NOTES")
+    @ApiImplicitParams({
+            @ApiImplicitParam(name = "id", value = "QUEUE_ID", required = true, dataType ="Int", example = "100"),
+            @ApiImplicitParam(name = "queue", value = "YARN_QUEUE_NAME",required = true, dataType ="String"),
+            @ApiImplicitParam(name = "queueName", value = "QUEUE_NAME",required = true, dataType ="String")
+    })
     @PostMapping(value = "/verify-queue")
     @ResponseStatus(HttpStatus.OK)
-    public Result verifyQueue(@RequestAttribute(value = Constants.SESSION_USER) User loginUser,
+    public Result verifyQueue(@ApiIgnore @RequestAttribute(value = Constants.SESSION_USER) User loginUser,
                                    @RequestParam(value ="queue") String queue,
                                    @RequestParam(value ="queueName") String queueName
     ) {
diff --git a/escheduler-api/src/main/java/cn/escheduler/api/controller/ResourcesController.java b/escheduler-api/src/main/java/cn/escheduler/api/controller/ResourcesController.java
index 947eace..81d8e49 100644
--- a/escheduler-api/src/main/java/cn/escheduler/api/controller/ResourcesController.java
+++ b/escheduler-api/src/main/java/cn/escheduler/api/controller/ResourcesController.java
@@ -25,6 +25,10 @@
 import cn.escheduler.common.enums.UdfType;
 import cn.escheduler.common.utils.ParameterUtils;
 import cn.escheduler.dao.model.User;
+import io.swagger.annotations.Api;
+import io.swagger.annotations.ApiImplicitParam;
+import io.swagger.annotations.ApiImplicitParams;
+import io.swagger.annotations.ApiOperation;
 import org.apache.commons.lang.StringUtils;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
@@ -35,6 +39,7 @@
 import org.springframework.http.ResponseEntity;
 import org.springframework.web.bind.annotation.*;
 import org.springframework.web.multipart.MultipartFile;
+import springfox.documentation.annotations.ApiIgnore;
 
 import java.util.Map;
 
@@ -43,6 +48,7 @@
 /**
  * resources controller
  */
+@Api(tags = "RESOURCES_TAG", position = 1)
 @RestController
 @RequestMapping("resources")
 public class ResourcesController extends BaseController{
@@ -63,8 +69,15 @@
      * @param desc
      * @param file
      */
+    @ApiOperation(value = "createResource", notes= "CREATE_RESOURCE_NOTES")
+    @ApiImplicitParams({
+            @ApiImplicitParam(name = "type", value = "RESOURCE_TYPE", required = true, dataType ="ResourceType"),
+            @ApiImplicitParam(name = "name", value = "RESOURCE_NAME", required = true, dataType ="String"),
+            @ApiImplicitParam(name = "des", value = "RESOURCE_DESC",  dataType ="String"),
+            @ApiImplicitParam(name = "file", value = "RESOURCE_FILE", required = true, dataType = "MultipartFile")
+    })
     @PostMapping(value = "/create")
-    public Result createResource(@RequestAttribute(value = Constants.SESSION_USER) User loginUser,
+    public Result createResource(@ApiIgnore @RequestAttribute(value = Constants.SESSION_USER) User loginUser,
                                  @RequestParam(value = "type") ResourceType type,
                                  @RequestParam(value ="name")String alias,
                                  @RequestParam(value = "desc", required = false) String desc,
@@ -86,8 +99,16 @@
      * @param alias
      * @param desc
      */
+    @ApiOperation(value = "createResource", notes= "CREATE_RESOURCE_NOTES")
+    @ApiImplicitParams({
+            @ApiImplicitParam(name = "id", value = "RESOURCE_ID", required = true, dataType ="Int", example = "100"),
+            @ApiImplicitParam(name = "type", value = "RESOURCE_TYPE", required = true, dataType ="ResourceType"),
+            @ApiImplicitParam(name = "name", value = "RESOURCE_NAME", required = true, dataType ="String"),
+            @ApiImplicitParam(name = "des", value = "RESOURCE_DESC",  dataType ="String"),
+            @ApiImplicitParam(name = "file", value = "RESOURCE_FILE", required = true,dataType = "MultipartFile")
+    })
     @PostMapping(value = "/update")
-    public Result updateResource(@RequestAttribute(value = Constants.SESSION_USER) User loginUser,
+    public Result updateResource(@ApiIgnore @RequestAttribute(value = Constants.SESSION_USER) User loginUser,
                                  @RequestParam(value ="id") int resourceId,
                                  @RequestParam(value = "type") ResourceType type,
                                  @RequestParam(value ="name")String alias,
@@ -108,10 +129,14 @@
      * @param loginUser
      * @return
      */
+    @ApiOperation(value = "querytResourceList", notes= "QUERY_RESOURCE_LIST_NOTES")
+    @ApiImplicitParams({
+            @ApiImplicitParam(name = "type", value = "RESOURCE_TYPE", required = true, dataType ="ResourceType")
+    })
     @GetMapping(value="/list")
     @ResponseStatus(HttpStatus.OK)
-    public Result querytResourceList(@RequestAttribute(value = Constants.SESSION_USER) User loginUser,
-                                     @RequestParam(value ="type")ResourceType type
+    public Result querytResourceList(@ApiIgnore @RequestAttribute(value = Constants.SESSION_USER) User loginUser,
+                                     @RequestParam(value ="type") ResourceType type
     ){
         try{
             logger.info("query resource list, login user:{}, resource type:{}", loginUser.getUserName(), type.toString());
@@ -131,10 +156,17 @@
      * @param pageSize
      * @return
      */
+    @ApiOperation(value = "querytResourceListPaging", notes= "QUERY_RESOURCE_LIST_PAGING_NOTES")
+    @ApiImplicitParams({
+            @ApiImplicitParam(name = "type", value = "RESOURCE_TYPE", required = true, dataType ="ResourceType"),
+            @ApiImplicitParam(name = "searchVal", value = "SEARCH_VAL", dataType ="String"),
+            @ApiImplicitParam(name = "pageNo", value = "PAGE_NO", dataType = "Int", example = "1"),
+            @ApiImplicitParam(name = "pageSize", value = "PAGE_SIZE", dataType ="Int",example = "20")
+    })
     @GetMapping(value="/list-paging")
     @ResponseStatus(HttpStatus.OK)
-    public Result querytResourceListPaging(@RequestAttribute(value = Constants.SESSION_USER) User loginUser,
-                                     @RequestParam(value ="type")ResourceType type,
+    public Result querytResourceListPaging(@ApiIgnore @RequestAttribute(value = Constants.SESSION_USER) User loginUser,
+                                     @RequestParam(value ="type") ResourceType type,
                                      @RequestParam("pageNo") Integer pageNo,
                                      @RequestParam(value = "searchVal", required = false) String searchVal,
                                      @RequestParam("pageSize") Integer pageSize
@@ -163,9 +195,13 @@
      * @param loginUser
      * @param resourceId
      */
+    @ApiOperation(value = "deleteResource", notes= "DELETE_RESOURCE_BY_ID_NOTES")
+    @ApiImplicitParams({
+            @ApiImplicitParam(name = "id", value = "RESOURCE_ID", required = true, dataType ="Int", example = "100")
+    })
     @GetMapping(value = "/delete")
     @ResponseStatus(HttpStatus.OK)
-    public Result deleteResource(@RequestAttribute(value = Constants.SESSION_USER) User loginUser,
+    public Result deleteResource(@ApiIgnore @RequestAttribute(value = Constants.SESSION_USER) User loginUser,
                                  @RequestParam(value ="id") int resourceId
     ) {
         try{
@@ -187,11 +223,16 @@
      * @param type
      * @return
      */
+    @ApiOperation(value = "verifyResourceName", notes= "VERIFY_RESOURCE_NAME_NOTES")
+    @ApiImplicitParams({
+            @ApiImplicitParam(name = "type", value = "RESOURCE_TYPE", required = true, dataType ="ResourceType"),
+            @ApiImplicitParam(name = "name", value = "RESOURCE_NAME", required = true, dataType ="String")
+    })
     @GetMapping(value = "/verify-name")
     @ResponseStatus(HttpStatus.OK)
-    public Result verifyResourceName(@RequestAttribute(value = Constants.SESSION_USER) User loginUser,
+    public Result verifyResourceName(@ApiIgnore @RequestAttribute(value = Constants.SESSION_USER) User loginUser,
                                      @RequestParam(value ="name") String alias,
-                                     @RequestParam(value ="type")ResourceType type
+                                     @RequestParam(value ="type") ResourceType type
     ) {
         try {
             logger.info("login user {}, verfiy resource alias: {},resource type: {}",
@@ -210,8 +251,14 @@
      * @param loginUser
      * @param resourceId
      */
+    @ApiOperation(value = "viewResource", notes= "VIEW_RESOURCE_BY_ID_NOTES")
+    @ApiImplicitParams({
+            @ApiImplicitParam(name = "id", value = "RESOURCE_ID", required = true, dataType ="Int", example = "100"),
+            @ApiImplicitParam(name = "skipLineNum", value = "SKIP_LINE_NUM", required = true, dataType ="Int", example = "100"),
+            @ApiImplicitParam(name = "limit", value = "LIMIT", required = true, dataType ="Int", example = "100")
+    })
     @GetMapping(value = "/view")
-    public Result viewResource(@RequestAttribute(value = Constants.SESSION_USER) User loginUser,
+    public Result viewResource(@ApiIgnore @RequestAttribute(value = Constants.SESSION_USER) User loginUser,
                                @RequestParam(value = "id") int resourceId,
                                @RequestParam(value = "skipLineNum") int skipLineNum,
                                @RequestParam(value = "limit") int limit
@@ -238,8 +285,16 @@
      * @param content
      * @return
      */
+    @ApiOperation(value = "onlineCreateResource", notes= "ONLINE_CREATE_RESOURCE_NOTES")
+    @ApiImplicitParams({
+            @ApiImplicitParam(name = "type", value = "RESOURCE_TYPE", required = true, dataType ="ResourceType"),
+            @ApiImplicitParam(name = "fileName", value = "RESOURCE_NAME",required = true,  dataType ="String"),
+            @ApiImplicitParam(name = "suffix", value = "SUFFIX", required = true, dataType ="String"),
+            @ApiImplicitParam(name = "des", value = "RESOURCE_DESC",  dataType ="String"),
+            @ApiImplicitParam(name = "content", value = "CONTENT",required = true,  dataType ="String")
+    })
     @PostMapping(value = "/online-create")
-    public Result onlineCreateResource(@RequestAttribute(value = Constants.SESSION_USER) User loginUser,
+    public Result onlineCreateResource(@ApiIgnore @RequestAttribute(value = Constants.SESSION_USER) User loginUser,
                                        @RequestParam(value = "type") ResourceType type,
                                        @RequestParam(value ="fileName")String fileName,
                                        @RequestParam(value ="suffix")String fileSuffix,
@@ -266,8 +321,13 @@
      * @param loginUser
      * @param resourceId
      */
+    @ApiOperation(value = "updateResourceContent", notes= "UPDATE_RESOURCE_NOTES")
+    @ApiImplicitParams({
+            @ApiImplicitParam(name = "id", value = "RESOURCE_ID", required = true, dataType ="Int", example = "100"),
+            @ApiImplicitParam(name = "content", value = "CONTENT",required = true,  dataType ="String")
+    })
     @PostMapping(value = "/update-content")
-    public Result updateResourceContent(@RequestAttribute(value = Constants.SESSION_USER) User loginUser,
+    public Result updateResourceContent(@ApiIgnore @RequestAttribute(value = Constants.SESSION_USER) User loginUser,
                                         @RequestParam(value = "id") int resourceId,
                                         @RequestParam(value = "content") String content
     ) {
@@ -291,9 +351,13 @@
      * @param loginUser
      * @param resourceId
      */
+    @ApiOperation(value = "downloadResource", notes= "DOWNLOAD_RESOURCE_NOTES")
+    @ApiImplicitParams({
+            @ApiImplicitParam(name = "id", value = "RESOURCE_ID", required = true, dataType ="Int", example = "100")
+    })
     @GetMapping(value = "/download")
     @ResponseBody
-    public ResponseEntity downloadResource(@RequestAttribute(value = Constants.SESSION_USER) User loginUser,
+    public ResponseEntity downloadResource(@ApiIgnore @RequestAttribute(value = Constants.SESSION_USER) User loginUser,
                                            @RequestParam(value = "id") int resourceId) {
         try{
             logger.info("login user {}, download resource : {}",
@@ -324,9 +388,20 @@
      * @param resourceId
      * @return
      */
+    @ApiOperation(value = "createUdfFunc", notes= "CREATE_UDF_FUNCTION_NOTES")
+    @ApiImplicitParams({
+            @ApiImplicitParam(name = "type", value = "UDF_TYPE", required = true, dataType ="UdfType"),
+            @ApiImplicitParam(name = "funcName", value = "FUNC_NAME",required = true,  dataType ="String"),
+            @ApiImplicitParam(name = "suffix", value = "CLASS_NAME", required = true, dataType ="String"),
+            @ApiImplicitParam(name = "argTypes", value = "ARG_TYPES",  dataType ="String"),
+            @ApiImplicitParam(name = "database", value = "DATABASE_NAME",  dataType ="String"),
+            @ApiImplicitParam(name = "desc", value = "UDF_DESC", dataType ="String"),
+            @ApiImplicitParam(name = "resourceId", value = "RESOURCE_ID", required = true, dataType ="Int", example = "100")
+
+    })
     @PostMapping(value = "/udf-func/create")
     @ResponseStatus(HttpStatus.CREATED)
-    public Result createUdfFunc(@RequestAttribute(value = Constants.SESSION_USER) User loginUser,
+    public Result createUdfFunc(@ApiIgnore @RequestAttribute(value = Constants.SESSION_USER) User loginUser,
                                 @RequestParam(value = "type") UdfType type,
                                 @RequestParam(value ="funcName")String funcName,
                                 @RequestParam(value ="className")String className,
@@ -353,9 +428,14 @@
      * @param id
      * @return
      */
+    @ApiOperation(value = "viewUIUdfFunction", notes= "VIEW_UDF_FUNCTION_NOTES")
+    @ApiImplicitParams({
+            @ApiImplicitParam(name = "resourceId", value = "RESOURCE_ID", required = true, dataType ="Int", example = "100")
+
+    })
     @GetMapping(value = "/udf-func/update-ui")
     @ResponseStatus(HttpStatus.OK)
-    public Result updateUIUdfFunction(@RequestAttribute(value = Constants.SESSION_USER) User loginUser,
+    public Result viewUIUdfFunction(@ApiIgnore @RequestAttribute(value = Constants.SESSION_USER) User loginUser,
                                       @RequestParam("id") int id)
     {
         Result result = new Result();
@@ -382,8 +462,19 @@
      * @param resourceId
      * @return
      */
+    @ApiOperation(value = "updateUdfFunc", notes= "UPDATE_UDF_FUNCTION_NOTES")
+    @ApiImplicitParams({
+            @ApiImplicitParam(name = "type", value = "UDF_TYPE", required = true, dataType ="UdfType"),
+            @ApiImplicitParam(name = "funcName", value = "FUNC_NAME",required = true,  dataType ="String"),
+            @ApiImplicitParam(name = "suffix", value = "CLASS_NAME", required = true, dataType ="String"),
+            @ApiImplicitParam(name = "argTypes", value = "ARG_TYPES",  dataType ="String"),
+            @ApiImplicitParam(name = "database", value = "DATABASE_NAME",  dataType ="String"),
+            @ApiImplicitParam(name = "desc", value = "UDF_DESC", dataType ="String"),
+            @ApiImplicitParam(name = "id", value = "RESOURCE_ID", required = true, dataType ="Int", example = "100")
+
+    })
     @PostMapping(value = "/udf-func/update")
-    public Result updateUdfFunc(@RequestAttribute(value = Constants.SESSION_USER) User loginUser,
+    public Result updateUdfFunc(@ApiIgnore @RequestAttribute(value = Constants.SESSION_USER) User loginUser,
                                 @RequestParam(value = "id") int udfFuncId,
                                 @RequestParam(value = "type") UdfType type,
                                 @RequestParam(value ="funcName")String funcName,
@@ -411,9 +502,15 @@
      * @param pageSize
      * @return
      */
+    @ApiOperation(value = "queryUdfFuncListPaging", notes= "QUERY_UDF_FUNCTION_LIST_PAGING_NOTES")
+    @ApiImplicitParams({
+            @ApiImplicitParam(name = "searchVal", value = "SEARCH_VAL", dataType ="String"),
+            @ApiImplicitParam(name = "pageNo", value = "PAGE_NO", dataType = "Int", example = "1"),
+            @ApiImplicitParam(name = "pageSize", value = "PAGE_SIZE", dataType ="Int",example = "20")
+    })
     @GetMapping(value="/udf-func/list-paging")
     @ResponseStatus(HttpStatus.OK)
-    public Result queryUdfFuncList(@RequestAttribute(value = Constants.SESSION_USER) User loginUser,
+    public Result queryUdfFuncList(@ApiIgnore @RequestAttribute(value = Constants.SESSION_USER) User loginUser,
                                    @RequestParam("pageNo") Integer pageNo,
                                    @RequestParam(value = "searchVal", required = false) String searchVal,
                                    @RequestParam("pageSize") Integer pageSize
@@ -435,14 +532,18 @@
     }
 
     /**
-     * query data resource by type
+     * query resource list by type
      *
      * @param loginUser
      * @return
      */
+    @ApiOperation(value = "queryResourceList", notes= "QUERY_RESOURCE_LIST_NOTES")
+    @ApiImplicitParams({
+            @ApiImplicitParam(name = "type", value = "UDF_TYPE", required = true, dataType ="UdfType")
+    })
     @GetMapping(value="/udf-func/list")
     @ResponseStatus(HttpStatus.OK)
-    public Result queryResourceList(@RequestAttribute(value = Constants.SESSION_USER) User loginUser,
+    public Result queryResourceList(@ApiIgnore @RequestAttribute(value = Constants.SESSION_USER) User loginUser,
                                     @RequestParam("type") UdfType type){
         try{
             logger.info("query datasource list, user:{}, type:{}", loginUser.getUserName(), type.toString());
@@ -461,9 +562,14 @@
      * @param name
      * @return
      */
+    @ApiOperation(value = "verifyUdfFuncName", notes= "VERIFY_UDF_FUNCTION_NAME_NOTES")
+    @ApiImplicitParams({
+            @ApiImplicitParam(name = "name", value = "FUNC_NAME",required = true,  dataType ="String")
+
+    })
     @GetMapping(value = "/udf-func/verify-name")
     @ResponseStatus(HttpStatus.OK)
-    public Result verifyUdfFuncName(@RequestAttribute(value = Constants.SESSION_USER) User loginUser,
+    public Result verifyUdfFuncName(@ApiIgnore @RequestAttribute(value = Constants.SESSION_USER) User loginUser,
                                     @RequestParam(value ="name") String name
     ) {
         logger.info("login user {}, verfiy udf function name: {}",
@@ -484,9 +590,13 @@
      * @param loginUser
      * @param udfFuncId
      */
+    @ApiOperation(value = "deleteUdfFunc", notes= "DELETE_UDF_FUNCTION_NOTES")
+    @ApiImplicitParams({
+            @ApiImplicitParam(name = "id", value = "RESOURCE_ID", required = true, dataType ="Int", example = "100")
+    })
     @GetMapping(value = "/udf-func/delete")
     @ResponseStatus(HttpStatus.OK)
-    public Result deleteUdfFunc(@RequestAttribute(value = Constants.SESSION_USER) User loginUser,
+    public Result deleteUdfFunc(@ApiIgnore @RequestAttribute(value = Constants.SESSION_USER) User loginUser,
                                 @RequestParam(value ="id") int udfFuncId
     ) {
         try{
@@ -506,9 +616,13 @@
      * @param userId
      * @return
      */
+    @ApiOperation(value = "authorizedFile", notes= "AUTHORIZED_FILE_NOTES")
+    @ApiImplicitParams({
+            @ApiImplicitParam(name = "userId", value = "USER_ID", required = true, dataType ="Int", example = "100")
+    })
     @GetMapping(value = "/authed-file")
     @ResponseStatus(HttpStatus.CREATED)
-    public Result authorizedFile(@RequestAttribute(value = Constants.SESSION_USER) User loginUser,
+    public Result authorizedFile(@ApiIgnore @RequestAttribute(value = Constants.SESSION_USER) User loginUser,
                                  @RequestParam("userId") Integer userId) {
         try{
             logger.info("authorized file resource, user: {}, user id:{}", loginUser.getUserName(), userId);
@@ -528,9 +642,13 @@
      * @param userId
      * @return
      */
+    @ApiOperation(value = "unauthorizedFile", notes= "UNAUTHORIZED_FILE_NOTES")
+    @ApiImplicitParams({
+            @ApiImplicitParam(name = "userId", value = "USER_ID", required = true, dataType ="Int", example = "100")
+    })
     @GetMapping(value = "/unauth-file")
     @ResponseStatus(HttpStatus.CREATED)
-    public Result unauthorizedFile(@RequestAttribute(value = Constants.SESSION_USER) User loginUser,
+    public Result unauthorizedFile(@ApiIgnore @RequestAttribute(value = Constants.SESSION_USER) User loginUser,
                                    @RequestParam("userId") Integer userId) {
         try{
             logger.info("resource unauthorized file, user:{}, unauthorized user id:{}", loginUser.getUserName(), userId);
@@ -550,9 +668,13 @@
      * @param userId
      * @return
      */
+    @ApiOperation(value = "unauthUDFFunc", notes= "UNAUTHORIZED_UDF_FUNC_NOTES")
+    @ApiImplicitParams({
+            @ApiImplicitParam(name = "userId", value = "USER_ID", required = true, dataType ="Int", example = "100")
+    })
     @GetMapping(value = "/unauth-udf-func")
     @ResponseStatus(HttpStatus.CREATED)
-    public Result unauthUDFFunc(@RequestAttribute(value = Constants.SESSION_USER) User loginUser,
+    public Result unauthUDFFunc(@ApiIgnore @RequestAttribute(value = Constants.SESSION_USER) User loginUser,
                                 @RequestParam("userId") Integer userId) {
         try{
             logger.info("unauthorized udf function, login user:{}, unauthorized user id:{}", loginUser.getUserName(), userId);
@@ -573,9 +695,13 @@
      * @param userId
      * @return
      */
+    @ApiOperation(value = "authUDFFunc", notes= "AUTHORIZED_UDF_FUNC_NOTES")
+    @ApiImplicitParams({
+            @ApiImplicitParam(name = "userId", value = "USER_ID", required = true, dataType ="Int", example = "100")
+    })
     @GetMapping(value = "/authed-udf-func")
     @ResponseStatus(HttpStatus.CREATED)
-    public Result authorizedUDFFunction(@RequestAttribute(value = Constants.SESSION_USER) User loginUser,
+    public Result authorizedUDFFunction(@ApiIgnore @RequestAttribute(value = Constants.SESSION_USER) User loginUser,
                                         @RequestParam("userId") Integer userId) {
         try{
             logger.info("auth udf function, login user:{}, auth user id:{}", loginUser.getUserName(), userId);
diff --git a/escheduler-api/src/main/java/cn/escheduler/api/controller/SchedulerController.java b/escheduler-api/src/main/java/cn/escheduler/api/controller/SchedulerController.java
index b0b7412..37c1ab8 100644
--- a/escheduler-api/src/main/java/cn/escheduler/api/controller/SchedulerController.java
+++ b/escheduler-api/src/main/java/cn/escheduler/api/controller/SchedulerController.java
@@ -26,11 +26,13 @@
 import cn.escheduler.common.enums.WarningType;
 import cn.escheduler.common.utils.ParameterUtils;
 import cn.escheduler.dao.model.User;
+import io.swagger.annotations.*;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.http.HttpStatus;
 import org.springframework.web.bind.annotation.*;
+import springfox.documentation.annotations.ApiIgnore;
 
 import java.util.Map;
 
@@ -40,9 +42,10 @@
 /**
  * schedule controller
  */
+@Api(tags = "SCHEDULER_TAG", position = 13)
 @RestController
 @RequestMapping("/projects/{projectName}/schedule")
-public class SchedulerController extends BaseController{
+public class SchedulerController extends BaseController {
 
     private static final Logger logger = LoggerFactory.getLogger(SchedulerController.class);
     public static final String DEFAULT_WARNING_TYPE = "NONE";
@@ -66,33 +69,45 @@
      * @param failureStrategy
      * @return
      */
-  @PostMapping("/create")
-  @ResponseStatus(HttpStatus.CREATED)
-  public Result createSchedule(@RequestAttribute(value = SESSION_USER) User loginUser,
-                                                       @PathVariable String projectName,
-                                                       @RequestParam(value = "processDefinitionId") Integer processDefinitionId,
-                                                       @RequestParam(value = "schedule") String schedule,
-                                                       @RequestParam(value = "warningType", required = false,defaultValue = DEFAULT_WARNING_TYPE) WarningType warningType,
-                                                       @RequestParam(value = "warningGroupId", required = false,defaultValue = DEFAULT_NOTIFY_GROUP_ID) int warningGroupId,
-                                                       @RequestParam(value = "failureStrategy", required = false, defaultValue = DEFAULT_FAILURE_POLICY) FailureStrategy failureStrategy,
-                                                       @RequestParam(value = "receivers", required = false) String receivers,
-                                                       @RequestParam(value = "receiversCc", required = false) String receiversCc,
-                                                       @RequestParam(value = "workerGroupId", required = false, defaultValue = "-1") int workerGroupId,
-                                                       @RequestParam(value = "processInstancePriority", required = false) Priority processInstancePriority) {
-    logger.info("login user {}, project name: {}, process name: {}, create schedule: {}, warning type: {}, warning group id: {}," +
-                    "failure policy: {},receivers : {},receiversCc : {},processInstancePriority : {}, workGroupId:{}",
-            loginUser.getUserName(), projectName, processDefinitionId, schedule, warningType, warningGroupId,
-            failureStrategy,receivers,receiversCc,processInstancePriority,workerGroupId);
-      try {
-          Map<String, Object> result = schedulerService.insertSchedule(loginUser, projectName, processDefinitionId, schedule,
-                  warningType, warningGroupId, failureStrategy, receivers,receiversCc,processInstancePriority,workerGroupId);
+    @ApiOperation(value = "createSchedule", notes= "CREATE_SCHEDULE_NOTES")
+    @ApiImplicitParams({
+            @ApiImplicitParam(name = "processDefinitionId", value = "PROCESS_DEFINITION_ID", required = true, dataType = "Int", example = "100"),
+            @ApiImplicitParam(name = "schedule", value = "SCHEDULE", dataType = "Int", example = "100"),
+            @ApiImplicitParam(name = "warningType", value = "WARNING_TYPE", type ="WarningType"),
+            @ApiImplicitParam(name = "warningGroupId", value = "WARNING_GROUP_ID", dataType = "Int", example = "100"),
+            @ApiImplicitParam(name = "failureStrategy", value = "FAILURE_STRATEGY", type ="FailureStrategy"),
+            @ApiImplicitParam(name = "receivers", value = "RECEIVERS", type ="String"),
+            @ApiImplicitParam(name = "receiversCc", value = "RECEIVERS_CC", type ="String"),
+            @ApiImplicitParam(name = "workerGroupId", value = "WORKER_GROUP_ID", dataType = "Int", example = "100"),
+            @ApiImplicitParam(name = "processInstancePriority", value = "PROCESS_INSTANCE_PRIORITY", type ="Priority"),
+    })
+    @PostMapping("/create")
+    @ResponseStatus(HttpStatus.CREATED)
+    public Result createSchedule(@ApiIgnore @RequestAttribute(value = SESSION_USER) User loginUser,
+                                 @ApiParam(name = "projectName", value = "PROJECT_NAME", required = true) @PathVariable String projectName,
+                                 @RequestParam(value = "processDefinitionId") Integer processDefinitionId,
+                                 @RequestParam(value = "schedule") String schedule,
+                                 @RequestParam(value = "warningType", required = false, defaultValue = DEFAULT_WARNING_TYPE) WarningType warningType,
+                                 @RequestParam(value = "warningGroupId", required = false, defaultValue = DEFAULT_NOTIFY_GROUP_ID) int warningGroupId,
+                                 @RequestParam(value = "failureStrategy", required = false, defaultValue = DEFAULT_FAILURE_POLICY) FailureStrategy failureStrategy,
+                                 @RequestParam(value = "receivers", required = false) String receivers,
+                                 @RequestParam(value = "receiversCc", required = false) String receiversCc,
+                                 @RequestParam(value = "workerGroupId", required = false, defaultValue = "-1") int workerGroupId,
+                                 @RequestParam(value = "processInstancePriority", required = false) Priority processInstancePriority) {
+        logger.info("login user {}, project name: {}, process name: {}, create schedule: {}, warning type: {}, warning group id: {}," +
+                        "failure policy: {},receivers : {},receiversCc : {},processInstancePriority : {}, workGroupId:{}",
+                loginUser.getUserName(), projectName, processDefinitionId, schedule, warningType, warningGroupId,
+                failureStrategy, receivers, receiversCc, processInstancePriority, workerGroupId);
+        try {
+            Map<String, Object> result = schedulerService.insertSchedule(loginUser, projectName, processDefinitionId, schedule,
+                    warningType, warningGroupId, failureStrategy, receivers, receiversCc, processInstancePriority, workerGroupId);
 
-          return returnDataList(result);
-      }catch (Exception e){
-          logger.error(CREATE_SCHEDULE_ERROR.getMsg(),e);
-          return error(CREATE_SCHEDULE_ERROR.getCode(), CREATE_SCHEDULE_ERROR.getMsg());
-      }
-  }
+            return returnDataList(result);
+        } catch (Exception e) {
+            logger.error(CREATE_SCHEDULE_ERROR.getMsg(), e);
+            return error(CREATE_SCHEDULE_ERROR.getCode(), CREATE_SCHEDULE_ERROR.getMsg());
+        }
+    }
 
     /**
      * updateProcessInstance schedule
@@ -106,33 +121,45 @@
      * @param failureStrategy
      * @return
      */
-  @PostMapping("/update")
-  public Result updateSchedule(@RequestAttribute(value = SESSION_USER) User loginUser,
-                                                       @PathVariable String projectName,
-                                                       @RequestParam(value = "id") Integer id,
-                                                       @RequestParam(value = "schedule") String schedule,
-                                                       @RequestParam(value = "warningType", required = false, defaultValue = DEFAULT_WARNING_TYPE) WarningType warningType,
-                                                       @RequestParam(value = "warningGroupId", required = false) int warningGroupId,
-                                                       @RequestParam(value = "failureStrategy", required = false, defaultValue = "END") FailureStrategy failureStrategy,
-                                                       @RequestParam(value = "receivers", required = false) String receivers,
-                                                       @RequestParam(value = "receiversCc", required = false) String receiversCc,
-                                                       @RequestParam(value = "workerGroupId", required = false, defaultValue = "-1") int workerGroupId,
-                                                       @RequestParam(value = "processInstancePriority", required = false) Priority processInstancePriority) {
-    logger.info("login user {}, project name: {},id: {}, updateProcessInstance schedule: {}, notify type: {}, notify mails: {}, " +
-                    "failure policy: {},receivers : {},receiversCc : {},processInstancePriority : {},workerGroupId:{}",
-            loginUser.getUserName(), projectName, id, schedule, warningType, warningGroupId, failureStrategy,
-            receivers,receiversCc,processInstancePriority,workerGroupId);
+    @ApiOperation(value = "updateSchedule", notes= "UPDATE_SCHEDULE_NOTES")
+    @ApiImplicitParams({
+            @ApiImplicitParam(name = "id", value = "SCHEDULE_ID", required = true, dataType = "Int", example = "100"),
+            @ApiImplicitParam(name = "schedule", value = "SCHEDULE", dataType = "Int", example = "100"),
+            @ApiImplicitParam(name = "warningType", value = "WARNING_TYPE", type ="WarningType"),
+            @ApiImplicitParam(name = "warningGroupId", value = "WARNING_GROUP_ID", dataType = "Int", example = "100"),
+            @ApiImplicitParam(name = "failureStrategy", value = "FAILURE_STRATEGY", type ="FailureStrategy"),
+            @ApiImplicitParam(name = "receivers", value = "RECEIVERS", type ="String"),
+            @ApiImplicitParam(name = "receiversCc", value = "RECEIVERS_CC", type ="String"),
+            @ApiImplicitParam(name = "workerGroupId", value = "WORKER_GROUP_ID", dataType = "Int", example = "100"),
+            @ApiImplicitParam(name = "processInstancePriority", value = "PROCESS_INSTANCE_PRIORITY", type ="Priority"),
+    })
+    @PostMapping("/update")
+    public Result updateSchedule(@ApiIgnore @RequestAttribute(value = SESSION_USER) User loginUser,
+                                 @ApiParam(name = "projectName", value = "PROJECT_NAME", required = true) @PathVariable String projectName,
+                                 @RequestParam(value = "id") Integer id,
+                                 @RequestParam(value = "schedule") String schedule,
+                                 @RequestParam(value = "warningType", required = false, defaultValue = DEFAULT_WARNING_TYPE) WarningType warningType,
+                                 @RequestParam(value = "warningGroupId", required = false) int warningGroupId,
+                                 @RequestParam(value = "failureStrategy", required = false, defaultValue = "END") FailureStrategy failureStrategy,
+                                 @RequestParam(value = "receivers", required = false) String receivers,
+                                 @RequestParam(value = "receiversCc", required = false) String receiversCc,
+                                 @RequestParam(value = "workerGroupId", required = false, defaultValue = "-1") int workerGroupId,
+                                 @RequestParam(value = "processInstancePriority", required = false) Priority processInstancePriority) {
+        logger.info("login user {}, project name: {},id: {}, updateProcessInstance schedule: {}, notify type: {}, notify mails: {}, " +
+                        "failure policy: {},receivers : {},receiversCc : {},processInstancePriority : {},workerGroupId:{}",
+                loginUser.getUserName(), projectName, id, schedule, warningType, warningGroupId, failureStrategy,
+                receivers, receiversCc, processInstancePriority, workerGroupId);
 
-      try {
-          Map<String, Object> result = schedulerService.updateSchedule(loginUser, projectName, id, schedule,
-                  warningType, warningGroupId, failureStrategy, receivers,receiversCc,null,processInstancePriority, workerGroupId);
-          return returnDataList(result);
+        try {
+            Map<String, Object> result = schedulerService.updateSchedule(loginUser, projectName, id, schedule,
+                    warningType, warningGroupId, failureStrategy, receivers, receiversCc, null, processInstancePriority, workerGroupId);
+            return returnDataList(result);
 
-      }catch (Exception e){
-          logger.error(UPDATE_SCHEDULE_ERROR.getMsg(),e);
-          return error(Status.UPDATE_SCHEDULE_ERROR.getCode(), Status.UPDATE_SCHEDULE_ERROR.getMsg());
-      }
-  }
+        } catch (Exception e) {
+            logger.error(UPDATE_SCHEDULE_ERROR.getMsg(), e);
+            return error(Status.UPDATE_SCHEDULE_ERROR.getCode(), Status.UPDATE_SCHEDULE_ERROR.getMsg());
+        }
+    }
 
     /**
      * publish schedule setScheduleState
@@ -143,21 +170,25 @@
      * @return
      * @throws Exception
      */
-  @PostMapping("/online")
-  public Result online(@RequestAttribute(value = SESSION_USER) User loginUser,
-                                               @PathVariable("projectName") String projectName,
-                                               @RequestParam("id") Integer id)  {
-    logger.info("login user {}, schedule setScheduleState, project name: {}, id: {}",
-            loginUser.getUserName(), projectName, id);
-      try {
-          Map<String, Object> result = schedulerService.setScheduleState(loginUser, projectName, id, ReleaseState.ONLINE);
-          return returnDataList(result);
+    @ApiOperation(value = "online", notes= "ONLINE_SCHEDULE_NOTES")
+    @ApiImplicitParams({
+            @ApiImplicitParam(name = "id", value = "SCHEDULE_ID", required = true, dataType = "Int", example = "100")
+    })
+    @PostMapping("/online")
+    public Result online(@ApiIgnore @RequestAttribute(value = SESSION_USER) User loginUser,
+                         @ApiParam(name = "projectName", value = "PROJECT_NAME", required = true) @PathVariable("projectName") String projectName,
+                         @RequestParam("id") Integer id) {
+        logger.info("login user {}, schedule setScheduleState, project name: {}, id: {}",
+                loginUser.getUserName(), projectName, id);
+        try {
+            Map<String, Object> result = schedulerService.setScheduleState(loginUser, projectName, id, ReleaseState.ONLINE);
+            return returnDataList(result);
 
-      }catch (Exception e){
-          logger.error(PUBLISH_SCHEDULE_ONLINE_ERROR.getMsg(),e);
-          return error(Status.PUBLISH_SCHEDULE_ONLINE_ERROR.getCode(), Status.PUBLISH_SCHEDULE_ONLINE_ERROR.getMsg());
-      }
-  }
+        } catch (Exception e) {
+            logger.error(PUBLISH_SCHEDULE_ONLINE_ERROR.getMsg(), e);
+            return error(Status.PUBLISH_SCHEDULE_ONLINE_ERROR.getCode(), Status.PUBLISH_SCHEDULE_ONLINE_ERROR.getMsg());
+        }
+    }
 
     /**
      * offline schedule
@@ -167,22 +198,26 @@
      * @param id
      * @return
      */
-  @PostMapping("/offline")
-  public Result offline(@RequestAttribute(value = SESSION_USER) User loginUser,
-                                                @PathVariable("projectName") String projectName,
-                                                @RequestParam("id") Integer id)  {
-    logger.info("login user {}, schedule offline, project name: {}, process definition id: {}",
-            loginUser.getUserName(), projectName, id);
+    @ApiOperation(value = "offline", notes= "OFFLINE_SCHEDULE_NOTES")
+    @ApiImplicitParams({
+            @ApiImplicitParam(name = "id", value = "SCHEDULE_ID", required = true, dataType = "Int", example = "100")
+    })
+    @PostMapping("/offline")
+    public Result offline(@ApiIgnore @RequestAttribute(value = SESSION_USER) User loginUser,
+                          @ApiParam(name = "projectName", value = "PROJECT_NAME", required = true) @PathVariable("projectName") String projectName,
+                          @RequestParam("id") Integer id) {
+        logger.info("login user {}, schedule offline, project name: {}, process definition id: {}",
+                loginUser.getUserName(), projectName, id);
 
-      try {
-          Map<String, Object> result = schedulerService.setScheduleState(loginUser, projectName, id, ReleaseState.OFFLINE);
-          return returnDataList(result);
+        try {
+            Map<String, Object> result = schedulerService.setScheduleState(loginUser, projectName, id, ReleaseState.OFFLINE);
+            return returnDataList(result);
 
-      }catch (Exception e){
-          logger.error(OFFLINE_SCHEDULE_ERROR.getMsg(),e);
-          return error(Status.OFFLINE_SCHEDULE_ERROR.getCode(), Status.OFFLINE_SCHEDULE_ERROR.getMsg());
-      }
-  }
+        } catch (Exception e) {
+            logger.error(OFFLINE_SCHEDULE_ERROR.getMsg(), e);
+            return error(Status.OFFLINE_SCHEDULE_ERROR.getCode(), Status.OFFLINE_SCHEDULE_ERROR.getMsg());
+        }
+    }
 
     /**
      * query schedule list paging
@@ -192,13 +227,21 @@
      * @param processDefinitionId
      * @return
      */
+    @ApiOperation(value = "queryScheduleListPaging", notes= "QUERY_SCHEDULE_LIST_PAGING_NOTES")
+    @ApiImplicitParams({
+            @ApiImplicitParam(name = "processDefinitionId", value = "PROCESS_DEFINITION_ID", required = true,dataType = "Int", example = "100"),
+            @ApiImplicitParam(name = "searchVal", value = "SEARCH_VAL",  type = "String"),
+            @ApiImplicitParam(name = "pageNo", value = "PAGE_NO",  dataType = "Int", example = "100"),
+            @ApiImplicitParam(name = "pageSize", value = "PAGE_SIZE",  dataType = "Int", example = "100")
+
+    })
   @GetMapping("/list-paging")
-  public Result querySchedule(@RequestAttribute(value = SESSION_USER) User loginUser,
-                                                      @PathVariable String projectName,
-                                                      @RequestParam Integer processDefinitionId,
-                                                      @RequestParam(value = "searchVal", required = false) String searchVal,
-                                                      @RequestParam("pageNo") Integer pageNo,
-                                                      @RequestParam("pageSize") Integer pageSize) {
+    public Result queryScheduleListPaging(@ApiIgnore @RequestAttribute(value = SESSION_USER) User loginUser,
+                                          @ApiParam(name = "projectName", value = "PROJECT_NAME", required = true) @PathVariable String projectName,
+                                          @RequestParam Integer processDefinitionId,
+                                          @RequestParam(value = "searchVal", required = false) String searchVal,
+                                          @RequestParam("pageNo") Integer pageNo,
+                                          @RequestParam("pageSize") Integer pageSize) {
     logger.info("login user {}, query schedule, project name: {}, process definition id: {}",
             loginUser.getUserName(), projectName, processDefinitionId);
       try {
@@ -210,28 +253,7 @@
           return error(Status.QUERY_SCHEDULE_LIST_PAGING_ERROR.getCode(), Status.QUERY_SCHEDULE_LIST_PAGING_ERROR.getMsg());
       }
 
-  }
-
-  /**
-   * query schedule list
-   *
-   * @param loginUser
-   * @param projectName
-   * @return
-   */
-  @PostMapping("/list")
-  public Result queryScheduleList(@RequestAttribute(value = SESSION_USER) User loginUser,
-                                  @PathVariable String projectName) {
-      try{
-          logger.info("login user {}, query schedule list, project name: {}",
-                  loginUser.getUserName(), projectName);
-          Map<String, Object> result = schedulerService.queryScheduleList(loginUser, projectName);
-          return returnDataList(result);
-      }catch (Exception e){
-          logger.error(QUERY_SCHEDULE_LIST_ERROR.getMsg(),e);
-          return error(Status.QUERY_SCHEDULE_LIST_ERROR.getCode(), Status.QUERY_SCHEDULE_LIST_ERROR.getMsg());
-      }
-  }
+    }
 
     /**
      * delete schedule by id
@@ -241,6 +263,10 @@
      * @param scheduleId
      * @return
      */
+    @ApiOperation(value = "deleteScheduleById", notes= "OFFLINE_SCHEDULE_NOTES")
+    @ApiImplicitParams({
+            @ApiImplicitParam(name = "scheduleId", value = "SCHEDULE_ID", required = true, dataType = "Int", example = "100")
+    })
     @GetMapping(value="/delete")
     @ResponseStatus(HttpStatus.OK)
     public Result deleteScheduleById(@RequestAttribute(value = SESSION_USER) User loginUser,
@@ -257,4 +283,54 @@
             return error(Status.DELETE_SCHEDULE_CRON_BY_ID_ERROR.getCode(), Status.DELETE_SCHEDULE_CRON_BY_ID_ERROR.getMsg());
         }
     }
+    /**
+     * query schedule list
+     *
+     * @param loginUser
+     * @param projectName
+     * @return
+     */
+    @ApiOperation(value = "queryScheduleList", notes= "QUERY_SCHEDULE_LIST_NOTES")
+    @PostMapping("/list")
+    public Result queryScheduleList(@ApiIgnore @RequestAttribute(value = SESSION_USER) User loginUser,
+                                    @ApiParam(name = "projectName", value = "PROJECT_NAME", required = true) @PathVariable String projectName) {
+        try {
+            logger.info("login user {}, query schedule list, project name: {}",
+                    loginUser.getUserName(), projectName);
+            Map<String, Object> result = schedulerService.queryScheduleList(loginUser, projectName);
+            return returnDataList(result);
+        } catch (Exception e) {
+            logger.error(QUERY_SCHEDULE_LIST_ERROR.getMsg(), e);
+            return error(Status.QUERY_SCHEDULE_LIST_ERROR.getCode(), Status.QUERY_SCHEDULE_LIST_ERROR.getMsg());
+        }
+    }
+
+    /**
+     * preview schedule
+     *
+     * @param loginUser
+     * @param projectName
+     * @param schedule
+     * @return
+     */
+    @ApiOperation(value = "previewSchedule", notes= "PREVIEW_SCHEDULE_NOTES")
+    @ApiImplicitParams({
+            @ApiImplicitParam(name = "schedule", value = "SCHEDULE", dataType = "String", example = "{'startTime':'2019-06-10 00:00:00','endTime':'2019-06-13 00:00:00','crontab':'0 0 3/6 * * ? *'}"),
+    })
+    @PostMapping("/preview")
+    @ResponseStatus(HttpStatus.CREATED)
+    public Result previewSchedule(@ApiIgnore @RequestAttribute(value = SESSION_USER) User loginUser,
+                                 @ApiParam(name = "projectName", value = "PROJECT_NAME", required = true) @PathVariable String projectName,
+                                 @RequestParam(value = "schedule") String schedule
+    ){
+        logger.info("login user {}, project name: {}, preview schedule: {}",
+                loginUser.getUserName(), projectName, schedule);
+        try {
+            Map<String, Object> result = schedulerService.previewSchedule(loginUser, projectName, schedule);
+            return returnDataList(result);
+        } catch (Exception e) {
+            logger.error(PREVIEW_SCHEDULE_ERROR.getMsg(), e);
+            return error(PREVIEW_SCHEDULE_ERROR.getCode(), PREVIEW_SCHEDULE_ERROR.getMsg());
+        }
+    }
 }
diff --git a/escheduler-api/src/main/java/cn/escheduler/api/controller/ServerController.java b/escheduler-api/src/main/java/cn/escheduler/api/controller/ServerController.java
deleted file mode 100644
index 7362e43..0000000
--- a/escheduler-api/src/main/java/cn/escheduler/api/controller/ServerController.java
+++ /dev/null
@@ -1,85 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements.  See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to You under the Apache License, Version 2.0
- * (the "License"); you may not use this file except in compliance with
- * the License.  You may obtain a copy of the License at
- *
- *    http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package cn.escheduler.api.controller;
-
-import cn.escheduler.api.service.ServerService;
-import cn.escheduler.api.utils.Constants;
-import cn.escheduler.api.utils.Result;
-import cn.escheduler.dao.model.User;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-import org.springframework.beans.factory.annotation.Autowired;
-import org.springframework.http.HttpStatus;
-import org.springframework.web.bind.annotation.*;
-
-import java.util.Map;
-
-import static cn.escheduler.api.enums.Status.LIST_MASTERS_ERROR;
-import static cn.escheduler.api.enums.Status.LIST_WORKERS_ERROR;
-
-/**
- * server controller
- */
-@RestController
-@RequestMapping("process")
-public class ServerController extends  BaseController{
-
-    private static final Logger logger = LoggerFactory.getLogger(ExecutorController.class);
-
-    @Autowired
-    private ServerService serverService;
-
-    /**
-     * master list
-     * @param loginUser
-     * @return
-     */
-    @GetMapping(value = "/master/list")
-    @ResponseStatus(HttpStatus.OK)
-    public Result listMaster(@RequestAttribute(value = Constants.SESSION_USER) User loginUser) {
-        logger.info("login user: {}, query all master", loginUser.getUserName());
-        try{
-            logger.info("list master, user:{}", loginUser.getUserName());
-            Map<String, Object> result = serverService.queryMaster(loginUser);
-            return returnDataList(result);
-        }catch (Exception e){
-            logger.error(LIST_MASTERS_ERROR.getMsg(),e);
-            return error(LIST_MASTERS_ERROR.getCode(),
-                    LIST_MASTERS_ERROR.getMsg());
-        }
-    }
-
-
-    /**
-     * worker list
-     * @param loginUser
-     * @return
-     */
-    @GetMapping(value = "/worker/list")
-    @ResponseStatus(HttpStatus.OK)
-    public Result listWorker(@RequestAttribute(value = Constants.SESSION_USER) User loginUser) {
-        logger.info("login user: {}, query all workers", loginUser.getUserName());
-        try{
-            Map<String, Object> result = serverService.queryWorker(loginUser);
-            return returnDataList(result);
-        }catch (Exception e){
-            logger.error(LIST_WORKERS_ERROR.getMsg(),e);
-            return error(LIST_WORKERS_ERROR.getCode(),
-                    LIST_WORKERS_ERROR.getMsg());
-        }
-    }
-}
diff --git a/escheduler-api/src/main/java/cn/escheduler/api/controller/TaskInstanceController.java b/escheduler-api/src/main/java/cn/escheduler/api/controller/TaskInstanceController.java
index 401f5e8..ebe4e86 100644
--- a/escheduler-api/src/main/java/cn/escheduler/api/controller/TaskInstanceController.java
+++ b/escheduler-api/src/main/java/cn/escheduler/api/controller/TaskInstanceController.java
@@ -23,11 +23,13 @@
 import cn.escheduler.common.enums.ExecutionStatus;
 import cn.escheduler.common.utils.ParameterUtils;
 import cn.escheduler.dao.model.User;
+import io.swagger.annotations.*;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.http.HttpStatus;
 import org.springframework.web.bind.annotation.*;
+import springfox.documentation.annotations.ApiIgnore;
 
 import java.util.Map;
 
@@ -36,6 +38,7 @@
 /**
  * task instance controller
  */
+@Api(tags = "TASK_INSTANCE_TAG", position = 11)
 @RestController
 @RequestMapping("/projects/{projectName}/task-instance")
 public class TaskInstanceController extends BaseController{
@@ -52,10 +55,22 @@
      * @param loginUser
      * @return
      */
+    @ApiOperation(value = "queryTaskListPaging", notes= "QUERY_TASK_INSTANCE_LIST_PAGING_NOTES")
+    @ApiImplicitParams({
+            @ApiImplicitParam(name = "processInstanceId", value = "PROCESS_INSTANCE_ID",required = false, dataType = "Int", example = "100"),
+            @ApiImplicitParam(name = "searchVal", value = "SEARCH_VAL", type ="String"),
+            @ApiImplicitParam(name = "taskName", value = "TASK_NAME", type ="String"),
+            @ApiImplicitParam(name = "stateType", value = "EXECUTION_STATUS", type ="ExecutionStatus"),
+            @ApiImplicitParam(name = "host", value = "HOST", type ="String"),
+            @ApiImplicitParam(name = "startDate", value = "START_DATE", type ="String"),
+            @ApiImplicitParam(name = "endDate", value = "END_DATE", type ="String"),
+            @ApiImplicitParam(name = "pageNo", value = "PAGE_NO", dataType = "Int", example = "1"),
+            @ApiImplicitParam(name = "pageSize", value = "PAGE_SIZE", dataType = "Int", example = "20")
+    })
     @GetMapping("/list-paging")
     @ResponseStatus(HttpStatus.OK)
-    public Result queryTaskListPaging(@RequestAttribute(value = Constants.SESSION_USER) User loginUser,
-                                      @PathVariable String projectName,
+    public Result queryTaskListPaging(@ApiIgnore @RequestAttribute(value = Constants.SESSION_USER) User loginUser,
+                                      @ApiParam(name = "projectName", value = "PROJECT_NAME", required = true) @PathVariable String projectName,
                                       @RequestParam(value = "processInstanceId", required = false, defaultValue = "0") Integer processInstanceId,
                                       @RequestParam(value = "searchVal", required = false) String searchVal,
                                       @RequestParam(value = "taskName", required = false) String taskName,
diff --git a/escheduler-api/src/main/java/cn/escheduler/api/controller/TaskRecordController.java b/escheduler-api/src/main/java/cn/escheduler/api/controller/TaskRecordController.java
index 72d7c8d..46afa1a 100644
--- a/escheduler-api/src/main/java/cn/escheduler/api/controller/TaskRecordController.java
+++ b/escheduler-api/src/main/java/cn/escheduler/api/controller/TaskRecordController.java
@@ -21,19 +21,25 @@
 import cn.escheduler.api.utils.Constants;
 import cn.escheduler.api.utils.Result;
 import cn.escheduler.dao.model.User;
+import io.swagger.annotations.Api;
+import io.swagger.annotations.ApiImplicitParam;
+import io.swagger.annotations.ApiImplicitParams;
+import io.swagger.annotations.ApiOperation;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.http.HttpStatus;
 import org.springframework.web.bind.annotation.*;
+import springfox.documentation.annotations.ApiIgnore;
 
 import java.util.Map;
 
 import static cn.escheduler.api.enums.Status.QUERY_TASK_RECORD_LIST_PAGING_ERROR;
 
 /**
- * task record controller
+ * data quality controller
  */
+@ApiIgnore
 @RestController
 @RequestMapping("/projects/task-record")
 public class TaskRecordController extends BaseController{
@@ -53,7 +59,7 @@
      */
     @GetMapping("/list-paging")
     @ResponseStatus(HttpStatus.OK)
-    public Result queryTaskRecordListPaging(@RequestAttribute(value = Constants.SESSION_USER) User loginUser,
+    public Result queryTaskRecordListPaging(@ApiIgnore @RequestAttribute(value = Constants.SESSION_USER) User loginUser,
                                             @RequestParam(value = "taskName", required = false) String taskName,
                                             @RequestParam(value = "state", required = false) String state,
                                             @RequestParam(value = "sourceTable", required = false) String sourceTable,
@@ -85,7 +91,7 @@
      */
     @GetMapping("/history-list-paging")
     @ResponseStatus(HttpStatus.OK)
-    public Result queryHistoryTaskRecordListPaging(@RequestAttribute(value = Constants.SESSION_USER) User loginUser,
+    public Result queryHistoryTaskRecordListPaging(@ApiIgnore @RequestAttribute(value = Constants.SESSION_USER) User loginUser,
                                             @RequestParam(value = "taskName", required = false) String taskName,
                                             @RequestParam(value = "state", required = false) String state,
                                             @RequestParam(value = "sourceTable", required = false) String sourceTable,
diff --git a/escheduler-api/src/main/java/cn/escheduler/api/controller/TenantController.java b/escheduler-api/src/main/java/cn/escheduler/api/controller/TenantController.java
index 46e5b29..1dc74bc 100644
--- a/escheduler-api/src/main/java/cn/escheduler/api/controller/TenantController.java
+++ b/escheduler-api/src/main/java/cn/escheduler/api/controller/TenantController.java
@@ -24,11 +24,16 @@
 import cn.escheduler.common.utils.ParameterUtils;
 import cn.escheduler.dao.model.User;
 import org.apache.commons.lang3.StringUtils;
+import io.swagger.annotations.Api;
+import io.swagger.annotations.ApiImplicitParam;
+import io.swagger.annotations.ApiImplicitParams;
+import io.swagger.annotations.ApiOperation;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.http.HttpStatus;
 import org.springframework.web.bind.annotation.*;
+import springfox.documentation.annotations.ApiIgnore;
 
 import java.util.Map;
 
@@ -38,6 +43,7 @@
 /**
  * tenant controller
  */
+@Api(tags = "TENANT_TAG", position = 1)
 @RestController
 @RequestMapping("/tenant")
 public class TenantController extends BaseController{
@@ -58,9 +64,17 @@
      * @param desc
      * @return
      */
+    @ApiOperation(value = "createTenant", notes= "CREATE_TENANT_NOTES")
+    @ApiImplicitParams({
+            @ApiImplicitParam(name = "tenantCode", value = "TENANT_CODE", required = true, dataType = "String"),
+            @ApiImplicitParam(name = "tenantName", value = "TENANT_NAME", required = true, dataType ="String"),
+            @ApiImplicitParam(name = "queueId", value = "QUEUE_ID", required = true, dataType ="Int",example = "100"),
+            @ApiImplicitParam(name = "desc", value = "TENANT_DESC", dataType ="String")
+
+    })
     @PostMapping(value = "/create")
     @ResponseStatus(HttpStatus.CREATED)
-    public Result createTenant(@RequestAttribute(value = Constants.SESSION_USER) User loginUser,
+    public Result createTenant(@ApiIgnore @RequestAttribute(value = Constants.SESSION_USER) User loginUser,
                                                        @RequestParam(value = "tenantCode") String tenantCode,
                                                        @RequestParam(value = "tenantName") String tenantName,
                                                        @RequestParam(value = "queueId") int queueId,
@@ -87,9 +101,15 @@
      * @param pageSize
      * @return
      */
+    @ApiOperation(value = "queryTenantlistPaging", notes= "QUERY_TENANT_LIST_PAGING_NOTES")
+    @ApiImplicitParams({
+            @ApiImplicitParam(name = "searchVal", value = "SEARCH_VAL", dataType ="String"),
+            @ApiImplicitParam(name = "pageNo", value = "PAGE_NO", dataType = "Int", example = "1"),
+            @ApiImplicitParam(name = "pageSize", value = "PAGE_SIZE", dataType ="Int",example = "20")
+    })
     @GetMapping(value="/list-paging")
     @ResponseStatus(HttpStatus.OK)
-    public Result queryTenantlistPaging(@RequestAttribute(value = Constants.SESSION_USER) User loginUser,
+    public Result queryTenantlistPaging(@ApiIgnore @RequestAttribute(value = Constants.SESSION_USER) User loginUser,
                                                      @RequestParam("pageNo") Integer pageNo,
                                                      @RequestParam(value = "searchVal", required = false) String searchVal,
                                                      @RequestParam("pageSize") Integer pageSize){
@@ -116,9 +136,10 @@
      * @param loginUser
      * @return
      */
+    @ApiOperation(value = "queryTenantlist", notes= "QUERY_TENANT_LIST_NOTES")
     @GetMapping(value="/list")
     @ResponseStatus(HttpStatus.OK)
-    public Result queryTenantlist(@RequestAttribute(value = Constants.SESSION_USER) User loginUser){
+    public Result queryTenantlist(@ApiIgnore @RequestAttribute(value = Constants.SESSION_USER) User loginUser){
         logger.info("login user {}, query tenant list");
         try{
             Map<String, Object> result = tenantService.queryTenantList(loginUser);
@@ -141,9 +162,18 @@
      * @param desc
      * @return
      */
+    @ApiOperation(value = "updateTenant", notes= "UPDATE_TENANT_NOTES")
+    @ApiImplicitParams({
+            @ApiImplicitParam(name = "ID", value = "TENANT_ID", required = true, dataType ="Int", example = "100"),
+            @ApiImplicitParam(name = "tenantCode", value = "TENANT_CODE", required = true, dataType = "String"),
+            @ApiImplicitParam(name = "tenantName", value = "TENANT_NAME", required = true, dataType ="String"),
+            @ApiImplicitParam(name = "queueId", value = "QUEUE_ID", required = true, dataType ="Int", example = "100"),
+            @ApiImplicitParam(name = "desc", value = "TENANT_DESC", type ="String")
+
+    })
     @PostMapping(value = "/update")
     @ResponseStatus(HttpStatus.OK)
-    public Result updateTenant(@RequestAttribute(value = Constants.SESSION_USER) User loginUser,
+    public Result updateTenant(@ApiIgnore @RequestAttribute(value = Constants.SESSION_USER) User loginUser,
                                                        @RequestParam(value = "id") int id,
                                                        @RequestParam(value = "tenantCode") String tenantCode,
                                                        @RequestParam(value = "tenantName") String tenantName,
@@ -167,9 +197,14 @@
      * @param id
      * @return
      */
+    @ApiOperation(value = "deleteTenantById", notes= "DELETE_TENANT_NOTES")
+    @ApiImplicitParams({
+            @ApiImplicitParam(name = "ID", value = "TENANT_ID", required = true, dataType ="Int", example = "100")
+
+    })
     @PostMapping(value = "/delete")
     @ResponseStatus(HttpStatus.OK)
-    public Result deleteTenantById(@RequestAttribute(value = Constants.SESSION_USER) User loginUser,
+    public Result deleteTenantById(@ApiIgnore @RequestAttribute(value = Constants.SESSION_USER) User loginUser,
                                    @RequestParam(value = "id") int id) {
         logger.info("login user {}, delete tenant, tenantCode: {},", loginUser.getUserName(), id);
         try {
@@ -189,9 +224,13 @@
      * @param tenantCode
      * @return
      */
+    @ApiOperation(value = "verifyTenantCode", notes= "VERIFY_TENANT_CODE_NOTES")
+    @ApiImplicitParams({
+            @ApiImplicitParam(name = "tenantCode", value = "TENANT_CODE", required = true, dataType = "String")
+    })
     @GetMapping(value = "/verify-tenant-code")
     @ResponseStatus(HttpStatus.OK)
-    public Result verifyTenantCode(@RequestAttribute(value = Constants.SESSION_USER) User loginUser,
+    public Result verifyTenantCode(@ApiIgnore @RequestAttribute(value = Constants.SESSION_USER) User loginUser,
                                    @RequestParam(value ="tenantCode") String tenantCode
     ) {
 
diff --git a/escheduler-api/src/main/java/cn/escheduler/api/controller/UsersController.java b/escheduler-api/src/main/java/cn/escheduler/api/controller/UsersController.java
index 41a6a63..21c5e6f 100644
--- a/escheduler-api/src/main/java/cn/escheduler/api/controller/UsersController.java
+++ b/escheduler-api/src/main/java/cn/escheduler/api/controller/UsersController.java
@@ -23,11 +23,16 @@
 import cn.escheduler.api.utils.Result;
 import cn.escheduler.common.utils.ParameterUtils;
 import cn.escheduler.dao.model.User;
+import io.swagger.annotations.Api;
+import io.swagger.annotations.ApiImplicitParam;
+import io.swagger.annotations.ApiImplicitParams;
+import io.swagger.annotations.ApiOperation;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.http.HttpStatus;
 import org.springframework.web.bind.annotation.*;
+import springfox.documentation.annotations.ApiIgnore;
 
 import java.util.Map;
 
@@ -37,14 +42,13 @@
 /**
  * user controller
  */
+@Api(tags = "USERS_TAG" , position = 14)
 @RestController
 @RequestMapping("/users")
 public class UsersController extends BaseController{
 
-
     private static final Logger logger = LoggerFactory.getLogger(UsersController.class);
 
-
     @Autowired
     private UsersService usersService;
 
@@ -59,9 +63,18 @@
      * @param phone
      * @return
      */
+    @ApiOperation(value = "createUser", notes= "CREATE_USER_NOTES")
+    @ApiImplicitParams({
+            @ApiImplicitParam(name = "userName", value = "USER_NAME",type = "String"),
+            @ApiImplicitParam(name = "userPassword", value = "USER_PASSWORD", type ="String"),
+            @ApiImplicitParam(name = "tenantId", value = "TENANT_ID", dataType = "Int", example = "100"),
+            @ApiImplicitParam(name = "queue", value = "QUEUE", dataType = "Int", example = "100"),
+            @ApiImplicitParam(name = "email", value = "EMAIL", dataType = "Int", example = "100"),
+            @ApiImplicitParam(name = "phone", value = "PHONE", dataType = "Int", example = "100")
+    })
     @PostMapping(value = "/create")
     @ResponseStatus(HttpStatus.CREATED)
-    public Result createUser(@RequestAttribute(value = Constants.SESSION_USER) User loginUser,
+    public Result createUser(@ApiIgnore @RequestAttribute(value = Constants.SESSION_USER) User loginUser,
                                                      @RequestParam(value = "userName") String userName,
                                                      @RequestParam(value = "userPassword") String userPassword,
                                                      @RequestParam(value = "tenantId") int tenantId,
@@ -89,9 +102,15 @@
      * @param pageSize
      * @return
      */
+    @ApiOperation(value = "queryUserList", notes= "QUERY_USER_LIST_NOTES")
+    @ApiImplicitParams({
+            @ApiImplicitParam(name = "pageNo", value = "PAGE_NO",dataType = "Int", example = "100"),
+            @ApiImplicitParam(name = "pageSize", value = "PAGE_SIZE", type ="String"),
+            @ApiImplicitParam(name = "searchVal", value = "SEARCH_VAL", type ="String")
+    })
     @GetMapping(value="/list-paging")
     @ResponseStatus(HttpStatus.OK)
-    public Result queryUserList(@RequestAttribute(value = Constants.SESSION_USER) User loginUser,
+    public Result queryUserList(@ApiIgnore @RequestAttribute(value = Constants.SESSION_USER) User loginUser,
                                 @RequestParam("pageNo") Integer pageNo,
                                 @RequestParam(value = "searchVal", required = false) String searchVal,
                                 @RequestParam("pageSize") Integer pageSize){
@@ -113,7 +132,7 @@
 
 
     /**
-     * updateProcessInstance user
+     * update user
      *
      * @param loginUser
      * @param id
@@ -124,9 +143,19 @@
      * @param phone
      * @return
      */
+    @ApiOperation(value = "updateUser", notes= "UPDATE_USER_NOTES")
+    @ApiImplicitParams({
+            @ApiImplicitParam(name = "id", value = "USER_ID",dataType = "Int", example = "100"),
+            @ApiImplicitParam(name = "userName", value = "USER_NAME",type = "String"),
+            @ApiImplicitParam(name = "userPassword", value = "USER_PASSWORD", type ="String"),
+            @ApiImplicitParam(name = "tenantId", value = "TENANT_ID", dataType = "Int", example = "100"),
+            @ApiImplicitParam(name = "queue", value = "QUEUE", dataType = "Int", example = "100"),
+            @ApiImplicitParam(name = "email", value = "EMAIL", dataType = "Int", example = "100"),
+            @ApiImplicitParam(name = "phone", value = "PHONE", dataType = "Int", example = "100")
+    })
     @PostMapping(value = "/update")
     @ResponseStatus(HttpStatus.OK)
-    public Result updateUser(@RequestAttribute(value = Constants.SESSION_USER) User loginUser,
+    public Result updateUser(@ApiIgnore @RequestAttribute(value = Constants.SESSION_USER) User loginUser,
                                                      @RequestParam(value = "id") int id,
                                                      @RequestParam(value = "userName") String userName,
                                                      @RequestParam(value = "userPassword") String userPassword,
@@ -151,9 +180,13 @@
      * @param id
      * @return
      */
+    @ApiOperation(value = "delUserById", notes= "DELETE_USER_BY_ID_NOTES")
+    @ApiImplicitParams({
+            @ApiImplicitParam(name = "id", value = "USER_ID",dataType = "Int", example = "100")
+    })
     @PostMapping(value = "/delete")
     @ResponseStatus(HttpStatus.OK)
-    public Result delUserById(@RequestAttribute(value = Constants.SESSION_USER) User loginUser,
+    public Result delUserById(@ApiIgnore @RequestAttribute(value = Constants.SESSION_USER) User loginUser,
                                                       @RequestParam(value = "id") int  id) {
         logger.info("login user {}, delete user, userId: {},", loginUser.getUserName(), id);
         try {
@@ -172,9 +205,14 @@
      * @param userId
      * @return
      */
+    @ApiOperation(value = "grantProject", notes= "GRANT_PROJECT_NOTES")
+    @ApiImplicitParams({
+            @ApiImplicitParam(name = "userId", value = "USER_ID",dataType = "Int", example = "100"),
+            @ApiImplicitParam(name = "projectIds", value = "PROJECT_IDS",type = "String")
+    })
     @PostMapping(value = "/grant-project")
     @ResponseStatus(HttpStatus.OK)
-    public Result grantProject(@RequestAttribute(value = Constants.SESSION_USER) User loginUser,
+    public Result grantProject(@ApiIgnore @RequestAttribute(value = Constants.SESSION_USER) User loginUser,
                                                        @RequestParam(value = "userId") int  userId,
                                                        @RequestParam(value = "projectIds") String projectIds) {
         logger.info("login user {}, grant project, userId: {},projectIds : {}", loginUser.getUserName(), userId,projectIds);
@@ -194,9 +232,14 @@
      * @param userId
      * @return
      */
+    @ApiOperation(value = "grantResource", notes= "GRANT_RESOURCE_NOTES")
+    @ApiImplicitParams({
+            @ApiImplicitParam(name = "userId", value = "USER_ID",dataType = "Int", example = "100"),
+            @ApiImplicitParam(name = "resourceIds", value = "RESOURCE_IDS",type = "String")
+    })
     @PostMapping(value = "/grant-file")
     @ResponseStatus(HttpStatus.OK)
-    public Result grantResource(@RequestAttribute(value = Constants.SESSION_USER) User loginUser,
+    public Result grantResource(@ApiIgnore @RequestAttribute(value = Constants.SESSION_USER) User loginUser,
                                                         @RequestParam(value = "userId") int  userId,
                                                         @RequestParam(value = "resourceIds") String resourceIds) {
         logger.info("login user {}, grant project, userId: {},resourceIds : {}", loginUser.getUserName(), userId,resourceIds);
@@ -217,9 +260,14 @@
      * @param userId
      * @return
      */
+    @ApiOperation(value = "grantUDFFunc", notes= "GRANT_UDF_FUNC_NOTES")
+    @ApiImplicitParams({
+            @ApiImplicitParam(name = "userId", value = "USER_ID",dataType = "Int", example = "100"),
+            @ApiImplicitParam(name = "udfIds", value = "UDF_IDS",type = "String")
+    })
     @PostMapping(value = "/grant-udf-func")
     @ResponseStatus(HttpStatus.OK)
-    public Result grantUDFFunc(@RequestAttribute(value = Constants.SESSION_USER) User loginUser,
+    public Result grantUDFFunc(@ApiIgnore @RequestAttribute(value = Constants.SESSION_USER) User loginUser,
                                                        @RequestParam(value = "userId") int  userId,
                                                        @RequestParam(value = "udfIds") String udfIds) {
         logger.info("login user {}, grant project, userId: {},resourceIds : {}", loginUser.getUserName(), userId,udfIds);
@@ -241,9 +289,14 @@
      * @param userId
      * @return
      */
+    @ApiOperation(value = "grantDataSource", notes= "GRANT_DATASOURCE_NOTES")
+    @ApiImplicitParams({
+            @ApiImplicitParam(name = "userId", value = "USER_ID",dataType = "Int", example = "100"),
+            @ApiImplicitParam(name = "datasourceIds", value = "DATASOURCE_IDS",type = "String")
+    })
     @PostMapping(value = "/grant-datasource")
     @ResponseStatus(HttpStatus.OK)
-    public Result grantDataSource(@RequestAttribute(value = Constants.SESSION_USER) User loginUser,
+    public Result grantDataSource(@ApiIgnore @RequestAttribute(value = Constants.SESSION_USER) User loginUser,
                                                           @RequestParam(value = "userId") int  userId,
                                                           @RequestParam(value = "datasourceIds") String datasourceIds) {
         logger.info("login user {}, grant project, userId: {},projectIds : {}", loginUser.getUserName(),userId,datasourceIds);
@@ -263,9 +316,10 @@
      * @param loginUser
      * @return
      */
+    @ApiOperation(value = "getUserInfo", notes= "GET_USER_INFO_NOTES")
     @GetMapping(value="/get-user-info")
     @ResponseStatus(HttpStatus.OK)
-    public Result getUserInfo(@RequestAttribute(value = Constants.SESSION_USER) User loginUser){
+    public Result getUserInfo(@ApiIgnore @RequestAttribute(value = Constants.SESSION_USER) User loginUser){
         logger.info("login user {},get user info : {}", loginUser.getUserName());
         try{
             Map<String, Object> result = usersService.getUserInfo(loginUser);
@@ -282,9 +336,10 @@
      * @param loginUser
      * @return
      */
+    @ApiOperation(value = "listUser", notes= "LIST_USER_NOTES")
     @GetMapping(value="/list")
     @ResponseStatus(HttpStatus.OK)
-    public Result listUser(@RequestAttribute(value = Constants.SESSION_USER) User loginUser){
+    public Result listUser(@ApiIgnore @RequestAttribute(value = Constants.SESSION_USER) User loginUser){
         logger.info("login user {}, user list");
         try{
             Map<String, Object> result = usersService.queryAllGeneralUsers(loginUser);
@@ -323,9 +378,13 @@
      * @param userName
      * @return
      */
+    @ApiOperation(value = "verifyUserName", notes= "VERIFY_USER_NAME_NOTES")
+    @ApiImplicitParams({
+            @ApiImplicitParam(name = "userName", value = "USER_NAME",type = "String")
+    })
     @GetMapping(value = "/verify-user-name")
     @ResponseStatus(HttpStatus.OK)
-    public Result verifyUserName(@RequestAttribute(value = Constants.SESSION_USER) User loginUser,
+    public Result verifyUserName(@ApiIgnore @RequestAttribute(value = Constants.SESSION_USER) User loginUser,
                                                          @RequestParam(value ="userName") String userName
     ) {
         try{
@@ -347,9 +406,13 @@
      * @param alertgroupId
      * @return
      */
+    @ApiOperation(value = "unauthorizedUser", notes= "UNAUTHORIZED_USER_NOTES")
+    @ApiImplicitParams({
+            @ApiImplicitParam(name = "alertgroupId", value = "ALERT_GROUP_ID",type = "String")
+    })
     @GetMapping(value = "/unauth-user")
     @ResponseStatus(HttpStatus.OK)
-    public Result unauthorizedUser(@RequestAttribute(value = Constants.SESSION_USER) User loginUser,
+    public Result unauthorizedUser(@ApiIgnore @RequestAttribute(value = Constants.SESSION_USER) User loginUser,
                                    @RequestParam("alertgroupId") Integer alertgroupId) {
         try{
             logger.info("unauthorized user, login user:{}, alert group id:{}",
@@ -370,9 +433,13 @@
      * @param alertgroupId
      * @return
      */
+    @ApiOperation(value = "authorizedUser", notes= "AUTHORIZED_USER_NOTES")
+    @ApiImplicitParams({
+            @ApiImplicitParam(name = "alertgroupId", value = "ALERT_GROUP_ID",type = "String")
+    })
     @GetMapping(value = "/authed-user")
     @ResponseStatus(HttpStatus.OK)
-    public Result authorizedUser(@RequestAttribute(value = Constants.SESSION_USER) User loginUser,
+    public Result authorizedUser(@ApiIgnore @RequestAttribute(value = Constants.SESSION_USER) User loginUser,
                                  @RequestParam("alertgroupId") Integer alertgroupId) {
         try{
             logger.info("authorized user , login user:{}, alert group id:{}",
diff --git a/escheduler-api/src/main/java/cn/escheduler/api/controller/WorkerGroupController.java b/escheduler-api/src/main/java/cn/escheduler/api/controller/WorkerGroupController.java
index 93ca2df..68774e7 100644
--- a/escheduler-api/src/main/java/cn/escheduler/api/controller/WorkerGroupController.java
+++ b/escheduler-api/src/main/java/cn/escheduler/api/controller/WorkerGroupController.java
@@ -19,21 +19,28 @@
 
 import cn.escheduler.api.enums.Status;
 import cn.escheduler.api.service.WorkerGroupService;
-import cn.escheduler.api.utils.Constants;
 import cn.escheduler.api.utils.Result;
 import cn.escheduler.common.utils.ParameterUtils;
 import cn.escheduler.dao.model.User;
+import io.swagger.annotations.Api;
+import io.swagger.annotations.ApiImplicitParam;
+import io.swagger.annotations.ApiImplicitParams;
+import io.swagger.annotations.ApiOperation;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.http.HttpStatus;
 import org.springframework.web.bind.annotation.*;
+import springfox.documentation.annotations.ApiIgnore;
 
 import java.util.Map;
 
+import static cn.escheduler.api.utils.Constants.SESSION_USER;
+
 /**
  * worker group controller
  */
+@Api(tags = "WORKER_GROUP_TAG", position = 1)
 @RestController
 @RequestMapping("/worker-group")
 public class WorkerGroupController extends BaseController{
@@ -53,9 +60,15 @@
      * @param ipList
      * @return
      */
+    @ApiOperation(value = "saveWorkerGroup", notes= "CREATE_WORKER_GROUP_NOTES")
+    @ApiImplicitParams({
+            @ApiImplicitParam(name = "id", value = "WORKER_GROUP_ID", dataType = "Int", example = "10", defaultValue = "0"),
+            @ApiImplicitParam(name = "name", value = "WORKER_GROUP_NAME", required = true, dataType ="String"),
+            @ApiImplicitParam(name = "ipList", value = "WORKER_IP_LIST", required = true, dataType ="String")
+    })
     @PostMapping(value = "/save")
     @ResponseStatus(HttpStatus.OK)
-    public Result saveWorkerGroup(@RequestAttribute(value = Constants.SESSION_USER) User loginUser,
+    public Result saveWorkerGroup(@ApiIgnore @RequestAttribute(value = SESSION_USER) User loginUser,
                              @RequestParam(value = "id", required = false, defaultValue = "0") int id,
                              @RequestParam(value = "name") String name,
                              @RequestParam(value = "ipList") String ipList
@@ -80,9 +93,15 @@
      * @param pageSize
      * @return
      */
+    @ApiOperation(value = "queryAllWorkerGroupsPaging", notes= "QUERY_WORKER_GROUP_PAGING_NOTES")
+    @ApiImplicitParams({
+            @ApiImplicitParam(name = "id", value = "WORKER_GROUP_ID", dataType = "Int", example = "10", defaultValue = "0"),
+            @ApiImplicitParam(name = "name", value = "WORKER_GROUP_NAME", required = true, dataType ="String"),
+            @ApiImplicitParam(name = "ipList", value = "WORKER_IP_LIST", required = true, dataType ="String")
+    })
     @GetMapping(value = "/list-paging")
     @ResponseStatus(HttpStatus.OK)
-    public Result queryAllWorkerGroupsPaging(@RequestAttribute(value = Constants.SESSION_USER) User loginUser,
+    public Result queryAllWorkerGroupsPaging(@ApiIgnore @RequestAttribute(value = SESSION_USER) User loginUser,
                                              @RequestParam("pageNo") Integer pageNo,
                                              @RequestParam(value = "searchVal", required = false) String searchVal,
                                              @RequestParam("pageSize") Integer pageSize
@@ -105,9 +124,10 @@
      * @param loginUser
      * @return
      */
+    @ApiOperation(value = "queryAllWorkerGroups", notes= "QUERY_WORKER_GROUP_LIST_NOTES")
     @GetMapping(value = "/all-groups")
     @ResponseStatus(HttpStatus.OK)
-    public Result queryAllWorkerGroups(@RequestAttribute(value = Constants.SESSION_USER) User loginUser
+    public Result queryAllWorkerGroups(@ApiIgnore @RequestAttribute(value = SESSION_USER) User loginUser
     ) {
         logger.info("query all worker group: login user {}",
                 loginUser.getUserName() );
@@ -127,9 +147,14 @@
      * @param id
      * @return
      */
+    @ApiOperation(value = "deleteById", notes= "DELETE_WORKER_GROUP_BY_ID_NOTES")
+    @ApiImplicitParams({
+            @ApiImplicitParam(name = "id", value = "WORKER_GROUP_ID", required = true, dataType = "Int", example = "10"),
+
+    })
     @GetMapping(value = "/delete-by-id")
     @ResponseStatus(HttpStatus.OK)
-    public Result deleteById(@RequestAttribute(value = Constants.SESSION_USER) User loginUser,
+    public Result deleteById(@ApiIgnore @RequestAttribute(value = SESSION_USER) User loginUser,
                                              @RequestParam("id") Integer id
     ) {
         logger.info("delete worker group: login user {}, id:{} ",
diff --git a/escheduler-api/src/main/java/cn/escheduler/api/enums/Status.java b/escheduler-api/src/main/java/cn/escheduler/api/enums/Status.java
index a76a615..4867f3c 100644
--- a/escheduler-api/src/main/java/cn/escheduler/api/enums/Status.java
+++ b/escheduler-api/src/main/java/cn/escheduler/api/enums/Status.java
@@ -161,7 +161,9 @@
     SAVE_ERROR(10136, "save error"),
     DELETE_PROJECT_ERROR_DEFINES_NOT_NULL(10137, "please delete the process definitions in project first!"),
     BATCH_DELETE_PROCESS_INSTANCE_BY_IDS_ERROR(10117,"batch delete process instance by ids {0} error"),
-
+    PREVIEW_SCHEDULE_ERROR(10139,"preview schedule error"),
+    PARSE_TO_CRON_EXPRESSION_ERROR(10140,"parse cron to cron expression error"),
+    SCHEDULE_START_TIME_END_TIME_SAME(10141,"The start time must not be the same as the end"),
 
 
     UDF_FUNCTION_NOT_EXIST(20001, "UDF function not found"),
@@ -232,6 +234,7 @@
 
     QUEUE_COUNT_ERROR(90001,"queue count error"),
 
+    KERBEROS_STARTUP_STATE(100001,"get kerberos startup state error"),
     ;
 
     private int code;
diff --git a/escheduler-api/src/main/java/cn/escheduler/api/service/DataSourceService.java b/escheduler-api/src/main/java/cn/escheduler/api/service/DataSourceService.java
index 36b9b45..2acaef0 100644
--- a/escheduler-api/src/main/java/cn/escheduler/api/service/DataSourceService.java
+++ b/escheduler-api/src/main/java/cn/escheduler/api/service/DataSourceService.java
@@ -21,7 +21,10 @@
 import cn.escheduler.api.utils.PageInfo;
 import cn.escheduler.api.utils.Result;
 import cn.escheduler.common.enums.DbType;
+import cn.escheduler.common.enums.UserType;
 import cn.escheduler.common.job.db.*;
+import cn.escheduler.common.utils.CommonUtils;
+import cn.escheduler.common.utils.JSONUtils;
 import cn.escheduler.dao.mapper.DataSourceMapper;
 import cn.escheduler.dao.mapper.DatasourceUserMapper;
 import cn.escheduler.dao.mapper.ProjectMapper;
@@ -30,6 +33,8 @@
 import cn.escheduler.dao.model.User;
 import com.alibaba.fastjson.JSONObject;
 import com.alibaba.fastjson.TypeReference;
+import org.apache.hadoop.conf.Configuration;
+import org.apache.hadoop.security.UserGroupInformation;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 import org.springframework.beans.factory.annotation.Autowired;
@@ -41,6 +46,8 @@
 import java.sql.SQLException;
 import java.util.*;
 
+import static cn.escheduler.common.utils.PropertyUtils.getString;
+
 /**
  * datasource service
  */
@@ -54,19 +61,16 @@
     public static final String TYPE = "type";
     public static final String HOST = "host";
     public static final String PORT = "port";
+    public static final String PRINCIPAL = "principal";
     public static final String DATABASE = "database";
     public static final String USER_NAME = "userName";
-    public static final String PASSWORD = "password";
+    public static final String PASSWORD = cn.escheduler.common.Constants.PASSWORD;
     public static final String OTHER = "other";
 
-    @Autowired
-    private ProjectMapper projectMapper;
 
     @Autowired
     private DataSourceMapper dataSourceMapper;
 
-    @Autowired
-    private ProjectService projectService;
 
     @Autowired
     private DatasourceUserMapper datasourceUserMapper;
@@ -239,6 +243,7 @@
         map.put(TYPE, dataSourceType);
         map.put(HOST, host);
         map.put(PORT, port);
+        map.put(PRINCIPAL, datasourceForm.getPrincipal());
         map.put(DATABASE, database);
         map.put(USER_NAME, datasourceForm.getUser());
         map.put(PASSWORD, datasourceForm.getPassword());
@@ -284,13 +289,37 @@
      * @return
      */
     private List<DataSource> getDataSources(User loginUser, String searchVal, Integer pageSize, PageInfo pageInfo) {
+        List<DataSource> dataSourceList = null;
         if (isAdmin(loginUser)) {
-            return dataSourceMapper.queryAllDataSourcePaging(searchVal, pageInfo.getStart(), pageSize);
+            dataSourceList = dataSourceMapper.queryAllDataSourcePaging(searchVal, pageInfo.getStart(), pageSize);
+        }else{
+            dataSourceList = dataSourceMapper.queryDataSourcePaging(loginUser.getId(), searchVal,
+                    pageInfo.getStart(), pageSize);
         }
-        return dataSourceMapper.queryDataSourcePaging(loginUser.getId(), searchVal,
-                pageInfo.getStart(), pageSize);
+
+        handlePasswd(dataSourceList);
+
+        return dataSourceList;
     }
 
+
+    /**
+     * handle datasource connection password for safety
+     * @param dataSourceList
+     */
+    private void handlePasswd(List<DataSource> dataSourceList) {
+
+        for (DataSource dataSource : dataSourceList) {
+
+            String connectionParams  = dataSource.getConnectionParams();
+            JSONObject  object = JSONObject.parseObject(connectionParams);
+            object.put(cn.escheduler.common.Constants.PASSWORD, cn.escheduler.common.Constants.XXXXXX);
+            dataSource.setConnectionParams(JSONUtils.toJson(object));
+
+        }
+    }
+
+
     /**
      * get datasource total num
      *
@@ -313,7 +342,14 @@
      */
     public Map<String, Object> queryDataSourceList(User loginUser, Integer type) {
         Map<String, Object> result = new HashMap<>(5);
-        List<DataSource> datasourceList = dataSourceMapper.queryDataSourceByType(loginUser.getId(), type);
+
+        List<DataSource> datasourceList;
+
+        if (isAdmin(loginUser)) {
+            datasourceList = dataSourceMapper.listAllDataSourceByType(type);
+        }else{
+            datasourceList = dataSourceMapper.queryDataSourceByType(loginUser.getId(), type);
+        }
 
         result.put(Constants.DATA_LIST, datasourceList);
         putMsg(result, Status.SUCCESS);
@@ -362,11 +398,21 @@
                     Class.forName(Constants.COM_MYSQL_JDBC_DRIVER);
                     break;
                 case HIVE:
-                    datasource = JSONObject.parseObject(parameter, HiveDataSource.class);
-                    Class.forName(Constants.ORG_APACHE_HIVE_JDBC_HIVE_DRIVER);
-                    break;
                 case SPARK:
-                    datasource = JSONObject.parseObject(parameter, SparkDataSource.class);
+                    if (CommonUtils.getKerberosStartupState())  {
+                            System.setProperty(cn.escheduler.common.Constants.JAVA_SECURITY_KRB5_CONF,
+                                    getString(cn.escheduler.common.Constants.JAVA_SECURITY_KRB5_CONF_PATH));
+                            Configuration configuration = new Configuration();
+                            configuration.set(cn.escheduler.common.Constants.HADOOP_SECURITY_AUTHENTICATION, "kerberos");
+                            UserGroupInformation.setConfiguration(configuration);
+                            UserGroupInformation.loginUserFromKeytab(getString(cn.escheduler.common.Constants.LOGIN_USER_KEY_TAB_USERNAME),
+                                    getString(cn.escheduler.common.Constants.LOGIN_USER_KEY_TAB_PATH));
+                    }
+                    if (dbType == DbType.HIVE){
+                        datasource = JSONObject.parseObject(parameter, HiveDataSource.class);
+                    }else if (dbType == DbType.SPARK){
+                        datasource = JSONObject.parseObject(parameter, SparkDataSource.class);
+                    }
                     Class.forName(Constants.ORG_APACHE_HIVE_JDBC_HIVE_DRIVER);
                     break;
                 case CLICKHOUSE:
@@ -442,10 +488,18 @@
      * @param other
      * @return
      */
-    public String buildParameter(String name, String desc, DbType type, String host, String port, String database, String userName, String password, String other) {
+    public String buildParameter(String name, String desc, DbType type, String host,
+                                 String port, String database,String principal,String userName,
+                                 String password, String other) {
 
         String address = buildAddress(type, host, port);
+
         String jdbcUrl = address + "/" + database;
+        if (CommonUtils.getKerberosStartupState() &&
+                (type == DbType.HIVE || type == DbType.SPARK)){
+            jdbcUrl += ";principal=" + principal;
+        }
+
         String separator = "";
         if (Constants.MYSQL.equals(type.name())
                 || Constants.POSTGRESQL.equals(type.name())
@@ -464,6 +518,10 @@
         parameterMap.put(Constants.JDBC_URL, jdbcUrl);
         parameterMap.put(Constants.USER, userName);
         parameterMap.put(Constants.PASSWORD, password);
+        if (CommonUtils.getKerberosStartupState() &&
+                (type == DbType.HIVE || type == DbType.SPARK)){
+            parameterMap.put(Constants.PRINCIPAL,principal);
+        }
         if (other != null && !"".equals(other)) {
             Map map = JSONObject.parseObject(other, new TypeReference<LinkedHashMap<String, String>>() {
             });
@@ -537,7 +595,7 @@
                 putMsg(result, Status.RESOURCE_NOT_EXIST);
                 return result;
             }
-            if(loginUser.getId() != dataSource.getUserId()){
+            if(loginUser.getId() != dataSource.getUserId() && loginUser.getUserType() != UserType.ADMIN_USER){
                 putMsg(result, Status.USER_NO_OPERATION_PERM);
                 return result;
             }
@@ -622,13 +680,13 @@
      */
     private String[] getHostsAndPort(String address) {
         String[] result = new String[2];
-        String[] tmpArray = address.split("//");
+        String[] tmpArray = address.split(cn.escheduler.common.Constants.DOUBLE_SLASH);
         String hostsAndPorts = tmpArray[tmpArray.length - 1];
-        StringBuilder hosts = new StringBuilder("");
-        String[] hostPortArray = hostsAndPorts.split(",");
-        String port = hostPortArray[0].split(":")[1];
+        StringBuilder hosts = new StringBuilder();
+        String[] hostPortArray = hostsAndPorts.split(cn.escheduler.common.Constants.COMMA);
+        String port = hostPortArray[0].split(cn.escheduler.common.Constants.COLON)[1];
         for (String hostPort : hostPortArray) {
-            hosts.append(hostPort.split(":")[0]).append(",");
+            hosts.append(hostPort.split(cn.escheduler.common.Constants.COLON)[0]).append(cn.escheduler.common.Constants.COMMA);
         }
         hosts.deleteCharAt(hosts.length() - 1);
         result[0] = hosts.toString();
diff --git a/escheduler-api/src/main/java/cn/escheduler/api/service/ExecutorService.java b/escheduler-api/src/main/java/cn/escheduler/api/service/ExecutorService.java
index 740fbc9..9602ac6 100644
--- a/escheduler-api/src/main/java/cn/escheduler/api/service/ExecutorService.java
+++ b/escheduler-api/src/main/java/cn/escheduler/api/service/ExecutorService.java
@@ -191,6 +191,16 @@
             return checkResult;
         }
 
+        // checkTenantExists();
+        Tenant tenant = processDao.getTenantForProcess(processDefinition.getTenantId(),
+                processDefinition.getUserId());
+        if(tenant == null){
+            logger.error("there is not any vaild tenant for the process definition: id:{},name:{}, ",
+                    processDefinition.getId(), processDefinition.getName());
+            putMsg(result, Status.PROCESS_INSTANCE_NOT_EXIST, processInstanceId);
+            return result;
+        }
+
         switch (executeType) {
             case REPEAT_RUNNING:
                 result = insertCommand(loginUser, processInstanceId, processDefinition.getId(), CommandType.REPEAT_RUNNING);
@@ -260,7 +270,7 @@
                 }
                 break;
             case RECOVER_SUSPENDED_PROCESS:
-                if (executionStatus.typeIsPause()) {
+                if (executionStatus.typeIsPause()|| executionStatus.typeIsCancel()) {
                     checkResult = true;
                 }
             default:
diff --git a/escheduler-api/src/main/java/cn/escheduler/api/service/MonitorService.java b/escheduler-api/src/main/java/cn/escheduler/api/service/MonitorService.java
index 4708b00..08e8bf5 100644
--- a/escheduler-api/src/main/java/cn/escheduler/api/service/MonitorService.java
+++ b/escheduler-api/src/main/java/cn/escheduler/api/service/MonitorService.java
@@ -18,13 +18,16 @@
 
 import cn.escheduler.api.enums.Status;
 import cn.escheduler.api.utils.Constants;
-import cn.escheduler.api.utils.ZookeeperMonitorUtils;
+import cn.escheduler.api.utils.ZookeeperMonitor;
 import cn.escheduler.dao.MonitorDBDao;
+import cn.escheduler.dao.model.MasterServer;
 import cn.escheduler.dao.model.MonitorRecord;
 import cn.escheduler.dao.model.User;
 import cn.escheduler.dao.model.ZookeeperRecord;
+import org.apache.hadoop.mapred.Master;
 import org.springframework.stereotype.Service;
 
+import java.util.ArrayList;
 import java.util.HashMap;
 import java.util.List;
 import java.util.Map;
@@ -52,6 +55,22 @@
 
   }
 
+  /**
+   * query master list
+   *
+   * @param loginUser
+   * @return
+   */
+  public Map<String,Object> queryMaster(User loginUser) {
+
+    Map<String, Object> result = new HashMap<>(5);
+
+    List<MasterServer> masterServers = new ZookeeperMonitor().getMasterServers();
+    result.put(Constants.DATA_LIST, masterServers);
+    putMsg(result,Status.SUCCESS);
+
+    return result;
+  }
 
   /**
    * query zookeeper state
@@ -61,7 +80,7 @@
   public Map<String,Object> queryZookeeperState(User loginUser) {
     Map<String, Object> result = new HashMap<>(5);
 
-    List<ZookeeperRecord> zookeeperRecordList = ZookeeperMonitorUtils.zookeeperInfoList();
+    List<ZookeeperRecord> zookeeperRecordList = ZookeeperMonitor.zookeeperInfoList();
 
     result.put(Constants.DATA_LIST, zookeeperRecordList);
     putMsg(result, Status.SUCCESS);
@@ -69,4 +88,22 @@
     return result;
 
   }
+
+
+  /**
+   * query master list
+   *
+   * @param loginUser
+   * @return
+   */
+  public Map<String,Object> queryWorker(User loginUser) {
+
+    Map<String, Object> result = new HashMap<>(5);
+
+    List<MasterServer> workerServers = new ZookeeperMonitor().getWorkerServers();
+    result.put(Constants.DATA_LIST, workerServers);
+    putMsg(result,Status.SUCCESS);
+
+    return result;
+  }
 }
diff --git a/escheduler-api/src/main/java/cn/escheduler/api/service/ProcessDefinitionService.java b/escheduler-api/src/main/java/cn/escheduler/api/service/ProcessDefinitionService.java
index 45ff487..a797ce5 100644
--- a/escheduler-api/src/main/java/cn/escheduler/api/service/ProcessDefinitionService.java
+++ b/escheduler-api/src/main/java/cn/escheduler/api/service/ProcessDefinitionService.java
@@ -24,6 +24,7 @@
 import cn.escheduler.common.enums.Flag;
 import cn.escheduler.common.enums.ReleaseState;
 import cn.escheduler.common.enums.TaskType;
+import cn.escheduler.common.enums.UserType;
 import cn.escheduler.common.graph.DAG;
 import cn.escheduler.common.model.TaskNode;
 import cn.escheduler.common.model.TaskNodeRelation;
@@ -127,6 +128,7 @@
         processDefine.setLocations(locations);
         processDefine.setConnects(connects);
         processDefine.setTimeout(processData.getTimeout());
+        processDefine.setTenantId(processData.getTenantId());
 
         //custom global params
         List<Property> globalParamsList = processData.getGlobalParams();
@@ -291,6 +293,7 @@
         processDefine.setLocations(locations);
         processDefine.setConnects(connects);
         processDefine.setTimeout(processData.getTimeout());
+        processDefine.setTenantId(processData.getTenantId());
 
         //custom global params
         List<Property> globalParamsList = new ArrayList<>();
@@ -365,7 +368,7 @@
         }
 
         // Determine if the login user is the owner of the process definition
-        if (loginUser.getId() != processDefinition.getUserId()) {
+        if (loginUser.getId() != processDefinition.getUserId() && loginUser.getUserType() != UserType.ADMIN_USER) {
             putMsg(result, Status.USER_NO_OPERATION_PERM);
             return result;
         }
diff --git a/escheduler-api/src/main/java/cn/escheduler/api/service/ProcessInstanceService.java b/escheduler-api/src/main/java/cn/escheduler/api/service/ProcessInstanceService.java
index 6475b42..f5f05a7 100644
--- a/escheduler-api/src/main/java/cn/escheduler/api/service/ProcessInstanceService.java
+++ b/escheduler-api/src/main/java/cn/escheduler/api/service/ProcessInstanceService.java
@@ -38,10 +38,7 @@
 import cn.escheduler.common.utils.ParameterUtils;
 import cn.escheduler.common.utils.placeholder.BusinessTimeUtils;
 import cn.escheduler.dao.ProcessDao;
-import cn.escheduler.dao.mapper.ProcessDefinitionMapper;
-import cn.escheduler.dao.mapper.ProcessInstanceMapper;
-import cn.escheduler.dao.mapper.ProjectMapper;
-import cn.escheduler.dao.mapper.TaskInstanceMapper;
+import cn.escheduler.dao.mapper.*;
 import cn.escheduler.dao.model.*;
 import com.alibaba.fastjson.JSON;
 import org.apache.commons.lang3.StringUtils;
@@ -97,6 +94,9 @@
     @Autowired
     LoggerService loggerService;
 
+    @Autowired
+    WorkerGroupMapper workerGroupMapper;
+
     /**
      * query process instance by id
      *
@@ -115,6 +115,21 @@
             return checkResult;
         }
         ProcessInstance processInstance = processDao.findProcessInstanceDetailById(processId);
+        String workerGroupName = "";
+        if(processInstance.getWorkerGroupId() == -1){
+            workerGroupName = DEFAULT;
+        }else{
+            WorkerGroup workerGroup = workerGroupMapper.queryById(processInstance.getWorkerGroupId());
+            if(workerGroup != null){
+                workerGroupName = DEFAULT;
+            }else{
+                workerGroupName = workerGroup.getName();
+            }
+        }
+        processInstance.setWorkerGroupName(workerGroupName);
+        ProcessDefinition processDefinition = processDao.findProcessDefineById(processInstance.getProcessDefinitionId());
+        processInstance.setReceivers(processDefinition.getReceivers());
+        processInstance.setReceiversCc(processDefinition.getReceiversCc());
         result.put(Constants.DATA_LIST, processInstance);
         putMsg(result, Status.SUCCESS);
 
@@ -364,6 +379,7 @@
         String globalParams = null;
         String originDefParams = null;
         int timeout = processInstance.getTimeout();
+        ProcessDefinition processDefinition = processDao.findProcessDefineById(processInstance.getProcessDefinitionId());
         if (StringUtils.isNotEmpty(processInstanceJson)) {
             ProcessData processData = JSONUtils.parseObject(processInstanceJson, ProcessData.class);
             //check workflow json is valid
@@ -379,6 +395,11 @@
                     processInstance.getCmdTypeIfComplement(), schedule);
             timeout = processData.getTimeout();
             processInstance.setTimeout(timeout);
+            Tenant tenant = processDao.getTenantForProcess(processData.getTenantId(),
+                    processDefinition.getUserId());
+            if(tenant != null){
+                processInstance.setTenantCode(tenant.getTenantCode());
+            }
             processInstance.setProcessInstanceJson(processInstanceJson);
             processInstance.setGlobalParams(globalParams);
         }
@@ -387,7 +408,6 @@
         int update = processDao.updateProcessInstance(processInstance);
         int updateDefine = 1;
         if (syncDefine && StringUtils.isNotEmpty(processInstanceJson)) {
-            ProcessDefinition processDefinition = processDao.findProcessDefineById(processInstance.getProcessDefinitionId());
             processDefinition.setProcessDefinitionJson(processInstanceJson);
             processDefinition.setGlobalParams(originDefParams);
             processDefinition.setLocations(locations);
diff --git a/escheduler-api/src/main/java/cn/escheduler/api/service/ProjectService.java b/escheduler-api/src/main/java/cn/escheduler/api/service/ProjectService.java
index c90d4da..30d8f82 100644
--- a/escheduler-api/src/main/java/cn/escheduler/api/service/ProjectService.java
+++ b/escheduler-api/src/main/java/cn/escheduler/api/service/ProjectService.java
@@ -76,15 +76,6 @@
             return descCheck;
         }
 
-        /**
-         * only general users can create projects. administrators have no corresponding tenants and can only view
-         * 管理员没有对应的租户,只能查看,只有普通用户才可以创建项目
-         */
-        if (!userService.isGeneral(loginUser)) {
-            putMsg(result, Status.USER_NO_OPERATION_PERM);
-            return result;
-        }
-
         Project project = projectMapper.queryByName(name);
         if (project != null) {
             putMsg(result, Status.PROJECT_ALREADY_EXISTS, name);
diff --git a/escheduler-api/src/main/java/cn/escheduler/api/service/ResourcesService.java b/escheduler-api/src/main/java/cn/escheduler/api/service/ResourcesService.java
index aeb7e18..651d960 100644
--- a/escheduler-api/src/main/java/cn/escheduler/api/service/ResourcesService.java
+++ b/escheduler-api/src/main/java/cn/escheduler/api/service/ResourcesService.java
@@ -21,6 +21,7 @@
 import cn.escheduler.api.utils.PageInfo;
 import cn.escheduler.api.utils.Result;
 import cn.escheduler.common.enums.ResourceType;
+import cn.escheduler.common.enums.UserType;
 import cn.escheduler.common.utils.FileUtils;
 import cn.escheduler.common.utils.HadoopUtils;
 import cn.escheduler.common.utils.PropertyUtils;
@@ -85,8 +86,8 @@
         Result result = new Result();
 
         // if hdfs not startup
-        if (!PropertyUtils.getBoolean(cn.escheduler.common.Constants.HDFS_STARTUP_STATE)){
-            logger.error("hdfs startup state: {}", PropertyUtils.getBoolean(cn.escheduler.common.Constants.HDFS_STARTUP_STATE));
+        if (!PropertyUtils.getResUploadStartupState()){
+            logger.error("resource upload startup state: {}", PropertyUtils.getResUploadStartupState());
             putMsg(result, Status.HDFS_NOT_STARTUP);
             return result;
         }
@@ -184,9 +185,9 @@
                                  ResourceType type) {
         Result result = new Result();
 
-        // if hdfs not startup
-        if (!PropertyUtils.getBoolean(cn.escheduler.common.Constants.HDFS_STARTUP_STATE)){
-            logger.error("hdfs startup state: {}", PropertyUtils.getBoolean(cn.escheduler.common.Constants.HDFS_STARTUP_STATE));
+        // if resource upload startup
+        if (!PropertyUtils.getResUploadStartupState()){
+            logger.error("resource upload startup state: {}", PropertyUtils.getResUploadStartupState());
             putMsg(result, Status.HDFS_NOT_STARTUP);
             return result;
         }
@@ -368,7 +369,12 @@
     public Map<String, Object> queryResourceList(User loginUser, ResourceType type) {
 
         Map<String, Object> result = new HashMap<>(5);
-        List<Resource> resourceList = resourcesMapper.queryResourceListAuthored(loginUser.getId(), type.ordinal());
+        List<Resource> resourceList;
+        if(isAdmin(loginUser)){
+            resourceList = resourcesMapper.listAllResourceByType(type.ordinal());
+        }else{
+            resourceList = resourcesMapper.queryResourceListAuthored(loginUser.getId(), type.ordinal());
+        }
         result.put(Constants.DATA_LIST, resourceList);
         putMsg(result,Status.SUCCESS);
 
@@ -385,9 +391,9 @@
     public Result delete(User loginUser, int resourceId) throws Exception {
         Result result = new Result();
 
-        // if hdfs not startup
-        if (!PropertyUtils.getBoolean(cn.escheduler.common.Constants.HDFS_STARTUP_STATE)){
-            logger.error("hdfs startup state: {}", PropertyUtils.getBoolean(cn.escheduler.common.Constants.HDFS_STARTUP_STATE));
+        // if resource upload startup
+        if (!PropertyUtils.getResUploadStartupState()){
+            logger.error("resource upload startup state: {}", PropertyUtils.getResUploadStartupState());
             putMsg(result, Status.HDFS_NOT_STARTUP);
             return result;
         }
@@ -399,7 +405,7 @@
             putMsg(result, Status.RESOURCE_NOT_EXIST);
             return result;
         }
-        if (loginUser.getId() != resource.getUserId()) {
+        if (loginUser.getId() != resource.getUserId() && loginUser.getUserType() != UserType.ADMIN_USER) {
             putMsg(result, Status.USER_NO_OPERATION_PERM);
             return result;
         }
@@ -483,9 +489,9 @@
     public Result readResource(int resourceId, int skipLineNum, int limit) {
         Result result = new Result();
 
-        // if hdfs not startup
-        if (!PropertyUtils.getBoolean(cn.escheduler.common.Constants.HDFS_STARTUP_STATE)){
-            logger.error("hdfs startup state: {}", PropertyUtils.getBoolean(cn.escheduler.common.Constants.HDFS_STARTUP_STATE));
+        // if resource upload startup
+        if (!PropertyUtils.getResUploadStartupState()){
+            logger.error("resource upload startup state: {}", PropertyUtils.getResUploadStartupState());
             putMsg(result, Status.HDFS_NOT_STARTUP);
             return result;
         }
@@ -550,9 +556,9 @@
     @Transactional(value = "TransactionManager",rollbackFor = Exception.class)
     public Result onlineCreateResource(User loginUser, ResourceType type, String fileName, String fileSuffix, String desc, String content) {
         Result result = new Result();
-        // if hdfs not startup
-        if (!PropertyUtils.getBoolean(cn.escheduler.common.Constants.HDFS_STARTUP_STATE)){
-            logger.error("hdfs startup state: {}", PropertyUtils.getBoolean(cn.escheduler.common.Constants.HDFS_STARTUP_STATE));
+        // if resource upload startup
+        if (!PropertyUtils.getResUploadStartupState()){
+            logger.error("resource upload startup state: {}", PropertyUtils.getResUploadStartupState());
             putMsg(result, Status.HDFS_NOT_STARTUP);
             return result;
         }
@@ -611,9 +617,9 @@
     public Result updateResourceContent(int resourceId, String content) {
         Result result = new Result();
 
-        // if hdfs not startup
-        if (!PropertyUtils.getBoolean(cn.escheduler.common.Constants.HDFS_STARTUP_STATE)){
-            logger.error("hdfs startup state: {}", PropertyUtils.getBoolean(cn.escheduler.common.Constants.HDFS_STARTUP_STATE));
+        // if resource upload startup
+        if (!PropertyUtils.getResUploadStartupState()){
+            logger.error("resource upload startup state: {}", PropertyUtils.getResUploadStartupState());
             putMsg(result, Status.HDFS_NOT_STARTUP);
             return result;
         }
@@ -706,9 +712,9 @@
      * @return
      */
     public org.springframework.core.io.Resource downloadResource(int resourceId) throws Exception {
-        // if hdfs not startup
-        if (!PropertyUtils.getBoolean(cn.escheduler.common.Constants.HDFS_STARTUP_STATE)){
-            logger.error("hdfs startup state: {}", PropertyUtils.getBoolean(cn.escheduler.common.Constants.HDFS_STARTUP_STATE));
+        // if resource upload startup
+        if (!PropertyUtils.getResUploadStartupState()){
+            logger.error("resource upload startup state: {}", PropertyUtils.getResUploadStartupState());
             throw new RuntimeException("hdfs not startup");
         }
 
diff --git a/escheduler-api/src/main/java/cn/escheduler/api/service/SchedulerService.java b/escheduler-api/src/main/java/cn/escheduler/api/service/SchedulerService.java
index 5ea5faf..2766a10 100644
--- a/escheduler-api/src/main/java/cn/escheduler/api/service/SchedulerService.java
+++ b/escheduler-api/src/main/java/cn/escheduler/api/service/SchedulerService.java
@@ -19,14 +19,13 @@
 
 import cn.escheduler.api.dto.ScheduleParam;
 import cn.escheduler.api.enums.Status;
-import cn.escheduler.api.quartz.ProcessScheduleJob;
-import cn.escheduler.api.quartz.QuartzExecutors;
 import cn.escheduler.api.utils.Constants;
 import cn.escheduler.api.utils.PageInfo;
 import cn.escheduler.common.enums.FailureStrategy;
 import cn.escheduler.common.enums.Priority;
 import cn.escheduler.common.enums.ReleaseState;
 import cn.escheduler.common.enums.WarningType;
+import cn.escheduler.common.utils.DateUtils;
 import cn.escheduler.common.utils.JSONUtils;
 import cn.escheduler.dao.ProcessDao;
 import cn.escheduler.dao.mapper.MasterServerMapper;
@@ -34,7 +33,11 @@
 import cn.escheduler.dao.mapper.ProjectMapper;
 import cn.escheduler.dao.mapper.ScheduleMapper;
 import cn.escheduler.dao.model.*;
+import cn.escheduler.dao.utils.cron.CronUtils;
+import cn.escheduler.server.quartz.ProcessScheduleJob;
+import cn.escheduler.server.quartz.QuartzExecutors;
 import org.apache.commons.lang3.StringUtils;
+import org.quartz.CronExpression;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 import org.springframework.beans.factory.annotation.Autowired;
@@ -42,6 +45,7 @@
 import org.springframework.transaction.annotation.Transactional;
 
 import java.io.IOException;
+import java.text.ParseException;
 import java.util.*;
 
 /**
@@ -115,6 +119,11 @@
         scheduleObj.setProcessDefinitionName(processDefinition.getName());
 
         ScheduleParam scheduleParam = JSONUtils.parseObject(schedule, ScheduleParam.class);
+        if (DateUtils.differSec(scheduleParam.getStartTime(),scheduleParam.getEndTime()) == 0) {
+            logger.warn("The start time must not be the same as the end");
+            putMsg(result,Status.SCHEDULE_START_TIME_END_TIME_SAME);
+            return result;
+        }
         scheduleObj.setStartTime(scheduleParam.getStartTime());
         scheduleObj.setEndTime(scheduleParam.getEndTime());
         if (!org.quartz.CronExpression.isValidExpression(scheduleParam.getCrontab())) {
@@ -201,6 +210,11 @@
         // updateProcessInstance param
         if (StringUtils.isNotEmpty(scheduleExpression)) {
             ScheduleParam scheduleParam = JSONUtils.parseObject(scheduleExpression, ScheduleParam.class);
+            if (DateUtils.differSec(scheduleParam.getStartTime(),scheduleParam.getEndTime()) == 0) {
+                logger.warn("The start time must not be the same as the end");
+                putMsg(result,Status.SCHEDULE_START_TIME_END_TIME_SAME);
+                return result;
+            }
             schedule.setStartTime(scheduleParam.getStartTime());
             schedule.setEndTime(scheduleParam.getEndTime());
             if (!org.quartz.CronExpression.isValidExpression(scheduleParam.getCrontab())) {
@@ -537,4 +551,32 @@
         }
         return result;
     }
+
+    /**
+     * preview schedule
+     * @param loginUser
+     * @param projectName
+     * @param schedule
+     * @return
+     */
+    public Map<String,Object> previewSchedule(User loginUser, String projectName, String schedule) {
+        Map<String, Object> result = new HashMap<>(5);
+        CronExpression cronExpression;
+        ScheduleParam scheduleParam = JSONUtils.parseObject(schedule, ScheduleParam.class);
+        Date now = new Date();
+
+        Date startTime = now.after(scheduleParam.getStartTime()) ? now : scheduleParam.getStartTime();
+        Date endTime = scheduleParam.getEndTime();
+        try {
+            cronExpression = CronUtils.parse2CronExpression(scheduleParam.getCrontab());
+        } catch (ParseException e) {
+            logger.error(e.getMessage(),e);
+            putMsg(result,Status.PARSE_TO_CRON_EXPRESSION_ERROR);
+            return result;
+        }
+        List<Date> selfFireDateList = CronUtils.getSelfFireDateList(startTime, endTime,cronExpression);
+        result.put(Constants.DATA_LIST, selfFireDateList.stream().map(t -> DateUtils.dateToString(t)).limit(cn.escheduler.common.Constants.PREVIEW_SCHEDULE_EXECUTE_COUNT));
+        putMsg(result, Status.SUCCESS);
+        return result;
+    }
 }
\ No newline at end of file
diff --git a/escheduler-api/src/main/java/cn/escheduler/api/service/TenantService.java b/escheduler-api/src/main/java/cn/escheduler/api/service/TenantService.java
index 1262423..21d5f27 100644
--- a/escheduler-api/src/main/java/cn/escheduler/api/service/TenantService.java
+++ b/escheduler-api/src/main/java/cn/escheduler/api/service/TenantService.java
@@ -96,7 +96,7 @@
     tenantMapper.insert(tenant);
 
     // if hdfs startup
-    if (PropertyUtils.getBoolean(cn.escheduler.common.Constants.HDFS_STARTUP_STATE)){
+    if (PropertyUtils.getResUploadStartupState()){
       String resourcePath = HadoopUtils.getHdfsDataBasePath() + "/" + tenantCode + "/resources";
       String udfsPath = HadoopUtils.getHdfsUdfDir(tenantCode);
       /**
@@ -178,7 +178,7 @@
       Tenant newTenant = tenantMapper.queryByTenantCode(tenantCode);
       if (newTenant == null){
         // if hdfs startup
-        if (PropertyUtils.getBoolean(cn.escheduler.common.Constants.HDFS_STARTUP_STATE)){
+        if (PropertyUtils.getResUploadStartupState()){
           String resourcePath = HadoopUtils.getHdfsDataBasePath() + "/" + tenantCode + "/resources";
           String udfsPath = HadoopUtils.getHdfsUdfDir(tenantCode);
           //init hdfs resource
@@ -235,28 +235,29 @@
       return result;
     }
 
-    // if hdfs startup
-    if (PropertyUtils.getBoolean(cn.escheduler.common.Constants.HDFS_STARTUP_STATE)){
+    // if resource upload startup
+    if (PropertyUtils.getResUploadStartupState()){
       String tenantPath = HadoopUtils.getHdfsDataBasePath() + "/" + tenant.getTenantCode();
 
-      String resourcePath = HadoopUtils.getHdfsDir(tenant.getTenantCode());
-      FileStatus[] fileStatus = HadoopUtils.getInstance().listFileStatus(resourcePath);
-      if (fileStatus.length > 0) {
-        putMsg(result, Status.HDFS_TERANT_RESOURCES_FILE_EXISTS);
-        return result;
-      }
-      fileStatus = HadoopUtils.getInstance().listFileStatus(HadoopUtils.getHdfsUdfDir(tenant.getTenantCode()));
-      if (fileStatus.length > 0) {
-        putMsg(result, Status.HDFS_TERANT_UDFS_FILE_EXISTS);
-        return result;
-      }
+      if (HadoopUtils.getInstance().exists(tenantPath)){
+        String resourcePath = HadoopUtils.getHdfsDir(tenant.getTenantCode());
+        FileStatus[] fileStatus = HadoopUtils.getInstance().listFileStatus(resourcePath);
+        if (fileStatus.length > 0) {
+          putMsg(result, Status.HDFS_TERANT_RESOURCES_FILE_EXISTS);
+          return result;
+        }
+        fileStatus = HadoopUtils.getInstance().listFileStatus(HadoopUtils.getHdfsUdfDir(tenant.getTenantCode()));
+        if (fileStatus.length > 0) {
+          putMsg(result, Status.HDFS_TERANT_UDFS_FILE_EXISTS);
+          return result;
+        }
 
-      HadoopUtils.getInstance().delete(tenantPath, true);
+        HadoopUtils.getInstance().delete(tenantPath, true);
+      }
     }
 
     tenantMapper.deleteById(id);
     putMsg(result, Status.SUCCESS);
-    
     return result;
   }
 
@@ -269,9 +270,6 @@
   public Map<String, Object> queryTenantList(User loginUser) {
 
     Map<String, Object> result = new HashMap<>(5);
-    if (checkAdmin(loginUser, result)) {
-      return result;
-    }
 
     List<Tenant> resourceList = tenantMapper.queryAllTenant();
     result.put(Constants.DATA_LIST, resourceList);
diff --git a/escheduler-api/src/main/java/cn/escheduler/api/service/UdfFuncService.java b/escheduler-api/src/main/java/cn/escheduler/api/service/UdfFuncService.java
index 52e605f..8ca399d 100644
--- a/escheduler-api/src/main/java/cn/escheduler/api/service/UdfFuncService.java
+++ b/escheduler-api/src/main/java/cn/escheduler/api/service/UdfFuncService.java
@@ -80,9 +80,9 @@
                                     int resourceId) {
         Result result = new Result();
 
-        // if hdfs not startup
-        if (!PropertyUtils.getBoolean(cn.escheduler.common.Constants.HDFS_STARTUP_STATE)){
-            logger.error("hdfs startup state: {}", PropertyUtils.getBoolean(cn.escheduler.common.Constants.HDFS_STARTUP_STATE));
+        // if resource upload startup
+        if (!PropertyUtils.getResUploadStartupState()){
+            logger.error("resource upload startup state: {}", PropertyUtils.getResUploadStartupState());
             putMsg(result, Status.HDFS_NOT_STARTUP);
             return result;
         }
@@ -167,9 +167,9 @@
         // verify udfFunc is exist
         UdfFunc udf = udfFuncMapper.queryUdfById(udfFuncId);
 
-        // if hdfs not startup
-        if (!PropertyUtils.getBoolean(cn.escheduler.common.Constants.HDFS_STARTUP_STATE)){
-            logger.error("hdfs startup state: {}", PropertyUtils.getBoolean(cn.escheduler.common.Constants.HDFS_STARTUP_STATE));
+        // if resource upload startup
+        if (!PropertyUtils.getResUploadStartupState()){
+            logger.error("resource upload startup state: {}", PropertyUtils.getResUploadStartupState());
             putMsg(result, Status.HDFS_NOT_STARTUP);
             return result;
         }
diff --git a/escheduler-api/src/main/java/cn/escheduler/api/service/UsersService.java b/escheduler-api/src/main/java/cn/escheduler/api/service/UsersService.java
index 5db8662..8004117 100644
--- a/escheduler-api/src/main/java/cn/escheduler/api/service/UsersService.java
+++ b/escheduler-api/src/main/java/cn/escheduler/api/service/UsersService.java
@@ -125,7 +125,7 @@
 
         Tenant tenant = tenantMapper.queryById(tenantId);
         // if hdfs startup
-        if (PropertyUtils.getBoolean(cn.escheduler.common.Constants.HDFS_STARTUP_STATE)){
+        if (PropertyUtils.getResUploadStartupState()){
             String userPath = HadoopUtils.getHdfsDataBasePath() + "/" + tenant.getTenantCode() + "/home/" + user.getId();
 
             HadoopUtils.getInstance().mkdir(userPath);
@@ -245,36 +245,36 @@
             Tenant newTenant = tenantMapper.queryById(tenantId);
             if (newTenant != null) {
                 // if hdfs startup
-                if (PropertyUtils.getBoolean(cn.escheduler.common.Constants.HDFS_STARTUP_STATE)){
+                if (PropertyUtils.getResUploadStartupState() && oldTenant != null){
                     String newTenantCode = newTenant.getTenantCode();
                     String oldResourcePath = HadoopUtils.getHdfsDataBasePath() + "/" + oldTenant.getTenantCode() + "/resources";
                     String oldUdfsPath = HadoopUtils.getHdfsUdfDir(oldTenant.getTenantCode());
 
+                    if (HadoopUtils.getInstance().exists(oldResourcePath)){
+                        String newResourcePath = HadoopUtils.getHdfsDataBasePath() + "/" + newTenantCode + "/resources";
+                        String newUdfsPath = HadoopUtils.getHdfsUdfDir(newTenantCode);
 
-                    String newResourcePath = HadoopUtils.getHdfsDataBasePath() + "/" + newTenantCode + "/resources";
-                    String newUdfsPath = HadoopUtils.getHdfsUdfDir(newTenantCode);
-
-                    //file resources list
-                    List<Resource> fileResourcesList = resourceMapper.queryResourceCreatedByUser(userId, 0);
-                    if (CollectionUtils.isNotEmpty(fileResourcesList)) {
-                        for (Resource resource : fileResourcesList) {
-                            HadoopUtils.getInstance().copy(oldResourcePath + "/" + resource.getAlias(), newResourcePath, false, true);
+                        //file resources list
+                        List<Resource> fileResourcesList = resourceMapper.queryResourceCreatedByUser(userId, 0);
+                        if (CollectionUtils.isNotEmpty(fileResourcesList)) {
+                            for (Resource resource : fileResourcesList) {
+                                HadoopUtils.getInstance().copy(oldResourcePath + "/" + resource.getAlias(), newResourcePath, false, true);
+                            }
                         }
-                    }
 
-                    //udf resources
-                    List<Resource> udfResourceList = resourceMapper.queryResourceCreatedByUser(userId, 1);
-                    if (CollectionUtils.isNotEmpty(udfResourceList)) {
-                        for (Resource resource : udfResourceList) {
-                            HadoopUtils.getInstance().copy(oldUdfsPath + "/" + resource.getAlias(), newUdfsPath, false, true);
+                        //udf resources
+                        List<Resource> udfResourceList = resourceMapper.queryResourceCreatedByUser(userId, 1);
+                        if (CollectionUtils.isNotEmpty(udfResourceList)) {
+                            for (Resource resource : udfResourceList) {
+                                HadoopUtils.getInstance().copy(oldUdfsPath + "/" + resource.getAlias(), newUdfsPath, false, true);
+                            }
                         }
+
+                        //Delete the user from the old tenant directory
+                        String oldUserPath = HadoopUtils.getHdfsDataBasePath() + "/" + oldTenant.getTenantCode() + "/home/" + userId;
+                        HadoopUtils.getInstance().delete(oldUserPath, true);
                     }
 
-                    //Delete the user from the old tenant directory
-                    String oldUserPath = HadoopUtils.getHdfsDataBasePath() + "/" + oldTenant.getTenantCode() + "/home/" + userId;
-                    HadoopUtils.getInstance().delete(oldUserPath, true);
-
-
                     //create user in the new tenant directory
                     String newUserPath = HadoopUtils.getHdfsDataBasePath() + "/" + newTenant.getTenantCode() + "/home/" + user.getId();
                     HadoopUtils.getInstance().mkdir(newUserPath);
@@ -307,11 +307,13 @@
         // delete user
         User user = userMapper.queryTenantCodeByUserId(id);
 
-
-        if (PropertyUtils.getBoolean(cn.escheduler.common.Constants.HDFS_STARTUP_STATE)){
-            String userPath = HadoopUtils.getHdfsDataBasePath() + "/" + user.getTenantCode() + "/home/" + id;
-
-            HadoopUtils.getInstance().delete(userPath, true);
+        if (user != null) {
+            if (PropertyUtils.getResUploadStartupState()) {
+                String userPath = HadoopUtils.getHdfsDataBasePath() + "/" + user.getTenantCode() + "/home/" + id;
+                if (HadoopUtils.getInstance().exists(userPath)) {
+                    HadoopUtils.getInstance().delete(userPath, true);
+                }
+            }
         }
 
         userMapper.delete(id);
diff --git a/escheduler-api/src/main/java/cn/escheduler/api/utils/CheckUtils.java b/escheduler-api/src/main/java/cn/escheduler/api/utils/CheckUtils.java
index 6a8c627..00c50f8 100644
--- a/escheduler-api/src/main/java/cn/escheduler/api/utils/CheckUtils.java
+++ b/escheduler-api/src/main/java/cn/escheduler/api/utils/CheckUtils.java
@@ -18,8 +18,10 @@
 
 
 import cn.escheduler.api.enums.Status;
+import cn.escheduler.common.enums.ResUploadType;
 import cn.escheduler.common.task.AbstractParameters;
 import cn.escheduler.common.utils.JSONUtils;
+import cn.escheduler.common.utils.PropertyUtils;
 import cn.escheduler.common.utils.TaskParametersUtils;
 import org.apache.commons.lang.StringUtils;
 
@@ -28,6 +30,7 @@
 import java.util.Map;
 import java.util.regex.Pattern;
 
+import static cn.escheduler.common.utils.PropertyUtils.getBoolean;
 
 
 /**
@@ -157,5 +160,4 @@
 
     return pattern.matcher(str).matches();
   }
-
 }
diff --git a/escheduler-api/src/main/java/cn/escheduler/api/utils/Constants.java b/escheduler-api/src/main/java/cn/escheduler/api/utils/Constants.java
index 79cf3e5..1dfe3ac 100644
--- a/escheduler-api/src/main/java/cn/escheduler/api/utils/Constants.java
+++ b/escheduler-api/src/main/java/cn/escheduler/api/utils/Constants.java
@@ -111,6 +111,7 @@
     public static final String ADDRESS = "address";
     public static final String DATABASE = "database";
     public static final String JDBC_URL = "jdbcUrl";
+    public static final String PRINCIPAL = "principal";
     public static final String USER = "user";
     public static final String PASSWORD = "password";
     public static final String OTHER = "other";
diff --git a/escheduler-api/src/main/java/cn/escheduler/api/utils/ZookeeperMonitorUtils.java b/escheduler-api/src/main/java/cn/escheduler/api/utils/ZookeeperMonitor.java
similarity index 64%
rename from escheduler-api/src/main/java/cn/escheduler/api/utils/ZookeeperMonitorUtils.java
rename to escheduler-api/src/main/java/cn/escheduler/api/utils/ZookeeperMonitor.java
index 6612a35..0f44b5f 100644
--- a/escheduler-api/src/main/java/cn/escheduler/api/utils/ZookeeperMonitorUtils.java
+++ b/escheduler-api/src/main/java/cn/escheduler/api/utils/ZookeeperMonitor.java
@@ -1,7 +1,9 @@
 package cn.escheduler.api.utils;
 
 import cn.escheduler.common.zk.AbstractZKClient;
+import cn.escheduler.dao.model.MasterServer;
 import cn.escheduler.dao.model.ZookeeperRecord;
+import cn.escheduler.server.ResInfo;
 import org.apache.commons.lang3.StringUtils;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
@@ -9,14 +11,15 @@
 import java.util.ArrayList;
 import java.util.Date;
 import java.util.List;
+import java.util.Map;
 
 
 /**
  *	monitor zookeeper info
  */
-public class ZookeeperMonitorUtils {
+public class ZookeeperMonitor extends AbstractZKClient{
 
-	private static final Logger LOG = LoggerFactory.getLogger(ZookeeperMonitorUtils.class);
+	private static final Logger LOG = LoggerFactory.getLogger(ZookeeperMonitor.class);
 	private static final String zookeeperList = AbstractZKClient.getZookeeperQuorum();
 
 	/**
@@ -33,6 +36,38 @@
 		return null;
 	}
 
+	/**
+	 * get server list.
+	 * @param isMaster
+	 * @return
+	 */
+	public List<MasterServer> getServers(boolean isMaster){
+		List<MasterServer> masterServers = new ArrayList<>();
+		Map<String, String> masterMap = getServerList(isMaster);
+		String parentPath = isMaster ? getMasterZNodeParentPath() : getWorkerZNodeParentPath();
+		for(String path : masterMap.keySet()){
+			MasterServer masterServer = ResInfo.parseHeartbeatForZKInfo(masterMap.get(path));
+			masterServer.setZkDirectory( parentPath + "/"+ path);
+			masterServers.add(masterServer);
+		}
+		return masterServers;
+	}
+
+	/**
+	 * get master servers
+	 * @return
+	 */
+	public List<MasterServer> getMasterServers(){
+	    return getServers(true);
+	}
+
+	/**
+	 * master construct is the same with worker, use the master instead
+	 * @return
+	 */
+	public List<MasterServer> getWorkerServers(){
+	    return getServers(false);
+	}
 
 	private static List<ZookeeperRecord> zookeeperInfoList(String zookeeperServers) {
 
diff --git a/escheduler-api/src/main/resources/application.properties b/escheduler-api/src/main/resources/application.properties
index bf003bf..b817c18 100644
--- a/escheduler-api/src/main/resources/application.properties
+++ b/escheduler-api/src/main/resources/application.properties
@@ -2,15 +2,18 @@
 server.port=12345
 
 # session config
-server.session.timeout=7200
+server.servlet.session.timeout=7200
 
-
-server.context-path=/escheduler/
+server.servlet.context-path=/escheduler/
 
 # file size limit for upload
-spring.http.multipart.max-file-size=1024MB
-spring.http.multipart.max-request-size=1024MB
+spring.servlet.multipart.max-file-size=1024MB
+spring.servlet.multipart.max-request-size=1024MB
 
 #post content
-server.max-http-post-size=5000000
+server.jetty.max-http-post-size=5000000
 
+spring.messages.encoding=UTF-8
+
+#i18n classpath folder , file prefix messages, if have many files, use "," seperator
+spring.messages.basename=i18n/messages
diff --git a/escheduler-api/src/main/resources/i18n/messages.properties b/escheduler-api/src/main/resources/i18n/messages.properties
new file mode 100644
index 0000000..a663c71
--- /dev/null
+++ b/escheduler-api/src/main/resources/i18n/messages.properties
@@ -0,0 +1,229 @@
+QUERY_SCHEDULE_LIST_NOTES=query schedule list
+EXECUTE_PROCESS_TAG=execute process related operation
+PROCESS_INSTANCE_EXECUTOR_TAG=process instance executor related operation
+RUN_PROCESS_INSTANCE_NOTES=run process instance 
+START_NODE_LIST=start node list(node name)
+TASK_DEPEND_TYPE=task depend type
+COMMAND_TYPE=command type
+RUN_MODE=run mode
+TIMEOUT=timeout
+EXECUTE_ACTION_TO_PROCESS_INSTANCE_NOTES=execute action to process instance 
+EXECUTE_TYPE=execute type
+START_CHECK_PROCESS_DEFINITION_NOTES=start check process definition 
+GET_RECEIVER_CC_NOTES=query receiver cc 
+DESC=description
+GROUP_NAME=group name
+GROUP_TYPE=group type
+QUERY_ALERT_GROUP_LIST_NOTES=query alert group list 
+UPDATE_ALERT_GROUP_NOTES=update alert group 
+DELETE_ALERT_GROUP_BY_ID_NOTES=delete alert group by id 
+VERIFY_ALERT_GROUP_NAME_NOTES=verify alert group name, check alert group exist or not 
+GRANT_ALERT_GROUP_NOTES=grant alert group 
+USER_IDS=user id list
+ALERT_GROUP_TAG=alert group related operation
+CREATE_ALERT_GROUP_NOTES=create alert group 
+WORKER_GROUP_TAG=worker group related operation
+SAVE_WORKER_GROUP_NOTES=create worker group
+WORKER_GROUP_NAME=worker group name
+WORKER_IP_LIST=worker ip list, eg. 192.168.1.1,192.168.1.2
+QUERY_WORKER_GROUP_PAGING_NOTES=query worker group paging
+QUERY_WORKER_GROUP_LIST_NOTES=query worker group list 
+DELETE_WORKER_GROUP_BY_ID_NOTES=delete worker group by id 
+DATA_ANALYSIS_TAG=analysis related operation of task state
+COUNT_TASK_STATE_NOTES=count task state 
+COUNT_PROCESS_INSTANCE_NOTES=count process instance state
+COUNT_PROCESS_DEFINITION_BY_USER_NOTES=count process definition by user 
+COUNT_COMMAND_STATE_NOTES=count command state 
+COUNT_QUEUE_STATE_NOTES=count the running status of the task in the queue\
+
+ACCESS_TOKEN_TAG=access token related operation
+MONITOR_TAG=monitor related operation
+MASTER_LIST_NOTES=master server list
+WORKER_LIST_NOTES=worker server list
+QUERY_DATABASE_STATE_NOTES=query database state 
+QUERY_ZOOKEEPER_STATE_NOTES=QUERY ZOOKEEPER STATE 
+TASK_STATE=task instance state
+SOURCE_TABLE=SOURCE TABLE
+DEST_TABLE=dest table
+TASK_DATE=task date
+QUERY_HISTORY_TASK_RECORD_LIST_PAGING_NOTES=query history task record list paging
+DATA_SOURCE_TAG=data source related operation
+CREATE_DATA_SOURCE_NOTES=create data source
+DATA_SOURCE_NAME=data source name
+DATA_SOURCE_NOTE=data source desc
+DB_TYPE=database type
+DATA_SOURCE_HOST=DATA SOURCE HOST
+DATA_SOURCE_PORT=data source port
+DATABASE_NAME=database name
+QUEUE_TAG=queue related operation
+QUERY_QUEUE_LIST_NOTES=query queue list 
+QUERY_QUEUE_LIST_PAGING_NOTES=query queue list paging  
+CREATE_QUEUE_NOTES=create queue
+YARN_QUEUE_NAME=yarn(hadoop) queue name
+QUEUE_ID=queue id
+TENANT_DESC=tenant desc
+QUERY_TENANT_LIST_PAGING_NOTES=query tenant list paging 
+QUERY_TENANT_LIST_NOTES=query tenant list 
+UPDATE_TENANT_NOTES=update tenant 
+DELETE_TENANT_NOTES=delete tenant 
+RESOURCES_TAG=resource center related operation
+CREATE_RESOURCE_NOTES=create resource 
+RESOURCE_TYPE=resource file type
+RESOURCE_NAME=resource name
+RESOURCE_DESC=resource file desc
+RESOURCE_FILE=resource file
+RESOURCE_ID=resource id
+QUERY_RESOURCE_LIST_NOTES=query resource list
+DELETE_RESOURCE_BY_ID_NOTES=delete resource by id
+VIEW_RESOURCE_BY_ID_NOTES=view resource by id
+ONLINE_CREATE_RESOURCE_NOTES=online create resource 
+SUFFIX=resource file suffix
+CONTENT=resource file content
+UPDATE_RESOURCE_NOTES=edit resource file online
+DOWNLOAD_RESOURCE_NOTES=download resource file
+CREATE_UDF_FUNCTION_NOTES=create udf function 
+UDF_TYPE=UDF type
+FUNC_NAME=function name
+CLASS_NAME=package and class name
+ARG_TYPES=arguments
+UDF_DESC=udf desc
+VIEW_UDF_FUNCTION_NOTES=view udf function 
+UPDATE_UDF_FUNCTION_NOTES=update udf function 
+QUERY_UDF_FUNCTION_LIST_PAGING_NOTES=query udf function list paging 
+VERIFY_UDF_FUNCTION_NAME_NOTES=verify udf function name 
+DELETE_UDF_FUNCTION_NOTES=delete udf function 
+AUTHORIZED_FILE_NOTES=authorized file 
+UNAUTHORIZED_FILE_NOTES=unauthorized file 
+AUTHORIZED_UDF_FUNC_NOTES=authorized udf func 
+UNAUTHORIZED_UDF_FUNC_NOTES=unauthorized udf func 
+VERIFY_QUEUE_NOTES=verify queue 
+TENANT_TAG=tenant related operation
+CREATE_TENANT_NOTES=create tenant 
+TENANT_CODE=tenant code
+TENANT_NAME=tenant name
+QUEUE_NAME=queue name
+PASSWORD=password
+DATA_SOURCE_OTHER=jdbc connection params, format:{"key1":"value1",...}
+PROJECT_TAG=project related operation
+CREATE_PROJECT_NOTES=create project 
+PROJECT_DESC=project description
+UPDATE_PROJECT_NOTES=update project 
+PROJECT_ID=project id
+QUERY_PROJECT_BY_ID_NOTES=query project info by project id
+QUERY_PROJECT_LIST_PAGING_NOTES=QUERY PROJECT LIST PAGING 
+DELETE_PROJECT_BY_ID_NOTES=delete project by id 
+QUERY_UNAUTHORIZED_PROJECT_NOTES=query unauthorized project
+QUERY_AUTHORIZED_PROJECT_NOTES=query authorized project
+TASK_RECORD_TAG=task record related operation
+QUERY_TASK_RECORD_LIST_PAGING_NOTES=query task record list paging 
+CREATE_TOKEN_NOTES=create token ,note: please login first
+QUERY_ACCESS_TOKEN_LIST_NOTES=query access token list paging
+SCHEDULE=schedule
+WARNING_TYPE=warning type(sending strategy)
+WARNING_GROUP_ID=warning group id
+FAILURE_STRATEGY=failure strategy
+RECEIVERS=receivers
+RECEIVERS_CC=receivers cc
+WORKER_GROUP_ID=worker server group id
+PROCESS_INSTANCE_PRIORITY=process instance priority
+UPDATE_SCHEDULE_NOTES=update schedule 
+SCHEDULE_ID=schedule id
+ONLINE_SCHEDULE_NOTES=online schedule
+OFFLINE_SCHEDULE_NOTES=offline schedule 
+QUERY_SCHEDULE_NOTES=query schedule 
+QUERY_SCHEDULE_LIST_PAGING_NOTES=query schedule list paging
+LOGIN_TAG=User login related operations
+USER_NAME=user name
+PROJECT_NAME=project name
+CREATE_PROCESS_DEFINITION_NOTES=create process definition
+PROCESS_DEFINITION_NAME=process definition name
+PROCESS_DEFINITION_JSON=process definition detail info (json format)
+PROCESS_DEFINITION_LOCATIONS=process definition node locations info (json format)
+PROCESS_INSTANCE_LOCATIONS=process instance node locations info (json format)
+PROCESS_DEFINITION_CONNECTS=process definition node connects info (json format)
+PROCESS_INSTANCE_CONNECTS=process instance node connects info (json format)
+PROCESS_DEFINITION_DESC=process definition desc
+PROCESS_DEFINITION_TAG=process definition related opertation
+SIGNOUT_NOTES=logout
+USER_PASSWORD=user password
+UPDATE_PROCESS_INSTANCE_NOTES=update process instance
+QUERY_PROCESS_INSTANCE_LIST_NOTES=query process instance list
+VERIFY_PROCCESS_DEFINITION_NAME_NOTES=verify proccess definition name
+LOGIN_NOTES=user login
+UPDATE_PROCCESS_DEFINITION_NOTES=update proccess definition
+PROCESS_DEFINITION_ID=process definition id
+RELEASE_PROCCESS_DEFINITION_NOTES=release proccess definition
+QUERY_PROCCESS_DEFINITION_BY_ID_NOTES=query proccess definition by id
+QUERY_PROCCESS_DEFINITION_LIST_NOTES=query proccess definition list
+QUERY_PROCCESS_DEFINITION_LIST_PAGING_NOTES=query proccess definition list paging
+PAGE_NO=page no
+PROCESS_INSTANCE_ID=process instance id
+PROCESS_INSTANCE_JSON=process instance info(json format)
+SCHEDULE_TIME=schedule time
+SYNC_DEFINE=update the information of the process instance to the process definition\
+
+RECOVERY_PROCESS_INSTANCE_FLAG=whether to recovery process instance 
+SEARCH_VAL=search val
+USER_ID=user id
+PAGE_SIZE=page size
+LIMIT=limit
+VIEW_TREE_NOTES=view tree
+GET_NODE_LIST_BY_DEFINITION_ID_NOTES=get task node list by process definition id
+PROCESS_DEFINITION_ID_LIST=process definition id list
+QUERY_PROCESS_INSTANCE_BY_ID_NOTES=query process instance by process instance id
+DELETE_PROCESS_INSTANCE_BY_ID_NOTES=delete process instance by process instance id
+TASK_ID=task instance id
+SKIP_LINE_NUM=skip line num
+QUERY_TASK_INSTANCE_LOG_NOTES=query task instance log 
+DOWNLOAD_TASK_INSTANCE_LOG_NOTES=download task instance log
+USERS_TAG=users related operation
+SCHEDULER_TAG=scheduler related operation
+CREATE_SCHEDULE_NOTES=create schedule 
+CREATE_USER_NOTES=create user
+TENANT_ID=tenant id
+QUEUE=queue
+EMAIL=email
+PHONE=phone
+QUERY_USER_LIST_NOTES=query user list 
+UPDATE_USER_NOTES=update user
+DELETE_USER_BY_ID_NOTES=delete user by id
+GRANT_PROJECT_NOTES=GRANT PROJECT 
+PROJECT_IDS=project ids(string format, multiple projects separated by ",")
+GRANT_RESOURCE_NOTES=grant resource file
+RESOURCE_IDS=resource ids(string format, multiple resources separated by ",")
+GET_USER_INFO_NOTES=get user info 
+LIST_USER_NOTES=list user
+VERIFY_USER_NAME_NOTES=verify user name
+UNAUTHORIZED_USER_NOTES=cancel authorization
+ALERT_GROUP_ID=alert group id
+AUTHORIZED_USER_NOTES=authorized user
+GRANT_UDF_FUNC_NOTES=grant udf function
+UDF_IDS=udf ids(string format, multiple udf functions separated by ",")
+GRANT_DATASOURCE_NOTES=grant datasource 
+DATASOURCE_IDS=datasource ids(string format, multiple datasources separated by ",")
+QUERY_SUBPROCESS_INSTANCE_BY_TASK_ID_NOTES=query subprocess instance by task instance id
+QUERY_PARENT_PROCESS_INSTANCE_BY_SUB_PROCESS_INSTANCE_ID_NOTES=query parent process instance info by sub process instance id
+QUERY_PROCESS_INSTANCE_GLOBAL_VARIABLES_AND_LOCAL_VARIABLES_NOTES=query process instance global variables and local variables
+VIEW_GANTT_NOTES=view gantt 
+SUB_PROCESS_INSTANCE_ID=sub process instance id
+TASK_NAME=task instance name
+TASK_INSTANCE_TAG=task instance related operation
+LOGGER_TAG=log related operation
+PROCESS_INSTANCE_TAG=process instance related operation
+EXECUTION_STATUS=runing status for workflow and task nodes
+HOST=ip address of running task
+START_DATE=start date
+END_DATE=end date
+QUERY_TASK_LIST_BY_PROCESS_INSTANCE_ID_NOTES=query task list by process instance id
+UPDATE_DATA_SOURCE_NOTES=update data source
+DATA_SOURCE_ID=DATA SOURCE ID
+QUERY_DATA_SOURCE_NOTES=query data source by id
+QUERY_DATA_SOURCE_LIST_BY_TYPE_NOTES=query data source list by database type
+QUERY_DATA_SOURCE_LIST_PAGING_NOTES=query data source list paging
+CONNECT_DATA_SOURCE_NOTES=CONNECT DATA SOURCE 
+CONNECT_DATA_SOURCE_TEST_NOTES=connect data source test 
+DELETE_DATA_SOURCE_NOTES=delete data source 
+VERIFY_DATA_SOURCE_NOTES=verify data source
+UNAUTHORIZED_DATA_SOURCE_NOTES=unauthorized data source
+AUTHORIZED_DATA_SOURCE_NOTES=authorized data source
+DELETE_SCHEDULER_BY_ID_NOTES=delete scheduler by id
diff --git a/escheduler-api/src/main/resources/i18n/messages_en_US.properties b/escheduler-api/src/main/resources/i18n/messages_en_US.properties
new file mode 100644
index 0000000..a663c71
--- /dev/null
+++ b/escheduler-api/src/main/resources/i18n/messages_en_US.properties
@@ -0,0 +1,229 @@
+QUERY_SCHEDULE_LIST_NOTES=query schedule list
+EXECUTE_PROCESS_TAG=execute process related operation
+PROCESS_INSTANCE_EXECUTOR_TAG=process instance executor related operation
+RUN_PROCESS_INSTANCE_NOTES=run process instance 
+START_NODE_LIST=start node list(node name)
+TASK_DEPEND_TYPE=task depend type
+COMMAND_TYPE=command type
+RUN_MODE=run mode
+TIMEOUT=timeout
+EXECUTE_ACTION_TO_PROCESS_INSTANCE_NOTES=execute action to process instance 
+EXECUTE_TYPE=execute type
+START_CHECK_PROCESS_DEFINITION_NOTES=start check process definition 
+GET_RECEIVER_CC_NOTES=query receiver cc 
+DESC=description
+GROUP_NAME=group name
+GROUP_TYPE=group type
+QUERY_ALERT_GROUP_LIST_NOTES=query alert group list 
+UPDATE_ALERT_GROUP_NOTES=update alert group 
+DELETE_ALERT_GROUP_BY_ID_NOTES=delete alert group by id 
+VERIFY_ALERT_GROUP_NAME_NOTES=verify alert group name, check alert group exist or not 
+GRANT_ALERT_GROUP_NOTES=grant alert group 
+USER_IDS=user id list
+ALERT_GROUP_TAG=alert group related operation
+CREATE_ALERT_GROUP_NOTES=create alert group 
+WORKER_GROUP_TAG=worker group related operation
+SAVE_WORKER_GROUP_NOTES=create worker group
+WORKER_GROUP_NAME=worker group name
+WORKER_IP_LIST=worker ip list, eg. 192.168.1.1,192.168.1.2
+QUERY_WORKER_GROUP_PAGING_NOTES=query worker group paging
+QUERY_WORKER_GROUP_LIST_NOTES=query worker group list 
+DELETE_WORKER_GROUP_BY_ID_NOTES=delete worker group by id 
+DATA_ANALYSIS_TAG=analysis related operation of task state
+COUNT_TASK_STATE_NOTES=count task state 
+COUNT_PROCESS_INSTANCE_NOTES=count process instance state
+COUNT_PROCESS_DEFINITION_BY_USER_NOTES=count process definition by user 
+COUNT_COMMAND_STATE_NOTES=count command state 
+COUNT_QUEUE_STATE_NOTES=count the running status of the task in the queue\
+
+ACCESS_TOKEN_TAG=access token related operation
+MONITOR_TAG=monitor related operation
+MASTER_LIST_NOTES=master server list
+WORKER_LIST_NOTES=worker server list
+QUERY_DATABASE_STATE_NOTES=query database state 
+QUERY_ZOOKEEPER_STATE_NOTES=QUERY ZOOKEEPER STATE 
+TASK_STATE=task instance state
+SOURCE_TABLE=SOURCE TABLE
+DEST_TABLE=dest table
+TASK_DATE=task date
+QUERY_HISTORY_TASK_RECORD_LIST_PAGING_NOTES=query history task record list paging
+DATA_SOURCE_TAG=data source related operation
+CREATE_DATA_SOURCE_NOTES=create data source
+DATA_SOURCE_NAME=data source name
+DATA_SOURCE_NOTE=data source desc
+DB_TYPE=database type
+DATA_SOURCE_HOST=DATA SOURCE HOST
+DATA_SOURCE_PORT=data source port
+DATABASE_NAME=database name
+QUEUE_TAG=queue related operation
+QUERY_QUEUE_LIST_NOTES=query queue list 
+QUERY_QUEUE_LIST_PAGING_NOTES=query queue list paging  
+CREATE_QUEUE_NOTES=create queue
+YARN_QUEUE_NAME=yarn(hadoop) queue name
+QUEUE_ID=queue id
+TENANT_DESC=tenant desc
+QUERY_TENANT_LIST_PAGING_NOTES=query tenant list paging 
+QUERY_TENANT_LIST_NOTES=query tenant list 
+UPDATE_TENANT_NOTES=update tenant 
+DELETE_TENANT_NOTES=delete tenant 
+RESOURCES_TAG=resource center related operation
+CREATE_RESOURCE_NOTES=create resource 
+RESOURCE_TYPE=resource file type
+RESOURCE_NAME=resource name
+RESOURCE_DESC=resource file desc
+RESOURCE_FILE=resource file
+RESOURCE_ID=resource id
+QUERY_RESOURCE_LIST_NOTES=query resource list
+DELETE_RESOURCE_BY_ID_NOTES=delete resource by id
+VIEW_RESOURCE_BY_ID_NOTES=view resource by id
+ONLINE_CREATE_RESOURCE_NOTES=online create resource 
+SUFFIX=resource file suffix
+CONTENT=resource file content
+UPDATE_RESOURCE_NOTES=edit resource file online
+DOWNLOAD_RESOURCE_NOTES=download resource file
+CREATE_UDF_FUNCTION_NOTES=create udf function 
+UDF_TYPE=UDF type
+FUNC_NAME=function name
+CLASS_NAME=package and class name
+ARG_TYPES=arguments
+UDF_DESC=udf desc
+VIEW_UDF_FUNCTION_NOTES=view udf function 
+UPDATE_UDF_FUNCTION_NOTES=update udf function 
+QUERY_UDF_FUNCTION_LIST_PAGING_NOTES=query udf function list paging 
+VERIFY_UDF_FUNCTION_NAME_NOTES=verify udf function name 
+DELETE_UDF_FUNCTION_NOTES=delete udf function 
+AUTHORIZED_FILE_NOTES=authorized file 
+UNAUTHORIZED_FILE_NOTES=unauthorized file 
+AUTHORIZED_UDF_FUNC_NOTES=authorized udf func 
+UNAUTHORIZED_UDF_FUNC_NOTES=unauthorized udf func 
+VERIFY_QUEUE_NOTES=verify queue 
+TENANT_TAG=tenant related operation
+CREATE_TENANT_NOTES=create tenant 
+TENANT_CODE=tenant code
+TENANT_NAME=tenant name
+QUEUE_NAME=queue name
+PASSWORD=password
+DATA_SOURCE_OTHER=jdbc connection params, format:{"key1":"value1",...}
+PROJECT_TAG=project related operation
+CREATE_PROJECT_NOTES=create project 
+PROJECT_DESC=project description
+UPDATE_PROJECT_NOTES=update project 
+PROJECT_ID=project id
+QUERY_PROJECT_BY_ID_NOTES=query project info by project id
+QUERY_PROJECT_LIST_PAGING_NOTES=QUERY PROJECT LIST PAGING 
+DELETE_PROJECT_BY_ID_NOTES=delete project by id 
+QUERY_UNAUTHORIZED_PROJECT_NOTES=query unauthorized project
+QUERY_AUTHORIZED_PROJECT_NOTES=query authorized project
+TASK_RECORD_TAG=task record related operation
+QUERY_TASK_RECORD_LIST_PAGING_NOTES=query task record list paging 
+CREATE_TOKEN_NOTES=create token ,note: please login first
+QUERY_ACCESS_TOKEN_LIST_NOTES=query access token list paging
+SCHEDULE=schedule
+WARNING_TYPE=warning type(sending strategy)
+WARNING_GROUP_ID=warning group id
+FAILURE_STRATEGY=failure strategy
+RECEIVERS=receivers
+RECEIVERS_CC=receivers cc
+WORKER_GROUP_ID=worker server group id
+PROCESS_INSTANCE_PRIORITY=process instance priority
+UPDATE_SCHEDULE_NOTES=update schedule 
+SCHEDULE_ID=schedule id
+ONLINE_SCHEDULE_NOTES=online schedule
+OFFLINE_SCHEDULE_NOTES=offline schedule 
+QUERY_SCHEDULE_NOTES=query schedule 
+QUERY_SCHEDULE_LIST_PAGING_NOTES=query schedule list paging
+LOGIN_TAG=User login related operations
+USER_NAME=user name
+PROJECT_NAME=project name
+CREATE_PROCESS_DEFINITION_NOTES=create process definition
+PROCESS_DEFINITION_NAME=process definition name
+PROCESS_DEFINITION_JSON=process definition detail info (json format)
+PROCESS_DEFINITION_LOCATIONS=process definition node locations info (json format)
+PROCESS_INSTANCE_LOCATIONS=process instance node locations info (json format)
+PROCESS_DEFINITION_CONNECTS=process definition node connects info (json format)
+PROCESS_INSTANCE_CONNECTS=process instance node connects info (json format)
+PROCESS_DEFINITION_DESC=process definition desc
+PROCESS_DEFINITION_TAG=process definition related opertation
+SIGNOUT_NOTES=logout
+USER_PASSWORD=user password
+UPDATE_PROCESS_INSTANCE_NOTES=update process instance
+QUERY_PROCESS_INSTANCE_LIST_NOTES=query process instance list
+VERIFY_PROCCESS_DEFINITION_NAME_NOTES=verify proccess definition name
+LOGIN_NOTES=user login
+UPDATE_PROCCESS_DEFINITION_NOTES=update proccess definition
+PROCESS_DEFINITION_ID=process definition id
+RELEASE_PROCCESS_DEFINITION_NOTES=release proccess definition
+QUERY_PROCCESS_DEFINITION_BY_ID_NOTES=query proccess definition by id
+QUERY_PROCCESS_DEFINITION_LIST_NOTES=query proccess definition list
+QUERY_PROCCESS_DEFINITION_LIST_PAGING_NOTES=query proccess definition list paging
+PAGE_NO=page no
+PROCESS_INSTANCE_ID=process instance id
+PROCESS_INSTANCE_JSON=process instance info(json format)
+SCHEDULE_TIME=schedule time
+SYNC_DEFINE=update the information of the process instance to the process definition\
+
+RECOVERY_PROCESS_INSTANCE_FLAG=whether to recovery process instance 
+SEARCH_VAL=search val
+USER_ID=user id
+PAGE_SIZE=page size
+LIMIT=limit
+VIEW_TREE_NOTES=view tree
+GET_NODE_LIST_BY_DEFINITION_ID_NOTES=get task node list by process definition id
+PROCESS_DEFINITION_ID_LIST=process definition id list
+QUERY_PROCESS_INSTANCE_BY_ID_NOTES=query process instance by process instance id
+DELETE_PROCESS_INSTANCE_BY_ID_NOTES=delete process instance by process instance id
+TASK_ID=task instance id
+SKIP_LINE_NUM=skip line num
+QUERY_TASK_INSTANCE_LOG_NOTES=query task instance log 
+DOWNLOAD_TASK_INSTANCE_LOG_NOTES=download task instance log
+USERS_TAG=users related operation
+SCHEDULER_TAG=scheduler related operation
+CREATE_SCHEDULE_NOTES=create schedule 
+CREATE_USER_NOTES=create user
+TENANT_ID=tenant id
+QUEUE=queue
+EMAIL=email
+PHONE=phone
+QUERY_USER_LIST_NOTES=query user list 
+UPDATE_USER_NOTES=update user
+DELETE_USER_BY_ID_NOTES=delete user by id
+GRANT_PROJECT_NOTES=GRANT PROJECT 
+PROJECT_IDS=project ids(string format, multiple projects separated by ",")
+GRANT_RESOURCE_NOTES=grant resource file
+RESOURCE_IDS=resource ids(string format, multiple resources separated by ",")
+GET_USER_INFO_NOTES=get user info 
+LIST_USER_NOTES=list user
+VERIFY_USER_NAME_NOTES=verify user name
+UNAUTHORIZED_USER_NOTES=cancel authorization
+ALERT_GROUP_ID=alert group id
+AUTHORIZED_USER_NOTES=authorized user
+GRANT_UDF_FUNC_NOTES=grant udf function
+UDF_IDS=udf ids(string format, multiple udf functions separated by ",")
+GRANT_DATASOURCE_NOTES=grant datasource 
+DATASOURCE_IDS=datasource ids(string format, multiple datasources separated by ",")
+QUERY_SUBPROCESS_INSTANCE_BY_TASK_ID_NOTES=query subprocess instance by task instance id
+QUERY_PARENT_PROCESS_INSTANCE_BY_SUB_PROCESS_INSTANCE_ID_NOTES=query parent process instance info by sub process instance id
+QUERY_PROCESS_INSTANCE_GLOBAL_VARIABLES_AND_LOCAL_VARIABLES_NOTES=query process instance global variables and local variables
+VIEW_GANTT_NOTES=view gantt 
+SUB_PROCESS_INSTANCE_ID=sub process instance id
+TASK_NAME=task instance name
+TASK_INSTANCE_TAG=task instance related operation
+LOGGER_TAG=log related operation
+PROCESS_INSTANCE_TAG=process instance related operation
+EXECUTION_STATUS=runing status for workflow and task nodes
+HOST=ip address of running task
+START_DATE=start date
+END_DATE=end date
+QUERY_TASK_LIST_BY_PROCESS_INSTANCE_ID_NOTES=query task list by process instance id
+UPDATE_DATA_SOURCE_NOTES=update data source
+DATA_SOURCE_ID=DATA SOURCE ID
+QUERY_DATA_SOURCE_NOTES=query data source by id
+QUERY_DATA_SOURCE_LIST_BY_TYPE_NOTES=query data source list by database type
+QUERY_DATA_SOURCE_LIST_PAGING_NOTES=query data source list paging
+CONNECT_DATA_SOURCE_NOTES=CONNECT DATA SOURCE 
+CONNECT_DATA_SOURCE_TEST_NOTES=connect data source test 
+DELETE_DATA_SOURCE_NOTES=delete data source 
+VERIFY_DATA_SOURCE_NOTES=verify data source
+UNAUTHORIZED_DATA_SOURCE_NOTES=unauthorized data source
+AUTHORIZED_DATA_SOURCE_NOTES=authorized data source
+DELETE_SCHEDULER_BY_ID_NOTES=delete scheduler by id
diff --git a/escheduler-api/src/main/resources/i18n/messages_zh_CN.properties b/escheduler-api/src/main/resources/i18n/messages_zh_CN.properties
new file mode 100644
index 0000000..b0d6694
--- /dev/null
+++ b/escheduler-api/src/main/resources/i18n/messages_zh_CN.properties
@@ -0,0 +1,227 @@
+QUERY_SCHEDULE_LIST_NOTES=查询定时列表
+PROCESS_INSTANCE_EXECUTOR_TAG=流程实例执行相关操作
+RUN_PROCESS_INSTANCE_NOTES=运行流程实例
+START_NODE_LIST=开始节点列表(节点name)
+TASK_DEPEND_TYPE=任务依赖类型
+COMMAND_TYPE=指令类型
+RUN_MODE=运行模式
+TIMEOUT=超时时间
+EXECUTE_ACTION_TO_PROCESS_INSTANCE_NOTES=执行流程实例的各种操作(暂停、停止、重跑、恢复等)
+EXECUTE_TYPE=执行类型
+START_CHECK_PROCESS_DEFINITION_NOTES=检查流程定义
+DESC=备注(描述)
+GROUP_NAME=组名称
+GROUP_TYPE=组类型
+QUERY_ALERT_GROUP_LIST_NOTES=告警组列表\
+
+UPDATE_ALERT_GROUP_NOTES=编辑(更新)告警组
+DELETE_ALERT_GROUP_BY_ID_NOTES=删除告警组通过ID
+VERIFY_ALERT_GROUP_NAME_NOTES=检查告警组是否存在
+GRANT_ALERT_GROUP_NOTES=授权告警组
+USER_IDS=用户ID列表
+ALERT_GROUP_TAG=告警组相关操作
+WORKER_GROUP_TAG=Worker分组管理
+SAVE_WORKER_GROUP_NOTES=创建Worker分组\
+
+WORKER_GROUP_NAME=Worker分组名称
+WORKER_IP_LIST=Worker ip列表,注意:多个IP地址以逗号分割\
+
+QUERY_WORKER_GROUP_PAGING_NOTES=Worker分组管理
+QUERY_WORKER_GROUP_LIST_NOTES=查询worker group分组
+DELETE_WORKER_GROUP_BY_ID_NOTES=删除worker group通过ID
+DATA_ANALYSIS_TAG=任务状态分析相关操作
+COUNT_TASK_STATE_NOTES=任务状态统计
+COUNT_PROCESS_INSTANCE_NOTES=统计流程实例状态
+COUNT_PROCESS_DEFINITION_BY_USER_NOTES=统计用户创建的流程定义
+COUNT_COMMAND_STATE_NOTES=统计命令状态
+COUNT_QUEUE_STATE_NOTES=统计队列里任务状态
+ACCESS_TOKEN_TAG=access token相关操作,需要先登录
+MONITOR_TAG=监控相关操作
+MASTER_LIST_NOTES=master服务列表
+WORKER_LIST_NOTES=worker服务列表
+QUERY_DATABASE_STATE_NOTES=查询数据库状态
+QUERY_ZOOKEEPER_STATE_NOTES=查询Zookeeper状态
+TASK_STATE=任务实例状态
+SOURCE_TABLE=源表
+DEST_TABLE=目标表
+TASK_DATE=任务时间
+QUERY_HISTORY_TASK_RECORD_LIST_PAGING_NOTES=分页查询历史任务记录列表
+DATA_SOURCE_TAG=数据源相关操作
+CREATE_DATA_SOURCE_NOTES=创建数据源
+DATA_SOURCE_NAME=数据源名称
+DATA_SOURCE_NOTE=数据源描述
+DB_TYPE=数据源类型
+DATA_SOURCE_HOST=IP主机名
+DATA_SOURCE_PORT=数据源端口
+DATABASE_NAME=数据库名
+QUEUE_TAG=队列相关操作
+QUERY_QUEUE_LIST_NOTES=查询队列列表
+QUERY_QUEUE_LIST_PAGING_NOTES=分页查询队列列表
+CREATE_QUEUE_NOTES=创建队列
+YARN_QUEUE_NAME=hadoop yarn队列名
+QUEUE_ID=队列ID
+TENANT_DESC=租户描述
+QUERY_TENANT_LIST_PAGING_NOTES=分页查询租户列表
+QUERY_TENANT_LIST_NOTES=查询租户列表
+UPDATE_TENANT_NOTES=更新租户
+DELETE_TENANT_NOTES=删除租户
+RESOURCES_TAG=资源中心相关操作
+CREATE_RESOURCE_NOTES=创建资源
+RESOURCE_TYPE=资源文件类型
+RESOURCE_NAME=资源文件名称
+RESOURCE_DESC=资源文件描述
+RESOURCE_FILE=资源文件
+RESOURCE_ID=资源ID
+QUERY_RESOURCE_LIST_NOTES=查询资源列表
+DELETE_RESOURCE_BY_ID_NOTES=删除资源通过ID
+VIEW_RESOURCE_BY_ID_NOTES=浏览资源通通过ID
+ONLINE_CREATE_RESOURCE_NOTES=在线创建资源
+SUFFIX=资源文件后缀
+CONTENT=资源文件内容
+UPDATE_RESOURCE_NOTES=在线更新资源文件
+DOWNLOAD_RESOURCE_NOTES=下载资源文件
+CREATE_UDF_FUNCTION_NOTES=创建UDF函数
+UDF_TYPE=UDF类型
+FUNC_NAME=函数名称
+CLASS_NAME=包名类名
+ARG_TYPES=参数
+UDF_DESC=udf描述,使用说明
+VIEW_UDF_FUNCTION_NOTES=查看udf函数
+UPDATE_UDF_FUNCTION_NOTES=更新udf函数
+QUERY_UDF_FUNCTION_LIST_PAGING_NOTES=分页查询udf函数列表
+VERIFY_UDF_FUNCTION_NAME_NOTES=验证udf函数名
+DELETE_UDF_FUNCTION_NOTES=删除UDF函数
+AUTHORIZED_FILE_NOTES=授权文件
+UNAUTHORIZED_FILE_NOTES=取消授权文件
+AUTHORIZED_UDF_FUNC_NOTES=授权udf函数
+UNAUTHORIZED_UDF_FUNC_NOTES=取消udf函数授权
+VERIFY_QUEUE_NOTES=验证队列
+TENANT_TAG=租户相关操作
+CREATE_TENANT_NOTES=创建租户
+TENANT_CODE=租户编码
+TENANT_NAME=租户名称
+QUEUE_NAME=队列名
+PASSWORD=密码
+DATA_SOURCE_OTHER=jdbc连接参数,格式为:{"key1":"value1",...}
+PROJECT_TAG=项目相关操作
+CREATE_PROJECT_NOTES=创建项目
+PROJECT_DESC=项目描述
+UPDATE_PROJECT_NOTES=更新项目
+PROJECT_ID=项目ID
+QUERY_PROJECT_BY_ID_NOTES=通过项目ID查询项目信息
+QUERY_PROJECT_LIST_PAGING_NOTES=分页查询项目列表
+DELETE_PROJECT_BY_ID_NOTES=删除项目通过ID
+QUERY_UNAUTHORIZED_PROJECT_NOTES=查询未授权的项目
+QUERY_AUTHORIZED_PROJECT_NOTES=查询授权项目
+TASK_RECORD_TAG=任务记录相关操作
+QUERY_TASK_RECORD_LIST_PAGING_NOTES=分页查询任务记录列表
+CREATE_TOKEN_NOTES=创建token,注意需要先登录
+QUERY_ACCESS_TOKEN_LIST_NOTES=分页查询access token列表
+SCHEDULE=定时
+WARNING_TYPE=发送策略
+WARNING_GROUP_ID=发送组ID
+FAILURE_STRATEGY=失败策略
+RECEIVERS=收件人
+RECEIVERS_CC=收件人(抄送)
+WORKER_GROUP_ID=Worker Server分组ID
+PROCESS_INSTANCE_PRIORITY=流程实例优先级
+UPDATE_SCHEDULE_NOTES=更新定时
+SCHEDULE_ID=定时ID
+ONLINE_SCHEDULE_NOTES=定时上线
+OFFLINE_SCHEDULE_NOTES=定时下线
+QUERY_SCHEDULE_NOTES=查询定时
+QUERY_SCHEDULE_LIST_PAGING_NOTES=分页查询定时
+LOGIN_TAG=用户登录相关操作
+USER_NAME=用户名
+PROJECT_NAME=项目名称
+CREATE_PROCESS_DEFINITION_NOTES=创建流程定义
+PROCESS_DEFINITION_NAME=流程定义名称
+PROCESS_DEFINITION_JSON=流程定义详细信息(json格式)
+PROCESS_DEFINITION_LOCATIONS=流程定义节点坐标位置信息(json格式)
+PROCESS_INSTANCE_LOCATIONS=流程实例节点坐标位置信息(json格式)
+PROCESS_DEFINITION_CONNECTS=流程定义节点图标连接信息(json格式)
+PROCESS_INSTANCE_CONNECTS=流程实例节点图标连接信息(json格式)
+PROCESS_DEFINITION_DESC=流程定义描述信息
+PROCESS_DEFINITION_TAG=流程定义相关操作
+SIGNOUT_NOTES=退出登录
+USER_PASSWORD=用户密码
+UPDATE_PROCESS_INSTANCE_NOTES=更新流程实例
+QUERY_PROCESS_INSTANCE_LIST_NOTES=查询流程实例列表
+VERIFY_PROCCESS_DEFINITION_NAME_NOTES=验证流程定义名字
+LOGIN_NOTES=用户登录
+UPDATE_PROCCESS_DEFINITION_NOTES=更新流程定义
+PROCESS_DEFINITION_ID=流程定义ID
+RELEASE_PROCCESS_DEFINITION_NOTES=发布流程定义
+QUERY_PROCCESS_DEFINITION_BY_ID_NOTES=查询流程定义通过流程定义ID
+QUERY_PROCCESS_DEFINITION_LIST_NOTES=查询流程定义列表
+QUERY_PROCCESS_DEFINITION_LIST_PAGING_NOTES=分页查询流程定义列表
+PAGE_NO=页码号
+PROCESS_INSTANCE_ID=流程实例ID
+PROCESS_INSTANCE_JSON=流程实例信息(json格式)
+SCHEDULE_TIME=定时时间
+SYNC_DEFINE=更新流程实例的信息是否同步到流程定义
+RECOVERY_PROCESS_INSTANCE_FLAG=是否恢复流程实例
+SEARCH_VAL=搜索值
+USER_ID=用户ID
+PAGE_SIZE=页大小
+LIMIT=显示多少条
+VIEW_TREE_NOTES=树状图
+GET_NODE_LIST_BY_DEFINITION_ID_NOTES=获得任务节点列表通过流程定义ID
+PROCESS_DEFINITION_ID_LIST=流程定义id列表
+QUERY_PROCESS_INSTANCE_BY_ID_NOTES=查询流程实例通过流程实例ID
+DELETE_PROCESS_INSTANCE_BY_ID_NOTES=删除流程实例通过流程实例ID
+TASK_ID=任务实例ID
+SKIP_LINE_NUM=忽略行数
+QUERY_TASK_INSTANCE_LOG_NOTES=查询任务实例日志
+DOWNLOAD_TASK_INSTANCE_LOG_NOTES=下载任务实例日志
+USERS_TAG=用户相关操作
+SCHEDULER_TAG=定时相关操作
+CREATE_SCHEDULE_NOTES=创建定时
+CREATE_USER_NOTES=创建用户
+TENANT_ID=租户ID
+QUEUE=使用的队列
+EMAIL=邮箱
+PHONE=手机号
+QUERY_USER_LIST_NOTES=查询用户列表
+UPDATE_USER_NOTES=更新用户
+DELETE_USER_BY_ID_NOTES=删除用户通过ID
+GRANT_PROJECT_NOTES=授权项目
+PROJECT_IDS=项目IDS(字符串格式,多个项目以","分割)
+GRANT_RESOURCE_NOTES=授权资源文件
+RESOURCE_IDS=资源ID列表(字符串格式,多个资源ID以","分割)
+GET_USER_INFO_NOTES=获取用户信息
+LIST_USER_NOTES=用户列表
+VERIFY_USER_NAME_NOTES=验证用户名
+UNAUTHORIZED_USER_NOTES=取消授权
+ALERT_GROUP_ID=报警组ID
+AUTHORIZED_USER_NOTES=授权用户
+GRANT_UDF_FUNC_NOTES=授权udf函数
+UDF_IDS=udf函数id列表(字符串格式,多个udf函数ID以","分割)
+GRANT_DATASOURCE_NOTES=授权数据源
+DATASOURCE_IDS=数据源ID列表(字符串格式,多个数据源ID以","分割)
+QUERY_SUBPROCESS_INSTANCE_BY_TASK_ID_NOTES=查询子流程实例通过任务实例ID
+QUERY_PARENT_PROCESS_INSTANCE_BY_SUB_PROCESS_INSTANCE_ID_NOTES=查询父流程实例信息通过子流程实例ID
+QUERY_PROCESS_INSTANCE_GLOBAL_VARIABLES_AND_LOCAL_VARIABLES_NOTES=查询流程实例全局变量和局部变量
+VIEW_GANTT_NOTES=浏览Gantt图
+SUB_PROCESS_INSTANCE_ID=子流程是咧ID
+TASK_NAME=任务实例名
+TASK_INSTANCE_TAG=任务实例相关操作
+LOGGER_TAG=日志相关操作
+PROCESS_INSTANCE_TAG=流程实例相关操作
+EXECUTION_STATUS=工作流和任务节点的运行状态
+HOST=运行任务的主机IP地址
+START_DATE=开始时间
+END_DATE=结束时间
+QUERY_TASK_LIST_BY_PROCESS_INSTANCE_ID_NOTES=通过流程实例ID查询任务列表
+UPDATE_DATA_SOURCE_NOTES=更新数据源
+DATA_SOURCE_ID=数据源ID
+QUERY_DATA_SOURCE_NOTES=查询数据源通过ID
+QUERY_DATA_SOURCE_LIST_BY_TYPE_NOTES=查询数据源列表通过数据源类型
+QUERY_DATA_SOURCE_LIST_PAGING_NOTES=分页查询数据源列表
+CONNECT_DATA_SOURCE_NOTES=连接数据源
+CONNECT_DATA_SOURCE_TEST_NOTES=连接数据源测试
+DELETE_DATA_SOURCE_NOTES=删除数据源
+VERIFY_DATA_SOURCE_NOTES=验证数据源
+UNAUTHORIZED_DATA_SOURCE_NOTES=未授权的数据源
+AUTHORIZED_DATA_SOURCE_NOTES=授权的数据源
+DELETE_SCHEDULER_BY_ID_NOTES=根据定时id删除定时数据
diff --git a/escheduler-api/src/test/java/cn/escheduler/api/controller/SchedulerControllerTest.java b/escheduler-api/src/test/java/cn/escheduler/api/controller/SchedulerControllerTest.java
index aee5371..e58f436 100644
--- a/escheduler-api/src/test/java/cn/escheduler/api/controller/SchedulerControllerTest.java
+++ b/escheduler-api/src/test/java/cn/escheduler/api/controller/SchedulerControllerTest.java
@@ -64,4 +64,17 @@
         Assert.assertEquals(Status.SUCCESS.getCode(),result.getCode().intValue());
         logger.info(mvcResult.getResponse().getContentAsString());
     }
+    @Test
+    public void previewSchedule() throws Exception {
+        MvcResult mvcResult = mockMvc.perform(post("/projects/{projectName}/schedule/preview","li_test_1")
+                .header("sessionId", "c24ed9d9-1c20-48a0-bd9c-5cfca14a4dcb")
+                .param("schedule","{'startTime':'2019-06-10 00:00:00','endTime':'2019-06-13 00:00:00','crontab':'0 0 3/6 * * ? *'}"))
+                .andExpect(status().isCreated())
+                .andExpect(content().contentType(MediaType.APPLICATION_JSON_UTF8))
+                .andReturn();
+
+        Result result = JSONUtils.parseObject(mvcResult.getResponse().getContentAsString(), Result.class);
+        Assert.assertEquals(Status.SUCCESS.getCode(),result.getCode().intValue());
+        logger.info(mvcResult.getResponse().getContentAsString());
+    }
 }
\ No newline at end of file
diff --git a/escheduler-api/src/test/java/cn/escheduler/api/utils/ZookeeperMonitorUtilsTest.java b/escheduler-api/src/test/java/cn/escheduler/api/utils/ZookeeperMonitorUtilsTest.java
new file mode 100644
index 0000000..87a26ba
--- /dev/null
+++ b/escheduler-api/src/test/java/cn/escheduler/api/utils/ZookeeperMonitorUtilsTest.java
@@ -0,0 +1,29 @@
+package cn.escheduler.api.utils;
+
+import cn.escheduler.dao.model.MasterServer;
+import org.junit.Assert;
+import org.junit.Test;
+
+
+import java.util.List;
+
+public class ZookeeperMonitorUtilsTest {
+
+
+    @Test
+    public void testGetMasterLsit(){
+
+        ZookeeperMonitor zookeeperMonitor = new ZookeeperMonitor();
+
+
+        List<MasterServer> masterServerList = zookeeperMonitor.getMasterServers();
+
+        List<MasterServer> workerServerList = zookeeperMonitor.getWorkerServers();
+
+        Assert.assertEquals(masterServerList.size(), 1);
+        Assert.assertEquals(workerServerList.size(), 1);
+
+
+    }
+
+}
\ No newline at end of file
diff --git a/escheduler-common/pom.xml b/escheduler-common/pom.xml
index cf5ba60..35b025b 100644
--- a/escheduler-common/pom.xml
+++ b/escheduler-common/pom.xml
@@ -4,7 +4,7 @@
 	<parent>
 		<artifactId>escheduler</artifactId>
 		<groupId>cn.analysys</groupId>
-		<version>1.0.4-SNAPSHOT</version>
+		<version>1.1.0-SNAPSHOT</version>
 	</parent>
 	<artifactId>escheduler-common</artifactId>
 	<name>escheduler-common</name>
@@ -264,10 +264,6 @@
 		</dependency>
 
 		<dependency>
-			<groupId>org.apache.httpcomponents</groupId>
-			<artifactId>httpclient</artifactId>
-		</dependency>
-		<dependency>
 			<groupId>org.apache.hive</groupId>
 			<artifactId>hive-jdbc</artifactId>
 			<exclusions>
diff --git a/escheduler-common/src/main/java/cn/escheduler/common/Constants.java b/escheduler-common/src/main/java/cn/escheduler/common/Constants.java
index e5137ef..6c39e0a 100644
--- a/escheduler-common/src/main/java/cn/escheduler/common/Constants.java
+++ b/escheduler-common/src/main/java/cn/escheduler/common/Constants.java
@@ -60,6 +60,23 @@
      */
     public static final String FS_DEFAULTFS = "fs.defaultFS";
 
+
+    /**
+     * fs s3a endpoint
+     */
+    public static final String FS_S3A_ENDPOINT = "fs.s3a.endpoint";
+
+    /**
+     * fs s3a access key
+     */
+    public static final String FS_S3A_ACCESS_KEY = "fs.s3a.access.key";
+
+    /**
+     * fs s3a secret key
+     */
+    public static final String FS_S3A_SECRET_KEY = "fs.s3a.secret.key";
+
+
     /**
      * yarn.resourcemanager.ha.rm.idsfs.defaultFS
      */
@@ -70,6 +87,11 @@
      */
     public static final String YARN_APPLICATION_STATUS_ADDRESS = "yarn.application.status.address";
 
+    /**
+     * hdfs configuration
+     * hdfs.root.user
+     */
+    public static final String HDFS_ROOT_USER = "hdfs.root.user";
 
     /**
      * hdfs configuration
@@ -118,9 +140,9 @@
     public static final String DEVELOPMENT_STATE = "development.state";
 
     /**
-     * hdfs.startup.state
+     * res.upload.startup.type
      */
-    public static final String HDFS_STARTUP_STATE = "hdfs.startup.state";
+    public static final String RES_UPLOAD_STARTUP_TYPE = "res.upload.startup.type";
 
     /**
      * zookeeper quorum
@@ -198,6 +220,11 @@
     public static final String SEMICOLON = ";";
 
     /**
+     * DOT .
+     */
+    public static final String DOT = ".";
+
+    /**
      * ZOOKEEPER_SESSION_TIMEOUT
      */
     public static final String ZOOKEEPER_SESSION_TIMEOUT = "zookeeper.session.timeout";
@@ -241,7 +268,11 @@
      */
     public static final String SCHEDULER_TASKS_QUEUE = "tasks_queue";
 
+    /**
+     * escheduler need kill tasks queue
+     */
     public static final String SCHEDULER_TASKS_KILL = "tasks_kill";
+
     public static final String ZOOKEEPER_SCHEDULER_ROOT = "zookeeper.escheduler.root";
 
     public static final String SCHEDULER_QUEUE_IMPL = "escheduler.queue.impl";
@@ -306,7 +337,7 @@
     /**
      * email regex
      */
-    public static final Pattern REGEX_MAIL_NAME = Pattern.compile("^[a-zA-Z0-9_-]+@[a-zA-Z0-9_-]+(\\.[a-zA-Z0-9_-]+)+$");
+    public static final Pattern REGEX_MAIL_NAME = Pattern.compile("^([a-z0-9A-Z]+[-|\\.]?)+[a-z0-9A-Z]@([a-z0-9A-Z]+(-[a-z0-9A-Z]+)?\\.)+[a-zA-Z]{2,}$");
 
     /**
      * read permission
@@ -341,11 +372,6 @@
      */
     public static final int MAX_TASK_TIMEOUT = 24 * 3600;
 
-    /**
-     * max task timeout
-     */
-    public static final int MAX_PROCESS_TIMEOUT = Integer.MAX_VALUE;
-
 
     /**
      * heartbeat threads number
@@ -462,6 +488,10 @@
 
     public static final String TASK_RECORD_PWD = "task.record.datasource.password";
 
+    public static final String DEFAULT = "Default";
+    public static final String PASSWORD = "password";
+    public static final String XXXXXX = "******";
+
     public static  String TASK_RECORD_TABLE_HIVE_LOG = "eamp_hive_log_hd";
 
     public static  String TASK_RECORD_TABLE_HISTORY_HIVE_LOG = "eamp_hive_hist_log_hd";
@@ -832,6 +862,43 @@
 
 
     /**
-     *
+     *  preview schedule execute count
      */
+    public static final int PREVIEW_SCHEDULE_EXECUTE_COUNT = 5;
+
+    /**
+     * java.security.krb5.conf
+     */
+    public static final String JAVA_SECURITY_KRB5_CONF = "java.security.krb5.conf";
+
+    /**
+     * java.security.krb5.conf.path
+     */
+    public static final String JAVA_SECURITY_KRB5_CONF_PATH = "java.security.krb5.conf.path";
+
+    /**
+     * hadoop.security.authentication
+     */
+    public static final String HADOOP_SECURITY_AUTHENTICATION = "hadoop.security.authentication";
+
+    /**
+     * hadoop.security.authentication
+     */
+    public static final String HADOOP_SECURITY_AUTHENTICATION_STARTUP_STATE = "hadoop.security.authentication.startup.state";
+
+
+    /**
+     * loginUserFromKeytab user
+     */
+    public static final String LOGIN_USER_KEY_TAB_USERNAME = "login.user.keytab.username";
+
+    /**
+     * default worker group id
+     */
+    public static final int DEFAULT_WORKER_ID = -1;
+
+    /**
+     * loginUserFromKeytab path
+     */
+    public static final String LOGIN_USER_KEY_TAB_PATH = "login.user.keytab.path";
 }
diff --git a/escheduler-common/src/main/java/cn/escheduler/common/enums/ExecutionStatus.java b/escheduler-common/src/main/java/cn/escheduler/common/enums/ExecutionStatus.java
index e2a7276..4efcc09 100644
--- a/escheduler-common/src/main/java/cn/escheduler/common/enums/ExecutionStatus.java
+++ b/escheduler-common/src/main/java/cn/escheduler/common/enums/ExecutionStatus.java
@@ -18,7 +18,7 @@
 
 
 /**
- * runing status for work flow and task nodes
+ * runing status for workflow and task nodes
  *
  */
 public enum ExecutionStatus {
diff --git a/escheduler-common/src/main/java/cn/escheduler/common/enums/ResUploadType.java b/escheduler-common/src/main/java/cn/escheduler/common/enums/ResUploadType.java
new file mode 100644
index 0000000..65d8be8
--- /dev/null
+++ b/escheduler-common/src/main/java/cn/escheduler/common/enums/ResUploadType.java
@@ -0,0 +1,29 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *    http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package cn.escheduler.common.enums;
+
+/**
+ * data base types
+ */
+public enum ResUploadType {
+  /**
+   * 0 hdfs
+   * 1 s3
+   * 2 none
+   */
+  HDFS,S3,NONE
+}
diff --git a/escheduler-common/src/main/java/cn/escheduler/common/enums/ZKNodeType.java b/escheduler-common/src/main/java/cn/escheduler/common/enums/ZKNodeType.java
new file mode 100644
index 0000000..371231f
--- /dev/null
+++ b/escheduler-common/src/main/java/cn/escheduler/common/enums/ZKNodeType.java
@@ -0,0 +1,15 @@
+package cn.escheduler.common.enums;
+
+/**
+ * zk node type
+ */
+public enum ZKNodeType {
+
+    /**
+     * 0 do not send warning;
+     * 1 send if process success;
+     * 2 send if process failed;
+     * 3 send if process ending;
+     */
+    MASTER, WORKER, DEAD_SERVER, TASK_QUEUE;
+}
diff --git a/escheduler-common/src/main/java/cn/escheduler/common/job/db/BaseDataSource.java b/escheduler-common/src/main/java/cn/escheduler/common/job/db/BaseDataSource.java
index af06240..f215d3e 100644
--- a/escheduler-common/src/main/java/cn/escheduler/common/job/db/BaseDataSource.java
+++ b/escheduler-common/src/main/java/cn/escheduler/common/job/db/BaseDataSource.java
@@ -46,6 +46,18 @@
   private String other;
 
   /**
+   * principal
+   */
+  private String principal;
+
+  public String getPrincipal() {
+    return principal;
+  }
+
+  public void setPrincipal(String principal) {
+    this.principal = principal;
+  }
+  /**
    * test whether the data source can be connected successfully
    * @throws Exception
    */
@@ -73,14 +85,14 @@
     this.password = password;
   }
 
-  public String getAddress() {
-    return address;
-  }
-
   public void setAddress(String address) {
     this.address = address;
   }
 
+  public String getAddress() {
+    return address;
+  }
+
   public String getDatabase() {
     return database;
   }
diff --git a/escheduler-common/src/main/java/cn/escheduler/common/job/db/HiveDataSource.java b/escheduler-common/src/main/java/cn/escheduler/common/job/db/HiveDataSource.java
index 28e3799..719c5eb 100644
--- a/escheduler-common/src/main/java/cn/escheduler/common/job/db/HiveDataSource.java
+++ b/escheduler-common/src/main/java/cn/escheduler/common/job/db/HiveDataSource.java
@@ -17,12 +17,12 @@
 package cn.escheduler.common.job.db;
 
 import org.apache.commons.lang3.StringUtils;
+import org.apache.hadoop.conf.Configuration;
+import org.apache.hadoop.security.UserGroupInformation;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
-import java.sql.Connection;
-import java.sql.DriverManager;
-import java.sql.SQLException;
+import java.sql.*;
 
 /**
  * data source of hive
@@ -32,6 +32,8 @@
   private static final Logger logger = LoggerFactory.getLogger(HiveDataSource.class);
 
 
+
+
   /**
    * gets the JDBC url for the data source connection
    * @return
@@ -43,7 +45,7 @@
       jdbcUrl += "/";
     }
 
-    jdbcUrl += getDatabase();
+    jdbcUrl += getDatabase() + ";principal=" + getPrincipal();
 
     if (StringUtils.isNotEmpty(getOther())) {
       jdbcUrl += ";" + getOther();
@@ -67,11 +69,10 @@
         try {
           con.close();
         } catch (SQLException e) {
-          logger.error("Postgre datasource try conn close conn error", e);
+          logger.error("hive datasource try conn close conn error", e);
           throw e;
         }
       }
     }
-
   }
 }
diff --git a/escheduler-common/src/main/java/cn/escheduler/common/job/db/SparkDataSource.java b/escheduler-common/src/main/java/cn/escheduler/common/job/db/SparkDataSource.java
index d9a24ee..13aa06e 100644
--- a/escheduler-common/src/main/java/cn/escheduler/common/job/db/SparkDataSource.java
+++ b/escheduler-common/src/main/java/cn/escheduler/common/job/db/SparkDataSource.java
@@ -31,7 +31,6 @@
 
   private static final Logger logger = LoggerFactory.getLogger(SparkDataSource.class);
 
-
   /**
    * gets the JDBC url for the data source connection
    * @return
@@ -43,7 +42,7 @@
       jdbcUrl += "/";
     }
 
-    jdbcUrl += getDatabase();
+    jdbcUrl += getDatabase() + ";principal=" + getPrincipal();
 
     if (StringUtils.isNotEmpty(getOther())) {
       jdbcUrl += ";" + getOther();
diff --git a/escheduler-common/src/main/java/cn/escheduler/common/queue/ITaskQueue.java b/escheduler-common/src/main/java/cn/escheduler/common/queue/ITaskQueue.java
index 4262932..6f6e979 100644
--- a/escheduler-common/src/main/java/cn/escheduler/common/queue/ITaskQueue.java
+++ b/escheduler-common/src/main/java/cn/escheduler/common/queue/ITaskQueue.java
@@ -24,20 +24,17 @@
     /**
      * take out all the elements
      *
-     * this method has deprecated
-     * use checkTaskExists instead
      *
      * @param key
      * @return
      */
-    @Deprecated
     List<String> getAllTasks(String key);
 
     /**
      * check task exists in the task queue or not
      *
      * @param key queue name
-     * @param task ${priority}_${processInstanceId}_${taskId}
+     * @param task ${processInstancePriority}_${processInstanceId}_${taskInstancePriority}_${taskId}
      * @return true if exists in the queue
      */
     boolean checkTaskExists(String key, String task);
@@ -54,10 +51,10 @@
      * an element pops out of the queue
      *
      * @param key  queue name
-     * @param remove  where remove the element
+     * @param n    how many elements to poll
      * @return
      */
-    String poll(String key, boolean remove);
+    List<String> poll(String key, int n);
 
     /**
      * remove a element from queue
diff --git a/escheduler-common/src/main/java/cn/escheduler/common/queue/TaskQueueFactory.java b/escheduler-common/src/main/java/cn/escheduler/common/queue/TaskQueueFactory.java
index c893106..2d17481 100644
--- a/escheduler-common/src/main/java/cn/escheduler/common/queue/TaskQueueFactory.java
+++ b/escheduler-common/src/main/java/cn/escheduler/common/queue/TaskQueueFactory.java
@@ -42,7 +42,7 @@
   public static ITaskQueue getTaskQueueInstance() {
     String queueImplValue = CommonUtils.getQueueImplValue();
     if (StringUtils.isNotBlank(queueImplValue)) {
-//      queueImplValue = StringUtils.trim(queueImplValue);
+//      queueImplValue = IpUtils.trim(queueImplValue);
 
 //      if (SCHEDULER_QUEUE_REDIS_IMPL.equals(queueImplValue)) {
 //        logger.info("task queue impl use reids ");
diff --git a/escheduler-common/src/main/java/cn/escheduler/common/queue/TaskQueueZkImpl.java b/escheduler-common/src/main/java/cn/escheduler/common/queue/TaskQueueZkImpl.java
index 28f696a..73f7499 100644
--- a/escheduler-common/src/main/java/cn/escheduler/common/queue/TaskQueueZkImpl.java
+++ b/escheduler-common/src/main/java/cn/escheduler/common/queue/TaskQueueZkImpl.java
@@ -19,17 +19,17 @@
 
 import cn.escheduler.common.Constants;
 import cn.escheduler.common.utils.Bytes;
+import cn.escheduler.common.utils.IpUtils;
+import cn.escheduler.common.utils.OSUtils;
 import cn.escheduler.common.zk.AbstractZKClient;
+import org.apache.commons.lang3.StringUtils;
 import org.apache.curator.framework.CuratorFramework;
 import org.apache.zookeeper.CreateMode;
 import org.apache.zookeeper.data.Stat;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
-import java.util.ArrayList;
-import java.util.HashSet;
-import java.util.List;
-import java.util.Set;
+import java.util.*;
 
 /**
  * A singleton of a task queue implemented with zookeeper
@@ -62,7 +62,6 @@
      * @param key   task queue name
      * @return
      */
-    @Deprecated
     @Override
     public List<String> getAllTasks(String key) {
         try {
@@ -80,7 +79,7 @@
      * check task exists in the task queue or not
      *
      * @param key       queue name
-     * @param task      ${priority}_${processInstanceId}_${taskId}
+     * @param task      ${processInstancePriority}_${processInstanceId}_${taskInstancePriority}_${taskId}
      * @return true if exists in the queue
      */
     @Override
@@ -110,7 +109,7 @@
      * add task to tasks queue
      *
      * @param key      task queue name
-     * @param value    ${priority}_${processInstanceId}_${taskId}
+     * @param value    ${processInstancePriority}_${processInstanceId}_${taskInstancePriority}_${taskId}_host1,host2,...
      */
     @Override
     public void add(String key, String value) {
@@ -118,9 +117,6 @@
             String taskIdPath = getTasksPath(key) + Constants.SINGLE_SLASH + value;
             String result = getZkClient().create().withMode(CreateMode.PERSISTENT).forPath(taskIdPath, Bytes.toBytes(value));
 
-//            String path = conf.getString(Constants.ZOOKEEPER_SCHEDULER_ROOT) + Constants.SINGLE_SLASH + Constants.SCHEDULER_TASKS_QUEUE + "_add" + Constants.SINGLE_SLASH + value;
-//            getZkClient().create().creatingParentContainersIfNeeded().withMode(CreateMode.PERSISTENT).forPath(path,
-//                    Bytes.toBytes(value));
             logger.info("add task : {} to tasks queue , result success",result);
         } catch (Exception e) {
             logger.error("add task to tasks queue exception",e);
@@ -132,16 +128,16 @@
     /**
      * An element pops out of the queue <p>
      * note:
-     *   ${processInstancePriority}_${processInstanceId}_${taskInstancePriority}_${taskId}
+     *   ${processInstancePriority}_${processInstanceId}_${taskInstancePriority}_${taskId}_host1,host2,...
      *   The tasks with the highest priority are selected by comparing the priorities of the above four levels from high to low.
      *
-     *   流程实例优先级_流程实例id_任务优先级_任务id       high <- low
+     *   流程优先级_流程实例id_任务优先级_任务id_任务执行的机器id1,任务执行的机器id2,...       high <- low
      * @param  key  task queue name
-     * @param  remove  whether remove the element
-     * @return the task id  to be executed
+     * @param  tasksNum    how many elements to poll
+     * @return the task ids  to be executed
      */
     @Override
-    public String poll(String key, boolean remove) {
+    public List<String> poll(String key, int tasksNum) {
         try{
             CuratorFramework zk = getZkClient();
             String tasksQueuePath = getTasksPath(key) + Constants.SINGLE_SLASH;
@@ -149,53 +145,120 @@
 
             if(list != null && list.size() > 0){
 
+                String workerIp = OSUtils.getHost();
+                String workerIpLongStr = String.valueOf(IpUtils.ipToLong(workerIp));
+
                 int size = list.size();
 
-                String formatTargetTask = null;
-                String targetTaskKey = null;
+
+                Set<String> taskTreeSet = new TreeSet<>(new Comparator<String>() {
+                    @Override
+                    public int compare(String o1, String o2) {
+
+                        String s1 = o1;
+                        String s2 = o2;
+                        String[] s1Array = s1.split(Constants.UNDERLINE);
+                        if(s1Array.length>4){
+                            // warning: if this length > 5, need to be changed
+                            s1 = s1.substring(0, s1.lastIndexOf(Constants.UNDERLINE) );
+                        }
+
+                        String[] s2Array = s2.split(Constants.UNDERLINE);
+                        if(s2Array.length>4){
+                            // warning: if this length > 5, need to be changed
+                            s2 = s2.substring(0, s2.lastIndexOf(Constants.UNDERLINE) );
+                        }
+
+                        return s1.compareTo(s2);
+                    }
+                });
+
                 for (int i = 0; i < size; i++) {
+
                     String taskDetail = list.get(i);
                     String[] taskDetailArrs = taskDetail.split(Constants.UNDERLINE);
 
-                    if(taskDetailArrs.length == 4){
+                    //forward compatibility 向前版本兼容
+                    if(taskDetailArrs.length >= 4){
+
                         //format ${processInstancePriority}_${processInstanceId}_${taskInstancePriority}_${taskId}
                         String formatTask = String.format("%s_%010d_%s_%010d", taskDetailArrs[0], Long.parseLong(taskDetailArrs[1]), taskDetailArrs[2], Long.parseLong(taskDetailArrs[3]));
-                        if(i > 0){
-                            int result = formatTask.compareTo(formatTargetTask);
-                            if(result < 0){
-                                formatTargetTask = formatTask;
-                                targetTaskKey = taskDetail;
+                        if(taskDetailArrs.length > 4){
+                            String taskHosts = taskDetailArrs[4];
+
+                            //task can assign to any worker host if equals default ip value of worker server
+                            if(!taskHosts.equals(String.valueOf(Constants.DEFAULT_WORKER_ID))){
+                                String[] taskHostsArr = taskHosts.split(Constants.COMMA);
+                                if(!Arrays.asList(taskHostsArr).contains(workerIpLongStr)){
+                                    continue;
+                                }
                             }
-                        }else{
-                            formatTargetTask = formatTask;
-                            targetTaskKey = taskDetail;
+                            formatTask += Constants.UNDERLINE + taskDetailArrs[4];
                         }
-                    }else{
-                        logger.error("task queue poll error, task detail :{} , please check!", taskDetail);
+                        taskTreeSet.add(formatTask);
+
                     }
+
                 }
 
-                if(formatTargetTask != null){
-                    String taskIdPath = tasksQueuePath + targetTaskKey;
+                List<String> taskslist = getTasksListFromTreeSet(tasksNum, taskTreeSet);
 
-                    logger.info("consume task {}", taskIdPath);
+                logger.info("consume tasks: {},there still have {} tasks need to be executed", Arrays.toString(taskslist.toArray()), size - taskslist.size());
 
-                    String[] vals = targetTaskKey.split(Constants.UNDERLINE);
-
-                    if(remove){
-                        removeNode(key, targetTaskKey);
-                    }
-                    logger.info("consume task: {},there still have {} tasks need to be executed", vals[vals.length - 1], size - 1);
-                    return targetTaskKey;
-                }else{
-                    logger.error("should not go here, task queue poll error, please check!");
-                }
+                return taskslist;
+            }else{
+                Thread.sleep(Constants.SLEEP_TIME_MILLIS);
             }
 
         } catch (Exception e) {
             logger.error("add task to tasks queue exception",e);
         }
-        return null;
+        return new ArrayList<String>();
+    }
+
+
+    /**
+     * get task list from tree set
+     *
+     * @param tasksNum
+     * @param taskTreeSet
+     */
+    public List<String> getTasksListFromTreeSet(int tasksNum, Set<String> taskTreeSet) {
+        Iterator<String> iterator = taskTreeSet.iterator();
+        int j = 0;
+        List<String> taskslist = new ArrayList<>(tasksNum);
+        while(iterator.hasNext()){
+            if(j++ < tasksNum){
+                String task = iterator.next();
+
+                taskslist.add(getOriginTaskFormat(task));
+            }
+        }
+        return taskslist;
+    }
+
+    /**
+     * format ${processInstancePriority}_${processInstanceId}_${taskInstancePriority}_${taskId}
+     * processInstanceId and task id need to be convert to int.
+     * @param formatTask
+     * @return
+     */
+    private String getOriginTaskFormat(String formatTask){
+        String[] taskArray = formatTask.split(Constants.UNDERLINE);
+        int processInstanceId = Integer.parseInt(taskArray[1]);
+        int taskId = Integer.parseInt(taskArray[3]);
+
+        StringBuilder sb = new StringBuilder(50);
+        String destTask = String.format("%s_%s_%s_%s", taskArray[0], processInstanceId, taskArray[2], taskId);
+
+        sb.append(destTask);
+
+        if(taskArray.length > 4){
+            for(int index = 4; index < taskArray.length; index++){
+                sb.append(Constants.UNDERLINE).append(taskArray[index]);
+            }
+        }
+        return sb.toString();
     }
 
     @Override
@@ -354,16 +417,6 @@
         }
     }
 
-
-    /**
-     * get zookeeper client of CuratorFramework
-     * @return
-     */
-    public CuratorFramework getZkClient() {
-        return zkClient;
-    }
-
-
     /**
      * Get the task queue path
      * @param key  task queue name
diff --git a/escheduler-common/src/main/java/cn/escheduler/common/utils/CommonUtils.java b/escheduler-common/src/main/java/cn/escheduler/common/utils/CommonUtils.java
index d016479..43087fb 100644
--- a/escheduler-common/src/main/java/cn/escheduler/common/utils/CommonUtils.java
+++ b/escheduler-common/src/main/java/cn/escheduler/common/utils/CommonUtils.java
@@ -17,6 +17,7 @@
 package cn.escheduler.common.utils;
 
 import cn.escheduler.common.Constants;
+import cn.escheduler.common.enums.ResUploadType;
 import org.apache.commons.lang3.StringUtils;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
@@ -63,4 +64,14 @@
 
 
 
+  /**
+   * if upload resource is HDFS and kerberos startup is true , else false
+   * @return
+   */
+  public static boolean getKerberosStartupState(){
+    String resUploadStartupType = PropertyUtils.getString(cn.escheduler.common.Constants.RES_UPLOAD_STARTUP_TYPE);
+    ResUploadType resUploadType = ResUploadType.valueOf(resUploadStartupType);
+    Boolean kerberosStartupState = getBoolean(cn.escheduler.common.Constants.HADOOP_SECURITY_AUTHENTICATION_STARTUP_STATE);
+    return resUploadType == ResUploadType.HDFS && kerberosStartupState;
+  }
 }
diff --git a/escheduler-common/src/main/java/cn/escheduler/common/utils/DateUtils.java b/escheduler-common/src/main/java/cn/escheduler/common/utils/DateUtils.java
index 11dc6bf..045db34 100644
--- a/escheduler-common/src/main/java/cn/escheduler/common/utils/DateUtils.java
+++ b/escheduler-common/src/main/java/cn/escheduler/common/utils/DateUtils.java
@@ -20,8 +20,10 @@
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
-import java.text.ParseException;
-import java.text.SimpleDateFormat;
+import java.time.Instant;
+import java.time.LocalDateTime;
+import java.time.ZoneId;
+import java.time.format.DateTimeFormatter;
 import java.util.Calendar;
 import java.util.Date;
 
@@ -30,145 +32,182 @@
  */
 public class DateUtils {
 
-	private static final Logger logger = LoggerFactory.getLogger(DateUtils.class);
+    private static final Logger logger = LoggerFactory.getLogger(DateUtils.class);
 
-	/**
-	 * @return get the formatted date string for the current time
-	 */
-	public static String getCurrentTime() {
-		return getCurrentTime(Constants.YYYY_MM_DD_HH_MM_SS);
-	}
+    /**
+     * <code>java.util.Date</code> to <code>java.time.LocalDateTime</code>
+     * use default zone
+     * @param date
+     * @return
+     */
+    private static LocalDateTime date2LocalDateTime(Date date) {
+        return LocalDateTime.ofInstant(date.toInstant(), ZoneId.systemDefault());
+    }
 
-	/**
-	 * @param format
-	 * @return get the date string in the specified format of the current time
-	 */
-	public static String getCurrentTime(String format) {
-		return new SimpleDateFormat(format).format(new Date());
-	}
+    /**
+     * <code>java.time.LocalDateTime</code> to <code>java.util.Date</code>
+     * use default zone
+     * @param localDateTime
+     * @return
+     */
+    private static Date localDateTime2Date(LocalDateTime localDateTime) {
+        Instant instant = localDateTime.atZone(ZoneId.systemDefault()).toInstant();
+        return Date.from(instant);
+    }
 
-	/**
-	 * @param date
-	 * @param format e.g. yyyy-MM-dd HH:mm:ss
-	 * @return get the formatted date string
-	 */
-	public static String format(Date date, String format) {
-		return new SimpleDateFormat(format).format(date);
-	}
+    /**
+     * @return get the formatted date string for the current time
+     */
+    public static String getCurrentTime() {
+        return getCurrentTime(Constants.YYYY_MM_DD_HH_MM_SS);
+    }
 
-	/**
-	 * @param date
-	 * @return convert time to yyyy-MM-dd HH:mm:ss format
-	 */
-	public static String dateToString(Date date){
-		return format(date,Constants.YYYY_MM_DD_HH_MM_SS);
-	}
+    /**
+     * @param format
+     * @return get the date string in the specified format of the current time
+     */
+    public static String getCurrentTime(String format) {
+//        return new SimpleDateFormat(format).format(new Date());
+        return LocalDateTime.now().format(DateTimeFormatter.ofPattern(format));
+    }
+
+    /**
+     * @param date
+     * @param format e.g. yyyy-MM-dd HH:mm:ss
+     * @return get the formatted date string
+     */
+    public static String format(Date date, String format) {
+//        return new SimpleDateFormat(format).format(date);
+        return format(date2LocalDateTime(date), format);
+    }
+
+    /**
+     * @param localDateTime
+     * @param format        e.g. yyyy-MM-dd HH:mm:ss
+     * @return get the formatted date string
+     */
+    public static String format(LocalDateTime localDateTime, String format) {
+        return localDateTime.format(DateTimeFormatter.ofPattern(format));
+    }
+
+    /**
+     * @param date
+     * @return convert time to yyyy-MM-dd HH:mm:ss format
+     */
+    public static String dateToString(Date date) {
+        return format(date, Constants.YYYY_MM_DD_HH_MM_SS);
+    }
 
 
-	/**
-	 * @param date
-	 * @return convert string to date and time
-	 */
-	public static Date parse(String date,String format){
-		try {
-			return new SimpleDateFormat(format).parse(date);
-		} catch (Exception e) {
-			logger.error("error while parse date:" + date, e);
-		}
-		return null;
-	}
+    /**
+     * @param date
+     * @return convert string to date and time
+     */
+    public static Date parse(String date, String format) {
+        try {
+            //     return new SimpleDateFormat(format).parse(date);
+            LocalDateTime ldt = LocalDateTime.parse(date, DateTimeFormatter.ofPattern(format));
+            return localDateTime2Date(ldt);
+        } catch (Exception e) {
+            logger.error("error while parse date:" + date, e);
+        }
+        return null;
+    }
 
-	/**
-	 * convert date str to yyyy-MM-dd HH:mm:ss format
-	 * @param str
-	 * @return
-	 */
-	public static Date stringToDate(String str){
-		return parse(str,Constants.YYYY_MM_DD_HH_MM_SS);
-	}
+    /**
+     * convert date str to yyyy-MM-dd HH:mm:ss format
+     *
+     * @param str
+     * @return
+     */
+    public static Date stringToDate(String str) {
+        return parse(str, Constants.YYYY_MM_DD_HH_MM_SS);
+    }
 
-	/**
-	 * get seconds between two dates
-	 *
-	 * @param d1
-	 * @param d2
-	 * @return
-	 */
-	public static long differSec(Date d1, Date d2) {
-		return (long) Math.ceil(differMs(d1, d2) / 1000.0);
-	}
+    /**
+     * get seconds between two dates
+     *
+     * @param d1
+     * @param d2
+     * @return
+     */
+    public static long differSec(Date d1, Date d2) {
+        return (long) Math.ceil(differMs(d1, d2) / 1000.0);
+    }
 
-	/**
-	 * get ms between two dates
-	 *
-	 * @param d1
-	 * @param d2
-	 * @return
-	 */
-	public static long differMs(Date d1, Date d2) {
-		return Math.abs(d1.getTime() - d2.getTime());
-	}
+    /**
+     * get ms between two dates
+     *
+     * @param d1
+     * @param d2
+     * @return
+     */
+    public static long differMs(Date d1, Date d2) {
+        return Math.abs(d1.getTime() - d2.getTime());
+    }
 
 
-	/**
-	 * get hours between two dates
-	 *
-	 * @param d1
-	 * @param d2
-	 * @return
-	 */
-	public static long diffHours(Date d1, Date d2) {
-		return (long) Math.ceil(diffMin(d1, d2) / 60.0);
-	}
+    /**
+     * get hours between two dates
+     *
+     * @param d1
+     * @param d2
+     * @return
+     */
+    public static long diffHours(Date d1, Date d2) {
+        return (long) Math.ceil(diffMin(d1, d2) / 60.0);
+    }
 
-	/**
-	 * get minutes between two dates
-	 *
-	 * @param d1
-	 * @param d2
-	 * @return
-	 */
-	public static long diffMin(Date d1, Date d2) {
-		return (long) Math.ceil(differSec(d1, d2) / 60.0);
-	}
+    /**
+     * get minutes between two dates
+     *
+     * @param d1
+     * @param d2
+     * @return
+     */
+    public static long diffMin(Date d1, Date d2) {
+        return (long) Math.ceil(differSec(d1, d2) / 60.0);
+    }
 
 
-	/**
+    /**
      * get the date of the specified date in the days before and after
-	 * @param date
-	 * @param day
-	 * @return
-	 */
-	public static Date getSomeDay(Date date, int day) {
-		Calendar calendar = Calendar.getInstance();
-		calendar.setTime(date);
-		calendar.add(Calendar.DATE, day);
-		return calendar.getTime();
-	}
+     *
+     * @param date
+     * @param day
+     * @return
+     */
+    public static Date getSomeDay(Date date, int day) {
+        Calendar calendar = Calendar.getInstance();
+        calendar.setTime(date);
+        calendar.add(Calendar.DATE, day);
+        return calendar.getTime();
+    }
 
-	/**
+    /**
      * compare two dates
-	 *
-	 * @param future
-	 * @param old
-	 * @return
-	 */
-	public static boolean compare(Date future, Date old) {
-		return future.getTime() > old.getTime();
-	}
+     *
+     * @param future
+     * @param old
+     * @return
+     */
+    public static boolean compare(Date future, Date old) {
+        return future.getTime() > old.getTime();
+    }
 
-	/**
-	 * convert schedule string to date
-	 * @param schedule
-	 * @return
-	 */
-	public static Date getScheduleDate(String schedule){
-	    return stringToDate(schedule);
-	}
+    /**
+     * convert schedule string to date
+     *
+     * @param schedule
+     * @return
+     */
+    public static Date getScheduleDate(String schedule) {
+        return stringToDate(schedule);
+    }
 
     /**
      * format time to readable
-	 *
+     *
      * @param ms
      * @return
      */
@@ -183,131 +222,135 @@
 
     }
 
-	/**
-	 * get monday
-	 *
-	 * note: Set the first day of the week to Monday, the default is Sunday
-	 */
-	public static Date getMonday(Date date) {
-		Calendar cal = Calendar.getInstance();
+    /**
+     * get monday
+     * <p>
+     * note: Set the first day of the week to Monday, the default is Sunday
+     */
+    public static Date getMonday(Date date) {
+        Calendar cal = Calendar.getInstance();
 
-		cal.setTime(date);
+        cal.setTime(date);
 
-		cal.setFirstDayOfWeek(Calendar.MONDAY);
-		cal.set(Calendar.DAY_OF_WEEK, Calendar.MONDAY);
+        cal.setFirstDayOfWeek(Calendar.MONDAY);
+        cal.set(Calendar.DAY_OF_WEEK, Calendar.MONDAY);
 
-		return cal.getTime();
-	}
+        return cal.getTime();
+    }
 
-	/**
-	 * get sunday
-	 *
-	 * note: Set the first day of the week to Monday, the default is Sunday
-	 */
-	public static Date getSunday(Date date) {
-		Calendar cal = Calendar.getInstance();
-		cal.setTime(date);
+    /**
+     * get sunday
+     * <p>
+     * note: Set the first day of the week to Monday, the default is Sunday
+     */
+    public static Date getSunday(Date date) {
+        Calendar cal = Calendar.getInstance();
+        cal.setTime(date);
 
-		cal.setFirstDayOfWeek(Calendar.MONDAY);
-		cal.set(Calendar.DAY_OF_WEEK, Calendar.SUNDAY);
+        cal.setFirstDayOfWeek(Calendar.MONDAY);
+        cal.set(Calendar.DAY_OF_WEEK, Calendar.SUNDAY);
 
-		return cal.getTime();
-	}
+        return cal.getTime();
+    }
 
-	/**
-	 * get first day of month
-	 */
-	public static Date getFirstDayOfMonth(Date date) {
-		Calendar cal = Calendar.getInstance();
+    /**
+     * get first day of month
+     */
+    public static Date getFirstDayOfMonth(Date date) {
+        Calendar cal = Calendar.getInstance();
 
-		cal.setTime(date);
-		cal.set(Calendar.DAY_OF_MONTH, 1);
+        cal.setTime(date);
+        cal.set(Calendar.DAY_OF_MONTH, 1);
 
-		return cal.getTime();
-	}
+        return cal.getTime();
+    }
 
-	/**
-	 * get first day of month
-	 */
-	public static Date getSomeHourOfDay(Date date, int hours) {
-		Calendar cal = Calendar.getInstance();
+    /**
+     * get first day of month
+     */
+    public static Date getSomeHourOfDay(Date date, int hours) {
+        Calendar cal = Calendar.getInstance();
 
-		cal.setTime(date);
-		cal.set(Calendar.HOUR_OF_DAY, cal.get(Calendar.HOUR_OF_DAY) - hours);
-		cal.set(Calendar.MINUTE, 0);
-		cal.set(Calendar.SECOND, 0);
+        cal.setTime(date);
+        cal.set(Calendar.HOUR_OF_DAY, cal.get(Calendar.HOUR_OF_DAY) - hours);
+        cal.set(Calendar.MINUTE, 0);
+        cal.set(Calendar.SECOND, 0);
 
-		return cal.getTime();
-	}
+        return cal.getTime();
+    }
 
-	/**
-	 * get last day of month
-	 */
-	public static Date getLastDayOfMonth(Date date) {
-		Calendar cal = Calendar.getInstance();
+    /**
+     * get last day of month
+     */
+    public static Date getLastDayOfMonth(Date date) {
+        Calendar cal = Calendar.getInstance();
 
-		cal.setTime(date);
+        cal.setTime(date);
 
-		cal.add(Calendar.MONTH, 1);
-		cal.set(Calendar.DAY_OF_MONTH, 1);
-		cal.add(Calendar.DAY_OF_MONTH, -1);
+        cal.add(Calendar.MONTH, 1);
+        cal.set(Calendar.DAY_OF_MONTH, 1);
+        cal.add(Calendar.DAY_OF_MONTH, -1);
 
-		return cal.getTime();
-	}
+        return cal.getTime();
+    }
 
-	/**
-	 * return YYYY-MM-DD 00:00:00
-	 * @param inputDay
-	 * @return
-	 */
-	public static Date getStartOfDay(Date inputDay){
-		Calendar cal = Calendar.getInstance();
-		cal.setTime(inputDay);
-		cal.set(Calendar.HOUR_OF_DAY, 0);
-		cal.set(Calendar.MINUTE, 0);
-		cal.set(Calendar.SECOND, 0);
-		return cal.getTime();
-	}
+    /**
+     * return YYYY-MM-DD 00:00:00
+     *
+     * @param inputDay
+     * @return
+     */
+    public static Date getStartOfDay(Date inputDay) {
+        Calendar cal = Calendar.getInstance();
+        cal.setTime(inputDay);
+        cal.set(Calendar.HOUR_OF_DAY, 0);
+        cal.set(Calendar.MINUTE, 0);
+        cal.set(Calendar.SECOND, 0);
+        return cal.getTime();
+    }
 
-	/**
-	 * return YYYY-MM-DD 23:59:59
-	 * @param inputDay
-	 * @return
-	 */
-	public static Date getEndOfDay(Date inputDay){
-		Calendar cal = Calendar.getInstance();
-		cal.setTime(inputDay);
-		cal.set(Calendar.HOUR_OF_DAY, 23);
-		cal.set(Calendar.MINUTE, 59);
-		cal.set(Calendar.SECOND, 59);
-		return cal.getTime();
-	}
+    /**
+     * return YYYY-MM-DD 23:59:59
+     *
+     * @param inputDay
+     * @return
+     */
+    public static Date getEndOfDay(Date inputDay) {
+        Calendar cal = Calendar.getInstance();
+        cal.setTime(inputDay);
+        cal.set(Calendar.HOUR_OF_DAY, 23);
+        cal.set(Calendar.MINUTE, 59);
+        cal.set(Calendar.SECOND, 59);
+        return cal.getTime();
+    }
 
-	/**
-	 * return YYYY-MM-DD 00:00:00
-	 * @param inputDay
-	 * @return
-	 */
-	public static Date getStartOfHour(Date inputDay){
-		Calendar cal = Calendar.getInstance();
-		cal.setTime(inputDay);
-		cal.set(Calendar.MINUTE, 0);
-		cal.set(Calendar.SECOND, 0);
-		return cal.getTime();
-	}
+    /**
+     * return YYYY-MM-DD 00:00:00
+     *
+     * @param inputDay
+     * @return
+     */
+    public static Date getStartOfHour(Date inputDay) {
+        Calendar cal = Calendar.getInstance();
+        cal.setTime(inputDay);
+        cal.set(Calendar.MINUTE, 0);
+        cal.set(Calendar.SECOND, 0);
+        return cal.getTime();
+    }
 
-	/**
-	 * return YYYY-MM-DD 23:59:59
-	 * @param inputDay
-	 * @return
-	 */
-	public static Date getEndOfHour(Date inputDay){
-		Calendar cal = Calendar.getInstance();
-		cal.setTime(inputDay);
-		cal.set(Calendar.MINUTE, 59);
-		cal.set(Calendar.SECOND, 59);
-		return cal.getTime();
-	}
+    /**
+     * return YYYY-MM-DD 23:59:59
+     *
+     * @param inputDay
+     * @return
+     */
+    public static Date getEndOfHour(Date inputDay) {
+        Calendar cal = Calendar.getInstance();
+        cal.setTime(inputDay);
+        cal.set(Calendar.MINUTE, 59);
+        cal.set(Calendar.SECOND, 59);
+        return cal.getTime();
+    }
 
 
 }
diff --git a/escheduler-common/src/main/java/cn/escheduler/common/utils/HadoopUtils.java b/escheduler-common/src/main/java/cn/escheduler/common/utils/HadoopUtils.java
index bba9e61..6f3e5e2 100644
--- a/escheduler-common/src/main/java/cn/escheduler/common/utils/HadoopUtils.java
+++ b/escheduler-common/src/main/java/cn/escheduler/common/utils/HadoopUtils.java
@@ -18,31 +18,30 @@
 
 import cn.escheduler.common.Constants;
 import cn.escheduler.common.enums.ExecutionStatus;
+import cn.escheduler.common.enums.ResUploadType;
 import com.alibaba.fastjson.JSON;
 import com.alibaba.fastjson.JSONException;
 import com.alibaba.fastjson.JSONObject;
 import org.apache.commons.io.IOUtils;
 import org.apache.commons.lang3.StringUtils;
 import org.apache.hadoop.conf.Configuration;
-import org.apache.hadoop.fs.FSDataInputStream;
+import org.apache.hadoop.fs.*;
 import org.apache.hadoop.fs.FileSystem;
-import org.apache.hadoop.fs.FileUtil;
-import org.apache.hadoop.fs.Path;
-import org.apache.hadoop.fs.FileStatus;
+import org.apache.hadoop.security.UserGroupInformation;
 import org.apache.hadoop.yarn.client.cli.RMAdminCLI;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
 import java.io.*;
+import java.security.PrivilegedExceptionAction;
 import java.util.List;
 import java.util.Map;
 import java.util.stream.Collectors;
 import java.util.stream.Stream;
 
 import static cn.escheduler.common.Constants.*;
-import static cn.escheduler.common.utils.PropertyUtils.getInt;
+import static cn.escheduler.common.utils.PropertyUtils.*;
 import static cn.escheduler.common.utils.PropertyUtils.getString;
-import static cn.escheduler.common.utils.PropertyUtils.getPrefixedProperties;
 
 /**
  * hadoop utils
@@ -52,12 +51,18 @@
 
     private static final Logger logger = LoggerFactory.getLogger(HadoopUtils.class);
 
+    private static String hdfsUser = PropertyUtils.getString(Constants.HDFS_ROOT_USER);
     private static volatile HadoopUtils instance = new HadoopUtils();
     private static volatile Configuration configuration;
     private static FileSystem fs;
 
+
     private HadoopUtils(){
+        if(StringUtils.isEmpty(hdfsUser)){
+            hdfsUser = PropertyUtils.getString(Constants.HDFS_ROOT_USER);
+        }
         init();
+        initHdfsPath();
     }
 
     public static HadoopUtils getInstance(){
@@ -65,6 +70,23 @@
     }
 
     /**
+     * init escheduler root path in hdfs
+     */
+    private void initHdfsPath(){
+        String hdfsPath = getString(Constants.DATA_STORE_2_HDFS_BASEPATH);
+        Path path = new Path(hdfsPath);
+
+        try {
+            if (!fs.exists(path)) {
+                fs.mkdirs(path);
+            }
+        } catch (Exception e) {
+            logger.error(e.getMessage(),e);
+        }
+    }
+
+
+    /**
      * init hadoop configuration
      */
     private void init() {
@@ -73,26 +95,62 @@
                 if (configuration == null) {
                     try {
                         configuration = new Configuration();
-                        String defaultFS = configuration.get(FS_DEFAULTFS);
-                        //first get key from core-site.xml hdfs-site.xml ,if null ,then try to get from properties file
-                        // the default is the local file system
-                        if(defaultFS.startsWith("file")){
-                            String defaultFSProp = getString(FS_DEFAULTFS);
-                            if(StringUtils.isNotBlank(defaultFSProp)){
-                                Map<String, String> fsRelatedProps = getPrefixedProperties("fs.");
-                                configuration.set(FS_DEFAULTFS,defaultFSProp);
-                                fsRelatedProps.entrySet().stream().forEach(entry -> configuration.set(entry.getKey(), entry.getValue()));
-                            }else{
-                                logger.error("property:{} can not to be empty, please set!");
-                                throw new RuntimeException("property:{} can not to be empty, please set!");
-                            }
-                        }else{
-                            logger.info("get property:{} -> {}, from core-site.xml hdfs-site.xml ", FS_DEFAULTFS, defaultFS);
-                        }
 
-                        if (fs == null) {
+                        String resUploadStartupType = PropertyUtils.getString(Constants.RES_UPLOAD_STARTUP_TYPE);
+                        ResUploadType resUploadType = ResUploadType.valueOf(resUploadStartupType);
+
+                        if (resUploadType == ResUploadType.HDFS){
+                            if (getBoolean(Constants.HADOOP_SECURITY_AUTHENTICATION_STARTUP_STATE)){
+                                System.setProperty(Constants.JAVA_SECURITY_KRB5_CONF,
+                                        getString(Constants.JAVA_SECURITY_KRB5_CONF_PATH));
+                                configuration.set(Constants.HADOOP_SECURITY_AUTHENTICATION,"kerberos");
+                                UserGroupInformation.setConfiguration(configuration);
+                                UserGroupInformation.loginUserFromKeytab(getString(Constants.LOGIN_USER_KEY_TAB_USERNAME),
+                                        getString(Constants.LOGIN_USER_KEY_TAB_PATH));
+                            }
+
+                            String defaultFS = configuration.get(FS_DEFAULTFS);
+                            //first get key from core-site.xml hdfs-site.xml ,if null ,then try to get from properties file
+                            // the default is the local file system
+                            if(defaultFS.startsWith("file")){
+                                String defaultFSProp = getString(FS_DEFAULTFS);
+                                if(StringUtils.isNotBlank(defaultFSProp)){
+                                    Map<String, String> fsRelatedProps = getPrefixedProperties("fs.");
+                                    configuration.set(FS_DEFAULTFS,defaultFSProp);
+                                    fsRelatedProps.entrySet().stream().forEach(entry -> configuration.set(entry.getKey(), entry.getValue()));
+                                }else{
+                                    logger.error("property:{} can not to be empty, please set!");
+                                    throw new RuntimeException("property:{} can not to be empty, please set!");
+                                }
+                            }else{
+                                logger.info("get property:{} -> {}, from core-site.xml hdfs-site.xml ", FS_DEFAULTFS, defaultFS);
+                            }
+
+                            if (fs == null) {
+                                if(StringUtils.isNotEmpty(hdfsUser)){
+                                    //UserGroupInformation ugi = UserGroupInformation.createProxyUser(hdfsUser,UserGroupInformation.getLoginUser());
+                                    UserGroupInformation ugi = UserGroupInformation.createRemoteUser(hdfsUser);
+                                    ugi.doAs(new PrivilegedExceptionAction<Boolean>() {
+                                        @Override
+                                        public Boolean run() throws Exception {
+                                            fs = FileSystem.get(configuration);
+                                            return true;
+                                        }
+                                    });
+                                }else{
+                                    logger.warn("hdfs.root.user is not set value!");
+                                    fs = FileSystem.get(configuration);
+                                }
+                            }
+                        }else if (resUploadType == ResUploadType.S3){
+                            configuration.set(FS_DEFAULTFS,getString(FS_DEFAULTFS));
+                            configuration.set(FS_S3A_ENDPOINT,getString(FS_S3A_ENDPOINT));
+                            configuration.set(FS_S3A_ACCESS_KEY,getString(FS_S3A_ACCESS_KEY));
+                            configuration.set(FS_S3A_SECRET_KEY,getString(FS_S3A_SECRET_KEY));
                             fs = FileSystem.get(configuration);
                         }
+
+
                         String rmHaIds = getString(YARN_RESOURCEMANAGER_HA_RM_IDS);
                         String appAddress = getString(Constants.YARN_APPLICATION_STATUS_ADDRESS);
                         if (!StringUtils.isEmpty(rmHaIds)) {
@@ -155,7 +213,7 @@
      */
     public List<String> catFile(String hdfsFilePath, int skipLineNums, int limit) throws IOException {
 
-        if(StringUtils.isBlank(hdfsFilePath)){
+        if (StringUtils.isBlank(hdfsFilePath)){
             logger.error("hdfs file path:{} is blank",hdfsFilePath);
             return null;
         }
diff --git a/escheduler-common/src/main/java/cn/escheduler/common/utils/IpUtils.java b/escheduler-common/src/main/java/cn/escheduler/common/utils/IpUtils.java
new file mode 100644
index 0000000..972135a
--- /dev/null
+++ b/escheduler-common/src/main/java/cn/escheduler/common/utils/IpUtils.java
@@ -0,0 +1,64 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *    http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package cn.escheduler.common.utils;
+
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+
+/**
+ * http utils
+ */
+public class IpUtils {
+
+  private static final Logger logger = LoggerFactory.getLogger(IpUtils.class);
+  public static final String DOT = ".";
+
+  /**
+   * ip str to long <p>
+   *
+   * @param ipStr ip string
+   */
+  public static Long ipToLong(String ipStr) {
+    String[] ipSet = ipStr.split("\\" + DOT);
+
+    return Long.parseLong(ipSet[0]) << 24 | Long.parseLong(ipSet[1]) << 16 | Long.parseLong(ipSet[2]) << 8 | Long.parseLong(ipSet[3]);
+  }
+
+  /**
+   * long to ip
+   * @param ipLong the long number converted from IP
+   * @return String
+   */
+  public static String longToIp(long ipLong) {
+    long[] ipNumbers = new long[4];
+    long tmp = 0xFF;
+    ipNumbers[0] = ipLong >> 24 & tmp;
+    ipNumbers[1] = ipLong >> 16 & tmp;
+    ipNumbers[2] = ipLong >> 8 & tmp;
+    ipNumbers[3] = ipLong & tmp;
+
+    StringBuilder sb = new StringBuilder(16);
+    sb.append(ipNumbers[0]).append(DOT)
+            .append(ipNumbers[1]).append(DOT)
+            .append(ipNumbers[2]).append(DOT)
+            .append(ipNumbers[3]);
+    return sb.toString();
+  }
+
+}
diff --git a/escheduler-common/src/main/java/cn/escheduler/common/utils/MysqlUtil.java b/escheduler-common/src/main/java/cn/escheduler/common/utils/MysqlUtils.java
similarity index 87%
rename from escheduler-common/src/main/java/cn/escheduler/common/utils/MysqlUtil.java
rename to escheduler-common/src/main/java/cn/escheduler/common/utils/MysqlUtils.java
index d2d1ef2..3520527 100644
--- a/escheduler-common/src/main/java/cn/escheduler/common/utils/MysqlUtil.java
+++ b/escheduler-common/src/main/java/cn/escheduler/common/utils/MysqlUtils.java
@@ -21,16 +21,16 @@
 
 import java.sql.*;
 
-public class MysqlUtil {
+public class MysqlUtils {
 
-	public static final Logger logger = LoggerFactory.getLogger(MysqlUtil.class);
+	public static final Logger logger = LoggerFactory.getLogger(MysqlUtils.class);
 
-	private static MysqlUtil instance;
+	private static MysqlUtils instance;
 
-	MysqlUtil() {
+	MysqlUtils() {
 	}
 
-	public static MysqlUtil getInstance() {
+	public static MysqlUtils getInstance() {
 		if (null == instance) {
 			syncInit();
 		}
@@ -39,7 +39,7 @@
 
 	private static synchronized void syncInit() {
 		if (instance == null) {
-			instance = new MysqlUtil();
+			instance = new MysqlUtils();
 		}
 	}
 
@@ -75,8 +75,8 @@
 		}
 	}
 
-	public static void realeaseResource(ResultSet rs, PreparedStatement ps, Connection conn) {
-		MysqlUtil.getInstance().release(rs,ps,conn);
+	public static void releaseResource(ResultSet rs, PreparedStatement ps, Connection conn) {
+		MysqlUtils.getInstance().release(rs,ps,conn);
 		if (null != rs) {
 			try {
 				rs.close();
diff --git a/escheduler-common/src/main/java/cn/escheduler/common/utils/PropertyUtils.java b/escheduler-common/src/main/java/cn/escheduler/common/utils/PropertyUtils.java
index f5dab12..475cbfb 100644
--- a/escheduler-common/src/main/java/cn/escheduler/common/utils/PropertyUtils.java
+++ b/escheduler-common/src/main/java/cn/escheduler/common/utils/PropertyUtils.java
@@ -16,6 +16,8 @@
  */
 package cn.escheduler.common.utils;
 
+import cn.escheduler.common.Constants;
+import cn.escheduler.common.enums.ResUploadType;
 import org.apache.commons.io.IOUtils;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
@@ -65,11 +67,15 @@
         }
     }
 
-/*
-    public static PropertyUtils getInstance(){
-        return propertyUtils;
+    /**
+     * judge whether resource upload startup
+     * @return
+     */
+    public static Boolean getResUploadStartupState(){
+        String resUploadStartupType = PropertyUtils.getString(Constants.RES_UPLOAD_STARTUP_TYPE);
+        ResUploadType resUploadType = ResUploadType.valueOf(resUploadStartupType);
+        return resUploadType == ResUploadType.HDFS || resUploadType == ResUploadType.S3;
     }
-*/
 
     /**
      * get property value
diff --git a/escheduler-common/src/main/java/cn/escheduler/common/zk/AbstractZKClient.java b/escheduler-common/src/main/java/cn/escheduler/common/zk/AbstractZKClient.java
index 177669b..3c58996 100644
--- a/escheduler-common/src/main/java/cn/escheduler/common/zk/AbstractZKClient.java
+++ b/escheduler-common/src/main/java/cn/escheduler/common/zk/AbstractZKClient.java
@@ -30,13 +30,12 @@
 import org.apache.curator.framework.state.ConnectionState;
 import org.apache.curator.framework.state.ConnectionStateListener;
 import org.apache.curator.retry.ExponentialBackoffRetry;
+import org.apache.hadoop.hbase.protobuf.generated.MasterProtos;
 import org.apache.zookeeper.CreateMode;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
-import java.util.ArrayList;
-import java.util.Date;
-import java.util.List;
+import java.util.*;
 
 import static cn.escheduler.common.Constants.*;
 
@@ -213,9 +212,9 @@
 	protected void initSystemZNode(){
 		try {
 			// read master node parent path from conf
-			masterZNodeParentPath = conf.getString(Constants.ZOOKEEPER_ESCHEDULER_MASTERS);
+			masterZNodeParentPath = getMasterZNodeParentPath();
 			// read worker node parent path from conf
-			workerZNodeParentPath = conf.getString(Constants.ZOOKEEPER_ESCHEDULER_WORKERS);
+			workerZNodeParentPath = getWorkerZNodeParentPath();
 
 			// read server node parent path from conf
 			deadServerZNodeParentPath = conf.getString(ZOOKEEPER_ESCHEDULER_DEAD_SERVERS);
@@ -243,6 +242,7 @@
 		}
 	}
 
+
 	public void removeDeadServerByHost(String host, String serverType) throws Exception {
         List<String> deadServers = zkClient.getChildren().forPath(deadServerZNodeParentPath);
         for(String serverPath : deadServers){
@@ -291,6 +291,8 @@
 
 	}
 
+
+
 	/**
 	 * for stop server
 	 * @param serverStoppable
@@ -312,7 +314,10 @@
 				childrenList = zkClient.getChildren().forPath(masterZNodeParentPath);
 			}
 		} catch (Exception e) {
-			logger.warn(e.getMessage(),e);
+			if(!e.getMessage().contains("java.lang.IllegalStateException: instance must be started")){
+				logger.warn(e.getMessage(),e);
+			}
+
 			return childrenList.size();
 		}
 		return childrenList.size();
@@ -336,6 +341,81 @@
 		return sb.toString();
 	}
 
+	/**
+	 * get master server list map.
+	 * result : {host : resource info}
+	 * @return
+	 */
+	public Map<String, String> getServerList(boolean isMaster ){
+
+		Map<String, String> masterMap = new HashMap<>();
+		try {
+			String path =  isMaster ? getMasterZNodeParentPath() : getWorkerZNodeParentPath();
+			List<String> serverList  = getZkClient().getChildren().forPath(path);
+			for(String server : serverList){
+				byte[] bytes  = getZkClient().getData().forPath(path + "/" + server);
+				masterMap.putIfAbsent(server, new String(bytes));
+			}
+		} catch (Exception e) {
+			e.printStackTrace();
+		}
+
+		return masterMap;
+	}
+
+	/**
+	 *  get zkclient
+	 * @return
+	 */
+	public  CuratorFramework getZkClient() {
+		return zkClient;
+	}
+
+	/**
+	 * get worker node parent path
+	 * @return
+	 */
+	protected String getWorkerZNodeParentPath(){return conf.getString(Constants.ZOOKEEPER_ESCHEDULER_WORKERS);};
+
+	/**
+	 * get master node parent path
+	 * @return
+	 */
+	protected String getMasterZNodeParentPath(){return conf.getString(Constants.ZOOKEEPER_ESCHEDULER_MASTERS);}
+
+	/**
+	 *  get master lock path
+	 * @return
+	 */
+	public String getMasterLockPath(){
+		return conf.getString(Constants.ZOOKEEPER_ESCHEDULER_LOCK_MASTERS);
+	}
+
+	/**
+	 *  get master start up lock path
+	 * @return
+	 */
+	public String getMasterStartUpLockPath(){
+		return conf.getString(Constants.ZOOKEEPER_ESCHEDULER_LOCK_FAILOVER_STARTUP_MASTERS);
+	}
+
+	/**
+	 *  get master failover lock path
+	 * @return
+	 */
+	public String getMasterFailoverLockPath(){
+		return conf.getString(Constants.ZOOKEEPER_ESCHEDULER_LOCK_FAILOVER_MASTERS);
+	}
+
+	/**
+	 * get worker failover lock path
+	 * @return
+	 */
+	public String getWorkerFailoverLockPath(){
+		return conf.getString(Constants.ZOOKEEPER_ESCHEDULER_LOCK_FAILOVER_WORKERS);
+	}
+
+
 	@Override
 	public String toString() {
 		return "AbstractZKClient{" +
diff --git a/escheduler-common/src/main/resources/common/common.properties b/escheduler-common/src/main/resources/common/common.properties
index 1cb995b..27d525f 100644
--- a/escheduler-common/src/main/resources/common/common.properties
+++ b/escheduler-common/src/main/resources/common/common.properties
@@ -10,11 +10,26 @@
 # process execute directory. self configuration, please make sure the directory exists and have read write permissions
 process.exec.basepath=/tmp/escheduler/exec
 
+# Users who have permission to create directories under the HDFS root path
+hdfs.root.user=hdfs
+
 # data base dir, resource file will store to this hadoop hdfs path, self configuration, please make sure the directory exists on hdfs and have read write permissions。"/escheduler" is recommended
 data.store2hdfs.basepath=/escheduler
 
-# whether hdfs starts
-hdfs.startup.state=true
+# resource upload startup type : HDFS,S3,NONE
+res.upload.startup.type=NONE
+
+# whether kerberos starts
+hadoop.security.authentication.startup.state=false
+
+# java.security.krb5.conf path
+java.security.krb5.conf.path=/opt/krb5.conf
+
+# loginUserFromKeytab user
+login.user.keytab.username=hdfs-mycluster@ESZ.COM
+
+# loginUserFromKeytab path
+login.user.keytab.path=/opt/hdfs.headless.keytab
 
 # system env path. self configuration, please make sure the directory and file exists and have read write execute permissions
 escheduler.env.path=/opt/.escheduler_env.sh
@@ -23,5 +38,5 @@
 resource.view.suffixs=txt,log,sh,conf,cfg,py,java,sql,hql,xml
 
 # is development state? default "false"
-development.state=false
+development.state=true
 
diff --git a/escheduler-common/src/main/resources/common/hadoop/hadoop.properties b/escheduler-common/src/main/resources/common/hadoop/hadoop.properties
index f210ae7..81452a8 100644
--- a/escheduler-common/src/main/resources/common/hadoop/hadoop.properties
+++ b/escheduler-common/src/main/resources/common/hadoop/hadoop.properties
@@ -1,6 +1,16 @@
-# ha or single namenode,If namenode ha needs to copy core-site.xml and hdfs-site.xml to the conf directory
+# ha or single namenode,If namenode ha needs to copy core-site.xml and hdfs-site.xml
+# to the conf directory,support s3,for example : s3a://escheduler
 fs.defaultFS=hdfs://mycluster:8020
 
+# s3 need,s3 endpoint
+fs.s3a.endpoint=http://192.168.199.91:9010
+
+# s3 need,s3 access key
+fs.s3a.access.key=A3DXS30FO22544RE
+
+# s3 need,s3 secret key
+fs.s3a.secret.key=OloCLq3n+8+sdPHUhJ21XrSxTC+JK
+
 #resourcemanager ha note this need ips , this empty if single
 yarn.resourcemanager.ha.rm.ids=192.168.xx.xx,192.168.xx.xx
 
diff --git a/escheduler-common/src/test/java/cn/escheduler/common/os/OSUtilsTest.java b/escheduler-common/src/test/java/cn/escheduler/common/os/OSUtilsTest.java
index 7d35bc8..4bf152b 100644
--- a/escheduler-common/src/test/java/cn/escheduler/common/os/OSUtilsTest.java
+++ b/escheduler-common/src/test/java/cn/escheduler/common/os/OSUtilsTest.java
@@ -38,6 +38,12 @@
 
 
     @Test
+    public void getHost(){
+        logger.info(OSUtils.getHost());
+    }
+
+
+    @Test
     public void memoryUsage() {
         logger.info("memoryUsage : {}", OSUtils.memoryUsage());// 0.3361799418926239
 //        printMemory(hal.getMemory());// 35 %
diff --git a/escheduler-common/src/test/java/cn/escheduler/common/queue/TaskQueueImplTest.java b/escheduler-common/src/test/java/cn/escheduler/common/queue/TaskQueueImplTest.java
index 03ba29a..b0f9776 100644
--- a/escheduler-common/src/test/java/cn/escheduler/common/queue/TaskQueueImplTest.java
+++ b/escheduler-common/src/test/java/cn/escheduler/common/queue/TaskQueueImplTest.java
@@ -17,12 +17,15 @@
 package cn.escheduler.common.queue;
 
 import cn.escheduler.common.Constants;
-import org.junit.Assert;
+import cn.escheduler.common.utils.IpUtils;
+import cn.escheduler.common.utils.OSUtils;
+import org.junit.After;
+import org.junit.Before;
 import org.junit.Test;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
-import java.util.Arrays;
+import java.util.List;
 import java.util.Random;
 
 import static org.junit.Assert.assertEquals;
@@ -34,59 +37,60 @@
 
     private static final Logger logger = LoggerFactory.getLogger(TaskQueueImplTest.class);
 
+    ITaskQueue tasksQueue = null;
 
-    @Test
-    public void testTaskQueue(){
-
-        ITaskQueue tasksQueue = TaskQueueFactory.getTaskQueueInstance();
+    @Before
+    public void before(){
+        tasksQueue = TaskQueueFactory.getTaskQueueInstance();
         //clear all data
         tasksQueue.delete();
 
+    }
+
+
+    @After
+    public void after(){
+        //clear all data
+        tasksQueue.delete();
+    }
+
+
+    @Test
+    public void testAdd(){
+
+
         //add
-        tasksQueue.add(Constants.SCHEDULER_TASKS_QUEUE,"1");
-        tasksQueue.add(Constants.SCHEDULER_TASKS_QUEUE,"2");
-        tasksQueue.add(Constants.SCHEDULER_TASKS_QUEUE,"3");
-        tasksQueue.add(Constants.SCHEDULER_TASKS_QUEUE,"4");
+        tasksQueue.add(Constants.SCHEDULER_TASKS_QUEUE,"1_0_1_1_-1");
+        tasksQueue.add(Constants.SCHEDULER_TASKS_QUEUE,"0_1_1_1_2130706433,3232236775");
+        tasksQueue.add(Constants.SCHEDULER_TASKS_QUEUE,"1_1_0_1_2130706433,3232236775,"+IpUtils.ipToLong(OSUtils.getHost()));
+        tasksQueue.add(Constants.SCHEDULER_TASKS_QUEUE,"1_2_1_1_2130706433,3232236775");
+
+        List<String> tasks = tasksQueue.poll(Constants.SCHEDULER_TASKS_QUEUE, 1);
+
+        if(tasks.size() <= 0){
+            return;
+        }
 
         //pop
-        String node1 = tasksQueue.poll(Constants.SCHEDULER_TASKS_QUEUE, false);
-        assertEquals(node1,"1");
-        String node2 = tasksQueue.poll(Constants.SCHEDULER_TASKS_QUEUE, false);
-        assertEquals(node2,"2");
+        String node1 = tasks.get(0);
 
-        //sadd
-        String task1 = "1.1.1.1-1-mr";
-        String task2 = "1.1.1.2-2-mr";
-        String task3 = "1.1.1.3-3-mr";
-        String task4 = "1.1.1.4-4-mr";
-        String task5 = "1.1.1.5-5-mr";
+        assertEquals(node1,"1_0_1_1_-1");
 
-        tasksQueue.sadd(Constants.SCHEDULER_TASKS_KILL,task1);
-        tasksQueue.sadd(Constants.SCHEDULER_TASKS_KILL,task2);
-        tasksQueue.sadd(Constants.SCHEDULER_TASKS_KILL,task3);
-        tasksQueue.sadd(Constants.SCHEDULER_TASKS_KILL,task4);
-        tasksQueue.sadd(Constants.SCHEDULER_TASKS_KILL,task5);
-        tasksQueue.sadd(Constants.SCHEDULER_TASKS_KILL,task5); //repeat task
+        tasks = tasksQueue.poll(Constants.SCHEDULER_TASKS_QUEUE, 1);
 
-        Assert.assertEquals(tasksQueue.smembers(Constants.SCHEDULER_TASKS_KILL).size(),5);
-        logger.info(Arrays.toString(tasksQueue.smembers(Constants.SCHEDULER_TASKS_KILL).toArray()));
-        //srem
-        tasksQueue.srem(Constants.SCHEDULER_TASKS_KILL,task5);
-        //smembers
-        Assert.assertEquals(tasksQueue.smembers(Constants.SCHEDULER_TASKS_KILL).size(),4);
-        logger.info(Arrays.toString(tasksQueue.smembers(Constants.SCHEDULER_TASKS_KILL).toArray()));
-
+        if(tasks.size() <= 0){
+            return;
+        }
 
     }
 
+
+
     /**
      * test one million data from zookeeper queue
      */
     @Test
     public void extremeTest(){
-        ITaskQueue tasksQueue = TaskQueueFactory.getTaskQueueInstance();
-        //clear all data
-        tasksQueue.delete();
         int total = 30 * 10000;
 
         for(int i = 0; i < total; i++)
@@ -99,14 +103,9 @@
             }
         }
 
-        String node1 = tasksQueue.poll(Constants.SCHEDULER_TASKS_QUEUE, false);
+        String node1 = tasksQueue.poll(Constants.SCHEDULER_TASKS_QUEUE, 1).get(0);
         assertEquals(node1,"0");
 
-        //clear all data
-        tasksQueue.delete();
-
-
-
     }
 
 }
diff --git a/escheduler-common/src/test/java/cn/escheduler/common/utils/IpUtilsTest.java b/escheduler-common/src/test/java/cn/escheduler/common/utils/IpUtilsTest.java
new file mode 100644
index 0000000..11a03a2
--- /dev/null
+++ b/escheduler-common/src/test/java/cn/escheduler/common/utils/IpUtilsTest.java
@@ -0,0 +1,41 @@
+package cn.escheduler.common.utils;
+
+import org.junit.Assert;
+import org.junit.Test;
+
+import static org.junit.Assert.*;
+
+public class IpUtilsTest {
+
+    @Test
+    public void ipToLong() {
+
+        String ip = "192.168.110.1";
+        String ip2 = "0.0.0.0";
+        long longNumber = IpUtils.ipToLong(ip);
+        long longNumber2 = IpUtils.ipToLong(ip2);
+        System.out.println(longNumber);
+        Assert.assertEquals(longNumber, 3232263681L);
+        Assert.assertEquals(longNumber2, 0L);
+
+        String ip3 = "255.255.255.255";
+        long longNumber3 = IpUtils.ipToLong(ip3);
+        System.out.println(longNumber3);
+        Assert.assertEquals(longNumber3, 4294967295L);
+
+    }
+
+    @Test
+    public void longToIp() {
+
+        String ip = "192.168.110.1";
+        String ip2 = "0.0.0.0";
+        long longNum = 3232263681L;
+        String i1 = IpUtils.longToIp(longNum);
+
+        String i2 = IpUtils.longToIp(0);
+
+        Assert.assertEquals(ip, i1);
+        Assert.assertEquals(ip2, i2);
+    }
+}
\ No newline at end of file
diff --git a/escheduler-dao/pom.xml b/escheduler-dao/pom.xml
index 42e2f87..4d8fb69 100644
--- a/escheduler-dao/pom.xml
+++ b/escheduler-dao/pom.xml
@@ -4,7 +4,7 @@
 	<parent>
 		<groupId>cn.analysys</groupId>
 		<artifactId>escheduler</artifactId>
-		<version>1.0.4-SNAPSHOT</version>
+		<version>1.1.0-SNAPSHOT</version>
 	</parent>
 	<artifactId>escheduler-dao</artifactId>
 	<name>escheduler-dao</name>
@@ -37,6 +37,10 @@
 					<groupId>org.apache.tomcat</groupId>
 					<artifactId>tomcat-jdbc</artifactId>
 				</exclusion>
+				<exclusion>
+					<artifactId>log4j-to-slf4j</artifactId>
+					<groupId>org.apache.logging.log4j</groupId>
+				</exclusion>
 			</exclusions>
 		</dependency>
 		<dependency>
@@ -137,8 +141,14 @@
             <artifactId>spring-test</artifactId>
             <scope>test</scope>
         </dependency>
+		<dependency>
+			<groupId>io.swagger</groupId>
+			<artifactId>swagger-annotations</artifactId>
+			<version>1.5.20</version>
+			<scope>compile</scope>
+		</dependency>
 
-    </dependencies>
+	</dependencies>
 
 
 	<build>
diff --git a/escheduler-dao/src/main/java/cn/escheduler/dao/ProcessDao.java b/escheduler-dao/src/main/java/cn/escheduler/dao/ProcessDao.java
index 09e2149..16d2bd0 100644
--- a/escheduler-dao/src/main/java/cn/escheduler/dao/ProcessDao.java
+++ b/escheduler-dao/src/main/java/cn/escheduler/dao/ProcessDao.java
@@ -25,6 +25,7 @@
 import cn.escheduler.common.queue.TaskQueueFactory;
 import cn.escheduler.common.task.subprocess.SubProcessParameters;
 import cn.escheduler.common.utils.DateUtils;
+import cn.escheduler.common.utils.IpUtils;
 import cn.escheduler.common.utils.JSONUtils;
 import cn.escheduler.common.utils.ParameterUtils;
 import cn.escheduler.dao.mapper.*;
@@ -58,6 +59,7 @@
     private final int[] stateArray = new int[]{ExecutionStatus.SUBMITTED_SUCCESS.ordinal(),
             ExecutionStatus.RUNNING_EXEUTION.ordinal(),
             ExecutionStatus.READY_PAUSE.ordinal(),
+//            ExecutionStatus.NEED_FAULT_TOLERANCE.ordinal(),
             ExecutionStatus.READY_STOP.ordinal()};
 
     @Autowired
@@ -96,6 +98,12 @@
     @Autowired
     private ErrorCommandMapper errorCommandMapper;
 
+    @Autowired
+    private WorkerServerMapper workerServerMapper;
+
+    @Autowired
+    private TenantMapper tenantMapper;
+
     /**
      * task queue impl
      */
@@ -110,7 +118,7 @@
      */
     @Override
     protected void init() {
-        userMapper=getMapper(UserMapper.class);
+        userMapper = getMapper(UserMapper.class);
         processDefineMapper = getMapper(ProcessDefinitionMapper.class);
         processInstanceMapper = getMapper(ProcessInstanceMapper.class);
         dataSourceMapper = getMapper(DataSourceMapper.class);
@@ -121,7 +129,9 @@
         udfFuncMapper = getMapper(UdfFuncMapper.class);
         resourceMapper = getMapper(ResourceMapper.class);
         workerGroupMapper = getMapper(WorkerGroupMapper.class);
+        workerServerMapper = getMapper(WorkerServerMapper.class);
         taskQueue = TaskQueueFactory.getTaskQueueInstance();
+        tenantMapper = getMapper(TenantMapper.class);
     }
 
 
@@ -483,11 +493,33 @@
         processInstance.setProcessInstanceJson(processDefinition.getProcessDefinitionJson());
         // set process instance priority
         processInstance.setProcessInstancePriority(command.getProcessInstancePriority());
-        processInstance.setWorkerGroupId(command.getWorkerGroupId());
+        int workerGroupId = command.getWorkerGroupId() == 0 ? -1 : command.getWorkerGroupId();
+        processInstance.setWorkerGroupId(workerGroupId);
         processInstance.setTimeout(processDefinition.getTimeout());
+        processInstance.setTenantId(processDefinition.getTenantId());
         return processInstance;
     }
 
+    /**
+     * get process tenant
+     * there is tenant id in definition, use the tenant of the definition.
+     * if there is not tenant id in the definiton or the tenant not exist
+     * use definition creator's tenant.
+     * @param tenantId
+     * @param userId
+     * @return
+     */
+    public Tenant getTenantForProcess(int tenantId, int userId){
+        Tenant tenant = null;
+        if(tenantId >= 0){
+            tenant = tenantMapper.queryById(tenantId);
+        }
+        if(tenant == null){
+            User user = userMapper.queryById(userId);
+            tenant = tenantMapper.queryById(user.getTenantId());
+        }
+        return tenant;
+    }
 
     /**
      * check command parameters is valid
@@ -581,6 +613,8 @@
             processInstance.setScheduleTime(command.getScheduleTime());
         }
         processInstance.setHost(host);
+
+        ExecutionStatus runStatus = ExecutionStatus.RUNNING_EXEUTION;
         int runTime = processInstance.getRunTimes();
         switch (commandType){
             case START_PROCESS:
@@ -610,6 +644,9 @@
                 // find pause tasks and init task's state
                 cmdParam.remove(Constants.CMDPARAM_RECOVERY_START_NODE_STRING);
                 List<Integer> suspendedNodeList = this.findTaskIdByInstanceState(processInstance.getId(), ExecutionStatus.PAUSE);
+                List<Integer> stopNodeList = findTaskIdByInstanceState(processInstance.getId(),
+                        ExecutionStatus.KILL);
+                suspendedNodeList.addAll(stopNodeList);
                 for(Integer taskId : suspendedNodeList){
                     // 把暂停状态初始化
                     initTaskInstance(this.findTaskInstanceById(taskId));
@@ -621,6 +658,7 @@
             case RECOVER_TOLERANCE_FAULT_PROCESS:
                 // recover tolerance fault process
                 processInstance.setRecovery(Flag.YES);
+                runStatus = processInstance.getState();
                 break;
             case COMPLEMENT_DATA:
                 // delete all the valid tasks when complement data
@@ -652,7 +690,7 @@
             default:
                 break;
         }
-        processInstance.setState(ExecutionStatus.RUNNING_EXEUTION);
+        processInstance.setState(runStatus);
         return processInstance;
     }
 
@@ -756,13 +794,16 @@
      * @param taskInstance
      */
     private void initTaskInstance(TaskInstance taskInstance){
-        if(taskInstance.getState().typeIsFailure() && !taskInstance.isSubProcess()){
-            taskInstance.setFlag(Flag.NO);
-            updateTaskInstance(taskInstance);
-        }else{
-            taskInstance.setState(ExecutionStatus.SUBMITTED_SUCCESS);
-            updateTaskInstance(taskInstance);
+
+        if(!taskInstance.isSubProcess()){
+            if(taskInstance.getState().typeIsCancel() || taskInstance.getState().typeIsFailure()){
+                taskInstance.setFlag(Flag.NO);
+                updateTaskInstance(taskInstance);
+                return;
+            }
         }
+        taskInstance.setState(ExecutionStatus.SUBMITTED_SUCCESS);
+        updateTaskInstance(taskInstance);
     }
 
     /**
@@ -970,17 +1011,64 @@
     }
 
     /**
-     * ${processInstancePriority}_${processInstanceId}_${taskInstancePriority}_${taskId}
+     * ${processInstancePriority}_${processInstanceId}_${taskInstancePriority}_${taskId}_${task executed by ip1},${ip2}...
      *
      * The tasks with the highest priority are selected by comparing the priorities of the above four levels from high to low.
      *
-     * 流程实例优先级_流程实例id_任务优先级_任务id       high <- low
+     * 流程实例优先级_流程实例id_任务优先级_任务id_任务执行机器ip1,ip2...          high <- low
      *
-     * @param task
+     * @param taskInstance
      * @return
      */
-    private String taskZkInfo(TaskInstance task) {
-        return String.valueOf(task.getProcessInstancePriority().ordinal()) + Constants.UNDERLINE + task.getProcessInstanceId() + Constants.UNDERLINE + task.getTaskInstancePriority().ordinal() + Constants.UNDERLINE + task.getId();
+    private String taskZkInfo(TaskInstance taskInstance) {
+
+        int taskWorkerGroupId = getTaskWorkerGroupId(taskInstance);
+
+        StringBuilder sb = new StringBuilder(100);
+
+        sb.append(taskInstance.getProcessInstancePriority().ordinal()).append(Constants.UNDERLINE)
+                .append(taskInstance.getProcessInstanceId()).append(Constants.UNDERLINE)
+                .append(taskInstance.getTaskInstancePriority().ordinal()).append(Constants.UNDERLINE)
+                .append(taskInstance.getId()).append(Constants.UNDERLINE);
+
+        if(taskWorkerGroupId > 0){
+            //not to find data from db
+            WorkerGroup workerGroup = queryWorkerGroupById(taskWorkerGroupId);
+            if(workerGroup == null ){
+                logger.info("task {} cannot find the worker group, use all worker instead.", taskInstance.getId());
+
+                sb.append(Constants.DEFAULT_WORKER_ID);
+                return sb.toString();
+            }
+
+            String ips = workerGroup.getIpList();
+
+            if(StringUtils.isBlank(ips)){
+                logger.error("task:{} worker group:{} parameters(ip_list) is null, this task would be running on all workers",
+                        taskInstance.getId(), workerGroup.getId());
+                sb.append(Constants.DEFAULT_WORKER_ID);
+                return sb.toString();
+            }
+
+            StringBuilder ipSb = new StringBuilder(100);
+            String[] ipArray = ips.split(COMMA);
+
+            for (String ip : ipArray) {
+               long ipLong = IpUtils.ipToLong(ip);
+                ipSb.append(ipLong).append(COMMA);
+            }
+
+            if(ipSb.length() > 0) {
+                ipSb.deleteCharAt(ipSb.length() - 1);
+            }
+
+            sb.append(ipSb);
+        }else{
+            sb.append(Constants.DEFAULT_WORKER_ID);
+        }
+
+
+        return  sb.toString();
     }
 
     /**
@@ -1566,7 +1654,6 @@
         for (ProcessInstance processInstance:processInstanceList){
             processNeedFailoverProcessInstances(processInstance);
         }
-
     }
 
     @Transactional(value = "TransactionManager",rollbackFor = Exception.class)
@@ -1633,6 +1720,36 @@
         return workerGroupMapper.queryById(workerGroupId);
     }
 
+    /**
+     * query worker server by host
+     * @param host
+     * @return
+     */
+    public List<WorkerServer> queryWorkerServerByHost(String host){
+
+        return workerServerMapper.queryWorkerByHost(host);
+
+    }
+
+
+    /**
+     * get task worker group id
+     *
+     * @param taskInstance
+     * @return
+     */
+    public int getTaskWorkerGroupId(TaskInstance taskInstance) {
+        int taskWorkerGroupId = taskInstance.getWorkerGroupId();
+        ProcessInstance processInstance = findProcessInstanceByTaskId(taskInstance.getId());
+        if(processInstance == null){
+            logger.error("cannot find the task:{} process instance", taskInstance.getId());
+            return Constants.DEFAULT_WORKER_ID;
+        }
+        int processWorkerGroupId = processInstance.getWorkerGroupId();
+
+        taskWorkerGroupId = (taskWorkerGroupId <= 0 ? processWorkerGroupId : taskWorkerGroupId);
+        return taskWorkerGroupId;
+    }
 
 
 }
diff --git a/escheduler-dao/src/main/java/cn/escheduler/dao/mapper/DataSourceMapper.java b/escheduler-dao/src/main/java/cn/escheduler/dao/mapper/DataSourceMapper.java
index 149d15e..66a7b61 100644
--- a/escheduler-dao/src/main/java/cn/escheduler/dao/mapper/DataSourceMapper.java
+++ b/escheduler-dao/src/main/java/cn/escheduler/dao/mapper/DataSourceMapper.java
@@ -216,4 +216,17 @@
     @SelectProvider(type = DataSourceMapperProvider.class, method = "queryDatasourceExceptUserId")
     List<DataSource> queryDatasourceExceptUserId(@Param("userId") int userId);
 
+    @Results(value = {
+            @Result(property = "id", column = "id", id = true, javaType = Integer.class, jdbcType = JdbcType.INTEGER),
+            @Result(property = "name", column = "name", javaType = String.class, jdbcType = JdbcType.VARCHAR),
+            @Result(property = "note", column = "note", javaType = String.class, jdbcType = JdbcType.VARCHAR),
+            @Result(property = "type", column = "type", typeHandler = EnumOrdinalTypeHandler.class, javaType = DbType.class, jdbcType = JdbcType.INTEGER),
+            @Result(property = "userId", column = "user_id", javaType = Integer.class, jdbcType = JdbcType.INTEGER),
+            @Result(property = "connectionParams", column = "connection_params", javaType = String.class, jdbcType = JdbcType.VARCHAR),
+            @Result(property = "createTime", column = "create_time", javaType = Timestamp.class, jdbcType = JdbcType.DATE),
+            @Result(property = "updateTime", column = "update_time", javaType = Timestamp.class, jdbcType = JdbcType.DATE)
+    })
+    @SelectProvider(type = DataSourceMapperProvider.class, method = "listAllDataSourceByType")
+    List<DataSource> listAllDataSourceByType(@Param("type") Integer type);
+
 }
diff --git a/escheduler-dao/src/main/java/cn/escheduler/dao/mapper/DataSourceMapperProvider.java b/escheduler-dao/src/main/java/cn/escheduler/dao/mapper/DataSourceMapperProvider.java
index 7322805..7613e55 100644
--- a/escheduler-dao/src/main/java/cn/escheduler/dao/mapper/DataSourceMapperProvider.java
+++ b/escheduler-dao/src/main/java/cn/escheduler/dao/mapper/DataSourceMapperProvider.java
@@ -175,8 +175,7 @@
   }
 
   /**
-   * 查询总的数据源数目
-   *
+   * Query the total number of data sources
    * @param parameter
    * @return
    */
@@ -228,4 +227,20 @@
       WHERE("user_id <> #{userId}");
     }}.toString();
   }
+
+
+  /**
+   * list all data source by type
+   *
+   * @param parameter
+   * @return
+   */
+  public String listAllDataSourceByType(Map<String, Object> parameter) {
+    return new SQL() {{
+      SELECT("*");
+      FROM(TABLE_NAME);
+      WHERE("type = #{type}");
+    }}.toString();
+  }
+
 }
diff --git a/escheduler-dao/src/main/java/cn/escheduler/dao/mapper/ProcessDefinitionMapper.java b/escheduler-dao/src/main/java/cn/escheduler/dao/mapper/ProcessDefinitionMapper.java
index b750b9d..0c88fa7 100644
--- a/escheduler-dao/src/main/java/cn/escheduler/dao/mapper/ProcessDefinitionMapper.java
+++ b/escheduler-dao/src/main/java/cn/escheduler/dao/mapper/ProcessDefinitionMapper.java
@@ -95,6 +95,7 @@
             @Result(property = "connects", column = "connects", javaType = String.class, jdbcType = JdbcType.VARCHAR),
             @Result(property = "projectName", column = "project_name", javaType = String.class, jdbcType = JdbcType.VARCHAR),
             @Result(property = "timeout", column = "timeout", javaType = Integer.class, jdbcType = JdbcType.INTEGER),
+            @Result(property = "tenantId", column = "tenant_id", javaType = Integer.class, jdbcType = JdbcType.INTEGER),
             @Result(property = "receivers", column = "receivers", javaType = String.class, jdbcType = JdbcType.VARCHAR),
             @Result(property = "receiversCc", column = "receivers_cc", javaType = String.class, jdbcType = JdbcType.VARCHAR)
 
@@ -123,6 +124,7 @@
             @Result(property = "locations", column = "locations", javaType = String.class, jdbcType = JdbcType.VARCHAR),
             @Result(property = "connects", column = "connects", javaType = String.class, jdbcType = JdbcType.VARCHAR),
             @Result(property = "timeout", column = "timeout", javaType = Integer.class, jdbcType = JdbcType.INTEGER),
+            @Result(property = "tenantId", column = "tenant_id", javaType = Integer.class, jdbcType = JdbcType.INTEGER),
             @Result(property = "projectName", column = "project_name", javaType = String.class, jdbcType = JdbcType.VARCHAR)
     })
     @SelectProvider(type = ProcessDefinitionMapperProvider.class, method = "queryByDefineName")
@@ -160,6 +162,7 @@
             @Result(property = "flag", column = "flag", typeHandler = EnumOrdinalTypeHandler.class, javaType = Flag.class, jdbcType = JdbcType.TINYINT),
             @Result(property = "userName", column = "user_name", javaType = String.class, jdbcType = JdbcType.VARCHAR),
             @Result(property = "timeout", column = "timeout", javaType = Integer.class, jdbcType = JdbcType.INTEGER),
+            @Result(property = "tenantId", column = "tenant_id", javaType = Integer.class, jdbcType = JdbcType.INTEGER),
             @Result(property = "projectName", column = "project_name", javaType = String.class, jdbcType = JdbcType.VARCHAR)
     })
     @SelectProvider(type = ProcessDefinitionMapperProvider.class, method = "queryAllDefinitionList")
@@ -187,6 +190,7 @@
             @Result(property = "userName", column = "user_name", javaType = String.class, jdbcType = JdbcType.VARCHAR),
             @Result(property = "scheduleReleaseState", column = "schedule_release_state",  typeHandler = EnumOrdinalTypeHandler.class, javaType = ReleaseState.class, jdbcType = JdbcType.TINYINT),
             @Result(property = "timeout", column = "timeout", javaType = Integer.class, jdbcType = JdbcType.INTEGER),
+            @Result(property = "tenantId", column = "tenant_id", javaType = Integer.class, jdbcType = JdbcType.INTEGER),
             @Result(property = "projectName", column = "project_name", javaType = String.class, jdbcType = JdbcType.VARCHAR)
     })
     @SelectProvider(type = ProcessDefinitionMapperProvider.class, method = "queryDefineListPaging")
@@ -216,6 +220,7 @@
             @Result(property = "connects", column = "connects", javaType = String.class, jdbcType = JdbcType.VARCHAR),
             @Result(property = "userName", column = "user_name", javaType = String.class, jdbcType = JdbcType.VARCHAR),
             @Result(property = "timeout", column = "timeout", javaType = Integer.class, jdbcType = JdbcType.INTEGER),
+            @Result(property = "tenantId", column = "tenant_id", javaType = Integer.class, jdbcType = JdbcType.INTEGER),
             @Result(property = "projectName", column = "project_name", javaType = String.class, jdbcType = JdbcType.VARCHAR)
     })
     @SelectProvider(type = ProcessDefinitionMapperProvider.class, method = "queryDefinitionListByIdList")
diff --git a/escheduler-dao/src/main/java/cn/escheduler/dao/mapper/ProcessDefinitionMapperProvider.java b/escheduler-dao/src/main/java/cn/escheduler/dao/mapper/ProcessDefinitionMapperProvider.java
index 48e8d0b..7d78aa9 100644
--- a/escheduler-dao/src/main/java/cn/escheduler/dao/mapper/ProcessDefinitionMapperProvider.java
+++ b/escheduler-dao/src/main/java/cn/escheduler/dao/mapper/ProcessDefinitionMapperProvider.java
@@ -56,6 +56,7 @@
                 VALUES("`create_time`", "#{processDefinition.createTime}");
                 VALUES("`update_time`", "#{processDefinition.updateTime}");
                 VALUES("`timeout`", "#{processDefinition.timeout}");
+                VALUES("`tenant_id`", "#{processDefinition.tenantId}");
                 VALUES("`flag`", EnumFieldUtil.genFieldStr("processDefinition.flag", ReleaseState.class));
                 VALUES("`user_id`", "#{processDefinition.userId}");
 
@@ -102,6 +103,7 @@
                 SET("`create_time`=#{processDefinition.createTime}");
                 SET("`update_time`=#{processDefinition.updateTime}");
                 SET("`timeout`=#{processDefinition.timeout}");
+                SET("`tenant_id`=#{processDefinition.tenantId}");
                 SET("`flag`="+EnumFieldUtil.genFieldStr("processDefinition.flag", Flag.class));
                 SET("`user_id`=#{processDefinition.userId}");
 
diff --git a/escheduler-dao/src/main/java/cn/escheduler/dao/mapper/ProcessInstanceMapper.java b/escheduler-dao/src/main/java/cn/escheduler/dao/mapper/ProcessInstanceMapper.java
index c76b7bd..98a7c22 100644
--- a/escheduler-dao/src/main/java/cn/escheduler/dao/mapper/ProcessInstanceMapper.java
+++ b/escheduler-dao/src/main/java/cn/escheduler/dao/mapper/ProcessInstanceMapper.java
@@ -97,6 +97,7 @@
             @Result(property = "queue", column = "queue", javaType = String.class, jdbcType = JdbcType.VARCHAR),
             @Result(property = "workerGroupId", column = "worker_group_id",  javaType = Integer.class, jdbcType = JdbcType.INTEGER),
             @Result(property = "timeout", column = "timeout",  javaType = Integer.class, jdbcType = JdbcType.INTEGER),
+            @Result(property = "tenantId", column = "tenant_id",  javaType = Integer.class, jdbcType = JdbcType.INTEGER),
             @Result(property = "processInstancePriority", column = "process_instance_priority", javaType = Priority.class, typeHandler = EnumOrdinalTypeHandler.class, jdbcType = JdbcType.TINYINT)
     })
     @SelectProvider(type = ProcessInstanceMapperProvider.class, method = "queryDetailById")
@@ -136,6 +137,7 @@
             @Result(property = "dependenceScheduleTimes", column = "dependence_schedule_times", javaType = String.class, jdbcType = JdbcType.VARCHAR),
             @Result(property = "workerGroupId", column = "worker_group_id",  javaType = Integer.class, jdbcType = JdbcType.INTEGER),
             @Result(property = "timeout", column = "timeout",  javaType = Integer.class, jdbcType = JdbcType.INTEGER),
+            @Result(property = "tenantId", column = "tenant_id",  javaType = Integer.class, jdbcType = JdbcType.INTEGER),
             @Result(property = "processInstancePriority", column = "process_instance_priority", javaType = Priority.class, typeHandler = EnumOrdinalTypeHandler.class, jdbcType = JdbcType.TINYINT)
     })
     @SelectProvider(type = ProcessInstanceMapperProvider.class, method = "queryById")
@@ -175,6 +177,7 @@
             @Result(property = "processInstanceJson", column = "process_instance_json", javaType = String.class, jdbcType = JdbcType.VARCHAR),
             @Result(property = "workerGroupId", column = "worker_group_id",  javaType = Integer.class, jdbcType = JdbcType.INTEGER),
             @Result(property = "timeout", column = "timeout",  javaType = Integer.class, jdbcType = JdbcType.INTEGER),
+            @Result(property = "tenantId", column = "tenant_id",  javaType = Integer.class, jdbcType = JdbcType.INTEGER),
             @Result(property = "processInstancePriority", column = "process_instance_priority", javaType = Priority.class, typeHandler = EnumOrdinalTypeHandler.class, jdbcType = JdbcType.TINYINT)
 
     })
@@ -214,6 +217,7 @@
             @Result(property = "processInstanceJson", column = "process_instance_json", javaType = String.class, jdbcType = JdbcType.VARCHAR),
             @Result(property = "workerGroupId", column = "worker_group_id",  javaType = Integer.class, jdbcType = JdbcType.INTEGER),
             @Result(property = "timeout", column = "timeout",  javaType = Integer.class, jdbcType = JdbcType.INTEGER),
+            @Result(property = "tenantId", column = "tenant_id",  javaType = Integer.class, jdbcType = JdbcType.INTEGER),
             @Result(property = "processInstancePriority", column = "process_instance_priority", javaType = Priority.class, typeHandler = EnumOrdinalTypeHandler.class, jdbcType = JdbcType.TINYINT)
 
     })
@@ -262,6 +266,7 @@
             @Result(property = "processInstanceJson", column = "process_instance_json", javaType = String.class, jdbcType = JdbcType.VARCHAR),
             @Result(property = "workerGroupId", column = "worker_group_id",  javaType = Integer.class, jdbcType = JdbcType.INTEGER),
             @Result(property = "timeout", column = "timeout",  javaType = Integer.class, jdbcType = JdbcType.INTEGER),
+            @Result(property = "tenantId", column = "tenant_id",  javaType = Integer.class, jdbcType = JdbcType.INTEGER),
             @Result(property = "processInstancePriority", column = "process_instance_priority", javaType = Priority.class, typeHandler = EnumOrdinalTypeHandler.class, jdbcType = JdbcType.TINYINT)
 
     })
@@ -359,6 +364,7 @@
             @Result(property = "processInstanceJson", column = "process_instance_json", javaType = String.class, jdbcType = JdbcType.VARCHAR),
             @Result(property = "workerGroupId", column = "worker_group_id",  javaType = Integer.class, jdbcType = JdbcType.INTEGER),
             @Result(property = "timeout", column = "timeout",  javaType = Integer.class, jdbcType = JdbcType.INTEGER),
+            @Result(property = "tenantId", column = "tenant_id",  javaType = Integer.class, jdbcType = JdbcType.INTEGER),
             @Result(property = "processInstancePriority", column = "process_instance_priority", javaType = Priority.class, typeHandler = EnumOrdinalTypeHandler.class, jdbcType = JdbcType.TINYINT)
 
     })
@@ -452,6 +458,7 @@
             @Result(property = "processInstanceJson", column = "process_instance_json", javaType = String.class, jdbcType = JdbcType.VARCHAR),
             @Result(property = "workerGroupId", column = "worker_group_id",  javaType = Integer.class, jdbcType = JdbcType.INTEGER),
             @Result(property = "timeout", column = "timeout",  javaType = Integer.class, jdbcType = JdbcType.INTEGER),
+            @Result(property = "tenantId", column = "tenant_id",  javaType = Integer.class, jdbcType = JdbcType.INTEGER),
             @Result(property = "processInstancePriority", column = "process_instance_priority", javaType = Priority.class, typeHandler = EnumOrdinalTypeHandler.class, jdbcType = JdbcType.TINYINT)
 
     })
@@ -497,6 +504,7 @@
             @Result(property = "processInstanceJson", column = "process_instance_json", javaType = String.class, jdbcType = JdbcType.VARCHAR),
             @Result(property = "workerGroupId", column = "worker_group_id",  javaType = Integer.class, jdbcType = JdbcType.INTEGER),
             @Result(property = "timeout", column = "timeout",  javaType = Integer.class, jdbcType = JdbcType.INTEGER),
+            @Result(property = "tenantId", column = "tenant_id",  javaType = Integer.class, jdbcType = JdbcType.INTEGER),
             @Result(property = "processInstancePriority", column = "process_instance_priority", javaType = Priority.class, typeHandler = EnumOrdinalTypeHandler.class, jdbcType = JdbcType.TINYINT)
 
     })
@@ -542,6 +550,7 @@
             @Result(property = "processInstanceJson", column = "process_instance_json", javaType = String.class, jdbcType = JdbcType.VARCHAR),
             @Result(property = "workerGroupId", column = "worker_group_id",  javaType = Integer.class, jdbcType = JdbcType.INTEGER),
             @Result(property = "timeout", column = "timeout",  javaType = Integer.class, jdbcType = JdbcType.INTEGER),
+            @Result(property = "tenantId", column = "tenant_id",  javaType = Integer.class, jdbcType = JdbcType.INTEGER),
             @Result(property = "processInstancePriority", column = "process_instance_priority", javaType = Priority.class, typeHandler = EnumOrdinalTypeHandler.class, jdbcType = JdbcType.TINYINT)
 
     })
@@ -585,6 +594,7 @@
             @Result(property = "processInstanceJson", column = "process_instance_json", javaType = String.class, jdbcType = JdbcType.VARCHAR),
             @Result(property = "workerGroupId", column = "worker_group_id",  javaType = Integer.class, jdbcType = JdbcType.INTEGER),
             @Result(property = "timeout", column = "timeout",  javaType = Integer.class, jdbcType = JdbcType.INTEGER),
+            @Result(property = "tenantId", column = "tenant_id",  javaType = Integer.class, jdbcType = JdbcType.INTEGER),
             @Result(property = "processInstancePriority", column = "process_instance_priority", javaType = Priority.class, typeHandler = EnumOrdinalTypeHandler.class, jdbcType = JdbcType.TINYINT)
     })
     @SelectProvider(type = ProcessInstanceMapperProvider.class, method = "queryLastRunningProcess")
@@ -628,6 +638,7 @@
             @Result(property = "processInstanceJson", column = "process_instance_json", javaType = String.class, jdbcType = JdbcType.VARCHAR),
             @Result(property = "workerGroupId", column = "worker_group_id",  javaType = Integer.class, jdbcType = JdbcType.INTEGER),
             @Result(property = "timeout", column = "timeout",  javaType = Integer.class, jdbcType = JdbcType.INTEGER),
+            @Result(property = "tenantId", column = "tenant_id",  javaType = Integer.class, jdbcType = JdbcType.INTEGER),
             @Result(property = "processInstancePriority", column = "process_instance_priority", javaType = Priority.class, typeHandler = EnumOrdinalTypeHandler.class, jdbcType = JdbcType.TINYINT)
     })
     @SelectProvider(type = ProcessInstanceMapperProvider.class, method = "queryLastManualProcess")
diff --git a/escheduler-dao/src/main/java/cn/escheduler/dao/mapper/ProcessInstanceMapperProvider.java b/escheduler-dao/src/main/java/cn/escheduler/dao/mapper/ProcessInstanceMapperProvider.java
index a6d09d4..78165e3 100644
--- a/escheduler-dao/src/main/java/cn/escheduler/dao/mapper/ProcessInstanceMapperProvider.java
+++ b/escheduler-dao/src/main/java/cn/escheduler/dao/mapper/ProcessInstanceMapperProvider.java
@@ -69,6 +69,7 @@
                 VALUES("`executor_id`", "#{processInstance.executorId}");
                 VALUES("`worker_group_id`", "#{processInstance.workerGroupId}");
                 VALUES("`timeout`", "#{processInstance.timeout}");
+                VALUES("`tenant_id`", "#{processInstance.tenantId}");
                 VALUES("`process_instance_priority`", EnumFieldUtil.genFieldStr("processInstance.processInstancePriority", Priority.class));
             }
         }.toString();
@@ -141,6 +142,7 @@
                 SET("`dependence_schedule_times`=#{processInstance.dependenceScheduleTimes}");
                 SET("`is_sub_process`="+EnumFieldUtil.genFieldStr("processInstance.isSubProcess", Flag.class));
                 SET("`executor_id`=#{processInstance.executorId}");
+                SET("`tenant_id`=#{processInstance.tenantId}");
                 SET("`worker_group_id`=#{processInstance.workerGroupId}");
                 SET("`timeout`=#{processInstance.timeout}");
 
@@ -220,11 +222,11 @@
     public String queryDetailById(Map<String, Object> parameter) {
         return new SQL() {
             {
-                SELECT("inst.*,q.queue_name as queue,t.tenant_code,UNIX_TIMESTAMP(inst.end_time)-UNIX_TIMESTAMP(inst.start_time) as duration");
+                SELECT("inst.*,UNIX_TIMESTAMP(inst.end_time)-UNIX_TIMESTAMP(inst.start_time) as duration");
 
-                FROM(TABLE_NAME + "  inst, t_escheduler_user u,t_escheduler_tenant t,t_escheduler_queue q");
+                FROM(TABLE_NAME + "  inst");
 
-                WHERE("inst.executor_id = u.id AND u.tenant_id = t.id AND t.queue_id = q.id AND inst.id = #{processId}");
+                WHERE("inst.id = #{processId}");
             }
         }.toString();
     }
@@ -402,7 +404,12 @@
 
                 FROM(TABLE_NAME);
 
-                WHERE("`host` = #{host} and `state` in (" + strStates.toString() +")");
+                Object host = parameter.get("host");
+                if(host != null && StringUtils.isNotEmpty(host.toString())){
+
+                    WHERE("`host` = #{host} ");
+                }
+                WHERE("`state` in (" + strStates.toString() +")");
                 ORDER_BY("`id` asc");
 
 
diff --git a/escheduler-dao/src/main/java/cn/escheduler/dao/mapper/ResourceMapper.java b/escheduler-dao/src/main/java/cn/escheduler/dao/mapper/ResourceMapper.java
index 9d2ab80..c57d151 100644
--- a/escheduler-dao/src/main/java/cn/escheduler/dao/mapper/ResourceMapper.java
+++ b/escheduler-dao/src/main/java/cn/escheduler/dao/mapper/ResourceMapper.java
@@ -274,5 +274,21 @@
     @SelectProvider(type = ResourceMapperProvider.class, method = "queryTenantCodeByResourceName")
     String queryTenantCodeByResourceName(@Param("resName") String  resName);
 
-
+  /**
+   * query resource list that the appointed user has permission
+   * @param type
+   * @return
+   */
+  @Results(value = {@Result(property = "id", column = "id", id = true, javaType = int.class, jdbcType = JdbcType.INTEGER),
+          @Result(property = "alias", column = "alias", javaType = String.class, jdbcType = JdbcType.VARCHAR),
+          @Result(property = "fileName", column = "file_name", javaType = String.class, jdbcType = JdbcType.VARCHAR),
+          @Result(property = "desc", column = "desc", javaType = String.class, jdbcType = JdbcType.VARCHAR),
+          @Result(property = "userId", column = "user_id", javaType = int.class, jdbcType = JdbcType.INTEGER),
+          @Result(property = "type", column = "type", typeHandler = EnumOrdinalTypeHandler.class, javaType = ResourceType.class, jdbcType = JdbcType.TINYINT),
+          @Result(property = "size", column = "size", javaType = Long.class, jdbcType = JdbcType.BIGINT),
+          @Result(property = "createTime", column = "create_time", javaType = Timestamp.class, jdbcType = JdbcType.DATE),
+          @Result(property = "updateTime", column = "update_time", javaType = Timestamp.class, jdbcType = JdbcType.DATE)
+  })
+  @SelectProvider(type = ResourceMapperProvider.class, method = "listAllResourceByType")
+  List<Resource> listAllResourceByType(@Param("type") Integer type);
 }
diff --git a/escheduler-dao/src/main/java/cn/escheduler/dao/mapper/ResourceMapperProvider.java b/escheduler-dao/src/main/java/cn/escheduler/dao/mapper/ResourceMapperProvider.java
index 4314b8f..a943bb6 100644
--- a/escheduler-dao/src/main/java/cn/escheduler/dao/mapper/ResourceMapperProvider.java
+++ b/escheduler-dao/src/main/java/cn/escheduler/dao/mapper/ResourceMapperProvider.java
@@ -295,4 +295,19 @@
       WHERE("type = #{type} and user_id = #{userId}");
     }}.toString();
   }
+
+  /**
+   * list all resource by type
+   *
+   * @param parameter
+   * @return
+   */
+  public String listAllResourceByType(Map<String, Object> parameter) {
+    return new SQL() {{
+      SELECT("*");
+      FROM(TABLE_NAME);
+      WHERE("type = #{type}");
+    }}.toString();
+  }
+
 }
diff --git a/escheduler-dao/src/main/java/cn/escheduler/dao/mapper/TaskInstanceMapperProvider.java b/escheduler-dao/src/main/java/cn/escheduler/dao/mapper/TaskInstanceMapperProvider.java
index 511b097..ce1e69f 100644
--- a/escheduler-dao/src/main/java/cn/escheduler/dao/mapper/TaskInstanceMapperProvider.java
+++ b/escheduler-dao/src/main/java/cn/escheduler/dao/mapper/TaskInstanceMapperProvider.java
@@ -228,7 +228,12 @@
                 SELECT("*, UNIX_TIMESTAMP(end_time)-UNIX_TIMESTAMP(start_time) as duration");
                 FROM(TABLE_NAME);
 
-                WHERE("`host` = #{host} and `state` in (" + strStates.toString() +")");
+                Object host = parameter.get("host");
+                if(host != null && StringUtils.isNotEmpty(host.toString())){
+
+                    WHERE("`host` = #{host} ");
+                }
+                WHERE("`state` in (" + strStates.toString() +")");
                 ORDER_BY("`id` asc");
             }
         }.toString();
diff --git a/escheduler-dao/src/main/java/cn/escheduler/dao/mapper/UserMapperProvider.java b/escheduler-dao/src/main/java/cn/escheduler/dao/mapper/UserMapperProvider.java
index 4a7df12..363cd38 100644
--- a/escheduler-dao/src/main/java/cn/escheduler/dao/mapper/UserMapperProvider.java
+++ b/escheduler-dao/src/main/java/cn/escheduler/dao/mapper/UserMapperProvider.java
@@ -187,7 +187,6 @@
         return new SQL() {{
             SELECT("count(0)");
             FROM(TABLE_NAME);
-            WHERE("user_type = 1");
             Object searchVal = parameter.get("searchVal");
             if(searchVal != null && StringUtils.isNotEmpty(searchVal.toString())){
                 WHERE( " user_name like concat('%', #{searchVal}, '%') ");
@@ -209,7 +208,6 @@
                 FROM(TABLE_NAME + " u ");
                 LEFT_OUTER_JOIN("t_escheduler_tenant t on u.tenant_id = t.id");
                 LEFT_OUTER_JOIN("t_escheduler_queue q on t.queue_id = q.id");
-                WHERE("u.user_type = 1");
                 Object searchVal = parameter.get("searchVal");
                 if(searchVal != null && StringUtils.isNotEmpty(searchVal.toString())){
                     WHERE( " u.user_name like concat('%', #{searchVal}, '%') ");
diff --git a/escheduler-dao/src/main/java/cn/escheduler/dao/mapper/WorkerServerMapper.java b/escheduler-dao/src/main/java/cn/escheduler/dao/mapper/WorkerServerMapper.java
index b5ea3aa..5e511a4 100644
--- a/escheduler-dao/src/main/java/cn/escheduler/dao/mapper/WorkerServerMapper.java
+++ b/escheduler-dao/src/main/java/cn/escheduler/dao/mapper/WorkerServerMapper.java
@@ -43,6 +43,23 @@
     List<WorkerServer> queryAllWorker();
 
     /**
+     * query worker list
+     *
+     * @return
+     */
+    @Results(value = {
+            @Result(property = "id", column = "id", javaType = Integer.class, jdbcType = JdbcType.INTEGER),
+            @Result(property = "host", column = "host", javaType = String.class, jdbcType = JdbcType.VARCHAR),
+            @Result(property = "port", column = "port", javaType = int.class, jdbcType = JdbcType.INTEGER),
+            @Result(property = "zkDirectory", column = "zk_directory", javaType = String.class, jdbcType = JdbcType.VARCHAR),
+            @Result(property = "resInfo", column = "res_info", javaType = String.class, jdbcType = JdbcType.VARCHAR),
+            @Result(property = "createTime", column = "create_time", javaType = Date.class, jdbcType = JdbcType.TIMESTAMP),
+            @Result(property = "lastHeartbeatTime", column = "last_heartbeat_time", javaType = Date.class, jdbcType = JdbcType.TIMESTAMP)
+    })
+    @SelectProvider(type = WorkerServerMapperProvider.class, method = "queryWorkerByHost")
+    List<WorkerServer> queryWorkerByHost(@Param("host") String host);
+
+    /**
      * insert worker server
      *
      * @param workerServer
diff --git a/escheduler-dao/src/main/java/cn/escheduler/dao/mapper/WorkerServerMapperProvider.java b/escheduler-dao/src/main/java/cn/escheduler/dao/mapper/WorkerServerMapperProvider.java
index bd5af7d..15b330e 100644
--- a/escheduler-dao/src/main/java/cn/escheduler/dao/mapper/WorkerServerMapperProvider.java
+++ b/escheduler-dao/src/main/java/cn/escheduler/dao/mapper/WorkerServerMapperProvider.java
@@ -38,6 +38,21 @@
     }
 
     /**
+     * query worker list
+     * @return
+     */
+    public String queryWorkerByHost(Map<String, Object> parameter) {
+        return new SQL() {{
+            SELECT("*");
+
+            FROM(TABLE_NAME);
+
+            WHERE("host = #{host}");
+        }}.toString();
+    }
+
+
+    /**
      * insert worker server
      * @param parameter
      * @return
diff --git a/escheduler-dao/src/main/java/cn/escheduler/dao/model/ProcessData.java b/escheduler-dao/src/main/java/cn/escheduler/dao/model/ProcessData.java
index 0623144..28dea1f 100644
--- a/escheduler-dao/src/main/java/cn/escheduler/dao/model/ProcessData.java
+++ b/escheduler-dao/src/main/java/cn/escheduler/dao/model/ProcessData.java
@@ -39,6 +39,8 @@
 
   private int timeout;
 
+  private int tenantId;
+
 
   public ProcessData() {
   }
@@ -92,4 +94,12 @@
   public void setTimeout(int timeout) {
     this.timeout = timeout;
   }
+
+  public int getTenantId() {
+    return tenantId;
+  }
+
+  public void setTenantId(int tenantId) {
+    this.tenantId = tenantId;
+  }
 }
diff --git a/escheduler-dao/src/main/java/cn/escheduler/dao/model/ProcessDefinition.java b/escheduler-dao/src/main/java/cn/escheduler/dao/model/ProcessDefinition.java
index fb0c1d0..868078b 100644
--- a/escheduler-dao/src/main/java/cn/escheduler/dao/model/ProcessDefinition.java
+++ b/escheduler-dao/src/main/java/cn/escheduler/dao/model/ProcessDefinition.java
@@ -141,6 +141,11 @@
      */
     private int timeout;
 
+    /**
+     * tenant id
+     */
+    private int tenantId;
+
 
     public String getName() {
         return name;
@@ -354,7 +359,15 @@
                 ", receiversCc='" + receiversCc + '\'' +
                 ", scheduleReleaseState=" + scheduleReleaseState +
                 ", timeout=" + timeout +
+                ", tenantId=" + tenantId +
                 '}';
     }
 
+    public int getTenantId() {
+        return tenantId;
+    }
+
+    public void setTenantId(int tenantId) {
+        this.tenantId = tenantId;
+    }
 }
diff --git a/escheduler-dao/src/main/java/cn/escheduler/dao/model/ProcessInstance.java b/escheduler-dao/src/main/java/cn/escheduler/dao/model/ProcessInstance.java
index f156752..5c9418c 100644
--- a/escheduler-dao/src/main/java/cn/escheduler/dao/model/ProcessInstance.java
+++ b/escheduler-dao/src/main/java/cn/escheduler/dao/model/ProcessInstance.java
@@ -188,6 +188,27 @@
      */
     private int timeout;
 
+
+    /**
+     * tenant id
+     */
+    private int tenantId;
+
+    /**
+     * worker group name. for api.
+     */
+    private String workerGroupName;
+
+    /**
+     * receivers for api
+     */
+    private String receivers;
+
+    /**
+     * receivers cc for api
+     */
+    private String receiversCc;
+
     public ProcessInstance(){
 
     }
@@ -534,6 +555,7 @@
                 ", processInstanceJson='" + processInstanceJson + '\'' +
                 ", executorId=" + executorId +
                 ", tenantCode='" + tenantCode + '\'' +
+                ", tenantId='" + tenantId + '\'' +
                 ", queue='" + queue + '\'' +
                 ", isSubProcess=" + isSubProcess +
                 ", locations='" + locations + '\'' +
@@ -546,4 +568,35 @@
                 '}';
     }
 
+    public void setTenantId(int tenantId) {
+        this.tenantId = tenantId;
+    }
+
+    public int getTenantId() {
+        return this.tenantId ;
+    }
+
+    public String getWorkerGroupName() {
+        return workerGroupName;
+    }
+
+    public void setWorkerGroupName(String workerGroupName) {
+        this.workerGroupName = workerGroupName;
+    }
+
+    public String getReceivers() {
+        return receivers;
+    }
+
+    public void setReceivers(String receivers) {
+        this.receivers = receivers;
+    }
+
+    public String getReceiversCc() {
+        return receiversCc;
+    }
+
+    public void setReceiversCc(String receiversCc) {
+        this.receiversCc = receiversCc;
+    }
 }
diff --git a/escheduler-dao/src/main/java/cn/escheduler/dao/model/User.java b/escheduler-dao/src/main/java/cn/escheduler/dao/model/User.java
index 6f831fb..bad6b63 100644
--- a/escheduler-dao/src/main/java/cn/escheduler/dao/model/User.java
+++ b/escheduler-dao/src/main/java/cn/escheduler/dao/model/User.java
@@ -17,12 +17,15 @@
 package cn.escheduler.dao.model;
 
 import cn.escheduler.common.enums.UserType;
+import io.swagger.annotations.ApiModel;
+import io.swagger.annotations.ApiModelProperty;
 
 import java.util.Date;
 
 /**
  * user
  */
+@ApiModel(description = "UserModelDesc")
 public class  User {
 
     /**
@@ -33,21 +36,25 @@
     /**
      * user name
      */
+    @ApiModelProperty(name = "userName", notes = "USER_NAME",dataType = "String",required = true)
     private String userName;
 
     /**
      * user password
      */
+    @ApiModelProperty(name = "userPassword", notes = "USER_PASSWORD",dataType = "String",required = true)
     private String userPassword;
 
     /**
      * mail
      */
+    @ApiModelProperty(name = "email", notes = "email",dataType = "String",required = true)
     private String email;
 
     /**
      * phone
      */
+    @ApiModelProperty(name = "phone", notes = "phone",dataType = "String",required = true)
     private String phone;
 
     /**
diff --git a/escheduler-dao/src/main/java/cn/escheduler/dao/upgrade/UpgradeDao.java b/escheduler-dao/src/main/java/cn/escheduler/dao/upgrade/UpgradeDao.java
index 96199b0..6fc8a61 100644
--- a/escheduler-dao/src/main/java/cn/escheduler/dao/upgrade/UpgradeDao.java
+++ b/escheduler-dao/src/main/java/cn/escheduler/dao/upgrade/UpgradeDao.java
@@ -16,7 +16,7 @@
  */
 package cn.escheduler.dao.upgrade;
 
-import cn.escheduler.common.utils.MysqlUtil;
+import cn.escheduler.common.utils.MysqlUtils;
 import cn.escheduler.common.utils.ScriptRunner;
 import cn.escheduler.dao.AbstractBaseDao;
 import cn.escheduler.dao.datasource.ConnectionFactory;
@@ -98,7 +98,7 @@
             logger.error(e.getMessage(),e);
             throw new RuntimeException(e.getMessage(),e);
         } finally {
-            MysqlUtil.realeaseResource(null, null, conn);
+            MysqlUtils.releaseResource(null, null, conn);
 
         }
 
@@ -126,7 +126,7 @@
             logger.error(e.getMessage(),e);
             throw new RuntimeException(e.getMessage(),e);
         } finally {
-            MysqlUtil.realeaseResource(null, null, conn);
+            MysqlUtils.releaseResource(null, null, conn);
 
         }
 
@@ -152,7 +152,7 @@
             logger.error(e.getMessage(),e);
             throw new RuntimeException(e.getMessage(),e);
         } finally {
-            MysqlUtil.realeaseResource(null, null, conn);
+            MysqlUtils.releaseResource(null, null, conn);
 
         }
 
@@ -179,7 +179,7 @@
             logger.error(e.getMessage(),e);
             throw new RuntimeException(e.getMessage(),e);
         } finally {
-            MysqlUtil.realeaseResource(null, null, conn);
+            MysqlUtils.releaseResource(null, null, conn);
 
         }
 
@@ -207,7 +207,7 @@
             logger.error(e.getMessage(),e);
             throw new RuntimeException("sql: " + sql, e);
         } finally {
-            MysqlUtil.realeaseResource(rs, pstmt, conn);
+            MysqlUtils.releaseResource(rs, pstmt, conn);
 
         }
     }
@@ -277,7 +277,7 @@
             logger.error(e.getMessage(),e);
             throw new RuntimeException(e.getMessage(),e);
         } finally {
-            MysqlUtil.realeaseResource(null, pstmt, conn);
+            MysqlUtils.releaseResource(null, pstmt, conn);
         }
 
     }
@@ -316,7 +316,7 @@
             logger.error(e.getMessage(),e);
             throw new RuntimeException(e.getMessage(),e);
         } finally {
-            MysqlUtil.realeaseResource(null, pstmt, conn);
+            MysqlUtils.releaseResource(null, pstmt, conn);
         }
 
     }
@@ -338,7 +338,7 @@
             logger.error(e.getMessage(),e);
             throw new RuntimeException("sql: " + upgradeSQL, e);
         } finally {
-            MysqlUtil.realeaseResource(null, pstmt, conn);
+            MysqlUtils.releaseResource(null, pstmt, conn);
         }
 
     }
diff --git a/escheduler-dao/src/main/java/cn/escheduler/dao/utils/DagHelper.java b/escheduler-dao/src/main/java/cn/escheduler/dao/utils/DagHelper.java
index bc52e85..bc509b1 100644
--- a/escheduler-dao/src/main/java/cn/escheduler/dao/utils/DagHelper.java
+++ b/escheduler-dao/src/main/java/cn/escheduler/dao/utils/DagHelper.java
@@ -18,16 +18,20 @@
 
 
 import cn.escheduler.common.enums.TaskDependType;
+import cn.escheduler.common.graph.DAG;
 import cn.escheduler.common.model.TaskNode;
 import cn.escheduler.common.model.TaskNodeRelation;
 import cn.escheduler.common.process.ProcessDag;
 import cn.escheduler.common.utils.JSONUtils;
 import cn.escheduler.dao.model.ProcessData;
+import cn.escheduler.dao.model.TaskInstance;
+import org.apache.commons.collections4.CollectionUtils;
+import org.apache.commons.lang3.StringUtils;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
-import java.util.ArrayList;
-import java.util.List;
+import java.util.*;
+import java.util.concurrent.ConcurrentHashMap;
 
 /**
  * dag tools
@@ -105,8 +109,7 @@
         }
 
         for (TaskNode taskNode : tmpTaskNodeList) {
-            if ( !taskNode.isForbidden()
-                    && null == findNodeByName(destTaskNodeList, taskNode.getName())) {
+            if (null == findNodeByName(destTaskNodeList, taskNode.getName())) {
                 destTaskNodeList.add(taskNode);
             }
         }
@@ -193,6 +196,24 @@
         return processDag;
     }
 
+    /**
+     * parse the forbidden task nodes in process definition.
+     * @param processDefinitionJson
+     * @return
+     */
+    public static Map<String, TaskNode> getForbiddenTaskNodeMaps(String processDefinitionJson){
+        Map<String, TaskNode> forbidTaskNodeMap = new ConcurrentHashMap<>();
+        ProcessData processData = JSONUtils.parseObject(processDefinitionJson, ProcessData.class);
+
+        List<TaskNode> taskNodeList = processData.getTasks();
+        for(TaskNode node : taskNodeList){
+            if(node.isForbidden()){
+                forbidTaskNodeMap.putIfAbsent(node.getName(), node);
+            }
+        }
+        return forbidTaskNodeMap;
+    }
+
 
     /**
      * find node by node name
@@ -210,4 +231,100 @@
         }
         return null;
     }
+
+
+    /**
+     * get start vertex in one dag
+     * it would find the post node if the start vertex is forbidden running
+     * @param parentNodeName the previous node
+     * @param dag
+     * @param completeTaskList
+     * @return
+     */
+    public static Collection<String> getStartVertex(String parentNodeName, DAG<String, TaskNode, TaskNodeRelation> dag,
+                                                    Map<String, TaskInstance> completeTaskList){
+
+        if(completeTaskList == null){
+            completeTaskList = new HashMap<>();
+        }
+        Collection<String> startVertexs = null;
+        if(StringUtils.isNotEmpty(parentNodeName)){
+            startVertexs = dag.getSubsequentNodes(parentNodeName);
+        }else{
+            startVertexs = dag.getBeginNode();
+        }
+
+        List<String> tmpStartVertexs = new ArrayList<>();
+        if(startVertexs!= null){
+            tmpStartVertexs.addAll(startVertexs);
+        }
+
+        for(String start : startVertexs){
+            TaskNode startNode = dag.getNode(start);
+            if(!startNode.isForbidden() && !completeTaskList.containsKey(start)){
+                continue;
+            }
+            Collection<String> postNodes = getStartVertex(start, dag, completeTaskList);
+
+            for(String post : postNodes){
+                if(checkForbiddenPostCanSubmit(post, dag)){
+                    tmpStartVertexs.add(post);
+                }
+            }
+            tmpStartVertexs.remove(start);
+        }
+
+        return tmpStartVertexs;
+    }
+
+    /**
+     *
+     * @param postNodeName
+     * @param dag
+     * @return
+     */
+    private static boolean checkForbiddenPostCanSubmit(String postNodeName,  DAG<String, TaskNode, TaskNodeRelation> dag){
+
+        TaskNode postNode = dag.getNode(postNodeName);
+        List<String> dependList = postNode.getDepList();
+
+        for(String dependNodeName : dependList){
+            TaskNode dependNode = dag.getNode(dependNodeName);
+            if(!dependNode.isForbidden()){
+                return false;
+            }
+        }
+        return true;
+    }
+
+
+
+    /***
+     * generate dag graph
+     * @param processDag
+     * @return
+     */
+    public static DAG<String, TaskNode, TaskNodeRelation> buildDagGraph(ProcessDag processDag) {
+
+        DAG<String,TaskNode,TaskNodeRelation> dag = new DAG<>();
+
+        /**
+         * add vertex
+         */
+        if (CollectionUtils.isNotEmpty(processDag.getNodes())){
+            for (TaskNode node : processDag.getNodes()){
+                dag.addNode(node.getName(),node);
+            }
+        }
+
+        /**
+         * add edge
+         */
+        if (CollectionUtils.isNotEmpty(processDag.getEdges())){
+            for (TaskNodeRelation edge : processDag.getEdges()){
+                dag.addEdge(edge.getStartNode(),edge.getEndNode());
+            }
+        }
+        return dag;
+    }
 }
diff --git a/escheduler-dao/src/main/resources/dao/data_source.properties b/escheduler-dao/src/main/resources/dao/data_source.properties
index cac3aa5..3c89dd1 100644
--- a/escheduler-dao/src/main/resources/dao/data_source.properties
+++ b/escheduler-dao/src/main/resources/dao/data_source.properties
@@ -1,9 +1,9 @@
 # base spring data source configuration
 spring.datasource.type=com.alibaba.druid.pool.DruidDataSource
 spring.datasource.driver-class-name=com.mysql.jdbc.Driver
-spring.datasource.url=jdbc:mysql://192.168.xx.xx:3306/escheduler?characterEncoding=UTF-8
-spring.datasource.username=xx
-spring.datasource.password=xx
+spring.datasource.url=jdbc:mysql://192.168.220.188:3306/escheduler_new?characterEncoding=UTF-8
+spring.datasource.username=root
+spring.datasource.password=root@123
 
 # connection configuration
 spring.datasource.initialSize=5
diff --git a/escheduler-dao/src/test/java/cn/escheduler/dao/mapper/UserMapperTest.java b/escheduler-dao/src/test/java/cn/escheduler/dao/mapper/UserMapperTest.java
index adede0c..d85a25f 100644
--- a/escheduler-dao/src/test/java/cn/escheduler/dao/mapper/UserMapperTest.java
+++ b/escheduler-dao/src/test/java/cn/escheduler/dao/mapper/UserMapperTest.java
@@ -72,4 +72,10 @@
         Assert.assertEquals(user.getUserName(), "qiaozhanwei");
     }
 
+    @Test
+    public void test(){
+        User user = userMapper.queryDetailsById(19);
+        System.out.println(user);
+    }
+
 }
diff --git a/escheduler-rpc/pom.xml b/escheduler-rpc/pom.xml
index 4ec4ea2..e3d7c91 100644
--- a/escheduler-rpc/pom.xml
+++ b/escheduler-rpc/pom.xml
@@ -4,7 +4,7 @@
     <parent>
         <artifactId>escheduler</artifactId>
         <groupId>cn.analysys</groupId>
-        <version>1.0.4-SNAPSHOT</version>
+        <version>1.1.0-SNAPSHOT</version>
     </parent>
     <modelVersion>4.0.0</modelVersion>
 
diff --git a/escheduler-server/pom.xml b/escheduler-server/pom.xml
index cd33d49..7f14927 100644
--- a/escheduler-server/pom.xml
+++ b/escheduler-server/pom.xml
@@ -3,7 +3,7 @@
 	<parent>
 		<artifactId>escheduler</artifactId>
 		<groupId>cn.analysys</groupId>
-		<version>1.0.4-SNAPSHOT</version>
+		<version>1.1.0-SNAPSHOT</version>
 	</parent>
 	<artifactId>escheduler-server</artifactId>
 	<name>escheduler-server</name>
@@ -50,10 +50,7 @@
 				</exclusion>
 			</exclusions>
 		</dependency>
-		<dependency>
-			<groupId>cn.analysys</groupId>
-			<artifactId>escheduler-api</artifactId>
-		</dependency>
+
 		<dependency>
 			<groupId>cn.analysys</groupId>
 			<artifactId>escheduler-rpc</artifactId>
@@ -92,7 +89,7 @@
 			<artifactId>escheduler-alert</artifactId>
 		</dependency>
 
-	</dependencies>
+    </dependencies>
 
 
 	<build>
diff --git a/escheduler-server/src/main/java/cn/escheduler/server/ResInfo.java b/escheduler-server/src/main/java/cn/escheduler/server/ResInfo.java
index 81f1269..844c7be 100644
--- a/escheduler-server/src/main/java/cn/escheduler/server/ResInfo.java
+++ b/escheduler-server/src/main/java/cn/escheduler/server/ResInfo.java
@@ -17,8 +17,12 @@
 package cn.escheduler.server;
 
 import cn.escheduler.common.Constants;
+import cn.escheduler.common.utils.DateUtils;
 import cn.escheduler.common.utils.JSONUtils;
 import cn.escheduler.common.utils.OSUtils;
+import cn.escheduler.dao.model.MasterServer;
+
+import java.util.Date;
 
 /**
  *  heartbeat for ZK reigster res info
@@ -98,6 +102,16 @@
     }
 
 
+    public static String getHeartBeatInfo(Date now){
+        return buildHeartbeatForZKInfo(OSUtils.getHost(),
+                OSUtils.getProcessID(),
+                OSUtils.cpuUsage(),
+                OSUtils.memoryUsage(),
+                DateUtils.dateToString(now),
+                DateUtils.dateToString(now));
+
+    }
+
     /**
      * build heartbeat info for zk
      * @param host
@@ -119,4 +133,25 @@
                 + lastHeartbeatTime;
     }
 
+    /**
+     * parse heartbeat info for zk
+     * @param heartBeatInfo
+     * @return
+     */
+    public static MasterServer parseHeartbeatForZKInfo(String heartBeatInfo){
+        MasterServer masterServer =  null;
+        String[] masterArray = heartBeatInfo.split(Constants.COMMA);
+        if(masterArray.length != 6){
+            return masterServer;
+
+        }
+        masterServer = new MasterServer();
+        masterServer.setHost(masterArray[0]);
+        masterServer.setPort(Integer.parseInt(masterArray[1]));
+        masterServer.setResInfo(getResInfoJson(Double.parseDouble(masterArray[2]), Double.parseDouble(masterArray[3])));
+        masterServer.setCreateTime(DateUtils.stringToDate(masterArray[4]));
+        masterServer.setLastHeartbeatTime(DateUtils.stringToDate(masterArray[5]));
+        return masterServer;
+    }
+
 }
diff --git a/escheduler-server/src/main/java/cn/escheduler/server/master/MasterServer.java b/escheduler-server/src/main/java/cn/escheduler/server/master/MasterServer.java
index 95705dd..bf0dcbf 100644
--- a/escheduler-server/src/main/java/cn/escheduler/server/master/MasterServer.java
+++ b/escheduler-server/src/main/java/cn/escheduler/server/master/MasterServer.java
@@ -16,8 +16,8 @@
  */
 package cn.escheduler.server.master;
 
-import cn.escheduler.api.quartz.ProcessScheduleJob;
-import cn.escheduler.api.quartz.QuartzExecutors;
+import cn.escheduler.server.quartz.ProcessScheduleJob;
+import cn.escheduler.server.quartz.QuartzExecutors;
 import cn.escheduler.common.Constants;
 import cn.escheduler.common.IStoppable;
 import cn.escheduler.common.thread.Stopper;
@@ -189,7 +189,6 @@
 
     public static void main(String[] args) {
         SpringApplication app = new SpringApplication(MasterServer.class);
-        app.setWebEnvironment(false);
         app.run(args);
     }
 
@@ -217,7 +216,7 @@
                 if(Stopper.isRunning()) {
                     // send heartbeat to zk
                     if (StringUtils.isBlank(zkMasterClient.getMasterZNode())) {
-                        logger.error("master send heartbeat to zk failed");
+                        logger.error("master send heartbeat to zk failed: can't find zookeeper regist path of master server");
                         return;
                     }
 
diff --git a/escheduler-server/src/main/java/cn/escheduler/server/master/runner/MasterExecThread.java b/escheduler-server/src/main/java/cn/escheduler/server/master/runner/MasterExecThread.java
index f12726c..9ce08d6 100644
--- a/escheduler-server/src/main/java/cn/escheduler/server/master/runner/MasterExecThread.java
+++ b/escheduler-server/src/main/java/cn/escheduler/server/master/runner/MasterExecThread.java
@@ -79,6 +79,7 @@
     private Map<String, TaskInstance> completeTaskList = new ConcurrentHashMap<>();
     private Map<String, TaskInstance> readyToSubmitTaskList = new ConcurrentHashMap<>();
     private Map<String, TaskInstance> dependFailedTask = new ConcurrentHashMap<>();
+    private Map<String, TaskNode> forbiddenTaskList = new ConcurrentHashMap<>();
     private List<TaskInstance> recoverToleranceFaultTaskList = new ArrayList<>();
 
     private AlertManager alertManager = new AlertManager();
@@ -269,6 +270,7 @@
     private void buildFlowDag() throws Exception {
         recoverNodeIdList = getStartTaskInstanceList(processInstance.getCommandParam());
 
+        forbiddenTaskList = DagHelper.getForbiddenTaskNodeMaps(processInstance.getProcessInstanceJson());
         // generate process to get DAG info
         List<String> recoveryNameList = getRecoveryNodeNameList();
         List<String> startNodeNameList = parseStartNodeName(processInstance.getCommandParam());
@@ -279,7 +281,8 @@
             return;
         }
         // generate process dag
-        dag = buildDagGraph(processDag);
+        dag = DagHelper.buildDagGraph(processDag);
+
     }
 
     private void initTaskQueue(){
@@ -411,6 +414,8 @@
         return taskInstance;
     }
 
+
+
     /**
      *  get post task instance by node
      *
@@ -421,14 +426,12 @@
     private List<TaskInstance> getPostTaskInstanceByNode(DAG<String, TaskNode, TaskNodeRelation> dag, String parentNodeName){
 
         List<TaskInstance> postTaskList = new ArrayList<>();
-        Collection<String> startVertex = null;
-        if(StringUtils.isNotEmpty(parentNodeName)){
-            startVertex = dag.getSubsequentNodes(parentNodeName);
-        }else{
-            startVertex = dag.getBeginNode();
+        Collection<String> startVertex = DagHelper.getStartVertex(parentNodeName, dag, completeTaskList);
+        if(startVertex == null){
+            return postTaskList;
         }
-        for (String nodeName : startVertex){
 
+        for (String nodeName : startVertex){
             // encapsulation task instance
             TaskInstance taskInstance = createTaskInstance(processInstance, nodeName ,
                     dag.getNode(nodeName),parentNodeName);
@@ -517,7 +520,10 @@
         List<String> depsNameList = taskNode.getDepList();
         for(String depsNode : depsNameList ){
 
-            // dependencies must be all complete
+            if(forbiddenTaskList.containsKey(depsNode)){
+                continue;
+            }
+            // dependencies must be fully completed
             if(!completeTaskList.containsKey(depsNode)){
                 return DependResult.WAITING;
             }
@@ -904,35 +910,6 @@
         }
     }
 
-    /***
-     * generate dag graph
-     * @param processDag
-     * @return
-     */
-    public DAG<String, TaskNode, TaskNodeRelation> buildDagGraph(ProcessDag processDag) {
-
-        DAG<String,TaskNode,TaskNodeRelation> dag = new DAG<>();
-
-        /**
-         * add vertex
-         */
-        if (CollectionUtils.isNotEmpty(processDag.getNodes())){
-            for (TaskNode node : processDag.getNodes()){
-                dag.addNode(node.getName(),node);
-            }
-        }
-
-        /**
-         * add edge
-         */
-        if (CollectionUtils.isNotEmpty(processDag.getEdges())){
-            for (TaskNodeRelation edge : processDag.getEdges()){
-                dag.addEdge(edge.getStartNode(),edge.getEndNode());
-            }
-        }
-        return dag;
-    }
-
     /**
      *  whether the retry interval is timed out
      * @param taskInstance
diff --git a/escheduler-api/src/main/java/cn/escheduler/api/quartz/ProcessScheduleJob.java b/escheduler-server/src/main/java/cn/escheduler/server/quartz/ProcessScheduleJob.java
similarity index 96%
rename from escheduler-api/src/main/java/cn/escheduler/api/quartz/ProcessScheduleJob.java
rename to escheduler-server/src/main/java/cn/escheduler/server/quartz/ProcessScheduleJob.java
index 96e283d..3e546be 100644
--- a/escheduler-api/src/main/java/cn/escheduler/api/quartz/ProcessScheduleJob.java
+++ b/escheduler-server/src/main/java/cn/escheduler/server/quartz/ProcessScheduleJob.java
@@ -14,7 +14,7 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-package cn.escheduler.api.quartz;
+package cn.escheduler.server.quartz;
 
 
 import cn.escheduler.common.Constants;
@@ -31,8 +31,8 @@
 
 import java.util.Date;
 
-import static cn.escheduler.api.quartz.QuartzExecutors.buildJobGroupName;
-import static cn.escheduler.api.quartz.QuartzExecutors.buildJobName;
+import static cn.escheduler.server.quartz.QuartzExecutors.buildJobGroupName;
+import static cn.escheduler.server.quartz.QuartzExecutors.buildJobName;
 
 /**
  * process schedule job
diff --git a/escheduler-api/src/main/java/cn/escheduler/api/quartz/QuartzExecutors.java b/escheduler-server/src/main/java/cn/escheduler/server/quartz/QuartzExecutors.java
similarity index 99%
rename from escheduler-api/src/main/java/cn/escheduler/api/quartz/QuartzExecutors.java
rename to escheduler-server/src/main/java/cn/escheduler/server/quartz/QuartzExecutors.java
index 916e4f3..03d71cf 100644
--- a/escheduler-api/src/main/java/cn/escheduler/api/quartz/QuartzExecutors.java
+++ b/escheduler-server/src/main/java/cn/escheduler/server/quartz/QuartzExecutors.java
@@ -14,7 +14,7 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-package cn.escheduler.api.quartz;
+package cn.escheduler.server.quartz;
 
 import cn.escheduler.common.Constants;
 import cn.escheduler.common.utils.JSONUtils;
diff --git a/escheduler-server/src/main/java/cn/escheduler/server/utils/ProcessUtils.java b/escheduler-server/src/main/java/cn/escheduler/server/utils/ProcessUtils.java
index 0c3aec6..2a76438 100644
--- a/escheduler-server/src/main/java/cn/escheduler/server/utils/ProcessUtils.java
+++ b/escheduler-server/src/main/java/cn/escheduler/server/utils/ProcessUtils.java
@@ -294,9 +294,8 @@
   /**
    * find logs and kill yarn tasks
    * @param taskInstance
-   * @throws IOException
    */
-  public static void killYarnJob(TaskInstance taskInstance) throws Exception {
+  public static void killYarnJob(TaskInstance taskInstance) {
     try {
       Thread.sleep(Constants.SLEEP_TIME_MILLIS);
       LogClient logClient = new LogClient(taskInstance.getHost(), Constants.RPC_PORT);
@@ -316,7 +315,7 @@
 
     } catch (Exception e) {
       logger.error("kill yarn job failed : " + e.getMessage(),e);
-      throw new RuntimeException("kill yarn job fail");
+//      throw new RuntimeException("kill yarn job fail");
     }
   }
 }
diff --git a/escheduler-server/src/main/java/cn/escheduler/server/worker/runner/FetchTaskThread.java b/escheduler-server/src/main/java/cn/escheduler/server/worker/runner/FetchTaskThread.java
index f163364..2d88fdb 100644
--- a/escheduler-server/src/main/java/cn/escheduler/server/worker/runner/FetchTaskThread.java
+++ b/escheduler-server/src/main/java/cn/escheduler/server/worker/runner/FetchTaskThread.java
@@ -23,13 +23,11 @@
 import cn.escheduler.common.utils.FileUtils;
 import cn.escheduler.common.utils.OSUtils;
 import cn.escheduler.dao.ProcessDao;
-import cn.escheduler.dao.model.ProcessDefinition;
-import cn.escheduler.dao.model.ProcessInstance;
-import cn.escheduler.dao.model.TaskInstance;
-import cn.escheduler.dao.model.WorkerGroup;
+import cn.escheduler.dao.model.*;
 import cn.escheduler.server.zk.ZKWorkerClient;
-import com.cronutils.utils.StringUtils;
 import org.apache.commons.configuration.Configuration;
+import org.apache.commons.lang3.StringUtils;
+import org.apache.curator.framework.CuratorFramework;
 import org.apache.curator.framework.recipes.locks.InterProcessMutex;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
@@ -101,15 +99,7 @@
      */
     private boolean checkWorkerGroup(TaskInstance taskInstance, String host){
 
-        int taskWorkerGroupId = taskInstance.getWorkerGroupId();
-        ProcessInstance processInstance = processDao.findProcessInstanceByTaskId(taskInstance.getId());
-        if(processInstance == null){
-            logger.error("cannot find the task:{} process instance", taskInstance.getId());
-            return false;
-        }
-        int processWorkerGroupId = processInstance.getWorkerGroupId();
-
-        taskWorkerGroupId = (taskWorkerGroupId <= 0 ? processWorkerGroupId : taskWorkerGroupId);
+        int taskWorkerGroupId = processDao.getTaskWorkerGroupId(taskInstance);
 
         if(taskWorkerGroupId <= 0){
             return true;
@@ -120,117 +110,127 @@
             return true;
         }
         String ips = workerGroup.getIpList();
-        if(ips == null){
+        if(StringUtils.isBlank(ips)){
             logger.error("task:{} worker group:{} parameters(ip_list) is null, this task would be running on all workers",
                     taskInstance.getId(), workerGroup.getId());
         }
-        String[] ipArray = ips.split(",");
+        String[] ipArray = ips.split(Constants.COMMA);
         List<String> ipList =  Arrays.asList(ipArray);
         return ipList.contains(host);
     }
 
 
+
+
     @Override
     public void run() {
 
         while (Stopper.isRunning()){
-
             InterProcessMutex mutex = null;
             try {
-                if(OSUtils.checkResource(this.conf, false)) {
 
-                    // creating distributed locks, lock path /escheduler/lock/worker
-                    String zNodeLockPath = zkWorkerClient.getWorkerLockPath();
-                    mutex = new InterProcessMutex(zkWorkerClient.getZkClient(), zNodeLockPath);
-                    mutex.acquire();
+                ThreadPoolExecutor poolExecutor = (ThreadPoolExecutor) workerExecService;
 
-                    ThreadPoolExecutor poolExecutor = (ThreadPoolExecutor) workerExecService;
+                //check memory and cpu usage and threads
+                if(OSUtils.checkResource(this.conf, false) && checkThreadCount(poolExecutor)) {
 
-                    for (int i = 0; i < taskNum; i++) {
-
-                        int activeCount = poolExecutor.getActiveCount();
-                        if (activeCount >= workerExecNums) {
-                            logger.info("thread insufficient , activeCount : {} , workerExecNums : {}",activeCount,workerExecNums);
-                            continue;
-                        }
+                    //whether have tasks, if no tasks , no need lock  //get all tasks
+                    List<String> tasksQueueList = taskQueue.getAllTasks(Constants.SCHEDULER_TASKS_QUEUE);
+                    if(tasksQueueList.size() > 0){
+                        // creating distributed locks, lock path /escheduler/lock/worker
+                        String zNodeLockPath = zkWorkerClient.getWorkerLockPath();
+                        mutex = new InterProcessMutex(zkWorkerClient.getZkClient(), zNodeLockPath);
+                        mutex.acquire();
 
                         // task instance id str
-                        String taskQueueStr = taskQueue.poll(Constants.SCHEDULER_TASKS_QUEUE, false);
+                        List<String> taskQueueStrArr = taskQueue.poll(Constants.SCHEDULER_TASKS_QUEUE, taskNum);
 
-                        if (!StringUtils.isEmpty(taskQueueStr )) {
+                        for(String taskQueueStr : taskQueueStrArr){
+                            if (StringUtils.isNotBlank(taskQueueStr )) {
 
-                            String[] taskStringArray = taskQueueStr.split(Constants.UNDERLINE);
-                            String taskInstIdStr = taskStringArray[taskStringArray.length - 1];
-                            Date now = new Date();
-                            Integer taskId = Integer.parseInt(taskInstIdStr);
+                                if (!checkThreadCount(poolExecutor)) {
+                                    break;
+                                }
 
-                            // find task instance by task id
-                            TaskInstance taskInstance = processDao.findTaskInstanceById(taskId);
+                                String[] taskStringArray = taskQueueStr.split(Constants.UNDERLINE);
+                                String taskInstIdStr = taskStringArray[3];
+                                Date now = new Date();
+                                Integer taskId = Integer.parseInt(taskInstIdStr);
 
-                            logger.info("worker fetch taskId : {} from queue ", taskId);
+                                // find task instance by task id
+                                TaskInstance taskInstance = processDao.findTaskInstanceById(taskId);
 
-                            int retryTimes = 30;
-                            // mainly to wait for the master insert task to succeed
-                            while (taskInstance == null && retryTimes > 0) {
-                                Thread.sleep(Constants.SLEEP_TIME_MILLIS);
-                                taskInstance = processDao.findTaskInstanceById(taskId);
-                                retryTimes--;
-                            }
+                                logger.info("worker fetch taskId : {} from queue ", taskId);
 
-                            if (taskInstance == null ) {
-                                logger.error("task instance is null. task id : {} ", taskId);
-                                continue;
-                            }
-                            if(!checkWorkerGroup(taskInstance, OSUtils.getHost())){
-                                continue;
-                            }
-                            taskQueue.removeNode(Constants.SCHEDULER_TASKS_QUEUE, taskQueueStr);
-                            logger.info("remove task:{} from queue", taskQueueStr);
+                                int retryTimes = 30;
+                                // mainly to wait for the master insert task to succeed
+                                while (taskInstance == null && retryTimes > 0) {
+                                    Thread.sleep(Constants.SLEEP_TIME_MILLIS);
+                                    taskInstance = processDao.findTaskInstanceById(taskId);
+                                    retryTimes--;
+                                }
 
-                            // set execute task worker host
-                            taskInstance.setHost(OSUtils.getHost());
-                            taskInstance.setStartTime(now);
+                                if (taskInstance == null ) {
+                                    logger.error("task instance is null. task id : {} ", taskId);
+                                    continue;
+                                }
+
+                                if(!checkWorkerGroup(taskInstance, OSUtils.getHost())){
+                                    continue;
+                                }
+                                taskQueue.removeNode(Constants.SCHEDULER_TASKS_QUEUE, taskQueueStr);
+                                logger.info("remove task:{} from queue", taskQueueStr);
+
+                                // set execute task worker host
+                                taskInstance.setHost(OSUtils.getHost());
+                                taskInstance.setStartTime(now);
 
 
-                            // get process instance
-                            ProcessInstance processInstance = processDao.findProcessInstanceDetailById(taskInstance.getProcessInstanceId());
+                                // get process instance
+                                ProcessInstance processInstance = processDao.findProcessInstanceDetailById(taskInstance.getProcessInstanceId());
 
-                            // get process define
-                            ProcessDefinition processDefine = processDao.findProcessDefineById(taskInstance.getProcessDefinitionId());
+                                // get process define
+                                ProcessDefinition processDefine = processDao.findProcessDefineById(taskInstance.getProcessDefinitionId());
 
 
-                            taskInstance.setProcessInstance(processInstance);
-                            taskInstance.setProcessDefine(processDefine);
+                                taskInstance.setProcessInstance(processInstance);
+                                taskInstance.setProcessDefine(processDefine);
 
 
-                            // get local execute path
-                            String execLocalPath = FileUtils.getProcessExecDir(processDefine.getProjectId(),
-                                    processDefine.getId(),
-                                    processInstance.getId(),
-                                    taskInstance.getId());
-                            logger.info("task instance  local execute path : {} ", execLocalPath);
+                                // get local execute path
+                                String execLocalPath = FileUtils.getProcessExecDir(processDefine.getProjectId(),
+                                        processDefine.getId(),
+                                        processInstance.getId(),
+                                        taskInstance.getId());
+                                logger.info("task instance  local execute path : {} ", execLocalPath);
 
 
-                            // set task execute path
-                            taskInstance.setExecutePath(execLocalPath);
+                                // set task execute path
+                                taskInstance.setExecutePath(execLocalPath);
+
+                            Tenant tenant = processDao.getTenantForProcess(processInstance.getTenantId(),
+                                    processDefine.getUserId());
 
                             // check and create Linux users
                             FileUtils.createWorkDirAndUserIfAbsent(execLocalPath,
-                                    processInstance.getTenantCode(), logger);
+                                    tenant.getTenantCode(), logger);
 
-                            logger.info("task : {} ready to submit to task scheduler thread",taskId);
-                            // submit task
-                            workerExecService.submit(new TaskScheduleThread(taskInstance, processDao));
+                                logger.info("task : {} ready to submit to task scheduler thread",taskId);
+                                // submit task
+                                workerExecService.submit(new TaskScheduleThread(taskInstance, processDao));
+
+                            }
                         }
+
                     }
+
                 }
 
                 Thread.sleep(Constants.SLEEP_TIME_MILLIS);
 
             }catch (Exception e){
                 logger.error("fetch task thread exception : " + e.getMessage(),e);
-            }
-            finally {
+            }finally {
                 if (mutex != null){
                     try {
                         mutex.release();
@@ -245,4 +245,18 @@
             }
         }
     }
+
+    /**
+     *
+     * @param poolExecutor
+     * @return
+     */
+    private boolean checkThreadCount(ThreadPoolExecutor poolExecutor) {
+        int activeCount = poolExecutor.getActiveCount();
+        if (activeCount >= workerExecNums) {
+            logger.info("thread insufficient , activeCount : {} , workerExecNums : {}, will sleep : {} millis for thread resource", activeCount, workerExecNums, Constants.SLEEP_TIME_MILLIS);
+            return false;
+        }
+        return true;
+    }
 }
\ No newline at end of file
diff --git a/escheduler-server/src/main/java/cn/escheduler/server/worker/runner/TaskScheduleThread.java b/escheduler-server/src/main/java/cn/escheduler/server/worker/runner/TaskScheduleThread.java
index c21a3a2..89226bf 100644
--- a/escheduler-server/src/main/java/cn/escheduler/server/worker/runner/TaskScheduleThread.java
+++ b/escheduler-server/src/main/java/cn/escheduler/server/worker/runner/TaskScheduleThread.java
@@ -34,8 +34,10 @@
 import cn.escheduler.common.utils.*;
 import cn.escheduler.dao.ProcessDao;
 import cn.escheduler.dao.TaskRecordDao;
+import cn.escheduler.dao.model.ProcessDefinition;
 import cn.escheduler.dao.model.ProcessInstance;
 import cn.escheduler.dao.model.TaskInstance;
+import cn.escheduler.dao.model.Tenant;
 import cn.escheduler.server.utils.LoggerUtils;
 import cn.escheduler.server.utils.ParamUtils;
 import cn.escheduler.server.worker.log.TaskLogger;
@@ -160,82 +162,94 @@
             // set task params
             taskProps.setTaskParams(taskNode.getParams());
             // set tenant code , execute task linux user
-            taskProps.setTenantCode(taskInstance.getProcessInstance().getTenantCode());
 
             ProcessInstance processInstance = processDao.findProcessInstanceByTaskId(taskInstance.getId());
-            String queue = processDao.queryQueueByProcessInstanceId(processInstance.getId());
 
             taskProps.setScheduleTime(processInstance.getScheduleTime());
             taskProps.setNodeName(taskInstance.getName());
             taskProps.setTaskInstId(taskInstance.getId());
             taskProps.setEnvFile(CommonUtils.getSystemEnvPath());
-            // set queue
-            if (StringUtils.isEmpty(queue)){
-                taskProps.setQueue(taskInstance.getProcessInstance().getQueue());
-            }else {
-                taskProps.setQueue(queue);
-            }
-            taskProps.setTaskStartTime(taskInstance.getStartTime());
-            taskProps.setDefinedParams(allParamMap);
 
-            // set task timeout
-            setTaskTimeout(taskProps, taskNode);
+            ProcessDefinition processDefine = processDao.findProcessDefineById(processInstance.getProcessDefinitionId());
 
-            taskProps.setDependence(taskInstance.getDependency());
+            Tenant tenant = processDao.getTenantForProcess(processInstance.getTenantId(),
+                    processDefine.getUserId());
 
-            taskProps.setTaskAppId(String.format("%s_%s_%s",
-                    taskInstance.getProcessDefine().getId(),
-                    taskInstance.getProcessInstance().getId(),
-                    taskInstance.getId()));
+            if(tenant == null){
+                processInstance.setTenantCode(tenant.getTenantCode());
+                logger.error("cannot find the tenant, process definition id:{}, tenant id:{}, user id:{}",
+                        processDefine.getId(), processDefine.getTenantId(), processDefine.getUserId()
+                );
+                status = ExecutionStatus.FAILURE;
+            }else{
+                taskProps.setTenantCode(tenant.getTenantCode());
+                String queue = processDao.queryQueueByProcessInstanceId(processInstance.getId());
+                // set queue
+                if (StringUtils.isEmpty(queue)){
+                    taskProps.setQueue(taskInstance.getProcessInstance().getQueue());
+                }else {
+                    taskProps.setQueue(tenant.getQueueName());
+                }
+                taskProps.setTaskStartTime(taskInstance.getStartTime());
+                taskProps.setDefinedParams(allParamMap);
 
-            // custom logger
-            TaskLogger taskLogger = new TaskLogger(LoggerUtils.buildTaskId(TASK_PREFIX,
-                    taskInstance.getProcessDefine().getId(),
-                    taskInstance.getProcessInstance().getId(),
-                    taskInstance.getId()));
+                // set task timeout
+                setTaskTimeout(taskProps, taskNode);
 
-            task = TaskManager.newTask(taskInstance.getTaskType(), taskProps, taskLogger);
+                taskProps.setDependence(taskInstance.getDependency());
 
-            // job init
-            task.init();
+                taskProps.setTaskAppId(String.format("%s_%s_%s",
+                        taskInstance.getProcessDefine().getId(),
+                        taskInstance.getProcessInstance().getId(),
+                        taskInstance.getId()));
 
-            // job handle
-            task.handle();
+                // custom logger
+                TaskLogger taskLogger = new TaskLogger(LoggerUtils.buildTaskId(TASK_PREFIX,
+                        taskInstance.getProcessDefine().getId(),
+                        taskInstance.getProcessInstance().getId(),
+                        taskInstance.getId()));
 
+                task = TaskManager.newTask(taskInstance.getTaskType(), taskProps, taskLogger);
 
-            logger.info("task : {} exit status code : {}", taskProps.getTaskAppId(),task.getExitStatusCode());
+                // job init
+                task.init();
 
-            if (task.getExitStatusCode() == Constants.EXIT_CODE_SUCCESS){
-                status = ExecutionStatus.SUCCESS;
-                // task recor flat : if true , start up qianfan
-                if (TaskRecordDao.getTaskRecordFlag()
-                        && TaskType.typeIsNormalTask(taskInstance.getTaskType())){
+                // job handle
+                task.handle();
+                logger.info("task : {} exit status code : {}", taskProps.getTaskAppId(),task.getExitStatusCode());
 
-                    AbstractParameters params = (AbstractParameters) JSONUtils.parseObject(taskProps.getTaskParams(), getCurTaskParamsClass());
+                if (task.getExitStatusCode() == Constants.EXIT_CODE_SUCCESS){
+                    status = ExecutionStatus.SUCCESS;
+                    // task recor flat : if true , start up qianfan
+                    if (TaskRecordDao.getTaskRecordFlag()
+                            && TaskType.typeIsNormalTask(taskInstance.getTaskType())){
 
-                    // replace placeholder
-                    Map<String, Property> paramsMap = ParamUtils.convert(taskProps.getUserDefParamsMap(),
-                            taskProps.getDefinedParams(),
-                            params.getLocalParametersMap(),
-                            processInstance.getCmdTypeIfComplement(),
-                            processInstance.getScheduleTime());
-                    if (paramsMap != null && !paramsMap.isEmpty()
-                            && paramsMap.containsKey("v_proc_date")){
-                        String vProcDate = paramsMap.get("v_proc_date").getValue();
-                        if (!StringUtils.isEmpty(vProcDate)){
-                            TaskRecordStatus taskRecordState = TaskRecordDao.getTaskRecordState(taskInstance.getName(), vProcDate);
-                            logger.info("task record status : {}",taskRecordState);
-                            if (taskRecordState == TaskRecordStatus.FAILURE){
-                                status = ExecutionStatus.FAILURE;
+                        AbstractParameters params = (AbstractParameters) JSONUtils.parseObject(taskProps.getTaskParams(), getCurTaskParamsClass());
+
+                        // replace placeholder
+                        Map<String, Property> paramsMap = ParamUtils.convert(taskProps.getUserDefParamsMap(),
+                                taskProps.getDefinedParams(),
+                                params.getLocalParametersMap(),
+                                processInstance.getCmdTypeIfComplement(),
+                                processInstance.getScheduleTime());
+                        if (paramsMap != null && !paramsMap.isEmpty()
+                                && paramsMap.containsKey("v_proc_date")){
+                            String vProcDate = paramsMap.get("v_proc_date").getValue();
+                            if (!StringUtils.isEmpty(vProcDate)){
+                                TaskRecordStatus taskRecordState = TaskRecordDao.getTaskRecordState(taskInstance.getName(), vProcDate);
+                                logger.info("task record status : {}",taskRecordState);
+                                if (taskRecordState == TaskRecordStatus.FAILURE){
+                                    status = ExecutionStatus.FAILURE;
+                                }
                             }
                         }
                     }
-                }
 
-            }else if (task.getExitStatusCode() == Constants.EXIT_CODE_KILL){
-                status = ExecutionStatus.KILL;
-            }else {
-                status = ExecutionStatus.FAILURE;
+                }else if (task.getExitStatusCode() == Constants.EXIT_CODE_KILL){
+                    status = ExecutionStatus.KILL;
+                }else {
+                    status = ExecutionStatus.FAILURE;
+                }
             }
         }catch (Exception e){
             logger.error("task escheduler failure : " + e.getMessage(),e);
diff --git a/escheduler-server/src/main/java/cn/escheduler/server/worker/task/PythonCommandExecutor.java b/escheduler-server/src/main/java/cn/escheduler/server/worker/task/PythonCommandExecutor.java
index 94caffe..e1df0b7 100644
--- a/escheduler-server/src/main/java/cn/escheduler/server/worker/task/PythonCommandExecutor.java
+++ b/escheduler-server/src/main/java/cn/escheduler/server/worker/task/PythonCommandExecutor.java
@@ -18,6 +18,7 @@
 
 import cn.escheduler.common.Constants;
 import cn.escheduler.common.utils.FileUtils;
+import cn.escheduler.common.utils.PropertyUtils;
 import org.apache.commons.lang3.StringUtils;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
@@ -71,11 +72,11 @@
         if (!Files.exists(Paths.get(commandFile))) {
             logger.info("generate command file:{}", commandFile);
 
-            StringBuilder sb = new StringBuilder(200);
+            StringBuilder sb = new StringBuilder();
             sb.append("#-*- encoding=utf8 -*-\n");
 
             sb.append("\n\n");
-            sb.append(String.format("import py_%s_node\n",taskAppId));
+            sb.append(execCommand);
             logger.info(sb.toString());
 
             // write data to file
@@ -86,8 +87,8 @@
     @Override
     protected String commandType() {
 
-        String envPath = System.getProperty("user.dir") + Constants.SINGLE_SLASH + "conf"+
-                Constants.SINGLE_SLASH +"env" + Constants.SINGLE_SLASH + Constants.ESCHEDULER_ENV_SH;
+        String envPath = PropertyUtils.getString(Constants.ESCHEDULER_ENV_PATH);
+
         String pythonHome = getPythonHome(envPath);
         if (StringUtils.isEmpty(pythonHome)){
             return PYTHON;
diff --git a/escheduler-server/src/main/java/cn/escheduler/server/worker/task/python/PythonTask.java b/escheduler-server/src/main/java/cn/escheduler/server/worker/task/python/PythonTask.java
index 49d7544..c446215 100644
--- a/escheduler-server/src/main/java/cn/escheduler/server/worker/task/python/PythonTask.java
+++ b/escheduler-server/src/main/java/cn/escheduler/server/worker/task/python/PythonTask.java
@@ -112,14 +112,14 @@
    */
   private String buildCommand() throws Exception {
     // generate scripts
-    String fileName = String.format("%s/py_%s_node.py", taskDir, taskProps.getTaskAppId());
-    Path path = new File(fileName).toPath();
+//    String fileName = String.format("%s/py_%s_node.py", taskDir, taskProps.getTaskAppId());
+//    Path path = new File(fileName).toPath();
 
 
 
-    if (Files.exists(path)) {
-      return fileName;
-    }
+//    if (Files.exists(path)) {
+//      return fileName;
+//    }
 
     String rawScript = pythonParameters.getRawScript().replaceAll("\\r\\n", "\n");
 
@@ -140,19 +140,20 @@
     }
 
 
-    pythonParameters.setRawScript(rawScript);
+//    pythonParameters.setRawScript(rawScript);
 
     logger.info("raw script : {}", pythonParameters.getRawScript());
     logger.info("task dir : {}", taskDir);
 
-    Set<PosixFilePermission> perms = PosixFilePermissions.fromString("rwxr-xr-x");
-    FileAttribute<Set<PosixFilePermission>> attr = PosixFilePermissions.asFileAttribute(perms);
-
-    Files.createFile(path, attr);
-
-    Files.write(path, pythonParameters.getRawScript().getBytes(), StandardOpenOption.APPEND);
-
-    return fileName;
+//    Set<PosixFilePermission> perms = PosixFilePermissions.fromString("rwxr-xr-x");
+//    FileAttribute<Set<PosixFilePermission>> attr = PosixFilePermissions.asFileAttribute(perms);
+//
+//    Files.createFile(path, attr);
+//
+//    Files.write(path, pythonParameters.getRawScript().getBytes(), StandardOpenOption.APPEND);
+//
+//    return fileName;
+    return rawScript;
   }
 
   @Override
diff --git a/escheduler-server/src/main/java/cn/escheduler/server/worker/task/sql/SqlTask.java b/escheduler-server/src/main/java/cn/escheduler/server/worker/task/sql/SqlTask.java
index 4eb567d..09f6467 100644
--- a/escheduler-server/src/main/java/cn/escheduler/server/worker/task/sql/SqlTask.java
+++ b/escheduler-server/src/main/java/cn/escheduler/server/worker/task/sql/SqlTask.java
@@ -29,6 +29,7 @@
 import cn.escheduler.common.task.sql.SqlParameters;
 import cn.escheduler.common.task.sql.SqlType;
 import cn.escheduler.common.utils.CollectionUtils;
+import cn.escheduler.common.utils.CommonUtils;
 import cn.escheduler.common.utils.ParameterUtils;
 import cn.escheduler.dao.AlertDao;
 import cn.escheduler.dao.DaoFactory;
@@ -43,6 +44,8 @@
 import com.alibaba.fastjson.serializer.SerializerFeature;
 import org.apache.commons.lang.StringUtils;
 import org.apache.commons.lang3.EnumUtils;
+import org.apache.hadoop.conf.Configuration;
+import org.apache.hadoop.security.UserGroupInformation;
 import org.slf4j.Logger;
 
 import java.sql.*;
@@ -51,6 +54,8 @@
 import java.util.regex.Pattern;
 import java.util.stream.Collectors;
 
+import static cn.escheduler.common.utils.PropertyUtils.getString;
+
 /**
  *  sql task
  */
@@ -228,7 +233,15 @@
                                         List<String> createFuncs){
         Connection connection = null;
         try {
-
+            if (CommonUtils.getKerberosStartupState())  {
+                System.setProperty(cn.escheduler.common.Constants.JAVA_SECURITY_KRB5_CONF,
+                        getString(cn.escheduler.common.Constants.JAVA_SECURITY_KRB5_CONF_PATH));
+                Configuration configuration = new Configuration();
+                configuration.set(cn.escheduler.common.Constants.HADOOP_SECURITY_AUTHENTICATION, "kerberos");
+                UserGroupInformation.setConfiguration(configuration);
+                UserGroupInformation.loginUserFromKeytab(getString(cn.escheduler.common.Constants.LOGIN_USER_KEY_TAB_USERNAME),
+                        getString(cn.escheduler.common.Constants.LOGIN_USER_KEY_TAB_PATH));
+            }
             if (DbType.HIVE.name().equals(sqlParameters.getType())) {
                 Properties paramProp = new Properties();
                 paramProp.setProperty("user", baseDataSource.getUser());
@@ -278,7 +291,7 @@
                         array.add(mapOfColValues);
                     }
 
-                    logger.info("execute sql : {}", JSONObject.toJSONString(array, SerializerFeature.WriteMapNullValue));
+                    logger.debug("execute sql : {}", JSONObject.toJSONString(array, SerializerFeature.WriteMapNullValue));
 
                     // send as an attachment
                     if (StringUtils.isEmpty(sqlParameters.getShowType())) {
@@ -374,7 +387,7 @@
         String showTypeName = sqlParameters.getShowType().replace(Constants.COMMA,"").trim();
         if(EnumUtils.isValidEnum(ShowType.class,showTypeName)){
             Map<String, Object> mailResult = MailUtils.sendMails(receviersList, receviersCcList, title, content, ShowType.valueOf(showTypeName));
-            if(!(Boolean) mailResult.get(cn.escheduler.api.utils.Constants.STATUS)){
+            if(!(Boolean) mailResult.get(cn.escheduler.common.Constants.STATUS)){
                 throw new RuntimeException("send mail failed!");
             }
         }else{
diff --git a/escheduler-server/src/main/java/cn/escheduler/server/zk/ZKMasterClient.java b/escheduler-server/src/main/java/cn/escheduler/server/zk/ZKMasterClient.java
index 85f9f6a..3596155 100644
--- a/escheduler-server/src/main/java/cn/escheduler/server/zk/ZKMasterClient.java
+++ b/escheduler-server/src/main/java/cn/escheduler/server/zk/ZKMasterClient.java
@@ -18,6 +18,7 @@
 
 import cn.escheduler.common.Constants;
 import cn.escheduler.common.enums.ExecutionStatus;
+import cn.escheduler.common.enums.ZKNodeType;
 import cn.escheduler.common.utils.CollectionUtils;
 import cn.escheduler.common.utils.DateUtils;
 import cn.escheduler.common.utils.OSUtils;
@@ -28,10 +29,11 @@
 import cn.escheduler.dao.ServerDao;
 import cn.escheduler.dao.model.ProcessInstance;
 import cn.escheduler.dao.model.TaskInstance;
+import cn.escheduler.dao.model.WorkerServer;
 import cn.escheduler.server.ResInfo;
 import cn.escheduler.server.utils.ProcessUtils;
+import org.apache.commons.lang.StringUtils;
 import org.apache.curator.framework.CuratorFramework;
-import org.apache.curator.framework.imps.CuratorFrameworkState;
 import org.apache.curator.framework.recipes.cache.PathChildrenCache;
 import org.apache.curator.framework.recipes.cache.PathChildrenCacheEvent;
 import org.apache.curator.framework.recipes.cache.PathChildrenCacheListener;
@@ -134,7 +136,9 @@
 
 			// check if fault tolerance is required,failure and tolerance
 			if (getActiveMasterNum() == 1) {
-				processDao.masterStartupFaultTolerant();
+				failoverWorker(null, true);
+//				processDao.masterStartupFaultTolerant();
+				failoverMaster(null);
 			}
 
 		}catch (Exception e){
@@ -190,31 +194,20 @@
 		Date now = new Date();
 		createTime = now ;
 		try {
+			String osHost = OSUtils.getHost();
 
-			// encapsulation master znnode
-			masterZNode = masterZNodeParentPath + "/" + OSUtils.getHost() + "_";
-			List<String> masterZNodeList = zkClient.getChildren().forPath(masterZNodeParentPath);
-
-			if (CollectionUtils.isNotEmpty(masterZNodeList)){
-				boolean flag = false;
-				for (String masterZNode : masterZNodeList){
-					if (masterZNode.startsWith(OSUtils.getHost())){
-						flag = true;
-						break;
-					}
-				}
-
-				if (flag){
-					logger.error("register failure , master already started on host : {}" , OSUtils.getHost());
-					// exit system
-					System.exit(-1);
-				}
+			// zookeeper node exists, cannot start a new one.
+			if(checkZKNodeExists(osHost, ZKNodeType.MASTER)){
+				logger.error("register failure , master already started on host : {}" , osHost);
+				// exit system
+				System.exit(-1);
 			}
 
 			// specify the format of stored data in ZK nodes
-			String heartbeatZKInfo = getOsInfo(now);
+			String heartbeatZKInfo = ResInfo.getHeartBeatInfo(now);
 			// create temporary sequence nodes for master znode
-			masterZNode = zkClient.create().withMode(CreateMode.EPHEMERAL_SEQUENTIAL).forPath(masterZNode, heartbeatZKInfo.getBytes());
+			masterZNode = zkClient.create().withMode(CreateMode.EPHEMERAL_SEQUENTIAL).forPath(
+					masterZNodeParentPath + "/" + OSUtils.getHost() + "_", heartbeatZKInfo.getBytes());
 
 			logger.info("register master node {} success" , masterZNode);
 
@@ -239,6 +232,46 @@
 
 
 	/**
+	 * check the zookeeper node already exists
+	 * @param host
+	 * @param zkNodeType
+	 * @return
+	 * @throws Exception
+	 */
+	private boolean checkZKNodeExists(String host, ZKNodeType zkNodeType) throws Exception {
+
+		String path = null;
+		switch (zkNodeType){
+			case MASTER:
+				path = masterZNodeParentPath;
+				break;
+			case WORKER:
+				path = workerZNodeParentPath;
+				break;
+			case DEAD_SERVER:
+				path = deadServerZNodeParentPath;
+				break;
+			default:
+				break;
+		}
+		if(StringUtils.isEmpty(path)){
+			logger.error("check zk node exists error, host:{}, zk node type:{}", host, zkNodeType.toString());
+			return false;
+		}
+
+		List<String> serverList = null;
+        serverList = zkClient.getChildren().forPath(path);
+		if (CollectionUtils.isNotEmpty(serverList)){
+            for (String masterZNode : serverList){
+                if (masterZNode.startsWith(host)){
+                    return true;
+                }
+                }
+            }
+        return false;
+	}
+
+	/**
 	 *  monitor master
 	 */
 	public void listenerMaster(){
@@ -279,17 +312,9 @@
 								for (int i = 0; i < Constants.ESCHEDULER_WARN_TIMES_FAILOVER;i++) {
 									alertDao.sendServerStopedAlert(1, masterHost, "Master-Server");
 								}
-
-								logger.info("start master failover ...");
-
-								List<ProcessInstance> needFailoverProcessInstanceList = processDao.queryNeedFailoverProcessInstances(masterHost);
-
-								//updateProcessInstance host is null and insert into command
-								for(ProcessInstance processInstance : needFailoverProcessInstanceList){
-									processDao.processNeedFailoverProcessInstances(processInstance);
+								if(StringUtils.isNotEmpty(masterHost)){
+									failoverMaster(masterHost);
 								}
-
-								logger.info("master failover end");
 							}catch (Exception e){
 								logger.error("master failover failed : " + e.getMessage(),e);
 							}finally {
@@ -331,6 +356,8 @@
 
 	}
 
+
+
 	/**
 	 *  monitor worker
 	 */
@@ -369,23 +396,9 @@
 									alertDao.sendServerStopedAlert(1, workerHost, "Worker-Server");
 								}
 
-								logger.info("start worker failover ...");
-
-
-								List<TaskInstance> needFailoverTaskInstanceList = processDao.queryNeedFailoverTaskInstances(workerHost);
-								for(TaskInstance taskInstance : needFailoverTaskInstanceList){
-									ProcessInstance instance = processDao.findProcessInstanceDetailById(taskInstance.getProcessInstanceId());
-									if(instance!=null){
-										taskInstance.setProcessInstance(instance);
-									}
-									// only kill yarn job if exists , the local thread has exited
-									ProcessUtils.killYarnJob(taskInstance);
-								}
-
-								//updateProcessInstance state value is NEED_FAULT_TOLERANCE
-								processDao.updateNeedFailoverTaskInstances(workerHost);
-
-								logger.info("worker failover end");
+								if(StringUtils.isNotEmpty(workerHost)){
+									failoverWorker(workerHost, true);
+                                }
 							}catch (Exception e){
 								logger.error("worker failover failed : " + e.getMessage(),e);
 							}
@@ -410,22 +423,6 @@
 
 	}
 
-
-	/**
-	 * get os info
-	 * @param now
-	 * @return
-	 */
-	private String getOsInfo(Date now) {
-		return ResInfo.buildHeartbeatForZKInfo(OSUtils.getHost(),
-				OSUtils.getProcessID(),
-				OSUtils.cpuUsage(),
-				OSUtils.memoryUsage(),
-				DateUtils.dateToString(now),
-				DateUtils.dateToString(now));
-	}
-
-
 	/**
 	 *  get master znode
 	 * @return
@@ -435,50 +432,100 @@
 	}
 
 
+
+
 	/**
-	 *  get master lock path
+	 * task needs failover if task start before worker starts
+     *
+	 * @param taskInstance
 	 * @return
 	 */
-	public String getMasterLockPath(){
-		return conf.getString(Constants.ZOOKEEPER_ESCHEDULER_LOCK_MASTERS);
+	private boolean checkTaskInstanceNeedFailover(TaskInstance taskInstance) throws Exception {
+
+		boolean taskNeedFailover = true;
+
+		// if the worker node exists in zookeeper, we must check the task starts after the worker
+	    if(checkZKNodeExists(taskInstance.getHost(), ZKNodeType.WORKER)){
+	        //if task start after worker starts, there is no need to failover the task.
+         	if(checkTaskAfterWorkerStart(taskInstance)){
+         	    taskNeedFailover = false;
+			}
+		}
+		return taskNeedFailover;
 	}
 
 	/**
-	 *  get master start up lock path
+	 * check task start after the worker server starts.
+	 * @param taskInstance
 	 * @return
 	 */
-	public String getMasterStartUpLockPath(){
-		return conf.getString(Constants.ZOOKEEPER_ESCHEDULER_LOCK_FAILOVER_STARTUP_MASTERS);
+	private boolean checkTaskAfterWorkerStart(TaskInstance taskInstance) {
+	    Date workerServerStartDate = null;
+		List<WorkerServer> workerServers = processDao.queryWorkerServerByHost(taskInstance.getHost());
+		if(workerServers.size() > 0){
+		    workerServerStartDate = workerServers.get(0).getCreateTime();
+		}
+
+		if(workerServerStartDate != null){
+			return taskInstance.getStartTime().after(workerServerStartDate);
+
+		}else{
+			return false;
+		}
 	}
 
 	/**
-	 *  get master failover lock path
-	 * @return
+	 * failover worker tasks
+	 * 1. kill yarn job if there are yarn jobs in tasks.
+	 * 2. change task state from running to need failover.
+	 * @param workerHost
 	 */
-	public String getMasterFailoverLockPath(){
-		return conf.getString(Constants.ZOOKEEPER_ESCHEDULER_LOCK_FAILOVER_MASTERS);
+	private void failoverWorker(String workerHost, boolean needCheckWorkerAlive) throws Exception {
+		logger.info("start worker[{}] failover ...", workerHost);
+
+		List<TaskInstance> needFailoverTaskInstanceList = processDao.queryNeedFailoverTaskInstances(workerHost);
+		for(TaskInstance taskInstance : needFailoverTaskInstanceList){
+			if(needCheckWorkerAlive){
+				if(!checkTaskInstanceNeedFailover(taskInstance)){
+					continue;
+                }
+			}
+
+			ProcessInstance instance = processDao.findProcessInstanceDetailById(taskInstance.getProcessInstanceId());
+			if(instance!=null){
+				taskInstance.setProcessInstance(instance);
+			}
+			// only kill yarn job if exists , the local thread has exited
+			ProcessUtils.killYarnJob(taskInstance);
+
+			taskInstance.setState(ExecutionStatus.NEED_FAULT_TOLERANCE);
+			processDao.saveTaskInstance(taskInstance);
+		}
+
+		//update task Instance state value is NEED_FAULT_TOLERANCE
+		// processDao.updateNeedFailoverTaskInstances(workerHost);
+		logger.info("end worker[{}] failover ...", workerHost);
 	}
 
 	/**
-	 * get worker failover lock path
-	 * @return
+	 * failover master tasks
+	 * @param masterHost
 	 */
-	public String getWorkerFailoverLockPath(){
-		return conf.getString(Constants.ZOOKEEPER_ESCHEDULER_LOCK_FAILOVER_WORKERS);
+	private void failoverMaster(String masterHost) {
+		logger.info("start master failover ...");
+
+		List<ProcessInstance> needFailoverProcessInstanceList = processDao.queryNeedFailoverProcessInstances(masterHost);
+
+		//updateProcessInstance host is null and insert into command
+		for(ProcessInstance processInstance : needFailoverProcessInstanceList){
+			processDao.processNeedFailoverProcessInstances(processInstance);
+		}
+
+		logger.info("master failover end");
 	}
 
 	/**
-	 *  get zkclient
-	 * @return
-	 */
-	public  CuratorFramework getZkClient() {
-		return zkClient;
-	}
-
-
-
-	/**
-	 *  get host ip
+	 *  get host ip, string format: masterParentPath/ip_000001/value
 	 * @param path
 	 * @return
 	 */
@@ -488,6 +535,7 @@
 
 		if(startIndex >= endIndex){
 			logger.error("parse ip error");
+			return "";
 		}
 		return path.substring(startIndex, endIndex);
 	}
diff --git a/escheduler-server/src/main/java/cn/escheduler/server/zk/ZKWorkerClient.java b/escheduler-server/src/main/java/cn/escheduler/server/zk/ZKWorkerClient.java
index 7cf899d..e00d72d 100644
--- a/escheduler-server/src/main/java/cn/escheduler/server/zk/ZKWorkerClient.java
+++ b/escheduler-server/src/main/java/cn/escheduler/server/zk/ZKWorkerClient.java
@@ -116,11 +116,10 @@
 
 	public String initWorkZNode() throws Exception {
 
-		Date now = new Date();
-		String heartbeatZKInfo = getOsInfo(now);
-
+		String heartbeatZKInfo = ResInfo.getHeartBeatInfo(new Date());
 
 		workerZNode = workerZNodeParentPath + "/" + OSUtils.getHost() + "_";
+
 		workerZNode = zkClient.create().withMode(CreateMode.EPHEMERAL_SEQUENTIAL).forPath(workerZNode,
 				heartbeatZKInfo.getBytes());
 		logger.info("register worker node {} success", workerZNode);
@@ -141,7 +140,6 @@
 			workerZNode = workerZNodeParentPath + "/" + OSUtils.getHost() + "_";
 			List<String> workerZNodeList = zkClient.getChildren().forPath(workerZNodeParentPath);
 
-
 			if (CollectionUtils.isNotEmpty(workerZNodeList)){
 				boolean flag = false;
 				for (String workerZNode : workerZNodeList){
@@ -242,21 +240,6 @@
 	}
 
 	/**
-	 * get os info
-	 * @param now
-	 * @return
-	 */
-	private String getOsInfo(Date now) {
-		return ResInfo.buildHeartbeatForZKInfo(OSUtils.getHost(),
-				OSUtils.getProcessID(),
-				OSUtils.cpuUsage(),
-				OSUtils.memoryUsage(),
-				DateUtils.dateToString(now),
-				DateUtils.dateToString(now));
-	}
-
-
-	/**
 	 * get worker znode
 	 * @return
 	 */
@@ -264,16 +247,6 @@
 		return workerZNode;
 	}
 
-
-	/**
-	 *  get zkclient
-	 * @return
-	 */
-	public  CuratorFramework getZkClient() {
-		return zkClient;
-	}
-
-
 	/**
 	 *  get worker lock path
 	 * @return
diff --git a/escheduler-server/src/test/java/cn/escheduler/server/master/MasterCommandTest.java b/escheduler-server/src/test/java/cn/escheduler/server/master/MasterCommandTest.java
index 3aed212..9c71795 100644
--- a/escheduler-server/src/test/java/cn/escheduler/server/master/MasterCommandTest.java
+++ b/escheduler-server/src/test/java/cn/escheduler/server/master/MasterCommandTest.java
@@ -18,15 +18,27 @@
 
 import cn.escheduler.common.enums.CommandType;
 import cn.escheduler.common.enums.FailureStrategy;
+import cn.escheduler.common.enums.TaskDependType;
 import cn.escheduler.common.enums.WarningType;
+import cn.escheduler.common.graph.DAG;
+import cn.escheduler.common.model.TaskNode;
+import cn.escheduler.common.model.TaskNodeRelation;
+import cn.escheduler.common.process.ProcessDag;
 import cn.escheduler.dao.datasource.ConnectionFactory;
 import cn.escheduler.dao.mapper.CommandMapper;
+import cn.escheduler.dao.mapper.ProcessDefinitionMapper;
 import cn.escheduler.dao.model.Command;
+import cn.escheduler.dao.model.ProcessDefinition;
+import cn.escheduler.dao.utils.DagHelper;
 import org.junit.Before;
 import org.junit.Test;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Map;
+
 /**
  *  master test
  */
@@ -36,9 +48,14 @@
 
     private CommandMapper commandMapper;
 
+    private ProcessDefinitionMapper processDefinitionMapper;
+
+
     @Before
     public void before(){
+
         commandMapper = ConnectionFactory.getSqlSession().getMapper(CommandMapper.class);
+        processDefinitionMapper = ConnectionFactory.getSqlSession().getMapper(ProcessDefinitionMapper.class);
     }
 
 
@@ -104,4 +121,29 @@
     }
 
 
+    @Test
+    public void testDagHelper(){
+
+        ProcessDefinition processDefinition = processDefinitionMapper.queryByDefineId(19);
+
+        try {
+            ProcessDag processDag = DagHelper.generateFlowDag(processDefinition.getProcessDefinitionJson(),
+                    new ArrayList<>(), new ArrayList<>(), TaskDependType.TASK_POST);
+
+            DAG<String,TaskNode,TaskNodeRelation> dag = DagHelper.buildDagGraph(processDag);
+            Collection<String> start = DagHelper.getStartVertex("1", dag, null);
+
+            System.out.println(start.toString());
+
+            Map<String, TaskNode> forbidden = DagHelper.getForbiddenTaskNodeMaps(processDefinition.getProcessDefinitionJson());
+            System.out.println(forbidden);
+        } catch (Exception e) {
+            e.printStackTrace();
+        }
+
+    }
+
+
+
+
 }
diff --git a/escheduler-server/src/test/java/cn/escheduler/server/zk/ZKWorkerClientTest.java b/escheduler-server/src/test/java/cn/escheduler/server/zk/ZKWorkerClientTest.java
index c8aa093..c8e4b93 100644
--- a/escheduler-server/src/test/java/cn/escheduler/server/zk/ZKWorkerClientTest.java
+++ b/escheduler-server/src/test/java/cn/escheduler/server/zk/ZKWorkerClientTest.java
@@ -1,6 +1,7 @@
 package cn.escheduler.server.zk;
 
 import cn.escheduler.common.Constants;
+import cn.escheduler.common.zk.AbstractZKClient;
 import org.junit.Test;
 
 import java.util.Arrays;
@@ -17,8 +18,8 @@
     public void getZKWorkerClient() throws Exception {
 
 
-        ZKWorkerClient zkWorkerClient = ZKWorkerClient.getZKWorkerClient();
-        zkWorkerClient.removeDeadServerByHost("127.0.0.1", Constants.WORKER_PREFIX);
+//        ZKWorkerClient zkWorkerClient = ZKWorkerClient.getZKWorkerClient();
+//        zkWorkerClient.removeDeadServerByHost("127.0.0.1", Constants.WORKER_PREFIX);
 
 
     }
diff --git a/escheduler-ui/src/js/conf/home/pages/dag/_source/config.js b/escheduler-ui/src/js/conf/home/pages/dag/_source/config.js
index 7408468..9c10658 100644
--- a/escheduler-ui/src/js/conf/home/pages/dag/_source/config.js
+++ b/escheduler-ui/src/js/conf/home/pages/dag/_source/config.js
@@ -26,7 +26,7 @@
  * @desc tooltip
  */
 const toolOper = (dagThis) => {
-  let disabled = Permissions.getAuth() === false ? false : !dagThis.$store.state.dag.isDetails
+  let disabled =!dagThis.$store.state.dag.isDetails// Permissions.getAuth() === false ? false : !dagThis.$store.state.dag.isDetails
   return [
     {
       code: 'pointer',
diff --git a/escheduler-ui/src/js/conf/home/pages/dag/_source/dag.vue b/escheduler-ui/src/js/conf/home/pages/dag/_source/dag.vue
index 37c4119..e122658 100644
--- a/escheduler-ui/src/js/conf/home/pages/dag/_source/dag.vue
+++ b/escheduler-ui/src/js/conf/home/pages/dag/_source/dag.vue
@@ -8,7 +8,7 @@
              :id="v"
              v-for="(item,v) in tasksTypeList"
              @mousedown="_getDagId(v)">
-          <div data-toggle="tooltip" :title="item.desc" :class="_isDetails">
+          <div data-toggle="tooltip" :title="item.desc">
             <div class="icos" :class="'icos-' + v" ></div>
           </div>
         </div>
@@ -28,6 +28,17 @@
                   @click="_toggleView"
                   icon="fa fa-code">
           </x-button>
+          <x-button
+            style="vertical-align: middle;"
+            data-toggle="tooltip"
+            :title="$t('Startup parameter')"
+            data-container="body"
+            type="primary"
+            size="xsmall"
+            :disabled="$route.name !== 'projects-instance-details'"
+            @click="_toggleParam"
+            icon="fa fa-chevron-circle-right">
+          </x-button>
           <span class="name">{{name}}</span>
           &nbsp;
           <span v-if="name"  class="copy-name" @click="_copyName" :data-clipboard-text="name"><i class="iconfont" data-container="body"  data-toggle="tooltip" title="复制名称" >&#xe61e;</i></span>
@@ -68,10 +79,9 @@
                   type="primary"
                   size="xsmall"
                   :loading="spinnerLoading"
-                  v-ps="['GENERAL_USER']"
                   @click="_saveChart"
                   icon="fa fa-save"
-                  :disabled="isDetails">
+                  >
             {{spinnerLoading ? 'Loading...' : $t('Save')}}
           </x-button>
         </div>
@@ -205,9 +215,9 @@
        * @param item
        */
       _getDagId (v) {
-        if (this.isDetails) {
-          return
-        }
+        // if (this.isDetails) {
+        //   return
+        // }
         this.dagBarId = v
       },
       /**
@@ -239,11 +249,12 @@
         })
       },
       _operationClass (item) {
-        if (item.disable) {
-          return this.toolOperCode === item.code ? 'active' : ''
-        } else {
-          return 'disable'
-        }
+        return this.toolOperCode === item.code ? 'active' : ''
+        // if (item.disable) {
+        //   return this.toolOperCode === item.code ? 'active' : ''
+        // } else {
+        //   return 'disable'
+        // }
       },
       /**
        * Storage interface
@@ -383,6 +394,13 @@
       _toggleView () {
         findComponentDownward(this.$root, `assist-dag-index`)._toggleView()
       },
+
+      /**
+       * Starting parameters
+       */
+      _toggleParam () {
+        findComponentDownward(this.$root, `starting-params-dag-index`)._toggleParam()
+      },
       /**
        * Create a node popup layer
        * @param Object id
@@ -441,8 +459,6 @@
       'tasks': {
         deep: true,
         handler (o) {
-          console.log('+++++ save dag params +++++')
-          console.log(o)
 
           // Edit state does not allow deletion of node a...
           this.setIsEditDag(true)
diff --git a/escheduler-ui/src/js/conf/home/pages/dag/_source/formModel/formModel.vue b/escheduler-ui/src/js/conf/home/pages/dag/_source/formModel/formModel.vue
index 7f36417..863a44a 100644
--- a/escheduler-ui/src/js/conf/home/pages/dag/_source/formModel/formModel.vue
+++ b/escheduler-ui/src/js/conf/home/pages/dag/_source/formModel/formModel.vue
@@ -165,7 +165,7 @@
     <div class="bottom-box">
       <div class="submit" style="background: #fff;">
         <x-button type="text" @click="close()"> {{$t('Cancel')}} </x-button>
-        <x-button type="primary" shape="circle" :loading="spinnerLoading" @click="ok()" :disabled="isDetails" v-ps="['GENERAL_USER']">{{spinnerLoading ? 'Loading...' : $t('Confirm add')}} </x-button>
+        <x-button type="primary" shape="circle" :loading="spinnerLoading" @click="ok()" :disabled="isDetails">{{spinnerLoading ? 'Loading...' : $t('Confirm add')}} </x-button>
       </div>
     </div>
   </div>
diff --git a/escheduler-ui/src/js/conf/home/pages/dag/_source/formModel/tasks/mr.vue b/escheduler-ui/src/js/conf/home/pages/dag/_source/formModel/tasks/mr.vue
index 14b50a5..3787d13 100644
--- a/escheduler-ui/src/js/conf/home/pages/dag/_source/formModel/tasks/mr.vue
+++ b/escheduler-ui/src/js/conf/home/pages/dag/_source/formModel/tasks/mr.vue
@@ -3,7 +3,7 @@
     <m-list-box>
       <div slot="text">{{$t('Program Type')}}</div>
       <div slot="content">
-        <x-select v-model="programType" :disabled="isDetails" style="width: 100px;">
+        <x-select v-model="programType" :disabled="isDetails" style="width: 110px;">
           <x-option
                   v-for="city in programTypeList"
                   :key="city.code"
diff --git a/escheduler-ui/src/js/conf/home/pages/dag/_source/plugIn/jsPlumbHandle.js b/escheduler-ui/src/js/conf/home/pages/dag/_source/plugIn/jsPlumbHandle.js
index e2c0c67..440deb4 100644
--- a/escheduler-ui/src/js/conf/home/pages/dag/_source/plugIn/jsPlumbHandle.js
+++ b/escheduler-ui/src/js/conf/home/pages/dag/_source/plugIn/jsPlumbHandle.js
@@ -71,7 +71,7 @@
   this.setConfig({
     isDrag: !store.state.dag.isDetails,
     isAttachment: false,
-    isNewNodes: Permissions.getAuth() === false ? false : !store.state.dag.isDetails,
+    isNewNodes: !store.state.dag.isDetails,//Permissions.getAuth() === false ? false : !store.state.dag.isDetails,
     isDblclick: true,
     isContextmenu: true,
     isClick: false
diff --git a/escheduler-ui/src/js/conf/home/pages/dag/_source/startingParam/index.vue b/escheduler-ui/src/js/conf/home/pages/dag/_source/startingParam/index.vue
new file mode 100644
index 0000000..b1494e0
--- /dev/null
+++ b/escheduler-ui/src/js/conf/home/pages/dag/_source/startingParam/index.vue
@@ -0,0 +1,114 @@
+<template>
+  <div class="starting-params-dag-index">
+    <template v-if="isView && isActive">
+       <div class="box">
+         <p class="box-hd"><i class="fa fa-chevron-circle-right"></i><b>{{$t('Startup parameter')}}</b></p>
+         <ul class="box-bd">
+           <li><span>{{$t('Startup type')}}:</span><span>{{_rtRunningType(startupParam.commandType)}}</span></li>
+           <li><span>{{$t('Complement range')}}:</span><span v-if="startupParam.commandParam && startupParam.commandParam.complementStartDate">{{startupParam.commandParam.complementStartDate}}-{{startupParam.commandParam.complementEndDate}}</span><span v-else>-</span></li>
+           <li><span>{{$t('Failure Strategy')}}:</span><span>{{startupParam.failureStrategy === 'END' ? $t('End') : $t('Continue')}}</span></li>
+           <li><span>{{$t('Process priority')}}:</span><span>{{startupParam.processInstancePriority}}</span></li>
+           <li><span>{{$t('Worker group')}}:</span><span v-if="workerGroupList.length">{{_rtWorkerGroupName(startupParam.workerGroupId)}}</span></li>
+           <li><span>{{$t('Notification strategy')}}:</span><span>{{_rtWarningType(startupParam.warningType)}}</span></li>
+           <li><span>{{$t('Notification group')}}:</span><span v-if="notifyGroupList.length">{{_rtNotifyGroupName(startupParam.warningGroupId)}}</span></li>
+           <li><span>{{$t('Recipient')}}:</span><span>{{startupParam.receivers || '-'}}</span></li>
+           <li><span>{{$t('Cc')}}:</span><span>{{startupParam.receiversCc || '-'}}</span></li>
+         </ul>
+       </div>
+    </template>
+  </div>
+</template>
+<script>
+  import store from '@/conf/home/store'
+  import { runningType } from '@/conf/home/pages/dag/_source/config'
+  import { warningTypeList } from '@/conf/home/pages/projects/pages/definition/pages/list/_source/util'
+
+  export default {
+    name: 'starting-params-dag-index',
+    data () {
+      return {
+        store,
+        startupParam: store.state.dag.startup,
+        isView: false,
+        isActive: true,
+        notifyGroupList: null,
+        workerGroupList: null
+      }
+    },
+    methods: {
+      _toggleParam () {
+        this.isView = !this.isView
+      },
+      _rtRunningType (code) {
+        return _.filter(runningType, v => v.code === code)[0].desc
+      },
+      _rtWarningType (id) {
+        return _.filter(warningTypeList, v => v.id === id)[0].code
+      },
+      _rtNotifyGroupName (id) {
+        let o = _.filter(this.notifyGroupList, v => v.id === id)
+        if (o && o.length) {
+          return o[0].code
+        }
+        return '-'
+      },
+      _rtWorkerGroupName (id) {
+        let o =  _.filter(this.workerGroupList, v => v.id === id)
+        if (o && o.length) {
+          return o[0].name
+        }
+        return '-'
+      },
+      _getNotifyGroupList () {
+        let notifyGroupListS = _.cloneDeep(this.store.state.dag.notifyGroupListS) || []
+        if (!notifyGroupListS.length) {
+          this.store.dispatch('dag/getNotifyGroupList').then(res => {
+            this.notifyGroupList = res
+          })
+        } else {
+          this.notifyGroupList = notifyGroupListS
+        }
+      },
+      _getWorkerGroupList () {
+        let stateWorkerGroupsList = this.store.state.security.workerGroupsListAll || []
+        if (!stateWorkerGroupsList.length) {
+          this.store.dispatch('security/getWorkerGroupsAll').then(res => {
+            this.workerGroupList = res
+          })
+        } else {
+          this.workerGroupList = stateWorkerGroupsList
+        }
+      }
+    },
+    watch: {
+      '$route': {
+        deep: true,
+        handler () {
+          this.isActive = false
+          this.$nextTick(() => (this.isActive = true))
+        }
+      }
+    },
+    mounted () {
+      this._getNotifyGroupList()
+      this._getWorkerGroupList()
+    }
+  }
+</script>
+<style lang="scss">
+  .starting-params-dag-index {
+    .box {
+      padding: 5px 10px 10px;
+      .box-hd {
+        .fa {
+          color: #0097e0;
+          margin-right: 4px;
+        }
+        font-size: 16px;
+      }
+      .box-bd {
+        margin-left: 20px;
+      }
+    }
+  }
+</style>
diff --git a/escheduler-ui/src/js/conf/home/pages/dag/_source/udp/_source/selectTenant.vue b/escheduler-ui/src/js/conf/home/pages/dag/_source/udp/_source/selectTenant.vue
new file mode 100644
index 0000000..9c39e8e
--- /dev/null
+++ b/escheduler-ui/src/js/conf/home/pages/dag/_source/udp/_source/selectTenant.vue
@@ -0,0 +1,56 @@
+<template>
+  <x-select
+          :disabled="isDetails"
+          @on-change="_onChange"
+          v-model="value"
+          style="width: 180px">
+    <x-option
+            v-for="item in itemList"
+            :key="item.id"
+            :value="item.id"
+            :label="item.tenantName">
+    </x-option>
+  </x-select>
+</template>
+<script>
+  import disabledState from '@/module/mixin/disabledState'
+  export default {
+    name: 'form-tenant',
+    data () {
+      return {
+        itemList: []
+      }
+    },
+    mixins: [disabledState],
+    props: {
+      value: {
+        type: Number,
+        default: -1
+      }
+    },
+    model: {
+      prop: 'value',
+      event: 'tenantSelectEvent'
+    },
+    methods: {
+      _onChange (o) {
+        this.value = o.value
+        this.$emit('tenantSelectEvent', o.value)
+      }
+    },
+    watch: {
+    },
+    created () {
+      let stateTenantAllList = this.store.state.security.tenantAllList || []
+      if (stateTenantAllList.length) {
+        this.itemList = stateTenantAllList
+      } else {
+        this.store.dispatch('security/getTenantList').then(res => {
+          this.$nextTick(() => {
+            this.itemList = res
+          })
+        })
+      }
+    }
+  }
+</script>
diff --git a/escheduler-ui/src/js/conf/home/pages/dag/_source/udp/udp.vue b/escheduler-ui/src/js/conf/home/pages/dag/_source/udp/udp.vue
index 8e11dca..a42feab 100644
--- a/escheduler-ui/src/js/conf/home/pages/dag/_source/udp/udp.vue
+++ b/escheduler-ui/src/js/conf/home/pages/dag/_source/udp/udp.vue
@@ -27,6 +27,10 @@
       </template>
 
       <div class="title" style="padding-top: 6px;">
+        <span class="text-b">{{$t('select tenant')}}</span>
+        <form-tenant v-model="tenantId"></form-tenant>
+      </div>
+      <div class="title" style="padding-top: 6px;">
         <span>超时告警</span>
         <span style="padding-left: 6px;">
           <x-switch v-model="checkedTimeout"></x-switch>
@@ -62,7 +66,7 @@
           </div>
         </template>
         <x-button type="text" @click="close()"> {{$t('Cancel')}} </x-button>
-        <x-button type="primary" shape="circle" @click="ok()" v-ps="['GENERAL_USER']" >{{$t('Add')}}</x-button>
+        <x-button type="primary" shape="circle" @click="ok()">{{$t('Add')}}</x-button>
       </div>
     </div>
   </div>
@@ -73,6 +77,7 @@
   import mLocalParams from '../formModel/tasks/_source/localParams'
   import disabledState from '@/module/mixin/disabledState'
   import Affirm from '../jumpAffirm'
+  import FormTenant from "./_source/selectTenant";
 
   export default {
     name: 'udp',
@@ -90,6 +95,8 @@
         syncDefine: true,
         // Timeout alarm
         timeout: 0,
+
+        tenantId: -1,
         // checked Timeout alarm
         checkedTimeout: true
       }
@@ -116,6 +123,7 @@
         this.store.commit('dag/setGlobalParams', _.cloneDeep(this.udpList))
         this.store.commit('dag/setName', _.cloneDeep(this.name))
         this.store.commit('dag/setTimeout', _.cloneDeep(this.timeout))
+        this.store.commit('dag/setTenantId', _.cloneDeep(this.tenantId))
         this.store.commit('dag/setDesc', _.cloneDeep(this.desc))
         this.store.commit('dag/setSyncDefine', this.syncDefine)
       },
@@ -181,9 +189,10 @@
       this.syncDefine = dag.syncDefine
       this.timeout = dag.timeout || 0
       this.checkedTimeout = this.timeout !== 0
+      this.tenantId = dag.tenantId || -1
     },
     mounted () {},
-    components: { mLocalParams }
+    components: {FormTenant, mLocalParams }
   }
 </script>
 
diff --git a/escheduler-ui/src/js/conf/home/pages/dag/definitionDetails.vue b/escheduler-ui/src/js/conf/home/pages/dag/definitionDetails.vue
index e377ebf..c8849d5 100644
--- a/escheduler-ui/src/js/conf/home/pages/dag/definitionDetails.vue
+++ b/escheduler-ui/src/js/conf/home/pages/dag/definitionDetails.vue
@@ -26,7 +26,7 @@
     methods: {
       ...mapMutations('dag', ['resetParams', 'setIsDetails']),
       ...mapActions('dag', ['getProcessList', 'getResourcesList', 'getProcessDetails']),
-      ...mapActions('security', ['getWorkerGroupsAll']),
+      ...mapActions('security', ['getTenantList','getWorkerGroupsAll']),
       /**
        * init
        */
@@ -43,7 +43,8 @@
           // get resource
           this.getResourcesList(),
           // get worker group list
-          this.getWorkerGroupsAll()
+          this.getWorkerGroupsAll(),
+          this.getTenantList()
         ]).then((data) => {
           let item = data[0]
           this.setIsDetails(item.releaseState === 'ONLINE')
diff --git a/escheduler-ui/src/js/conf/home/pages/dag/index.vue b/escheduler-ui/src/js/conf/home/pages/dag/index.vue
index b9aee79..7a3429f 100644
--- a/escheduler-ui/src/js/conf/home/pages/dag/index.vue
+++ b/escheduler-ui/src/js/conf/home/pages/dag/index.vue
@@ -25,7 +25,7 @@
     methods: {
       ...mapMutations('dag', ['resetParams']),
       ...mapActions('dag', ['getProcessList', 'getResourcesList']),
-      ...mapActions('security', ['getWorkerGroupsAll']),
+      ...mapActions('security', ['getTenantList','getWorkerGroupsAll']),
       /**
        * init
        */
@@ -40,7 +40,8 @@
           // get resource
           this.getResourcesList(),
           // get worker group list
-          this.getWorkerGroupsAll()
+          this.getWorkerGroupsAll(),
+          this.getTenantList()
         ]).then((data) => {
           this.isLoading = false
           // Whether to pop up the box?
@@ -65,4 +66,4 @@
     },
     components: { mDag, mSpin }
   }
-</script>
\ No newline at end of file
+</script>
diff --git a/escheduler-ui/src/js/conf/home/pages/dag/instanceDetails.vue b/escheduler-ui/src/js/conf/home/pages/dag/instanceDetails.vue
index 3e6c49c..705a151 100644
--- a/escheduler-ui/src/js/conf/home/pages/dag/instanceDetails.vue
+++ b/escheduler-ui/src/js/conf/home/pages/dag/instanceDetails.vue
@@ -1,6 +1,7 @@
 <template>
   <div class="home-main index-model">
     <m-variable></m-variable>
+    <m-starting-param></m-starting-param>
     <m-dag v-if="!isLoading" :type="'instance'"></m-dag>
     <m-spin :is-spin="isLoading"></m-spin>
   </div>
@@ -10,6 +11,7 @@
   import { mapActions, mapMutations } from 'vuex'
   import mSpin from '@/module/components/spin/spin'
   import mVariable from './_source/variable'
+  import mStartingParam from './_source/startingParam'
   import Affirm from './_source/jumpAffirm'
   import disabledState from '@/module/mixin/disabledState'
 
@@ -26,7 +28,7 @@
     methods: {
       ...mapMutations('dag', ['setIsDetails', 'resetParams']),
       ...mapActions('dag', ['getProcessList', 'getResourcesList', 'getInstancedetail']),
-      ...mapActions('security', ['getWorkerGroupsAll']),
+      ...mapActions('security', ['getTenantList','getWorkerGroupsAll']),
       /**
        * init
        */
@@ -43,7 +45,8 @@
           // get resources
           this.getResourcesList(),
           // get worker group list
-          this.getWorkerGroupsAll()
+          this.getWorkerGroupsAll(),
+          this.getTenantList()
         ]).then((data) => {
           let item = data[0]
           let flag = false
@@ -90,6 +93,6 @@
     },
     mounted () {
     },
-    components: { mDag, mSpin, mVariable }
+    components: { mDag, mSpin, mVariable, mStartingParam }
   }
-</script>
\ No newline at end of file
+</script>
diff --git a/escheduler-ui/src/js/conf/home/pages/datasource/pages/list/_source/createDataSource.vue b/escheduler-ui/src/js/conf/home/pages/datasource/pages/list/_source/createDataSource.vue
index 8b09570..cc6d25e 100644
--- a/escheduler-ui/src/js/conf/home/pages/datasource/pages/list/_source/createDataSource.vue
+++ b/escheduler-ui/src/js/conf/home/pages/datasource/pages/list/_source/createDataSource.vue
@@ -63,6 +63,17 @@
             </x-input>
           </template>
         </m-list-box-f>
+        <m-list-box-f :class="{hidden:showPrincipal}">
+          <template slot="name"><b>*</b>Principal</template>
+          <template slot="content">
+            <x-input
+              type="input"
+              v-model="principal"
+              :placeholder="$t('Please enter Principal')"
+              autocomplete="off">
+            </x-input>
+          </template>
+        </m-list-box-f>
         <m-list-box-f>
           <template slot="name"><b>*</b>{{$t('User Name')}}</template>
           <template slot="content">
@@ -143,6 +154,8 @@
         port: '',
         // data storage name
         database: '',
+        // principal
+        principal:'',
         // database username
         userName: '',
         // Database password
@@ -150,12 +163,15 @@
         // Jdbc connection parameter
         other: '',
         // btn test loading
-        testLoading: false
+        testLoading: false,
+        showPrincipal: true,
+        isShowPrincipal:true
       }
     },
     props: {
       item: Object
     },
+
     methods: {
       _rtOtherPlaceholder () {
         return `${i18n.$t('Please enter format')} {"key1":"value1","key2":"value2"...} ${i18n.$t('connection parameter')}`
@@ -187,6 +203,7 @@
           host: this.host,
           port: this.port,
           database: this.database,
+          principal:this.principal,
           userName: this.userName,
           password: this.password,
           other: this.other
@@ -289,6 +306,7 @@
           this.note = res.note
           this.host = res.host
           this.port = res.port
+          this.principal = res.principal
           this.database = res.database
           this.userName = res.userName
           this.password = res.password
@@ -298,12 +316,31 @@
         })
       }
     },
-    watch: {},
+    watch: {
+      'type'(value){
+        if((value =='HIVE'||value == 'SPARK')&&this.isShowPrincipal== true){
+          this.showPrincipal = false
+        }else{
+          this.showPrincipal = true
+        }
+      }
+    },
     created () {
       // Backfill
       if (this.item.id) {
         this._getEditDatasource()
       }
+
+      return new Promise((resolve, reject) => {
+        this.store.dispatch('datasource/getKerberosStartupState').then(res => {
+          this.isShowPrincipal=res
+        }).catch(e => {
+          this.$message.error(e.msg || '')
+          reject(e)
+        })
+      })
+
+
     },
     mounted () {
     },
diff --git a/escheduler-ui/src/js/conf/home/pages/datasource/pages/list/_source/list.vue b/escheduler-ui/src/js/conf/home/pages/datasource/pages/list/_source/list.vue
index b32aeeb..a08051b 100644
--- a/escheduler-ui/src/js/conf/home/pages/datasource/pages/list/_source/list.vue
+++ b/escheduler-ui/src/js/conf/home/pages/datasource/pages/list/_source/list.vue
@@ -56,7 +56,6 @@
                     shape="circle"
                     size="xsmall"
                     data-toggle="tooltip"
-                    v-ps="['GENERAL_USER']"
                     :title="$t('Edit')"
                     icon="iconfont icon-bianjixiugai"
                     @click="_edit(item)">
@@ -77,8 +76,7 @@
                         size="xsmall"
                         icon="iconfont icon-shanchu"
                         data-toggle="tooltip"
-                        :title="$t('delete')"
-                        v-ps="['GENERAL_USER']">
+                        :title="$t('delete')">
                 </x-button>
               </template>
             </x-poptip>
diff --git a/escheduler-ui/src/js/conf/home/pages/datasource/pages/list/index.vue b/escheduler-ui/src/js/conf/home/pages/datasource/pages/list/index.vue
index de94e0e..8d50127 100644
--- a/escheduler-ui/src/js/conf/home/pages/datasource/pages/list/index.vue
+++ b/escheduler-ui/src/js/conf/home/pages/datasource/pages/list/index.vue
@@ -3,7 +3,7 @@
     <template slot="conditions">
       <m-conditions @on-conditions="_onConditions">
         <template slot="button-group">
-          <x-button type="ghost" size="small" @click="_create('')" v-ps="['GENERAL_USER']">{{$t('Create Datasource')}}</x-button>
+          <x-button type="ghost" size="small" @click="_create('')">{{$t('Create Datasource')}}</x-button>
         </template>
       </m-conditions>
     </template>
diff --git a/escheduler-ui/src/js/conf/home/pages/monitor/pages/servers/master.vue b/escheduler-ui/src/js/conf/home/pages/monitor/pages/servers/master.vue
index 22d4878..914bab2 100644
--- a/escheduler-ui/src/js/conf/home/pages/monitor/pages/servers/master.vue
+++ b/escheduler-ui/src/js/conf/home/pages/monitor/pages/servers/master.vue
@@ -6,7 +6,7 @@
           <div class="row-title">
             <div class="left">
               <span class="sp">IP: {{item.host}}</span>
-              <span class="sp">{{$t('Port')}}: {{item.port}}</span>
+              <span class="sp">{{$t('Process Pid')}}: {{item.port}}</span>
               <span class="sp">{{$t('Zk registration directory')}}: {{item.zkDirectory}}</span>
             </div>
             <div class="right">
@@ -93,4 +93,4 @@
 </script>
 <style lang="scss" rel="stylesheet/scss">
   @import "./servers";
-</style>
\ No newline at end of file
+</style>
diff --git a/escheduler-ui/src/js/conf/home/pages/monitor/pages/servers/statistics.vue b/escheduler-ui/src/js/conf/home/pages/monitor/pages/servers/statistics.vue
new file mode 100644
index 0000000..a552e4c
--- /dev/null
+++ b/escheduler-ui/src/js/conf/home/pages/monitor/pages/servers/statistics.vue
@@ -0,0 +1,115 @@
+<template>
+  <m-list-construction :title="$t('statistics') + $t('Manage')">
+    <template slot="content">
+      <div class="servers-wrapper mysql-model" v-show="2">
+        <div class="row">
+          <div class="col-md-3">
+            <div class="text-num-model text">
+              <div class="title">
+                <span >{{$t('process number of waiting for running')}}</span>
+              </div>
+              <div class="value-p">
+                <b :style="{color:color[0]}"> {{commandCountData.normalCount}}</b>
+              </div>
+            </div>
+          </div>
+        <div class="col-md-3">
+          <div class="text-num-model text">
+            <div class="title">
+              <span >{{$t('failure command number')}}</span>
+            </div>
+            <div class="value-p">
+              <b :style="{color:color[1]}"> {{commandCountData.errorCount}}</b>
+            </div>
+          </div>
+        </div>
+        <div class="col-md-3">
+          <div class="text-num-model text">
+            <div class="title">
+              <span >{{$t('tasks number of waiting running')}}</span>
+            </div>
+            <div class="value-p">
+              <b :style="{color:color[0]}"> {{queueCount.taskQueue}}</b>
+            </div>
+          </div>
+        </div>
+        <div class="col-md-3">
+          <div class="text-num-model text">
+            <div class="title">
+              <span >{{$t('task number of ready to kill')}}</span>
+            </div>
+            <div class="value-p">
+              <b :style="{color:color[1]}">{{queueCount.taskKill}}</b>
+            </div>
+          </div>
+        </div>
+      </div>
+      </div>
+      <m-spin :is-spin="isLoading" ></m-spin>
+    </template>
+  </m-list-construction>
+</template>
+
+<script>
+  import { mapActions } from 'vuex'
+  import mSpin from '@/module/components/spin/spin'
+  import mNoData from '@/module/components/noData/noData'
+  import themeData from '@/module/echarts/themeData.json'
+  import mListConstruction from '@/module/components/listConstruction/listConstruction'
+
+  export default {
+      name: 'statistics',
+    data () {
+      return {
+        isLoading: false,
+        queueCount: {},
+        commandCountData: {},
+        color: themeData.color
+      }
+    },
+    props:{},
+    methods: {
+      //...mapActions('monitor', ['getDatabaseData'])
+      // ...mapActions('projects', ['getCommandStateCount']),
+      ...mapActions('projects', ['getQueueCount']),
+      ...mapActions('projects', ['getCommandStateCount']),
+    },
+    watch: {},
+    created () {
+      this.isLoading = true
+      this.getQueueCount().then(res => {
+        this.queueCount = res.data
+        this.isLoading = false
+      }).catch(() => {
+        this.isLoading = false
+       })
+
+      this.getCommandStateCount().then(res => {
+        let normal = 0
+        let error = 0
+        _.forEach(res.data, (v, i) => {
+          let key = _.keys(v)
+          if(key[0] == 'errorCount') {
+            error = error + v.errorCount
+          }
+          if(key[1] == 'normalCount'){
+            normal = normal + v.normalCount
+          }
+          }
+        )
+        this.commandCountData = {
+          'normalCount': normal,
+          'errorCount' : error
+        }
+      }).catch( () => {
+      })
+    },
+    mounted () {
+    },
+    components: { mListConstruction, mSpin, mNoData }
+  }
+
+</script>
+<style lang="scss" rel="stylesheet/scss">
+  @import "./servers";
+</style>
diff --git a/escheduler-ui/src/js/conf/home/pages/monitor/pages/servers/worker.vue b/escheduler-ui/src/js/conf/home/pages/monitor/pages/servers/worker.vue
index 3cf0993..960beeb 100644
--- a/escheduler-ui/src/js/conf/home/pages/monitor/pages/servers/worker.vue
+++ b/escheduler-ui/src/js/conf/home/pages/monitor/pages/servers/worker.vue
@@ -6,7 +6,7 @@
           <div class="row-title">
             <div class="left">
               <span class="sp">IP: {{item.host}}</span>
-              <span class="sp">{{$t('Port')}}: {{item.port}}</span>
+              <span class="sp">{{$t('Process Pid')}}: {{item.port}}</span>
               <span class="sp">{{$t('Zk registration directory')}}: {{item.zkDirectory}}</span>
             </div>
             <div class="right">
@@ -94,4 +94,4 @@
 </script>
 <style lang="scss" rel="stylesheet/scss">
   @import "./servers";
-</style>
\ No newline at end of file
+</style>
diff --git a/escheduler-ui/src/js/conf/home/pages/projects/pages/_source/instanceConditions/index.vue b/escheduler-ui/src/js/conf/home/pages/projects/pages/_source/instanceConditions/index.vue
index 33d1796..4388d47 100644
--- a/escheduler-ui/src/js/conf/home/pages/projects/pages/_source/instanceConditions/index.vue
+++ b/escheduler-ui/src/js/conf/home/pages/projects/pages/_source/instanceConditions/index.vue
@@ -36,10 +36,10 @@
         </x-select>
       </div>
       <div class="list">
-        <x-input v-model="searchParams.host" style="width: 140px;" size="small" :placeholder="$t('host')"></x-input>
+        <x-input v-model="searchParams.host" @on-enterkey="_ckQuery" style="width: 140px;" size="small" :placeholder="$t('host')"></x-input>
       </div>
       <div class="list">
-        <x-input v-model="searchParams.searchVal" style="width: 200px;" size="small" :placeholder="$t('name')"></x-input>
+        <x-input v-model="searchParams.searchVal" @on-enterkey="_ckQuery" style="width: 200px;" size="small" :placeholder="$t('name')"></x-input>
       </div>
     </template>
   </m-conditions>
diff --git a/escheduler-ui/src/js/conf/home/pages/projects/pages/definition/pages/list/_source/email.vue b/escheduler-ui/src/js/conf/home/pages/projects/pages/definition/pages/list/_source/email.vue
index cc6cb57..f5c38f9 100644
--- a/escheduler-ui/src/js/conf/home/pages/projects/pages/definition/pages/list/_source/email.vue
+++ b/escheduler-ui/src/js/conf/home/pages/projects/pages/definition/pages/list/_source/email.vue
@@ -32,9 +32,12 @@
                   v-model="email"
                   :disabled="disabled"
                   :placeholder="$t('Please enter email')"
+                  @blur="_emailEnter"
                   @keydown.tab="_emailTab"
                   @keyup.delete="_emailDelete"
                   @keyup.enter="_emailEnter"
+                  @keyup.space="_emailEnter"
+                  @keyup.186="_emailEnter"
                   @keyup.up="_emailKeyup('up')"
                   @keyup.down="_emailKeyup('down')">
         </span>
@@ -78,6 +81,11 @@
        * Manually add a mailbox
        */
       _manualEmail () {
+        if (this.email === '') {
+          return
+        }
+        this.email = _.trim(this.email).replace(/(;$)|(;$)/g, "")
+
         let email = this.email
 
         let is = (n) => {
diff --git a/escheduler-ui/src/js/conf/home/pages/projects/pages/definition/pages/list/_source/list.vue b/escheduler-ui/src/js/conf/home/pages/projects/pages/definition/pages/list/_source/list.vue
index aa2be8e..f9e8dba 100644
--- a/escheduler-ui/src/js/conf/home/pages/projects/pages/definition/pages/list/_source/list.vue
+++ b/escheduler-ui/src/js/conf/home/pages/projects/pages/definition/pages/list/_source/list.vue
@@ -58,12 +58,12 @@
             <span v-if="!item.scheduleReleaseState">-</span>
           </td>
           <td>
-            <x-button type="info" shape="circle" size="xsmall" data-toggle="tooltip" :title="$t('Edit')" @click="_edit(item)" :disabled="item.releaseState === 'ONLINE'" v-ps="['GENERAL_USER']" icon="iconfont icon-bianji"><!--{{$t('编辑')}}--></x-button>
-            <x-button type="success" shape="circle" size="xsmall" data-toggle="tooltip" :title="$t('Start')" @click="_start(item)" :disabled="item.releaseState !== 'ONLINE'" v-ps="['GENERAL_USER']" icon="iconfont icon-qidong"><!--{{$t('启动')}}--></x-button>
-            <x-button type="info" shape="circle" size="xsmall" data-toggle="tooltip" :title="$t('Timing')" @click="_timing(item)" :disabled="item.releaseState !== 'ONLINE' || item.scheduleReleaseState !== null" v-ps="['GENERAL_USER']" icon="iconfont icon-timer"><!--{{$t('定时')}}--></x-button>
-            <x-button type="warning" shape="circle" size="xsmall" data-toggle="tooltip" :title="$t('online')" @click="_poponline(item)" v-if="item.releaseState === 'OFFLINE'" v-ps="['GENERAL_USER']" icon="iconfont icon-erji-xiaxianjilu-copy"><!--{{$t('下线')}}--></x-button>
-            <x-button type="error" shape="circle" size="xsmall" data-toggle="tooltip" :title="$t('offline')" @click="_downline(item)" v-if="item.releaseState === 'ONLINE'" v-ps="['GENERAL_USER']" icon="iconfont icon-erji-xiaxianjilu"><!--{{$t('上线')}}--></x-button>
-            <x-button type="info" shape="circle" size="xsmall" data-toggle="tooltip" :title="$t('Cron Manage')" @click="_timingManage(item)" :disabled="item.releaseState !== 'ONLINE'" v-ps="['GENERAL_USER']" icon="iconfont icon-paibanguanli"><!--{{$t('定时管理')}}--></x-button>
+            <x-button type="info" shape="circle" size="xsmall" data-toggle="tooltip" :title="$t('Edit')" @click="_edit(item)" :disabled="item.releaseState === 'ONLINE'"  icon="iconfont icon-bianji"><!--{{$t('编辑')}}--></x-button>
+            <x-button type="success" shape="circle" size="xsmall" data-toggle="tooltip" :title="$t('Start')" @click="_start(item)" :disabled="item.releaseState !== 'ONLINE'"  icon="iconfont icon-qidong"><!--{{$t('启动')}}--></x-button>
+            <x-button type="info" shape="circle" size="xsmall" data-toggle="tooltip" :title="$t('Timing')" @click="_timing(item)" :disabled="item.releaseState !== 'ONLINE' || item.scheduleReleaseState !== null"  icon="iconfont icon-timer"><!--{{$t('定时')}}--></x-button>
+            <x-button type="warning" shape="circle" size="xsmall" data-toggle="tooltip" :title="$t('online')" @click="_poponline(item)" v-if="item.releaseState === 'OFFLINE'"  icon="iconfont icon-erji-xiaxianjilu-copy"><!--{{$t('下线')}}--></x-button>
+            <x-button type="error" shape="circle" size="xsmall" data-toggle="tooltip" :title="$t('offline')" @click="_downline(item)" v-if="item.releaseState === 'ONLINE'"  icon="iconfont icon-erji-xiaxianjilu"><!--{{$t('上线')}}--></x-button>
+            <x-button type="info" shape="circle" size="xsmall" data-toggle="tooltip" :title="$t('Cron Manage')" @click="_timingManage(item)" :disabled="item.releaseState !== 'ONLINE'"  icon="iconfont icon-paibanguanli"><!--{{$t('定时管理')}}--></x-button>
             <x-poptip
               :ref="'poptip-delete-' + $index"
               placement="bottom-end"
@@ -81,8 +81,7 @@
                   size="xsmall"
                   :disabled="item.releaseState === 'ONLINE'"
                   data-toggle="tooltip"
-                  :title="$t('delete')"
-                  v-ps="['GENERAL_USER']">
+                  :title="$t('delete')">
                 </x-button>
               </template>
             </x-poptip>
diff --git a/escheduler-ui/src/js/conf/home/pages/projects/pages/definition/pages/list/_source/start.vue b/escheduler-ui/src/js/conf/home/pages/projects/pages/definition/pages/list/_source/start.vue
index ddb6b0a..c2e3c33 100644
--- a/escheduler-ui/src/js/conf/home/pages/projects/pages/definition/pages/list/_source/start.vue
+++ b/escheduler-ui/src/js/conf/home/pages/projects/pages/definition/pages/list/_source/start.vue
@@ -137,7 +137,7 @@
     </template>
     <div class="submit">
       <x-button type="text" @click="close()"> {{$t('Cancel')}} </x-button>
-      <x-button type="primary" shape="circle" :loading="spinnerLoading" @click="ok()" v-ps="['GENERAL_USER']">{{spinnerLoading ? 'Loading...' : $t('Start')}} </x-button>
+      <x-button type="primary" shape="circle" :loading="spinnerLoading" @click="ok()">{{spinnerLoading ? 'Loading...' : $t('Start')}} </x-button>
     </div>
   </div>
 </template>
diff --git a/escheduler-ui/src/js/conf/home/pages/projects/pages/definition/pages/list/_source/timing.vue b/escheduler-ui/src/js/conf/home/pages/projects/pages/definition/pages/list/_source/timing.vue
index ba67536..42bb790 100644
--- a/escheduler-ui/src/js/conf/home/pages/projects/pages/definition/pages/list/_source/timing.vue
+++ b/escheduler-ui/src/js/conf/home/pages/projects/pages/definition/pages/list/_source/timing.vue
@@ -21,9 +21,11 @@
       </div>
     </div>
     <div class="clearfix list">
+      <x-button type="info"  style="margin-left:20px" shape="circle" :loading="spinnerLoading" @click="preview()">执行时间</x-button>
       <div class="text">
         {{$t('Timing')}}
       </div>
+
       <div class="cont">
         <template>
           <x-poptip :ref="'poptip'" placement="bottom-start">
@@ -44,6 +46,13 @@
       </div>
     </div>
     <div class="clearfix list">
+      <div style = "padding-left: 150px;">未来五次执行时间</div>
+      <ul style = "padding-left: 150px;">
+          <li v-for="time in previewTimes">{{time}}</li>
+      </ul>
+    </div>
+
+    <div class="clearfix list">
       <div class="text">
         {{$t('Failure Strategy')}}
       </div>
@@ -127,7 +136,7 @@
     </div>
     <div class="submit">
       <x-button type="text" @click="close()"> {{$t('Cancel')}} </x-button>
-      <x-button type="primary" shape="circle" :loading="spinnerLoading" @click="ok()" v-ps="['GENERAL_USER']">{{spinnerLoading ? 'Loading...' : (item.crontab ? $t('Edit') : $t('Create'))}} </x-button>
+      <x-button type="primary" shape="circle" :loading="spinnerLoading" @click="ok()">{{spinnerLoading ? 'Loading...' : (item.crontab ? $t('Edit') : $t('Create'))}} </x-button>
     </div>
   </div>
 </template>
@@ -162,7 +171,8 @@
         receiversCc: [],
         i18n: i18n.globalScope.LOCALE,
         processInstancePriority: 'MEDIUM',
-        workerGroupId: -1
+        workerGroupId: -1,
+        previewTimes: []
       }
     },
     props: {
@@ -180,6 +190,11 @@
           return false
         }
 
+        if (this.scheduleTime[0] === this.scheduleTime[1]) {
+          this.$message.warning(`${i18n.$t('The start time must not be the same as the end')}`)
+          return false
+        }
+
         if (!this.crontab) {
           this.$message.warning(`${i18n.$t('Please enter crontab')}`)
           return false
@@ -225,6 +240,24 @@
         }
       },
 
+      _preview () {
+              if (this._verification()) {
+                let api = 'dag/previewSchedule'
+                let searchParams = {
+                  schedule: JSON.stringify({
+                    startTime: this.scheduleTime[0],
+                    endTime: this.scheduleTime[1],
+                    crontab: this.crontab
+                  })
+                }
+                let msg = ''
+
+                this.store.dispatch(api, searchParams).then(res => {
+                  this.previewTimes = res
+                })
+              }
+            },
+
       _getNotifyGroupList () {
         return new Promise((resolve, reject) => {
           let notifyGroupListS = _.cloneDeep(this.store.state.dag.notifyGroupListS) || []
@@ -248,6 +281,9 @@
       },
       close () {
         this.$emit('close')
+      },
+      preview () {
+        this._preview()
       }
     },
     watch: {
diff --git a/escheduler-ui/src/js/conf/home/pages/projects/pages/definition/pages/list/_source/util.js b/escheduler-ui/src/js/conf/home/pages/projects/pages/definition/pages/list/_source/util.js
index db6c8aa..2259dea 100644
--- a/escheduler-ui/src/js/conf/home/pages/projects/pages/definition/pages/list/_source/util.js
+++ b/escheduler-ui/src/js/conf/home/pages/projects/pages/definition/pages/list/_source/util.js
@@ -37,7 +37,7 @@
 ]
 
 const isEmial = (val) => {
-  let regEmail = /^([a-zA-Z0-9]+[_|\_|\.]?)*[a-zA-Z0-9]+@([a-zA-Z0-9]+[_|\_|\.]?)*[a-zA-Z0-9]+\.[a-zA-Z]{2,3}$/ // eslint-disable-line
+  let regEmail = /^([a-zA-Z0-9]+[_|\-|\.]?)*[a-zA-Z0-9]+@([a-zA-Z0-9]+[_|\-|\.]?)*[a-zA-Z0-9]+\.[a-zA-Z]{2,3}$/ // eslint-disable-line
   return regEmail.test(val)
 }
 
diff --git a/escheduler-ui/src/js/conf/home/pages/projects/pages/definition/pages/list/index.vue b/escheduler-ui/src/js/conf/home/pages/projects/pages/definition/pages/list/index.vue
index bc63896..bf8612d 100644
--- a/escheduler-ui/src/js/conf/home/pages/projects/pages/definition/pages/list/index.vue
+++ b/escheduler-ui/src/js/conf/home/pages/projects/pages/definition/pages/list/index.vue
@@ -3,7 +3,7 @@
     <template slot="conditions">
       <m-conditions @on-conditions="_onConditions">
         <template slot="button-group">
-          <x-button type="ghost" size="small"  v-ps="['GENERAL_USER']" @click="() => this.$router.push({name: 'definition-create'})">{{$t('Create process')}}</x-button>
+          <x-button type="ghost" size="small"  @click="() => this.$router.push({name: 'definition-create'})">{{$t('Create process')}}</x-button>
         </template>
       </m-conditions>
     </template>
diff --git a/escheduler-ui/src/js/conf/home/pages/projects/pages/index/index.vue b/escheduler-ui/src/js/conf/home/pages/projects/pages/index/index.vue
index 28f61f9..f695f3a 100644
--- a/escheduler-ui/src/js/conf/home/pages/projects/pages/index/index.vue
+++ b/escheduler-ui/src/js/conf/home/pages/projects/pages/index/index.vue
@@ -33,30 +33,6 @@
             </div>
           </div>
         </div>
-        <div class="row" style="padding-top: 20px;">
-          <div class="col-md-6">
-          </div>
-          <div class="col-md-6">
-            <div class="chart-title">
-              <span>{{$t('Queue statistics')}}</span>
-            </div>
-            <div class="row">
-              <m-queue-count :search-params="searchParams">
-              </m-queue-count>
-            </div>
-          </div>
-        </div>
-        <div class="row">
-          <div class="col-md-12">
-            <div class="chart-title" style="margin-bottom: 20px;margin-top: 30px">
-              <span>{{$t('Command status statistics')}}</span>
-            </div>
-            <div>
-              <m-command-state-count :search-params="searchParams">
-              </m-command-state-count>
-            </div>
-          </div>
-        </div>
         <div class="row">
           <div class="col-md-12">
             <div class="chart-title" style="margin-bottom: -20px;margin-top: 30px">
diff --git a/escheduler-ui/src/js/conf/home/pages/projects/pages/instance/pages/list/_source/list.vue b/escheduler-ui/src/js/conf/home/pages/projects/pages/instance/pages/list/_source/list.vue
index f98383c..619407a 100644
--- a/escheduler-ui/src/js/conf/home/pages/projects/pages/instance/pages/list/_source/list.vue
+++ b/escheduler-ui/src/js/conf/home/pages/projects/pages/instance/pages/list/_source/list.vue
@@ -73,7 +73,6 @@
                         data-toggle="tooltip"
                         :title="$t('Edit')"
                         @click="_reEdit(item)"
-                        v-ps="['GENERAL_USER']"
                         icon="iconfont icon-bianjixiugai"
                         :disabled="item.state !== 'SUCCESS' && item.state !== 'PAUSE' && item.state !== 'FAILURE' && item.state !== 'STOP'"></x-button>
               <x-button type="info"
@@ -82,7 +81,6 @@
                         data-toggle="tooltip"
                         :title="$t('Rerun')"
                         @click="_reRun(item,$index)"
-                        v-ps="['GENERAL_USER']"
                         icon="iconfont icon-shuaxin"
                         :disabled="item.state !== 'SUCCESS' && item.state !== 'PAUSE' && item.state !== 'FAILURE' && item.state !== 'STOP'"></x-button>
               <x-button type="success"
@@ -91,26 +89,23 @@
                         data-toggle="tooltip"
                         :title="$t('Recovery Failed')"
                         @click="_restore(item,$index)"
-                        v-ps="['GENERAL_USER']"
                         icon="iconfont icon-cuowuguanbishibai"
                         :disabled="item.state !== 'FAILURE'"></x-button>
               <x-button type="error"
                         shape="circle"
                         size="xsmall"
                         data-toggle="tooltip"
-                        :title="$t('Stop')"
-                        @click="_stop(item)"
-                        v-ps="['GENERAL_USER']"
-                        icon="iconfont icon-zanting1"
-                        :disabled="item.state !== 'RUNNING_EXEUTION'"></x-button>
+                        :title="item.state === 'STOP' ? $t('Recovery Suspend') : $t('Stop')"
+                        @click="_stop(item,$index)"
+                        :icon="item.state === 'STOP' ? 'iconfont icon-ai06' : 'iconfont icon-zanting'"
+                        :disabled="item.state !== 'RUNNING_EXEUTION' && item.state != 'STOP'"></x-button>
               <x-button type="warning"
                         shape=&q