You are viewing a plain text version of this content. The canonical link for it is here.
Posted to dev@servicecomb.apache.org by GitBox <gi...@apache.org> on 2018/01/20 06:01:43 UTC

[GitHub] liubao68 closed pull request #522: [SCB-266] Support generic rest

liubao68 closed pull request #522: [SCB-266] Support generic rest
URL: https://github.com/apache/incubator-servicecomb-java-chassis/pull/522
 
 
   

This is a PR merged from a forked repository.
As GitHub hides the original diff on merge, it is displayed below for
the sake of provenance:

As this is a foreign pull request (from a fork), the diff is supplied
below (as it won't show otherwise due to GitHub magic):

diff --git a/common/common-javassist/src/main/java/org/apache/servicecomb/common/javassist/JavassistUtils.java b/common/common-javassist/src/main/java/org/apache/servicecomb/common/javassist/JavassistUtils.java
index f75247ff4..e3691744f 100644
--- a/common/common-javassist/src/main/java/org/apache/servicecomb/common/javassist/JavassistUtils.java
+++ b/common/common-javassist/src/main/java/org/apache/servicecomb/common/javassist/JavassistUtils.java
@@ -20,6 +20,7 @@
 import static java.util.Locale.ENGLISH;
 
 import java.lang.reflect.Modifier;
+import java.nio.charset.StandardCharsets;
 import java.util.Arrays;
 import java.util.IdentityHashMap;
 import java.util.List;
@@ -30,6 +31,7 @@
 import org.springframework.util.ClassUtils;
 
 import com.fasterxml.jackson.databind.JavaType;
+import com.google.common.hash.Hashing;
 
 import javassist.CannotCompileException;
 import javassist.ClassPool;
@@ -82,6 +84,25 @@ private JavassistUtils() {
     return createEnum(null, clsName, values);
   }
 
+  @SuppressWarnings("rawtypes")
+  public static Class<? extends Enum> getOrCreateEnumWithPackageName(ClassLoader classLoader, String packageName,
+      List<String> enums) {
+    String strEnums = enums.toString();
+    String enumClsName =
+        packageName + ".Enum_" + Hashing.sha256().hashString(strEnums, StandardCharsets.UTF_8).toString();
+    return JavassistUtils.getOrCreateEnumWithClassName(classLoader, enumClsName, enums);
+  }
+
+  @SuppressWarnings({"rawtypes", "unchecked"})
+  public static synchronized Class<? extends Enum> getOrCreateEnumWithClassName(ClassLoader classLoader, String clsName,
+      List<String> values) {
+    try {
+      return (Class<? extends Enum>) classLoader.loadClass(clsName);
+    } catch (ClassNotFoundException e) {
+      return createEnum(classLoader, clsName, values);
+    }
+  }
+
   @SuppressWarnings({"rawtypes", "unchecked"})
   public static Class<? extends Enum> createEnum(ClassLoader classLoader, String clsName, List<String> values) {
     if (values == null || values.size() == 0) {
@@ -148,7 +169,6 @@ private static void addEnumValuesMethod(CtClass ctClass, List<String> values) th
       if (config.isIntf()) {
         ctClass = classPool.makeInterface(config.getClassName());
       } else {
-
         ctClass = classPool.makeClass(config.getClassName());
       }
     }
diff --git a/common/common-javassist/src/test/java/org/apache/servicecomb/common/javassist/TestJavassistUtils.java b/common/common-javassist/src/test/java/org/apache/servicecomb/common/javassist/TestJavassistUtils.java
index f7f11bbe9..73c4c7ff1 100644
--- a/common/common-javassist/src/test/java/org/apache/servicecomb/common/javassist/TestJavassistUtils.java
+++ b/common/common-javassist/src/test/java/org/apache/servicecomb/common/javassist/TestJavassistUtils.java
@@ -18,18 +18,24 @@
 package org.apache.servicecomb.common.javassist;
 
 import java.lang.reflect.Field;
+import java.lang.reflect.InvocationTargetException;
 import java.lang.reflect.Method;
 import java.lang.reflect.Type;
+import java.nio.charset.StandardCharsets;
+import java.util.Arrays;
 import java.util.List;
 import java.util.Map;
 import java.util.Set;
 
+import javax.ws.rs.core.Response.Status;
+
 import org.apache.servicecomb.foundation.common.utils.ReflectUtils;
 import org.junit.Assert;
 import org.junit.Test;
 
 import com.fasterxml.jackson.databind.JavaType;
 import com.fasterxml.jackson.databind.type.TypeFactory;
+import com.google.common.hash.Hashing;
 
 import javassist.ClassPool;
 import mockit.Deencapsulation;
@@ -160,14 +166,71 @@ public void multiWrapper() throws Exception {
     Assert.assertEquals("test", fieldValues[1]);
   }
 
+  @Test
+  public void getOrCreateEnumWithClassName_get() {
+    Class<?> cls = Status.class;
+
+    Assert.assertSame(cls,
+        JavassistUtils.getOrCreateEnumWithClassName(Thread.currentThread().getContextClassLoader(),
+            Status.class.getName(),
+            null));
+  }
+
+  @Test
+  public void getOrCreateEnumWithClassName_create()
+      throws NoSuchMethodException, IllegalAccessException, InvocationTargetException {
+    String name = "cse.ut.EnumAbc";
+    Assert.assertNull(ClassPool.getDefault().getOrNull(name));
+
+    @SuppressWarnings("rawtypes")
+    Class<? extends Enum> cls =
+        JavassistUtils
+            .getOrCreateEnumWithClassName(Thread.currentThread().getContextClassLoader(),
+                name,
+                Arrays.asList("a", "b"));
+    checkEnum(name, cls);
+  }
+
+  @Test
+  public void getOrCreateEnumWithPackageName()
+      throws NoSuchMethodException, IllegalAccessException, InvocationTargetException {
+    List<String> enums = Arrays.asList("a", "b");
+    String packageName = "cse.ut";
+    String clsName =
+        packageName + ".Enum_" + Hashing.sha256().hashString(enums.toString(), StandardCharsets.UTF_8).toString();
+    Assert.assertNull(ClassPool.getDefault().getOrNull(clsName));
+
+    @SuppressWarnings("rawtypes")
+    Class<? extends Enum> cls =
+        JavassistUtils
+            .getOrCreateEnumWithPackageName(Thread.currentThread().getContextClassLoader(),
+                packageName,
+                Arrays.asList("a", "b"));
+
+    checkEnum(clsName, cls);
+    Assert.assertSame(cls,
+        JavassistUtils
+            .getOrCreateEnumWithPackageName(Thread.currentThread().getContextClassLoader(),
+                packageName,
+                Arrays.asList("a", "b")));
+
+  }
+
   @Test
   public void testEnum() throws Exception {
+    String name = "cse.ut.EnumAbc2";
     @SuppressWarnings("rawtypes")
-    Class<? extends Enum> cls = JavassistUtils.createEnum("cse.ut.EnumAbc", "a", "b");
+    Class<? extends Enum> cls = JavassistUtils.createEnum(name, "a", "b");
+    checkEnum(name, cls);
+  }
+
+  @SuppressWarnings("rawtypes")
+  protected void checkEnum(String expectName, Class<? extends Enum> cls)
+      throws NoSuchMethodException, IllegalAccessException, InvocationTargetException {
     Method method = cls.getMethod("values");
     Enum<?>[] values = (Enum<?>[]) method.invoke(null);
 
-    Assert.assertEquals("cse.ut.EnumAbc", cls.getName());
+    Assert.assertEquals(expectName, cls.getName());
     Assert.assertEquals(2, values.length);
     Assert.assertEquals("a", values[0].name());
     Assert.assertEquals(0, values[0].ordinal());
@@ -192,8 +255,10 @@ public void testGetNameForGenerateCode() {
 
   @Test
   public void managerClassPool() {
-    ClassLoader classLoader1 = new ClassLoader() { };
-    ClassLoader classLoader2 = new ClassLoader() { };
+    ClassLoader classLoader1 = new ClassLoader() {
+    };
+    ClassLoader classLoader2 = new ClassLoader() {
+    };
 
     ClassPool p1 = Deencapsulation.invoke(JavassistUtils.class, "getOrCreateClassPool", classLoader1);
     ClassPool p2 = Deencapsulation.invoke(JavassistUtils.class, "getOrCreateClassPool", classLoader2);
diff --git a/common/common-protobuf/src/main/java/org/apache/servicecomb/codec/protobuf/utils/ProtobufSchemaUtils.java b/common/common-protobuf/src/main/java/org/apache/servicecomb/codec/protobuf/utils/ProtobufSchemaUtils.java
index 14fb7a37f..f1c985fed 100644
--- a/common/common-protobuf/src/main/java/org/apache/servicecomb/codec/protobuf/utils/ProtobufSchemaUtils.java
+++ b/common/common-protobuf/src/main/java/org/apache/servicecomb/codec/protobuf/utils/ProtobufSchemaUtils.java
@@ -158,7 +158,9 @@ public static WrapSchema getOrCreateSchema(Type type) {
       WrapClassConfig config = new WrapClassConfig();
       config.setType(WrapType.NORMAL_WRAP);
 
-      config.setClassName("gen.wrap.protobuf." + key.replaceAll("[<>]", "_").replace("[", "array_"));
+      config.setClassName(
+          "gen.wrap.protobuf." +
+              org.apache.servicecomb.swagger.generator.core.utils.ClassUtils.correctClassName(key));
       if (!Void.TYPE.isAssignableFrom(javaType.getRawClass())) {
         config.addField("field0", javaType);
       }
diff --git a/common/common-rest/src/main/java/org/apache/servicecomb/common/rest/codec/RestObjectMapper.java b/common/common-rest/src/main/java/org/apache/servicecomb/common/rest/codec/RestObjectMapper.java
index 3e279164b..ee48c49e3 100644
--- a/common/common-rest/src/main/java/org/apache/servicecomb/common/rest/codec/RestObjectMapper.java
+++ b/common/common-rest/src/main/java/org/apache/servicecomb/common/rest/codec/RestObjectMapper.java
@@ -17,6 +17,9 @@
 
 package org.apache.servicecomb.common.rest.codec;
 
+import java.text.FieldPosition;
+import java.util.Date;
+
 import com.fasterxml.jackson.core.JsonParser.Feature;
 import com.fasterxml.jackson.databind.DeserializationFeature;
 import com.fasterxml.jackson.databind.JavaType;
@@ -24,6 +27,7 @@
 import com.fasterxml.jackson.databind.SerializationFeature;
 import com.fasterxml.jackson.databind.type.TypeFactory;
 import com.fasterxml.jackson.databind.util.ISO8601DateFormat;
+import com.fasterxml.jackson.databind.util.ISO8601Utils;
 
 public final class RestObjectMapper extends ObjectMapper {
   public static final RestObjectMapper INSTANCE = new RestObjectMapper();
@@ -34,7 +38,18 @@
 
   private RestObjectMapper() {
     // swagger???date??ISO8601??????????????????cse???????
-    setDateFormat(new ISO8601DateFormat());
+    setDateFormat(new ISO8601DateFormat() {
+      private static final long serialVersionUID = 7798938088541203312L;
+
+      // to support millis
+      @Override
+      public StringBuffer format(Date date, StringBuffer toAppendTo, FieldPosition fieldPosition) {
+        String value = ISO8601Utils.format(date, true);
+        toAppendTo.append(value);
+        return toAppendTo;
+      }
+    });
+
     getFactory().disable(Feature.AUTO_CLOSE_SOURCE);
     disable(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES);
     disable(SerializationFeature.FAIL_ON_EMPTY_BEANS);
diff --git a/common/common-rest/src/test/java/org/apache/servicecomb/common/rest/codec/param/TestCookieProcessor.java b/common/common-rest/src/test/java/org/apache/servicecomb/common/rest/codec/param/TestCookieProcessor.java
index 59fc8215c..e896369c2 100644
--- a/common/common-rest/src/test/java/org/apache/servicecomb/common/rest/codec/param/TestCookieProcessor.java
+++ b/common/common-rest/src/test/java/org/apache/servicecomb/common/rest/codec/param/TestCookieProcessor.java
@@ -131,7 +131,7 @@ public void testSetValue() throws Exception {
   @Test
   public void testSetValueDate() throws Exception {
     Date date = new Date();
-    String strDate = ISO8601Utils.format(date);
+    String strDate = ISO8601Utils.format(date, true);
 
     createClientRequest();
 
diff --git a/common/common-rest/src/test/java/org/apache/servicecomb/common/rest/codec/param/TestHeaderProcessor.java b/common/common-rest/src/test/java/org/apache/servicecomb/common/rest/codec/param/TestHeaderProcessor.java
index 1e9d9fa51..649fa72a0 100644
--- a/common/common-rest/src/test/java/org/apache/servicecomb/common/rest/codec/param/TestHeaderProcessor.java
+++ b/common/common-rest/src/test/java/org/apache/servicecomb/common/rest/codec/param/TestHeaderProcessor.java
@@ -173,7 +173,7 @@ public void testSetValueNull() throws Exception {
   @Test
   public void testSetValueDate() throws Exception {
     Date date = new Date();
-    String strDate = ISO8601Utils.format(date);
+    String strDate = ISO8601Utils.format(date, true);
 
     createClientRequest();
 
diff --git a/demo/demo-schema/src/main/java/org/apache/servicecomb/demo/Generic.java b/demo/demo-schema/src/main/java/org/apache/servicecomb/demo/Generic.java
new file mode 100644
index 000000000..401bb52a2
--- /dev/null
+++ b/demo/demo-schema/src/main/java/org/apache/servicecomb/demo/Generic.java
@@ -0,0 +1,21 @@
+/*
+ * 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.demo;
+
+public class Generic<T> {
+  public T value;
+}
diff --git a/demo/demo-schema/src/main/java/org/apache/servicecomb/demo/server/User.java b/demo/demo-schema/src/main/java/org/apache/servicecomb/demo/server/User.java
index e7d4e94d4..c55ed1e88 100644
--- a/demo/demo-schema/src/main/java/org/apache/servicecomb/demo/server/User.java
+++ b/demo/demo-schema/src/main/java/org/apache/servicecomb/demo/server/User.java
@@ -17,6 +17,10 @@
 
 package org.apache.servicecomb.demo.server;
 
+import org.apache.servicecomb.foundation.common.utils.JsonUtils;
+
+import com.fasterxml.jackson.core.JsonProcessingException;
+
 public class User {
   private String name = "nameA";
 
@@ -62,4 +66,12 @@ public void setIndex(int index) {
   public String toString() {
     return "User [name=" + name + ", age=" + age + ", index=" + index + "]";
   }
+
+  public String jsonString() {
+    try {
+      return JsonUtils.writeValueAsString(this);
+    } catch (JsonProcessingException e) {
+      throw new IllegalStateException(e);
+    }
+  }
 }
diff --git a/demo/demo-springmvc/springmvc-client/src/main/java/org/apache/servicecomb/demo/springmvc/client/CodeFirstRestTemplateSpringmvc.java b/demo/demo-springmvc/springmvc-client/src/main/java/org/apache/servicecomb/demo/springmvc/client/CodeFirstRestTemplateSpringmvc.java
index 9076f852a..722f7d26d 100644
--- a/demo/demo-springmvc/springmvc-client/src/main/java/org/apache/servicecomb/demo/springmvc/client/CodeFirstRestTemplateSpringmvc.java
+++ b/demo/demo-springmvc/springmvc-client/src/main/java/org/apache/servicecomb/demo/springmvc/client/CodeFirstRestTemplateSpringmvc.java
@@ -68,6 +68,8 @@
 
   private TestObject testObject = new TestObject();
 
+  private TestGeneric testGeneric = new TestGeneric();
+
   @Override
   protected void testOnlyRest(RestTemplate template, String cseUrlPrefix) {
     try {
@@ -78,6 +80,7 @@ protected void testOnlyRest(RestTemplate template, String cseUrlPrefix) {
 
     testResponse.runRest();
     testObject.runRest();
+    testGeneric.runRest();
 
     super.testOnlyRest(template, cseUrlPrefix);
   }
@@ -86,6 +89,7 @@ protected void testOnlyRest(RestTemplate template, String cseUrlPrefix) {
   protected void testOnlyHighway(RestTemplate template, String cseUrlPrefix) {
     testResponse.runHighway();
     testObject.runHighway();
+    testGeneric.runHighway();
 
     super.testOnlyHighway(template, cseUrlPrefix);
   }
@@ -94,6 +98,7 @@ protected void testOnlyHighway(RestTemplate template, String cseUrlPrefix) {
   protected void testAllTransport(String microserviceName, RestTemplate template, String cseUrlPrefix) {
     testResponse.runAllTransport();
     testObject.runAllTransport();
+    testGeneric.runAllTransport();
 
     testResponseEntity("springmvc", template, cseUrlPrefix);
     testCodeFirstTestForm(template, cseUrlPrefix);
diff --git a/demo/demo-springmvc/springmvc-client/src/main/java/org/apache/servicecomb/demo/springmvc/client/CodeFirstSprigmvcIntf.java b/demo/demo-springmvc/springmvc-client/src/main/java/org/apache/servicecomb/demo/springmvc/client/CodeFirstSpringmvcIntf.java
similarity index 75%
rename from demo/demo-springmvc/springmvc-client/src/main/java/org/apache/servicecomb/demo/springmvc/client/CodeFirstSprigmvcIntf.java
rename to demo/demo-springmvc/springmvc-client/src/main/java/org/apache/servicecomb/demo/springmvc/client/CodeFirstSpringmvcIntf.java
index 12b6b544f..5109d19bd 100644
--- a/demo/demo-springmvc/springmvc-client/src/main/java/org/apache/servicecomb/demo/springmvc/client/CodeFirstSprigmvcIntf.java
+++ b/demo/demo-springmvc/springmvc-client/src/main/java/org/apache/servicecomb/demo/springmvc/client/CodeFirstSpringmvcIntf.java
@@ -23,11 +23,13 @@
 import javax.xml.ws.Holder;
 
 import org.apache.servicecomb.demo.EmptyObject;
+import org.apache.servicecomb.demo.Generic;
 import org.apache.servicecomb.demo.server.User;
 import org.apache.servicecomb.swagger.invocation.Response;
+import org.springframework.http.HttpStatus;
 import org.springframework.http.ResponseEntity;
 
-public interface CodeFirstSprigmvcIntf {
+public interface CodeFirstSpringmvcIntf {
   ResponseEntity<Date> responseEntity(Date date);
 
   Response cseResponse();
@@ -42,5 +44,15 @@
 
   Holder<Object> testHolderObject(Holder<Object> input);
 
-  Holder<User> testGeneric(Holder<User> input);
+  Holder<User> testHolderUser(Holder<User> input);
+
+  Generic<User> testGenericUser(Generic<User> input);
+
+  Generic<Long> testGenericLong(Generic<Long> input);
+
+  Generic<Date> testGenericDate(Generic<Date> input);
+
+  Generic<HttpStatus> testGenericEnum(Generic<HttpStatus> input);
+
+  Generic<Generic<User>> testGenericGenericUser(Generic<Generic<User>> input);
 }
diff --git a/demo/demo-springmvc/springmvc-client/src/main/java/org/apache/servicecomb/demo/springmvc/client/TestGeneric.java b/demo/demo-springmvc/springmvc-client/src/main/java/org/apache/servicecomb/demo/springmvc/client/TestGeneric.java
new file mode 100644
index 000000000..34a3475a9
--- /dev/null
+++ b/demo/demo-springmvc/springmvc-client/src/main/java/org/apache/servicecomb/demo/springmvc/client/TestGeneric.java
@@ -0,0 +1,130 @@
+/*
+ * 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.demo.springmvc.client;
+
+import java.util.Date;
+
+import javax.xml.ws.Holder;
+
+import org.apache.servicecomb.demo.Generic;
+import org.apache.servicecomb.demo.TestMgr;
+import org.apache.servicecomb.demo.server.User;
+import org.apache.servicecomb.provider.pojo.Invoker;
+import org.apache.servicecomb.provider.springmvc.reference.CseRestTemplate;
+import org.springframework.http.HttpStatus;
+import org.springframework.web.client.RestTemplate;
+
+import com.fasterxml.jackson.databind.util.ISO8601Utils;
+
+public class TestGeneric {
+  private CodeFirstSpringmvcIntf intf;
+
+  private RestTemplate restTemplate = new CseRestTemplate();
+
+  private String prefix = "cse://springmvc/codeFirstSpringmvc";
+
+  public TestGeneric() {
+    intf = Invoker.createProxy("springmvc", "codeFirst", CodeFirstSpringmvcIntf.class);
+  }
+
+  public void runRest() {
+    testHolderUser_rest();
+    testGenericUser_rest();
+    testGenericGenericUser_rest();
+    testGenericLong_rest();
+    testGenericDate_rest();
+    testGenericEnum_rest();
+  }
+
+  public void runHighway() {
+  }
+
+  public void runAllTransport() {
+  }
+
+  @SuppressWarnings("unchecked")
+  private void testGenericEnum_rest() {
+    Generic<HttpStatus> generic = new Generic<>();
+    generic.value = HttpStatus.OK;
+    Generic<HttpStatus> result = intf.testGenericEnum(generic);
+    TestMgr.check(HttpStatus.OK, result.value);
+    TestMgr.check(HttpStatus.class, result.value.getClass());
+
+    result = restTemplate.postForObject(prefix + "/genericEnum", generic, Generic.class);
+    TestMgr.check(HttpStatus.OK, result.value);
+    TestMgr.check(HttpStatus.class, result.value.getClass());
+  }
+
+  @SuppressWarnings("unchecked")
+  private void testGenericDate_rest() {
+    Generic<Date> generic = new Generic<>();
+    generic.value = new Date(1001);
+    Generic<Date> result = intf.testGenericDate(generic);
+    TestMgr.check("1970-01-01T00:00:01.001Z", ISO8601Utils.format(result.value, true));
+    TestMgr.check(Date.class, result.value.getClass());
+
+    result = restTemplate.postForObject(prefix + "/genericDate", generic, Generic.class);
+    TestMgr.check("1970-01-01T00:00:01.001Z", ISO8601Utils.format(result.value, true));
+    TestMgr.check(Date.class, result.value.getClass());
+  }
+
+  @SuppressWarnings("unchecked")
+  private void testGenericLong_rest() {
+    Generic<Long> generic = new Generic<>();
+    generic.value = 100L;
+    Generic<Long> result = intf.testGenericLong(generic);
+    TestMgr.check(100, result.value);
+    TestMgr.check(Long.class, result.value.getClass());
+
+    result = restTemplate.postForObject(prefix + "/genericLong", generic, Generic.class);
+    TestMgr.check(100, result.value);
+    TestMgr.check(Long.class, result.value.getClass());
+  }
+
+  @SuppressWarnings("unchecked")
+  private void testGenericGenericUser_rest() {
+    Generic<Generic<User>> generic = new Generic<>();
+    generic.value = new Generic<>();
+    generic.value.value = new User();
+    Generic<Generic<User>> result = intf.testGenericGenericUser(generic);
+    TestMgr.check("{\"name\":\"nameA\",\"age\":100,\"index\":0,\"names\":null}", result.value.value.jsonString());
+
+    result = restTemplate.postForObject(prefix + "/genericGenericUser", generic, Generic.class);
+    TestMgr.check("{\"name\":\"nameA\",\"age\":100,\"index\":0,\"names\":null}", result.value.value.jsonString());
+  }
+
+  @SuppressWarnings("unchecked")
+  private void testGenericUser_rest() {
+    Generic<User> generic = new Generic<>();
+    generic.value = new User();
+    Generic<User> result = intf.testGenericUser(generic);
+    TestMgr.check("{\"name\":\"nameA\",\"age\":100,\"index\":0,\"names\":null}", result.value.jsonString());
+
+    result = restTemplate.postForObject(prefix + "/genericUser", generic, Generic.class);
+    TestMgr.check("{\"name\":\"nameA\",\"age\":100,\"index\":0,\"names\":null}", result.value.jsonString());
+  }
+
+  @SuppressWarnings("unchecked")
+  private void testHolderUser_rest() {
+    Holder<User> holder = new Holder<>(new User());
+    Holder<User> result = intf.testHolderUser(holder);
+    TestMgr.check("{\"name\":\"nameA\",\"age\":100,\"index\":0,\"names\":null}", result.value.jsonString());
+
+    result = restTemplate.postForObject(prefix + "/holderUser", holder, Holder.class);
+    TestMgr.check("{\"name\":\"nameA\",\"age\":100,\"index\":0,\"names\":null}", result.value.jsonString());
+  }
+}
diff --git a/demo/demo-springmvc/springmvc-client/src/main/java/org/apache/servicecomb/demo/springmvc/client/TestObject.java b/demo/demo-springmvc/springmvc-client/src/main/java/org/apache/servicecomb/demo/springmvc/client/TestObject.java
index 90820058b..a48258716 100644
--- a/demo/demo-springmvc/springmvc-client/src/main/java/org/apache/servicecomb/demo/springmvc/client/TestObject.java
+++ b/demo/demo-springmvc/springmvc-client/src/main/java/org/apache/servicecomb/demo/springmvc/client/TestObject.java
@@ -27,20 +27,19 @@
 
 import org.apache.servicecomb.demo.EmptyObject;
 import org.apache.servicecomb.demo.TestMgr;
-import org.apache.servicecomb.demo.server.User;
 import org.apache.servicecomb.provider.pojo.Invoker;
 import org.apache.servicecomb.provider.springmvc.reference.CseRestTemplate;
 import org.springframework.web.client.RestTemplate;
 
 public class TestObject {
-  private CodeFirstSprigmvcIntf intf;
+  private CodeFirstSpringmvcIntf intf;
 
   private RestTemplate restTemplate = new CseRestTemplate();
 
   private String prefix = "cse://springmvc/codeFirstSpringmvc";
 
   public TestObject() {
-    intf = Invoker.createProxy("springmvc", "codeFirst", CodeFirstSprigmvcIntf.class);
+    intf = Invoker.createProxy("springmvc", "codeFirst", CodeFirstSpringmvcIntf.class);
   }
 
   public void runRest() {
@@ -57,17 +56,6 @@ public void runAllTransport() {
     testObject();
     testListObject();
     testHolderObject();
-    testGeneric();
-  }
-
-  @SuppressWarnings("unchecked")
-  private void testGeneric() {
-    Holder<User> holder = new Holder<>(new User());
-    Holder<User> result = intf.testGeneric(holder);
-    TestMgr.check("{name=nameA, age=100, index=0, names=null}", result.value);
-
-    result = restTemplate.postForObject(prefix + "/generic", holder, Holder.class);
-    TestMgr.check("{name=nameA, age=100, index=0, names=null}", result.value);
   }
 
   @SuppressWarnings("unchecked")
diff --git a/demo/demo-springmvc/springmvc-client/src/main/java/org/apache/servicecomb/demo/springmvc/client/TestResponse.java b/demo/demo-springmvc/springmvc-client/src/main/java/org/apache/servicecomb/demo/springmvc/client/TestResponse.java
index 4036bed5f..e56c3fefe 100644
--- a/demo/demo-springmvc/springmvc-client/src/main/java/org/apache/servicecomb/demo/springmvc/client/TestResponse.java
+++ b/demo/demo-springmvc/springmvc-client/src/main/java/org/apache/servicecomb/demo/springmvc/client/TestResponse.java
@@ -25,10 +25,10 @@
 import org.springframework.http.ResponseEntity;
 
 public class TestResponse {
-  private CodeFirstSprigmvcIntf intf;
+  private CodeFirstSpringmvcIntf intf;
 
   public TestResponse() {
-    intf = Invoker.createProxy("springmvc", "codeFirst", CodeFirstSprigmvcIntf.class);
+    intf = Invoker.createProxy("springmvc", "codeFirst", CodeFirstSpringmvcIntf.class);
   }
 
   public void runRest() {
diff --git a/demo/demo-springmvc/springmvc-server/src/main/java/org/apache/servicecomb/demo/springmvc/server/CodeFirstSpringmvc.java b/demo/demo-springmvc/springmvc-server/src/main/java/org/apache/servicecomb/demo/springmvc/server/CodeFirstSpringmvc.java
index aceba90c3..839844ec4 100644
--- a/demo/demo-springmvc/springmvc-server/src/main/java/org/apache/servicecomb/demo/springmvc/server/CodeFirstSpringmvc.java
+++ b/demo/demo-springmvc/springmvc-server/src/main/java/org/apache/servicecomb/demo/springmvc/server/CodeFirstSpringmvc.java
@@ -34,6 +34,7 @@
 import org.apache.servicecomb.common.rest.codec.RestObjectMapper;
 import org.apache.servicecomb.core.Const;
 import org.apache.servicecomb.demo.EmptyObject;
+import org.apache.servicecomb.demo.Generic;
 import org.apache.servicecomb.demo.compute.Person;
 import org.apache.servicecomb.demo.ignore.InputModelForTestIgnore;
 import org.apache.servicecomb.demo.ignore.OutputModelForTestIgnore;
@@ -387,8 +388,47 @@ public Object testObject(@RequestBody Object input) {
     return input;
   }
 
-  @PostMapping(path = "/generic")
-  public Holder<User> testGeneric(@RequestBody Holder<User> input) {
+  @PostMapping(path = "/holderUser")
+  public Holder<User> testHolderUser(@RequestBody Holder<User> input) {
+    Assert.isInstanceOf(Holder.class, input);
+    Assert.isInstanceOf(User.class, input.value);
+    return input;
+  }
+
+  @PostMapping(path = "/genericUser")
+  public Generic<User> testGenericUser(@RequestBody Generic<User> input) {
+    Assert.isInstanceOf(Generic.class, input);
+    Assert.isInstanceOf(User.class, input.value);
+    return input;
+  }
+
+  @PostMapping(path = "/genericLong")
+  public Generic<Long> testGenericLong(@RequestBody Generic<Long> input) {
+    Assert.isInstanceOf(Generic.class, input);
+    Assert.isInstanceOf(Long.class, input.value);
+    return input;
+  }
+
+  @PostMapping(path = "/genericDate")
+  public Generic<Date> testGenericDate(@RequestBody Generic<Date> input) {
+    Assert.isInstanceOf(Generic.class, input);
+    Assert.isInstanceOf(Date.class, input.value);
+    System.out.println(input.value);
+    return input;
+  }
+
+  @PostMapping(path = "/genericEnum")
+  public Generic<HttpStatus> testGenericEnum(@RequestBody Generic<HttpStatus> input) {
+    Assert.isInstanceOf(Generic.class, input);
+    Assert.isInstanceOf(HttpStatus.class, input.value);
+    return input;
+  }
+
+  @PostMapping(path = "/genericGenericUser")
+  public Generic<Generic<User>> testGenericGenericUser(@RequestBody Generic<Generic<User>> input) {
+    Assert.isInstanceOf(Generic.class, input);
+    Assert.isInstanceOf(Generic.class, input.value);
+    Assert.isInstanceOf(User.class, input.value.value);
     return input;
   }
 }
diff --git a/handlers/handler-loadbalance/src/main/java/org/apache/servicecomb/loadbalance/filter/CseServerDiscoveryFilter.java b/handlers/handler-loadbalance/src/main/java/org/apache/servicecomb/loadbalance/filter/CseServerDiscoveryFilter.java
index ae96e0002..ac93cf910 100644
--- a/handlers/handler-loadbalance/src/main/java/org/apache/servicecomb/loadbalance/filter/CseServerDiscoveryFilter.java
+++ b/handlers/handler-loadbalance/src/main/java/org/apache/servicecomb/loadbalance/filter/CseServerDiscoveryFilter.java
@@ -23,12 +23,17 @@
 import org.apache.servicecomb.loadbalance.CseServer;
 import org.apache.servicecomb.serviceregistry.api.registry.MicroserviceInstance;
 import org.apache.servicecomb.serviceregistry.cache.CacheEndpoint;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
 
 public class CseServerDiscoveryFilter extends EndpointDiscoveryFilter {
+  private static final Logger LOGGER = LoggerFactory.getLogger(CseServerDiscoveryFilter.class);
+
   @Override
   protected Object createEndpoint(String transportName, String endpoint, MicroserviceInstance instance) {
     Transport transport = CseContext.getInstance().getTransportManager().findTransport(transportName);
     if (transport == null) {
+      LOGGER.info("not deployed transport {}, ignore {}.", transportName, endpoint);
       return null;
     }
 
diff --git a/service-registry/src/main/java/org/apache/servicecomb/serviceregistry/consumer/MicroserviceVersions.java b/service-registry/src/main/java/org/apache/servicecomb/serviceregistry/consumer/MicroserviceVersions.java
index 56a87d0bf..8a712bb22 100644
--- a/service-registry/src/main/java/org/apache/servicecomb/serviceregistry/consumer/MicroserviceVersions.java
+++ b/service-registry/src/main/java/org/apache/servicecomb/serviceregistry/consumer/MicroserviceVersions.java
@@ -94,6 +94,7 @@ public String getMicroserviceName() {
 
   public void submitPull() {
     pendingPullCount.incrementAndGet();
+
     pullInstances();
   }
 
@@ -108,8 +109,7 @@ public void pullInstances() {
         revision);
     if (microserviceInstances == null) {
       // exception happens and try pull again later.
-      pendingPullCount.incrementAndGet();
-      appManager.getEventBus().post(new PullMicroserviceVersionsInstancesEvent(this, TimeUnit.SECONDS.toMillis(1)));
+      postPullInstanceEvent(TimeUnit.SECONDS.toMillis(1));
       return;
     }
     if (!microserviceInstances.isNeedRefresh()) {
@@ -118,7 +118,25 @@ public void pullInstances() {
     List<MicroserviceInstance> pulledInstances = microserviceInstances.getInstancesResponse().getInstances();
     String rev = microserviceInstances.getRevision();
 
-    setInstances(pulledInstances, rev);
+    safeSetInstances(pulledInstances, rev);
+  }
+
+  protected void safeSetInstances(List<MicroserviceInstance> pulledInstances, String rev) {
+    try {
+      setInstances(pulledInstances, rev);
+    } catch (Throwable e) {
+      LOGGER.error("Failed to setInstances, appId={}, microserviceName={}.",
+          getAppId(),
+          getMicroserviceName(),
+          e);
+      // exception happens and try pull again later.
+      postPullInstanceEvent(TimeUnit.SECONDS.toMillis(1));
+    }
+  }
+
+  private void postPullInstanceEvent(long msTime) {
+    pendingPullCount.incrementAndGet();
+    appManager.getEventBus().post(new PullMicroserviceVersionsInstancesEvent(this, msTime));
   }
 
   private void setInstances(List<MicroserviceInstance> pulledInstances, String rev) {
@@ -193,7 +211,6 @@ public void onMicroserviceInstanceChanged(MicroserviceInstanceChangedEvent chang
     //   if pull 1/2/3, and then delete 3, but "delete 3" received before pull result, will have wrong 3.
     // EXPIRE::
     //   black/white config in SC changed, we must refresh all data from sc.
-    pendingPullCount.incrementAndGet();
-    appManager.getEventBus().post(new PullMicroserviceVersionsInstancesEvent(this, TimeUnit.SECONDS.toMillis(1)));
+    postPullInstanceEvent(TimeUnit.MILLISECONDS.toMillis(1));
   }
 }
diff --git a/service-registry/src/main/java/org/apache/servicecomb/serviceregistry/registry/AbstractServiceRegistry.java b/service-registry/src/main/java/org/apache/servicecomb/serviceregistry/registry/AbstractServiceRegistry.java
index ca24d818c..9b67ed54f 100644
--- a/service-registry/src/main/java/org/apache/servicecomb/serviceregistry/registry/AbstractServiceRegistry.java
+++ b/service-registry/src/main/java/org/apache/servicecomb/serviceregistry/registry/AbstractServiceRegistry.java
@@ -226,7 +226,7 @@ public MicroserviceInstances findServiceInstances(String appId, String serviceNa
     }
 
     if (!microserviceInstances.isNeedRefresh()) {
-      LOGGER.info("instances revision is not changed, service={}/{}/{}", appId, serviceName, versionRule);
+      LOGGER.debug("instances revision is not changed, service={}/{}/{}", appId, serviceName, versionRule);
       return microserviceInstances;
     }
 
diff --git a/service-registry/src/test/java/org/apache/servicecomb/serviceregistry/consumer/TestMicroserviceVersions.java b/service-registry/src/test/java/org/apache/servicecomb/serviceregistry/consumer/TestMicroserviceVersions.java
index ebecb6952..770318cfc 100644
--- a/service-registry/src/test/java/org/apache/servicecomb/serviceregistry/consumer/TestMicroserviceVersions.java
+++ b/service-registry/src/test/java/org/apache/servicecomb/serviceregistry/consumer/TestMicroserviceVersions.java
@@ -278,4 +278,18 @@ public void onEvent(PullMicroserviceVersionsInstancesEvent pullEvent) {
 
     Assert.assertEquals(2, pendingPullCount.get());
   }
+
+  @Test
+  public void safeSetInstances() {
+    new MockUp<MicroserviceVersions>(microserviceVersions) {
+      @Mock
+      void setInstances(List<MicroserviceInstance> pulledInstances, String rev) {
+        throw new Error("failed to set instances");
+      }
+    };
+
+    microserviceVersions.safeSetInstances(null, null);
+    
+    Assert.assertEquals(1, pendingPullCount.get());
+  }
 }
diff --git a/swagger/swagger-generator/generator-core/src/main/java/org/apache/servicecomb/swagger/converter/AbstractConverter.java b/swagger/swagger-generator/generator-core/src/main/java/org/apache/servicecomb/swagger/converter/AbstractConverter.java
index c6d549b28..94e1b88a9 100644
--- a/swagger/swagger-generator/generator-core/src/main/java/org/apache/servicecomb/swagger/converter/AbstractConverter.java
+++ b/swagger/swagger-generator/generator-core/src/main/java/org/apache/servicecomb/swagger/converter/AbstractConverter.java
@@ -21,6 +21,9 @@
 
 import org.apache.servicecomb.swagger.generator.core.SwaggerConst;
 import org.apache.servicecomb.swagger.generator.core.utils.ClassUtils;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.util.StringUtils;
 
 import com.fasterxml.jackson.databind.JavaType;
 import com.fasterxml.jackson.databind.type.TypeFactory;
@@ -28,6 +31,7 @@
 import io.swagger.models.Swagger;
 
 public abstract class AbstractConverter implements Converter {
+  private static final Logger LOGGER = LoggerFactory.getLogger(AbstractConverter.class);
 
   protected abstract Map<String, Object> findVendorExtensions(Object def);
 
@@ -35,23 +39,39 @@
 
   @Override
   public JavaType convert(ClassLoader classLoader, String packageName, Swagger swagger, Object def) {
+    TypeFactory typeFactory = TypeFactory
+        .defaultInstance()
+        .withClassLoader(classLoader);
+
     Map<String, Object> vendorExtensions = findVendorExtensions(def);
-    JavaType javaType = getJavaTypeByVendorExtensions(classLoader, vendorExtensions);
-    if (javaType != null) {
-      return javaType;
+    String canonical = ClassUtils.getVendorExtension(vendorExtensions, SwaggerConst.EXT_JAVA_CLASS);
+    if (!StringUtils.isEmpty(canonical)) {
+      Class<?> clsResult = ClassUtils.getClassByName(classLoader, canonical);
+      if (clsResult != null) {
+        return typeFactory.constructType(clsResult);
+      }
     }
 
-    return doConvert(classLoader, packageName, swagger, def);
-  }
+    // ensure all depend model exist
+    // maybe create dynamic class by canonical
+    JavaType result = doConvert(classLoader, packageName, swagger, def);
 
-  private JavaType getJavaTypeByVendorExtensions(ClassLoader classLoader,
-      Map<String, Object> vendorExtensions) {
-    Class<?> cls =
-        ClassUtils.getClassByVendorExtensions(classLoader, vendorExtensions, SwaggerConst.EXT_JAVA_CLASS);
-    if (cls == null) {
-      return null;
+    String rawClassName = ClassUtils.getRawClassName(canonical);
+    if (StringUtils.isEmpty(rawClassName)) {
+      return result;
     }
 
-    return TypeFactory.defaultInstance().constructType(cls);
+    try {
+      JavaType rawType = typeFactory.constructFromCanonical(rawClassName);
+
+      if (rawType.getRawClass().getTypeParameters().length > 0) {
+        return typeFactory.constructFromCanonical(canonical);
+      }
+
+      return result;
+    } catch (IllegalArgumentException e) {
+      LOGGER.info("failed to load generic class, use {}.", result.getGenericSignature(), e);
+      return result;
+    }
   }
 }
diff --git a/swagger/swagger-generator/generator-core/src/main/java/org/apache/servicecomb/swagger/converter/model/ModelImplConverter.java b/swagger/swagger-generator/generator-core/src/main/java/org/apache/servicecomb/swagger/converter/model/ModelImplConverter.java
index 60f42344a..d8020051f 100644
--- a/swagger/swagger-generator/generator-core/src/main/java/org/apache/servicecomb/swagger/converter/model/ModelImplConverter.java
+++ b/swagger/swagger-generator/generator-core/src/main/java/org/apache/servicecomb/swagger/converter/model/ModelImplConverter.java
@@ -19,7 +19,9 @@
 
 import org.apache.servicecomb.swagger.converter.ConverterMgr;
 import org.apache.servicecomb.swagger.converter.property.MapPropertyConverter;
+import org.apache.servicecomb.swagger.generator.core.SwaggerConst;
 import org.apache.servicecomb.swagger.generator.core.utils.ClassUtils;
+import org.springframework.util.StringUtils;
 
 import com.fasterxml.jackson.databind.JavaType;
 import com.fasterxml.jackson.databind.type.TypeFactory;
@@ -55,11 +57,28 @@ public JavaType doConvert(ClassLoader classLoader, String packageName, Swagger s
       return TypeFactory.defaultInstance().constructType(Object.class);
     }
 
-    // ??name?property????class
+    return getOrCreateType(classLoader, packageName, swagger, modelImpl);
+  }
+
+  protected String getOrCreateClassName(String packageName, ModelImpl modelImpl) throws Error {
+    // dynamic create class by name and property
+    // try to use x-java-class first
+    // if not defined then use new class name
+    String canonical = ClassUtils.getVendorExtension(findVendorExtensions(modelImpl), SwaggerConst.EXT_JAVA_CLASS);
+    if (!StringUtils.isEmpty(canonical)) {
+      return canonical;
+    }
+
     if (packageName == null) {
-      throw new Error("packageName should not be null");
+      throw new IllegalStateException("packageName should not be null");
     }
-    String clsName = packageName + "." + modelImpl.getName();
+    return packageName + "." + modelImpl.getName();
+  }
+
+  protected JavaType getOrCreateType(ClassLoader classLoader, String packageName, Swagger swagger, ModelImpl modelImpl)
+      throws Error {
+    String clsName = getOrCreateClassName(packageName, modelImpl);
+    clsName = ClassUtils.correctClassName(clsName);
     Class<?> cls =
         ClassUtils.getOrCreateClass(classLoader, packageName, swagger, modelImpl.getProperties(), clsName);
     return TypeFactory.defaultInstance().constructType(cls);
diff --git a/swagger/swagger-generator/generator-core/src/main/java/org/apache/servicecomb/swagger/converter/property/StringPropertyConverter.java b/swagger/swagger-generator/generator-core/src/main/java/org/apache/servicecomb/swagger/converter/property/StringPropertyConverter.java
index a4372a199..b34272cc0 100644
--- a/swagger/swagger-generator/generator-core/src/main/java/org/apache/servicecomb/swagger/converter/property/StringPropertyConverter.java
+++ b/swagger/swagger-generator/generator-core/src/main/java/org/apache/servicecomb/swagger/converter/property/StringPropertyConverter.java
@@ -17,9 +17,7 @@
 
 package org.apache.servicecomb.swagger.converter.property;
 
-import java.util.HashMap;
 import java.util.List;
-import java.util.Map;
 
 import org.apache.servicecomb.common.javassist.JavassistUtils;
 import org.apache.servicecomb.swagger.converter.ConverterMgr;
@@ -31,32 +29,6 @@
 import io.swagger.models.properties.StringProperty;
 
 public class StringPropertyConverter extends AbstractPropertyConverter {
-  // ???????enum??
-  // key?enum names? value?enum cls javaType
-  private static Map<String, JavaType> enumMap = new HashMap<>();
-
-  private static final Object LOCK = new Object();
-
-  // ?????enum??????????double check
-  private static JavaType getOrCreateEnumByNames(ClassLoader classLoader, String packageName, List<String> enums) {
-    String strEnums = enums.toString();
-
-    synchronized (LOCK) {
-      JavaType javaType = enumMap.get(strEnums);
-      if (javaType != null) {
-        return javaType;
-      }
-
-      String enumClsName = packageName + ".Enum" + enumMap.size();
-      @SuppressWarnings("rawtypes")
-      Class<? extends Enum> enumCls = JavassistUtils.createEnum(classLoader, enumClsName, enums);
-      javaType = TypeFactory.defaultInstance().constructType(enumCls);
-      enumMap.put(strEnums, javaType);
-
-      return javaType;
-    }
-  }
-
   public static JavaType findJavaType(ClassLoader classLoader, String packageName, Swagger swagger, String type,
       String format, List<String> enums) {
     if (!isEnum(enums)) {
@@ -64,7 +36,8 @@ public static JavaType findJavaType(ClassLoader classLoader, String packageName,
     }
 
     // enum????????class
-    return getOrCreateEnumByNames(classLoader, packageName, enums);
+    Class<?> enumCls = JavassistUtils.getOrCreateEnumWithPackageName(classLoader, packageName, enums);
+    return TypeFactory.defaultInstance().constructType(enumCls);
   }
 
   public static boolean isEnum(StringProperty stringProperty) {
diff --git a/swagger/swagger-generator/generator-core/src/main/java/org/apache/servicecomb/swagger/extend/ModelResolverExt.java b/swagger/swagger-generator/generator-core/src/main/java/org/apache/servicecomb/swagger/extend/ModelResolverExt.java
index 7f028ae3e..6b7d876aa 100644
--- a/swagger/swagger-generator/generator-core/src/main/java/org/apache/servicecomb/swagger/extend/ModelResolverExt.java
+++ b/swagger/swagger-generator/generator-core/src/main/java/org/apache/servicecomb/swagger/extend/ModelResolverExt.java
@@ -35,6 +35,7 @@
 import org.springframework.util.StringUtils;
 
 import com.fasterxml.jackson.databind.JavaType;
+import com.google.common.annotations.VisibleForTesting;
 
 import io.swagger.converter.ModelConverter;
 import io.swagger.converter.ModelConverterContext;
@@ -70,8 +71,9 @@ private void loadPropertyCreators() {
         .forEach(this::addPropertyCreator);
   }
 
-  private void setType(JavaType type, Map<String, Object> vendorExtensions) {
-    vendorExtensions.put(ExtendConst.EXT_JAVA_CLASS, type.getRawClass().getName());
+  @VisibleForTesting
+  protected void setType(JavaType type, Map<String, Object> vendorExtensions) {
+    vendorExtensions.put(ExtendConst.EXT_JAVA_CLASS, type.toCanonical());
   }
 
   private void checkType(JavaType type) {
diff --git a/swagger/swagger-generator/generator-core/src/main/java/org/apache/servicecomb/swagger/generator/core/utils/ClassUtils.java b/swagger/swagger-generator/generator-core/src/main/java/org/apache/servicecomb/swagger/generator/core/utils/ClassUtils.java
index b3b85befb..f9a33f394 100644
--- a/swagger/swagger-generator/generator-core/src/main/java/org/apache/servicecomb/swagger/generator/core/utils/ClassUtils.java
+++ b/swagger/swagger-generator/generator-core/src/main/java/org/apache/servicecomb/swagger/generator/core/utils/ClassUtils.java
@@ -144,16 +144,38 @@ public static boolean isRawJsonType(Parameter param) {
 
   public static Class<?> getClassByVendorExtensions(ClassLoader classLoader, Map<String, Object> vendorExtensions,
       String clsKey) {
-    if (vendorExtensions == null) {
+    String clsName = getVendorExtension(vendorExtensions, clsKey);
+    if (StringUtils.isEmpty(clsName)) {
       return null;
     }
 
-    String clsName = (String) vendorExtensions.get(clsKey);
-    if (StringUtils.isEmpty(clsName)) {
+    return getClassByName(classLoader, clsName);
+  }
+
+  public static String getRawClassName(String canonical) {
+    if (StringUtils.isEmpty(canonical)) {
       return null;
     }
 
-    return getClassByName(classLoader, clsName);
+    int idx = canonical.indexOf("<");
+    if (idx == 0) {
+      throw new IllegalStateException("Invalid class canonical: " + canonical);
+    }
+
+    if (idx < 0) {
+      return canonical;
+    }
+
+    return canonical.substring(0, idx);
+  }
+
+  @SuppressWarnings("unchecked")
+  public static <T> T getVendorExtension(Map<String, Object> vendorExtensions, String key) {
+    if (vendorExtensions == null) {
+      return null;
+    }
+
+    return (T) vendorExtensions.get(key);
   }
 
   public static Class<?> getOrCreateInterface(SwaggerGenerator generator) {
@@ -249,7 +271,7 @@ public static String correctClassName(String name) {
         continue;
       }
 
-      part = part.replace('-', '_');
+      part = part.replaceAll("[<>-]", "_").replace("[", "array_");
       if (Character.isDigit(part.charAt(0)) || SourceVersion.isKeyword(part)) {
         part = "_" + part;
       }
diff --git a/swagger/swagger-generator/generator-core/src/test/java/org/apache/servicecomb/swagger/converter/TestAbstractConverter.java b/swagger/swagger-generator/generator-core/src/test/java/org/apache/servicecomb/swagger/converter/TestAbstractConverter.java
new file mode 100644
index 000000000..98ac8eb47
--- /dev/null
+++ b/swagger/swagger-generator/generator-core/src/test/java/org/apache/servicecomb/swagger/converter/TestAbstractConverter.java
@@ -0,0 +1,83 @@
+/*
+ * 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.swagger.converter;
+
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Optional;
+
+import org.apache.servicecomb.swagger.generator.core.SwaggerConst;
+import org.junit.Assert;
+import org.junit.Test;
+
+import com.fasterxml.jackson.databind.JavaType;
+import com.fasterxml.jackson.databind.type.TypeFactory;
+
+import io.swagger.models.Swagger;
+import mockit.Mocked;
+
+public class TestAbstractConverter {
+  Map<String, Object> vendorExtensions = new HashMap<>();
+
+  JavaType doConvertResult;
+
+  ClassLoader classLoader = new ClassLoader() {
+  };
+
+  AbstractConverter converter = new AbstractConverter() {
+    @Override
+    protected Map<String, Object> findVendorExtensions(Object def) {
+      return vendorExtensions;
+    }
+
+    @Override
+    protected JavaType doConvert(ClassLoader classLoader, String packageName, Swagger swagger, Object def) {
+      return doConvertResult;
+    }
+  };
+
+  @Test
+  public void convert_canonical_normal() {
+    doConvertResult = TypeFactory.defaultInstance().constructType(String.class);
+    vendorExtensions.put(SwaggerConst.EXT_JAVA_CLASS, "java.lang.String");
+
+    Assert.assertSame(doConvertResult, converter.convert(classLoader, null, null, null));
+  }
+
+  @Test
+  public void convert_noCanonical(@Mocked JavaType type) {
+    doConvertResult = type;
+
+    Assert.assertSame(type, converter.convert(classLoader, null, null, null));
+  }
+
+  @Test
+  public void convert_canonical_generic() {
+    doConvertResult = TypeFactory.defaultInstance().constructParametricType(Optional.class, String.class);
+    vendorExtensions.put(SwaggerConst.EXT_JAVA_CLASS, "java.util.Optional<java.lang.String>");
+
+    Assert.assertSame(doConvertResult, converter.convert(classLoader, null, null, null));
+  }
+
+  @Test
+  public void convert_canonical_rawNotExist() {
+    doConvertResult = TypeFactory.defaultInstance().constructType(String.class);
+    vendorExtensions.put(SwaggerConst.EXT_JAVA_CLASS, "xxx<java.lang.String>");
+
+    Assert.assertSame(doConvertResult, converter.convert(classLoader, null, null, null));
+  }
+}
diff --git a/swagger/swagger-generator/generator-core/src/test/java/org/apache/servicecomb/swagger/converter/model/TestModelImplConverter.java b/swagger/swagger-generator/generator-core/src/test/java/org/apache/servicecomb/swagger/converter/model/TestModelImplConverter.java
new file mode 100644
index 000000000..cc82bbeec
--- /dev/null
+++ b/swagger/swagger-generator/generator-core/src/test/java/org/apache/servicecomb/swagger/converter/model/TestModelImplConverter.java
@@ -0,0 +1,71 @@
+/*
+ * 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.swagger.converter.model;
+
+import org.apache.servicecomb.swagger.generator.core.SwaggerConst;
+import org.hamcrest.Matchers;
+import org.junit.Assert;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.ExpectedException;
+
+import com.fasterxml.jackson.databind.JavaType;
+
+import io.swagger.models.ModelImpl;
+import io.swagger.models.Swagger;
+import mockit.Mocked;
+
+public class TestModelImplConverter {
+  ModelImplConverter converter = new ModelImplConverter();
+
+  @Rule
+  public ExpectedException expectedException = ExpectedException.none();
+
+  @Test
+  public void getOrCreateClassName_get() {
+    String canonical = "name";
+    ModelImpl model = new ModelImpl();
+    model.getVendorExtensions().put(SwaggerConst.EXT_JAVA_CLASS, canonical);
+    Assert.assertEquals(canonical,
+        converter.getOrCreateClassName(null, model));
+  }
+
+  @Test
+  public void getOrCreateClassName_create_packageNull() {
+    expectedException.expect(IllegalStateException.class);
+    expectedException.expectMessage(Matchers.is("packageName should not be null"));
+
+    converter.getOrCreateClassName(null, new ModelImpl());
+  }
+
+  @Test
+  public void getOrCreateClassName_create() {
+    Assert.assertEquals("pkg.name", converter.getOrCreateClassName("pkg", new ModelImpl().name("name")));;
+  }
+
+  @Test
+  public void getOrCreateType(@Mocked Swagger swagger) {
+    String canonical = "name-";
+    ModelImpl model = new ModelImpl();
+    model.getVendorExtensions().put(SwaggerConst.EXT_JAVA_CLASS, canonical);
+
+    JavaType type = converter.getOrCreateType(new ClassLoader() {
+    }, null, swagger, model);
+
+    Assert.assertEquals("name_", type.getRawClass().getName());
+  }
+}
diff --git a/swagger/swagger-generator/generator-core/src/test/java/org/apache/servicecomb/swagger/extend/TestModelResolverExt.java b/swagger/swagger-generator/generator-core/src/test/java/org/apache/servicecomb/swagger/extend/TestModelResolverExt.java
new file mode 100644
index 000000000..9cc4abba1
--- /dev/null
+++ b/swagger/swagger-generator/generator-core/src/test/java/org/apache/servicecomb/swagger/extend/TestModelResolverExt.java
@@ -0,0 +1,42 @@
+/*
+ * 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.swagger.extend;
+
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import org.apache.servicecomb.swagger.generator.core.SwaggerConst;
+import org.junit.Assert;
+import org.junit.Test;
+
+import com.fasterxml.jackson.databind.JavaType;
+import com.fasterxml.jackson.databind.type.TypeFactory;
+
+public class TestModelResolverExt {
+  static ModelResolverExt resolver = new ModelResolverExt();
+
+  Map<String, Object> vendorExtensions = new HashMap<>();
+
+  @Test
+  public void setType() {
+    JavaType type = TypeFactory.defaultInstance().constructCollectionType(List.class, String.class);
+    resolver.setType(type, vendorExtensions);
+
+    Assert.assertEquals("java.util.List<java.lang.String>", vendorExtensions.get(SwaggerConst.EXT_JAVA_CLASS));
+  }
+}
diff --git a/swagger/swagger-generator/generator-core/src/test/java/org/apache/servicecomb/swagger/generator/core/TestClassUtils.java b/swagger/swagger-generator/generator-core/src/test/java/org/apache/servicecomb/swagger/generator/core/TestClassUtils.java
index efcbdabcc..a23702cdb 100644
--- a/swagger/swagger-generator/generator-core/src/test/java/org/apache/servicecomb/swagger/generator/core/TestClassUtils.java
+++ b/swagger/swagger-generator/generator-core/src/test/java/org/apache/servicecomb/swagger/generator/core/TestClassUtils.java
@@ -22,6 +22,7 @@
 
 import java.lang.reflect.Method;
 import java.util.Arrays;
+import java.util.HashMap;
 import java.util.HashSet;
 import java.util.List;
 import java.util.Map;
@@ -33,8 +34,11 @@
 import org.apache.servicecomb.swagger.generator.core.schema.User;
 import org.apache.servicecomb.swagger.generator.core.unittest.UnitTestSwaggerUtils;
 import org.apache.servicecomb.swagger.generator.core.utils.ClassUtils;
+import org.hamcrest.Matchers;
 import org.junit.Assert;
+import org.junit.Rule;
 import org.junit.Test;
+import org.junit.rules.ExpectedException;
 import org.mockito.Mockito;
 
 import io.swagger.annotations.SwaggerDefinition;
@@ -107,6 +111,12 @@
         "while"));
   }
 
+  ClassLoader classLoader = new ClassLoader() {
+  };
+
+  @Rule
+  public ExpectedException expectedException = ExpectedException.none();
+
   @Test
   public void testHasAnnotation() {
     Assert.assertEquals(true, ClassUtils.hasAnnotation(TestClassUtils.class, SwaggerDefinition.class));
@@ -170,6 +180,12 @@ public void testCorrectClassNameEmptyPart() {
     Assert.assertThat(result, is("_._.a._.a._._"));
   }
 
+  @Test
+  public void testCorrectClassNameCanonical() {
+    String result = ClassUtils.correctClassName("java.util.List<java.lang.String>[");
+    Assert.assertThat(result, is("java.util.List_java.lang.String_array_"));
+  }
+
   @Test
   public void testGetOrCreateClass() {
     String className = this.getClass().getCanonicalName();
@@ -204,4 +220,54 @@ public void testGetOrCreateClassOnPropertyIsNull() {
     Class<?> result = ClassUtils.getOrCreateClass(classLoader, "", new Swagger(), null, className);
     Assert.assertEquals(this.getClass(), result);
   }
+
+  @Test
+  public void getClassByVendorExtensions_noName() {
+    Map<String, Object> vendorExtensions = new HashMap<>();
+
+    Assert
+        .assertNull(ClassUtils.getClassByVendorExtensions(classLoader, vendorExtensions, SwaggerConst.EXT_JAVA_CLASS));
+  }
+
+  @Test
+  public void getClassByVendorExtensions_notExist() {
+    Map<String, Object> vendorExtensions = new HashMap<>();
+    vendorExtensions.put(SwaggerConst.EXT_JAVA_CLASS, "-" + String.class.getName());
+
+    Assert
+        .assertNull(ClassUtils.getClassByVendorExtensions(classLoader, vendorExtensions, SwaggerConst.EXT_JAVA_CLASS));
+  }
+
+  @Test
+  public void getClassByVendorExtensions_normal() {
+    Map<String, Object> vendorExtensions = new HashMap<>();
+    vendorExtensions.put(SwaggerConst.EXT_JAVA_CLASS, String.class.getName());
+
+    Assert.assertSame(String.class,
+        ClassUtils.getClassByVendorExtensions(classLoader, vendorExtensions, SwaggerConst.EXT_JAVA_CLASS));
+  }
+
+  @Test
+  public void getRawClassName_empty() {
+    Assert.assertNull(ClassUtils.getRawClassName(null));
+    Assert.assertNull(ClassUtils.getRawClassName(""));
+  }
+
+  @Test
+  public void getRawClassName_normal() {
+    Assert.assertEquals(String.class.getName(), ClassUtils.getRawClassName(String.class.getName()));
+  }
+
+  @Test
+  public void getRawClassName_generic() {
+    Assert.assertEquals("abc", ClassUtils.getRawClassName("abc<d>"));
+  }
+
+  @Test
+  public void getRawClassName_invalid() {
+    expectedException.expect(IllegalStateException.class);
+    expectedException.expectMessage(Matchers.is("Invalid class canonical: <abc>"));
+
+    ClassUtils.getRawClassName("<abc>");
+  }
 }
diff --git a/swagger/swagger-invocation/invocation-core/src/main/java/org/apache/servicecomb/swagger/invocation/arguments/ArgumentsMapperFactory.java b/swagger/swagger-invocation/invocation-core/src/main/java/org/apache/servicecomb/swagger/invocation/arguments/ArgumentsMapperFactory.java
index 454bc2768..8e4f183dc 100644
--- a/swagger/swagger-invocation/invocation-core/src/main/java/org/apache/servicecomb/swagger/invocation/arguments/ArgumentsMapperFactory.java
+++ b/swagger/swagger-invocation/invocation-core/src/main/java/org/apache/servicecomb/swagger/invocation/arguments/ArgumentsMapperFactory.java
@@ -99,6 +99,11 @@ protected boolean isSwaggerWrapBody(ArgumentsMapperConfig config, List<ProviderP
       return false;
     }
 
+    // no public field, it's not rpc wrapper class
+    if (((Class<?>) swaggerType).getFields().length == 0) {
+      return false;
+    }
+
     swaggerType = ((Class<?>) swaggerType).getFields()[0].getGenericType();
     Converter converter = converterMgr.findConverter(type, firstProviderParam, swaggerType);
     if (ConverterCommon.class.isInstance(converter)) {


 

----------------------------------------------------------------
This is an automated message from the Apache Git Service.
To respond to the message, please log on GitHub and use the
URL above to go to the specific comment.
 
For queries about this service, please contact Infrastructure at:
users@infra.apache.org


With regards,
Apache Git Services