[ 
https://issues.apache.org/jira/browse/SCB-928?page=com.atlassian.jira.plugin.system.issuetabpanels:comment-tabpanel&focusedCommentId=16650088#comment-16650088
 ] 

ASF GitHub Bot commented on SCB-928:
------------------------------------

liubao68 closed pull request #952: [SCB-928] support "collection-format" 
feature on query param
URL: https://github.com/apache/incubator-servicecomb-java-chassis/pull/952
 
 
   

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/param/QueryProcessorCreator.java
 
b/common/common-rest/src/main/java/org/apache/servicecomb/common/rest/codec/param/QueryProcessorCreator.java
index 713711ed8..952c6495a 100644
--- 
a/common/common-rest/src/main/java/org/apache/servicecomb/common/rest/codec/param/QueryProcessorCreator.java
+++ 
b/common/common-rest/src/main/java/org/apache/servicecomb/common/rest/codec/param/QueryProcessorCreator.java
@@ -23,6 +23,7 @@
 
 import org.apache.commons.lang3.StringUtils;
 import org.apache.servicecomb.common.rest.codec.RestClientRequest;
+import 
org.apache.servicecomb.swagger.converter.property.SwaggerParamCollectionFormat;
 
 import com.fasterxml.jackson.databind.JavaType;
 import com.fasterxml.jackson.databind.type.TypeFactory;
@@ -38,18 +39,25 @@
     // This configuration is used for temporary use only. Do not use it if you 
are sure how it works. And may be deleted in future.
     private boolean emptyAsNull = DynamicPropertyFactory.getInstance()
         .getBooleanProperty("servicecomb.rest.parameter.query.emptyAsNull", 
false).get();
+
     // This configuration is used for temporary use only. Do not use it if you 
are sure how it works. And may be deleted in future.
     private boolean ignoreDefaultValue = DynamicPropertyFactory.getInstance()
         
.getBooleanProperty("servicecomb.rest.parameter.query.ignoreDefaultValue", 
false).get();
 
