You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@servicecomb.apache.org by GitBox <gi...@apache.org> on 2018/08/29 02:56:25 UTC

[GitHub] liubao68 closed pull request #882: [SCB-206] Support setting produces and consumes by @Api

liubao68 closed pull request #882: [SCB-206] Support setting produces and consumes by @Api
URL: https://github.com/apache/incubator-servicecomb-java-chassis/pull/882
 
 
   

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-rest/src/main/java/org/apache/servicecomb/common/rest/codec/RestClientRequest.java b/common/common-rest/src/main/java/org/apache/servicecomb/common/rest/codec/RestClientRequest.java
index f70c7c7fe..a300273e8 100644
--- a/common/common-rest/src/main/java/org/apache/servicecomb/common/rest/codec/RestClientRequest.java
+++ b/common/common-rest/src/main/java/org/apache/servicecomb/common/rest/codec/RestClientRequest.java
@@ -19,6 +19,7 @@
 
 import javax.servlet.http.Part;
 
+import io.vertx.core.MultiMap;
 import io.vertx.core.buffer.Buffer;
 
 /**
@@ -34,6 +35,8 @@
 
   void putHeader(String name, String value);
 
+  MultiMap getHeaders();
+
   void addForm(String name, Object value);
 
   Buffer getBodyBuffer() throws Exception;
diff --git a/common/common-rest/src/main/java/org/apache/servicecomb/common/rest/codec/param/BodyProcessorCreator.java b/common/common-rest/src/main/java/org/apache/servicecomb/common/rest/codec/param/BodyProcessorCreator.java
index ff5f5872d..e3016a0f4 100644
--- a/common/common-rest/src/main/java/org/apache/servicecomb/common/rest/codec/param/BodyProcessorCreator.java
+++ b/common/common-rest/src/main/java/org/apache/servicecomb/common/rest/codec/param/BodyProcessorCreator.java
@@ -17,6 +17,7 @@
 
 package org.apache.servicecomb.common.rest.codec.param;
 
+import java.io.IOException;
 import java.io.InputStream;
 import java.lang.reflect.Type;
 import java.util.Locale;
@@ -33,6 +34,7 @@
 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.exc.MismatchedInputException;
@@ -40,6 +42,7 @@
 
 import io.swagger.models.parameters.Parameter;
 import io.vertx.core.buffer.Buffer;
+import io.vertx.core.buffer.impl.BufferImpl;
 
 public class BodyProcessorCreator implements ParamValueProcessorCreator {
   private static final Logger LOGGER = LoggerFactory.getLogger(BodyProcessorCreator.class);
@@ -99,12 +102,44 @@ public Object getValue(HttpServletRequest request) throws Exception {
 
     @Override
     public void setValue(RestClientRequest clientRequest, Object arg) throws Exception {
+      ensureContentType(clientRequest);
+      if (arg != null) {
+        Buffer buffer = createBodyBuffer(
+            clientRequest.getHeaders().get(HttpHeaders.CONTENT_TYPE),
+            arg);
+        clientRequest.write(buffer);
+      }
+    }
+
+    /**
+     * Deserialize body object into body buffer, according to the Content-Type.
+     *
+     * @param contentType the Content-Type of request
+     * @param arg body param object
+     * @return the deserialized body buffer
+     * @throws IOException
+     */
+    private Buffer createBodyBuffer(String contentType, Object arg) throws IOException {
+      if (MediaType.TEXT_PLAIN.equals(contentType)) {
+        if (!String.class.isInstance(arg)) {
+          throw new IllegalArgumentException("Content-Type is text/plain while arg type is not String");
+        }
+        return new BufferImpl().appendBytes(((String) arg).getBytes());
+      }
+
       try (BufferOutputStream output = new BufferOutputStream()) {
-        clientRequest.putHeader(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON);
         RestObjectMapperFactory.getConsumerWriterMapper().writeValue(output, arg);
-        if (arg != null) {
-          clientRequest.write(output.getBuffer());
-        }
+        return output.getBuffer();
+      }
+    }
+
+    /**
+     * If the Content-Type has not been set yet, set application/json as default value.
+     */
+    private void ensureContentType(RestClientRequest clientRequest) {
+      if (null == clientRequest.getHeaders()
+          || StringUtils.isEmpty(clientRequest.getHeaders().get(HttpHeaders.CONTENT_TYPE))) {
+        clientRequest.putHeader(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON);
       }
     }
 
