wujimin closed pull request #699: [SCB-532] Support recursive dependency URL: https://github.com/apache/incubator-servicecomb-java-chassis/pull/699
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/CtType.java b/common/common-javassist/src/main/java/org/apache/servicecomb/common/javassist/CtType.java index d0da1e010..4c53818e1 100644 --- a/common/common-javassist/src/main/java/org/apache/servicecomb/common/javassist/CtType.java +++ b/common/common-javassist/src/main/java/org/apache/servicecomb/common/javassist/CtType.java @@ -22,6 +22,7 @@ import javassist.CtClass; import javassist.CtPrimitiveType; import javassist.NotFoundException; +import javassist.bytecode.SignatureAttribute.ClassType; public class CtType { private static final ClassPool PRIMITIVE_CLASSPOOL = JavassistUtils.getOrCreateClassPool(int.class.getClassLoader()); @@ -32,11 +33,22 @@ private String genericSignature; + public CtType(CtClass ctClass) { + ClassType classType = new ClassType(ctClass.getName(), null); + init(ctClass, false, classType.encode()); + } + public CtType(CtClass ctClass, boolean hasGenericTypes, String genericSignature) { init(ctClass, hasGenericTypes, genericSignature); } public CtType(JavaType javaType) { + if (CtTypeJavaType.class.isInstance(javaType)) { + CtType ctType = ((CtTypeJavaType) javaType).getType(); + init(ctType.ctClass, ctType.hasGenericTypes, ctType.genericSignature); + return; + } + ClassLoader classLoader = javaType.getRawClass().getClassLoader(); try { ClassPool classPool = JavassistUtils.getOrCreateClassPool(classLoader); diff --git a/common/common-javassist/src/main/java/org/apache/servicecomb/common/javassist/CtTypeJavaType.java b/common/common-javassist/src/main/java/org/apache/servicecomb/common/javassist/CtTypeJavaType.java new file mode 100644 index 000000000..6e03bd94d --- /dev/null +++ b/common/common-javassist/src/main/java/org/apache/servicecomb/common/javassist/CtTypeJavaType.java @@ -0,0 +1,52 @@ +/* + * 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.common.javassist; + +import com.fasterxml.jackson.databind.type.SimpleType; + +/** + * just a wrapper for CtType + * pending create class from CtClass to support recursive dependency class + */ +public class CtTypeJavaType extends SimpleType { + private CtType type; + + public CtTypeJavaType(CtType type) { + super(CtTypeJavaType.class); + this.type = type; + } + + public CtType getType() { + return type; + } + + @Override + protected String buildCanonicalName() { + return type.getCtClass().getName(); + } + + @Override + public String getGenericSignature() { + return type.getGenericSignature(); + } + + + @Override + public StringBuilder getGenericSignature(StringBuilder sb) { + return sb.append(type.getGenericSignature()); + } +} 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 025ea9474..66bb661e1 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 @@ -139,7 +139,7 @@ private static void addEnumValuesMethod(CtClass ctClass, List<String> values) th return createClass(null, config); } - public static Class<?> createClass(ClassLoader classLoader, ClassConfig config) { + public static CtClass createCtClass(ClassLoader classLoader, ClassConfig config) { if (classLoader == null) { classLoader = Thread.currentThread().getContextClassLoader(); } @@ -185,10 +185,47 @@ private static void addEnumValuesMethod(CtClass ctClass, List<String> values) th } } - LOGGER.info("generate {} in classLoader {}.", config.getClassName(), classLoader); - return ctClass.toClass(classLoader, null); + LOGGER.info("create CtClass {} in classLoader {}.", config.getClassName(), classLoader); + return ctClass; + } catch (Throwable e) { + throw new IllegalStateException( + String.format("Failed to create CtClass %s in classLoader %s.", config.getClassName(), + classLoader), e); + } + } + + public static Class<?> createClass(ClassLoader classLoader, ClassConfig config) { + if (classLoader == null) { + classLoader = Thread.currentThread().getContextClassLoader(); + } + + CtClass ctClass = createCtClass(classLoader, config); + return createClass(classLoader, ctClass); + } + + public static Class<?> createClass(ClassLoader classLoader, CtClass ctClass) { + if (classLoader == null) { + classLoader = Thread.currentThread().getContextClassLoader(); + } + + String clsName = ctClass.getName(); + try { + // must try load from classloader first + // because class A depend on class B + // when load class A, will load class B + // after this, if CtClass B invoke toClass again, will cause problem. + return classLoader.loadClass(clsName); + } catch (ClassNotFoundException e) { + // ignore it + } + + try { + Class<?> cls = ctClass.toClass(classLoader, null); + LOGGER.info("create class {} in classLoader {}.", clsName, classLoader); + return cls; } catch (Throwable e) { - throw new Error(String.format("Failed to create %s in classLoader %s.", config.getClassName(), classLoader), e); + throw new IllegalStateException( + String.format("Failed to create %s in classLoader %s.", clsName, classLoader), e); } } diff --git a/common/common-javassist/src/test/java/org/apache/servicecomb/common/javassist/TestCtType.java b/common/common-javassist/src/test/java/org/apache/servicecomb/common/javassist/TestCtType.java index 40ecd7b3a..1bf190737 100644 --- a/common/common-javassist/src/test/java/org/apache/servicecomb/common/javassist/TestCtType.java +++ b/common/common-javassist/src/test/java/org/apache/servicecomb/common/javassist/TestCtType.java @@ -25,6 +25,7 @@ import com.fasterxml.jackson.databind.JavaType; import com.fasterxml.jackson.databind.type.TypeFactory; +import javassist.ClassPool; import javassist.CtClass; import javassist.NotFoundException; @@ -33,6 +34,17 @@ @Test public void fromCtClass() { + CtClass ctClass = Mockito.mock(CtClass.class); + Mockito.when(ctClass.getName()).thenReturn("a.b.c"); + ctType = new CtType(ctClass); + + Assert.assertSame(ctClass, ctType.getCtClass()); + Assert.assertFalse(ctType.hasGenericTypes()); + Assert.assertEquals("La/b/c;", ctType.getGenericSignature()); + } + + @Test + public void fromCtClass_fullInfo() { CtClass ctClass = Mockito.mock(CtClass.class); ctType = new CtType(ctClass, true, "Ljava/util/List<[B;>;"); @@ -87,4 +99,17 @@ public void fromJavaType_listBytes() throws NotFoundException { Assert.assertTrue(ctType.hasGenericTypes()); Assert.assertEquals("Ljava/util/List<[B>;", ctType.getGenericSignature()); } + + @Test + public void fromCtTypeJavaType() throws NotFoundException { + CtClass ctClass = ClassPool.getDefault().get(String.class.getCanonicalName()); + CtType otherCtType = new CtType(ctClass, false, "Ljava/lang/String;"); + CtTypeJavaType ctTypeJavaType = new CtTypeJavaType(otherCtType); + + ctType = new CtType(ctTypeJavaType); + + Assert.assertSame(ctClass, ctType.getCtClass()); + Assert.assertFalse(ctType.hasGenericTypes()); + Assert.assertEquals("Ljava/lang/String;", ctType.getGenericSignature()); + } } diff --git a/common/common-javassist/src/test/java/org/apache/servicecomb/common/javassist/TestCtTypeJavaType.java b/common/common-javassist/src/test/java/org/apache/servicecomb/common/javassist/TestCtTypeJavaType.java new file mode 100644 index 000000000..46213a316 --- /dev/null +++ b/common/common-javassist/src/test/java/org/apache/servicecomb/common/javassist/TestCtTypeJavaType.java @@ -0,0 +1,48 @@ +/* + * 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.common.javassist; + +import java.util.List; + +import org.junit.Assert; +import org.junit.Test; + +import com.fasterxml.jackson.databind.JavaType; +import com.fasterxml.jackson.databind.type.TypeFactory; + +public class TestCtTypeJavaType { + Class<?> cls = TestCtTypeJavaType.class; + + JavaType javaType = TypeFactory.defaultInstance().constructType(cls); + + CtType ctType = new CtType(javaType); + + CtTypeJavaType ctTypeJavaType = new CtTypeJavaType(ctType); + + @Test + public void construct() { + Assert.assertSame(ctType, ctTypeJavaType.getType()); + Assert.assertEquals(cls.getTypeName(), ctTypeJavaType.buildCanonicalName()); + } + + @Test + public void getGenericSignature() { + JavaType listJavaType = TypeFactory.defaultInstance().constructCollectionType(List.class, ctTypeJavaType); + Assert.assertEquals("Ljava/util/List<Lorg/apache/servicecomb/common/javassist/TestCtTypeJavaType;>;", + listJavaType.getGenericSignature()); + } +} diff --git a/demo/demo-edge/business-2.0.0/src/main/java/org/apache/servicecomb/demo/edge/business/Impl.java b/demo/demo-edge/business-2.0.0/src/main/java/org/apache/servicecomb/demo/edge/business/Impl.java index e24f36ebd..dc346fa9c 100644 --- a/demo/demo-edge/business-2.0.0/src/main/java/org/apache/servicecomb/demo/edge/business/Impl.java +++ b/demo/demo-edge/business-2.0.0/src/main/java/org/apache/servicecomb/demo/edge/business/Impl.java @@ -28,12 +28,15 @@ import org.apache.commons.lang.StringUtils; import org.apache.servicecomb.demo.edge.model.AppClientDataRsp; import org.apache.servicecomb.demo.edge.model.ChannelRequestBase; +import org.apache.servicecomb.demo.edge.model.DependTypeA; +import org.apache.servicecomb.demo.edge.model.RecursiveSelfType; import org.apache.servicecomb.demo.edge.model.ResultWithInstance; import org.apache.servicecomb.provider.rest.common.RestSchema; import org.springframework.http.HttpHeaders; import org.springframework.http.MediaType; import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestMethod; @@ -93,4 +96,14 @@ protected File createBigFile() throws IOException { public File bigFile() throws IOException { return createBigFile(); } + + @PostMapping(path = "recursiveSelf") + public RecursiveSelfType recursiveSelf(@RequestBody RecursiveSelfType value) { + return value; + } + + @PostMapping(path = "dependType") + public DependTypeA dependType(@RequestBody DependTypeA value) { + return value; + } } diff --git a/demo/demo-edge/consumer/src/main/java/org/apache/servicecomb/demo/edge/consumer/Consumer.java b/demo/demo-edge/consumer/src/main/java/org/apache/servicecomb/demo/edge/consumer/Consumer.java index 7bdbb544d..cff8d62b4 100644 --- a/demo/demo-edge/consumer/src/main/java/org/apache/servicecomb/demo/edge/consumer/Consumer.java +++ b/demo/demo-edge/consumer/src/main/java/org/apache/servicecomb/demo/edge/consumer/Consumer.java @@ -29,6 +29,9 @@ import org.apache.servicecomb.core.endpoint.EndpointsCache; import org.apache.servicecomb.demo.edge.model.AppClientDataRsp; import org.apache.servicecomb.demo.edge.model.ChannelRequestBase; +import org.apache.servicecomb.demo.edge.model.DependTypeA; +import org.apache.servicecomb.demo.edge.model.DependTypeB; +import org.apache.servicecomb.demo.edge.model.RecursiveSelfType; import org.apache.servicecomb.demo.edge.model.ResultWithInstance; import org.apache.servicecomb.foundation.common.net.URIEndpointObject; import org.apache.servicecomb.provider.springmvc.reference.RestTemplateBuilder; @@ -78,6 +81,8 @@ public Consumer() { public void run() { prepareEdge(); + testRecursiveSelf(); + testDependType(); testDownload(); testDownloadBigFile(); @@ -106,6 +111,40 @@ public void run() { checkResult("v2/dec", decV2Result, "2.0.0"); } + protected void testRecursiveSelf() { + String url = edgePrefix + "/v2/recursiveSelf"; + + HttpHeaders headers = new HttpHeaders(); + headers.setContentType(MediaType.APPLICATION_JSON); + + RecursiveSelfType recursiveSelfType = new RecursiveSelfType(); + recursiveSelfType.setField(new RecursiveSelfType()); + recursiveSelfType.getField().setValue(10); + + HttpEntity<RecursiveSelfType> entity = new HttpEntity<>(recursiveSelfType, headers); + + RecursiveSelfType response = template.postForObject(url, entity, RecursiveSelfType.class); + Assert.isTrue(response.getValue() == 0, "default must be 0"); + Assert.isTrue(response.getField().getValue() == 10, "must be 10"); + Assert.isNull(response.getField().getField(), "must be null"); + } + + protected void testDependType() { + String url = edgePrefix + "/v2/dependType"; + + HttpHeaders headers = new HttpHeaders(); + headers.setContentType(MediaType.APPLICATION_JSON); + + DependTypeA dependTypeA = new DependTypeA(); + dependTypeA.setB(new DependTypeB()); + dependTypeA.getB().setValue(10); + + HttpEntity<DependTypeA> entity = new HttpEntity<>(dependTypeA, headers); + + DependTypeA response = template.postForObject(url, entity, DependTypeA.class); + Assert.isTrue(response.getB().getValue() == 10, "must be 10"); + } + protected void testDownloadBigFile() { String url = edgePrefix + "/v2/bigFile"; AtomicInteger size = new AtomicInteger(); @@ -180,7 +219,7 @@ protected void invokeBusiness(String urlPrefix, ChannelRequestBase request) { HttpHeaders headers = new HttpHeaders(); headers.setContentType(MediaType.APPLICATION_JSON); - HttpEntity<ChannelRequestBase> entity = new HttpEntity<ChannelRequestBase>(request, headers); + HttpEntity<ChannelRequestBase> entity = new HttpEntity<>(request, headers); ResponseEntity<AppClientDataRsp> response = template.postForEntity(url, entity, AppClientDataRsp.class); System.out.println("urlPrefix: " + urlPrefix); diff --git a/demo/demo-edge/model/src/main/java/org/apache/servicecomb/demo/edge/model/DependTypeA.java b/demo/demo-edge/model/src/main/java/org/apache/servicecomb/demo/edge/model/DependTypeA.java new file mode 100644 index 000000000..605cc88c3 --- /dev/null +++ b/demo/demo-edge/model/src/main/java/org/apache/servicecomb/demo/edge/model/DependTypeA.java @@ -0,0 +1,29 @@ +/* + * 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.edge.model; + +public class DependTypeA { + private DependTypeB b; + + public DependTypeB getB() { + return b; + } + + public void setB(DependTypeB b) { + this.b = b; + } +} diff --git a/demo/demo-edge/model/src/main/java/org/apache/servicecomb/demo/edge/model/DependTypeB.java b/demo/demo-edge/model/src/main/java/org/apache/servicecomb/demo/edge/model/DependTypeB.java new file mode 100644 index 000000000..19067900c --- /dev/null +++ b/demo/demo-edge/model/src/main/java/org/apache/servicecomb/demo/edge/model/DependTypeB.java @@ -0,0 +1,39 @@ +/* + * 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.edge.model; + +public class DependTypeB { + private DependTypeA a; + + private int value; + + public DependTypeA getA() { + return a; + } + + public void setA(DependTypeA a) { + this.a = a; + } + + public int getValue() { + return value; + } + + public void setValue(int value) { + this.value = value; + } +} diff --git a/demo/demo-edge/model/src/main/java/org/apache/servicecomb/demo/edge/model/RecursiveSelfType.java b/demo/demo-edge/model/src/main/java/org/apache/servicecomb/demo/edge/model/RecursiveSelfType.java new file mode 100644 index 000000000..2ae58adcf --- /dev/null +++ b/demo/demo-edge/model/src/main/java/org/apache/servicecomb/demo/edge/model/RecursiveSelfType.java @@ -0,0 +1,39 @@ +/* + * 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.edge.model; + +public class RecursiveSelfType { + private RecursiveSelfType field; + + private int value; + + public RecursiveSelfType getField() { + return field; + } + + public void setField(RecursiveSelfType field) { + this.field = field; + } + + public int getValue() { + return value; + } + + public void setValue(int value) { + this.value = value; + } +} 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 f23f8daec..fbd9e486d 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 @@ -41,9 +41,10 @@ public JavaType convert(SwaggerToClassGenerator swaggerToClassGenerator, Object Map<String, Object> vendorExtensions = findVendorExtensions(def); String canonical = ClassUtils.getClassName(vendorExtensions); if (!StringUtils.isEmpty(canonical)) { - Class<?> clsResult = ClassUtils.getClassByName(swaggerToClassGenerator.getClassLoader(), canonical); - if (clsResult != null) { - return typeFactory.constructType(clsResult); + try { + return swaggerToClassGenerator.getTypeFactory().constructFromCanonical(canonical); + } catch (Throwable e) { + // ignore this } } diff --git a/swagger/swagger-generator/generator-core/src/main/java/org/apache/servicecomb/swagger/converter/SwaggerToClassGenerator.java b/swagger/swagger-generator/generator-core/src/main/java/org/apache/servicecomb/swagger/converter/SwaggerToClassGenerator.java index 3205c0227..1db2db309 100644 --- a/swagger/swagger-generator/generator-core/src/main/java/org/apache/servicecomb/swagger/converter/SwaggerToClassGenerator.java +++ b/swagger/swagger-generator/generator-core/src/main/java/org/apache/servicecomb/swagger/converter/SwaggerToClassGenerator.java @@ -22,6 +22,7 @@ import java.util.Map.Entry; import org.apache.servicecomb.common.javassist.ClassConfig; +import org.apache.servicecomb.common.javassist.CtTypeJavaType; import org.apache.servicecomb.common.javassist.JavassistUtils; import org.apache.servicecomb.common.javassist.MethodConfig; import org.apache.servicecomb.swagger.generator.core.SwaggerConst; @@ -39,6 +40,8 @@ import io.swagger.models.Swagger; import io.swagger.models.parameters.Parameter; import io.swagger.models.properties.Property; +import javassist.ClassPool; +import javassist.CtClass; /** * generate interface from swagger<br> @@ -85,6 +88,8 @@ private TypeFactory typeFactory; + private ClassPool classPool; + // key is swagger model or property @VisibleForTesting protected Map<Object, JavaType> swaggerObjectMap = new IdentityHashMap<>(); @@ -101,6 +106,11 @@ public SwaggerToClassGenerator(ClassLoader classLoader, Swagger swagger, String this.packageName = packageName; this.typeFactory = TypeFactory.defaultInstance().withClassLoader(classLoader); + this.classPool = JavassistUtils.getOrCreateClassPool(classLoader); + } + + public ClassPool getClassPool() { + return classPool; } public void setInterfaceName(String interfaceName) { @@ -136,6 +146,7 @@ public TypeFactory getTypeFactory() { mapDefinitionsToExistingClasses(); convertDefinitions(); convertResponses(); + convertPendingCtClasses(); convertToInterface(); return interfaceCls; @@ -232,6 +243,19 @@ protected void convertToInterface() { interfaceCls = JavassistUtils.createClass(classLoader, classConfig); } + protected void convertPendingCtClasses() { + for (Entry<Object, JavaType> entry : swaggerObjectMap.entrySet()) { + JavaType javaType = entry.getValue(); + if (!CtTypeJavaType.class.isInstance(javaType)) { + continue; + } + + CtClass ctClass = ((CtTypeJavaType) javaType).getType().getCtClass(); + Class<?> cls = JavassistUtils.createClass(classLoader, ctClass); + entry.setValue(typeFactory.constructType(cls)); + } + } + public JavaType convert(Object swaggerObject) { JavaType javaType = swaggerObjectMap.get(swaggerObject); if (javaType == null) { @@ -241,6 +265,16 @@ public JavaType convert(Object swaggerObject) { return javaType; } + /** + * just only for invoker know that there is no recursive dependency + * + */ + public JavaType forceConvert(Object swaggerObject) { + convert(swaggerObject); + convertPendingCtClasses(); + return swaggerObjectMap.get(swaggerObject); + } + protected void updateJavaClassInVendor(Map<String, Object> vendorExtensions, String shortClsName) { String clsName = ClassUtils.getClassName(vendorExtensions); if (StringUtils.isEmpty(clsName)) { 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 827c07bb4..fa38d7432 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 @@ -21,6 +21,8 @@ import java.util.Map.Entry; import org.apache.servicecomb.common.javassist.ClassConfig; +import org.apache.servicecomb.common.javassist.CtType; +import org.apache.servicecomb.common.javassist.CtTypeJavaType; import org.apache.servicecomb.common.javassist.JavassistUtils; import org.apache.servicecomb.swagger.converter.ConverterMgr; import org.apache.servicecomb.swagger.converter.SwaggerToClassGenerator; @@ -33,6 +35,7 @@ import io.swagger.models.ModelImpl; import io.swagger.models.properties.ObjectProperty; import io.swagger.models.properties.Property; +import javassist.CtClass; public class ModelImplConverter extends AbstractModelConverter { @Override @@ -65,18 +68,31 @@ protected JavaType getOrCreateType(SwaggerToClassGenerator swaggerToClassGenerat String clsName = ClassUtils.getClassName(findVendorExtensions(modelImpl)); clsName = ClassUtils.correctClassName(clsName); - Class<?> cls = getOrCreateClass(swaggerToClassGenerator, modelImpl.getProperties(), clsName); - return TypeFactory.defaultInstance().constructType(cls); + return getOrCreateType(swaggerToClassGenerator, modelImpl.getProperties(), clsName); } - protected Class<?> getOrCreateClass(SwaggerToClassGenerator swaggerToClassGenerator, + protected JavaType getOrCreateType(SwaggerToClassGenerator swaggerToClassGenerator, Map<String, Property> properties, String clsName) { Class<?> cls = ClassUtils.getClassByName(swaggerToClassGenerator.getClassLoader(), clsName); if (cls != null) { - return cls; + return swaggerToClassGenerator.getTypeFactory().constructType(cls); } + CtClass ctClass = getOrCreateCtClass(swaggerToClassGenerator, properties, clsName); + return new CtTypeJavaType(new CtType(ctClass)); + } + + private CtClass getOrCreateCtClass(SwaggerToClassGenerator swaggerToClassGenerator, Map<String, Property> properties, + String clsName) { + CtClass ctClass = swaggerToClassGenerator.getClassPool().getOrNull(clsName); + if (ctClass != null) { + return ctClass; + } + + // must ensure already create CtClass, otherwise recursive dependency class will create failed. + swaggerToClassGenerator.getClassPool().makeClass(clsName); + ClassConfig classConfig = new ClassConfig(); classConfig.setClassName(clsName); @@ -87,7 +103,6 @@ protected JavaType getOrCreateType(SwaggerToClassGenerator swaggerToClassGenerat } } - cls = JavassistUtils.createClass(swaggerToClassGenerator.getClassLoader(), classConfig); - return cls; + return JavassistUtils.createCtClass(swaggerToClassGenerator.getClassLoader(), classConfig); } } diff --git a/swagger/swagger-generator/generator-core/src/main/java/org/apache/servicecomb/swagger/converter/property/ArrayPropertyConverter.java b/swagger/swagger-generator/generator-core/src/main/java/org/apache/servicecomb/swagger/converter/property/ArrayPropertyConverter.java index fb0631c48..e7f8d161e 100644 --- a/swagger/swagger-generator/generator-core/src/main/java/org/apache/servicecomb/swagger/converter/property/ArrayPropertyConverter.java +++ b/swagger/swagger-generator/generator-core/src/main/java/org/apache/servicecomb/swagger/converter/property/ArrayPropertyConverter.java @@ -24,7 +24,6 @@ import org.apache.servicecomb.swagger.converter.SwaggerToClassGenerator; import com.fasterxml.jackson.databind.JavaType; -import com.fasterxml.jackson.databind.type.TypeFactory; import io.swagger.models.properties.ArrayProperty; import io.swagger.models.properties.Property; @@ -40,7 +39,7 @@ public static JavaType findJavaType(SwaggerToClassGenerator swaggerToClassGenera if (Boolean.TRUE.equals(uniqueItems)) { collectionClass = Set.class; } - return TypeFactory.defaultInstance().constructCollectionType(collectionClass, itemJavaType); + return swaggerToClassGenerator.getTypeFactory().constructCollectionType(collectionClass, itemJavaType); } @Override 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 index 6e0e178b9..be5dbdcf4 100644 --- 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 @@ -102,7 +102,7 @@ public void convert_empty() { Model model = swagger.getDefinitions().get(Empty.class.getSimpleName()); model.getVendorExtensions().put(SwaggerConst.EXT_JAVA_CLASS, "pkg.Empty"); - JavaType javaType = swaggerToClassGenerator.convert(model); + JavaType javaType = swaggerToClassGenerator.forceConvert(model); Assert.assertEquals("pkg.Empty", javaType.getRawClass().getName()); } @@ -125,7 +125,7 @@ public void convert_createClass() throws NoSuchFieldException { swagger.addDefinition("cls", model); - JavaType javaType = swaggerToClassGenerator.convert(model); + JavaType javaType = swaggerToClassGenerator.forceConvert(model); Class<?> cls = javaType.getRawClass(); Assert.assertEquals("pkg.Model", cls.getName()); diff --git a/swagger/swagger-generator/generator-springmvc/src/test/java/org/apache/servicecomb/swagger/converter/swaggerToClassGenerator/TestSwaggerToClassGenerator_base.java b/swagger/swagger-generator/generator-springmvc/src/test/java/org/apache/servicecomb/swagger/converter/swaggerToClassGenerator/TestSwaggerToClassGenerator_base.java new file mode 100644 index 000000000..3ef0189d2 --- /dev/null +++ b/swagger/swagger-generator/generator-springmvc/src/test/java/org/apache/servicecomb/swagger/converter/swaggerToClassGenerator/TestSwaggerToClassGenerator_base.java @@ -0,0 +1,55 @@ +/* + * 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.swaggerToClassGenerator; + +import org.apache.servicecomb.common.javassist.JavassistUtils; +import org.apache.servicecomb.swagger.converter.SwaggerToClassGenerator; +import org.apache.servicecomb.swagger.generator.core.SwaggerConst; +import org.apache.servicecomb.swagger.generator.core.SwaggerGenerator; +import org.apache.servicecomb.swagger.generator.core.SwaggerGeneratorContext; +import org.apache.servicecomb.swagger.generator.springmvc.SpringmvcSwaggerGeneratorContext; + +import io.swagger.models.Model; +import io.swagger.models.Swagger; + +public class TestSwaggerToClassGenerator_base { + ClassLoader classLoader = new ClassLoader() { + }; + + SwaggerGeneratorContext context = new SpringmvcSwaggerGeneratorContext(); + + SwaggerGenerator swaggerGenerator = new SwaggerGenerator(context, ToClassSchema.class); + + Swagger swagger = swaggerGenerator.generate(); + + SwaggerToClassGenerator swaggerToClassGenerator = new SwaggerToClassGenerator(classLoader, swagger, "gen"); + + Class<?> swaggerIntf; + + public TestSwaggerToClassGenerator_base(boolean clearXJavaClass) { + if (clearXJavaClass) { + for (Model model : swagger.getDefinitions().values()) { + model.getVendorExtensions().remove(SwaggerConst.EXT_JAVA_CLASS); + } + } + swaggerIntf = swaggerToClassGenerator.convert(); + } + + public void tearDown() { + JavassistUtils.clearByClassLoader(classLoader); + } +} diff --git a/swagger/swagger-generator/generator-springmvc/src/test/java/org/apache/servicecomb/swagger/converter/swaggerToClassGenerator/TestSwaggerToClassGenerator_create.java b/swagger/swagger-generator/generator-springmvc/src/test/java/org/apache/servicecomb/swagger/converter/swaggerToClassGenerator/TestSwaggerToClassGenerator_create.java new file mode 100644 index 000000000..bfd713812 --- /dev/null +++ b/swagger/swagger-generator/generator-springmvc/src/test/java/org/apache/servicecomb/swagger/converter/swaggerToClassGenerator/TestSwaggerToClassGenerator_create.java @@ -0,0 +1,61 @@ +/* + * 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.swaggerToClassGenerator; + +import java.lang.reflect.Method; + +import org.apache.servicecomb.foundation.common.utils.ReflectUtils; +import org.junit.AfterClass; +import org.junit.Assert; +import org.junit.Test; + +public class TestSwaggerToClassGenerator_create { + static TestSwaggerToClassGenerator_base base = new TestSwaggerToClassGenerator_base(true); + + @AfterClass + public static void tearDown() { + base.tearDown(); + } + + @Test + public void recursiveSelf() throws NoSuchFieldException { + Method method = ReflectUtils.findMethod(base.swaggerIntf, "recursiveSelf"); + Class<?> returnType = method.getReturnType(); + + Assert.assertEquals("gen.RecursiveSelfType", returnType.getName()); + Assert.assertEquals("gen.RecursiveSelfType", returnType.getField("field").getType().getName()); + } + + @Test + public void dependType() throws NoSuchFieldException { + Method method = ReflectUtils.findMethod(base.swaggerIntf, "dependType"); + Class<?> returnType = method.getReturnType(); + + Assert.assertEquals("gen.DependTypeA", returnType.getName()); + Assert.assertEquals("gen.DependTypeB", returnType.getField("b").getType().getName()); + } + + @Test + public void generic() throws NoSuchFieldException { + Method method = ReflectUtils.findMethod(base.swaggerIntf, "generic"); + Class<?> returnType = method.getReturnType(); + + Assert.assertEquals("gen.GenericRecursiveSelfTypeDependTypeA", returnType.getName()); + Assert.assertEquals("gen.RecursiveSelfType", returnType.getField("v1").getType().getName()); + Assert.assertEquals("gen.DependTypeA", returnType.getField("v2").getType().getName()); + } +} diff --git a/swagger/swagger-generator/generator-springmvc/src/test/java/org/apache/servicecomb/swagger/converter/swaggerToClassGenerator/TestSwaggerToClassGenerator_reuse.java b/swagger/swagger-generator/generator-springmvc/src/test/java/org/apache/servicecomb/swagger/converter/swaggerToClassGenerator/TestSwaggerToClassGenerator_reuse.java new file mode 100644 index 000000000..e4a067117 --- /dev/null +++ b/swagger/swagger-generator/generator-springmvc/src/test/java/org/apache/servicecomb/swagger/converter/swaggerToClassGenerator/TestSwaggerToClassGenerator_reuse.java @@ -0,0 +1,63 @@ +/* + * 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.swaggerToClassGenerator; + +import java.lang.reflect.Method; +import java.lang.reflect.ParameterizedType; + +import org.apache.servicecomb.foundation.common.utils.ReflectUtils; +import org.apache.servicecomb.swagger.converter.swaggerToClassGenerator.model.DependTypeA; +import org.apache.servicecomb.swagger.converter.swaggerToClassGenerator.model.Generic; +import org.apache.servicecomb.swagger.converter.swaggerToClassGenerator.model.RecursiveSelfType; +import org.junit.AfterClass; +import org.junit.Assert; +import org.junit.Test; + +public class TestSwaggerToClassGenerator_reuse { + static TestSwaggerToClassGenerator_base base = new TestSwaggerToClassGenerator_base(false); + + @AfterClass + public static void tearDown() { + base.tearDown(); + } + + @Test + public void recursiveSelf() { + Method method = ReflectUtils.findMethod(base.swaggerIntf, "recursiveSelf"); + Class<?> returnType = method.getReturnType(); + + Assert.assertSame(RecursiveSelfType.class, returnType); + } + + @Test + public void dependType() { + Method method = ReflectUtils.findMethod(base.swaggerIntf, "dependType"); + Class<?> returnType = method.getReturnType(); + + Assert.assertSame(DependTypeA.class, returnType); + } + + @Test + public void generic() { + Method method = ReflectUtils.findMethod(base.swaggerIntf, "generic"); + ParameterizedType returnType = (ParameterizedType) method.getGenericReturnType(); + + Assert.assertSame(Generic.class, returnType.getRawType()); + Assert.assertSame(RecursiveSelfType.class, returnType.getActualTypeArguments()[0]); + Assert.assertSame(DependTypeA.class, returnType.getActualTypeArguments()[1]); + } +} diff --git a/swagger/swagger-generator/generator-springmvc/src/test/java/org/apache/servicecomb/swagger/converter/swaggerToClassGenerator/ToClassSchema.java b/swagger/swagger-generator/generator-springmvc/src/test/java/org/apache/servicecomb/swagger/converter/swaggerToClassGenerator/ToClassSchema.java new file mode 100644 index 000000000..e0f6cacdd --- /dev/null +++ b/swagger/swagger-generator/generator-springmvc/src/test/java/org/apache/servicecomb/swagger/converter/swaggerToClassGenerator/ToClassSchema.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.converter.swaggerToClassGenerator; + + +import org.apache.servicecomb.swagger.converter.swaggerToClassGenerator.model.DependTypeA; +import org.apache.servicecomb.swagger.converter.swaggerToClassGenerator.model.Generic; +import org.apache.servicecomb.swagger.converter.swaggerToClassGenerator.model.RecursiveSelfType; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RequestMapping; + +@RequestMapping(path = "/") +public class ToClassSchema { + @GetMapping(path = "recursiveSelf") + public RecursiveSelfType recursiveSelf() { + return null; + } + + @GetMapping(path = "dependType") + public DependTypeA dependType() { + return null; + } + + @GetMapping(path = "generic") + public Generic<RecursiveSelfType, DependTypeA> generic() { + return null; + } +} diff --git a/swagger/swagger-generator/generator-springmvc/src/test/java/org/apache/servicecomb/swagger/converter/swaggerToClassGenerator/model/DependTypeA.java b/swagger/swagger-generator/generator-springmvc/src/test/java/org/apache/servicecomb/swagger/converter/swaggerToClassGenerator/model/DependTypeA.java new file mode 100644 index 000000000..3fc8a96d1 --- /dev/null +++ b/swagger/swagger-generator/generator-springmvc/src/test/java/org/apache/servicecomb/swagger/converter/swaggerToClassGenerator/model/DependTypeA.java @@ -0,0 +1,29 @@ +/* + * 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.swaggerToClassGenerator.model; + +public class DependTypeA { + private DependTypeB b; + + public DependTypeB getB() { + return b; + } + + public void setB(DependTypeB b) { + this.b = b; + } +} diff --git a/swagger/swagger-generator/generator-springmvc/src/test/java/org/apache/servicecomb/swagger/converter/swaggerToClassGenerator/model/DependTypeB.java b/swagger/swagger-generator/generator-springmvc/src/test/java/org/apache/servicecomb/swagger/converter/swaggerToClassGenerator/model/DependTypeB.java new file mode 100644 index 000000000..5957ca32f --- /dev/null +++ b/swagger/swagger-generator/generator-springmvc/src/test/java/org/apache/servicecomb/swagger/converter/swaggerToClassGenerator/model/DependTypeB.java @@ -0,0 +1,29 @@ +/* + * 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.swaggerToClassGenerator.model; + +public class DependTypeB { + private DependTypeA a; + + public DependTypeA getA() { + return a; + } + + public void setA(DependTypeA a) { + this.a = a; + } +} diff --git a/swagger/swagger-generator/generator-springmvc/src/test/java/org/apache/servicecomb/swagger/converter/swaggerToClassGenerator/model/Generic.java b/swagger/swagger-generator/generator-springmvc/src/test/java/org/apache/servicecomb/swagger/converter/swaggerToClassGenerator/model/Generic.java new file mode 100644 index 000000000..69aec3d6b --- /dev/null +++ b/swagger/swagger-generator/generator-springmvc/src/test/java/org/apache/servicecomb/swagger/converter/swaggerToClassGenerator/model/Generic.java @@ -0,0 +1,39 @@ +/* + * 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.swaggerToClassGenerator.model; + +public class Generic<T1, T2> { + private T1 v1; + + private T2 v2; + + public T1 getV1() { + return v1; + } + + public void setV1(T1 v1) { + this.v1 = v1; + } + + public T2 getV2() { + return v2; + } + + public void setV2(T2 v2) { + this.v2 = v2; + } +} diff --git a/swagger/swagger-generator/generator-springmvc/src/test/java/org/apache/servicecomb/swagger/converter/swaggerToClassGenerator/model/RecursiveSelfType.java b/swagger/swagger-generator/generator-springmvc/src/test/java/org/apache/servicecomb/swagger/converter/swaggerToClassGenerator/model/RecursiveSelfType.java new file mode 100644 index 000000000..9b8b663d4 --- /dev/null +++ b/swagger/swagger-generator/generator-springmvc/src/test/java/org/apache/servicecomb/swagger/converter/swaggerToClassGenerator/model/RecursiveSelfType.java @@ -0,0 +1,29 @@ +/* + * 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.swaggerToClassGenerator.model; + +public class RecursiveSelfType { + private RecursiveSelfType field; + + public RecursiveSelfType getField() { + return field; + } + + public void setField(RecursiveSelfType field) { + this.field = field; + } +} ---------------------------------------------------------------- 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: us...@infra.apache.org With regards, Apache Git Services