diff --git a/pom.xml b/pom.xml
new file mode 100644
index 0000000..dac72e3
--- /dev/null
+++ b/pom.xml
@@ -0,0 +1,68 @@
+
+
+ 4.0.0
+
+ io.metersphere
+ metersphere-plugin-ParallelController
+ 1.0.0
+
+
+ UTF-8
+ 1.8
+ ${pom.basedir}/src/main/resources
+
+
+
+
+ io.metersphere
+ metersphere-plugin-core
+ 1.0.1
+ provided
+
+
+ com.blazemeter
+ jmeter-parallel
+ 0.11
+
+
+ org.apache.commons
+ commons-collections4
+ 4.4
+ provided
+
+
+
+
+
+ org.apache.maven.plugins
+ maven-compiler-plugin
+
+
+ 1.8
+
+
+
+ org.apache.maven.plugins
+ maven-assembly-plugin
+ 2.5.5
+
+
+ ${custom.lib-path}/xml/assembly.xml
+
+
+
+
+ make-assembly
+ package
+
+ single
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/src/main/java/io/metersphere/plugin/parallel/UiScriptApiImpl.java b/src/main/java/io/metersphere/plugin/parallel/UiScriptApiImpl.java
new file mode 100644
index 0000000..b752773
--- /dev/null
+++ b/src/main/java/io/metersphere/plugin/parallel/UiScriptApiImpl.java
@@ -0,0 +1,54 @@
+package io.metersphere.plugin.parallel;
+
+import io.metersphere.plugin.core.api.UiScriptApi;
+import io.metersphere.plugin.core.ui.PluginResource;
+import io.metersphere.plugin.core.ui.UiScript;
+import io.metersphere.plugin.core.utils.LogUtil;
+
+import java.io.InputStream;
+import java.util.LinkedList;
+import java.util.List;
+
+public class UiScriptApiImpl extends UiScriptApi {
+ /**
+ * 企业版插件增加 这个方法
+ *
+ * @return 是否是企业版插件
+ */
+ public boolean xpack() {
+ return false;
+ }
+
+ @Override
+ public PluginResource init() {
+ LogUtil.info("开始初始化脚本内容 ");
+ List uiScripts = new LinkedList<>();
+ String scriptString = getJson("/json/ui_parallel.json");
+ UiScript script = new UiScript("parallel_controller", "并行控制器", "io.metersphere.plugin.parallel.controller.MsParallelController", scriptString);
+ script.setJmeterClazz("GenericController");
+
+ // 添加可选参数
+ script.setFormOption(getJson("/json/ui_form.json"));
+
+ uiScripts.add(script);
+ LogUtil.info("初始化脚本内容结束 ");
+ return new PluginResource("parallel-v1.0.0", uiScripts);
+ }
+
+ @Override
+ public String customMethod(String req) {
+ LogUtil.info("Parallel Controller 自定义方法");
+ return null;
+ }
+
+ public String getJson(String path) {
+ try {
+ InputStream in = UiScriptApiImpl.class.getResourceAsStream(path);
+ String json = org.apache.commons.io.IOUtils.toString(in);
+ return json;
+ } catch (Exception ex) {
+ LogUtil.error(ex.getMessage());
+ }
+ return null;
+ }
+}
diff --git a/src/main/java/io/metersphere/plugin/parallel/controller/MsParallelController.java b/src/main/java/io/metersphere/plugin/parallel/controller/MsParallelController.java
new file mode 100644
index 0000000..d909425
--- /dev/null
+++ b/src/main/java/io/metersphere/plugin/parallel/controller/MsParallelController.java
@@ -0,0 +1,82 @@
+package io.metersphere.plugin.parallel.controller;
+
+import com.alibaba.fastjson.JSON;
+import com.alibaba.fastjson.annotation.JSONField;
+import io.metersphere.plugin.core.MsParameter;
+import io.metersphere.plugin.core.MsTestElement;
+import io.metersphere.plugin.core.utils.LogUtil;
+import io.metersphere.plugin.parallel.utils.ElementUtil;
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+import org.apache.commons.collections.CollectionUtils;
+import org.apache.jmeter.testelement.TestElement;
+import org.apache.jorphan.collections.HashTree;
+import com.blazemeter.jmeter.controller.ParallelSampler;
+
+import java.util.LinkedList;
+import java.util.List;
+@Data
+@EqualsAndHashCode(callSuper = true)
+public class MsParallelController extends MsTestElement {
+ public MsParallelController() {
+
+ }
+ private String clazzName = "io.metersphere.plugin.parallel.controller.MsParallelController";
+
+ @JSONField(ordinal = 21)
+ private Boolean limitMaxThread;
+ @JSONField(ordinal = 22)
+ private String maxThreads;
+
+ @Override
+ public void toHashTree(HashTree tree, List hashTree, MsParameter config) {
+ LogUtil.info("===========开始转换MsDummySampler ==================");
+ if (!this.isEnable()) {
+ return;
+ }
+ ParallelSampler initParallelSampler = initParallelSampler();
+ if (initParallelSampler != null) {
+ final HashTree groupTree = tree.add(initParallelSampler());
+ if (CollectionUtils.isNotEmpty(hashTree)) {
+ hashTree.forEach(el -> {
+ // 给所有孩子加一个父亲标志
+ el.setParent(this);
+ el.toHashTree(groupTree, el.getHashTree(), config);
+ });
+ }
+ }
+ else {
+ LogUtil.error("Connect Sampler 生成失败");
+ throw new RuntimeException("Connect Sampler生成失败");
+ }
+ }
+
+ public ParallelSampler initParallelSampler() {
+ try {
+ ParallelSampler parallelSampler = new ParallelSampler();
+ // base 执行时需要的参数
+ parallelSampler.setProperty("MS-ID", this.getId());
+ String indexPath = this.getIndex();
+ parallelSampler.setProperty("MS-RESOURCE-ID", this.getResourceId() + "_" + ElementUtil.getFullIndexPath(this.getParent(), indexPath));
+ List id_names = new LinkedList<>();
+ ElementUtil.getScenarioSet(this, id_names);
+ parallelSampler.setProperty("MS-SCENARIO", JSON.toJSONString(id_names));
+
+ parallelSampler.setEnabled(this.isEnable());
+ parallelSampler.setName(this.getName());
+ parallelSampler.setProperty(TestElement.GUI_CLASS, "com.blazemeter.jmeter.controller.ParallelControllerGui");
+ parallelSampler.setProperty(TestElement.TEST_CLASS, "com.blazemeter.jmeter.controller.ParallelSampler");
+
+ parallelSampler.setProperty("PARENT_SAMPLE", false);
+ parallelSampler.setProperty("LIMIT_MAX_THREAD_NUMBER", this.getLimitMaxThread());
+ if (this.getLimitMaxThread() != null && this.getLimitMaxThread()) {
+ parallelSampler.setProperty("MAX_THREAD_NUMBER", this.getMaxThreads());
+ }
+
+ return parallelSampler;
+ } catch (Exception e) {
+ e.printStackTrace();
+ return null;
+ }
+ }
+}
diff --git a/src/main/java/io/metersphere/plugin/parallel/utils/ElementUtil.java b/src/main/java/io/metersphere/plugin/parallel/utils/ElementUtil.java
new file mode 100644
index 0000000..8bf010a
--- /dev/null
+++ b/src/main/java/io/metersphere/plugin/parallel/utils/ElementUtil.java
@@ -0,0 +1,26 @@
+package io.metersphere.plugin.parallel.utils;
+
+import io.metersphere.plugin.core.MsTestElement;
+import org.apache.commons.lang3.StringUtils;
+
+import java.util.List;
+
+public class ElementUtil {
+ public static void getScenarioSet(MsTestElement element, List id_names) {
+ if (StringUtils.equals(element.getType(), "scenario")) {
+ id_names.add(element.getResourceId() + "_" + element.getName());
+ }
+ if (element.getParent() == null) {
+ return;
+ }
+ getScenarioSet(element.getParent(), id_names);
+ }
+
+ public static String getFullIndexPath(MsTestElement element, String path) {
+ if (element == null || element.getParent() == null) {
+ return path;
+ }
+ path = element.getIndex() + "_" + path;
+ return getFullIndexPath(element.getParent(), path);
+ }
+}
diff --git a/src/main/resources/json/ui_form.json b/src/main/resources/json/ui_form.json
new file mode 100644
index 0000000..3c55deb
--- /dev/null
+++ b/src/main/resources/json/ui_form.json
@@ -0,0 +1,11 @@
+{
+ "form": {
+ "inline": false,
+ "labelPosition": "left",
+ "size": "mini",
+ "labelWidth": "200px",
+ "hideRequiredAsterisk": false,
+ "showMessage": true,
+ "inlineMessage": false
+ }
+}
\ No newline at end of file
diff --git a/src/main/resources/json/ui_parallel.json b/src/main/resources/json/ui_parallel.json
new file mode 100644
index 0000000..4869f7c
--- /dev/null
+++ b/src/main/resources/json/ui_parallel.json
@@ -0,0 +1,33 @@
+[
+ {
+ "type": "switch",
+ "field": "limitMaxThread",
+ "title": "Limit max thread number",
+ "info": "",
+ "_fc_drag_tag": "switch",
+ "hidden": false,
+ "display": true,
+ "value": false,
+ "props": {
+ "activeText": "",
+ "inactiveText": ""
+ },
+ "control": [
+ {
+ "value": true,
+ "rule": [
+ {
+ "type": "inputNumber",
+ "field": "maxThreads",
+ "title": "Max threads",
+ "info": "",
+ "_fc_drag_tag": "inputNumber",
+ "hidden": false,
+ "display": true,
+ "value": 5
+ }
+ ]
+ }
+ ]
+ }
+]
\ No newline at end of file
diff --git a/src/main/resources/metersphere-plugin-ParallelController-1.0.0-jar-with-all-dependencies b/src/main/resources/metersphere-plugin-ParallelController-1.0.0-jar-with-all-dependencies
new file mode 100644
index 0000000..7816134
--- /dev/null
+++ b/src/main/resources/metersphere-plugin-ParallelController-1.0.0-jar-with-all-dependencies
@@ -0,0 +1 @@
+io.metersphere.plugin.parallel.UiScriptApiImpl
\ No newline at end of file
diff --git a/src/main/resources/xml/assembly.xml b/src/main/resources/xml/assembly.xml
new file mode 100644
index 0000000..7af2ea3
--- /dev/null
+++ b/src/main/resources/xml/assembly.xml
@@ -0,0 +1,26 @@
+
+ jar-with-all-dependencies
+
+ jar
+
+ false
+
+
+ /
+ true
+ true
+ runtime
+ false
+
+
+ /
+ true
+ system
+ false
+
+
+
+
\ No newline at end of file