You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@servicecomb.apache.org by ma...@apache.org on 2019/08/19 07:02:10 UTC

[servicecomb-toolkit] branch master updated: SCB-1444 support to generate mutiple modules in the same project

This is an automated email from the ASF dual-hosted git repository.

mabin pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/servicecomb-toolkit.git


The following commit(s) were added to refs/heads/master by this push:
     new 3761544  SCB-1444 support to generate mutiple modules in the same project
     new fab250d  Merge pull request #18 from MabinGo/supportMutipleModules
3761544 is described below

commit 37615449761421b8c99e48afb2826c917ecadf40
Author: MabinGo <bi...@huawei.com>
AuthorDate: Fri Aug 16 15:47:41 2019 +0800

    SCB-1444 support to generate mutiple modules in the same project
    
    Signed-off-by: MabinGo <bi...@huawei.com>
---
 .../toolkit/codegen/DefaultCodeGenerator.java      | 15 +++-
 .../codegen/GeneratorExternalConfigConstant.java   |  5 ++
 .../codegen/GetRelativeBasePathLambda.java}        | 26 +++----
 .../toolkit/codegen/MultiContractGenerator.java    | 87 ++++++++++++++++++++++
 .../toolkit/codegen/ServiceCombCodegen.java        | 15 +++-
 .../ServiceComb/consumer/apiConsumer.mustache      |  2 +-
 .../resources/ServiceComb/consumer/pom.mustache    |  6 +-
 .../ServiceComb/libraries/JAX-RS/api.mustache      |  2 +-
 .../ServiceComb/libraries/JAX-RS/pom.mustache      |  6 +-
 .../ServiceComb/libraries/POJO/pom.mustache        |  6 +-
 .../ServiceComb/libraries/SpringBoot/api.mustache  |  2 +-
 .../ServiceComb/libraries/SpringBoot/pom.mustache  |  6 +-
 .../ServiceComb/libraries/SpringMVC/api.mustache   |  2 +-
 .../ServiceComb/libraries/SpringMVC/pom.mustache   |  6 +-
 .../main/resources/ServiceComb/model/pom.mustache  |  4 +-
 .../resources/ServiceComb/project/pom.mustache     |  6 +-
 .../servicecomb/toolkit/codegen/GeneratorTest.java | 11 +--
 .../servicecomb/toolkit/codegen/ReflectUtils.java  | 27 ++++---
 .../servicecomb/toolkit/common/FileUtils.java      |  8 +-
 .../servicecomb/toolkit/plugin/GenerateMojo.java   | 18 ++++-
 .../servicecomb/toolkit/plugin/GenerateUtil.java   | 65 +++++++++++-----
 .../toolkit/plugin/GenerateMojoTest.java           |  4 +-
 22 files changed, 244 insertions(+), 85 deletions(-)

diff --git a/codegen/src/main/java/org/apache/servicecomb/toolkit/codegen/DefaultCodeGenerator.java b/codegen/src/main/java/org/apache/servicecomb/toolkit/codegen/DefaultCodeGenerator.java
index 201eb76..3d1006f 100755
--- a/codegen/src/main/java/org/apache/servicecomb/toolkit/codegen/DefaultCodeGenerator.java
+++ b/codegen/src/main/java/org/apache/servicecomb/toolkit/codegen/DefaultCodeGenerator.java
@@ -17,16 +17,16 @@
 
 package org.apache.servicecomb.toolkit.codegen;
 
+import java.util.List;
 import java.util.Map;
 
 import org.apache.servicecomb.toolkit.CodeGenerator;
 
