split elastic-job-cloud as a independent repo
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..6ddbc53
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,30 @@
+# maven ignore
+target/
+*.jar
+*.war
+*.zip
+*.tar
+
+# eclipse ignore
+.settings/
+.project
+.classpath
+
+# idea ignore
+.idea/
+*.ipr
+*.iml
+*.iws
+
+# temp ignore
+logs/
+*.doc
+*.log
+*.cache
+*.diff
+*.patch
+*.tmp
+
+# system ignore
+.DS_Store
+Thumbs.db
diff --git a/.travis.yml b/.travis.yml
new file mode 100644
index 0000000..1d72086
--- /dev/null
+++ b/.travis.yml
@@ -0,0 +1,9 @@
+language: java
+jdk:
+  - oraclejdk8
+  #- oraclejdk7
+  #- openjdk7
+before_script:
+    - echo "MAVEN_OPTS='-Xmx1024m -XX:MaxPermSize=256m'" > ~/.mavenrc
+after_success:
+  - mvn clean cobertura:cobertura coveralls:report
diff --git a/LICENSE b/LICENSE
new file mode 100644
index 0000000..8f71f43
--- /dev/null
+++ b/LICENSE
@@ -0,0 +1,202 @@
+                                 Apache License
+                           Version 2.0, January 2004
+                        http://www.apache.org/licenses/
+
+   TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+   1. Definitions.
+
+      "License" shall mean the terms and conditions for use, reproduction,
+      and distribution as defined by Sections 1 through 9 of this document.
+
+      "Licensor" shall mean the copyright owner or entity authorized by
+      the copyright owner that is granting the License.
+
+      "Legal Entity" shall mean the union of the acting entity and all
+      other entities that control, are controlled by, or are under common
+      control with that entity. For the purposes of this definition,
+      "control" means (i) the power, direct or indirect, to cause the
+      direction or management of such entity, whether by contract or
+      otherwise, or (ii) ownership of fifty percent (50%) or more of the
+      outstanding shares, or (iii) beneficial ownership of such entity.
+
+      "You" (or "Your") shall mean an individual or Legal Entity
+      exercising permissions granted by this License.
+
+      "Source" form shall mean the preferred form for making modifications,
+      including but not limited to software source code, documentation
+      source, and configuration files.
+
+      "Object" form shall mean any form resulting from mechanical
+      transformation or translation of a Source form, including but
+      not limited to compiled object code, generated documentation,
+      and conversions to other media types.
+
+      "Work" shall mean the work of authorship, whether in Source or
+      Object form, made available under the License, as indicated by a
+      copyright notice that is included in or attached to the work
+      (an example is provided in the Appendix below).
+
+      "Derivative Works" shall mean any work, whether in Source or Object
+      form, that is based on (or derived from) the Work and for which the
+      editorial revisions, annotations, elaborations, or other modifications
+      represent, as a whole, an original work of authorship. For the purposes
+      of this License, Derivative Works shall not include works that remain
+      separable from, or merely link (or bind by name) to the interfaces of,
+      the Work and Derivative Works thereof.
+
+      "Contribution" shall mean any work of authorship, including
+      the original version of the Work and any modifications or additions
+      to that Work or Derivative Works thereof, that is intentionally
+      submitted to Licensor for inclusion in the Work by the copyright owner
+      or by an individual or Legal Entity authorized to submit on behalf of
+      the copyright owner. For the purposes of this definition, "submitted"
+      means any form of electronic, verbal, or written communication sent
+      to the Licensor or its representatives, including but not limited to
+      communication on electronic mailing lists, source code control systems,
+      and issue tracking systems that are managed by, or on behalf of, the
+      Licensor for the purpose of discussing and improving the Work, but
+      excluding communication that is conspicuously marked or otherwise
+      designated in writing by the copyright owner as "Not a Contribution."
+
+      "Contributor" shall mean Licensor and any individual or Legal Entity
+      on behalf of whom a Contribution has been received by Licensor and
+      subsequently incorporated within the Work.
+
+   2. Grant of Copyright License. Subject to the terms and conditions of
+      this License, each Contributor hereby grants to You a perpetual,
+      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+      copyright license to reproduce, prepare Derivative Works of,
+      publicly display, publicly perform, sublicense, and distribute the
+      Work and such Derivative Works in Source or Object form.
+
+   3. Grant of Patent License. Subject to the terms and conditions of
+      this License, each Contributor hereby grants to You a perpetual,
+      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+      (except as stated in this section) patent license to make, have made,
+      use, offer to sell, sell, import, and otherwise transfer the Work,
+      where such license applies only to those patent claims licensable
+      by such Contributor that are necessarily infringed by their
+      Contribution(s) alone or by combination of their Contribution(s)
+      with the Work to which such Contribution(s) was submitted. If You
+      institute patent litigation against any entity (including a
+      cross-claim or counterclaim in a lawsuit) alleging that the Work
+      or a Contribution incorporated within the Work constitutes direct
+      or contributory patent infringement, then any patent licenses
+      granted to You under this License for that Work shall terminate
+      as of the date such litigation is filed.
+
+   4. Redistribution. You may reproduce and distribute copies of the
+      Work or Derivative Works thereof in any medium, with or without
+      modifications, and in Source or Object form, provided that You
+      meet the following conditions:
+
+      (a) You must give any other recipients of the Work or
+          Derivative Works a copy of this License; and
+
+      (b) You must cause any modified files to carry prominent notices
+          stating that You changed the files; and
+
+      (c) You must retain, in the Source form of any Derivative Works
+          that You distribute, all copyright, patent, trademark, and
+          attribution notices from the Source form of the Work,
+          excluding those notices that do not pertain to any part of
+          the Derivative Works; and
+
+      (d) If the Work includes a "NOTICE" text file as part of its
+          distribution, then any Derivative Works that You distribute must
+          include a readable copy of the attribution notices contained
+          within such NOTICE file, excluding those notices that do not
+          pertain to any part of the Derivative Works, in at least one
+          of the following places: within a NOTICE text file distributed
+          as part of the Derivative Works; within the Source form or
+          documentation, if provided along with the Derivative Works; or,
+          within a display generated by the Derivative Works, if and
+          wherever such third-party notices normally appear. The contents
+          of the NOTICE file are for informational purposes only and
+          do not modify the License. You may add Your own attribution
+          notices within Derivative Works that You distribute, alongside
+          or as an addendum to the NOTICE text from the Work, provided
+          that such additional attribution notices cannot be construed
+          as modifying the License.
+
+      You may add Your own copyright statement to Your modifications and
+      may provide additional or different license terms and conditions
+      for use, reproduction, or distribution of Your modifications, or
+      for any such Derivative Works as a whole, provided Your use,
+      reproduction, and distribution of the Work otherwise complies with
+      the conditions stated in this License.
+
+   5. Submission of Contributions. Unless You explicitly state otherwise,
+      any Contribution intentionally submitted for inclusion in the Work
+      by You to the Licensor shall be under the terms and conditions of
+      this License, without any additional terms or conditions.
+      Notwithstanding the above, nothing herein shall supersede or modify
+      the terms of any separate license agreement you may have executed
+      with Licensor regarding such Contributions.
+
+   6. Trademarks. This License does not grant permission to use the trade
+      names, trademarks, service marks, or product names of the Licensor,
+      except as required for reasonable and customary use in describing the
+      origin of the Work and reproducing the content of the NOTICE file.
+
+   7. Disclaimer of Warranty. Unless required by applicable law or
+      agreed to in writing, Licensor provides the Work (and each
+      Contributor provides its Contributions) on an "AS IS" BASIS,
+      WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+      implied, including, without limitation, any warranties or conditions
+      of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
+      PARTICULAR PURPOSE. You are solely responsible for determining the
+      appropriateness of using or redistributing the Work and assume any
+      risks associated with Your exercise of permissions under this License.
+
+   8. Limitation of Liability. In no event and under no legal theory,
+      whether in tort (including negligence), contract, or otherwise,
+      unless required by applicable law (such as deliberate and grossly
+      negligent acts) or agreed to in writing, shall any Contributor be
+      liable to You for damages, including any direct, indirect, special,
+      incidental, or consequential damages of any character arising as a
+      result of this License or out of the use or inability to use the
+      Work (including but not limited to damages for loss of goodwill,
+      work stoppage, computer failure or malfunction, or any and all
+      other commercial damages or losses), even if such Contributor
+      has been advised of the possibility of such damages.
+
+   9. Accepting Warranty or Additional Liability. While redistributing
+      the Work or Derivative Works thereof, You may choose to offer,
+      and charge a fee for, acceptance of support, warranty, indemnity,
+      or other liability obligations and/or rights consistent with this
+      License. However, in accepting such obligations, You may act only
+      on Your own behalf and on Your sole responsibility, not on behalf
+      of any other Contributor, and only if You agree to indemnify,
+      defend, and hold each Contributor harmless for any liability
+      incurred by, or claims asserted against, such Contributor by reason
+      of your accepting any such warranty or additional liability.
+
+   END OF TERMS AND CONDITIONS
+
+   APPENDIX: How to apply the Apache License to your work.
+
+      To apply the Apache License to your work, attach the following
+      boilerplate notice, with the fields enclosed by brackets "{}"
+      replaced with your own identifying information. (Don't include
+      the brackets!)  The text should be enclosed in the appropriate
+      comment syntax for the file format. We also recommend that a
+      file or class name and description of purpose be included on the
+      same "printed page" as the copyright notice for easier
+      identification within third-party archives.
+
+   Copyright {yyyy} {name of copyright owner}
+
+   Licensed 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.
+
diff --git a/README.md b/README.md
new file mode 100644
index 0000000..55e6ea0
--- /dev/null
+++ b/README.md
@@ -0,0 +1,77 @@
+# Elastic-Job - distributed scheduled job solution
+
+[![Build Status](https://secure.travis-ci.org/elasticjob/elastic-job.png?branch=master)](https://travis-ci.org/elasticjob/elastic-job)
+[![Maven Status](https://maven-badges.herokuapp.com/maven-central/com.dangdang/elastic-job/badge.svg)](https://maven-badges.herokuapp.com/maven-central/com.dangdang/elastic-job)
+[![Coverage Status](https://coveralls.io/repos/elasticjob/elastic-job/badge.svg?branch=master&service=github)](https://coveralls.io/github/elasticjob/elastic-job?branch=master)
+[![GitHub release](https://img.shields.io/github/release/elasticjob/elastic-job.svg)](https://github.com/elasticjob/elastic-job/releases)
+[![License](https://img.shields.io/badge/license-Apache%202-4EB1BA.svg)](https://www.apache.org/licenses/LICENSE-2.0.html)
+
+# [Homepage](http://elasticjob.io/)
+
+# [中文主页](http://elasticjob.io/index_zh.html)
+
+# Elastic-Job-Cloud Framework[![GitHub release](https://img.shields.io/badge/release-download-orange.svg)](https://elasticjob.io/dist/elastic-job-cloud-scheduler-2.1.5.tar.gz)
+
+# Overview
+
+Elastic-Job is a distributed scheduled job solution. Elastic-Job is composited from 2 independent sub projects: Elastic-Job-Lite and Elastic-Job-Cloud.
+
+Elastic-Job-Cloud is a Mesos framework which use Mesos + Docker(todo) to manage and isolate resources and processes.
+
+Elastic-Job-Lite and Elastic-Job-Cloud provide unified API. Developers only need code one time, then decide to deploy Lite or Cloud as you want.
+
+# Features
+
+* Distributed schedule job coordinate
+* Elastic scale in and scale out supported
+* Failover
+* Misfired jobs refire
+* Sharding consistently, same sharding item for a job only one running instance
+* Self diagnose and recover when distribute environment unstable
+* Parallel scheduling supported
+* Job lifecycle operation
+* Lavish job types
+* Spring integrated and namespace supported
+* Web console
+* Application distributed automatically
+* Fenzo based resources allocated elastically
+* Docker based processes isolation support (TBD)
+
+# Architecture
+
+## Elastic-Job-Cloud
+
+![Elastic-Job-Cloud Architecture](http://ovfotjrsi.bkt.clouddn.com/docs/img/architecture/elastic_job_cloud.png)
+
+# [Release Notes](https://github.com/elasticjob/elastic-job/releases)
+
+# [Roadmap](ROADMAP.md)
+
+# Quick Start
+
+## Add maven dependency
+
+```xml
+<!-- import elastic-job cloud executor -->
+<dependency>
+    <groupId>io.elasticjob</groupId>
+    <artifactId>elastic-job-cloud-executor</artifactId>
+    <version>${lasted.release.version}</version>
+</dependency>
+```
+
+## Job development
+
+Same with `Elastic-Job-Lite`
+
+## Job App configuration
+
+```shell
+curl -l -H "Content-type: application/json" -X POST -d '{"appName":"yourAppName","appURL":"http://app_host:8080/foo-job.tar.gz","cpuCount":0.1,"memoryMB":64.0,"bootstrapScript":"bin/start.sh","appCacheEnable":true}' http://elastic_job_cloud_host:8899/api/app
+```
+
+## Job configuration
+
+```shell
+curl -l -H "Content-type: application/json" -X POST -d '{"jobName":"foo_job","appName":"yourAppName","jobClass":"yourJobClass","jobType":"SIMPLE","jobExecutionType":"TRANSIENT","cron":"0/5 * * * * ?","shardingTotalCount":5,"cpuCount":0.1,"memoryMB":64.0,"failover":true,"misfire":true,"bootstrapScript":"bin/start.sh"}' http://elastic_job_cloud_host:8899/api/job/register
+```
diff --git a/README_ZH.md b/README_ZH.md
new file mode 100644
index 0000000..ee4458e
--- /dev/null
+++ b/README_ZH.md
@@ -0,0 +1,91 @@
+# Elastic-Job - 分布式作业调度解决方案
+
+[![Build Status](https://secure.travis-ci.org/elasticjob/elastic-job.png?branch=master)](https://travis-ci.org/elasticjob/elastic-job)
+[![Maven Status](https://maven-badges.herokuapp.com/maven-central/com.dangdang/elastic-job/badge.svg)](https://maven-badges.herokuapp.com/maven-central/com.dangdang/elastic-job)
+[![Coverage Status](https://coveralls.io/repos/elasticjob/elastic-job/badge.svg?branch=master&service=github)](https://coveralls.io/github/elasticjob/elastic-job?branch=master)
+[![GitHub release](https://img.shields.io/github/release/elasticjob/elastic-job.svg)](https://github.com/elasticjob/elastic-job/releases)
+[![License](https://img.shields.io/badge/license-Apache%202-4EB1BA.svg)](https://www.apache.org/licenses/LICENSE-2.0.html)
+
+# 概述
+
+Elastic-Job是一个分布式调度解决方案,由两个相互独立的子项目Elastic-Job-Lite和Elastic-Job-Cloud组成。
+
+Elastic-Job-Cloud使用Mesos + Docker的解决方案,额外提供资源治理、应用分发以及进程隔离等服务。
+
+# 功能列表
+
+* 应用自动分发
+* 基于Fenzo的弹性资源分配
+* 分布式调度协调
+* 弹性扩容缩容
+* 失效转移
+* 错过执行作业重触发
+* 作业分片一致性,保证同一分片在分布式环境中仅一个执行实例
+* 支持并行调度
+* 支持作业生命周期操作
+* 丰富的作业类型
+* Spring整合
+* 运维平台
+* 基于Docker的进程隔离(TBD)
+
+# 架构图
+
+## Elastic-Job-Cloud
+
+![Elastic-Job-Cloud Architecture](http://ovfotjrsi.bkt.clouddn.com/docs/img/architecture/elastic_job_cloud.png)
+
+
+# [Release Notes](https://github.com/elasticjob/elastic-job/releases)
+
+# [Roadmap](ROADMAP.md)
+
+# 快速入门
+
+## 引入maven依赖
+
+```xml
+<!-- 引入elastic-job-cloud执行器模块 -->
+<dependency>
+    <groupId>io.elasticjob</groupId>
+    <artifactId>elastic-job-cloud-executor</artifactId>
+    <version>${latest.release.version}</version>
+</dependency>
+```
+
+## 作业开发
+
+```java
+public class MyElasticJob implements SimpleJob {
+    
+    @Override
+    public void execute(ShardingContext context) {
+        switch (context.getShardingItem()) {
+            case 0: 
+                // do something by sharding item 0
+                break;
+            case 1: 
+                // do something by sharding item 1
+                break;
+            case 2: 
+                // do something by sharding item 2
+                break;
+            // case n: ...
+        }
+    }
+}
+```
+
+## 打包作业
+tar -cvf yourJobs.tar.gz yourJobs
+
+## 发布APP
+
+```shell
+curl -l -H "Content-type: application/json" -X POST -d '{"appName":"foo_app","appURL":"http://app_host:8080/yourJobs.gz","cpuCount":0.1,"memoryMB":64.0,"bootstrapScript":"bin/start.sh","appCacheEnable":true,"eventTraceSamplingCount":0}' http://elastic_job_cloud_host:8899/api/app
+```
+
+## 发布作业
+
+```shell
+curl -l -H "Content-type: application/json" -X POST -d '{"jobName":"foo_job","jobClass":"yourJobClass","jobType":"SIMPLE","jobExecutionType":"TRANSIENT","cron":"0/5 * * * * ?","shardingTotalCount":5,"cpuCount":0.1,"memoryMB":64.0,"appName":"foo_app","failover":true,"misfire":true,"bootstrapScript":"bin/start.sh"}' http://elastic_job_cloud_host:8899/api/job/register
+```
diff --git a/RELEASE-NOTES.md b/RELEASE-NOTES.md
new file mode 100644
index 0000000..9b6beeb
--- /dev/null
+++ b/RELEASE-NOTES.md
@@ -0,0 +1,351 @@
+## 3.0.0.M1
+
+### 缺陷修正
+
+1. [ISSUE #384](https://github.com/elasticjob/elastic-job/issues/384) Cloud的执行器线程ContextClassLoader为空
+
+
+## 2.1.5
+
+### 新功能
+
+1. [ISSUE #373](https://github.com/elasticjob/elastic-job/issues/373) Cloud可区分处理TASK_UNREACHABLE, TASK_UNKNOW, TASK_DROPPED, TASK_GONE等状态
+
+### 缺陷修正
+
+1. [ISSUE #367](https://github.com/elasticjob/elastic-job/issues/367) Cloud禁用作业并未停止生成Ready队列,造成重新启用后堆积作业大量执行
+1. [ISSUE #382](https://github.com/elasticjob/elastic-job/issues/382) 界面验证错误,不应校验分片总数上限
+1. [ISSUE #383](https://github.com/elasticjob/elastic-job/issues/383) 界面验证错误,不应校验监听端口下限
+
+## 2.1.4
+
+### 功能提升
+
+1. [ISSUE #29](https://github.com/elasticjob/elastic-job/issues/29) 英文版的job-console
+1. [ISSUE #352](https://github.com/elasticjob/elastic-job/issues/352) elastic-job-cloud-executor本地运行模式
+
+### 缺陷修正
+
+1. [ISSUE #322](https://github.com/elasticjob/elastic-job/issues/322) elastic-job-cloud-scheduler调度任务评估资源时考虑对executor的资源使用情况
+1. [ISSUE #341](https://github.com/elasticjob/elastic-job/issues/341) elastic-job-cloud-console中script作业配置缺少执行脚本
+1. [ISSUE #343](https://github.com/elasticjob/elastic-job/issues/343) elastic-job-cloud-console中Script类型作业执行脚本不正确
+1. [ISSUE #345](https://github.com/elasticjob/elastic-job/issues/345) elastic-job-lite-console任务全部禁用时状态显示不正确
+1. [ISSUE #351](https://github.com/elasticjob/elastic-job/issues/351) elastic-job-lite-console管理后台添加注册中心,登录凭证栏无法输入‘:‘
+
+## 2.1.3
+
+### 功能提升
+
+1. [ISSUE #327](https://github.com/elasticjob/elastic-job/issues/327) spring命名空间支持使用xml方式配置bean
+1. [ISSUE #336](https://github.com/elasticjob/elastic-job/issues/336) Cloud作业提交失败返回错误详细信息到framework
+
+### 缺陷修正
+
+1. [ISSUE #321](https://github.com/elasticjob/elastic-job/issues/321) elastic-job-lite界面在添加注册中心时命名空间不支持/
+1. [ISSUE #333](https://github.com/elasticjob/elastic-job/issues/333) elastic-job-lite界面中注册中心配置中登录凭证隐式显示
+1. [ISSUE #334](https://github.com/elasticjob/elastic-job/issues/334) elastic-job-lite界面在windows平台上找不到conf\auth.properties文件
+1. [ISSUE #335](https://github.com/elasticjob/elastic-job/issues/335) elastic-job-lite界面guest账户在conf\auth.properties文件中配置不起作用
+
+## 2.1.2
+
+### 新功能
+
+1. [ISSUE #301](https://github.com/elasticjob/elastic-job/issues/301) Console增加Guest权限配置,guest只允许查看,不允许更改
+1. [ISSUE #312](https://github.com/elasticjob/elastic-job/issues/312) Cloud支持自修复功能
+
+### 功能提升
+
+1. [ISSUE #293](https://github.com/elasticjob/elastic-job/issues/293) Lite Console数据源配置增加连接测试功能
+1. [ISSUE #296](https://github.com/elasticjob/elastic-job/issues/296) Cloud运维界面重构,与lite风格一致
+1. [ISSUE #302](https://github.com/elasticjob/elastic-job/issues/302) 失效转移与作业运行状态监听分离
+1. [ISSUE #304](https://github.com/elasticjob/elastic-job/issues/304) Cloud增加与Mesos角色关联功能
+1. [ISSUE #316](https://github.com/elasticjob/elastic-job/issues/316) Lite中运行中任务关联进程ID
+
+### 缺陷修正
+
+1. [ISSUE #291](https://github.com/elasticjob/elastic-job/issues/291) elastic-job控制台信息失败原因信息展示不全
+1. [ISSUE #306](https://github.com/elasticjob/elastic-job/issues/306) 切换是否监控作业执行状态且作业间隔时间短时可能发生作业无法继续运行
+1. [ISSUE #310](https://github.com/elasticjob/elastic-job/issues/310) 配置检查本机与注册中心的时间误差秒后,创建过多顺序节点
+
+## 2.1.1
+
+### 新功能
+
+1. [ISSUE #242](https://github.com/elasticjob/elastic-job/issues/242) Elastic-Job-Cloud支持删除应用及作业功能
+1. [ISSUE #243](https://github.com/elasticjob/elastic-job/issues/243) Elastic-Job-Cloud支持禁用/启用应用及作业功能
+
+### 功能提升
+
+1. [ISSUE #268](https://github.com/elasticjob/elastic-job/issues/268) 精简POM依赖
+
+### 缺陷修正
+
+1. [ISSUE #266](https://github.com/elasticjob/elastic-job/issues/266) Elastic-Job-Lite启动脚本指定端口无效
+1. [ISSUE #269](https://github.com/elasticjob/elastic-job/issues/269) EventTrace失败记录不受采样率影响并且记录失败时间
+1. [ISSUE #270](https://github.com/elasticjob/elastic-job/issues/270) 控制台点击按钮后发起两次请求
+1. [ISSUE #272](https://github.com/elasticjob/elastic-job/issues/272) Elastic-Job-Lite界面作业维度,只有全部服务器被禁用时,才应显示为被禁用
+1. [ISSUE #275](https://github.com/elasticjob/elastic-job/issues/275) 停掉Zookeeper后,再重启Zookeeper,任务不会继续执行
+1. [ISSUE #276](https://github.com/elasticjob/elastic-job/issues/276) 开启失效转移且分片任务执行后,任务会重复执行
+1. [ISSUE #279](https://github.com/elasticjob/elastic-job/issues/279) 添加事件追踪数据源,数据库连接地址不能带参数
+1. [ISSUE #280](https://github.com/elasticjob/elastic-job/issues/280) 作业历史页面的历史状态显示不正确
+1. [ISSUE #283](https://github.com/elasticjob/elastic-job/issues/283) 作业不设置overwrite且本地配置与注册中心不一致时,作业启动的cron应以注册中心为准
+1. [ISSUE #290](https://github.com/elasticjob/elastic-job/issues/290) Elastic-Job-Cloud删除被禁用的APP或JOB时,对应的disabled节点数据无法删除
+
+## 2.1.0
+
+### 新功能
+
+1. [ISSUE #195](https://github.com/elasticjob/elastic-job/issues/195) Elastic-Job-Lite自诊断并修复分布式不稳定造成的问题
+1. [ISSUE #248](https://github.com/elasticjob/elastic-job/issues/248) Elastic-Job-Lite同一台作业服务器可以行多个相同作业名称的JVM实例(Cloud Native)
+1. [ISSUE #249](https://github.com/elasticjob/elastic-job/issues/249) Elastic-Job-Lite运维界面支持事件追踪查询
+
+### 功能提升
+
+1. [ISSUE #240](https://github.com/elasticjob/elastic-job/issues/240) Elastic-Job-Lite运维界面重构
+1. [ISSUE #262](https://github.com/elasticjob/elastic-job/issues/262) Elastic-Job-Lite控制台删除作业配置
+
+### 缺陷修正
+
+1. [ISSUE #237](https://github.com/elasticjob/elastic-job/issues/238) 增加REST API对分片总数不小于1的校验
+1. [ISSUE #238](https://github.com/elasticjob/elastic-job/issues/238) IP正则表达式错误
+1. [ISSUE #246](https://github.com/elasticjob/elastic-job/issues/246) 通过JobOperateAPI.remove()后,再JobScheduler.init()创建相同作业后多次触发执行
+1. [ISSUE #250](https://github.com/elasticjob/elastic-job/issues/250) Misfire任务多触发一次
+
+### 结构调整
+
+1. [ISSUE #263](https://github.com/elasticjob/elastic-job/issues/263) Elastic-Job-Lite作业操作API重新梳理
+1. [ISSUE #264](https://github.com/elasticjob/elastic-job/issues/264) Elastic-Job-Lite数据存储结构调整,但向前兼容
+
+## 2.0.5
+
+### 缺陷修正
+
+1. [ISSUE 222](https://github.com/elasticjob/elastic-job/issues/222) elastic-job-lite-spring的reg配置参数max-retries不起作用
+1. [ISSUE 231](https://github.com/elasticjob/elastic-job/issues/231) 批量删除cloud作业时,mesos会提前同步TASK_LOST消息给framework,导致作业被重新放入ready队列并执行
+
+### 新功能
+
+1. [ISSUE #191](https://github.com/elasticjob/elastic-job/issues/191) Framework的HA功能
+1. [ISSUE #217](https://github.com/elasticjob/elastic-job/issues/217) cloud版本增加APP维度配置
+1. [ISSUE #223](https://github.com/elasticjob/elastic-job/issues/223) cloud版本常驻作业事件追踪采样率
+
+## 2.0.4
+
+### 缺陷修正
+
+1. [ISSUE #189](https://github.com/elasticjob/elastic-job/issues/189) 管理后台执行失效操作,但任务还在执行
+1. [ISSUE #204](https://github.com/elasticjob/elastic-job/issues/204) 异步事件执行消息顺序不一致导致数据库数据不准确
+1. [ISSUE #209](https://github.com/elasticjob/elastic-job/issues/209) cloud作业资源分配算法改进
+
+### 新功能
+
+1. [ISSUE #203](https://github.com/elasticjob/elastic-job/issues/203) cloud类型作业增加运行统计,并提供REST API查询
+1. [ISSUE #215](https://github.com/elasticjob/elastic-job/issues/215) cloud版本运维管理界面
+
+### 功能提升
+
+1. [ISSUE #187](https://github.com/elasticjob/elastic-job/issues/187) ShardingContext中增加taskId属性,供业务方使用 
+
+## 2.0.3
+
+### 缺陷修正
+
+1. [ISSUE #177](https://github.com/elasticjob/elastic-job/issues/177) 2.0.2版本Spring命名空间的job:script空指针
+1. [ISSUE #185](https://github.com/elasticjob/elastic-job/issues/185) Executor多占用分片资源导致资源浪费问题
+
+### 新功能
+
+1. [ISSUE #178](https://github.com/elasticjob/elastic-job/issues/178) 事件驱动触发作业
+
+### 功能提升
+
+1. [ISSUE #179](https://github.com/elasticjob/elastic-job/issues/179) Transient的Script类型作业优化,无需Java的Executor支持
+1. [ISSUE #182](https://github.com/elasticjob/elastic-job/issues/182) 增加对spring boot的支持
+
+### 结构调整
+
+1. [ISSUE #184](https://github.com/elasticjob/elastic-job/issues/184) ExecutorServiceHandler接口方法调整,增加jobName区分用来区分不同作业线程名
+1. [ISSUE #186](https://github.com/elasticjob/elastic-job/issues/186) 去除Spring命名空间DTO相关代码,简化SpringJobScheduler使用
+
+## 2.0.2
+
+### 缺陷修正
+
+1. [ISSUE #64](https://github.com/elasticjob/elastic-job/issues/64) Spring命名空间,若注册多个同Class的作业Bean,会导致作业Bean查找不准确
+1. [ISSUE #115](https://github.com/elasticjob/elastic-job/issues/115) console新增注册中心,没有连接成功,后台一直反复连接并报错
+1. [ISSUE #151](https://github.com/elasticjob/elastic-job/issues/151) 基于关系型数据库的事件追踪缺乏对MySQL之外数据库的支持
+1. [ISSUE #152](https://github.com/elasticjob/elastic-job/issues/152) job自定义异常处理器无效,总是被DefaultJobExceptionHandler处理
+1. [ISSUE #156](https://github.com/elasticjob/elastic-job/issues/156) 作业事件追踪整体调用链路数据采集
+1. [ISSUE #158](https://github.com/elasticjob/elastic-job/issues/158) 作业在暂停时错过分片时机将不会再分片
+1. [ISSUE #161](https://github.com/elasticjob/elastic-job/issues/161) Lite版本部署至某些版本的Tomcat无法启动
+1. [ISSUE #163](https://github.com/elasticjob/elastic-job/issues/163) 任务设置disable=true后,启动项目还是会自动执行任务
+1. [ISSUE #165](https://github.com/elasticjob/elastic-job/issues/165) 所有服务节点都disable时会导致分片线程死锁
+1. [ISSUE #167](https://github.com/elasticjob/elastic-job/issues/167) Failover作业增加源执行任务ID记录
+
+### 功能提升
+
+1. [ISSUE #159](https://github.com/elasticjob/elastic-job/issues/159) 提供从Spring 3.1.0.REELASE至Spring 4任何版本的支持
+1. [ISSUE #164](https://github.com/elasticjob/elastic-job/issues/164) 作业Spring命名空间中已声明的JobBean不需要再声明@Component或在Spring xml中定义
+
+### 结构调整
+
+1. [ISSUE #153](https://github.com/elasticjob/elastic-job/issues/153) 事件追踪配置集中化
+1. [ISSUE #160](https://github.com/elasticjob/elastic-job/issues/160) 调整maven模块结构,提供elastic-job-common及其二级模块,原elastic-job-core模块迁移至elastic-job-common-core
+
+## 2.0.1
+
+### 缺陷修正
+
+1. [ISSUE #141](https://github.com/elasticjob/elastic-job/issues/141) 删除reg模块从zk读取信息功能,使reg命名空间的placeholder完全可用
+1. [ISSUE #143](https://github.com/elasticjob/elastic-job/issues/143) elastic-job-cloud-scheduler内存泄漏问题
+1. [ISSUE #145](https://github.com/elasticjob/elastic-job/issues/145) 修改作业日志的数据库连接后日志还是会写入老的数据库
+1. [ISSUE #146](https://github.com/elasticjob/elastic-job/issues/146) 作业的线程池复用问题
+1. [ISSUE #147](https://github.com/elasticjob/elastic-job/issues/147) console作业维度加载不出来 后台有报空指针错误
+1. [ISSUE #149](https://github.com/elasticjob/elastic-job/issues/149) 运维平台删除作业,偶尔会遇到删除不全的情况
+1. [ISSUE #150](https://github.com/elasticjob/elastic-job/issues/150) Cloud的misfire功能在作业堆积时将会一直执行
+
+## 2.0.0
+
+### 新功能
+
+1. Elastic-Job-Cloud初始版本
+1. 重构原Elastic-Job至Elastic-Job-Lite
+
+### 缺陷修正
+
+1. [ISSUE #119](https://github.com/elasticjob/elastic-job/issues/119) spring容器关闭时,quartz未正常关闭 
+1. [ISSUE #123](https://github.com/elasticjob/elastic-job/issues/123) 单机跑定时任务,zk断开后重连,没有触发leader选举 
+1. [ISSUE #127](https://github.com/elasticjob/elastic-job/issues/127) Spring方式配置作业id无法使用占位符
+
+## 1.1.1
+
+### 结构调整
+
+1. [ISSUE #116](https://github.com/elasticjob/elastic-job/issues/116) 作业接口的handleJobExecutionException参数变更
+
+### 功能提升
+
+1. [ISSUE #110](https://github.com/elasticjob/elastic-job/issues/110) 手动触发作业
+
+### 缺陷修正
+1. [ISSUE #99](https://github.com/elasticjob/elastic-job/issues/99) 删除作业异步导致作业删除后, 还未结束的作业继续创建zk数据
+
+## 1.1.0
+
+### 结构调整
+
+1. [ISSUE #97](https://github.com/elasticjob/elastic-job/issues/97) JobConfiguration重构为SimpleJobConfiguration,DataflowJobConfiguration,ScriptJobConfiguration
+1. [ISSUE #102](https://github.com/elasticjob/elastic-job/issues/102) 重新定义Java/Spring Config API,使用Factory+Builder模式代替原有的Constructor+Setter模式
+1. [ISSUE #104](https://github.com/elasticjob/elastic-job/issues/104) 移除@Deprecated代码
+1. [ISSUE #105](https://github.com/elasticjob/elastic-job/issues/105) 重构Spring命名空间驼峰式定义
+1. [ISSUE #106](https://github.com/elasticjob/elastic-job/issues/106) isStreaming配置化
+1. [ISSUE #107](https://github.com/elasticjob/elastic-job/issues/107) reg-center更名为registry-center-ref
+
+## 1.0.8
+
+### 新功能
+
+1. [ISSUE #95](https://github.com/elasticjob/elastic-job/issues/95) 增加脚本类型作业支持
+
+## 1.0.7
+
+### 结构调整
+
+1. [ISSUE #88](https://github.com/elasticjob/elastic-job/issues/88) stop作业改名为pause
+
+### 新功能
+
+1. [ISSUE #91](https://github.com/elasticjob/elastic-job/issues/91) 作业生命周期操作API
+
+### 功能提升
+
+1. [ISSUE #84](https://github.com/elasticjob/elastic-job/issues/84) 控制台提供作业启用/禁用按钮操作
+1. [ISSUE #87](https://github.com/elasticjob/elastic-job/issues/87) 调整主节点选举流程,作业关闭,禁用和暂停将触发主节点选举
+1. [ISSUE #93](https://github.com/elasticjob/elastic-job/issues/93) 注册中心配置提供baseSleepTimeMilliseconds、maxSleepTimeMilliseconds和maxRetries的默认值
+
+### 缺陷修正
+1. [ISSUE #92](https://github.com/elasticjob/elastic-job/issues/92) 修改分片总数参数导致仅单一节点执行的监听抛出超时异常
+
+## 1.0.6
+
+### 功能提升
+
+1. [ISSUE #71](https://github.com/elasticjob/elastic-job/issues/71) 作业关闭功能(shutdown)
+1. [ISSUE #72](https://github.com/elasticjob/elastic-job/issues/72) 关闭的作业可删除
+1. [ISSUE #81](https://github.com/elasticjob/elastic-job/issues/81) 使用集中清理作业上次结束状态代替各自清理,各自清理可能导致作业机下线而产生未清理的结束状态
+
+### 缺陷修正
+
+1. [ISSUE #74](https://github.com/elasticjob/elastic-job/issues/74) 流式处理且失效转移时,失效转移的分片项不能执行一次即停止
+1. [ISSUE #77](https://github.com/elasticjob/elastic-job/issues/77) dataflow类型作业,fetchData如果有数据,则应与processData成对执行
+1. [ISSUE #78](https://github.com/elasticjob/elastic-job/issues/78) Spring方式配置作业监听启用AOP导致不能正常使用问题
+
+## 1.0.5
+
+### 功能提升
+
+1. [ISSUE #2](https://github.com/elasticjob/elastic-job/issues/2) 增加前置和后置任务
+1. [ISSUE #60](https://github.com/elasticjob/elastic-job/issues/60) 可于Dataflow类型作业定制化线程池配置
+1. [ISSUE #62](https://github.com/elasticjob/elastic-job/issues/61) 作业状态清理提速
+1. [ISSUE #65](https://github.com/elasticjob/elastic-job/issues/65) 增加前置和后置任务Spring命名空间支持
+
+### 缺陷修正
+
+1. [ISSUE #61](https://github.com/elasticjob/elastic-job/issues/61) 分片和主节点选举同时发生时,死锁问题解决
+1. [ISSUE #63](https://github.com/elasticjob/elastic-job/issues/63) 获取作业TreeCache时可能会获取到前缀相同的其他作业的TreeCache
+1. [ISSUE #69](https://github.com/elasticjob/elastic-job/issues/69) 分片时如在Zk中有的作业服务器sharding节点不存在将导致无法重新分片
+
+### 结构调整
+
+1. [ISSUE #59](https://github.com/elasticjob/elastic-job/issues/59) 将elastic-job依赖的curator从2.8.0升级至2.10.0
+
+## 1.0.4
+
+### 功能提升
+1. [ISSUE #16](https://github.com/elasticjob/elastic-job/issues/16) 提供内嵌zookeeper,简化开发环境
+1. [ISSUE #28](https://github.com/elasticjob/elastic-job/issues/28) Dataflow类型作业增加processData批量处理数据的方法
+1. [ISSUE #56](https://github.com/elasticjob/elastic-job/issues/56) 作业自定义参数设置
+
+### 结构调整
+
+1. [ISSUE #57](https://github.com/elasticjob/elastic-job/issues/57) 精简模块,移除elastic-job-test模块
+1. [ISSUE #58](https://github.com/elasticjob/elastic-job/issues/58) 增加批量处理功能导致的作业类型接口变更
+
+## 1.0.3
+
+### 功能提升
+
+1. [ISSUE #39](https://github.com/elasticjob/elastic-job/issues/39) 增加作业辅助监听功能,通过dump命令抓取作业运行时信息
+1. [ISSUE #43](https://github.com/elasticjob/elastic-job/issues/43) 增加作业异常处理回调接口
+
+### 缺陷修正
+
+1. [ISSUE #30](https://github.com/elasticjob/elastic-job/issues/30) 注册中心宕机较长时间后重新恢复,作业无法继续执行
+1. [ISSUE #36](https://github.com/elasticjob/elastic-job/issues/36) 任务在控制台暂停之后,无法恢复运行
+1. [ISSUE #40](https://github.com/elasticjob/elastic-job/issues/40) TreeCache使用粒度过粗导致内存溢出
+
+## 1.0.2
+
+### 功能提升
+
+1. [ISSUE #6](https://github.com/elasticjob/elastic-job/issues/6) 校对作业服务器与注册中心时间误差
+1. [ISSUE #8](https://github.com/elasticjob/elastic-job/issues/8) 增加misfire开关,默认开启错过任务重新执行
+1. [ISSUE #9](https://github.com/elasticjob/elastic-job/issues/9) 分片策略可配置化
+1. [ISSUE #10](https://github.com/elasticjob/elastic-job/issues/10) 提供根据作业名称hash值取奇偶数分片排序策略
+1. [ISSUE #14](https://github.com/elasticjob/elastic-job/issues/14) 控制台修改cron表达式后,任务将实时更新cron
+1. [ISSUE #20](https://github.com/elasticjob/elastic-job/issues/20) 运维界面任务列表显示增加cron表达式
+1. [ISSUE #54](https://github.com/elasticjob/elastic-job/issues/54) SequencePerpetual类型作业性能提升,将抓取数据改为多线程,之前仅处理数据为多线程
+1. [ISSUE #55](https://github.com/elasticjob/elastic-job/issues/55) offset存储功能
+
+### 缺陷修正
+
+1. [ISSUE #1](https://github.com/elasticjob/elastic-job/issues/1) 复杂网络环境下IP地址获取不准确的问题
+1. [ISSUE #13](https://github.com/elasticjob/elastic-job/issues/13) 作业抛出运行时异常后,后续不会继续触发
+1. [ISSUE #53](https://github.com/elasticjob/elastic-job/issues/53) Dataflow的Sequence类型作业采用多线程抓取数据
+
+### 结构调整
+
+1. [ISSUE #17](https://github.com/elasticjob/elastic-job/issues/17) 作业类型接口变更
+
+## 1.0.1
+1. 初始版本
diff --git a/ROADMAP.md b/ROADMAP.md
new file mode 100644
index 0000000..0724ab7
--- /dev/null
+++ b/ROADMAP.md
@@ -0,0 +1,61 @@
+# Roadmap
+
+- [x] Unified Job Config API
+    - [x] Core Config
+    - [x] Type Config
+    - [x] Root Config
+- [x] Job Types
+    - [x] Simple
+    - [x] Dataflow
+    - [x] Script
+    - [ ] Http
+- [x] Event Trace
+    - [x] Event Publisher
+    - [x] Database Event Listener
+    - [ ] Other Event Listener
+- [ ] Unified Schedule API
+- [ ] Unified Resource API
+- [x] Transient Job
+    - [x] High Availability
+    - [x] Elastic scale in/out
+    - [x] Failover
+    - [x] Misfire
+    - [x] Idempotency
+- [x] Daemon Job
+    - [x] High Availability
+    - [x] Elastic scale in/out
+    - [ ] Failover
+    - [ ] Misfire
+    - [x] Idempotency
+- [x] Mesos Scheduler
+    - [x] High Availability
+    - [x] Reconcile
+    - [ ] Redis Based Queue Improvement
+    - [ ] Http Driver
+- [x] Mesos Executor
+    - [x] Executor Reuse Pool
+    - [ ] Progress Reporting
+    - [ ] Health Detection
+    - [ ] Log Redirect
+- [x] Lifecycle Management
+    - [x] Job Add/Remove
+    - [ ] Job Pause/Resume
+    - [x] Job Disable/Enable
+    - [ ] Job Shutdown
+    - [x] App Add/Remove
+    - [x] App Disable/Enable
+    - [x] Restful API
+    - [x] Web Console
+- [ ] Job Dependency
+    - [ ] Listener
+    - [ ] Workflow
+    - [ ] DAG
+- [x] Job Distribution
+    - [x] Mesos Based Distribution
+    - [ ] Docker Based Distribution
+- [x] Resources Management
+    - [x] Resources Allocate
+    - [ ] Cross Data Center
+    - [ ] A/B Test
+- [x] Spring Integrate
+    - [x] Bean Injection
diff --git a/elastic-job-cloud-executor/pom.xml b/elastic-job-cloud-executor/pom.xml
new file mode 100644
index 0000000..079fca4
--- /dev/null
+++ b/elastic-job-cloud-executor/pom.xml
@@ -0,0 +1,80 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+    <parent>
+        <artifactId>elastic-job-cloud</artifactId>
+        <groupId>io.elasticjob</groupId>
+        <version>3.0.0.M1-SNAPSHOT</version>
+    </parent>
+    <modelVersion>4.0.0</modelVersion>
+    <artifactId>elastic-job-cloud-executor</artifactId>
+    <name>${project.artifactId}</name>
+    
+    <dependencies>
+        <dependency>
+            <groupId>io.elasticjob</groupId>
+            <artifactId>elastic-job-cloud-common</artifactId>
+            <version>${project.parent.version}</version>
+        </dependency>
+        
+        <dependency>
+            <groupId>org.apache.commons</groupId>
+            <artifactId>commons-lang3</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.apache.mesos</groupId>
+            <artifactId>mesos</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.springframework</groupId>
+            <artifactId>spring-context</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>commons-dbcp</groupId>
+            <artifactId>commons-dbcp</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>mysql</groupId>
+            <artifactId>mysql-connector-java</artifactId>
+        </dependency>
+        
+        <dependency>
+            <groupId>org.projectlombok</groupId>
+            <artifactId>lombok</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>junit</groupId>
+            <artifactId>junit</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.unitils</groupId>
+            <artifactId>unitils-core</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.mockito</groupId>
+            <artifactId>mockito-core</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.mockito</groupId>
+            <artifactId>mockito-inline</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>com.h2database</groupId>
+            <artifactId>h2</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.slf4j</groupId>
+            <artifactId>jcl-over-slf4j</artifactId>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.slf4j</groupId>
+            <artifactId>log4j-over-slf4j</artifactId>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>ch.qos.logback</groupId>
+            <artifactId>logback-classic</artifactId>
+            <scope>test</scope>
+        </dependency>
+    </dependencies>
+</project>
diff --git a/elastic-job-cloud-executor/src/main/java/io/elasticjob/cloud/api/JobBootstrap.java b/elastic-job-cloud-executor/src/main/java/io/elasticjob/cloud/api/JobBootstrap.java
new file mode 100644
index 0000000..a1ae34f
--- /dev/null
+++ b/elastic-job-cloud-executor/src/main/java/io/elasticjob/cloud/api/JobBootstrap.java
@@ -0,0 +1,44 @@
+/*
+ * Copyright 1999-2015 dangdang.com.
+ * <p>
+ * Licensed 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.
+ * </p>
+ */
+
+package io.elasticjob.cloud.api;
+
+import io.elasticjob.cloud.executor.TaskExecutor;
+import lombok.AccessLevel;
+import lombok.NoArgsConstructor;
+import org.apache.mesos.MesosExecutorDriver;
+import org.apache.mesos.Protos;
+
+/**
+ * 云作业启动器.
+ * 
+ * <p>需将应用打包, 并在main方法中直接调用Bootstrap.execute方法</p>
+ *
+ * @author caohao
+ * @author zhangliang
+ */
+@NoArgsConstructor(access = AccessLevel.PRIVATE)
+public final class JobBootstrap {
+    
+    /**
+     * 执行作业.
+     */
+    public static void execute() {
+        MesosExecutorDriver driver = new MesosExecutorDriver(new TaskExecutor());
+        System.exit(Protos.Status.DRIVER_STOPPED == driver.run() ? 0 : -1);
+    }
+}
diff --git a/elastic-job-cloud-executor/src/main/java/io/elasticjob/cloud/executor/CloudJobFacade.java b/elastic-job-cloud-executor/src/main/java/io/elasticjob/cloud/executor/CloudJobFacade.java
new file mode 100644
index 0000000..a9ff9de
--- /dev/null
+++ b/elastic-job-cloud-executor/src/main/java/io/elasticjob/cloud/executor/CloudJobFacade.java
@@ -0,0 +1,113 @@
+/*
+ * Copyright 1999-2015 dangdang.com.
+ * <p>
+ * Licensed 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.
+ * </p>
+ */
+
+package io.elasticjob.cloud.executor;
+
+import io.elasticjob.cloud.config.JobRootConfiguration;
+import io.elasticjob.cloud.config.dataflow.DataflowJobConfiguration;
+import io.elasticjob.cloud.context.TaskContext;
+import io.elasticjob.cloud.event.JobEventBus;
+import io.elasticjob.cloud.event.type.JobExecutionEvent;
+import io.elasticjob.cloud.event.type.JobStatusTraceEvent;
+import io.elasticjob.cloud.exception.JobExecutionEnvironmentException;
+import lombok.RequiredArgsConstructor;
+
+import java.util.Collection;
+
+/**
+ * 为调度器提供内部服务的门面类.
+ * 
+ * @author zhangliang
+ */
+@RequiredArgsConstructor
+public final class CloudJobFacade implements JobFacade {
+    
+    private final ShardingContexts shardingContexts;
+    
+    private final JobConfigurationContext jobConfig;
+    
+    private final JobEventBus jobEventBus;
+    
+    @Override
+    public JobRootConfiguration loadJobRootConfiguration(final boolean fromCache) {
+        return jobConfig;
+    }
+    
+    @Override
+    public void checkJobExecutionEnvironment() throws JobExecutionEnvironmentException {
+    }
+    
+    @Override
+    public void failoverIfNecessary() {
+    }
+    
+    @Override
+    public void registerJobBegin(final ShardingContexts shardingContexts) {
+    }
+    
+    @Override
+    public void registerJobCompleted(final ShardingContexts shardingContexts) {
+    }
+    
+    public ShardingContexts getShardingContexts() {
+        return shardingContexts;
+    }
+    
+    @Override
+    public boolean misfireIfRunning(final Collection<Integer> shardingItems) {
+        return false;
+    }
+    
+    @Override
+    public void clearMisfire(final Collection<Integer> shardingItems) {
+    }
+    
+    @Override
+    public boolean isExecuteMisfired(final Collection<Integer> shardingItems) {
+        return false;
+    }
+    
+    @Override
+    public boolean isEligibleForJobRunning() {
+        return jobConfig.getTypeConfig() instanceof DataflowJobConfiguration && ((DataflowJobConfiguration) jobConfig.getTypeConfig()).isStreamingProcess();
+    }
+    
+    @Override
+    public boolean isNeedSharding() {
+        return false;
+    }
+    
+    @Override
+    public void beforeJobExecuted(final ShardingContexts shardingContexts) {
+    }
+    
+    @Override
+    public void afterJobExecuted(final ShardingContexts shardingContexts) {
+    }
+    
+    @Override
+    public void postJobExecutionEvent(final JobExecutionEvent jobExecutionEvent) {
+        jobEventBus.post(jobExecutionEvent);
+    }
+    
+    @Override
+    public void postJobStatusTraceEvent(final String taskId, final JobStatusTraceEvent.State state, final String message) {
+        TaskContext taskContext = TaskContext.from(taskId);
+        jobEventBus.post(new JobStatusTraceEvent(taskContext.getMetaInfo().getJobName(), taskContext.getId(), taskContext.getSlaveId(), 
+                JobStatusTraceEvent.Source.CLOUD_EXECUTOR, taskContext.getType(), String.valueOf(taskContext.getMetaInfo().getShardingItems()), state, message));
+    }
+}
diff --git a/elastic-job-cloud-executor/src/main/java/io/elasticjob/cloud/executor/DaemonTaskScheduler.java b/elastic-job-cloud-executor/src/main/java/io/elasticjob/cloud/executor/DaemonTaskScheduler.java
new file mode 100644
index 0000000..30ab6ff
--- /dev/null
+++ b/elastic-job-cloud-executor/src/main/java/io/elasticjob/cloud/executor/DaemonTaskScheduler.java
@@ -0,0 +1,179 @@
+/*
+ * Copyright 1999-2015 dangdang.com.
+ * <p>
+ * Licensed 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.
+ * </p>
+ */
+
+package io.elasticjob.cloud.executor;
+
+import io.elasticjob.cloud.api.ElasticJob;
+import io.elasticjob.cloud.executor.JobExecutorFactory;
+import io.elasticjob.cloud.executor.JobFacade;
+import io.elasticjob.cloud.executor.ShardingContexts;
+import io.elasticjob.cloud.config.JobRootConfiguration;
+import io.elasticjob.cloud.exception.JobSystemException;
+import lombok.RequiredArgsConstructor;
+import lombok.Setter;
+import org.apache.mesos.ExecutorDriver;
+import org.apache.mesos.Protos;
+import org.quartz.CronScheduleBuilder;
+import org.quartz.CronTrigger;
+import org.quartz.Job;
+import org.quartz.JobBuilder;
+import org.quartz.JobDetail;
+import org.quartz.JobExecutionContext;
+import org.quartz.JobExecutionException;
+import org.quartz.Scheduler;
+import org.quartz.SchedulerException;
+import org.quartz.TriggerBuilder;
+import org.quartz.impl.StdSchedulerFactory;
+import org.quartz.plugins.management.ShutdownHookPlugin;
+
+import java.util.Properties;
+import java.util.concurrent.ConcurrentHashMap;
+
+/**
+ * 常驻作业调度器.
+ * 
+ * @author zhangliang
+ * @author caohao
+ */
+@RequiredArgsConstructor
+public final class DaemonTaskScheduler {
+    
+    public static final String ELASTIC_JOB_DATA_MAP_KEY = "elasticJob";
+    
+    private static final String JOB_FACADE_DATA_MAP_KEY = "jobFacade";
+    
+    private static final String EXECUTOR_DRIVER_DATA_MAP_KEY = "executorDriver";
+    
+    private static final String TASK_ID_DATA_MAP_KEY = "taskId";
+    
+    private static final ConcurrentHashMap<String, Scheduler> RUNNING_SCHEDULERS = new ConcurrentHashMap<>(1024, 1);
+    
+    private final ElasticJob elasticJob;
+    
+    private final JobRootConfiguration jobRootConfig;
+    
+    private final JobFacade jobFacade;
+    
+    private final ExecutorDriver executorDriver;
+    
+    private final Protos.TaskID taskId;
+    
+    /**
+     * 初始化作业.
+     */
+    public void init() {
+        JobDetail jobDetail = JobBuilder.newJob(DaemonJob.class).withIdentity(jobRootConfig.getTypeConfig().getCoreConfig().getJobName()).build();
+        jobDetail.getJobDataMap().put(ELASTIC_JOB_DATA_MAP_KEY, elasticJob);
+        jobDetail.getJobDataMap().put(JOB_FACADE_DATA_MAP_KEY, jobFacade);
+        jobDetail.getJobDataMap().put(EXECUTOR_DRIVER_DATA_MAP_KEY, executorDriver);
+        jobDetail.getJobDataMap().put(TASK_ID_DATA_MAP_KEY, taskId);
+        try {
+            scheduleJob(initializeScheduler(), jobDetail, taskId.getValue(), jobRootConfig.getTypeConfig().getCoreConfig().getCron());
+        } catch (final SchedulerException ex) {
+            throw new JobSystemException(ex);
+        }
+    }
+    
+    private Scheduler initializeScheduler() throws SchedulerException {
+        StdSchedulerFactory factory = new StdSchedulerFactory();
+        factory.initialize(getBaseQuartzProperties());
+        return factory.getScheduler();
+    }
+    
+    private Properties getBaseQuartzProperties() {
+        Properties result = new Properties();
+        result.put("org.quartz.threadPool.class", org.quartz.simpl.SimpleThreadPool.class.getName());
+        result.put("org.quartz.threadPool.threadCount", "1");
+        result.put("org.quartz.scheduler.instanceName", taskId.getValue());
+        if (!jobRootConfig.getTypeConfig().getCoreConfig().isMisfire()) {
+            result.put("org.quartz.jobStore.misfireThreshold", "1");
+        }
+        result.put("org.quartz.plugin.shutdownhook.class", ShutdownHookPlugin.class.getName());
+        result.put("org.quartz.plugin.shutdownhook.cleanShutdown", Boolean.TRUE.toString());
+        return result;
+    }
+    
+    private void scheduleJob(final Scheduler scheduler, final JobDetail jobDetail, final String triggerIdentity, final String cron) {
+        try {
+            if (!scheduler.checkExists(jobDetail.getKey())) {
+                scheduler.scheduleJob(jobDetail, createTrigger(triggerIdentity, cron));
+            }
+            scheduler.start();
+            RUNNING_SCHEDULERS.putIfAbsent(scheduler.getSchedulerName(), scheduler);
+        } catch (final SchedulerException ex) {
+            throw new JobSystemException(ex);
+        }
+    }
+    
+    private CronTrigger createTrigger(final String triggerIdentity, final String cron) {
+        return TriggerBuilder.newTrigger().withIdentity(triggerIdentity).withSchedule(CronScheduleBuilder.cronSchedule(cron).withMisfireHandlingInstructionDoNothing()).build();
+    }
+    
+    /**
+     * 停止任务调度.
+     * 
+     * @param taskID 任务主键
+     */
+    public static void shutdown(final Protos.TaskID taskID) {
+        Scheduler scheduler = RUNNING_SCHEDULERS.remove(taskID.getValue());
+        if (null != scheduler) {
+            try {
+                scheduler.shutdown();
+            } catch (final SchedulerException ex) {
+                throw new JobSystemException(ex);
+            }
+        }
+    }
+    
+    /**
+     * 常驻作业.
+     * 
+     * @author zhangliang
+     */
+    public static final class DaemonJob implements Job {
+        
+        @Setter
+        private ElasticJob elasticJob;
+        
+        @Setter
+        private JobFacade jobFacade;
+        
+        @Setter
+        private ExecutorDriver executorDriver;
+    
+        @Setter
+        private Protos.TaskID taskId;
+        
+        @Override
+        public void execute(final JobExecutionContext context) throws JobExecutionException {
+            ShardingContexts shardingContexts = jobFacade.getShardingContexts();
+            int jobEventSamplingCount = shardingContexts.getJobEventSamplingCount();
+            int currentJobEventSamplingCount = shardingContexts.getCurrentJobEventSamplingCount();
+            if (jobEventSamplingCount > 0 && ++currentJobEventSamplingCount < jobEventSamplingCount) {
+                shardingContexts.setCurrentJobEventSamplingCount(currentJobEventSamplingCount);
+                jobFacade.getShardingContexts().setAllowSendJobEvent(false);
+                JobExecutorFactory.getJobExecutor(elasticJob, jobFacade).execute();
+            } else {
+                jobFacade.getShardingContexts().setAllowSendJobEvent(true);
+                executorDriver.sendStatusUpdate(Protos.TaskStatus.newBuilder().setTaskId(taskId).setState(Protos.TaskState.TASK_RUNNING).setMessage("BEGIN").build());
+                JobExecutorFactory.getJobExecutor(elasticJob, jobFacade).execute();
+                executorDriver.sendStatusUpdate(Protos.TaskStatus.newBuilder().setTaskId(taskId).setState(Protos.TaskState.TASK_RUNNING).setMessage("COMPLETE").build());
+                shardingContexts.setCurrentJobEventSamplingCount(0);
+            }
+        }
+    }
+}
diff --git a/elastic-job-cloud-executor/src/main/java/io/elasticjob/cloud/executor/JobConfigurationContext.java b/elastic-job-cloud-executor/src/main/java/io/elasticjob/cloud/executor/JobConfigurationContext.java
new file mode 100644
index 0000000..cf18f06
--- /dev/null
+++ b/elastic-job-cloud-executor/src/main/java/io/elasticjob/cloud/executor/JobConfigurationContext.java
@@ -0,0 +1,87 @@
+/*
+ * Copyright 1999-2015 dangdang.com.
+ * <p>
+ * Licensed 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.
+ * </p>
+ */
+
+package io.elasticjob.cloud.executor;
+
+import io.elasticjob.cloud.api.JobType;
+import io.elasticjob.cloud.config.JobCoreConfiguration;
+import io.elasticjob.cloud.config.JobRootConfiguration;
+import io.elasticjob.cloud.config.JobTypeConfiguration;
+import io.elasticjob.cloud.config.script.ScriptJobConfiguration;
+import io.elasticjob.cloud.config.dataflow.DataflowJobConfiguration;
+import io.elasticjob.cloud.config.simple.SimpleJobConfiguration;
+import io.elasticjob.cloud.executor.handler.JobProperties.JobPropertiesEnum;
+import com.google.common.base.Preconditions;
+import com.google.common.base.Strings;
+import lombok.Getter;
+
+import java.util.Map;
+
+/**
+ * 内部的作业配置上下文.
+ *
+ * @author caohao
+ */
+public final class JobConfigurationContext implements JobRootConfiguration {
+    
+    private static final String IGNORE_CRON = "ignoredCron";
+    
+    private JobTypeConfiguration jobTypeConfig;
+    
+    @Getter
+    private String beanName;
+    
+    @Getter
+    private String applicationContext;
+    
+    public JobConfigurationContext(final Map<String, String> jobConfigurationMap) {
+        int ignoredShardingTotalCount = 1;
+        String jobClass = jobConfigurationMap.get("jobClass");
+        String jobType = jobConfigurationMap.get("jobType");
+        String jobName = jobConfigurationMap.get("jobName");
+        String cron = Strings.isNullOrEmpty(jobConfigurationMap.get("cron")) ? IGNORE_CRON : jobConfigurationMap.get("cron");
+        Preconditions.checkArgument(!Strings.isNullOrEmpty(jobName), "jobName can not be empty.");
+        Preconditions.checkArgument(!Strings.isNullOrEmpty(jobType), "jobType can not be empty.");
+        Preconditions.checkArgument(!Strings.isNullOrEmpty(jobClass), "jobClass can not be empty.");
+        JobCoreConfiguration jobCoreConfig = JobCoreConfiguration.newBuilder(jobName, cron, ignoredShardingTotalCount).build();
+        jobCoreConfig.getJobProperties().put(JobPropertiesEnum.EXECUTOR_SERVICE_HANDLER.name(), jobConfigurationMap.get("executorServiceHandler"));
+        jobCoreConfig.getJobProperties().put(JobPropertiesEnum.JOB_EXCEPTION_HANDLER.name(), jobConfigurationMap.get("jobExceptionHandler"));
+        if (JobType.DATAFLOW.name().equals(jobType)) {
+            jobTypeConfig = new DataflowJobConfiguration(jobCoreConfig, jobClass, Boolean.valueOf(jobConfigurationMap.get("streamingProcess")));
+        } else if (JobType.SIMPLE.name().equals(jobType)) {
+            jobTypeConfig = new SimpleJobConfiguration(jobCoreConfig, jobClass);
+        } else if (JobType.SCRIPT.name().equals(jobType)) {
+            jobTypeConfig = new ScriptJobConfiguration(jobCoreConfig, jobConfigurationMap.get("scriptCommandLine"));
+        }
+        beanName = jobConfigurationMap.get("beanName");
+        applicationContext = jobConfigurationMap.get("applicationContext");
+    }
+    
+    /**
+     * 判断是否为瞬时作业.
+     * 
+     * @return 是否为瞬时作业
+     */
+    public boolean isTransient() {
+        return IGNORE_CRON.equals(jobTypeConfig.getCoreConfig().getCron());
+    }
+    
+    @Override
+    public JobTypeConfiguration getTypeConfig() {
+        return jobTypeConfig;
+    }
+}
diff --git a/elastic-job-cloud-executor/src/main/java/io/elasticjob/cloud/executor/TaskExecutor.java b/elastic-job-cloud-executor/src/main/java/io/elasticjob/cloud/executor/TaskExecutor.java
new file mode 100644
index 0000000..f7af24f
--- /dev/null
+++ b/elastic-job-cloud-executor/src/main/java/io/elasticjob/cloud/executor/TaskExecutor.java
@@ -0,0 +1,179 @@
+/*
+ * Copyright 1999-2015 dangdang.com.
+ * <p>
+ * Licensed 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.
+ * </p>
+ */
+
+package io.elasticjob.cloud.executor;
+
+import io.elasticjob.cloud.api.ElasticJob;
+import io.elasticjob.cloud.api.script.ScriptJob;
+import io.elasticjob.cloud.event.JobEventBus;
+import io.elasticjob.cloud.event.rdb.JobEventRdbConfiguration;
+import io.elasticjob.cloud.exception.ExceptionUtil;
+import io.elasticjob.cloud.exception.JobSystemException;
+import io.elasticjob.cloud.util.concurrent.ExecutorServiceObject;
+import com.google.common.base.Strings;
+import lombok.RequiredArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
+import org.apache.commons.dbcp.BasicDataSource;
+import org.apache.commons.lang3.SerializationUtils;
+import org.apache.mesos.Executor;
+import org.apache.mesos.ExecutorDriver;
+import org.apache.mesos.Protos;
+import org.apache.mesos.Protos.TaskInfo;
+import org.springframework.context.support.ClassPathXmlApplicationContext;
+
+import java.util.HashMap;
+import java.util.Map;
+import java.util.concurrent.ExecutorService;
+
+/**
+ * 作业任务执行器.
+ *
+ * @author zhangliang
+ */
+@Slf4j
+public final class TaskExecutor implements Executor {
+    
+    private final ExecutorService executorService;
+    
+    private final Map<String, ClassPathXmlApplicationContext> applicationContexts = new HashMap<>();
+    
+    private volatile JobEventBus jobEventBus = new JobEventBus();
+    
+    public TaskExecutor() {
+        executorService = new ExecutorServiceObject("cloud-task-executor", Runtime.getRuntime().availableProcessors() * 100).createExecutorService();
+    }
+    
+    @Override
+    public void registered(final ExecutorDriver executorDriver, final Protos.ExecutorInfo executorInfo, final Protos.FrameworkInfo frameworkInfo, final Protos.SlaveInfo slaveInfo) {
+        if (!executorInfo.getData().isEmpty()) {
+            Map<String, String> data = SerializationUtils.deserialize(executorInfo.getData().toByteArray());
+            BasicDataSource dataSource = new BasicDataSource();
+            dataSource.setDriverClassName(data.get("event_trace_rdb_driver"));
+            dataSource.setUrl(data.get("event_trace_rdb_url"));
+            dataSource.setPassword(data.get("event_trace_rdb_password"));
+            dataSource.setUsername(data.get("event_trace_rdb_username"));
+            jobEventBus = new JobEventBus(new JobEventRdbConfiguration(dataSource));
+        }
+    }
+    
+    @Override
+    public void reregistered(final ExecutorDriver executorDriver, final Protos.SlaveInfo slaveInfo) {
+    }
+    
+    @Override
+    public void disconnected(final ExecutorDriver executorDriver) {
+    }
+    
+    @Override
+    public void launchTask(final ExecutorDriver executorDriver, final Protos.TaskInfo taskInfo) {
+        executorService.submit(new TaskThread(executorDriver, taskInfo));
+    }
+    
+    @Override
+    public void killTask(final ExecutorDriver executorDriver, final Protos.TaskID taskID) {
+        executorDriver.sendStatusUpdate(Protos.TaskStatus.newBuilder().setTaskId(taskID).setState(Protos.TaskState.TASK_KILLED).build());
+        DaemonTaskScheduler.shutdown(taskID);
+    }
+    
+    @Override
+    public void frameworkMessage(final ExecutorDriver executorDriver, final byte[] bytes) {
+        if (null != bytes && "STOP".equals(new String(bytes))) {
+            log.error("call frameworkMessage executor stopped.");
+            executorDriver.stop();
+        }
+    }
+    
+    @Override
+    public void shutdown(final ExecutorDriver executorDriver) {
+    }
+    
+    @Override
+    public void error(final ExecutorDriver executorDriver, final String message) {
+        log.error("call executor error, message is: {}", message);
+    }
+    
+    @RequiredArgsConstructor
+    class TaskThread implements Runnable {
+        
+        private final ExecutorDriver executorDriver;
+        
+        private final TaskInfo taskInfo;
+        
+        @Override
+        public void run() {
+            Thread.currentThread().setContextClassLoader(TaskThread.class.getClassLoader());
+            executorDriver.sendStatusUpdate(Protos.TaskStatus.newBuilder().setTaskId(taskInfo.getTaskId()).setState(Protos.TaskState.TASK_RUNNING).build());
+            Map<String, Object> data = SerializationUtils.deserialize(taskInfo.getData().toByteArray());
+            ShardingContexts shardingContexts = (ShardingContexts) data.get("shardingContext");
+            @SuppressWarnings("unchecked")
+            JobConfigurationContext jobConfig = new JobConfigurationContext((Map<String, String>) data.get("jobConfigContext"));
+            try {
+                ElasticJob elasticJob = getElasticJobInstance(jobConfig);
+                final CloudJobFacade jobFacade = new CloudJobFacade(shardingContexts, jobConfig, jobEventBus);
+                if (jobConfig.isTransient()) {
+                    JobExecutorFactory.getJobExecutor(elasticJob, jobFacade).execute();
+                    executorDriver.sendStatusUpdate(Protos.TaskStatus.newBuilder().setTaskId(taskInfo.getTaskId()).setState(Protos.TaskState.TASK_FINISHED).build());
+                } else {
+                    new DaemonTaskScheduler(elasticJob, jobConfig, jobFacade, executorDriver, taskInfo.getTaskId()).init();
+                }
+                // CHECKSTYLE:OFF
+            } catch (final Throwable ex) {
+                // CHECKSTYLE:ON
+                log.error("Elastic-Job-Cloud-Executor error", ex);
+                executorDriver.sendStatusUpdate(Protos.TaskStatus.newBuilder().setTaskId(taskInfo.getTaskId()).setState(Protos.TaskState.TASK_ERROR).setMessage(ExceptionUtil.transform(ex)).build());
+                executorDriver.stop();
+                throw ex;
+            }
+        }
+        
+        private ElasticJob getElasticJobInstance(final JobConfigurationContext jobConfig) {
+            if (!Strings.isNullOrEmpty(jobConfig.getBeanName()) && !Strings.isNullOrEmpty(jobConfig.getApplicationContext())) {
+                return getElasticJobBean(jobConfig);
+            } else {
+                return getElasticJobClass(jobConfig);
+            }
+        }
+        
+        private ElasticJob getElasticJobBean(final JobConfigurationContext jobConfig) {
+            String applicationContextFile = jobConfig.getApplicationContext();
+            if (null == applicationContexts.get(applicationContextFile)) {
+                synchronized (applicationContexts) {
+                    if (null == applicationContexts.get(applicationContextFile)) {
+                        applicationContexts.put(applicationContextFile, new ClassPathXmlApplicationContext(applicationContextFile));
+                    }
+                }
+            }
+            return (ElasticJob) applicationContexts.get(applicationContextFile).getBean(jobConfig.getBeanName());
+        }
+        
+        private ElasticJob getElasticJobClass(final JobConfigurationContext jobConfig) {
+            String jobClass = jobConfig.getTypeConfig().getJobClass();
+            try {
+                Class<?> elasticJobClass = Class.forName(jobClass);
+                if (!ElasticJob.class.isAssignableFrom(elasticJobClass)) {
+                    throw new JobSystemException("Elastic-Job: Class '%s' must implements ElasticJob interface.", jobClass);
+                }
+                if (elasticJobClass != ScriptJob.class) {
+                    return (ElasticJob) elasticJobClass.newInstance();
+                }
+                return null;
+            } catch (final ReflectiveOperationException ex) {
+                throw new JobSystemException("Elastic-Job: Class '%s' initialize failure, the error message is '%s'.", jobClass, ex.getMessage());
+            }
+        }
+    }
+}
diff --git a/elastic-job-cloud-executor/src/main/java/io/elasticjob/cloud/executor/local/LocalCloudJobConfiguration.java b/elastic-job-cloud-executor/src/main/java/io/elasticjob/cloud/executor/local/LocalCloudJobConfiguration.java
new file mode 100644
index 0000000..c4b8639
--- /dev/null
+++ b/elastic-job-cloud-executor/src/main/java/io/elasticjob/cloud/executor/local/LocalCloudJobConfiguration.java
@@ -0,0 +1,52 @@
+/*
+ * Copyright 1999-2015 dangdang.com.
+ * <p>
+ * Licensed 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.
+ * </p>
+ */
+
+package io.elasticjob.cloud.executor.local;
+
+import io.elasticjob.cloud.config.JobRootConfiguration;
+import io.elasticjob.cloud.config.JobTypeConfiguration;
+import lombok.AllArgsConstructor;
+import lombok.Getter;
+import lombok.RequiredArgsConstructor;
+
+/**
+ * 本地云作业配置.
+ * 
+ * @author gaohongtao
+ */
+@RequiredArgsConstructor
+@AllArgsConstructor
+@Getter
+public final class LocalCloudJobConfiguration implements JobRootConfiguration {
+    
+    private final JobTypeConfiguration typeConfig;
+    
+    private final int shardingItem;
+    
+    private String beanName;
+    
+    private String applicationContext;
+    
+    /**
+     * 获取作业名称.
+     * 
+     * @return 作业名称
+     */
+    public String getJobName() {
+        return typeConfig.getCoreConfig().getJobName();
+    }
+}
diff --git a/elastic-job-cloud-executor/src/main/java/io/elasticjob/cloud/executor/local/LocalTaskExecutor.java b/elastic-job-cloud-executor/src/main/java/io/elasticjob/cloud/executor/local/LocalTaskExecutor.java
new file mode 100644
index 0000000..b77708f
--- /dev/null
+++ b/elastic-job-cloud-executor/src/main/java/io/elasticjob/cloud/executor/local/LocalTaskExecutor.java
@@ -0,0 +1,117 @@
+/*
+ * Copyright 1999-2015 dangdang.com.
+ * <p>
+ * Licensed 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.
+ * </p>
+ */
+
+package io.elasticjob.cloud.executor.local;
+
+import io.elasticjob.cloud.api.ElasticJob;
+import io.elasticjob.cloud.api.JobType;
+import io.elasticjob.cloud.api.dataflow.DataflowJob;
+import io.elasticjob.cloud.api.simple.SimpleJob;
+import io.elasticjob.cloud.config.JobCoreConfiguration;
+import io.elasticjob.cloud.config.script.ScriptJobConfiguration;
+import io.elasticjob.cloud.event.JobEventBus;
+import io.elasticjob.cloud.executor.AbstractElasticJobExecutor;
+import io.elasticjob.cloud.executor.CloudJobFacade;
+import io.elasticjob.cloud.executor.JobConfigurationContext;
+import io.elasticjob.cloud.executor.ShardingContexts;
+import io.elasticjob.cloud.executor.type.DataflowJobExecutor;
+import io.elasticjob.cloud.config.dataflow.DataflowJobConfiguration;
+import io.elasticjob.cloud.exception.JobSystemException;
+import io.elasticjob.cloud.executor.type.ScriptJobExecutor;
+import io.elasticjob.cloud.executor.type.SimpleJobExecutor;
+import io.elasticjob.cloud.util.config.ShardingItemParameters;
+import com.google.common.base.Joiner;
+import com.google.common.base.Strings;
+import lombok.RequiredArgsConstructor;
+import org.springframework.context.support.ClassPathXmlApplicationContext;
+
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * 本地作业执行器.
+ * 
+ * @author gaohongtao
+ * @author zhangliang
+ */
+@RequiredArgsConstructor
+public final class LocalTaskExecutor {
+    
+    private final LocalCloudJobConfiguration localCloudJobConfiguration;
+    
+    /**
+     * 本地执行作业.
+     */
+    @SuppressWarnings("unchecked")
+    public void execute() {
+        AbstractElasticJobExecutor jobExecutor;
+        CloudJobFacade jobFacade = new CloudJobFacade(getShardingContexts(), getJobConfigurationContext(), new JobEventBus());
+        switch (localCloudJobConfiguration.getTypeConfig().getJobType()) {
+            case SIMPLE:
+                jobExecutor = new SimpleJobExecutor(getJobInstance(SimpleJob.class), jobFacade);
+                break;
+            case DATAFLOW:
+                jobExecutor = new DataflowJobExecutor(getJobInstance(DataflowJob.class), jobFacade);
+                break;
+            case SCRIPT:
+                jobExecutor = new ScriptJobExecutor(jobFacade);
+                break;
+            default:
+                throw new UnsupportedOperationException(localCloudJobConfiguration.getTypeConfig().getJobType().name());
+        }
+        jobExecutor.execute();
+    }
+    
+    private ShardingContexts getShardingContexts() {
+        JobCoreConfiguration coreConfig = localCloudJobConfiguration.getTypeConfig().getCoreConfig();
+        Map<Integer, String> shardingItemMap = new HashMap<>(1, 1);
+        shardingItemMap.put(localCloudJobConfiguration.getShardingItem(),
+                new ShardingItemParameters(coreConfig.getShardingItemParameters()).getMap().get(localCloudJobConfiguration.getShardingItem()));
+        return new ShardingContexts(Joiner.on("@-@").join(localCloudJobConfiguration.getJobName(), localCloudJobConfiguration.getShardingItem(), "READY", "foo_slave_id", "foo_uuid"),
+                localCloudJobConfiguration.getJobName(), coreConfig.getShardingTotalCount(), coreConfig.getJobParameter(), shardingItemMap);
+    }
+    
+    private JobConfigurationContext getJobConfigurationContext() {
+        Map<String, String> jobConfigurationMap = new HashMap<>();
+        jobConfigurationMap.put("jobClass", localCloudJobConfiguration.getTypeConfig().getJobClass());
+        jobConfigurationMap.put("jobType", localCloudJobConfiguration.getTypeConfig().getJobType().name());
+        jobConfigurationMap.put("jobName", localCloudJobConfiguration.getJobName());
+        jobConfigurationMap.put("beanName", localCloudJobConfiguration.getBeanName());
+        jobConfigurationMap.put("applicationContext", localCloudJobConfiguration.getApplicationContext());
+        if (JobType.DATAFLOW == localCloudJobConfiguration.getTypeConfig().getJobType()) {
+            jobConfigurationMap.put("streamingProcess", Boolean.toString(((DataflowJobConfiguration) localCloudJobConfiguration.getTypeConfig()).isStreamingProcess()));
+        } else if (JobType.SCRIPT == localCloudJobConfiguration.getTypeConfig().getJobType()) {
+            jobConfigurationMap.put("scriptCommandLine", ((ScriptJobConfiguration) localCloudJobConfiguration.getTypeConfig()).getScriptCommandLine());
+        }
+        return new JobConfigurationContext(jobConfigurationMap);
+    }
+    
+    private <T extends ElasticJob> T getJobInstance(final Class<T> clazz) {
+        Object result;
+        if (Strings.isNullOrEmpty(localCloudJobConfiguration.getApplicationContext())) {
+            String jobClass = localCloudJobConfiguration.getTypeConfig().getJobClass();
+            try {
+                result = Class.forName(jobClass).newInstance();
+            } catch (final ReflectiveOperationException ex) {
+                throw new JobSystemException("Elastic-Job: Class '%s' initialize failure, the error message is '%s'.", jobClass, ex.getMessage());
+            }
+        } else {
+            result = new ClassPathXmlApplicationContext(localCloudJobConfiguration.getApplicationContext()).getBean(localCloudJobConfiguration.getBeanName());
+        }
+        return clazz.cast(result);
+    }
+}
diff --git a/elastic-job-cloud-executor/src/test/java/io/elasticjob/cloud/executor/AllCloudExecutorTests.java b/elastic-job-cloud-executor/src/test/java/io/elasticjob/cloud/executor/AllCloudExecutorTests.java
new file mode 100644
index 0000000..e2009a8
--- /dev/null
+++ b/elastic-job-cloud-executor/src/test/java/io/elasticjob/cloud/executor/AllCloudExecutorTests.java
@@ -0,0 +1,38 @@
+/*
+ * Copyright 1999-2015 dangdang.com.
+ * <p>
+ * Licensed 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.
+ * </p>
+ */
+
+package io.elasticjob.cloud.executor;
+
+import io.elasticjob.cloud.executor.local.AllLocalExecutorTests;
+import lombok.AccessLevel;
+import lombok.NoArgsConstructor;
+import org.junit.runner.RunWith;
+import org.junit.runners.Suite;
+import org.junit.runners.Suite.SuiteClasses;
+
+@RunWith(Suite.class)
+@SuiteClasses({
+        CloudJobFacadeTest.class,
+        DaemonTaskSchedulerTest.class, 
+        JobConfigurationContextTest.class, 
+        TaskExecutorTest.class, 
+        TaskExecutorThreadTest.class, 
+        AllLocalExecutorTests.class
+    })
+@NoArgsConstructor(access = AccessLevel.PRIVATE)
+public final class AllCloudExecutorTests {
+}
diff --git a/elastic-job-cloud-executor/src/test/java/io/elasticjob/cloud/executor/CloudJobFacadeTest.java b/elastic-job-cloud-executor/src/test/java/io/elasticjob/cloud/executor/CloudJobFacadeTest.java
new file mode 100644
index 0000000..6ed0a5e
--- /dev/null
+++ b/elastic-job-cloud-executor/src/test/java/io/elasticjob/cloud/executor/CloudJobFacadeTest.java
@@ -0,0 +1,160 @@
+/*
+ * Copyright 1999-2015 dangdang.com.
+ * <p>
+ * Licensed 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.
+ * </p>
+ */
+
+package io.elasticjob.cloud.executor;
+
+import io.elasticjob.cloud.api.ElasticJob;
+import io.elasticjob.cloud.api.JobType;
+import io.elasticjob.cloud.context.ExecutionType;
+import io.elasticjob.cloud.config.JobRootConfiguration;
+import io.elasticjob.cloud.event.JobEventBus;
+import io.elasticjob.cloud.event.type.JobExecutionEvent;
+import io.elasticjob.cloud.exception.JobExecutionEnvironmentException;
+import io.elasticjob.cloud.event.type.JobStatusTraceEvent;
+import org.junit.Test;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+import java.util.HashMap;
+import java.util.Map;
+
+import static org.hamcrest.CoreMatchers.is;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertThat;
+import static org.junit.Assert.assertTrue;
+import static org.mockito.Mockito.verify;
+
+public class CloudJobFacadeTest {
+    
+    private final ShardingContexts shardingContexts;
+    
+    private final JobConfigurationContext jobConfig;
+    
+    @Mock
+    private JobEventBus eventBus;
+    
+    private final JobFacade jobFacade;
+    
+    public CloudJobFacadeTest() {
+        MockitoAnnotations.initMocks(this);
+        shardingContexts = getShardingContexts();
+        jobConfig = new JobConfigurationContext(getJobConfigurationMap(JobType.SIMPLE, false));
+        jobFacade = new CloudJobFacade(shardingContexts, jobConfig, eventBus);
+    }
+    
+    private ShardingContexts getShardingContexts() {
+        Map<Integer, String> shardingItemParameters = new HashMap<>(1, 1);
+        shardingItemParameters.put(0, "A");
+        return new ShardingContexts("fake_task_id", "test_job", 3, "", shardingItemParameters);
+    }
+    
+    private Map<String, String> getJobConfigurationMap(final JobType jobType, final boolean streamingProcess) {
+        Map<String, String> result = new HashMap<>(10, 1);
+        result.put("jobName", "test_job");
+        result.put("jobClass", ElasticJob.class.getCanonicalName());
+        result.put("jobType", jobType.name());
+        result.put("streamingProcess", Boolean.toString(streamingProcess));
+        return result;
+    }
+    
+    @Test
+    public void assertLoadJobRootConfiguration() {
+        assertThat(jobFacade.loadJobRootConfiguration(true), is((JobRootConfiguration) jobConfig));
+    }
+    
+    @Test
+    public void assertCheckJobExecutionEnvironment() throws JobExecutionEnvironmentException {
+        jobFacade.checkJobExecutionEnvironment();
+    }
+    
+    @Test
+    public void assertFailoverIfNecessary() {
+        jobFacade.failoverIfNecessary();
+    }
+    
+    @Test
+    public void assertRegisterJobBegin() {
+        jobFacade.registerJobBegin(null);
+    }
+    
+    @Test
+    public void assertRegisterJobCompleted() {
+        jobFacade.registerJobCompleted(null);
+    }
+    
+    @Test
+    public void assertGetShardingContext() {
+        assertThat(jobFacade.getShardingContexts(), is(shardingContexts));
+    }
+    
+    @Test
+    public void assertMisfireIfNecessary() {
+        jobFacade.misfireIfRunning(null);
+    }
+    
+    @Test
+    public void assertClearMisfire() {
+        jobFacade.clearMisfire(null);
+    }
+    
+    @Test
+    public void assertIsExecuteMisfired() {
+        assertFalse(jobFacade.isExecuteMisfired(null));
+    }
+    
+    @Test
+    public void assertIsEligibleForJobRunningWhenIsNotDataflowJob() {
+        assertFalse(jobFacade.isEligibleForJobRunning());
+    }
+    
+    @Test
+    public void assertIsEligibleForJobRunningWhenIsDataflowJobAndIsNotStreamingProcess() {
+        assertFalse(new CloudJobFacade(shardingContexts, new JobConfigurationContext(getJobConfigurationMap(JobType.DATAFLOW, false)), new JobEventBus()).isEligibleForJobRunning());
+    }
+    
+    @Test
+    public void assertIsEligibleForJobRunningWhenIsDataflowJobAndIsStreamingProcess() {
+        assertTrue(new CloudJobFacade(shardingContexts, new JobConfigurationContext(getJobConfigurationMap(JobType.DATAFLOW, true)), new JobEventBus()).isEligibleForJobRunning());
+    }
+    
+    @Test
+    public void assertIsNeedSharding() {
+        assertFalse(jobFacade.isNeedSharding());
+    }
+    
+    @Test
+    public void assertBeforeJobExecuted() {
+        jobFacade.beforeJobExecuted(null);
+    }
+    
+    @Test
+    public void assertAfterJobExecuted() {
+        jobFacade.afterJobExecuted(null);
+    }
+    
+    @Test
+    public void assertPostJobExecutionEvent() {
+        JobExecutionEvent jobExecutionEvent = new JobExecutionEvent("fake_task_id", "test_job", JobExecutionEvent.ExecutionSource.NORMAL_TRIGGER, 0);
+        jobFacade.postJobExecutionEvent(jobExecutionEvent);
+        verify(eventBus).post(jobExecutionEvent);
+    }
+    
+    @Test
+    public void assertPostJobStatusTraceEvent() {
+        jobFacade.postJobStatusTraceEvent(String.format("%s@-@0@-@%s@-@fake_slave_id@-@0", "test_job", ExecutionType.READY), JobStatusTraceEvent.State.TASK_RUNNING, "message is empty.");
+    }
+}
diff --git a/elastic-job-cloud-executor/src/test/java/io/elasticjob/cloud/executor/DaemonTaskSchedulerTest.java b/elastic-job-cloud-executor/src/test/java/io/elasticjob/cloud/executor/DaemonTaskSchedulerTest.java
new file mode 100644
index 0000000..fc7876a
--- /dev/null
+++ b/elastic-job-cloud-executor/src/test/java/io/elasticjob/cloud/executor/DaemonTaskSchedulerTest.java
@@ -0,0 +1,93 @@
+/*
+ * Copyright 1999-2015 dangdang.com.
+ * <p>
+ * Licensed 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.
+ * </p>
+ */
+
+package io.elasticjob.cloud.executor;
+
+import io.elasticjob.cloud.executor.fixture.TestScriptJobConfiguration;
+import io.elasticjob.cloud.context.ExecutionType;
+import org.apache.mesos.ExecutorDriver;
+import org.apache.mesos.Protos.TaskID;
+import org.apache.mesos.Protos.TaskState;
+import org.apache.mesos.Protos.TaskStatus;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.junit.MockitoJUnitRunner;
+import org.quartz.JobExecutionContext;
+
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+@RunWith(MockitoJUnitRunner.class)
+public final class DaemonTaskSchedulerTest {
+    
+    @Mock
+    private JobFacade jobFacade;
+    
+    @Mock
+    private ExecutorDriver executorDriver;
+    
+    @Mock
+    private JobExecutionContext jobExecutionContext;
+    
+    @Mock
+    private AbstractElasticJobExecutor jobExecutor;
+    
+    @Mock
+    private ShardingContexts shardingContexts;
+    
+    private TaskID taskId = TaskID.newBuilder().setValue(String.format("%s@-@0@-@%s@-@fake_slave_id@-@0", "test_job", ExecutionType.READY)).build();
+    
+    private DaemonTaskScheduler.DaemonJob daemonJob;
+    
+    @Before
+    public void setUp() throws NoSuchFieldException {
+        daemonJob = new DaemonTaskScheduler.DaemonJob();
+        daemonJob.setElasticJob(null);
+        daemonJob.setJobFacade(jobFacade);
+        daemonJob.setExecutorDriver(executorDriver);
+        daemonJob.setTaskId(taskId);
+    }
+    
+    @Test
+    public void assertJobRun() throws Exception {
+        when(jobFacade.getShardingContexts()).thenReturn(shardingContexts);
+        when(jobFacade.loadJobRootConfiguration(true)).thenReturn(new TestScriptJobConfiguration("test.sh"));
+        daemonJob.execute(jobExecutionContext);
+        verify(shardingContexts).setAllowSendJobEvent(true);
+        verify(executorDriver).sendStatusUpdate(TaskStatus.newBuilder().setTaskId(taskId).setState(TaskState.TASK_RUNNING).setMessage("BEGIN").build());
+        verify(executorDriver).sendStatusUpdate(TaskStatus.newBuilder().setTaskId(taskId).setState(TaskState.TASK_RUNNING).setMessage("COMPLETE").build());
+        verify(shardingContexts).setCurrentJobEventSamplingCount(0);
+    }
+    
+    @Test
+    public void assertJobRunWithEventSampling() throws Exception {
+        when(shardingContexts.getJobEventSamplingCount()).thenReturn(2);
+        when(jobFacade.getShardingContexts()).thenReturn(shardingContexts);
+        when(jobFacade.loadJobRootConfiguration(true)).thenReturn(new TestScriptJobConfiguration("test.sh"));
+        daemonJob.execute(jobExecutionContext);
+        verify(shardingContexts).setCurrentJobEventSamplingCount(1);
+        verify(shardingContexts).setAllowSendJobEvent(false);
+        when(shardingContexts.getCurrentJobEventSamplingCount()).thenReturn(1);
+        daemonJob.execute(jobExecutionContext);
+        verify(shardingContexts).setAllowSendJobEvent(true);
+        verify(executorDriver).sendStatusUpdate(TaskStatus.newBuilder().setTaskId(taskId).setState(TaskState.TASK_RUNNING).setMessage("BEGIN").build());
+        verify(executorDriver).sendStatusUpdate(TaskStatus.newBuilder().setTaskId(taskId).setState(TaskState.TASK_RUNNING).setMessage("COMPLETE").build());
+        verify(shardingContexts).setCurrentJobEventSamplingCount(0);
+    }
+}
diff --git a/elastic-job-cloud-executor/src/test/java/io/elasticjob/cloud/executor/JobConfigurationContextTest.java b/elastic-job-cloud-executor/src/test/java/io/elasticjob/cloud/executor/JobConfigurationContextTest.java
new file mode 100644
index 0000000..dc3ebdb
--- /dev/null
+++ b/elastic-job-cloud-executor/src/test/java/io/elasticjob/cloud/executor/JobConfigurationContextTest.java
@@ -0,0 +1,82 @@
+/*
+ * Copyright 1999-2015 dangdang.com.
+ * <p>
+ * Licensed 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.
+ * </p>
+ */
+
+package io.elasticjob.cloud.executor;
+
+import io.elasticjob.cloud.api.JobType;
+import io.elasticjob.cloud.config.script.ScriptJobConfiguration;
+import io.elasticjob.cloud.executor.fixture.TestJob;
+import io.elasticjob.cloud.config.dataflow.DataflowJobConfiguration;
+import io.elasticjob.cloud.config.simple.SimpleJobConfiguration;
+import io.elasticjob.cloud.exception.JobExecutionEnvironmentException;
+import org.junit.Test;
+
+import java.util.HashMap;
+import java.util.Map;
+
+import static junit.framework.TestCase.assertFalse;
+import static org.hamcrest.core.Is.is;
+import static org.junit.Assert.assertThat;
+import static org.junit.Assert.assertTrue;
+
+public class JobConfigurationContextTest {
+    
+    @Test
+    public void assertSimpleJobConfigurationContext() throws JobExecutionEnvironmentException {
+        assertTrue(new JobConfigurationContext(buildJobConfigurationContextMap(JobType.SIMPLE)).getTypeConfig() instanceof SimpleJobConfiguration); 
+    }
+    
+    @Test
+    public void assertDataflowJobConfigurationContext() throws JobExecutionEnvironmentException {
+        assertTrue(new JobConfigurationContext(buildJobConfigurationContextMap(JobType.DATAFLOW)).getTypeConfig() instanceof DataflowJobConfiguration);
+    }
+    
+    @Test
+    public void assertScriptJobConfigurationContext() throws JobExecutionEnvironmentException {
+        assertTrue(new JobConfigurationContext(buildJobConfigurationContextMap(JobType.SCRIPT)).getTypeConfig() instanceof ScriptJobConfiguration);
+    }
+    
+    @Test
+    public void assertSpringSimpleJobConfigurationContext() throws JobExecutionEnvironmentException {
+        Map<String, String> context = buildJobConfigurationContextMap(JobType.SIMPLE);
+        context.put("beanName", "springSimpleJobName");
+        context.put("applicationContext", "applicationContext.xml");
+        assertThat(new JobConfigurationContext(context).getBeanName(), is("springSimpleJobName"));
+        assertThat(new JobConfigurationContext(context).getApplicationContext(), is("applicationContext.xml"));
+    }
+    
+    @Test
+    public void assertSimpleJobConfigurationContextWithExecutionType() throws JobExecutionEnvironmentException {
+        Map<String, String> context = buildJobConfigurationContextMap(JobType.SIMPLE);
+        assertTrue(new JobConfigurationContext(context).isTransient());
+        context.put("cron", "0/1 * * * * ?");
+        assertFalse(new JobConfigurationContext(context).isTransient());
+    }
+    
+    private Map<String, String> buildJobConfigurationContextMap(final JobType jobType) {
+        Map<String, String> result = new HashMap<>();
+        result.put("jobName", "configuration_map_job");
+        result.put("jobClass", TestJob.class.getCanonicalName());
+        result.put("jobType", jobType.name());
+        if (jobType == JobType.DATAFLOW) {
+            result.put("streamingProcess", Boolean.TRUE.toString());
+        } else if (jobType == JobType.SCRIPT) {
+            result.put("scriptCommandLine", "echo test");
+        }
+        return result;
+    }
+}
diff --git a/elastic-job-cloud-executor/src/test/java/io/elasticjob/cloud/executor/TaskExecutorTest.java b/elastic-job-cloud-executor/src/test/java/io/elasticjob/cloud/executor/TaskExecutorTest.java
new file mode 100644
index 0000000..66534ac
--- /dev/null
+++ b/elastic-job-cloud-executor/src/test/java/io/elasticjob/cloud/executor/TaskExecutorTest.java
@@ -0,0 +1,121 @@
+/*
+ * Copyright 1999-2015 dangdang.com.
+ * <p>
+ * Licensed 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.
+ * </p>
+ */
+
+package io.elasticjob.cloud.executor;
+
+import com.google.protobuf.ByteString;
+import org.apache.commons.lang3.SerializationUtils;
+import org.apache.mesos.ExecutorDriver;
+import org.apache.mesos.Protos;
+import org.apache.mesos.Protos.ExecutorInfo;
+import org.apache.mesos.Protos.FrameworkInfo;
+import org.apache.mesos.Protos.SlaveInfo;
+import org.apache.mesos.Protos.TaskID;
+import org.apache.mesos.Protos.TaskInfo;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.junit.MockitoJUnitRunner;
+import org.unitils.util.ReflectionUtils;
+
+import java.util.HashMap;
+import java.util.concurrent.ExecutorService;
+
+import static org.mockito.Mockito.verify;
+
+@RunWith(MockitoJUnitRunner.class)
+public final class TaskExecutorTest {
+    
+    @Mock
+    private ExecutorDriver executorDriver;
+    
+    @Mock
+    private ExecutorService executorService;
+    
+    private ExecutorInfo executorInfo;
+    
+    private SlaveInfo slaveInfo = SlaveInfo.getDefaultInstance();
+    
+    private FrameworkInfo frameworkInfo = FrameworkInfo.getDefaultInstance();
+    
+    private TaskExecutor taskExecutor;
+    
+    @Before
+    public void setUp() throws NoSuchFieldException {
+        taskExecutor = new TaskExecutor();
+        ReflectionUtils.setFieldValue(taskExecutor, "executorService", executorService);
+        executorInfo = ExecutorInfo.getDefaultInstance();
+    }
+    
+    @Test
+    public void assertKillTask() {
+        TaskID taskID = Protos.TaskID.newBuilder().setValue("task_id").build();
+        taskExecutor.killTask(executorDriver, taskID);
+        verify(executorDriver).sendStatusUpdate(Protos.TaskStatus.newBuilder().setTaskId(taskID).setState(Protos.TaskState.TASK_KILLED).build());
+    }
+    
+    @Test
+    public void assertRegisteredWithoutData() {
+        // CHECKSTYLE:OFF
+        HashMap<String, String> data = new HashMap<>(4, 1);
+        // CHECKSTYLE:ON
+        data.put("event_trace_rdb_driver", "org.h2.Driver");
+        data.put("event_trace_rdb_url", "jdbc:h2:mem:test_executor");
+        data.put("event_trace_rdb_username", "sa");
+        data.put("event_trace_rdb_password", "");
+        ExecutorInfo executorInfo = ExecutorInfo.newBuilder().setExecutorId(Protos.ExecutorID.newBuilder().setValue("test_executor")).setCommand(Protos.CommandInfo.getDefaultInstance())
+                .setData(ByteString.copyFrom(SerializationUtils.serialize(data))).build();
+        taskExecutor.registered(executorDriver, executorInfo, frameworkInfo, slaveInfo);
+    }
+    
+    @Test
+    public void assertRegisteredWithData() {
+        taskExecutor.registered(executorDriver, executorInfo, frameworkInfo, slaveInfo);
+    }
+    
+    @Test
+    public void assertLaunchTask() {
+        taskExecutor.launchTask(executorDriver, TaskInfo.newBuilder().setName("test_job")
+                .setTaskId(TaskID.newBuilder().setValue("fake_task_id")).setSlaveId(Protos.SlaveID.newBuilder().setValue("slave-S0")).build());
+    }
+    
+    @Test
+    public void assertReregistered() {
+        taskExecutor.reregistered(executorDriver, slaveInfo);
+    }
+    
+    @Test
+    public void assertDisconnected() {
+        taskExecutor.disconnected(executorDriver);
+    }
+    
+    @Test
+    public void assertFrameworkMessage() {
+        taskExecutor.frameworkMessage(executorDriver, null);
+    }
+    
+    @Test
+    public void assertShutdown() {
+        taskExecutor.shutdown(executorDriver);
+    }
+    
+    @Test
+    public void assertError() {
+        taskExecutor.error(executorDriver, "");
+    }
+}
diff --git a/elastic-job-cloud-executor/src/test/java/io/elasticjob/cloud/executor/TaskExecutorThreadTest.java b/elastic-job-cloud-executor/src/test/java/io/elasticjob/cloud/executor/TaskExecutorThreadTest.java
new file mode 100644
index 0000000..24601f3
--- /dev/null
+++ b/elastic-job-cloud-executor/src/test/java/io/elasticjob/cloud/executor/TaskExecutorThreadTest.java
@@ -0,0 +1,159 @@
+/*
+ * Copyright 1999-2015 dangdang.com.
+ * <p>
+ * Licensed 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.
+ * </p>
+ */
+
+package io.elasticjob.cloud.executor;
+
+import io.elasticjob.cloud.api.JobType;
+import io.elasticjob.cloud.context.ExecutionType;
+import io.elasticjob.cloud.executor.fixture.TestJob;
+import io.elasticjob.cloud.exception.JobSystemException;
+import com.google.protobuf.ByteString;
+import org.apache.commons.lang3.SerializationUtils;
+import org.apache.mesos.ExecutorDriver;
+import org.apache.mesos.Protos;
+import org.apache.mesos.Protos.TaskID;
+import org.apache.mesos.Protos.TaskInfo;
+import org.apache.mesos.Protos.TaskState;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.junit.MockitoJUnitRunner;
+
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.LinkedHashMap;
+import java.util.Map;
+
+import static junit.framework.TestCase.assertTrue;
+import static org.mockito.Mockito.verify;
+
+@RunWith(MockitoJUnitRunner.class)
+public final class TaskExecutorThreadTest {
+    
+    @Mock
+    private ExecutorDriver executorDriver;
+    
+    private final String taskId = String.format("%s@-@0@-@%s@-@fake_slave_id@-@0", "test_job", ExecutionType.READY);
+    
+    @Test
+    public void assertLaunchTaskWithDaemonTaskAndJavaSimpleJob() {
+        TaskInfo taskInfo = buildJavaTransientTaskInfo();
+        TaskExecutor.TaskThread taskThread = new TaskExecutor().new TaskThread(executorDriver, taskInfo);
+        taskThread.run();
+        verify(executorDriver).sendStatusUpdate(Protos.TaskStatus.newBuilder().setTaskId(taskInfo.getTaskId()).setState(TaskState.TASK_RUNNING).build());
+        verify(executorDriver).sendStatusUpdate(Protos.TaskStatus.newBuilder().setTaskId(taskInfo.getTaskId()).setState(Protos.TaskState.TASK_FINISHED).build());
+    }
+    
+    @Test
+    public void assertLaunchTaskWithTransientTaskAndSpringSimpleJob() {
+        TaskInfo taskInfo = buildSpringDaemonTaskInfo();
+        TaskExecutor.TaskThread taskThread = new TaskExecutor().new TaskThread(executorDriver, taskInfo);
+        taskThread.run();
+        verify(executorDriver).sendStatusUpdate(Protos.TaskStatus.newBuilder().setTaskId(taskInfo.getTaskId()).setState(TaskState.TASK_RUNNING).build());
+    }
+    
+    @Test
+    public void assertLaunchTaskWithDaemonTaskAndJavaScriptJob() {
+        TaskInfo taskInfo = buildSpringScriptTransientTaskInfo();
+        TaskExecutor.TaskThread taskThread = new TaskExecutor().new TaskThread(executorDriver, taskInfo);
+        taskThread.run();
+        verify(executorDriver).sendStatusUpdate(Protos.TaskStatus.newBuilder().setTaskId(taskInfo.getTaskId()).setState(TaskState.TASK_RUNNING).build());
+        verify(executorDriver).sendStatusUpdate(Protos.TaskStatus.newBuilder().setTaskId(taskInfo.getTaskId()).setState(Protos.TaskState.TASK_FINISHED).build());
+    }
+    
+    @Test
+    public void assertLaunchTaskWithWrongElasticJobClass() {
+        TaskInfo taskInfo = buildWrongElasticJobClass();
+        TaskExecutor.TaskThread taskThread = new TaskExecutor().new TaskThread(executorDriver, taskInfo);
+        try {
+            taskThread.run();
+        } catch (final JobSystemException ex) {
+            assertTrue(ex.getMessage().startsWith("Elastic-Job: Class 'io.elasticjob.cloud.executor.TaskExecutorThreadTest' must implements ElasticJob interface."));
+        }
+    }
+    
+    @Test
+    public void assertLaunchTaskWithWrongClass() {
+        TaskInfo taskInfo = buildWrongClass();
+        TaskExecutor.TaskThread taskThread = new TaskExecutor().new TaskThread(executorDriver, taskInfo);
+        try {
+            taskThread.run();    
+        } catch (final JobSystemException ex) {
+            assertTrue(ex.getMessage().startsWith("Elastic-Job: Class 'WrongClass' initialize failure, the error message is 'WrongClass'."));
+        }
+    }
+    
+    private TaskInfo buildWrongClass() {
+        return buildTaskInfo(buildBaseJobConfigurationContextMapWithJobClassAndCron("WrongClass", "ignoredCron")).build();
+    }
+    
+    private TaskInfo buildWrongElasticJobClass() {
+        return buildTaskInfo(buildBaseJobConfigurationContextMapWithJobClassAndCron(TaskExecutorThreadTest.class.getCanonicalName(), "ignoredCron")).build();
+    }
+    
+    private TaskInfo buildSpringDaemonTaskInfo() {
+        return buildTaskInfo(buildSpringJobConfigurationContextMap()).build();
+    }
+    
+    private TaskInfo buildJavaTransientTaskInfo() {
+        return buildTaskInfo(buildBaseJobConfigurationContextMapWithJobClassAndCron(TestJob.class.getCanonicalName(), "ignoredCron")).build();
+    }
+    
+    private TaskInfo buildSpringScriptTransientTaskInfo() {
+        return buildTaskInfo(buildBaseJobConfigurationContextMap(TestJob.class.getCanonicalName(), "ignoredCron", JobType.SCRIPT)).build();
+    }
+    
+    private TaskInfo.Builder buildTaskInfo(final Map<String, String> jobConfigurationContext) {
+        return TaskInfo.newBuilder().setData(ByteString.copyFrom(serialize(jobConfigurationContext)))
+                .setName("test_job").setTaskId(TaskID.newBuilder().setValue(taskId)).setSlaveId(Protos.SlaveID.newBuilder().setValue("slave-S0"));
+    }
+    
+    private byte[] serialize(final Map<String, String> jobConfigurationContext) {
+        // CHECKSTYLE:OFF
+        LinkedHashMap<String, Object> result = new LinkedHashMap<>(2, 1);
+        // CHECKSTYLE:ON
+        ShardingContexts shardingContexts = new ShardingContexts(taskId, "test_job", 1, "", Collections.singletonMap(1, "a"));
+        result.put("shardingContext", shardingContexts);
+        result.put("jobConfigContext", jobConfigurationContext);
+        return SerializationUtils.serialize(result);
+    }
+    
+    private Map<String, String> buildSpringJobConfigurationContextMap() {
+        Map<String, String> context = buildBaseJobConfigurationContextMapWithJobClass(TestJob.class.getCanonicalName());
+        context.put("beanName", "testJob");
+        context.put("applicationContext", "applicationContext.xml");
+        return context;
+    }
+    
+    private Map<String, String> buildBaseJobConfigurationContextMapWithJobClass(final String jobClass) {
+        return buildBaseJobConfigurationContextMapWithJobClassAndCron(jobClass, "0/1 * * * * ?");
+    }
+    
+    private Map<String, String> buildBaseJobConfigurationContextMapWithJobClassAndCron(final String jobClass, final String cron) {
+        return buildBaseJobConfigurationContextMap(jobClass, cron, JobType.SIMPLE);
+    }
+    
+    private Map<String, String> buildBaseJobConfigurationContextMap(final String jobClass, final String cron, final JobType jobType) {
+        Map<String, String> result = new HashMap<>();
+        result.put("jobName", "test_job");
+        result.put("cron", cron);
+        result.put("jobClass", jobClass);
+        result.put("jobType", jobType.name());
+        result.put("scriptCommandLine", "echo \"\"");
+        return result;
+    }
+}
diff --git a/elastic-job-cloud-executor/src/test/java/io/elasticjob/cloud/executor/fixture/TestJob.java b/elastic-job-cloud-executor/src/test/java/io/elasticjob/cloud/executor/fixture/TestJob.java
new file mode 100644
index 0000000..c48f115
--- /dev/null
+++ b/elastic-job-cloud-executor/src/test/java/io/elasticjob/cloud/executor/fixture/TestJob.java
@@ -0,0 +1,28 @@
+/*
+ * Copyright 1999-2015 dangdang.com.
+ * <p>
+ * Licensed 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.
+ * </p>
+ */
+
+package io.elasticjob.cloud.executor.fixture;
+
+import io.elasticjob.cloud.api.ShardingContext;
+import io.elasticjob.cloud.api.simple.SimpleJob;
+
+public final class TestJob implements SimpleJob {
+    
+    @Override
+    public void execute(final ShardingContext shardingContext) {
+    }
+}
diff --git a/elastic-job-cloud-executor/src/test/java/io/elasticjob/cloud/executor/fixture/TestScriptJobConfiguration.java b/elastic-job-cloud-executor/src/test/java/io/elasticjob/cloud/executor/fixture/TestScriptJobConfiguration.java
new file mode 100644
index 0000000..f9f172f
--- /dev/null
+++ b/elastic-job-cloud-executor/src/test/java/io/elasticjob/cloud/executor/fixture/TestScriptJobConfiguration.java
@@ -0,0 +1,38 @@
+/*
+ * Copyright 1999-2015 dangdang.com.
+ * <p>
+ * Licensed 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.
+ * </p>
+ */
+
+package io.elasticjob.cloud.executor.fixture;
+
+
+import io.elasticjob.cloud.config.JobTypeConfiguration;
+import io.elasticjob.cloud.config.script.ScriptJobConfiguration;
+import io.elasticjob.cloud.config.JobCoreConfiguration;
+import io.elasticjob.cloud.config.JobRootConfiguration;
+import io.elasticjob.cloud.executor.handler.JobProperties;
+import lombok.RequiredArgsConstructor;
+
+@RequiredArgsConstructor
+public final class TestScriptJobConfiguration implements JobRootConfiguration {
+    
+    private final String scriptCommandLine;
+    
+    @Override
+    public JobTypeConfiguration getTypeConfig() {
+        return new ScriptJobConfiguration(JobCoreConfiguration.newBuilder("test_script_job", "0/1 * * * * ?", 3)
+                .jobProperties(JobProperties.JobPropertiesEnum.JOB_EXCEPTION_HANDLER.getKey(), "ignoredExceptionHandler").build(), scriptCommandLine);
+    }
+}
diff --git a/elastic-job-cloud-executor/src/test/java/io/elasticjob/cloud/executor/local/AllLocalExecutorTests.java b/elastic-job-cloud-executor/src/test/java/io/elasticjob/cloud/executor/local/AllLocalExecutorTests.java
new file mode 100644
index 0000000..7360cf1
--- /dev/null
+++ b/elastic-job-cloud-executor/src/test/java/io/elasticjob/cloud/executor/local/AllLocalExecutorTests.java
@@ -0,0 +1,29 @@
+/*
+ * Copyright 1999-2015 dangdang.com.
+ * <p>
+ * Licensed 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.
+ * </p>
+ */
+
+package io.elasticjob.cloud.executor.local;
+
+import lombok.AccessLevel;
+import lombok.NoArgsConstructor;
+import org.junit.runner.RunWith;
+import org.junit.runners.Suite;
+
+@RunWith(Suite.class)
+@Suite.SuiteClasses(LocalTaskExecutorTest.class)
+@NoArgsConstructor(access = AccessLevel.PRIVATE)
+public final class AllLocalExecutorTests {
+}
diff --git a/elastic-job-cloud-executor/src/test/java/io/elasticjob/cloud/executor/local/LocalTaskExecutorTest.java b/elastic-job-cloud-executor/src/test/java/io/elasticjob/cloud/executor/local/LocalTaskExecutorTest.java
new file mode 100644
index 0000000..ba7e0e7
--- /dev/null
+++ b/elastic-job-cloud-executor/src/test/java/io/elasticjob/cloud/executor/local/LocalTaskExecutorTest.java
@@ -0,0 +1,107 @@
+/*
+ * Copyright 1999-2015 dangdang.com.
+ * <p>
+ * Licensed 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.
+ * </p>
+ */
+
+package io.elasticjob.cloud.executor.local;
+
+import io.elasticjob.cloud.config.script.ScriptJobConfiguration;
+import io.elasticjob.cloud.executor.local.fixture.TestDataflowJob;
+import io.elasticjob.cloud.executor.local.fixture.TestSimpleJob;
+import io.elasticjob.cloud.config.JobCoreConfiguration;
+import io.elasticjob.cloud.config.dataflow.DataflowJobConfiguration;
+import io.elasticjob.cloud.config.simple.SimpleJobConfiguration;
+import io.elasticjob.cloud.exception.JobSystemException;
+import org.junit.Before;
+import org.junit.Test;
+
+import java.io.IOException;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import java.nio.file.attribute.PosixFilePermissions;
+import java.util.Arrays;
+
+import static org.hamcrest.core.Is.is;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertThat;
+import static org.junit.Assert.assertTrue;
+
+public final class LocalTaskExecutorTest {
+    
+    @Before
+    public void setUp() {
+        TestSimpleJob.setShardingContext(null);
+        TestDataflowJob.setInput(null);
+        TestDataflowJob.setOutput(null);
+    }
+    
+    @Test
+    public void assertSimpleJob() {
+        new LocalTaskExecutor(new LocalCloudJobConfiguration(new SimpleJobConfiguration(JobCoreConfiguration
+                .newBuilder(TestSimpleJob.class.getSimpleName(), "*/2 * * * * ?", 3).build(), TestSimpleJob.class.getName()), 1)).execute();
+        assertThat(TestSimpleJob.getShardingContext().getJobName(), is(TestSimpleJob.class.getSimpleName()));
+        assertThat(TestSimpleJob.getShardingContext().getShardingItem(), is(1));
+        assertThat(TestSimpleJob.getShardingContext().getShardingTotalCount(), is(3));
+        assertThat(TestSimpleJob.getShardingContext().getShardingItem(), is(1));
+        assertNull(TestSimpleJob.getShardingContext().getShardingParameter());
+        assertThat(TestSimpleJob.getShardingContext().getJobParameter(), is(""));
+    }
+    
+    @Test
+    public void assertSpringSimpleJob() {
+        new LocalTaskExecutor(new LocalCloudJobConfiguration(new SimpleJobConfiguration(JobCoreConfiguration.newBuilder(
+                TestSimpleJob.class.getSimpleName(), "*/2 * * * * ?", 3).shardingItemParameters("0=Beijing,1=Shanghai,2=Guangzhou").jobParameter("dbName=dangdang").build(), 
+                TestSimpleJob.class.getName()), 1, "testSimpleJob", "applicationContext.xml")).execute();
+        assertThat(TestSimpleJob.getShardingContext().getJobName(), is(TestSimpleJob.class.getSimpleName()));
+        assertThat(TestSimpleJob.getShardingContext().getShardingTotalCount(), is(3));
+        assertThat(TestSimpleJob.getShardingContext().getJobParameter(), is("dbName=dangdang"));
+        assertThat(TestSimpleJob.getShardingContext().getShardingItem(), is(1));
+        assertThat(TestSimpleJob.getShardingParameters().size(), is(1));
+        assertThat(TestSimpleJob.getShardingParameters().iterator().next(), is("Shanghai"));
+    }
+    
+    @Test
+    public void assertDataflowJob() {
+        TestDataflowJob.setInput(Arrays.asList("1", "2", "3"));
+        new LocalTaskExecutor(new LocalCloudJobConfiguration(new DataflowJobConfiguration(JobCoreConfiguration
+                .newBuilder(TestDataflowJob.class.getSimpleName(), "*/2 * * * * ?", 10).build(), TestDataflowJob.class.getName(), false), 5)).execute();
+        assertFalse(TestDataflowJob.getOutput().isEmpty());
+        for (String each : TestDataflowJob.getOutput()) {
+            assertTrue(each.endsWith("-d"));
+        }
+    }
+    
+    @Test
+    public void assertScriptJob() throws IOException {
+        new LocalTaskExecutor(new LocalCloudJobConfiguration(
+                new ScriptJobConfiguration(JobCoreConfiguration.newBuilder("TestScriptJob", "*/2 * * * * ?", 3).build(), buildScriptCommandLine()), 1)).execute();
+    }
+    
+    private static String buildScriptCommandLine() throws IOException {
+        if (System.getProperties().getProperty("os.name").contains("Windows")) {
+            return Paths.get(LocalTaskExecutorTest.class.getResource("/script/TestScriptJob.bat").getPath().substring(1)).toString();
+        }
+        Path result = Paths.get(LocalTaskExecutorTest.class.getResource("/script/TestScriptJob.sh").getPath());
+        Files.setPosixFilePermissions(result, PosixFilePermissions.fromString("rwxr-xr-x"));
+        return result.toString();
+    }
+    
+    @Test(expected = JobSystemException.class)
+    public void assertNotExistsJobClass() {
+        new LocalTaskExecutor(new LocalCloudJobConfiguration(new SimpleJobConfiguration(JobCoreConfiguration.newBuilder("not exist", "*/2 * * * * ?", 3).build(), "not exist"), 1)).execute();
+    }
+}
diff --git a/elastic-job-cloud-executor/src/test/java/io/elasticjob/cloud/executor/local/fixture/TestDataflowJob.java b/elastic-job-cloud-executor/src/test/java/io/elasticjob/cloud/executor/local/fixture/TestDataflowJob.java
new file mode 100644
index 0000000..7d72e0c
--- /dev/null
+++ b/elastic-job-cloud-executor/src/test/java/io/elasticjob/cloud/executor/local/fixture/TestDataflowJob.java
@@ -0,0 +1,52 @@
+/*
+ * Copyright 1999-2015 dangdang.com.
+ * <p>
+ * Licensed 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.
+ * </p>
+ */
+
+package io.elasticjob.cloud.executor.local.fixture;
+
+import io.elasticjob.cloud.api.ShardingContext;
+import io.elasticjob.cloud.api.dataflow.DataflowJob;
+import com.google.common.base.Function;
+import com.google.common.collect.Lists;
+import lombok.Getter;
+import lombok.Setter;
+
+import java.util.List;
+
+public final class TestDataflowJob implements DataflowJob<String> {
+    
+    @Setter
+    private static List<String> input;
+    
+    @Getter
+    @Setter
+    private static List<String> output;
+    
+    @Override
+    public List<String> fetchData(final ShardingContext shardingContext) {
+        return input;
+    }
+    
+    @Override
+    public void processData(final ShardingContext shardingContext, final List<String> data) {
+        output = Lists.transform(input, new Function<String, String>() {
+            @Override
+            public String apply(final String input) {
+                return input + "-d";
+            }
+        });
+    }
+}
diff --git a/elastic-job-cloud-executor/src/test/java/io/elasticjob/cloud/executor/local/fixture/TestSimpleJob.java b/elastic-job-cloud-executor/src/test/java/io/elasticjob/cloud/executor/local/fixture/TestSimpleJob.java
new file mode 100644
index 0000000..11abb26
--- /dev/null
+++ b/elastic-job-cloud-executor/src/test/java/io/elasticjob/cloud/executor/local/fixture/TestSimpleJob.java
@@ -0,0 +1,45 @@
+/*
+ * Copyright 1999-2015 dangdang.com.
+ * <p>
+ * Licensed 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.
+ * </p>
+ */
+
+package io.elasticjob.cloud.executor.local.fixture;
+
+import io.elasticjob.cloud.api.ShardingContext;
+import io.elasticjob.cloud.api.simple.SimpleJob;
+import com.google.common.base.Strings;
+import lombok.Getter;
+import lombok.Setter;
+
+import java.util.Set;
+import java.util.concurrent.ConcurrentSkipListSet;
+
+public final class TestSimpleJob implements SimpleJob {
+    
+    @Getter
+    @Setter
+    private static ShardingContext shardingContext;
+    
+    @Getter
+    private static Set<String> shardingParameters = new ConcurrentSkipListSet<>();
+    
+    @Override
+    public void execute(final ShardingContext shardingContext) {
+        TestSimpleJob.shardingContext = shardingContext;
+        if (!Strings.isNullOrEmpty(shardingContext.getShardingParameter())) {
+            shardingParameters.add(shardingContext.getShardingParameter());    
+        }
+    }
+}
diff --git a/elastic-job-cloud-executor/src/test/resources/applicationContext.xml b/elastic-job-cloud-executor/src/test/resources/applicationContext.xml
new file mode 100644
index 0000000..3fb8979
--- /dev/null
+++ b/elastic-job-cloud-executor/src/test/resources/applicationContext.xml
@@ -0,0 +1,9 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<beans xmlns="http://www.springframework.org/schema/beans"
+    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+    xsi:schemaLocation="http://www.springframework.org/schema/beans 
+                        http://www.springframework.org/schema/beans/spring-beans.xsd 
+                        ">
+    <bean id="testJob" class="io.elasticjob.cloud.executor.fixture.TestJob" />
+    <bean id="testSimpleJob" class="io.elasticjob.cloud.executor.local.fixture.TestSimpleJob" />
+</beans>
diff --git a/elastic-job-cloud-executor/src/test/resources/logback-test.xml b/elastic-job-cloud-executor/src/test/resources/logback-test.xml
new file mode 100644
index 0000000..a5ee1ef
--- /dev/null
+++ b/elastic-job-cloud-executor/src/test/resources/logback-test.xml
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<configuration>
+    <property name="log.context.name" value="elastic-job-cloud-executor" />
+    <property name="log.charset" value="UTF-8" />
+    <property name="log.pattern" value="[%-5level] %date --%thread-- [%logger] %msg %n" />
+    
+    <contextName>${log.context.name}</contextName>
+    
+    <appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
+        <filter class="ch.qos.logback.classic.filter.ThresholdFilter">
+            <level>ERROR</level>
+        </filter>
+        <encoder charset="${log.charset}">
+            <pattern>${log.pattern}</pattern>
+        </encoder>
+    </appender>
+    
+    <root>
+        <appender-ref ref="STDOUT" />
+    </root>
+    
+    <logger name="io.elasticjob.cloud.executor.TaskExecutor" level="OFF" />
+</configuration>
diff --git a/elastic-job-cloud-executor/src/test/resources/script/TestScriptJob.bat b/elastic-job-cloud-executor/src/test/resources/script/TestScriptJob.bat
new file mode 100755
index 0000000..d8105ef
--- /dev/null
+++ b/elastic-job-cloud-executor/src/test/resources/script/TestScriptJob.bat
@@ -0,0 +1 @@
+@echo sharding context is %*
diff --git a/elastic-job-cloud-executor/src/test/resources/script/TestScriptJob.sh b/elastic-job-cloud-executor/src/test/resources/script/TestScriptJob.sh
new file mode 100644
index 0000000..90b266f
--- /dev/null
+++ b/elastic-job-cloud-executor/src/test/resources/script/TestScriptJob.sh
@@ -0,0 +1,2 @@
+#!/bin/bash
+echo sharding context is $*
diff --git a/elastic-job-cloud-scheduler/pom.xml b/elastic-job-cloud-scheduler/pom.xml
new file mode 100644
index 0000000..c0a5872
--- /dev/null
+++ b/elastic-job-cloud-scheduler/pom.xml
@@ -0,0 +1,141 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+    <parent>
+        <artifactId>elastic-job-cloud</artifactId>
+        <groupId>io.elasticjob</groupId>
+        <version>3.0.0.M1-SNAPSHOT</version>
+    </parent>
+    <modelVersion>4.0.0</modelVersion>
+    <artifactId>elastic-job-cloud-scheduler</artifactId>
+    <name>${project.artifactId}</name>
+    
+    <properties>
+        <maven-assembly-plugin.version>2.5.5</maven-assembly-plugin.version>
+    </properties>
+    
+    <dependencies>
+        <dependency>
+            <groupId>io.elasticjob</groupId>
+            <artifactId>elastic-job-cloud-common</artifactId>
+            <version>${project.parent.version}</version>
+        </dependency>
+        
+        <dependency>
+            <groupId>org.apache.commons</groupId>
+            <artifactId>commons-lang3</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>commons-codec</groupId>
+            <artifactId>commons-codec</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>com.google.guava</groupId>
+            <artifactId>guava</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.apache.mesos</groupId>
+            <artifactId>mesos</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>com.netflix.fenzo</groupId>
+            <artifactId>fenzo-core</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>commons-dbcp</groupId>
+            <artifactId>commons-dbcp</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>mysql</groupId>
+            <artifactId>mysql-connector-java</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.slf4j</groupId>
+            <artifactId>slf4j-api</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.slf4j</groupId>
+            <artifactId>jcl-over-slf4j</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.slf4j</groupId>
+            <artifactId>log4j-over-slf4j</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>ch.qos.logback</groupId>
+            <artifactId>logback-classic</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>com.sun.jersey</groupId>
+            <artifactId>jersey-servlet</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>com.sun.jersey</groupId>
+            <artifactId>jersey-json</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>com.sun.jersey</groupId>
+            <artifactId>jersey-client</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.eclipse.jetty.aggregate</groupId>
+            <artifactId>jetty-all-server</artifactId>
+        </dependency>
+
+        <dependency>
+            <groupId>org.projectlombok</groupId>
+            <artifactId>lombok</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.apache.curator</groupId>
+            <artifactId>curator-test</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>junit</groupId>
+            <artifactId>junit</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.unitils</groupId>
+            <artifactId>unitils-core</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.mockito</groupId>
+            <artifactId>mockito-core</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.mockito</groupId>
+            <artifactId>mockito-inline</artifactId>
+        </dependency>
+    </dependencies>
+    
+    <build>
+        <resources>
+            <resource>
+               <directory>src/main/resources</directory>
+               <excludes>
+                   <exclude>bin/*</exclude>
+                   <exclude>conf/*</exclude>
+                   <exclude>assembly/*</exclude>
+               </excludes>
+            </resource>
+        </resources>
+        <plugins>
+            <plugin>
+                <groupId>org.apache.maven.plugins</groupId>
+                <artifactId>maven-assembly-plugin</artifactId>
+                <configuration>
+                    <descriptors>
+                        <descriptor>src/main/resources/assembly/assembly.xml</descriptor>
+                    </descriptors>
+                </configuration>
+                <executions>
+                    <execution>
+                        <phase>package</phase>
+                        <goals>
+                            <goal>assembly</goal>
+                        </goals>
+                    </execution>
+                </executions>
+            </plugin>
+        </plugins>
+    </build>
+</project>
diff --git a/elastic-job-cloud-scheduler/src/main/java/io/elasticjob/cloud/scheduler/Bootstrap.java b/elastic-job-cloud-scheduler/src/main/java/io/elasticjob/cloud/scheduler/Bootstrap.java
new file mode 100644
index 0000000..c428973
--- /dev/null
+++ b/elastic-job-cloud-scheduler/src/main/java/io/elasticjob/cloud/scheduler/Bootstrap.java
@@ -0,0 +1,67 @@
+/*
+ * Copyright 1999-2015 dangdang.com.
+ * <p>
+ * Licensed 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.
+ * </p>
+ */
+
+package io.elasticjob.cloud.scheduler;
+
+import io.elasticjob.cloud.scheduler.ha.HANode;
+import io.elasticjob.cloud.scheduler.env.BootstrapEnvironment;
+import io.elasticjob.cloud.scheduler.ha.SchedulerElectionCandidate;
+import io.elasticjob.cloud.reg.base.CoordinatorRegistryCenter;
+import io.elasticjob.cloud.reg.zookeeper.ZookeeperElectionService;
+import io.elasticjob.cloud.reg.zookeeper.ZookeeperRegistryCenter;
+import lombok.AccessLevel;
+import lombok.NoArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
+import org.apache.curator.framework.CuratorFramework;
+
+import java.util.concurrent.CountDownLatch;
+
+/**
+ * Mesos框架启动器.
+ *
+ * @author caohao
+ */
+@NoArgsConstructor(access = AccessLevel.PRIVATE)
+@Slf4j
+public final class Bootstrap {
+    
+    /**
+     * 启动入口.
+     * 
+     * @param args 命令行参数无需传入
+     * @throws InterruptedException 线程中断异常
+     */
+    // CHECKSTYLE:OFF
+    public static void main(final String[] args) throws InterruptedException {
+        // CHECKSTYLE:ON
+        CoordinatorRegistryCenter regCenter = new ZookeeperRegistryCenter(BootstrapEnvironment.getInstance().getZookeeperConfiguration());
+        regCenter.init();
+        final ZookeeperElectionService electionService = new ZookeeperElectionService(
+                BootstrapEnvironment.getInstance().getFrameworkHostPort(), (CuratorFramework) regCenter.getRawClient(), HANode.ELECTION_NODE, new SchedulerElectionCandidate(regCenter));
+        electionService.start();
+        final CountDownLatch latch = new CountDownLatch(1);
+        latch.await();
+        Runtime.getRuntime().addShutdownHook(new Thread("shutdown-hook") {
+        
+            @Override
+            public void run() {
+                electionService.stop();
+                latch.countDown();
+            }
+        });
+    }
+}
diff --git a/elastic-job-cloud-scheduler/src/main/java/io/elasticjob/cloud/scheduler/config/app/CloudAppConfiguration.java b/elastic-job-cloud-scheduler/src/main/java/io/elasticjob/cloud/scheduler/config/app/CloudAppConfiguration.java
new file mode 100644
index 0000000..e0033e4
--- /dev/null
+++ b/elastic-job-cloud-scheduler/src/main/java/io/elasticjob/cloud/scheduler/config/app/CloudAppConfiguration.java
@@ -0,0 +1,49 @@
+/*
+ * Copyright 1999-2015 dangdang.com.
+ * <p>
+ * Licensed 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.
+ * </p>
+ */
+
+package io.elasticjob.cloud.scheduler.config.app;
+
+import lombok.AllArgsConstructor;
+import lombok.Getter;
+import lombok.RequiredArgsConstructor;
+import lombok.ToString;
+
+/**
+ * 云作业App配置对象.
+ *
+ * @author caohao
+ */
+@AllArgsConstructor
+@RequiredArgsConstructor
+@Getter
+@ToString
+public final class CloudAppConfiguration {
+    
+    private final String appName;
+    
+    private final String appURL;
+    
+    private final String bootstrapScript;
+    
+    private double cpuCount = 1;
+    
+    private double memoryMB = 128;
+    
+    private boolean appCacheEnable = true;
+    
+    private int eventTraceSamplingCount;
+}
diff --git a/elastic-job-cloud-scheduler/src/main/java/io/elasticjob/cloud/scheduler/config/app/CloudAppConfigurationGsonFactory.java b/elastic-job-cloud-scheduler/src/main/java/io/elasticjob/cloud/scheduler/config/app/CloudAppConfigurationGsonFactory.java
new file mode 100644
index 0000000..2a38552
--- /dev/null
+++ b/elastic-job-cloud-scheduler/src/main/java/io/elasticjob/cloud/scheduler/config/app/CloudAppConfigurationGsonFactory.java
@@ -0,0 +1,131 @@
+/*
+ * Copyright 1999-2015 dangdang.com.
+ * <p>
+ * Licensed 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.
+ * </p>
+ */
+
+package io.elasticjob.cloud.scheduler.config.app;
+
+import io.elasticjob.cloud.util.json.GsonFactory;
+import com.google.gson.TypeAdapter;
+import com.google.gson.stream.JsonReader;
+import com.google.gson.stream.JsonWriter;
+import lombok.AccessLevel;
+import lombok.NoArgsConstructor;
+
+import java.io.IOException;
+
+import static io.elasticjob.cloud.scheduler.config.constants.CloudConfigurationConstants.APP_CACHE_ENABLE;
+import static io.elasticjob.cloud.scheduler.config.constants.CloudConfigurationConstants.APP_NAME;
+import static io.elasticjob.cloud.scheduler.config.constants.CloudConfigurationConstants.APP_URL;
+import static io.elasticjob.cloud.scheduler.config.constants.CloudConfigurationConstants.BOOTSTRAP_SCRIPT;
+import static io.elasticjob.cloud.scheduler.config.constants.CloudConfigurationConstants.CPU_COUNT;
+import static io.elasticjob.cloud.scheduler.config.constants.CloudConfigurationConstants.EVENT_TRACE_SAMPLING_COUNT;
+import static io.elasticjob.cloud.scheduler.config.constants.CloudConfigurationConstants.MEMORY_MB;
+
+/**
+ * 云作业App配置的Gson工厂.
+ *
+ * @author caohao
+ */
+@NoArgsConstructor(access = AccessLevel.PRIVATE)
+public final class CloudAppConfigurationGsonFactory {
+    
+    static  {
+        GsonFactory.registerTypeAdapter(CloudAppConfiguration.class, new CloudAppConfigurationGsonTypeAdapter());
+    }
+    
+    /**
+     * 将云作业App配置转换为JSON字符串.
+     *
+     * @param cloudAppConfig 云作业App配置对象
+     * @return 作业配置JSON字符串
+     */
+    public static String toJson(final CloudAppConfiguration cloudAppConfig) {
+        return GsonFactory.getGson().toJson(cloudAppConfig);
+    }
+    
+    /**
+     * 将JSON字符串转换为云作业App配置.
+     *
+     * @param cloudAppConfigJson 云作业App配置JSON字符串
+     * @return 作业配置对象
+     */
+    public static CloudAppConfiguration fromJson(final String cloudAppConfigJson) {
+        return GsonFactory.getGson().fromJson(cloudAppConfigJson, CloudAppConfiguration.class);
+    }
+    
+    /**
+     * 云作业App配置的Json转换适配器.
+     *
+     * @author caohao
+     */
+    public static final class CloudAppConfigurationGsonTypeAdapter extends TypeAdapter<CloudAppConfiguration> {
+    
+        @Override
+        public CloudAppConfiguration read(final JsonReader in) throws IOException {
+            String appURL = "";
+            String appName = "";
+            String bootstrapScript = "";
+            double cpuCount = 1.0d;
+            double memoryMB = 128.0d;
+            boolean appCacheEnable = true;
+            int eventTraceSamplingCount = 0;
+            in.beginObject();
+            while (in.hasNext()) {
+                String jsonName = in.nextName();
+                switch (jsonName) {
+                    case APP_NAME:
+                        appName = in.nextString();
+                        break;
+                    case APP_URL:
+                        appURL = in.nextString();
+                        break;
+                    case BOOTSTRAP_SCRIPT:
+                        bootstrapScript = in.nextString();
+                        break;
+                    case CPU_COUNT:
+                        cpuCount = in.nextDouble();
+                        break;
+                    case MEMORY_MB:
+                        memoryMB = in.nextDouble();
+                        break;
+                    case APP_CACHE_ENABLE:
+                        appCacheEnable = in.nextBoolean();
+                        break;
+                    case EVENT_TRACE_SAMPLING_COUNT:
+                        eventTraceSamplingCount = in.nextInt();
+                        break;
+                    default:
+                        break;
+                }
+            }
+            in.endObject();
+            return new CloudAppConfiguration(appName, appURL, bootstrapScript, cpuCount, memoryMB, appCacheEnable, eventTraceSamplingCount);
+        }
+    
+        @Override
+        public void write(final JsonWriter out, final CloudAppConfiguration value) throws IOException {
+            out.beginObject();
+            out.name(APP_NAME).value(value.getAppName());
+            out.name(APP_URL).value(value.getAppURL());
+            out.name(BOOTSTRAP_SCRIPT).value(value.getBootstrapScript());
+            out.name(CPU_COUNT).value(value.getCpuCount());
+            out.name(MEMORY_MB).value(value.getMemoryMB());
+            out.name(APP_CACHE_ENABLE).value(value.isAppCacheEnable());
+            out.name(EVENT_TRACE_SAMPLING_COUNT).value(value.getEventTraceSamplingCount());
+            out.endObject();
+        }
+    }
+}
diff --git a/elastic-job-cloud-scheduler/src/main/java/io/elasticjob/cloud/scheduler/config/app/CloudAppConfigurationNode.java b/elastic-job-cloud-scheduler/src/main/java/io/elasticjob/cloud/scheduler/config/app/CloudAppConfigurationNode.java
new file mode 100644
index 0000000..ce867ba
--- /dev/null
+++ b/elastic-job-cloud-scheduler/src/main/java/io/elasticjob/cloud/scheduler/config/app/CloudAppConfigurationNode.java
@@ -0,0 +1,38 @@
+/*
+ * Copyright 1999-2015 dangdang.com.
+ * <p>
+ * Licensed 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.
+ * </p>
+ */
+
+package io.elasticjob.cloud.scheduler.config.app;
+
+import lombok.AccessLevel;
+import lombok.NoArgsConstructor;
+
+/**
+ * 云作业App配置节点路径.
+ *
+ * @author caohao
+ */
+@NoArgsConstructor(access = AccessLevel.PRIVATE)
+public final class CloudAppConfigurationNode {
+    
+    public static final String ROOT =  "/config/app";
+    
+    private static final String APP_CONFIG =  ROOT + "/%s";
+    
+    static String getRootNodePath(final String appName) {
+        return String.format(APP_CONFIG, appName);
+    }
+}
diff --git a/elastic-job-cloud-scheduler/src/main/java/io/elasticjob/cloud/scheduler/config/app/CloudAppConfigurationService.java b/elastic-job-cloud-scheduler/src/main/java/io/elasticjob/cloud/scheduler/config/app/CloudAppConfigurationService.java
new file mode 100644
index 0000000..a81d1ef
--- /dev/null
+++ b/elastic-job-cloud-scheduler/src/main/java/io/elasticjob/cloud/scheduler/config/app/CloudAppConfigurationService.java
@@ -0,0 +1,95 @@
+/*
+ * Copyright 1999-2015 dangdang.com.
+ * <p>
+ * Licensed 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.
+ * </p>
+ */
+
+package io.elasticjob.cloud.scheduler.config.app;
+
+import io.elasticjob.cloud.reg.base.CoordinatorRegistryCenter;
+import com.google.common.base.Optional;
+import lombok.RequiredArgsConstructor;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.List;
+
+/**
+ * 云作业App配置服务.
+ *
+ * @author caohao
+ */
+@RequiredArgsConstructor
+public final class CloudAppConfigurationService {
+    
+    private final CoordinatorRegistryCenter regCenter;
+    
+    /**
+     * 添加云作业APP配置.
+     *
+     * @param appConfig 云作业App配置对象
+     */
+    public void add(final CloudAppConfiguration appConfig) {
+        regCenter.persist(CloudAppConfigurationNode.getRootNodePath(appConfig.getAppName()), CloudAppConfigurationGsonFactory.toJson(appConfig));
+    }
+    
+    /**
+     * 修改云作业APP配置.
+     *
+     * @param appConfig 云作业App配置对象
+     */
+    public void update(final CloudAppConfiguration appConfig) {
+        regCenter.update(CloudAppConfigurationNode.getRootNodePath(appConfig.getAppName()), CloudAppConfigurationGsonFactory.toJson(appConfig));
+    }
+    
+    /**
+     * 根据云作业App名称获取App配置.
+     *
+     * @param appName 云作业App名称
+     * @return 云作业App配置
+     */
+    public Optional<CloudAppConfiguration> load(final String appName) {
+        return Optional.fromNullable(CloudAppConfigurationGsonFactory.fromJson(regCenter.get(CloudAppConfigurationNode.getRootNodePath(appName))));
+    }
+    
+    /**
+     * 获取所有注册的云作业App配置.
+     *
+     * @return 注册的云作业App配置
+     */
+    public Collection<CloudAppConfiguration> loadAll() {
+        if (!regCenter.isExisted(CloudAppConfigurationNode.ROOT)) {
+            return Collections.emptyList();
+        }
+        List<String> appNames = regCenter.getChildrenKeys(CloudAppConfigurationNode.ROOT);
+        Collection<CloudAppConfiguration> result = new ArrayList<>(appNames.size());
+        for (String each : appNames) {
+            Optional<CloudAppConfiguration> config = load(each);
+            if (config.isPresent()) {
+                result.add(config.get());
+            }
+        }
+        return result;
+    }
+    
+    /**
+     * 删除云作业App配置.
+     *
+     * @param appName 云作业App名称
+     */
+    public void remove(final String appName) {
+        regCenter.remove(CloudAppConfigurationNode.getRootNodePath(appName));
+    }
+}
diff --git a/elastic-job-cloud-scheduler/src/main/java/io/elasticjob/cloud/scheduler/config/constants/CloudConfigurationConstants.java b/elastic-job-cloud-scheduler/src/main/java/io/elasticjob/cloud/scheduler/config/constants/CloudConfigurationConstants.java
new file mode 100644
index 0000000..c5c6c01
--- /dev/null
+++ b/elastic-job-cloud-scheduler/src/main/java/io/elasticjob/cloud/scheduler/config/constants/CloudConfigurationConstants.java
@@ -0,0 +1,48 @@
+/*
+ * Copyright 1999-2015 dangdang.com.
+ * <p>
+ * Licensed 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.
+ * </p>
+ */
+
+package io.elasticjob.cloud.scheduler.config.constants;
+
+/**
+ * Cloud配置的常量.
+ *
+ * @author caohao
+ */
+public final class CloudConfigurationConstants {
+    
+    public static final String APP_NAME = "appName";
+    
+    public static final String BOOTSTRAP_SCRIPT = "bootstrapScript";
+    
+    public static final String APP_CACHE_ENABLE = "appCacheEnable";
+    
+    public static final String APP_URL = "appURL";
+    
+    public static final String EVENT_TRACE_SAMPLING_COUNT = "eventTraceSamplingCount";
+    
+    public static final String CPU_COUNT = "cpuCount";
+    
+    public static final String MEMORY_MB = "memoryMB";
+    
+    public static final String JOB_EXECUTION_TYPE = "jobExecutionType";
+    
+    public static final String BEAN_NAME = "beanName";
+    
+    public static final String APPLICATION_CONTEXT = "applicationContext";
+    
+    
+}
diff --git a/elastic-job-cloud-scheduler/src/main/java/io/elasticjob/cloud/scheduler/config/job/CloudJobConfiguration.java b/elastic-job-cloud-scheduler/src/main/java/io/elasticjob/cloud/scheduler/config/job/CloudJobConfiguration.java
new file mode 100644
index 0000000..10e488d
--- /dev/null
+++ b/elastic-job-cloud-scheduler/src/main/java/io/elasticjob/cloud/scheduler/config/job/CloudJobConfiguration.java
@@ -0,0 +1,58 @@
+/*
+ * Copyright 1999-2015 dangdang.com.
+ * <p>
+ * Licensed 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.
+ * </p>
+ */
+
+package io.elasticjob.cloud.scheduler.config.job;
+
+import io.elasticjob.cloud.config.JobTypeConfiguration;
+import io.elasticjob.cloud.config.JobRootConfiguration;
+import lombok.AllArgsConstructor;
+import lombok.Getter;
+import lombok.RequiredArgsConstructor;
+
+/**
+ * 云作业配置对象.
+ *
+ * @author zhangliang
+ */
+@RequiredArgsConstructor
+@AllArgsConstructor
+@Getter
+public final class CloudJobConfiguration implements JobRootConfiguration {
+    
+    private final String appName;
+    
+    private final JobTypeConfiguration typeConfig;
+    
+    private final double cpuCount;
+    
+    private final double memoryMB;
+    
+    private final CloudJobExecutionType jobExecutionType;
+    
+    private String beanName;
+    
+    private String applicationContext; 
+    
+    /**
+     * 获取作业名称.
+     *
+     * @return 作业名称
+     */
+    public String getJobName() {
+        return typeConfig.getCoreConfig().getJobName();
+    }
+}
diff --git a/elastic-job-cloud-scheduler/src/main/java/io/elasticjob/cloud/scheduler/config/job/CloudJobConfigurationGsonFactory.java b/elastic-job-cloud-scheduler/src/main/java/io/elasticjob/cloud/scheduler/config/job/CloudJobConfigurationGsonFactory.java
new file mode 100644
index 0000000..9e4d6b6
--- /dev/null
+++ b/elastic-job-cloud-scheduler/src/main/java/io/elasticjob/cloud/scheduler/config/job/CloudJobConfigurationGsonFactory.java
@@ -0,0 +1,120 @@
+/*
+ * Copyright 1999-2015 dangdang.com.
+ * <p>
+ * Licensed 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.
+ * </p>
+ */
+
+package io.elasticjob.cloud.scheduler.config.job;
+
+import io.elasticjob.cloud.scheduler.config.constants.CloudConfigurationConstants;
+import io.elasticjob.cloud.config.JobTypeConfiguration;
+import io.elasticjob.cloud.util.json.AbstractJobConfigurationGsonTypeAdapter;
+import io.elasticjob.cloud.util.json.GsonFactory;
+import com.google.common.base.Preconditions;
+import com.google.gson.stream.JsonReader;
+import com.google.gson.stream.JsonWriter;
+import lombok.AccessLevel;
+import lombok.NoArgsConstructor;
+
+import java.io.IOException;
+import java.util.Map;
+
+/**
+ * Cloud作业配置的Gson工厂.
+ *
+ * @author zhangliang
+ * @author caohao
+ */
+@NoArgsConstructor(access = AccessLevel.PRIVATE)
+public final class CloudJobConfigurationGsonFactory {
+    
+    static  {
+        GsonFactory.registerTypeAdapter(CloudJobConfiguration.class, new CloudJobConfigurationGsonTypeAdapter());
+    }
+    
+    /**
+     * 将作业配置转换为JSON字符串.
+     *
+     * @param cloudJobConfig 作业配置对象
+     * @return 作业配置JSON字符串
+     */
+    public static String toJson(final CloudJobConfiguration cloudJobConfig) {
+        return GsonFactory.getGson().toJson(cloudJobConfig);
+    }
+    
+    /**
+     * 将JSON字符串转换为作业配置.
+     *
+     * @param cloudJobConfigJson 作业配置JSON字符串
+     * @return 作业配置对象
+     */
+    public static CloudJobConfiguration fromJson(final String cloudJobConfigJson) {
+        return GsonFactory.getGson().fromJson(cloudJobConfigJson, CloudJobConfiguration.class);
+    }
+    
+    /**
+     * Cloud作业配置的Json转换适配器.
+     *
+     * @author zhangliang
+     */
+    public static final class CloudJobConfigurationGsonTypeAdapter extends AbstractJobConfigurationGsonTypeAdapter<CloudJobConfiguration> {
+        
+        @Override
+        protected void addToCustomizedValueMap(final String jsonName, final JsonReader in, final Map<String, Object> customizedValueMap) throws IOException {
+            switch (jsonName) {
+                case CloudConfigurationConstants.CPU_COUNT:
+                case CloudConfigurationConstants.MEMORY_MB:
+                    customizedValueMap.put(jsonName, in.nextDouble());
+                    break;
+                case CloudConfigurationConstants.APP_NAME:
+                case CloudConfigurationConstants.APPLICATION_CONTEXT:
+                case CloudConfigurationConstants.BEAN_NAME:
+                case CloudConfigurationConstants.JOB_EXECUTION_TYPE:
+                    customizedValueMap.put(jsonName, in.nextString());
+                    break;
+                default:
+                    in.skipValue();
+                    break;
+            }
+        }
+        
+        @Override
+        protected CloudJobConfiguration getJobRootConfiguration(final JobTypeConfiguration typeConfig, final Map<String, Object> customizedValueMap) {
+            Preconditions.checkNotNull(customizedValueMap.get(CloudConfigurationConstants.APP_NAME), "appName cannot be null.");
+            Preconditions.checkNotNull(customizedValueMap.get(CloudConfigurationConstants.CPU_COUNT), "cpuCount cannot be null.");
+            Preconditions.checkArgument((double) customizedValueMap.get(CloudConfigurationConstants.CPU_COUNT) >= 0.001, "cpuCount cannot be less than 0.001");
+            Preconditions.checkNotNull(customizedValueMap.get(CloudConfigurationConstants.MEMORY_MB), "memoryMB cannot be null.");
+            Preconditions.checkArgument((double) customizedValueMap.get(CloudConfigurationConstants.MEMORY_MB) >= 1, "memory cannot be less than 1");
+            Preconditions.checkNotNull(customizedValueMap.get(CloudConfigurationConstants.JOB_EXECUTION_TYPE), "jobExecutionType cannot be null.");
+            if (customizedValueMap.containsKey(CloudConfigurationConstants.BEAN_NAME) && customizedValueMap.containsKey(CloudConfigurationConstants.APPLICATION_CONTEXT)) {
+                return new CloudJobConfiguration((String) customizedValueMap.get(CloudConfigurationConstants.APP_NAME), typeConfig, (double) customizedValueMap.get(CloudConfigurationConstants.CPU_COUNT), 
+                        (double) customizedValueMap.get(CloudConfigurationConstants.MEMORY_MB), CloudJobExecutionType.valueOf(customizedValueMap.get(CloudConfigurationConstants.JOB_EXECUTION_TYPE).toString()), 
+                        customizedValueMap.get(CloudConfigurationConstants.BEAN_NAME).toString(), customizedValueMap.get(CloudConfigurationConstants.APPLICATION_CONTEXT).toString());
+            } else {
+                return new CloudJobConfiguration((String) customizedValueMap.get(CloudConfigurationConstants.APP_NAME), typeConfig, (double) customizedValueMap.get(CloudConfigurationConstants.CPU_COUNT), 
+                        (double) customizedValueMap.get(CloudConfigurationConstants.MEMORY_MB), CloudJobExecutionType.valueOf(customizedValueMap.get(CloudConfigurationConstants.JOB_EXECUTION_TYPE).toString()));
+            }
+        }
+        
+        @Override
+        protected void writeCustomized(final JsonWriter out, final CloudJobConfiguration value) throws IOException {
+            out.name(CloudConfigurationConstants.APP_NAME).value(value.getAppName());
+            out.name(CloudConfigurationConstants.CPU_COUNT).value(value.getCpuCount());
+            out.name(CloudConfigurationConstants.MEMORY_MB).value(value.getMemoryMB());
+            out.name(CloudConfigurationConstants.JOB_EXECUTION_TYPE).value(value.getJobExecutionType().name());
+            out.name(CloudConfigurationConstants.BEAN_NAME).value(value.getBeanName());
+            out.name(CloudConfigurationConstants.APPLICATION_CONTEXT).value(value.getApplicationContext());
+        }
+    }
+}
diff --git a/elastic-job-cloud-scheduler/src/main/java/io/elasticjob/cloud/scheduler/config/job/CloudJobConfigurationListener.java b/elastic-job-cloud-scheduler/src/main/java/io/elasticjob/cloud/scheduler/config/job/CloudJobConfigurationListener.java
new file mode 100644
index 0000000..b86e423
--- /dev/null
+++ b/elastic-job-cloud-scheduler/src/main/java/io/elasticjob/cloud/scheduler/config/job/CloudJobConfigurationListener.java
@@ -0,0 +1,117 @@
+/*
+ * Copyright 1999-2015 dangdang.com.
+ * <p>
+ * Licensed 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.
+ * </p>
+ */
+
+package io.elasticjob.cloud.scheduler.config.job;
+
+import io.elasticjob.cloud.scheduler.producer.ProducerManager;
+import io.elasticjob.cloud.scheduler.state.ready.ReadyService;
+import io.elasticjob.cloud.reg.base.CoordinatorRegistryCenter;
+import lombok.extern.slf4j.Slf4j;
+import org.apache.curator.framework.CuratorFramework;
+import org.apache.curator.framework.recipes.cache.TreeCache;
+import org.apache.curator.framework.recipes.cache.TreeCacheEvent;
+import org.apache.curator.framework.recipes.cache.TreeCacheEvent.Type;
+import org.apache.curator.framework.recipes.cache.TreeCacheListener;
+
+import java.util.Collections;
+import java.util.concurrent.Executors;
+
+/**
+ * 云作业配置变更监听.
+ *
+ * @author zhangliang
+ * @author caohao
+ */
+@Slf4j
+public final class CloudJobConfigurationListener implements TreeCacheListener {
+    
+    private final CoordinatorRegistryCenter regCenter;
+    
+    private final ProducerManager producerManager;
+    
+    private final ReadyService readyService;
+    
+    public CloudJobConfigurationListener(final CoordinatorRegistryCenter regCenter, final ProducerManager producerManager) {
+        this.regCenter = regCenter;
+        readyService = new ReadyService(regCenter);
+        this.producerManager = producerManager;
+    }
+    
+    @Override
+    public void childEvent(final CuratorFramework client, final TreeCacheEvent event) throws Exception {
+        String path = null == event.getData() ? "" : event.getData().getPath();
+        if (isJobConfigNode(event, path, Type.NODE_ADDED)) {
+            CloudJobConfiguration jobConfig = getJobConfig(event);
+            if (null != jobConfig) {
+                producerManager.schedule(jobConfig);
+            }
+        } else if (isJobConfigNode(event, path, Type.NODE_UPDATED)) {
+            CloudJobConfiguration jobConfig = getJobConfig(event);
+            if (null == jobConfig) {
+                return;
+            }
+            if (CloudJobExecutionType.DAEMON == jobConfig.getJobExecutionType()) {
+                readyService.remove(Collections.singletonList(jobConfig.getJobName()));
+            }
+            if (!jobConfig.getTypeConfig().getCoreConfig().isMisfire()) {
+                readyService.setMisfireDisabled(jobConfig.getJobName());
+            }
+            producerManager.reschedule(jobConfig.getJobName());
+        } else if (isJobConfigNode(event, path, Type.NODE_REMOVED)) {
+            String jobName = path.substring(CloudJobConfigurationNode.ROOT.length() + 1, path.length());
+            producerManager.unschedule(jobName);
+        }
+    }
+    
+    private boolean isJobConfigNode(final TreeCacheEvent event, final String path, final Type type) {
+        return type == event.getType() && path.startsWith(CloudJobConfigurationNode.ROOT) && path.length() > CloudJobConfigurationNode.ROOT.length();
+    }
+    
+    private CloudJobConfiguration getJobConfig(final TreeCacheEvent event) {
+        try {
+            return CloudJobConfigurationGsonFactory.fromJson(new String(event.getData().getData()));
+            // CHECKSTYLE:OFF
+        } catch (final Exception ex) {
+            log.warn("Wrong Cloud Job Configuration with:", ex.getMessage());
+            // CHECKSTYLE:ON
+            return null;
+        }
+    }
+    
+    /**
+     * 启动云作业配置变更监听服务.
+     */
+    public void start() {
+        getCache().getListenable().addListener(this, Executors.newSingleThreadExecutor());
+    }
+    
+    /**
+     * 停止云作业配置变更监听服务.
+     */
+    public void stop() {
+        getCache().getListenable().removeListener(this);
+    }
+    
+    private TreeCache getCache() {
+        TreeCache result = (TreeCache) regCenter.getRawCache(CloudJobConfigurationNode.ROOT);
+        if (null != result) {
+            return result;
+        }
+        regCenter.addCacheData(CloudJobConfigurationNode.ROOT);
+        return (TreeCache) regCenter.getRawCache(CloudJobConfigurationNode.ROOT);
+    }
+}
diff --git a/elastic-job-cloud-scheduler/src/main/java/io/elasticjob/cloud/scheduler/config/job/CloudJobConfigurationNode.java b/elastic-job-cloud-scheduler/src/main/java/io/elasticjob/cloud/scheduler/config/job/CloudJobConfigurationNode.java
new file mode 100644
index 0000000..2fc4bef
--- /dev/null
+++ b/elastic-job-cloud-scheduler/src/main/java/io/elasticjob/cloud/scheduler/config/job/CloudJobConfigurationNode.java
@@ -0,0 +1,38 @@
+/*
+ * Copyright 1999-2015 dangdang.com.
+ * <p>
+ * Licensed 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.
+ * </p>
+ */
+
+package io.elasticjob.cloud.scheduler.config.job;
+
+import lombok.AccessLevel;
+import lombok.NoArgsConstructor;
+
+/**
+ * 作业配置节点路径.
+ *
+ * @author zhangliang
+ */
+@NoArgsConstructor(access = AccessLevel.PRIVATE)
+public final class CloudJobConfigurationNode {
+    
+    public static final String ROOT =  "/config/job";
+    
+    private static final String JOB_CONFIG =  ROOT + "/%s";
+    
+    static String getRootNodePath(final String jobName) {
+        return String.format(JOB_CONFIG, jobName);
+    }
+}
diff --git a/elastic-job-cloud-scheduler/src/main/java/io/elasticjob/cloud/scheduler/config/job/CloudJobConfigurationService.java b/elastic-job-cloud-scheduler/src/main/java/io/elasticjob/cloud/scheduler/config/job/CloudJobConfigurationService.java
new file mode 100644
index 0000000..6f05814
--- /dev/null
+++ b/elastic-job-cloud-scheduler/src/main/java/io/elasticjob/cloud/scheduler/config/job/CloudJobConfigurationService.java
@@ -0,0 +1,95 @@
+/*
+ * Copyright 1999-2015 dangdang.com.
+ * <p>
+ * Licensed 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.
+ * </p>
+ */
+
+package io.elasticjob.cloud.scheduler.config.job;
+
+import com.google.common.base.Optional;
+import io.elasticjob.cloud.reg.base.CoordinatorRegistryCenter;
+import lombok.RequiredArgsConstructor;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.List;
+
+/**
+ * 作业配置服务.
+ *
+ * @author zhangliang
+ */
+@RequiredArgsConstructor
+public final class CloudJobConfigurationService {
+    
+    private final CoordinatorRegistryCenter regCenter;
+    
+    /**
+     * 添加云作业配置.
+     * 
+     * @param jobConfig 云作业配置对象
+     */
+    public void add(final CloudJobConfiguration jobConfig) {
+        regCenter.persist(CloudJobConfigurationNode.getRootNodePath(jobConfig.getJobName()), CloudJobConfigurationGsonFactory.toJson(jobConfig));
+    }
+    
+    /**
+     * 修改云作业配置.
+     *
+     * @param jobConfig 云作业配置对象
+     */
+    public void update(final CloudJobConfiguration jobConfig) {
+        regCenter.update(CloudJobConfigurationNode.getRootNodePath(jobConfig.getJobName()), CloudJobConfigurationGsonFactory.toJson(jobConfig));
+    }
+    
+    /**
+     * 获取所有注册的云作业配置.
+     * 
+     * @return 注册的云作业配置
+     */
+    public Collection<CloudJobConfiguration> loadAll() {
+        if (!regCenter.isExisted(CloudJobConfigurationNode.ROOT)) {
+            return Collections.emptyList();
+        }
+        List<String> jobNames = regCenter.getChildrenKeys(CloudJobConfigurationNode.ROOT);
+        Collection<CloudJobConfiguration> result = new ArrayList<>(jobNames.size());
+        for (String each : jobNames) {
+            Optional<CloudJobConfiguration> config = load(each);
+            if (config.isPresent()) {
+                result.add(config.get());
+            }
+        }
+        return result;
+    }
+    
+    /**
+     * 根据作业名称获取云作业配置.
+     * 
+     * @param jobName 作业名称
+     * @return 云作业配置
+     */
+    public Optional<CloudJobConfiguration> load(final String jobName) {
+        return Optional.fromNullable(CloudJobConfigurationGsonFactory.fromJson(regCenter.get(CloudJobConfigurationNode.getRootNodePath(jobName))));
+    }
+    
+    /**
+     * 删除云作业.
+     *
+     * @param jobName 作业名称
+     */
+    public void remove(final String jobName) {
+        regCenter.remove(CloudJobConfigurationNode.getRootNodePath(jobName));
+    }
+}
diff --git a/elastic-job-cloud-scheduler/src/main/java/io/elasticjob/cloud/scheduler/config/job/CloudJobExecutionType.java b/elastic-job-cloud-scheduler/src/main/java/io/elasticjob/cloud/scheduler/config/job/CloudJobExecutionType.java
new file mode 100644
index 0000000..08623a3
--- /dev/null
+++ b/elastic-job-cloud-scheduler/src/main/java/io/elasticjob/cloud/scheduler/config/job/CloudJobExecutionType.java
@@ -0,0 +1,28 @@
+/*
+ * Copyright 1999-2015 dangdang.com.
+ * <p>
+ * Licensed 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.
+ * </p>
+ */
+
+package io.elasticjob.cloud.scheduler.config.job;
+
+/**
+ * 作业执行类型.
+ *
+ * @author zhangliang
+ */
+public enum CloudJobExecutionType {
+    
+    DAEMON, TRANSIENT
+}
diff --git a/elastic-job-cloud-scheduler/src/main/java/io/elasticjob/cloud/scheduler/context/JobContext.java b/elastic-job-cloud-scheduler/src/main/java/io/elasticjob/cloud/scheduler/context/JobContext.java
new file mode 100644
index 0000000..e476051
--- /dev/null
+++ b/elastic-job-cloud-scheduler/src/main/java/io/elasticjob/cloud/scheduler/context/JobContext.java
@@ -0,0 +1,58 @@
+/*
+ * Copyright 1999-2015 dangdang.com.
+ * <p>
+ * Licensed 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.
+ * </p>
+ */
+
+package io.elasticjob.cloud.scheduler.context;
+
+import io.elasticjob.cloud.scheduler.config.job.CloudJobConfiguration;
+import io.elasticjob.cloud.context.ExecutionType;
+import lombok.Getter;
+import lombok.RequiredArgsConstructor;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * 作业运行上下文.
+ *
+ * @author zhangliang
+ */
+@RequiredArgsConstructor
+@Getter
+public final class JobContext {
+    
+    private final CloudJobConfiguration jobConfig;
+    
+    private final List<Integer> assignedShardingItems;
+    
+    private final ExecutionType type;
+    
+    /**
+     * 通过作业配置创建作业运行上下文.
+     * 
+     * @param jobConfig 作业配置
+     * @param type 执行类型
+     * @return 作业运行上下文
+     */
+    public static JobContext from(final CloudJobConfiguration jobConfig, final ExecutionType type) {
+        int shardingTotalCount = jobConfig.getTypeConfig().getCoreConfig().getShardingTotalCount();
+        List<Integer> shardingItems = new ArrayList<>(shardingTotalCount);
+        for (int i = 0; i < shardingTotalCount; i++) {
+            shardingItems.add(i);
+        }
+        return new JobContext(jobConfig, shardingItems, type);
+    }
+}
diff --git a/elastic-job-cloud-scheduler/src/main/java/io/elasticjob/cloud/scheduler/env/BootstrapEnvironment.java b/elastic-job-cloud-scheduler/src/main/java/io/elasticjob/cloud/scheduler/env/BootstrapEnvironment.java
new file mode 100644
index 0000000..c2c32ab
--- /dev/null
+++ b/elastic-job-cloud-scheduler/src/main/java/io/elasticjob/cloud/scheduler/env/BootstrapEnvironment.java
@@ -0,0 +1,228 @@
+/*
+ * Copyright 1999-2015 dangdang.com.
+ * <p>
+ * Licensed 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.
+ * </p>
+ */
+
+package io.elasticjob.cloud.scheduler.env;
+
+import io.elasticjob.cloud.event.rdb.JobEventRdbConfiguration;
+import io.elasticjob.cloud.reg.zookeeper.ZookeeperConfiguration;
+import com.google.common.base.Optional;
+import com.google.common.base.Preconditions;
+import com.google.common.base.Strings;
+import lombok.Getter;
+import lombok.RequiredArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
+import org.apache.commons.dbcp.BasicDataSource;
+
+import java.io.FileInputStream;
+import java.io.IOException;
+import java.util.HashMap;
+import java.util.Properties;
+
+/**
+ * 启动环境对象.
+ *
+ * @author zhangliang
+ */
+@Slf4j
+public final class BootstrapEnvironment {
+    
+    @Getter
+    private static BootstrapEnvironment instance = new BootstrapEnvironment();
+    
+    private static final String PROPERTIES_PATH = "conf/elastic-job-cloud-scheduler.properties";
+    
+    private final Properties properties;
+    
+    private BootstrapEnvironment() {
+        properties = getProperties();
+    }
+    
+    private Properties getProperties() {
+        Properties result = new Properties();
+        try (FileInputStream fileInputStream = new FileInputStream(PROPERTIES_PATH)) {
+            result.load(fileInputStream);
+        } catch (final IOException ex) {
+            log.warn("Can not load properties file from path: '{}'.", PROPERTIES_PATH);
+        }
+        setPropertiesByEnv(result);
+        return result;
+    }
+    
+    private void setPropertiesByEnv(final Properties prop) {
+        for (EnvironmentArgument each : EnvironmentArgument.values()) {
+            String key = each.getKey();
+            String value = System.getenv(key);
+            if (!Strings.isNullOrEmpty(value)) {
+                log.info("Load property {} with value {} from ENV.", key, value);
+                prop.setProperty(each.getKey(), value);
+            }
+        }
+    }
+    
+    /**
+     * 获取Framework的Hostname和Port.
+     *
+     * @return Framework的Hostname和Port
+     */
+    public String getFrameworkHostPort() {
+        return String.format("%s:%d", getMesosConfiguration().getHostname(), getRestfulServerConfiguration().getPort());
+    }
+    
+    /**
+     * 获取Mesos配置对象.
+     *
+     * @return Mesos配置对象
+     */
+    public MesosConfiguration getMesosConfiguration() {
+        return new MesosConfiguration(getValue(EnvironmentArgument.USER), getValue(EnvironmentArgument.MESOS_URL), getValue(EnvironmentArgument.HOSTNAME));
+    }
+    
+    /**
+     * 获取Zookeeper配置对象.
+     * 
+     * @return Zookeeper配置对象
+     */
+    // TODO 其他zkConfig的值可配置
+    public ZookeeperConfiguration getZookeeperConfiguration() {
+        ZookeeperConfiguration result = new ZookeeperConfiguration(getValue(EnvironmentArgument.ZOOKEEPER_SERVERS), getValue(EnvironmentArgument.ZOOKEEPER_NAMESPACE));
+        String digest = getValue(EnvironmentArgument.ZOOKEEPER_DIGEST);
+        if (!Strings.isNullOrEmpty(digest)) {
+            result.setDigest(digest);
+        }
+        return result;
+    }
+    
+    /**
+     * 获取Restful服务器配置对象.
+     *
+     * @return Restful服务器配置对象
+     */
+    public RestfulServerConfiguration getRestfulServerConfiguration() {
+        return new RestfulServerConfiguration(Integer.parseInt(getValue(EnvironmentArgument.PORT)));
+    }
+    
+    /**
+     * 获取Mesos框架配置对象.
+     *
+     * @return Mesos框架配置对象
+     */
+    public FrameworkConfiguration getFrameworkConfiguration() {
+        return new FrameworkConfiguration(Integer.parseInt(getValue(EnvironmentArgument.JOB_STATE_QUEUE_SIZE)), Integer.parseInt(getValue(EnvironmentArgument.RECONCILE_INTERVAL_MINUTES)));
+    }
+    
+    /**
+     * 获取作业数据库事件配置.
+     *
+     * @return 作业数据库事件配置
+     */
+    public Optional<JobEventRdbConfiguration> getJobEventRdbConfiguration() {
+        String driver = getValue(EnvironmentArgument.EVENT_TRACE_RDB_DRIVER);
+        String url = getValue(EnvironmentArgument.EVENT_TRACE_RDB_URL);
+        String username = getValue(EnvironmentArgument.EVENT_TRACE_RDB_USERNAME);
+        String password = getValue(EnvironmentArgument.EVENT_TRACE_RDB_PASSWORD);
+        if (!Strings.isNullOrEmpty(driver) && !Strings.isNullOrEmpty(url) && !Strings.isNullOrEmpty(username)) {
+            BasicDataSource dataSource = new BasicDataSource();
+            dataSource.setDriverClassName(driver);
+            dataSource.setUrl(url);
+            dataSource.setUsername(username);
+            dataSource.setPassword(password);
+            return Optional.of(new JobEventRdbConfiguration(dataSource));
+        }
+        return Optional.absent();
+    }
+    
+    /**
+     * 获取作业数据库事件配置Map.
+     *
+     * @return 作业数据库事件配置Map
+     */
+    // CHECKSTYLE:OFF
+    public HashMap<String, String> getJobEventRdbConfigurationMap() {
+        HashMap<String, String> result = new HashMap<>(4, 1);
+        // CHECKSTYLE:ON
+        result.put(EnvironmentArgument.EVENT_TRACE_RDB_DRIVER.getKey(), getValue(EnvironmentArgument.EVENT_TRACE_RDB_DRIVER));
+        result.put(EnvironmentArgument.EVENT_TRACE_RDB_URL.getKey(), getValue(EnvironmentArgument.EVENT_TRACE_RDB_URL));
+        result.put(EnvironmentArgument.EVENT_TRACE_RDB_USERNAME.getKey(), getValue(EnvironmentArgument.EVENT_TRACE_RDB_USERNAME));
+        result.put(EnvironmentArgument.EVENT_TRACE_RDB_PASSWORD.getKey(), getValue(EnvironmentArgument.EVENT_TRACE_RDB_PASSWORD));
+        return result;
+    }
+    
+    /**
+     * 获取该framework的mesos角色.
+     * 
+     * @return 角色的可选值.
+     */
+    public Optional<String> getMesosRole() {
+        String role = getValue(EnvironmentArgument.MESOS_ROLE);
+        if (Strings.isNullOrEmpty(role)) {
+            return Optional.absent();
+        }
+        return Optional.of(role);
+    }
+    
+    private String getValue(final EnvironmentArgument environmentArgument) {
+        String result = properties.getProperty(environmentArgument.getKey(), environmentArgument.getDefaultValue());
+        if (environmentArgument.isRequired()) {
+            Preconditions.checkState(!Strings.isNullOrEmpty(result), String.format("Property `%s` is required.", environmentArgument.getKey()));
+        }
+        return result;
+    }
+    
+    /**
+     * 环境参数.
+     * 
+     * @author zhangliang
+     */
+    @RequiredArgsConstructor
+    @Getter
+    public enum EnvironmentArgument {
+        
+        HOSTNAME("hostname", "localhost", true),
+        
+        MESOS_URL("mesos_url", "zk://localhost:2181/mesos", true),
+        
+        MESOS_ROLE("mesos_role", "", false),
+        
+        USER("user", "", false),
+        
+        ZOOKEEPER_SERVERS("zk_servers", "localhost:2181", true),
+        
+        ZOOKEEPER_NAMESPACE("zk_namespace", "elastic-job-cloud", true),
+        
+        ZOOKEEPER_DIGEST("zk_digest", "", false),
+        
+        PORT("http_port", "8899", true),
+        
+        JOB_STATE_QUEUE_SIZE("job_state_queue_size", "10000", true),
+        
+        EVENT_TRACE_RDB_DRIVER("event_trace_rdb_driver", "", false),
+
+        EVENT_TRACE_RDB_URL("event_trace_rdb_url", "", false),
+
+        EVENT_TRACE_RDB_USERNAME("event_trace_rdb_username", "", false),
+
+        EVENT_TRACE_RDB_PASSWORD("event_trace_rdb_password", "", false),
+    
+        RECONCILE_INTERVAL_MINUTES("reconcile_interval_minutes", "-1", false);
+        
+        private final String key;
+        
+        private final String defaultValue;
+        
+        private final boolean required;
+    }
+}
diff --git a/elastic-job-cloud-scheduler/src/main/java/io/elasticjob/cloud/scheduler/env/FrameworkConfiguration.java b/elastic-job-cloud-scheduler/src/main/java/io/elasticjob/cloud/scheduler/env/FrameworkConfiguration.java
new file mode 100644
index 0000000..1a1cec1
--- /dev/null
+++ b/elastic-job-cloud-scheduler/src/main/java/io/elasticjob/cloud/scheduler/env/FrameworkConfiguration.java
@@ -0,0 +1,44 @@
+/*
+ * Copyright 1999-2015 dangdang.com.
+ * <p>
+ * Licensed 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.
+ * </p>
+ */
+
+package io.elasticjob.cloud.scheduler.env;
+
+import lombok.Getter;
+import lombok.RequiredArgsConstructor;
+
+/**
+ * Mesos框架配置项.
+ *
+ * @author zhangliang
+ */
+@RequiredArgsConstructor
+@Getter
+public final class FrameworkConfiguration {
+    
+    private final int jobStateQueueSize;
+    
+    private final int reconcileIntervalMinutes;
+    
+    /**
+     * 是否启用协调服务.
+     * 
+     * @return true 启用 false 不启用
+     */
+    public boolean isEnabledReconcile() {
+        return reconcileIntervalMinutes > 0;
+    }
+}
diff --git a/elastic-job-cloud-scheduler/src/main/java/io/elasticjob/cloud/scheduler/env/MesosConfiguration.java b/elastic-job-cloud-scheduler/src/main/java/io/elasticjob/cloud/scheduler/env/MesosConfiguration.java
new file mode 100644
index 0000000..5742ad6
--- /dev/null
+++ b/elastic-job-cloud-scheduler/src/main/java/io/elasticjob/cloud/scheduler/env/MesosConfiguration.java
@@ -0,0 +1,47 @@
+/*
+ * Copyright 1999-2015 dangdang.com.
+ * <p>
+ * Licensed 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.
+ * </p>
+ */
+
+package io.elasticjob.cloud.scheduler.env;
+
+import lombok.Getter;
+import lombok.RequiredArgsConstructor;
+
+/**
+ * Mesos配置项.
+ *
+ * @author zhangliang
+ */
+@RequiredArgsConstructor
+@Getter
+public final class MesosConfiguration {
+    
+    /**
+     * 框架名称.
+     */
+    public static final String FRAMEWORK_NAME = "Elastic-Job-Cloud";
+    
+    /**
+     * 框架失效转移超时秒数. 默认为1周
+     */
+    public static final double FRAMEWORK_FAILOVER_TIMEOUT_SECONDS = 60 * 60 * 24 * 7D;
+    
+    private final String user;
+    
+    private final String url;
+    
+    private final String hostname;
+}
diff --git a/elastic-job-cloud-scheduler/src/main/java/io/elasticjob/cloud/scheduler/env/RestfulServerConfiguration.java b/elastic-job-cloud-scheduler/src/main/java/io/elasticjob/cloud/scheduler/env/RestfulServerConfiguration.java
new file mode 100644
index 0000000..357dd69
--- /dev/null
+++ b/elastic-job-cloud-scheduler/src/main/java/io/elasticjob/cloud/scheduler/env/RestfulServerConfiguration.java
@@ -0,0 +1,33 @@
+/*
+ * Copyright 1999-2015 dangdang.com.
+ * <p>
+ * Licensed 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.
+ * </p>
+ */
+
+package io.elasticjob.cloud.scheduler.env;
+
+import lombok.Getter;
+import lombok.RequiredArgsConstructor;
+
+/**
+ * Restful服务器配置项.
+ *
+ * @author zhangliang
+ */
+@RequiredArgsConstructor
+@Getter
+public final class RestfulServerConfiguration {
+    
+    private final int port;
+}
diff --git a/elastic-job-cloud-scheduler/src/main/java/io/elasticjob/cloud/scheduler/ha/FrameworkIDService.java b/elastic-job-cloud-scheduler/src/main/java/io/elasticjob/cloud/scheduler/ha/FrameworkIDService.java
new file mode 100644
index 0000000..48b03d4
--- /dev/null
+++ b/elastic-job-cloud-scheduler/src/main/java/io/elasticjob/cloud/scheduler/ha/FrameworkIDService.java
@@ -0,0 +1,55 @@
+/*
+ * Copyright 1999-2015 dangdang.com.
+ * <p>
+ * Licensed 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.
+ * </p>
+ */
+
+package io.elasticjob.cloud.scheduler.ha;
+
+import io.elasticjob.cloud.reg.base.CoordinatorRegistryCenter;
+import com.google.common.base.Optional;
+import com.google.common.base.Strings;
+import lombok.RequiredArgsConstructor;
+
+/**
+ * FrameworkID 的保存器.
+ * 
+ * @author gaohongtao
+ */
+@RequiredArgsConstructor
+public final class FrameworkIDService {
+    
+    private final CoordinatorRegistryCenter regCenter;
+    
+    /**
+     * 获取FrameworkID,返回值是一个可选的结果.
+     * 
+     * @return 获取FrameworkID的可选结果
+     */
+    public Optional<String> fetch() {
+        String frameworkId = regCenter.getDirectly(HANode.FRAMEWORK_ID_NODE);
+        return Strings.isNullOrEmpty(frameworkId) ? Optional.<String>absent() : Optional.of(frameworkId);
+    }
+    
+    /**
+     * 保存FrameworkID.
+     * 
+     * @param id Framework的ID
+     */
+    public void save(final String id) {
+        if (!regCenter.isExisted(HANode.FRAMEWORK_ID_NODE)) {
+            regCenter.persist(HANode.FRAMEWORK_ID_NODE, id);
+        }
+    }
+}
diff --git a/elastic-job-cloud-scheduler/src/main/java/io/elasticjob/cloud/scheduler/ha/HANode.java b/elastic-job-cloud-scheduler/src/main/java/io/elasticjob/cloud/scheduler/ha/HANode.java
new file mode 100644
index 0000000..5865005
--- /dev/null
+++ b/elastic-job-cloud-scheduler/src/main/java/io/elasticjob/cloud/scheduler/ha/HANode.java
@@ -0,0 +1,41 @@
+/*
+ * Copyright 1999-2015 dangdang.com.
+ * <p>
+ * Licensed 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.
+ * </p>
+ */
+
+package io.elasticjob.cloud.scheduler.ha;
+
+/**
+ * 保存HA相关数据节点.
+ * 
+ * @author gaohongtao
+ */
+public final class HANode {
+    
+    /**
+     * HA根节点.
+     */
+    public static final String ROOT = "/ha";
+    
+    /**
+     * FrameworkID保存的节点.
+     */
+    public static final String FRAMEWORK_ID_NODE = ROOT + "/framework_id";
+    
+    /**
+     * 选举节点.
+     */
+    public static final String ELECTION_NODE = ROOT + "/election";
+}
diff --git a/elastic-job-cloud-scheduler/src/main/java/io/elasticjob/cloud/scheduler/ha/SchedulerElectionCandidate.java b/elastic-job-cloud-scheduler/src/main/java/io/elasticjob/cloud/scheduler/ha/SchedulerElectionCandidate.java
new file mode 100644
index 0000000..69d9aa5
--- /dev/null
+++ b/elastic-job-cloud-scheduler/src/main/java/io/elasticjob/cloud/scheduler/ha/SchedulerElectionCandidate.java
@@ -0,0 +1,55 @@
+/*
+ * Copyright 1999-2015 dangdang.com.
+ * <p>
+ * Licensed 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.
+ * </p>
+ */
+
+package io.elasticjob.cloud.scheduler.ha;
+
+import io.elasticjob.cloud.scheduler.mesos.SchedulerService;
+import io.elasticjob.cloud.reg.base.ElectionCandidate;
+import io.elasticjob.cloud.exception.JobSystemException;
+import io.elasticjob.cloud.reg.base.CoordinatorRegistryCenter;
+
+/**
+ * 调度器选举候选人.
+ *
+ * @author caohao
+ */
+public final class SchedulerElectionCandidate implements ElectionCandidate {
+    
+    private final CoordinatorRegistryCenter regCenter;
+    
+    private SchedulerService schedulerService;
+    
+    public SchedulerElectionCandidate(final CoordinatorRegistryCenter regCenter) {
+        this.regCenter = regCenter;
+    }
+    
+    @Override
+    public void startLeadership() throws Exception {
+        try {
+            schedulerService = new SchedulerService(regCenter);
+            schedulerService.start();
+            //CHECKSTYLE:OFF
+        } catch (final Throwable throwable) {
+            throw new JobSystemException(throwable);
+        }
+    }
+    
+    @Override
+    public void stopLeadership() {
+        schedulerService.stop();
+    }
+}
diff --git a/elastic-job-cloud-scheduler/src/main/java/io/elasticjob/cloud/scheduler/mesos/AppConstraintEvaluator.java b/elastic-job-cloud-scheduler/src/main/java/io/elasticjob/cloud/scheduler/mesos/AppConstraintEvaluator.java
new file mode 100644
index 0000000..ac24567
--- /dev/null
+++ b/elastic-job-cloud-scheduler/src/main/java/io/elasticjob/cloud/scheduler/mesos/AppConstraintEvaluator.java
@@ -0,0 +1,169 @@
+/*
+ * Copyright 1999-2015 dangdang.com.
+ * <p>
+ * Licensed 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.
+ * </p>
+ */
+
+package io.elasticjob.cloud.scheduler.mesos;
+
+import io.elasticjob.cloud.scheduler.config.app.CloudAppConfiguration;
+import io.elasticjob.cloud.scheduler.config.job.CloudJobConfiguration;
+import io.elasticjob.cloud.context.TaskContext;
+import com.google.common.base.Optional;
+import com.google.common.base.Preconditions;
+import com.netflix.fenzo.ConstraintEvaluator;
+import com.netflix.fenzo.TaskAssignmentResult;
+import com.netflix.fenzo.TaskRequest;
+import com.netflix.fenzo.TaskTrackerState;
+import com.netflix.fenzo.VirtualMachineCurrentState;
+import com.sun.jersey.api.client.ClientHandlerException;
+import com.sun.jersey.api.client.UniformInterfaceException;
+import lombok.AccessLevel;
+import lombok.RequiredArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
+import org.codehaus.jettison.json.JSONException;
+
+import java.util.ArrayList;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+
+/**
+ * App目标slave适配度限制器.
+ * 
+ * <p>
+ * 选择slave时需要考虑其上是否运行有App的executor,如果没有运行executor需要将其资源消耗考虑进适配计算算法中.
+ * </p>
+ * 
+ * @author gaohongtao
+ */
+@Slf4j
+@RequiredArgsConstructor(access = AccessLevel.PRIVATE)
+public final class AppConstraintEvaluator implements ConstraintEvaluator {
+    
+    private static AppConstraintEvaluator instance;
+    
+    private final Set<String> runningApps = new HashSet<>();
+    
+    private final FacadeService facadeService;
+    
+    /**
+     * 初始化.
+     * 
+     * @param facadeService 为Mesos提供的门面服务
+     */
+    public static void init(final FacadeService facadeService) {
+        instance = new AppConstraintEvaluator(facadeService);
+    }
+    
+    static AppConstraintEvaluator getInstance() {
+        Preconditions.checkNotNull(instance);
+        return instance;
+    }
+    
+    void loadAppRunningState() {
+        try {
+            for (MesosStateService.ExecutorStateInfo each : facadeService.loadExecutorInfo()) {
+                runningApps.add(each.getId());
+            }
+        } catch (final JSONException | UniformInterfaceException | ClientHandlerException e) {
+            clearAppRunningState();
+        }
+    }
+    
+    void clearAppRunningState() {
+        runningApps.clear();
+    }
+    
+    @Override
+    public String getName() {
+        return "App-Fitness-Calculator";
+    }
+    
+    @Override
+    public Result evaluate(final TaskRequest taskRequest, final VirtualMachineCurrentState targetVM, final TaskTrackerState taskTrackerState) {
+        double assigningCpus = 0.0d;
+        double assigningMemoryMB = 0.0d;
+        final String slaveId = targetVM.getAllCurrentOffers().iterator().next().getSlaveId().getValue();
+        try {
+            if (isAppRunningOnSlave(taskRequest.getId(), slaveId)) {
+                return new Result(true, "");
+            }
+            Set<String> calculatedApps = new HashSet<>();
+            List<TaskRequest> taskRequests = new ArrayList<>(targetVM.getTasksCurrentlyAssigned().size() + 1);
+            taskRequests.add(taskRequest);
+            for (TaskAssignmentResult each : targetVM.getTasksCurrentlyAssigned()) {
+                taskRequests.add(each.getRequest());
+            }
+            for (TaskRequest each : taskRequests) {
+                assigningCpus += each.getCPUs();
+                assigningMemoryMB += each.getMemory();
+                if (isAppRunningOnSlave(each.getId(), slaveId)) {
+                    continue;
+                }
+                CloudAppConfiguration assigningAppConfig = getAppConfiguration(each.getId());
+                if (!calculatedApps.add(assigningAppConfig.getAppName())) {
+                    continue;
+                }
+                assigningCpus += assigningAppConfig.getCpuCount();
+                assigningMemoryMB += assigningAppConfig.getMemoryMB();
+            }
+        } catch (final LackConfigException ex) {
+            log.warn("Lack config, disable {}", getName(), ex);
+            return new Result(true, "");
+        }
+        if (assigningCpus > targetVM.getCurrAvailableResources().cpuCores()) {
+            log.debug("Failure {} {} cpus:{}/{}", taskRequest.getId(), slaveId, assigningCpus, targetVM.getCurrAvailableResources().cpuCores());
+            return new Result(false, String.format("cpu:%s/%s", assigningCpus, targetVM.getCurrAvailableResources().cpuCores()));
+        }
+        if (assigningMemoryMB > targetVM.getCurrAvailableResources().memoryMB()) {
+            log.debug("Failure {} {} mem:{}/{}", taskRequest.getId(), slaveId, assigningMemoryMB, targetVM.getCurrAvailableResources().memoryMB());
+            return new Result(false, String.format("mem:%s/%s", assigningMemoryMB, targetVM.getCurrAvailableResources().memoryMB()));
+        }
+        log.debug("Success {} {} cpus:{}/{} mem:{}/{}", taskRequest.getId(), slaveId, assigningCpus, targetVM.getCurrAvailableResources()
+                .cpuCores(), assigningMemoryMB, targetVM.getCurrAvailableResources().memoryMB());
+        return new Result(true, String.format("cpus:%s/%s mem:%s/%s", assigningCpus, targetVM.getCurrAvailableResources()
+                .cpuCores(), assigningMemoryMB, targetVM.getCurrAvailableResources().memoryMB()));
+    }
+    
+    private boolean isAppRunningOnSlave(final String taskId, final String slaveId) throws LackConfigException {
+        TaskContext taskContext = TaskContext.from(taskId);
+        taskContext.setSlaveId(slaveId);
+        return runningApps.contains(taskContext.getExecutorId(getJobConfiguration(taskContext).getAppName()));
+    }
+    
+    private CloudAppConfiguration getAppConfiguration(final String taskId) throws LackConfigException {
+        CloudJobConfiguration jobConfig = getJobConfiguration(TaskContext.from(taskId));
+        Optional<CloudAppConfiguration> appConfigOptional = facadeService.loadAppConfig(jobConfig.getAppName());
+        if (!appConfigOptional.isPresent()) {
+            throw new LackConfigException("APP", jobConfig.getAppName());
+        }
+        return appConfigOptional.get();
+    }
+    
+    private CloudJobConfiguration getJobConfiguration(final TaskContext taskContext) throws LackConfigException {
+        Optional<CloudJobConfiguration> jobConfigOptional = facadeService.load(taskContext.getMetaInfo().getJobName());
+        if (!jobConfigOptional.isPresent()) {
+            throw new LackConfigException("JOB", taskContext.getMetaInfo().getJobName());
+        }
+        return jobConfigOptional.get();
+    }
+    
+    private class LackConfigException extends Exception {
+        
+        LackConfigException(final String scope, final String configName) {
+            super(String.format("Lack %s's config %s", scope, configName));
+        }
+    }
+}
diff --git a/elastic-job-cloud-scheduler/src/main/java/io/elasticjob/cloud/scheduler/mesos/FacadeService.java b/elastic-job-cloud-scheduler/src/main/java/io/elasticjob/cloud/scheduler/mesos/FacadeService.java
new file mode 100644
index 0000000..647fec7
--- /dev/null
+++ b/elastic-job-cloud-scheduler/src/main/java/io/elasticjob/cloud/scheduler/mesos/FacadeService.java
@@ -0,0 +1,348 @@
+/*
+ * Copyright 1999-2015 dangdang.com.
+ * <p>
+ * Licensed 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.
+ * </p>
+ */
+
+package io.elasticjob.cloud.scheduler.mesos;
+
+import io.elasticjob.cloud.scheduler.config.app.CloudAppConfigurationService;
+import io.elasticjob.cloud.scheduler.config.job.CloudJobConfiguration;
+import io.elasticjob.cloud.scheduler.state.disable.app.DisableAppService;
+import io.elasticjob.cloud.scheduler.state.disable.job.DisableJobService;
+import io.elasticjob.cloud.scheduler.state.running.RunningService;
+import io.elasticjob.cloud.context.ExecutionType;
+import io.elasticjob.cloud.scheduler.config.app.CloudAppConfiguration;
+import io.elasticjob.cloud.scheduler.config.job.CloudJobConfigurationService;
+import io.elasticjob.cloud.scheduler.config.job.CloudJobExecutionType;
+import io.elasticjob.cloud.scheduler.context.JobContext;
+import io.elasticjob.cloud.scheduler.state.failover.FailoverService;
+import io.elasticjob.cloud.scheduler.state.failover.FailoverTaskInfo;
+import io.elasticjob.cloud.scheduler.state.ready.ReadyService;
+import io.elasticjob.cloud.context.TaskContext;
+import io.elasticjob.cloud.reg.base.CoordinatorRegistryCenter;
+import com.google.common.base.Function;
+import com.google.common.base.Optional;
+import com.google.common.collect.Lists;
+import lombok.extern.slf4j.Slf4j;
+import org.codehaus.jettison.json.JSONException;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+/**
+ * 为Mesos提供的门面服务.
+ *
+ * @author zhangliang
+ * @author caohao
+ */
+@Slf4j
+public final class FacadeService {
+    
+    private final CloudAppConfigurationService appConfigService;
+    
+    private final CloudJobConfigurationService jobConfigService;
+    
+    private final ReadyService readyService;
+    
+    private final RunningService runningService;
+    
+    private final FailoverService failoverService;
+    
+    private final DisableAppService disableAppService;
+    
+    private final DisableJobService disableJobService;
+    
+    private final MesosStateService mesosStateService;
+    
+    public FacadeService(final CoordinatorRegistryCenter regCenter) {
+        appConfigService = new CloudAppConfigurationService(regCenter);
+        jobConfigService = new CloudJobConfigurationService(regCenter);
+        readyService = new ReadyService(regCenter);
+        runningService = new RunningService(regCenter);
+        failoverService = new FailoverService(regCenter);
+        disableAppService = new DisableAppService(regCenter);
+        disableJobService = new DisableJobService(regCenter);
+        mesosStateService = new MesosStateService(regCenter);
+    }
+    
+    /**
+     * 启动门面服务.
+     */
+    public void start() {
+        log.info("Elastic Job: Start facade service");
+        runningService.start();
+    }
+    
+    /**
+     * 获取有资格运行的作业.
+     * 
+     * @return 作业上下文集合
+     */
+    public Collection<JobContext> getEligibleJobContext() {
+        Collection<JobContext> failoverJobContexts = failoverService.getAllEligibleJobContexts();
+        Collection<JobContext> readyJobContexts = readyService.getAllEligibleJobContexts(failoverJobContexts);
+        Collection<JobContext> result = new ArrayList<>(failoverJobContexts.size() + readyJobContexts.size());
+        result.addAll(failoverJobContexts);
+        result.addAll(readyJobContexts);
+        return result;
+    }
+    
+    /**
+     * 从队列中删除已运行的作业.
+     * 
+     * @param taskContexts 任务上下文集合
+     */
+    public void removeLaunchTasksFromQueue(final List<TaskContext> taskContexts) {
+        List<TaskContext> failoverTaskContexts = new ArrayList<>(taskContexts.size());
+        Collection<String> readyJobNames = new HashSet<>(taskContexts.size(), 1);
+        for (TaskContext each : taskContexts) {
+            switch (each.getType()) {
+                case FAILOVER:
+                    failoverTaskContexts.add(each);
+                    break;
+                case READY:
+                    readyJobNames.add(each.getMetaInfo().getJobName());
+                    break;
+                default:
+                    break;
+            }
+        }
+        failoverService.remove(Lists.transform(failoverTaskContexts, new Function<TaskContext, TaskContext.MetaInfo>() {
+            
+            @Override
+            public TaskContext.MetaInfo apply(final TaskContext input) {
+                return input.getMetaInfo();
+            }
+        }));
+        readyService.remove(readyJobNames);
+    }
+    
+    /**
+     * 将任务运行时上下文放入运行时队列.
+     *
+     * @param taskContext 任务运行时上下文
+     */
+    public void addRunning(final TaskContext taskContext) {
+        runningService.add(taskContext);
+    }
+    
+    /**
+     * 更新常驻作业运行状态.
+     * 
+     * @param taskContext 任务运行时上下文
+     * @param isIdle 是否空闲
+     */
+    public void updateDaemonStatus(final TaskContext taskContext, final boolean isIdle) {
+        runningService.updateIdle(taskContext, isIdle);
+    }
+    
+    /**
+     * 将任务从运行时队列删除.
+     *
+     * @param taskContext 任务运行时上下文
+     */
+    public void removeRunning(final TaskContext taskContext) {
+        runningService.remove(taskContext);
+    }
+    
+    /**
+     * 记录失效转移队列.
+     * 
+     * @param taskContext 任务上下文
+     */
+    public void recordFailoverTask(final TaskContext taskContext) {
+        Optional<CloudJobConfiguration> jobConfigOptional = jobConfigService.load(taskContext.getMetaInfo().getJobName());
+        if (!jobConfigOptional.isPresent()) {
+            return;
+        }
+        if (isDisable(jobConfigOptional.get())) {
+            return;
+        }
+        CloudJobConfiguration jobConfig = jobConfigOptional.get();
+        if (jobConfig.getTypeConfig().getCoreConfig().isFailover() || CloudJobExecutionType.DAEMON == jobConfig.getJobExecutionType()) {
+            failoverService.add(taskContext);
+        }
+    }
+    
+    private boolean isDisable(final CloudJobConfiguration jobConfiguration) {
+        return disableAppService.isDisabled(jobConfiguration.getAppName()) || disableJobService.isDisabled(jobConfiguration.getJobName());
+    }
+    
+    /**
+     * 将瞬时作业放入待执行队列.
+     *
+     * @param jobName 作业名称
+     */
+    public void addTransient(final String jobName) {
+        readyService.addTransient(jobName);
+    }
+    
+    /**
+     * 根据作业名称获取云作业配置.
+     *
+     * @param jobName 作业名称
+     * @return 云作业配置
+     */
+    public Optional<CloudJobConfiguration> load(final String jobName) {
+        return jobConfigService.load(jobName);
+    }
+    
+    /**
+     * 根据作业应用名称获取云作业应用配置.
+     *
+     * @param appName 作业应用名称
+     * @return 云作业应用配置
+     */
+    public Optional<CloudAppConfiguration> loadAppConfig(final String appName) {
+        return appConfigService.load(appName);
+    }
+    
+    /**
+     * 根据作业元信息获取失效转移作业Id.
+     *
+     * @param metaInfo 作业元信息
+     * @return 失效转移作业Id
+     */
+    public Optional<String> getFailoverTaskId(final TaskContext.MetaInfo metaInfo) {
+        return failoverService.getTaskId(metaInfo);
+    }
+    
+    /**
+     * 将常驻作业放入待执行队列.
+     *
+     * @param jobName 作业名称
+     */
+    public void addDaemonJobToReadyQueue(final String jobName) {
+        Optional<CloudJobConfiguration> jobConfigOptional = jobConfigService.load(jobName);
+        if (!jobConfigOptional.isPresent()) {
+            return;
+        }
+        if (isDisable(jobConfigOptional.get())) {
+            return;
+        }
+        readyService.addDaemon(jobName);
+    }
+    
+    /**
+     * 根据作业执行类型判断作业是否在运行.
+     *
+     * <p>READY类型的作业为整体, 任意一片运行都视为作业运行. FAILOVER则仅以当前分片运行为运行依据.</p>
+     * 
+     * @param taskContext 任务运行时上下文
+     * @return 作业是否在运行
+     */
+    public boolean isRunning(final TaskContext taskContext) {
+        return ExecutionType.FAILOVER != taskContext.getType() && !runningService.getRunningTasks(taskContext.getMetaInfo().getJobName()).isEmpty()
+                || ExecutionType.FAILOVER == taskContext.getType() && runningService.isTaskRunning(taskContext.getMetaInfo());
+    }
+    
+    /**
+     * 添加任务主键和主机名称的映射.
+     *
+     * @param taskId 任务主键
+     * @param hostname 主机名称
+     */
+    public void addMapping(final String taskId, final String hostname) {
+        runningService.addMapping(taskId, hostname);
+    }
+    
+    /**
+     * 根据任务主键获取主机名称并清除该任务.
+     *
+     * @param taskId 任务主键
+     * @return 删除任务的主机名称
+     */
+    public String popMapping(final String taskId) {
+        return runningService.popMapping(taskId);
+    }
+    
+    /**
+     * 获取待运行的全部任务.
+     *
+     * @return 待运行的全部任务
+     */
+    public Map<String, Integer> getAllReadyTasks() {
+        return readyService.getAllReadyTasks();
+    }
+    
+    /**
+     * 获取所有运行中的任务.
+     *
+     * @return 运行中任务集合
+     */
+    public Map<String, Set<TaskContext>> getAllRunningTasks() {
+        return runningService.getAllRunningTasks();
+    }
+    
+    /**
+     * 获取待失效转移的全部任务.
+     *
+     * @return 待失效转移的全部任务
+     */
+    public Map<String, Collection<FailoverTaskInfo>> getAllFailoverTasks() {
+        return failoverService.getAllFailoverTasks();
+    }
+    
+    /**
+     * 判断作业是否被禁用.
+     * 
+     * @param jobName 作业名称
+     * @return 作业是否被禁用
+     */
+    public boolean isJobDisabled(final String jobName) {
+        Optional<CloudJobConfiguration> jobConfiguration = jobConfigService.load(jobName);
+        return !jobConfiguration.isPresent() || disableAppService.isDisabled(jobConfiguration.get().getAppName()) || disableJobService.isDisabled(jobName);
+    }
+    
+    /**
+     * 将作业移出禁用队列.
+     *
+     * @param jobName 作业名称
+     */
+    public void enableJob(final String jobName) {
+        disableJobService.remove(jobName);
+    }
+    
+    /**
+     * 将作业放入禁用队列.
+     *
+     * @param jobName 作业名称
+     */
+    public void disableJob(final String jobName) {
+        disableJobService.add(jobName);
+    }
+    
+    /**
+     * 获取所有正在运行的Executor的信息.
+     * 
+     * @return Executor信息集合
+     */
+    public Collection<MesosStateService.ExecutorStateInfo> loadExecutorInfo() throws JSONException {
+        return mesosStateService.executors();
+    }
+    
+    /**
+     * 停止门面服务.
+     */
+    public void stop() {
+        log.info("Elastic Job: Stop facade service");
+        // TODO 停止作业调度
+        runningService.clear();
+    }
+}
diff --git a/elastic-job-cloud-scheduler/src/main/java/io/elasticjob/cloud/scheduler/mesos/JobTaskRequest.java b/elastic-job-cloud-scheduler/src/main/java/io/elasticjob/cloud/scheduler/mesos/JobTaskRequest.java
new file mode 100644
index 0000000..f2c13e6
--- /dev/null
+++ b/elastic-job-cloud-scheduler/src/main/java/io/elasticjob/cloud/scheduler/mesos/JobTaskRequest.java
@@ -0,0 +1,106 @@
+/*
+ * Copyright 1999-2015 dangdang.com.
+ * <p>
+ * Licensed 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.
+ * </p>
+ */
+
+package io.elasticjob.cloud.scheduler.mesos;
+
+import io.elasticjob.cloud.scheduler.config.job.CloudJobConfiguration;
+import io.elasticjob.cloud.context.TaskContext;
+import com.netflix.fenzo.ConstraintEvaluator;
+import com.netflix.fenzo.TaskRequest;
+import com.netflix.fenzo.VMTaskFitnessCalculator;
+import lombok.RequiredArgsConstructor;
+
+import java.util.Collections;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * 作业任务请求对象.
+ *
+ * @author zhangliang
+ */
+@RequiredArgsConstructor
+public final class JobTaskRequest implements TaskRequest {
+    
+    private final TaskContext taskContext;
+    
+    private final CloudJobConfiguration jobConfig;
+    
+    @Override
+    public String getId() {
+        return taskContext.getId();
+    }
+    
+    @Override
+    public String taskGroupName() {
+        return "";
+    }
+    
+    @Override
+    public double getCPUs() {
+        return jobConfig.getCpuCount();
+    }
+    
+    @Override
+    public double getMemory() {
+        return jobConfig.getMemoryMB();
+    }
+    
+    @Override
+    public double getNetworkMbps() {
+        return 0;
+    }
+    
+    @Override
+    public double getDisk() {
+        return 10d;
+    }
+    
+    @Override
+    public int getPorts() {
+        return 1;
+    }
+    
+    @Override
+    public Map<String, Double> getScalarRequests() {
+        return null;
+    }
+    
+    @Override
+    public List<? extends ConstraintEvaluator> getHardConstraints() {
+        return Collections.singletonList(AppConstraintEvaluator.getInstance());
+    }
+    
+    @Override
+    public List<? extends VMTaskFitnessCalculator> getSoftConstraints() {
+        return null;
+    }
+    
+    @Override
+    public void setAssignedResources(final AssignedResources assignedResources) {
+    }
+    
+    @Override
+    public AssignedResources getAssignedResources() {
+        return null;
+    }
+    
+    @Override
+    public Map<String, NamedResourceSetRequest> getCustomNamedResources() {
+        return Collections.emptyMap();
+    }
+}
diff --git a/elastic-job-cloud-scheduler/src/main/java/io/elasticjob/cloud/scheduler/mesos/LaunchingTasks.java b/elastic-job-cloud-scheduler/src/main/java/io/elasticjob/cloud/scheduler/mesos/LaunchingTasks.java
new file mode 100644
index 0000000..2e3b5b0
--- /dev/null
+++ b/elastic-job-cloud-scheduler/src/main/java/io/elasticjob/cloud/scheduler/mesos/LaunchingTasks.java
@@ -0,0 +1,96 @@
+/*
+ * Copyright 1999-2015 dangdang.com.
+ * <p>
+ * Licensed 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.
+ * </p>
+ */
+
+package io.elasticjob.cloud.scheduler.mesos;
+
+import io.elasticjob.cloud.context.ExecutionType;
+import io.elasticjob.cloud.scheduler.context.JobContext;
+import io.elasticjob.cloud.context.TaskContext;
+import com.netflix.fenzo.TaskAssignmentResult;
+import com.netflix.fenzo.TaskRequest;
+import com.netflix.fenzo.VMAssignmentResult;
+import lombok.extern.slf4j.Slf4j;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * 分配任务行为包.
+ *
+ * @author zhangliang
+ */
+@Slf4j
+public final class LaunchingTasks {
+    
+    private final Map<String, JobContext> eligibleJobContextsMap;
+    
+    public LaunchingTasks(final Collection<JobContext> eligibleJobContexts) {
+        eligibleJobContextsMap = new HashMap<>(eligibleJobContexts.size(), 1);
+        for (JobContext each : eligibleJobContexts) {
+            eligibleJobContextsMap.put(each.getJobConfig().getJobName(), each);
+        }
+    }
+    
+    List<TaskRequest> getPendingTasks() {
+        List<TaskRequest> result = new ArrayList<>(eligibleJobContextsMap.size() * 10);
+        for (JobContext each : eligibleJobContextsMap.values()) {
+            result.addAll(createTaskRequests(each));
+        }
+        return result;
+    }
+    
+    private Collection<TaskRequest> createTaskRequests(final JobContext jobContext) {
+        Collection<TaskRequest> result = new ArrayList<>(jobContext.getAssignedShardingItems().size());
+        for (int each : jobContext.getAssignedShardingItems()) {
+            result.add(new JobTaskRequest(new TaskContext(jobContext.getJobConfig().getJobName(), Collections.singletonList(each), jobContext.getType()), jobContext.getJobConfig()));
+        }
+        return result;
+    }
+    
+    Collection<String> getIntegrityViolationJobs(final Collection<VMAssignmentResult> vmAssignmentResults) {
+        Map<String, Integer> assignedJobShardingTotalCountMap = getAssignedJobShardingTotalCountMap(vmAssignmentResults);
+        Collection<String> result = new HashSet<>(assignedJobShardingTotalCountMap.size(), 1);
+        for (Map.Entry<String, Integer> entry : assignedJobShardingTotalCountMap.entrySet()) {
+            JobContext jobContext = eligibleJobContextsMap.get(entry.getKey());
+            if (ExecutionType.FAILOVER != jobContext.getType() && !entry.getValue().equals(jobContext.getJobConfig().getTypeConfig().getCoreConfig().getShardingTotalCount())) {
+                log.warn("Job {} is not assigned at this time, because resources not enough to run all sharding instances.", entry.getKey());
+                result.add(entry.getKey());
+            }
+        }
+        return result;
+    }
+    
+    private Map<String, Integer> getAssignedJobShardingTotalCountMap(final Collection<VMAssignmentResult> vmAssignmentResults) {
+        Map<String, Integer> result = new HashMap<>(eligibleJobContextsMap.size(), 1);
+        for (VMAssignmentResult vmAssignmentResult: vmAssignmentResults) {
+            for (TaskAssignmentResult tasksAssigned: vmAssignmentResult.getTasksAssigned()) {
+                String jobName = TaskContext.from(tasksAssigned.getTaskId()).getMetaInfo().getJobName();
+                if (result.containsKey(jobName)) {
+                    result.put(jobName, result.get(jobName) + 1);
+                } else {
+                    result.put(jobName, 1);
+                }
+            }
+        }
+        return result;
+    }
+}
diff --git a/elastic-job-cloud-scheduler/src/main/java/io/elasticjob/cloud/scheduler/mesos/LeasesQueue.java b/elastic-job-cloud-scheduler/src/main/java/io/elasticjob/cloud/scheduler/mesos/LeasesQueue.java
new file mode 100644
index 0000000..824b1be
--- /dev/null
+++ b/elastic-job-cloud-scheduler/src/main/java/io/elasticjob/cloud/scheduler/mesos/LeasesQueue.java
@@ -0,0 +1,71 @@
+/*
+ * Copyright 1999-2015 dangdang.com.
+ * <p>
+ * Licensed 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.
+ * </p>
+ */
+
+package io.elasticjob.cloud.scheduler.mesos;
+
+import com.netflix.fenzo.VirtualMachineLease;
+import com.netflix.fenzo.plugins.VMLeaseObject;
+import lombok.AccessLevel;
+import lombok.NoArgsConstructor;
+import org.apache.mesos.Protos;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.concurrent.BlockingQueue;
+import java.util.concurrent.LinkedBlockingQueue;
+
+/**
+ * 资源预占队列.
+ *
+ * @author zhangliang
+ */
+@NoArgsConstructor(access = AccessLevel.PRIVATE)
+public final class LeasesQueue {
+    
+    private static final LeasesQueue INSTANCE = new LeasesQueue();
+    
+    private final BlockingQueue<VirtualMachineLease> queue = new LinkedBlockingQueue<>();
+    
+    /**
+     * 获取实例.
+     * 
+     * @return 单例对象
+     */
+    public static LeasesQueue getInstance() {
+        return INSTANCE;
+    }
+    
+    /**
+     * 添加资源至队列预占.
+     *
+     * @param offer 资源
+     */
+    public void offer(final Protos.Offer offer) {
+        queue.offer(new VMLeaseObject(offer));
+    }
+    
+    /**
+     * 出栈队列资源.
+     * 
+     * @return 队列资源集合
+     */
+    public List<VirtualMachineLease> drainTo() {
+        List<VirtualMachineLease> result = new ArrayList<>(queue.size());
+        queue.drainTo(result);
+        return result;
+    }
+}
diff --git a/elastic-job-cloud-scheduler/src/main/java/io/elasticjob/cloud/scheduler/mesos/MesosStateService.java b/elastic-job-cloud-scheduler/src/main/java/io/elasticjob/cloud/scheduler/mesos/MesosStateService.java
new file mode 100644
index 0000000..c53bcc4
--- /dev/null
+++ b/elastic-job-cloud-scheduler/src/main/java/io/elasticjob/cloud/scheduler/mesos/MesosStateService.java
@@ -0,0 +1,179 @@
+/*
+ * Copyright 1999-2015 dangdang.com.
+ * <p>
+ * Licensed 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.
+ * </p>
+ */
+
+package io.elasticjob.cloud.scheduler.mesos;
+
+import io.elasticjob.cloud.scheduler.ha.FrameworkIDService;
+import io.elasticjob.cloud.reg.base.CoordinatorRegistryCenter;
+import com.google.common.base.Function;
+import com.google.common.base.Optional;
+import com.google.common.base.Preconditions;
+import com.google.common.base.Strings;
+import com.google.common.collect.Collections2;
+import com.google.common.collect.Lists;
+import com.google.gson.JsonArray;
+import com.google.gson.JsonObject;
+import com.sun.jersey.api.client.Client;
+import lombok.Builder;
+import lombok.Getter;
+import lombok.extern.slf4j.Slf4j;
+import org.codehaus.jettison.json.JSONArray;
+import org.codehaus.jettison.json.JSONException;
+import org.codehaus.jettison.json.JSONObject;
+
+import java.util.Collection;
+import java.util.List;
+
+/**
+ * Mesos状态服务.
+ * 
+ * @author gaohongtao
+ */
+@Slf4j
+public class MesosStateService {
+    
+    private static String stateUrl;
+    
+    private final FrameworkIDService frameworkIDService;
+    
+    public MesosStateService(final CoordinatorRegistryCenter regCenter) {
+        frameworkIDService = new FrameworkIDService(regCenter);
+    }
+    
+    /**
+     * 注册Mesos的Master信息.
+     * 
+     * @param hostName Master的主机名
+     * @param port Master端口
+     */
+    public static synchronized void register(final String hostName, final int port) {
+        stateUrl = String.format("http://%s:%d/state", hostName, port);
+    }
+    
+    /**
+     * 注销Mesos的Master信息.
+     */
+    public static synchronized void deregister() {
+        stateUrl = null;
+    }
+    
+    /**
+     * 获取沙箱信息.
+     * 
+     * @param appName 作业云配置App的名字
+     * @return 沙箱信息
+     * @throws JSONException 解析JSON格式异常
+     */
+    public JsonArray sandbox(final String appName) throws JSONException {
+        JSONObject state = fetch(stateUrl);
+        JsonArray result = new JsonArray();
+        for (JSONObject each : findExecutors(state.getJSONArray("frameworks"), appName)) {
+            JSONArray slaves = state.getJSONArray("slaves");
+            String slaveHost = null;
+            for (int i = 0; i < slaves.length(); i++) {
+                JSONObject slave = slaves.getJSONObject(i);
+                if (each.getString("slave_id").equals(slave.getString("id"))) {
+                    slaveHost = slave.getString("pid").split("@")[1];
+                }
+            }
+            Preconditions.checkNotNull(slaveHost);
+            JSONObject slaveState = fetch(String.format("http://%s/state", slaveHost));
+            String workDir = slaveState.getJSONObject("flags").getString("work_dir");
+            Collection<JSONObject> executorsOnSlave = findExecutors(slaveState.getJSONArray("frameworks"), appName);
+            for (JSONObject executorOnSlave : executorsOnSlave) {
+                JsonObject r = new JsonObject();
+                r.addProperty("hostname", slaveState.getString("hostname"));
+                r.addProperty("path", executorOnSlave.getString("directory").replace(workDir, ""));
+                result.add(r);
+            }
+        }
+        return result;
+    }
+    
+    /**
+     * 查找执行器信息.
+     * 
+     * @param appName 作业云配置App的名字
+     * @return 执行器信息
+     * @throws JSONException 解析JSON格式异常
+     */
+    public Collection<ExecutorStateInfo> executors(final String appName) throws JSONException {
+        return Collections2.transform(findExecutors(fetch(stateUrl).getJSONArray("frameworks"), appName), new Function<JSONObject, ExecutorStateInfo>() {
+            @Override
+            public ExecutorStateInfo apply(final JSONObject input) {
+                try {
+                    return ExecutorStateInfo.builder().id(getExecutorId(input)).slaveId(input.getString("slave_id")).build();
+                } catch (final JSONException ex) {
+                    throw new RuntimeException(ex);
+                }
+            }
+        });
+    }
+    
+    /**
+     * 获取所有执行器.
+     *
+     * @return 执行器信息
+     * @throws JSONException 解析JSON格式异常
+     */
+    public Collection<ExecutorStateInfo> executors() throws JSONException {
+        return executors(null);
+    }
+    
+    private JSONObject fetch(final String url) {
+        Preconditions.checkState(!Strings.isNullOrEmpty(url));
+        return Client.create().resource(url).get(JSONObject.class);
+    }
+    
+    private Collection<JSONObject> findExecutors(final JSONArray frameworks, final String appName) throws JSONException {
+        List<JSONObject> result = Lists.newArrayList();
+        Optional<String> frameworkIDOptional = frameworkIDService.fetch();
+        String frameworkID;
+        if (frameworkIDOptional.isPresent()) {
+            frameworkID = frameworkIDOptional.get();
+        } else {
+            return result;
+        }
+        for (int i = 0; i < frameworks.length(); i++) {
+            JSONObject framework = frameworks.getJSONObject(i);
+            if (!framework.getString("id").equals(frameworkID)) {
+                continue;
+            }
+            JSONArray executors = framework.getJSONArray("executors");
+            for (int j = 0; j < executors.length(); j++) {
+                JSONObject executor = executors.getJSONObject(j);
+                if (null == appName || appName.equals(getExecutorId(executor).split("@-@")[0])) {
+                    result.add(executor);
+                }
+            }
+        }
+        return result;
+    }
+    
+    private String getExecutorId(final JSONObject executor) throws JSONException {
+        return executor.has("id") ? executor.getString("id") : executor.getString("executor_id");
+    }
+    
+    @Builder
+    @Getter
+    public static final class ExecutorStateInfo {
+        
+        private final String id;
+        
+        private final String slaveId;
+    }
+}
diff --git a/elastic-job-cloud-scheduler/src/main/java/io/elasticjob/cloud/scheduler/mesos/ReconcileService.java b/elastic-job-cloud-scheduler/src/main/java/io/elasticjob/cloud/scheduler/mesos/ReconcileService.java
new file mode 100644
index 0000000..08a03e9
--- /dev/null
+++ b/elastic-job-cloud-scheduler/src/main/java/io/elasticjob/cloud/scheduler/mesos/ReconcileService.java
@@ -0,0 +1,108 @@
+/*
+ * Copyright 1999-2015 dangdang.com.
+ * <p>
+ * Licensed 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.
+ * </p>
+ */
+
+package io.elasticjob.cloud.scheduler.mesos;
+
+import io.elasticjob.cloud.scheduler.env.BootstrapEnvironment;
+import io.elasticjob.cloud.scheduler.env.FrameworkConfiguration;
+import io.elasticjob.cloud.context.TaskContext;
+import com.google.common.base.Function;
+import com.google.common.collect.Collections2;
+import com.google.common.util.concurrent.AbstractScheduledService;
+import lombok.RequiredArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
+import org.apache.mesos.Protos;
+import org.apache.mesos.SchedulerDriver;
+
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.Set;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.locks.ReentrantLock;
+
+/**
+ * 协调Mesos与调度器之间的作业状态.
+ * 
+ * @author gaohongtao
+ */
+@RequiredArgsConstructor
+@Slf4j
+public class ReconcileService extends AbstractScheduledService {
+    
+    private final SchedulerDriver schedulerDriver;
+    
+    private final FacadeService facadeService;
+    
+    private final ReentrantLock lock = new ReentrantLock();
+    
+    @Override
+    protected void runOneIteration() throws Exception {
+        lock.lock();
+        try {
+            explicitReconcile();
+            implicitReconcile();
+        } finally {
+            lock.unlock();
+        }
+    }
+    
+    /**
+     * 全量的显示协调.
+     */
+    public void explicitReconcile() {
+        lock.lock();
+        try {
+            Set<TaskContext> runningTask = new HashSet<>();
+            for (Set<TaskContext> each : facadeService.getAllRunningTasks().values()) {
+                runningTask.addAll(each);
+            }
+            if (runningTask.isEmpty()) {
+                return;
+            }
+            log.info("Requesting {} tasks reconciliation with the Mesos master", runningTask.size());
+            schedulerDriver.reconcileTasks(Collections2.transform(runningTask, new Function<TaskContext, Protos.TaskStatus>() {
+                @Override
+                public Protos.TaskStatus apply(final TaskContext input) {
+                    return Protos.TaskStatus.newBuilder()
+                            .setTaskId(Protos.TaskID.newBuilder().setValue(input.getId()).build())
+                            .setSlaveId(Protos.SlaveID.newBuilder().setValue(input.getSlaveId()).build())
+                            .setState(Protos.TaskState.TASK_RUNNING).build();
+                }
+            }));
+        } finally {
+            lock.unlock();
+        }
+    }
+    
+    /**
+     * 隐式协调.
+     */
+    public void implicitReconcile() {
+        lock.lock();
+        try {
+            schedulerDriver.reconcileTasks(Collections.<Protos.TaskStatus>emptyList());
+        } finally {
+            lock.unlock();
+        }
+    }
+    
+    @Override
+    protected Scheduler scheduler() {
+        FrameworkConfiguration configuration = BootstrapEnvironment.getInstance().getFrameworkConfiguration();
+        return Scheduler.newFixedDelaySchedule(configuration.getReconcileIntervalMinutes(), configuration.getReconcileIntervalMinutes(), TimeUnit.MINUTES);
+    }
+}
diff --git a/elastic-job-cloud-scheduler/src/main/java/io/elasticjob/cloud/scheduler/mesos/SchedulerEngine.java b/elastic-job-cloud-scheduler/src/main/java/io/elasticjob/cloud/scheduler/mesos/SchedulerEngine.java
new file mode 100644
index 0000000..eefb38c
--- /dev/null
+++ b/elastic-job-cloud-scheduler/src/main/java/io/elasticjob/cloud/scheduler/mesos/SchedulerEngine.java
@@ -0,0 +1,168 @@
+/*
+ * Copyright 1999-2015 dangdang.com.
+ * <p>
+ * Licensed 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.
+ * </p>
+ */
+
+package io.elasticjob.cloud.scheduler.mesos;
+
+import io.elasticjob.cloud.scheduler.statistics.StatisticManager;
+import io.elasticjob.cloud.scheduler.ha.FrameworkIDService;
+import io.elasticjob.cloud.context.TaskContext;
+import io.elasticjob.cloud.event.JobEventBus;
+import io.elasticjob.cloud.event.type.JobStatusTraceEvent;
+import com.netflix.fenzo.TaskScheduler;
+import lombok.RequiredArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
+import org.apache.mesos.Protos;
+import org.apache.mesos.Scheduler;
+import org.apache.mesos.SchedulerDriver;
+
+import java.util.List;
+
+/**
+ * 作业云引擎.
+ *
+ * @author zhangliang
+ */
+@RequiredArgsConstructor
+@Slf4j
+public final class SchedulerEngine implements Scheduler {
+    
+    private final TaskScheduler taskScheduler;
+    
+    private final FacadeService facadeService;
+    
+    private final JobEventBus jobEventBus;
+    
+    private final FrameworkIDService frameworkIDService;
+    
+    private final StatisticManager statisticManager;
+    
+    @Override
+    public void registered(final SchedulerDriver schedulerDriver, final Protos.FrameworkID frameworkID, final Protos.MasterInfo masterInfo) {
+        log.info("call registered");
+        frameworkIDService.save(frameworkID.getValue());
+        taskScheduler.expireAllLeases();
+        MesosStateService.register(masterInfo.getHostname(), masterInfo.getPort());
+    }
+    
+    @Override
+    public void reregistered(final SchedulerDriver schedulerDriver, final Protos.MasterInfo masterInfo) {
+        log.info("call reregistered");
+        taskScheduler.expireAllLeases();
+        MesosStateService.register(masterInfo.getHostname(), masterInfo.getPort());
+    }
+    
+    @Override
+    public void resourceOffers(final SchedulerDriver schedulerDriver, final List<Protos.Offer> offers) {
+        for (Protos.Offer offer: offers) {
+            log.trace("Adding offer {} from host {}", offer.getId(), offer.getHostname());
+            LeasesQueue.getInstance().offer(offer);
+        }
+    }
+    
+    @Override
+    public void offerRescinded(final SchedulerDriver schedulerDriver, final Protos.OfferID offerID) {
+        log.trace("call offerRescinded: {}", offerID);
+        taskScheduler.expireLease(offerID.getValue());
+    }
+    
+    @Override
+    public void statusUpdate(final SchedulerDriver schedulerDriver, final Protos.TaskStatus taskStatus) {
+        String taskId = taskStatus.getTaskId().getValue();
+        TaskContext taskContext = TaskContext.from(taskId);
+        String jobName = taskContext.getMetaInfo().getJobName();
+        log.trace("call statusUpdate task state is: {}, task id is: {}", taskStatus.getState(), taskId);
+        jobEventBus.post(new JobStatusTraceEvent(jobName, taskContext.getId(), taskContext.getSlaveId(), JobStatusTraceEvent.Source.CLOUD_SCHEDULER, 
+                taskContext.getType(), String.valueOf(taskContext.getMetaInfo().getShardingItems()), JobStatusTraceEvent.State.valueOf(taskStatus.getState().name()), taskStatus.getMessage()));
+        switch (taskStatus.getState()) {
+            case TASK_RUNNING:
+                if (!facadeService.load(jobName).isPresent()) {
+                    schedulerDriver.killTask(Protos.TaskID.newBuilder().setValue(taskId).build());
+                }
+                if ("BEGIN".equals(taskStatus.getMessage())) {
+                    facadeService.updateDaemonStatus(taskContext, false);
+                } else if ("COMPLETE".equals(taskStatus.getMessage())) {
+                    facadeService.updateDaemonStatus(taskContext, true);
+                    statisticManager.taskRunSuccessfully();
+                }
+                break;
+            case TASK_FINISHED:
+                facadeService.removeRunning(taskContext);
+                unAssignTask(taskId);
+                statisticManager.taskRunSuccessfully();
+                break;
+            case TASK_KILLED:
+                log.warn("task id is: {}, status is: {}, message is: {}, source is: {}", taskId, taskStatus.getState(), taskStatus.getMessage(), taskStatus.getSource());
+                facadeService.removeRunning(taskContext);
+                facadeService.addDaemonJobToReadyQueue(jobName);
+                unAssignTask(taskId);
+                break;
+            case TASK_LOST:
+            case TASK_DROPPED:
+            case TASK_GONE:
+            case TASK_GONE_BY_OPERATOR:
+            case TASK_FAILED:
+            case TASK_ERROR:
+                log.warn("task id is: {}, status is: {}, message is: {}, source is: {}", taskId, taskStatus.getState(), taskStatus.getMessage(), taskStatus.getSource());
+                facadeService.removeRunning(taskContext);
+                facadeService.recordFailoverTask(taskContext);
+                unAssignTask(taskId);
+                statisticManager.taskRunFailed();
+                break;
+            case TASK_UNKNOWN:
+            case TASK_UNREACHABLE:
+                log.error("task id is: {}, status is: {}, message is: {}, source is: {}", taskId, taskStatus.getState(), taskStatus.getMessage(), taskStatus.getSource());
+                statisticManager.taskRunFailed();
+                break;
+            default:
+                break;
+        }
+    }
+    
+    private void unAssignTask(final String taskId) {
+        String hostname = facadeService.popMapping(taskId);
+        if (null != hostname) {
+            taskScheduler.getTaskUnAssigner().call(TaskContext.getIdForUnassignedSlave(taskId), hostname);
+        }
+    }
+    
+    @Override
+    public void frameworkMessage(final SchedulerDriver schedulerDriver, final Protos.ExecutorID executorID, final Protos.SlaveID slaveID, final byte[] bytes) {
+        log.trace("call frameworkMessage slaveID: {}, bytes: {}", slaveID, new String(bytes));
+    }
+    
+    @Override
+    public void disconnected(final SchedulerDriver schedulerDriver) {
+        log.warn("call disconnected");
+        MesosStateService.deregister();
+    }
+    
+    @Override
+    public void slaveLost(final SchedulerDriver schedulerDriver, final Protos.SlaveID slaveID) {
+        log.warn("call slaveLost slaveID is: {}", slaveID);
+        taskScheduler.expireAllLeasesByVMId(slaveID.getValue());
+    }
+    
+    @Override
+    public void executorLost(final SchedulerDriver schedulerDriver, final Protos.ExecutorID executorID, final Protos.SlaveID slaveID, final int i) {
+        log.warn("call executorLost slaveID is: {}, executorID is: {}", slaveID, executorID);
+    }
+    
+    @Override
+    public void error(final SchedulerDriver schedulerDriver, final String message) {
+        log.error("call error, message is: {}", message);
+    }
+}
diff --git a/elastic-job-cloud-scheduler/src/main/java/io/elasticjob/cloud/scheduler/mesos/SchedulerService.java b/elastic-job-cloud-scheduler/src/main/java/io/elasticjob/cloud/scheduler/mesos/SchedulerService.java
new file mode 100644
index 0000000..6ec0e49
--- /dev/null
+++ b/elastic-job-cloud-scheduler/src/main/java/io/elasticjob/cloud/scheduler/mesos/SchedulerService.java
@@ -0,0 +1,157 @@
+/*
+ * Copyright 1999-2015 dangdang.com.
+ * <p>
+ * Licensed 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.
+ * </p>
+ */
+
+package io.elasticjob.cloud.scheduler.mesos;
+
+import io.elasticjob.cloud.scheduler.restful.RestfulService;
+import io.elasticjob.cloud.scheduler.statistics.StatisticManager;
+import io.elasticjob.cloud.event.rdb.JobEventRdbConfiguration;
+import io.elasticjob.cloud.scheduler.config.job.CloudJobConfigurationListener;
+import io.elasticjob.cloud.scheduler.env.BootstrapEnvironment;
+import io.elasticjob.cloud.scheduler.env.MesosConfiguration;
+import io.elasticjob.cloud.scheduler.ha.FrameworkIDService;
+import io.elasticjob.cloud.scheduler.producer.ProducerManager;
+import io.elasticjob.cloud.event.JobEventBus;
+import io.elasticjob.cloud.reg.base.CoordinatorRegistryCenter;
+import com.google.common.base.Optional;
+import com.google.common.util.concurrent.Service;
+import com.netflix.fenzo.TaskScheduler;
+import com.netflix.fenzo.VirtualMachineLease;
+import com.netflix.fenzo.functions.Action1;
+import lombok.AllArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
+import org.apache.mesos.MesosSchedulerDriver;
+import org.apache.mesos.Protos;
+import org.apache.mesos.SchedulerDriver;
+
+/**
+ * 调度服务.
+ *
+ * @author zhangliang
+ * @author caohao
+ */
+@Slf4j
+@AllArgsConstructor
+public final class SchedulerService {
+    
+    private static final String WEB_UI_PROTOCOL = "http://";
+    
+    private final BootstrapEnvironment env;
+    
+    private final FacadeService facadeService;
+    
+    private final SchedulerDriver schedulerDriver;
+    
+    private final ProducerManager producerManager;
+    
+    private final StatisticManager statisticManager;
+    
+    private final CloudJobConfigurationListener cloudJobConfigurationListener;
+    
+    private final Service taskLaunchScheduledService;
+    
+    private final RestfulService restfulService;
+    
+    private final ReconcileService reconcileService;
+    
+    public SchedulerService(final CoordinatorRegistryCenter regCenter) {
+        env = BootstrapEnvironment.getInstance();
+        facadeService = new FacadeService(regCenter);
+        statisticManager = StatisticManager.getInstance(regCenter, env.getJobEventRdbConfiguration());
+        TaskScheduler taskScheduler = getTaskScheduler();
+        JobEventBus jobEventBus = getJobEventBus();
+        schedulerDriver = getSchedulerDriver(taskScheduler, jobEventBus, new FrameworkIDService(regCenter));
+        producerManager = new ProducerManager(schedulerDriver, regCenter);
+        cloudJobConfigurationListener =  new CloudJobConfigurationListener(regCenter, producerManager);
+        taskLaunchScheduledService = new TaskLaunchScheduledService(schedulerDriver, taskScheduler, facadeService, jobEventBus);
+        reconcileService = new ReconcileService(schedulerDriver, facadeService);
+        restfulService = new RestfulService(regCenter, env.getRestfulServerConfiguration(), producerManager, reconcileService);
+    }
+    
+    private SchedulerDriver getSchedulerDriver(final TaskScheduler taskScheduler, final JobEventBus jobEventBus, final FrameworkIDService frameworkIDService) {
+        Optional<String> frameworkIDOptional = frameworkIDService.fetch();
+        Protos.FrameworkInfo.Builder builder = Protos.FrameworkInfo.newBuilder();
+        if (frameworkIDOptional.isPresent()) {
+            builder.setId(Protos.FrameworkID.newBuilder().setValue(frameworkIDOptional.get()).build());
+        }
+        Optional<String> role = env.getMesosRole();
+        String frameworkName = MesosConfiguration.FRAMEWORK_NAME;
+        if (role.isPresent()) {
+            builder.setRole(role.get());
+            frameworkName += "-" + role.get();
+        }
+        builder.addCapabilitiesBuilder().setType(Protos.FrameworkInfo.Capability.Type.PARTITION_AWARE);
+        MesosConfiguration mesosConfig = env.getMesosConfiguration();
+        Protos.FrameworkInfo frameworkInfo = builder.setUser(mesosConfig.getUser()).setName(frameworkName)
+                .setHostname(mesosConfig.getHostname()).setFailoverTimeout(MesosConfiguration.FRAMEWORK_FAILOVER_TIMEOUT_SECONDS)
+                .setWebuiUrl(WEB_UI_PROTOCOL + env.getFrameworkHostPort()).setCheckpoint(true).build();
+        return new MesosSchedulerDriver(new SchedulerEngine(taskScheduler, facadeService, jobEventBus, frameworkIDService, statisticManager), frameworkInfo, mesosConfig.getUrl());
+    }
+    
+    private TaskScheduler getTaskScheduler() {
+        return new TaskScheduler.Builder()
+                .withLeaseOfferExpirySecs(1000000000L)
+                .withLeaseRejectAction(new Action1<VirtualMachineLease>() {
+                    
+                    @Override
+                    public void call(final VirtualMachineLease lease) {
+                        log.warn("Declining offer on '{}'", lease.hostname());
+                        schedulerDriver.declineOffer(lease.getOffer().getId());
+                    }
+                }).build();
+    }
+    
+    private JobEventBus getJobEventBus() {
+        Optional<JobEventRdbConfiguration> rdbConfig = env.getJobEventRdbConfiguration();
+        if (rdbConfig.isPresent()) {
+            return new JobEventBus(rdbConfig.get());
+        }
+        return new JobEventBus();
+    }
+    
+    /**
+     * 以守护进程方式启动.
+     */
+    public void start() {
+        facadeService.start();
+        producerManager.startup();
+        statisticManager.startup();
+        cloudJobConfigurationListener.start();
+        taskLaunchScheduledService.startAsync();
+        restfulService.start();
+        schedulerDriver.start();
+        if (env.getFrameworkConfiguration().isEnabledReconcile()) {
+            reconcileService.startAsync();
+        }
+    }
+    
+    /**
+     * 停止运行.
+     */
+    public void stop() {
+        restfulService.stop();
+        taskLaunchScheduledService.stopAsync();
+        cloudJobConfigurationListener.stop();
+        statisticManager.shutdown();
+        producerManager.shutdown();
+        schedulerDriver.stop(true);
+        facadeService.stop();
+        if (env.getFrameworkConfiguration().isEnabledReconcile()) {
+            reconcileService.stopAsync();
+        }
+    }
+}
diff --git a/elastic-job-cloud-scheduler/src/main/java/io/elasticjob/cloud/scheduler/mesos/SupportedExtractionType.java b/elastic-job-cloud-scheduler/src/main/java/io/elasticjob/cloud/scheduler/mesos/SupportedExtractionType.java
new file mode 100644
index 0000000..fc0b18b
--- /dev/null
+++ b/elastic-job-cloud-scheduler/src/main/java/io/elasticjob/cloud/scheduler/mesos/SupportedExtractionType.java
@@ -0,0 +1,60 @@
+/*
+ * Copyright 1999-2015 dangdang.com.
+ * <p>
+ * Licensed 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.
+ * </p>
+ */
+
+package io.elasticjob.cloud.scheduler.mesos;
+
+import lombok.NoArgsConstructor;
+
+import java.util.HashSet;
+import java.util.Set;
+
+/**
+ * Mesos所支持的压缩类型.
+ *
+ * @author zhangliang
+ */
+@NoArgsConstructor
+public final class SupportedExtractionType {
+    
+    private static final Set<String> EXTRACTION_TYPES = new HashSet<>(9, 1);
+    
+    static {
+        EXTRACTION_TYPES.add(".tar");
+        EXTRACTION_TYPES.add(".tar.gz");
+        EXTRACTION_TYPES.add(".tar.bz2");
+        EXTRACTION_TYPES.add(".tar.xz");
+        EXTRACTION_TYPES.add(".gz");
+        EXTRACTION_TYPES.add(".tgz");
+        EXTRACTION_TYPES.add(".tbz2");
+        EXTRACTION_TYPES.add(".txz");
+        EXTRACTION_TYPES.add(".zip");
+    }
+    
+    /**
+     * 判断URL的文件是否为压缩格式.
+     * @param appURL 应用URL地址
+     * @return URL的文件是否为压缩格式
+     */
+    public static boolean isExtraction(final String appURL) {
+        for (String each : EXTRACTION_TYPES) {
+            if (appURL.endsWith(each)) {
+                return true;
+            }
+        }
+        return false;
+    }
+}
diff --git a/elastic-job-cloud-scheduler/src/main/java/io/elasticjob/cloud/scheduler/mesos/TaskInfoData.java b/elastic-job-cloud-scheduler/src/main/java/io/elasticjob/cloud/scheduler/mesos/TaskInfoData.java
new file mode 100644
index 0000000..b36da03
--- /dev/null
+++ b/elastic-job-cloud-scheduler/src/main/java/io/elasticjob/cloud/scheduler/mesos/TaskInfoData.java
@@ -0,0 +1,73 @@
+/*
+ * Copyright 1999-2015 dangdang.com.
+ * <p>
+ * Licensed 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.
+ * </p>
+ */
+
+package io.elasticjob.cloud.scheduler.mesos;
+
+import io.elasticjob.cloud.scheduler.config.job.CloudJobConfiguration;
+import io.elasticjob.cloud.scheduler.config.job.CloudJobExecutionType;
+import io.elasticjob.cloud.config.script.ScriptJobConfiguration;
+import io.elasticjob.cloud.executor.ShardingContexts;
+import io.elasticjob.cloud.config.dataflow.DataflowJobConfiguration;
+import io.elasticjob.cloud.executor.handler.JobProperties;
+import lombok.RequiredArgsConstructor;
+import org.apache.commons.lang3.SerializationUtils;
+
+import java.util.LinkedHashMap;
+import java.util.Map;
+
+/**
+ * 随任务传递的数据.
+ *
+ * @author zhangliang
+ */
+@RequiredArgsConstructor
+public final class TaskInfoData {
+    
+    private final ShardingContexts shardingContexts;
+    
+    private final CloudJobConfiguration jobConfig;
+    
+    /**
+     * 序列化.
+     * 
+     * @return 序列化后的字节数组
+     */
+    public byte[] serialize() {
+        LinkedHashMap<String, Object> result = new LinkedHashMap<>(2, 1);
+        result.put("shardingContext", shardingContexts);
+        result.put("jobConfigContext", buildJobConfigurationContext());
+        return SerializationUtils.serialize(result);
+    }
+    
+    private Map<String, String> buildJobConfigurationContext() {
+        Map<String, String> result = new LinkedHashMap<>(16, 1);
+        result.put("jobType", jobConfig.getTypeConfig().getJobType().name());
+        result.put("jobName", jobConfig.getJobName());
+        result.put("jobClass", jobConfig.getTypeConfig().getJobClass());
+        result.put("cron", CloudJobExecutionType.DAEMON == jobConfig.getJobExecutionType() ? jobConfig.getTypeConfig().getCoreConfig().getCron() : "");
+        result.put("jobExceptionHandler", jobConfig.getTypeConfig().getCoreConfig().getJobProperties().get(JobProperties.JobPropertiesEnum.JOB_EXCEPTION_HANDLER));
+        result.put("executorServiceHandler", jobConfig.getTypeConfig().getCoreConfig().getJobProperties().get(JobProperties.JobPropertiesEnum.EXECUTOR_SERVICE_HANDLER));
+        if (jobConfig.getTypeConfig() instanceof DataflowJobConfiguration) {
+            result.put("streamingProcess", Boolean.toString(((DataflowJobConfiguration) jobConfig.getTypeConfig()).isStreamingProcess()));
+        } else if (jobConfig.getTypeConfig() instanceof ScriptJobConfiguration) {
+            result.put("scriptCommandLine", ((ScriptJobConfiguration) jobConfig.getTypeConfig()).getScriptCommandLine());
+        }
+        result.put("beanName", jobConfig.getBeanName());
+        result.put("applicationContext", jobConfig.getApplicationContext());
+        return result;
+    }
+}
diff --git a/elastic-job-cloud-scheduler/src/main/java/io/elasticjob/cloud/scheduler/mesos/TaskLaunchScheduledService.java b/elastic-job-cloud-scheduler/src/main/java/io/elasticjob/cloud/scheduler/mesos/TaskLaunchScheduledService.java
new file mode 100644
index 0000000..fe9eb6e
--- /dev/null
+++ b/elastic-job-cloud-scheduler/src/main/java/io/elasticjob/cloud/scheduler/mesos/TaskLaunchScheduledService.java
@@ -0,0 +1,272 @@
+/*
+ * Copyright 1999-2015 dangdang.com.
+ * <p>
+ * Licensed 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.
+ * </p>
+ */
+
+package io.elasticjob.cloud.scheduler.mesos;
+
+import io.elasticjob.cloud.api.JobType;
+import io.elasticjob.cloud.scheduler.config.app.CloudAppConfiguration;
+import io.elasticjob.cloud.scheduler.config.job.CloudJobConfiguration;
+import io.elasticjob.cloud.scheduler.config.job.CloudJobExecutionType;
+import io.elasticjob.cloud.scheduler.env.BootstrapEnvironment;
+import io.elasticjob.cloud.config.script.ScriptJobConfiguration;
+import io.elasticjob.cloud.context.ExecutionType;
+import io.elasticjob.cloud.context.TaskContext;
+import io.elasticjob.cloud.event.JobEventBus;
+import io.elasticjob.cloud.event.type.JobStatusTraceEvent;
+import io.elasticjob.cloud.executor.ShardingContexts;
+import io.elasticjob.cloud.util.json.GsonFactory;
+import io.elasticjob.cloud.util.config.ShardingItemParameters;
+import com.google.common.base.Joiner;
+import com.google.common.base.Optional;
+import com.google.common.base.Predicate;
+import com.google.common.collect.Iterables;
+import com.google.common.util.concurrent.AbstractScheduledService;
+import com.google.protobuf.ByteString;
+import com.netflix.fenzo.TaskAssignmentResult;
+import com.netflix.fenzo.TaskRequest;
+import com.netflix.fenzo.TaskScheduler;
+import com.netflix.fenzo.VMAssignmentResult;
+import com.netflix.fenzo.VirtualMachineLease;
+import lombok.RequiredArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
+import org.apache.commons.exec.CommandLine;
+import org.apache.commons.lang3.SerializationUtils;
+import org.apache.mesos.Protos;
+import org.apache.mesos.Protos.OfferID;
+import org.apache.mesos.Protos.TaskInfo;
+import org.apache.mesos.SchedulerDriver;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Map;
+import java.util.Map.Entry;
+import java.util.concurrent.TimeUnit;
+
+/**
+ * 任务提交调度服务.
+ * 
+ * @author zhangliang
+ * @author gaohongtao
+ */
+@RequiredArgsConstructor
+@Slf4j
+public final class TaskLaunchScheduledService extends AbstractScheduledService {
+    
+    private final SchedulerDriver schedulerDriver;
+    
+    private final TaskScheduler taskScheduler;
+    
+    private final FacadeService facadeService;
+    
+    private final JobEventBus jobEventBus;
+    
+    private final BootstrapEnvironment env = BootstrapEnvironment.getInstance();
+    
+    @Override
+    protected String serviceName() {
+        return "task-launch-processor";
+    }
+    
+    @Override
+    protected Scheduler scheduler() {
+        return Scheduler.newFixedDelaySchedule(2, 10, TimeUnit.SECONDS);
+    }
+    
+    @Override
+    protected void startUp() throws Exception {
+        log.info("Elastic Job: Start {}", serviceName());
+        AppConstraintEvaluator.init(facadeService);
+    }
+    
+    @Override
+    protected void shutDown() throws Exception {
+        log.info("Elastic Job: Stop {}", serviceName());
+    }
+    
+    @Override
+    protected void runOneIteration() throws Exception {
+        try {
+            LaunchingTasks launchingTasks = new LaunchingTasks(facadeService.getEligibleJobContext());
+            List<TaskRequest> taskRequests = launchingTasks.getPendingTasks();
+            if (!taskRequests.isEmpty()) {
+                AppConstraintEvaluator.getInstance().loadAppRunningState();
+            }
+            Collection<VMAssignmentResult> vmAssignmentResults = taskScheduler.scheduleOnce(taskRequests, LeasesQueue.getInstance().drainTo()).getResultMap().values();
+            List<TaskContext> taskContextsList = new LinkedList<>();
+            Map<List<Protos.OfferID>, List<Protos.TaskInfo>> offerIdTaskInfoMap = new HashMap<>();
+            for (VMAssignmentResult each: vmAssignmentResults) {
+                List<VirtualMachineLease> leasesUsed = each.getLeasesUsed();
+                List<Protos.TaskInfo> taskInfoList = new ArrayList<>(each.getTasksAssigned().size() * 10);
+                taskInfoList.addAll(getTaskInfoList(launchingTasks.getIntegrityViolationJobs(vmAssignmentResults), each, leasesUsed.get(0).hostname(), leasesUsed.get(0).getOffer()));
+                for (Protos.TaskInfo taskInfo : taskInfoList) {
+                    taskContextsList.add(TaskContext.from(taskInfo.getTaskId().getValue()));
+                }
+                offerIdTaskInfoMap.put(getOfferIDs(leasesUsed), taskInfoList);
+            }
+            for (TaskContext each : taskContextsList) {
+                facadeService.addRunning(each);
+                jobEventBus.post(createJobStatusTraceEvent(each));
+            }
+            facadeService.removeLaunchTasksFromQueue(taskContextsList);
+            for (Entry<List<OfferID>, List<TaskInfo>> each : offerIdTaskInfoMap.entrySet()) {
+                schedulerDriver.launchTasks(each.getKey(), each.getValue());
+            }
+            //CHECKSTYLE:OFF
+        } catch (Throwable throwable) {
+            //CHECKSTYLE:ON
+            log.error("Launch task error", throwable);
+        } finally {
+            AppConstraintEvaluator.getInstance().clearAppRunningState();
+        }
+    }
+    
+    private List<Protos.TaskInfo> getTaskInfoList(final Collection<String> integrityViolationJobs, final VMAssignmentResult vmAssignmentResult, final String hostname, final Protos.Offer offer) {
+        List<Protos.TaskInfo> result = new ArrayList<>(vmAssignmentResult.getTasksAssigned().size());
+        for (TaskAssignmentResult each: vmAssignmentResult.getTasksAssigned()) {
+            TaskContext taskContext = TaskContext.from(each.getTaskId());
+            String jobName = taskContext.getMetaInfo().getJobName();
+            if (!integrityViolationJobs.contains(jobName) && !facadeService.isRunning(taskContext) && !facadeService.isJobDisabled(jobName)) {
+                Protos.TaskInfo taskInfo = getTaskInfo(offer, each);
+                if (null != taskInfo) {
+                    result.add(taskInfo);
+                    facadeService.addMapping(taskInfo.getTaskId().getValue(), hostname);
+                    taskScheduler.getTaskAssigner().call(each.getRequest(), hostname);
+                }
+            }
+        }
+        return result;
+    }
+    
+    private Protos.TaskInfo getTaskInfo(final Protos.Offer offer, final TaskAssignmentResult taskAssignmentResult) {
+        TaskContext taskContext = TaskContext.from(taskAssignmentResult.getTaskId());
+        Optional<CloudJobConfiguration> jobConfigOptional = facadeService.load(taskContext.getMetaInfo().getJobName());
+        if (!jobConfigOptional.isPresent()) {
+            return null;
+        }
+        CloudJobConfiguration jobConfig = jobConfigOptional.get();
+        Optional<CloudAppConfiguration> appConfigOptional = facadeService.loadAppConfig(jobConfig.getAppName());
+        if (!appConfigOptional.isPresent()) {
+            return null;
+        }
+        CloudAppConfiguration appConfig = appConfigOptional.get();
+        taskContext.setSlaveId(offer.getSlaveId().getValue());
+        ShardingContexts shardingContexts = getShardingContexts(taskContext, appConfig, jobConfig);
+        boolean isCommandExecutor = CloudJobExecutionType.TRANSIENT == jobConfig.getJobExecutionType() && JobType.SCRIPT == jobConfig.getTypeConfig().getJobType();
+        String script = appConfig.getBootstrapScript();
+        if (isCommandExecutor) {
+            script = ((ScriptJobConfiguration) jobConfig.getTypeConfig()).getScriptCommandLine();
+        }
+        Protos.CommandInfo.URI uri = buildURI(appConfig, isCommandExecutor);
+        Protos.CommandInfo command = buildCommand(uri, script, shardingContexts, isCommandExecutor);
+        if (isCommandExecutor) {
+            return buildCommandExecutorTaskInfo(taskContext, jobConfig, shardingContexts, offer, command);
+        } else {
+            return buildCustomizedExecutorTaskInfo(taskContext, appConfig, jobConfig, shardingContexts, offer, command);
+        }
+    }
+    
+    private ShardingContexts getShardingContexts(final TaskContext taskContext, final CloudAppConfiguration appConfig, final CloudJobConfiguration jobConfig) {
+        Map<Integer, String> shardingItemParameters = new ShardingItemParameters(jobConfig.getTypeConfig().getCoreConfig().getShardingItemParameters()).getMap();
+        Map<Integer, String> assignedShardingItemParameters = new HashMap<>(1, 1);
+        int shardingItem = taskContext.getMetaInfo().getShardingItems().get(0);
+        assignedShardingItemParameters.put(shardingItem, shardingItemParameters.containsKey(shardingItem) ? shardingItemParameters.get(shardingItem) : "");
+        return new ShardingContexts(taskContext.getId(), jobConfig.getJobName(), jobConfig.getTypeConfig().getCoreConfig().getShardingTotalCount(),
+                jobConfig.getTypeConfig().getCoreConfig().getJobParameter(), assignedShardingItemParameters, appConfig.getEventTraceSamplingCount());
+    }
+    
+    private Protos.TaskInfo buildCommandExecutorTaskInfo(final TaskContext taskContext, final CloudJobConfiguration jobConfig, final ShardingContexts shardingContexts,
+                                                         final Protos.Offer offer, final Protos.CommandInfo command) {
+        Protos.TaskInfo.Builder result = Protos.TaskInfo.newBuilder().setTaskId(Protos.TaskID.newBuilder().setValue(taskContext.getId()).build())
+                .setName(taskContext.getTaskName()).setSlaveId(offer.getSlaveId())
+                .addResources(buildResource("cpus", jobConfig.getCpuCount(), offer.getResourcesList()))
+                .addResources(buildResource("mem", jobConfig.getMemoryMB(), offer.getResourcesList()))
+                .setData(ByteString.copyFrom(new TaskInfoData(shardingContexts, jobConfig).serialize()));
+        return result.setCommand(command).build();
+    }
+    
+    private Protos.TaskInfo buildCustomizedExecutorTaskInfo(final TaskContext taskContext, final CloudAppConfiguration appConfig, final CloudJobConfiguration jobConfig, 
+                                                            final ShardingContexts shardingContexts, final Protos.Offer offer, final Protos.CommandInfo command) {
+        Protos.TaskInfo.Builder result = Protos.TaskInfo.newBuilder().setTaskId(Protos.TaskID.newBuilder().setValue(taskContext.getId()).build())
+                .setName(taskContext.getTaskName()).setSlaveId(offer.getSlaveId())
+                .addResources(buildResource("cpus", jobConfig.getCpuCount(), offer.getResourcesList()))
+                .addResources(buildResource("mem", jobConfig.getMemoryMB(), offer.getResourcesList()))
+                .setData(ByteString.copyFrom(new TaskInfoData(shardingContexts, jobConfig).serialize()));
+        Protos.ExecutorInfo.Builder executorBuilder = Protos.ExecutorInfo.newBuilder().setExecutorId(Protos.ExecutorID.newBuilder()
+                .setValue(taskContext.getExecutorId(jobConfig.getAppName()))).setCommand(command)
+                .addResources(buildResource("cpus", appConfig.getCpuCount(), offer.getResourcesList()))
+                .addResources(buildResource("mem", appConfig.getMemoryMB(), offer.getResourcesList()));
+        if (env.getJobEventRdbConfiguration().isPresent()) {
+            executorBuilder.setData(ByteString.copyFrom(SerializationUtils.serialize(env.getJobEventRdbConfigurationMap()))).build();
+        }
+        return result.setExecutor(executorBuilder.build()).build();
+    }
+    
+    private Protos.CommandInfo.URI buildURI(final CloudAppConfiguration appConfig, final boolean isCommandExecutor) {
+        Protos.CommandInfo.URI.Builder result = Protos.CommandInfo.URI.newBuilder().setValue(appConfig.getAppURL()).setCache(appConfig.isAppCacheEnable());
+        if (isCommandExecutor && !SupportedExtractionType.isExtraction(appConfig.getAppURL())) {
+            result.setExecutable(true);
+        } else {
+            result.setExtract(true);
+        }
+        return result.build();
+    }
+    
+    private Protos.CommandInfo buildCommand(final Protos.CommandInfo.URI uri, final String script, final ShardingContexts shardingContexts, final boolean isCommandExecutor) {
+        Protos.CommandInfo.Builder result = Protos.CommandInfo.newBuilder().addUris(uri).setShell(true);
+        if (isCommandExecutor) {
+            CommandLine commandLine = CommandLine.parse(script);
+            commandLine.addArgument(GsonFactory.getGson().toJson(shardingContexts), false);
+            result.setValue(Joiner.on(" ").join(commandLine.getExecutable(), Joiner.on(" ").join(commandLine.getArguments())));
+        } else {
+            result.setValue(script);
+        }
+        return result.build();
+    }
+    
+    private Protos.Resource buildResource(final String type, final double resourceValue, final List<Protos.Resource> resources) {
+        return Protos.Resource.newBuilder().mergeFrom(Iterables.find(resources, new Predicate<Protos.Resource>() {
+            @Override
+            public boolean apply(final Protos.Resource input) {
+                return input.getName().equals(type);
+            }
+        })).setScalar(Protos.Value.Scalar.newBuilder().setValue(resourceValue)).build();
+    }
+    
+    private JobStatusTraceEvent createJobStatusTraceEvent(final TaskContext taskContext) {
+        TaskContext.MetaInfo metaInfo = taskContext.getMetaInfo();
+        JobStatusTraceEvent result = new JobStatusTraceEvent(metaInfo.getJobName(), taskContext.getId(), taskContext.getSlaveId(),
+                JobStatusTraceEvent.Source.CLOUD_SCHEDULER, taskContext.getType(), String.valueOf(metaInfo.getShardingItems()), JobStatusTraceEvent.State.TASK_STAGING, "");
+        if (ExecutionType.FAILOVER == taskContext.getType()) {
+            Optional<String> taskContextOptional = facadeService.getFailoverTaskId(metaInfo);
+            if (taskContextOptional.isPresent()) {
+                result.setOriginalTaskId(taskContextOptional.get());
+            }
+        }
+        return result;
+    }
+    
+    private List<Protos.OfferID> getOfferIDs(final List<VirtualMachineLease> leasesUsed) {
+        List<Protos.OfferID> result = new ArrayList<>();
+        for (VirtualMachineLease virtualMachineLease: leasesUsed) {
+            result.add(virtualMachineLease.getOffer().getId());
+        }
+        return result;
+    }
+}
diff --git a/elastic-job-cloud-scheduler/src/main/java/io/elasticjob/cloud/scheduler/producer/ProducerManager.java b/elastic-job-cloud-scheduler/src/main/java/io/elasticjob/cloud/scheduler/producer/ProducerManager.java
new file mode 100644
index 0000000..b611921
--- /dev/null
+++ b/elastic-job-cloud-scheduler/src/main/java/io/elasticjob/cloud/scheduler/producer/ProducerManager.java
@@ -0,0 +1,201 @@
+/*
+ * Copyright 1999-2015 dangdang.com.
+ * <p>
+ * Licensed 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.
+ * </p>
+ */
+
+package io.elasticjob.cloud.scheduler.producer;
+
+import io.elasticjob.cloud.scheduler.config.app.CloudAppConfigurationService;
+import io.elasticjob.cloud.scheduler.config.job.CloudJobConfiguration;
+import io.elasticjob.cloud.scheduler.config.job.CloudJobConfigurationService;
+import io.elasticjob.cloud.scheduler.config.job.CloudJobExecutionType;
+import io.elasticjob.cloud.scheduler.state.disable.app.DisableAppService;
+import io.elasticjob.cloud.scheduler.state.disable.job.DisableJobService;
+import io.elasticjob.cloud.scheduler.state.running.RunningService;
+import io.elasticjob.cloud.exception.AppConfigurationException;
+import io.elasticjob.cloud.exception.JobConfigurationException;
+import io.elasticjob.cloud.scheduler.config.app.CloudAppConfiguration;
+import io.elasticjob.cloud.scheduler.state.ready.ReadyService;
+import io.elasticjob.cloud.context.TaskContext;
+import io.elasticjob.cloud.reg.base.CoordinatorRegistryCenter;
+import com.google.common.base.Optional;
+import com.google.common.collect.Lists;
+import lombok.extern.slf4j.Slf4j;
+import org.apache.mesos.Protos;
+import org.apache.mesos.Protos.ExecutorID;
+import org.apache.mesos.Protos.SlaveID;
+import org.apache.mesos.SchedulerDriver;
+
+/**
+ * 发布任务作业调度管理器.
+ *
+ * @author caohao
+ * @author zhangliang
+ */
+@Slf4j
+public final class ProducerManager {
+    
+    private final CloudAppConfigurationService appConfigService;
+    
+    private final CloudJobConfigurationService configService;
+            
+    private final ReadyService readyService;
+    
+    private final RunningService runningService;
+    
+    private final DisableAppService disableAppService;
+    
+    private final DisableJobService disableJobService;
+    
+    private final TransientProducerScheduler transientProducerScheduler;
+    
+    private final SchedulerDriver schedulerDriver;
+    
+    public ProducerManager(final SchedulerDriver schedulerDriver, final CoordinatorRegistryCenter regCenter) {
+        this.schedulerDriver = schedulerDriver;
+        appConfigService = new CloudAppConfigurationService(regCenter);
+        configService = new CloudJobConfigurationService(regCenter);
+        readyService = new ReadyService(regCenter);
+        runningService = new RunningService(regCenter);
+        disableAppService = new DisableAppService(regCenter);
+        disableJobService = new DisableJobService(regCenter);
+        transientProducerScheduler = new TransientProducerScheduler(readyService);
+    }
+    
+    /**
+     * 启动作业调度器.
+     */
+    public void startup() {
+        log.info("Start producer manager");
+        transientProducerScheduler.start();
+        for (CloudJobConfiguration each : configService.loadAll()) {
+            schedule(each);
+        }
+    }
+    
+    /**
+     * 注册作业.
+     * 
+     * @param jobConfig 作业配置
+     */
+    public void register(final CloudJobConfiguration jobConfig) {
+        if (disableJobService.isDisabled(jobConfig.getJobName())) {
+            throw new JobConfigurationException("Job '%s' has been disable.", jobConfig.getJobName());
+        }
+        Optional<CloudAppConfiguration> appConfigFromZk = appConfigService.load(jobConfig.getAppName());
+        if (!appConfigFromZk.isPresent()) {
+            throw new AppConfigurationException("Register app '%s' firstly.", jobConfig.getAppName());
+        }
+        Optional<CloudJobConfiguration> jobConfigFromZk = configService.load(jobConfig.getJobName());
+        if (jobConfigFromZk.isPresent()) {
+            throw new JobConfigurationException("Job '%s' already existed.", jobConfig.getJobName());
+        }
+        configService.add(jobConfig);
+        schedule(jobConfig);
+    }
+    
+    /**
+     * 更新作业配置.
+     *
+     * @param jobConfig 作业配置
+     */
+    public void update(final CloudJobConfiguration jobConfig) {
+        Optional<CloudJobConfiguration> jobConfigFromZk = configService.load(jobConfig.getJobName());
+        if (!jobConfigFromZk.isPresent()) {
+            throw new JobConfigurationException("Cannot found job '%s', please register first.", jobConfig.getJobName());
+        }
+        configService.update(jobConfig);
+        reschedule(jobConfig.getJobName());
+    }
+    
+    /**
+     * 注销作业.
+     * 
+     * @param jobName 作业名称
+     */
+    public void deregister(final String jobName) {
+        Optional<CloudJobConfiguration> jobConfig = configService.load(jobName);
+        if (jobConfig.isPresent()) {
+            disableJobService.remove(jobName);
+            configService.remove(jobName);
+        }
+        unschedule(jobName);
+    }
+    
+    /**
+     * 调度作业.
+     * 
+     * @param jobConfig 作业配置
+     */
+    public void schedule(final CloudJobConfiguration jobConfig) {
+        if (disableAppService.isDisabled(jobConfig.getAppName()) || disableJobService.isDisabled(jobConfig.getJobName())) {
+            return;
+        }
+        if (CloudJobExecutionType.TRANSIENT == jobConfig.getJobExecutionType()) {
+            transientProducerScheduler.register(jobConfig);
+        } else if (CloudJobExecutionType.DAEMON == jobConfig.getJobExecutionType()) {
+            readyService.addDaemon(jobConfig.getJobName());
+        }
+    }
+    
+    /**
+     * 停止调度作业.
+     *
+     * @param jobName 作业名称
+     */
+    public void unschedule(final String jobName) {
+        for (TaskContext each : runningService.getRunningTasks(jobName)) {
+            schedulerDriver.killTask(Protos.TaskID.newBuilder().setValue(each.getId()).build());
+        }
+        runningService.remove(jobName);
+        readyService.remove(Lists.newArrayList(jobName));
+        Optional<CloudJobConfiguration> jobConfig = configService.load(jobName);
+        if (jobConfig.isPresent()) {
+            transientProducerScheduler.deregister(jobConfig.get());
+        }
+    }
+    
+    /**
+     * 重新调度作业.
+     *
+     * @param jobName 作业名称
+     */
+    public void reschedule(final String jobName) {
+        unschedule(jobName);
+        Optional<CloudJobConfiguration> jobConfig = configService.load(jobName);
+        if (jobConfig.isPresent()) {
+            schedule(jobConfig.get());
+        }
+    }
+    
+    /**
+     * 向Executor发送消息.
+     * 
+     * @param executorId 接受消息的executorId
+     * @param slaveId 运行executor的slaveId
+     * @param data 消息内容
+     */
+    public void sendFrameworkMessage(final ExecutorID executorId, final SlaveID slaveId, final byte[] data) {
+        schedulerDriver.sendFrameworkMessage(executorId, slaveId, data);
+    }
+    
+    /**
+     * 关闭作业调度器.
+     */
+    public void shutdown() {
+        log.info("Stop producer manager");
+        transientProducerScheduler.shutdown();
+    }
+}
diff --git a/elastic-job-cloud-scheduler/src/main/java/io/elasticjob/cloud/scheduler/producer/TransientProducerRepository.java b/elastic-job-cloud-scheduler/src/main/java/io/elasticjob/cloud/scheduler/producer/TransientProducerRepository.java
new file mode 100644
index 0000000..2e15f82
--- /dev/null
+++ b/elastic-job-cloud-scheduler/src/main/java/io/elasticjob/cloud/scheduler/producer/TransientProducerRepository.java
@@ -0,0 +1,75 @@
+/*
+ * Copyright 1999-2015 dangdang.com.
+ * <p>
+ * Licensed 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.
+ * </p>
+ */
+
+package io.elasticjob.cloud.scheduler.producer;
+
+import org.quartz.JobKey;
+
+import java.util.Collections;
+import java.util.List;
+import java.util.Map.Entry;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.CopyOnWriteArrayList;
+
+/**
+ * 瞬时作业生成器数据访问对象.
+ *
+ * @author caohao
+ * @author zhangliang
+ */
+final class TransientProducerRepository {
+    
+    private final ConcurrentHashMap<JobKey, List<String>> cronTasks = new ConcurrentHashMap<>(256, 1);
+    
+    synchronized void put(final JobKey jobKey, final String jobName) {
+        remove(jobName);
+        List<String> taskList = cronTasks.get(jobKey);
+        if (null == taskList) {
+            taskList = new CopyOnWriteArrayList<>();
+            taskList.add(jobName);
+            cronTasks.put(jobKey, taskList);
+            return;
+        }
+        if (!taskList.contains(jobName)) {
+            taskList.add(jobName);
+        }
+    }
+    
+    synchronized void remove(final String jobName) {
+        for (Entry<JobKey, List<String>> each : cronTasks.entrySet()) {
+            JobKey jobKey = each.getKey();
+            List<String> jobNames = each.getValue();
+            jobNames.remove(jobName);
+            if (jobNames.isEmpty()) {
+                cronTasks.remove(jobKey);
+            }
+        }
+    }
+    
+    List<String> get(final JobKey jobKey) {
+        List<String> result = cronTasks.get(jobKey);
+        return null == result ? Collections.<String>emptyList() : result;
+    }
+    
+    boolean containsKey(final JobKey jobKey) {
+        return cronTasks.containsKey(jobKey);
+    }
+    
+    void removeAll() {
+        cronTasks.clear();
+    }
+}
diff --git a/elastic-job-cloud-scheduler/src/main/java/io/elasticjob/cloud/scheduler/producer/TransientProducerScheduler.java b/elastic-job-cloud-scheduler/src/main/java/io/elasticjob/cloud/scheduler/producer/TransientProducerScheduler.java
new file mode 100644
index 0000000..bd54084
--- /dev/null
+++ b/elastic-job-cloud-scheduler/src/main/java/io/elasticjob/cloud/scheduler/producer/TransientProducerScheduler.java
@@ -0,0 +1,157 @@
+/*
+ * Copyright 1999-2015 dangdang.com.
+ * <p>
+ * Licensed 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.
+ * </p>
+ */
+
+package io.elasticjob.cloud.scheduler.producer;
+
+import io.elasticjob.cloud.scheduler.config.job.CloudJobConfiguration;
+import io.elasticjob.cloud.scheduler.state.ready.ReadyService;
+import io.elasticjob.cloud.exception.JobSystemException;
+import lombok.Setter;
+import org.quartz.CronScheduleBuilder;
+import org.quartz.Job;
+import org.quartz.JobBuilder;
+import org.quartz.JobDetail;
+import org.quartz.JobExecutionContext;
+import org.quartz.JobExecutionException;
+import org.quartz.JobKey;
+import org.quartz.Scheduler;
+import org.quartz.SchedulerException;
+import org.quartz.Trigger;
+import org.quartz.TriggerBuilder;
+import org.quartz.TriggerKey;
+import org.quartz.impl.StdSchedulerFactory;
+import org.quartz.plugins.management.ShutdownHookPlugin;
+import org.quartz.simpl.SimpleThreadPool;
+
+import java.util.List;
+import java.util.Properties;
+
+/**
+ * 发布瞬时作业任务的调度器.
+ *
+ * @author caohao
+ */
+final class TransientProducerScheduler {
+    
+    private final TransientProducerRepository repository;
+    
+    private final ReadyService readyService;
+    
+    private Scheduler scheduler;
+    
+    TransientProducerScheduler(final ReadyService readyService) {
+        repository = new TransientProducerRepository();
+        this.readyService = readyService;
+    }
+    
+    void start() {
+        scheduler = getScheduler();
+        try {
+            scheduler.start();
+        } catch (final SchedulerException ex) {
+            throw new JobSystemException(ex);
+        }
+    }
+    
+    private Scheduler getScheduler() {
+        StdSchedulerFactory factory = new StdSchedulerFactory();
+        try {
+            factory.initialize(getQuartzProperties());
+            return factory.getScheduler();
+        } catch (final SchedulerException ex) {
+            throw new JobSystemException(ex);
+        }
+    }
+    
+    private Properties getQuartzProperties() {
+        Properties result = new Properties();
+        result.put("org.quartz.threadPool.class", SimpleThreadPool.class.getName());
+        result.put("org.quartz.threadPool.threadCount", Integer.toString(Runtime.getRuntime().availableProcessors() * 2));
+        result.put("org.quartz.scheduler.instanceName", "ELASTIC_JOB_CLOUD_TRANSIENT_PRODUCER");
+        result.put("org.quartz.plugin.shutdownhook.class", ShutdownHookPlugin.class.getName());
+        result.put("org.quartz.plugin.shutdownhook.cleanShutdown", Boolean.TRUE.toString());
+        return result;
+    }
+    
+    // TODO 并发优化
+    synchronized void register(final CloudJobConfiguration jobConfig) {
+        String cron = jobConfig.getTypeConfig().getCoreConfig().getCron();
+        JobKey jobKey = buildJobKey(cron);
+        repository.put(jobKey, jobConfig.getJobName());
+        try {
+            if (!scheduler.checkExists(jobKey)) {
+                scheduler.scheduleJob(buildJobDetail(jobKey), buildTrigger(jobKey.getName()));
+            }
+        } catch (final SchedulerException ex) {
+            throw new JobSystemException(ex);
+        }
+    }
+    
+    private JobDetail buildJobDetail(final JobKey jobKey) {
+        JobDetail result = JobBuilder.newJob(ProducerJob.class).withIdentity(jobKey).build();
+        result.getJobDataMap().put("repository", repository);
+        result.getJobDataMap().put("readyService", readyService);
+        return result;
+    }
+    
+    private Trigger buildTrigger(final String cron) {
+        return TriggerBuilder.newTrigger().withIdentity(cron).withSchedule(CronScheduleBuilder.cronSchedule(cron).withMisfireHandlingInstructionDoNothing()).build();
+    }
+    
+    synchronized void deregister(final CloudJobConfiguration jobConfig) {
+        repository.remove(jobConfig.getJobName());
+        String cron = jobConfig.getTypeConfig().getCoreConfig().getCron();
+        if (!repository.containsKey(buildJobKey(cron))) {
+            try {
+                scheduler.unscheduleJob(TriggerKey.triggerKey(cron));
+            } catch (final SchedulerException ex) {
+                throw new JobSystemException(ex);
+            }
+        }
+    }
+    
+    private JobKey buildJobKey(final String cron) {
+        return JobKey.jobKey(cron);
+    }
+    
+    void shutdown() {
+        try {
+            if (null != scheduler && !scheduler.isShutdown()) {
+                scheduler.shutdown();
+            }
+        } catch (final SchedulerException ex) {
+            throw new JobSystemException(ex);
+        }
+        repository.removeAll();
+    }
+    
+    @Setter
+    public static final class ProducerJob implements Job {
+        
+        private TransientProducerRepository repository;
+        
+        private ReadyService readyService;
+        
+        @Override
+        public void execute(final JobExecutionContext context) throws JobExecutionException {
+            List<String> jobNames = repository.get(context.getJobDetail().getKey());
+            for (String each : jobNames) {
+                readyService.addTransient(each);
+            }
+        }
+    }
+}
diff --git a/elastic-job-cloud-scheduler/src/main/java/io/elasticjob/cloud/scheduler/restful/CloudAppRestfulApi.java b/elastic-job-cloud-scheduler/src/main/java/io/elasticjob/cloud/scheduler/restful/CloudAppRestfulApi.java
new file mode 100644
index 0000000..676629d
--- /dev/null
+++ b/elastic-job-cloud-scheduler/src/main/java/io/elasticjob/cloud/scheduler/restful/CloudAppRestfulApi.java
@@ -0,0 +1,232 @@
+/*
+ * Copyright 1999-2015 dangdang.com.
+ * <p>
+ * Licensed 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.
+ * </p>
+ */
+
+package io.elasticjob.cloud.scheduler.restful;
+
+import io.elasticjob.cloud.scheduler.config.app.CloudAppConfigurationGsonFactory;
+import io.elasticjob.cloud.scheduler.mesos.MesosStateService;
+import io.elasticjob.cloud.scheduler.state.disable.app.DisableAppService;
+import io.elasticjob.cloud.scheduler.config.app.CloudAppConfiguration;
+import io.elasticjob.cloud.scheduler.config.app.CloudAppConfigurationService;
+import io.elasticjob.cloud.scheduler.config.job.CloudJobConfiguration;
+import io.elasticjob.cloud.scheduler.config.job.CloudJobConfigurationService;
+import io.elasticjob.cloud.scheduler.producer.ProducerManager;
+import io.elasticjob.cloud.exception.AppConfigurationException;
+import io.elasticjob.cloud.exception.JobSystemException;
+import io.elasticjob.cloud.reg.base.CoordinatorRegistryCenter;
+import io.elasticjob.cloud.util.json.GsonFactory;
+import com.google.common.base.Optional;
+import org.apache.mesos.Protos.ExecutorID;
+import org.apache.mesos.Protos.SlaveID;
+import org.codehaus.jettison.json.JSONException;
+
+import javax.ws.rs.Consumes;
+import javax.ws.rs.DELETE;
+import javax.ws.rs.GET;
+import javax.ws.rs.POST;
+import javax.ws.rs.PUT;
+import javax.ws.rs.Path;
+import javax.ws.rs.PathParam;
+import javax.ws.rs.Produces;
+import javax.ws.rs.core.MediaType;
+import javax.ws.rs.core.Response;
+import java.util.Collection;
+
+import static javax.ws.rs.core.Response.Status.NOT_FOUND;
+
+/**
+ * 云作业应用的REST API.
+ *
+ * @author caohao
+ */
+@Path("/app")
+public final class CloudAppRestfulApi {
+    
+    private static CoordinatorRegistryCenter regCenter;
+    
+    private static ProducerManager producerManager;
+    
+    private final CloudAppConfigurationService appConfigService;
+    
+    private final CloudJobConfigurationService jobConfigService;
+    
+    private final DisableAppService disableAppService;
+    
+    private final MesosStateService mesosStateService;
+    
+    public CloudAppRestfulApi() {
+        appConfigService = new CloudAppConfigurationService(regCenter);
+        jobConfigService = new CloudJobConfigurationService(regCenter);
+        mesosStateService = new MesosStateService(regCenter);
+        disableAppService = new DisableAppService(regCenter);
+    }
+    
+    /**
+     * 初始化.
+     *
+     * @param producerManager 生产管理器
+     * @param regCenter 注册中心
+     */
+    public static void init(final CoordinatorRegistryCenter regCenter, final ProducerManager producerManager) {
+        CloudAppRestfulApi.regCenter = regCenter;
+        CloudAppRestfulApi.producerManager = producerManager;
+        GsonFactory.registerTypeAdapter(CloudAppConfiguration.class, new CloudAppConfigurationGsonFactory.CloudAppConfigurationGsonTypeAdapter());
+    }
+    
+    /**
+     * 注册应用配置.
+     * 
+     * @param appConfig 应用配置
+     */
+    @POST
+    @Consumes(MediaType.APPLICATION_JSON)
+    public void register(final CloudAppConfiguration appConfig) {
+        Optional<CloudAppConfiguration> appConfigFromZk = appConfigService.load(appConfig.getAppName());
+        if (appConfigFromZk.isPresent()) {
+            throw new AppConfigurationException("app '%s' already existed.", appConfig.getAppName());
+        }
+        appConfigService.add(appConfig);
+    }
+    
+    /**
+     * 更新应用配置.
+     *
+     * @param appConfig 应用配置
+     */
+    @PUT
+    @Consumes(MediaType.APPLICATION_JSON)
+    public void update(final CloudAppConfiguration appConfig) {
+        appConfigService.update(appConfig);
+    }
+    
+    /**
+     * 查询应用配置.
+     *
+     * @param appName 应用配置名称
+     * @return 应用配置
+     */
+    @GET
+    @Path("/{appName}")
+    @Consumes(MediaType.APPLICATION_JSON)
+    public Response detail(@PathParam("appName") final String appName) {
+        Optional<CloudAppConfiguration> appConfig = appConfigService.load(appName);
+        if (!appConfig.isPresent()) {
+            return Response.status(NOT_FOUND).build();
+        }
+        return Response.ok(appConfig.get()).build();
+    }
+    
+    /**
+     * 查询全部应用配置.
+     * 
+     * @return 全部应用配置
+     */
+    @GET
+    @Path("/list")
+    @Produces(MediaType.APPLICATION_JSON)
+    public Collection<CloudAppConfiguration> findAllApps() {
+        return appConfigService.loadAll();
+    }
+    
+    /**
+     * 查询应用是否被禁用.
+     * 
+     * @param appName 应用名称
+     * @return 应用是否被禁用
+     * @throws JSONException JSON解析异常
+     */
+    @GET
+    @Path("/{appName}/disable")
+    @Produces(MediaType.APPLICATION_JSON)
+    public boolean isDisabled(@PathParam("appName") final String appName) throws JSONException {
+        return disableAppService.isDisabled(appName);
+    }
+    
+    /**
+     * 禁用应用.
+     *
+     * @param appName 应用名称
+     */
+    @POST
+    @Path("/{appName}/disable")
+    public void disable(@PathParam("appName") final String appName) {
+        if (appConfigService.load(appName).isPresent()) {
+            disableAppService.add(appName);
+            for (CloudJobConfiguration each : jobConfigService.loadAll()) {
+                if (appName.equals(each.getAppName())) {
+                    producerManager.unschedule(each.getJobName());
+                }
+            }
+        }
+    }
+    
+    /**
+     * 启用应用.
+     * 
+     * @param appName 应用名称
+     * @throws JSONException JSON解析异常
+     */
+    @DELETE
+    @Path("/{appName}/disable")
+    public void enable(@PathParam("appName") final String appName) throws JSONException {
+        if (appConfigService.load(appName).isPresent()) {
+            disableAppService.remove(appName);
+            for (CloudJobConfiguration each : jobConfigService.loadAll()) {
+                if (appName.equals(each.getAppName())) {
+                    producerManager.reschedule(each.getJobName());
+                }
+            }
+        }
+    }
+    
+    /**
+     * 注销应用.
+     *
+     * @param appName 应用名称
+     */
+    @DELETE
+    @Path("/{appName}")
+    @Consumes(MediaType.APPLICATION_JSON)
+    public void deregister(@PathParam("appName") final String appName) {
+        if (appConfigService.load(appName).isPresent()) {
+            removeAppAndJobConfigurations(appName);
+            stopExecutors(appName);
+        }
+    }
+    
+    private void removeAppAndJobConfigurations(final String appName) {
+        for (CloudJobConfiguration each : jobConfigService.loadAll()) {
+            if (appName.equals(each.getAppName())) {
+                producerManager.deregister(each.getJobName());
+            }
+        }
+        disableAppService.remove(appName);
+        appConfigService.remove(appName);
+    }
+    
+    private void stopExecutors(final String appName) {
+        try {
+            Collection<MesosStateService.ExecutorStateInfo> executorBriefInfo = mesosStateService.executors(appName);
+            for (MesosStateService.ExecutorStateInfo each : executorBriefInfo) {
+                producerManager.sendFrameworkMessage(ExecutorID.newBuilder().setValue(each.getId()).build(),
+                        SlaveID.newBuilder().setValue(each.getSlaveId()).build(), "STOP".getBytes());
+            }
+        } catch (final JSONException ex) {
+            throw new JobSystemException(ex);
+        }
+    }
+}
diff --git a/elastic-job-cloud-scheduler/src/main/java/io/elasticjob/cloud/scheduler/restful/CloudJobRestfulApi.java b/elastic-job-cloud-scheduler/src/main/java/io/elasticjob/cloud/scheduler/restful/CloudJobRestfulApi.java
new file mode 100644
index 0000000..e361d4b
--- /dev/null
+++ b/elastic-job-cloud-scheduler/src/main/java/io/elasticjob/cloud/scheduler/restful/CloudJobRestfulApi.java
@@ -0,0 +1,484 @@
+/*
+ * Copyright 1999-2015 dangdang.com.
+ * <p>
+ * Licensed 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.
+ * </p>
+ */
+
+package io.elasticjob.cloud.scheduler.restful;
+
+import io.elasticjob.cloud.scheduler.config.job.CloudJobConfiguration;
+import io.elasticjob.cloud.scheduler.statistics.StatisticManager;
+import io.elasticjob.cloud.event.rdb.JobEventRdbConfiguration;
+import io.elasticjob.cloud.event.rdb.JobEventRdbSearch;
+import io.elasticjob.cloud.event.type.JobExecutionEvent;
+import io.elasticjob.cloud.scheduler.config.job.CloudJobConfigurationGsonFactory;
+import io.elasticjob.cloud.scheduler.config.job.CloudJobConfigurationService;
+import io.elasticjob.cloud.scheduler.config.job.CloudJobExecutionType;
+import io.elasticjob.cloud.scheduler.env.BootstrapEnvironment;
+import io.elasticjob.cloud.scheduler.mesos.FacadeService;
+import io.elasticjob.cloud.scheduler.producer.ProducerManager;
+import io.elasticjob.cloud.scheduler.state.failover.FailoverTaskInfo;
+import io.elasticjob.cloud.statistics.StatisticInterval;
+import io.elasticjob.cloud.statistics.type.job.JobRegisterStatistics;
+import io.elasticjob.cloud.statistics.type.task.TaskResultStatistics;
+import io.elasticjob.cloud.context.TaskContext;
+import io.elasticjob.cloud.event.type.JobStatusTraceEvent;
+import io.elasticjob.cloud.exception.JobSystemException;
+import io.elasticjob.cloud.reg.base.CoordinatorRegistryCenter;
+import io.elasticjob.cloud.statistics.type.job.JobExecutionTypeStatistics;
+import io.elasticjob.cloud.statistics.type.job.JobRunningStatistics;
+import io.elasticjob.cloud.statistics.type.job.JobTypeStatistics;
+import io.elasticjob.cloud.statistics.type.task.TaskRunningStatistics;
+import io.elasticjob.cloud.util.json.GsonFactory;
+import com.google.common.base.Optional;
+import com.google.common.base.Preconditions;
+import com.google.common.base.Strings;
+import lombok.extern.slf4j.Slf4j;
+import org.codehaus.jettison.json.JSONException;
+
+import javax.ws.rs.Consumes;
+import javax.ws.rs.DELETE;
+import javax.ws.rs.GET;
+import javax.ws.rs.POST;
+import javax.ws.rs.PUT;
+import javax.ws.rs.Path;
+import javax.ws.rs.PathParam;
+import javax.ws.rs.Produces;
+import javax.ws.rs.QueryParam;
+import javax.ws.rs.core.Context;
+import javax.ws.rs.core.MediaType;
+import javax.ws.rs.core.Response;
+import javax.ws.rs.core.UriInfo;
+import java.text.ParseException;
+import java.text.SimpleDateFormat;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.Date;
+import java.util.HashMap;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Map;
+import java.util.Map.Entry;
+import java.util.Set;
+
+import static javax.ws.rs.core.Response.Status.NOT_FOUND;
+
+/**
+ * 作业云Job的REST API.
+ *
+ * @author zhangliang
+ * @author liguangyun
+ */
+@Path("/job")
+@Slf4j
+public final class CloudJobRestfulApi {
+    
+    private static CoordinatorRegistryCenter regCenter;
+    
+    private static JobEventRdbSearch jobEventRdbSearch;
+    
+    private static ProducerManager producerManager;
+    
+    private final CloudJobConfigurationService configService;
+    
+    private final FacadeService facadeService;
+    
+    private final StatisticManager statisticManager;
+    
+    public CloudJobRestfulApi() {
+        Preconditions.checkNotNull(regCenter);
+        configService = new CloudJobConfigurationService(regCenter);
+        facadeService = new FacadeService(regCenter);
+        Optional<JobEventRdbConfiguration> jobEventRdbConfiguration = Optional.absent();
+        statisticManager = StatisticManager.getInstance(regCenter, jobEventRdbConfiguration);
+    }
+    
+    /**
+     * 初始化.
+     * 
+     * @param regCenter 注册中心
+     * @param producerManager 生产管理器
+     */
+    public static void init(final CoordinatorRegistryCenter regCenter, final ProducerManager producerManager) {
+        CloudJobRestfulApi.regCenter = regCenter;
+        CloudJobRestfulApi.producerManager = producerManager;
+        GsonFactory.registerTypeAdapter(CloudJobConfiguration.class, new CloudJobConfigurationGsonFactory.CloudJobConfigurationGsonTypeAdapter());
+        Optional<JobEventRdbConfiguration> jobEventRdbConfig = BootstrapEnvironment.getInstance().getJobEventRdbConfiguration();
+        if (jobEventRdbConfig.isPresent()) {
+            jobEventRdbSearch = new JobEventRdbSearch(jobEventRdbConfig.get().getDataSource());
+        } else {
+            jobEventRdbSearch = null;
+        }
+    }
+    
+    /**
+     * 注册作业.
+     * 
+     * @param jobConfig 作业配置
+     */
+    @POST
+    @Path("/register")
+    @Consumes(MediaType.APPLICATION_JSON)
+    public void register(final CloudJobConfiguration jobConfig) {
+        producerManager.register(jobConfig);
+    }
+    
+    /**
+     * 更新作业配置.
+     *
+     * @param jobConfig 作业配置
+     */
+    @PUT
+    @Path("/update")
+    @Consumes(MediaType.APPLICATION_JSON)
+    public void update(final CloudJobConfiguration jobConfig) {
+        producerManager.update(jobConfig);
+    }
+    
+    /**
+     * 注销作业.
+     * 
+     * @param jobName 作业名称
+     */
+    @DELETE
+    @Path("/deregister")
+    @Consumes(MediaType.APPLICATION_JSON)
+    public void deregister(final String jobName) {
+        producerManager.deregister(jobName);
+    }
+    
+    /**
+     * 查询作业是否被禁用.
+     *
+     * @param jobName 作业名称
+     * @return 作业是否被禁用
+     * @throws JSONException JSON解析异常
+     */
+    @GET
+    @Path("/{jobName}/disable")
+    @Produces(MediaType.APPLICATION_JSON)
+    public boolean isDisabled(@PathParam("jobName") final String jobName) throws JSONException {
+        return facadeService.isJobDisabled(jobName);
+    }
+    
+    /**
+     * 启用作业.
+     *
+     * @param jobName 作业名称
+     * @throws JSONException JSON解析异常
+     */
+    @DELETE
+    @Path("/{jobName}/disable")
+    public void enable(@PathParam("jobName") final String jobName) throws JSONException {
+        Optional<CloudJobConfiguration> configOptional = configService.load(jobName);
+        if (configOptional.isPresent()) {
+            facadeService.enableJob(jobName);
+            producerManager.reschedule(jobName);
+        }
+    }
+    
+    /**
+     * 禁用作业.
+     *
+     * @param jobName 作业名称
+     */
+    @POST
+    @Path("/{jobName}/disable")
+    public void disable(@PathParam("jobName") final String jobName) {
+        if (configService.load(jobName).isPresent()) {
+            facadeService.disableJob(jobName);
+            producerManager.unschedule(jobName);
+        }
+    }
+    
+    /**
+     * 触发一次作业.
+     *
+     * @param jobName 作业名称
+     */
+    @POST
+    @Path("/trigger")
+    @Consumes(MediaType.APPLICATION_JSON)
+    public void trigger(final String jobName) {
+        Optional<CloudJobConfiguration> config = configService.load(jobName);
+        if (config.isPresent() && CloudJobExecutionType.DAEMON == config.get().getJobExecutionType()) {
+            throw new JobSystemException("Daemon job '%s' cannot support trigger.", jobName);
+        }
+        facadeService.addTransient(jobName);
+    }
+    
+    /**
+     * 查询作业详情.
+     *
+     * @param jobName 作业名称
+     * @return 作业配置对象
+     */
+    @GET
+    @Path("/jobs/{jobName}")
+    @Consumes(MediaType.APPLICATION_JSON)
+    public Response detail(@PathParam("jobName") final String jobName) {
+        Optional<CloudJobConfiguration> jobConfig = configService.load(jobName);
+        if (!jobConfig.isPresent()) {
+            return Response.status(NOT_FOUND).build();
+        }
+        return Response.ok(jobConfig.get()).build();
+    }
+    
+    /**
+     * 查找全部作业.
+     * 
+     * @return 全部作业
+     */
+    @GET
+    @Path("/jobs")
+    @Consumes(MediaType.APPLICATION_JSON)
+    public Collection<CloudJobConfiguration> findAllJobs() {
+        return configService.loadAll();
+    }
+    
+    /**
+     * 查找运行中的全部任务.
+     * 
+     * @return 运行中的全部任务
+     */
+    @GET
+    @Path("tasks/running")
+    @Consumes(MediaType.APPLICATION_JSON)
+    public Collection<TaskContext> findAllRunningTasks() {
+        List<TaskContext> result = new LinkedList<>();
+        for (Set<TaskContext> each : facadeService.getAllRunningTasks().values()) {
+            result.addAll(each);
+        }
+        return result;
+    }
+    
+    /**
+     * 查找待运行的全部任务.
+     * 
+     * @return 待运行的全部任务
+     */
+    @GET
+    @Path("tasks/ready")
+    @Consumes(MediaType.APPLICATION_JSON)
+    public Collection<Map<String, String>> findAllReadyTasks() {
+        Map<String, Integer> readyTasks = facadeService.getAllReadyTasks();
+        List<Map<String, String>> result = new ArrayList<>(readyTasks.size());
+        for (Entry<String, Integer> each : readyTasks.entrySet()) {
+            Map<String, String> oneTask = new HashMap<>(2, 1);
+            oneTask.put("jobName", each.getKey());
+            oneTask.put("times", String.valueOf(each.getValue()));
+            result.add(oneTask);
+        }
+        return result;
+    }
+    
+    /**
+     * 查找待失效转移的全部任务.
+     * 
+     * @return 失效转移的全部任务
+     */
+    @GET
+    @Path("tasks/failover")
+    @Consumes(MediaType.APPLICATION_JSON)
+    public Collection<FailoverTaskInfo> findAllFailoverTasks() {
+        List<FailoverTaskInfo> result = new LinkedList<>();
+        for (Collection<FailoverTaskInfo> each : facadeService.getAllFailoverTasks().values()) {
+            result.addAll(each);
+        }
+        return result;
+    }
+    
+    /**
+     * 检索作业运行轨迹.
+     * 
+     * @param info URL信息
+     * @return 作业运行轨迹结果
+     * @throws ParseException 解析异常
+     */
+    @GET
+    @Path("events/executions")
+    @Consumes(MediaType.APPLICATION_JSON)
+    public JobEventRdbSearch.Result<JobExecutionEvent> findJobExecutionEvents(@Context final UriInfo info) throws ParseException {
+        if (!isRdbConfigured()) {
+            return new JobEventRdbSearch.Result<>(0, Collections.<JobExecutionEvent>emptyList());
+        }
+        return jobEventRdbSearch.findJobExecutionEvents(buildCondition(info, new String[]{"jobName", "taskId", "ip", "isSuccess"}));
+    }
+    
+    /**
+     * 检索作业运行状态轨迹.
+     * 
+     * @param info URL信息
+     * @return 作业运行轨迹结果
+     * @throws ParseException 转换异常
+     */
+    @GET
+    @Path("events/statusTraces")
+    @Consumes(MediaType.APPLICATION_JSON)
+    public JobEventRdbSearch.Result<JobStatusTraceEvent> findJobStatusTraceEvents(@Context final UriInfo info) throws ParseException {
+        if (!isRdbConfigured()) {
+            return new JobEventRdbSearch.Result<>(0, Collections.<JobStatusTraceEvent>emptyList());
+        }
+        return jobEventRdbSearch.findJobStatusTraceEvents(buildCondition(info, new String[]{"jobName", "taskId", "slaveId", "source", "executionType", "state"}));
+    }
+    
+    private boolean isRdbConfigured() {
+        return null != jobEventRdbSearch;
+    }
+    
+    private JobEventRdbSearch.Condition buildCondition(final UriInfo info, final String[] params) throws ParseException {
+        int perPage = 10;
+        int page = 1;
+        if (!Strings.isNullOrEmpty(info.getQueryParameters().getFirst("per_page"))) {
+            perPage = Integer.parseInt(info.getQueryParameters().getFirst("per_page"));
+        }
+        if (!Strings.isNullOrEmpty(info.getQueryParameters().getFirst("page"))) {
+            page = Integer.parseInt(info.getQueryParameters().getFirst("page"));
+        }
+        String sort = info.getQueryParameters().getFirst("sort");
+        String order = info.getQueryParameters().getFirst("order");
+        Date startTime = null;
+        Date endTime = null;
+        Map<String, Object> fields = getQueryParameters(info, params);
+        SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
+        if (!Strings.isNullOrEmpty(info.getQueryParameters().getFirst("startTime"))) {
+            startTime = simpleDateFormat.parse(info.getQueryParameters().getFirst("startTime"));
+        }
+        if (!Strings.isNullOrEmpty(info.getQueryParameters().getFirst("endTime"))) {
+            endTime = simpleDateFormat.parse(info.getQueryParameters().getFirst("endTime"));
+        }
+        return new JobEventRdbSearch.Condition(perPage, page, sort, order, startTime, endTime, fields);
+    }
+    
+    private Map<String, Object> getQueryParameters(final UriInfo info, final String[] params) {
+        final Map<String, Object> result = new HashMap<>();
+        for (String each : params) {
+            if (!Strings.isNullOrEmpty(info.getQueryParameters().getFirst(each))) {
+                result.put(each, info.getQueryParameters().getFirst(each));
+            }
+        }
+        return result;
+    }
+    
+    /**
+     * 获取任务运行结果统计数据.
+     * 
+     * @param since 时间跨度
+     * @return 任务运行结果统计数据
+     */
+    @GET
+    @Path("/statistics/tasks/results")
+    @Consumes(MediaType.APPLICATION_JSON)
+    public List<TaskResultStatistics> findTaskResultStatistics(@QueryParam("since") final String since) {
+        if ("last24hours".equals(since)) {
+            return statisticManager.findTaskResultStatisticsDaily();
+        } else {
+            return Collections.emptyList();
+        }
+    }
+    
+    /**
+     * 获取任务运行结果统计数据.
+     * 
+     * @param period 时间跨度
+     * @return 任务运行结果统计数据
+     */
+    @GET
+    @Path("/statistics/tasks/results/{period}")
+    @Consumes(MediaType.APPLICATION_JSON)
+    public TaskResultStatistics getTaskResultStatistics(@PathParam("period") final String period) {
+        if ("online".equals(period)) {
+            return statisticManager.getTaskResultStatisticsSinceOnline();
+        } else if ("lastWeek".equals(period)) {
+            return statisticManager.getTaskResultStatisticsWeekly();
+        } else if ("lastHour".equals(period)) {
+            return statisticManager.findLatestTaskResultStatistics(StatisticInterval.HOUR);
+        } else if ("lastMinute".equals(period)) {
+            return statisticManager.findLatestTaskResultStatistics(StatisticInterval.MINUTE);
+        } else {
+            return new TaskResultStatistics(0, 0, StatisticInterval.DAY, new Date());
+        }
+    }
+    
+    /**
+     * 获取任务运行统计数据集合.
+     * 
+     * @param since 时间跨度
+     * @return 任务运行统计数据集合
+     */
+    @GET
+    @Path("/statistics/tasks/running")
+    @Consumes(MediaType.APPLICATION_JSON)
+    public List<TaskRunningStatistics> findTaskRunningStatistics(@QueryParam("since") final String since) {
+        if ("lastWeek".equals(since)) {
+            return statisticManager.findTaskRunningStatisticsWeekly();
+        } else {
+            return Collections.emptyList();
+        }
+    }
+    
+    /**
+     * 获取作业类型统计数据.
+     * 
+     * @return 作业类型统计数据
+     */
+    @GET
+    @Path("/statistics/jobs/type")
+    @Consumes(MediaType.APPLICATION_JSON)
+    public JobTypeStatistics getJobTypeStatistics() {
+        return statisticManager.getJobTypeStatistics();
+    }
+    
+    /**
+     * 获取作业执行类型统计数据.
+     * 
+     * @return 作业执行类型统计数据
+     */
+    @GET
+    @Path("/statistics/jobs/executionType")
+    @Consumes(MediaType.APPLICATION_JSON)
+    public JobExecutionTypeStatistics getJobExecutionTypeStatistics() {
+        return statisticManager.getJobExecutionTypeStatistics();
+    }
+    
+    /**
+     * 获取一周以来作业运行统计数据集合.
+     * 
+     * @param since 时间跨度
+     * @return 一周以来任务运行统计数据集合
+     */
+    @GET
+    @Path("/statistics/jobs/running")
+    @Consumes(MediaType.APPLICATION_JSON)
+    public List<JobRunningStatistics> findJobRunningStatistics(@QueryParam("since") final String since) {
+        if ("lastWeek".equals(since)) {
+            return statisticManager.findJobRunningStatisticsWeekly();
+        } else {
+            return Collections.emptyList();
+        }
+    }
+    
+    /**
+     * 获取自上线以来作业注册统计数据集合.
+     * 
+     * @return 自上线以来作业注册统计数据集合
+     */
+    @GET
+    @Path("/statistics/jobs/register")
+    @Consumes(MediaType.APPLICATION_JSON)
+    public List<JobRegisterStatistics> findJobRegisterStatistics() {
+        return statisticManager.findJobRegisterStatisticsSinceOnline();
+    }
+}
diff --git a/elastic-job-cloud-scheduler/src/main/java/io/elasticjob/cloud/scheduler/restful/CloudOperationRestfulApi.java b/elastic-job-cloud-scheduler/src/main/java/io/elasticjob/cloud/scheduler/restful/CloudOperationRestfulApi.java
new file mode 100644
index 0000000..3e99db8
--- /dev/null
+++ b/elastic-job-cloud-scheduler/src/main/java/io/elasticjob/cloud/scheduler/restful/CloudOperationRestfulApi.java
@@ -0,0 +1,103 @@
+/*
+ * Copyright 1999-2015 dangdang.com.
+ * <p>
+ * Licensed 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.
+ * </p>
+ */
+
+package io.elasticjob.cloud.scheduler.restful;
+
+import io.elasticjob.cloud.scheduler.mesos.MesosStateService;
+import io.elasticjob.cloud.scheduler.mesos.ReconcileService;
+import io.elasticjob.cloud.reg.base.CoordinatorRegistryCenter;
+import com.google.common.base.Preconditions;
+import com.google.common.base.Strings;
+import com.google.gson.JsonArray;
+import lombok.extern.slf4j.Slf4j;
+import org.codehaus.jettison.json.JSONException;
+
+import javax.ws.rs.GET;
+import javax.ws.rs.POST;
+import javax.ws.rs.Path;
+import javax.ws.rs.QueryParam;
+
+/**
+ * 作业云维护服务.
+ * 
+ * @author gaohongtao.
+ */
+@Path("/operate")
+@Slf4j
+public final class CloudOperationRestfulApi {
+    
+    private static ReconcileService reconcileService;
+    
+    private static final long RECONCILE_MILLIS_INTERVAL = 10 * 1000L;
+    
+    private static MesosStateService mesosStateService;
+    
+    private static long lastReconcileTime;
+    
+    /**
+     * 初始化.
+     * 
+     * @param regCenter 注册中心
+     * @param reconcileService 协调服务
+     */
+    public static void init(final CoordinatorRegistryCenter regCenter, final ReconcileService reconcileService) {
+        CloudOperationRestfulApi.reconcileService = reconcileService;
+        CloudOperationRestfulApi.mesosStateService = new MesosStateService(regCenter);
+    }
+    
+    /**
+     * 显示协调服务.
+     * 
+     */
+    @POST
+    @Path("/reconcile/explicit")
+    public void explicitReconcile() {
+        validReconcileInterval();
+        reconcileService.explicitReconcile();
+    }
+    
+    /**
+     * 隐式协调服务.
+     */
+    @POST
+    @Path("/reconcile/implicit")
+    public void implicitReconcile() {
+        validReconcileInterval();
+        reconcileService.implicitReconcile();
+    }
+    
+    private void validReconcileInterval() {
+        if (System.currentTimeMillis() < lastReconcileTime + RECONCILE_MILLIS_INTERVAL) {
+            throw new RuntimeException("Repeat explicitReconcile");
+        }
+        lastReconcileTime = System.currentTimeMillis();
+    }
+    
+    /**
+     * 获取作业云App的沙箱信息.
+     *
+     * @param appName 云作业App配置名称
+     * @return 沙箱信息
+     * @throws JSONException JSON解析异常
+     */
+    @GET
+    @Path("/sandbox")
+    public JsonArray sandbox(@QueryParam("appName") final String appName) throws JSONException {
+        Preconditions.checkArgument(!Strings.isNullOrEmpty(appName), "Lack param 'appName'");
+        return mesosStateService.sandbox(appName);
+    }
+}
diff --git a/elastic-job-cloud-scheduler/src/main/java/io/elasticjob/cloud/scheduler/restful/RestfulService.java b/elastic-job-cloud-scheduler/src/main/java/io/elasticjob/cloud/scheduler/restful/RestfulService.java
new file mode 100644
index 0000000..6d42275
--- /dev/null
+++ b/elastic-job-cloud-scheduler/src/main/java/io/elasticjob/cloud/scheduler/restful/RestfulService.java
@@ -0,0 +1,67 @@
+/*
+ * Copyright 1999-2015 dangdang.com.
+ * <p>
+ * Licensed 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.
+ * </p>
+ */
+
+package io.elasticjob.cloud.scheduler.restful;
+
+import io.elasticjob.cloud.scheduler.env.RestfulServerConfiguration;
+import io.elasticjob.cloud.scheduler.mesos.ReconcileService;
+import io.elasticjob.cloud.scheduler.producer.ProducerManager;
+import io.elasticjob.cloud.reg.base.CoordinatorRegistryCenter;
+import io.elasticjob.cloud.restful.RestfulServer;
+import io.elasticjob.cloud.security.WwwAuthFilter;
+import com.google.common.base.Optional;
+
+/**
+ * 云作业Restful服务.
+ *
+ * @author caohao
+ */
+public final class RestfulService {
+    
+    private static final String CONSOLE_PATH = "console";
+    
+    private final RestfulServer restfulServer;
+    
+    public RestfulService(final CoordinatorRegistryCenter regCenter, final RestfulServerConfiguration config, final ProducerManager producerManager, final ReconcileService reconcileService) {
+        restfulServer = new RestfulServer(config.getPort());
+        CloudJobRestfulApi.init(regCenter, producerManager);
+        CloudAppRestfulApi.init(regCenter, producerManager);
+        CloudOperationRestfulApi.init(regCenter, reconcileService);
+    }
+    
+    /**
+     * 启动Restful服务.
+     */
+    public void start() {
+        try {
+            restfulServer.addFilter(WwwAuthFilter.class, "*/")
+                         .addFilter(WwwAuthFilter.class, "*.html")
+                         .start(RestfulService.class.getPackage().getName(), Optional.of(CONSOLE_PATH));
+            //CHECKSTYLE:OFF
+        } catch (final Exception ex) {
+            //CHECKSTYLE:ON
+            throw new RuntimeException(ex.getCause());
+        }
+    }
+    
+    /**
+     * 停止Restful服务.
+     */
+    public void stop() {
+        restfulServer.stop();
+    }
+}
diff --git a/elastic-job-cloud-scheduler/src/main/java/io/elasticjob/cloud/scheduler/state/StateNode.java b/elastic-job-cloud-scheduler/src/main/java/io/elasticjob/cloud/scheduler/state/StateNode.java
new file mode 100644
index 0000000..06f470f
--- /dev/null
+++ b/elastic-job-cloud-scheduler/src/main/java/io/elasticjob/cloud/scheduler/state/StateNode.java
@@ -0,0 +1,35 @@
+/*
+ * Copyright 1999-2015 dangdang.com.
+ * <p>
+ * Licensed 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.
+ * </p>
+ */
+
+package io.elasticjob.cloud.scheduler.state;
+
+import lombok.AccessLevel;
+import lombok.NoArgsConstructor;
+
+/**
+ * 状态根节点路径.
+ *
+ * @author zhangliang
+ */
+@NoArgsConstructor(access = AccessLevel.PRIVATE)
+public final class StateNode {
+    
+    /**
+     * 状态根节点.
+     */
+    public static final String ROOT = "/state";
+}
diff --git a/elastic-job-cloud-scheduler/src/main/java/io/elasticjob/cloud/scheduler/state/disable/app/DisableAppNode.java b/elastic-job-cloud-scheduler/src/main/java/io/elasticjob/cloud/scheduler/state/disable/app/DisableAppNode.java
new file mode 100644
index 0000000..d9810f0
--- /dev/null
+++ b/elastic-job-cloud-scheduler/src/main/java/io/elasticjob/cloud/scheduler/state/disable/app/DisableAppNode.java
@@ -0,0 +1,39 @@
+/*
+ * Copyright 1999-2015 dangdang.com.
+ * <p>
+ * Licensed 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.
+ * </p>
+ */
+
+package io.elasticjob.cloud.scheduler.state.disable.app;
+
+import io.elasticjob.cloud.scheduler.state.StateNode;
+import lombok.AccessLevel;
+import lombok.NoArgsConstructor;
+
+/**
+ * 禁用应用队列节点路径.
+ *
+ * @author caohao
+ */
+@NoArgsConstructor(access = AccessLevel.PRIVATE)
+final class DisableAppNode {
+    
+    static final String ROOT = StateNode.ROOT + "/disable/app";
+    
+    private static final String DISABLE_APP = ROOT + "/%s";
+    
+    static String getDisableAppNodePath(final String appName) {
+        return String.format(DISABLE_APP, appName);
+    }
+}
diff --git a/elastic-job-cloud-scheduler/src/main/java/io/elasticjob/cloud/scheduler/state/disable/app/DisableAppService.java b/elastic-job-cloud-scheduler/src/main/java/io/elasticjob/cloud/scheduler/state/disable/app/DisableAppService.java
new file mode 100644
index 0000000..cbda018
--- /dev/null
+++ b/elastic-job-cloud-scheduler/src/main/java/io/elasticjob/cloud/scheduler/state/disable/app/DisableAppService.java
@@ -0,0 +1,74 @@
+/*
+ * Copyright 1999-2015 dangdang.com.
+ * <p>
+ * Licensed 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.
+ * </p>
+ */
+
+package io.elasticjob.cloud.scheduler.state.disable.app;
+
+import io.elasticjob.cloud.scheduler.env.BootstrapEnvironment;
+import io.elasticjob.cloud.reg.base.CoordinatorRegistryCenter;
+import lombok.extern.slf4j.Slf4j;
+
+/**
+ * 禁用应用队列服务.
+ *
+ * @author caohao
+ */
+@Slf4j
+public class DisableAppService {
+    
+    private final BootstrapEnvironment env = BootstrapEnvironment.getInstance();
+    
+    private final CoordinatorRegistryCenter regCenter;
+    
+    public DisableAppService(final CoordinatorRegistryCenter regCenter) {
+        this.regCenter = regCenter;
+    }
+    
+    /**
+     * 将应用放入禁用队列.
+     *
+     * @param appName 应用名称
+     */
+    public void add(final String appName) {
+        if (regCenter.getNumChildren(DisableAppNode.ROOT) > env.getFrameworkConfiguration().getJobStateQueueSize()) {
+            log.warn("Cannot add disable app, caused by read state queue size is larger than {}.", env.getFrameworkConfiguration().getJobStateQueueSize());
+            return;
+        }
+        String disableAppNodePath = DisableAppNode.getDisableAppNodePath(appName);
+        if (!regCenter.isExisted(disableAppNodePath)) {
+            regCenter.persist(disableAppNodePath, appName);
+        }
+    }
+    
+    /**
+     * 从禁用应用队列中删除应用.
+     * 
+     * @param appName 待删除的应用名称
+     */
+    public void remove(final String appName) {
+        regCenter.remove(DisableAppNode.getDisableAppNodePath(appName));
+    }
+    
+    /**
+     * 判断应用是否在禁用应用队列中.
+     * 
+     * @param appName 应用名称
+     * @return 应用是否被禁用
+     */
+    public boolean isDisabled(final String appName) {
+        return regCenter.isExisted(DisableAppNode.getDisableAppNodePath(appName));
+    }
+}
diff --git a/elastic-job-cloud-scheduler/src/main/java/io/elasticjob/cloud/scheduler/state/disable/job/DisableJobNode.java b/elastic-job-cloud-scheduler/src/main/java/io/elasticjob/cloud/scheduler/state/disable/job/DisableJobNode.java
new file mode 100644
index 0000000..b323282
--- /dev/null
+++ b/elastic-job-cloud-scheduler/src/main/java/io/elasticjob/cloud/scheduler/state/disable/job/DisableJobNode.java
@@ -0,0 +1,39 @@
+/*
+ * Copyright 1999-2015 dangdang.com.
+ * <p>
+ * Licensed 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.
+ * </p>
+ */
+
+package io.elasticjob.cloud.scheduler.state.disable.job;
+
+import io.elasticjob.cloud.scheduler.state.StateNode;
+import lombok.AccessLevel;
+import lombok.NoArgsConstructor;
+
+/**
+ * 禁用作业队列节点路径.
+ *
+ * @author caohao
+ */
+@NoArgsConstructor(access = AccessLevel.PRIVATE)
+final class DisableJobNode {
+    
+    static final String ROOT = StateNode.ROOT + "/disable/job";
+    
+    private static final String DISABLE_JOB = ROOT + "/%s";
+    
+    static String getDisableJobNodePath(final String jobName) {
+        return String.format(DISABLE_JOB, jobName);
+    }
+}
diff --git a/elastic-job-cloud-scheduler/src/main/java/io/elasticjob/cloud/scheduler/state/disable/job/DisableJobService.java b/elastic-job-cloud-scheduler/src/main/java/io/elasticjob/cloud/scheduler/state/disable/job/DisableJobService.java
new file mode 100644
index 0000000..31d95b9
--- /dev/null
+++ b/elastic-job-cloud-scheduler/src/main/java/io/elasticjob/cloud/scheduler/state/disable/job/DisableJobService.java
@@ -0,0 +1,74 @@
+/*
+ * Copyright 1999-2015 dangdang.com.
+ * <p>
+ * Licensed 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.
+ * </p>
+ */
+
+package io.elasticjob.cloud.scheduler.state.disable.job;
+
+import io.elasticjob.cloud.scheduler.env.BootstrapEnvironment;
+import io.elasticjob.cloud.reg.base.CoordinatorRegistryCenter;
+import lombok.extern.slf4j.Slf4j;
+
+/**
+ * 禁用作业队列服务.
+ *
+ * @author caohao
+ */
+@Slf4j
+public class DisableJobService {
+    
+    private final BootstrapEnvironment env = BootstrapEnvironment.getInstance();
+    
+    private final CoordinatorRegistryCenter regCenter;
+    
+    public DisableJobService(final CoordinatorRegistryCenter regCenter) {
+        this.regCenter = regCenter;
+    }
+    
+    /**
+     * 将作业放入禁用队列.
+     *
+     * @param jobName 作业名称
+     */
+    public void add(final String jobName) {
+        if (regCenter.getNumChildren(DisableJobNode.ROOT) > env.getFrameworkConfiguration().getJobStateQueueSize()) {
+            log.warn("Cannot add disable job, caused by read state queue size is larger than {}.", env.getFrameworkConfiguration().getJobStateQueueSize());
+            return;
+        }
+        String disableJobNodePath = DisableJobNode.getDisableJobNodePath(jobName);
+        if (!regCenter.isExisted(disableJobNodePath)) {
+            regCenter.persist(disableJobNodePath, jobName);
+        }
+    }
+    
+    /**
+     * 从作业禁用队列中删除作业.
+     *
+     * @param jobName 待删除的作业名称
+     */
+    public void remove(final String jobName) {
+        regCenter.remove(DisableJobNode.getDisableJobNodePath(jobName));
+    }
+    
+    /**
+     * 判断作业是否在作业禁用队列中.
+     *
+     * @param jobName 作业名称
+     * @return 作业是否被禁用
+     */
+    public boolean isDisabled(final String jobName) {
+        return regCenter.isExisted(DisableJobNode.getDisableJobNodePath(jobName));
+    }
+}
diff --git a/elastic-job-cloud-scheduler/src/main/java/io/elasticjob/cloud/scheduler/state/failover/FailoverNode.java b/elastic-job-cloud-scheduler/src/main/java/io/elasticjob/cloud/scheduler/state/failover/FailoverNode.java
new file mode 100644
index 0000000..3738e42
--- /dev/null
+++ b/elastic-job-cloud-scheduler/src/main/java/io/elasticjob/cloud/scheduler/state/failover/FailoverNode.java
@@ -0,0 +1,46 @@
+/*
+ * Copyright 1999-2015 dangdang.com.
+ * <p>
+ * Licensed 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.
+ * </p>
+ */
+
+package io.elasticjob.cloud.scheduler.state.failover;
+
+import io.elasticjob.cloud.context.TaskContext;
+import io.elasticjob.cloud.scheduler.state.StateNode;
+import lombok.AccessLevel;
+import lombok.NoArgsConstructor;
+
+/**
+ * 待失效转移任务队列节点路径.
+ *
+ * @author zhangliang
+ */
+@NoArgsConstructor(access = AccessLevel.PRIVATE)
+final class FailoverNode {
+    
+    static final String ROOT = StateNode.ROOT + "/failover";
+    
+    private static final String FAILOVER_JOB = ROOT + "/%s";
+    
+    private static final String FAILOVER_TASK = FAILOVER_JOB + "/%s";
+    
+    static String getFailoverJobNodePath(final String jobName) {
+        return String.format(FAILOVER_JOB, jobName);
+    }
+    
+    static String getFailoverTaskNodePath(final String taskMetaInfo) {
+        return String.format(FAILOVER_TASK, TaskContext.MetaInfo.from(taskMetaInfo).getJobName(), taskMetaInfo);
+    }
+}
diff --git a/elastic-job-cloud-scheduler/src/main/java/io/elasticjob/cloud/scheduler/state/failover/FailoverService.java b/elastic-job-cloud-scheduler/src/main/java/io/elasticjob/cloud/scheduler/state/failover/FailoverService.java
new file mode 100644
index 0000000..6b19710
--- /dev/null
+++ b/elastic-job-cloud-scheduler/src/main/java/io/elasticjob/cloud/scheduler/state/failover/FailoverService.java
@@ -0,0 +1,188 @@
+/*
+ * Copyright 1999-2015 dangdang.com.
+ * <p>
+ * Licensed 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.
+ * </p>
+ */
+
+package io.elasticjob.cloud.scheduler.state.failover;
+
+import io.elasticjob.cloud.scheduler.config.job.CloudJobConfiguration;
+import io.elasticjob.cloud.scheduler.config.job.CloudJobConfigurationService;
+import io.elasticjob.cloud.scheduler.env.BootstrapEnvironment;
+import io.elasticjob.cloud.scheduler.state.running.RunningService;
+import io.elasticjob.cloud.context.ExecutionType;
+import io.elasticjob.cloud.scheduler.context.JobContext;
+import io.elasticjob.cloud.context.TaskContext;
+import io.elasticjob.cloud.reg.base.CoordinatorRegistryCenter;
+import com.google.common.base.Charsets;
+import com.google.common.base.Optional;
+import com.google.common.base.Strings;
+import com.google.common.hash.HashCode;
+import com.google.common.hash.Hashing;
+import lombok.extern.slf4j.Slf4j;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+/**
+ * 失效转移队列服务.
+ *
+ * @author zhangliang
+ */
+@Slf4j
+public final class FailoverService {
+    
+    private final BootstrapEnvironment env = BootstrapEnvironment.getInstance();
+    
+    private final CoordinatorRegistryCenter regCenter;
+    
+    private final CloudJobConfigurationService configService;
+    
+    private final RunningService runningService;
+    
+    public FailoverService(final CoordinatorRegistryCenter regCenter) {
+        this.regCenter = regCenter;
+        configService = new CloudJobConfigurationService(regCenter);
+        runningService = new RunningService(regCenter);
+    }
+    
+    /**
+     * 将任务放入失效转移队列.
+     *
+     * @param taskContext 任务运行时上下文
+     */
+    public void add(final TaskContext taskContext) {
+        if (regCenter.getNumChildren(FailoverNode.ROOT) > env.getFrameworkConfiguration().getJobStateQueueSize()) {
+            log.warn("Cannot add job, caused by read state queue size is larger than {}.", env.getFrameworkConfiguration().getJobStateQueueSize());
+            return;
+        }
+        String failoverTaskNodePath = FailoverNode.getFailoverTaskNodePath(taskContext.getMetaInfo().toString());
+        if (!regCenter.isExisted(failoverTaskNodePath) && !runningService.isTaskRunning(taskContext.getMetaInfo())) {
+            // TODO Daemon类型作业增加存储是否立即失效转移
+            regCenter.persist(failoverTaskNodePath, taskContext.getId());
+        }
+    }
+    
+    /**
+     * 从失效转移队列中获取所有有资格执行的作业上下文.
+     *
+     * @return 有资格执行的作业上下文集合
+     */
+    public Collection<JobContext> getAllEligibleJobContexts() {
+        if (!regCenter.isExisted(FailoverNode.ROOT)) {
+            return Collections.emptyList();
+        }
+        List<String> jobNames = regCenter.getChildrenKeys(FailoverNode.ROOT);
+        Collection<JobContext> result = new ArrayList<>(jobNames.size());
+        Set<HashCode> assignedTasks = new HashSet<>(jobNames.size() * 10, 1);
+        for (String each : jobNames) {
+            List<String> taskIdList = regCenter.getChildrenKeys(FailoverNode.getFailoverJobNodePath(each));
+            if (taskIdList.isEmpty()) {
+                regCenter.remove(FailoverNode.getFailoverJobNodePath(each));
+                continue;
+            }
+            Optional<CloudJobConfiguration> jobConfig = configService.load(each);
+            if (!jobConfig.isPresent()) {
+                regCenter.remove(FailoverNode.getFailoverJobNodePath(each));
+                continue;
+            }
+            List<Integer> assignedShardingItems = getAssignedShardingItems(each, taskIdList, assignedTasks);
+            if (!assignedShardingItems.isEmpty() && jobConfig.isPresent()) {
+                result.add(new JobContext(jobConfig.get(), assignedShardingItems, ExecutionType.FAILOVER));    
+            }
+        }
+        return result;
+    }
+    
+    private List<Integer> getAssignedShardingItems(final String jobName, final List<String> taskIdList, final Set<HashCode> assignedTasks) {
+        List<Integer> result = new ArrayList<>(taskIdList.size());
+        for (String each : taskIdList) {
+            TaskContext.MetaInfo metaInfo = TaskContext.MetaInfo.from(each);
+            if (assignedTasks.add(Hashing.md5().newHasher().putString(jobName, Charsets.UTF_8).putInt(metaInfo.getShardingItems().get(0)).hash()) && !runningService.isTaskRunning(metaInfo)) {
+                result.add(metaInfo.getShardingItems().get(0));
+            }
+        }
+        return result;
+    }
+    
+    /**
+     * 从失效转移队列中删除相关任务.
+     * 
+     * @param metaInfoList 待删除的任务元信息集合
+     */
+    public void remove(final Collection<TaskContext.MetaInfo> metaInfoList) {
+        for (TaskContext.MetaInfo each : metaInfoList) {
+            regCenter.remove(FailoverNode.getFailoverTaskNodePath(each.toString()));
+        }
+    }
+    
+    /**
+     * 从失效转移队列中查找任务.
+     *
+     * @param metaInfo 任务元信息
+     * @return 失效转移任务Id
+     */
+    public Optional<String> getTaskId(final TaskContext.MetaInfo metaInfo) {
+        String failoverTaskNodePath = FailoverNode.getFailoverTaskNodePath(metaInfo.toString());
+        Optional<String> result = Optional.absent();
+        if (regCenter.isExisted(failoverTaskNodePath)) {
+            result = Optional.of(regCenter.get(failoverTaskNodePath));
+        }
+        return result;
+    }
+    
+    /**
+     * 获取待失效转移的全部任务.
+     * 
+     * @return 待失效转移的全部任务
+     */
+    public Map<String, Collection<FailoverTaskInfo>> getAllFailoverTasks() {
+        if (!regCenter.isExisted(FailoverNode.ROOT)) {
+            return Collections.emptyMap();
+        }
+        List<String> jobNames = regCenter.getChildrenKeys(FailoverNode.ROOT);
+        Map<String, Collection<FailoverTaskInfo>> result = new HashMap<>(jobNames.size(), 1);
+        for (String each : jobNames) {
+            Collection<FailoverTaskInfo> failoverTasks = getFailoverTasks(each);
+            if (!failoverTasks.isEmpty()) {
+                result.put(each, failoverTasks);
+            }
+        }
+        return result;
+    }
+    
+    /**
+     * 获取待失效转移的任务集合.
+     *
+     * @param jobName 作业名称
+     * @return 待失效转移的任务集合
+     */
+    private Collection<FailoverTaskInfo> getFailoverTasks(final String jobName) {
+        List<String> failOverTasks = regCenter.getChildrenKeys(FailoverNode.getFailoverJobNodePath(jobName));
+        List<FailoverTaskInfo> result = new ArrayList<>(failOverTasks.size());
+        for (String each : failOverTasks) {
+            String originalTaskId = regCenter.get(FailoverNode.getFailoverTaskNodePath(each));
+            if (!Strings.isNullOrEmpty(originalTaskId)) {
+                result.add(new FailoverTaskInfo(TaskContext.MetaInfo.from(each), originalTaskId));
+            }
+        }
+        return result;
+    }
+}
diff --git a/elastic-job-cloud-scheduler/src/main/java/io/elasticjob/cloud/scheduler/state/failover/FailoverTaskInfo.java b/elastic-job-cloud-scheduler/src/main/java/io/elasticjob/cloud/scheduler/state/failover/FailoverTaskInfo.java
new file mode 100644
index 0000000..be44270
--- /dev/null
+++ b/elastic-job-cloud-scheduler/src/main/java/io/elasticjob/cloud/scheduler/state/failover/FailoverTaskInfo.java
@@ -0,0 +1,36 @@
+/*
+ * Copyright 1999-2015 dangdang.com.
+ * <p>
+ * Licensed 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.
+ * </p>
+ */
+
+package io.elasticjob.cloud.scheduler.state.failover;
+
+import io.elasticjob.cloud.context.TaskContext;
+import lombok.Getter;
+import lombok.RequiredArgsConstructor;
+
+/**
+ * 待失效转移任务节点信息.
+ *
+ * @author liguangyun
+ */
+@RequiredArgsConstructor
+@Getter
+public final class FailoverTaskInfo {
+    
+    private final TaskContext.MetaInfo taskInfo;
+    
+    private final String originalTaskId;
+}
diff --git a/elastic-job-cloud-scheduler/src/main/java/io/elasticjob/cloud/scheduler/state/ready/ReadyNode.java b/elastic-job-cloud-scheduler/src/main/java/io/elasticjob/cloud/scheduler/state/ready/ReadyNode.java
new file mode 100644
index 0000000..2778d42
--- /dev/null
+++ b/elastic-job-cloud-scheduler/src/main/java/io/elasticjob/cloud/scheduler/state/ready/ReadyNode.java
@@ -0,0 +1,39 @@
+/*
+ * Copyright 1999-2015 dangdang.com.
+ * <p>
+ * Licensed 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.
+ * </p>
+ */
+
+package io.elasticjob.cloud.scheduler.state.ready;
+
+import io.elasticjob.cloud.scheduler.state.StateNode;
+import lombok.AccessLevel;
+import lombok.NoArgsConstructor;
+
+/**
+ * 待运行作业队列节点路径.
+ *
+ * @author zhangliang
+ */
+@NoArgsConstructor(access = AccessLevel.PRIVATE)
+final class ReadyNode {
+    
+    static final String ROOT = StateNode.ROOT + "/ready";
+    
+    private static final String READY_JOB = ROOT + "/%s";
+    
+    static String getReadyJobNodePath(final String jobName) {
+        return String.format(READY_JOB, jobName);
+    }
+}
diff --git a/elastic-job-cloud-scheduler/src/main/java/io/elasticjob/cloud/scheduler/state/ready/ReadyService.java b/elastic-job-cloud-scheduler/src/main/java/io/elasticjob/cloud/scheduler/state/ready/ReadyService.java
new file mode 100644
index 0000000..660f7be
--- /dev/null
+++ b/elastic-job-cloud-scheduler/src/main/java/io/elasticjob/cloud/scheduler/state/ready/ReadyService.java
@@ -0,0 +1,188 @@
+/*
+ * Copyright 1999-2015 dangdang.com.
+ * <p>
+ * Licensed 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.
+ * </p>
+ */
+
+package io.elasticjob.cloud.scheduler.state.ready;
+
+import io.elasticjob.cloud.scheduler.config.job.CloudJobConfiguration;
+import io.elasticjob.cloud.scheduler.config.job.CloudJobConfigurationService;
+import io.elasticjob.cloud.scheduler.config.job.CloudJobExecutionType;
+import io.elasticjob.cloud.scheduler.context.JobContext;
+import io.elasticjob.cloud.scheduler.env.BootstrapEnvironment;
+import io.elasticjob.cloud.scheduler.state.running.RunningService;
+import io.elasticjob.cloud.context.ExecutionType;
+import io.elasticjob.cloud.reg.base.CoordinatorRegistryCenter;
+import com.google.common.base.Function;
+import com.google.common.base.Optional;
+import com.google.common.base.Strings;
+import com.google.common.collect.Collections2;
+import lombok.extern.slf4j.Slf4j;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * 待运行作业队列服务.
+ *
+ * @author zhangliang
+ * @author liguangyun
+ */
+@Slf4j
+public final class ReadyService {
+    
+    private final BootstrapEnvironment env = BootstrapEnvironment.getInstance();
+    
+    private final CoordinatorRegistryCenter regCenter;
+    
+    private final CloudJobConfigurationService configService;
+    
+    private final RunningService runningService;
+    
+    public ReadyService(final CoordinatorRegistryCenter regCenter) {
+        this.regCenter = regCenter;
+        configService = new CloudJobConfigurationService(regCenter);
+        runningService = new RunningService(regCenter);
+    }
+    
+    /**
+     * 将瞬时作业放入待执行队列.
+     * 
+     * @param jobName 作业名称
+     */
+    public void addTransient(final String jobName) {
+        if (regCenter.getNumChildren(ReadyNode.ROOT) > env.getFrameworkConfiguration().getJobStateQueueSize()) {
+            log.warn("Cannot add transient job, caused by read state queue size is larger than {}.", env.getFrameworkConfiguration().getJobStateQueueSize());
+            return;
+        }
+        Optional<CloudJobConfiguration> cloudJobConfig = configService.load(jobName);
+        if (!cloudJobConfig.isPresent() || CloudJobExecutionType.TRANSIENT != cloudJobConfig.get().getJobExecutionType()) {
+            return;
+        }
+        String readyJobNode = ReadyNode.getReadyJobNodePath(jobName);
+        String times = regCenter.getDirectly(readyJobNode);
+        if (cloudJobConfig.get().getTypeConfig().getCoreConfig().isMisfire()) {
+            regCenter.persist(readyJobNode, Integer.toString(null == times ? 1 : Integer.parseInt(times) + 1));
+        } else {
+            regCenter.persist(ReadyNode.getReadyJobNodePath(jobName), "1");
+        }
+    }
+    
+    /**
+     * 将常驻作业放入待执行队列.
+     *
+     * @param jobName 作业名称
+     */
+    public void addDaemon(final String jobName) {
+        if (regCenter.getNumChildren(ReadyNode.ROOT) > env.getFrameworkConfiguration().getJobStateQueueSize()) {
+            log.warn("Cannot add daemon job, caused by read state queue size is larger than {}.", env.getFrameworkConfiguration().getJobStateQueueSize());
+            return;
+        }
+        Optional<CloudJobConfiguration> cloudJobConfig = configService.load(jobName);
+        if (!cloudJobConfig.isPresent() || CloudJobExecutionType.DAEMON != cloudJobConfig.get().getJobExecutionType() || runningService.isJobRunning(jobName)) {
+            return;
+        }
+        regCenter.persist(ReadyNode.getReadyJobNodePath(jobName), "1");
+    }
+    
+    /**
+     * 设置禁用错过重执行.
+     * 
+     * @param jobName 作业名称
+     */
+    public void setMisfireDisabled(final String jobName) {
+        Optional<CloudJobConfiguration> cloudJobConfig = configService.load(jobName);
+        if (cloudJobConfig.isPresent() && null != regCenter.getDirectly(ReadyNode.getReadyJobNodePath(jobName))) {
+            regCenter.persist(ReadyNode.getReadyJobNodePath(jobName), "1");
+        }
+    }
+    
+    /**
+     * 从待执行队列中获取所有有资格执行的作业上下文.
+     *
+     * @param ineligibleJobContexts 无资格执行的作业上下文
+     * @return 有资格执行的作业上下文集合
+     */
+    public Collection<JobContext> getAllEligibleJobContexts(final Collection<JobContext> ineligibleJobContexts) {
+        if (!regCenter.isExisted(ReadyNode.ROOT)) {
+            return Collections.emptyList();
+        }
+        Collection<String> ineligibleJobNames = Collections2.transform(ineligibleJobContexts, new Function<JobContext, String>() {
+            
+            @Override
+            public String apply(final JobContext input) {
+                return input.getJobConfig().getJobName();
+            }
+        });
+        List<String> jobNames = regCenter.getChildrenKeys(ReadyNode.ROOT);
+        List<JobContext> result = new ArrayList<>(jobNames.size());
+        for (String each : jobNames) {
+            if (ineligibleJobNames.contains(each)) {
+                continue;
+            }
+            Optional<CloudJobConfiguration> jobConfig = configService.load(each);
+            if (!jobConfig.isPresent()) {
+                regCenter.remove(ReadyNode.getReadyJobNodePath(each));
+                continue;
+            }
+            if (!runningService.isJobRunning(each)) {
+                result.add(JobContext.from(jobConfig.get(), ExecutionType.READY));
+            }
+        }
+        return result;
+    }
+    
+    /**
+     * 从待执行队列中删除相关作业.
+     *
+     * @param jobNames 待删除的作业名集合
+     */
+    public void remove(final Collection<String> jobNames) {
+        for (String each : jobNames) {
+            String readyJobNode = ReadyNode.getReadyJobNodePath(each);
+            String timesStr = regCenter.getDirectly(readyJobNode);
+            int times = null == timesStr ? 0 : Integer.parseInt(timesStr);
+            if (times <= 1) {
+                regCenter.remove(readyJobNode);
+            } else {
+                regCenter.persist(readyJobNode, Integer.toString(times - 1));
+            }
+        }
+    }
+    
+    /**
+     * 获取待运行的全部任务.
+     * 
+     * @return 待运行的全部任务
+     */
+    public Map<String, Integer> getAllReadyTasks() {
+        if (!regCenter.isExisted(ReadyNode.ROOT)) {
+            return Collections.emptyMap();
+        }
+        List<String> jobNames = regCenter.getChildrenKeys(ReadyNode.ROOT);
+        Map<String, Integer> result = new HashMap<>(jobNames.size(), 1);
+        for (String each : jobNames) {
+            String times = regCenter.get(ReadyNode.getReadyJobNodePath(each));
+            if (!Strings.isNullOrEmpty(times)) {
+                result.put(each, Integer.parseInt(times));
+            }
+        }
+        return result;
+    }
+}
diff --git a/elastic-job-cloud-scheduler/src/main/java/io/elasticjob/cloud/scheduler/state/running/RunningNode.java b/elastic-job-cloud-scheduler/src/main/java/io/elasticjob/cloud/scheduler/state/running/RunningNode.java
new file mode 100644
index 0000000..f88b5b9
--- /dev/null
+++ b/elastic-job-cloud-scheduler/src/main/java/io/elasticjob/cloud/scheduler/state/running/RunningNode.java
@@ -0,0 +1,46 @@
+/*
+ * Copyright 1999-2015 dangdang.com.
+ * <p>
+ * Licensed 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.
+ * </p>
+ */
+
+package io.elasticjob.cloud.scheduler.state.running;
+
+import io.elasticjob.cloud.scheduler.state.StateNode;
+import io.elasticjob.cloud.context.TaskContext;
+import lombok.AccessLevel;
+import lombok.NoArgsConstructor;
+
+/**
+ * 运行中任务节点路径.
+ *
+ * @author zhangliang
+ */
+@NoArgsConstructor(access = AccessLevel.PRIVATE)
+final class RunningNode {
+    
+    static final String ROOT = StateNode.ROOT + "/running";
+    
+    private static final String RUNNING_JOB = ROOT + "/%s";
+    
+    private static final String RUNNING_TASK = RUNNING_JOB + "/%s";
+    
+    static String getRunningJobNodePath(final String jobName) {
+        return String.format(RUNNING_JOB, jobName);
+    }
+    
+    static String getRunningTaskNodePath(final String taskMetaInfo) {
+        return String.format(RUNNING_TASK, TaskContext.MetaInfo.from(taskMetaInfo).getJobName(), taskMetaInfo);
+    }
+}
diff --git a/elastic-job-cloud-scheduler/src/main/java/io/elasticjob/cloud/scheduler/state/running/RunningService.java b/elastic-job-cloud-scheduler/src/main/java/io/elasticjob/cloud/scheduler/state/running/RunningService.java
new file mode 100644
index 0000000..7cd9d28
--- /dev/null
+++ b/elastic-job-cloud-scheduler/src/main/java/io/elasticjob/cloud/scheduler/state/running/RunningService.java
@@ -0,0 +1,268 @@
+/*
+ * Copyright 1999-2015 dangdang.com.
+ * <p>
+ * Licensed 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.
+ * </p>
+ */
+
+package io.elasticjob.cloud.scheduler.state.running;
+
+import io.elasticjob.cloud.scheduler.config.job.CloudJobConfiguration;
+import io.elasticjob.cloud.scheduler.config.job.CloudJobConfigurationService;
+import io.elasticjob.cloud.scheduler.config.job.CloudJobExecutionType;
+import io.elasticjob.cloud.context.TaskContext;
+import io.elasticjob.cloud.reg.base.CoordinatorRegistryCenter;
+import com.google.common.base.Function;
+import com.google.common.base.Optional;
+import com.google.common.base.Predicate;
+import com.google.common.collect.Iterators;
+import com.google.common.collect.Lists;
+import com.google.common.collect.Sets;
+import lombok.Getter;
+import lombok.RequiredArgsConstructor;
+
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.CopyOnWriteArraySet;
+
+/**
+ * 任务运行时服务.
+ *
+ * @author zhangliang
+ */
+@RequiredArgsConstructor
+public final class RunningService {
+    
+    private static final int TASK_INITIAL_SIZE = 1024;
+    
+    // TODO 使用JMX导出
+    @Getter
+    private static final ConcurrentHashMap<String, Set<TaskContext>> RUNNING_TASKS = new ConcurrentHashMap<>(TASK_INITIAL_SIZE);
+    
+    private static final ConcurrentHashMap<String, String> TASK_HOSTNAME_MAPPER = new ConcurrentHashMap<>(TASK_INITIAL_SIZE);
+    
+    private final CoordinatorRegistryCenter regCenter;
+    
+    private final CloudJobConfigurationService configurationService;
+    
+    public RunningService(final CoordinatorRegistryCenter regCenter) {
+        this.regCenter = regCenter;
+        this.configurationService = new CloudJobConfigurationService(regCenter);
+    }
+    
+    /**
+     * 启动任务运行队列.
+     */
+    public void start() {
+        clear();
+        List<String> jobKeys = regCenter.getChildrenKeys(RunningNode.ROOT);
+        for (String each : jobKeys) {
+            if (!configurationService.load(each).isPresent()) {
+                remove(each);
+                continue;
+            }
+            RUNNING_TASKS.put(each, Sets.newCopyOnWriteArraySet(Lists.transform(regCenter.getChildrenKeys(RunningNode.getRunningJobNodePath(each)), new Function<String, TaskContext>() {
+                
+                @Override
+                public TaskContext apply(final String input) {
+                    return TaskContext.from(regCenter.get(RunningNode.getRunningTaskNodePath(TaskContext.MetaInfo.from(input).toString())));
+                }
+            })));
+        }
+    }
+    
+    /**
+     * 将任务运行时上下文放入运行时队列.
+     * 
+     * @param taskContext 任务运行时上下文
+     */
+    public void add(final TaskContext taskContext) {
+        if (!configurationService.load(taskContext.getMetaInfo().getJobName()).isPresent()) {
+            return;
+        }
+        getRunningTasks(taskContext.getMetaInfo().getJobName()).add(taskContext);
+        if (!isDaemon(taskContext.getMetaInfo().getJobName())) {
+            return;
+        }
+        String runningTaskNodePath = RunningNode.getRunningTaskNodePath(taskContext.getMetaInfo().toString());
+        if (!regCenter.isExisted(runningTaskNodePath)) {
+            regCenter.persist(runningTaskNodePath, taskContext.getId());
+        }
+    }
+    
+    private boolean isDaemon(final String jobName) {
+        Optional<CloudJobConfiguration> cloudJobConfigurationOptional = configurationService.load(jobName);
+        return cloudJobConfigurationOptional.isPresent() && CloudJobExecutionType.DAEMON == cloudJobConfigurationOptional.get().getJobExecutionType();
+    }
+    
+    /**
+     * 更新作业闲置状态.
+     * @param taskContext 任务运行时上下文
+     * @param isIdle 是否闲置
+     */
+    public void updateIdle(final TaskContext taskContext, final boolean isIdle) {
+        synchronized (RUNNING_TASKS) {
+            Optional<TaskContext> taskContextOptional = findTask(taskContext);
+            if (taskContextOptional.isPresent()) {
+                taskContextOptional.get().setIdle(isIdle);
+            } else {
+                add(taskContext);
+            }
+        }
+    }
+    
+    private Optional<TaskContext> findTask(final TaskContext taskContext) {
+        return Iterators.tryFind(getRunningTasks(taskContext.getMetaInfo().getJobName()).iterator(), new Predicate<TaskContext>() {
+            @Override
+            public boolean apply(final TaskContext input) {
+                return input.equals(taskContext);
+            }
+        });
+    }
+    
+    /**
+     * 将作业从运行时队列删除.
+     *
+     * @param jobName 作业名称
+     */
+    public void remove(final String jobName) {
+        RUNNING_TASKS.remove(jobName);
+        if (!isDaemonOrAbsent(jobName)) {
+            return;
+        }
+        regCenter.remove(RunningNode.getRunningJobNodePath(jobName));
+    }
+        
+    /**
+     * 将任务从运行时队列删除.
+     * 
+     * @param taskContext 任务运行时上下文
+     */
+    public void remove(final TaskContext taskContext) {
+        getRunningTasks(taskContext.getMetaInfo().getJobName()).remove(taskContext);
+        if (!isDaemonOrAbsent(taskContext.getMetaInfo().getJobName())) {
+            return;
+        }
+        regCenter.remove(RunningNode.getRunningTaskNodePath(taskContext.getMetaInfo().toString()));
+        String jobRootNode = RunningNode.getRunningJobNodePath(taskContext.getMetaInfo().getJobName());
+        if (regCenter.isExisted(jobRootNode) && regCenter.getChildrenKeys(jobRootNode).isEmpty()) {
+            regCenter.remove(jobRootNode);
+        }
+    }
+    
+    private boolean isDaemonOrAbsent(final String jobName) {
+        Optional<CloudJobConfiguration> cloudJobConfigurationOptional = configurationService.load(jobName);
+        return !cloudJobConfigurationOptional.isPresent() || CloudJobExecutionType.DAEMON == cloudJobConfigurationOptional.get().getJobExecutionType();
+    }
+    
+    /**
+     * 判断作业是否运行.
+     *
+     * @param jobName 作业名称
+     * @return 作业是否运行
+     */
+    public boolean isJobRunning(final String jobName) {
+        return !getRunningTasks(jobName).isEmpty();
+    }
+    
+    /**
+     * 判断任务是否运行.
+     *
+     * @param metaInfo 任务元信息
+     * @return 任务是否运行
+     */
+    public boolean isTaskRunning(final TaskContext.MetaInfo metaInfo) {
+        for (TaskContext each : getRunningTasks(metaInfo.getJobName())) {
+            if (each.getMetaInfo().equals(metaInfo)) {
+                return true;
+            }
+        }
+        return false;
+    }
+    
+    /**
+     * 获取运行中的任务集合.
+     *
+     * @param jobName 作业名称
+     * @return 运行中的任务集合
+     */
+    public Collection<TaskContext> getRunningTasks(final String jobName) {
+        Set<TaskContext> taskContexts = new CopyOnWriteArraySet<>();
+        Collection<TaskContext> result = RUNNING_TASKS.putIfAbsent(jobName, taskContexts);
+        return null == result ? taskContexts : result;
+    }
+    
+    /**
+     * 获取运行中的全部任务.
+     *
+     * @return 运行中的全部任务
+     */
+    public Map<String, Set<TaskContext>> getAllRunningTasks() {
+        Map<String, Set<TaskContext>> result = new HashMap<>(RUNNING_TASKS.size(), 1);
+        result.putAll(RUNNING_TASKS);
+        return result;
+    }
+    
+    /**
+     * 获取所有的运行中的常驻作业.
+     * 
+     * @return 运行中常驻作业集合
+     */
+    public Set<TaskContext> getAllRunningDaemonTasks() {
+        List<String> jobKeys = regCenter.getChildrenKeys(RunningNode.ROOT);
+        for (String each : jobKeys) {
+            if (!RUNNING_TASKS.containsKey(each)) {
+                remove(each);
+            }
+        }
+        Set<TaskContext> result = Sets.newHashSet();
+        for (Map.Entry<String, Set<TaskContext>> each : RUNNING_TASKS.entrySet()) {
+            if (isDaemonOrAbsent(each.getKey())) {
+                result.addAll(each.getValue());
+            }
+        }
+        return result;
+    }
+    
+    /**
+     * 添加任务主键和主机名称的映射.
+     *
+     * @param taskId 任务主键
+     * @param hostname 主机名称
+     */
+    public void addMapping(final String taskId, final String hostname) {
+        TASK_HOSTNAME_MAPPER.putIfAbsent(taskId, hostname);
+    }
+    
+    /**
+     * 根据任务主键获取主机名称并清除该任务.
+     *
+     * @param taskId 任务主键
+     * @return 删除任务的主机名称
+     */
+    public String popMapping(final String taskId) {
+        return TASK_HOSTNAME_MAPPER.remove(taskId);
+    }
+    
+    /**
+     * 清理所有运行时状态.
+     */
+    public void clear() {
+        RUNNING_TASKS.clear();
+        TASK_HOSTNAME_MAPPER.clear();
+    }
+}
diff --git a/elastic-job-cloud-scheduler/src/main/java/io/elasticjob/cloud/scheduler/statistics/StatisticManager.java b/elastic-job-cloud-scheduler/src/main/java/io/elasticjob/cloud/scheduler/statistics/StatisticManager.java
new file mode 100644
index 0000000..9d37967
--- /dev/null
+++ b/elastic-job-cloud-scheduler/src/main/java/io/elasticjob/cloud/scheduler/statistics/StatisticManager.java
@@ -0,0 +1,295 @@
+/*
+ * Copyright 1999-2015 dangdang.com.
+ * <p>
+ * Licensed 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.
+ * </p>
+ */
+
+package io.elasticjob.cloud.scheduler.statistics;
+
+import io.elasticjob.cloud.api.JobType;
+import io.elasticjob.cloud.scheduler.statistics.job.JobRunningStatisticJob;
+import io.elasticjob.cloud.event.rdb.JobEventRdbConfiguration;
+import io.elasticjob.cloud.scheduler.config.job.CloudJobConfiguration;
+import io.elasticjob.cloud.scheduler.config.job.CloudJobConfigurationService;
+import io.elasticjob.cloud.scheduler.config.job.CloudJobExecutionType;
+import io.elasticjob.cloud.scheduler.statistics.job.RegisteredJobStatisticJob;
+import io.elasticjob.cloud.scheduler.statistics.job.TaskResultStatisticJob;
+import io.elasticjob.cloud.scheduler.statistics.util.StatisticTimeUtils;
+import io.elasticjob.cloud.statistics.type.job.JobRegisterStatistics;
+import io.elasticjob.cloud.statistics.type.task.TaskResultStatistics;
+import io.elasticjob.cloud.reg.base.CoordinatorRegistryCenter;
+import io.elasticjob.cloud.statistics.StatisticInterval;
+import io.elasticjob.cloud.statistics.rdb.StatisticRdbRepository;
+import io.elasticjob.cloud.statistics.type.job.JobExecutionTypeStatistics;
+import io.elasticjob.cloud.statistics.type.job.JobRunningStatistics;
+import io.elasticjob.cloud.statistics.type.job.JobTypeStatistics;
+import io.elasticjob.cloud.statistics.type.task.TaskRunningStatistics;
+import com.google.common.base.Optional;
+import lombok.AccessLevel;
+import lombok.RequiredArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
+
+import java.sql.SQLException;
+import java.text.ParseException;
+import java.text.SimpleDateFormat;
+import java.util.Collections;
+import java.util.Date;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * 统计作业调度管理器.
+ *
+ * @author liguangyun
+ */
+@Slf4j
+@RequiredArgsConstructor(access = AccessLevel.PRIVATE)
+public final class StatisticManager {
+    
+    private static volatile StatisticManager instance;
+    
+    private final CoordinatorRegistryCenter registryCenter;
+    
+    private final CloudJobConfigurationService configurationService;
+    
+    private final Optional<JobEventRdbConfiguration> jobEventRdbConfiguration;
+    
+    private final StatisticsScheduler scheduler;
+    
+    private final Map<StatisticInterval, TaskResultMetaData> statisticData;
+    
+    private StatisticRdbRepository rdbRepository;
+    
+    private StatisticManager(final CoordinatorRegistryCenter registryCenter, final Optional<JobEventRdbConfiguration> jobEventRdbConfiguration,
+                             final StatisticsScheduler scheduler, final Map<StatisticInterval, TaskResultMetaData> statisticData) {
+        this.registryCenter = registryCenter;
+        this.configurationService = new CloudJobConfigurationService(registryCenter);
+        this.jobEventRdbConfiguration = jobEventRdbConfiguration;
+        this.scheduler = scheduler;
+        this.statisticData = statisticData;
+    }
+    
+    /**
+     * 获取统计作业调度管理器.
+     * 
+     * @param regCenter 注册中心
+     * @param jobEventRdbConfiguration 作业数据库事件配置
+     * @return 调度管理器对象
+     */
+    public static StatisticManager getInstance(final CoordinatorRegistryCenter regCenter, final Optional<JobEventRdbConfiguration> jobEventRdbConfiguration) {
+        if (null == instance) {
+            synchronized (StatisticManager.class) {
+                if (null == instance) {
+                    Map<StatisticInterval, TaskResultMetaData> statisticData = new HashMap<>();
+                    statisticData.put(StatisticInterval.MINUTE, new TaskResultMetaData());
+                    statisticData.put(StatisticInterval.HOUR, new TaskResultMetaData());
+                    statisticData.put(StatisticInterval.DAY, new TaskResultMetaData());
+                    instance = new StatisticManager(regCenter, jobEventRdbConfiguration, new StatisticsScheduler(), statisticData);
+                    init();
+                }
+            }
+        }
+        return instance;
+    }
+    
+    private static void init() {
+        if (instance.jobEventRdbConfiguration.isPresent()) {
+            try {
+                instance.rdbRepository = new StatisticRdbRepository(instance.jobEventRdbConfiguration.get().getDataSource());
+            } catch (final SQLException ex) {
+                log.error("Init StatisticRdbRepository error:", ex);
+            }
+        }
+    }
+    
+    /**
+     * 启动统计作业调度.
+     */
+    public void startup() {
+        if (null != rdbRepository) {
+            scheduler.start();
+            scheduler.register(new TaskResultStatisticJob(StatisticInterval.MINUTE, statisticData.get(StatisticInterval.MINUTE), rdbRepository));
+            scheduler.register(new TaskResultStatisticJob(StatisticInterval.HOUR, statisticData.get(StatisticInterval.HOUR), rdbRepository));
+            scheduler.register(new TaskResultStatisticJob(StatisticInterval.DAY, statisticData.get(StatisticInterval.DAY), rdbRepository));
+            scheduler.register(new JobRunningStatisticJob(registryCenter, rdbRepository));
+            scheduler.register(new RegisteredJobStatisticJob(configurationService, rdbRepository));
+        }
+    }
+    
+    /**
+     * 停止统计作业调度.
+     */
+    public void shutdown() {
+        scheduler.shutdown();
+    }
+    
+    /**
+     * 任务运行成功.
+     */
+    public void taskRunSuccessfully() {
+        statisticData.get(StatisticInterval.MINUTE).incrementAndGetSuccessCount();
+        statisticData.get(StatisticInterval.HOUR).incrementAndGetSuccessCount();
+        statisticData.get(StatisticInterval.DAY).incrementAndGetSuccessCount();
+    }
+    
+    /**
+     * 作业运行失败.
+     */
+    public void taskRunFailed() {
+        statisticData.get(StatisticInterval.MINUTE).incrementAndGetFailedCount();
+        statisticData.get(StatisticInterval.HOUR).incrementAndGetFailedCount();
+        statisticData.get(StatisticInterval.DAY).incrementAndGetFailedCount();
+    }
+    
+    private boolean isRdbConfigured() {
+        return null != rdbRepository;
+    }
+    
+    /**
+     * 获取最近一周的任务运行结果统计数据.
+     * 
+     * @return 任务运行结果统计数据对象
+     */
+    public TaskResultStatistics getTaskResultStatisticsWeekly() {
+        if (!isRdbConfigured()) {
+            return new TaskResultStatistics(0, 0, StatisticInterval.DAY, new Date());
+        }
+        return rdbRepository.getSummedTaskResultStatistics(StatisticTimeUtils.getStatisticTime(StatisticInterval.DAY, -7), StatisticInterval.DAY);
+    }
+    
+    /**
+     * 获取自上线以来的任务运行结果统计数据.
+     * 
+     * @return 任务运行结果统计数据对象
+     */
+    public TaskResultStatistics getTaskResultStatisticsSinceOnline() {
+        if (!isRdbConfigured()) {
+            return new TaskResultStatistics(0, 0, StatisticInterval.DAY, new Date());
+        }
+        return rdbRepository.getSummedTaskResultStatistics(getOnlineDate(), StatisticInterval.DAY);
+    }
+    
+    /**
+     * 获取最近一个统计周期的任务运行结果统计数据.
+     * 
+     * @param statisticInterval 统计周期
+     * @return 任务运行结果统计数据对象
+     */
+    public TaskResultStatistics findLatestTaskResultStatistics(final StatisticInterval statisticInterval) {
+        if (isRdbConfigured()) {
+            Optional<TaskResultStatistics> result = rdbRepository.findLatestTaskResultStatistics(statisticInterval);
+            if (result.isPresent()) {
+                return result.get();
+            }
+        }
+        return new TaskResultStatistics(0, 0, statisticInterval, new Date());
+    }
+    
+    /**
+     * 获取最近一天的任务运行结果统计数据集合.
+     * 
+     * @return 任务运行结果统计数据对象集合
+     */
+    public List<TaskResultStatistics> findTaskResultStatisticsDaily() {
+        if (!isRdbConfigured()) {
+            return Collections.emptyList();
+        }
+        return rdbRepository.findTaskResultStatistics(StatisticTimeUtils.getStatisticTime(StatisticInterval.HOUR, -24), StatisticInterval.MINUTE);
+    }
+    
+    /**
+     * 获取作业类型统计数据.
+     * 
+     * @return 作业类型统计数据对象
+     */
+    public JobTypeStatistics getJobTypeStatistics() {
+        int scriptJobCnt = 0;
+        int simpleJobCnt = 0;
+        int dataflowJobCnt = 0;
+        for (CloudJobConfiguration each : configurationService.loadAll()) {
+            if (JobType.SCRIPT.equals(each.getTypeConfig().getJobType())) {
+                scriptJobCnt++;
+            } else if (JobType.SIMPLE.equals(each.getTypeConfig().getJobType())) {
+                simpleJobCnt++;
+            } else if (JobType.DATAFLOW.equals(each.getTypeConfig().getJobType())) {
+                dataflowJobCnt++;
+            }
+        }
+        return new JobTypeStatistics(scriptJobCnt, simpleJobCnt, dataflowJobCnt);
+    }
+    
+    /**
+     * 获取作业执行类型统计数据.
+     * 
+     * @return 作业执行类型统计数据对象
+     */
+    public JobExecutionTypeStatistics getJobExecutionTypeStatistics() {
+        int transientJobCnt = 0;
+        int daemonJobCnt = 0;
+        for (CloudJobConfiguration each : configurationService.loadAll()) {
+            if (CloudJobExecutionType.TRANSIENT.equals(each.getJobExecutionType())) {
+                transientJobCnt++;
+            } else if (CloudJobExecutionType.DAEMON.equals(each.getJobExecutionType())) {
+                daemonJobCnt++;
+            }
+        }
+        return new JobExecutionTypeStatistics(transientJobCnt, daemonJobCnt);
+    }
+    
+    /**
+     * 获取最近一周的运行中的任务统计数据集合.
+     * 
+     * @return 运行中的任务统计数据对象集合
+     */
+    public List<TaskRunningStatistics> findTaskRunningStatisticsWeekly() {
+        if (!isRdbConfigured()) {
+            return Collections.emptyList();
+        }
+        return rdbRepository.findTaskRunningStatistics(StatisticTimeUtils.getStatisticTime(StatisticInterval.DAY, -7));
+    }
+    
+    /**
+     * 获取最近一周的运行中的作业统计数据集合.
+     * 
+     * @return 运行中的任务统计数据对象集合
+     */
+    public List<JobRunningStatistics> findJobRunningStatisticsWeekly() {
+        if (!isRdbConfigured()) {
+            return Collections.emptyList();
+        }
+        return rdbRepository.findJobRunningStatistics(StatisticTimeUtils.getStatisticTime(StatisticInterval.DAY, -7));
+    }
+    
+    /**
+     * 获取自上线以来的运行中的任务统计数据集合.
+     * 
+     * @return 运行中的任务统计数据对象集合
+     */
+    public List<JobRegisterStatistics> findJobRegisterStatisticsSinceOnline() {
+        if (!isRdbConfigured()) {
+            return Collections.emptyList();
+        }
+        return rdbRepository.findJobRegisterStatistics(getOnlineDate());
+    }
+    
+    private Date getOnlineDate() {
+        SimpleDateFormat formatter = new SimpleDateFormat("yyyy-MM-dd");
+        try {
+            return formatter.parse("2016-12-16");
+        } catch (final ParseException ex) {
+            return null;
+        }
+    }
+}
diff --git a/elastic-job-cloud-scheduler/src/main/java/io/elasticjob/cloud/scheduler/statistics/StatisticsScheduler.java b/elastic-job-cloud-scheduler/src/main/java/io/elasticjob/cloud/scheduler/statistics/StatisticsScheduler.java
new file mode 100644
index 0000000..fdd6a97
--- /dev/null
+++ b/elastic-job-cloud-scheduler/src/main/java/io/elasticjob/cloud/scheduler/statistics/StatisticsScheduler.java
@@ -0,0 +1,103 @@
+/*
+ * Copyright 1999-2015 dangdang.com.
+ * <p>
+ * Licensed 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.
+ * </p>
+ */
+
+package io.elasticjob.cloud.scheduler.statistics;
+
+import io.elasticjob.cloud.scheduler.statistics.job.StatisticJob;
+import io.elasticjob.cloud.exception.JobStatisticException;
+import org.quartz.JobDetail;
+import org.quartz.Scheduler;
+import org.quartz.SchedulerException;
+import org.quartz.impl.StdSchedulerFactory;
+import org.quartz.plugins.management.ShutdownHookPlugin;
+import org.quartz.simpl.SimpleThreadPool;
+
+import java.util.Properties;
+
+/**
+ * 统计作业调度器.
+ *
+ * @author liguangyun
+ */
+final class StatisticsScheduler {
+    
+    private final StdSchedulerFactory factory;
+    
+    private Scheduler scheduler;
+    
+    /**
+     * 构造函数.
+     */
+    StatisticsScheduler() {
+        factory = new StdSchedulerFactory();
+        try {
+            factory.initialize(getQuartzProperties());
+        } catch (final SchedulerException ex) {
+            throw new JobStatisticException(ex);
+        }
+    }
+    
+    private Properties getQuartzProperties() {
+        Properties result = new Properties();
+        result.put("org.quartz.threadPool.class", SimpleThreadPool.class.getName());
+        result.put("org.quartz.threadPool.threadCount", Integer.toString(1));
+        result.put("org.quartz.scheduler.instanceName", "ELASTIC_JOB_CLOUD_STATISTICS_SCHEDULER");
+        result.put("org.quartz.plugin.shutdownhook.class", ShutdownHookPlugin.class.getName());
+        result.put("org.quartz.plugin.shutdownhook.cleanShutdown", Boolean.TRUE.toString());
+        return result;
+    }
+    
+    /**
+     * 启动调度器.
+     */
+    void start() {
+        try {
+            scheduler = factory.getScheduler();
+            scheduler.start();
+        } catch (final SchedulerException ex) {
+            throw new JobStatisticException(ex);
+        }
+    }
+    
+    /**
+     * 注册统计作业.
+     * 
+     * @param statisticJob 统计作业
+     */
+    void register(final StatisticJob statisticJob) {
+        try {
+            JobDetail jobDetail = statisticJob.buildJobDetail();
+            jobDetail.getJobDataMap().putAll(statisticJob.getDataMap());
+            scheduler.scheduleJob(jobDetail, statisticJob.buildTrigger());
+        } catch (final SchedulerException ex) {
+            throw new JobStatisticException(ex);
+        }
+    }
+    
+    /**
+     * 停止调度.
+     */
+    void shutdown() {
+        try {
+            if (null != scheduler && !scheduler.isShutdown()) {
+                scheduler.shutdown();
+            }
+        } catch (final SchedulerException ex) {
+            throw new JobStatisticException(ex);
+        }
+    }
+}
diff --git a/elastic-job-cloud-scheduler/src/main/java/io/elasticjob/cloud/scheduler/statistics/TaskResultMetaData.java b/elastic-job-cloud-scheduler/src/main/java/io/elasticjob/cloud/scheduler/statistics/TaskResultMetaData.java
new file mode 100644
index 0000000..1d8de18
--- /dev/null
+++ b/elastic-job-cloud-scheduler/src/main/java/io/elasticjob/cloud/scheduler/statistics/TaskResultMetaData.java
@@ -0,0 +1,84 @@
+/*
+ * Copyright 1999-2015 dangdang.com.
+ * <p>
+ * Licensed 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.
+ * </p>
+ */
+
+package io.elasticjob.cloud.scheduler.statistics;
+
+import java.util.concurrent.atomic.AtomicInteger;
+
+/**
+ * 统计元数据.
+ *
+ * @author liguangyun
+ */
+public final class TaskResultMetaData {
+    
+    private final AtomicInteger successCount;
+    
+    private final AtomicInteger failedCount;
+    
+    /**
+     * 构造函数.
+     */
+    public TaskResultMetaData() {
+        successCount = new AtomicInteger(0);
+        failedCount = new AtomicInteger(0);
+    }
+    
+    /**
+     * 增加并获取成功数.
+     * 
+     * @return 成功数
+     */
+    public int incrementAndGetSuccessCount() {
+        return successCount.incrementAndGet();
+    }
+    
+    /**
+     * 增加并获取失败数.
+     * 
+     * @return 失败数
+     */
+    public int incrementAndGetFailedCount() {
+        return failedCount.incrementAndGet();
+    }
+    
+    /**
+     * 获取成功数.
+     * 
+     * @return 成功数
+     */
+    public int getSuccessCount() {
+        return successCount.get();
+    }
+    
+    /**
+     * 获取失败数.
+     * 
+     * @return 失败数
+     */
+    public int getFailedCount() {
+        return failedCount.get();
+    }
+    
+    /**
+     * 重置成功数、失败数.
+     */
+    public void reset() {
+        successCount.set(0);
+        failedCount.set(0);
+    }
+}
diff --git a/elastic-job-cloud-scheduler/src/main/java/io/elasticjob/cloud/scheduler/statistics/job/AbstractStatisticJob.java b/elastic-job-cloud-scheduler/src/main/java/io/elasticjob/cloud/scheduler/statistics/job/AbstractStatisticJob.java
new file mode 100644
index 0000000..a2f966e
--- /dev/null
+++ b/elastic-job-cloud-scheduler/src/main/java/io/elasticjob/cloud/scheduler/statistics/job/AbstractStatisticJob.java
@@ -0,0 +1,54 @@
+/*
+ * Copyright 1999-2015 dangdang.com.
+ * <p>
+ * Licensed 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.
+ * </p>
+ */
+
+package io.elasticjob.cloud.scheduler.statistics.job;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Date;
+import java.util.List;
+
+import io.elasticjob.cloud.scheduler.statistics.util.StatisticTimeUtils;
+import io.elasticjob.cloud.statistics.StatisticInterval;
+
+/**
+ * 统计作业抽象类.
+ *
+ * @author liguangyun
+ */
+abstract class AbstractStatisticJob implements StatisticJob {
+    
+    String getJobName() {
+        return this.getClass().getSimpleName();
+    }
+    
+    String getTriggerName() {
+        return this.getClass().getSimpleName() + "Trigger";
+    }
+    
+    List<Date> findBlankStatisticTimes(final Date latestStatisticTime, final StatisticInterval statisticInterval) {
+        List<Date> result = new ArrayList<>();
+        int previousInterval = -1;
+        Date previousTime = StatisticTimeUtils.getStatisticTime(statisticInterval, previousInterval);
+        while (previousTime.after(latestStatisticTime)) {
+            result.add(previousTime);
+            previousTime = StatisticTimeUtils.getStatisticTime(statisticInterval, --previousInterval);
+        }
+        Collections.sort(result);
+        return result;
+    }
+}
diff --git a/elastic-job-cloud-scheduler/src/main/java/io/elasticjob/cloud/scheduler/statistics/job/JobRunningStatisticJob.java b/elastic-job-cloud-scheduler/src/main/java/io/elasticjob/cloud/scheduler/statistics/job/JobRunningStatisticJob.java
new file mode 100644
index 0000000..56f42a7
--- /dev/null
+++ b/elastic-job-cloud-scheduler/src/main/java/io/elasticjob/cloud/scheduler/statistics/job/JobRunningStatisticJob.java
@@ -0,0 +1,157 @@
+/*
+ * Copyright 1999-2015 dangdang.com.
+ * <p>
+ * Licensed 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.
+ * </p>
+ */
+
+package io.elasticjob.cloud.scheduler.statistics.job;
+
+import io.elasticjob.cloud.reg.base.CoordinatorRegistryCenter;
+import io.elasticjob.cloud.scheduler.state.running.RunningService;
+import io.elasticjob.cloud.scheduler.statistics.util.StatisticTimeUtils;
+import io.elasticjob.cloud.context.TaskContext;
+import io.elasticjob.cloud.statistics.StatisticInterval;
+import io.elasticjob.cloud.statistics.rdb.StatisticRdbRepository;
+import io.elasticjob.cloud.statistics.type.job.JobRunningStatistics;
+import io.elasticjob.cloud.statistics.type.task.TaskRunningStatistics;
+import com.google.common.base.Optional;
+import lombok.NoArgsConstructor;
+import lombok.Setter;
+import lombok.extern.slf4j.Slf4j;
+import org.quartz.CronScheduleBuilder;
+import org.quartz.JobBuilder;
+import org.quartz.JobDetail;
+import org.quartz.JobExecutionContext;
+import org.quartz.JobExecutionException;
+import org.quartz.Trigger;
+import org.quartz.TriggerBuilder;
+
+import java.util.Date;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+/**
+ * 运行中的任务统计作业.
+ *
+ * @author liguangyun
+ */
+@Setter
+@NoArgsConstructor
+@Slf4j
+public final class JobRunningStatisticJob extends AbstractStatisticJob {
+    
+    private static final StatisticInterval EXECUTE_INTERVAL = StatisticInterval.MINUTE;
+    
+    private RunningService runningService;
+    
+    private StatisticRdbRepository repository;
+    
+    /**
+     * 构造函数.
+     * @param registryCenter 注册中心
+     * @param rdbRepository 基于rdb的数据仓库对象
+     */
+    public JobRunningStatisticJob(final CoordinatorRegistryCenter registryCenter, final StatisticRdbRepository rdbRepository) {
+        runningService = new RunningService(registryCenter);
+        this.repository = rdbRepository;
+    }
+    
+    @Override
+    public JobDetail buildJobDetail() {
+        return JobBuilder.newJob(this.getClass()).withIdentity(getJobName()).build();
+    }
+    
+    @Override
+    public Trigger buildTrigger() {
+        return TriggerBuilder.newTrigger()
+                .withIdentity(getTriggerName())
+                .withSchedule(CronScheduleBuilder.cronSchedule(EXECUTE_INTERVAL.getCron())
+                .withMisfireHandlingInstructionDoNothing()).build();
+    }
+    
+    @Override
+    public Map<String, Object> getDataMap() {
+        Map<String, Object> result = new HashMap<>(2);
+        result.put("runningService", runningService);
+        result.put("repository", repository);
+        return result;
+    }
+    
+    @Override
+    public void execute(final JobExecutionContext context) throws JobExecutionException {
+        Map<String, Set<TaskContext>> allRunningTasks = runningService.getAllRunningTasks();
+        statisticJob(getJobRunningCount(allRunningTasks));
+        statisticTask(getTaskRunningCount(allRunningTasks));
+    }
+    
+    private void statisticJob(final int runningCount) {
+        Optional<JobRunningStatistics> latestOne = repository.findLatestJobRunningStatistics();
+        if (latestOne.isPresent()) {
+            fillBlankIfNeeded(latestOne.get());
+        }
+        JobRunningStatistics jobRunningStatistics = new JobRunningStatistics(runningCount, StatisticTimeUtils.getCurrentStatisticTime(EXECUTE_INTERVAL));
+        log.debug("Add jobRunningStatistics, runningCount is:{}", runningCount);
+        repository.add(jobRunningStatistics);
+    }
+    
+    private void statisticTask(final int runningCount) {
+        Optional<TaskRunningStatistics> latestOne = repository.findLatestTaskRunningStatistics();
+        if (latestOne.isPresent()) {
+            fillBlankIfNeeded(latestOne.get());
+        }
+        TaskRunningStatistics taskRunningStatistics = new TaskRunningStatistics(runningCount, StatisticTimeUtils.getCurrentStatisticTime(EXECUTE_INTERVAL));
+        log.debug("Add taskRunningStatistics, runningCount is:{}", runningCount);
+        repository.add(taskRunningStatistics);
+    }
+    
+    private int getJobRunningCount(final Map<String, Set<TaskContext>> allRunningTasks) {
+        int result = 0;
+        for (Map.Entry<String, Set<TaskContext>> entry : allRunningTasks.entrySet()) {
+            if (!entry.getValue().isEmpty()) {
+                result++;
+            }
+        }
+        return result;
+    }
+    
+    private int getTaskRunningCount(final Map<String, Set<TaskContext>> allRunningTasks) {
+        int result = 0;
+        for (Map.Entry<String, Set<TaskContext>> entry : allRunningTasks.entrySet()) {
+            result += entry.getValue().size();
+        }
+        return result;
+    }
+    
+    private void fillBlankIfNeeded(final JobRunningStatistics latestOne) {
+        List<Date> blankDateRange = findBlankStatisticTimes(latestOne.getStatisticsTime(), EXECUTE_INTERVAL);
+        if (!blankDateRange.isEmpty()) {
+            log.debug("Fill blank range of jobRunningStatistics, range is:{}", blankDateRange);
+        }
+        for (Date each : blankDateRange) {
+            repository.add(new JobRunningStatistics(latestOne.getRunningCount(), each));
+        }
+    }
+    
+    private void fillBlankIfNeeded(final TaskRunningStatistics latestOne) {
+        List<Date> blankDateRange = findBlankStatisticTimes(latestOne.getStatisticsTime(), EXECUTE_INTERVAL);
+        if (!blankDateRange.isEmpty()) {
+            log.debug("Fill blank range of taskRunningStatistics, range is:{}", blankDateRange);
+        }
+        for (Date each : blankDateRange) {
+            repository.add(new TaskRunningStatistics(latestOne.getRunningCount(), each));
+        }
+    }
+}
diff --git a/elastic-job-cloud-scheduler/src/main/java/io/elasticjob/cloud/scheduler/statistics/job/RegisteredJobStatisticJob.java b/elastic-job-cloud-scheduler/src/main/java/io/elasticjob/cloud/scheduler/statistics/job/RegisteredJobStatisticJob.java
new file mode 100644
index 0000000..bca23f0
--- /dev/null
+++ b/elastic-job-cloud-scheduler/src/main/java/io/elasticjob/cloud/scheduler/statistics/job/RegisteredJobStatisticJob.java
@@ -0,0 +1,102 @@
+/*
+ * Copyright 1999-2015 dangdang.com.
+ * <p>
+ * Licensed 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.
+ * </p>
+ */
+
+package io.elasticjob.cloud.scheduler.statistics.job;
+
+import io.elasticjob.cloud.scheduler.config.job.CloudJobConfigurationService;
+import io.elasticjob.cloud.scheduler.statistics.util.StatisticTimeUtils;
+import io.elasticjob.cloud.statistics.StatisticInterval;
+import io.elasticjob.cloud.statistics.rdb.StatisticRdbRepository;
+import io.elasticjob.cloud.statistics.type.job.JobRegisterStatistics;
+import com.google.common.base.Optional;
+import lombok.AllArgsConstructor;
+import lombok.NoArgsConstructor;
+import lombok.Setter;
+import lombok.extern.slf4j.Slf4j;
+import org.quartz.CronScheduleBuilder;
+import org.quartz.JobBuilder;
+import org.quartz.JobDetail;
+import org.quartz.JobExecutionContext;
+import org.quartz.JobExecutionException;
+import org.quartz.Trigger;
+import org.quartz.TriggerBuilder;
+
+import java.util.Date;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * 已注册作业统计作业.
+ *
+ * @author liguangyun
+ */
+@Setter
+@NoArgsConstructor
+@AllArgsConstructor
+@Slf4j
+public final class RegisteredJobStatisticJob extends AbstractStatisticJob {
+    
+    private CloudJobConfigurationService configurationService;
+    
+    private StatisticRdbRepository repository;
+    
+    private final StatisticInterval execInterval = StatisticInterval.DAY;
+    
+    @Override
+    public JobDetail buildJobDetail() {
+        return JobBuilder.newJob(this.getClass()).withIdentity(getJobName()).build();
+    }
+    
+    @Override
+    public Trigger buildTrigger() {
+        return TriggerBuilder.newTrigger()
+                .withIdentity(getTriggerName())
+                .withSchedule(CronScheduleBuilder.cronSchedule(execInterval.getCron())
+                .withMisfireHandlingInstructionDoNothing()).build();
+    }
+    
+    @Override
+    public Map<String, Object> getDataMap() {
+        Map<String, Object> result = new HashMap<>(2);
+        result.put("configurationService", configurationService);
+        result.put("repository", repository);
+        return result;
+    }
+    
+    @Override
+    public void execute(final JobExecutionContext context) throws JobExecutionException {
+        Optional<JobRegisterStatistics> latestOne = repository.findLatestJobRegisterStatistics();
+        if (latestOne.isPresent()) {
+            fillBlankIfNeeded(latestOne.get());
+        }
+        int registeredCount = configurationService.loadAll().size();
+        JobRegisterStatistics jobRegisterStatistics = new JobRegisterStatistics(registeredCount, StatisticTimeUtils.getCurrentStatisticTime(execInterval));
+        log.debug("Add jobRegisterStatistics, registeredCount is:{}", registeredCount);
+        repository.add(jobRegisterStatistics);
+    }
+    
+    private void fillBlankIfNeeded(final JobRegisterStatistics latestOne) {
+        List<Date> blankDateRange = findBlankStatisticTimes(latestOne.getStatisticsTime(), execInterval);
+        if (!blankDateRange.isEmpty()) {
+            log.debug("Fill blank range of jobRegisterStatistics, range is:{}", blankDateRange);
+        }
+        for (Date each : blankDateRange) {
+            repository.add(new JobRegisterStatistics(latestOne.getRegisteredCount(), each));
+        }
+    }
+}
diff --git a/elastic-job-cloud-scheduler/src/main/java/io/elasticjob/cloud/scheduler/statistics/job/StatisticJob.java b/elastic-job-cloud-scheduler/src/main/java/io/elasticjob/cloud/scheduler/statistics/job/StatisticJob.java
new file mode 100644
index 0000000..ed20b98
--- /dev/null
+++ b/elastic-job-cloud-scheduler/src/main/java/io/elasticjob/cloud/scheduler/statistics/job/StatisticJob.java
@@ -0,0 +1,53 @@
+/*
+ * Copyright 1999-2015 dangdang.com.
+ * <p>
+ * Licensed 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.
+ * </p>
+ */
+
+package io.elasticjob.cloud.scheduler.statistics.job;
+
+import java.util.Map;
+
+import org.quartz.Job;
+import org.quartz.JobDetail;
+import org.quartz.Trigger;
+
+/**
+ * 统计作业.
+ *
+ * @author liguangyun
+ */
+public interface StatisticJob extends Job {
+    
+    /**
+     * 构建JobDetail.
+     * 
+     * @return JobDetail对象
+     */
+    JobDetail buildJobDetail();
+    
+    /**
+     * 构建Trigger.
+     * 
+     * @return Trigger对象
+     */
+    Trigger buildTrigger();
+    
+    /**
+     * 获取对象属性Map.
+     * 
+     * @return 对象属性Map,KEY为属性名称,VALUE为属性实例
+     */
+    Map<String, Object> getDataMap();
+}
diff --git a/elastic-job-cloud-scheduler/src/main/java/io/elasticjob/cloud/scheduler/statistics/job/TaskResultStatisticJob.java b/elastic-job-cloud-scheduler/src/main/java/io/elasticjob/cloud/scheduler/statistics/job/TaskResultStatisticJob.java
new file mode 100644
index 0000000..d95a5a8
--- /dev/null
+++ b/elastic-job-cloud-scheduler/src/main/java/io/elasticjob/cloud/scheduler/statistics/job/TaskResultStatisticJob.java
@@ -0,0 +1,108 @@
+/*
+ * Copyright 1999-2015 dangdang.com.
+ * <p>
+ * Licensed 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.
+ * </p>
+ */
+
+package io.elasticjob.cloud.scheduler.statistics.job;
+
+import io.elasticjob.cloud.scheduler.statistics.TaskResultMetaData;
+import io.elasticjob.cloud.scheduler.statistics.util.StatisticTimeUtils;
+import io.elasticjob.cloud.statistics.StatisticInterval;
+import io.elasticjob.cloud.statistics.rdb.StatisticRdbRepository;
+import io.elasticjob.cloud.statistics.type.task.TaskResultStatistics;
+import com.google.common.base.Optional;
+import lombok.AllArgsConstructor;
+import lombok.NoArgsConstructor;
+import lombok.Setter;
+import lombok.extern.slf4j.Slf4j;
+import org.quartz.CronScheduleBuilder;
+import org.quartz.JobBuilder;
+import org.quartz.JobDetail;
+import org.quartz.JobExecutionContext;
+import org.quartz.JobExecutionException;
+import org.quartz.Trigger;
+import org.quartz.TriggerBuilder;
+
+import java.util.Date;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * 任务运行结果统计作业.
+ *
+ * @author liguangyun
+ */
+@Setter
+@NoArgsConstructor
+@AllArgsConstructor
+@Slf4j
+public final class TaskResultStatisticJob extends AbstractStatisticJob {
+    
+    private StatisticInterval statisticInterval;
+    
+    private TaskResultMetaData sharedData;
+    
+    private StatisticRdbRepository repository;
+    
+    @Override
+    public JobDetail buildJobDetail() {
+        JobDetail result = JobBuilder.newJob(this.getClass()).withIdentity(getJobName() + "_" + statisticInterval).build();
+        result.getJobDataMap().put("statisticUnit", statisticInterval);
+        return result;
+    }
+    
+    @Override
+    public Trigger buildTrigger() {
+        return TriggerBuilder.newTrigger()
+                .withIdentity(getTriggerName() + "_" + statisticInterval)
+                .withSchedule(CronScheduleBuilder.cronSchedule(statisticInterval.getCron())
+                .withMisfireHandlingInstructionDoNothing()).build();
+    }
+    
+    @Override
+    public Map<String, Object> getDataMap() {
+        Map<String, Object> result = new HashMap<>(3);
+        result.put("statisticInterval", statisticInterval);
+        result.put("sharedData", sharedData);
+        result.put("repository", repository);
+        return result;
+    }
+    
+    @Override
+    public void execute(final JobExecutionContext context) throws JobExecutionException {
+        Optional<TaskResultStatistics> latestOne = repository.findLatestTaskResultStatistics(statisticInterval);
+        if (latestOne.isPresent()) {
+            fillBlankIfNeeded(latestOne.get());
+        }
+        TaskResultStatistics taskResultStatistics = new TaskResultStatistics(
+                sharedData.getSuccessCount(), sharedData.getFailedCount(), statisticInterval,
+                StatisticTimeUtils.getCurrentStatisticTime(statisticInterval));
+        log.debug("Add taskResultStatistics, statisticInterval is:{}, successCount is:{}, failedCount is:{}", 
+                statisticInterval, sharedData.getSuccessCount(), sharedData.getFailedCount());
+        repository.add(taskResultStatistics);
+        sharedData.reset();
+    }
+    
+    private void fillBlankIfNeeded(final TaskResultStatistics latestOne) {
+        List<Date> blankDateRange = findBlankStatisticTimes(latestOne.getStatisticsTime(), statisticInterval);
+        if (!blankDateRange.isEmpty()) {
+            log.debug("Fill blank range of taskResultStatistics, range is:{}", blankDateRange);
+        }
+        for (Date each : blankDateRange) {
+            repository.add(new TaskResultStatistics(latestOne.getSuccessCount(), latestOne.getFailedCount(), statisticInterval, each));
+        }
+    }
+}
diff --git a/elastic-job-cloud-scheduler/src/main/java/io/elasticjob/cloud/scheduler/statistics/util/StatisticTimeUtils.java b/elastic-job-cloud-scheduler/src/main/java/io/elasticjob/cloud/scheduler/statistics/util/StatisticTimeUtils.java
new file mode 100644
index 0000000..0e412f1
--- /dev/null
+++ b/elastic-job-cloud-scheduler/src/main/java/io/elasticjob/cloud/scheduler/statistics/util/StatisticTimeUtils.java
@@ -0,0 +1,75 @@
+/*
+ * Copyright 1999-2015 dangdang.com.
+ * <p>
+ * Licensed 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.
+ * </p>
+ */
+
+package io.elasticjob.cloud.scheduler.statistics.util;
+
+import java.util.Calendar;
+import java.util.Date;
+
+import io.elasticjob.cloud.statistics.StatisticInterval;
+
+import lombok.AccessLevel;
+import lombok.NoArgsConstructor;
+
+/**
+ * 统计时间工具类.
+ *
+ * @author liguangyun
+ */
+@NoArgsConstructor(access = AccessLevel.PRIVATE)
+public final class StatisticTimeUtils {
+    
+    /**
+     * 获取以interval为时间间隔单位的统计时间.
+     * 
+     * @param interval 时间间隔
+     * @return 时间对象
+     */
+    public static Date getCurrentStatisticTime(final StatisticInterval interval) {
+        return getStatisticTime(interval, 0);
+    }
+    
+    /**
+     * 偏移offset个时间间隔单位,获取以interval为时间间隔单位的统计时间.
+     * offset为负数表示时间向过去偏移,正数表示向未来偏移.
+     * 
+     * @param interval 时间间隔
+     * @param offset 时间偏移量
+     * @return 时间对象
+     */
+    public static Date getStatisticTime(final StatisticInterval interval, final int offset) {
+        Calendar calendar = Calendar.getInstance();
+        calendar.set(Calendar.MILLISECOND, 0);
+        calendar.set(Calendar.SECOND, 0);
+        switch (interval) {
+            case DAY:
+                calendar.set(Calendar.MINUTE, 0);
+                calendar.set(Calendar.HOUR_OF_DAY, 0);
+                calendar.add(Calendar.DATE, offset);
+                break;
+            case HOUR:
+                calendar.set(Calendar.MINUTE, 0);
+                calendar.add(Calendar.HOUR_OF_DAY, offset);
+                break;
+            case MINUTE:
+            default:
+                calendar.add(Calendar.MINUTE, offset);
+                break;
+        }
+        return calendar.getTime();
+    }
+}
diff --git a/elastic-job-cloud-scheduler/src/main/resources/assembly/assembly.xml b/elastic-job-cloud-scheduler/src/main/resources/assembly/assembly.xml
new file mode 100644
index 0000000..4d4fca7
--- /dev/null
+++ b/elastic-job-cloud-scheduler/src/main/resources/assembly/assembly.xml
@@ -0,0 +1,26 @@
+<assembly>
+    <formats>
+        <format>tar.gz</format>
+    </formats>
+    <fileSets>
+        <fileSet>
+            <directory>src/main/resources/conf</directory>
+            <outputDirectory>conf</outputDirectory>
+            <directoryMode>0755</directoryMode>
+            <fileMode>0644</fileMode>
+        </fileSet>
+        <fileSet>
+            <directory>src/main/resources/bin</directory>
+            <outputDirectory>bin</outputDirectory>
+            <directoryMode>0755</directoryMode>
+            <fileMode>0755</fileMode>
+        </fileSet>
+    </fileSets>
+    
+    <dependencySets>
+        <dependencySet>
+            <outputDirectory>lib</outputDirectory>
+            <directoryMode>0755</directoryMode>
+        </dependencySet>
+    </dependencySets>
+</assembly>
diff --git a/elastic-job-cloud-scheduler/src/main/resources/bin/dcos.sh b/elastic-job-cloud-scheduler/src/main/resources/bin/dcos.sh
new file mode 100755
index 0000000..57276b5
--- /dev/null
+++ b/elastic-job-cloud-scheduler/src/main/resources/bin/dcos.sh
@@ -0,0 +1,9 @@
+#!/bin/bash
+cd `dirname $0`
+cd ..
+DEPLOY_DIR=`pwd`
+LIB_DIR=${DEPLOY_DIR}/lib/*
+CONTAINER_MAIN=io.elasticjob.cloud.scheduler.Bootstrap
+JAVA_OPTS=" -Djava.awt.headless=true -Djava.net.preferIPv4Stack=true -Djava.library.path=/usr/local/lib:/usr/lib:/usr/lib64"
+
+java ${JAVA_OPTS} -classpath ${LIB_DIR}:. ${CONTAINER_MAIN}
diff --git a/elastic-job-cloud-scheduler/src/main/resources/bin/start.sh b/elastic-job-cloud-scheduler/src/main/resources/bin/start.sh
new file mode 100755
index 0000000..4e6ae43
--- /dev/null
+++ b/elastic-job-cloud-scheduler/src/main/resources/bin/start.sh
@@ -0,0 +1,17 @@
+#!/bin/bash
+cd `dirname $0`
+cd ..
+DEPLOY_DIR=`pwd`
+CONF_DIR=${DEPLOY_DIR}/conf
+LIB_DIR=${DEPLOY_DIR}/lib/*
+CONTAINER_MAIN=io.elasticjob.cloud.scheduler.Bootstrap
+JAVA_OPTS=" -Djava.awt.headless=true -Djava.net.preferIPv4Stack=true -Djava.library.path=/usr/local/lib:/usr/lib:/usr/lib64"
+
+source ${CONF_DIR}/elastic-job-cloud-scheduler.properties
+if [ ${hostname} = "" ] || [ ${hostname} = "127.0.0.1" ] || [ ${hostname} = "localhost" ]; then
+  echo "Please config hostname in conf/elastic-job-cloud-scheduler.properties with a routable IP address."
+  exit;
+fi
+export LIBPROCESS_IP=${hostname}
+
+java ${JAVA_OPTS} -classpath ${CONF_DIR}/*:${LIB_DIR}:. ${CONTAINER_MAIN}
diff --git a/elastic-job-cloud-scheduler/src/main/resources/conf/auth.properties b/elastic-job-cloud-scheduler/src/main/resources/conf/auth.properties
new file mode 100644
index 0000000..9e7cb9a
--- /dev/null
+++ b/elastic-job-cloud-scheduler/src/main/resources/conf/auth.properties
@@ -0,0 +1,4 @@
+root.username=root
+root.password=root
+guest.username=guest
+guest.password=guest
diff --git a/elastic-job-cloud-scheduler/src/main/resources/conf/elastic-job-cloud-scheduler.properties b/elastic-job-cloud-scheduler/src/main/resources/conf/elastic-job-cloud-scheduler.properties
new file mode 100644
index 0000000..73358e8
--- /dev/null
+++ b/elastic-job-cloud-scheduler/src/main/resources/conf/elastic-job-cloud-scheduler.properties
@@ -0,0 +1,45 @@
+# Routable IP address
+hostname=127.0.0.1
+
+# Username for mesos framework
+user=
+
+# Mesos zookeeper address
+mesos_url=zk://127.0.0.1:2181/mesos
+
+# Role for mesos framework
+
+#mesos_role=
+
+# Elastic-Job-Cloud's zookeeper address
+zk_servers=127.0.0.1:2181
+
+# Elastic-Job-Cloud's zookeeper namespace
+zk_namespace=elastic-job-cloud
+
+# Elastic-Job-Cloud's zookeeper digest
+zk_digest=
+
+# Job rest API port
+http_port=8899
+        
+# Max size of job accumulated
+job_state_queue_size=10000
+
+# Event trace rdb config
+
+#event_trace_rdb_driver=com.mysql.jdbc.Driver
+
+#event_trace_rdb_url=jdbc:mysql://localhost:3306/elastic_job_cloud_log
+
+#event_trace_rdb_username=root
+
+#event_trace_rdb_password=
+
+# Task reconciliation interval
+
+#reconcile_interval_minutes=-1
+
+# Enable/Disable mesos partition aware feature
+
+# enable_partition_aware=false
diff --git a/elastic-job-cloud-scheduler/src/main/resources/console/css/common.css b/elastic-job-cloud-scheduler/src/main/resources/console/css/common.css
new file mode 100644
index 0000000..9a1a3a6
--- /dev/null
+++ b/elastic-job-cloud-scheduler/src/main/resources/console/css/common.css
@@ -0,0 +1,144 @@
+.form-group.toolbar label {
+    float: left;
+    width: 130px;
+    height: 52px;
+    padding-left: 20px;
+    text-align: center;
+}
+.form-group.toolbar span {
+    float: left;
+    width: 40px;
+    height: 52px;
+    text-align: center;
+}
+.form-horizontal label i{
+    padding: 0 7px 0 0;
+    color: #fb4d59;
+    vertical-align: -2px;
+}
+.form-horizontal :-moz-placeholder { /* Mozilla Firefox 4 to 18 */
+    color: #DCDCDC;
+    opacity: 1;
+}
+.form-horizontal ::-moz-placeholder { /* Mozilla Firefox 19+ */
+    color: #DCDCDC;
+    opacity: 1;
+}
+.form-horizontal ::-webkit-input-placeholder{
+    color: #DCDCDC;
+}
+
+.form-horizontal input:-ms-input-placeholder{
+    color: #DCDCDC;
+    opacity: 1;
+}
+.form-horizontal input::-webkit-input-placeholder{
+    color: #DCDCDC;
+    opacity: 1;
+}
+/* dashboard  css */
+.table-line{
+    background-color:#00c0ef;
+    height:3px;width:100%;
+    margin-bottom:0;
+}
+.chart-right{
+    margin-right:-15px;
+}
+.chart-left{
+    margin-left:-6px;
+}
+.chart-size{
+    padding-left:0;
+    padding-right:0;
+}
+.chart-size.row{
+    margin:auto 0;
+}
+.chart-size-solation{
+    padding-left:0;
+    padding-right:0;
+    height:230px;
+}
+.set-size{
+    margin:0 auto;
+    width:98%;
+    height:220px;
+}
+.page-height-min{
+    min-height:780px;
+}
+/* app_overview job_overview  css */
+.form-group a{
+    width:80px;
+    font-size:14px; 
+    padding: 6px;
+    margin-left:23px;
+}
+.detail-model-size{
+    width:160%;
+    margin-left:-180px;
+}
+.update-model-size{
+    width:210%;
+    margin-left:-290px;
+}
+.size-font{
+    font-size:20px;
+}
+.center-font{
+    text-align:center;
+}
+.right-navigation-bar{
+    margin-left:20px;
+    width:190px;
+}
+.width-appURL{
+    margin-left:74px;
+}
+.app-width-size{
+    width:97%;
+}
+/* index css */
+#control-sidebar-theme-demo-options-tab li {
+    float:left; 
+    width: 33.33333%; 
+    padding: 5px;
+}
+#control-sidebar-theme-demo-options-tab li a {
+    display: block; 
+    box-shadow: 0 0 3px rgba(0,0,0,0.4);
+}
+.top-span {
+    display:block; 
+    width: 100%; 
+    float: left; 
+    height: 7px; 
+}
+#span-black{
+    background: #fefefe;
+}
+.down-span-left {
+    display:block; 
+    width: 20%; 
+    float: left; 
+    height: 20px; 
+    background: #222d32;
+}
+.down-span-right {
+    display:block; 
+    width: 80%; 
+    float: left; 
+    height: 20px; 
+    background: #f4f5f7;
+}
+#modal-dialog-width{
+    width: 60%;
+}
+#execute-result {
+    float: top;
+    padding-left: 20px;
+}
+#logo-font-size {
+    font-size: 18px;
+}
diff --git a/elastic-job-cloud-scheduler/src/main/resources/console/favicon.ico b/elastic-job-cloud-scheduler/src/main/resources/console/favicon.ico
new file mode 100644
index 0000000..634dc4c
--- /dev/null
+++ b/elastic-job-cloud-scheduler/src/main/resources/console/favicon.ico
Binary files differ
diff --git a/elastic-job-cloud-scheduler/src/main/resources/console/html/app/add_app.html b/elastic-job-cloud-scheduler/src/main/resources/console/html/app/add_app.html
new file mode 100644
index 0000000..3b17528
--- /dev/null
+++ b/elastic-job-cloud-scheduler/src/main/resources/console/html/app/add_app.html
@@ -0,0 +1,81 @@
+<div class="box-body">
+    <div role="tabpanel" class="tab-pane active" onsubmit="return false;">
+        <form id="app-form" class="form-horizontal">
+            <div class="row">
+                <div class="col-sm-5">
+                    <div class="form-group">
+                        <label for="app-name" class="col-sm-6 control-label" data-lang="app-name"><i>*</i></label>
+                        <div class="col-sm-6">
+                            <input type="text" placeholder="yourappName" id="app-name" name="appName" class="form-control" />
+                        </div>
+                    </div>
+                </div>
+                <div class="col-sm-5">
+                    <div class="form-group">
+                        <label for="bootstrap-script" class="col-sm-6 control-label" data-lang="app-bootstrap-script"><i>*</i></label>
+                        <div class="col-sm-6">
+                            <input type="text" placeholder="bin/start.sh" id="bootstrap-script" name=bootstrapScript class="form-control" data-toggle="tooltip" data-placement="bottom" title="启动脚本,如:bin\start.sh。"/>
+                        </div>
+                    </div>
+                </div>
+            </div>
+            <div class="row">
+                <div class="col-sm-5">
+                    <div class="form-group">
+                        <label for="cpu-count" class="col-sm-6 control-label" data-lang="app-cpu-count"><i>*</i></label>
+                        <div class="col-sm-6">
+                            <input type="number" value="1.0" min="0.1" step="0.1" id="cpu-count" name="cpuCount" class="form-control" />
+                        </div>
+                    </div>
+                </div>
+                <div class="col-sm-5">
+                    <div class="form-group">
+                        <label for="app-memory" class="col-sm-6 control-label" data-lang="app-memory"><i>*</i></label>
+                        <div class="col-sm-6">
+                            <input type="number" value="128" min="16" id="app-memory" name="appMemory" class="form-control" />
+                        </div>
+                    </div>
+                </div>
+            </div>
+            <div class="row">
+                <div class="col-sm-5">
+                    <div class="form-group">
+                        <label for="event-trace-sampling-count" class="col-sm-6 control-label" data-lang="app-event-trace-sampling-count"><i>*</i></label>
+                        <div class="col-sm-6">
+                            <input type="number" value="0" min="0" id="event-trace-sampling-count" name="eventTraceSamplingCount" class="form-control" />
+                        </div>
+                    </div>
+                </div>
+                <div class="col-sm-5">
+                    <div class="form-group">
+                        <label for="app-cache-enable" class="col-sm-6 control-label" data-lang="app-cache-enable"></label>
+                        <div class="col-sm-6">
+                            <input type="checkbox" checked="checked"  id="app-cache-enable" name="appCacheEnable" />
+                        </div>
+                    </div>
+                </div>
+            </div>
+            <div class="row">
+                <div class="form-group">
+                    <div class="width-appURL">
+                        <label for="app-url" class="col-sm-2 control-label" data-lang="app-url"><i>*</i></label>
+                        <div class="col-sm-8">
+                            <div class="app-width-size">
+                                <input type="text" placeholder="http://file_host:8080/foo-job.tar.gz" id="app-url" name="appURL" class="form-control" data-toggle="tooltip" data-placement="bottom" title="必须是可以通过网络访问到的路径。如:http://file_host:8080/your-job.tar.gz"/>
+                            </div>
+                        </div>
+                    </div>
+                </div>
+            </div>
+            <div class="form-group">
+                <div class="center-font">
+                    <button id="save-button" type="submit" class="btn-xs btn-primary" data-lang="operation-submit"></button>
+                </div>
+            </div>
+        </form>
+    </div>
+</div>
+<script src="lib/BootstrapValidator/js/bootstrapValidator.js"></script>
+<script src="lib/BootstrapValidator/js/bootstrapValidator_zh_CN.js"></script>
+<script src="js/app/app_common.js"></script>
+<script src="js/app/add_app.js"></script>
diff --git a/elastic-job-cloud-scheduler/src/main/resources/console/html/app/apps_overview.html b/elastic-job-cloud-scheduler/src/main/resources/console/html/app/apps_overview.html
new file mode 100644
index 0000000..84c0aaa
--- /dev/null
+++ b/elastic-job-cloud-scheduler/src/main/resources/console/html/app/apps_overview.html
@@ -0,0 +1,82 @@
+<div class="content-wrapper">
+    <div class="page-height-min">
+        <section class="content-header">
+            <h1 data-lang="sidebar-config"></h1>
+            <ol class="breadcrumb">
+                <li class="active"><i class="fa fa-laptop" data-lang="sidebar-app"></i></li>
+                <li class="active" data-lang="sidebar-config"></li>
+            </ol>
+        </section>
+        <section class="content">
+            <table id="app-table" data-pagination="true" data-page-list="[10, 20, 50, 100]" data-search="true" data-show-refresh="true" data-show-toggle="true" data-striped="true" data-show-columns="true">
+                <thead>
+                <tr>
+                    <th data-field="appName" data-sortable="true"><span data-lang="app-name"></span></th>
+                    <th data-field="appURL" data-sortable="true"><span data-lang="app-url"></span></th>
+                    <th data-field="bootstrapScript" data-sortable="true"><span data-lang="app-bootstrap-script"></span></th>
+                    <th data-field="operation" data-formatter="operationApp"><span data-lang="operation"></span></th>
+                </tr>
+                </thead>
+            </table>
+            <button type="button" class="btn-xs btn-success" data-toggle="modal" id="add-app" data-lang="operation-add"></button>
+        </section>
+    </div>
+</div>
+<div class="modal" id="data-detail-app" tabindex="-1" role="dialog" aria-labelledby="detail-modal-label" aria-hidden="true">
+    <div class="modal-dialog" >
+        <div class="update-model-size">
+            <div class="modal-content">
+                <div class="modal-header">
+                    <button type="button" class="close" data-dismiss="modal" aria-label="Close"><span aria-hidden="true">×</span></button>
+                    <h2 class="modal-title" data-lang="app-detail"></h2>
+                </div>
+                <div class="modal-body" id ="detail-app-body">
+                </div>
+            </div>
+        </div>
+    </div>
+</div>
+<div class="modal" id="data-update-app" tabindex="-1" role="dialog" aria-labelledby="modify-modal-label" aria-hidden="true">
+    <div class="modal-dialog">
+        <div class="update-model-size">
+            <div class="modal-content">
+                <div class="modal-header">
+                    <button type="button" class="close" data-dismiss="modal" aria-label="Close"><span aria-hidden="true">×</span></button>
+                    <h2 class="modal-title" data-lang="update-app"></h2>
+                </div>
+                <div class="modal-body" id="update-app-body">
+                </div>
+            </div>
+        </div>
+    </div>
+</div>
+<div class="modal" id="data-add-app" tabindex="-1" role="dialog" aria-labelledby="add-modal-label" aria-hidden="true">
+    <div class="modal-dialog">
+        <div class="update-model-size">
+            <div class="modal-content">
+                <div class="modal-header">
+                    <button type="button" class="close" data-dismiss="modal" aria-label="Close"><span aria-hidden="true">×</span></button>
+                    <h2 class="modal-title" data-lang="add-app"></h2>
+                </div>
+                <div class="modal-body" id="add-app-body">
+                </div>
+            </div>
+        </div>
+    </div>
+</div>
+<div class="modal" id="delete-data-app" tabindex="-1" role="dialog" aria-labelledby="delete-modal-label" aria-hidden="true">
+    <div class="modal-dialog">
+        <div class="modal-content">
+            <div class="modal-body">
+                <h3 class="size-font" data-lang="confirm-to-delete"></h3>
+            </div>
+            <div class="modal-footer">
+                <button type="button" class="btn-xs btn-danger" id="delete-app-confirm" data-dismiss="modal" data-lang="operation-confirm"></button>
+                <button type="button" class="btn-xs btn-success" id="delete-app-remove" data-dismiss="modal" data-lang="operation-cancel"></button>
+            </div>
+        </div>
+    </div>
+</div>
+<script src="lib/bootstrap-table/bootstrap-table.js"></script>
+<script src="js/common/common.js"></script>
+<script src="js/app/apps_overview.js"></script>
diff --git a/elastic-job-cloud-scheduler/src/main/resources/console/html/app/detail_app.html b/elastic-job-cloud-scheduler/src/main/resources/console/html/app/detail_app.html
new file mode 100644
index 0000000..e9ef84d
--- /dev/null
+++ b/elastic-job-cloud-scheduler/src/main/resources/console/html/app/detail_app.html
@@ -0,0 +1,72 @@
+<div class="box-body">
+    <div role="tabpanel" class="tab-pane active" onsubmit="return false;">
+        <form id="app-form" class="form-horizontal">
+            <div class="row">
+                <div class="col-sm-5">
+                    <div class="form-group">
+                        <label for="app-name" class="col-sm-6 control-label" data-lang="app-name"><i>*</i></label>
+                        <div class="col-sm-6">
+                            <input type="text" disabled="disabled" placeholder="yourappName" id="app-name" name="appName" class="form-control" />
+                        </div>
+                    </div>
+                </div>
+                <div class="col-sm-5" >
+                    <div class="form-group">
+                        <label for="bootstrap-script" class="col-sm-6 control-label" data-lang="app-bootstrap-script"><i>*</i></label>
+                        <div class="col-sm-6">
+                            <input type="text" disabled="disabled" placeholder="bin/start.sh" id="bootstrap-script" name="bootstrapScript" class="form-control" data-toggle="tooltip" data-placement="bottom" title="启动脚本,如:bin\start.sh。"/>
+                        </div>
+                    </div>
+                </div>
+            </div>
+            <div class="row">
+                <div class="col-sm-5">
+                    <div class="form-group">
+                        <label for="cpu-count" class="col-sm-6 control-label" data-lang="app-cpu-count"><i>*</i></label>
+                        <div class="col-sm-6">
+                            <input type="number" disabled="disabled" value="1.0" min="1.0" step="0.1" id="cpu-count" name="cpuCount" class="form-control" />
+                        </div>
+                    </div>
+                </div>
+                <div class="col-sm-5">
+                    <div class="form-group">
+                        <label for="app-memory" class="col-sm-6 control-label" data-lang="app-memory"><i>*</i></label>
+                        <div class="col-sm-6">
+                            <input type="number" disabled="disabled" value="256" min="1" id="app-memory" name="appMemory" class="form-control" />
+                        </div>
+                    </div>
+                </div>
+            </div>
+            <div class="row">
+                <div class="col-sm-5">
+                    <div class="form-group">
+                        <label for="event-trace-sampling-count" class="col-sm-6 control-label" data-lang="app-event-trace-sampling-count"><i>*</i></label>
+                        <div class="col-sm-6">
+                            <input type="number" disabled="disabled" id="event-trace-sampling-count" value="0" min="0" name="eventTraceSamplingCount" class="form-control" />
+                        </div>
+                    </div>
+                </div>
+                <div class="col-sm-5">
+                    <div class="form-group">
+                        <label for="app-cache-enable" class="col-sm-6 control-label" data-lang="app-cache-enable"></label>
+                        <div class="col-sm-6">
+                            <input type="checkbox" disabled="disabled" checked="checked" id="app-cache-enable" name="appCacheEnable" />
+                        </div>
+                    </div>
+                </div>
+            </div>
+            <div class="row">
+                <div class="width-appURL">
+                    <div class="form-group">
+                        <label for="app-url" class="col-sm-2 control-label" data-lang="app-url"><i>*</i></label>
+                        <div class="col-sm-8">
+                           <div class="app-width-size">
+                                <input type="text" disabled="disabled" placeholder="http://file_host:8080/foo-job.tar.gz" id="app-url" name="appURL" class="form-control" data-toggle="tooltip" data-placement="bottom" title="必须是可以通过网络访问到的路径。如:http://file_host:8080/your-job.tar.gz"/>
+                           </div>
+                        </div>
+                    </div>
+                 </div>
+            </div>
+        </form>
+    </div>
+</div>
diff --git a/elastic-job-cloud-scheduler/src/main/resources/console/html/app/modify_app.html b/elastic-job-cloud-scheduler/src/main/resources/console/html/app/modify_app.html
new file mode 100644
index 0000000..b37c3f1
--- /dev/null
+++ b/elastic-job-cloud-scheduler/src/main/resources/console/html/app/modify_app.html
@@ -0,0 +1,81 @@
+<div class="box-body">
+    <div role="tabpanel" class="tab-pane active" onsubmit="return false;">
+        <form id="app-form" class="form-horizontal">
+            <div class="row">
+                <div class="col-sm-5">
+                    <div class="form-group">
+                        <label for="app-name" class="col-sm-6 control-label" data-lang="app-name"><i>*</i></label>
+                        <div class="col-sm-6">
+                            <input type="text" disabled="disabled" placeholder="yourappName" id="app-name" name="appName" class="form-control" />
+                        </div>
+                    </div>
+                </div>
+                <div class="col-sm-5" >
+                    <div class="form-group">
+                        <label for="bootstrap-script" class="col-sm-6 control-label" data-lang="app-bootstrap-script"><i>*</i></label>
+                        <div class="col-sm-6">
+                            <input type="text" disabled="disabled" placeholder="bin/start.sh" id="bootstrap-script" name="bootstrapScript" class="form-control" data-toggle="tooltip" data-placement="bottom" title="启动脚本,如:bin\start.sh。"/>
+                        </div>
+                    </div>
+                </div>
+            </div>
+            <div class="row">
+                <div class="col-sm-5">
+                    <div class="form-group">
+                        <label for="cpu-count" class="col-sm-6 control-label" data-lang="app-cpu-count"><i>*</i></label>
+                        <div class="col-sm-6">
+                            <input type="number" disabled="disabled" value="1.0" min="1.0" step="0.1" id="cpu-count" name="cpuCount" class="form-control" />
+                        </div>
+                    </div>
+                </div>
+                <div class="col-sm-5">
+                    <div class="form-group">
+                        <label for="app-memory" class="col-sm-6 control-label" data-lang="app-memory"><i>*</i></label>
+                        <div class="col-sm-6">
+                            <input type="number" disabled="disabled" value="256" min="1" id="app-memory" name="appMemory" class="form-control" />
+                        </div>
+                    </div>
+                </div>
+            </div>
+            <div class="row">
+                <div class="col-sm-5">
+                    <div class="form-group">
+                        <label for="event-trace-sampling-count" class="col-sm-6 control-label" data-lang="app-event-trace-sampling-count"><i>*</i></label>
+                        <div class="col-sm-6">
+                            <input type="number" id="event-trace-sampling-count" value="0" min="0" name="eventTraceSamplingCount" class="form-control" />
+                        </div>
+                    </div>
+                </div>
+                <div class="col-sm-5">
+                    <div class="form-group">
+                        <label for="app-cache-enable" class="col-sm-6 control-label" data-lang="app-cache-enable"></label>
+                        <div class="col-sm-6">
+                            <input type="checkbox" checked="checked" id="app-cache-enable" name="appCacheEnable" />
+                        </div>
+                    </div>
+                </div>
+            </div>
+            <div class="row">
+                <div class="width-appURL">
+                    <div class="form-group">
+                        <label for="app-url" class="col-sm-2 control-label" data-lang="app-url"><i>*</i></label>
+                        <div class="col-sm-8">
+                           <div class="app-width-size">
+                                <input type="text" disabled="disabled" placeholder="http://file_host:8080/foo-job.tar.gz" id="app-url" name="appURL" class="form-control" data-toggle="tooltip" data-placement="bottom" title="必须是可以通过网络访问到的路径。如:http://file_host:8080/your-job.tar.gz"/>
+                           </div>
+                        </div>
+                    </div>
+                 </div>
+            </div>
+            <div class="form-group">
+                <div class="center-font">
+                    <button  id="save-button" class="btn-xs btn-primary" type="submit" data-lang="operation-submit"></button>
+                </div>
+            </div>
+        </form>
+    </div>
+</div>
+<script src="lib/BootstrapValidator/js/bootstrapValidator.js"></script>
+<script src="lib/BootstrapValidator/js/bootstrapValidator_zh_CN.js"></script>
+<script src="js/app/app_common.js"></script>
+<script src="js/app/modify_app.js"></script>
diff --git a/elastic-job-cloud-scheduler/src/main/resources/console/html/history/job_dashboard.html b/elastic-job-cloud-scheduler/src/main/resources/console/html/history/job_dashboard.html
new file mode 100644
index 0000000..2855c3c
--- /dev/null
+++ b/elastic-job-cloud-scheduler/src/main/resources/console/html/history/job_dashboard.html
@@ -0,0 +1,117 @@
+<div class="content-wrapper">
+    <section class="content-header">
+        <h1 data-lang="sidebar-job-dashboard"></h1>
+        <ol class="breadcrumb">
+            <li class="active"><i class="fa fa-history" data-lang="sidebar-job-history"></i></li>
+            <li class="active" data-lang="sidebar-job-dashboard"></li>
+        </ol>
+    </section>
+    <hr class="table-line"> 
+    <div class="box-body">
+        <div class="row" >
+            <div class="col-sm-7">
+                <div class="chart-right">
+                    <div class="box box-danger">
+                        <div class="box-header with-border">
+                            <h3 class="box-title" data-lang="dashboard-succ-and-fail-count"></h3>
+                            <div class="box-tools pull-right">
+                                <button class="btn btn-box-tool" data-widget="collapse"><i class="fa fa-minus"></i></button>
+                            </div>
+                        </div>
+                        <div class="box-body" > 
+                            <div class="chart-size">
+                                <div class="row">
+                                    <div class="col-sm-4">
+                                        <div id="total-jobs-lastMinute" class="chart-size-solation"></div>
+                                    </div>
+                                    <div class="col-sm-4">
+                                        <div id="total-jobs-lastHour" class="chart-size-solation"></div>
+                                    </div>
+                                    <div class="col-sm-4">
+                                        <div id="total-jobs-weekly" class="chart-size-solation"></div>
+                                    </div>
+                                </div>
+                            </div>
+                        </div>
+                    </div>
+                </div>
+            </div>
+            <div class="col-sm-5">
+                <div class="chart-left">
+                    <div class="box box-danger">
+                        <div class="box-header with-border">
+                            <h3 class="box-title" data-lang="dashboard-job-type"></h3>
+                            <div class="box-tools pull-right">
+                                <button class="btn btn-box-tool" data-widget="collapse"><i class="fa fa-minus"></i></button>
+                            </div>
+                        </div>
+                        <div class="box-body">
+                            <div class="chart-size">
+                                <div class="row">
+                                    <div class="col-sm-6">
+                                        <div id="job-type" class="chart-size-solation"></div>
+                                    </div>
+                                    <div class="col-sm-6">
+                                        <div id="job-execution-type" class="chart-size-solation"></div>
+                                    </div>
+                                </div>
+                            </div>
+                        </div>
+                    </div>
+                </div> 
+            </div>
+        </div>
+        <div class="row">
+            <div class="col-sm-12">
+                <div class="box box-info">
+                    <div class="box-header with-border">
+                        <h3 class="box-title" data-lang="dashboard-succ-and-fail-count"></h3>
+                        <div class="box-tools pull-right">
+                            <button class="btn btn-box-tool" data-widget="collapse"><i class="fa fa-minus"></i></button>
+                        </div>
+                    </div>
+                    <div class="box-body" >
+                        <div class="row">
+                            <div id="statictis_jobs" class="set-size"></div>
+                        </div>
+                    </div>
+                </div>
+            </div>
+        </div>
+        <div class="row">
+            <div class="col-sm-12">
+                <div class="box box-info">
+                    <div class="box-header with-border">
+                        <h3 class="box-title" data-lang="dashboard-job-task-running-count"></h3>
+                        <div class="box-tools pull-right">
+                            <button class="btn btn-box-tool" data-widget="collapse"><i class="fa fa-minus"></i></button>
+                        </div>
+                    </div>
+                    <div class="box-body">
+                        <div class="row" >
+                            <div id="run-jobs" class="set-size"></div>
+                        </div>
+                    </div>
+                </div>
+            </div>
+        </div>
+        <div class="row">
+            <div class="col-sm-12">
+                <div class="box box-success">
+                    <div class="box-header with-border">
+                        <h3 class="box-title" data-lang="dashboard-current-jobs-count"></h3>
+                        <div class="box-tools pull-right">
+                            <button class="btn btn-box-tool" data-widget="collapse"><i class="fa fa-minus"></i></button>
+                        </div>
+                    </div>
+                    <div class="box-body">
+                        <div class="row">
+                            <div id="regist-jobs" class="set-size"></div>
+                        </div>
+                    </div>
+                </div>
+            </div>
+        </div>
+    </div>
+</div>
+<script src="js/history/job_dashboard.js"></script>
diff --git a/elastic-job-cloud-scheduler/src/main/resources/console/html/history/job_exec_details.html b/elastic-job-cloud-scheduler/src/main/resources/console/html/history/job_exec_details.html
new file mode 100644
index 0000000..ed49fea
--- /dev/null
+++ b/elastic-job-cloud-scheduler/src/main/resources/console/html/history/job_exec_details.html
@@ -0,0 +1,84 @@
+<div class="content-wrapper">
+    <div class="page-height-min">
+        <section class="content-header">
+            <h1 data-lang="sidebar-job-trace"></h1>
+            <ol class="breadcrumb">
+                <li class="active"><i class="fa fa-history" data-lang="sidebar-job-history"></i></li>
+                <li class="active" data-lang="sidebar-job-trace"></li>
+            </ol>
+        </section>
+        <section class="content">
+            <div id="job-exec-detail-toolbar-div">
+                <div class="form-inline" role="form">
+                    <div class="form-group toolbar">
+                        <label for="job-name" data-lang="job-name"></label>
+                        <input type="text" class="form-control" id="job-name" placeholder="">
+                    </div>
+                    <div class="form-group toolbar">
+                        <label for="task-id" data-lang="task-id"></label>
+                        <input type="text" class="form-control" size="55" id="task-id" placeholder="">
+                    </div>
+                    <br/>
+                    <div class="form-group toolbar">
+                        <label for="ip" data-lang="server-ip"></label>
+                        <input type="text" class="form-control" data-inputmask="'alias': 'ip'" data-mask="" id="ip">
+                    </div>
+                    <div class="form-group toolbar">
+                        <label for="start-time" data-lang="start-time"></label>
+                        <input type="text" class="form-control pull-right custom-datepicker" id="start-time">
+                    </div>
+                    <div class="form-group toolbar">
+                        <label for="end-time" data-lang="complete-time"></label>
+                        <input type="text" class="form-control pull-right custom-datepicker" id="end-time">
+                    </div><br>
+                    <div id="execute-result">
+                        <div class="form-group">
+                            <label data-lang="execute-result"></label>:
+                            <input type="radio" name="isSuccess" value="1"><label data-lang="execute-result-success"></label>
+                            <input type="radio" name="isSuccess" value="0"><label data-lang="execute-result-failure"></label>
+                            <input type="radio" name="isSuccess" value="" checked="checked"><label data-lang="execute-result-all"></label>
+                        </div>
+                    </div>
+                </div>
+            </div>
+            <table id="job-exec-details-table" 
+                data-show-refresh="true"
+                data-show-toggle="true"
+                data-striped="true"
+                data-toggle="table"
+                data-url="/api/job/events/executions"
+                data-flat="true"
+                data-click-to-select="true"
+                data-row-style="rowStyle"
+                data-query-params="queryParams"
+                data-query-params-type="notLimit"
+                data-side-pagination="server"
+                data-pagination="true"
+                data-page-list="[10, 20, 50, 100]"
+                data-show-columns="true"
+                data-toolbar="#job-exec-detail-toolbar-div">
+                <thead>
+                    <tr>
+                        <th data-field="jobName" data-sortable="true"><span data-lang="job-name"></span></th>
+                        <th data-field="taskId"><span data-lang="task-id"></span></th>
+                        <th data-field="ip" data-sortable="true"><span data-lang="server-ip"></span></th>
+                        <th data-field="shardingItem"><span data-lang="job-shrading-item"></span></th>
+                        <th data-field="source" data-sortable="true"><span data-lang="execute-source"></span></th>
+                        <th data-field="success" data-sortable="true" data-formatter="successFormatter"><span data-lang="execute-result"></span></th>
+                        <th data-field="failureCause.plainText" data-formatter="splitFormatter"><span data-lang="failure-reason"></span></th>
+                        <th data-field="startTime" data-sortable="true" data-formatter="dateTimeFormatter"><span data-lang="start-time"></span></th>
+                        <th data-field="completeTime" data-sortable="true" data-formatter="dateTimeFormatter"><span data-lang="complete-time"></span></th>
+                    </tr>
+                </thead>
+            </table>
+        </section>
+    </div>
+</div>
+<script src="lib/bootstrap-table/bootstrap-table.min.js"></script>
+<script src="lib/daterangepicker/moment.min.js"></script>
+<script src="lib/daterangepicker/daterangepicker.js"></script>
+<script src="lib/input-mask/jquery.inputmask.js"></script>
+<script src="lib/input-mask/jquery.inputmask.date.extensions.js"></script>
+<script src="lib/input-mask/jquery.inputmask.extensions.js"></script>
+<script src="js/history/history_common.js"></script>
+<script src="js/history/job_exec_details.js"></script>
diff --git a/elastic-job-cloud-scheduler/src/main/resources/console/html/history/job_exec_status.html b/elastic-job-cloud-scheduler/src/main/resources/console/html/history/job_exec_status.html
new file mode 100644
index 0000000..85ed4cb
--- /dev/null
+++ b/elastic-job-cloud-scheduler/src/main/resources/console/html/history/job_exec_status.html
@@ -0,0 +1,105 @@
+<div class="content-wrapper">
+    <div class="page-height-min">
+        <section class="content-header">
+            <h1 data-lang="sidebar-history-status"></h1>
+            <ol class="breadcrumb">
+                <li class="active"><i class="fa fa-history" data-lang="sidebar-job-history"></i></li>
+                <li class="active" data-lang="sidebar-history-status"></li>
+            </ol>
+        </section>
+        <section class="content">
+            <div id="jobExecStatusToolbar">
+                <div class="form-inline" role="form">
+                    <div class="form-group toolbar">
+                        <label for="job-name" data-lang="job-name"></label>
+                        <input type="text" class="form-control" id="job-name" placeholder="">
+                    </div>
+                    <div class="form-group toolbar">
+                        <label for="task-id" data-lang="task-id"></label>
+                        <input type="text" class="form-control" size="54" id="task-id" placeholder="">
+                    </div>
+                    <br/>
+                    <div class="form-group toolbar">
+                        <label for="slave-id" data-lang="server-ip"></label>
+                        <input type="text" class="form-control" id="slave-id" placeholder="">
+                    </div>
+                    <div class="form-group toolbar">
+                        <label for="source" data-lang="execute-source"></label>
+                        <select id="source" name="source" class="form-control" data-toggle="tooltip" data-placement="bottom" >
+                            <option value="" data-lang="execute-result-all"></option>
+                            <option value="CLOUD_SCHEDULER">CLOUD_SCHEDULER</option>
+                            <option value="CLOUD_EXECUTOR">CLOUD_EXECUTOR</option>
+                        </select>
+                    </div>
+                    <div class="form-group toolbar">
+                        <label for="execution-type" data-lang="execute-type"></label>
+                        <select id="execution-type" name="jobExecutionType" class="form-control" data-toggle="tooltip" data-placement="bottom" >
+                            <option value="" data-lang="execute-result-all"></option>
+                            <option value="FAILOVER" >FAILOVER</option>
+                            <option value="READY">READY</option>
+                        </select> 
+                    </div>
+                    <br/>
+                    <div class="form-group toolbar">
+                        <label for="state" data-lang="status"></label>
+                        <select id="state" name="state" class="form-control" data-toggle="tooltip" data-placement="bottom" >
+                            <option value="" data-lang="execute-result-all"></option>
+                            <option value="TASK_STAGING" data-lang="status-staging"></option>
+                            <option value="TASK_FAILED" data-lang="status-task-failed"></option>
+                            <option value="TASK_FINISHED" data-lang="status-task-finished"></option>
+                            <option value="TASK_RUNNING" data-lang="status-running"></option>
+                            <option value="TASK_ERROR" data-lang="status-task-error"></option>
+                            <option value="TASK_KILLED" data-lang="status-task-killed"></option>
+                        </select> 
+                    </div>
+                    <div class="form-group toolbar">
+                        <label for="start-time" data-lang="creation-start-time"></label>
+                        <input type="text" class="form-control pull-right custom-datepicker" id="start-time">
+                    </div>
+                    <div class="form-group toolbar">
+                        <label for="end-time" data-lang="creation-end-time"></label>
+                        <input type="text" class="form-control pull-right custom-datepicker" id="end-time">
+                    </div>
+                </div>
+            </div>
+            <table id="job-exec-status-table" 
+                data-show-refresh="true"
+                data-show-toggle="true"
+                data-striped="true"
+                data-toggle="table"
+                data-url="/api/job/events/statusTraces"
+                data-flat="true"
+                data-click-to-select="true"
+                data-row-style="rowStyle"
+                data-query-params="queryParams"
+                data-query-params-type="notLimit"
+                data-side-pagination="server"
+                data-pagination="true"
+                data-page-list="[10, 20, 50, 100]"
+                data-show-columns="true"
+                data-toolbar="#jobExecStatusToolbar">
+                <thead>
+                    <tr>
+                        <th data-field="jobName" data-sortable="true"><span data-lang="job-name"></span></th>
+                        <th data-field="taskId"><span data-lang="task-id"></span></th>
+                        <th data-field="slaveId" data-sortable="true"><span data-lang="server-ip"></span></th>
+                        <th data-field="source" data-sortable="true"><span data-lang="execute-source"></span></th>
+                        <th data-field="shardingItems"><span data-lang="job-sharding-item"></span></th>
+                        <th data-field="executionType" data-sortable="true"><span data-lang="execute-type"></span></th>
+                        <th data-field="state" data-sortable="true" data-formatter="stateFormatter"><span data-lang="status"></span></th>
+                        <th data-field="creationTime" data-sortable="true" data-formatter="dateTimeFormatter"><span data-lang="creation-time"></span></th>
+                        <th data-field="message" data-formatter="splitRemarkFormatter"><span data-lang="comments"></span></th>
+                    </tr>
+                </thead>
+            </table>
+        </section>
+    </div>
+</div>
+<script src="lib/bootstrap-table/bootstrap-table.min.js"></script>
+<script src="lib/daterangepicker/moment.min.js"></script>
+<script src="lib/daterangepicker/daterangepicker.js"></script>
+<script src="lib/input-mask/jquery.inputmask.js"></script>
+<script src="lib/input-mask/jquery.inputmask.date.extensions.js"></script>
+<script src="lib/input-mask/jquery.inputmask.extensions.js"></script>
+<script src="js/history/history_common.js"></script>
+<script src="js/history/job_exec_status.js"></script>
diff --git a/elastic-job-cloud-scheduler/src/main/resources/console/html/job/add_job.html b/elastic-job-cloud-scheduler/src/main/resources/console/html/job/add_job.html
new file mode 100644
index 0000000..8e6561b
--- /dev/null
+++ b/elastic-job-cloud-scheduler/src/main/resources/console/html/job/add_job.html
@@ -0,0 +1,174 @@
+<div class="box-body">
+    <div role="tabpanel" class="tab-pane active" onsubmit="return false;">
+        <form id="job-form" class="form-horizontal">
+            <div class="row">
+                <div class="col-sm-4">
+                    <div class="form-group">
+                        <label for="job-name" class="col-sm-6 control-label" data-lang="job-name"><i>*</i></label>
+                        <div class="col-sm-6">
+                            <input type="text" placeholder="yourJob" id="job-name" name="jobName" class="form-control" />
+                        </div>
+                    </div>
+                </div>
+                <div class="col-sm-4">
+                    <div class="form-group">
+                        <label for="job-app-name" class="col-sm-6 control-label" data-lang="app-name"><i>*</i></label>
+                        <div class="col-sm-6">
+                            <input type="text" placeholder="yourappName" id="job-app-name" name="jobAppName" class="form-control" data-toggle="tooltip" data-placement="bottom" title="作业所在的应用名称,必须是在应用中已注册。"/>
+                        </div>
+                    </div>
+                </div>
+                <div class="col-sm-3">
+                    <div class="form-group">
+                        <label for="job-execution-type" class="col-sm-6 control-label" data-lang="job-execution-type"><i>*</i></label>
+                        <div class="col-sm-6">
+                            <select id="job-execution-type" name="jobExecutionType" class="form-control" >
+                                <option value="DAEMON">DAEMON</option>
+                                <option value="TRANSIENT">TRANSIENT</option>
+                            </select> 
+                        </div>
+                    </div>
+                </div>
+            </div>
+            <div class="form-group" id="job-class-model">
+                <label for="job-class" class="col-sm-2 control-label" data-lang="job-class"><i>*</i></label>
+                <div class="col-sm-9">
+                    <input type="text" placeholder="yourJobClass" id="job-class" name="jobClass" class="form-control" data-toggle="tooltip" data-placement="bottom" title="作业实现类,需实现ElasticJob接口,脚本型作业不需要配置"/>
+                </div>
+            </div>
+            <div class="row">
+                <div class="col-sm-4">
+                    <div class="form-group">
+                        <label for="job-type" class="col-sm-6 control-label" data-lang="job-type"><i>*</i></label>
+                        <div class="col-sm-6">
+                            <select id="job-type" name="jobType" class="form-control" >
+                                <option value="SIMPLE" required>SIMPLE</option>
+                                <option value="DATAFLOW">DATAFLOW</option>
+                                <option value="SCRIPT">SCRIPT</option>
+                            </select>
+                        </div>
+                    </div>
+                </div>
+            </div>
+            <div class="row">
+                <div class="col-sm-4">
+                    <div class="form-group">
+                        <label for="cron" class="col-sm-6 control-label" data-lang="job-cron"><i>*</i></label>
+                        <div class="col-sm-6">
+                            <input type="text" placeholder="0/5 * * * * ?" id="cron" name="cron" class="form-control" data-toggle="tooltip" data-placement="bottom"  title="作业启动时间的cron表达式。如:0/5 * * * * ?"/>
+                        </div>
+                    </div>
+                </div>
+                <div class="col-sm-4">
+                    <div class="form-group">
+                        <label for="sharding-total-count" class="col-sm-6 control-label" data-lang="job-sharding-total-count"><i>*</i></label>
+                        <div class="col-sm-6">
+                            <input type="number" min=1 value=1 id="sharding-total-count" name="shardingTotalCount" class="form-control" data-toggle="tooltip" data-placement="bottom" title="作业分片总数"/>
+                        </div>
+                    </div>
+                </div>
+                <div class="col-sm-3">
+                    <div class="form-group">
+                        <label for="job-parameter" class="col-sm-6 control-label" data-lang="job-parameter"></label>
+                        <div class="col-sm-6">
+                            <input type="text" id="job-parameter" name="jobParameter" class="form-control" data-toggle="tooltip" data-placement="bottom" title="作业自定义参数,可通过传递该参数为作业调度的业务方法传参,用于实现带参数的作业例:每次获取的数据量、作业实例从数据库读取的主键。" />
+                        </div>
+                    </div>
+                </div>
+            </div>
+            <div class="row">
+                <div class="col-sm-4">
+                    <div class="form-group">
+                        <label for="cpu-count" class="col-sm-6 control-label" data-lang="job-cpu-count"><i>*</i></label>
+                        <div class="col-sm-6">
+                            <input type="number" value="0.001" min="0.001" step="0.001" id="cpu-count" name="cpuCount" class="form-control" data-toggle="tooltip"  data-placement="bottom" title="单片作业所需要的CPU数量,最小值为0.001"/>
+                        </div>
+                    </div>
+                </div>
+                <div class="col-sm-4">
+                    <div class="form-group">
+                        <label for="job-memory" class="col-sm-6 control-label" data-lang="job-memory"><i>*</i></label>
+                        <div class="col-sm-6">
+                            <input type="number" value="1" min="1" id="job-memory" name="jobMemory" class="form-control" data-toggle="tooltip" data-placement="bottom" title="单片作业所需要的内存MB,最小值为1"/>
+                        </div>
+                    </div>
+                </div>
+                <div class="col-sm-3">
+                    <div class="form-group">
+                        <label for="bean-name" class="col-sm-6 control-label" data-lang="job-bean-name"></label>
+                        <div class="col-sm-6">
+                            <input type="text" placeholder="yourBeanName" id="bean-name" name="beanName" class="form-control" data-toggle="tooltip" data-placement="bottom" title="Spring容器中配置的bean名称" />
+                        </div>
+                    </div>
+                </div>
+            </div>
+            <div class="row">
+                <div class="col-sm-4">
+                    <div class="form-group">
+                        <label for="failover" class="col-sm-6 control-label" data-lang="job-failover"></label>
+                        <div class="col-sm-6">
+                            <input type="checkbox" id="failover" name="failover" data-toggle="tooltip" data-placement="bottom" title="是否开启任务执行失效转移,开启表示如果作业在一次任务执行中途宕机,允许将该次未完成的任务在另一作业节点上补偿执行。" />
+                        </div>
+                    </div>
+                </div>
+                <div class="col-sm-4">
+                    <div class="form-group">
+                        <label for="misfire" class="col-sm-6 control-label" data-lang="job-misfire"></label>
+                        <div class="col-sm-6">
+                            <input type="checkbox" id="misfire" name="misfire" data-toggle="tooltip" data-placement="bottom" title="是否开启任务错过重新执行" />
+                        </div>
+                    </div>
+                </div>
+                <div class="col-sm-4">
+                    <div class="form-group">
+                        <label for="streaming-process" hidden="hidden" id="streaming-process-box" class="col-sm-6 control-label" data-lang="job-streaming-process"></label>
+                        <div class="col-sm-6" >
+                            <input hidden="hidden" type="checkbox" id="streaming-process" name="streamingProcess" data-toggle="tooltip" data-placement="bottom" title="DATAFLOW类型作业,是否流式处理数据如果流式处理数据, 则fetchData不返回空结果将持续执行作业,如果非流式处理数据, 则处理数据完成后作业结束。" />
+                        </div>
+                    </div>
+                </div>
+            </div>
+            <div class="row">
+                <div class="form-group">
+                    <label for="application-context" class="col-sm-2 control-label" data-lang="job-application-context"></label>
+                    <div class="col-sm-9">
+                        <input type="text" placeholder="META-INF\applicationContext.xml" id="application-context" name="applicationContext" class="form-control" data-toggle="tooltip" data-placement="bottom" title="Spring方式配置Spring配置文件相对路径以及名称,如:META-INF\applicationContext.xml" />
+                    </div>
+                </div>
+            </div>
+            <div class="row">
+                <div class="form-group">
+                    <label for="sharding-item-parameters" class="col-sm-2 control-label" data-lang="job-sharding-item-parameters"></label>
+                    <div class="col-sm-9">
+                        <textarea id="sharding-item-parameters" placeholder="0=a,1=b,2=c" name="shardingItemParameters" class="form-control" data-toggle="tooltip" data-placement="bottom" title="分片序列号和参数用等号分隔,多个键值对用逗号分隔,类似map。分片序列号从0开始,不可大于或等于作业分片总数。如:0=a,1=b,2=c"></textarea>
+                    </div>
+                </div>
+            </div>
+            <div class="row">
+                <div class="form-group" id="bootstrap-script-div" hidden="hidden">
+                    <label for="script-command-line" class="col-sm-2 control-label" data-lang="job-script-command-line"><i>*</i></label>
+                    <div class="col-sm-9">
+                        <input type="text" id="script-command-line" name="scriptCommandLine" class="form-control" data-toggle="tooltip" data-placement="bottom" title="SCRIPT类型作业命令行执行脚本" />
+                    </div>
+                </div>
+            </div>
+            <div class="row">
+                <div class="form-group">
+                    <label for="description" class="col-sm-2 control-label" data-lang="job-description"></label>
+                    <div class="col-sm-9">
+                        <textarea id="description" name="description" class="form-control"></textarea>
+                    </div>
+                </div>
+            </div> 
+            <div class="form-group">
+                <div class="center-font">
+                    <button id="save-button" class="btn-xs btn-primary" type="submit" data-lang="operation-submit"></button>
+                </div>
+            </div>
+        </form>
+    </div>
+</div>
+<script src="lib/BootstrapValidator/js/bootstrapValidator.js"></script>
+<script src="lib/BootstrapValidator/js/bootstrapValidator_zh_CN.js"></script>
+<script src="js/job/job_common.js"></script>
+<script src="js/job/add_job.js"></script>
diff --git a/elastic-job-cloud-scheduler/src/main/resources/console/html/job/detail_job.html b/elastic-job-cloud-scheduler/src/main/resources/console/html/job/detail_job.html
new file mode 100644
index 0000000..022bd72
--- /dev/null
+++ b/elastic-job-cloud-scheduler/src/main/resources/console/html/job/detail_job.html
@@ -0,0 +1,166 @@
+<div class="box-body">
+    <div role="tabpanel" class="tab-pane active" onsubmit="return false;">
+        <form id="job-form" class="form-horizontal">
+            <div class="row">
+                <div class="col-sm-4">
+                    <div class="form-group">
+                        <label for="job-name" class="col-sm-6 control-label" data-lang="job-name"><i>*</i></label>
+                        <div class="col-sm-6">
+                            <input type="text" disabled="disabled" placeholder="yourJob" id="job-name" name="jobName" class="form-control" />
+                        </div>
+                    </div>
+                </div>
+                <div class="col-sm-4">
+                    <div class="form-group">
+                        <label for="job-app-name" class="col-sm-6 control-label" data-lang="app-name"><i>*</i></label>
+                        <div class="col-sm-6">
+                            <input type="text" disabled="disabled" placeholder="yourappName" id="job-app-name" name="jobAppName" class="form-control" data-toggle="tooltip" data-placement="bottom" title="作业所在的应用名称,必须是在应用中已注册。"/>
+                        </div>
+                    </div>
+                </div>
+                <div class="col-sm-3">
+                    <div class="form-group">
+                        <label for="job-execution-type" class="col-sm-6 control-label" data-lang="job-execution-type"><i>*</i></label>
+                        <div class="col-sm-6">
+                            <select id="job-execution-type" disabled="disabled" name="jobExecutionType" class="form-control" >
+                                <option value="DAEMON">DAEMON</option>
+                                <option value="TRANSIENT">TRANSIENT</option>
+                            </select> 
+                        </div>
+                    </div>
+                </div>
+            </div>
+            <div class="form-group" id="job-class-model">
+                <label for="job-class" class="col-sm-2 control-label" data-lang="job-class"><i>*</i></label>
+                <div class="col-sm-9">
+                    <input type="text" disabled="disabled" placeholder="yourJobClass" id="job-class" name="jobClass" class="form-control" data-toggle="tooltip" data-placement="bottom" title="作业实现类,需实现ElasticJob接口,脚本型作业不需要配置"/>
+                </div>
+            </div>
+            <div class="row">
+                <div class="col-sm-4">
+                    <div class="form-group">
+                        <label for="job-type" class="col-sm-6 control-label" data-lang="job-type"><i>*</i></label>
+                        <div class="col-sm-6">
+                            <select id="job-type" disabled="disabled" name="jobType" class="form-control" >
+                                <option value="SIMPLE" required>SIMPLE</option>
+                                <option value="DATAFLOW">DATAFLOW</option>
+                                <option value="SCRIPT">SCRIPT</option>
+                            </select>
+                        </div>
+                    </div>
+                </div>
+            </div>
+            <div class="row">
+                <div class="col-sm-4">
+                    <div class="form-group">
+                        <label for="cron" class="col-sm-6 control-label" data-lang="job-cron"><i>*</i></label>
+                        <div class="col-sm-6">
+                            <input type="text" disabled="disabled" placeholder="0/5 * * * * ?" id="cron" name="cron" class="form-control" data-toggle="tooltip" data-placement="bottom"  title="作业启动时间的cron表达式。如:0/5 * * * * ?"/>
+                        </div>
+                    </div>
+                </div>
+                <div class="col-sm-4">
+                    <div class="form-group">
+                        <label for="sharding-total-count" class="col-sm-6 control-label" data-lang="job-sharding-total-count"><i>*</i></label>
+                        <div class="col-sm-6">
+                            <input type="number" disabled="disabled" min=1 value=1 id="sharding-total-count" name="shardingTotalCount" class="form-control" data-toggle="tooltip" data-placement="bottom" title="作业分片总数"/>
+                        </div>
+                    </div>
+                </div>
+                <div class="col-sm-3">
+                    <div class="form-group">
+                        <label for="job-parameter" class="col-sm-6 control-label" data-lang="job-parameter"></label>
+                        <div class="col-sm-6">
+                            <input type="text" disabled="disabled" id="job-parameter" name="jobParameter" class="form-control" data-toggle="tooltip" data-placement="bottom" title="作业自定义参数,可通过传递该参数为作业调度的业务方法传参,用于实现带参数的作业例:每次获取的数据量、作业实例从数据库读取的主键。" />
+                        </div>
+                    </div>
+                </div>
+            </div>
+            <div class="row">
+                <div class="col-sm-4">
+                    <div class="form-group">
+                        <label for="cpu-count" class="col-sm-6 control-label" data-lang="job-cpu-count"><i>*</i></label>
+                        <div class="col-sm-6">
+                            <input type="number" disabled="disabled" value="0.001" min="0.001" step="0.001" id="cpu-count" name="cpuCount" class="form-control" data-toggle="tooltip"  data-placement="bottom" title="单片作业所需要的CPU数量,最小值为0.001"/>
+                        </div>
+                    </div>
+                </div>
+                <div class="col-sm-4">
+                    <div class="form-group">
+                        <label for="job-memory" class="col-sm-6 control-label" data-lang="job-memory"><i>*</i></label>
+                        <div class="col-sm-6">
+                            <input type="number" disabled="disabled" value="1" min="1" id="job-memory" name="jobMemory" class="form-control" data-toggle="tooltip" data-placement="bottom" title="单片作业所需要的内存MB,最小值为1"/>
+                        </div>
+                    </div>
+                </div>
+                <div class="col-sm-3">
+                    <div class="form-group">
+                        <label for="bean-name" class="col-sm-6 control-label" data-lang="job-bean-name"></label>
+                        <div class="col-sm-6">
+                            <input type="text" disabled="disabled" placeholder="yourBeanName" id="bean-name" name="beanName" class="form-control" data-toggle="tooltip" data-placement="bottom" title="Spring容器中配置的bean名称" />
+                        </div>
+                    </div>
+                </div>
+            </div>
+            <div class="row">
+                <div class="col-sm-4">
+                    <div class="form-group">
+                        <label for="failover" class="col-sm-6 control-label" data-lang="job-failover"></label>
+                        <div class="col-sm-6">
+                            <input type="checkbox" disabled="disabled" id="failover" name="failover" data-toggle="tooltip" data-placement="bottom" title="是否开启任务执行失效转移,开启表示如果作业在一次任务执行中途宕机,允许将该次未完成的任务在另一作业节点上补偿执行。" />
+                        </div>
+                    </div>
+                </div>
+                <div class="col-sm-4">
+                    <div class="form-group">
+                        <label for="misfire" class="col-sm-6 control-label" data-lang="job-misfire"></label>
+                        <div class="col-sm-6">
+                            <input type="checkbox" disabled="disabled" id="misfire" name="misfire" data-toggle="tooltip" data-placement="bottom" title="是否开启任务错过重新执行" />
+                        </div>
+                    </div>
+                </div>
+                <div class="col-sm-4">
+                    <div class="form-group">
+                        <label for="streaming-process" hidden="hidden" id="streaming-process-box" class="col-sm-6 control-label" data-lang="job-streaming-process"></label>
+                        <div class="col-sm-6" >
+                            <input hidden="hidden" disabled="disabled" type="checkbox" id="streaming-process" name="streamingProcess" data-toggle="tooltip" data-placement="bottom" title="DATAFLOW类型作业,是否流式处理数据如果流式处理数据, 则fetchData不返回空结果将持续执行作业,如果非流式处理数据, 则处理数据完成后作业结束。" />
+                        </div>
+                    </div>
+                </div>
+            </div>
+            <div class="row">
+                <div class="form-group">
+                    <label for="application-context" class="col-sm-2 control-label" data-lang="job-application-context"></label>
+                    <div class="col-sm-9">
+                        <input type="text" disabled="disabled" placeholder="META-INF\applicationContext.xml" id="application-context" name="applicationContext" class="form-control" data-toggle="tooltip" data-placement="bottom" title="Spring方式配置Spring配置文件相对路径以及名称,如:META-INF\applicationContext.xml" />
+                    </div>
+                </div>
+            </div>
+            <div class="row">
+                <div class="form-group">
+                    <label for="sharding-item-parameters" class="col-sm-2 control-label" data-lang="job-sharding-item-parameters"></label>
+                    <div class="col-sm-9">
+                        <textarea id="sharding-item-parameters" disabled="disabled" placeholder="0=a,1=b,2=c" name="shardingItemParameters" class="form-control" data-toggle="tooltip" data-placement="bottom" title="分片序列号和参数用等号分隔,多个键值对用逗号分隔,类似map。分片序列号从0开始,不可大于或等于作业分片总数。如:0=a,1=b,2=c"></textarea>
+                    </div>
+                </div>
+            </div>
+            <div class="row">
+                <div class="form-group" id="bootstrap-script-div" hidden="hidden">
+                    <label for="script-command-line" class="col-sm-2 control-label" data-lang="job-script-command-line"><i>*</i></label>
+                    <div class="col-sm-9">
+                        <input type="text" id="script-command-line" disabled="disabled" name="scriptCommandLine" class="form-control" data-toggle="tooltip" data-placement="bottom" title="SCRIPT类型作业命令行执行脚本" />
+                    </div>
+                </div>
+            </div>
+            <div class="row">
+                <div class="form-group">
+                    <label for="description" class="col-sm-2 control-label" data-lang="job-description"></label>
+                    <div class="col-sm-9">
+                        <textarea id="description" disabled="disabled" name="description" class="form-control"></textarea>
+                    </div>
+                </div>
+            </div>
+        </form>
+    </div>
+</div>
+
diff --git a/elastic-job-cloud-scheduler/src/main/resources/console/html/job/job_status.html b/elastic-job-cloud-scheduler/src/main/resources/console/html/job/job_status.html
new file mode 100644
index 0000000..ea76b7d
--- /dev/null
+++ b/elastic-job-cloud-scheduler/src/main/resources/console/html/job/job_status.html
@@ -0,0 +1,100 @@
+<div class="content-wrapper">
+    <div class="page-height-min">
+        <section class="content-header">
+            <h1 data-lang="job-status"></h1>
+            <ol class="breadcrumb">
+                <li class="active"><i class="fa fa-tasks" data-lang="sidebar-job"></i></li>
+                <li class="active" data-lang="status"></li>
+            </ol>
+        </section>
+        <section class="content">
+            <ul class="nav nav-tabs" role="tablist">
+                <li id="running_tab" role="presentation" class="active"><a href="#running" aria-controls="running" role="tab" data-toggle="tab" data-lang="tab-running-tasks"></a></li>
+                <li id="ready_tab" role="presentation"><a href="#ready" aria-controls="ready" role="tab" data-toggle="tab" data-lang="tab-ready-jobs"></a></li>
+                <li id="failover_tab" role="presentation"><a href="#failover" aria-controls="failover" role="tab" data-toggle="tab" data-lang="tab-failover-tasks"></a></li>
+            </ul>
+            <div class="tab-content">
+                <div role="tabpanel" class="tab-pane active" id="running">
+                    <table id="running" 
+                        data-show-refresh="true"
+                        data-show-toggle="true"
+                        data-striped="true"
+                        data-toggle="table"
+                        data-url="/api/job/tasks/running"
+                        data-flat="true"
+                        data-click-to-select="true"
+                        data-row-style="rowStyle"
+                        data-search="true"
+                        data-strict-search="false"
+                        data-query-params="queryParams"
+                        data-query-params-type="notLimit"
+                        data-pagination="true"
+                        data-page-list="[10, 20, 50, 100]"
+                        data-show-columns="true">
+                        <thead>
+                            <tr>
+                                <th data-field="id" data-sortable="true"><span data-lang="task-id"></span></th>
+                                <th data-field="metaInfo.jobName" data-sortable="true"><span data-lang="task-name"></span></th>
+                                <th data-field="slaveId" data-sortable="true"><span data-lang="server-ip"></span></th>
+                                <th data-field="type" data-sortable="true"><span data-lang="job-execution-type"></span></th>
+                                <th data-field="metaInfo.shardingItems"><span data-lang="job-sharding-item"></span></th>
+                            </tr>
+                        </thead>
+                    </table>
+                </div>
+                <div role="tabpanel" class="tab-pane" id="ready">
+                    <table id="ready" 
+                        data-show-refresh="true"
+                        data-show-toggle="true"
+                        data-striped="true"
+                        data-toggle="table"
+                        data-url="/api/job/tasks/ready"
+                        data-flat="true"
+                        data-click-to-select="true"
+                        data-row-style="rowStyle"
+                        data-search="true"
+                        data-strict-search="false"
+                        data-query-params="queryParams"
+                        data-query-params-type="notLimit"
+                        data-pagination="true"
+                        data-page-list="[10, 20, 50, 100]"
+                        data-show-columns="true">
+                        <thead>
+                            <tr>
+                                <th data-field="jobName" data-sortable="true"><span data-lang="job-name"></span></th>
+                                <th data-field="times" data-sortable="true"><span data-lang="remaining-execute-times"></span></th>
+                            </tr>
+                        </thead>
+                    </table>
+                </div>
+                <div role="tabpanel" class="tab-pane" id="failover">
+                    <table id="failover" 
+                        data-show-refresh="true"
+                        data-show-toggle="true"
+                        data-striped="true"
+                        data-toggle="table"
+                        data-url="/api/job/tasks/failover"
+                        data-flat="true"
+                        data-click-to-select="true"
+                        data-row-style="rowStyle"
+                        data-search="true"
+                        data-strict-search="false"
+                        data-query-params="queryParams"
+                        data-query-params-type="notLimit"
+                        data-pagination="true"
+                        data-page-list="[10, 20, 50, 100]"
+                        data-show-columns="true">
+                        <thead>
+                            <tr>
+                                <th data-field="taskInfo.jobName" data-sortable="true"><span data-lang="job-name"></span></th>
+                                <th data-field="originalTaskId"><span data-lang="original-task-id"></span></th>
+                                <th data-field="taskInfo.shardingItems"><span data-lang="job-sharding-item"></span></th>
+                            </tr>
+                        </thead>
+                    </table>
+                </div>
+            </div>
+        </section>
+    </div>
+</div>
+<script src="lib/bootstrap-table/bootstrap-table.min.js"></script>
diff --git a/elastic-job-cloud-scheduler/src/main/resources/console/html/job/jobs_overview.html b/elastic-job-cloud-scheduler/src/main/resources/console/html/job/jobs_overview.html
new file mode 100644
index 0000000..5641634
--- /dev/null
+++ b/elastic-job-cloud-scheduler/src/main/resources/console/html/job/jobs_overview.html
@@ -0,0 +1,110 @@
+<div class="content-wrapper">
+    <div class="page-height-min">
+        <section class="content-header">
+            <h1 data-lang="sidebar-config"></h1>
+            <ol class="breadcrumb">
+                <li class="active"><i class="fa fa-tasks" data-lang="sidebar-job"></i></li>
+                <li class="active" data-lang="sidebar-config"></li>
+            </ol>
+        </section>
+        <section class="content">
+            <table id="job-table" data-pagination="true" data-page-list="[10, 20, 50, 100]" data-search="true" data-show-refresh="true" data-show-toggle="true" data-striped="true" data-show-columns="true">
+                <thead>
+                <tr>
+                    <th data-field="jobName" data-sortable="true"><span data-lang="job-name"></span></th>
+                    <th data-field="appName" data-sortable="true"><span data-lang="app-name"></span></th>
+                    <th data-field="jobClass" data-sortable="true"><span data-lang="job-class"></span></th>
+                    <th data-field="shardingTotalCount" data-sortable="true"><span data-lang="job-sharding-total-count"></span></th>
+                    <th data-field="cron" data-sortable="true"><span data-lang="job-cron"></span></th>
+                    <th data-field="operation" data-formatter="operationJob"><span data-lang="operation"></span></th>
+                </tr>
+                </thead>
+            </table>
+            <button type="button" class="btn-xs btn-success" data-toggle="modal" id="add-job" data-lang="operation-add"></button>
+        </section>
+    </div>
+</div>
+<div class="modal" id="data-detail-job" tabindex="-1" role="dialog" aria-labelledby="detail-modal-label" aria-hidden="true">
+    <div class="modal-dialog">
+        <div class="update-model-size">
+            <div class="modal-content">
+                <div class="modal-header">
+                    <button type="button" class="close" data-dismiss="modal" aria-label="Close"><span aria-hidden="true">×</span></button>
+                    <h2 class="modal-title" data-lang="job-detail"></h2>
+                </div>
+                <div class="modal-body" id="detail-job-body">
+                </div>
+            </div>
+        </div>
+    </div>
+</div>
+<div class="modal" id="delete-data" tabindex="-1" role="dialog" aria-labelledby="delete-modal-label" aria-hidden="true">
+    <div class="modal-dialog">
+        <div class="modal-content">
+            <div class="modal-body">
+                <h3 class="size-font" data-lang="confirm-to-delete-job"></h3>
+            </div>
+            <div class="modal-footer">
+                <button type="button" class="btn-xs btn-danger" id="delete-job-confirm" data-dismiss="modal" data-lang="operation-confirm"></button>
+                <button type="button" class="btn-xs btn-success" id="delete-job-remove" data-dismiss="modal" data-lang="operation-cancel"></button>
+            </div>
+        </div>
+    </div>
+</div>
+<div class="modal" id="data-update-job" tabindex="-1" role="dialog" aria-labelledby="modify-modal-label" aria-hidden="true">
+    <div class="modal-dialog">
+        <div class="update-model-size">
+            <div class="modal-content">
+                <div class="modal-header">
+                    <button type="button" class="close" data-dismiss="modal" aria-label="Close"><span aria-hidden="true">×</span></button>
+                    <h2 class="modal-title" data-lang="update-job"></h2>
+                </div>
+                <div class="modal-body" id="update-job-body">
+                </div>
+            </div>
+        </div>
+    </div>
+</div>
+<div class="modal" id="data-add-job" tabindex="-1" role="dialog" aria-labelledby="add-modal-label" aria-hidden="true">
+    <div class="modal-dialog">
+        <div class="update-model-size">
+            <div class="modal-content">
+                <div class="modal-header">
+                    <button type="button" class="close" data-dismiss="modal" aria-label="Close"><span aria-hidden="true">×</span></button>
+                    <h2 class="modal-title" data-lang="add-job"></h2>
+                </div>
+                <div class="modal-body" id="add-job-body">
+                </div>
+            </div>
+        </div>
+    </div>
+</div>
+<div class="modal" id="delete-data—bean-name" tabindex="-1" role="dialog" aria-labelledby="bean-name-modal-label" aria-hidden="true">
+    <div class="modal-dialog">
+        <div class="modal-content">
+            <div class="modal-header">
+                <button type="button" class="close" data-dismiss="modal" aria-label="Close"><span aria-hidden="true">×</span></button>
+                <h3 data-lang="job-spring-type-settings"></h3>
+            </div>
+            <div class="modal-body">
+                <h3 class="size-font" data-lang="job-bean-name-info"></h3>
+            </div>
+        </div>
+    </div>
+</div>
+<div class="modal" id="delete-data-application-context" tabindex="-1" role="dialog" aria-labelledby="application-context-modal-label" aria-hidden="true">
+    <div class="modal-dialog">
+        <div class="modal-content">
+            <div class="modal-header">
+                <button type="button" class="close" data-dismiss="modal" aria-label="Close"><span aria-hidden="true">×</span></button>
+                <h3 data-lang="job-spring-type-settings"></h3>
+            </div>
+            <div class="modal-body">
+                <h3 class="size-font" data-lang="job-application-context-info"></h3>
+            </div>
+        </div>
+    </div>
+</div>
+<script src="lib/bootstrap-table/bootstrap-table.js"></script>
+<script src="js/common/common.js"></script>
+<script src="js/job/jobs_overview.js"></script>
diff --git a/elastic-job-cloud-scheduler/src/main/resources/console/html/job/modify_job.html b/elastic-job-cloud-scheduler/src/main/resources/console/html/job/modify_job.html
new file mode 100644
index 0000000..a4441da
--- /dev/null
+++ b/elastic-job-cloud-scheduler/src/main/resources/console/html/job/modify_job.html
@@ -0,0 +1,174 @@
+<div class="box-body">
+    <div role="tabpanel" class="tab-pane active" onsubmit="return false;">
+        <form id="job-form" class="form-horizontal">
+            <div class="row">
+                <div class="col-sm-4">
+                    <div class="form-group">
+                        <label for="job-name" class="col-sm-6 control-label" data-lang="job-name"><i>*</i></label>
+                        <div class="col-sm-6">
+                            <input type="text" disabled="disabled" placeholder="yourJob" id="job-name" name="jobName" class="form-control" />
+                        </div>
+                    </div>
+                </div>
+                <div class="col-sm-4">
+                    <div class="form-group">
+                        <label for="job-app-name" class="col-sm-6 control-label" data-lang="app-name"><i>*</i></label>
+                        <div class="col-sm-6">
+                            <input type="text" disabled="disabled" placeholder="yourappName" id="job-app-name" name="jobAppName" class="form-control" data-toggle="tooltip" data-placement="bottom" title="作业所在的应用名称,必须是在应用中已注册。"/>
+                        </div>
+                    </div>
+                </div>
+                <div class="col-sm-3">
+                    <div class="form-group">
+                        <label for="job-execution-type" class="col-sm-6 control-label" data-lang="job-execution-type"><i>*</i></label>
+                        <div class="col-sm-6">
+                            <select id="job-execution-type" name="jobExecutionType" class="form-control" >
+                                <option value="DAEMON">DAEMON</option>
+                                <option value="TRANSIENT">TRANSIENT</option>
+                            </select> 
+                        </div>
+                    </div>
+                </div>
+            </div>
+            <div class="form-group" id="job-class-model">
+                <label for="job-class" class="col-sm-2 control-label" data-lang="job-class"><i>*</i></label>
+                <div class="col-sm-9">
+                    <input type="text" placeholder="yourJobClass" id="job-class" name="jobClass" class="form-control" data-toggle="tooltip" data-placement="bottom" title="作业实现类,需实现ElasticJob接口,脚本型作业不需要配置"/>
+                </div>
+            </div>
+            <div class="row">
+                <div class="col-sm-4">
+                    <div class="form-group">
+                        <label for="job-type" class="col-sm-6 control-label" data-lang="job-type"><i>*</i></label>
+                        <div class="col-sm-6">
+                            <select id="job-type" name="jobType" class="form-control" >
+                                <option value="SIMPLE" required>SIMPLE</option>
+                                <option value="DATAFLOW">DATAFLOW</option>
+                                <option value="SCRIPT">SCRIPT</option>
+                            </select>
+                        </div>
+                    </div>
+                </div>
+            </div>
+            <div class="row">
+                <div class="col-sm-4">
+                    <div class="form-group">
+                        <label for="cron" class="col-sm-6 control-label" data-lang="job-cron"><i>*</i></label>
+                        <div class="col-sm-6">
+                            <input type="text" placeholder="0/5 * * * * ?" id="cron" name="cron" class="form-control" data-toggle="tooltip" data-placement="bottom"  title="作业启动时间的cron表达式。如:0/5 * * * * ?"/>
+                        </div>
+                    </div>
+                </div>
+                <div class="col-sm-4">
+                    <div class="form-group">
+                        <label for="sharding-total-count" class="col-sm-6 control-label" data-lang="job-sharding-total-count"><i>*</i></label>
+                        <div class="col-sm-6">
+                            <input type="number" min=1 value=1 id="sharding-total-count" name="shardingTotalCount" class="form-control" data-toggle="tooltip" data-placement="bottom" title="作业分片总数"/>
+                        </div>
+                    </div>
+                </div>
+                <div class="col-sm-3">
+                    <div class="form-group">
+                        <label for="job-parameter" class="col-sm-6 control-label" data-lang="job-parameter"></label>
+                        <div class="col-sm-6">
+                            <input type="text" id="job-parameter" name="jobParameter" class="form-control" data-toggle="tooltip" data-placement="bottom" title="作业自定义参数,可通过传递该参数为作业调度的业务方法传参,用于实现带参数的作业例:每次获取的数据量、作业实例从数据库读取的主键。" />
+                        </div>
+                    </div>
+                </div>
+            </div>
+            <div class="row">
+                <div class="col-sm-4">
+                    <div class="form-group">
+                        <label for="cpu-count" class="col-sm-6 control-label" data-lang="job-cpu-count"><i>*</i></label>
+                        <div class="col-sm-6">
+                            <input type="number" value="0.001" min="0.001" step="0.001" id="cpu-count" name="cpuCount" class="form-control" data-toggle="tooltip"  data-placement="bottom" title="单片作业所需要的CPU数量,最小值为0.001"/>
+                        </div>
+                    </div>
+                </div>
+                <div class="col-sm-4">
+                    <div class="form-group">
+                        <label for="job-memory" class="col-sm-6 control-label" data-lang="job-memory"><i>*</i></label>
+                        <div class="col-sm-6">
+                            <input type="number" value="1" min="1" id="job-memory" name="jobMemory" class="form-control" data-toggle="tooltip" data-placement="bottom" title="单片作业所需要的内存MB,最小值为1"/>
+                        </div>
+                    </div>
+                </div>
+                <div class="col-sm-3">
+                    <div class="form-group">
+                        <label for="bean-name" class="col-sm-6 control-label" data-lang="job-bean-name"></label>
+                        <div class="col-sm-6">
+                            <input type="text" placeholder="yourBeanName" id="bean-name" name="beanName" class="form-control" data-toggle="tooltip" data-placement="bottom" title="Spring容器中配置的bean名称" />
+                        </div>
+                    </div>
+                </div>
+            </div>
+            <div class="row">
+                <div class="col-sm-4">
+                    <div class="form-group">
+                        <label for="failover" class="col-sm-6 control-label" data-lang="job-failover"></label>
+                        <div class="col-sm-6">
+                            <input type="checkbox" id="failover" name="failover" data-toggle="tooltip" data-placement="bottom" title="只有开启监控作业执行时状态的情况下才可以开启失效转移" />
+                        </div>
+                    </div>
+                </div>
+                <div class="col-sm-4">
+                    <div class="form-group">
+                        <label for="misfire" class="col-sm-6 control-label" data-lang="job-misfire"></label>
+                        <div class="col-sm-6">
+                            <input type="checkbox" id="misfire" name="misfire" data-toggle="tooltip" data-placement="bottom" title="是否开启任务错过重新执行" />
+                        </div>
+                    </div>
+                </div>
+                <div class="col-sm-4">
+                    <div class="form-group">
+                        <label for="streaming-process" hidden="hidden" id="streaming-process-box" class="col-sm-6 control-label" data-lang="job-streaming-process"></label>
+                        <div class="col-sm-6" >
+                            <input hidden="hidden" type="checkbox" id="streaming-process" name="streamingProcess" data-toggle="tooltip" data-placement="bottom" title="DATAFLOW类型作业,是否流式处理数据如果流式处理数据, 则fetchData不返回空结果将持续执行作业,如果非流式处理数据, 则处理数据完成后作业结束。" />
+                        </div>
+                    </div>
+                </div>
+            </div>
+            <div class="row">
+                <div class="form-group">
+                    <label for="application-context" class="col-sm-2 control-label" data-lang="job-application-context"></label>
+                    <div class="col-sm-9">
+                        <input type="text" placeholder="META-INF\applicationContext.xml" id="application-context" name="applicationContext" class="form-control" data-toggle="tooltip" data-placement="bottom" title="Spring方式配置Spring配置文件相对路径以及名称,如:META-INF\applicationContext.xml" />
+                    </div>
+                </div>
+            </div>
+            <div class="row">
+                <div class="form-group">
+                    <label for="sharding-item-parameters" class="col-sm-2 control-label" data-lang="job-sharding-item-parameters"></label>
+                    <div class="col-sm-9">
+                        <textarea id="sharding-item-parameters" placeholder="0=a,1=b,2=c" name="shardingItemParameters" class="form-control" data-toggle="tooltip" data-placement="bottom" title="分片序列号和参数用等号分隔,多个键值对用逗号分隔,类似map。分片序列号从0开始,不可大于或等于作业分片总数。如:0=a,1=b,2=c"></textarea>
+                    </div>
+                </div>
+            </div>
+            <div class="row">
+                <div class="form-group" id="bootstrap-script-div" hidden="hidden">
+                    <label for="script-command-line" class="col-sm-2 control-label" data-lang="job-script-command-line"><i>*</i></label>
+                    <div class="col-sm-9">
+                        <input type="text" id="script-command-line" name="scriptCommandLine" class="form-control" data-toggle="tooltip" data-placement="bottom" title="SCRIPT类型作业命令行执行脚本" />
+                    </div>
+                </div>
+            </div>
+            <div class="row">
+                <div class="form-group">
+                    <label for="description" class="col-sm-2 control-label" data-lang="job-description"></label>
+                    <div class="col-sm-9">
+                        <textarea id="description" name="description" class="form-control"></textarea>
+                    </div>
+                </div>
+            </div> 
+            <div class="form-group">
+                <div class="center-font">
+                    <button id="save-button" class="btn-xs btn-primary" type="submit" data-lang="operation-submit"></button>
+                </div>
+            </div>
+        </form>
+    </div>
+</div>
+<script src="lib/BootstrapValidator/js/bootstrapValidator.js"></script>
+<script src="lib/BootstrapValidator/js/bootstrapValidator_zh_CN.js"></script>
+<script src="js/job/job_common.js"></script>
+<script src="js/job/modify_job.js"></script>
diff --git a/elastic-job-cloud-scheduler/src/main/resources/console/i18n/message.properties b/elastic-job-cloud-scheduler/src/main/resources/console/i18n/message.properties
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/elastic-job-cloud-scheduler/src/main/resources/console/i18n/message.properties
diff --git a/elastic-job-cloud-scheduler/src/main/resources/console/i18n/message_en.properties b/elastic-job-cloud-scheduler/src/main/resources/console/i18n/message_en.properties
new file mode 100644
index 0000000..c9d2980
--- /dev/null
+++ b/elastic-job-cloud-scheduler/src/main/resources/console/i18n/message_en.properties
@@ -0,0 +1,173 @@
+sidebar-app=App
+sidebar-config=Config
+sidebar-job=Job
+sidebar-status=Status
+sidebar-job-history=Job history
+sidebar-job-dashboard=History dashboard
+sidebar-job-trace=Job trace
+sidebar-history-status=History status
+
+switch-language=Switch language
+language-zh=中文
+language-en=English
+
+dangdang=dangdang.com
+
+switch-theme-title=Switch theme
+switch-theme-blue=Blue
+switch-theme-white=White
+switch-theme-purple=Purple
+switch-theme-green=Green
+switch-theme-yellow=Yellow
+switch-theme-red=Red
+switch-theme-blue-light=Blue Light
+switch-theme-white-light=White Light
+switch-theme-purple-light=Purple Light
+switch-theme-green-light=Green Light
+switch-theme-yellow-light=Yellow Light
+switch-theme-red-light=Red Light
+
+app-name=App name
+app-bootstrap-script=Bootstrap script
+app-cpu-count=CPU count
+app-memory=Memory(MB)
+app-event-trace-sampling-count=Event trace sampling count
+app-cache-enable=App cache enable
+app-url=App URL
+app-detail=App detail
+update-app=Update app
+add-app=Add app
+app-name-not-null=App name cannot be null
+app-name-length-limit=App name length should less than 100 characters
+app-name-exists=App name has already existed
+app-bootstrap-script-not-null=App bootstrap script cannot be null
+app-cpu-count-not-null=App CPU count cannot be null
+app-cpu-count-regexp-limit=CPU count should be a positive number
+app-memory-not-null=Memory cannot be null
+event-trace-sampling-count-not-null=Event trace sampling count cannot be null
+app-url-not-null=App URL cannot be null
+app-name-unregistered=App name is unregistered
+app-disabled=The application for the job has failed and the operation failed
+
+add-job=Add job
+job-detail=Job detail
+update-job=Update job
+job-name=Job name
+job-execution-type=Job execution type
+job-class=Job class
+job-type=Job type
+job-cron=Crontab
+job-sharding-total-count=Sharding total count
+job-parameter=Job parameter
+job-cpu-count=CPU count
+job-memory=Memory
+job-bean-name=Bean name
+job-failover=Failover
+job-misfire=Misfire
+job-streaming-process=Streaming process
+job-application-context=Application context
+job-sharding-item-parameters=Sharding item parameters
+job-script-command-line=Script command line
+job-description=Description
+job-status=Job status
+job-sharding-item=Sharding item
+job-spring-type-settings=Job spring type settings
+job-application-context-info=Job application context info
+job-bean-name-info=Job bean name info
+job-class-not-null=Job class cannot be null
+job-class-regexp-limit=The job class can only use Numbers, letters, underscores (_) and dot (.).
+job-name-not-null=Job name cannot be null
+job-name-length-limit=Job name length should less than 100 characters
+job-name-exists=Job name has already existed
+job-cron-length-limit=Job crontab should less than 40 characters
+job-cron-not-null=Job crontab cannot be null
+job-cpu-count-not-null=CPU count cannot be null
+job-cpu-count-regexp-limit=CPU count should be a positive number
+job-memory-not-null=Memory cannot be null
+job-sharding-count-not-null=Sharding count cannot be null
+job-script-command-line-not-null=Script command line cannot be null
+job-sharding-item-parameters-regexp-limit=The sharding item split item format is incorrect, format: 0 = xx, 1 = yy
+
+tab-running-tasks=Running tasks
+tab-ready-jobs=Ready jobs
+tab-failover-tasks=Failover tasks
+
+task-id=Task ID
+task-name=Task name
+server-ip=Server IP
+remaining-execution-times=Remaining execution times
+original-task-id=Original task ID
+
+dashboard-succ-and-fail-count=Success and failure count
+dashboard-job-type=Job type
+dashboard-job-task-running-count=Job and task running count
+dashboard-current-jobs-count=Current jobs count
+job-info-for-one-minute=Job info for one minute
+job-info-for-one-hour=Job info for one hour
+job-info-for-one-week=Job info for one week
+job-success-count=Job success count
+job-failure-count=Job failure count
+task-running-count=Task running count
+job-running-count=Job running count
+highchart-tooltip-info1=Mouse drag can be scaled
+highchart-tooltip-info2=Gesture operations are scaled
+
+placeholder-bootstrap-script=bootstrap-script, eg: bin//start.sh
+placeholder-app-url=Path should be accessed through the network, eg: http://file_host:8080/your-job.tar.gz
+placeholder-job-app-name=The application name of the job must be registered in the application
+placeholder-job-class=The job class needs to implement the ElasticJob interface, which does not require configuration
+placeholder-cron=The cron expression for the job start time. eg: 0/5 * * *?
+placeholder-sharding-total-count=Job sharding total count
+placeholder-job-parameter=Job customization parameters, can by passing this parameter for job scheduling the business method of ginseng, eg: take parameters job every time for the amount of data read from the database and operation instance of the primary key.
+placeholder-cpu-count=The minimum number of CPUs required for a single operation is 0.001
+placeholder-job-memory=The memory MB required for a single piece of work is a minimum of 1
+placeholder-bean-name=The bean name configured in the Spring container
+placeholder-failover=The failure transfer can be turned on only if the monitoring job is executed
+placeholder-misfire=Whether the task is open misses the re-execution
+placeholder-streaming-process=DATAFLOW type operation, whether if flow processing data flow processing data, the fetchData returns an empty result will not perform the operation, if the flow processing data, processing data after the completion of the work end.
+placeholder-application-context=Spring configure the Spring configuration file relative path and name, eg: META-INF\\applicationContext.xml
+placeholder-sharding-item-parameters=Separate serial Numbers and parameters are separated by equals, and multiple key values are separated by commas, similar to map. The serial serial Numbers start from 0, not greater than or equal to the total number of assignments. eg: 0 = a, 1 = b, 2 = c
+placeholder-script-command-line=SCRIPT type job command line
+
+operation=Operation
+operation-add=Add
+operation-submit=Submit
+operation-confirm=Confirm
+operation-cancel=Cancel
+operation-delete=Delete
+operation-reset=Reset
+operation-reset-scaling=Reset scaling
+operation-enable=Enable
+operation-disable=Disable
+operation-detail=Detail
+operation-update=Update
+
+status=Status
+status-running=Running
+status-staging=Staging
+status-task-failed=Failed
+status-task-finished=Finished
+status-task-error=Error
+status-task-killed=Killed
+
+creation-start-time=Creation start time
+creation-end-time=Creation end time
+creation-time=Creation time
+start-time=Start time
+complete-time=Complete time
+failure-reason=Failure reason
+comments=Comments
+
+execute-result=Execute result
+execute-result-all=All
+execute-result-success=Success
+execute-result-failure=Failure
+execute-result-null=Null
+execute-source=Execute source
+execute-type=Execute type
+remaining-execute-times=Remaining execute times
+
+confirm-to-close=Are you sure to close it?
+confirm-to-delete=Do you want to delete the application and related jobs?
+operation-succeed=Operation complete successfully
+confirm-to-delete-job=Do you want to delete the job?
diff --git a/elastic-job-cloud-scheduler/src/main/resources/console/i18n/message_zh.properties b/elastic-job-cloud-scheduler/src/main/resources/console/i18n/message_zh.properties
new file mode 100644
index 0000000..5504958
--- /dev/null
+++ b/elastic-job-cloud-scheduler/src/main/resources/console/i18n/message_zh.properties
@@ -0,0 +1,173 @@
+sidebar-app=应用
+sidebar-config=配置
+sidebar-job=作业
+sidebar-status=状态
+sidebar-job-history=作业历史
+sidebar-job-dashboard=历史dashboard
+sidebar-job-trace=作业运行轨迹
+sidebar-history-status=作业运行状态
+
+switch-language=选择语言
+language-zh=中文
+language-en=English
+
+dangdang=当当网
+
+switch-theme-title=更改主题
+switch-theme-blue=蓝色
+switch-theme-white=白色
+switch-theme-purple=紫色
+switch-theme-green=绿色
+switch-theme-yellow=黄色
+switch-theme-red=红色
+switch-theme-blue-light=蓝色高亮
+switch-theme-white-light=白色高亮
+switch-theme-purple-light=紫色高亮
+switch-theme-green-light=绿色高亮
+switch-theme-yellow-light=黄色高亮
+switch-theme-red-light=红色高亮
+
+app-name=应用名称
+app-bootstrap-script=启动脚本
+app-cpu-count=CPU核数
+app-memory=占用内存(MB)
+app-event-trace-sampling-count=作业事件采样次数(仅Daemon)
+app-cache-enable=是否在本地缓存应用
+app-url=应用所在路径
+app-detail=应用详情
+update-app=修改应用
+add-app=添加应用
+app-name-not-null=应用名称不能为空
+app-name-length-limit=应用名称长度不能超过100字符大小
+app-name-exists=应用已经注册
+app-bootstrap-script-not-null=启动脚本不能为空
+app-cpu-count-not-null=CPU核数不能为空
+app-cpu-count-regexp-limit=CPU核数应为正数
+app-memory-not-null=单片内存不能为空
+event-trace-sampling-count-not-null=作业事件采样次数(Daemon)不能为空
+app-url-not-null=应用所在路径不能为空
+app-name-unregistered=应用未注册
+app-disabled=作业对应的应用已失效,操作失败
+
+add-job=添加作业
+job-detail=作业详情
+update-job=修改作业
+job-name=作业名称
+job-execution-type=执行类型
+job-type=作业类型
+job-class=作业实现类
+job-cron=Cron表达式
+job-sharding-total-count=作业分片总数
+job-parameter=自定义参数
+job-cpu-count=CPU核数
+job-memory=单片作业内存(MB)
+job-bean-name=实体名称
+job-failover=支持自动失效转移
+job-misfire=支持错过重执行
+job-streaming-process=是否流式处理数据
+job-application-context=Spring配置文件相对路径及名称
+job-sharding-item-parameters=分片序列号/参数对照表
+job-script-command-line=脚本作业全路径
+job-description=作业描述信息
+job-status=作业状态
+job-sharding-item=分片项
+job-spring-type-settings=Spring方式配置
+job-application-context-info=请填写Spring配置文件相对路径及名称
+job-bean-name-info=请填写beanName名称
+job-class-not-null=作业实现类不能为空
+job-class-regexp-limit=作业实现类只能使用数字、字母、下划线(_)和点号(.)
+job-name-not-null=作业名称不能为空
+job-name-length-limit=作业名称长度不能超过100字符大小
+job-name-exists=作业名称已经注册
+job-cron-length-limit=cron表达式不能超过40字符大小
+job-cron-not-null=cron表达式不能为空
+job-cpu-count-not-null=CPU核数不能为空
+job-cpu-count-regexp-limit=CPU核数只能包含数字和小数点
+job-memory-not-null=单片作业内存不能为空
+job-sharding-count-not-null=分片数量不能为空
+job-script-command-line-not-null=SCRIPT类型作业命令行执行脚本不能为空
+job-sharding-item-parameters-regexp-limit=作业分片项格式不正确, 格式: 0=xx,1=yy
+
+tab-running-tasks=运行任务
+tab-ready-jobs=待运行作业
+tab-failover-tasks=待失效转移任务
+
+task-id=任务主键
+task-name=任务名称
+server-ip=服务器IP
+remaining-execution-times=剩余执行次数
+original-task-id=原任务主键
+
+dashboard-succ-and-fail-count=作业成功/失败数
+dashboard-job-type=作业分类
+dashboard-job-task-running-count=作业/任务运行数
+dashboard-current-jobs-count=接入平台作业数
+job-info-for-one-minute=一分钟作业情况
+job-info-for-one-hour=一小时作业情况
+job-info-for-one-week=一周作业情况
+job-success-count=作业成功数
+job-failure-count=作业失败数
+task-running-count=任务运行数
+job-running-count=作业运行数
+highchart-tooltip-info1=鼠标拖动可以进行缩放
+highchart-tooltip-info2=手势操作进行缩放
+
+placeholder-bootstrap-script=启动脚本,如:bin\\start.sh。
+placeholder-app-url=必须是可以通过网络访问到的路径。如:http://file_host:8080/your-job.tar.gz
+placeholder-job-app-name=作业所在的应用名称,必须是在应用中已注册。
+placeholder-job-class=作业实现类,需实现ElasticJob接口,脚本型作业不需要配置
+placeholder-cron=作业启动时间的cron表达式。如:0/5 * * * * ?
+placeholder-sharding-total-count=作业分片总数
+placeholder-job-parameter=作业自定义参数,可通过传递该参数为作业调度的业务方法传参,用于实现带参数的作业例:每次获取的数据量、作业实例从数据库读取的主键。
+placeholder-cpu-count=单片作业所需要的CPU核数,最小值为0.001
+placeholder-job-memory=单片作业所需要的内存MB,最小值为1
+placeholder-bean-name=Spring容器中配置的bean名称
+placeholder-failover=只有开启监控作业执行时状态的情况下才可以开启失效转移
+placeholder-misfire=是否开启任务错过重新执行
+placeholder-streaming-process=DATAFLOW类型作业,是否流式处理数据如果流式处理数据, 则fetchData不返回空结果将持续执行作业,如果非流式处理数据, 则处理数据完成后作业结束。
+placeholder-application-context=Spring方式配置Spring配置文件相对路径以及名称,如:META-INF\\applicationContext.xml
+placeholder-sharding-item-parameters=分片序列号和参数用等号分隔,多个键值对用逗号分隔,类似map。分片序列号从0开始,不可大于或等于作业分片总数。如:0=a,1=b,2=c
+placeholder-script-command-line=SCRIPT类型作业命令行执行脚本
+
+operation=操作
+operation-add=添加
+operation-submit=提交
+operation-confirm=确认
+operation-cancel=关闭
+operation-delete=删除
+operation-reset=重置
+operation-reset-scaling=重置缩放比例
+operation-enable=生效
+operation-disable=失效
+operation-detail=详情
+operation-update=修改
+
+status=状态
+status-running=运行中
+status-staging=等待运行
+status-task-failed=运行失败
+status-task-finished=已完成
+status-task-error=启动失败
+status-task-killed=主动终止
+
+creation-start-time=创建开始时间
+creation-end-time=创建结束时间
+creation-time=创建时间
+start-time=开始时间
+complete-time=完成时间
+failure-reason=失败原因
+comments=备注
+
+execute-result=执行结果
+execute-result-all=全部
+execute-result-success=成功
+execute-result-failure=失败
+execute-result-null=空
+execute-source=执行来源
+execute-type=执行类型
+remaining-execute-times=剩余执行次数
+
+confirm-to-close=确认要关闭吗?
+confirm-to-delete=确认要删除该应用及其相关联的作业吗?
+operation-succeed=操作已成功完成
+confirm-to-delete-job=确认要删除该作业吗?
diff --git a/elastic-job-cloud-scheduler/src/main/resources/console/index.html b/elastic-job-cloud-scheduler/src/main/resources/console/index.html
new file mode 100644
index 0000000..c45b102
--- /dev/null
+++ b/elastic-job-cloud-scheduler/src/main/resources/console/index.html
@@ -0,0 +1,284 @@
+<!DOCTYPE html>
+<html>
+    <head>
+        <meta charset="utf-8">
+        <meta http-equiv="X-UA-Compatible" content="IE=edge">
+        <title>Elastic Job Cloud Console</title>
+        <meta content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no" name="viewport">
+        <link rel="stylesheet" href="lib/bootstrap/css/bootstrap.min.css">
+        <link rel="stylesheet" href="lib/font-awesome-4.5.0/css/font-awesome.min.css">
+        <link rel="stylesheet" href="lib/AdminLTE/css/AdminLTE.min.css">
+        <link rel="stylesheet" href="lib/AdminLTE/css/skins/_all-skins.min.css">
+        <link rel="stylesheet" href="css/common.css">
+        <link rel="stylesheet" href="lib/bootstrap-table/bootstrap-table.min.css">
+        <link rel="stylesheet" href="lib/daterangepicker/daterangepicker.css">
+        <script src="lib/jQuery/jQuery-2.1.4.min.js"></script>
+        <script src="lib/jQuery/jquery.i18n.properties-min.js"></script>
+        <script src="lib/bootstrap/js/bootstrap.min.js"></script>
+        <script src="lib/AdminLTE/js/app.min.js"></script>
+        <script src="lib/highcharts/js/highcharts.js"></script>
+        <script src="js/common/common.js"></script>
+        <script src="js/index.js"></script>
+    </head>
+    <body class="hold-transition skin-blue-light sidebar-mini">
+        <div class="wrapper">
+            <header class="main-header">
+                <a class="logo">
+                    <span class="logo-lg"><span id="logo-font-size">Elastic Job Cloud v2.1.6</span></span>
+                </a>
+                <nav class="navbar navbar-static-top" role="navigation">
+                    <div id="navbar" class="navbar-custom-menu">
+                        <ul class="nav navbar-nav">
+                            <li class="dropdown">
+                                <a href="#" class="dropdown-toggle">
+                                    <i class="fa fa-user"></i>
+                                    <span id="authority"></span>
+                                </a>
+                            </li>
+                            <li><a href="#" data-toggle="control-sidebar"><i class="fa fa-flag"></i></a></li>
+                            <li class="dropdown">
+                                <a href="#" class="dropdown-toggle" data-toggle="dropdown" data-lang="switch-language"></a>
+                                <ul class="dropdown-menu">
+                                    <li><a href="#" id="lang-zh" data-lang="language-zh"></a></li>
+                                    <li><a href="#" id="lang-en" data-lang="language-en"></a></li>
+                                </ul>
+                            </li>
+                        </ul>
+                    </div>
+                </nav>
+            </header>
+            <aside class="main-sidebar">
+                <section class="sidebar">
+                    <ul class="sidebar-menu">
+                        <li class="treeview" id="app">
+                            <a href="#">
+                                <i class="fa fa-laptop" data-lang="sidebar-app"></i><i class="pull-right fa fa-angle-left "></i>
+                                <div class="pull-right">
+                                    <span id="app-nav-tag" class="label label-primary"></span>
+                                </div>
+                            </a>
+                            <ul class="treeview-menu">
+                                <li><a href="#" id="register-app" class="sub-menu"><i class="fa fa-circle-o" data-lang="sidebar-config"></i></a></li>
+                            </ul>
+                        </li>
+                        <li class="treeview" id="job">
+                            <a href="#">
+                                <i class="fa fa-tasks" data-lang="sidebar-job"></i><i class="pull-right fa fa-angle-left"></i>
+                                <div class="pull-right">
+                                    <span id="job-nav-tag" class="label label-primary"></span>
+                                </div>
+                            </a>
+                            <ul class="treeview-menu">
+                                <li><a href="#" id="register-job" class="sub-menu"><i class="fa fa-circle-o" data-lang="sidebar-config"></i></a></li>
+                                <li><a href="#" id="status" class="sub-menu"><i class="fa fa-circle-o" data-lang="sidebar-status"></i></a></li>
+                            </ul>
+                        </li>
+                        <li class="treeview" id="history">
+                            <a href="#">
+                                <i class="fa fa-history" data-lang="sidebar-job-history"></i><i class="pull-right fa fa-angle-left "></i>
+                            </a>
+                            <ul class="treeview-menu">
+                                <li><a href="#" id="dashboard" class ="sub-menu"><i class="fa fa-circle-o" data-lang="sidebar-job-dashboard"></i></a></li>
+                                <li><a href="#" id="exec-details" class ="sub-menu"><i class="fa fa-circle-o" data-lang="sidebar-job-trace"></i></a></li>
+                                <li><a href="#" id="exec-status" class ="sub-menu"><i class="fa fa-circle-o" data-lang="sidebar-history-status"></i></a></li>
+                           </ul>
+                        </li>
+                    </ul>
+            </section>
+            </aside>
+            <div id="content-right" class="lang-en"></div>
+            <footer class="main-footer">
+                    <strong>Copyright &copy; 2004-2017 <a href="http://www.dangdang.com" data-lang="dangdang"></a>.</strong> All rights reserved.
+            </footer>
+            <aside class="control-sidebar control-sidebar-dark">
+                <div class="tab-content">
+                    <div id="control-sidebar-theme-demo-options-tab" class="tab-pane active">
+                        <h4 class="control-sidebar-heading" data-lang="switch-theme-title"></h4>
+                        <ul class="list-unstyled clearfix">
+                            <li>
+                                <a href="javascript:void(0);" data-skin="skin-blue" class="clearfix full-opacity-hover">
+                                <div>
+                                    <span class="bg-blue top-span"></span>
+                                </div>
+                                <div>
+                                    <span class="down-span-left"></span>
+                                    <span class="down-span-right"></span>
+                                </div>
+                                </a>
+                                <p class="text-center no-margin" data-lang="switch-theme-blue"></p>
+                            </li>
+                            <li>
+                                <a href="javascript:void(0);" data-skin="skin-black" class="clearfix full-opacity-hover">
+                                <div>
+                                    <span class="top-span"></span>
+                                </div>
+                                <div>
+                                    <span class="down-span-left"></span>
+                                    <span class="down-span-right"></span>
+                                </div>
+                                </a>
+                                <p class="text-center no-margin" data-lang="switch-theme-white"></p>
+                            </li>
+                            <li>
+                                <a href="javascript:void(0);" data-skin="skin-purple" class="clearfix full-opacity-hover">
+                                <div>
+                                    <span class="bg-purple top-span"></span>
+                                </div>
+                                <div>
+                                    <span class="down-span-left"></span>
+                                    <span class="down-span-right"></span>
+                                </div>
+                                </a>
+                                <p class="text-center no-margin" data-lang="switch-theme-purple"></p>
+                            </li>
+                            <li>
+                                <a href="javascript:void(0);" data-skin="skin-green" class="clearfix full-opacity-hover">
+                                <div>
+                                    <span class="bg-green top-span"></span>
+                                </div>
+                                <div>
+                                    <span class="down-span-left"></span>
+                                    <span class="down-span-right"></span>
+                                </div>
+                                </a>
+                                <p class="text-center no-margin" data-lang="switch-theme-green"></p>
+                            </li>
+                            <li>
+                                <a href="javascript:void(0);" data-skin="skin-yellow" class="clearfix full-opacity-hover">
+                                <div>
+                                    <span class="bg-yellow top-span"></span>
+                                </div>
+                                <div>
+                                    <span class="down-span-left"></span>
+                                    <span class="down-span-right"></span>
+                                </div>
+                                </a>
+                                <p class="text-center no-margin" data-lang="switch-theme-yellow"></p>
+                            </li>
+                            <li>
+                                <a href="javascript:void(0);" data-skin="skin-red" class="clearfix full-opacity-hover">
+                                <div>
+                                    <span class="bg-red top-span"></span>
+                                </div>
+                                <div>
+                                    <span class="down-span-left"></span>
+                                    <span class="down-span-right"></span>
+                                </div>
+                                </a>
+                                <p class="text-center no-margin" data-lang="switch-theme-red"></p>
+                            </li>
+                            <li>
+                                <a href="javascript:void(0);" data-skin="skin-blue-light" class="clearfix full-opacity-hover">
+                                <div>
+                                    <span class="bg-light-blue top-span"></span>
+                                </div>
+                                <div>
+                                    <span class="down-span-left"></span>
+                                    <span class="down-span-right"></span>
+                                </div>
+                                </a>
+                                <p class="text-center no-margin" data-lang="switch-theme-blue-light"></p>
+                            </li>
+                            <li>
+                                <a href="javascript:void(0);" data-skin="skin-black-light" class="clearfix full-opacity-hover">
+                                <div>
+                                    <span class="top-span"></span>
+                                </div>
+                                <div>
+                                    <span class="down-span-left"></span>
+                                    <span class="down-span-right"></span>
+                                </div>
+                                </a>
+                                <p class="text-center no-margin" data-lang="switch-theme-white-light"></p>
+                            </li>
+                            <li>
+                                <a href="javascript:void(0);" data-skin="skin-purple-light" class="clearfix full-opacity-hover">
+                                <div>
+                                    <span class="bg-purple top-span"></span>
+                                </div>
+                                <div>
+                                    <span class="down-span-left"></span>
+                                    <span class="down-span-right"></span>
+                                </div>
+                                </a>
+                                <p class="text-center no-margin" data-lang="switch-theme-purple-light"></p>
+                            </li>
+                            <li>
+                                <a href="javascript:void(0);" data-skin="skin-green-light" class="clearfix full-opacity-hover">
+                                <div>
+                                    <span class="bg-green top-span"></span>
+                                </div>
+                                <div>
+                                    <span class="down-span-left"></span>
+                                    <span class="down-span-right"></span>
+                                </div>
+                                </a>
+                                <p class="text-center no-margin" data-lang="switch-theme-green-light"></p>
+                            </li>
+                            <li>
+                                <a href="javascript:void(0);" data-skin="skin-yellow-light" class="clearfix full-opacity-hover">
+                                <div>
+                                    <span class="bg-yellow top-span"></span>
+                                </div>
+                                <div>
+                                    <span class="down-span-left"></span>
+                                    <span class="down-span-right"></span>
+                                </div>
+                                </a>
+                                <p class="text-center no-margin" data-lang="switch-theme-yellow-light"></p>
+                            </li>
+                            <li>
+                                <a href="javascript:void(0);" data-skin="skin-red-light" class="clearfix full-opacity-hover">
+                                <div>
+                                    <span class="bg-red top-span"></span>
+                                </div>
+                                <div>
+                                    <span class="down-span-left"></span>
+                                    <span class="down-span-right"></span>
+                                </div>
+                                </a>
+                                <p class="text-center no-margin" data-lang="switch-theme-red-light"></p>
+                            </li>
+                        </ul>
+                    </div>
+                </div>
+            </aside>
+            <div id="success-dialog" class="modal">
+                <div class="modal-dialog">
+                    <div class="modal-content">
+                        <div class="modal-body">
+                            <p class="size-font" data-lang="operation-succeed"></p>
+                        </div>
+                        <div class="modal-footer">
+                            <button type="button" class="btn-xs btn-success" data-dismiss="modal" data-lang="operation-cancel"></button>
+                        </div>
+                    </div>
+                </div>
+            </div>
+            <div id="history-message-modal" class="modal">
+                <div class="modal-dialog" id="modal-dialog-width">
+                    <div class="modal-content">
+                        <div class="modal-body">
+                            <h5 id="history-message"></h5>
+                        </div>
+                        <div class="modal-footer">
+                            <button type="button" class="btn-xs btn-danger" data-dismiss="modal" data-lang="operation-cancel"></button>
+                        </div>
+                    </div>
+                </div>
+            </div>
+            <div id="fail-dialog" class="modal">
+                <div class="modal-dialog">
+                    <div class="modal-content">
+                        <div class="modal-body">
+                            <p class="size-font" data-lang="app-disabled"></p>
+                        </div>
+                        <div class="modal-footer">
+                            <button type="button" class="btn-xs btn-success" data-dismiss="modal" data-lang="operation-cancel"></button>
+                        </div>
+                    </div>
+                </div>
+            </div>
+        </div>
+    </body>
+</html>
diff --git a/elastic-job-cloud-scheduler/src/main/resources/console/js/app/add_app.js b/elastic-job-cloud-scheduler/src/main/resources/console/js/app/add_app.js
new file mode 100644
index 0000000..03cd792
--- /dev/null
+++ b/elastic-job-cloud-scheduler/src/main/resources/console/js/app/add_app.js
@@ -0,0 +1,4 @@
+$(function() {
+    validate();
+    submitConfirm("post", $("#data-add-app"));
+});
diff --git a/elastic-job-cloud-scheduler/src/main/resources/console/js/app/app_common.js b/elastic-job-cloud-scheduler/src/main/resources/console/js/app/app_common.js
new file mode 100644
index 0000000..a0d9937
--- /dev/null
+++ b/elastic-job-cloud-scheduler/src/main/resources/console/js/app/app_common.js
@@ -0,0 +1,115 @@
+function validate() {
+    $("#app-form").bootstrapValidator({
+        message: "This value is not valid",
+        feedbackIcons: {
+            valid: "glyphicon glyphicon-ok",
+            invalid: "glyphicon glyphicon-remove",
+            validating: "glyphicon glyphicon-refresh"
+        },
+        fields: {
+            appName: {
+                validators: {
+                    notEmpty: {
+                        message: $.i18n.prop("app-name-not-null")
+                    },
+                    stringLength: {
+                        max: 100,
+                        message: $.i18n.prop("app-name-length-limit")
+                    },
+                    callback: {
+                        message: $.i18n.prop("app-name-exists"),
+                        callback: function() {
+                            var appName = $("#app-name").val();
+                            var result = true;
+                                $.ajax({
+                                    url: "/api/app/" + appName,
+                                    contentType: "application/json",
+                                    async: false,
+                                    success: function(data) {
+                                        if (null !== data) {
+                                            result = false;
+                                        }
+                                    }
+                                });
+                            return result;
+                        }
+                    }
+                }
+            },
+            bootstrapScript: {
+                validators: {
+                    notEmpty: {
+                        message: $.i18n.prop("app-bootstrap-script-not-null")
+                    }
+                }
+            },
+            cpuCount: {
+                validators: {
+                    notEmpty: {
+                        message: $.i18n.prop("app-cpu-count-not-null")
+                    },
+                    regexp: {
+                        regexp: /^(-?\d+)(\.\d+)?$/,
+                        message: $.i18n.prop("app-cpu-count-regexp-limit")
+                    }
+                }
+            },
+            appMemory: {
+                validators: {
+                    notEmpty: {
+                        message: $.i18n.prop("app-memory-not-null")
+                    }
+                }
+            },
+            eventTraceSamplingCount: {
+                validators: {
+                    notEmpty: {
+                        message: $.i18n.prop("event-trace-sampling-count-not-null")
+                    }
+                }
+            },
+            appURL: {
+                validators: {
+                    notEmpty: {
+                        message: $.i18n.prop("app-url-not-null")
+                    }
+                }
+            }
+        }
+    });
+}
+
+function submitConfirm(type, modal) {
+    $("#save-button").on("click", function() {
+        var bootstrapValidator = $("#app-form").data("bootstrapValidator");
+        bootstrapValidator.validate();
+        if(bootstrapValidator.isValid()) {
+            $.ajax({
+                type: type,
+                dataType: "json",
+                data: JSON.stringify(getApp()),
+                url: "/api/app",
+                contentType: "application/json",
+                success: function(data) {
+                    modal.modal("hide");
+                    $("#app-table").bootstrapTable("refresh");
+                    $(".modal-backdrop").remove();
+                    $("body").removeClass("modal-open");
+                    refreshAppNavTag();
+                }
+            });
+        }
+    });
+}
+
+function getApp() {
+    return {
+        appName: $("#app-name").val(),
+        cpuCount: $("#cpu-count").val(),
+        memoryMB: $("#app-memory").val(),
+        bootstrapScript: $("#bootstrap-script").val(),
+        appCacheEnable: $("#app-cache-enable").prop("checked"),
+        appURL: $("#app-url").val(),
+        eventTraceSamplingCount: $("#event-trace-sampling-count").val()
+    };
+}
diff --git a/elastic-job-cloud-scheduler/src/main/resources/console/js/app/apps_overview.js b/elastic-job-cloud-scheduler/src/main/resources/console/js/app/apps_overview.js
new file mode 100644
index 0000000..0c9f2ad
--- /dev/null
+++ b/elastic-job-cloud-scheduler/src/main/resources/console/js/app/apps_overview.js
@@ -0,0 +1,161 @@
+$(function() {
+    authorityControl();
+    renderAppOverview();
+    $("#add-app").click(function() {
+        $(".box-body").remove();
+        $("#add-app-body").load("html/app/add_app.html", null, function() {
+            doLocale();
+            tooltipLocale();
+        });
+        $("#data-add-app").modal({backdrop: "static", keyboard: true});
+    });
+    bindDetailAppButton();
+    bindModifyAppButton();
+    bindEnableAppButton();
+    bindDisableAppButton();
+    bindDeleteAppButton();
+});
+
+function renderAppOverview() {
+    var jsonData = {
+        url: "/api/app/list",
+        cache: false
+    };
+    $("#app-table").bootstrapTable({
+        columns: jsonData.columns,
+        url: jsonData.url,
+        cache: jsonData.cache
+    }).on("all.bs.table", function() {
+        doLocale();
+    });
+}
+
+function operationApp(val, row) {
+    var detailButton = "<button operation='detailApp' class='btn-xs btn-info' appName='" + row.appName + "' data-lang='operation-detail'></button>";
+    var modifyButton = "<button operation='modifyApp' class='btn-xs btn-warning' appName='" + row.appName + "' data-lang='operation-update'></button>";
+    var deleteButton = "<button operation='deleteApp' class='btn-xs btn-danger' appName='" + row.appName + "' data-lang='operation-delete'></button>";
+    var enableButton = "<button operation='enableApp' class='btn-xs btn-success' appName='" + row.appName + "' data-lang='operation-enable'></button>";
+    var disableButton = "<button operation='disableApp' class='btn-xs btn-warning' appName='" + row.appName + "' data-lang='operation-disable'></button>";
+    var operationId = detailButton + "&nbsp;" + modifyButton  +"&nbsp;" + deleteButton;
+    if(selectAppStatus(row.appName)) {
+        operationId = operationId + "&nbsp;" + enableButton;
+    } else {
+        operationId = operationId + "&nbsp;" + disableButton;
+    }
+    return operationId;
+}
+
+function bindDetailAppButton() {
+    $(document).off("click", "button[operation='detailApp'][data-toggle!='modal']");
+    $(document).on("click", "button[operation='detailApp'][data-toggle!='modal']", function(event) {
+        var appName = $(event.currentTarget).attr("appName");
+        $.ajax({
+            url: "/api/app/" + appName,
+            contentType: "application/json",
+            success: function(result) {
+                if (null !== result) {
+                    $(".box-body").remove();
+                    $("#detail-app-body").load("html/app/detail_app.html", null, function() {
+                        doLocale();
+                        tooltipLocale();
+                        renderApp(result);
+                        $("#data-detail-app").modal({backdrop : "static", keyboard : true});
+                        $("#close-button").on("click", function() {
+                            $("#data-detail-app").modal("hide");
+                        });
+                    });
+                }
+            }
+        });
+    });
+}
+
+function bindModifyAppButton() {
+    $(document).off("click", "button[operation='modifyApp'][data-toggle!='modal']");
+    $(document).on("click", "button[operation='modifyApp'][data-toggle!='modal']", function(event) {
+        var appName = $(event.currentTarget).attr("appName");
+        $.ajax({
+            url: "/api/app/" + appName,
+            success: function(result) {
+                if(null !== result) {
+                    $(".box-body").remove();
+                    $("#update-app-body").load("html/app/modify_app.html", null, function() {
+                        doLocale();
+                        tooltipLocale();
+                        renderApp(result);
+                        $("#data-update-app").modal({backdrop : "static", keyboard : true});
+                    });
+                }
+            }
+        });
+    });
+}
+
+function bindEnableAppButton() {
+    $(document).off("click", "button[operation='enableApp'][data-toggle!='modal']");
+    $(document).on("click", "button[operation='enableApp'][data-toggle!='modal']", function(event) {
+        var appName = $(event.currentTarget).attr("appName");
+        $.ajax({
+            url: "/api/app/" + appName + "/disable",
+            type: "DELETE",
+            contentType: "application/json",
+            success: function(result) {
+                showSuccessDialog();
+                $("#app-table").bootstrapTable("refresh");
+            }
+        });
+    });
+}
+
+function bindDisableAppButton() {
+    $(document).off("click", "button[operation='disableApp'][data-toggle!='modal']");
+    $(document).on("click", "button[operation='disableApp'][data-toggle!='modal']", function(event) {
+        var appName = $(event.currentTarget).attr("appName");
+        $.ajax({
+            url: "/api/app/" + appName + "/disable",
+            type: "POST",
+            contentType: "application/json",
+            success: function(result) {
+                showSuccessDialog();
+                $("#app-table").bootstrapTable("refresh");
+            }
+        });
+    });
+}
+
+function bindDeleteAppButton() {
+    $(document).off("click", "button[operation='deleteApp'][data-toggle!='modal']");
+    $(document).on("click", "button[operation='deleteApp'][data-toggle!='modal']", function(event) {
+        var appName = $(event.currentTarget).attr("appName");
+        $("#delete-data-app").modal({backdrop : "static", keyboard : true});
+        var flag = true;
+        $("#delete-app-remove").on("click", function() {
+            flag = false;
+        });
+        $("#delete-app-confirm").on("click", function() {
+            if(flag) {
+                $.ajax({
+                    url: "/api/app/" + appName,
+                    type: "DELETE",
+                    contentType: "application/json",
+                    success: function(result) {
+                        $("#app-table").bootstrapTable("refresh");
+                        $("#delete-data-app").hide();
+                        refreshAppNavTag();
+                        refreshJobNavTag();
+                    }
+                });
+            }
+        });
+    });
+}
+
+function renderApp(app) {
+    $("#app-name").attr("value", app.appName);
+    $("#cpu-count").attr("value", app.cpuCount); 
+    $("#app-memory").attr("value", app.memoryMB);
+    $("#bootstrap-script").attr("value", app.bootstrapScript);
+    $("#app-url").attr("value", app.appURL);
+    $("#event-trace-sampling-count").val(app.eventTraceSamplingCount);
+    $("#app-cache-enable").prop("checked", app.appCacheEnable);
+}
diff --git a/elastic-job-cloud-scheduler/src/main/resources/console/js/app/modify_app.js b/elastic-job-cloud-scheduler/src/main/resources/console/js/app/modify_app.js
new file mode 100644
index 0000000..01f53a4
--- /dev/null
+++ b/elastic-job-cloud-scheduler/src/main/resources/console/js/app/modify_app.js
@@ -0,0 +1,4 @@
+$(function() {
+    validate();
+    submitConfirm("put", $("#data-update-app"));
+});
diff --git a/elastic-job-cloud-scheduler/src/main/resources/console/js/common/common.js b/elastic-job-cloud-scheduler/src/main/resources/console/js/common/common.js
new file mode 100644
index 0000000..18e0ae3
--- /dev/null
+++ b/elastic-job-cloud-scheduler/src/main/resources/console/js/common/common.js
@@ -0,0 +1,144 @@
+$(function() {
+    renderSkin();
+    controlSubMenuStyle();
+});
+
+function showSuccessDialog() {
+    $("#success-dialog").modal("show");
+    setTimeout('$("#success-dialog").modal("hide")', 2000);
+}
+
+function showFailDialog() {
+    $("#fail-dialog").modal("show");
+    setTimeout('$("#fail-dialog").modal("hide")', 2000);
+}
+
+function refreshJobNavTag() {
+    $.ajax({
+        url: "/api/job/jobs",
+        cache: false,
+        success: function(data) {
+            $("#job-nav-tag").text(data.length);
+        }
+    });
+}
+
+function refreshAppNavTag() {
+    $.ajax({
+        url: "/api/app/list",
+        cache: false,
+        success: function(data) {
+            $("#app-nav-tag").text(data.length);
+        }
+    });
+}
+
+var my_skins = [
+    "skin-blue",
+    "skin-black",
+    "skin-red",
+    "skin-yellow",
+    "skin-purple",
+    "skin-green",
+    "skin-blue-light",
+    "skin-black-light",
+    "skin-red-light",
+    "skin-yellow-light",
+    "skin-purple-light",
+    "skin-green-light"
+];
+
+function renderSkin() {
+    $("[data-skin]").on("click", function(event) {
+        event.preventDefault();
+        changeSkin($(this).data("skin"));
+    });
+}
+
+function changeSkin(skinClass) {
+    $.each(my_skins, function(index) {
+        $("body").removeClass(my_skins[index]);
+    });
+    $("body").addClass(skinClass);
+}
+
+function controlSubMenuStyle() {
+    $(".sub-menu").click(function() {
+        $(this).parent().parent().children().removeClass("active");
+        $(this).parent().addClass("active");
+    });
+}
+
+function selectAppStatus(appName) {
+    var resultValue = null;
+    $.ajax({
+        type: "GET",
+        async: false,
+        url: "/api/app/" + appName + "/disable",
+        contentType: "application/json",
+        success: function(result) {
+            resultValue = result;
+        }
+    });
+    return resultValue;
+}
+
+function authorityControl() {
+    $.ajax({
+        type: "HEAD",
+        url : "/",
+        complete: function(xhr, data) {
+            if ("guest" === xhr.getResponseHeader("identify")) {
+                $("table").on("all.bs.table", function() {
+                    $(".content-wrapper .btn-xs").not(".btn-info").attr("disabled", true);
+                    $(".content-wrapper .btn-xs").not(".btn-info").removeClass().addClass("btn-xs");
+                });
+            }
+            if ("" === $("#authority").text()) {
+                $("#authority").text(xhr.getResponseHeader("identify"));
+            }
+        }
+    });
+}
+
+function i18n(lang) {
+    jQuery.i18n.properties({
+        name : 'message',
+        path : '/i18n/',
+        mode : 'map',
+        language : lang,
+        cache: true,
+        encoding: 'UTF-8',
+        callback : function() {
+            for (var i in $.i18n.map) {
+                $('[data-lang="'+i+'"]').html($.i18n.prop(i));
+            }
+        }
+    });
+}
+
+function doLocale() {
+    if ($("#content-right").hasClass("lang-en")) {
+        i18n("en");
+    } else {
+        i18n("zh");
+    }
+}
+
+function switchLanguage() {
+    $("#lang-zh").click(function() {
+        $("#content-right").removeClass("lang-en").addClass("lang-zh");
+        doLocale();
+    });
+    $("#lang-en").click(function() {
+        $("#content-right").removeClass("lang-zh").addClass("lang-en");
+        doLocale();
+    });
+}
+
+function tooltipLocale(){
+    for (var i = 0; i < $("[data-toggle='tooltip']").length; i++) {
+        var object = $("[data-toggle='tooltip']")[i];
+        $(object).attr('title',$.i18n.prop("placeholder-" + object.getAttribute("id"))).tooltip('fixTitle');
+    }
+}
diff --git a/elastic-job-cloud-scheduler/src/main/resources/console/js/history/history_common.js b/elastic-job-cloud-scheduler/src/main/resources/console/js/history/history_common.js
new file mode 100644
index 0000000..b8d87b4
--- /dev/null
+++ b/elastic-job-cloud-scheduler/src/main/resources/console/js/history/history_common.js
@@ -0,0 +1,42 @@
+$(function() {
+    $customDatepicker = $(".custom-datepicker");
+    $customDatepicker.daterangepicker({singleDatePicker : true, timePicker : true, timePicker24Hour : true, timePickerSeconds : true, autoUpdateInput : false});
+    $customDatepicker.on("apply.daterangepicker", function(event, picker) {
+        $(this).val(picker.startDate.format("YYYY-MM-DD HH:mm:ss"));
+    });
+    $customDatepicker.on("cancel.daterangepicker", function(event, picker) {
+        $(this).val("");
+    });
+});
+
+Date.prototype.format = function(fmt) {
+    var date = {
+    "M+" : this.getMonth() + 1,
+    "d+" : this.getDate(),
+    "h+" : this.getHours() % 12 == 0 ? 12 : this.getHours() % 12,
+    "H+" : this.getHours(),
+    "m+" : this.getMinutes(),
+    "s+" : this.getSeconds()
+    };
+    if(/(y+)/.test(fmt)) {
+        fmt=fmt.replace(RegExp.$1, (this.getFullYear() + "").substr(4 - RegExp.$1.length));
+    }
+    for(var each in date) {
+        if(new RegExp("(" + each + ")").test(fmt)) {
+            fmt = fmt.replace(RegExp.$1, (RegExp.$1.length == 1) ? (date[each]) : (("00" + date[each]).substr(("" + date[each]).length)));
+        }
+    }
+    return fmt;
+};
+
+function dateTimeFormatter(value) {
+    if(null == value){
+        return "";
+    }
+    return new Date(value).format("yyyy-MM-dd HH:mm:ss");
+}
+
+function showHistoryMessage(value) {
+    $("#history-message").html(value);
+    $("#history-message-modal").modal("show");
+}
diff --git a/elastic-job-cloud-scheduler/src/main/resources/console/js/history/job_dashboard.js b/elastic-job-cloud-scheduler/src/main/resources/console/js/history/job_dashboard.js
new file mode 100644
index 0000000..0e5fb7d
--- /dev/null
+++ b/elastic-job-cloud-scheduler/src/main/resources/console/js/history/job_dashboard.js
@@ -0,0 +1,274 @@
+$(function() {
+    doLocale();
+    renderPieChartSinceLastMinuteData();
+    renderPieChartSinceLastHourData();
+    renderPieChartSinceLastWeekData();
+    renderJobTypePieChart();
+    renderJobExecutionTypePieChart();
+    renderStaticsJobsLineChart();
+    renderRunningJobsAndTasksLineChart();
+    renderRegisteredJobs();
+});
+
+function doLocale() {
+    if ($("#content-right").hasClass("lang-en")) {
+        i18n("en");
+    } else {
+        i18n("zh");
+    }
+    renderPieChartSinceLastMinuteData();
+    renderPieChartSinceLastHourData();
+    renderPieChartSinceLastWeekData();
+    renderJobTypePieChart();
+    renderJobExecutionTypePieChart();
+    renderStaticsJobsLineChart();
+    renderRunningJobsAndTasksLineChart();
+    renderRegisteredJobs();
+}
+
+function renderPieChartSinceLastMinuteData() {
+    $.ajax({
+        url: "/api/job/statistics/tasks/results/lastMinute",
+        dataType: "json",
+        success: function(jobData) {
+            if(null !== jobData) {
+                var chartName = "#total-jobs-lastMinute";
+                var color = ["rgb(144,237,125)","red"];
+                var jobResult = [[$.i18n.prop("execute-result-success"), jobData.successCount], [$.i18n.prop("execute-result-failure"), jobData.failedCount]];
+                renderPieChart(chartName, $.i18n.prop("job-info-for-one-minute"), color, jobResult);
+            }
+        }
+    });
+}
+
+function renderPieChartSinceLastHourData() {
+    $.ajax({
+        url: "/api/job/statistics/tasks/results/lastHour",
+        dataType: "json",
+        success: function(jobData) {
+            if(null !== jobData) {
+                var chartName = "#total-jobs-lastHour";
+                var color = ["rgb(144,237,125)", "red"];
+                var jobResult = [[$.i18n.prop("execute-result-success"), jobData.successCount], [$.i18n.prop("execute-result-failure"), jobData.failedCount]];
+                renderPieChart(chartName, $.i18n.prop("job-info-for-one-hour"), color, jobResult);
+            }
+        }
+    });
+}
+
+function renderPieChartSinceLastWeekData() {
+    $.ajax({
+        url: "/api/job/statistics/tasks/results/lastWeek",
+        dataType: "json",
+        success: function(jobData) {
+            if(null !== jobData) {
+                var chartName = "#total-jobs-weekly";
+                var color = ["rgb(144,237,125)", "red"];
+                var jobResult = [[$.i18n.prop("execute-result-success"), jobData.successCount], [$.i18n.prop("execute-result-failure"), jobData.failedCount]];
+                renderPieChart(chartName, $.i18n.prop("job-info-for-one-week"), color, jobResult);
+            }
+        }
+    });
+}
+
+function renderJobTypePieChart() {
+    $.ajax({
+        url: "/api/job/statistics/jobs/type",
+        dataType: "json",
+        success: function(jobData) {
+            if(null !== jobData) {
+                var chartName = "#job-type";
+                var color = ["rgb(144, 237, 125)", "rgb(247, 163, 92)", "rgb(67, 67, 72)"];
+                var jobResult = [["DATAFLOW", jobData.dataflowJobCount], ["SIMPLE", jobData.simpleJobCount], ["SCRIPT", jobData.scriptJobCount]];
+                renderPieChart(chartName,