diff --git a/common/common-rest/src/main/java/org/apache/servicecomb/common/rest/codec/param/RestClientRequestImpl.java b/common/common-rest/src/main/java/org/apache/servicecomb/common/rest/codec/param/RestClientRequestImpl.java
index 9116d3fff..8b124ec8a 100644
--- a/common/common-rest/src/main/java/org/apache/servicecomb/common/rest/codec/param/RestClientRequestImpl.java
+++ b/common/common-rest/src/main/java/org/apache/servicecomb/common/rest/codec/param/RestClientRequestImpl.java
@@ -40,6 +40,7 @@
 import org.slf4j.LoggerFactory;
 
 import io.vertx.core.Context;
+import io.vertx.core.MultiMap;
 import io.vertx.core.buffer.Buffer;
 import io.vertx.core.http.HttpClientRequest;
 import io.vertx.core.http.HttpHeaders;
@@ -272,4 +273,9 @@ public void addForm(String name, Object value) {
   public void putHeader(String name, String value) {
     request.putHeader(name, value);
   }
+
+  @Override
+  public MultiMap getHeaders() {
+    return request.headers();
+  }
 }
diff --git a/common/common-rest/src/test/java/org/apache/servicecomb/common/rest/codec/param/TestBodyProcessor.java b/common/common-rest/src/test/java/org/apache/servicecomb/common/rest/codec/param/TestBodyProcessor.java
index 0b9fb1f46..a7afaa580 100644
--- a/common/common-rest/src/test/java/org/apache/servicecomb/common/rest/codec/param/TestBodyProcessor.java
+++ b/common/common-rest/src/test/java/org/apache/servicecomb/common/rest/codec/param/TestBodyProcessor.java
@@ -17,8 +17,11 @@
 
 package org.apache.servicecomb.common.rest.codec.param;
 
+import static org.junit.Assert.fail;
+
 import java.io.IOException;
 import java.nio.charset.StandardCharsets;
+import java.util.Date;
 import java.util.HashMap;
 import java.util.Map;
 
@@ -32,13 +35,16 @@
 import org.apache.servicecomb.common.rest.codec.param.BodyProcessorCreator.RawJsonBodyProcessor;
 import org.apache.servicecomb.foundation.vertx.stream.BufferInputStream;
 import org.junit.Assert;
+import org.junit.Before;
 import org.junit.Test;
 
 import com.fasterxml.jackson.databind.type.TypeFactory;
 
 import io.netty.buffer.ByteBuf;
 import io.netty.buffer.Unpooled;
+import io.vertx.core.MultiMap;
 import io.vertx.core.buffer.Buffer;
+import io.vertx.core.http.impl.headers.VertxHttpHeaders;
 import mockit.Expectations;
 import mockit.Mock;
 import mockit.MockUp;
@@ -48,7 +54,7 @@
   @Mocked
   HttpServletRequest request;
 
-  Map<String, String> headers = new HashMap<>();
+  MultiMap headers;
 
   RestClientRequest clientRequest;
 
@@ -72,13 +78,18 @@ private void createClientRequest() {
     clientRequest = new MockUp<RestClientRequest>() {
       @Mock
       void putHeader(String name, String value) {
-        headers.put(name, value);
+        headers.add(name, value);
       }
 
       @Mock
       void write(Buffer bodyBuffer) {
         outputBodyBuffer = bodyBuffer;
       }
+
+      @Mock
+      MultiMap getHeaders() {
+        return headers;
+      }
     }.getMockInstance();
   }
 
@@ -96,6 +107,11 @@ private void setupGetValue(Class<?> type) throws IOException {
     initInputStream();
   }
 
+  @Before
+  public void before() {
+    headers = new VertxHttpHeaders();
+  }
+
   @Test
   public void testGetValueHaveAttr() throws Exception {
     int body = 10;
@@ -122,7 +138,7 @@ public void testGetValueNoAttrNoStream() throws Exception {
     };
 
     Object result = processor.getValue(request);
-    Assert.assertEquals(null, result);
+    Assert.assertNull(result);
   }
 
   @Test
@@ -173,6 +189,32 @@ public void testSetValue() throws Exception {
     Assert.assertEquals("\"value\"", outputBodyBuffer.toString());
   }
 