-import io.swagger.codegen.DefaultGenerator;
 import io.swagger.codegen.config.CodegenConfigurator;
 
 public class DefaultCodeGenerator implements CodeGenerator {
 
-  private DefaultGenerator generator = new DefaultGenerator();
+  private MultiContractGenerator generator = new MultiContractGenerator();
 
   @Override
   public boolean canProcess(String type) {
@@ -35,9 +35,16 @@ public class DefaultCodeGenerator implements CodeGenerator {
 
   @Override
   public void configure(Map<String, Object> config) {
-    CodegenConfigurator opts = (CodegenConfigurator) config.get("configurator");
+
     generator.setGenerateSwaggerMetadata(false);
-    generator.opts(opts.toClientOptInput());
+    List<CodegenConfigurator> optsList = (List<CodegenConfigurator>) config.get("configurators");
+    if (optsList == null) {
+      generator.addOpts(((CodegenConfigurator) config.get("configurator")).toClientOptInput());
+      return;
+    }
+    optsList.forEach(opts -> {
+      generator.addOpts(opts.toClientOptInput());
+    });
   }
 
   @Override
diff --git a/codegen/src/main/java/org/apache/servicecomb/toolkit/codegen/GeneratorExternalConfigConstant.java b/codegen/src/main/java/org/apache/servicecomb/toolkit/codegen/GeneratorExternalConfigConstant.java
old mode 100644
new mode 100755
index cefb272..df8fe25
--- a/codegen/src/main/java/org/apache/servicecomb/toolkit/codegen/GeneratorExternalConfigConstant.java
+++ b/codegen/src/main/java/org/apache/servicecomb/toolkit/codegen/GeneratorExternalConfigConstant.java
@@ -25,4 +25,9 @@ public class GeneratorExternalConfigConstant {
 
   public final static String MODEL_PROJECT_NAME = "modelProjectName";
 
+  public final static String PROVIDER_ARTIFACT_ID = "providerArtifactId";
+
+  public final static String CONSUMER_ARTIFACT_ID = "consumerArtifactId";
+
+  public final static String MODEL_ARTIFACT_ID = "modelArtifactId";
 }
diff --git a/codegen/src/test/java/org/apache/servicecomb/toolkit/codegen/ReflectUtils.java b/codegen/src/main/java/org/apache/servicecomb/toolkit/codegen/GetRelativeBasePathLambda.java
similarity index 62%
copy from codegen/src/test/java/org/apache/servicecomb/toolkit/codegen/ReflectUtils.java
copy to codegen/src/main/java/org/apache/servicecomb/toolkit/codegen/GetRelativeBasePathLambda.java
index b5892a3..90ba0e8 100755
--- a/codegen/src/test/java/org/apache/servicecomb/toolkit/codegen/ReflectUtils.java
+++ b/codegen/src/main/java/org/apache/servicecomb/toolkit/codegen/GetRelativeBasePathLambda.java
@@ -15,24 +15,24 @@
  * limitations under the License.
  */
 
+
 package org.apache.servicecomb.toolkit.codegen;
 
-import java.lang.reflect.Field;
+import java.io.IOException;
+import java.io.Writer;
+
+import com.samskivert.mustache.Mustache;
+import com.samskivert.mustache.Template.Fragment;
 
-class ReflectUtils {
+public class GetRelativeBasePathLambda implements Mustache.Lambda {
 
-  static Object getProperty(Object obj, String propName) {
+  private String HOST_PORT_PATTERN = "(\\w+://)(\\w+)?(:\\d*)?";
 
-    try {
-      Field propFiled = obj.getClass().getDeclaredField(propName);
-      propFiled.setAccessible(true);
-      return propFiled.get(obj);
-    } catch (NoSuchFieldException e) {
-      e.printStackTrace();
-    } catch (IllegalAccessException e) {
-      e.printStackTrace();
-    }
+  @Override
+  public void execute(Fragment fragment, Writer writer) throws IOException {
 
-    return null;
+    String text = fragment.execute();
+    String relativeBasePath = text.replaceAll(HOST_PORT_PATTERN, "");
+    writer.write(relativeBasePath);
   }
 }
diff --git a/codegen/src/main/java/org/apache/servicecomb/toolkit/codegen/MultiContractGenerator.java b/codegen/src/main/java/org/apache/servicecomb/toolkit/codegen/MultiContractGenerator.java
new file mode 100755
index 0000000..4bc5642
--- /dev/null
+++ b/codegen/src/main/java/org/apache/servicecomb/toolkit/codegen/MultiContractGenerator.java
@@ -0,0 +1,87 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.servicecomb.toolkit.codegen;
+
+import java.io.File;
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+import io.swagger.codegen.ClientOptInput;
+import io.swagger.codegen.DefaultGenerator;
+import io.swagger.codegen.Generator;
+
+public class MultiContractGenerator extends DefaultGenerator {
+
+  private List<ClientOptInput> optsList = new ArrayList<>();
+
+  public Generator addOpts(ClientOptInput opts) {
+    optsList.add(opts);
+    return this;
+  }
+
+  public void generateParentProject(List<File> files, List<Map<String, Object>> modules) {
+
+    this.config = optsList.get(0).getConfig();
+    String outputFilename = opts.getConfig().outputFolder() + File.separator + "pom.xml";
+
+    if (!config.shouldOverwrite(outputFilename)) {
+      LOGGER.info("Skipped overwriting " + outputFilename);
+    }
+    Map<String, Object> templateData = this.config.additionalProperties();
+    templateData.put("modules", modules);
+    try {
+      files.add(processTemplateToFile(templateData, "project/pom.mustache", outputFilename));
+    } catch (IOException e) {
+      throw new RuntimeException("Failed to generate parent project pom.xml", e);
+    }
+  }
+
+  @Override
+  public List<File> generate() {
+
+    if (optsList == null || optsList.size() == 0) {
+      return null;
+    }
+
+    List<File> fileList = new ArrayList<>();
+    List<Map<String, Object>> modules = new ArrayList<>();
+
+    Set<Object> moduleSet = new HashSet<>();
+    for (ClientOptInput opts : optsList) {
+      opts.getConfig().additionalProperties().put("isMultipleModule", false);
+      moduleSet.add(opts.getConfig().additionalProperties().get(GeneratorExternalConfigConstant.PROVIDER_PROJECT_NAME));
+      moduleSet.add(opts.getConfig().additionalProperties().get(GeneratorExternalConfigConstant.CONSUMER_PROJECT_NAME));
+      moduleSet.add(opts.getConfig().additionalProperties().get(GeneratorExternalConfigConstant.MODEL_PROJECT_NAME));
+      this.opts(opts);
+      fileList.addAll(super.generate());
+    }
+
+    moduleSet.forEach(module -> {
+      modules.add(Collections.singletonMap("module", module));
+    });
+
+    generateParentProject(fileList, modules);
+
+    return fileList;
+  }
+}
diff --git a/codegen/src/main/java/org/apache/servicecomb/toolkit/codegen/ServiceCombCodegen.java b/codegen/src/main/java/org/apache/servicecomb/toolkit/codegen/ServiceCombCodegen.java
index 530d569..60a5d32 100755
--- a/codegen/src/main/java/org/apache/servicecomb/toolkit/codegen/ServiceCombCodegen.java
+++ b/codegen/src/main/java/org/apache/servicecomb/toolkit/codegen/ServiceCombCodegen.java
@@ -109,7 +109,8 @@ public class ServiceCombCodegen extends AbstractJavaCodegen implements CodegenCo
     supportedLibraries.put(DEFAULT_LIBRARY, "ServiceComb Server application using the SpringMVC programming model.");
     supportedLibraries.put(POJO_LIBRARY, "ServiceComb Server application using the POJO programming model.");
     supportedLibraries.put(JAX_RS_LIBRARY, "ServiceComb Server application using the JAX-RS programming model.");
-    supportedLibraries.put(SPRING_BOOT_LIBRARY, "ServiceComb Server application using the SpringBoot programming model.");
+    supportedLibraries
+        .put(SPRING_BOOT_LIBRARY, "ServiceComb Server application using the SpringBoot programming model.");
 
     setLibrary(DEFAULT_LIBRARY);
 
@@ -182,9 +183,9 @@ public class ServiceCombCodegen extends AbstractJavaCodegen implements CodegenCo
     }
     additionalProperties.put("camelcase", new CamelCaseLambda());
     additionalProperties.put("getGenericClassType", new GetGenericClassTypeLambda());
+    additionalProperties.put("getRelativeBasePath",new GetRelativeBasePathLambda());
     additionalProperties.put("removeImplSuffix", new RemoveImplSuffixLambda());
     additionalProperties.put("applicationId", applicationId);
-    additionalProperties.put("microserviceName", microserviceName);
 
     if (additionalProperties.get(GeneratorExternalConfigConstant.PROVIDER_PROJECT_NAME) != null) {
       providerProject = (String) additionalProperties.get(GeneratorExternalConfigConstant.PROVIDER_PROJECT_NAME);
@@ -195,6 +196,14 @@ public class ServiceCombCodegen extends AbstractJavaCodegen implements CodegenCo
     if (additionalProperties.get(GeneratorExternalConfigConstant.MODEL_PROJECT_NAME) != null) {
       modelProject = (String) additionalProperties.get(GeneratorExternalConfigConstant.MODEL_PROJECT_NAME);
     }
+    if (additionalProperties.get("microserviceName") != null) {
+      microserviceName = (String) additionalProperties.get("microserviceName");
+    }
+    additionalProperties.put("microserviceName", microserviceName);
+
+    additionalProperties.computeIfAbsent(GeneratorExternalConfigConstant.PROVIDER_ARTIFACT_ID, k -> providerProject);
+    additionalProperties.computeIfAbsent(GeneratorExternalConfigConstant.CONSUMER_ARTIFACT_ID, k -> consumerProject);
+    additionalProperties.computeIfAbsent(GeneratorExternalConfigConstant.MODEL_ARTIFACT_ID, k -> modelProject);
 
     boolean isMultipleModule = (boolean) Optional.ofNullable(additionalProperties.get("isMultipleModule")).orElse(true);
     if (isMultipleModule) {
@@ -309,7 +318,7 @@ public class ServiceCombCodegen extends AbstractJavaCodegen implements CodegenCo
         "microservice.yaml")
     );
 
-    apiTemplateFiles.put(apiConsumerTemplate, "Consumer.java");
+    apiTemplateFiles.put(apiConsumerTemplate, ".java");
   }
 
   @Override
diff --git a/codegen/src/main/resources/ServiceComb/consumer/apiConsumer.mustache b/codegen/src/main/resources/ServiceComb/consumer/apiConsumer.mustache
index a8a1209..8a4d434 100755
--- a/codegen/src/main/resources/ServiceComb/consumer/apiConsumer.mustache
+++ b/codegen/src/main/resources/ServiceComb/consumer/apiConsumer.mustache
@@ -54,7 +54,7 @@ public class {{classname}}Consumer {
 
     RestTemplate restTemplate = RestTemplateBuilder.create();
 
-    ResponseEntity<{{>returnTypes}}> result = restTemplate.exchange("cse://{{microserviceName}}/{{path}}",HttpMethod.{{httpMethod}},httpEntity,{{#getGenericClassType}}{{>returnTypes}}{{/getGenericClassType}}.class,params);
+    ResponseEntity<{{>returnTypes}}> result = restTemplate.exchange("cse://{{microserviceName}}{{#getRelativeBasePath}}{{basePath}}{{/getRelativeBasePath}}/{{path}}",HttpMethod.{{httpMethod}},httpEntity,{{#getGenericClassType}}{{>returnTypes}}{{/getGenericClassType}}.class,params);
 
     // do something
 
diff --git a/codegen/src/main/resources/ServiceComb/consumer/pom.mustache b/codegen/src/main/resources/ServiceComb/consumer/pom.mustache
index 9a1e531..a961d90 100755
--- a/codegen/src/main/resources/ServiceComb/consumer/pom.mustache
+++ b/codegen/src/main/resources/ServiceComb/consumer/pom.mustache
@@ -3,9 +3,9 @@
 
   <modelVersion>4.0.0</modelVersion>
   <groupId>{{groupId}}</groupId>
-  <artifactId>consumer</artifactId>
+  <artifactId>{{consumerArtifactId}}</artifactId>
   <packaging>jar</packaging>
-  <name>{{artifactId}}</name>
+  <name>{{consumerArtifactId}}</name>
   <version>{{artifactVersion}}</version>
 
   <properties>
@@ -28,7 +28,7 @@
   <dependencies>
     <dependency>
       <groupId>{{groupId}}</groupId>
-      <artifactId>model</artifactId>
+      <artifactId>{{modelArtifactId}}</artifactId>
       <version>${project.version}</version>
     </dependency>
 
diff --git a/codegen/src/main/resources/ServiceComb/libraries/JAX-RS/api.mustache b/codegen/src/main/resources/ServiceComb/libraries/JAX-RS/api.mustache
index 9b628dc..a793e54 100755
--- a/codegen/src/main/resources/ServiceComb/libraries/JAX-RS/api.mustache
+++ b/codegen/src/main/resources/ServiceComb/libraries/JAX-RS/api.mustache
@@ -18,7 +18,7 @@ import io.swagger.annotations.ApiParam;
 {{#hasProduces}}@Produces({ {{#produces}}"{{{mediaType}}}"{{#hasMore}}, {{/hasMore}}{{/produces}} }){{/hasProduces}}
 {{#operations}}
 @RestSchema(schemaId = "{{#camelcase}}{{classname}}{{/camelcase}}")
-@Path("/")
+@Path("{{#getRelativeBasePath}}{{basePath}}{{/getRelativeBasePath}}")
 public class {{classname}} {
 {{#operation}}
 
diff --git a/codegen/src/main/resources/ServiceComb/libraries/JAX-RS/pom.mustache b/codegen/src/main/resources/ServiceComb/libraries/JAX-RS/pom.mustache
index 5e66f46..c46a048 100755
--- a/codegen/src/main/resources/ServiceComb/libraries/JAX-RS/pom.mustache
+++ b/codegen/src/main/resources/ServiceComb/libraries/JAX-RS/pom.mustache
@@ -8,9 +8,9 @@
   </parent>
 
   <modelVersion>4.0.0</modelVersion>
-  <artifactId>provider</artifactId>
+  <artifactId>{{providerArtifactId}}</artifactId>
   <packaging>jar</packaging>
-  <name>{{artifactId}}</name>
+  <name>{{providerArtifactId}}</name>
 
   <properties>
     <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
@@ -32,7 +32,7 @@
   <dependencies>
     <dependency>
       <groupId>{{groupId}}</groupId>
-      <artifactId>model</artifactId>
+      <artifactId>{{modelArtifactId}}</artifactId>
       <version>${project.version}</version>
     </dependency>
 
diff --git a/codegen/src/main/resources/ServiceComb/libraries/POJO/pom.mustache b/codegen/src/main/resources/ServiceComb/libraries/POJO/pom.mustache
index 48b6611..0090547 100755
--- a/codegen/src/main/resources/ServiceComb/libraries/POJO/pom.mustache
+++ b/codegen/src/main/resources/ServiceComb/libraries/POJO/pom.mustache
@@ -8,9 +8,9 @@
   </parent>
 
   <modelVersion>4.0.0</modelVersion>
-  <artifactId>provider</artifactId>
+  <artifactId>{{providerArtifactId}}</artifactId>
   <packaging>jar</packaging>
-  <name>{{artifactId}}</name>
+  <name>{{providerArtifactId}}</name>
 
   <properties>
       <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
@@ -32,7 +32,7 @@
   <dependencies>
     <dependency>
       <groupId>{{groupId}}</groupId>
-      <artifactId>model</artifactId>
+      <artifactId>{{modelArtifactId}}</artifactId>
       <version>${project.version}</version>
     </dependency>
 
diff --git a/codegen/src/main/resources/ServiceComb/libraries/SpringBoot/api.mustache b/codegen/src/main/resources/ServiceComb/libraries/SpringBoot/api.mustache
index 21a8a7b..6adc4a8 100755
--- a/codegen/src/main/resources/ServiceComb/libraries/SpringBoot/api.mustache
+++ b/codegen/src/main/resources/ServiceComb/libraries/SpringBoot/api.mustache
@@ -18,7 +18,7 @@ import org.springframework.web.bind.annotation.RequestPart;
 import org.springframework.web.multipart.MultipartFile;
 
 @RestSchema(schemaId = "{{#camelcase}}{{classname}}{{/camelcase}}")
-@RequestMapping(value = "/", produces = {APPLICATION_JSON_VALUE})
+@RequestMapping(value = "{{#getRelativeBasePath}}{{basePath}}{{/getRelativeBasePath}}", produces = {APPLICATION_JSON_VALUE})
 {{#operations}}
 public class {{classname}} {
 
diff --git a/codegen/src/main/resources/ServiceComb/libraries/SpringBoot/pom.mustache b/codegen/src/main/resources/ServiceComb/libraries/SpringBoot/pom.mustache
index 5e3ae14..a8c2934 100755
--- a/codegen/src/main/resources/ServiceComb/libraries/SpringBoot/pom.mustache
+++ b/codegen/src/main/resources/ServiceComb/libraries/SpringBoot/pom.mustache
@@ -8,9 +8,9 @@
   </parent>
 
   <modelVersion>4.0.0</modelVersion>
-  <artifactId>provider</artifactId>
+  <artifactId>{{providerArtifactId}}</artifactId>
   <packaging>jar</packaging>
-  <name>provider</name>
+  <name>{{providerArtifactId}}</name>
 
   <properties>
     <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
@@ -32,7 +32,7 @@
   <dependencies>
     <dependency>
       <groupId>{{groupId}}</groupId>
-      <artifactId>model</artifactId>
+      <artifactId>{{modelArtifactId}}</artifactId>
       <version>${project.version}</version>
     </dependency>
 
diff --git a/codegen/src/main/resources/ServiceComb/libraries/SpringMVC/api.mustache b/codegen/src/main/resources/ServiceComb/libraries/SpringMVC/api.mustache
index 4dfec93..928fd60 100755
--- a/codegen/src/main/resources/ServiceComb/libraries/SpringMVC/api.mustache
+++ b/codegen/src/main/resources/ServiceComb/libraries/SpringMVC/api.mustache
@@ -18,7 +18,7 @@ import org.springframework.web.bind.annotation.RequestPart;
 import org.springframework.web.multipart.MultipartFile;
 
 @RestSchema(schemaId = "{{#camelcase}}{{classname}}{{/camelcase}}")
-@RequestMapping(value = "/", produces = {APPLICATION_JSON_VALUE})
+@RequestMapping(value = "{{#getRelativeBasePath}}{{basePath}}{{/getRelativeBasePath}}", produces = {APPLICATION_JSON_VALUE})
 {{#operations}}
 public class {{classname}} {
 {{#operation}}
diff --git a/codegen/src/main/resources/ServiceComb/libraries/SpringMVC/pom.mustache b/codegen/src/main/resources/ServiceComb/libraries/SpringMVC/pom.mustache
index 353a2ba..1751094 100755
--- a/codegen/src/main/resources/ServiceComb/libraries/SpringMVC/pom.mustache
+++ b/codegen/src/main/resources/ServiceComb/libraries/SpringMVC/pom.mustache
@@ -8,9 +8,9 @@
   </parent>
 
   <modelVersion>4.0.0</modelVersion>
-  <artifactId>provider</artifactId>
+  <artifactId>{{providerArtifactId}}</artifactId>
   <packaging>jar</packaging>
-  <name>{{artifactId}}</name>
+  <name>{{providerArtifactId}}</name>
 
   <properties>
     <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
@@ -20,7 +20,7 @@
   <dependencies>
     <dependency>
       <groupId>{{groupId}}</groupId>
-      <artifactId>model</artifactId>
+      <artifactId>{{modelArtifactId}}</artifactId>
       <version>${project.version}</version>
     </dependency>
 
diff --git a/codegen/src/main/resources/ServiceComb/model/pom.mustache b/codegen/src/main/resources/ServiceComb/model/pom.mustache
index 15086b9..aafa2e7 100755
--- a/codegen/src/main/resources/ServiceComb/model/pom.mustache
+++ b/codegen/src/main/resources/ServiceComb/model/pom.mustache
@@ -8,9 +8,9 @@
   </parent>
 
   <modelVersion>4.0.0</modelVersion>
-  <artifactId>model</artifactId>
+  <artifactId>{{modelArtifactId}}</artifactId>
   <packaging>jar</packaging>
-  <name>{{artifactId}}</name>
+  <name>{{modelArtifactId}}</name>
 
   <properties>
     <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
diff --git a/codegen/src/main/resources/ServiceComb/project/pom.mustache b/codegen/src/main/resources/ServiceComb/project/pom.mustache
index 19b43fb..aab78a1 100755
--- a/codegen/src/main/resources/ServiceComb/project/pom.mustache
+++ b/codegen/src/main/resources/ServiceComb/project/pom.mustache
@@ -16,9 +16,9 @@
   </properties>
 
   <modules>
-    <module>provider</module>
-    <module>consumer</module>
-    <module>model</module>
+    {{#modules}}
+      <module>{{module}}</module>
+    {{/modules}}
   </modules>
 
   <dependencyManagement>
diff --git a/codegen/src/test/java/org/apache/servicecomb/toolkit/codegen/GeneratorTest.java b/codegen/src/test/java/org/apache/servicecomb/toolkit/codegen/GeneratorTest.java
index 4f18260..94899ed 100755
--- a/codegen/src/test/java/org/apache/servicecomb/toolkit/codegen/GeneratorTest.java
+++ b/codegen/src/test/java/org/apache/servicecomb/toolkit/codegen/GeneratorTest.java
@@ -32,13 +32,13 @@ import org.junit.Assert;
 import org.junit.Test;
 
 import io.swagger.codegen.CodegenType;
-import io.swagger.codegen.DefaultGenerator;
 import io.swagger.codegen.config.CodegenConfigurator;
 
 public class GeneratorTest {
 
   @Test
-  public void testGenerateProgrammingModels() throws IOException, URISyntaxException {
+  public void testGenerateProgrammingModels()
+      throws IOException, URISyntaxException, NoSuchFieldException, IllegalAccessException {
 
     generateCode("SpringMVC");
     generateCode("POJO");
@@ -46,7 +46,8 @@ public class GeneratorTest {
     generateCode("SpringBoot");
   }
 
-  private void generateCode(String programmingModel) throws IOException, URISyntaxException {
+  private void generateCode(String programmingModel)
+      throws IOException, URISyntaxException, IllegalAccessException, NoSuchFieldException {
 
     Path tempDir = Files.createTempDirectory(null);
     Path specFilePath = Paths.get(GeneratorTest.class.getClassLoader().getResource("swagger.yaml").toURI());
@@ -60,7 +61,7 @@ public class GeneratorTest {
     configurator.addAdditionalProperty(GeneratorExternalConfigConstant.CONSUMER_PROJECT_NAME, "mock-consumer");
     configurator.addAdditionalProperty(GeneratorExternalConfigConstant.MODEL_PROJECT_NAME, "mock-model");
     DefaultCodeGenerator codeGenerator = new DefaultCodeGenerator();
-    codeGenerator.configure(Collections.singletonMap("configurator", configurator));
+    codeGenerator.configure(Collections.singletonMap("configurators", Collections.singletonList(configurator)));
 
     try {
       codeGenerator.generate();
@@ -70,7 +71,7 @@ public class GeneratorTest {
     }
 
     Object internalGenerator = ReflectUtils.getProperty(codeGenerator, "generator");
-    Assert.assertEquals(DefaultGenerator.class, internalGenerator.getClass());
+    Assert.assertEquals(MultiContractGenerator.class, internalGenerator.getClass());
     Object swaggerCodegenConfig = ReflectUtils.getProperty(internalGenerator, "config");
     Assert.assertEquals(ServiceCombCodegen.class, swaggerCodegenConfig.getClass());
     Assert.assertEquals("ServiceComb", ((ServiceCombCodegen) swaggerCodegenConfig).getName());
diff --git a/codegen/src/test/java/org/apache/servicecomb/toolkit/codegen/ReflectUtils.java b/codegen/src/test/java/org/apache/servicecomb/toolkit/codegen/ReflectUtils.java
index b5892a3..601d7ee 100755
--- a/codegen/src/test/java/org/apache/servicecomb/toolkit/codegen/ReflectUtils.java
+++ b/codegen/src/test/java/org/apache/servicecomb/toolkit/codegen/ReflectUtils.java
@@ -21,18 +21,27 @@ import java.lang.reflect.Field;
 
 class ReflectUtils {
 
-  static Object getProperty(Object obj, String propName) {
+  static Object getProperty(Object obj, String propName) throws IllegalAccessException, NoSuchFieldException {
 
+    Field propFiled = getFiled(obj.getClass(), propName);
+
+    if (propFiled == null) {
+      return null;
+    }
+
+    propFiled.setAccessible(true);
+    return propFiled.get(obj);
+  }
+
+  private static Field getFiled(Class cls, String propName) throws NoSuchFieldException {
     try {
-      Field propFiled = obj.getClass().getDeclaredField(propName);
-      propFiled.setAccessible(true);
-      return propFiled.get(obj);
+      return cls.getDeclaredField(propName);
     } catch (NoSuchFieldException e) {
-      e.printStackTrace();
-    } catch (IllegalAccessException e) {
-      e.printStackTrace();
+      if (cls.getSuperclass() != null) {
+        return getFiled(cls.getSuperclass(), propName);
+      } else {
+        throw new NoSuchFieldException("No such field: " + propName);
+      }
     }
-
-    return null;
   }
 }
diff --git a/common/src/main/java/org/apache/servicecomb/toolkit/common/FileUtils.java b/common/src/main/java/org/apache/servicecomb/toolkit/common/FileUtils.java
index 7915216..cbeb093 100755
--- a/common/src/main/java/org/apache/servicecomb/toolkit/common/FileUtils.java
+++ b/common/src/main/java/org/apache/servicecomb/toolkit/common/FileUtils.java
@@ -39,7 +39,7 @@ public class FileUtils {
 
     File path = new File(pathName);
     if (path.exists()) {
-      deleteDirectory(pathName);
+      return;
     }
 
     if (!path.mkdirs()) {
@@ -78,12 +78,14 @@ public class FileUtils {
     return filesGroup;
   }
 
-  private static void deleteDirectory(String pathName) throws IOException {
+  public static void deleteDirectory(String pathName) throws IOException {
 
     File path = new File(pathName);
 
     if (!path.isDirectory()) {
-      Files.delete(Paths.get(pathName));
+      if(path.exists()){
+        Files.delete(Paths.get(pathName));
+      }
       return;
     }
 
diff --git a/toolkit-maven-plugin/src/main/java/org/apache/servicecomb/toolkit/plugin/GenerateMojo.java b/toolkit-maven-plugin/src/main/java/org/apache/servicecomb/toolkit/plugin/GenerateMojo.java
index 1ea72a1..cb598a9 100755
--- a/toolkit-maven-plugin/src/main/java/org/apache/servicecomb/toolkit/plugin/GenerateMojo.java
+++ b/toolkit-maven-plugin/src/main/java/org/apache/servicecomb/toolkit/plugin/GenerateMojo.java
@@ -74,14 +74,19 @@ public class GenerateMojo extends AbstractMojo {
 
     if (MavenPluginUtil.isParentProject(project)) {
       for (MavenProject subProject : project.getCollectedProjects()) {
-        generate(subProject);
+        generateContract(subProject);
+        generateDocument(subProject);
       }
+
+      generateCode(project);
     } else {
-      generate(project);
+      generateContract(project);
+      generateDocument(project);
+      generateCode(project);
     }
   }
 
-  private void generate(MavenProject project) {
+  private void generateContract(MavenProject project) {
 
     switch (SourceType.valueOf(sourceType.toUpperCase())) {
       case CODE:
@@ -95,7 +100,7 @@ public class GenerateMojo extends AbstractMojo {
         }
 
         GenerateUtil.generateContract(project, contractOutput, contractFileType, "default");
-        contractLocation = contractOutput;
+        contractLocation = outputDirectory + File.separator + "contract";
         if (Objects.requireNonNull(new File(contractOutput).listFiles()).length == 0) {
           //noinspection ResultOfMethodCallIgnored
           new File(contractOutput).delete();
@@ -117,7 +122,9 @@ public class GenerateMojo extends AbstractMojo {
       default:
         throw new RuntimeException("Not support source type " + sourceType);
     }
+  }
 
+  private void generateCode(MavenProject project) {
     //generate microservice project
     if (service == null) {
       LOGGER.info("Cannot generate code without service configuration");
@@ -138,6 +145,9 @@ public class GenerateMojo extends AbstractMojo {
         throw new RuntimeException("Failed to generate code", e);
       }
     }
+  }
+
+  private void generateDocument(MavenProject project) {
 
     //generate document
     String documentOutput =
diff --git a/toolkit-maven-plugin/src/main/java/org/apache/servicecomb/toolkit/plugin/GenerateUtil.java b/toolkit-maven-plugin/src/main/java/org/apache/servicecomb/toolkit/plugin/GenerateUtil.java
index 15af20e..4c98736 100755
--- a/toolkit-maven-plugin/src/main/java/org/apache/servicecomb/toolkit/plugin/GenerateUtil.java
+++ b/toolkit-maven-plugin/src/main/java/org/apache/servicecomb/toolkit/plugin/GenerateUtil.java
@@ -25,8 +25,10 @@ import java.nio.file.Path;
 import java.nio.file.Paths;
 import java.nio.file.SimpleFileVisitor;
 import java.nio.file.attribute.BasicFileAttributes;
+import java.util.ArrayList;
 import java.util.Collections;
 import java.util.HashMap;
+import java.util.List;
 import java.util.Map;
 import java.util.Objects;
 
@@ -37,13 +39,20 @@ import org.apache.servicecomb.toolkit.CodeGenerator;
 import org.apache.servicecomb.toolkit.ContractsGenerator;
 import org.apache.servicecomb.toolkit.DocGenerator;
 import org.apache.servicecomb.toolkit.GeneratorFactory;
+import org.apache.servicecomb.toolkit.codegen.GeneratorExternalConfigConstant;
 import org.apache.servicecomb.toolkit.codegen.ProjectMetaConstant;
 
 import io.swagger.codegen.config.CodegenConfigurator;
 
 class GenerateUtil {
 
-  static void generateContract(MavenProject project, String contractOutput, String contractFileType,
+  private static String providerProjectNameSuffix = "-provider";
+
+  private static String consumerProjectNameSuffix = "-consumer";
+
+  private static String modelProjectNameSuffix = "-model";
+
+  public static void generateContract(MavenProject project, String contractOutput, String contractFileType,
       String type) {
 
     Map<String, Object> contractConfig = new HashMap<>();
@@ -75,6 +84,9 @@ class GenerateUtil {
       @Override
       public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IOException {
 
+        if (Files.isDirectory(file)) {
+          return super.visitFile(file, attrs);
+        }
         Map<String, Object> docGeneratorConfig = new HashMap<>();
 
         docGeneratorConfig.put("contractContent", SwaggerUtils.parseSwagger(file.toUri().toURL()));
@@ -96,36 +108,51 @@ class GenerateUtil {
       throw new RuntimeException("Cannot found code generator's implementation");
     }
 
-    CodegenConfigurator configurator = new CodegenConfigurator();
-    configurator.setOutputDir(codeOutput)
-        .setLang("ServiceComb")
-        .setApiPackage(service.getPackageName())
-        .setGroupId(service.getGroupId())
-        .setArtifactId(service.getArtifactId())
-        .setModelPackage(service.getPackageName())
-        .setLibrary(service.getProgrammingModel())
-        .addAdditionalProperty("mainClassPackage", service.getPackageName())
-        .setArtifactVersion(service.getArtifactVersion())
-        .addAdditionalProperty(ProjectMetaConstant.SERVICE_TYPE, service.getServiceType());
-
-    configurator.getAdditionalProperties().putAll(externalConfig);
-
     File contractFile = new File(contractLocation);
     if (contractFile.isDirectory()) {
 
+      List<CodegenConfigurator> configurators = new ArrayList<>();
       Files.walkFileTree(Paths.get(contractFile.toURI()), new SimpleFileVisitor<Path>() {
         @Override
         public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IOException {
-          configurator.setInputSpec(file.toFile().getCanonicalPath());
-          Objects.requireNonNull(codeGenerator).configure(Collections.singletonMap("configurator", configurator));
-          codeGenerator.generate();
+          if (Files.isDirectory(file)) {
+            return super.visitFile(file, attrs);
+          }
+
+          CodegenConfigurator configurator = new CodegenConfigurator();
+          configurator.setOutputDir(codeOutput)
+              .setLang("ServiceComb")
+              .setApiPackage(service.getPackageName())
+              .setGroupId(service.getGroupId())
+              .setArtifactId(service.getArtifactId())
+              .setModelPackage(service.getPackageName())
+              .setLibrary(service.getProgrammingModel())
+              .addAdditionalProperty("mainClassPackage", service.getPackageName())
+              .setArtifactVersion(service.getArtifactVersion())
+              .addAdditionalProperty(ProjectMetaConstant.SERVICE_TYPE, service.getServiceType())
+              .addAdditionalProperty(GeneratorExternalConfigConstant.PROVIDER_PROJECT_NAME,
+                  file.getParent().getFileName() + providerProjectNameSuffix)
+              .addAdditionalProperty(GeneratorExternalConfigConstant.CONSUMER_PROJECT_NAME,
+                  file.getParent().getFileName() + consumerProjectNameSuffix)
+              .addAdditionalProperty(GeneratorExternalConfigConstant.MODEL_PROJECT_NAME,
+                  file.getParent().getFileName() + modelProjectNameSuffix)
+              .addAdditionalProperty("apiName", file.toFile().getName().split("\\.")[0])
+              .addAdditionalProperty("microserviceName", file.getParent().getFileName().toString());
 
+          configurator.setInputSpec(file.toFile().getCanonicalPath());
+          configurators.add(configurator);
           return super.visitFile(file, attrs);
         }
       });
+
+      Objects.requireNonNull(codeGenerator).configure(Collections.singletonMap("configurators", configurators));
+      codeGenerator.generate();
     } else {
+
+      CodegenConfigurator configurator = new CodegenConfigurator();
       configurator.setInputSpec(contractLocation);
-      Objects.requireNonNull(codeGenerator).configure(Collections.singletonMap("configurator", configurator));
+      Objects.requireNonNull(codeGenerator)
+          .configure(Collections.singletonMap("configurators", Collections.singletonList(configurator)));
       codeGenerator.generate();
     }
   }
diff --git a/toolkit-maven-plugin/src/test/java/org/apache/servicecomb/toolkit/plugin/GenerateMojoTest.java b/toolkit-maven-plugin/src/test/java/org/apache/servicecomb/toolkit/plugin/GenerateMojoTest.java
index a1fa401..55924a1 100755
--- a/toolkit-maven-plugin/src/test/java/org/apache/servicecomb/toolkit/plugin/GenerateMojoTest.java
+++ b/toolkit-maven-plugin/src/test/java/org/apache/servicecomb/toolkit/plugin/GenerateMojoTest.java
@@ -69,12 +69,13 @@ public class GenerateMojoTest {
     try {
       outputDirectory = "target/GenerateMojoTest";
 
+      FileUtils.deleteDirectory(outputDirectory);
       testResourcesEx.setVariableValueToObject("sourceType", "code");
       testResourcesEx.setVariableValueToObject("outputDirectory", outputDirectory);
 
       testResourcesEx.execute();
 
-      assertFalse(new File(testResourcesEx.getVariableValueFromObject("contractLocation")).exists());
+      assertFalse(new File(testResourcesEx.getVariableValueFromObject("contractLocation")).listFiles().length != 0);
     } catch (MojoFailureException e) {
       fail("Run 'testGenerateMojo' failed, unexpected to catch MojoFailureException: " + e.getMessage());
     }
@@ -150,6 +151,7 @@ public class GenerateMojoTest {
 
     testResourcesEx.setVariableValueToObject("sourceType", "code");
     testResourcesEx.setVariableValueToObject("outputDirectory", outputDirectory);
+    FileUtils.deleteDirectory(projectOutput);
     FileUtils.createDirectory(projectOutput);
     testResourcesEx.setVariableValueToObject("service", null);
     testResourcesEx.execute();