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/09 00:59:57 UTC
[incubator-servicecomb-java-chassis] 01/02: [SCB-800] params in
BeanParam is generated by declared field order
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 33036e194f2d726e58e08450d9b3c14392d86c22
Author: yaohaishi <ya...@huawei.com>
AuthorDate: Mon Aug 6 17:07:14 2018 +0800
[SCB-800] params in BeanParam is generated by declared field order
---
.../annotation/BeanParamAnnotationProcessor.java | 110 +++++++++---
.../BeanParamAnnotationProcessorTest.java | 185 ++++++++++++++++++---
2 files changed, 247 insertions(+), 48 deletions(-)
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
index a3a8eae..0d7f32d 100644
--- 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
@@ -17,11 +17,17 @@
package org.apache.servicecomb.swagger.generator.jaxrs.processor.annotation;
+import java.beans.IntrospectionException;
+import java.beans.PropertyDescriptor;
import java.lang.annotation.Annotation;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.lang.reflect.Type;
+import java.util.Arrays;
+import java.util.HashMap;
import java.util.HashSet;
+import java.util.Map;
+import java.util.Objects;
import java.util.Set;
import javax.ws.rs.CookieParam;
@@ -37,7 +43,10 @@ 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 com.fasterxml.jackson.annotation.JsonIgnore;
+
import io.swagger.models.parameters.AbstractSerializableParameter;
+import io.swagger.models.parameters.Parameter;
/**
* For {@link javax.ws.rs.BeanParam}
@@ -58,12 +67,11 @@ public class BeanParamAnnotationProcessor implements ParameterAnnotationProcesso
@Override
public void process(Object annotation, OperationGenerator operationGenerator, int paramIdx) {
final Class<?> beanParamClazz = operationGenerator.getProviderMethod().getParameterTypes()[paramIdx];
+ Map<String, Parameter> swaggerParamMap = new HashMap<>();
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) {
+ traversalParamField(operationGenerator, beanParamClazz, swaggerParamMap);
+ } catch (IllegalArgumentException | IntrospectionException e) {
throw new Error(String.format(
"Processing param failed, method=%s:%s, beanParamIdx=%d",
operationGenerator.getProviderMethod().getDeclaringClass().getName(),
@@ -71,41 +79,79 @@ public class BeanParamAnnotationProcessor implements ParameterAnnotationProcesso
paramIdx)
, e);
}
+
+ // set swagger params into operationGenerator, in declared field order
+ Field[] declaredProducerFields = beanParamClazz.getDeclaredFields();
+ Arrays.stream(declaredProducerFields)
+ .map(declaredProducerField -> swaggerParamMap.get(declaredProducerField.getName()))
+ .filter(Objects::nonNull)
+ .forEach(operationGenerator::addProviderParameter);
}
/**
- * Process those setter methods tagged by JAX-RS param annotations.
+ * Traversal fields of {@code beanParamClazz},
+ * generate swagger params according to JAX-RS param annotations and set them into {@code swaggerParamMap}.
+ *
+ * @param swaggerParamMap the map contains the generated swagger param,
+ * key is the name defined by source code(the declared field name) and value is the swagger param
*/
- private void processParamSetter(OperationGenerator operationGenerator, Class<?> beanParamClazz) {
- for (Method method : beanParamClazz.getDeclaredMethods()) {
- if (!method.getName().startsWith(SETTER_METHOD_PREFIX)) {
- // only process setter methods
+ private void traversalParamField(OperationGenerator operationGenerator, Class<?> beanParamClazz,
+ Map<String, Parameter> swaggerParamMap) throws IntrospectionException {
+ for (Field beanParamField : beanParamClazz.getDeclaredFields()) {
+ // ignore synthetic member to avoid build failure
+ // see https://github.com/jacoco/jacoco/issues/168
+ if (fieldShouldIgnore(beanParamField) || beanParamField.isSynthetic()) {
continue;
}
- // There should be one and only one param in a setter method
- final Type genericParamType = method.getGenericParameterTypes()[0];
- processBeanParamMember(operationGenerator, method.getAnnotations(), genericParamType);
+ // try to process this field directly
+ Parameter swaggerParam = generateSwaggerParam(operationGenerator, beanParamField.getAnnotations(),
+ beanParamField.getGenericType());
+ if (null == swaggerParam) {
+ // if swaggerParam is null, maybe the JAX-RS param annotation is tagged onto the write method
+ swaggerParam = processFieldSetter(operationGenerator, beanParamClazz, beanParamField);
+ }
+
+ if (null == swaggerParam) {
+ throw new IllegalArgumentException(String.format(
+ "There is a field[%s] cannot be mapped to swagger param. Maybe you should tag @JsonIgnore on it.",
+ beanParamField.getName())
+ );
+ }
+ swaggerParamMap.put(beanParamField.getName(), swaggerParam);
}
}
/**
- * Process those fields tagged by JAX-RS param annotations.
+ * Sometimes user may tag JAX-RS param annotations on setter method instead of fields.
+ *
+ * @param beanParamClazz class of the BeanParam
+ * @param beanParamField the field of BeanParam whose setter method is processed
+ * @return the generated swagger param, or null if the setter method is not tagged by JAX-RS param annotations
+ * @throws IntrospectionException see {@linkplain PropertyDescriptor#PropertyDescriptor(String, Class)}
*/
- private void processParamField(OperationGenerator operationGenerator, Class<?> beanParamClazz) {
- for (Field beanParamField : beanParamClazz.getDeclaredFields()) {
- processBeanParamMember(operationGenerator, beanParamField.getAnnotations(), beanParamField.getGenericType());
+ private Parameter processFieldSetter(OperationGenerator operationGenerator, Class<?> beanParamClazz,
+ Field beanParamField) throws IntrospectionException {
+ Parameter swaggerParam = null;
+ PropertyDescriptor propertyDescriptor = new PropertyDescriptor(beanParamField.getName(), beanParamClazz);
+ Method writeMethod = propertyDescriptor.getWriteMethod();
+ if (null != writeMethod) {
+ swaggerParam = generateSwaggerParam(operationGenerator, writeMethod.getAnnotations(),
+ beanParamField.getGenericType());
}
+ return swaggerParam;
}
/**
- * 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}.
+ * Generate a swagger parameter according to {@code annotations} and {@code genericType}.
*
* @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
+ * @return a swagger param, or null if there is no JAX-RS annotation in {@code annotations}
*/
- private void processBeanParamMember(OperationGenerator operationGenerator, Annotation[] annotations,
+ private Parameter generateSwaggerParam(
+ OperationGenerator operationGenerator,
+ Annotation[] annotations,
Type genericType) {
String defaultValue = null;
for (Annotation fieldAnnotation : annotations) {
@@ -116,24 +162,26 @@ public class BeanParamAnnotationProcessor implements ParameterAnnotationProcesso
continue;
}
- setUpParameter(operationGenerator, fieldAnnotation, genericType, defaultValue);
+ return setUpParameter(operationGenerator, fieldAnnotation, genericType, defaultValue);
}
+ return null;
}
/**
- * Generate swagger parameter, set default value, and add it into {@code operationGenerator}.
+ * Generate swagger parameter, set default value, and return it.
*
* @param operationGenerator operationGenerator
* @param fieldAnnotation JAX-RS param annotation
* @param genericParamType type of the parameter
* @param defaultValue default value, can be null
+ * @return the generated swagger Parameter
*/
- private void setUpParameter(
+ private Parameter setUpParameter(
OperationGenerator operationGenerator,
Annotation fieldAnnotation,
Type genericParamType,
String defaultValue) {
- AbstractSerializableParameter<?> parameter = generateParameter(
+ AbstractSerializableParameter<?> parameter = createParameter(
operationGenerator.getContext(),
fieldAnnotation,
genericParamType);
@@ -141,7 +189,7 @@ public class BeanParamAnnotationProcessor implements ParameterAnnotationProcesso
if (null != defaultValue) {
parameter.setDefaultValue(defaultValue);
}
- operationGenerator.addProviderParameter(parameter);
+ return parameter;
}
/**
@@ -152,7 +200,7 @@ public class BeanParamAnnotationProcessor implements ParameterAnnotationProcesso
* @param genericParamType default value, can be null
* @return the generated swagger parameter
*/
- private AbstractSerializableParameter<?> generateParameter(
+ private AbstractSerializableParameter<?> createParameter(
SwaggerGeneratorContext swaggerGeneratorContext,
Annotation fieldAnnotation,
Type genericParamType) {
@@ -166,4 +214,16 @@ public class BeanParamAnnotationProcessor implements ParameterAnnotationProcesso
ParamUtils.setParameterType(genericParamType, parameter);
return parameter;
}
+
+ /**
+ * Those fields tagged by @JsonIgnore should be ignored.
+ */
+ private boolean fieldShouldIgnore(Field beanParamField) {
+ for (Annotation annotation : beanParamField.getAnnotations()) {
+ if (annotation instanceof JsonIgnore) {
+ return true;
+ }
+ }
+ return false;
+ }
}
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
index f81e2dd..bc22169 100644
--- 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
@@ -18,10 +18,12 @@
package org.apache.servicecomb.swagger.generator.jaxrs.processor.annotation;
import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.fail;
import java.lang.reflect.Method;
import java.util.List;
+import javax.servlet.http.Part;
import javax.ws.rs.BeanParam;
import javax.ws.rs.CookieParam;
import javax.ws.rs.DefaultValue;
@@ -35,6 +37,8 @@ import org.apache.servicecomb.swagger.generator.core.SwaggerGenerator;
import org.apache.servicecomb.swagger.generator.jaxrs.JaxrsSwaggerGeneratorContext;
import org.junit.Test;
+import com.fasterxml.jackson.annotation.JsonIgnore;
+
import io.swagger.models.parameters.AbstractSerializableParameter;
import io.swagger.models.parameters.Parameter;
@@ -49,7 +53,8 @@ public class BeanParamAnnotationProcessorTest {
final List<Parameter> providerParameters = operationGenerator.getProviderParameters();
assertEquals(5, providerParameters.size());
- AbstractSerializableParameter<?> parameter = (AbstractSerializableParameter<?>) providerParameters.get(0);
+ AbstractSerializableParameter<?> parameter;
+ parameter = (AbstractSerializableParameter<?>) providerParameters.get(0);
assertEquals("path", parameter.getIn());
assertEquals("path0", parameter.getName());
assertEquals("pa", parameter.getDefault());
@@ -60,20 +65,45 @@ public class BeanParamAnnotationProcessorTest {
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);
+ parameter = (AbstractSerializableParameter<?>) providerParameters.get(3);
assertEquals("cookie", parameter.getIn());
assertEquals("cookie4", parameter.getName());
assertEquals("integer", parameter.getType());
assertEquals("int64", parameter.getFormat());
+ parameter = (AbstractSerializableParameter<?>) providerParameters.get(4);
+ assertEquals("header", parameter.getIn());
+ assertEquals("header2", parameter.getName());
+ assertEquals("string", parameter.getType());
+ }
+
+ @Test
+ public void processOnParamWithPart() throws NoSuchMethodException {
+ BeanParamAnnotationProcessor processor = new BeanParamAnnotationProcessor();
+ final OperationGenerator operationGenerator = mockOperationGenerator("testBeanParamWithPart",
+ BeanParamWithPart.class);
+
+ processor.process(null, operationGenerator, 0);
+
+ final List<Parameter> providerParameters = operationGenerator.getProviderParameters();
+ assertEquals(3, providerParameters.size());
+ AbstractSerializableParameter<?> parameter;
+ parameter = (AbstractSerializableParameter<?>) providerParameters.get(0);
+ assertEquals("queryStr", parameter.getName());
+ assertEquals("query", parameter.getIn());
+ assertEquals("boolean", parameter.getType());
+ parameter = (AbstractSerializableParameter<?>) providerParameters.get(1);
+ assertEquals("up0", parameter.getName());
+ assertEquals("formData", parameter.getIn());
+ assertEquals("file", parameter.getType());
+ parameter = (AbstractSerializableParameter<?>) providerParameters.get(2);
+ assertEquals("up1", parameter.getName());
+ assertEquals("formData", parameter.getIn());
+ assertEquals("file", parameter.getType());
}
@Test
@@ -84,6 +114,7 @@ public class BeanParamAnnotationProcessorTest {
try {
processor.process(null, operationGenerator, 0);
+ fail("A error is expected!");
} catch (Error e) {
assertEquals("Processing param failed, method=org.apache.servicecomb.swagger.generator.jaxrs.processor"
+ ".annotation.BeanParamAnnotationProcessorTest$TestProvider:testBeanParamComplexField, beanParamIdx=0",
@@ -102,6 +133,7 @@ public class BeanParamAnnotationProcessorTest {
try {
processor.process(null, operationGenerator, 0);
+ fail("A error is expected!");
} catch (Error e) {
assertEquals("Processing param failed, method=org.apache.servicecomb.swagger.generator.jaxrs.processor"
+ ".annotation.BeanParamAnnotationProcessorTest$TestProvider:testBeanParamComplexSetter, beanParamIdx=0",
@@ -112,6 +144,38 @@ public class BeanParamAnnotationProcessorTest {
}
}
+ @Test
+ public void processOnParamFieldUntagged() throws NoSuchMethodException {
+ BeanParamAnnotationProcessor processor = new BeanParamAnnotationProcessor();
+ OperationGenerator operationGenerator = mockOperationGenerator("testBeanParamWithUntaggedField",
+ BeanParamWithUntaggedField.class);
+
+ try {
+ processor.process(null, operationGenerator, 0);
+ fail("A error is expected!");
+ } catch (Error e) {
+ assertEquals("There is a field[name] cannot be mapped to swagger param. "
+ + "Maybe you should tag @JsonIgnore on it.",
+ e.getCause().getMessage());
+ }
+ }
+
+ @Test
+ public void processOnParamFieldWithJsonIgnore() throws NoSuchMethodException {
+ BeanParamAnnotationProcessor processor = new BeanParamAnnotationProcessor();
+ OperationGenerator operationGenerator = mockOperationGenerator("testBeanParamWithJsonIgnore",
+ BeanParamWithJsonIgnoredTagged.class);
+
+ processor.process(null, operationGenerator, 0);
+
+ final List<Parameter> providerParameters = operationGenerator.getProviderParameters();
+ assertEquals(1, providerParameters.size());
+ AbstractSerializableParameter<?> parameter = (AbstractSerializableParameter<?>) providerParameters.get(0);
+ assertEquals("name", parameter.getName());
+ assertEquals("query", parameter.getIn());
+ assertEquals("string", parameter.getType());
+ }
+
private OperationGenerator mockOperationGenerator(String methodName, Class<?>... paramTypes)
throws NoSuchMethodException {
final Method providerMethod = TestProvider.class.getDeclaredMethod(methodName, paramTypes);
@@ -125,6 +189,10 @@ public class BeanParamAnnotationProcessorTest {
return aggregatedParam.toString();
}
+ public String testBeanParamWithPart(@BeanParam BeanParamWithPart aggregatedParam) {
+ return aggregatedParam.toString();
+ }
+
public String testBeanParamComplexField(@BeanParam BeanParamComplexField param) {
return param.toString();
}
@@ -132,6 +200,14 @@ public class BeanParamAnnotationProcessorTest {
public String testBeanParamComplexSetter(@BeanParam BeanParamComplexSetter param) {
return param.toString();
}
+
+ public String testBeanParamWithUntaggedField(@BeanParam BeanParamWithUntaggedField param) {
+ return param.toString();
+ }
+
+ public String testBeanParamWithJsonIgnore(@BeanParam BeanParamWithJsonIgnoredTagged param) {
+ return param.toString();
+ }
}
static class AggregatedParam {
@@ -153,18 +229,16 @@ public class BeanParamAnnotationProcessorTest {
return strVal;
}
- public AggregatedParam setStrVal(String strVal) {
+ public void setStrVal(String strVal) {
this.strVal = strVal;
- return this;
}
public int getIntVal() {
return intVal;
}
- public AggregatedParam setIntVal(int intVal) {
+ public void setIntVal(int intVal) {
this.intVal = intVal;
- return this;
}
public long getLongVal() {
@@ -173,9 +247,8 @@ public class BeanParamAnnotationProcessorTest {
@DefaultValue("12")
@FormParam("form3")
- public AggregatedParam setLongVal(long longVal) {
+ public void setLongVal(long longVal) {
this.longVal = longVal;
- return this;
}
public long getCookieVal() {
@@ -183,18 +256,51 @@ public class BeanParamAnnotationProcessorTest {
}
@CookieParam("cookie4")
- public AggregatedParam setCookieVal(long cookieVal) {
+ public void setCookieVal(long cookieVal) {
this.cookieVal = cookieVal;
- return this;
}
public String getHeaderVal() {
return headerVal;
}
- public AggregatedParam setHeaderVal(String headerVal) {
+ public void setHeaderVal(String headerVal) {
this.headerVal = headerVal;
- return this;
+ }
+ }
+
+ static class BeanParamWithPart {
+ @QueryParam("queryStr")
+ private boolean queryStr;
+
+ @FormParam("up0")
+ private Part up0;
+
+ private Part up1;
+
+ public boolean isQueryStr() {
+ return queryStr;
+ }
+
+ public void setQueryStr(boolean queryStr) {
+ this.queryStr = queryStr;
+ }
+
+ public Part getUp0() {
+ return up0;
+ }
+
+ public void setUp0(Part up0) {
+ this.up0 = up0;
+ }
+
+ public Part getUp1() {
+ return up1;
+ }
+
+ @FormParam("up1")
+ public void setUp1(Part up1) {
+ this.up1 = up1;
}
}
@@ -206,10 +312,8 @@ public class BeanParamAnnotationProcessorTest {
return complex;
}
- public BeanParamComplexField setComplex(
- AggregatedParam complex) {
+ public void setComplex(AggregatedParam complex) {
this.complex = complex;
- return this;
}
}
@@ -221,10 +325,45 @@ public class BeanParamAnnotationProcessorTest {
}
@HeaderParam("h")
- public BeanParamComplexSetter setComplex(
- AggregatedParam complex) {
+ public void setComplex(AggregatedParam complex) {
this.complex = complex;
- return this;
}
}
-}
\ No newline at end of file
+
+ static class BeanParamWithUntaggedField {
+ private String name;
+
+ public String getName() {
+ return name;
+ }
+
+ public void setName(String name) {
+ this.name = name;
+ }
+ }
+
+ static class BeanParamWithJsonIgnoredTagged {
+ @QueryParam("name")
+ private String name;
+
+ @JsonIgnore
+ private AggregatedParam ignored;
+
+ public String getName() {
+ return name;
+ }
+
+ public void setName(String name) {
+ this.name = name;
+ }
+
+ public AggregatedParam getIgnored() {
+ return ignored;
+ }
+
+ public void setIgnored(
+ AggregatedParam ignored) {
+ this.ignored = ignored;
+ }
+ }
+}