+  @Test
+  public void testSetValueTextPlain() throws Exception {
+    createClientRequest();
+    createProcessor(String.class);
+    headers.add(HttpHeaders.CONTENT_TYPE, MediaType.TEXT_PLAIN);
+
+    processor.setValue(clientRequest, "value");
+    Assert.assertEquals(MediaType.TEXT_PLAIN, headers.get(HttpHeaders.CONTENT_TYPE));
+    Assert.assertEquals("value", outputBodyBuffer.toString());
+  }
+
+  @Test
+  public void testSetValueTextPlainTypeMismatch() {
+    createClientRequest();
+    createProcessor(String.class);
+    headers.add(HttpHeaders.CONTENT_TYPE, MediaType.TEXT_PLAIN);
+
+    try {
+      processor.setValue(clientRequest, new Date());
+      fail("an exception is expected!");
+    } catch (Exception e) {
+      Assert.assertEquals(IllegalArgumentException.class, e.getClass());
+      Assert.assertEquals("Content-Type is text/plain while arg type is not String", e.getMessage());
+    }
+  }
+
   @Test
   public void testGetParameterPath() {
     createProcessor(String.class);
@@ -220,7 +262,7 @@ public void testGetValueRawJsonNoAttrNoStream() throws Exception {
     };
 
     Object result = processor.getValue(request);
-    Assert.assertEquals(null, result);
+    Assert.assertNull(result);
   }
 
   @Test
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 af27ccdf3..e5013f9cf 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
@@ -30,7 +30,6 @@
 
 import org.apache.commons.io.FileUtils;
 import org.apache.servicecomb.bizkeeper.BizkeeperExceptionUtils;
-import org.apache.servicecomb.core.exception.CseException;
 import org.apache.servicecomb.demo.CodeFirstRestTemplate;
 import org.apache.servicecomb.demo.TestMgr;
 import org.apache.servicecomb.foundation.common.part.FilePart;
@@ -75,6 +74,8 @@
 
   private TestRestTemplate testRestTemplate = new TestRestTemplate();
 
