You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@servicecomb.apache.org by li...@apache.org on 2018/08/01 09:53:38 UTC
[incubator-servicecomb-java-chassis] 02/06: [SCB-777] Add
BeanParamAnnotationProcessor
This is an automated email from the ASF dual-hosted git repository.
liubao pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/incubator-servicecomb-java-chassis.git
commit a6853602f784dc382b12795397ced64f71276d93
Author: yaohaishi <ya...@huawei.com>
AuthorDate: Sat Jul 28 17:35:29 2018 +0800
[SCB-777] Add BeanParamAnnotationProcessor
---
.../swagger/generator/core/utils/ParamUtils.java | 23 ++-
.../swagger/generator/core/TestParamUtils.java | 29 +++
.../jaxrs/JaxrsSwaggerGeneratorContext.java | 3 +
.../annotation/BeanParamAnnotationProcessor.java | 169 +++++++++++++++
.../BeanParamAnnotationProcessorTest.java | 230 +++++++++++++++++++++
5 files changed, 453 insertions(+), 1 deletion(-)
diff --git a/swagger/swagger-generator/generator-core/src/main/java/org/apache/servicecomb/swagger/generator/core/utils/ParamUtils.java b/swagger/swagger-generator/generator-core/src/main/java/org/apache/servicecomb/swagger/generator/core/utils/ParamUtils.java
index db6d201..924bc65 100644
--- a/swagger/swagger-generator/generator-core/src/main/java/org/apache/servicecomb/swagger/generator/core/utils/ParamUtils.java
+++ b/swagger/swagger-generator/generator-core/src/main/java/org/apache/servicecomb/swagger/generator/core/utils/ParamUtils.java
@@ -113,7 +113,7 @@ public final class ParamUtils {
Property property = ModelConverters.getInstance().readAsProperty(paramType);
if (isComplexProperty(property)) {
- // 简单参数不可以是复杂类型
+ // cannot set a simple parameter(header, query, etc.) as complex type
String msg = String.format("not allow complex type for %s parameter, method=%s:%s, paramIdx=%d, type=%s",
parameter.getIn(),
method.getDeclaringClass().getName(),
@@ -125,6 +125,27 @@ public final class ParamUtils {
parameter.setProperty(property);
}
+ /**
+ * Set param type info. For {@linkplain javax.ws.rs.BeanParam BeanParam} scenario.
+ *
+ * @param paramType type of the swagger parameter
+ * @param parameter swagger parameter
+ */
+ public static void setParameterType(Type paramType, AbstractSerializableParameter<?> parameter) {
+ Property property = ModelConverters.getInstance().readAsProperty(paramType);
+
+ if (isComplexProperty(property)) {
+ // cannot set a simple parameter(header, query, etc.) as complex type
+ throw new IllegalArgumentException(
+ String.format(
+ "not allow such type of param:[%s], param name is [%s]",
+ property.getClass(),
+ parameter.getName())
+ );
+ }
+ parameter.setProperty(property);
+ }
+
public static boolean isComplexProperty(Property property) {
if (RefProperty.class.isInstance(property) || ObjectProperty.class.isInstance(property)
|| MapProperty.class.isInstance(property)) {
diff --git a/swagger/swagger-generator/generator-core/src/test/java/org/apache/servicecomb/swagger/generator/core/TestParamUtils.java b/swagger/swagger-generator/generator-core/src/test/java/org/apache/servicecomb/swagger/generator/core/TestParamUtils.java
index 1343f5f..b116174 100644
--- a/swagger/swagger-generator/generator-core/src/test/java/org/apache/servicecomb/swagger/generator/core/TestParamUtils.java
+++ b/swagger/swagger-generator/generator-core/src/test/java/org/apache/servicecomb/swagger/generator/core/TestParamUtils.java
@@ -19,6 +19,7 @@ package org.apache.servicecomb.swagger.generator.core;
import static org.mockito.Mockito.when;
+import java.util.ArrayList;
import java.util.HashMap;
import java.util.Map;
@@ -28,7 +29,10 @@ import org.junit.Assert;
import org.junit.Test;
import org.mockito.Mockito;
+import io.swagger.models.parameters.AbstractSerializableParameter;
+import io.swagger.models.parameters.HeaderParameter;
import io.swagger.models.parameters.Parameter;
+import io.swagger.models.parameters.QueryParameter;
import io.swagger.models.properties.ArrayProperty;
import io.swagger.models.properties.MapProperty;
import io.swagger.models.properties.ObjectProperty;
@@ -66,4 +70,29 @@ public class TestParamUtils {
property = new StringProperty();
Assert.assertFalse(ParamUtils.isComplexProperty(property));
}
+
+ @Test
+ public void setParameterTypeByTypeNormal() {
+ AbstractSerializableParameter<?> parameter = new QueryParameter();
+ ParamUtils.setParameterType(String.class, parameter);
+ Assert.assertEquals("string", parameter.getType());
+
+ parameter = new HeaderParameter();
+ ParamUtils.setParameterType(long.class, parameter);
+ Assert.assertEquals("integer", parameter.getType());
+ Assert.assertEquals("int64", parameter.getFormat());
+ }
+
+ @Test
+ public void setParameterTypeByTypeOnComplexType() {
+ AbstractSerializableParameter<?> parameter = new QueryParameter();
+ parameter.setName("testName");
+ try {
+ ParamUtils.setParameterType(ArrayList.class, parameter);
+ Assert.fail("an exception is expected!");
+ } catch (IllegalArgumentException e) {
+ Assert.assertEquals("not allow such type of param:[class io.swagger.models.properties.ArrayProperty], "
+ + "param name is [testName]", e.getMessage());
+ }
+ }
}
diff --git a/swagger/swagger-generator/generator-jaxrs/src/main/java/org/apache/servicecomb/swagger/generator/jaxrs/JaxrsSwaggerGeneratorContext.java b/swagger/swagger-generator/generator-jaxrs/src/main/java/org/apache/servicecomb/swagger/generator/jaxrs/JaxrsSwaggerGeneratorContext.java
index 1c707cf..7acabe0 100644
--- a/swagger/swagger-generator/generator-jaxrs/src/main/java/org/apache/servicecomb/swagger/generator/jaxrs/JaxrsSwaggerGeneratorContext.java
+++ b/swagger/swagger-generator/generator-jaxrs/src/main/java/org/apache/servicecomb/swagger/generator/jaxrs/JaxrsSwaggerGeneratorContext.java
@@ -20,6 +20,7 @@ package org.apache.servicecomb.swagger.generator.jaxrs;
import java.lang.annotation.Annotation;
import java.lang.reflect.Method;
+import javax.ws.rs.BeanParam;
import javax.ws.rs.Consumes;
import javax.ws.rs.CookieParam;
import javax.ws.rs.DELETE;
@@ -35,6 +36,7 @@ import javax.ws.rs.Produces;
import javax.ws.rs.QueryParam;
import org.apache.servicecomb.swagger.generator.core.utils.ClassUtils;
+import org.apache.servicecomb.swagger.generator.jaxrs.processor.annotation.BeanParamAnnotationProcessor;
import org.apache.servicecomb.swagger.generator.jaxrs.processor.annotation.ConsumesAnnotationProcessor;
import org.apache.servicecomb.swagger.generator.jaxrs.processor.annotation.CookieParamAnnotationProcessor;
import org.apache.servicecomb.swagger.generator.jaxrs.processor.annotation.FormParamAnnotationProcessor;
@@ -110,5 +112,6 @@ public class JaxrsSwaggerGeneratorContext extends RestSwaggerGeneratorContext {
parameterAnnotationMgr.register(HeaderParam.class, new HeaderParamAnnotationProcessor());
parameterAnnotationMgr.register(QueryParam.class, new QueryParamAnnotationProcessor());
+ parameterAnnotationMgr.register(BeanParam.class, new BeanParamAnnotationProcessor());
}
}
diff --git a/swagger/swagger-generator/generator-jaxrs/src/main/java/org/apache/servicecomb/swagger/generator/jaxrs/processor/annotation/BeanParamAnnotationProcessor.java b/swagger/swagger-generator/generator-jaxrs/src/main/java/org/apache/servicecomb/swagger/generator/jaxrs/processor/annotation/BeanParamAnnotationProcessor.java
new file mode 100644
index 0000000..a3a8eae
--- /dev/null
+++ b/swagger/swagger-generator/generator-jaxrs/src/main/java/org/apache/servicecomb/swagger/generator/jaxrs/processor/annotation/BeanParamAnnotationProcessor.java
@@ -0,0 +1,169 @@
+/*
+ * 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.generator.jaxrs.processor.annotation;
+
+import java.lang.annotation.Annotation;
+import java.lang.reflect.Field;
+import java.lang.reflect.Method;
+import java.lang.reflect.Type;
+import java.util.HashSet;
+import java.util.Set;
+
+import javax.ws.rs.CookieParam;
+import javax.ws.rs.DefaultValue;
+import javax.ws.rs.FormParam;
+import javax.ws.rs.HeaderParam;
+import javax.ws.rs.PathParam;
+import javax.ws.rs.QueryParam;
+
+import org.apache.servicecomb.swagger.generator.core.OperationGenerator;
+import org.apache.servicecomb.swagger.generator.core.ParameterAnnotationProcessor;
+import org.apache.servicecomb.swagger.generator.core.SwaggerGeneratorContext;
+import org.apache.servicecomb.swagger.generator.core.processor.parameter.AbstractParameterProcessor;
+import org.apache.servicecomb.swagger.generator.core.utils.ParamUtils;
+
+import io.swagger.models.parameters.AbstractSerializableParameter;
+
+/**
+ * For {@link javax.ws.rs.BeanParam}
+ */
+public class BeanParamAnnotationProcessor implements ParameterAnnotationProcessor {
+ public static final Set<Class<?>> SUPPORTED_PARAM_ANNOTATIONS = new HashSet<>();
+
+ public static final String SETTER_METHOD_PREFIX = "set";
+
+ static {
+ SUPPORTED_PARAM_ANNOTATIONS.add(PathParam.class);
+ SUPPORTED_PARAM_ANNOTATIONS.add(QueryParam.class);
+ SUPPORTED_PARAM_ANNOTATIONS.add(HeaderParam.class);
+ SUPPORTED_PARAM_ANNOTATIONS.add(CookieParam.class);
+ SUPPORTED_PARAM_ANNOTATIONS.add(FormParam.class);
+ }
+
+ @Override
+ public void process(Object annotation, OperationGenerator operationGenerator, int paramIdx) {
+ final Class<?> beanParamClazz = operationGenerator.getProviderMethod().getParameterTypes()[paramIdx];
+ try {
+ // traversal fields, get those JAX-RS params
+ processParamField(operationGenerator, beanParamClazz);
+ // traversal setter methods, some setter method may also be tagged with param annotations
+ processParamSetter(operationGenerator, beanParamClazz);
+ } catch (IllegalArgumentException e) {
+ throw new Error(String.format(
+ "Processing param failed, method=%s:%s, beanParamIdx=%d",
+ operationGenerator.getProviderMethod().getDeclaringClass().getName(),
+ operationGenerator.getProviderMethod().getName(),
+ paramIdx)
+ , e);
+ }
+ }
+
+ /**
+ * Process those setter methods tagged by JAX-RS param annotations.
+ */
+ private void processParamSetter(OperationGenerator operationGenerator, Class<?> beanParamClazz) {
+ for (Method method : beanParamClazz.getDeclaredMethods()) {
+ if (!method.getName().startsWith(SETTER_METHOD_PREFIX)) {
+ // only process setter methods
+ continue;
+ }
+ // There should be one and only one param in a setter method
+ final Type genericParamType = method.getGenericParameterTypes()[0];
+ processBeanParamMember(operationGenerator, method.getAnnotations(), genericParamType);
+ }
+ }
+
+ /**
+ * Process those fields tagged by JAX-RS param annotations.
+ */
+ private void processParamField(OperationGenerator operationGenerator, Class<?> beanParamClazz) {
+ for (Field beanParamField : beanParamClazz.getDeclaredFields()) {
+ processBeanParamMember(operationGenerator, beanParamField.getAnnotations(), beanParamField.getGenericType());
+ }
+ }
+
+ /**
+ * Process a swagger parameter defined by field or setter method in this BeanParam.
+ * After processing, a swagger parameter is generated and set into {@code operationGenerator}.
+ *
+ * @param operationGenerator operationGenerator
+ * @param annotations annotations on fields or setter methods
+ * @param genericType type of the fields, or the param type of the setter methods
+ */
+ private void processBeanParamMember(OperationGenerator operationGenerator, Annotation[] annotations,
+ Type genericType) {
+ String defaultValue = null;
+ for (Annotation fieldAnnotation : annotations) {
+ if (!SUPPORTED_PARAM_ANNOTATIONS.contains(fieldAnnotation.annotationType())) {
+ if (fieldAnnotation instanceof DefaultValue) {
+ defaultValue = ((DefaultValue) fieldAnnotation).value();
+ }
+ continue;
+ }
+
+ setUpParameter(operationGenerator, fieldAnnotation, genericType, defaultValue);
+ }
+ }
+
+ /**
+ * Generate swagger parameter, set default value, and add it into {@code operationGenerator}.
+ *
+ * @param operationGenerator operationGenerator
+ * @param fieldAnnotation JAX-RS param annotation
+ * @param genericParamType type of the parameter
+ * @param defaultValue default value, can be null
+ */
+ private void setUpParameter(
+ OperationGenerator operationGenerator,
+ Annotation fieldAnnotation,
+ Type genericParamType,
+ String defaultValue) {
+ AbstractSerializableParameter<?> parameter = generateParameter(
+ operationGenerator.getContext(),
+ fieldAnnotation,
+ genericParamType);
+
+ if (null != defaultValue) {
+ parameter.setDefaultValue(defaultValue);
+ }
+ operationGenerator.addProviderParameter(parameter);
+ }
+
+ /**
+ * Generate a swagger parameter, set up name and type info.
+ *
+ * @param swaggerGeneratorContext context data carried by {@linkplain OperationGenerator}
+ * @param fieldAnnotation JAX-RS param annotation
+ * @param genericParamType default value, can be null
+ * @return the generated swagger parameter
+ */
+ private AbstractSerializableParameter<?> generateParameter(
+ SwaggerGeneratorContext swaggerGeneratorContext,
+ Annotation fieldAnnotation,
+ Type genericParamType) {
+ // find the corresponding ParameterProcessor and process the parameter
+ final AbstractParameterProcessor<?> parameterAnnotationProcessor =
+ (AbstractParameterProcessor<?>) swaggerGeneratorContext
+ .findParameterAnnotationProcessor(fieldAnnotation.annotationType());
+ AbstractSerializableParameter<?> parameter = parameterAnnotationProcessor.createParameter();
+ String paramName = parameterAnnotationProcessor.getAnnotationParameterName(fieldAnnotation);
+ parameter.setName(paramName);
+ ParamUtils.setParameterType(genericParamType, parameter);
+ return parameter;
+ }
+}
diff --git a/swagger/swagger-generator/generator-jaxrs/src/test/java/org/apache/servicecomb/swagger/generator/jaxrs/processor/annotation/BeanParamAnnotationProcessorTest.java b/swagger/swagger-generator/generator-jaxrs/src/test/java/org/apache/servicecomb/swagger/generator/jaxrs/processor/annotation/BeanParamAnnotationProcessorTest.java
new file mode 100644
index 0000000..f81e2dd
--- /dev/null
+++ b/swagger/swagger-generator/generator-jaxrs/src/test/java/org/apache/servicecomb/swagger/generator/jaxrs/processor/annotation/BeanParamAnnotationProcessorTest.java
@@ -0,0 +1,230 @@
+/*
+ * 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.generator.jaxrs.processor.annotation;
+
+import static org.junit.Assert.assertEquals;
+
+import java.lang.reflect.Method;
+import java.util.List;
+
+import javax.ws.rs.BeanParam;
+import javax.ws.rs.CookieParam;
+import javax.ws.rs.DefaultValue;
+import javax.ws.rs.FormParam;
+import javax.ws.rs.HeaderParam;
+import javax.ws.rs.PathParam;
+import javax.ws.rs.QueryParam;
+
+import org.apache.servicecomb.swagger.generator.core.OperationGenerator;
+import org.apache.servicecomb.swagger.generator.core.SwaggerGenerator;
+import org.apache.servicecomb.swagger.generator.jaxrs.JaxrsSwaggerGeneratorContext;
+import org.junit.Test;
+
+import io.swagger.models.parameters.AbstractSerializableParameter;
+import io.swagger.models.parameters.Parameter;
+
+public class BeanParamAnnotationProcessorTest {
+
+ @Test
+ public void processSuccess() throws NoSuchMethodException {
+ BeanParamAnnotationProcessor processor = new BeanParamAnnotationProcessor();
+ final OperationGenerator operationGenerator = mockOperationGenerator("testBeanParam", AggregatedParam.class);
+
+ processor.process(null, operationGenerator, 0);
+
+ final List<Parameter> providerParameters = operationGenerator.getProviderParameters();
+ assertEquals(5, providerParameters.size());
+ AbstractSerializableParameter<?> parameter = (AbstractSerializableParameter<?>) providerParameters.get(0);
+ assertEquals("path", parameter.getIn());
+ assertEquals("path0", parameter.getName());
+ assertEquals("pa", parameter.getDefault());
+ assertEquals("string", parameter.getType());
+ parameter = (AbstractSerializableParameter<?>) providerParameters.get(1);
+ assertEquals("query", parameter.getIn());
+ assertEquals("query1", parameter.getName());
+ assertEquals("integer", parameter.getType());
+ assertEquals("int32", parameter.getFormat());
+ parameter = (AbstractSerializableParameter<?>) providerParameters.get(2);
+ assertEquals("header", parameter.getIn());
+ assertEquals("header2", parameter.getName());
+ assertEquals("string", parameter.getType());
+ parameter = (AbstractSerializableParameter<?>) providerParameters.get(3);
+ assertEquals("formData", parameter.getIn());
+ assertEquals("form3", parameter.getName());
+ assertEquals(12L, parameter.getDefault());
+ assertEquals("integer", parameter.getType());
+ assertEquals("int64", parameter.getFormat());
+ parameter = (AbstractSerializableParameter<?>) providerParameters.get(4);
+ assertEquals("cookie", parameter.getIn());
+ assertEquals("cookie4", parameter.getName());
+ assertEquals("integer", parameter.getType());
+ assertEquals("int64", parameter.getFormat());
+ }
+
+ @Test
+ public void processOnComplexBeanParamField() throws NoSuchMethodException {
+ BeanParamAnnotationProcessor processor = new BeanParamAnnotationProcessor();
+ final OperationGenerator operationGenerator = mockOperationGenerator("testBeanParamComplexField",
+ BeanParamComplexField.class);
+
+ try {
+ processor.process(null, operationGenerator, 0);
+ } catch (Error e) {
+ assertEquals("Processing param failed, method=org.apache.servicecomb.swagger.generator.jaxrs.processor"
+ + ".annotation.BeanParamAnnotationProcessorTest$TestProvider:testBeanParamComplexField, beanParamIdx=0",
+ e.getMessage());
+ assertEquals("not allow such type of param:[class io.swagger.models.properties.RefProperty], "
+ + "param name is [q]",
+ e.getCause().getMessage());
+ }
+ }
+
+ @Test
+ public void processOnComplexBeanParamSetter() throws NoSuchMethodException {
+ BeanParamAnnotationProcessor processor = new BeanParamAnnotationProcessor();
+ final OperationGenerator operationGenerator = mockOperationGenerator("testBeanParamComplexSetter",
+ BeanParamComplexSetter.class);
+
+ try {
+ processor.process(null, operationGenerator, 0);
+ } catch (Error e) {
+ assertEquals("Processing param failed, method=org.apache.servicecomb.swagger.generator.jaxrs.processor"
+ + ".annotation.BeanParamAnnotationProcessorTest$TestProvider:testBeanParamComplexSetter, beanParamIdx=0",
+ e.getMessage());
+ assertEquals("not allow such type of param:[class io.swagger.models.properties.RefProperty], "
+ + "param name is [h]",
+ e.getCause().getMessage());
+ }
+ }
+
+ private OperationGenerator mockOperationGenerator(String methodName, Class<?>... paramTypes)
+ throws NoSuchMethodException {
+ final Method providerMethod = TestProvider.class.getDeclaredMethod(methodName, paramTypes);
+ final SwaggerGenerator swaggerGenerator = new SwaggerGenerator(new JaxrsSwaggerGeneratorContext(),
+ TestProvider.class);
+ return new OperationGenerator(swaggerGenerator, providerMethod);
+ }
+
+ static class TestProvider {
+ public String testBeanParam(@BeanParam AggregatedParam aggregatedParam) {
+ return aggregatedParam.toString();
+ }
+
+ public String testBeanParamComplexField(@BeanParam BeanParamComplexField param) {
+ return param.toString();
+ }
+
+ public String testBeanParamComplexSetter(@BeanParam BeanParamComplexSetter param) {
+ return param.toString();
+ }
+ }
+
+ static class AggregatedParam {
+ @DefaultValue("pa")
+ @PathParam("path0")
+ private String strVal;
+
+ @QueryParam("query1")
+ private int intVal;
+
+ private long longVal;
+
+ private long cookieVal;
+
+ @HeaderParam("header2")
+ private String headerVal;
+
+ public String getStrVal() {
+ return strVal;
+ }
+
+ public AggregatedParam setStrVal(String strVal) {
+ this.strVal = strVal;
+ return this;
+ }
+
+ public int getIntVal() {
+ return intVal;
+ }
+
+ public AggregatedParam setIntVal(int intVal) {
+ this.intVal = intVal;
+ return this;
+ }
+
+ public long getLongVal() {
+ return longVal;
+ }
+
+ @DefaultValue("12")
+ @FormParam("form3")
+ public AggregatedParam setLongVal(long longVal) {
+ this.longVal = longVal;
+ return this;
+ }
+
+ public long getCookieVal() {
+ return cookieVal;
+ }
+
+ @CookieParam("cookie4")
+ public AggregatedParam setCookieVal(long cookieVal) {
+ this.cookieVal = cookieVal;
+ return this;
+ }
+
+ public String getHeaderVal() {
+ return headerVal;
+ }
+
+ public AggregatedParam setHeaderVal(String headerVal) {
+ this.headerVal = headerVal;
+ return this;
+ }
+ }
+
+ static class BeanParamComplexField {
+ @QueryParam("q")
+ private AggregatedParam complex;
+
+ public AggregatedParam getComplex() {
+ return complex;
+ }
+
+ public BeanParamComplexField setComplex(
+ AggregatedParam complex) {
+ this.complex = complex;
+ return this;
+ }
+ }
+
+ static class BeanParamComplexSetter {
+ private AggregatedParam complex;
+
+ public AggregatedParam getComplex() {
+ return complex;
+ }
+
+ @HeaderParam("h")
+ public BeanParamComplexSetter setComplex(
+ AggregatedParam complex) {
+ this.complex = complex;
+ return this;
+ }
+ }
+}
\ No newline at end of file