-    public QueryProcessor(String paramPath, JavaType targetType, Object 
defaultValue) {
+    private SwaggerParamCollectionFormat collectionFormat;
+
+    public QueryProcessor(String paramPath, JavaType targetType, Object 
defaultValue, String collectionFormat) {
       super(paramPath, targetType, defaultValue);
+      if (StringUtils.isNoneEmpty(collectionFormat)) {
+        this.collectionFormat = 
SwaggerParamCollectionFormat.valueOf(collectionFormat.toUpperCase());
+      }
     }
 
     @Override
     public Object getValue(HttpServletRequest request) throws Exception {
       Object value = null;
-      if (targetType.isContainerType()) {
+      if (targetType.isContainerType()
+          && SwaggerParamCollectionFormat.MULTI.equals(collectionFormat)) {
         value = request.getParameterValues(paramPath);
       } else {
         value = request.getParameter(paramPath);
@@ -65,6 +73,9 @@ public Object getValue(HttpServletRequest request) throws 
Exception {
             value = defaultValue;
           }
         }
+        if (null != collectionFormat) {
+          value = collectionFormat.splitParam((String) value);
+        }
       }
 
       return convertValue(value, targetType);
@@ -79,6 +90,10 @@ public void setValue(RestClientRequest clientRequest, Object 
arg) throws Excepti
     public String getProcessorType() {
       return PARAMTYPE;
     }
+
+    public SwaggerParamCollectionFormat getCollectionFormat() {
+      return collectionFormat;
+    }
   }
 
   public QueryProcessorCreator() {
@@ -87,7 +102,9 @@ public QueryProcessorCreator() {
 
   @Override
   public ParamValueProcessor create(Parameter parameter, Type 
genericParamType) {
+    QueryParameter queryParameter = (QueryParameter) parameter;
     JavaType targetType = 
TypeFactory.defaultInstance().constructType(genericParamType);
-    return new QueryProcessor(parameter.getName(), targetType, 
((QueryParameter) parameter).getDefaultValue());
+    return new QueryProcessor(parameter.getName(), targetType, 
queryParameter.getDefaultValue(),
+        queryParameter.getCollectionFormat());
   }
 }
diff --git 
a/common/common-rest/src/main/java/org/apache/servicecomb/common/rest/definition/path/PathVarParamWriter.java
 
b/common/common-rest/src/main/java/org/apache/servicecomb/common/rest/definition/path/PathVarParamWriter.java
index 3cde1c02f..a99507313 100644
--- 
a/common/common-rest/src/main/java/org/apache/servicecomb/common/rest/definition/path/PathVarParamWriter.java
+++ 
b/common/common-rest/src/main/java/org/apache/servicecomb/common/rest/definition/path/PathVarParamWriter.java
@@ -18,6 +18,7 @@
 package org.apache.servicecomb.common.rest.definition.path;
 
 import org.apache.servicecomb.common.rest.definition.RestParam;
+import 
org.apache.servicecomb.common.rest.definition.path.URLPathBuilder.URLPathStringBuilder;
 import org.apache.servicecomb.foundation.common.http.HttpUtils;
 
 /**
@@ -29,9 +30,9 @@ public PathVarParamWriter(RestParam param) {
   }
 
   @Override
-  public void write(StringBuilder builder, Object[] args) throws Exception {
+  public void write(URLPathStringBuilder builder, Object[] args) throws 
Exception {
     String paramValue = getParamValue(args).toString();
     String encodedPathParam = HttpUtils.encodePathParam(paramValue);
-    builder.append(encodedPathParam);
+    builder.appendPath(encodedPathParam);
   }
 }
diff --git 
a/common/common-rest/src/main/java/org/apache/servicecomb/common/rest/definition/path/QueryVarParamWriter.java
 
b/common/common-rest/src/main/java/org/apache/servicecomb/common/rest/definition/path/QueryVarParamWriter.java
index e07742952..db14dfcc4 100644
--- 
a/common/common-rest/src/main/java/org/apache/servicecomb/common/rest/definition/path/QueryVarParamWriter.java
+++ 
b/common/common-rest/src/main/java/org/apache/servicecomb/common/rest/definition/path/QueryVarParamWriter.java
@@ -19,27 +19,29 @@
 
 import java.net.URLEncoder;
 import java.nio.charset.StandardCharsets;
+import java.util.Arrays;
 import java.util.Collection;
 
 import org.apache.servicecomb.common.rest.codec.RestObjectMapperFactory;
+import 
org.apache.servicecomb.common.rest.codec.param.QueryProcessorCreator.QueryProcessor;
 import org.apache.servicecomb.common.rest.definition.RestParam;
+import 
org.apache.servicecomb.common.rest.definition.path.URLPathBuilder.URLPathStringBuilder;
+import 
org.apache.servicecomb.swagger.converter.property.SwaggerParamCollectionFormat;
 
 public class QueryVarParamWriter extends AbstractUrlParamWriter {
-  // ? or &
-  private char prefix;
 
-  public QueryVarParamWriter(char prefix, RestParam param) {
+  private SwaggerParamCollectionFormat collectionFormat;
+
+  public QueryVarParamWriter(RestParam param) {
     this.param = param;
-    this.prefix = prefix;
+    this.collectionFormat = ((QueryProcessor) 
param.getParamProcessor()).getCollectionFormat();
   }
 
   @Override
-  public void write(StringBuilder builder, Object[] args) throws Exception {
-    builder.append(prefix);
-
+  public void write(URLPathStringBuilder builder, Object[] args) throws 
Exception {
     Object value = getParamValue(args);
     if (value == null) {
-      // 连key都不写进去,才能表达null的概念
+      // do not write query key to express "null"
       return;
     }
 
@@ -53,49 +55,54 @@ public void write(StringBuilder builder, Object[] args) 
throws Exception {
       return;
     }
 
-    writeKeyEqual(builder);
-    builder.append(encodeNotNullValue(value));
-  }
-
-  private void writeKeyEqual(StringBuilder builder) {
-    builder.append(param.getParamName()).append('=');
+    builder.appendQuery(param.getParamName(), encodeNotNullValue(value));
   }
 
   @SuppressWarnings("unchecked")
-  private void writeCollection(StringBuilder builder, Object value) throws 
Exception {
+  private void writeCollection(URLPathStringBuilder builder, Object value) 
throws Exception {
+    if (shouldJoinParams()) {
+      writeJoinedParams(builder, (Collection<?>) value);
+      return;
+    }
+
     for (Object item : (Collection<Object>) value) {
       writeItem(builder, item);
     }
+  }
 
-    if (((Collection<Object>) value).size() != 0) {
-      deleteLastChar(builder);
+  private void writeArray(URLPathStringBuilder builder, Object value) throws 
Exception {
+    if (shouldJoinParams()) {
+      writeJoinedParams(builder, Arrays.asList(((Object[]) value)));
+      return;
     }
-  }
 
-  private void writeArray(StringBuilder builder, Object value) throws 
Exception {
     for (Object item : (Object[]) value) {
       writeItem(builder, item);
     }
+  }
 
-    if (((Object[]) value).length != 0) {
-      deleteLastChar(builder);
+  private void writeJoinedParams(URLPathStringBuilder builder, Collection<?> 
value) throws Exception {
+    String joinedParam = collectionFormat.joinParam(value);
+    if (null == joinedParam) {
+      return;
     }
+    builder.appendQuery(param.getParamName(), encodeNotNullValue(joinedParam));
   }
 
-  private void deleteLastChar(StringBuilder builder) {
-    builder.setLength(builder.length() - 1);
+  /**
+   * Whether to join params with separator.
+   * For collection format csv/ssv/tsv/pipes
+   */
+  private boolean shouldJoinParams() {
+    return null != collectionFormat && SwaggerParamCollectionFormat.MULTI != 
collectionFormat;
   }
 
-  private void writeItem(StringBuilder builder, Object item) throws Exception {
-    writeKeyEqual(builder);
-
-    // TODO:数组元素为null,当前找不到表达方式,通过issue跟踪,有解决方案后再来处理
-    // http://code.huawei.com/CSE/cse-java-chassis/issues/133
-    if (item != null) {
-      builder.append(encodeNotNullValue(item));
+  private void writeItem(URLPathStringBuilder builder, Object item) throws 
Exception {
+    if (null == item) {
+      return;
     }
 
-    builder.append('&');
+    builder.appendQuery(param.getParamName(), encodeNotNullValue(item));
   }
 
   private String encodeNotNullValue(Object value) throws Exception {
diff --git 
a/common/common-rest/src/main/java/org/apache/servicecomb/common/rest/definition/path/StaticUrlParamWriter.java
 
b/common/common-rest/src/main/java/org/apache/servicecomb/common/rest/definition/path/StaticUrlParamWriter.java
index c54e0f54e..315a6778e 100644
--- 
a/common/common-rest/src/main/java/org/apache/servicecomb/common/rest/definition/path/StaticUrlParamWriter.java
+++ 
b/common/common-rest/src/main/java/org/apache/servicecomb/common/rest/definition/path/StaticUrlParamWriter.java
@@ -17,6 +17,8 @@
 
 package org.apache.servicecomb.common.rest.definition.path;
 
+import 
org.apache.servicecomb.common.rest.definition.path.URLPathBuilder.URLPathStringBuilder;
+
 public class StaticUrlParamWriter implements UrlParamWriter {
 
   private String staticPath;
@@ -26,7 +28,7 @@ public StaticUrlParamWriter(String staticPath) {
   }
 
   @Override
-  public void write(StringBuilder builder, Object[] args) {
-    builder.append(staticPath);
+  public void write(URLPathStringBuilder builder, Object[] args) {
+    builder.appendPath(staticPath);
   }
 }
diff --git 
a/common/common-rest/src/main/java/org/apache/servicecomb/common/rest/definition/path/URLPathBuilder.java
 
b/common/common-rest/src/main/java/org/apache/servicecomb/common/rest/definition/path/URLPathBuilder.java
index 0f2639841..8a948f558 100644
--- 
a/common/common-rest/src/main/java/org/apache/servicecomb/common/rest/definition/path/URLPathBuilder.java
+++ 
b/common/common-rest/src/main/java/org/apache/servicecomb/common/rest/definition/path/URLPathBuilder.java
@@ -47,12 +47,7 @@ private void initQueryWriterList(Map<String, RestParam> 
paramMap) {
         continue;
       }
 
-      char prefix = '&';
-      if (queryParamWriterList.isEmpty()) {
-        prefix = '?';
-      }
-
-      UrlParamWriter dynamicWriter = new QueryVarParamWriter(prefix, param);
+      UrlParamWriter dynamicWriter = new QueryVarParamWriter(param);
       queryParamWriterList.add(dynamicWriter);
     }
   }
@@ -90,31 +85,57 @@ private void initPathWriterList(String rawPath, Map<String, 
RestParam> paramMap)
     }
   }
 
-
   public String createRequestPath(Object[] args) throws Exception {
-    StringBuilder builder = new StringBuilder();
+    URLPathStringBuilder builder = new URLPathStringBuilder();
 
     genPathString(builder, args);
     genQueryString(builder, args);
 
-    return builder.toString();
+    return builder.build();
   }
 
   public String createPathString(Object[] args) throws Exception {
-    StringBuilder builder = new StringBuilder();
+    URLPathStringBuilder builder = new URLPathStringBuilder();
     genPathString(builder, args);
-    return builder.toString();
+    return builder.build();
   }
 
-  private void genPathString(StringBuilder builder, Object[] args) throws 
Exception {
+  private void genPathString(URLPathStringBuilder builder, Object[] args) 
throws Exception {
     for (UrlParamWriter writer : this.pathParamWriterList) {
       writer.write(builder, args);
     }
   }
 
-  private void genQueryString(StringBuilder builder, Object[] args) throws 
Exception {
+  private void genQueryString(URLPathStringBuilder builder, Object[] args) 
throws Exception {
     for (UrlParamWriter writer : queryParamWriterList) {
       writer.write(builder, args);
     }
   }
+
+  public static class URLPathStringBuilder {
+    private StringBuilder stringBuilder = new StringBuilder();
+
+    private boolean queryPrefixNotWrite = true;
+
+    public URLPathStringBuilder appendPath(String s) {
+      stringBuilder.append(s);
+      return this;
+    }
+
+    public URLPathStringBuilder appendQuery(String key, String value) {
+      if (queryPrefixNotWrite) {
+        stringBuilder.append('?');
+        queryPrefixNotWrite = false;
+      } else {
+        stringBuilder.append('&');
+      }
+
+      stringBuilder.append(key).append("=").append(value);
+      return this;
+    }
+
+    public String build() {
+      return stringBuilder.toString();
+    }
+  }
 }
diff --git 
a/common/common-rest/src/main/java/org/apache/servicecomb/common/rest/definition/path/UrlParamWriter.java
 
b/common/common-rest/src/main/java/org/apache/servicecomb/common/rest/definition/path/UrlParamWriter.java
index ac6e3dfb6..a1a5fa6b3 100644
--- 
a/common/common-rest/src/main/java/org/apache/servicecomb/common/rest/definition/path/UrlParamWriter.java
+++ 
b/common/common-rest/src/main/java/org/apache/servicecomb/common/rest/definition/path/UrlParamWriter.java
@@ -17,6 +17,8 @@
 
 package org.apache.servicecomb.common.rest.definition.path;
 
+import 
org.apache.servicecomb.common.rest.definition.path.URLPathBuilder.URLPathStringBuilder;
+
 public interface UrlParamWriter {
-  void write(StringBuilder builder, Object[] args) throws Exception;
+  void write(URLPathStringBuilder builder, Object[] args) throws Exception;
 }
diff --git 
a/common/common-rest/src/test/java/org/apache/servicecomb/common/rest/codec/param/TestQueryProcessor.java
 
b/common/common-rest/src/test/java/org/apache/servicecomb/common/rest/codec/param/TestQueryProcessor.java
index 75f1ee040..ec896a2fb 100644
--- 
a/common/common-rest/src/test/java/org/apache/servicecomb/common/rest/codec/param/TestQueryProcessor.java
+++ 
b/common/common-rest/src/test/java/org/apache/servicecomb/common/rest/codec/param/TestQueryProcessor.java
@@ -33,8 +33,8 @@
   @Mocked
   HttpServletRequest request;
 
-  private ParamValueProcessor createProcessor(String name, Class<?> type) {
-    return new QueryProcessor(name, 
TypeFactory.defaultInstance().constructType(type), null);
+  private ParamValueProcessor createProcessor(String name, Class<?> type, 
String collectionFormat) {
+    return new QueryProcessor(name, 
TypeFactory.defaultInstance().constructType(type), null, collectionFormat);
   }
 
   @Test
@@ -46,7 +46,7 @@ public void testGetValueNormal() throws Exception {
       }
     };
 
-    ParamValueProcessor processor = createProcessor("name", String.class);
+    ParamValueProcessor processor = createProcessor("name", String.class, 
"multi");
     Object value = processor.getValue(request);
     Assert.assertEquals("value", value);
   }
@@ -56,18 +56,32 @@ public void testGetValueContainerType() throws Exception {
     new Expectations() {
       {
         request.getParameterValues("name");
-        result = new String[] {"value"};
+        result = new String[] {"value", "value2"};
       }
     };
 
-    ParamValueProcessor processor = createProcessor("name", String[].class);
+    ParamValueProcessor processor = createProcessor("name", String[].class, 
"multi");
     String[] value = (String[]) processor.getValue(request);
-    Assert.assertThat(value, Matchers.arrayContaining("value"));
+    Assert.assertThat(value, Matchers.arrayContaining("value", "value2"));
+  }
+
+  @Test
+  public void testGetValueOnCollectionFormatIsCsv() throws Exception {
+    new Expectations() {
+      {
+        request.getParameter("name");
+        result = "value2,value3";
+      }
+    };
+
+    ParamValueProcessor processor = createProcessor("name", String[].class, 
"csv");
+    String[] value = (String[]) processor.getValue(request);
+    Assert.assertThat(value, Matchers.arrayContaining("value2", "value3"));
   }
 
   @Test
   public void testGetProcessorType() {
-    ParamValueProcessor processor = createProcessor("name", String.class);
+    ParamValueProcessor processor = createProcessor("name", String.class, 
"multi");
     Assert.assertEquals("query", processor.getProcessorType());
   }
 }
diff --git 
a/common/common-rest/src/test/java/org/apache/servicecomb/common/rest/definition/TestPath.java
 
b/common/common-rest/src/test/java/org/apache/servicecomb/common/rest/definition/TestPath.java
index f018c899d..bfac3fa99 100644
--- 
a/common/common-rest/src/test/java/org/apache/servicecomb/common/rest/definition/TestPath.java
+++ 
b/common/common-rest/src/test/java/org/apache/servicecomb/common/rest/definition/TestPath.java
@@ -17,6 +17,8 @@
 
 package org.apache.servicecomb.common.rest.definition;
 
+import static org.junit.Assert.fail;
+
 import java.util.Arrays;
 import java.util.HashMap;
 import java.util.Map;
@@ -24,6 +26,7 @@
 import org.apache.servicecomb.common.rest.definition.path.PathRegExp;
 import org.apache.servicecomb.common.rest.definition.path.QueryVarParamWriter;
 import org.apache.servicecomb.common.rest.definition.path.URLPathBuilder;
+import 
org.apache.servicecomb.common.rest.definition.path.URLPathBuilder.URLPathStringBuilder;
 import org.junit.After;
 import org.junit.Assert;
 import org.junit.Before;
@@ -38,11 +41,11 @@
 public class TestPath {
 
   @Before
-  public void setUp() throws Exception {
+  public void setUp() {
   }
 
   @After
-  public void tearDown() throws Exception {
+  public void tearDown() {
   }
 
   @Test
@@ -61,29 +64,33 @@ public void testPathRegExp() throws Exception {
     Assert.assertEquals(0, oSecondPathRegExp.getStaticCharCount());
     Assert.assertNotEquals(null, (oPathRegExp.match("//{test}//", new 
HashMap<>())));
     // Error Scenarios
-    oPathRegExp = new PathRegExp("//{test \t}//");
+    new PathRegExp("//{test \t}//");
     // Error Scenarios for double {{
     try {
-      oPathRegExp = new PathRegExp("//{test{");
+      new PathRegExp("//{test{");
+      fail("an exception is expected!");
     } catch (Exception e) {
       Assert.assertEquals(true, e.getMessage().contains("A variable must not 
contain an extra"));
     }
     // Error Scenarios for illegal }}
     try {
-      oPathRegExp = new PathRegExp("//}");
+      new PathRegExp("//}");
+      fail("an exception is expected!");
     } catch (Exception e) {
       Assert.assertEquals(true, e.getMessage().contains("is only allowed as"));
     }
     // Error Scenarios for illegal ;
     try {
-      oPathRegExp = new PathRegExp("//;");
+      new PathRegExp("//;");
+      fail("an exception is expected!");
     } catch (Exception e) {
       Assert.assertEquals(true, e.getMessage().contains("matrix parameters are 
not allowed in"));
     }
 
     // Error Scenarios for NO } ;
     try {
-      oPathRegExp = new PathRegExp("//{test");
+      new PathRegExp("//{test");
+      fail("an exception is expected!");
     } catch (Exception e) {
       Assert.assertEquals(true, e.getMessage().contains("No '}' found after"));
     }
@@ -110,7 +117,7 @@ public void testUrlPathBuilder() throws Exception {
   }
 
   @Test
-  public void testQueryVarParamWriter() throws Exception {
+  public void testQueryVarParamWriter() {
     boolean status = true;
     new MockUp<RestParam>() {
       @Mock
@@ -126,7 +133,7 @@ protected Object getParamValue(Object[] args) {
     };
 
     Parameter parameter = new QueryParameter();
-    QueryVarParamWriter writer = new QueryVarParamWriter('&', new RestParam(0, 
parameter, String.class));
+    QueryVarParamWriter writer = new QueryVarParamWriter(new RestParam(0, 
parameter, String.class));
     try {
       verify(writer, "T", "&queryVar=T");
       verify(writer, null, "&");
@@ -142,8 +149,9 @@ protected Object getParamValue(Object[] args) {
     Assert.assertTrue(status);
   }
 
+  // TODO expect not used?
   private void verify(QueryVarParamWriter writer, Object arg, String expect) 
throws Exception {
-    StringBuilder sb = new StringBuilder();
+    URLPathStringBuilder sb = new URLPathStringBuilder();
     Object[] args = new Object[] {arg};
     writer.write(sb, args);
   }
diff --git 
a/common/common-rest/src/test/java/org/apache/servicecomb/common/rest/definition/path/PathVarParamWriterTest.java
 
b/common/common-rest/src/test/java/org/apache/servicecomb/common/rest/definition/path/PathVarParamWriterTest.java
index dbc87f9f2..fd2400984 100644
--- 
a/common/common-rest/src/test/java/org/apache/servicecomb/common/rest/definition/path/PathVarParamWriterTest.java
+++ 
b/common/common-rest/src/test/java/org/apache/servicecomb/common/rest/definition/path/PathVarParamWriterTest.java
@@ -18,6 +18,7 @@
 package org.apache.servicecomb.common.rest.definition.path;
 
 import org.apache.servicecomb.common.rest.definition.RestParam;
+import 
org.apache.servicecomb.common.rest.definition.path.URLPathBuilder.URLPathStringBuilder;
 import org.junit.Assert;
 import org.junit.Test;
 
@@ -30,44 +31,44 @@
   public void writePlainPath() throws Exception {
     PathVarParamWriter pathVarParamWriter = createPathVarParamWriter();
 
-    StringBuilder pathBuilder = new StringBuilder();
+    URLPathStringBuilder pathBuilder = new URLPathStringBuilder();
     pathVarParamWriter.write(pathBuilder, new Object[] {"abc"});
-    Assert.assertEquals("abc", pathBuilder.toString());
+    Assert.assertEquals("abc", pathBuilder.build());
   }
 
   @Test
   public void writePathWithSpace() throws Exception {
     PathVarParamWriter pathVarParamWriter = createPathVarParamWriter();
 
-    StringBuilder pathBuilder = new StringBuilder();
+    URLPathStringBuilder pathBuilder = new URLPathStringBuilder();
     pathVarParamWriter.write(pathBuilder, new String[] {"a 20bc"});
-    Assert.assertEquals("a%2020bc", pathBuilder.toString());
+    Assert.assertEquals("a%2020bc", pathBuilder.build());
   }
 
   @Test
   public void writePathWithPercentage() throws Exception {
     PathVarParamWriter pathVarParamWriter = createPathVarParamWriter();
-    StringBuilder pathBuilder = new StringBuilder();
-    pathBuilder.append("/api/");
+    URLPathStringBuilder pathBuilder = new URLPathStringBuilder();
+    pathBuilder.appendPath("/api/");
     pathVarParamWriter.write(pathBuilder, new String[] {"a%%bc"});
-    Assert.assertEquals("/api/a%25%25bc", pathBuilder.toString());
+    Assert.assertEquals("/api/a%25%25bc", pathBuilder.build());
   }
 
   @Test
   public void writePathParamWithSlash() throws Exception {
     PathVarParamWriter pathVarParamWriter = createPathVarParamWriter();
-    StringBuilder pathBuilder = new StringBuilder();
-    pathBuilder.append("/api/");
+    URLPathStringBuilder pathBuilder = new URLPathStringBuilder();
+    pathBuilder.appendPath("/api/");
     pathVarParamWriter.write(pathBuilder, new String[] {"a/bc"});
-    Assert.assertEquals("/api/a%2Fbc", pathBuilder.toString());
+    Assert.assertEquals("/api/a%2Fbc", pathBuilder.build());
   }
 
   @Test
   public void writeIntegerParam() throws Exception {
     PathVarParamWriter pathVarParamWriter = createPathVarParamWriter();
-    StringBuilder pathBuilder = new StringBuilder();
+    URLPathStringBuilder pathBuilder = new URLPathStringBuilder();
     pathVarParamWriter.write(pathBuilder, new Integer[] {12});
-    Assert.assertEquals("12", pathBuilder.toString());
+    Assert.assertEquals("12", pathBuilder.build());
   }
 
   private PathVarParamWriter createPathVarParamWriter() {
diff --git 
a/common/common-rest/src/test/java/org/apache/servicecomb/common/rest/definition/path/QueryVarParamWriterTest.java
 
b/common/common-rest/src/test/java/org/apache/servicecomb/common/rest/definition/path/QueryVarParamWriterTest.java
new file mode 100644
index 000000000..33648a618
--- /dev/null
+++ 
b/common/common-rest/src/test/java/org/apache/servicecomb/common/rest/definition/path/QueryVarParamWriterTest.java
@@ -0,0 +1,231 @@
+/*
+ * 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.rest.definition.path;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.List;
+
+import org.apache.servicecomb.common.rest.definition.RestParam;
+import 
org.apache.servicecomb.common.rest.definition.path.URLPathBuilder.URLPathStringBuilder;
+import org.junit.Assert;
+import org.junit.BeforeClass;
+import org.junit.Test;
+
+import io.swagger.models.parameters.QueryParameter;
+
+public class QueryVarParamWriterTest {
+
+  private static QueryVarParamWriter queryVarParamWriterCsv;
+
+  private static QueryVarParamWriter queryVarParamWriterMulti;
+
+  private static QueryVarParamWriter queryVarParamWriterDefault;
+
+  @BeforeClass
+  public static void beforeClass() {
+    QueryParameter parameter = new QueryParameter();
+    parameter.setName("q");
+    parameter.setCollectionFormat("csv");
+    queryVarParamWriterCsv = new QueryVarParamWriter(
+        new RestParam(0, parameter, String[].class));
+
+    parameter = new QueryParameter();
+    parameter.setName("q");
+    parameter.setCollectionFormat("multi");
+    queryVarParamWriterMulti = new QueryVarParamWriter(
+        new RestParam(0, parameter, String[].class));
+
+    parameter = new QueryParameter();
+    parameter.setName("q");
+    queryVarParamWriterDefault = new QueryVarParamWriter(
+        new RestParam(0, parameter, String[].class));
+  }
+
+  @Test
+  public void write() throws Exception {
+    URLPathStringBuilder stringBuilder = new URLPathStringBuilder();
+    Object[] args = {"a"};
+    queryVarParamWriterCsv.write(stringBuilder, args);
+    Assert.assertEquals("?q=a", stringBuilder.build());
+    stringBuilder = new URLPathStringBuilder();
+    queryVarParamWriterMulti.write(stringBuilder, args);
+    Assert.assertEquals("?q=a", stringBuilder.build());
+    stringBuilder = new URLPathStringBuilder();
+    queryVarParamWriterDefault.write(stringBuilder, args);
+    Assert.assertEquals("?q=a", stringBuilder.build());
+  }
+
+  @Test
+  public void writeNull() throws Exception {
+    URLPathStringBuilder stringBuilder = new URLPathStringBuilder();
+    Object[] args = {null};
+    queryVarParamWriterCsv.write(stringBuilder, args);
+    Assert.assertEquals("", stringBuilder.build());
+    stringBuilder = new URLPathStringBuilder();
+    queryVarParamWriterMulti.write(stringBuilder, args);
+    Assert.assertEquals("", stringBuilder.build());
+    stringBuilder = new URLPathStringBuilder();
+    queryVarParamWriterDefault.write(stringBuilder, args);
+    Assert.assertEquals("", stringBuilder.build());
+  }
+
+  @Test
+  public void writeArray() throws Exception {
+    URLPathStringBuilder stringBuilder = new URLPathStringBuilder();
+    Object[] args = new Object[] {new String[] {"ab", "cd", "ef"}};
+    queryVarParamWriterCsv.write(stringBuilder, args);
+    Assert.assertEquals("?q=ab%2Ccd%2Cef", stringBuilder.build());
+    stringBuilder = new URLPathStringBuilder();
+    queryVarParamWriterMulti.write(stringBuilder, args);
+    Assert.assertEquals("?q=ab&q=cd&q=ef", stringBuilder.build());
+    stringBuilder = new URLPathStringBuilder();
+    queryVarParamWriterDefault.write(stringBuilder, args);
+    Assert.assertEquals("?q=ab&q=cd&q=ef", stringBuilder.build());
+
+    // encode space char
+    stringBuilder = new URLPathStringBuilder();
+    args[0] = new String[] {"a b", " ", "", "ef"};
+    queryVarParamWriterCsv.write(stringBuilder, args);
+    Assert.assertEquals("?q=a+b%2C+%2C%2Cef", stringBuilder.build());
+    stringBuilder = new URLPathStringBuilder();
+    queryVarParamWriterMulti.write(stringBuilder, args);
+    Assert.assertEquals("?q=a+b&q=+&q=&q=ef", stringBuilder.build());
+    stringBuilder = new URLPathStringBuilder();
+    queryVarParamWriterDefault.write(stringBuilder, args);
+    Assert.assertEquals("?q=a+b&q=+&q=&q=ef", stringBuilder.build());
+
+    // pass blank string
+    stringBuilder = new URLPathStringBuilder();
+    args[0] = new String[] {""};
+    queryVarParamWriterCsv.write(stringBuilder, args);
+    Assert.assertEquals("?q=", stringBuilder.build());
+    stringBuilder = new URLPathStringBuilder();
+    queryVarParamWriterMulti.write(stringBuilder, args);
+    Assert.assertEquals("?q=", stringBuilder.build());
+    stringBuilder = new URLPathStringBuilder();
+    queryVarParamWriterDefault.write(stringBuilder, args);
+    Assert.assertEquals("?q=", stringBuilder.build());
+
+    // pass empty
+    stringBuilder = new URLPathStringBuilder();
+    args[0] = new String[] {};
+    queryVarParamWriterCsv.write(stringBuilder, args);
+    Assert.assertEquals("", stringBuilder.build());
+    stringBuilder = new URLPathStringBuilder();
+    queryVarParamWriterMulti.write(stringBuilder, args);
+    Assert.assertEquals("", stringBuilder.build());
+    stringBuilder = new URLPathStringBuilder();
+    queryVarParamWriterDefault.write(stringBuilder, args);
+    Assert.assertEquals("", stringBuilder.build());
+    // pass null
+    args[0] = new String[] {null};
+    stringBuilder = new URLPathStringBuilder();
+    queryVarParamWriterCsv.write(stringBuilder, args);
+    Assert.assertEquals("", stringBuilder.build());
+    stringBuilder = new URLPathStringBuilder();
+    queryVarParamWriterMulti.write(stringBuilder, args);
+    Assert.assertEquals("", stringBuilder.build());
+    stringBuilder = new URLPathStringBuilder();
+    queryVarParamWriterDefault.write(stringBuilder, args);
+    Assert.assertEquals("", stringBuilder.build());
+    args[0] = new String[] {null, "ab", null, "cd", null, null, "", null, 
"ef", null};
+    stringBuilder = new URLPathStringBuilder();
+    queryVarParamWriterCsv.write(stringBuilder, args);
+    Assert.assertEquals("?q=ab%2Ccd%2C%2Cef", stringBuilder.build());
+    stringBuilder = new URLPathStringBuilder();
+    queryVarParamWriterMulti.write(stringBuilder, args);
+    Assert.assertEquals("?q=ab&q=cd&q=&q=ef", stringBuilder.build());
+    stringBuilder = new URLPathStringBuilder();
+    queryVarParamWriterDefault.write(stringBuilder, args);
+    Assert.assertEquals("?q=ab&q=cd&q=&q=ef", stringBuilder.build());
+  }
+
+  @Test
+  public void writeList() throws Exception {
+    List<String> queryList = Arrays.asList("ab", "cd", "ef");
+    Object[] args = {queryList};
+    URLPathStringBuilder stringBuilder = new URLPathStringBuilder();
+    queryVarParamWriterCsv.write(stringBuilder, args);
+    Assert.assertEquals("?q=ab%2Ccd%2Cef", stringBuilder.build());
+    stringBuilder = new URLPathStringBuilder();
+    queryVarParamWriterMulti.write(stringBuilder, args);
+    Assert.assertEquals("?q=ab&q=cd&q=ef", stringBuilder.build());
+    stringBuilder = new URLPathStringBuilder();
+    queryVarParamWriterDefault.write(stringBuilder, args);
+    Assert.assertEquals("?q=ab&q=cd&q=ef", stringBuilder.build());
+
+    // encode space char
+    args[0] = Arrays.asList("a b", " ", "", "ef");
+    stringBuilder = new URLPathStringBuilder();
+    queryVarParamWriterCsv.write(stringBuilder, args);
+    Assert.assertEquals("?q=a+b%2C+%2C%2Cef", stringBuilder.build());
+    stringBuilder = new URLPathStringBuilder();
+    queryVarParamWriterMulti.write(stringBuilder, args);
+    Assert.assertEquals("?q=a+b&q=+&q=&q=ef", stringBuilder.build());
+    stringBuilder = new URLPathStringBuilder();
+    queryVarParamWriterDefault.write(stringBuilder, args);
+    Assert.assertEquals("?q=a+b&q=+&q=&q=ef", stringBuilder.build());
+
+    // pass blank string
+    stringBuilder = new URLPathStringBuilder();
+    args[0] = Collections.singletonList("");
+    queryVarParamWriterCsv.write(stringBuilder, args);
+    Assert.assertEquals("?q=", stringBuilder.build());
+    stringBuilder = new URLPathStringBuilder();
+    queryVarParamWriterMulti.write(stringBuilder, args);
+    Assert.assertEquals("?q=", stringBuilder.build());
+    stringBuilder = new URLPathStringBuilder();
+    queryVarParamWriterDefault.write(stringBuilder, args);
+    Assert.assertEquals("?q=", stringBuilder.build());
+
+    // pass empty
+    stringBuilder = new URLPathStringBuilder();
+    args[0] = new ArrayList<>();
+    queryVarParamWriterCsv.write(stringBuilder, args);
+    Assert.assertEquals("", stringBuilder.build());
+    stringBuilder = new URLPathStringBuilder();
+    queryVarParamWriterMulti.write(stringBuilder, args);
+    Assert.assertEquals("", stringBuilder.build());
+    stringBuilder = new URLPathStringBuilder();
+    queryVarParamWriterDefault.write(stringBuilder, args);
+    Assert.assertEquals("", stringBuilder.build());
+    // pass null
+    args[0] = Collections.singletonList(null);
+    stringBuilder = new URLPathStringBuilder();
+    queryVarParamWriterCsv.write(stringBuilder, args);
+    Assert.assertEquals("", stringBuilder.build());
+    stringBuilder = new URLPathStringBuilder();
+    queryVarParamWriterMulti.write(stringBuilder, args);
+    Assert.assertEquals("", stringBuilder.build());
+    stringBuilder = new URLPathStringBuilder();
+    queryVarParamWriterDefault.write(stringBuilder, args);
+    Assert.assertEquals("", stringBuilder.build());
+    args[0] = Arrays.asList(null, "ab", null, "cd", null, null, "", null, 
"ef", null);
+    stringBuilder = new URLPathStringBuilder();
+    queryVarParamWriterCsv.write(stringBuilder, args);
+    Assert.assertEquals("?q=ab%2Ccd%2C%2Cef", stringBuilder.build());
+    stringBuilder = new URLPathStringBuilder();
+    queryVarParamWriterMulti.write(stringBuilder, args);
+    Assert.assertEquals("?q=ab&q=cd&q=&q=ef", stringBuilder.build());
+    stringBuilder = new URLPathStringBuilder();
+    queryVarParamWriterDefault.write(stringBuilder, args);
+    Assert.assertEquals("?q=ab&q=cd&q=&q=ef", stringBuilder.build());
+  }
+}
diff --git 
a/common/common-rest/src/test/java/org/apache/servicecomb/common/rest/definition/path/URLPathBuilderTest.java
 
b/common/common-rest/src/test/java/org/apache/servicecomb/common/rest/definition/path/URLPathBuilderTest.java
new file mode 100644
index 000000000..75af18720
--- /dev/null
+++ 
b/common/common-rest/src/test/java/org/apache/servicecomb/common/rest/definition/path/URLPathBuilderTest.java
@@ -0,0 +1,106 @@
+/*
+ * 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.rest.definition.path;
+
+import java.lang.reflect.Type;
+import java.util.LinkedHashMap;
+import java.util.Map;
+
+import org.apache.servicecomb.common.rest.definition.RestParam;
+import org.junit.Assert;
+import org.junit.Test;
+
+import io.swagger.models.parameters.Parameter;
+import io.swagger.models.parameters.PathParameter;
+import io.swagger.models.parameters.QueryParameter;
+
+public class URLPathBuilderTest {
+  @Test
+  public void testNormal() throws Exception {
+    Map<String, RestParam> paramMap = new LinkedHashMap<>();
+    addParam("p0", int.class, PathParameter::new, paramMap);
+    addParam("p1", String.class, PathParameter::new, paramMap);
+    addParam("q0", int.class, QueryParameter::new, paramMap);
+    addParam("q1", String.class, QueryParameter::new, paramMap);
+
+    URLPathBuilder urlPathBuilder = new URLPathBuilder("/path/{p0}/and/{p1}", 
paramMap);
+    Object[] args = {10, "abcPath", 11, "queryABC"};
+    Assert.assertEquals("/path/10/and/abcPath?q0=11&q1=queryABC",
+        urlPathBuilder.createRequestPath(args));
+    Assert.assertEquals("/path/10/and/abcPath",
+        urlPathBuilder.createPathString(args));
+  }
+
+  @Test
+  public void testEncode() throws Exception {
+    Map<String, RestParam> paramMap = new LinkedHashMap<>();
+    addParam("p", String.class, PathParameter::new, paramMap);
+    addParam("q", String.class, QueryParameter::new, paramMap);
+
+    URLPathBuilder urlPathBuilder = new URLPathBuilder("/path/{p}", paramMap);
+    Object[] args = {"ab%% %cd%", "ab%% %cd%"};
+    Assert.assertEquals("/path/ab%25%25%20%25cd%25?q=ab%25%25+%25cd%25",
+        urlPathBuilder.createRequestPath(args));
+    Assert.assertEquals("/path/ab%25%25%20%25cd%25",
+        urlPathBuilder.createPathString(args));
+  }
+
+  @Test
+  public void testMultiQuery() throws Exception {
+    Map<String, RestParam> paramMap = new LinkedHashMap<>();
+    addParam("strArr", String[].class, QueryParameter::new, paramMap);
+    addParam("intArr", int[].class, QueryParameter::new, paramMap);
+
+    URLPathBuilder urlPathBuilder = new URLPathBuilder("/path", paramMap);
+    Object[] args = new Object[] {
+        new Object[] {"a", "b", "c"},
+        new Object[] {1, 2, 3}
+    };
+    
Assert.assertEquals("/path?strArr=a&strArr=b&strArr=c&intArr=1&intArr=2&intArr=3",
+        urlPathBuilder.createRequestPath(args));
+    args = new Object[] {
+        new Object[] {},
+        new Object[] {1, 2, 3}
+    };
+    Assert.assertEquals("/path?intArr=1&intArr=2&intArr=3",
+        urlPathBuilder.createRequestPath(args));
+    args = new Object[] {
+        new Object[] {"a", "b", "c"},
+        new Object[] {}
+    };
+    Assert.assertEquals("/path?strArr=a&strArr=b&strArr=c",
+        urlPathBuilder.createRequestPath(args));
+    args = new Object[] {
+        new Object[] {},
+        new Object[] {}
+    };
+    Assert.assertEquals("/path",
+        urlPathBuilder.createRequestPath(args));
+  }
+
+  private void addParam(String paramName, Type paramType,
+      ParameterConstructor constructor, Map<String, RestParam> paramMap) {
+    Parameter parameter = constructor.construct();
+    parameter.setName(paramName);
+    paramMap.put(paramName, new RestParam(paramMap.size(), parameter, 
paramType));
+  }
+
+  static interface ParameterConstructor {
+    Parameter construct();
+  }
+}
diff --git 
a/common/common-rest/src/test/java/org/apache/servicecomb/common/rest/definition/path/URLPathStringBuilderTest.java
 
b/common/common-rest/src/test/java/org/apache/servicecomb/common/rest/definition/path/URLPathStringBuilderTest.java
new file mode 100644
index 000000000..2fc9a55aa
--- /dev/null
+++ 
b/common/common-rest/src/test/java/org/apache/servicecomb/common/rest/definition/path/URLPathStringBuilderTest.java
@@ -0,0 +1,54 @@
+/*
+ * 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.rest.definition.path;
+
+import static org.junit.Assert.assertEquals;
+
+import 
org.apache.servicecomb.common.rest.definition.path.URLPathBuilder.URLPathStringBuilder;
+import org.junit.Test;
+
+public class URLPathStringBuilderTest {
+  @Test
+  public void testNormal() {
+    URLPathStringBuilder builder = new URLPathStringBuilder();
+    builder.appendPath("/path");
+    builder.appendQuery("q", "abc");
+    assertEquals("/path?q=abc", builder.build());
+  }
+
+  @Test
+  public void appendPath() {
+    URLPathStringBuilder builder = new URLPathStringBuilder();
+    builder.appendPath("/abc");
+    assertEquals("/abc", builder.build());
+    builder.appendPath("/de fg");
+    assertEquals("/abc/de fg", builder.build());
+  }
+
+  @Test
+  public void appendQuery() {
+    URLPathStringBuilder builder = new URLPathStringBuilder();
+    assertEquals("", builder.build());
+    builder.appendQuery("ab", "cd");
+    assertEquals("?ab=cd", builder.build());
+    builder.appendQuery("ef", "");
+    assertEquals("?ab=cd&ef=", builder.build());
+    builder.appendQuery("gh", "jk");
+    assertEquals("?ab=cd&ef=&gh=jk", builder.build());
+  }
+}
\ No newline at end of file
diff --git 
a/foundations/foundation-common/src/main/resources/config/base/log4j.properties 
b/foundations/foundation-common/src/main/resources/config/base/log4j.properties
index fbbee883b..83de4e475 100644
--- 
a/foundations/foundation-common/src/main/resources/config/base/log4j.properties
+++ 
b/foundations/foundation-common/src/main/resources/config/base/log4j.properties
@@ -34,3 +34,6 @@ log4j.appender.paas.MaxBackupIndex=10
 log4j.appender.paas.layout=org.apache.log4j.PatternLayout
 log4j.appender.paas.layout.ConversionPattern=[%d{yyyy-MM-dd 
HH:mm:ss,SSS/zzz}][%t][%p]%m %l%n
 log4j.appender.paas.logPermission=rw-------
+
+# do not print configuration content
+log4j.logger.com.netflix.config.DynamicPropertyUpdater=OFF
diff --git 
a/integration-tests/it-consumer/src/main/java/org/apache/servicecomb/it/schema/ApiParamJaxrsSchema.java
 
b/integration-tests/it-consumer/src/main/java/org/apache/servicecomb/it/schema/ApiParamJaxrsSchema.java
index f5b814330..fe83aa59c 100644
--- 
a/integration-tests/it-consumer/src/main/java/org/apache/servicecomb/it/schema/ApiParamJaxrsSchema.java
+++ 
b/integration-tests/it-consumer/src/main/java/org/apache/servicecomb/it/schema/ApiParamJaxrsSchema.java
@@ -42,6 +42,13 @@ public void query(@ApiParam(value = "desc of query param") 
@QueryParam("input")
 
   }
 
+  @POST
+  @Path("/queryArr")
+  public void queryArr(@ApiParam(value = "desc of queryArr param")
+  @QueryParam("input") String[] input) {
+
+  }
+
   @POST
   @Path("/header")
   public void header(@ApiParam(value = "desc of header param") 
@HeaderParam("input") int input) {
diff --git 
a/integration-tests/it-consumer/src/main/java/org/apache/servicecomb/it/schema/ApiParamSpringmvcSchema.java
 
b/integration-tests/it-consumer/src/main/java/org/apache/servicecomb/it/schema/ApiParamSpringmvcSchema.java
index 8cd6e5463..b52f9f418 100644
--- 
a/integration-tests/it-consumer/src/main/java/org/apache/servicecomb/it/schema/ApiParamSpringmvcSchema.java
+++ 
b/integration-tests/it-consumer/src/main/java/org/apache/servicecomb/it/schema/ApiParamSpringmvcSchema.java
@@ -43,12 +43,22 @@ public void body(@ApiParam(value = "desc of body param",
 
   @PostMapping(path = "/query")
   public void query(@ApiParam(value = "desc of query param",
+      required = true,
+      readOnly = true,
+      allowEmptyValue = true,
+      name = "inputEx",
+      example = "10") int input) {
+
+  }
+
+  @PostMapping(path = "/queryArr")
+  public void queryArr(@ApiParam(value = "desc of queryArr param",
       required = true,
       readOnly = true,
       allowEmptyValue = true,
       name = "inputEx",
       example = "10",
-      collectionFormat = "fmt") int input) {
+      collectionFormat = "csv") int[] inputArr) {
 
   }
 
diff --git 
a/integration-tests/it-consumer/src/main/java/org/apache/servicecomb/it/testcase/TestApiParam.java
 
b/integration-tests/it-consumer/src/main/java/org/apache/servicecomb/it/testcase/TestApiParam.java
index f3e120cd6..08dd41458 100644
--- 
a/integration-tests/it-consumer/src/main/java/org/apache/servicecomb/it/testcase/TestApiParam.java
+++ 
b/integration-tests/it-consumer/src/main/java/org/apache/servicecomb/it/testcase/TestApiParam.java
@@ -64,6 +64,13 @@ public void jaxrsQuery() {
     check("apiParamJaxrs", "query");
   }
 
+  @Test
+  public void jaxrsQueryArray() {
+    check("apiParamJaxrs", "queryArr", "query");
+
+    Assert.assertEquals("multi", ((QueryParameter) 
parameter).getCollectionFormat());
+  }
+
   @Test
   public void jaxrsHeader() {
     check("apiParamJaxrs", "header");
@@ -98,7 +105,19 @@ public void springmvcQuery() {
     Assert.assertTrue(parameter.getAllowEmptyValue());
     Assert.assertEquals("inputEx", parameter.getName());
     Assert.assertEquals(10L, ((QueryParameter) parameter).getExample());
-    Assert.assertEquals("fmt", ((QueryParameter) 
parameter).getCollectionFormat());
+    Assert.assertNull(((QueryParameter) parameter).getCollectionFormat());
+  }
+
+  @Test
+  public void springmvcQueryArray() {
+    check("apiParamSpringmvc", "queryArr", "query");
+
+    Assert.assertTrue(parameter.getRequired());
+    Assert.assertTrue(parameter.isReadOnly());
+    Assert.assertTrue(parameter.getAllowEmptyValue());
+    Assert.assertEquals("inputEx", parameter.getName());
+    Assert.assertEquals("10", ((QueryParameter) parameter).getExample());
+    Assert.assertEquals("csv", ((QueryParameter) 
parameter).getCollectionFormat());
   }
 
   @Test
diff --git 
a/integration-tests/it-consumer/src/main/java/org/apache/servicecomb/it/testcase/TestDataTypePrimitive.java
 
b/integration-tests/it-consumer/src/main/java/org/apache/servicecomb/it/testcase/TestDataTypePrimitive.java
index bf0d693ef..54c6285cc 100644
--- 
a/integration-tests/it-consumer/src/main/java/org/apache/servicecomb/it/testcase/TestDataTypePrimitive.java
+++ 
b/integration-tests/it-consumer/src/main/java/org/apache/servicecomb/it/testcase/TestDataTypePrimitive.java
@@ -115,6 +115,19 @@
 
     // enum
     Color enumBody(Color color);
+
+    // query array
+    String queryArr(String[] queryArr);
+
+    String queryArrCSV(String[] queryArr);
+
+    String queryArrSSV(String[] queryArr);
+
+    String queryArrTSV(String[] queryArr);
+
+    String queryArrPIPES(String[] queryArr);
+
+    String queryArrMULTI(String[] queryArr);
   }
 
   private static Consumers<DataTypePojoIntf> consumersPojo = new 
Consumers<>("dataTypePojo", DataTypePojoIntf.class);
@@ -156,7 +169,6 @@ public void double_pojo_rt() {
         0.0);
   }
 
-
   @Test
   public void string_pojo_rt() {
     String expect = "serviceComb";
@@ -329,7 +341,7 @@ protected void intHeader_rt(Consumers<DataTypeRestIntf> 
consumers) {
     HttpHeaders headers = new HttpHeaders();
     headers.add("input", "10");
 
-    HttpEntity<?> entity = new HttpEntity<>(null, headers);
+    HttpEntity<?> entity = new HttpEntity<>(headers);
     ResponseEntity<Integer> response = consumers.getSCBRestTemplate()
         .exchange("/intHeader",
             HttpMethod.GET,
@@ -342,8 +354,7 @@ protected void doubleHeader_rt(Consumers<DataTypeRestIntf> 
consumers) {
     HttpHeaders headers = new HttpHeaders();
     headers.add("input", "10.2");
 
-    @SuppressWarnings("rawtypes")
-    HttpEntity entity = new HttpEntity<>(null, headers);
+    HttpEntity<?> entity = new HttpEntity<>(headers);
     ResponseEntity<Double> response = consumers.getSCBRestTemplate()
         .exchange("/doubleHeader",
             HttpMethod.GET,
@@ -362,7 +373,7 @@ protected void stringHeader_rt(Consumers<DataTypeRestIntf> 
consumers) {
     HttpHeaders headers = new HttpHeaders();
     headers.add("input", expect);
 
-    HttpEntity<?> entity = new HttpEntity<>(null, headers);
+    HttpEntity<?> entity = new HttpEntity<>(headers);
     ResponseEntity<String> response = consumers.getSCBRestTemplate()
         .exchange("/stringHeader",
             HttpMethod.GET,
@@ -401,7 +412,7 @@ void intCookie_rt(Consumers<DataTypeRestIntf> consumers) {
     HttpHeaders headers = new HttpHeaders();
     headers.add("Cookie", "input=10");
 
-    HttpEntity<?> entity = new HttpEntity<>(null, headers);
+    HttpEntity<?> entity = new HttpEntity<>(headers);
     ResponseEntity<Integer> response = consumers.getSCBRestTemplate()
         .exchange("/intCookie",
             HttpMethod.GET,
@@ -414,8 +425,7 @@ void doubleCookie_rt(Consumers<DataTypeRestIntf> consumers) 
{
     HttpHeaders headers = new HttpHeaders();
     headers.add("Cookie", "input=10.2");
 
-    @SuppressWarnings("rawtypes")
-    HttpEntity entity = new HttpEntity<>(null, headers);
+    HttpEntity<?> entity = new HttpEntity<>(headers);
     ResponseEntity<Double> response = consumers.getSCBRestTemplate()
         .exchange("/doubleCookie",
             HttpMethod.GET,
@@ -434,7 +444,7 @@ void stringCookie_rt(Consumers<DataTypeRestIntf> consumers) 
{
     HttpHeaders headers = new HttpHeaders();
     headers.add("Cookie", "input=" + expect);
 
-    HttpEntity<?> entity = new HttpEntity<>(null, headers);
+    HttpEntity<?> entity = new HttpEntity<>(headers);
     ResponseEntity<String> response = consumers.getSCBRestTemplate()
         .exchange("/stringCookie",
             HttpMethod.GET,
@@ -463,10 +473,10 @@ public void stringForm_jaxrs_intf() {
   public void intForm_jaxrs_rt() {
     Map<String, Integer> map = new HashMap<>();
     map.put("input", 10);
-    HttpEntity<Map<String, Integer>> formEntiry = new HttpEntity<>(map);
+    HttpEntity<Map<String, Integer>> formEntity = new HttpEntity<>(map);
 
     assertEquals(10,
-        (int) consumersJaxrs.getSCBRestTemplate().postForEntity("/intForm", 
formEntiry, int.class).getBody());
+        (int) consumersJaxrs.getSCBRestTemplate().postForEntity("/intForm", 
formEntity, int.class).getBody());
     //just use map is ok
     assertEquals(10,
         (int) consumersJaxrs.getSCBRestTemplate().postForEntity("/intForm", 
map, int.class).getBody());
@@ -476,10 +486,10 @@ public void intForm_jaxrs_rt() {
   public void doubleForm_jaxrs_rt() {
     Map<String, Double> map = new HashMap<>();
     map.put("input", 10.2);
-    HttpEntity<Map<String, Double>> formEntiry = new HttpEntity<>(map);
+    HttpEntity<Map<String, Double>> formEntity = new HttpEntity<>(map);
 
     assertEquals(10.2,
-        consumersJaxrs.getSCBRestTemplate().postForEntity("/doubleForm", 
formEntiry, double.class).getBody(),
+        consumersJaxrs.getSCBRestTemplate().postForEntity("/doubleForm", 
formEntity, double.class).getBody(),
         0.0);
     //just use map is ok
     assertEquals(10.2,
@@ -491,11 +501,11 @@ public void stringForm_jaxrs_rt() {
     String expect = "serviceComb";
     Map<String, String> map = new HashMap<>();
     map.put("input", expect);
-    HttpEntity<Map<String, String>> formEntiry = new HttpEntity<>(map);
+    HttpEntity<Map<String, String>> formEntity = new HttpEntity<>(map);
 
     assertEquals(expect,
         consumersJaxrs.getSCBRestTemplate()
-            .postForEntity("/stringForm", formEntiry, String.class)
+            .postForEntity("/stringForm", formEntity, String.class)
             .getBody());
 
     //you can use another method to invoke it
@@ -742,20 +752,20 @@ public void stringForm_springmvc_intf() {
   public void intForm_springmvc_rt() {
     Map<String, Integer> map = new HashMap<>();
     map.put("input", 10);
-    HttpEntity<Map<String, Integer>> formEntiry = new HttpEntity<>(map);
+    HttpEntity<Map<String, Integer>> formEntity = new HttpEntity<>(map);
 
     assertEquals(10,
-        (int) 
consumersSpringmvc.getSCBRestTemplate().postForEntity("/intForm", formEntiry, 
int.class).getBody());
+        (int) 
consumersSpringmvc.getSCBRestTemplate().postForEntity("/intForm", formEntity, 
int.class).getBody());
   }
 
   @Test
   public void doubleForm_springmvc_rt() {
     Map<String, Double> map = new HashMap<>();
     map.put("input", 10.2);
-    HttpEntity<Map<String, Double>> formEntiry = new HttpEntity<>(map);
+    HttpEntity<Map<String, Double>> formEntity = new HttpEntity<>(map);
 
     assertEquals(10.2,
-        consumersSpringmvc.getSCBRestTemplate().postForEntity("/doubleForm", 
formEntiry, double.class)
+        consumersSpringmvc.getSCBRestTemplate().postForEntity("/doubleForm", 
formEntity, double.class)
             .getBody(), 0.0);
   }
 
@@ -764,10 +774,10 @@ public void stringForm_springmvc_rt() {
     String expect = "serviceComb";
     Map<String, String> map = new HashMap<>();
     map.put("input", expect);
-    HttpEntity<Map<String, String>> formEntiry = new HttpEntity<>(map);
+    HttpEntity<Map<String, String>> formEntity = new HttpEntity<>(map);
 
     assertEquals(expect,
-        consumersSpringmvc.getSCBRestTemplate().postForEntity("/stringForm", 
formEntiry, String.class)
+        consumersSpringmvc.getSCBRestTemplate().postForEntity("/stringForm", 
formEntity, String.class)
             .getBody());
 
     assertEquals(expect,
@@ -906,8 +916,7 @@ protected void floatHeader_rt(Consumers<DataTypeRestIntf> 
consumers) {
     HttpHeaders headers = new HttpHeaders();
     headers.add("input", "10.2f");
 
-    @SuppressWarnings("rawtypes")
-    HttpEntity entity = new HttpEntity<>(null, headers);
+    HttpEntity<?> entity = new HttpEntity<>(headers);
     ResponseEntity<Float> response = consumers.getSCBRestTemplate()
         .exchange("/floatHeader",
             HttpMethod.GET,
@@ -930,8 +939,7 @@ void floatCookie_rt(Consumers<DataTypeRestIntf> consumers) {
     HttpHeaders headers = new HttpHeaders();
     headers.add("Cookie", "input=10.2f");
 
-    @SuppressWarnings("rawtypes")
-    HttpEntity entity = new HttpEntity<>(null, headers);
+    HttpEntity<?> entity = new HttpEntity<>(headers);
     ResponseEntity<Float> response = consumers.getSCBRestTemplate()
         .exchange("/floatCookie",
             HttpMethod.GET,
@@ -949,10 +957,10 @@ public void floatForm_jaxrs_intf() {
   public void floatForm_jaxrs_rt() {
     Map<String, Float> map = new HashMap<>();
     map.put("input", 10.2f);
-    HttpEntity<Map<String, Float>> formEntiry = new HttpEntity<>(map);
+    HttpEntity<Map<String, Float>> formEntity = new HttpEntity<>(map);
 
     assertEquals(10.2f,
-        consumersJaxrs.getSCBRestTemplate().postForEntity("/floatForm", 
formEntiry, float.class).getBody(),
+        consumersJaxrs.getSCBRestTemplate().postForEntity("/floatForm", 
formEntity, float.class).getBody(),
         0.0f);
     //just use map is ok
     assertEquals(10.2f,
@@ -1032,10 +1040,10 @@ public void floatForm_springmvc_intf() {
   public void floatForm_springmvc_rt() {
     Map<String, Float> map = new HashMap<>();
     map.put("input", 10.2f);
-    HttpEntity<Map<String, Float>> formEntiry = new HttpEntity<>(map);
+    HttpEntity<Map<String, Float>> formEntity = new HttpEntity<>(map);
 
     assertEquals(10.2f,
-        consumersSpringmvc.getSCBRestTemplate().postForEntity("/floatForm", 
formEntiry, float.class)
+        consumersSpringmvc.getSCBRestTemplate().postForEntity("/floatForm", 
formEntity, float.class)
             .getBody(), 0.0f);
   }
 
@@ -1072,4 +1080,178 @@ public void enumBody_springmvc_rt() {
     assertEquals(Color.BLUE,
         consumersSpringmvc.getSCBRestTemplate().postForObject("/enumBody", 
Color.BLUE, Color.class));
   }
+
+  // query array
+  @Test
+  public void queryArr_springmvc_intf() {
+    // default
+    assertEquals("[a, b, c]3",
+        consumersSpringmvc.getIntf().queryArr(new String[] {"a", "b", "c"}));
+    assertEquals("[a, ,  , b, c]5",
+        consumersSpringmvc.getIntf().queryArr(new String[] {"a", "", " ", "b", 
"c"}));
+    // CSV
+    assertEquals("[a, b, c]3",
+        consumersSpringmvc.getIntf().queryArrCSV(new String[] {"a", "b", 
"c"}));
+    assertEquals("[a, b, , c]4",
+        consumersSpringmvc.getIntf().queryArrCSV(new String[] {null, "a", 
null, null, "b", null, "", null, "c", null}));
+    assertEquals("[a, ,  , b, c]5",
+        consumersSpringmvc.getIntf().queryArrCSV(new String[] {"a", "", " ", 
"b", "c"}));
+    // SSV
+    assertEquals("[a, b, c]3",
+        consumersSpringmvc.getIntf().queryArrSSV(new String[] {"a", "b", 
"c"}));
+    assertEquals("[a, b, , c]4",
+        consumersSpringmvc.getIntf().queryArrSSV(new String[] {null, "a", 
null, null, "b", null, "", null, "c", null}));
+    // TSV
+    assertEquals("[a, b, c]3",
+        consumersSpringmvc.getIntf().queryArrTSV(new String[] {"a", "b", 
"c"}));
+    assertEquals("[a, b, , c]4",
+        consumersSpringmvc.getIntf().queryArrTSV(new String[] {null, "a", 
null, null, "b", null, "", null, "c", null}));
+    assertEquals("[a, ,  , b, c]5",
+        consumersSpringmvc.getIntf().queryArrTSV(new String[] {"a", "", " ", 
"b", "c"}));
+    // PIPES
+    assertEquals("[a, b, c]3",
+        consumersSpringmvc.getIntf().queryArrPIPES(new String[] {"a", "b", 
"c"}));
+    assertEquals("[a, b, , c]4",
+        consumersSpringmvc.getIntf()
+            .queryArrPIPES(new String[] {null, "a", null, null, "b", null, "", 
null, "c", null}));
+    assertEquals("[a, ,  , b, c]5",
+        consumersSpringmvc.getIntf().queryArrPIPES(new String[] {"a", "", " ", 
"b", "c"}));
+    // MULTI
+    assertEquals("[a, b, c]3",
+        consumersSpringmvc.getIntf().queryArrMULTI(new String[] {"a", "b", 
"c"}));
+    assertEquals("[a, b, , c]4",
+        consumersSpringmvc.getIntf()
+            .queryArrMULTI(new String[] {null, "a", null, null, "b", null, "", 
null, "c", null}));
+    assertEquals("[a, ,  , b, c]5",
+        consumersSpringmvc.getIntf().queryArrMULTI(new String[] {"a", "", " ", 
"b", "c"}));
+  }
+
+  @Test
+  public void queryArr_springmvc_rt() {
+    // default
+    assertEquals("[a, b, c]3",
+        consumersSpringmvc.getSCBRestTemplate()
+            .getForObject("/queryArr?queryArr=a&queryArr=b&queryArr=c", 
String.class));
+    assertEquals("[a, ,  , b, c]5",
+        consumersSpringmvc.getSCBRestTemplate()
+            .getForObject("/queryArr?queryArr=a&queryArr=&queryArr= 
&queryArr=b&queryArr=c", String.class));
+    // csv
+    assertEquals("[a, b, c]3",
+        consumersSpringmvc.getSCBRestTemplate()
+            .getForObject("/queryArrCSV?queryArr=a,b,c", String.class));
+    assertEquals("[a, ,  , b, c]5",
+        consumersSpringmvc.getSCBRestTemplate()
+            .getForObject("/queryArrCSV?queryArr=a,, ,b,c", String.class));
+    // ssv
+    assertEquals("[a, b, c]3",
+        consumersSpringmvc.getSCBRestTemplate()
+            .getForObject("/queryArrSSV?queryArr=a b c", String.class));
+    // tsv
+    assertEquals("[a, b, c]3",
+        consumersSpringmvc.getSCBRestTemplate()
+            .getForObject("/queryArrTSV?queryArr=a\tb\tc", String.class));
+    assertEquals("[a, ,  , b, c]5",
+        consumersSpringmvc.getSCBRestTemplate()
+            .getForObject("/queryArrTSV?queryArr=a\t\t \tb\tc", String.class));
+    // pipes
+    assertEquals("[a, b, c]3",
+        consumersSpringmvc.getSCBRestTemplate()
+            .getForObject("/queryArrPIPES?queryArr=a|b|c", String.class));
+    assertEquals("[a, ,  , b, c]5",
+        consumersSpringmvc.getSCBRestTemplate()
+            .getForObject("/queryArrPIPES?queryArr=a|| |b|c", String.class));
+    // multi
+    assertEquals("[a, b, c]3",
+        consumersSpringmvc.getSCBRestTemplate()
+            .getForObject("/queryArrMULTI?queryArr=a&queryArr=b&queryArr=c", 
String.class));
+    assertEquals("[a, ,  , b, c]5",
+        consumersSpringmvc.getSCBRestTemplate()
+            .getForObject("/queryArrMULTI?queryArr=a&queryArr=&queryArr= 
&queryArr=b&queryArr=c", String.class));
+  }
+
+  @Test
+  public void queryArr_jaxrs_intf() {
+    assertEquals("[a, b, c]3",
+        consumersJaxrs.getIntf().queryArr(new String[] {"a", "b", "c"}));
+    assertEquals("[a, b, , c]4",
+        consumersJaxrs.getIntf().queryArr(new String[] {null, "a", null, null, 
"b", null, "", null, "c", null}));
+    assertEquals("[a, ,  , b, c]5",
+        consumersJaxrs.getIntf().queryArr(new String[] {"a", "", " ", "b", 
"c"}));
+
+    assertEquals("[a, b, c]3",
+        consumersJaxrs.getIntf().queryArrCSV(new String[] {"a", "b", "c"}));
+    assertEquals("[a, b, , c]4",
+        consumersJaxrs.getIntf().queryArrCSV(new String[] {null, "a", null, 
null, "b", null, "", null, "c", null}));
+    assertEquals("[a, ,  , b, c]5",
+        consumersJaxrs.getIntf().queryArrCSV(new String[] {"a", "", " ", "b", 
"c"}));
+
+    assertEquals("[a, b, c]3",
+        consumersJaxrs.getIntf().queryArrSSV(new String[] {"a", "b", "c"}));
+    assertEquals("[a, b, , c]4",
+        consumersJaxrs.getIntf().queryArrSSV(new String[] {null, "a", null, 
null, "b", null, "", null, "c", null}));
+
+    assertEquals("[a, b, c]3",
+        consumersJaxrs.getIntf().queryArrTSV(new String[] {"a", "b", "c"}));
+    assertEquals("[a, b, , c]4",
+        consumersJaxrs.getIntf().queryArrTSV(new String[] {null, "a", null, 
null, "b", null, "", null, "c", null}));
+    assertEquals("[a, ,  , b, c]5",
+        consumersJaxrs.getIntf().queryArrTSV(new String[] {"a", "", " ", "b", 
"c"}));
+
+    assertEquals("[a, b, c]3",
+        consumersJaxrs.getIntf().queryArrPIPES(new String[] {"a", "b", "c"}));
+    assertEquals("[a, b, , c]4",
+        consumersJaxrs.getIntf().queryArrPIPES(new String[] {null, "a", null, 
null, "b", null, "", null, "c", null}));
+    assertEquals("[a, ,  , b, c]5",
+        consumersJaxrs.getIntf().queryArrPIPES(new String[] {"a", "", " ", 
"b", "c"}));
+
+    assertEquals("[a, b, c]3",
+        consumersJaxrs.getIntf().queryArrMULTI(new String[] {"a", "b", "c"}));
+    assertEquals("[a, b, , c]4",
+        consumersJaxrs.getIntf().queryArrMULTI(new String[] {null, "a", null, 
null, "b", null, "", null, "c", null}));
+    assertEquals("[a, ,  , b, c]5",
+        consumersJaxrs.getIntf().queryArrMULTI(new String[] {"a", "", " ", 
"b", "c"}));
+  }
+
+  @Test
+  public void queryArr_jaxrs_rt() {
+    // default
+    assertEquals("[a, b, c]3",
+        consumersJaxrs.getSCBRestTemplate()
+            .getForObject("/queryArr?queryArr=a&queryArr=b&queryArr=c", 
String.class));
+    assertEquals("[a, ,  , b, c]5",
+        consumersJaxrs.getSCBRestTemplate()
+            .getForObject("/queryArr?queryArr=a&queryArr=&queryArr= 
&queryArr=b&queryArr=c", String.class));
+    // csv
+    assertEquals("[a, b, c]3",
+        consumersJaxrs.getSCBRestTemplate()
+            .getForObject("/queryArrCSV?queryArr=a,b,c", String.class));
+    assertEquals("[a, ,  , b, c]5",
+        consumersJaxrs.getSCBRestTemplate()
+            .getForObject("/queryArrCSV?queryArr=a,, ,b,c", String.class));
+    // ssv
+    assertEquals("[a, b, c]3",
+        consumersJaxrs.getSCBRestTemplate()
+            .getForObject("/queryArrSSV?queryArr=a b c", String.class));
+    // tsv
+    assertEquals("[a, b, c]3",
+        consumersJaxrs.getSCBRestTemplate()
+            .getForObject("/queryArrTSV?queryArr=a\tb\tc", String.class));
+    assertEquals("[a, ,  , b, c]5",
+        consumersJaxrs.getSCBRestTemplate()
+            .getForObject("/queryArrTSV?queryArr=a\t\t \tb\tc", String.class));
+    // pipes
+    assertEquals("[a, b, c]3",
+        consumersJaxrs.getSCBRestTemplate()
+            .getForObject("/queryArrPIPES?queryArr=a|b|c", String.class));
+    assertEquals("[a, ,  , b, c]5",
+        consumersJaxrs.getSCBRestTemplate()
+            .getForObject("/queryArrPIPES?queryArr=a|| |b|c", String.class));
+    // multi
+    assertEquals("[a, b, c]3",
+        consumersJaxrs.getSCBRestTemplate()
+            .getForObject("/queryArrMULTI?queryArr=a&queryArr=b&queryArr=c", 
String.class));
+    assertEquals("[a, ,  , b, c]5",
+        consumersJaxrs.getSCBRestTemplate()
+            .getForObject("/queryArrMULTI?queryArr=a&queryArr=&queryArr= 
&queryArr=b&queryArr=c", String.class));
+  }
 }
diff --git 
a/integration-tests/it-producer/src/main/java/org/apache/servicecomb/it/schema/DataTypeJaxrsSchema.java
 
b/integration-tests/it-producer/src/main/java/org/apache/servicecomb/it/schema/DataTypeJaxrsSchema.java
index e36aca328..032a94b8f 100644
--- 
a/integration-tests/it-producer/src/main/java/org/apache/servicecomb/it/schema/DataTypeJaxrsSchema.java
+++ 
b/integration-tests/it-producer/src/main/java/org/apache/servicecomb/it/schema/DataTypeJaxrsSchema.java
@@ -16,6 +16,8 @@
  */
 package org.apache.servicecomb.it.schema;
 
+import java.util.Arrays;
+
 import javax.ws.rs.CookieParam;
 import javax.ws.rs.FormParam;
 import javax.ws.rs.GET;
@@ -28,6 +30,8 @@
 import org.apache.servicecomb.foundation.test.scaffolding.model.Color;
 import org.apache.servicecomb.provider.rest.common.RestSchema;
 
+import io.swagger.annotations.ApiParam;
+
 @RestSchema(schemaId = "dataTypeJaxrs")
 @Path("/v1/dataTypeJaxrs")
 public class DataTypeJaxrsSchema {
@@ -207,4 +211,41 @@ public float floatAdd(@QueryParam("num1") float num1, 
@QueryParam("num2") float
   public Color enumBody(Color color) {
     return color;
   }
+
+  // query array
+  @Path("queryArr")
+  @GET
+  public String queryArr(@QueryParam("queryArr") String[] queryArr) {
+    return Arrays.toString(queryArr) + queryArr.length;
+  }
+
+  @Path("queryArrCSV")
+  @GET
+  public String queryArrCSV(@ApiParam(collectionFormat = "csv") 
@QueryParam("queryArr") String[] queryArr) {
+    return Arrays.toString(queryArr) + queryArr.length;
+  }
+
+  @Path("queryArrSSV")
+  @GET
+  public String queryArrSSV(@ApiParam(collectionFormat = "ssv") 
@QueryParam("queryArr") String[] queryArr) {
+    return Arrays.toString(queryArr) + queryArr.length;
+  }
+
+  @Path("queryArrTSV")
+  @GET
+  public String queryArrTSV(@ApiParam(collectionFormat = "tsv") 
@QueryParam("queryArr") String[] queryArr) {
+    return Arrays.toString(queryArr) + queryArr.length;
+  }
+
+  @Path("queryArrPIPES")
+  @GET
+  public String queryArrPIPES(@ApiParam(collectionFormat = "pipes") 
@QueryParam("queryArr") String[] queryArr) {
+    return Arrays.toString(queryArr) + queryArr.length;
+  }
+
+  @Path("queryArrMULTI")
+  @GET
+  public String queryArrMULTI(@ApiParam(collectionFormat = "multi") 
@QueryParam("queryArr") String[] queryArr) {
+    return Arrays.toString(queryArr) + queryArr.length;
+  }
 }
diff --git 
a/integration-tests/it-producer/src/main/java/org/apache/servicecomb/it/schema/DataTypeSpringmvcSchema.java
 
b/integration-tests/it-producer/src/main/java/org/apache/servicecomb/it/schema/DataTypeSpringmvcSchema.java
index af72afb06..7c800b022 100644
--- 
a/integration-tests/it-producer/src/main/java/org/apache/servicecomb/it/schema/DataTypeSpringmvcSchema.java
+++ 
b/integration-tests/it-producer/src/main/java/org/apache/servicecomb/it/schema/DataTypeSpringmvcSchema.java
@@ -16,6 +16,8 @@
  */
 package org.apache.servicecomb.it.schema;
 
+import java.util.Arrays;
+
 import org.apache.servicecomb.foundation.test.scaffolding.model.Color;
 import org.apache.servicecomb.provider.rest.common.RestSchema;
 import org.springframework.web.bind.annotation.CookieValue;
@@ -28,6 +30,8 @@
 import org.springframework.web.bind.annotation.RequestMapping;
 import org.springframework.web.bind.annotation.RequestParam;
 
+import io.swagger.annotations.ApiParam;
+
 @RestSchema(schemaId = "dataTypeSpringmvc")
 @RequestMapping(path = "/v1/dataTypeSpringmvc")
 public class DataTypeSpringmvcSchema {
@@ -98,7 +102,6 @@ public String stringForm(@RequestAttribute("input") String 
input) {
     return input;
   }
 
-
   @GetMapping(path = "stringConcat")
   public String stringConcat(String str1, String str2) {
     return str1 + str2;
@@ -180,4 +183,35 @@ public float floatAdd(float num1, float num2) {
   public Color enumBody(@RequestBody Color color) {
     return color;
   }
+
+  // query array
+  @GetMapping("queryArr")
+  public String queryArr(@RequestParam("queryArr") String[] queryArr) {
+    return Arrays.toString(queryArr) + queryArr.length;
+  }
+
+  @GetMapping("queryArrCSV")
+  public String queryArrCSV(@ApiParam(collectionFormat = "csv") 
@RequestParam("queryArr") String[] queryArr) {
+    return Arrays.toString(queryArr) + queryArr.length;
+  }
+
+  @GetMapping("queryArrSSV")
+  public String queryArrSSV(@ApiParam(collectionFormat = "ssv") 
@RequestParam("queryArr") String[] queryArr) {
+    return Arrays.toString(queryArr) + queryArr.length;
+  }
+
+  @GetMapping("queryArrTSV")
+  public String queryArrTSV(@ApiParam(collectionFormat = "tsv") 
@RequestParam("queryArr") String[] queryArr) {
+    return Arrays.toString(queryArr) + queryArr.length;
+  }
+
+  @GetMapping("queryArrPIPES")
+  public String queryArrPIPES(@ApiParam(collectionFormat = "pipes") 
@RequestParam("queryArr") String[] queryArr) {
+    return Arrays.toString(queryArr) + queryArr.length;
+  }
+
+  @GetMapping("queryArrMULTI")
+  public String queryArrMULTI(@ApiParam(collectionFormat = "multi") 
@RequestParam("queryArr") String[] queryArr) {
+    return Arrays.toString(queryArr) + queryArr.length;
+  }
 }
diff --git 
a/swagger/swagger-generator/generator-core/src/main/java/org/apache/servicecomb/swagger/converter/property/SwaggerParamCollectionFormat.java
 
b/swagger/swagger-generator/generator-core/src/main/java/org/apache/servicecomb/swagger/converter/property/SwaggerParamCollectionFormat.java
new file mode 100644
index 000000000..3ae7aaac7
--- /dev/null
+++ 
b/swagger/swagger-generator/generator-core/src/main/java/org/apache/servicecomb/swagger/converter/property/SwaggerParamCollectionFormat.java
@@ -0,0 +1,102 @@
+/*
+ * 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.property;
+
+import java.util.Collection;
+
+public enum SwaggerParamCollectionFormat {
+  CSV("csv", ","),
+  SSV("ssv", " "),
+  TSV("tsv", "\t"),
+  PIPES("pipes", "|") {
+    @Override
+    public String[] splitParam(String rawParam) {
+      if (null == rawParam) {
+        return new String[0];
+      }
+      return rawParam.split("\\|", -1);
+    }
+  },
+  MULTI("multi", null) {
+    /**
+     * In fact, {@link SwaggerParamCollectionFormat#MULTI#splitParam(String)} 
of {@link SwaggerParamCollectionFormat#MULTI}
+     * should never be invoked. We just override this method to ensure it does 
not throw exception.
+     */
+    @Override
+    public String[] splitParam(String rawParam) {
+      if (null == rawParam) {
+        return new String[0];
+      }
+      return new String[] {rawParam};
+    }
+  };
+
+  final private String collectionFormat;
+
+  final private String separator;
+
+  SwaggerParamCollectionFormat(String collectionFormat, String separator) {
+    this.collectionFormat = collectionFormat;
+    this.separator = separator;
+  }
+
+  public String getCollectionFormat() {
+    return collectionFormat;
+  }
+
+  public String getSeparator() {
+    return separator;
+  }
+
+  public String[] splitParam(String rawParam) {
+    if (null == rawParam) {
+      return new String[0];
+    }
+    return rawParam.split(separator, -1);
+  }
+
+  /**
+   * Join params with {@link #separator}.
+   * Null element will be ignored since {@code null} cannot be described in 
query array param.
+   *
+   * @return joined params, or return {@code null} if {@code params} is null 
or all elements of {@code params} are null.
+   */
+  public String joinParam(Collection<?> params) {
+    if (null == params || params.isEmpty()) {
+      return null;
+    }
+
+    StringBuilder paramBuilder = new StringBuilder();
+    int nullCount = 0;
+    for (Object param : params) {
+      if (param == null) {
+        nullCount++;
+        continue;
+      }
+
+      paramBuilder.append(param).append(separator);
+    }
+    if (nullCount == params.size()) {
+      return null;
+    }
+
+    paramBuilder.setLength(paramBuilder.length() - 1);
+
+    return paramBuilder.toString();
+  }
+}
diff --git 
a/swagger/swagger-generator/generator-core/src/test/java/org/apache/servicecomb/swagger/converter/property/SwaggerParamCollectionFormatTest.java
 
b/swagger/swagger-generator/generator-core/src/test/java/org/apache/servicecomb/swagger/converter/property/SwaggerParamCollectionFormatTest.java
new file mode 100644
index 000000000..5b42cd506
--- /dev/null
+++ 
b/swagger/swagger-generator/generator-core/src/test/java/org/apache/servicecomb/swagger/converter/property/SwaggerParamCollectionFormatTest.java
@@ -0,0 +1,200 @@
+/*
+ * 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.property;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNull;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.List;
+
+import org.hamcrest.Matchers;
+import org.junit.Assert;
+import org.junit.Test;
+
+public class SwaggerParamCollectionFormatTest {
+  @Test
+  public void splitParamNormal() {
+    Assert.assertThat(SwaggerParamCollectionFormat.CSV.splitParam("a,b,c"),
+        Matchers.arrayContaining("a", "b", "c"));
+    Assert.assertThat(SwaggerParamCollectionFormat.SSV.splitParam("10 11 12"),
+        Matchers.arrayContaining("10", "11", "12"));
+    Assert.assertThat(SwaggerParamCollectionFormat.TSV.splitParam("a\tb\tc"),
+        Matchers.arrayContaining("a", "b", "c"));
+    Assert.assertThat(SwaggerParamCollectionFormat.PIPES.splitParam("a|b|c"),
+        Matchers.arrayContaining("a", "b", "c"));
+  }
+
+  @Test
+  public void splitParamMulti() {
+    Assert.assertThat(SwaggerParamCollectionFormat.MULTI.splitParam("a,b,c"),
+        Matchers.arrayContaining("a,b,c"));
+  }
+
+  @Test
+  public void splitParam_SingleElement() {
+    Assert.assertThat(SwaggerParamCollectionFormat.CSV.splitParam("a"),
+        Matchers.arrayContaining("a"));
+    Assert.assertThat(SwaggerParamCollectionFormat.SSV.splitParam("a"),
+        Matchers.arrayContaining("a"));
+    Assert.assertThat(SwaggerParamCollectionFormat.TSV.splitParam("a"),
+        Matchers.arrayContaining("a"));
+    Assert.assertThat(SwaggerParamCollectionFormat.PIPES.splitParam("a"),
+        Matchers.arrayContaining("a"));
+    Assert.assertThat(SwaggerParamCollectionFormat.MULTI.splitParam("a"),
+        Matchers.arrayContaining("a"));
+  }
+
+  @Test
+  public void splitParam_NullElement() {
+    Assert.assertThat(SwaggerParamCollectionFormat.CSV.splitParam(null),
+        Matchers.emptyArray());
+    Assert.assertThat(SwaggerParamCollectionFormat.SSV.splitParam(null),
+        Matchers.emptyArray());
+    Assert.assertThat(SwaggerParamCollectionFormat.TSV.splitParam(null),
+        Matchers.emptyArray());
+    Assert.assertThat(SwaggerParamCollectionFormat.PIPES.splitParam(null),
+        Matchers.emptyArray());
+    Assert.assertThat(SwaggerParamCollectionFormat.MULTI.splitParam(null),
+        Matchers.emptyArray());
+  }
+
+  @Test
+  public void splitParam_BlankElement() {
+    Assert.assertThat(SwaggerParamCollectionFormat.CSV.splitParam(""),
+        Matchers.arrayContaining(""));
+    Assert.assertThat(SwaggerParamCollectionFormat.SSV.splitParam(""),
+        Matchers.arrayContaining(""));
+    Assert.assertThat(SwaggerParamCollectionFormat.TSV.splitParam(""),
+        Matchers.arrayContaining(""));
+    Assert.assertThat(SwaggerParamCollectionFormat.PIPES.splitParam(""),
+        Matchers.arrayContaining(""));
+    Assert.assertThat(SwaggerParamCollectionFormat.MULTI.splitParam(""),
+        Matchers.arrayContaining(""));
+
+    Assert.assertThat(SwaggerParamCollectionFormat.CSV.splitParam("a,,b"),
+        Matchers.arrayContaining("a", "", "b"));
+    Assert.assertThat(SwaggerParamCollectionFormat.SSV.splitParam("a  b"),
+        Matchers.arrayContaining("a", "", "b"));
+    Assert.assertThat(SwaggerParamCollectionFormat.TSV.splitParam("a\t\tb"),
+        Matchers.arrayContaining("a", "", "b"));
+    Assert.assertThat(SwaggerParamCollectionFormat.PIPES.splitParam("a||b"),
+        Matchers.arrayContaining("a", "", "b"));
+
+    Assert.assertThat(SwaggerParamCollectionFormat.CSV.splitParam("a,,"),
+        Matchers.arrayContaining("a", "", ""));
+    Assert.assertThat(SwaggerParamCollectionFormat.SSV.splitParam("a  "),
+        Matchers.arrayContaining("a", "", ""));
+    Assert.assertThat(SwaggerParamCollectionFormat.TSV.splitParam("a\t\t"),
+        Matchers.arrayContaining("a", "", ""));
+    String[] actual = SwaggerParamCollectionFormat.PIPES.splitParam("a||");
+    Assert.assertThat(Arrays.toString(actual), actual,
+        Matchers.arrayContaining("a", "", ""));
+
+    Assert.assertThat(SwaggerParamCollectionFormat.CSV.splitParam(",,b"),
+        Matchers.arrayContaining("", "", "b"));
+    Assert.assertThat(SwaggerParamCollectionFormat.SSV.splitParam("  b"),
+        Matchers.arrayContaining("", "", "b"));
+    Assert.assertThat(SwaggerParamCollectionFormat.TSV.splitParam("\t\tb"),
+        Matchers.arrayContaining("", "", "b"));
+    Assert.assertThat(SwaggerParamCollectionFormat.PIPES.splitParam("||b"),
+        Matchers.arrayContaining("", "", "b"));
+  }
+
+  @Test
+  public void joinNormal() {
+    List<String> params = Arrays.asList("a", "b", "c");
+    assertEquals("a,b,c", SwaggerParamCollectionFormat.CSV.joinParam(params));
+    assertEquals("a b c", SwaggerParamCollectionFormat.SSV.joinParam(params));
+    assertEquals("a\tb\tc", 
SwaggerParamCollectionFormat.TSV.joinParam(params));
+    assertEquals("a|b|c", 
SwaggerParamCollectionFormat.PIPES.joinParam(params));
+  }
+
+  @Test
+  public void join_SingleElement() {
+    List<String> params = Collections.singletonList("a");
+    assertEquals("a", SwaggerParamCollectionFormat.CSV.joinParam(params));
+    assertEquals("a", SwaggerParamCollectionFormat.SSV.joinParam(params));
+    assertEquals("a", SwaggerParamCollectionFormat.TSV.joinParam(params));
+    assertEquals("a", SwaggerParamCollectionFormat.PIPES.joinParam(params));
+  }
+
+  @Test
+  public void join_EmptyArray() {
+    
Assert.assertNull(SwaggerParamCollectionFormat.CSV.joinParam(Collections.EMPTY_LIST));
+  }
+
+  @Test
+  public void join_NullAndBlankElement() {
+    
Assert.assertNull(SwaggerParamCollectionFormat.CSV.joinParam(Collections.singletonList(null)));
+
+    assertEquals("", 
SwaggerParamCollectionFormat.CSV.joinParam(Collections.singleton("")));
+    assertEquals("a,,b,c", 
SwaggerParamCollectionFormat.CSV.joinParam(Arrays.asList("a", "", "b", "c")));
+    assertEquals("a  b c", 
SwaggerParamCollectionFormat.SSV.joinParam(Arrays.asList("a", "", "b", "c")));
+    assertEquals("a\t\tb\tc", 
SwaggerParamCollectionFormat.TSV.joinParam(Arrays.asList("a", "", "b", "c")));
+    assertEquals("a||b|c", 
SwaggerParamCollectionFormat.PIPES.joinParam(Arrays.asList("a", "", "b", "c")));
+
+    assertEquals("a,b,,c",
+        SwaggerParamCollectionFormat.CSV
+            .joinParam(Arrays.asList(null, "a", null, "b", null, "", null, 
null, "c", null)));
+    assertEquals("a b  c",
+        SwaggerParamCollectionFormat.SSV
+            .joinParam(Arrays.asList(null, "a", null, "b", null, "", null, 
null, "c", null)));
+    assertEquals("a\tb\t\tc",
+        SwaggerParamCollectionFormat.TSV
+            .joinParam(Arrays.asList(null, "a", null, "b", null, "", null, 
null, "c", null)));
+    assertEquals("a|b||c",
+        SwaggerParamCollectionFormat.PIPES
+            .joinParam(Arrays.asList(null, "a", null, "b", null, "", null, 
null, "c", null)));
+
+    assertEquals("a,b,,c",
+        SwaggerParamCollectionFormat.CSV
+            .joinParam(Arrays.asList(null, null, "a", null, "b", null, "", 
null, null, "c", null, null)));
+    assertEquals("a b  c",
+        SwaggerParamCollectionFormat.SSV
+            .joinParam(Arrays.asList(null, null, "a", null, "b", null, "", 
null, null, "c", null, null)));
+    assertEquals("a\tb\t\tc",
+        SwaggerParamCollectionFormat.TSV
+            .joinParam(Arrays.asList(null, null, "a", null, "b", null, "", 
null, null, "c", null, null)));
+    assertEquals("a|b||c",
+        SwaggerParamCollectionFormat.PIPES
+            .joinParam(Arrays.asList(null, null, "a", null, "b", null, "", 
null, null, "c", null, null)));
+  }
+
+  @Test
+  public void join_NullArray() {
+    assertNull(SwaggerParamCollectionFormat.CSV.joinParam(null));
+  }
+
+  /**
+   * In fact, the {@link SwaggerParamCollectionFormat#joinParam(Collection)} 
of {@link SwaggerParamCollectionFormat#MULTI}
+   * should never be invoked.
+   * This test is just for ensuring the method does not throw exception.
+   */
+  @Test
+  public void joinMulti() {
+    SwaggerParamCollectionFormat.MULTI.joinParam(Arrays.asList("a", "b", "c"));
+    
SwaggerParamCollectionFormat.MULTI.joinParam(Collections.singletonList("a"));
+    assertNull(SwaggerParamCollectionFormat.MULTI.joinParam(new 
ArrayList<String>()));
+    
assertNull(SwaggerParamCollectionFormat.MULTI.joinParam(Collections.singleton(null)));
+    assertNull(SwaggerParamCollectionFormat.MULTI.joinParam(null));
+  }
+}
\ No newline at end of file


 

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


> support swagger "collection-format" feature
> -------------------------------------------
>
>                 Key: SCB-928
>                 URL: https://issues.apache.org/jira/browse/SCB-928
>             Project: Apache ServiceComb
>          Issue Type: Task
>          Components: Java-Chassis
>            Reporter: wujimin
>            Assignee: YaoHaishi
>            Priority: Major
>




--
This message was sent by Atlassian JIRA
(v7.6.3#76005)

Reply via email to