This is an automated email from the ASF dual-hosted git repository.

wujimin pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/servicecomb-java-chassis.git


The following commit(s) were added to refs/heads/master by this push:
     new 5507eec  [SCB-1961] add rest server codec filter
5507eec is described below

commit 5507eec39888b5eedf0f4452dcdb6574787a7db8
Author: wujimin <wuji...@huawei.com>
AuthorDate: Sat May 30 16:20:13 2020 +0800

    [SCB-1961] add rest server codec filter
---
 .../common/rest/AbstractRestInvocation.java        |  14 +-
 .../common/rest/filter/RestFilterProvider.java     |  32 ++-
 .../rest/filter/inner/RestServerCodecFilter.java   | 130 ++++++++++++
 .../rest/filter/inner/ServerRestArgsFilter.java    |  15 +-
 ...g.apache.servicecomb.core.filter.FilterProvider |  18 ++
 .../rest/RestProducerInvocationCreatorTest.java    |   4 +-
 .../filter/inner/RestServerCodecFilterTest.java    | 221 +++++++++++++++++++++
 .../apache/servicecomb/core/filter/FilterMeta.java |  11 +
 .../{FilterMeta.java => FilterProvider.java}       |  22 +-
 9 files changed, 403 insertions(+), 64 deletions(-)

diff --git 
a/common/common-rest/src/main/java/org/apache/servicecomb/common/rest/AbstractRestInvocation.java
 
b/common/common-rest/src/main/java/org/apache/servicecomb/common/rest/AbstractRestInvocation.java
index 92bac2f..f0bc2ca 100644
--- 
a/common/common-rest/src/main/java/org/apache/servicecomb/common/rest/AbstractRestInvocation.java
+++ 
b/common/common-rest/src/main/java/org/apache/servicecomb/common/rest/AbstractRestInvocation.java
@@ -21,7 +21,6 @@ import java.nio.charset.StandardCharsets;
 import java.util.Collections;
 import java.util.List;
 import java.util.Map;
-import java.util.Map.Entry;
 import java.util.concurrent.CompletableFuture;
 
 import javax.ws.rs.core.HttpHeaders;
@@ -33,6 +32,7 @@ import 
org.apache.servicecomb.common.rest.codec.produce.ProduceProcessorManager;
 import org.apache.servicecomb.common.rest.definition.RestOperationMeta;
 import org.apache.servicecomb.common.rest.filter.HttpServerFilter;
 import 
org.apache.servicecomb.common.rest.filter.HttpServerFilterBeforeSendResponseExecutor;
+import org.apache.servicecomb.common.rest.filter.inner.RestServerCodecFilter;
 import org.apache.servicecomb.common.rest.locator.OperationLocator;
 import org.apache.servicecomb.common.rest.locator.ServicePathManager;
 import org.apache.servicecomb.core.Const;
