diff --git a/RELEASE-NOTES.md b/RELEASE-NOTES.md
index 4693e35831..dc3b6022ad 100644
--- a/RELEASE-NOTES.md
+++ b/RELEASE-NOTES.md
@@ -2,6 +2,8 @@
### 功能提升
+1. [ISSUE #352](https://github.com/dangdangdotcom/elastic-job/issues/352) Cloud作业本地运行
+
### 缺陷修正
1. [ISSUE #322](https://github.com/dangdangdotcom/elastic-job/issues/322) elastic-job-cloud-scheduler调度任务评估资源时考虑对executor的资源使用情况
diff --git a/elastic-job-cloud/elastic-job-cloud-executor/src/main/java/com/dangdang/ddframe/job/cloud/executor/local/LocalCloudJobConfiguration.java b/elastic-job-cloud/elastic-job-cloud-executor/src/main/java/com/dangdang/ddframe/job/cloud/executor/local/LocalCloudJobConfiguration.java
new file mode 100644
index 0000000000..0d7dab8c81
--- /dev/null
+++ b/elastic-job-cloud/elastic-job-cloud-executor/src/main/java/com/dangdang/ddframe/job/cloud/executor/local/LocalCloudJobConfiguration.java
@@ -0,0 +1,54 @@
+/*
+ * Copyright 1999-2015 dangdang.com.
+ *
+ * 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.
+ *
+ */
+
+package com.dangdang.ddframe.job.cloud.executor.local;
+
+import com.dangdang.ddframe.job.config.JobRootConfiguration;
+import com.dangdang.ddframe.job.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 LocalCloudJobExecutionType executionType;
+
+ private final int shardingItem;
+
+ private String beanName;
+
+ private String applicationContext;
+
+ /**
+ * 获取作业名称.
+ *
+ * @return 作业名称
+ */
+ public String getJobName() {
+ return typeConfig.getCoreConfig().getJobName();
+ }
+}
diff --git a/elastic-job-cloud/elastic-job-cloud-executor/src/main/java/com/dangdang/ddframe/job/cloud/executor/local/LocalCloudJobExecutionType.java b/elastic-job-cloud/elastic-job-cloud-executor/src/main/java/com/dangdang/ddframe/job/cloud/executor/local/LocalCloudJobExecutionType.java
new file mode 100644
index 0000000000..2976b2ee2a
--- /dev/null
+++ b/elastic-job-cloud/elastic-job-cloud-executor/src/main/java/com/dangdang/ddframe/job/cloud/executor/local/LocalCloudJobExecutionType.java
@@ -0,0 +1,28 @@
+/*
+ * Copyright 1999-2015 dangdang.com.
+ *
+ * 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.
+ *
+ */
+
+package com.dangdang.ddframe.job.cloud.executor.local;
+
+/**
+ * 本地作业执行类型.
+ *
+ * @author gaohongtao
+ */
+public enum LocalCloudJobExecutionType {
+
+ DAEMON, TRANSIENT
+}
diff --git a/elastic-job-cloud/elastic-job-cloud-executor/src/main/java/com/dangdang/ddframe/job/cloud/executor/local/LocalTaskExecutor.java b/elastic-job-cloud/elastic-job-cloud-executor/src/main/java/com/dangdang/ddframe/job/cloud/executor/local/LocalTaskExecutor.java
new file mode 100644
index 0000000000..56b151b251
--- /dev/null
+++ b/elastic-job-cloud/elastic-job-cloud-executor/src/main/java/com/dangdang/ddframe/job/cloud/executor/local/LocalTaskExecutor.java
@@ -0,0 +1,115 @@
+/*
+ * Copyright 1999-2015 dangdang.com.
+ *
+ * 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.
+ *
+ */
+
+package com.dangdang.ddframe.job.cloud.executor.local;
+
+import com.dangdang.ddframe.job.api.ElasticJob;
+import com.dangdang.ddframe.job.api.ShardingContext;
+import com.dangdang.ddframe.job.api.dataflow.DataflowJob;
+import com.dangdang.ddframe.job.api.simple.SimpleJob;
+import com.dangdang.ddframe.job.config.JobCoreConfiguration;
+import com.dangdang.ddframe.job.config.script.ScriptJobConfiguration;
+import com.dangdang.ddframe.job.exception.JobConfigurationException;
+import com.dangdang.ddframe.job.exception.JobSystemException;
+import com.dangdang.ddframe.job.executor.ShardingContexts;
+import com.dangdang.ddframe.job.util.config.ShardingItemParameters;
+import com.dangdang.ddframe.job.util.json.GsonFactory;
+import com.google.common.base.Strings;
+import lombok.RequiredArgsConstructor;
+import org.apache.commons.exec.CommandLine;
+import org.apache.commons.exec.DefaultExecutor;
+import org.springframework.context.support.ClassPathXmlApplicationContext;
+
+import java.io.IOException;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import static com.dangdang.ddframe.job.api.JobType.DATAFLOW;
+import static com.dangdang.ddframe.job.api.JobType.SIMPLE;
+
+/**
+ * 本地作业执行器.
+ *
+ * @author gaohongtao
+ */
+@RequiredArgsConstructor
+public final class LocalTaskExecutor {
+
+ private final LocalCloudJobConfiguration localCloudJobConfiguration;
+
+ /**
+ * 本地执行作业.
+ */
+ public void execute() {
+ if (SIMPLE == localCloudJobConfiguration.getTypeConfig().getJobType()) {
+ getJobInstance(SimpleJob.class).execute(getShardingContext());
+ } else if (DATAFLOW == localCloudJobConfiguration.getTypeConfig().getJobType()) {
+ processDataflow();
+ } else {
+ processScript();
+ }
+ }
+
+ private T getJobInstance(final Class clazz) {
+ if (Strings.isNullOrEmpty(localCloudJobConfiguration.getApplicationContext())) {
+ String jobClass = localCloudJobConfiguration.getTypeConfig().getJobClass();
+ try {
+ return clazz.cast(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 {
+ return clazz.cast(new ClassPathXmlApplicationContext(localCloudJobConfiguration.getApplicationContext()).getBean(localCloudJobConfiguration.getBeanName()));
+ }
+ }
+
+ private ShardingContext getShardingContext() {
+ JobCoreConfiguration coreConfig = localCloudJobConfiguration.getTypeConfig().getCoreConfig();
+ String shardingItem = new ShardingItemParameters(coreConfig.getShardingItemParameters()).getMap().get(localCloudJobConfiguration.getShardingItem());
+ Map shardingItemMap = new HashMap<>(1);
+ if (!Strings.isNullOrEmpty(shardingItem)) {
+ shardingItemMap.put(localCloudJobConfiguration.getShardingItem(), shardingItem);
+ }
+ return new ShardingContext(new ShardingContexts("foo", localCloudJobConfiguration.getJobName(), coreConfig
+ .getShardingTotalCount(), coreConfig.getJobParameter(), shardingItemMap), localCloudJobConfiguration.getShardingItem());
+ }
+
+ @SuppressWarnings("unchecked")
+ private void processDataflow() {
+ final ShardingContext shardingContext = getShardingContext();
+ DataflowJob dataflowJob = getJobInstance(DataflowJob.class);
+ List data = dataflowJob.fetchData(shardingContext);
+ if (null != data && !data.isEmpty()) {
+ dataflowJob.processData(shardingContext, data);
+ }
+ }
+
+ private void processScript() {
+ final String scriptCommandLine = ((ScriptJobConfiguration) localCloudJobConfiguration.getTypeConfig()).getScriptCommandLine();
+ if (Strings.isNullOrEmpty(scriptCommandLine)) {
+ throw new JobConfigurationException("Cannot find script command line for job '%s', job is not executed.", localCloudJobConfiguration.getJobName());
+ }
+ CommandLine commandLine = CommandLine.parse(scriptCommandLine);
+ commandLine.addArgument(GsonFactory.getGson().toJson(getShardingContext()), false);
+ try {
+ new DefaultExecutor().execute(commandLine);
+ } catch (final IOException ex) {
+ throw new JobConfigurationException("Execute script failure.", ex);
+ }
+ }
+}
diff --git a/elastic-job-cloud/elastic-job-cloud-executor/src/test/java/com/dangdang/ddframe/job/cloud/executor/AllCloudExecutorTests.java b/elastic-job-cloud/elastic-job-cloud-executor/src/test/java/com/dangdang/ddframe/job/cloud/executor/AllCloudExecutorTests.java
index cb6c845e74..888d3cdb09 100644
--- a/elastic-job-cloud/elastic-job-cloud-executor/src/test/java/com/dangdang/ddframe/job/cloud/executor/AllCloudExecutorTests.java
+++ b/elastic-job-cloud/elastic-job-cloud-executor/src/test/java/com/dangdang/ddframe/job/cloud/executor/AllCloudExecutorTests.java
@@ -17,6 +17,7 @@
package com.dangdang.ddframe.job.cloud.executor;
+import com.dangdang.ddframe.job.cloud.executor.local.AllLocalExecutorTests;
import lombok.AccessLevel;
import lombok.NoArgsConstructor;
import org.junit.runner.RunWith;
@@ -29,7 +30,8 @@
DaemonTaskSchedulerTest.class,
JobConfigurationContextTest.class,
TaskExecutorTest.class,
- TaskExecutorThreadTest.class
+ TaskExecutorThreadTest.class,
+ AllLocalExecutorTests.class
})
@NoArgsConstructor(access = AccessLevel.PRIVATE)
public final class AllCloudExecutorTests {
diff --git a/elastic-job-cloud/elastic-job-cloud-executor/src/test/java/com/dangdang/ddframe/job/cloud/executor/local/AllLocalExecutorTests.java b/elastic-job-cloud/elastic-job-cloud-executor/src/test/java/com/dangdang/ddframe/job/cloud/executor/local/AllLocalExecutorTests.java
new file mode 100644
index 0000000000..d5e31ecd35
--- /dev/null
+++ b/elastic-job-cloud/elastic-job-cloud-executor/src/test/java/com/dangdang/ddframe/job/cloud/executor/local/AllLocalExecutorTests.java
@@ -0,0 +1,29 @@
+/*
+ * Copyright 1999-2015 dangdang.com.
+ *
+ * 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.
+ *
+ */
+
+package com.dangdang.ddframe.job.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/elastic-job-cloud-executor/src/test/java/com/dangdang/ddframe/job/cloud/executor/local/LocalTaskExecutorTest.java b/elastic-job-cloud/elastic-job-cloud-executor/src/test/java/com/dangdang/ddframe/job/cloud/executor/local/LocalTaskExecutorTest.java
new file mode 100644
index 0000000000..4bbdf2f577
--- /dev/null
+++ b/elastic-job-cloud/elastic-job-cloud-executor/src/test/java/com/dangdang/ddframe/job/cloud/executor/local/LocalTaskExecutorTest.java
@@ -0,0 +1,102 @@
+/*
+ * Copyright 1999-2015 dangdang.com.
+ *
+ * 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.
+ *
+ */
+
+package com.dangdang.ddframe.job.cloud.executor.local;
+
+import com.dangdang.ddframe.job.cloud.executor.local.fixture.TestDataflowJob;
+import com.dangdang.ddframe.job.cloud.executor.local.fixture.TestSimpleJob;
+import com.dangdang.ddframe.job.config.JobCoreConfiguration;
+import com.dangdang.ddframe.job.config.dataflow.DataflowJobConfiguration;
+import com.dangdang.ddframe.job.config.script.ScriptJobConfiguration;
+import com.dangdang.ddframe.job.config.simple.SimpleJobConfiguration;
+import com.dangdang.ddframe.job.exception.JobConfigurationException;
+import com.dangdang.ddframe.job.exception.JobSystemException;
+import org.junit.Before;
+import org.junit.Test;
+
+import java.util.Arrays;
+
+import static com.dangdang.ddframe.job.cloud.executor.local.LocalCloudJobExecutionType.DAEMON;
+import static com.dangdang.ddframe.job.cloud.executor.local.LocalCloudJobExecutionType.TRANSIENT;
+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 class LocalTaskExecutorTest {
+
+ @Before
+ public void setUp() throws Exception {
+ TestSimpleJob.setShardingContext(null);
+ TestDataflowJob.setInput(null);
+ TestDataflowJob.setOutput(null);
+ }
+
+ @Test
+ public void assertSimpleJob() throws Exception {
+ new LocalTaskExecutor(new LocalCloudJobConfiguration(new SimpleJobConfiguration(JobCoreConfiguration
+ .newBuilder(TestSimpleJob.class.getSimpleName(), "*/2 * * * * ?", 3).build(), TestSimpleJob.class.getName()), TRANSIENT, 1)).execute();
+ assertThat(TestSimpleJob.getShardingContext().getJobName(), is(TestSimpleJob.class.getSimpleName()));
+ assertThat(TestSimpleJob.getShardingContext().getShardingItem(), is(1));
+ assertThat(TestSimpleJob.getShardingContext().getShardingTotalCount(), is(3));
+ assertNull(TestSimpleJob.getShardingContext().getShardingParameter());
+ assertThat(TestSimpleJob.getShardingContext().getJobParameter(), is(""));
+ }
+
+ @Test(expected = JobSystemException.class)
+ public void assertNotExistsJobClass() throws Exception {
+ new LocalTaskExecutor(new LocalCloudJobConfiguration(new SimpleJobConfiguration(JobCoreConfiguration
+ .newBuilder("not exist", "*/2 * * * * ?", 3).build(), "not exist"), TRANSIENT, 1)).execute();
+ }
+
+ @Test
+ public void assertSpringSimpleJob() throws Exception {
+ 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()), DAEMON, 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.getShardingParameters().size(), is(1));
+ assertThat(TestSimpleJob.getShardingParameters().iterator().next(), is("Shanghai"));
+ }
+
+ @Test
+ public void assertDataflow() throws Exception {
+ 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), TRANSIENT, 5)).execute();
+ assertFalse(TestDataflowJob.getOutput().isEmpty());
+ for (String each : TestDataflowJob.getOutput()) {
+ assertTrue(each.endsWith("-d"));
+ }
+ }
+
+ @Test(expected = JobConfigurationException.class)
+ public void assertScriptEmpty() throws Exception {
+ new LocalTaskExecutor(new LocalCloudJobConfiguration(new ScriptJobConfiguration(JobCoreConfiguration
+ .newBuilder(TestDataflowJob.class.getSimpleName(), "*/2 * * * * ?", 10).build(), ""), TRANSIENT, 5)).execute();
+ }
+
+ @Test(expected = JobConfigurationException.class)
+ public void assertScriptNotExists() throws Exception {
+ new LocalTaskExecutor(new LocalCloudJobConfiguration(new ScriptJobConfiguration(JobCoreConfiguration
+ .newBuilder(TestDataflowJob.class.getSimpleName(), "*/2 * * * * ?", 10).build(), "not_exists_file param1"), TRANSIENT, 5)).execute();
+ }
+}
diff --git a/elastic-job-cloud/elastic-job-cloud-executor/src/test/java/com/dangdang/ddframe/job/cloud/executor/local/fixture/TestDataflowJob.java b/elastic-job-cloud/elastic-job-cloud-executor/src/test/java/com/dangdang/ddframe/job/cloud/executor/local/fixture/TestDataflowJob.java
new file mode 100644
index 0000000000..8758a8f3b6
--- /dev/null
+++ b/elastic-job-cloud/elastic-job-cloud-executor/src/test/java/com/dangdang/ddframe/job/cloud/executor/local/fixture/TestDataflowJob.java
@@ -0,0 +1,52 @@
+/*
+ * Copyright 1999-2015 dangdang.com.
+ *
+ * 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.
+ *
+ */
+
+package com.dangdang.ddframe.job.cloud.executor.local.fixture;
+
+import com.dangdang.ddframe.job.api.ShardingContext;
+import com.dangdang.ddframe.job.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 class TestDataflowJob implements DataflowJob {
+
+ @Setter
+ private static List input;
+
+ @Getter
+ @Setter
+ private static List output;
+
+ @Override
+ public List fetchData(final ShardingContext shardingContext) {
+ return input;
+ }
+
+ @Override
+ public void processData(final ShardingContext shardingContext, final List data) {
+ output = Lists.transform(input, new Function() {
+ @Override
+ public String apply(final String input) {
+ return input + "-d";
+ }
+ });
+ }
+}
diff --git a/elastic-job-cloud/elastic-job-cloud-executor/src/test/java/com/dangdang/ddframe/job/cloud/executor/local/fixture/TestSimpleJob.java b/elastic-job-cloud/elastic-job-cloud-executor/src/test/java/com/dangdang/ddframe/job/cloud/executor/local/fixture/TestSimpleJob.java
new file mode 100644
index 0000000000..ab33eac50d
--- /dev/null
+++ b/elastic-job-cloud/elastic-job-cloud-executor/src/test/java/com/dangdang/ddframe/job/cloud/executor/local/fixture/TestSimpleJob.java
@@ -0,0 +1,45 @@
+/*
+ * Copyright 1999-2015 dangdang.com.
+ *
+ * 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.
+ *
+ */
+
+package com.dangdang.ddframe.job.cloud.executor.local.fixture;
+
+import com.dangdang.ddframe.job.api.ShardingContext;
+import com.dangdang.ddframe.job.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 class TestSimpleJob implements SimpleJob {
+
+ @Getter
+ @Setter
+ private static ShardingContext shardingContext;
+
+ @Getter
+ private static Set 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/elastic-job-cloud-executor/src/test/resources/applicationContext.xml b/elastic-job-cloud/elastic-job-cloud-executor/src/test/resources/applicationContext.xml
index 033830355a..2228a19673 100644
--- a/elastic-job-cloud/elastic-job-cloud-executor/src/test/resources/applicationContext.xml
+++ b/elastic-job-cloud/elastic-job-cloud-executor/src/test/resources/applicationContext.xml
@@ -5,4 +5,5 @@
http://www.springframework.org/schema/beans/spring-beans.xsd
">
+
diff --git a/elastic-job-doc/elastic-job-cloud-doc/content/02-guide/event-trace.md b/elastic-job-doc/elastic-job-cloud-doc/content/02-guide/event-trace.md
index 61de0a093c..d76d684744 100644
--- a/elastic-job-doc/elastic-job-cloud-doc/content/02-guide/event-trace.md
+++ b/elastic-job-doc/elastic-job-cloud-doc/content/02-guide/event-trace.md
@@ -4,7 +4,7 @@ date = "2016-09-27T16:14:21+08:00"
title = "事件追踪"
weight = 30
prev = "/02-guide/high-availability/"
-next = "/03-design"
+next = "/02-guide/local-executor/"
+++
Elastic-Job的Lite版和Cloud版都提供了事件追踪功能,可通过事件订阅的方式处理调度过程的重要事件,用于查询、统计和监控。Elastic-Job目前提供了基于关系型数据库两种事件订阅方式记录事件。
diff --git a/elastic-job-doc/elastic-job-cloud-doc/content/02-guide/index.md b/elastic-job-doc/elastic-job-cloud-doc/content/02-guide/index.md
index b315a4107a..3a14de76a0 100644
--- a/elastic-job-doc/elastic-job-cloud-doc/content/02-guide/index.md
+++ b/elastic-job-doc/elastic-job-cloud-doc/content/02-guide/index.md
@@ -18,4 +18,6 @@ chapter = true
- Elastic-Job-Cloud的Scheduler如何保证高可用?请阅读[高可用](/02-guide/high-availability/)。
- - 为了便于记录、查询、统计及监控作业运行情况,Elastic-Job提供了[事件追踪](/02-guide/event-trace/)功能。
\ No newline at end of file
+ - 为了便于记录、查询、统计及监控作业运行情况,Elastic-Job提供了[事件追踪](/02-guide/event-trace/)功能。
+
+ - 为了方便在开发机上调试,运行作业,Elastic-Job提供了[本地运行作业](/02-guide/local-executor)功能。
\ No newline at end of file
diff --git a/elastic-job-doc/elastic-job-cloud-doc/content/02-guide/local-executor.md b/elastic-job-doc/elastic-job-cloud-doc/content/02-guide/local-executor.md
new file mode 100644
index 0000000000..bb2511ce4a
--- /dev/null
+++ b/elastic-job-doc/elastic-job-cloud-doc/content/02-guide/local-executor.md
@@ -0,0 +1,42 @@
++++
+toc = true
+date = "2017-06-05T16:14:21+08:00"
+title = "本地运行作业"
+weight = 40
+prev = "/02-guide/event-trace/"
+next = "/03-design"
++++
+
+在开发`Elastic-Job-Cloud`作业的时候,开发人员会希望能够本地运行作业。目前作业云提供了该功能,您只需要使用简单的API配置作业,就可以像在
+`Mesos`集群中一样本地运行作业。
+
+本地运行作业无需安装`Mesos`环境。
+
+## 本地作业配置
+
+使用`com.dangdang.ddframe.job.cloud.executor.local.LocalCloudJobConfiguration`来配置本地作业。
+
+```java
+LocalCloudJobConfiguration config = new LocalCloudJobConfiguration(
+ new SimpleJobConfiguration(JobCoreConfiguration.newBuilder("FooJob", "*/2 * * * * ?", 3) //1
+ .shardingItemParameters("0=Beijing,1=Shanghai,2=Guangzhou")
+ .jobParameter("dbName=dangdang").build()
+ , "com.dangdang.foo.FooJob")
+ , LocalCloudJobExecutionType.DAEMON //2
+ , 1 //3
+ , "testSimpleJob" //4
+ , "applicationContext.xml")
+```
+
+1. 配置作业类型和作业基本信息。
+1. 配置作业的执行类型。
+1. 配置当前运行的作业是第几个分片。
+1. 配置Spring相关参数。
+
+## 运行本地作业
+
+使用`com.dangdang.ddframe.job.cloud.executor.local.LocalTaskExecutor`运行作业。
+
+```java
+new LocalTaskExecutor(config).execute();
+```
\ No newline at end of file