+  private TestContentType testContentType = new TestContentType();
+
   @Override
   protected void testOnlyRest(RestTemplate template, String cseUrlPrefix) {
     testDownload.runRest();
@@ -89,6 +90,7 @@ protected void testOnlyRest(RestTemplate template, String cseUrlPrefix) {
     testObject.runRest();
     testGeneric.runRest();
     testRestTemplate.runRest();
+    testContentType.runAllTest();
 
     super.testOnlyRest(template, cseUrlPrefix);
   }
@@ -184,7 +186,7 @@ private void testFallback(RestTemplate template, String cseUrlPrefix) {
       result = template.getForObject(cseUrlPrefix + "/fallback/throwexception/throwexception", String.class);
       TestMgr.check(false, true);
     } catch (Exception e) {
-      TestMgr.check(((CseException) e.getCause()).getMessage(),
+      TestMgr.check(e.getCause().getMessage(),
           BizkeeperExceptionUtils.createBizkeeperException(BizkeeperExceptionUtils.SERVICECOMB_BIZKEEPER_FALLBACK,
               null,
               "springmvc.codeFirst.fallbackThrowException").getMessage());
diff --git a/demo/demo-springmvc/springmvc-client/src/main/java/org/apache/servicecomb/demo/springmvc/client/TestContentType.java b/demo/demo-springmvc/springmvc-client/src/main/java/org/apache/servicecomb/demo/springmvc/client/TestContentType.java
new file mode 100644
index 000000000..65fc1461d
--- /dev/null
+++ b/demo/demo-springmvc/springmvc-client/src/main/java/org/apache/servicecomb/demo/springmvc/client/TestContentType.java
@@ -0,0 +1,90 @@
+/*
+ * 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 javax.ws.rs.core.MediaType;
+
+import org.apache.servicecomb.demo.TestMgr;
+import org.apache.servicecomb.provider.springmvc.reference.CseHttpEntity;
+import org.apache.servicecomb.provider.springmvc.reference.RestTemplateBuilder;
+import org.springframework.http.HttpHeaders;
+import org.springframework.http.HttpMethod;
+import org.springframework.http.ResponseEntity;
+import org.springframework.web.client.RestTemplate;
+
+public class TestContentType {
+
+  private RestTemplate restTemplate = RestTemplateBuilder.create();
+
+  public void runAllTest() {
+    testGlobalSetting();
+    testApiOperation();
+    testRequestMapping();
+    testResponseTypeOverwrite();
+  }
+
+  private void testGlobalSetting() {
+    HttpHeaders requestHeaders = new HttpHeaders();
+    requestHeaders.add(HttpHeaders.CONTENT_TYPE, MediaType.TEXT_PLAIN);
+    CseHttpEntity<String> requestEntity = new CseHttpEntity<>("from testGlobalSetting", requestHeaders);
+    ResponseEntity<String> responseEntity = restTemplate
+        .exchange("cse://springmvc/contentTypeSpringmvc/testGlobalSetting", HttpMethod.POST,
+            requestEntity, String.class);
+    TestMgr.check(
+        "testGlobalSetting: name=[from testGlobalSetting], request content-type=[" + MediaType.TEXT_PLAIN + "]",
+        responseEntity.getBody());
+    TestMgr.check(MediaType.TEXT_PLAIN, extractContentType(responseEntity.getHeaders().getContentType()));
+  }
+
+  private void testApiOperation() {
+    HttpHeaders requestHeaders = new HttpHeaders();
+    requestHeaders.add(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON);
+    CseHttpEntity<String> requestEntity = new CseHttpEntity<>("from testApiOperation", requestHeaders);
+    ResponseEntity<String> responseEntity = restTemplate
+        .exchange("cse://springmvc/contentTypeSpringmvc/testApiOperation", HttpMethod.POST,
+            requestEntity, String.class);
+    TestMgr.check(
+        "testApiOperation: name=[from testApiOperation], request content-type=[" + MediaType.APPLICATION_JSON + "]",
+        responseEntity.getBody());
+    TestMgr.check(MediaType.APPLICATION_JSON, extractContentType(responseEntity.getHeaders().getContentType()));
+  }
+
+  private void testRequestMapping() {
+    HttpHeaders requestHeaders = new HttpHeaders();
+    requestHeaders.add(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON);
+    CseHttpEntity<String> requestEntity = new CseHttpEntity<>("from testRequestMapping", requestHeaders);
+    ResponseEntity<String> responseEntity = restTemplate
+        .exchange("cse://springmvc/contentTypeSpringmvc/testRequestMapping", HttpMethod.POST,
+            requestEntity, String.class);
+    TestMgr.check(
+        "testRequestMapping: name=[from testRequestMapping], request content-type=[" + MediaType.APPLICATION_JSON + "]",
+        responseEntity.getBody());
+    TestMgr.check(MediaType.APPLICATION_JSON, extractContentType(responseEntity.getHeaders().getContentType()));
+  }
+
+  private void testResponseTypeOverwrite() {
+    ResponseEntity<String> responseEntity = restTemplate
+        .getForEntity("cse://springmvc/contentTypeSpringmvcOverwrite/testResponseTypeOverwrite", String.class);
+    TestMgr.check("testResponseTypeOverwrite: OK", responseEntity.getBody());
+    TestMgr.check(MediaType.TEXT_PLAIN, extractContentType(responseEntity.getHeaders().getContentType()));
+  }
+
+  private String extractContentType(org.springframework.http.MediaType mediaType) {
+    return mediaType.getType() + "/" + mediaType.getSubtype();
+  }
+}
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 6ec2bc59a..ba8d97ab7 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
@@ -22,7 +22,6 @@
 import java.util.Date;
 import java.util.List;
 import java.util.Map;
-import java.util.concurrent.atomic.AtomicBoolean;
 import java.util.concurrent.atomic.AtomicInteger;
 
 import javax.servlet.http.HttpServletRequest;
diff --git a/demo/demo-springmvc/springmvc-server/src/main/java/org/apache/servicecomb/demo/springmvc/server/ContentTypeSpringmvc.java b/demo/demo-springmvc/springmvc-server/src/main/java/org/apache/servicecomb/demo/springmvc/server/ContentTypeSpringmvc.java
new file mode 100644
index 000000000..94281caea
--- /dev/null
+++ b/demo/demo-springmvc/springmvc-server/src/main/java/org/apache/servicecomb/demo/springmvc/server/ContentTypeSpringmvc.java
@@ -0,0 +1,51 @@
+/*
+ * 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.server;
+
+import javax.servlet.http.HttpServletRequest;
+import javax.ws.rs.core.MediaType;
+
+import org.apache.servicecomb.provider.rest.common.RestSchema;
+import org.springframework.web.bind.annotation.RequestBody;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RequestMethod;
+
+import io.swagger.annotations.Api;
+import io.swagger.annotations.ApiOperation;
+
+@RestSchema(schemaId = "contentTypeSpringmvc")
+@RequestMapping("/contentTypeSpringmvc")
+@Api(consumes = MediaType.TEXT_PLAIN, produces = MediaType.TEXT_PLAIN)
+public class ContentTypeSpringmvc {
+  @RequestMapping(path = "/testGlobalSetting", method = RequestMethod.POST)
+  public String testGlobalSetting(@RequestBody String name, HttpServletRequest request) {
+    return String.format("testGlobalSetting: name=[%s], request content-type=[%s]", name, request.getContentType());
+  }
+
+  @RequestMapping(path = "/testApiOperation", method = RequestMethod.POST)
+  @ApiOperation(value = "testApiOperation desc", consumes = MediaType.APPLICATION_JSON, produces = MediaType.APPLICATION_JSON)
+  public String testApiOperation(@RequestBody String name, HttpServletRequest request) {
+    return String.format("testApiOperation: name=[%s], request content-type=[%s]", name, request.getContentType());
+  }
+
+  @RequestMapping(path = "/testRequestMapping", method = RequestMethod.POST,
+      consumes = MediaType.APPLICATION_JSON, produces = MediaType.APPLICATION_JSON)
+  public String testRequestMapping(@RequestBody String name, HttpServletRequest request) {
+    return String.format("testRequestMapping: name=[%s], request content-type=[%s]", name, request.getContentType());
+  }
+}
diff --git a/demo/demo-springmvc/springmvc-server/src/main/java/org/apache/servicecomb/demo/springmvc/server/ContentTypeSpringmvcOverwrite.java b/demo/demo-springmvc/springmvc-server/src/main/java/org/apache/servicecomb/demo/springmvc/server/ContentTypeSpringmvcOverwrite.java
new file mode 100644
index 000000000..74b2d1327
--- /dev/null
+++ b/demo/demo-springmvc/springmvc-server/src/main/java/org/apache/servicecomb/demo/springmvc/server/ContentTypeSpringmvcOverwrite.java
@@ -0,0 +1,36 @@
+/*
+ * 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.server;
+
+import javax.ws.rs.core.MediaType;
+
+import org.apache.servicecomb.provider.rest.common.RestSchema;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RequestMethod;
+
+import io.swagger.annotations.Api;
+
+@RestSchema(schemaId = "contentTypeSpringmvcOverwrite")
+@RequestMapping(value = "/contentTypeSpringmvcOverwrite", produces = MediaType.TEXT_PLAIN)
+@Api(produces = MediaType.APPLICATION_JSON)
+public class ContentTypeSpringmvcOverwrite {
+  @RequestMapping(value = "/testResponseTypeOverwrite", method = RequestMethod.GET)
+  public String testResponseTypeOverwrite() {
+    return "testResponseTypeOverwrite: OK";
+  }
+}
diff --git a/demo/demo-springmvc/springmvc-server/src/main/java/org/apache/servicecomb/demo/springmvc/server/ProducerTestsAfterBootup.java b/demo/demo-springmvc/springmvc-server/src/main/java/org/apache/servicecomb/demo/springmvc/server/ProducerTestsAfterBootup.java
index 7420815dd..6023d1dd9 100644
--- a/demo/demo-springmvc/springmvc-server/src/main/java/org/apache/servicecomb/demo/springmvc/server/ProducerTestsAfterBootup.java
+++ b/demo/demo-springmvc/springmvc-server/src/main/java/org/apache/servicecomb/demo/springmvc/server/ProducerTestsAfterBootup.java
@@ -56,11 +56,10 @@ public void testSchemaNotChange() {
     TestMgr.check("2986daa46b229ec125443122dd7b51ee9a64879f1750d0996f948ce0718685c7",
         RegistryUtils.calcSchemaSummary(codeFirst));
     TestMgr.check(codeFirst.length(), 889);
-
   }
 
-  public void testRegisterPath() {
-    TestMgr.check(RegistryUtils.getMicroservice().getPaths().size(), 10);
+  public void testRegisteredBasePath() {
+    TestMgr.check(12, RegistryUtils.getMicroservice().getPaths().size());
   }
 
   private String getSwaggerContent(Swagger swagger) {
@@ -75,7 +74,7 @@ private String getSwaggerContent(Swagger swagger) {
   public void onBootEvent(BootEvent event) {
     if (event.getEventType() == BootListener.EventType.AFTER_REGISTRY) {
       testSchemaNotChange();
-      testRegisterPath();
+      testRegisteredBasePath();
       if (!TestMgr.isSuccess()) {
         TestMgr.summary();
         throw new IllegalStateException("some tests are failed. ");
diff --git a/swagger/swagger-generator/generator-core/src/main/java/org/apache/servicecomb/swagger/generator/core/processor/annotation/ApiProcessor.java b/swagger/swagger-generator/generator-core/src/main/java/org/apache/servicecomb/swagger/generator/core/processor/annotation/ApiProcessor.java
index 57b4dbff3..f882e340a 100644
--- a/swagger/swagger-generator/generator-core/src/main/java/org/apache/servicecomb/swagger/generator/core/processor/annotation/ApiProcessor.java
+++ b/swagger/swagger-generator/generator-core/src/main/java/org/apache/servicecomb/swagger/generator/core/processor/annotation/ApiProcessor.java
@@ -17,11 +17,16 @@
 
 package org.apache.servicecomb.swagger.generator.core.processor.annotation;
 
+import java.util.List;
+import java.util.stream.Collectors;
+import java.util.stream.Stream;
+
 import org.apache.servicecomb.swagger.generator.core.ClassAnnotationProcessor;
 import org.apache.servicecomb.swagger.generator.core.SwaggerGenerator;
 import org.springframework.util.StringUtils;
 
 import io.swagger.annotations.Api;
+import io.swagger.models.Swagger;
 
 public class ApiProcessor implements ClassAnnotationProcessor {
   @Override
@@ -29,6 +34,52 @@ public void process(Object annotation, SwaggerGenerator swaggerGenerator) {
     Api api = (Api) annotation;
 
     setTags(api, swaggerGenerator);
+    // except for @Api, @RequestMapping can also set consumes and produces
+    // @RequestMapping takes HIGHER priority than @Api for legacy reason
+    processConsumes(api, swaggerGenerator.getSwagger());
+    processProduces(api, swaggerGenerator.getSwagger());
+  }
+
+  private void processProduces(Api api, Swagger swagger) {
+    List<String> validProducesList = getValidStringList(api.produces());
+    if (isBlank(swagger.getProduces()) && !validProducesList.isEmpty()) {
+      swagger.setProduces(validProducesList);
+    }
+  }
+
+  private void processConsumes(Api api, Swagger swagger) {
+    List<String> validConsumesList = getValidStringList(api.consumes());
+    if (isBlank(swagger.getConsumes()) && !validConsumesList.isEmpty()) {
+      swagger.setConsumes(validConsumesList);
+    }
+  }
+
+  /**
+   * Split {@link Api#consumes} and {@link Api#produces}, and filter empty items.
+   */
+  private List<String> getValidStringList(String rawString) {
+    return Stream.of(rawString.split(","))
+        .filter(s -> !StringUtils.isEmpty(s))
+        .map(String::trim)
+        .collect(Collectors.toList());
+  }
+
+  /**
+   * @return true if {@code stringList} is empty or each element of {@code stringList} is empty;
+   * otherwise false.
+   */
+  private boolean isBlank(List<String> stringList) {
+    boolean isEmpty = null == stringList || stringList.isEmpty();
+    if (isEmpty) {
+      return true;
+    }
+
+    for (String s : stringList) {
+      if (StringUtils.isEmpty(s)) {
+        return true;
+      }
+    }
+    return false;
   }
 
   private void setTags(Api api, SwaggerGenerator swaggerGenerator) {
diff --git a/swagger/swagger-generator/generator-core/src/test/java/org/apache/servicecomb/swagger/generator/core/processor/annotation/ApiProcessorTest.java b/swagger/swagger-generator/generator-core/src/test/java/org/apache/servicecomb/swagger/generator/core/processor/annotation/ApiProcessorTest.java
index 62f5a3e05..047daac0d 100644
--- a/swagger/swagger-generator/generator-core/src/test/java/org/apache/servicecomb/swagger/generator/core/processor/annotation/ApiProcessorTest.java
+++ b/swagger/swagger-generator/generator-core/src/test/java/org/apache/servicecomb/swagger/generator/core/processor/annotation/ApiProcessorTest.java
@@ -19,12 +19,19 @@
 
 import static org.hamcrest.Matchers.contains;
 import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNull;
 import static org.junit.Assert.assertThat;
 
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.List;
 import java.util.Set;
 
+import javax.ws.rs.core.MediaType;
+
 import org.apache.servicecomb.swagger.generator.core.SwaggerGenerator;
 import org.apache.servicecomb.swagger.generator.core.SwaggerGeneratorContext;
+import org.hamcrest.Matchers;
 import org.junit.Test;
 import org.mockito.Mockito;
 
@@ -35,22 +42,66 @@
 
   @Test
   public void process() {
-    SwaggerGenerator swaggerGenerator = new SwaggerGenerator(Mockito.mock(SwaggerGeneratorContext.class),
-        null);
+    SwaggerGenerator swaggerGenerator = getSwaggerGenerator();
     apiProcessor.process(SwaggerTestTarget.class.getAnnotation(Api.class),
         swaggerGenerator);
 
     assertThat(swaggerGenerator.getDefaultTags(), contains("tag1", "tag2"));
+    assertNull(swaggerGenerator.getSwagger().getConsumes());
+    assertNull(swaggerGenerator.getSwagger().getProduces());
   }
 
   @Test
   public void processOnNoTag() {
-    SwaggerGenerator swaggerGenerator = new SwaggerGenerator(Mockito.mock(SwaggerGeneratorContext.class),
-        null);
+    SwaggerGenerator swaggerGenerator = getSwaggerGenerator();
     apiProcessor.process(SwaggerTestTargetWithNoTag.class.getAnnotation(Api.class), swaggerGenerator);
 
     Set<String> tags = swaggerGenerator.getDefaultTags();
     assertEquals(0, tags.size());
+    assertNull(swaggerGenerator.getSwagger().getConsumes());
+    assertNull(swaggerGenerator.getSwagger().getProduces());
+  }
+
+  @Test
+  public void processOverWriteEmptyConsumesAndProduces() {
+    SwaggerGenerator swaggerGenerator = getSwaggerGenerator();
+    swaggerGenerator.getSwagger().setConsumes(Arrays.asList("", "  "));
+    swaggerGenerator.getSwagger().setProduces(Arrays.asList("", "  "));
+    apiProcessor.process(SwaggerTestTargetWithConsumesAndProduces.class.getAnnotation(Api.class), swaggerGenerator);
+
+    List<String> consumes = swaggerGenerator.getSwagger().getConsumes();
+    assertThat(consumes, Matchers.contains(MediaType.TEXT_PLAIN, MediaType.APPLICATION_JSON));
+    List<String> produces = swaggerGenerator.getSwagger().getProduces();
+    assertThat(produces, Matchers.contains(MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON));
+  }
+
+  @Test
+  public void processNotOverWriteValidConsumesAndProduces() {
+    SwaggerGenerator swaggerGenerator = getSwaggerGenerator();
+    swaggerGenerator.getSwagger().setConsumes(Collections.singletonList(MediaType.MULTIPART_FORM_DATA));
+    swaggerGenerator.getSwagger().setProduces(Collections.singletonList(MediaType.MULTIPART_FORM_DATA));
+    apiProcessor.process(SwaggerTestTargetWithConsumesAndProduces.class.getAnnotation(Api.class), swaggerGenerator);
+
+    List<String> consumes = swaggerGenerator.getSwagger().getConsumes();
+    assertThat(consumes, Matchers.contains(MediaType.MULTIPART_FORM_DATA));
+    List<String> produces = swaggerGenerator.getSwagger().getProduces();
+    assertThat(produces, Matchers.contains(MediaType.MULTIPART_FORM_DATA));
+  }
+
+  @Test
+  public void processWithConsumesAndProduces() {
+    SwaggerGenerator swaggerGenerator = getSwaggerGenerator();
+    apiProcessor.process(SwaggerTestTargetWithConsumesAndProduces.class.getAnnotation(Api.class), swaggerGenerator);
+
+    List<String> consumes = swaggerGenerator.getSwagger().getConsumes();
+    assertThat(consumes, Matchers.contains(MediaType.TEXT_PLAIN, MediaType.APPLICATION_JSON));
+    List<String> produces = swaggerGenerator.getSwagger().getProduces();
+    assertThat(produces, Matchers.contains(MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON));
+  }
+
+  private SwaggerGenerator getSwaggerGenerator() {
+    return new SwaggerGenerator(Mockito.mock(SwaggerGeneratorContext.class),
+        null);
   }
 
   @Api(tags = {"tag1", "tag2", "", "tag1"})
@@ -60,4 +111,9 @@ public void processOnNoTag() {
   @Api
   private class SwaggerTestTargetWithNoTag {
   }
+
+  @Api(consumes = MediaType.TEXT_PLAIN + " , " + MediaType.APPLICATION_JSON,
+      produces = MediaType.APPLICATION_XML + "," + MediaType.APPLICATION_JSON)
+  private class SwaggerTestTargetWithConsumesAndProduces {
+  }
 }
diff --git a/swagger/swagger-generator/generator-springmvc/src/test/java/org/apache/servicecomb/swagger/generator/springmvc/processor/annotation/RequestMappingClassAnnotationProcessorTest.java b/swagger/swagger-generator/generator-springmvc/src/test/java/org/apache/servicecomb/swagger/generator/springmvc/processor/annotation/RequestMappingClassAnnotationProcessorTest.java
new file mode 100644
index 000000000..35d829aac
--- /dev/null
+++ b/swagger/swagger-generator/generator-springmvc/src/test/java/org/apache/servicecomb/swagger/generator/springmvc/processor/annotation/RequestMappingClassAnnotationProcessorTest.java
@@ -0,0 +1,98 @@
+/*
+ * 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.springmvc.processor.annotation;
+
+import javax.ws.rs.core.MediaType;
+
+import org.apache.servicecomb.swagger.generator.core.SwaggerGenerator;
+import org.apache.servicecomb.swagger.generator.core.SwaggerGeneratorContext;
+import org.hamcrest.Matchers;
+import org.junit.Assert;
+import org.junit.Test;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RequestMethod;
+
+import io.swagger.annotations.Api;
+import mockit.Mock;
+import mockit.MockUp;
+
+public class RequestMappingClassAnnotationProcessorTest {
+
+  private RequestMappingClassAnnotationProcessor processor = new RequestMappingClassAnnotationProcessor();
+
+  @Test
+  public void process() {
+    SwaggerGenerator swaggerGenerator = getSwaggerGenerator();
+    processor.process(SwaggerTestTarget.class.getAnnotation(RequestMapping.class), swaggerGenerator);
+
+    Assert.assertEquals("/test", swaggerGenerator.getSwagger().getBasePath());
+    Assert.assertEquals("post", swaggerGenerator.getHttpMethod());
+    Assert.assertThat(swaggerGenerator.getSwagger().getConsumes(),
+        Matchers.contains(MediaType.TEXT_PLAIN, MediaType.APPLICATION_JSON));
+    Assert.assertThat(swaggerGenerator.getSwagger().getProduces(),
+        Matchers.contains(MediaType.APPLICATION_JSON, MediaType.TEXT_PLAIN));
+  }
+
+  /**
+   * {@link RequestMapping#value()} takes higher priority than {@link RequestMapping#path()}
+   */
+  @Test
+  public void process_ValueOverWritePath() {
+    SwaggerGenerator swaggerGenerator = getSwaggerGenerator();
+    processor.process(SwaggerTestTarget_ValueOverWritePath.class.getAnnotation(RequestMapping.class), swaggerGenerator);
+
+    Assert.assertEquals("/testValue", swaggerGenerator.getSwagger().getBasePath());
+  }
+
+  /**
+   * {@link RequestMapping} takes higher priority than {@link Api} on setting {@code consumes} and {@code produces}
+   */
+  @Test
+  public void process_OverWriteConsumesAndProduces() {
+    SwaggerGenerator swaggerGenerator = getSwaggerGenerator();
+    swaggerGenerator.getSwagger().addConsumes(MediaType.APPLICATION_XML);
+    swaggerGenerator.getSwagger().addProduces(MediaType.APPLICATION_XML);
+    processor.process(SwaggerTestTarget.class.getAnnotation(RequestMapping.class), swaggerGenerator);
+
+    Assert.assertThat(swaggerGenerator.getSwagger().getConsumes(),
+        Matchers.contains(MediaType.TEXT_PLAIN, MediaType.APPLICATION_JSON));
+    Assert.assertThat(swaggerGenerator.getSwagger().getProduces(),
+        Matchers.contains(MediaType.APPLICATION_JSON, MediaType.TEXT_PLAIN));
+  }
+
+  private SwaggerGenerator getSwaggerGenerator() {
+    SwaggerGeneratorContext swaggerGeneratorContext = new MockUp<SwaggerGeneratorContext>() {
+      @Mock
+      String resolveStringValue(String strVal) {
+        return strVal;
+      }
+    }.getMockInstance();
+
+    return new SwaggerGenerator(swaggerGeneratorContext, null);
+  }
+
+  @RequestMapping(path = "/test", method = RequestMethod.POST,
+      consumes = {MediaType.TEXT_PLAIN, MediaType.APPLICATION_JSON},
+      produces = {MediaType.APPLICATION_JSON, MediaType.TEXT_PLAIN})
+  private class SwaggerTestTarget {
+  }
+
+  @RequestMapping(value = "/testValue", path = "/testPath")
+  private class SwaggerTestTarget_ValueOverWritePath {
+  }
+}


 

----------------------------------------------------------------
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