@@ -268,16 +268,8 @@ public abstract class AbstractRestInvocation {
 
   @SuppressWarnings("deprecation")
   protected void sendResponse(Response response) {
-    if (response.getHeaders().getHeaderMap() != null) {
-      for (Entry<String, List<Object>> entry : 
response.getHeaders().getHeaderMap().entrySet()) {
-        for (Object value : entry.getValue()) {
-          if (!entry.getKey().equalsIgnoreCase(HttpHeaders.CONTENT_LENGTH)
-              && !entry.getKey().equalsIgnoreCase("Transfer-Encoding")) {
-            responseEx.addHeader(entry.getKey(), String.valueOf(value));
-          }
-        }
-      }
-    }
+    
RestServerCodecFilter.copyHeadersToHttpResponse(response.getHeaders().getHeaderMap(),
 responseEx);
+
     responseEx.setStatus(response.getStatusCode(), response.getReasonPhrase());
     responseEx.setAttribute(RestConst.INVOCATION_HANDLER_RESPONSE, response);
     responseEx.setAttribute(RestConst.INVOCATION_HANDLER_PROCESSOR, 
produceProcessor);
diff --git 
a/core/src/main/java/org/apache/servicecomb/core/filter/FilterMeta.java 
b/common/common-rest/src/main/java/org/apache/servicecomb/common/rest/filter/RestFilterProvider.java
similarity index 60%
copy from core/src/main/java/org/apache/servicecomb/core/filter/FilterMeta.java
copy to 
common/common-rest/src/main/java/org/apache/servicecomb/common/rest/filter/RestFilterProvider.java
index 680ba45..b91a96e 100644
--- a/core/src/main/java/org/apache/servicecomb/core/filter/FilterMeta.java
+++ 
b/common/common-rest/src/main/java/org/apache/servicecomb/common/rest/filter/RestFilterProvider.java
@@ -14,26 +14,20 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
+package org.apache.servicecomb.common.rest.filter;
 
-package org.apache.servicecomb.core.filter;
+import java.util.Arrays;
+import java.util.List;
 
-import java.lang.annotation.Documented;
-import java.lang.annotation.ElementType;
-import java.lang.annotation.Inherited;
-import java.lang.annotation.Retention;
-import java.lang.annotation.RetentionPolicy;
-import java.lang.annotation.Target;
+import org.apache.servicecomb.common.rest.filter.inner.RestServerCodecFilter;
+import org.apache.servicecomb.core.filter.Filter;
+import org.apache.servicecomb.core.filter.FilterProvider;
 
-@Documented
-@Retention(RetentionPolicy.RUNTIME)
-@Target(ElementType.TYPE)
-@Inherited
-public @interface FilterMeta {
-  String name();
-
-  /**
-   *
-   * @return true to use same instance for all filter chains
-   */
-  boolean shareable() default true;
+public class RestFilterProvider implements FilterProvider {
+  @Override
+  public List<Class<? extends Filter>> getFilters() {
+    return Arrays.asList(
+        RestServerCodecFilter.class
+    );
+  }
 }
diff --git 
a/common/common-rest/src/main/java/org/apache/servicecomb/common/rest/filter/inner/RestServerCodecFilter.java
 
b/common/common-rest/src/main/java/org/apache/servicecomb/common/rest/filter/inner/RestServerCodecFilter.java
new file mode 100644
index 0000000..7842302
--- /dev/null
+++ 
b/common/common-rest/src/main/java/org/apache/servicecomb/common/rest/filter/inner/RestServerCodecFilter.java
@@ -0,0 +1,130 @@
+/*
+ * 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.filter.inner;
+
+import static com.google.common.net.HttpHeaders.CONTENT_LENGTH;
+import static com.google.common.net.HttpHeaders.TRANSFER_ENCODING;
+import static javax.ws.rs.core.Response.Status.INTERNAL_SERVER_ERROR;
+import static 
org.apache.servicecomb.core.exception.Exceptions.exceptionToResponse;
+import static 
org.apache.servicecomb.swagger.invocation.InvocationType.PRODUCER;
+
+import java.util.List;
+import java.util.Map;
+import java.util.Map.Entry;
+import java.util.concurrent.CompletableFuture;
+
+import javax.servlet.http.Part;
+
+import org.apache.servicecomb.common.rest.HttpTransportContext;
+import org.apache.servicecomb.common.rest.RestConst;
+import org.apache.servicecomb.common.rest.codec.RestCodec;
+import org.apache.servicecomb.common.rest.codec.produce.ProduceProcessor;
+import org.apache.servicecomb.common.rest.definition.RestOperationMeta;
+import org.apache.servicecomb.core.Invocation;
+import org.apache.servicecomb.core.definition.OperationMeta;
+import org.apache.servicecomb.core.filter.Filter;
+import org.apache.servicecomb.core.filter.FilterMeta;
+import org.apache.servicecomb.core.filter.FilterNode;
+import org.apache.servicecomb.foundation.common.utils.AsyncUtils;
+import org.apache.servicecomb.foundation.vertx.http.HttpServletRequestEx;
+import org.apache.servicecomb.foundation.vertx.http.HttpServletResponseEx;
+import org.apache.servicecomb.foundation.vertx.stream.BufferOutputStream;
+import org.apache.servicecomb.swagger.invocation.Response;
+
+import io.netty.buffer.Unpooled;
+
+@FilterMeta(name = "rest-server-codec", invocationType = PRODUCER)
+public class RestServerCodecFilter implements Filter {
+  @Override
+  public CompletableFuture<Response> onFilter(Invocation invocation, 
FilterNode nextNode) {
+    return CompletableFuture.completedFuture(invocation)
+        .thenCompose(this::decodeRequest)
+        .thenCompose(nextNode::onFilter)
+        .exceptionally(exception -> exceptionToResponse(invocation, exception, 
INTERNAL_SERVER_ERROR))
+        .thenCompose(response -> encodeResponse(invocation, response));
+  }
+
+  private CompletableFuture<Invocation> decodeRequest(Invocation invocation) {
+    HttpTransportContext transportContext = invocation.getTransportContext();
+    HttpServletRequestEx requestEx = transportContext.getRequestEx();
+
+    OperationMeta operationMeta = invocation.getOperationMeta();
+    RestOperationMeta restOperationMeta = 
operationMeta.getExtData(RestConst.SWAGGER_REST_OPERATION);
+    Map<String, Object> swaggerArguments = RestCodec.restToArgs(requestEx, 
restOperationMeta);
+    invocation.setSwaggerArguments(swaggerArguments);
+
+    return CompletableFuture.completedFuture(invocation);
+  }
+
+  private CompletableFuture<Response> encodeResponse(Invocation invocation, 
Response response) {
+    HttpTransportContext transportContext = invocation.getTransportContext();
+    ProduceProcessor produceProcessor = transportContext.getProduceProcessor();
+    HttpServletResponseEx responseEx = transportContext.getResponseEx();
+    boolean download = isDownloadFileResponseType(invocation, response);
+
+    return encodeResponse(response, download, produceProcessor, responseEx);
+  }
+
+  @SuppressWarnings("deprecation")
+  public static CompletableFuture<Response> encodeResponse(Response response, 
boolean download,
+      ProduceProcessor produceProcessor, HttpServletResponseEx responseEx) {
+    responseEx.setStatus(response.getStatusCode(), response.getReasonPhrase());
+    copyHeadersToHttpResponse(response.getHeaders().getHeaderMap(), 
responseEx);
+
+    if (download) {
+      return CompletableFuture.completedFuture(response);
+    }
+
+    responseEx.setContentType(produceProcessor.getName() + "; charset=utf-8");
+    try (BufferOutputStream output = new 
BufferOutputStream(Unpooled.compositeBuffer())) {
+      produceProcessor.encodeResponse(output, response.getResult());
+
+      responseEx.setBodyBuffer(output.getBuffer());
+
+      return CompletableFuture.completedFuture(response);
+    } catch (Throwable e) {
+      return AsyncUtils.completeExceptionally(e);
+    }
+  }
+
+  /**
+   * Check whether this response is a downloaded file response,
+   * according to the schema recorded in {@link 
org.apache.servicecomb.swagger.invocation.response.ResponsesMeta}
+   * and response status code.
+   * @return true if this response is a downloaded file, otherwise false.
+   */
+  public static boolean isDownloadFileResponseType(Invocation invocation, 
Response response) {
+    return Part.class.isAssignableFrom(
+        invocation.findResponseType(response.getStatusCode()).getRawClass());
+  }
+
+  public static void copyHeadersToHttpResponse(Map<String, List<Object>> 
headerMap, HttpServletResponseEx responseEx) {
+    if (headerMap == null) {
+      return;
+    }
+
+    for (Entry<String, List<Object>> entry : headerMap.entrySet()) {
+      for (Object value : entry.getValue()) {
+        if (!entry.getKey().equalsIgnoreCase(CONTENT_LENGTH)
+            && !entry.getKey().equalsIgnoreCase(TRANSFER_ENCODING)) {
+          responseEx.addHeader(entry.getKey(), String.valueOf(value));
+        }
+      }
+    }
+  }
+}
diff --git 
a/common/common-rest/src/main/java/org/apache/servicecomb/common/rest/filter/inner/ServerRestArgsFilter.java
 
b/common/common-rest/src/main/java/org/apache/servicecomb/common/rest/filter/inner/ServerRestArgsFilter.java
index 4043cb4..63ae8ee 100644
--- 
a/common/common-rest/src/main/java/org/apache/servicecomb/common/rest/filter/inner/ServerRestArgsFilter.java
+++ 
b/common/common-rest/src/main/java/org/apache/servicecomb/common/rest/filter/inner/ServerRestArgsFilter.java
@@ -17,11 +17,11 @@
 
 package org.apache.servicecomb.common.rest.filter.inner;
 
+import static 
org.apache.servicecomb.common.rest.filter.inner.RestServerCodecFilter.isDownloadFileResponseType;
+
 import java.util.Map;
 import java.util.concurrent.CompletableFuture;
 
-import javax.servlet.http.Part;
-
 import org.apache.servicecomb.common.rest.RestConst;
 import org.apache.servicecomb.common.rest.codec.RestCodec;
 import org.apache.servicecomb.common.rest.codec.produce.ProduceProcessor;
@@ -91,15 +91,4 @@ public class ServerRestArgsFilter implements 
HttpServerFilter {
     }
     return future;
   }
-
-  /**
-   * Check whether this response is a downloaded file response,
-   * according to the schema recorded in {@link 
org.apache.servicecomb.swagger.invocation.response.ResponsesMeta}
-   * and response status code.
-   * @return true if this response is a downloaded file, otherwise false.
-   */
-  private boolean isDownloadFileResponseType(Invocation invocation, Response 
response) {
-    return Part.class.isAssignableFrom(
-        invocation.findResponseType(response.getStatusCode()).getRawClass());
-  }
 }
diff --git 
a/common/common-rest/src/main/resources/META-INF/services/org.apache.servicecomb.core.filter.FilterProvider
 
b/common/common-rest/src/main/resources/META-INF/services/org.apache.servicecomb.core.filter.FilterProvider
new file mode 100644
index 0000000..cf4b594
--- /dev/null
+++ 
b/common/common-rest/src/main/resources/META-INF/services/org.apache.servicecomb.core.filter.FilterProvider
@@ -0,0 +1,18 @@
+#
+# 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.
+#
+
+org.apache.servicecomb.common.rest.filter.RestFilterProvider
\ No newline at end of file
diff --git 
a/common/common-rest/src/test/java/org/apache/servicecomb/common/rest/RestProducerInvocationCreatorTest.java
 
b/common/common-rest/src/test/java/org/apache/servicecomb/common/rest/RestProducerInvocationCreatorTest.java
index fd4b72a..a79b1fc 100644
--- 
a/common/common-rest/src/test/java/org/apache/servicecomb/common/rest/RestProducerInvocationCreatorTest.java
+++ 
b/common/common-rest/src/test/java/org/apache/servicecomb/common/rest/RestProducerInvocationCreatorTest.java
@@ -78,7 +78,7 @@ public class RestProducerInvocationCreatorTest {
   static SCBEngine engine;
 
   @BeforeClass
-  public static void beforeClass() throws Exception {
+  public static void beforeClass() {
     ArchaiusUtils.resetConfig();
     ConfigUtil.installDynamicConfig();
 
@@ -87,7 +87,7 @@ public class RestProducerInvocationCreatorTest {
   }
 
   @AfterClass
-  public static void afterClass() throws Exception {
+  public static void afterClass() {
     engine.destroy();
     ArchaiusUtils.resetConfig();
   }
diff --git 
a/common/common-rest/src/test/java/org/apache/servicecomb/common/rest/filter/inner/RestServerCodecFilterTest.java
 
b/common/common-rest/src/test/java/org/apache/servicecomb/common/rest/filter/inner/RestServerCodecFilterTest.java
new file mode 100644
index 0000000..a937d51
--- /dev/null
+++ 
b/common/common-rest/src/test/java/org/apache/servicecomb/common/rest/filter/inner/RestServerCodecFilterTest.java
@@ -0,0 +1,221 @@
+/*
+ * 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.filter.inner;
+
+import static com.google.common.net.HttpHeaders.CONTENT_LENGTH;
+import static com.google.common.net.HttpHeaders.TRANSFER_ENCODING;
+import static javax.ws.rs.core.Response.Status.INTERNAL_SERVER_ERROR;
+import static org.assertj.core.api.Assertions.assertThat;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.concurrent.CompletableFuture;
+import java.util.concurrent.ExecutionException;
+
+import org.apache.servicecomb.common.rest.HttpTransportContext;
+import org.apache.servicecomb.common.rest.RestConst;
+import org.apache.servicecomb.common.rest.definition.RestOperationMeta;
+import org.apache.servicecomb.config.ConfigUtil;
+import org.apache.servicecomb.core.Endpoint;
+import org.apache.servicecomb.core.Invocation;
+import org.apache.servicecomb.core.SCBEngine;
+import org.apache.servicecomb.core.SCBStatus;
+import org.apache.servicecomb.core.bootstrap.SCBBootstrap;
+import org.apache.servicecomb.core.definition.OperationMeta;
+import org.apache.servicecomb.core.filter.FilterNode;
+import org.apache.servicecomb.core.invocation.InvocationFactory;
+import org.apache.servicecomb.foundation.test.scaffolding.config.ArchaiusUtils;
+import 
org.apache.servicecomb.foundation.test.scaffolding.exception.RuntimeExceptionWithoutStackTrace;
+import org.apache.servicecomb.foundation.vertx.http.HttpServletResponseEx;
+import org.apache.servicecomb.swagger.invocation.Response;
+import org.apache.servicecomb.swagger.invocation.response.Headers;
+import org.junit.AfterClass;
+import org.junit.Before;
+import org.junit.BeforeClass;
+import org.junit.Test;
+
+import com.fasterxml.jackson.databind.type.TypeFactory;
+
+import io.vertx.core.json.Json;
+import mockit.Expectations;
+import mockit.Mocked;
+import mockit.Verifications;
+
+public class RestServerCodecFilterTest {
+  RestServerCodecFilter codecFilter = new RestServerCodecFilter();
+
+  Invocation invocation;
+
+  @Mocked
+  Endpoint endpoint;
+
+  @Mocked
+  OperationMeta operationMeta;
+
+  @Mocked
+  RestOperationMeta restOperationMeta;
+
+  @Mocked
+  HttpTransportContext transportContext;
+
+  @Mocked
+  HttpServletResponseEx responseEx;
+
+  Headers headers = new Headers();
+
+  FilterNode nextNode = new FilterNode((invocation, next) -> {
+    Response response = Response.ok("ok");
+    response.setHeaders(headers);
+    return CompletableFuture.completedFuture(response);
+  });
+
+  static SCBEngine engine;
+
+  @BeforeClass
+  public static void beforeClass() {
+    ArchaiusUtils.resetConfig();
+    ConfigUtil.installDynamicConfig();
+
+    engine = SCBBootstrap.createSCBEngineForTest();
+    engine.setStatus(SCBStatus.UP);
+  }
+
+  @AfterClass
+  public static void afterClass() {
+    engine.destroy();
+    ArchaiusUtils.resetConfig();
+  }
+
+  @Before
+  public void setUp() {
+    invocation = InvocationFactory.forProvider(endpoint, operationMeta, null);
+  }
+
+  private void mockDecodeRequestFail() {
+    new Expectations(invocation) {
+      {
+        invocation.getTransportContext();
+        result = transportContext;
+
+        transportContext.getRequestEx();
+        result = new RuntimeExceptionWithoutStackTrace("encode request 
failed");
+      }
+    };
+  }
+
+  @Test
+  public void should_not_invoke_filter_when_decode_request_failed(@Mocked 
FilterNode nextNode) {
+    mockDecodeRequestFail();
+
+    codecFilter.onFilter(invocation, nextNode);
+
+    new Verifications() {
+      {
+        nextNode.onFilter(invocation);
+        times = 0;
+      }
+    };
+  }
+
+  @Test
+  public void should_convert_exception_to_response_when_decode_request_failed()
+      throws ExecutionException, InterruptedException {
+    mockDecodeRequestFail();
+    new Expectations(invocation) {
+      {
+        invocation.findResponseType(anyInt);
+        result = TypeFactory.defaultInstance().constructType(String.class);
+      }
+    };
+
+    Response response = codecFilter.onFilter(invocation, nextNode).get();
+
+    assertThat(response.getStatus()).isEqualTo(INTERNAL_SERVER_ERROR);
+    assertThat(Json.encode(response.getResult()))
+        .isEqualTo("{\"code\":\"SCB.5000\",\"message\":\"encode request 
failed\"}");
+  }
+
+  private void success_invocation() throws InterruptedException, 
ExecutionException {
+    new Expectations(invocation) {
+      {
+        invocation.getTransportContext();
+        result = transportContext;
+
+        operationMeta.getExtData(RestConst.SWAGGER_REST_OPERATION);
+        result = restOperationMeta;
+
+        invocation.findResponseType(anyInt);
+        result = TypeFactory.defaultInstance().constructType(String.class);
+      }
+    };
+
+    codecFilter.onFilter(invocation, nextNode).get();
+  }
+
+  @Test
+  public void should_encode_response_header() throws ExecutionException, 
InterruptedException {
+    headers.addHeader("h1", "v1");
+    success_invocation();
+
+    new Verifications() {
+      {
+        String name;
+        String value;
+        responseEx.addHeader(name = withCapture(), value = withCapture());
+        assertThat(name).isEqualTo("h1");
+        assertThat(value).isEqualTo("v1");
+      }
+    };
+  }
+
+  @Test
+  public void should_not_encode_content_length_header() throws 
ExecutionException, InterruptedException {
+    headers.addHeader("h1", "v1")
+        .addHeader("h2", "v2")
+        .addHeader(CONTENT_LENGTH, 10);
+    success_invocation();
+
+    new Verifications() {
+      {
+        List<String> names = new ArrayList<>();
+        List<String> values = new ArrayList<>();
+        responseEx.addHeader(withCapture(names), withCapture(values));
+        assertThat(names).containsExactly("h1", "h2");
+        assertThat(values).containsExactly("v1", "v2");
+      }
+    };
+  }
+
+  @Test
+  public void should_not_encode_transfer_encoding_header() throws 
ExecutionException, InterruptedException {
+    headers.addHeader("h1", "v1")
+        .addHeader("h2", "v2")
+        .addHeader(TRANSFER_ENCODING, "test");
+    success_invocation();
+
+    new Verifications() {
+      {
+        List<String> names = new ArrayList<>();
+        List<String> values = new ArrayList<>();
+        responseEx.addHeader(withCapture(names), withCapture(values));
+        assertThat(names).containsExactly("h1", "h2");
+        assertThat(values).containsExactly("v1", "v2");
+      }
+    };
+  }
+}
\ No newline at end of file
diff --git 
a/core/src/main/java/org/apache/servicecomb/core/filter/FilterMeta.java 
b/core/src/main/java/org/apache/servicecomb/core/filter/FilterMeta.java
index 680ba45..e40aca4 100644
--- a/core/src/main/java/org/apache/servicecomb/core/filter/FilterMeta.java
+++ b/core/src/main/java/org/apache/servicecomb/core/filter/FilterMeta.java
@@ -17,6 +17,9 @@
 
 package org.apache.servicecomb.core.filter;
 
+import static 
org.apache.servicecomb.swagger.invocation.InvocationType.CONSUMER;
+import static 
org.apache.servicecomb.swagger.invocation.InvocationType.PRODUCER;
+
 import java.lang.annotation.Documented;
 import java.lang.annotation.ElementType;
 import java.lang.annotation.Inherited;
@@ -24,6 +27,8 @@ import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
 import java.lang.annotation.Target;
 
+import org.apache.servicecomb.swagger.invocation.InvocationType;
+
 @Documented
 @Retention(RetentionPolicy.RUNTIME)
 @Target(ElementType.TYPE)
@@ -33,6 +38,12 @@ public @interface FilterMeta {
 
   /**
    *
+   * @return can be used for the specific invocation type
+   */
+  InvocationType[] invocationType() default {CONSUMER, PRODUCER};
+
+  /**
+   *
    * @return true to use same instance for all filter chains
    */
   boolean shareable() default true;
diff --git 
a/core/src/main/java/org/apache/servicecomb/core/filter/FilterMeta.java 
b/core/src/main/java/org/apache/servicecomb/core/filter/FilterProvider.java
similarity index 63%
copy from core/src/main/java/org/apache/servicecomb/core/filter/FilterMeta.java
copy to 
core/src/main/java/org/apache/servicecomb/core/filter/FilterProvider.java
index 680ba45..df14364 100644
--- a/core/src/main/java/org/apache/servicecomb/core/filter/FilterMeta.java
+++ b/core/src/main/java/org/apache/servicecomb/core/filter/FilterProvider.java
@@ -14,26 +14,10 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-
 package org.apache.servicecomb.core.filter;
 
-import java.lang.annotation.Documented;
-import java.lang.annotation.ElementType;
-import java.lang.annotation.Inherited;
-import java.lang.annotation.Retention;
-import java.lang.annotation.RetentionPolicy;
-import java.lang.annotation.Target;
-
-@Documented
-@Retention(RetentionPolicy.RUNTIME)
-@Target(ElementType.TYPE)
-@Inherited
-public @interface FilterMeta {
-  String name();
+import java.util.List;
 
-  /**
-   *
-   * @return true to use same instance for all filter chains
-   */
-  boolean shareable() default true;
+public interface FilterProvider {
+  List<Class<? extends Filter>> getFilters();
 }

Reply via email to