You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@dubbo.apache.org by al...@apache.org on 2024/07/23 07:47:27 UTC
(dubbo) branch 3.3 updated: Rest Refine (#14334)
This is an automated email from the ASF dual-hosted git repository.
albumenj pushed a commit to branch 3.3
in repository https://gitbox.apache.org/repos/asf/dubbo.git
The following commit(s) were added to refs/heads/3.3 by this push:
new c5e2f93117 Rest Refine (#14334)
c5e2f93117 is described below
commit c5e2f93117d322ae66b2bbbca967fad0b64c1806
Author: Sean Yang <ox...@gmail.com>
AuthorDate: Tue Jul 23 15:47:21 2024 +0800
Rest Refine (#14334)
---
.artifacts | 1 -
.editorconfig | 22 +-
.github/workflows/build-and-test-pr.yml | 2 +-
.../common/utils/SystemPropertyConfigUtils.java | 5 +-
.../dubbo/config/nested/AggregationConfig.java | 2 +
.../apache/dubbo/config/nested/BaggageConfig.java | 2 +
.../dubbo/config/{ => nested}/CorsConfig.java | 5 +-
.../apache/dubbo/config/nested/ExporterConfig.java | 2 +
.../dubbo/config/nested/HistogramConfig.java | 2 +
.../apache/dubbo/config/nested/Http3Config.java | 250 +++++++++++++
.../dubbo/config/nested/PrometheusConfig.java | 2 +
.../dubbo/config/nested/PropagationConfig.java | 2 +
.../dubbo/config/{ => nested}/RestConfig.java | 74 ++--
.../apache/dubbo/config/nested/SamplingConfig.java | 2 +
.../apache/dubbo/config/nested/ServletConfig.java | 81 +++++
.../apache/dubbo/config/nested/TripleConfig.java | 274 ++-------------
.../rpc/model/ReflectionServiceDescriptor.java | 3 +-
.../src/main/resources/application.yml | 6 +-
dubbo-dependencies-bom/pom.xml | 36 +-
.../dubbo-dependencies-zookeeper-curator5/pom.xml | 4 +-
dubbo-distribution/dubbo-all-shaded/pom.xml | 8 -
dubbo-distribution/dubbo-all/pom.xml | 8 -
dubbo-distribution/dubbo-bom/pom.xml | 5 -
.../filter/AbstractObservationFilterTest.java | 84 -----
.../filter/ObservationReceiverFilterTest.java | 128 -------
.../filter/ObservationSenderFilterTest.java | 76 ----
dubbo-plugin/dubbo-rest-jaxrs/pom.xml | 47 +++
.../tri/rest/support/jaxrs/BeanArgumentBinder.java | 276 +++------------
...stAdaptee.java => JaxrsHttpRequestAdapter.java} | 4 +-
...eAdaptee.java => JaxrsHttpResponseAdapter.java} | 4 +-
.../tri/rest/support/jaxrs/JaxrsRestToolKit.java | 31 +-
.../rest/support/jaxrs/ParamConverterFactory.java | 59 ++--
.../jaxrs/filter/ContainerRequestContextImpl.java | 6 +-
.../tri/rest/support/jaxrs/RestProtocolTest.groovy | 78 +++++
.../jaxrs/compatible/JaxrsRestProtocolTest.java | 22 --
.../JaxrsDemoService.java} | 25 +-
.../JaxrsDemoServiceImpl.java} | 24 +-
.../ParamConverterProviderImpl.java | 20 +-
.../User.java} | 57 ++-
.../tri/rest/support/jaxrs/service/UserForm.java | 46 ++-
.../javax.ws.rs.ext.ParamConverterProvider | 2 +-
.../src/test/resources/log4j2-test.xml | 1 +
dubbo-plugin/dubbo-rest-servlet/pom.xml | 46 ---
...moting.http12.message.HttpMessageAdapterFactory | 1 -
...rpc.protocol.tri.rest.argument.ArgumentResolver | 1 -
...c.protocol.tri.rest.filter.RestExtensionAdapter | 1 -
.../src/test/resources/log4j2-test.xml | 30 --
dubbo-plugin/dubbo-rest-spring/pom.xml | 49 ++-
.../spring/AbstractSpringArgumentResolver.java | 10 +-
.../tri/rest/support/spring/Annotations.java | 2 +
.../rest/support/spring/BeanArgumentBinder.java | 103 +-----
...esolver.java => BindParamArgumentResolver.java} | 29 +-
.../spring/SpringMvcRequestMappingResolver.java | 37 +-
.../tri/rest/support/spring/SpringRestToolKit.java | 7 +
...rpc.protocol.tri.rest.argument.ArgumentResolver | 1 +
.../rest/support/spring/RestProtocolTest.groovy | 72 ++++
.../support/spring/service/SpringDemoService.java} | 15 +-
.../spring/service/SpringDemoServiceImpl.java} | 9 +-
.../src/test/resources/log4j2-test.xml | 1 +
dubbo-plugin/dubbo-triple-servlet/pom.xml | 29 +-
.../rest/support/servlet/DummyFilterConfig.java | 0
.../rest/support/servlet/DummyServletContext.java | 40 ++-
.../tri/rest/support/servlet/FilterAdapter.java | 0
.../protocol/tri/rest/support/servlet/Helper.java | 0
.../rest/support/servlet/HttpSessionFactory.java | 0
.../support/servlet/ServletArgumentResolver.java | 0
.../servlet/ServletHttpMessageAdapterFactory.java | 10 +-
.../servlet/ServletHttpRequestAdapter.java} | 10 +-
.../servlet/ServletHttpResponseAdapter.java} | 11 +-
.../protocol/tri/servlet/HttpMetadataAdapter.java | 6 +-
.../rpc/protocol/tri/servlet/TripleFilter.java | 3 +-
...moting.http12.message.HttpMessageAdapterFactory | 2 +
...rpc.protocol.tri.rest.argument.ArgumentResolver | 2 +
...c.protocol.tri.rest.filter.RestExtensionAdapter | 2 +
dubbo-plugin/pom.xml | 1 -
.../remoting/http12/CompositeInputStream.java | 37 +-
.../apache/dubbo/remoting/http12/HttpRequest.java | 2 +
.../http12/message/DefaultHttpRequest.java | 15 +-
.../remoting/http12/message/codec/CodecUtils.java | 8 +
.../apache/dubbo/remoting/http12/rest/Mapping.java | 41 +--
.../apache/dubbo/remoting/http12/rest/Param.java | 39 +--
.../dubbo/remoting/http12/rest/ParamType.java | 18 +-
.../dubbo/remoting/transport/netty4/Helper.java | 55 ++-
.../dubbo-remoting-zookeeper-curator5/pom.xml | 5 -
.../main/java/org/apache/dubbo/rpc/Constants.java | 4 +-
dubbo-rpc/dubbo-rpc-triple/pom.xml | 45 +++
.../dubbo/rpc/protocol/tri/TripleConstant.java | 1 -
.../tri/h12/HttpRequestHandlerMapping.java | 121 -------
.../protocol/tri/h12/UnaryServerCallListener.java | 10 +-
.../h12/grpc/GrpcHttp2ServerTransportListener.java | 15 +
.../tri/h12/grpc/GrpcRequestHandlerMapping.java | 57 ++-
.../DefaultHttp11ServerTransportListener.java | 2 +-
.../http2/GenericHttp2ServerTransportListener.java | 21 +-
.../dubbo/rpc/protocol/tri/rest/Messages.java | 2 +-
.../dubbo/rpc/protocol/tri/rest/RestConstants.java | 6 +-
.../protocol/tri/rest/RestHttpMessageCodec.java | 4 +
.../rest/argument/CompositeArgumentResolver.java | 4 +
.../tri/rest/argument/GeneralTypeConverter.java | 28 +-
.../NamedValueArgumentResolverSupport.java | 27 +-
.../protocol/tri/rest/cors/CorsHeaderFilter.java | 20 +-
.../tri/rest/mapping/ContentNegotiator.java | 4 +
.../mapping/DefaultRequestMappingRegistry.java | 227 +++++++++---
.../rpc/protocol/tri/rest/mapping/RadixTree.java | 101 ++----
.../protocol/tri/rest/mapping/RequestMapping.java | 95 +++--
.../tri/rest/mapping/RequestMappingResolver.java | 4 +
.../tri/rest/mapping/condition/PathExpression.java | 22 +-
.../tri/rest/mapping/condition/PathParser.java | 47 +--
.../tri/rest/mapping/condition/PathSegment.java | 17 +-
.../tri/rest/mapping/meta/AnnotationSupport.java | 20 +-
.../protocol/tri/rest/mapping/meta/BeanMeta.java | 387 +++++++++++++++++++++
.../protocol/tri/rest/mapping/meta/CorsMeta.java | 24 +-
.../protocol/tri/rest/mapping/meta/MethodMeta.java | 31 +-
.../tri/rest/mapping/meta/MethodParameterMeta.java | 10 +
.../tri/rest/mapping/meta/ParameterMeta.java | 24 +-
.../tri/rest/mapping/meta/ServiceMeta.java | 14 +-
.../tri/rest/support/basic}/Annotations.java | 26 +-
.../support/basic/BasicRequestMappingResolver.java | 121 +++++++
.../tri/rest/support/basic/BasicRestToolKit.java | 28 +-
.../tri/rest/support/basic/BeanArgumentBinder.java | 375 ++++++++++++++++++++
.../support/basic/FallbackArgumentResolver.java | 91 +++++
.../rest/support/basic/ParamArgumentResolver.java | 217 ++++++++++++
...ltRestToolKit.java => AbstractRestToolKit.java} | 16 +-
.../rpc/protocol/tri/rest/util/KeyString.java | 118 +++++++
.../rpc/protocol/tri/rest/util/PathUtils.java | 27 +-
.../rpc/protocol/tri/rest/util/RequestUtils.java | 85 ++++-
.../rpc/protocol/tri/rest/util/RestUtils.java | 31 ++
.../rpc/protocol/tri/rest/util/TypeUtils.java | 170 ++++++++-
...rpc.protocol.tri.rest.argument.ArgumentResolver | 2 +
...rotocol.tri.rest.mapping.RequestMappingResolver | 1 +
...bo.rpc.protocol.tri.route.RequestHandlerMapping | 3 +-
.../protocol/tri/rest/mapping/RadixTreeTest.groovy | 47 +++
.../mapping/condition/PathExpressionTest.groovy | 261 ++++++++++++++
.../tri/rest/support/basic/RestProtocolTest.groovy | 191 ++++++++++
.../protocol/tri/rest/test/BaseServiceTest.groovy | 39 ++-
.../protocol/tri/rest/util/PathUtilsTest.groovy | 147 ++++++++
.../protocol/tri/rest/mapping/RadixTreeTest.java | 56 ---
.../tri/rest/mapping/condition/PathParserTest.java | 49 ---
.../dubbo/rpc/protocol/tri/rest/service/Book.java | 65 ++++
.../rpc/protocol/tri/rest/service/DemoService.java | 50 +++
.../protocol/tri/rest/service/DemoServiceImpl.java | 103 ++++++
.../dubbo/rpc/protocol/tri/rest/service/User.java | 283 +++++++++++++++
.../rpc/protocol/tri/test/MockH2StreamChannel.java | 78 +++++
.../protocol/tri/test/MockHttp2OutputMessage.java | 37 +-
.../dubbo/rpc/protocol/tri/test/TestProtocol.java | 65 ++++
.../dubbo/rpc/protocol/tri/test/TestRequest.java | 209 +++++++++++
.../dubbo/rpc/protocol/tri/test/TestResponse.java | 119 +++++++
.../dubbo/rpc/protocol/tri/test/TestRunner.java | 76 ++++
.../rpc/protocol/tri/test/TestRunnerBuilder.java | 78 +++++
.../rpc/protocol/tri/test/TestRunnerImpl.java | 341 ++++++++++++++++++
.../TestServerTransportListener.java} | 32 +-
.../dubbo/internal/org.apache.dubbo.rpc.Protocol | 1 +
...ubbo.rpc.protocol.tri.rest.filter.RestExtension | 0
...c.protocol.tri.rest.filter.RestExtensionAdapter | 0
.../src/test/resources/log4j2-test.xml | 1 +
.../DubboTriple3AutoConfiguration.java | 8 +-
.../DubboTripleAutoConfiguration.java | 12 +-
.../DubboConfigurationProperties.java | 50 ---
.../dubbo-spring-boot-starter/pom.xml | 1 -
dubbo-test/dubbo-dependencies-all/pom.xml | 5 -
pom.xml | 8 +-
160 files changed, 5704 insertions(+), 1951 deletions(-)
diff --git a/.artifacts b/.artifacts
index 41ce8e207a..2c21cd0bca 100644
--- a/.artifacts
+++ b/.artifacts
@@ -115,6 +115,5 @@ dubbo-tracing
dubbo-xds
dubbo-plugin-loom
dubbo-rest-jaxrs
-dubbo-rest-servlet
dubbo-rest-spring
dubbo-triple-servlet
diff --git a/.editorconfig b/.editorconfig
index 5de62e0893..5afaa5666b 100644
--- a/.editorconfig
+++ b/.editorconfig
@@ -42,26 +42,40 @@ ij_java_keep_simple_lambdas_in_one_line = true
ij_java_keep_simple_methods_in_one_line = true
ij_java_keep_blank_lines_in_code = 1
ij_java_keep_blank_lines_in_declarations = 1
+ij_java_blank_lines_after_class_header = 1
ij_java_class_count_to_use_import_on_demand = 999
ij_java_names_count_to_use_import_on_demand = 999
ij_java_imports_layout = org.apache.dubbo.**, |, javax.**, |, java.**, |, *, |, $*
ij_java_insert_inner_class_imports = true
ij_java_space_before_array_initializer_left_brace = true
ij_java_method_parameters_new_line_after_left_paren = true
-ij_java_wrap_comments = true
-ij_java_wrap_long_lines = true
+ij_java_wrap_comments = false
+ij_java_wrap_long_lines = false
ij_java_enum_constants_wrap = split_into_lines
-ij_java_method_call_chain_wrap = split_into_lines
+ij_java_method_call_chain_wrap = on_every_item
ij_java_method_parameters_wrap = on_every_item
-ij_java_extends_list_wrap = on_every_item
+ij_java_extends_list_wrap = normal
ij_java_extends_keyword_wrap = normal
ij_java_binary_operation_wrap = normal
ij_java_binary_operation_sign_on_next_line = true
+[*.groovy]
+max_line_length = 180
+ij_groovy_label_indent_size = 4
+ij_groovy_keep_blank_lines_in_code = 1
+ij_groovy_keep_blank_lines_in_declarations = 1
+ij_groovy_blank_lines_after_class_header = 1
+ij_groovy_class_count_to_use_import_on_demand = 999
+ij_groovy_names_count_to_use_import_on_demand = 999
+ij_groovy_imports_layout = org.apache.dubbo.**, |, javax.**, |, java.**, |, *, |, $*
+ij_groovy_space_after_type_cast = false
+
[*.json]
tab_width = 2
+indent_size = 2
[*.{yml,yaml}]
+tab_width = 2
indent_size = 2
[*.xml]
diff --git a/.github/workflows/build-and-test-pr.yml b/.github/workflows/build-and-test-pr.yml
index f179ec5495..7a40dbca15 100644
--- a/.github/workflows/build-and-test-pr.yml
+++ b/.github/workflows/build-and-test-pr.yml
@@ -326,7 +326,7 @@ jobs:
cd test && bash ./build-test-image.sh
- name: "Run tests"
run: cd test && bash ./run-tests.sh
- - name: "merge jacoco resule"
+ - name: "merge jacoco result"
run: |
cd test/dubbo-test-jacoco-merger && mvn clean compile exec:java -Dexec.mainClass="org.apache.dubbo.test.JacocoMerge" -Dexec.args="${{github.workspace}}"
- name: "Upload jacoco"
diff --git a/dubbo-common/src/main/java/org/apache/dubbo/common/utils/SystemPropertyConfigUtils.java b/dubbo-common/src/main/java/org/apache/dubbo/common/utils/SystemPropertyConfigUtils.java
index 857807e79e..87f0ce3a14 100644
--- a/dubbo-common/src/main/java/org/apache/dubbo/common/utils/SystemPropertyConfigUtils.java
+++ b/dubbo-common/src/main/java/org/apache/dubbo/common/utils/SystemPropertyConfigUtils.java
@@ -37,7 +37,10 @@ public class SystemPropertyConfigUtils {
for (Field field : fields) {
try {
assert systemProperties != null;
- systemProperties.add((String) field.get(null));
+ Object value = field.get(null);
+ if (value instanceof String) {
+ systemProperties.add((String) value);
+ }
} catch (IllegalAccessException e) {
throw new IllegalStateException(
String.format("%s does not have field of %s", clazz.getName(), field.getName()));
diff --git a/dubbo-common/src/main/java/org/apache/dubbo/config/nested/AggregationConfig.java b/dubbo-common/src/main/java/org/apache/dubbo/config/nested/AggregationConfig.java
index 553d834354..bcbdf052a8 100644
--- a/dubbo-common/src/main/java/org/apache/dubbo/config/nested/AggregationConfig.java
+++ b/dubbo-common/src/main/java/org/apache/dubbo/config/nested/AggregationConfig.java
@@ -23,6 +23,8 @@ import java.io.Serializable;
*/
public class AggregationConfig implements Serializable {
+ private static final long serialVersionUID = 4878693820314125085L;
+
/**
* Enable aggregation or not.
*/
diff --git a/dubbo-common/src/main/java/org/apache/dubbo/config/nested/BaggageConfig.java b/dubbo-common/src/main/java/org/apache/dubbo/config/nested/BaggageConfig.java
index 45bea909de..6ff8e9df9f 100644
--- a/dubbo-common/src/main/java/org/apache/dubbo/config/nested/BaggageConfig.java
+++ b/dubbo-common/src/main/java/org/apache/dubbo/config/nested/BaggageConfig.java
@@ -27,6 +27,8 @@ import java.util.List;
*/
public class BaggageConfig implements Serializable {
+ private static final long serialVersionUID = -4750259290735346439L;
+
/**
* Whether baggage is enabled or not.
*/
diff --git a/dubbo-common/src/main/java/org/apache/dubbo/config/CorsConfig.java b/dubbo-common/src/main/java/org/apache/dubbo/config/nested/CorsConfig.java
similarity index 97%
rename from dubbo-common/src/main/java/org/apache/dubbo/config/CorsConfig.java
rename to dubbo-common/src/main/java/org/apache/dubbo/config/nested/CorsConfig.java
index 657f20d039..1b604927d6 100644
--- a/dubbo-common/src/main/java/org/apache/dubbo/config/CorsConfig.java
+++ b/dubbo-common/src/main/java/org/apache/dubbo/config/nested/CorsConfig.java
@@ -14,12 +14,13 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package org.apache.dubbo.config;
+package org.apache.dubbo.config.nested;
import java.io.Serializable;
public class CorsConfig implements Serializable {
- private static final long serialVersionUID = 1L;
+
+ private static final long serialVersionUID = -7106481576053641726L;
/**
* A list of origins for which cross-origin requests are allowed. Values may be a specific domain, e.g.
diff --git a/dubbo-common/src/main/java/org/apache/dubbo/config/nested/ExporterConfig.java b/dubbo-common/src/main/java/org/apache/dubbo/config/nested/ExporterConfig.java
index 6edfc53204..fd17a99c53 100644
--- a/dubbo-common/src/main/java/org/apache/dubbo/config/nested/ExporterConfig.java
+++ b/dubbo-common/src/main/java/org/apache/dubbo/config/nested/ExporterConfig.java
@@ -28,6 +28,8 @@ import java.util.Map;
*/
public class ExporterConfig implements Serializable {
+ private static final long serialVersionUID = -559392305178067845L;
+
/**
* Configuration for the Zipkin.
*/
diff --git a/dubbo-common/src/main/java/org/apache/dubbo/config/nested/HistogramConfig.java b/dubbo-common/src/main/java/org/apache/dubbo/config/nested/HistogramConfig.java
index 504faf03c8..66dbf466e0 100644
--- a/dubbo-common/src/main/java/org/apache/dubbo/config/nested/HistogramConfig.java
+++ b/dubbo-common/src/main/java/org/apache/dubbo/config/nested/HistogramConfig.java
@@ -23,6 +23,8 @@ import java.io.Serializable;
*/
public class HistogramConfig implements Serializable {
+ private static final long serialVersionUID = 8152538916051803031L;
+
/**
* Whether histograms are enabled or not. Default is not enabled (false).
*/
diff --git a/dubbo-common/src/main/java/org/apache/dubbo/config/nested/Http3Config.java b/dubbo-common/src/main/java/org/apache/dubbo/config/nested/Http3Config.java
new file mode 100644
index 0000000000..757516196f
--- /dev/null
+++ b/dubbo-common/src/main/java/org/apache/dubbo/config/nested/Http3Config.java
@@ -0,0 +1,250 @@
+/*
+ * 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.dubbo.config.nested;
+
+import java.io.Serializable;
+
+public class Http3Config implements Serializable {
+
+ private static final long serialVersionUID = -4443828713331129834L;
+
+ /**
+ * Enable http3 support
+ * <p>The default value is false.
+ */
+ private Boolean enable;
+
+ /**
+ * See <a href="https://docs.rs/quiche/0.6.0/quiche/struct.Config.html#method.set_initial_max_data">set_initial_max_data</a>.
+ * <p>The default value is 8MiB.
+ */
+ private Integer initialMaxData;
+
+ /**
+ * If configured this will enable <a href="https://tools.ietf.org/html/draft-ietf-quic-datagram-01">Datagram support.</a>
+ */
+ private Integer recvQueueLen;
+
+ /**
+ * If configured this will enable <a href="https://tools.ietf.org/html/draft-ietf-quic-datagram-01">Datagram support.</a>
+ */
+ private Integer sendQueueLen;
+
+ /**
+ * See
+ * <a href="https://docs.rs/quiche/0.6.0/quiche/struct.Config.html#method.set_initial_max_stream_data_bidi_local">set_initial_max_stream_data_bidi_local</a>.
+ * <p>The default value is 1MiB.
+ */
+ private Integer initialMaxStreamDataBidiLocal;
+
+ /**
+ * See
+ * <a href="https://docs.rs/quiche/0.6.0/quiche/struct.Config.html#method.set_initial_max_stream_data_bidi_remote">set_initial_max_stream_data_bidi_remote</a>.
+ * <p>The default value is 1MiB.
+ */
+ private Integer initialMaxStreamDataBidiRemote;
+
+ /**
+ * See
+ * <a href="https://docs.rs/quiche/0.6.0/quiche/struct.Config.html#method.set_initial_max_stream_data_uni">set_initial_max_stream_data_uni</a>.
+ * <p>The default value is 0.
+ */
+ private Integer initialMaxStreamDataUni;
+
+ /**
+ * See
+ * <a href="https://docs.rs/quiche/0.6.0/quiche/struct.Config.html#method.set_initial_max_streams_bidi">set_initial_max_streams_bidi</a>.
+ * <p>The default value is 1B(2^30).
+ */
+ private Long initialMaxStreamsBidi;
+
+ /**
+ * See
+ * <a href="https://docs.rs/quiche/0.6.0/quiche/struct.Config.html#method.set_initial_max_streams_uni">set_initial_max_streams_uni</a>.
+ * <p>
+ * <p>The default value is 1B(2^30).
+ */
+ private Long initialMaxStreamsUni;
+
+ /**
+ * See
+ * <a href="https://docs.rs/quiche/0.6.0/quiche/struct.Config.html#method.set_ack_delay_exponent">set_ack_delay_exponent</a>.
+ * <p>The default value is 3.
+ */
+ private Integer maxAckDelayExponent;
+
+ /**
+ * See
+ * <a href="https://docs.rs/quiche/0.6.0/quiche/struct.Config.html#method.set_max_ack_delay">set_max_ack_delay</a>.
+ * <p>The default value is 25 milliseconds.
+ */
+ private Integer maxAckDelay;
+
+ /**
+ * See
+ * <a href="https://docs.rs/quiche/0.6.0/quiche/struct.Config.html#method.set_disable_active_migration">set_disable_active_migration</a>.
+ * <p>The default value is {@code false}.
+ */
+ private Boolean disableActiveMigration;
+
+ /**
+ * See
+ * <a href="https://docs.rs/quiche/0.6.0/quiche/struct.Config.html#method.enable_hystart">enable_hystart</a>.
+ * <p>The default value is {@code true}.
+ */
+ private Boolean enableHystart;
+
+ /**
+ * Sets the congestion control algorithm to use.
+ * <p>Supported algorithms are {@code "RENO"} or {@code "CUBIC"} or {@code "BBR"}.
+ * <p>The default value is {@code "CUBIC"}.
+ */
+ private String ccAlgorithm;
+
+ public Boolean getEnable() {
+ return enable;
+ }
+
+ public void setEnable(Boolean enable) {
+ this.enable = enable;
+ }
+
+ public Integer getInitialMaxData() {
+ return initialMaxData;
+ }
+
+ public void setInitialMaxData(Integer initialMaxData) {
+ this.initialMaxData = initialMaxData;
+ }
+
+ public Integer getRecvQueueLen() {
+ return recvQueueLen;
+ }
+
+ public void setRecvQueueLen(Integer recvQueueLen) {
+ this.recvQueueLen = recvQueueLen;
+ }
+
+ public Integer getSendQueueLen() {
+ return sendQueueLen;
+ }
+
+ public void setSendQueueLen(Integer sendQueueLen) {
+ this.sendQueueLen = sendQueueLen;
+ }
+
+ public Integer getInitialMaxStreamDataBidiLocal() {
+ return initialMaxStreamDataBidiLocal;
+ }
+
+ public void setInitialMaxStreamDataBidiLocal(Integer initialMaxStreamDataBidiLocal) {
+ this.initialMaxStreamDataBidiLocal = initialMaxStreamDataBidiLocal;
+ }
+
+ public Integer getInitialMaxStreamDataBidiRemote() {
+ return initialMaxStreamDataBidiRemote;
+ }
+
+ public void setInitialMaxStreamDataBidiRemote(Integer initialMaxStreamDataBidiRemote) {
+ this.initialMaxStreamDataBidiRemote = initialMaxStreamDataBidiRemote;
+ }
+
+ public Integer getInitialMaxStreamDataUni() {
+ return initialMaxStreamDataUni;
+ }
+
+ public void setInitialMaxStreamDataUni(Integer initialMaxStreamDataUni) {
+ this.initialMaxStreamDataUni = initialMaxStreamDataUni;
+ }
+
+ public Long getInitialMaxStreamsBidi() {
+ return initialMaxStreamsBidi;
+ }
+
+ public void setInitialMaxStreamsBidi(Long initialMaxStreamsBidi) {
+ this.initialMaxStreamsBidi = initialMaxStreamsBidi;
+ }
+
+ public Long getInitialMaxStreamsUni() {
+ return initialMaxStreamsUni;
+ }
+
+ public void setInitialMaxStreamsUni(Long initialMaxStreamsUni) {
+ this.initialMaxStreamsUni = initialMaxStreamsUni;
+ }
+
+ public Integer getMaxAckDelayExponent() {
+ return maxAckDelayExponent;
+ }
+
+ public void setMaxAckDelayExponent(Integer maxAckDelayExponent) {
+ this.maxAckDelayExponent = maxAckDelayExponent;
+ }
+
+ public Integer getMaxAckDelay() {
+ return maxAckDelay;
+ }
+
+ public void setMaxAckDelay(Integer maxAckDelay) {
+ this.maxAckDelay = maxAckDelay;
+ }
+
+ public Boolean getDisableActiveMigration() {
+ return disableActiveMigration;
+ }
+
+ public void setDisableActiveMigration(Boolean disableActiveMigration) {
+ this.disableActiveMigration = disableActiveMigration;
+ }
+
+ public Boolean getEnableHystart() {
+ return enableHystart;
+ }
+
+ public void setEnableHystart(Boolean enableHystart) {
+ this.enableHystart = enableHystart;
+ }
+
+ public String getCcAlgorithm() {
+ return ccAlgorithm;
+ }
+
+ public void setCcAlgorithm(String ccAlgorithm) {
+ this.ccAlgorithm = ccAlgorithm;
+ }
+
+ public void checkDefault() {
+ if (initialMaxData == null) {
+ initialMaxData = 1 << 23;
+ }
+ if (initialMaxStreamDataBidiLocal == null) {
+ initialMaxStreamDataBidiLocal = 1 << 20;
+ }
+ if (initialMaxStreamDataBidiRemote == null) {
+ initialMaxStreamDataBidiRemote = 1 << 20;
+ }
+ if (initialMaxStreamDataUni == null) {
+ initialMaxStreamDataUni = 1 << 20;
+ }
+ if (initialMaxStreamsBidi == null) {
+ initialMaxStreamsBidi = (long) 1 << 30;
+ }
+ if (initialMaxStreamsUni == null) {
+ initialMaxStreamsUni = (long) 1 << 30;
+ }
+ }
+}
diff --git a/dubbo-common/src/main/java/org/apache/dubbo/config/nested/PrometheusConfig.java b/dubbo-common/src/main/java/org/apache/dubbo/config/nested/PrometheusConfig.java
index a9b8ded533..cc066678c9 100644
--- a/dubbo-common/src/main/java/org/apache/dubbo/config/nested/PrometheusConfig.java
+++ b/dubbo-common/src/main/java/org/apache/dubbo/config/nested/PrometheusConfig.java
@@ -25,6 +25,8 @@ import java.io.Serializable;
*/
public class PrometheusConfig implements Serializable {
+ private static final long serialVersionUID = 2238807632335823129L;
+
/**
* Prometheus exporter configuration
*/
diff --git a/dubbo-common/src/main/java/org/apache/dubbo/config/nested/PropagationConfig.java b/dubbo-common/src/main/java/org/apache/dubbo/config/nested/PropagationConfig.java
index c7ee0ba7dd..d2e650d273 100644
--- a/dubbo-common/src/main/java/org/apache/dubbo/config/nested/PropagationConfig.java
+++ b/dubbo-common/src/main/java/org/apache/dubbo/config/nested/PropagationConfig.java
@@ -23,6 +23,8 @@ import java.io.Serializable;
*/
public class PropagationConfig implements Serializable {
+ private static final long serialVersionUID = -2570106396211532046L;
+
public static final String B3 = "B3";
public static final String W3C = "W3C";
diff --git a/dubbo-common/src/main/java/org/apache/dubbo/config/RestConfig.java b/dubbo-common/src/main/java/org/apache/dubbo/config/nested/RestConfig.java
similarity index 63%
rename from dubbo-common/src/main/java/org/apache/dubbo/config/RestConfig.java
rename to dubbo-common/src/main/java/org/apache/dubbo/config/nested/RestConfig.java
index dc7e988928..518d3d3b6d 100644
--- a/dubbo-common/src/main/java/org/apache/dubbo/config/RestConfig.java
+++ b/dubbo-common/src/main/java/org/apache/dubbo/config/nested/RestConfig.java
@@ -14,7 +14,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package org.apache.dubbo.config;
+package org.apache.dubbo.config.nested;
import org.apache.dubbo.config.support.Nested;
@@ -25,35 +25,14 @@ import java.io.Serializable;
*/
public class RestConfig implements Serializable {
- private static final long serialVersionUID = 1L;
-
- /**
- * Maximum allowed size for request bodies.
- * Limits the size of request to prevent excessively large request.
- * <p>The default value is 8MiB.
- */
- private Integer maxBodySize;
-
- /**
- * Maximum allowed size for response bodies.
- * Limits the size of responses to prevent excessively large response.
- * <p>The default value is 8MiB.
- */
- private Integer maxResponseBodySize;
+ private static final long serialVersionUID = -8068568976367034755L;
/**
* Whether path matching should be match paths with a trailing slash.
* If enabled, a method mapped to "/users" also matches to "/users/".
* <p>The default value is {@code true}.
*/
- private Boolean trailingSlashMatch;
-
- /**
- * Whether path matching should be case-sensitive.
- * If enabled, a method mapped to "/users" won't match to "/Users/".
- * <p>The default value is {@code false}.
- */
- private Boolean caseSensitiveMatch;
+ private boolean trailingSlashMatch;
/**
* Whether path matching uses suffix pattern matching (".*").
@@ -62,7 +41,14 @@ public class RestConfig implements Serializable {
* inferred from the URL suffix, e.g., ".json" for "application/json".
* <p>The default value is {@code true}.
*/
- private Boolean suffixPatternMatch;
+ private boolean suffixPatternMatch;
+
+ /**
+ * Whether path matching should be case-sensitive.
+ * If enabled, a method mapped to "/users" won't match to "/Users/".
+ * <p>The default value is {@code true}.
+ */
+ private boolean caseSensitiveMatch;
/**
* The parameter name that can be used to specify the response format.
@@ -71,49 +57,33 @@ public class RestConfig implements Serializable {
private String formatParameterName;
/**
- * The config is used to set the Global CORS configuration properties.
+ * The cors configuration.
*/
@Nested
private CorsConfig cors;
- public Integer getMaxBodySize() {
- return maxBodySize;
- }
-
- public void setMaxBodySize(Integer maxBodySize) {
- this.maxBodySize = maxBodySize;
- }
-
- public Integer getMaxResponseBodySize() {
- return maxResponseBodySize;
- }
-
- public void setMaxResponseBodySize(Integer maxResponseBodySize) {
- this.maxResponseBodySize = maxResponseBodySize;
- }
-
- public Boolean getTrailingSlashMatch() {
+ public boolean isTrailingSlashMatch() {
return trailingSlashMatch;
}
- public void setTrailingSlashMatch(Boolean trailingSlashMatch) {
+ public void setTrailingSlashMatch(boolean trailingSlashMatch) {
this.trailingSlashMatch = trailingSlashMatch;
}
- public Boolean getCaseSensitiveMatch() {
- return caseSensitiveMatch;
+ public boolean isSuffixPatternMatch() {
+ return suffixPatternMatch;
}
- public void setCaseSensitiveMatch(Boolean caseSensitiveMatch) {
- this.caseSensitiveMatch = caseSensitiveMatch;
+ public void setSuffixPatternMatch(boolean suffixPatternMatch) {
+ this.suffixPatternMatch = suffixPatternMatch;
}
- public Boolean getSuffixPatternMatch() {
- return suffixPatternMatch;
+ public boolean isCaseSensitiveMatch() {
+ return caseSensitiveMatch;
}
- public void setSuffixPatternMatch(Boolean suffixPatternMatch) {
- this.suffixPatternMatch = suffixPatternMatch;
+ public void setCaseSensitiveMatch(boolean caseSensitiveMatch) {
+ this.caseSensitiveMatch = caseSensitiveMatch;
}
public String getFormatParameterName() {
diff --git a/dubbo-common/src/main/java/org/apache/dubbo/config/nested/SamplingConfig.java b/dubbo-common/src/main/java/org/apache/dubbo/config/nested/SamplingConfig.java
index 26dc0c2ea2..aa2ed072ed 100644
--- a/dubbo-common/src/main/java/org/apache/dubbo/config/nested/SamplingConfig.java
+++ b/dubbo-common/src/main/java/org/apache/dubbo/config/nested/SamplingConfig.java
@@ -23,6 +23,8 @@ import java.io.Serializable;
*/
public class SamplingConfig implements Serializable {
+ private static final long serialVersionUID = -7456034528275916549L;
+
/**
* Probability in the range from 0.0 to 1.0 that a trace will be sampled.
*/
diff --git a/dubbo-common/src/main/java/org/apache/dubbo/config/nested/ServletConfig.java b/dubbo-common/src/main/java/org/apache/dubbo/config/nested/ServletConfig.java
new file mode 100644
index 0000000000..30b67ebf57
--- /dev/null
+++ b/dubbo-common/src/main/java/org/apache/dubbo/config/nested/ServletConfig.java
@@ -0,0 +1,81 @@
+/*
+ * 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.dubbo.config.nested;
+
+import java.io.Serializable;
+
+public class ServletConfig implements Serializable {
+
+ private static final long serialVersionUID = 1091478303358670173L;
+
+ /**
+ * Enable servlet support, requests are transport through the servlet container,
+ * which only supports unary calls due to protocol limitations
+ * <p>The default value is false.
+ */
+ private Boolean enable;
+
+ /**
+ * The timeout in milliseconds for the servlet async timeout.
+ * <p>The default value is 180_000.
+ */
+ private Integer timeout;
+
+ /**
+ * The URL patterns that the servlet filter will be registered for.
+ * <p>The default value is '/*'.
+ */
+ private String[] filterUrlPatterns;
+
+ /**
+ * The order of the servlet filter.
+ * <p>The default value is -1000000.
+ */
+ private Integer filterOrder;
+
+ public Boolean getEnable() {
+ return enable;
+ }
+
+ public void setEnable(Boolean enable) {
+ this.enable = enable;
+ }
+
+ public Integer getTimeout() {
+ return timeout;
+ }
+
+ public void setTimeout(Integer timeout) {
+ this.timeout = timeout;
+ }
+
+ public String[] getFilterUrlPatterns() {
+ return filterUrlPatterns;
+ }
+
+ public void setFilterUrlPatterns(String[] filterUrlPatterns) {
+ this.filterUrlPatterns = filterUrlPatterns;
+ }
+
+ public Integer getFilterOrder() {
+ return filterOrder;
+ }
+
+ public void setFilterOrder(Integer filterOrder) {
+ this.filterOrder = filterOrder;
+ }
+}
diff --git a/dubbo-common/src/main/java/org/apache/dubbo/config/nested/TripleConfig.java b/dubbo-common/src/main/java/org/apache/dubbo/config/nested/TripleConfig.java
index 96b2141b62..e0dca0664a 100644
--- a/dubbo-common/src/main/java/org/apache/dubbo/config/nested/TripleConfig.java
+++ b/dubbo-common/src/main/java/org/apache/dubbo/config/nested/TripleConfig.java
@@ -16,6 +16,8 @@
*/
package org.apache.dubbo.config.nested;
+import org.apache.dubbo.config.support.Nested;
+
import java.io.Serializable;
/**
@@ -26,14 +28,14 @@ public class TripleConfig implements Serializable {
private static final long serialVersionUID = -3682252713701362155L;
/**
- * Maximum allowed size for HTTP1 request bodies.
+ * Maximum allowed size for HTTP request bodies.
* Limits the size of request to prevent excessively large request.
* <p>The default value is 8MiB.
*/
private Integer maxBodySize;
/**
- * Maximum allowed size for HTTP1 response bodies.
+ * Maximum allowed size for HTTP response bodies.
* Limits the size of responses to prevent excessively large response.
* <p>The default value is 8MiB.
*/
@@ -41,10 +43,11 @@ public class TripleConfig implements Serializable {
/**
* Set the maximum chunk size.
- * HTTP requests and responses can be quite large, in which case it's better to process the data as a stream of
- * chunks.
+ * HTTP requests and responses can be quite large,
+ * in which case it's better to process the data as a stream of chunks.
* This sets the limit, in bytes, at which Netty will send a chunk down the pipeline.
* <p>The default value is 8MiB.
+ * <p>For HTTP/1
*/
private Integer maxChunkSize;
@@ -53,6 +56,7 @@ public class TripleConfig implements Serializable {
* This limits how much memory Netty will use when parsing HTTP header key-value pairs.
* You would typically set this to the same value as {@link #setMaxInitialLineLength(Integer)}.
* <p>The default value is 8KiB.
+ * <p>For HTTP/1
*/
private Integer maxHeaderSize;
@@ -61,156 +65,62 @@ public class TripleConfig implements Serializable {
* This limits how much memory Netty will use when parsed the initial HTTP header line.
* You would typically set this to the same value as {@link #setMaxHeaderSize(Integer)}.
* <p>The default value is 4096.
+ * <p>For HTTP/1
*/
private Integer maxInitialLineLength;
/**
* Set the initial size of the temporary buffer used when parsing the lines of the HTTP headers.
* <p>The default value is 16384 octets.
+ * <p>For HTTP/1
*/
private Integer initialBufferSize;
/**
* The header table size.
+ * <p>For HTTP/1
*/
private Integer headerTableSize;
/**
- * Whether to enable push, default is false.
+ * Whether to enable push
+ * <p>The default value is false.
+ * <p>For HTTP/2
*/
private Boolean enablePush;
/**
* Maximum concurrent streams.
+ * <p>For HTTP/2
*/
private Integer maxConcurrentStreams;
/**
* Initial window size.
+ * <p>For HTTP/2
*/
private Integer initialWindowSize;
/**
* Maximum frame size.
+ * <p>For HTTP/2
*/
private Integer maxFrameSize;
/**
* Maximum header list size.
+ * <p>For HTTP/2
*/
private Integer maxHeaderListSize;
- /**
- * Enable http3 support
- * <p>The default value is false.
- */
- private Boolean enableHttp3;
-
- /**
- * See <a href="https://docs.rs/quiche/0.6.0/quiche/struct.Config.html#method.set_initial_max_data">set_initial_max_data</a>.
- * <p>The default value is 8MiB.
- */
- private Integer http3InitialMaxData;
-
- /**
- * If configured this will enable <a href="https://tools.ietf.org/html/draft-ietf-quic-datagram-01">Datagram support.</a>
- */
- private Integer http3RecvQueueLen;
-
- /**
- * If configured this will enable <a href="https://tools.ietf.org/html/draft-ietf-quic-datagram-01">Datagram support.</a>
- */
- private Integer http3SendQueueLen;
-
- /**
- * See
- * <a href="https://docs.rs/quiche/0.6.0/quiche/struct.Config.html#method.set_initial_max_stream_data_bidi_local">set_initial_max_stream_data_bidi_local</a>.
- * <p>The default value is 1MiB.
- */
- private Integer http3InitialMaxStreamDataBidiLocal;
-
- /**
- * See
- * <a href="https://docs.rs/quiche/0.6.0/quiche/struct.Config.html#method.set_initial_max_stream_data_bidi_remote">set_initial_max_stream_data_bidi_remote</a>.
- * <p>The default value is 1MiB.
- */
- private Integer http3InitialMaxStreamDataBidiRemote;
-
- /**
- * See
- * <a href="https://docs.rs/quiche/0.6.0/quiche/struct.Config.html#method.set_initial_max_stream_data_uni">set_initial_max_stream_data_uni</a>.
- * <p>The default value is 0.
- */
- private Integer http3InitialMaxStreamDataUni;
+ @Nested
+ private RestConfig rest;
- /**
- * See
- * <a href="https://docs.rs/quiche/0.6.0/quiche/struct.Config.html#method.set_initial_max_streams_bidi">set_initial_max_streams_bidi</a>.
- * <p>The default value is 1B(2^30).
- */
- private Long http3InitialMaxStreamsBidi;
+ @Nested
+ private Http3Config http3;
- /**
- * See
- * <a href="https://docs.rs/quiche/0.6.0/quiche/struct.Config.html#method.set_initial_max_streams_uni">set_initial_max_streams_uni</a>.
- * <p>
- * <p>The default value is 1B(2^30).
- */
- private Long http3InitialMaxStreamsUni;
-
- /**
- * See
- * <a href="https://docs.rs/quiche/0.6.0/quiche/struct.Config.html#method.set_ack_delay_exponent">set_ack_delay_exponent</a>.
- * <p>The default value is 3.
- */
- private Integer http3MaxAckDelayExponent;
-
- /**
- * See
- * <a href="https://docs.rs/quiche/0.6.0/quiche/struct.Config.html#method.set_max_ack_delay">set_max_ack_delay</a>.
- * <p>The default value is 25 milliseconds.
- */
- private Integer http3MaxAckDelay;
-
- /**
- * See
- * <a href="https://docs.rs/quiche/0.6.0/quiche/struct.Config.html#method.set_disable_active_migration">set_disable_active_migration</a>.
- * <p>The default value is {@code false}.
- */
- private Boolean http3DisableActiveMigration;
-
- /**
- * See
- * <a href="https://docs.rs/quiche/0.6.0/quiche/struct.Config.html#method.enable_hystart">enable_hystart</a>.
- * <p>The default value is {@code true}.
- */
- private Boolean http3EnableHystart;
-
- /**
- * Sets the congestion control algorithm to use.
- * <p>Supported algorithms are {@code "RENO"} or {@code "CUBIC"} or {@code "BBR"}.
- * <p>The default value is {@code "CUBIC"}.
- */
- private String http3CcAlgorithm;
-
- /**
- * Enable servlet support, requests are transport through the servlet container,
- * which only supports unary calls due to protocol limitations
- * <p>The default value is false.
- */
- private Boolean enableServlet;
-
- /**
- * The URL patterns that the servlet filter will be registered for.
- * <p>The default value is '/*'.
- */
- private String[] servletFilterUrlPatterns;
-
- /**
- * The order of the servlet filter.
- * <p>The default value is -1000000.
- */
- private Integer servletFilterOrder;
+ @Nested
+ private ServletConfig servlet;
public Integer getMaxBodySize() {
return maxBodySize;
@@ -308,140 +218,28 @@ public class TripleConfig implements Serializable {
this.maxHeaderListSize = maxHeaderListSize;
}
- public Boolean getEnableHttp3() {
- return enableHttp3;
- }
-
- public void setEnableHttp3(Boolean enableHttp3) {
- this.enableHttp3 = enableHttp3;
- }
-
- public Integer getHttp3InitialMaxData() {
- return http3InitialMaxData;
- }
-
- public void setHttp3InitialMaxData(Integer http3InitialMaxData) {
- this.http3InitialMaxData = http3InitialMaxData;
- }
-
- public Integer getHttp3RecvQueueLen() {
- return http3RecvQueueLen;
- }
-
- public void setHttp3RecvQueueLen(Integer http3RecvQueueLen) {
- this.http3RecvQueueLen = http3RecvQueueLen;
- }
-
- public Integer getHttp3SendQueueLen() {
- return http3SendQueueLen;
- }
-
- public void setHttp3SendQueueLen(Integer http3SendQueueLen) {
- this.http3SendQueueLen = http3SendQueueLen;
- }
-
- public Integer getHttp3InitialMaxStreamDataBidiLocal() {
- return http3InitialMaxStreamDataBidiLocal;
- }
-
- public void setHttp3InitialMaxStreamDataBidiLocal(Integer http3InitialMaxStreamDataBidiLocal) {
- this.http3InitialMaxStreamDataBidiLocal = http3InitialMaxStreamDataBidiLocal;
- }
-
- public Integer getHttp3InitialMaxStreamDataBidiRemote() {
- return http3InitialMaxStreamDataBidiRemote;
- }
-
- public void setHttp3InitialMaxStreamDataBidiRemote(Integer http3InitialMaxStreamDataBidiRemote) {
- this.http3InitialMaxStreamDataBidiRemote = http3InitialMaxStreamDataBidiRemote;
- }
-
- public Integer getHttp3InitialMaxStreamDataUni() {
- return http3InitialMaxStreamDataUni;
- }
-
- public void setHttp3InitialMaxStreamDataUni(Integer http3InitialMaxStreamDataUni) {
- this.http3InitialMaxStreamDataUni = http3InitialMaxStreamDataUni;
- }
-
- public Long getHttp3InitialMaxStreamsBidi() {
- return http3InitialMaxStreamsBidi;
- }
-
- public void setHttp3InitialMaxStreamsBidi(Long http3InitialMaxStreamsBidi) {
- this.http3InitialMaxStreamsBidi = http3InitialMaxStreamsBidi;
- }
-
- public Long getHttp3InitialMaxStreamsUni() {
- return http3InitialMaxStreamsUni;
- }
-
- public void setHttp3InitialMaxStreamsUni(Long http3InitialMaxStreamsUni) {
- this.http3InitialMaxStreamsUni = http3InitialMaxStreamsUni;
- }
-
- public Integer getHttp3MaxAckDelayExponent() {
- return http3MaxAckDelayExponent;
- }
-
- public void setHttp3MaxAckDelayExponent(Integer http3MaxAckDelayExponent) {
- this.http3MaxAckDelayExponent = http3MaxAckDelayExponent;
- }
-
- public Integer getHttp3MaxAckDelay() {
- return http3MaxAckDelay;
- }
-
- public void setHttp3MaxAckDelay(Integer http3MaxAckDelay) {
- this.http3MaxAckDelay = http3MaxAckDelay;
- }
-
- public Boolean getHttp3DisableActiveMigration() {
- return http3DisableActiveMigration;
- }
-
- public void setHttp3DisableActiveMigration(Boolean http3DisableActiveMigration) {
- this.http3DisableActiveMigration = http3DisableActiveMigration;
- }
-
- public Boolean getHttp3EnableHystart() {
- return http3EnableHystart;
- }
-
- public void setHttp3EnableHystart(Boolean http3EnableHystart) {
- this.http3EnableHystart = http3EnableHystart;
- }
-
- public String getHttp3CcAlgorithm() {
- return http3CcAlgorithm;
- }
-
- public void setHttp3CcAlgorithm(String http3CcAlgorithm) {
- this.http3CcAlgorithm = http3CcAlgorithm;
- }
-
- public Boolean getEnableServlet() {
- return enableServlet;
+ public RestConfig getRest() {
+ return rest;
}
- public void setEnableServlet(Boolean enableServlet) {
- this.enableServlet = enableServlet;
+ public void setRest(RestConfig rest) {
+ this.rest = rest;
}
- public String[] getServletFilterUrlPatterns() {
- return servletFilterUrlPatterns;
+ public Http3Config getHttp3() {
+ return http3;
}
- public void setServletFilterUrlPatterns(String[] servletFilterUrlPatterns) {
- this.servletFilterUrlPatterns = servletFilterUrlPatterns;
+ public void setHttp3(Http3Config http3) {
+ this.http3 = http3;
}
- public Integer getServletFilterOrder() {
- return servletFilterOrder;
+ public ServletConfig getServlet() {
+ return servlet;
}
- public void setServletFilterOrder(Integer servletFilterOrder) {
- this.servletFilterOrder = servletFilterOrder;
+ public void setServlet(ServletConfig servlet) {
+ this.servlet = servlet;
}
public void checkDefault() {
diff --git a/dubbo-common/src/main/java/org/apache/dubbo/rpc/model/ReflectionServiceDescriptor.java b/dubbo-common/src/main/java/org/apache/dubbo/rpc/model/ReflectionServiceDescriptor.java
index dc04146933..9a3c0c491b 100644
--- a/dubbo-common/src/main/java/org/apache/dubbo/rpc/model/ReflectionServiceDescriptor.java
+++ b/dubbo-common/src/main/java/org/apache/dubbo/rpc/model/ReflectionServiceDescriptor.java
@@ -48,7 +48,8 @@ public class ReflectionServiceDescriptor implements ServiceDescriptor {
}
public void addMethod(MethodDescriptor methodDescriptor) {
- methods.put(methodDescriptor.getMethodName(), Collections.singletonList(methodDescriptor));
+ methods.computeIfAbsent(methodDescriptor.getMethodName(), k -> new ArrayList<>(1))
+ .add(methodDescriptor);
}
public ReflectionServiceDescriptor(Class<?> interfaceClass) {
diff --git a/dubbo-demo/dubbo-demo-spring-boot/dubbo-demo-spring-boot-provider/src/main/resources/application.yml b/dubbo-demo/dubbo-demo-spring-boot/dubbo-demo-spring-boot-provider/src/main/resources/application.yml
index b75b8578e9..e2642b6612 100644
--- a/dubbo-demo/dubbo-demo-spring-boot/dubbo-demo-spring-boot-provider/src/main/resources/application.yml
+++ b/dubbo-demo/dubbo-demo-spring-boot/dubbo-demo-spring-boot-provider/src/main/resources/application.yml
@@ -24,9 +24,11 @@ dubbo:
qos-enable: false
protocol:
name: tri
- port: ${server.port}
+ port: -1
+ #port: ${server.port}
triple:
- enable-servlet: true
+ servlet:
+ enable: true
registry:
id: zk-registry
address: zookeeper://127.0.0.1:2181
diff --git a/dubbo-dependencies-bom/pom.xml b/dubbo-dependencies-bom/pom.xml
index ae59978c1a..a29d7da02c 100644
--- a/dubbo-dependencies-bom/pom.xml
+++ b/dubbo-dependencies-bom/pom.xml
@@ -17,7 +17,6 @@
-->
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
-
<parent>
<groupId>org.apache</groupId>
<artifactId>apache</artifactId>
@@ -109,7 +108,7 @@
<protobuf-java_version>3.25.3</protobuf-java_version>
<javax_annotation-api_version>1.3.2</javax_annotation-api_version>
<servlet_version>3.1.0</servlet_version>
- <servlet6_version>6.1.0</servlet6_version>
+ <jakarta_servlet_version>6.1.0</jakarta_servlet_version>
<jetty_version>9.4.54.v20240208</jetty_version>
<validation_new_version>3.1.0</validation_new_version>
<validation_version>1.1.0.Final</validation_version>
@@ -152,8 +151,7 @@
<log4j2_version>2.23.1</log4j2_version>
<commons_io_version>2.16.1</commons_io_version>
<commons-codec_version>1.16.0</commons-codec_version>
-
- <embedded_redis_version>1.4.3</embedded_redis_version>
+ <groovy_version>4.0.21</groovy_version>
<!-- Test libs -->
<junit_jupiter_version>5.9.3</junit_jupiter_version>
@@ -161,7 +159,9 @@
<awaitility_version>4.2.0</awaitility_version>
<hamcrest_version>2.2</hamcrest_version>
<cglib_version>2.2.2</cglib_version>
+ <embedded_redis_version>1.4.3</embedded_redis_version>
<mockito_version>4.11.0</mockito_version>
+ <spock_version>2.3-groovy-4.0</spock_version>
<jaxb_version>2.2.7</jaxb_version>
<activation_version>1.2.0</activation_version>
@@ -183,10 +183,11 @@
<spotless.action>check</spotless.action>
<dubbo-shared-resources.version>1.0.0</dubbo-shared-resources.version>
<palantirJavaFormat.version>2.38.0</palantirJavaFormat.version>
- <revision>3.3.0-beta.5-SNAPSHOT</revision>
<open_feign_version>3.1.5</open_feign_version>
<jakarta.xml.bind-api.version>4.0.1</jakarta.xml.bind-api.version>
<jaxb-runtime.version>2.3.3-b02</jaxb-runtime.version>
+
+ <revision>3.3.0-beta.5-SNAPSHOT</revision>
</properties>
<dependencyManagement>
@@ -408,7 +409,7 @@
<dependency>
<groupId>jakarta.servlet</groupId>
<artifactId>jakarta.servlet-api</artifactId>
- <version>${servlet6_version}</version>
+ <version>${jakarta_servlet_version}</version>
<scope>provided</scope>
</dependency>
<dependency>
@@ -616,6 +617,11 @@
<artifactId>api</artifactId>
<version>${envoy_api_version}</version>
</dependency>
+ <dependency>
+ <groupId>org.apache.groovy</groupId>
+ <artifactId>groovy</artifactId>
+ <version>${groovy_version}</version>
+ </dependency>
<!-- for dubbo-rpc-webservice -->
<dependency>
@@ -742,6 +748,24 @@
<version>${spring_version}</version>
<scope>test</scope>
</dependency>
+ <dependency>
+ <groupId>org.spockframework</groupId>
+ <artifactId>spock-core</artifactId>
+ <version>${spock_version}</version>
+ <scope>test</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.spockframework</groupId>
+ <artifactId>spock-spring</artifactId>
+ <version>${spock_version}</version>
+ <scope>test</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.spockframework</groupId>
+ <artifactId>spock-junit4</artifactId>
+ <version>${spock_version}</version>
+ <scope>test</scope>
+ </dependency>
<dependency>
<groupId>com.google.code.gson</groupId>
diff --git a/dubbo-dependencies/dubbo-dependencies-zookeeper-curator5/pom.xml b/dubbo-dependencies/dubbo-dependencies-zookeeper-curator5/pom.xml
index afdc096434..49d878d74e 100644
--- a/dubbo-dependencies/dubbo-dependencies-zookeeper-curator5/pom.xml
+++ b/dubbo-dependencies/dubbo-dependencies-zookeeper-curator5/pom.xml
@@ -17,7 +17,6 @@
-->
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
-
<parent>
<groupId>org.apache</groupId>
<artifactId>apache</artifactId>
@@ -31,7 +30,6 @@
<packaging>pom</packaging>
<properties>
- <revision>3.3.0-beta.5-SNAPSHOT</revision>
<maven_flatten_version>1.6.0</maven_flatten_version>
<slf4j_version>1.7.36</slf4j_version>
<curator5_version>5.1.0</curator5_version>
@@ -40,6 +38,8 @@
<spotless.action>check</spotless.action>
<dubbo-shared-resources.version>1.0.0</dubbo-shared-resources.version>
<palantirJavaFormat.version>2.38.0</palantirJavaFormat.version>
+
+ <revision>3.3.0-beta.5-SNAPSHOT</revision>
</properties>
<dependencies>
diff --git a/dubbo-distribution/dubbo-all-shaded/pom.xml b/dubbo-distribution/dubbo-all-shaded/pom.xml
index 0eb7510b02..f1c94a1336 100644
--- a/dubbo-distribution/dubbo-all-shaded/pom.xml
+++ b/dubbo-distribution/dubbo-all-shaded/pom.xml
@@ -119,13 +119,6 @@
<scope>compile</scope>
<optional>true</optional>
</dependency>
- <dependency>
- <groupId>org.apache.dubbo</groupId>
- <artifactId>dubbo-rest-servlet</artifactId>
- <version>${project.version}</version>
- <scope>compile</scope>
- <optional>true</optional>
- </dependency>
<dependency>
<groupId>org.apache.dubbo</groupId>
<artifactId>dubbo-rest-spring</artifactId>
@@ -480,7 +473,6 @@
<include>org.apache.dubbo:dubbo-rpc-injvm</include>
<include>org.apache.dubbo:dubbo-rpc-triple</include>
<include>org.apache.dubbo:dubbo-rest-jaxrs</include>
- <include>org.apache.dubbo:dubbo-rest-servlet</include>
<include>org.apache.dubbo:dubbo-rest-spring</include>
<include>org.apache.dubbo:dubbo-triple-servlet</include>
<include>org.apache.dubbo:dubbo-serialization-api</include>
diff --git a/dubbo-distribution/dubbo-all/pom.xml b/dubbo-distribution/dubbo-all/pom.xml
index 99b7a90c0b..c0c2e69313 100644
--- a/dubbo-distribution/dubbo-all/pom.xml
+++ b/dubbo-distribution/dubbo-all/pom.xml
@@ -269,13 +269,6 @@
<scope>compile</scope>
<optional>true</optional>
</dependency>
- <dependency>
- <groupId>org.apache.dubbo</groupId>
- <artifactId>dubbo-rest-servlet</artifactId>
- <version>${project.version}</version>
- <scope>compile</scope>
- <optional>true</optional>
- </dependency>
<dependency>
<groupId>org.apache.dubbo</groupId>
<artifactId>dubbo-rest-spring</artifactId>
@@ -517,7 +510,6 @@
<include>org.apache.dubbo:dubbo-rpc-injvm</include>
<include>org.apache.dubbo:dubbo-rpc-triple</include>
<include>org.apache.dubbo:dubbo-rest-jaxrs</include>
- <include>org.apache.dubbo:dubbo-rest-servlet</include>
<include>org.apache.dubbo:dubbo-rest-spring</include>
<include>org.apache.dubbo:dubbo-triple-servlet</include>
<include>org.apache.dubbo:dubbo-serialization-api</include>
diff --git a/dubbo-distribution/dubbo-bom/pom.xml b/dubbo-distribution/dubbo-bom/pom.xml
index 48afc409d4..2e805de08f 100644
--- a/dubbo-distribution/dubbo-bom/pom.xml
+++ b/dubbo-distribution/dubbo-bom/pom.xml
@@ -329,11 +329,6 @@
<artifactId>dubbo-rest-jaxrs</artifactId>
<version>${project.version}</version>
</dependency>
- <dependency>
- <groupId>org.apache.dubbo</groupId>
- <artifactId>dubbo-rest-servlet</artifactId>
- <version>${project.version}</version>
- </dependency>
<dependency>
<groupId>org.apache.dubbo</groupId>
<artifactId>dubbo-rest-spring</artifactId>
diff --git a/dubbo-metrics/dubbo-tracing/src/test/java/org/apache/dubbo/tracing/filter/AbstractObservationFilterTest.java b/dubbo-metrics/dubbo-tracing/src/test/java/org/apache/dubbo/tracing/filter/AbstractObservationFilterTest.java
deleted file mode 100644
index ce56ae3662..0000000000
--- a/dubbo-metrics/dubbo-tracing/src/test/java/org/apache/dubbo/tracing/filter/AbstractObservationFilterTest.java
+++ /dev/null
@@ -1,84 +0,0 @@
-/*
- * 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.dubbo.tracing.filter;
-
-import org.apache.dubbo.config.ApplicationConfig;
-import org.apache.dubbo.config.TracingConfig;
-import org.apache.dubbo.rpc.AppResponse;
-import org.apache.dubbo.rpc.BaseFilter;
-import org.apache.dubbo.rpc.Invoker;
-import org.apache.dubbo.rpc.RpcInvocation;
-import org.apache.dubbo.rpc.model.ApplicationModel;
-import org.apache.dubbo.tracing.MockInvocation;
-
-import io.micrometer.tracing.test.SampleTestRunner;
-import org.junit.jupiter.api.AfterEach;
-
-import static org.mockito.BDDMockito.given;
-import static org.mockito.Mockito.mock;
-
-abstract class AbstractObservationFilterTest extends SampleTestRunner {
-
- ApplicationModel applicationModel;
- RpcInvocation invocation;
-
- BaseFilter filter;
-
- Invoker<?> invoker = mock(Invoker.class);
-
- static final String INTERFACE_NAME = "org.apache.dubbo.MockInterface";
- static final String METHOD_NAME = "mockMethod";
- static final String GROUP = "mockGroup";
- static final String VERSION = "1.0.0";
-
- @AfterEach
- public void teardown() {
- if (applicationModel != null) {
- applicationModel.destroy();
- }
- }
-
- abstract BaseFilter createFilter(ApplicationModel applicationModel);
-
- void setupConfig() {
- ApplicationConfig config = new ApplicationConfig();
- config.setName("MockObservations");
-
- applicationModel = ApplicationModel.defaultModel();
- applicationModel.getApplicationConfigManager().setApplication(config);
-
- invocation = new RpcInvocation(new MockInvocation());
- invocation.addInvokedInvoker(invoker);
-
- applicationModel.getBeanFactory().registerBean(getObservationRegistry());
- TracingConfig tracingConfig = new TracingConfig();
- tracingConfig.setEnabled(true);
- applicationModel.getApplicationConfigManager().setTracing(tracingConfig);
-
- filter = createFilter(applicationModel);
-
- given(invoker.invoke(invocation)).willReturn(new AppResponse("success"));
-
- initParam();
- }
-
- private void initParam() {
- invocation.setTargetServiceUniqueName(GROUP + "/" + INTERFACE_NAME + ":" + VERSION);
- invocation.setMethodName(METHOD_NAME);
- invocation.setParameterTypes(new Class[] {String.class});
- }
-}
diff --git a/dubbo-metrics/dubbo-tracing/src/test/java/org/apache/dubbo/tracing/filter/ObservationReceiverFilterTest.java b/dubbo-metrics/dubbo-tracing/src/test/java/org/apache/dubbo/tracing/filter/ObservationReceiverFilterTest.java
deleted file mode 100644
index 76eac81ad9..0000000000
--- a/dubbo-metrics/dubbo-tracing/src/test/java/org/apache/dubbo/tracing/filter/ObservationReceiverFilterTest.java
+++ /dev/null
@@ -1,128 +0,0 @@
-/*
- * 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.dubbo.tracing.filter;
-
-import org.apache.dubbo.common.URL;
-import org.apache.dubbo.rpc.AppResponse;
-import org.apache.dubbo.rpc.Filter;
-import org.apache.dubbo.rpc.Invocation;
-import org.apache.dubbo.rpc.Invoker;
-import org.apache.dubbo.rpc.Result;
-import org.apache.dubbo.rpc.RpcContext;
-import org.apache.dubbo.rpc.RpcException;
-import org.apache.dubbo.rpc.model.ApplicationModel;
-
-import io.micrometer.common.KeyValues;
-import io.micrometer.core.tck.MeterRegistryAssert;
-import io.micrometer.tracing.Span;
-import io.micrometer.tracing.Tracer;
-import io.micrometer.tracing.test.simple.SpansAssert;
-import org.assertj.core.api.BDDAssertions;
-
-class ObservationReceiverFilterTest extends AbstractObservationFilterTest {
-
- @Override
- public SampleTestRunnerConsumer yourCode() {
- return (buildingBlocks, meterRegistry) -> {
- setupConfig();
- setupAttachments(buildingBlocks.getTracer());
- invoker = new AssertingInvoker(buildingBlocks.getTracer());
-
- ObservationReceiverFilter senderFilter = (ObservationReceiverFilter) filter;
- senderFilter.invoke(invoker, invocation);
- senderFilter.onResponse(null, invoker, invocation);
-
- MeterRegistryAssert.then(meterRegistry)
- .hasMeterWithNameAndTags(
- "rpc.server.duration",
- KeyValues.of(
- "rpc.method",
- "mockMethod",
- "rpc.service",
- "DemoService",
- "rpc.system",
- "apache_dubbo"));
- SpansAssert.then(buildingBlocks.getFinishedSpans())
- .hasASpanWithNameIgnoreCase("DemoService/mockMethod", spanAssert -> spanAssert
- .hasTag("rpc.method", "mockMethod")
- .hasTag("rpc.service", "DemoService")
- .hasTag("rpc.system", "apache_dubbo"));
- };
- }
-
- void setupAttachments(Tracer tracer) {
- RpcContext.getServerAttachment()
- .setUrl(URL.valueOf("test://test:11/test?accesslog=true&group=dubbo&version=1.1&side=consumer"));
- RpcContext.getServerAttachment().setMethodName("foo");
- RpcContext.getServerAttachment().setRemoteAddress("foo.bar.com", 8080);
- RpcContext.getServerAttachment()
- .setAttachment("X-B3-TraceId", tracer.currentSpan().context().traceId());
- RpcContext.getServerAttachment()
- .setAttachment("X-B3-SpanId", tracer.currentSpan().context().spanId());
- RpcContext.getServerAttachment().setAttachment("X-B3-Sampled", "1");
- }
-
- @Override
- Filter createFilter(ApplicationModel applicationModel) {
- return new ObservationReceiverFilter(applicationModel);
- }
-
- static class AssertingInvoker implements Invoker {
-
- private final String expectedTraceId;
-
- private final String parentSpanId;
-
- private final Tracer tracer;
-
- AssertingInvoker(Tracer tracer) {
- this.tracer = tracer;
- this.expectedTraceId = tracer.currentSpan().context().traceId();
- this.parentSpanId = tracer.currentSpan().context().spanId();
- }
-
- @Override
- public URL getUrl() {
- return null;
- }
-
- @Override
- public boolean isAvailable() {
- return true;
- }
-
- @Override
- public void destroy() {}
-
- @Override
- public Class getInterface() {
- return AssertingInvoker.class;
- }
-
- @Override
- public Result invoke(Invocation invocation) throws RpcException {
- Span span = this.tracer.currentSpan();
- BDDAssertions.then(span.context().traceId())
- .as("Should propagate the trace id from the attributes")
- .isEqualTo(this.expectedTraceId);
- BDDAssertions.then(span.context().spanId())
- .as("A child span must be created")
- .isNotEqualTo(this.parentSpanId);
- return new AppResponse("OK");
- }
- }
-}
diff --git a/dubbo-metrics/dubbo-tracing/src/test/java/org/apache/dubbo/tracing/filter/ObservationSenderFilterTest.java b/dubbo-metrics/dubbo-tracing/src/test/java/org/apache/dubbo/tracing/filter/ObservationSenderFilterTest.java
deleted file mode 100644
index 8239c4128e..0000000000
--- a/dubbo-metrics/dubbo-tracing/src/test/java/org/apache/dubbo/tracing/filter/ObservationSenderFilterTest.java
+++ /dev/null
@@ -1,76 +0,0 @@
-/*
- * 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.dubbo.tracing.filter;
-
-import org.apache.dubbo.common.URL;
-import org.apache.dubbo.rpc.RpcContext;
-import org.apache.dubbo.rpc.cluster.filter.ClusterFilter;
-import org.apache.dubbo.rpc.model.ApplicationModel;
-
-import io.micrometer.common.KeyValues;
-import io.micrometer.core.tck.MeterRegistryAssert;
-import io.micrometer.tracing.test.simple.SpansAssert;
-import org.assertj.core.api.BDDAssertions;
-
-class ObservationSenderFilterTest extends AbstractObservationFilterTest {
-
- @Override
- public SampleTestRunnerConsumer yourCode() {
- return (buildingBlocks, meterRegistry) -> {
- setupConfig();
- setupAttachments();
-
- ObservationSenderFilter senderFilter = (ObservationSenderFilter) filter;
- senderFilter.invoke(invoker, invocation);
- senderFilter.onResponse(null, invoker, invocation);
-
- BDDAssertions.then(invocation.getObjectAttachment("X-B3-TraceId")).isNotNull();
- MeterRegistryAssert.then(meterRegistry)
- .hasMeterWithNameAndTags(
- "rpc.client.duration",
- KeyValues.of(
- "net.peer.name",
- "foo.bar.com",
- "net.peer.port",
- "8080",
- "rpc.method",
- "mockMethod",
- "rpc.service",
- "DemoService",
- "rpc.system",
- "apache_dubbo"));
- SpansAssert.then(buildingBlocks.getFinishedSpans())
- .hasASpanWithNameIgnoreCase("DemoService/mockMethod", spanAssert -> spanAssert
- .hasTag("net.peer.name", "foo.bar.com")
- .hasTag("net.peer.port", "8080")
- .hasTag("rpc.method", "mockMethod")
- .hasTag("rpc.service", "DemoService")
- .hasTag("rpc.system", "apache_dubbo"));
- };
- }
-
- void setupAttachments() {
- RpcContext.getClientAttachment()
- .setUrl(URL.valueOf("test://test:11/test?accesslog=true&group=dubbo&version=1.1&side=consumer"));
- RpcContext.getClientAttachment().setRemoteAddress("foo.bar.com", 8080);
- }
-
- @Override
- ClusterFilter createFilter(ApplicationModel applicationModel) {
- return new ObservationSenderFilter(applicationModel);
- }
-}
diff --git a/dubbo-plugin/dubbo-rest-jaxrs/pom.xml b/dubbo-plugin/dubbo-rest-jaxrs/pom.xml
index 6370496dfa..32b43a2d00 100644
--- a/dubbo-plugin/dubbo-rest-jaxrs/pom.xml
+++ b/dubbo-plugin/dubbo-rest-jaxrs/pom.xml
@@ -62,5 +62,52 @@
<artifactId>jaxb-runtime</artifactId>
<scope>test</scope>
</dependency>
+ <dependency>
+ <groupId>org.yaml</groupId>
+ <artifactId>snakeyaml</artifactId>
+ <scope>test</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.apache.dubbo</groupId>
+ <artifactId>dubbo-rpc-triple</artifactId>
+ <version>${project.version}</version>
+ <type>test-jar</type>
+ <scope>test</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.spockframework</groupId>
+ <artifactId>spock-core</artifactId>
+ <scope>test</scope>
+ </dependency>
</dependencies>
+
+ <build>
+ <plugins>
+ <plugin>
+ <groupId>org.codehaus.gmavenplus</groupId>
+ <artifactId>gmavenplus-plugin</artifactId>
+ <executions>
+ <execution>
+ <goals>
+ <goal>compileTests</goal>
+ </goals>
+ </execution>
+ </executions>
+ </plugin>
+ </plugins>
+ </build>
+
+ <profiles>
+ <profile>
+ <id>rpc-rest-test</id>
+ <dependencies>
+ <dependency>
+ <groupId>org.apache.dubbo</groupId>
+ <artifactId>dubbo-rpc-rest</artifactId>
+ <version>3.3.0-beta.2</version>
+ <scope>test</scope>
+ </dependency>
+ </dependencies>
+ </profile>
+ </profiles>
</project>
diff --git a/dubbo-plugin/dubbo-rest-jaxrs/src/main/java/org/apache/dubbo/rpc/protocol/tri/rest/support/jaxrs/BeanArgumentBinder.java b/dubbo-plugin/dubbo-rest-jaxrs/src/main/java/org/apache/dubbo/rpc/protocol/tri/rest/support/jaxrs/BeanArgumentBinder.java
index 294875bd95..90f0da7955 100644
--- a/dubbo-plugin/dubbo-rest-jaxrs/src/main/java/org/apache/dubbo/rpc/protocol/tri/rest/support/jaxrs/BeanArgumentBinder.java
+++ b/dubbo-plugin/dubbo-rest-jaxrs/src/main/java/org/apache/dubbo/rpc/protocol/tri/rest/support/jaxrs/BeanArgumentBinder.java
@@ -27,254 +27,90 @@ import org.apache.dubbo.rpc.protocol.tri.rest.RestException;
import org.apache.dubbo.rpc.protocol.tri.rest.argument.ArgumentResolver;
import org.apache.dubbo.rpc.protocol.tri.rest.argument.CompositeArgumentResolver;
import org.apache.dubbo.rpc.protocol.tri.rest.mapping.meta.AnnotationMeta;
+import org.apache.dubbo.rpc.protocol.tri.rest.mapping.meta.BeanMeta;
+import org.apache.dubbo.rpc.protocol.tri.rest.mapping.meta.BeanMeta.ConstructorMeta;
+import org.apache.dubbo.rpc.protocol.tri.rest.mapping.meta.BeanMeta.FieldMeta;
+import org.apache.dubbo.rpc.protocol.tri.rest.mapping.meta.BeanMeta.SetMethodMeta;
import org.apache.dubbo.rpc.protocol.tri.rest.mapping.meta.ParameterMeta;
-import org.apache.dubbo.rpc.protocol.tri.rest.util.RestToolKit;
+import org.apache.dubbo.rpc.protocol.tri.rest.util.TypeUtils;
-import java.lang.reflect.AnnotatedElement;
-import java.lang.reflect.Constructor;
-import java.lang.reflect.Field;
-import java.lang.reflect.Method;
-import java.lang.reflect.Modifier;
-import java.lang.reflect.Parameter;
-import java.lang.reflect.Type;
-import java.util.ArrayList;
-import java.util.List;
+import java.util.HashSet;
import java.util.Map;
+import java.util.Set;
final class BeanArgumentBinder {
- private final ArgumentResolver argumentResolver;
+ private static final ThreadLocal<Set<Class<?>>> LOCAL = new ThreadLocal<>();
private final Map<Pair<Class<?>, String>, BeanMeta> cache = CollectionUtils.newConcurrentHashMap();
+ private final ArgumentResolver argumentResolver;
BeanArgumentBinder(FrameworkModel frameworkModel) {
ScopeBeanFactory beanFactory = frameworkModel.getBeanFactory();
argumentResolver = beanFactory.getOrRegisterBean(CompositeArgumentResolver.class);
}
- public Object bind(ParameterMeta parameter, HttpRequest request, HttpResponse response) {
+ public Object bind(ParameterMeta paramMeta, HttpRequest request, HttpResponse response) {
+ Set<Class<?>> walked = LOCAL.get();
+ if (walked == null) {
+ LOCAL.set(new HashSet<>());
+ }
try {
- return resolveArgument(parameter, request, response);
+ return resolveArgument(paramMeta, request, response);
} catch (Exception e) {
- throw new RestException(Messages.ARGUMENT_BIND_ERROR, parameter.getName(), parameter.getType(), e);
- }
- }
-
- private Object resolveArgument(ParameterMeta param, HttpRequest request, HttpResponse response) throws Exception {
- AnnotationMeta<?> form = param.findAnnotation(Annotations.Form);
- if (form != null || param.isHierarchyAnnotated(Annotations.BeanParam)) {
- String prefix = form == null ? null : form.getString("prefix");
- BeanMeta beanMeta = cache.computeIfAbsent(
- Pair.of(param.getActualType(), prefix),
- k -> new BeanMeta(param.getToolKit(), k.getValue(), k.getKey()));
-
- ConstructorMeta constructor = beanMeta.constructor;
- ParameterMeta[] parameters = constructor.parameters;
- int len = parameters.length;
- Object[] args = new Object[len];
- for (int i = 0; i < len; i++) {
- args[i] = resolveArgument(parameters[i], request, response);
- }
- Object bean = constructor.newInstance(args);
-
- for (FieldMeta fieldMeta : beanMeta.fields) {
- fieldMeta.set(bean, resolveArgument(fieldMeta, request, response));
- }
-
- for (SetMethodMeta methodMeta : beanMeta.methods) {
- methodMeta.invoke(bean, resolveArgument(methodMeta, request, response));
+ throw new RestException(e, Messages.ARGUMENT_BIND_ERROR, paramMeta.getName(), paramMeta.getType());
+ } finally {
+ if (walked == null) {
+ LOCAL.remove();
}
- return bean;
}
-
- return argumentResolver.resolve(param, request, response);
}
- private static class BeanMeta {
-
- private final List<FieldMeta> fields = new ArrayList<>();
- private final List<SetMethodMeta> methods = new ArrayList<>();
- private final ConstructorMeta constructor;
-
- BeanMeta(RestToolKit toolKit, String prefix, Class<?> type) {
- constructor = resolveConstructor(toolKit, prefix, type);
- resolveFieldAndMethod(toolKit, prefix, type);
- }
-
- private ConstructorMeta resolveConstructor(RestToolKit toolKit, String prefix, Class<?> type) {
- Constructor<?>[] constructors = type.getConstructors();
- Constructor<?> ct = null;
- if (constructors.length == 1) {
- ct = constructors[0];
- } else {
- try {
- ct = type.getDeclaredConstructor();
- } catch (NoSuchMethodException ignored) {
+ private Object resolveArgument(ParameterMeta paramMeta, HttpRequest request, HttpResponse response) {
+ if (paramMeta.isSimple()) {
+ return argumentResolver.resolve(paramMeta, request, response);
+ }
+
+ // Prevent infinite loops
+ if (LOCAL.get().add(paramMeta.getActualType())) {
+ AnnotationMeta<?> form = paramMeta.findAnnotation(Annotations.Form);
+ if (form != null || paramMeta.isHierarchyAnnotated(Annotations.BeanParam)) {
+ String prefix = form == null ? null : form.getString("prefix");
+ BeanMeta beanMeta = cache.computeIfAbsent(
+ Pair.of(paramMeta.getActualType(), prefix),
+ k -> new BeanMeta(paramMeta.getToolKit(), k.getValue(), k.getKey()));
+
+ ConstructorMeta constructor = beanMeta.getConstructor();
+ ParameterMeta[] parameters = constructor.getParameters();
+ Object bean;
+ int len = parameters.length;
+ if (len == 0) {
+ bean = constructor.newInstance();
+ } else {
+ Object[] args = new Object[len];
+ for (int i = 0; i < len; i++) {
+ args[i] = resolveArgument(parameters[i], request, response);
+ }
+ bean = constructor.newInstance(args);
}
- }
- if (ct == null) {
- throw new IllegalArgumentException("No available default constructor found in " + type);
- }
- return new ConstructorMeta(toolKit, prefix, ct);
- }
- private void resolveFieldAndMethod(RestToolKit toolKit, String prefix, Class<?> type) {
- if (type == Object.class) {
- return;
- }
- for (Field field : type.getDeclaredFields()) {
- int modifiers = field.getModifiers();
- if (Modifier.isStatic(modifiers) || Modifier.isFinal(modifiers)) {
- continue;
- }
- if (field.getAnnotations().length == 0) {
- continue;
- }
- if (!field.isAccessible()) {
- field.setAccessible(true);
- }
- fields.add(new FieldMeta(toolKit, prefix, field));
- }
- for (Method method : type.getDeclaredMethods()) {
- if (method.getParameterCount() != 1) {
- continue;
+ Set<String> resolved = new HashSet<>();
+ for (FieldMeta fieldMeta : beanMeta.getFields()) {
+ resolved.add(fieldMeta.getName());
+ fieldMeta.setValue(bean, resolveArgument(fieldMeta, request, response));
}
- int modifiers = method.getModifiers();
- if ((modifiers & (Modifier.PUBLIC | Modifier.ABSTRACT | Modifier.STATIC)) == Modifier.PUBLIC) {
- Parameter parameter = method.getParameters()[0];
- String name = method.getName();
- if (name.length() > 3 && name.startsWith("set")) {
- name = Character.toLowerCase(name.charAt(3)) + name.substring(4);
- methods.add(new SetMethodMeta(toolKit, method, parameter, prefix, name));
+
+ for (SetMethodMeta methodMeta : beanMeta.getMethods()) {
+ if (resolved.contains(methodMeta.getName())) {
+ continue;
}
+ methodMeta.setValue(bean, resolveArgument(methodMeta, request, response));
}
- }
- resolveFieldAndMethod(toolKit, prefix, type.getSuperclass());
- }
- }
-
- private static final class ConstructorMeta {
- private final Constructor<?> constructor;
- private final ConstructorParameterMeta[] parameters;
-
- ConstructorMeta(RestToolKit toolKit, String prefix, Constructor<?> constructor) {
- this.constructor = constructor;
- parameters = initParameters(toolKit, prefix, constructor);
- }
-
- private ConstructorParameterMeta[] initParameters(RestToolKit toolKit, String prefix, Constructor<?> ct) {
- Parameter[] cps = ct.getParameters();
- int len = cps.length;
- ConstructorParameterMeta[] parameters = new ConstructorParameterMeta[len];
- for (int i = 0; i < len; i++) {
- parameters[i] = new ConstructorParameterMeta(toolKit, cps[i], prefix);
+ return bean;
}
- return parameters;
}
- Object newInstance(Object[] args) throws Exception {
- return constructor.newInstance(args);
- }
- }
-
- public static final class ConstructorParameterMeta extends ParameterMeta {
-
- private final Parameter parameter;
-
- ConstructorParameterMeta(RestToolKit toolKit, Parameter parameter, String prefix) {
- super(toolKit, prefix, parameter.isNamePresent() ? parameter.getName() : null);
- this.parameter = parameter;
- }
-
- @Override
- protected AnnotatedElement getAnnotatedElement() {
- return parameter;
- }
-
- @Override
- public Class<?> getType() {
- return parameter.getType();
- }
-
- @Override
- public Type getGenericType() {
- return parameter.getParameterizedType();
- }
-
- @Override
- public String getDescription() {
- return "ConstructorParameter{" + parameter + '}';
- }
- }
-
- private static final class FieldMeta extends ParameterMeta {
-
- private final Field field;
-
- FieldMeta(RestToolKit toolKit, String prefix, Field field) {
- super(toolKit, prefix, field.getName());
- this.field = field;
- }
-
- @Override
- public Class<?> getType() {
- return field.getType();
- }
-
- @Override
- public Type getGenericType() {
- return field.getGenericType();
- }
-
- @Override
- protected AnnotatedElement getAnnotatedElement() {
- return field;
- }
-
- public void set(Object bean, Object value) throws Exception {
- field.set(bean, value);
- }
-
- @Override
- public String getDescription() {
- return "FieldParameter{" + field + '}';
- }
- }
-
- private static final class SetMethodMeta extends ParameterMeta {
-
- private final Method method;
- private final Parameter parameter;
-
- SetMethodMeta(RestToolKit toolKit, Method method, Parameter parameter, String prefix, String name) {
- super(toolKit, prefix, name);
- this.method = method;
- this.parameter = parameter;
- }
-
- @Override
- public Class<?> getType() {
- return parameter.getType();
- }
-
- @Override
- public Type getGenericType() {
- return parameter.getParameterizedType();
- }
-
- @Override
- protected AnnotatedElement getAnnotatedElement() {
- return parameter;
- }
-
- public void invoke(Object bean, Object value) throws Exception {
- method.invoke(bean, value);
- }
-
- @Override
- public String getDescription() {
- return "SetMethodParameter{" + method + '}';
- }
+ return TypeUtils.nullDefault(paramMeta.getType());
}
}
diff --git a/dubbo-plugin/dubbo-rest-jaxrs/src/main/java/org/apache/dubbo/rpc/protocol/tri/rest/support/jaxrs/JaxrsHttpRequestAdaptee.java b/dubbo-plugin/dubbo-rest-jaxrs/src/main/java/org/apache/dubbo/rpc/protocol/tri/rest/support/jaxrs/JaxrsHttpRequestAdapter.java
similarity index 97%
rename from dubbo-plugin/dubbo-rest-jaxrs/src/main/java/org/apache/dubbo/rpc/protocol/tri/rest/support/jaxrs/JaxrsHttpRequestAdaptee.java
rename to dubbo-plugin/dubbo-rest-jaxrs/src/main/java/org/apache/dubbo/rpc/protocol/tri/rest/support/jaxrs/JaxrsHttpRequestAdapter.java
index 3ff3f85381..6309415356 100644
--- a/dubbo-plugin/dubbo-rest-jaxrs/src/main/java/org/apache/dubbo/rpc/protocol/tri/rest/support/jaxrs/JaxrsHttpRequestAdaptee.java
+++ b/dubbo-plugin/dubbo-rest-jaxrs/src/main/java/org/apache/dubbo/rpc/protocol/tri/rest/support/jaxrs/JaxrsHttpRequestAdapter.java
@@ -33,14 +33,14 @@ import org.jboss.resteasy.specimpl.ResteasyHttpHeaders;
import org.jboss.resteasy.spi.ResteasyAsynchronousContext;
import org.jboss.resteasy.spi.ResteasyUriInfo;
-public final class JaxrsHttpRequestAdaptee implements org.jboss.resteasy.spi.HttpRequest {
+public final class JaxrsHttpRequestAdapter implements org.jboss.resteasy.spi.HttpRequest {
private final HttpRequest request;
private HttpHeaders headers;
private ResteasyUriInfo uriInfo;
- public JaxrsHttpRequestAdaptee(HttpRequest request) {
+ public JaxrsHttpRequestAdapter(HttpRequest request) {
this.request = request;
}
diff --git a/dubbo-plugin/dubbo-rest-jaxrs/src/main/java/org/apache/dubbo/rpc/protocol/tri/rest/support/jaxrs/JaxrsHttpResponseAdaptee.java b/dubbo-plugin/dubbo-rest-jaxrs/src/main/java/org/apache/dubbo/rpc/protocol/tri/rest/support/jaxrs/JaxrsHttpResponseAdapter.java
similarity index 95%
rename from dubbo-plugin/dubbo-rest-jaxrs/src/main/java/org/apache/dubbo/rpc/protocol/tri/rest/support/jaxrs/JaxrsHttpResponseAdaptee.java
rename to dubbo-plugin/dubbo-rest-jaxrs/src/main/java/org/apache/dubbo/rpc/protocol/tri/rest/support/jaxrs/JaxrsHttpResponseAdapter.java
index 94a4bf417a..d0e7997256 100644
--- a/dubbo-plugin/dubbo-rest-jaxrs/src/main/java/org/apache/dubbo/rpc/protocol/tri/rest/support/jaxrs/JaxrsHttpResponseAdaptee.java
+++ b/dubbo-plugin/dubbo-rest-jaxrs/src/main/java/org/apache/dubbo/rpc/protocol/tri/rest/support/jaxrs/JaxrsHttpResponseAdapter.java
@@ -24,13 +24,13 @@ import javax.ws.rs.core.NewCookie;
import java.io.OutputStream;
-public final class JaxrsHttpResponseAdaptee implements org.jboss.resteasy.spi.HttpResponse {
+public final class JaxrsHttpResponseAdapter implements org.jboss.resteasy.spi.HttpResponse {
private final HttpResponse response;
private MultivaluedMap<String, Object> headers;
- public JaxrsHttpResponseAdaptee(HttpResponse response) {
+ public JaxrsHttpResponseAdapter(HttpResponse response) {
this.response = response;
}
diff --git a/dubbo-plugin/dubbo-rest-jaxrs/src/main/java/org/apache/dubbo/rpc/protocol/tri/rest/support/jaxrs/JaxrsRestToolKit.java b/dubbo-plugin/dubbo-rest-jaxrs/src/main/java/org/apache/dubbo/rpc/protocol/tri/rest/support/jaxrs/JaxrsRestToolKit.java
index 89d1861dc5..56c3365e53 100644
--- a/dubbo-plugin/dubbo-rest-jaxrs/src/main/java/org/apache/dubbo/rpc/protocol/tri/rest/support/jaxrs/JaxrsRestToolKit.java
+++ b/dubbo-plugin/dubbo-rest-jaxrs/src/main/java/org/apache/dubbo/rpc/protocol/tri/rest/support/jaxrs/JaxrsRestToolKit.java
@@ -21,18 +21,15 @@ import org.apache.dubbo.remoting.http12.HttpResponse;
import org.apache.dubbo.rpc.model.FrameworkModel;
import org.apache.dubbo.rpc.protocol.tri.rest.RestConstants;
import org.apache.dubbo.rpc.protocol.tri.rest.mapping.meta.ParameterMeta;
-import org.apache.dubbo.rpc.protocol.tri.rest.util.DefaultRestToolKit;
+import org.apache.dubbo.rpc.protocol.tri.rest.util.AbstractRestToolKit;
import javax.ws.rs.core.MultivaluedHashMap;
import javax.ws.rs.core.MultivaluedMap;
import javax.ws.rs.ext.ParamConverter;
-import java.util.Optional;
-
-final class JaxrsRestToolKit extends DefaultRestToolKit {
+final class JaxrsRestToolKit extends AbstractRestToolKit {
private final BeanArgumentBinder binder;
-
private final ParamConverterFactory paramConverterFactory;
public JaxrsRestToolKit(FrameworkModel frameworkModel) {
@@ -41,8 +38,13 @@ final class JaxrsRestToolKit extends DefaultRestToolKit {
paramConverterFactory = new ParamConverterFactory();
}
- @SuppressWarnings({"unchecked", "rawtypes"})
@Override
+ public int getDialect() {
+ return RestConstants.DIALECT_JAXRS;
+ }
+
+ @Override
+ @SuppressWarnings({"unchecked", "rawtypes"})
public Object convert(Object value, ParameterMeta parameter) {
if (MultivaluedMap.class.isAssignableFrom(parameter.getType())) {
if (value instanceof MultivaluedMap) {
@@ -51,26 +53,15 @@ final class JaxrsRestToolKit extends DefaultRestToolKit {
return typeConverter.convert(value, MultivaluedHashMap.class);
}
- Optional<ParamConverter> optional = paramConverterFactory.getParamConverter(
+ ParamConverter converter = paramConverterFactory.getParamConverter(
parameter.getType(), parameter.getGenericType(), parameter.getRealAnnotations());
- if (optional.isPresent()) {
- ParamConverter paramConverter = optional.get();
- Object result = value.getClass() == String.class
- ? paramConverter.fromString((String) value)
- : paramConverter.toString(value);
- if (result != null) {
- return result;
- }
+ if (converter != null) {
+ return value instanceof String ? converter.fromString((String) value) : converter.toString(value);
}
return super.convert(value, parameter);
}
- @Override
- public int getDialect() {
- return RestConstants.DIALECT_JAXRS;
- }
-
@Override
public Object bind(ParameterMeta parameter, HttpRequest request, HttpResponse response) {
return binder.bind(parameter, request, response);
diff --git a/dubbo-plugin/dubbo-rest-jaxrs/src/main/java/org/apache/dubbo/rpc/protocol/tri/rest/support/jaxrs/ParamConverterFactory.java b/dubbo-plugin/dubbo-rest-jaxrs/src/main/java/org/apache/dubbo/rpc/protocol/tri/rest/support/jaxrs/ParamConverterFactory.java
index bef0a63b90..15cf8dc36c 100644
--- a/dubbo-plugin/dubbo-rest-jaxrs/src/main/java/org/apache/dubbo/rpc/protocol/tri/rest/support/jaxrs/ParamConverterFactory.java
+++ b/dubbo-plugin/dubbo-rest-jaxrs/src/main/java/org/apache/dubbo/rpc/protocol/tri/rest/support/jaxrs/ParamConverterFactory.java
@@ -16,10 +16,9 @@
*/
package org.apache.dubbo.rpc.protocol.tri.rest.support.jaxrs;
+import org.apache.dubbo.common.lang.Nullable;
import org.apache.dubbo.common.logger.ErrorTypeAwareLogger;
-import org.apache.dubbo.common.logger.LoggerFactory;
import org.apache.dubbo.common.utils.CollectionUtils;
-import org.apache.dubbo.common.utils.Pair;
import javax.ws.rs.ext.ParamConverter;
import javax.ws.rs.ext.ParamConverterProvider;
@@ -27,6 +26,7 @@ import javax.ws.rs.ext.ParamConverterProvider;
import java.lang.annotation.Annotation;
import java.lang.reflect.Type;
import java.util.ArrayList;
+import java.util.Collections;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
@@ -34,40 +34,45 @@ import java.util.Optional;
import java.util.ServiceLoader;
import static org.apache.dubbo.common.constants.LoggerCodeConstants.COMMON_ERROR_LOAD_EXTENSION;
+import static org.apache.dubbo.common.logger.LoggerFactory.getErrorTypeAwareLogger;
-@SuppressWarnings({"rawtypes"})
-public class ParamConverterFactory {
+final class ParamConverterFactory {
- private static final ErrorTypeAwareLogger logger =
- LoggerFactory.getErrorTypeAwareLogger(ParamConverterFactory.class);
- private final Map<Pair<Pair<Class<?>, Type>, Annotation[]>, Optional<ParamConverter>> cache =
- CollectionUtils.newConcurrentHashMap();
+ private static final ErrorTypeAwareLogger logger = getErrorTypeAwareLogger(ParamConverterFactory.class);
+ private final Map<List<Object>, Optional<ParamConverter<?>>> cache = CollectionUtils.newConcurrentHashMap();
private final List<ParamConverterProvider> providers = new ArrayList<>();
ParamConverterFactory() {
- ServiceLoader<ParamConverterProvider> serviceLoader = ServiceLoader.load(ParamConverterProvider.class);
- Iterator<ParamConverterProvider> iterator = serviceLoader.iterator();
- while (iterator.hasNext()) {
+ Iterator<ParamConverterProvider> it =
+ ServiceLoader.load(ParamConverterProvider.class).iterator();
+ while (it.hasNext()) {
try {
- ParamConverterProvider paramConverterProvider = iterator.next();
- providers.add(paramConverterProvider);
- } catch (Throwable e) {
- logger.error(COMMON_ERROR_LOAD_EXTENSION, "", "", "Spi Fail to load ParamConverterProvider");
+ providers.add(it.next());
+ } catch (Throwable t) {
+ logger.error(COMMON_ERROR_LOAD_EXTENSION, "", "", "Failed to load ParamConverterProvider", t);
}
}
}
- public <T> Optional<ParamConverter> getParamConverter(
- Class<T> rawType, Type genericType, Annotation[] annotations) {
- Pair<Pair<Class<?>, Type>, Annotation[]> pair = Pair.of(Pair.of(rawType, genericType), annotations);
- return cache.computeIfAbsent(pair, k -> {
- for (ParamConverterProvider provider : providers) {
- ParamConverter converter = provider.getConverter(rawType, genericType, annotations);
- if (converter != null) {
- return Optional.of(converter);
- }
- }
- return Optional.empty();
- });
+ @Nullable
+ @SuppressWarnings("unchecked")
+ public <T> ParamConverter<T> getParamConverter(Class<T> type, Type genericType, Annotation[] annotations) {
+ if (providers.isEmpty()) {
+ return null;
+ }
+ List<Object> key = new ArrayList<>(annotations.length + 2);
+ key.add(type);
+ key.add(genericType);
+ Collections.addAll(key, annotations);
+ return (ParamConverter<T>) cache.computeIfAbsent(key, k -> {
+ for (ParamConverterProvider provider : providers) {
+ ParamConverter<T> converter = provider.getConverter(type, genericType, annotations);
+ if (converter != null) {
+ return Optional.of(converter);
+ }
+ }
+ return Optional.empty();
+ })
+ .orElse(null);
}
}
diff --git a/dubbo-plugin/dubbo-rest-jaxrs/src/main/java/org/apache/dubbo/rpc/protocol/tri/rest/support/jaxrs/filter/ContainerRequestContextImpl.java b/dubbo-plugin/dubbo-rest-jaxrs/src/main/java/org/apache/dubbo/rpc/protocol/tri/rest/support/jaxrs/filter/ContainerRequestContextImpl.java
index 907524f0f6..713c20120a 100644
--- a/dubbo-plugin/dubbo-rest-jaxrs/src/main/java/org/apache/dubbo/rpc/protocol/tri/rest/support/jaxrs/filter/ContainerRequestContextImpl.java
+++ b/dubbo-plugin/dubbo-rest-jaxrs/src/main/java/org/apache/dubbo/rpc/protocol/tri/rest/support/jaxrs/filter/ContainerRequestContextImpl.java
@@ -20,8 +20,8 @@ import org.apache.dubbo.remoting.http12.HttpCookie;
import org.apache.dubbo.remoting.http12.HttpRequest;
import org.apache.dubbo.remoting.http12.HttpResponse;
import org.apache.dubbo.rpc.protocol.tri.rest.support.jaxrs.Helper;
-import org.apache.dubbo.rpc.protocol.tri.rest.support.jaxrs.JaxrsHttpRequestAdaptee;
-import org.apache.dubbo.rpc.protocol.tri.rest.support.jaxrs.JaxrsHttpResponseAdaptee;
+import org.apache.dubbo.rpc.protocol.tri.rest.support.jaxrs.JaxrsHttpRequestAdapter;
+import org.apache.dubbo.rpc.protocol.tri.rest.support.jaxrs.JaxrsHttpResponseAdapter;
import org.apache.dubbo.rpc.protocol.tri.rest.support.jaxrs.MultivaluedMapWrapper;
import javax.ws.rs.container.ContainerRequestContext;
@@ -107,7 +107,7 @@ final class ContainerRequestContextImpl implements ContainerRequestContext {
public Request getRequest() {
Request req = this.req;
if (req == null) {
- req = new RequestImpl(new JaxrsHttpRequestAdaptee(request), new JaxrsHttpResponseAdaptee(response));
+ req = new RequestImpl(new JaxrsHttpRequestAdapter(request), new JaxrsHttpResponseAdapter(response));
this.req = req;
}
return req;
diff --git a/dubbo-plugin/dubbo-rest-jaxrs/src/test/groovy/org/apache/dubbo/rpc/protocol/tri/rest/support/jaxrs/RestProtocolTest.groovy b/dubbo-plugin/dubbo-rest-jaxrs/src/test/groovy/org/apache/dubbo/rpc/protocol/tri/rest/support/jaxrs/RestProtocolTest.groovy
new file mode 100644
index 0000000000..75e34de268
--- /dev/null
+++ b/dubbo-plugin/dubbo-rest-jaxrs/src/test/groovy/org/apache/dubbo/rpc/protocol/tri/rest/support/jaxrs/RestProtocolTest.groovy
@@ -0,0 +1,78 @@
+/*
+ * 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.dubbo.rpc.protocol.tri.rest.support.jaxrs
+
+import org.apache.dubbo.remoting.http12.message.MediaType
+import org.apache.dubbo.rpc.protocol.tri.rest.service.DemoServiceImpl
+import org.apache.dubbo.rpc.protocol.tri.rest.support.jaxrs.service.JaxrsDemoServiceImpl
+import org.apache.dubbo.rpc.protocol.tri.rest.test.BaseServiceTest
+import org.apache.dubbo.rpc.protocol.tri.test.TestRequest
+import org.apache.dubbo.rpc.protocol.tri.test.TestRunnerBuilder
+
+class RestProtocolTest extends BaseServiceTest {
+
+ @Override
+ void setupService(TestRunnerBuilder builder) {
+ builder.provider(new DemoServiceImpl())
+ builder.provider(new JaxrsDemoServiceImpl())
+ }
+
+ def "hello world"() {
+ expect:
+ runner.get(path) == output
+ where:
+ path | output
+ '/hello?name=world' | 'hello world'
+ '/hello/?name=world' | 'hello world'
+ '/hello.yml?name=world' | 'hello world'
+ }
+
+ def "form param test"() {
+ expect:
+ TestRequest request = new TestRequest(
+ path: path,
+ contentType: MediaType.APPLICATION_FROM_URLENCODED,
+ body: body
+ )
+ runner.post(request) == output
+ where:
+ path | body | output
+ '/formTest' | ['user.first': 'sam', 'user.last': 'lee'] | '{"contentType":"application/x-www-form-urlencoded","firstName":"sam","lastName":"lee"}'
+ }
+
+ def "bean param test"() {
+ expect:
+ TestRequest request = new TestRequest(
+ path: path,
+ contentType: MediaType.APPLICATION_FROM_URLENCODED,
+ body: body
+ )
+ runner.post(request) == output
+ where:
+ path | body | output
+ '/beanTest/2?name=sam' | ['first': 'sam', 'last': 'lee'] | '{"form":{"contentType":"application/x-www-form-urlencoded","firstName":"sam","lastName":"lee"},"id":2,"name":"sam"}'
+ }
+
+ def "param converter test"() {
+ expect:
+ runner.get(path) == output
+ where:
+ path | output
+ '/convertTest?user=3,sam' | '{"id":3,"name":"sam"}'
+ }
+}
diff --git a/dubbo-plugin/dubbo-rest-jaxrs/src/test/java/org/apache/dubbo/rpc/protocol/tri/rest/support/jaxrs/compatible/JaxrsRestProtocolTest.java b/dubbo-plugin/dubbo-rest-jaxrs/src/test/java/org/apache/dubbo/rpc/protocol/tri/rest/support/jaxrs/compatible/JaxrsRestProtocolTest.java
index f46c31310a..fa4b681843 100644
--- a/dubbo-plugin/dubbo-rest-jaxrs/src/test/java/org/apache/dubbo/rpc/protocol/tri/rest/support/jaxrs/compatible/JaxrsRestProtocolTest.java
+++ b/dubbo-plugin/dubbo-rest-jaxrs/src/test/java/org/apache/dubbo/rpc/protocol/tri/rest/support/jaxrs/compatible/JaxrsRestProtocolTest.java
@@ -39,8 +39,6 @@ import org.apache.dubbo.rpc.protocol.tri.rest.support.jaxrs.compatible.rest.Anot
import org.apache.dubbo.rpc.protocol.tri.rest.support.jaxrs.compatible.rest.AnotherUserRestServiceImpl;
import org.apache.dubbo.rpc.protocol.tri.rest.support.jaxrs.compatible.rest.HttpMethodService;
import org.apache.dubbo.rpc.protocol.tri.rest.support.jaxrs.compatible.rest.HttpMethodServiceImpl;
-import org.apache.dubbo.rpc.protocol.tri.rest.support.jaxrs.compatible.rest.ParamConverterService;
-import org.apache.dubbo.rpc.protocol.tri.rest.support.jaxrs.compatible.rest.ParamConverterServiceImpl;
import org.apache.dubbo.rpc.protocol.tri.rest.support.jaxrs.compatible.rest.RestDemoForTestException;
import org.apache.dubbo.rpc.protocol.tri.rest.support.jaxrs.compatible.rest.RestDemoService;
import org.apache.dubbo.rpc.protocol.tri.rest.support.jaxrs.compatible.rest.RestDemoServiceImpl;
@@ -564,24 +562,4 @@ class JaxrsRestProtocolTest {
repository.registerProvider(providerModel);
return url.setServiceModel(providerModel);
}
-
- @Test
- void testParamConverter() {
- ParamConverterService service = new ParamConverterServiceImpl();
- URL exportUrl = URL.valueOf(
- "tri://127.0.0.1:" + availablePort + "/rest?interface=" + ParamConverterService.class.getName());
-
- URL nettyUrl = this.registerProvider(exportUrl, service, ParamConverterService.class);
-
- tProtocol.export(proxy.getInvoker(service, ParamConverterService.class, nettyUrl));
-
- ParamConverterService paramConverterService =
- this.proxy.getProxy(protocol.refer(ParamConverterService.class, nettyUrl));
-
- User user = paramConverterService.convert(User.getInstance());
- User u = new User();
- u.setAge(20);
- u.setId(1L);
- Assertions.assertEquals(u, user);
- }
}
diff --git a/dubbo-plugin/dubbo-rest-jaxrs/src/test/java/org/apache/dubbo/rpc/protocol/tri/rest/support/jaxrs/compatible/rest/ParamConverterService.java b/dubbo-plugin/dubbo-rest-jaxrs/src/test/java/org/apache/dubbo/rpc/protocol/tri/rest/support/jaxrs/service/JaxrsDemoService.java
similarity index 65%
copy from dubbo-plugin/dubbo-rest-jaxrs/src/test/java/org/apache/dubbo/rpc/protocol/tri/rest/support/jaxrs/compatible/rest/ParamConverterService.java
copy to dubbo-plugin/dubbo-rest-jaxrs/src/test/java/org/apache/dubbo/rpc/protocol/tri/rest/support/jaxrs/service/JaxrsDemoService.java
index fa1c20c12d..d23ac72f4c 100644
--- a/dubbo-plugin/dubbo-rest-jaxrs/src/test/java/org/apache/dubbo/rpc/protocol/tri/rest/support/jaxrs/compatible/rest/ParamConverterService.java
+++ b/dubbo-plugin/dubbo-rest-jaxrs/src/test/java/org/apache/dubbo/rpc/protocol/tri/rest/support/jaxrs/service/JaxrsDemoService.java
@@ -14,18 +14,27 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package org.apache.dubbo.rpc.protocol.tri.rest.support.jaxrs.compatible.rest;
-
-import org.apache.dubbo.rpc.protocol.tri.rest.support.jaxrs.compatible.User;
+package org.apache.dubbo.rpc.protocol.tri.rest.support.jaxrs.service;
+import javax.ws.rs.BeanParam;
import javax.ws.rs.GET;
+import javax.ws.rs.POST;
import javax.ws.rs.Path;
import javax.ws.rs.QueryParam;
-@Path("/ParamConverterService")
-public interface ParamConverterService {
+import org.jboss.resteasy.annotations.Form;
+
+public interface JaxrsDemoService {
+
+ @POST
+ @Path("/formTest")
+ UserForm formTest(@Form(prefix = "user") UserForm userForm);
+
+ @POST
+ @Path("/beanTest/{id}")
+ User getTest(@BeanParam User user);
- @GET()
- @Path("/convert")
- User convert(@QueryParam("user") User user);
+ @GET
+ @Path("/convertTest")
+ User convertTest(@QueryParam("user") User user);
}
diff --git a/dubbo-plugin/dubbo-rest-jaxrs/src/test/java/org/apache/dubbo/rpc/protocol/tri/rest/support/jaxrs/compatible/rest/ParamConverterService.java b/dubbo-plugin/dubbo-rest-jaxrs/src/test/java/org/apache/dubbo/rpc/protocol/tri/rest/support/jaxrs/service/JaxrsDemoServiceImpl.java
similarity index 67%
copy from dubbo-plugin/dubbo-rest-jaxrs/src/test/java/org/apache/dubbo/rpc/protocol/tri/rest/support/jaxrs/compatible/rest/ParamConverterService.java
copy to dubbo-plugin/dubbo-rest-jaxrs/src/test/java/org/apache/dubbo/rpc/protocol/tri/rest/support/jaxrs/service/JaxrsDemoServiceImpl.java
index fa1c20c12d..0cab619b50 100644
--- a/dubbo-plugin/dubbo-rest-jaxrs/src/test/java/org/apache/dubbo/rpc/protocol/tri/rest/support/jaxrs/compatible/rest/ParamConverterService.java
+++ b/dubbo-plugin/dubbo-rest-jaxrs/src/test/java/org/apache/dubbo/rpc/protocol/tri/rest/support/jaxrs/service/JaxrsDemoServiceImpl.java
@@ -14,18 +14,22 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package org.apache.dubbo.rpc.protocol.tri.rest.support.jaxrs.compatible.rest;
+package org.apache.dubbo.rpc.protocol.tri.rest.support.jaxrs.service;
-import org.apache.dubbo.rpc.protocol.tri.rest.support.jaxrs.compatible.User;
+public class JaxrsDemoServiceImpl implements JaxrsDemoService {
-import javax.ws.rs.GET;
-import javax.ws.rs.Path;
-import javax.ws.rs.QueryParam;
+ @Override
+ public UserForm formTest(UserForm userForm) {
+ return userForm;
+ }
-@Path("/ParamConverterService")
-public interface ParamConverterService {
+ @Override
+ public User getTest(User user) {
+ return user;
+ }
- @GET()
- @Path("/convert")
- User convert(@QueryParam("user") User user);
+ @Override
+ public User convertTest(User user) {
+ return user;
+ }
}
diff --git a/dubbo-plugin/dubbo-rest-jaxrs/src/test/java/org/apache/dubbo/rpc/protocol/tri/rest/support/jaxrs/compatible/rest/ParamConverterProviderImpl.java b/dubbo-plugin/dubbo-rest-jaxrs/src/test/java/org/apache/dubbo/rpc/protocol/tri/rest/support/jaxrs/service/ParamConverterProviderImpl.java
similarity index 73%
rename from dubbo-plugin/dubbo-rest-jaxrs/src/test/java/org/apache/dubbo/rpc/protocol/tri/rest/support/jaxrs/compatible/rest/ParamConverterProviderImpl.java
rename to dubbo-plugin/dubbo-rest-jaxrs/src/test/java/org/apache/dubbo/rpc/protocol/tri/rest/support/jaxrs/service/ParamConverterProviderImpl.java
index 24d0a472cb..84b03445e1 100644
--- a/dubbo-plugin/dubbo-rest-jaxrs/src/test/java/org/apache/dubbo/rpc/protocol/tri/rest/support/jaxrs/compatible/rest/ParamConverterProviderImpl.java
+++ b/dubbo-plugin/dubbo-rest-jaxrs/src/test/java/org/apache/dubbo/rpc/protocol/tri/rest/support/jaxrs/service/ParamConverterProviderImpl.java
@@ -14,9 +14,9 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package org.apache.dubbo.rpc.protocol.tri.rest.support.jaxrs.compatible.rest;
+package org.apache.dubbo.rpc.protocol.tri.rest.support.jaxrs.service;
-import org.apache.dubbo.rpc.protocol.tri.rest.support.jaxrs.compatible.User;
+import org.apache.dubbo.common.utils.StringUtils;
import javax.ws.rs.ext.ParamConverter;
import javax.ws.rs.ext.ParamConverterProvider;
@@ -27,25 +27,29 @@ import java.lang.reflect.Type;
public class ParamConverterProviderImpl implements ParamConverterProvider {
@Override
- public <T> ParamConverter<T> getConverter(Class<T> rawType, Type type, Annotation[] annotations) {
+ @SuppressWarnings("unchecked")
+ public <T> ParamConverter<T> getConverter(Class<T> rawType, Type genericType, Annotation[] annotations) {
if (rawType.isAssignableFrom(User.class)) {
return (ParamConverter<T>) new UserParamConverter();
}
return null;
}
- static class UserParamConverter implements ParamConverter<User> {
+ static final class UserParamConverter implements ParamConverter<User> {
@Override
- public User fromString(String param) {
+ public User fromString(String value) {
+ String[] arr = StringUtils.tokenize(value, ',');
User user = new User();
- user.setId(1L);
- user.setAge(20);
+ if (arr.length > 1) {
+ user.setId(Long.valueOf(arr[0]));
+ user.setName(arr[1]);
+ }
return user;
}
@Override
public String toString(User user) {
- return "User{" + "id=" + user.getId() + ", name='" + user.getName() + '\'' + ", age=" + user.getAge() + '}';
+ return "User{" + "id=" + user.getId() + ", name='" + user.getName() + "\"}";
}
}
}
diff --git a/dubbo-plugin/dubbo-rest-jaxrs/src/test/java/org/apache/dubbo/rpc/protocol/tri/rest/support/jaxrs/compatible/rest/ParamConverterService.java b/dubbo-plugin/dubbo-rest-jaxrs/src/test/java/org/apache/dubbo/rpc/protocol/tri/rest/support/jaxrs/service/User.java
similarity index 50%
rename from dubbo-plugin/dubbo-rest-jaxrs/src/test/java/org/apache/dubbo/rpc/protocol/tri/rest/support/jaxrs/compatible/rest/ParamConverterService.java
rename to dubbo-plugin/dubbo-rest-jaxrs/src/test/java/org/apache/dubbo/rpc/protocol/tri/rest/support/jaxrs/service/User.java
index fa1c20c12d..a901d1345f 100644
--- a/dubbo-plugin/dubbo-rest-jaxrs/src/test/java/org/apache/dubbo/rpc/protocol/tri/rest/support/jaxrs/compatible/rest/ParamConverterService.java
+++ b/dubbo-plugin/dubbo-rest-jaxrs/src/test/java/org/apache/dubbo/rpc/protocol/tri/rest/support/jaxrs/service/User.java
@@ -14,18 +14,55 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package org.apache.dubbo.rpc.protocol.tri.rest.support.jaxrs.compatible.rest;
+package org.apache.dubbo.rpc.protocol.tri.rest.support.jaxrs.service;
-import org.apache.dubbo.rpc.protocol.tri.rest.support.jaxrs.compatible.User;
-
-import javax.ws.rs.GET;
-import javax.ws.rs.Path;
+import javax.ws.rs.BeanParam;
+import javax.ws.rs.PathParam;
import javax.ws.rs.QueryParam;
-@Path("/ParamConverterService")
-public interface ParamConverterService {
+public class User {
+
+ @PathParam("id")
+ private Long id;
+
+ @QueryParam("name")
+ private String name;
+
+ @BeanParam
+ private User owner;
+
+ @BeanParam
+ private UserForm form;
+
+ public Long getId() {
+ return id;
+ }
+
+ public void setId(Long id) {
+ this.id = id;
+ }
+
+ public String getName() {
+ return name;
+ }
+
+ public void setName(String name) {
+ this.name = name;
+ }
+
+ public User getOwner() {
+ return owner;
+ }
+
+ public void setOwner(User owner) {
+ this.owner = owner;
+ }
+
+ public UserForm getForm() {
+ return form;
+ }
- @GET()
- @Path("/convert")
- User convert(@QueryParam("user") User user);
+ public void setForm(UserForm form) {
+ this.form = form;
+ }
}
diff --git a/dubbo-common/src/main/java/org/apache/dubbo/config/nested/PropagationConfig.java b/dubbo-plugin/dubbo-rest-jaxrs/src/test/java/org/apache/dubbo/rpc/protocol/tri/rest/support/jaxrs/service/UserForm.java
similarity index 52%
copy from dubbo-common/src/main/java/org/apache/dubbo/config/nested/PropagationConfig.java
copy to dubbo-plugin/dubbo-rest-jaxrs/src/test/java/org/apache/dubbo/rpc/protocol/tri/rest/support/jaxrs/service/UserForm.java
index c7ee0ba7dd..60f202f469 100644
--- a/dubbo-common/src/main/java/org/apache/dubbo/config/nested/PropagationConfig.java
+++ b/dubbo-plugin/dubbo-rest-jaxrs/src/test/java/org/apache/dubbo/rpc/protocol/tri/rest/support/jaxrs/service/UserForm.java
@@ -14,35 +14,43 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package org.apache.dubbo.config.nested;
+package org.apache.dubbo.rpc.protocol.tri.rest.support.jaxrs.service;
-import java.io.Serializable;
+import javax.ws.rs.FormParam;
+import javax.ws.rs.HeaderParam;
-/**
- * Configuration for the propagation.
- */
-public class PropagationConfig implements Serializable {
+public class UserForm {
+
+ @FormParam("first")
+ String firstName;
+
+ @FormParam("last")
+ String lastName;
- public static final String B3 = "B3";
+ @HeaderParam("Content-Type")
+ String contentType;
- public static final String W3C = "W3C";
+ public String getFirstName() {
+ return firstName;
+ }
- /**
- * Tracing context propagation type.
- */
- private String type = W3C;
+ public void setFirstName(String firstName) {
+ this.firstName = firstName;
+ }
- public PropagationConfig() {}
+ public String getLastName() {
+ return lastName;
+ }
- public PropagationConfig(String type) {
- this.type = type;
+ public void setLastName(String lastName) {
+ this.lastName = lastName;
}
- public String getType() {
- return type;
+ public String getContentType() {
+ return contentType;
}
- public void setType(String type) {
- this.type = type;
+ public void setContentType(String contentType) {
+ this.contentType = contentType;
}
}
diff --git a/dubbo-plugin/dubbo-rest-jaxrs/src/test/resources/META-INF/services/javax.ws.rs.ext.ParamConverterProvider b/dubbo-plugin/dubbo-rest-jaxrs/src/test/resources/META-INF/services/javax.ws.rs.ext.ParamConverterProvider
index b794deb158..ab4379a54a 100644
--- a/dubbo-plugin/dubbo-rest-jaxrs/src/test/resources/META-INF/services/javax.ws.rs.ext.ParamConverterProvider
+++ b/dubbo-plugin/dubbo-rest-jaxrs/src/test/resources/META-INF/services/javax.ws.rs.ext.ParamConverterProvider
@@ -1 +1 @@
-org.apache.dubbo.rpc.protocol.tri.rest.support.jaxrs.compatible.rest.ParamConverterProviderImpl
+org.apache.dubbo.rpc.protocol.tri.rest.support.jaxrs.service.ParamConverterProviderImpl
diff --git a/dubbo-plugin/dubbo-rest-jaxrs/src/test/resources/log4j2-test.xml b/dubbo-plugin/dubbo-rest-jaxrs/src/test/resources/log4j2-test.xml
index 34d1c53e88..91337f333b 100644
--- a/dubbo-plugin/dubbo-rest-jaxrs/src/test/resources/log4j2-test.xml
+++ b/dubbo-plugin/dubbo-rest-jaxrs/src/test/resources/log4j2-test.xml
@@ -23,6 +23,7 @@
</Appenders>
<Loggers>
<Logger name="org.apache.dubbo.rpc.protocol.tri.stream.StreamUtils" level="error"/>
+ <Logger name="org.apache.dubbo.rpc.protocol.tri.h12" level="debug"/>
<Root level="info">
<AppenderRef ref="Console"/>
</Root>
diff --git a/dubbo-plugin/dubbo-rest-servlet/pom.xml b/dubbo-plugin/dubbo-rest-servlet/pom.xml
deleted file mode 100644
index c4efd467da..0000000000
--- a/dubbo-plugin/dubbo-rest-servlet/pom.xml
+++ /dev/null
@@ -1,46 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--
- ~ 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.
- -->
-<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
- <modelVersion>4.0.0</modelVersion>
- <parent>
- <groupId>org.apache.dubbo</groupId>
- <artifactId>dubbo-plugin</artifactId>
- <version>${revision}</version>
- <relativePath>../pom.xml</relativePath>
- </parent>
-
- <artifactId>dubbo-rest-servlet</artifactId>
-
- <dependencies>
- <dependency>
- <groupId>org.apache.dubbo</groupId>
- <artifactId>dubbo-rpc-triple</artifactId>
- <version>${project.version}</version>
- </dependency>
- <dependency>
- <groupId>javax.servlet</groupId>
- <artifactId>javax.servlet-api</artifactId>
- </dependency>
- <dependency>
- <groupId>org.apache.dubbo</groupId>
- <artifactId>dubbo-remoting-netty4</artifactId>
- <version>${project.version}</version>
- <scope>test</scope>
- </dependency>
- </dependencies>
-</project>
diff --git a/dubbo-plugin/dubbo-rest-servlet/src/main/resources/META-INF/dubbo/internal/org.apache.dubbo.remoting.http12.message.HttpMessageAdapterFactory b/dubbo-plugin/dubbo-rest-servlet/src/main/resources/META-INF/dubbo/internal/org.apache.dubbo.remoting.http12.message.HttpMessageAdapterFactory
deleted file mode 100644
index ac45cbe947..0000000000
--- a/dubbo-plugin/dubbo-rest-servlet/src/main/resources/META-INF/dubbo/internal/org.apache.dubbo.remoting.http12.message.HttpMessageAdapterFactory
+++ /dev/null
@@ -1 +0,0 @@
-servlet=org.apache.dubbo.rpc.protocol.tri.rest.support.servlet.ServletHttpMessageAdapterFactory
diff --git a/dubbo-plugin/dubbo-rest-servlet/src/main/resources/META-INF/dubbo/internal/org.apache.dubbo.rpc.protocol.tri.rest.argument.ArgumentResolver b/dubbo-plugin/dubbo-rest-servlet/src/main/resources/META-INF/dubbo/internal/org.apache.dubbo.rpc.protocol.tri.rest.argument.ArgumentResolver
deleted file mode 100644
index 5f4a984c54..0000000000
--- a/dubbo-plugin/dubbo-rest-servlet/src/main/resources/META-INF/dubbo/internal/org.apache.dubbo.rpc.protocol.tri.rest.argument.ArgumentResolver
+++ /dev/null
@@ -1 +0,0 @@
-servlet=org.apache.dubbo.rpc.protocol.tri.rest.support.servlet.ServletArgumentResolver
diff --git a/dubbo-plugin/dubbo-rest-servlet/src/main/resources/META-INF/dubbo/internal/org.apache.dubbo.rpc.protocol.tri.rest.filter.RestExtensionAdapter b/dubbo-plugin/dubbo-rest-servlet/src/main/resources/META-INF/dubbo/internal/org.apache.dubbo.rpc.protocol.tri.rest.filter.RestExtensionAdapter
deleted file mode 100644
index ec669308fb..0000000000
--- a/dubbo-plugin/dubbo-rest-servlet/src/main/resources/META-INF/dubbo/internal/org.apache.dubbo.rpc.protocol.tri.rest.filter.RestExtensionAdapter
+++ /dev/null
@@ -1 +0,0 @@
-servlet-filter=org.apache.dubbo.rpc.protocol.tri.rest.support.servlet.FilterAdapter
diff --git a/dubbo-plugin/dubbo-rest-servlet/src/test/resources/log4j2-test.xml b/dubbo-plugin/dubbo-rest-servlet/src/test/resources/log4j2-test.xml
deleted file mode 100644
index 34d1c53e88..0000000000
--- a/dubbo-plugin/dubbo-rest-servlet/src/test/resources/log4j2-test.xml
+++ /dev/null
@@ -1,30 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--
- ~ 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.
- -->
-<Configuration status="WARN">
- <Appenders>
- <Console name="Console" target="SYSTEM_OUT" follow="true">
- <PatternLayout pattern="%d{HH:mm:ss.SSS} |-%highlight{%-5p} [%t] %40.40c:%-3L -| %m%n%rEx{filters(jdk.internal.reflect,java.lang.reflect,sun.reflect,org.junit,org.mockito)}" charset="UTF-8"/>
- </Console>
- </Appenders>
- <Loggers>
- <Logger name="org.apache.dubbo.rpc.protocol.tri.stream.StreamUtils" level="error"/>
- <Root level="info">
- <AppenderRef ref="Console"/>
- </Root>
- </Loggers>
-</Configuration>
diff --git a/dubbo-plugin/dubbo-rest-spring/pom.xml b/dubbo-plugin/dubbo-rest-spring/pom.xml
index 21b3d9c084..a18d8a9f23 100644
--- a/dubbo-plugin/dubbo-rest-spring/pom.xml
+++ b/dubbo-plugin/dubbo-rest-spring/pom.xml
@@ -39,9 +39,14 @@
</dependency>
<dependency>
<groupId>org.apache.dubbo</groupId>
- <artifactId>dubbo-rest-servlet</artifactId>
+ <artifactId>dubbo-triple-servlet</artifactId>
<version>${project.version}</version>
</dependency>
+ <dependency>
+ <groupId>javax.servlet</groupId>
+ <artifactId>javax.servlet-api</artifactId>
+ <scope>provided</scope>
+ </dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
@@ -71,5 +76,47 @@
<artifactId>jaxb-runtime</artifactId>
<scope>test</scope>
</dependency>
+ <dependency>
+ <groupId>org.apache.dubbo</groupId>
+ <artifactId>dubbo-rpc-triple</artifactId>
+ <version>${project.version}</version>
+ <type>test-jar</type>
+ <scope>test</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.spockframework</groupId>
+ <artifactId>spock-core</artifactId>
+ <scope>test</scope>
+ </dependency>
</dependencies>
+
+ <build>
+ <plugins>
+ <plugin>
+ <groupId>org.codehaus.gmavenplus</groupId>
+ <artifactId>gmavenplus-plugin</artifactId>
+ <executions>
+ <execution>
+ <goals>
+ <goal>compileTests</goal>
+ </goals>
+ </execution>
+ </executions>
+ </plugin>
+ </plugins>
+ </build>
+
+ <profiles>
+ <profile>
+ <id>rpc-rest-test</id>
+ <dependencies>
+ <dependency>
+ <groupId>org.apache.dubbo</groupId>
+ <artifactId>dubbo-rpc-rest</artifactId>
+ <version>3.3.0-beta.2</version>
+ <scope>test</scope>
+ </dependency>
+ </dependencies>
+ </profile>
+ </profiles>
</project>
diff --git a/dubbo-plugin/dubbo-rest-spring/src/main/java/org/apache/dubbo/rpc/protocol/tri/rest/support/spring/AbstractSpringArgumentResolver.java b/dubbo-plugin/dubbo-rest-spring/src/main/java/org/apache/dubbo/rpc/protocol/tri/rest/support/spring/AbstractSpringArgumentResolver.java
index fef61723e7..5f3b89e278 100644
--- a/dubbo-plugin/dubbo-rest-spring/src/main/java/org/apache/dubbo/rpc/protocol/tri/rest/support/spring/AbstractSpringArgumentResolver.java
+++ b/dubbo-plugin/dubbo-rest-spring/src/main/java/org/apache/dubbo/rpc/protocol/tri/rest/support/spring/AbstractSpringArgumentResolver.java
@@ -16,17 +16,25 @@
*/
package org.apache.dubbo.rpc.protocol.tri.rest.support.spring;
+import org.apache.dubbo.common.utils.StringUtils;
import org.apache.dubbo.rpc.protocol.tri.rest.argument.AbstractAnnotationBaseArgumentResolver;
import org.apache.dubbo.rpc.protocol.tri.rest.mapping.meta.AnnotationMeta;
import org.apache.dubbo.rpc.protocol.tri.rest.mapping.meta.NamedValueMeta;
import org.apache.dubbo.rpc.protocol.tri.rest.mapping.meta.ParameterMeta;
import java.lang.annotation.Annotation;
+import java.util.Optional;
public abstract class AbstractSpringArgumentResolver extends AbstractAnnotationBaseArgumentResolver {
@Override
protected NamedValueMeta createNamedValueMeta(ParameterMeta param, AnnotationMeta<Annotation> ann) {
- return new NamedValueMeta(ann.getValue(), Helper.isRequired(ann), Helper.defaultValue(ann));
+ boolean required = param.getType() != Optional.class && Helper.isRequired(ann);
+ return new NamedValueMeta(ann.getValue(), required, Helper.defaultValue(ann));
+ }
+
+ @Override
+ protected Object filterValue(Object value, NamedValueMeta meta) {
+ return StringUtils.EMPTY_STRING.equals(value) ? meta.defaultValue() : value;
}
}
diff --git a/dubbo-plugin/dubbo-rest-spring/src/main/java/org/apache/dubbo/rpc/protocol/tri/rest/support/spring/Annotations.java b/dubbo-plugin/dubbo-rest-spring/src/main/java/org/apache/dubbo/rpc/protocol/tri/rest/support/spring/Annotations.java
index be8b1c5865..17936d09ce 100644
--- a/dubbo-plugin/dubbo-rest-spring/src/main/java/org/apache/dubbo/rpc/protocol/tri/rest/support/spring/Annotations.java
+++ b/dubbo-plugin/dubbo-rest-spring/src/main/java/org/apache/dubbo/rpc/protocol/tri/rest/support/spring/Annotations.java
@@ -31,9 +31,11 @@ public enum Annotations implements AnnotationEnum {
RequestPart,
RequestBody,
ModelAttribute,
+ BindParam,
ResponseStatus,
CrossOrigin,
ExceptionHandler,
+ HttpExchange("org.springframework.web.service.annotation.HttpExchange"),
Nonnull("javax.annotation.Nonnull");
private final String className;
diff --git a/dubbo-plugin/dubbo-rest-spring/src/main/java/org/apache/dubbo/rpc/protocol/tri/rest/support/spring/BeanArgumentBinder.java b/dubbo-plugin/dubbo-rest-spring/src/main/java/org/apache/dubbo/rpc/protocol/tri/rest/support/spring/BeanArgumentBinder.java
index 17005d5fda..d020e5425a 100644
--- a/dubbo-plugin/dubbo-rest-spring/src/main/java/org/apache/dubbo/rpc/protocol/tri/rest/support/spring/BeanArgumentBinder.java
+++ b/dubbo-plugin/dubbo-rest-spring/src/main/java/org/apache/dubbo/rpc/protocol/tri/rest/support/spring/BeanArgumentBinder.java
@@ -26,15 +26,11 @@ import org.apache.dubbo.rpc.protocol.tri.rest.Messages;
import org.apache.dubbo.rpc.protocol.tri.rest.RestException;
import org.apache.dubbo.rpc.protocol.tri.rest.argument.ArgumentResolver;
import org.apache.dubbo.rpc.protocol.tri.rest.argument.CompositeArgumentResolver;
+import org.apache.dubbo.rpc.protocol.tri.rest.mapping.meta.BeanMeta.ConstructorMeta;
import org.apache.dubbo.rpc.protocol.tri.rest.mapping.meta.ParameterMeta;
import org.apache.dubbo.rpc.protocol.tri.rest.util.RequestUtils;
-import org.apache.dubbo.rpc.protocol.tri.rest.util.RestToolKit;
-import java.lang.reflect.AnnotatedElement;
-import java.lang.reflect.Constructor;
import java.lang.reflect.Modifier;
-import java.lang.reflect.Parameter;
-import java.lang.reflect.Type;
import java.util.Map;
import org.springframework.beans.MutablePropertyValues;
@@ -44,23 +40,25 @@ import org.springframework.validation.BindingResult;
import org.springframework.validation.DataBinder;
import org.springframework.web.bind.WebDataBinder;
+import static org.apache.dubbo.rpc.protocol.tri.rest.mapping.meta.BeanMeta.resolveConstructor;
+
final class BeanArgumentBinder {
+ private static final Map<Class<?>, ConstructorMeta> CACHE = CollectionUtils.newConcurrentHashMap();
+
private final ArgumentResolver argumentResolver;
private final ConversionService conversionService;
- private final Map<Class<?>, ConstructorMeta> cache = CollectionUtils.newConcurrentHashMap();
-
BeanArgumentBinder(FrameworkModel frameworkModel, ConversionService conversionService) {
ScopeBeanFactory beanFactory = frameworkModel.getBeanFactory();
argumentResolver = beanFactory.getOrRegisterBean(CompositeArgumentResolver.class);
this.conversionService = conversionService;
}
- public Object bind(ParameterMeta parameter, HttpRequest request, HttpResponse response) {
- String name = StringUtils.defaultIf(parameter.getName(), DataBinder.DEFAULT_OBJECT_NAME);
+ public Object bind(ParameterMeta paramMeta, HttpRequest request, HttpResponse response) {
+ String name = StringUtils.defaultIf(paramMeta.getName(), DataBinder.DEFAULT_OBJECT_NAME);
try {
- Object bean = buildBean(parameter, request, response);
+ Object bean = createBean(paramMeta, request, response);
WebDataBinder binder = new WebDataBinder(bean, name);
binder.setConversionService(conversionService);
binder.bind(new MutablePropertyValues(RequestUtils.getParametersMap(request)));
@@ -70,89 +68,26 @@ final class BeanArgumentBinder {
}
return binder.getTarget();
} catch (Exception e) {
- throw new RestException(e, Messages.ARGUMENT_BIND_ERROR, name, parameter.getType());
+ throw new RestException(e, Messages.ARGUMENT_BIND_ERROR, name, paramMeta.getType());
}
}
- private Object buildBean(ParameterMeta parameter, HttpRequest request, HttpResponse response) throws Exception {
- Class<?> type = parameter.getActualType();
+ private Object createBean(ParameterMeta paramMeta, HttpRequest request, HttpResponse response) {
+ Class<?> type = paramMeta.getActualType();
if (Modifier.isAbstract(type.getModifiers())) {
- throw new IllegalStateException(Messages.ARGUMENT_COULD_NOT_RESOLVED.format(parameter.getDescription()));
+ throw new IllegalStateException(Messages.ARGUMENT_COULD_NOT_RESOLVED.format(paramMeta.getDescription()));
}
- ConstructorMeta ct = cache.computeIfAbsent(type, k -> resolveConstructor(parameter.getToolKit(), k));
- ParameterMeta[] parameters = ct.parameters;
+ ConstructorMeta ct = CACHE.computeIfAbsent(type, k -> resolveConstructor(paramMeta.getToolKit(), null, type));
+ ParameterMeta[] parameters = ct.getParameters();
int len = parameters.length;
+ if (len == 0) {
+ return ct.newInstance();
+ }
Object[] args = new Object[len];
for (int i = 0; i < len; i++) {
- args[i] = argumentResolver.resolve(parameters[i], request, response);
+ ParameterMeta parameter = parameters[i];
+ args[i] = parameter.isSimple() ? argumentResolver.resolve(parameter, request, response) : null;
}
return ct.newInstance(args);
}
-
- private ConstructorMeta resolveConstructor(RestToolKit toolKit, Class<?> type) {
- Constructor<?>[] constructors = type.getConstructors();
- Constructor<?> ct = null;
- if (constructors.length == 1) {
- ct = constructors[0];
- } else {
- try {
- ct = type.getDeclaredConstructor();
- } catch (NoSuchMethodException ignored) {
- }
- }
- if (ct == null) {
- throw new IllegalArgumentException("No available default constructor found in " + type);
- }
- return new ConstructorMeta(toolKit, ct);
- }
-
- private static final class ConstructorMeta {
-
- private final Constructor<?> constructor;
- private final ConstructorParameterMeta[] parameters;
-
- ConstructorMeta(RestToolKit toolKit, Constructor<?> constructor) {
- this.constructor = constructor;
- parameters = initParameters(toolKit, constructor);
- }
-
- private ConstructorParameterMeta[] initParameters(RestToolKit toolKit, Constructor<?> ct) {
- Parameter[] cps = ct.getParameters();
- int len = cps.length;
- ConstructorParameterMeta[] parameters = new ConstructorParameterMeta[len];
- for (int i = 0; i < len; i++) {
- parameters[i] = new ConstructorParameterMeta(toolKit, cps[i]);
- }
- return parameters;
- }
-
- Object newInstance(Object[] args) throws Exception {
- return constructor.newInstance(args);
- }
- }
-
- public static final class ConstructorParameterMeta extends ParameterMeta {
-
- private final Parameter parameter;
-
- ConstructorParameterMeta(RestToolKit toolKit, Parameter parameter) {
- super(toolKit, parameter.isNamePresent() ? parameter.getName() : null);
- this.parameter = parameter;
- }
-
- @Override
- protected AnnotatedElement getAnnotatedElement() {
- return parameter;
- }
-
- @Override
- public Class<?> getType() {
- return parameter.getType();
- }
-
- @Override
- public Type getGenericType() {
- return parameter.getParameterizedType();
- }
- }
}
diff --git a/dubbo-plugin/dubbo-rest-spring/src/main/java/org/apache/dubbo/rpc/protocol/tri/rest/support/spring/AbstractSpringArgumentResolver.java b/dubbo-plugin/dubbo-rest-spring/src/main/java/org/apache/dubbo/rpc/protocol/tri/rest/support/spring/BindParamArgumentResolver.java
similarity index 54%
copy from dubbo-plugin/dubbo-rest-spring/src/main/java/org/apache/dubbo/rpc/protocol/tri/rest/support/spring/AbstractSpringArgumentResolver.java
copy to dubbo-plugin/dubbo-rest-spring/src/main/java/org/apache/dubbo/rpc/protocol/tri/rest/support/spring/BindParamArgumentResolver.java
index fef61723e7..a4906b7aec 100644
--- a/dubbo-plugin/dubbo-rest-spring/src/main/java/org/apache/dubbo/rpc/protocol/tri/rest/support/spring/AbstractSpringArgumentResolver.java
+++ b/dubbo-plugin/dubbo-rest-spring/src/main/java/org/apache/dubbo/rpc/protocol/tri/rest/support/spring/BindParamArgumentResolver.java
@@ -16,17 +16,42 @@
*/
package org.apache.dubbo.rpc.protocol.tri.rest.support.spring;
+import org.apache.dubbo.common.extension.Activate;
+import org.apache.dubbo.remoting.http12.HttpRequest;
+import org.apache.dubbo.remoting.http12.HttpResponse;
import org.apache.dubbo.rpc.protocol.tri.rest.argument.AbstractAnnotationBaseArgumentResolver;
import org.apache.dubbo.rpc.protocol.tri.rest.mapping.meta.AnnotationMeta;
import org.apache.dubbo.rpc.protocol.tri.rest.mapping.meta.NamedValueMeta;
import org.apache.dubbo.rpc.protocol.tri.rest.mapping.meta.ParameterMeta;
+import org.apache.dubbo.rpc.protocol.tri.rest.util.RequestUtils;
import java.lang.annotation.Annotation;
-public abstract class AbstractSpringArgumentResolver extends AbstractAnnotationBaseArgumentResolver {
+@Activate(onClass = "org.springframework.web.bind.annotation.BindParam")
+public final class BindParamArgumentResolver extends AbstractAnnotationBaseArgumentResolver {
+
+ @Override
+ public Class<Annotation> accept() {
+ return Annotations.BindParam.type();
+ }
@Override
protected NamedValueMeta createNamedValueMeta(ParameterMeta param, AnnotationMeta<Annotation> ann) {
- return new NamedValueMeta(ann.getValue(), Helper.isRequired(ann), Helper.defaultValue(ann));
+ return new NamedValueMeta(ann.getValue(), param.isAnnotated(Annotations.Nonnull), null);
+ }
+
+ @Override
+ protected Object resolveValue(NamedValueMeta meta, HttpRequest request, HttpResponse response) {
+ return request.parameter(meta.name());
+ }
+
+ @Override
+ protected Object resolveCollectionValue(NamedValueMeta meta, HttpRequest request, HttpResponse response) {
+ return request.parameterValues(meta.name());
+ }
+
+ @Override
+ protected Object resolveMapValue(NamedValueMeta meta, HttpRequest request, HttpResponse response) {
+ return RequestUtils.getParametersMap(request);
}
}
diff --git a/dubbo-plugin/dubbo-rest-spring/src/main/java/org/apache/dubbo/rpc/protocol/tri/rest/support/spring/SpringMvcRequestMappingResolver.java b/dubbo-plugin/dubbo-rest-spring/src/main/java/org/apache/dubbo/rpc/protocol/tri/rest/support/spring/SpringMvcRequestMappingResolver.java
index e6de4f568b..728156a98d 100644
--- a/dubbo-plugin/dubbo-rest-spring/src/main/java/org/apache/dubbo/rpc/protocol/tri/rest/support/spring/SpringMvcRequestMappingResolver.java
+++ b/dubbo-plugin/dubbo-rest-spring/src/main/java/org/apache/dubbo/rpc/protocol/tri/rest/support/spring/SpringMvcRequestMappingResolver.java
@@ -62,44 +62,50 @@ public class SpringMvcRequestMappingResolver implements RequestMappingResolver {
@Override
public RequestMapping resolve(ServiceMeta serviceMeta) {
AnnotationMeta<?> requestMapping = serviceMeta.findMergedAnnotation(Annotations.RequestMapping);
- if (requestMapping == null) {
+ AnnotationMeta<?> httpExchange = serviceMeta.findMergedAnnotation(Annotations.HttpExchange);
+ if (requestMapping == null && httpExchange == null) {
return null;
}
- AnnotationMeta<?> responseStatus = serviceMeta.findMergedAnnotation(Annotations.ResponseStatus);
- AnnotationMeta<?> crossOrigin = serviceMeta.findMergedAnnotation(Annotations.CrossOrigin);
- String[] methods = requestMapping.getStringArray("method");
- return builder(requestMapping, responseStatus)
+
+ String[] methods = requestMapping == null
+ ? httpExchange.getStringArray("method")
+ : requestMapping.getStringArray("method");
+ return builder(requestMapping, httpExchange, serviceMeta.findMergedAnnotation(Annotations.ResponseStatus))
.method(methods)
.name(serviceMeta.getType().getSimpleName())
.contextPath(serviceMeta.getContextPath())
- .cors(buildCorsMeta(crossOrigin, methods))
+ .cors(buildCorsMeta(serviceMeta.findMergedAnnotation(Annotations.CrossOrigin), methods))
.build();
}
@Override
public RequestMapping resolve(MethodMeta methodMeta) {
AnnotationMeta<?> requestMapping = methodMeta.findMergedAnnotation(Annotations.RequestMapping);
- if (requestMapping == null) {
+ AnnotationMeta<?> httpExchange = methodMeta.findMergedAnnotation(Annotations.HttpExchange);
+ if (requestMapping == null && httpExchange == null) {
AnnotationMeta<?> exceptionHandler = methodMeta.getAnnotation(Annotations.ExceptionHandler);
if (exceptionHandler != null) {
+ methodMeta.initParameters();
methodMeta.getServiceMeta().addExceptionHandler(methodMeta);
}
return null;
}
+
ServiceMeta serviceMeta = methodMeta.getServiceMeta();
- AnnotationMeta<?> responseStatus = methodMeta.findMergedAnnotation(Annotations.ResponseStatus);
- AnnotationMeta<?> crossOrigin = methodMeta.findMergedAnnotation(Annotations.CrossOrigin);
- String[] methods = requestMapping.getStringArray("method");
- return builder(requestMapping, responseStatus)
+ String[] methods = requestMapping == null
+ ? httpExchange.getStringArray("method")
+ : requestMapping.getStringArray("method");
+ return builder(requestMapping, httpExchange, methodMeta.findMergedAnnotation(Annotations.ResponseStatus))
.method(methods)
.name(methodMeta.getMethod().getName())
.contextPath(serviceMeta.getContextPath())
.custom(new ServiceVersionCondition(serviceMeta.getServiceGroup(), serviceMeta.getServiceVersion()))
- .cors(buildCorsMeta(crossOrigin, methods))
+ .cors(buildCorsMeta(methodMeta.findMergedAnnotation(Annotations.CrossOrigin), methods))
.build();
}
- private Builder builder(AnnotationMeta<?> requestMapping, AnnotationMeta<?> responseStatus) {
+ private Builder builder(
+ AnnotationMeta<?> requestMapping, AnnotationMeta<?> httpExchange, AnnotationMeta<?> responseStatus) {
Builder builder = RequestMapping.builder();
if (responseStatus != null) {
HttpStatus value = responseStatus.getEnum("value");
@@ -109,6 +115,11 @@ public class SpringMvcRequestMappingResolver implements RequestMappingResolver {
builder.responseReason(reason);
}
}
+ if (requestMapping == null) {
+ return builder.path(httpExchange.getValueArray())
+ .consume(httpExchange.getStringArray("contentType"))
+ .produce(httpExchange.getStringArray("accept"));
+ }
return builder.path(requestMapping.getValueArray())
.param(requestMapping.getStringArray("params"))
.header(requestMapping.getStringArray("headers"))
diff --git a/dubbo-plugin/dubbo-rest-spring/src/main/java/org/apache/dubbo/rpc/protocol/tri/rest/support/spring/SpringRestToolKit.java b/dubbo-plugin/dubbo-rest-spring/src/main/java/org/apache/dubbo/rpc/protocol/tri/rest/support/spring/SpringRestToolKit.java
index 9e5011b683..a2638c7748 100644
--- a/dubbo-plugin/dubbo-rest-spring/src/main/java/org/apache/dubbo/rpc/protocol/tri/rest/support/spring/SpringRestToolKit.java
+++ b/dubbo-plugin/dubbo-rest-spring/src/main/java/org/apache/dubbo/rpc/protocol/tri/rest/support/spring/SpringRestToolKit.java
@@ -32,6 +32,7 @@ import org.apache.dubbo.rpc.protocol.tri.rest.util.RestUtils;
import java.lang.annotation.Annotation;
import java.lang.reflect.AnnotatedElement;
import java.lang.reflect.Method;
+import java.util.Collection;
import java.util.Map;
import org.springframework.beans.factory.config.ConfigurableBeanFactory;
@@ -97,6 +98,12 @@ final class SpringRestToolKit implements RestToolKit {
@Override
public Object convert(Object value, ParameterMeta parameter) {
+ if (value instanceof Collection) {
+ Object target = typeConverter.convert(value, parameter.getGenericType());
+ if (target != null) {
+ return target;
+ }
+ }
TypeDescriptor targetType = (TypeDescriptor) parameter.getTypeDescriptor();
if (targetType == null) {
MethodParameterMeta meta = (MethodParameterMeta) parameter;
diff --git a/dubbo-plugin/dubbo-rest-spring/src/main/resources/META-INF/dubbo/internal/org.apache.dubbo.rpc.protocol.tri.rest.argument.ArgumentResolver b/dubbo-plugin/dubbo-rest-spring/src/main/resources/META-INF/dubbo/internal/org.apache.dubbo.rpc.protocol.tri.rest.argument.ArgumentResolver
index 7b0136aee7..36e95f532c 100644
--- a/dubbo-plugin/dubbo-rest-spring/src/main/resources/META-INF/dubbo/internal/org.apache.dubbo.rpc.protocol.tri.rest.argument.ArgumentResolver
+++ b/dubbo-plugin/dubbo-rest-spring/src/main/resources/META-INF/dubbo/internal/org.apache.dubbo.rpc.protocol.tri.rest.argument.ArgumentResolver
@@ -7,6 +7,7 @@ spring-request-attribute=org.apache.dubbo.rpc.protocol.tri.rest.support.spring.R
spring-request-part=org.apache.dubbo.rpc.protocol.tri.rest.support.spring.RequestPartArgumentResolver
spring-request-body=org.apache.dubbo.rpc.protocol.tri.rest.support.spring.RequestBodyArgumentResolver
spring-model-attribute=org.apache.dubbo.rpc.protocol.tri.rest.support.spring.ModelAttributeArgumentResolver
+spring-bind-param=org.apache.dubbo.rpc.protocol.tri.rest.support.spring.BindParamArgumentResolver
spring-misc=org.apache.dubbo.rpc.protocol.tri.rest.support.spring.SpringMiscArgumentResolver
spring-fallback=org.apache.dubbo.rpc.protocol.tri.rest.support.spring.FallbackArgumentResolver
diff --git a/dubbo-plugin/dubbo-rest-spring/src/test/groovy/org/apache/dubbo/rpc/protocol/tri/rest/support/spring/RestProtocolTest.groovy b/dubbo-plugin/dubbo-rest-spring/src/test/groovy/org/apache/dubbo/rpc/protocol/tri/rest/support/spring/RestProtocolTest.groovy
new file mode 100644
index 0000000000..ec1935b122
--- /dev/null
+++ b/dubbo-plugin/dubbo-rest-spring/src/test/groovy/org/apache/dubbo/rpc/protocol/tri/rest/support/spring/RestProtocolTest.groovy
@@ -0,0 +1,72 @@
+/*
+ * 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.dubbo.rpc.protocol.tri.rest.support.spring
+
+import org.apache.dubbo.rpc.protocol.tri.rest.service.DemoServiceImpl
+import org.apache.dubbo.rpc.protocol.tri.rest.support.spring.service.SpringDemoServiceImpl
+import org.apache.dubbo.rpc.protocol.tri.rest.test.BaseServiceTest
+import org.apache.dubbo.rpc.protocol.tri.test.TestRunnerBuilder
+
+class RestProtocolTest extends BaseServiceTest {
+
+ @Override
+ void setupService(TestRunnerBuilder builder) {
+ builder.provider(new DemoServiceImpl())
+ builder.provider(new SpringDemoServiceImpl())
+ }
+
+ def "hello world"() {
+ expect:
+ runner.get(path) == output
+ where:
+ path | output
+ '/hello?name=world' | 'hello world'
+ '/hello/?name=world' | 'hello world'
+ '/hello.yml?name=world' | 'hello world'
+ }
+
+ def "spring bean argument test"() {
+ expect:
+ runner.get(path) contains output
+ where:
+ path | output
+ '/springBean?id=1&name=sam' | '"id":1,"name":"sam"'
+ '/springBean?name=sam&phone=123&email=a@b.com' | '"email":"a@b.com","name":"sam","phone":"123"'
+ '/springBean?group.name=g1&group.owner.name=jack' | '"group":{"id":0,"name":"g1","owner":{"name":"jack"'
+ '/springBean?group.parent.parent.children[0].name=xx' | '"group":{"id":0,"parent":{"id":0,"parent":{"children":[{"id":0,"name":"xx"}],"id":0}}}'
+ '/springBean?ids=3&ids=4' | '"ids":[3,4]'
+ '/springBean?ids[]=3&ids[]=4' | '"ids":[3,4]'
+ '/springBean?ids[1]=3&ids[2]=4' | '"ids":[0,3,4]'
+ '/springBean?scores=3&scores=4' | '"scores":[3,4]'
+ '/springBean?scores[]=3&scores[]=4' | '"scores":[3,4]'
+ '/springBean?scores[1]=3&scores[2]=4' | '"scores":[null,3,4]'
+ '/springBean?tags[0].name=a&tags[0].value=b&tags[1].name=c&tags[1].value=d' | '"tags":[{"name":"a","value":"b"},{"name":"c","value":"d"}]'
+ '/springBean?tagsA[0].name=a&tagsA[0].value=b&tagsA[1].name=c&tagsA[1].value=d' | '"tagsA":[{"name":"a","value":"b"},{"name":"c","value":"d"}]'
+ '/springBean?tagsB[0].name=e&tagsB[1].name=c&tagsB[1].value=d' | '"tagsB":[{"name":"e","value":"b"},{"name":"c","value":"d"}]'
+ '/springBean?tagsC[0].name=e&tagsC[1].name=c&tagsC[1].value=d' | '"tagsC":[{"name":"e","value":"b"},{"name":"c","value":"d"}]'
+ '/springBean?groupMaps[0].one.name=a&groupMaps[1].two.name=b' | '"groupMaps":[{},{}]' //x
+ '/springBean?id=1&features.a=xx&features.b=2' | '"features":{},"id":1' //x
+ '/springBean?id=1&features[a]=xx&features[b]=2' | '"features":{"a":"xx","b":"2"}'
+ '/springBean?group.id=2&group.features.a=1&group.features.b=xx' | '"group":{"features":{},"id":2}' //x
+ '/springBean?tagMap.a.name=a&tagMap.a.value=b&tagMap.b.name=c&tagMap.b.value=d' | '"tagMap":{}' //x
+ '/springBean?tagMapA.a.name=e&tagMapA.b.name=c&tagMapA.b.value=d' | '"tagMapA":{"a":{"name":"a","value":"b"}}' //x
+ '/springBean?tagMapB[2].name=a&tagMapB[2].value=b&tagMapB[3].name=c' | '"tagMapB":{2:{"name":"a","value":"b"},3:{"name":"c"}}'
+ '/springBean?groupsMap.one[0].name=a&groupsMap.one[1].name=b&groupsMap.two[1].name=c' | '"groupsMap":{}' //x
+ }
+
+}
diff --git a/dubbo-plugin/dubbo-rest-jaxrs/src/test/java/org/apache/dubbo/rpc/protocol/tri/rest/support/jaxrs/compatible/rest/ParamConverterServiceImpl.java b/dubbo-plugin/dubbo-rest-spring/src/test/java/org/apache/dubbo/rpc/protocol/tri/rest/support/spring/service/SpringDemoService.java
similarity index 72%
copy from dubbo-plugin/dubbo-rest-jaxrs/src/test/java/org/apache/dubbo/rpc/protocol/tri/rest/support/jaxrs/compatible/rest/ParamConverterServiceImpl.java
copy to dubbo-plugin/dubbo-rest-spring/src/test/java/org/apache/dubbo/rpc/protocol/tri/rest/support/spring/service/SpringDemoService.java
index 2b0bedfd21..bdf66f566a 100644
--- a/dubbo-plugin/dubbo-rest-jaxrs/src/test/java/org/apache/dubbo/rpc/protocol/tri/rest/support/jaxrs/compatible/rest/ParamConverterServiceImpl.java
+++ b/dubbo-plugin/dubbo-rest-spring/src/test/java/org/apache/dubbo/rpc/protocol/tri/rest/support/spring/service/SpringDemoService.java
@@ -14,13 +14,14 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package org.apache.dubbo.rpc.protocol.tri.rest.support.jaxrs.compatible.rest;
+package org.apache.dubbo.rpc.protocol.tri.rest.support.spring.service;
-import org.apache.dubbo.rpc.protocol.tri.rest.support.jaxrs.compatible.User;
+import org.apache.dubbo.rpc.protocol.tri.rest.service.User.UserEx;
-public class ParamConverterServiceImpl implements ParamConverterService {
- @Override
- public User convert(User user) {
- return user;
- }
+import org.springframework.web.bind.annotation.GetMapping;
+
+public interface SpringDemoService {
+
+ @GetMapping("/springBean")
+ UserEx beanArgTest(UserEx user);
}
diff --git a/dubbo-plugin/dubbo-rest-jaxrs/src/test/java/org/apache/dubbo/rpc/protocol/tri/rest/support/jaxrs/compatible/rest/ParamConverterServiceImpl.java b/dubbo-plugin/dubbo-rest-spring/src/test/java/org/apache/dubbo/rpc/protocol/tri/rest/support/spring/service/SpringDemoServiceImpl.java
similarity index 76%
copy from dubbo-plugin/dubbo-rest-jaxrs/src/test/java/org/apache/dubbo/rpc/protocol/tri/rest/support/jaxrs/compatible/rest/ParamConverterServiceImpl.java
copy to dubbo-plugin/dubbo-rest-spring/src/test/java/org/apache/dubbo/rpc/protocol/tri/rest/support/spring/service/SpringDemoServiceImpl.java
index 2b0bedfd21..97ef3cfe71 100644
--- a/dubbo-plugin/dubbo-rest-jaxrs/src/test/java/org/apache/dubbo/rpc/protocol/tri/rest/support/jaxrs/compatible/rest/ParamConverterServiceImpl.java
+++ b/dubbo-plugin/dubbo-rest-spring/src/test/java/org/apache/dubbo/rpc/protocol/tri/rest/support/spring/service/SpringDemoServiceImpl.java
@@ -14,13 +14,14 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package org.apache.dubbo.rpc.protocol.tri.rest.support.jaxrs.compatible.rest;
+package org.apache.dubbo.rpc.protocol.tri.rest.support.spring.service;
-import org.apache.dubbo.rpc.protocol.tri.rest.support.jaxrs.compatible.User;
+import org.apache.dubbo.rpc.protocol.tri.rest.service.User.UserEx;
+
+public class SpringDemoServiceImpl implements SpringDemoService {
-public class ParamConverterServiceImpl implements ParamConverterService {
@Override
- public User convert(User user) {
+ public UserEx beanArgTest(UserEx user) {
return user;
}
}
diff --git a/dubbo-plugin/dubbo-rest-spring/src/test/resources/log4j2-test.xml b/dubbo-plugin/dubbo-rest-spring/src/test/resources/log4j2-test.xml
index 34d1c53e88..91337f333b 100644
--- a/dubbo-plugin/dubbo-rest-spring/src/test/resources/log4j2-test.xml
+++ b/dubbo-plugin/dubbo-rest-spring/src/test/resources/log4j2-test.xml
@@ -23,6 +23,7 @@
</Appenders>
<Loggers>
<Logger name="org.apache.dubbo.rpc.protocol.tri.stream.StreamUtils" level="error"/>
+ <Logger name="org.apache.dubbo.rpc.protocol.tri.h12" level="debug"/>
<Root level="info">
<AppenderRef ref="Console"/>
</Root>
diff --git a/dubbo-plugin/dubbo-triple-servlet/pom.xml b/dubbo-plugin/dubbo-triple-servlet/pom.xml
index 0258304f99..ab55ef6942 100644
--- a/dubbo-plugin/dubbo-triple-servlet/pom.xml
+++ b/dubbo-plugin/dubbo-triple-servlet/pom.xml
@@ -28,7 +28,7 @@
<properties>
<servlet4_version>4.0.1</servlet4_version>
- <sources_directory>${project.build.directory}/generated-sources/java/org/apache/dubbo/rpc/protocol/tri/servlet/jakarta</sources_directory>
+ <sources_directory>${project.build.directory}/generated-sources/java/org/apache/dubbo/rpc/protocol/tri</sources_directory>
</properties>
<dependencies>
@@ -69,22 +69,43 @@
<artifactId>maven-antrun-plugin</artifactId>
<executions>
<execution>
- <id>copy-sources</id>
+ <id>generate-jakarta</id>
<goals>
<goal>run</goal>
</goals>
<phase>generate-sources</phase>
<configuration>
<target>
- <copy overwrite="true" todir="${sources_directory}">
+ <copy overwrite="true" todir="${sources_directory}/servlet/jakarta">
<fileset dir="src/main/java/org/apache/dubbo/rpc/protocol/tri/servlet" />
</copy>
- <replace token="tri.servlet;" value="tri.servlet.jakarta;">
+ <copy overwrite="true" todir="${sources_directory}/rest/support/servlet/jakarta">
+ <fileset dir="src/main/java/org/apache/dubbo/rpc/protocol/tri/rest/support/servlet" />
+ </copy>
+ <replace token=".servlet;" value=".servlet.jakarta;">
<fileset dir="${sources_directory}" />
</replace>
<replace token="javax.servlet" value="jakarta.servlet">
<fileset dir="${sources_directory}" />
</replace>
+ <replace file="${sources_directory}/rest/support/servlet/jakarta/ServletHttpRequestAdapter.java">
+ <replacetoken>/* jakarta placeholder */</replacetoken>
+ <replacevalue><![CDATA[@Override
+ public String getRequestId() {
+ return "";
+ }
+
+ @Override
+ public String getProtocolRequestId() {
+ return "";
+ }
+
+ @Override
+ public jakarta.servlet.ServletConnection getServletConnection() {
+ return null;
+ }]]></replacevalue>
+ </replace>
+ <replace file="${sources_directory}/rest/support/servlet/jakarta/ServletHttpMessageAdapterFactory.java" token="-100" value="-200" />
</target>
</configuration>
</execution>
diff --git a/dubbo-plugin/dubbo-rest-servlet/src/main/java/org/apache/dubbo/rpc/protocol/tri/rest/support/servlet/DummyFilterConfig.java b/dubbo-plugin/dubbo-triple-servlet/src/main/java/org/apache/dubbo/rpc/protocol/tri/rest/support/servlet/DummyFilterConfig.java
similarity index 100%
rename from dubbo-plugin/dubbo-rest-servlet/src/main/java/org/apache/dubbo/rpc/protocol/tri/rest/support/servlet/DummyFilterConfig.java
rename to dubbo-plugin/dubbo-triple-servlet/src/main/java/org/apache/dubbo/rpc/protocol/tri/rest/support/servlet/DummyFilterConfig.java
diff --git a/dubbo-plugin/dubbo-rest-servlet/src/main/java/org/apache/dubbo/rpc/protocol/tri/rest/support/servlet/DummyServletContext.java b/dubbo-plugin/dubbo-triple-servlet/src/main/java/org/apache/dubbo/rpc/protocol/tri/rest/support/servlet/DummyServletContext.java
similarity index 91%
rename from dubbo-plugin/dubbo-rest-servlet/src/main/java/org/apache/dubbo/rpc/protocol/tri/rest/support/servlet/DummyServletContext.java
rename to dubbo-plugin/dubbo-triple-servlet/src/main/java/org/apache/dubbo/rpc/protocol/tri/rest/support/servlet/DummyServletContext.java
index 3bafcd4367..e3b382537c 100644
--- a/dubbo-plugin/dubbo-rest-servlet/src/main/java/org/apache/dubbo/rpc/protocol/tri/rest/support/servlet/DummyServletContext.java
+++ b/dubbo-plugin/dubbo-triple-servlet/src/main/java/org/apache/dubbo/rpc/protocol/tri/rest/support/servlet/DummyServletContext.java
@@ -28,6 +28,7 @@ import javax.servlet.RequestDispatcher;
import javax.servlet.Servlet;
import javax.servlet.ServletContext;
import javax.servlet.ServletRegistration;
+import javax.servlet.ServletRegistration.Dynamic;
import javax.servlet.SessionCookieConfig;
import javax.servlet.SessionTrackingMode;
import javax.servlet.descriptor.JspConfigDescriptor;
@@ -111,17 +112,14 @@ final class DummyServletContext implements ServletContext {
return null;
}
- @Override
public Servlet getServlet(String name) {
throw new UnsupportedOperationException();
}
- @Override
public Enumeration<Servlet> getServlets() {
throw new UnsupportedOperationException();
}
- @Override
public Enumeration<String> getServletNames() {
throw new UnsupportedOperationException();
}
@@ -131,7 +129,6 @@ final class DummyServletContext implements ServletContext {
LoggerFactory.getLogger(DummyServletContext.class).info(msg);
}
- @Override
public void log(Exception exception, String msg) {
LoggerFactory.getLogger(DummyServletContext.class).info(msg, exception);
}
@@ -211,6 +208,11 @@ final class DummyServletContext implements ServletContext {
throw new UnsupportedOperationException();
}
+ @Override
+ public Dynamic addJspFile(String servletName, String jspFile) {
+ return null;
+ }
+
@Override
public <T extends Servlet> T createServlet(Class<T> clazz) {
throw new UnsupportedOperationException();
@@ -315,4 +317,34 @@ final class DummyServletContext implements ServletContext {
public String getVirtualServerName() {
throw new UnsupportedOperationException();
}
+
+ @Override
+ public int getSessionTimeout() {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public void setSessionTimeout(int i) {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public String getRequestCharacterEncoding() {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public void setRequestCharacterEncoding(String encoding) {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public String getResponseCharacterEncoding() {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public void setResponseCharacterEncoding(String encoding) {
+ throw new UnsupportedOperationException();
+ }
}
diff --git a/dubbo-plugin/dubbo-rest-servlet/src/main/java/org/apache/dubbo/rpc/protocol/tri/rest/support/servlet/FilterAdapter.java b/dubbo-plugin/dubbo-triple-servlet/src/main/java/org/apache/dubbo/rpc/protocol/tri/rest/support/servlet/FilterAdapter.java
similarity index 100%
rename from dubbo-plugin/dubbo-rest-servlet/src/main/java/org/apache/dubbo/rpc/protocol/tri/rest/support/servlet/FilterAdapter.java
rename to dubbo-plugin/dubbo-triple-servlet/src/main/java/org/apache/dubbo/rpc/protocol/tri/rest/support/servlet/FilterAdapter.java
diff --git a/dubbo-plugin/dubbo-rest-servlet/src/main/java/org/apache/dubbo/rpc/protocol/tri/rest/support/servlet/Helper.java b/dubbo-plugin/dubbo-triple-servlet/src/main/java/org/apache/dubbo/rpc/protocol/tri/rest/support/servlet/Helper.java
similarity index 100%
rename from dubbo-plugin/dubbo-rest-servlet/src/main/java/org/apache/dubbo/rpc/protocol/tri/rest/support/servlet/Helper.java
rename to dubbo-plugin/dubbo-triple-servlet/src/main/java/org/apache/dubbo/rpc/protocol/tri/rest/support/servlet/Helper.java
diff --git a/dubbo-plugin/dubbo-rest-servlet/src/main/java/org/apache/dubbo/rpc/protocol/tri/rest/support/servlet/HttpSessionFactory.java b/dubbo-plugin/dubbo-triple-servlet/src/main/java/org/apache/dubbo/rpc/protocol/tri/rest/support/servlet/HttpSessionFactory.java
similarity index 100%
rename from dubbo-plugin/dubbo-rest-servlet/src/main/java/org/apache/dubbo/rpc/protocol/tri/rest/support/servlet/HttpSessionFactory.java
rename to dubbo-plugin/dubbo-triple-servlet/src/main/java/org/apache/dubbo/rpc/protocol/tri/rest/support/servlet/HttpSessionFactory.java
diff --git a/dubbo-plugin/dubbo-rest-servlet/src/main/java/org/apache/dubbo/rpc/protocol/tri/rest/support/servlet/ServletArgumentResolver.java b/dubbo-plugin/dubbo-triple-servlet/src/main/java/org/apache/dubbo/rpc/protocol/tri/rest/support/servlet/ServletArgumentResolver.java
similarity index 100%
rename from dubbo-plugin/dubbo-rest-servlet/src/main/java/org/apache/dubbo/rpc/protocol/tri/rest/support/servlet/ServletArgumentResolver.java
rename to dubbo-plugin/dubbo-triple-servlet/src/main/java/org/apache/dubbo/rpc/protocol/tri/rest/support/servlet/ServletArgumentResolver.java
diff --git a/dubbo-plugin/dubbo-rest-servlet/src/main/java/org/apache/dubbo/rpc/protocol/tri/rest/support/servlet/ServletHttpMessageAdapterFactory.java b/dubbo-plugin/dubbo-triple-servlet/src/main/java/org/apache/dubbo/rpc/protocol/tri/rest/support/servlet/ServletHttpMessageAdapterFactory.java
similarity index 90%
rename from dubbo-plugin/dubbo-rest-servlet/src/main/java/org/apache/dubbo/rpc/protocol/tri/rest/support/servlet/ServletHttpMessageAdapterFactory.java
rename to dubbo-plugin/dubbo-triple-servlet/src/main/java/org/apache/dubbo/rpc/protocol/tri/rest/support/servlet/ServletHttpMessageAdapterFactory.java
index 1f85d58b74..1862593203 100644
--- a/dubbo-plugin/dubbo-rest-servlet/src/main/java/org/apache/dubbo/rpc/protocol/tri/rest/support/servlet/ServletHttpMessageAdapterFactory.java
+++ b/dubbo-plugin/dubbo-triple-servlet/src/main/java/org/apache/dubbo/rpc/protocol/tri/rest/support/servlet/ServletHttpMessageAdapterFactory.java
@@ -29,7 +29,7 @@ import javax.servlet.ServletContext;
@Activate(order = -100, onClass = "javax.servlet.http.HttpServletRequest")
public final class ServletHttpMessageAdapterFactory
- implements HttpMessageAdapterFactory<ServletHttpRequestAdaptee, HttpMetadata, Void> {
+ implements HttpMessageAdapterFactory<ServletHttpRequestAdapter, HttpMetadata, Void> {
private final FrameworkModel frameworkModel;
private final ServletContext servletContext;
@@ -51,13 +51,13 @@ public final class ServletHttpMessageAdapterFactory
}
@Override
- public ServletHttpRequestAdaptee adaptRequest(HttpMetadata rawRequest, HttpChannel channel) {
- return new ServletHttpRequestAdaptee(rawRequest, channel, servletContext, httpSessionFactory);
+ public ServletHttpRequestAdapter adaptRequest(HttpMetadata rawRequest, HttpChannel channel) {
+ return new ServletHttpRequestAdapter(rawRequest, channel, servletContext, httpSessionFactory);
}
@Override
- public HttpResponse adaptResponse(ServletHttpRequestAdaptee request, HttpMetadata rawRequest, Void rawResponse) {
- return new ServletHttpResponseAdaptee();
+ public HttpResponse adaptResponse(ServletHttpRequestAdapter request, HttpMetadata rawRequest, Void rawResponse) {
+ return new ServletHttpResponseAdapter();
}
public FilterConfig adaptFilterConfig(String filterName) {
diff --git a/dubbo-plugin/dubbo-rest-servlet/src/main/java/org/apache/dubbo/rpc/protocol/tri/rest/support/servlet/ServletHttpRequestAdaptee.java b/dubbo-plugin/dubbo-triple-servlet/src/main/java/org/apache/dubbo/rpc/protocol/tri/rest/support/servlet/ServletHttpRequestAdapter.java
similarity index 98%
rename from dubbo-plugin/dubbo-rest-servlet/src/main/java/org/apache/dubbo/rpc/protocol/tri/rest/support/servlet/ServletHttpRequestAdaptee.java
rename to dubbo-plugin/dubbo-triple-servlet/src/main/java/org/apache/dubbo/rpc/protocol/tri/rest/support/servlet/ServletHttpRequestAdapter.java
index 6099a6c8d3..46c7b9a637 100644
--- a/dubbo-plugin/dubbo-rest-servlet/src/main/java/org/apache/dubbo/rpc/protocol/tri/rest/support/servlet/ServletHttpRequestAdaptee.java
+++ b/dubbo-plugin/dubbo-triple-servlet/src/main/java/org/apache/dubbo/rpc/protocol/tri/rest/support/servlet/ServletHttpRequestAdapter.java
@@ -50,7 +50,7 @@ import java.util.List;
import java.util.Locale;
import java.util.Map;
-public class ServletHttpRequestAdaptee extends DefaultHttpRequest implements HttpServletRequest {
+public class ServletHttpRequestAdapter extends DefaultHttpRequest implements HttpServletRequest {
private final ServletContext servletContext;
private final HttpSessionFactory sessionFactory;
@@ -58,7 +58,7 @@ public class ServletHttpRequestAdaptee extends DefaultHttpRequest implements Htt
private ServletInputStream sis;
private BufferedReader reader;
- public ServletHttpRequestAdaptee(
+ public ServletHttpRequestAdapter(
HttpMetadata metadata,
HttpChannel channel,
ServletContext servletContext,
@@ -211,7 +211,6 @@ public class ServletHttpRequestAdaptee extends DefaultHttpRequest implements Htt
return false;
}
- @Override
public boolean isRequestedSessionIdFromUrl() {
return false;
}
@@ -376,7 +375,6 @@ public class ServletHttpRequestAdaptee extends DefaultHttpRequest implements Htt
throw new UnsupportedOperationException();
}
- @Override
public String getRealPath(String path) {
return null;
}
@@ -437,9 +435,11 @@ public class ServletHttpRequestAdaptee extends DefaultHttpRequest implements Htt
return DispatcherType.REQUEST;
}
+ /* jakarta placeholder */
+
@Override
public String toString() {
- return "ServletHttpRequestAdaptee{" + fieldToString() + '}';
+ return "ServletHttpRequestAdapter{" + fieldToString() + '}';
}
private static final class HttpInputStream extends ServletInputStream {
diff --git a/dubbo-plugin/dubbo-rest-servlet/src/main/java/org/apache/dubbo/rpc/protocol/tri/rest/support/servlet/ServletHttpResponseAdaptee.java b/dubbo-plugin/dubbo-triple-servlet/src/main/java/org/apache/dubbo/rpc/protocol/tri/rest/support/servlet/ServletHttpResponseAdapter.java
similarity index 95%
rename from dubbo-plugin/dubbo-rest-servlet/src/main/java/org/apache/dubbo/rpc/protocol/tri/rest/support/servlet/ServletHttpResponseAdaptee.java
rename to dubbo-plugin/dubbo-triple-servlet/src/main/java/org/apache/dubbo/rpc/protocol/tri/rest/support/servlet/ServletHttpResponseAdapter.java
index 75068cf399..85e3f2c973 100644
--- a/dubbo-plugin/dubbo-rest-servlet/src/main/java/org/apache/dubbo/rpc/protocol/tri/rest/support/servlet/ServletHttpResponseAdaptee.java
+++ b/dubbo-plugin/dubbo-triple-servlet/src/main/java/org/apache/dubbo/rpc/protocol/tri/rest/support/servlet/ServletHttpResponseAdapter.java
@@ -34,7 +34,7 @@ import java.util.Collection;
import java.util.Date;
import java.util.Locale;
-public class ServletHttpResponseAdaptee extends DefaultHttpResponse implements HttpServletResponse {
+public class ServletHttpResponseAdapter extends DefaultHttpResponse implements HttpServletResponse {
private ServletOutputStream sos;
private PrintWriter writer;
@@ -59,16 +59,18 @@ public class ServletHttpResponseAdaptee extends DefaultHttpResponse implements H
return RequestUtils.encodeURL(url);
}
- @Override
public String encodeUrl(String url) {
return RequestUtils.encodeURL(url);
}
- @Override
public String encodeRedirectUrl(String url) {
return RequestUtils.encodeURL(url);
}
+ public void sendRedirect(String location, int sc, boolean clearBuffer) {
+ sendRedirect(location);
+ }
+
@Override
public void setDateHeader(String name, long date) {
setHeader(name, new Date(date));
@@ -89,7 +91,6 @@ public class ServletHttpResponseAdaptee extends DefaultHttpResponse implements H
addHeader(name, String.valueOf(value));
}
- @Override
public void setStatus(int sc, String sm) {
setStatus(sc);
setBody(sm);
@@ -182,7 +183,7 @@ public class ServletHttpResponseAdaptee extends DefaultHttpResponse implements H
@Override
public String toString() {
- return "ServletHttpResponseAdaptee{" + fieldToString() + '}';
+ return "ServletHttpResponseAdapter{" + fieldToString() + '}';
}
private static final class HttpOutputStream extends ServletOutputStream {
diff --git a/dubbo-plugin/dubbo-triple-servlet/src/main/java/org/apache/dubbo/rpc/protocol/tri/servlet/HttpMetadataAdapter.java b/dubbo-plugin/dubbo-triple-servlet/src/main/java/org/apache/dubbo/rpc/protocol/tri/servlet/HttpMetadataAdapter.java
index 15af2654c7..83e29af618 100644
--- a/dubbo-plugin/dubbo-triple-servlet/src/main/java/org/apache/dubbo/rpc/protocol/tri/servlet/HttpMetadataAdapter.java
+++ b/dubbo-plugin/dubbo-triple-servlet/src/main/java/org/apache/dubbo/rpc/protocol/tri/servlet/HttpMetadataAdapter.java
@@ -25,7 +25,7 @@ import java.util.ArrayList;
import java.util.Enumeration;
import java.util.List;
-final class HttpMetadataAdapter implements Http2Header {
+public final class HttpMetadataAdapter implements Http2Header {
private final HttpServletRequest request;
@@ -35,6 +35,10 @@ final class HttpMetadataAdapter implements Http2Header {
this.request = request;
}
+ public HttpServletRequest getRequest() {
+ return request;
+ }
+
@Override
public HttpHeaders headers() {
HttpHeaders headers = this.headers;
diff --git a/dubbo-plugin/dubbo-triple-servlet/src/main/java/org/apache/dubbo/rpc/protocol/tri/servlet/TripleFilter.java b/dubbo-plugin/dubbo-triple-servlet/src/main/java/org/apache/dubbo/rpc/protocol/tri/servlet/TripleFilter.java
index 17f1d107a6..b009588853 100644
--- a/dubbo-plugin/dubbo-triple-servlet/src/main/java/org/apache/dubbo/rpc/protocol/tri/servlet/TripleFilter.java
+++ b/dubbo-plugin/dubbo-triple-servlet/src/main/java/org/apache/dubbo/rpc/protocol/tri/servlet/TripleFilter.java
@@ -20,6 +20,7 @@ import org.apache.dubbo.common.URL;
import org.apache.dubbo.common.io.StreamUtils;
import org.apache.dubbo.common.logger.ErrorTypeAwareLogger;
import org.apache.dubbo.common.logger.LoggerFactory;
+import org.apache.dubbo.common.utils.StringUtils;
import org.apache.dubbo.remoting.http12.h2.H2StreamChannel;
import org.apache.dubbo.remoting.http12.h2.Http2InputMessageFrame;
import org.apache.dubbo.remoting.http12.h2.Http2ServerTransportListenerFactory;
@@ -70,7 +71,7 @@ public class TripleFilter implements Filter {
pathResolver = frameworkModel.getDefaultExtension(PathResolver.class);
mappingRegistry = frameworkModel.getBeanFactory().getOrRegisterBean(DefaultRequestMappingRegistry.class);
String timeoutString = config.getInitParameter("timeout");
- defaultTimeout = timeoutString == null ? 180_000 : Integer.parseInt(timeoutString);
+ defaultTimeout = StringUtils.isEmpty(timeoutString) ? 180_000 : Integer.parseInt(timeoutString);
}
@Override
diff --git a/dubbo-plugin/dubbo-triple-servlet/src/main/resources/META-INF/dubbo/internal/org.apache.dubbo.remoting.http12.message.HttpMessageAdapterFactory b/dubbo-plugin/dubbo-triple-servlet/src/main/resources/META-INF/dubbo/internal/org.apache.dubbo.remoting.http12.message.HttpMessageAdapterFactory
new file mode 100644
index 0000000000..791e0a790b
--- /dev/null
+++ b/dubbo-plugin/dubbo-triple-servlet/src/main/resources/META-INF/dubbo/internal/org.apache.dubbo.remoting.http12.message.HttpMessageAdapterFactory
@@ -0,0 +1,2 @@
+servlet=org.apache.dubbo.rpc.protocol.tri.rest.support.servlet.ServletHttpMessageAdapterFactory
+jakarta-servlet=org.apache.dubbo.rpc.protocol.tri.rest.support.servlet.jakarta.ServletHttpMessageAdapterFactory
diff --git a/dubbo-plugin/dubbo-triple-servlet/src/main/resources/META-INF/dubbo/internal/org.apache.dubbo.rpc.protocol.tri.rest.argument.ArgumentResolver b/dubbo-plugin/dubbo-triple-servlet/src/main/resources/META-INF/dubbo/internal/org.apache.dubbo.rpc.protocol.tri.rest.argument.ArgumentResolver
new file mode 100644
index 0000000000..fd026764ba
--- /dev/null
+++ b/dubbo-plugin/dubbo-triple-servlet/src/main/resources/META-INF/dubbo/internal/org.apache.dubbo.rpc.protocol.tri.rest.argument.ArgumentResolver
@@ -0,0 +1,2 @@
+servlet=org.apache.dubbo.rpc.protocol.tri.rest.support.servlet.ServletArgumentResolver
+jakarta-servlet=org.apache.dubbo.rpc.protocol.tri.rest.support.servlet.jakarta.ServletArgumentResolver
diff --git a/dubbo-plugin/dubbo-triple-servlet/src/main/resources/META-INF/dubbo/internal/org.apache.dubbo.rpc.protocol.tri.rest.filter.RestExtensionAdapter b/dubbo-plugin/dubbo-triple-servlet/src/main/resources/META-INF/dubbo/internal/org.apache.dubbo.rpc.protocol.tri.rest.filter.RestExtensionAdapter
new file mode 100644
index 0000000000..3a2169eb35
--- /dev/null
+++ b/dubbo-plugin/dubbo-triple-servlet/src/main/resources/META-INF/dubbo/internal/org.apache.dubbo.rpc.protocol.tri.rest.filter.RestExtensionAdapter
@@ -0,0 +1,2 @@
+servlet-filter=org.apache.dubbo.rpc.protocol.tri.rest.support.servlet.FilterAdapter
+jakarta-servlet-filter=org.apache.dubbo.rpc.protocol.tri.rest.support.servlet.jakarta.FilterAdapter
diff --git a/dubbo-plugin/pom.xml b/dubbo-plugin/pom.xml
index df9b5dccde..1cfb0efb14 100644
--- a/dubbo-plugin/pom.xml
+++ b/dubbo-plugin/pom.xml
@@ -41,7 +41,6 @@
<module>dubbo-filter-cache</module>
<module>dubbo-filter-validation</module>
<module>dubbo-rest-jaxrs</module>
- <module>dubbo-rest-servlet</module>
<module>dubbo-rest-spring</module>
<module>dubbo-triple-servlet</module>
</modules>
diff --git a/dubbo-remoting/dubbo-remoting-http12/src/main/java/org/apache/dubbo/remoting/http12/CompositeInputStream.java b/dubbo-remoting/dubbo-remoting-http12/src/main/java/org/apache/dubbo/remoting/http12/CompositeInputStream.java
index 9b8b248eb0..58c069e52e 100644
--- a/dubbo-remoting/dubbo-remoting-http12/src/main/java/org/apache/dubbo/remoting/http12/CompositeInputStream.java
+++ b/dubbo-remoting/dubbo-remoting-http12/src/main/java/org/apache/dubbo/remoting/http12/CompositeInputStream.java
@@ -60,6 +60,42 @@ public class CompositeInputStream extends InputStream {
return -1;
}
+ @Override
+ public int read(byte[] b, int off, int len) throws IOException {
+ if (b == null) {
+ throw new NullPointerException();
+ } else if (off < 0 || len < 0 || len > b.length - off) {
+ throw new IndexOutOfBoundsException();
+ } else if (len == 0) {
+ return 0;
+ }
+
+ int total = 0;
+ InputStream inputStream;
+ while ((inputStream = inputStreams.peek()) != null) {
+ int available = inputStream.available();
+ if (available == 0) {
+ releaseHeadStream();
+ continue;
+ }
+
+ int read = inputStream.read(b, off + total, Math.min(len - total, available));
+ if (read != -1) {
+ total += read;
+ readIndex += read;
+ releaseIfNecessary(inputStream);
+
+ if (total == len) {
+ return total;
+ }
+ } else {
+ releaseHeadStream();
+ }
+ }
+
+ return total > 0 ? total : -1;
+ }
+
@Override
public int available() {
return totalAvailable - readIndex;
@@ -81,7 +117,6 @@ public class CompositeInputStream extends InputStream {
private void releaseIfNecessary(InputStream inputStream) throws IOException {
int available = inputStream.available();
if (available == 0) {
- inputStream.close();
releaseHeadStream();
}
}
diff --git a/dubbo-remoting/dubbo-remoting-http12/src/main/java/org/apache/dubbo/remoting/http12/HttpRequest.java b/dubbo-remoting/dubbo-remoting-http12/src/main/java/org/apache/dubbo/remoting/http12/HttpRequest.java
index 50fcc001d7..4aa03ff4a5 100644
--- a/dubbo-remoting/dubbo-remoting-http12/src/main/java/org/apache/dubbo/remoting/http12/HttpRequest.java
+++ b/dubbo-remoting/dubbo-remoting-http12/src/main/java/org/apache/dubbo/remoting/http12/HttpRequest.java
@@ -116,6 +116,8 @@ public interface HttpRequest extends RequestMetadata {
Collection<String> queryParameterNames();
+ Map<String, List<String>> queryParameters();
+
String formParameter(String name);
List<String> formParameterValues(String name);
diff --git a/dubbo-remoting/dubbo-remoting-http12/src/main/java/org/apache/dubbo/remoting/http12/message/DefaultHttpRequest.java b/dubbo-remoting/dubbo-remoting-http12/src/main/java/org/apache/dubbo/remoting/http12/message/DefaultHttpRequest.java
index 41dea99f99..5c88a5e1b0 100644
--- a/dubbo-remoting/dubbo-remoting-http12/src/main/java/org/apache/dubbo/remoting/http12/message/DefaultHttpRequest.java
+++ b/dubbo-remoting/dubbo-remoting-http12/src/main/java/org/apache/dubbo/remoting/http12/message/DefaultHttpRequest.java
@@ -81,6 +81,10 @@ public class DefaultHttpRequest implements HttpRequest {
}
}
+ public HttpMetadata getMetadata() {
+ return metadata;
+ }
+
@Override
public boolean isHttp2() {
return metadata instanceof Http2Header;
@@ -436,6 +440,11 @@ public class DefaultHttpRequest implements HttpRequest {
return getDecoder().parameters().keySet();
}
+ @Override
+ public Map<String, List<String>> queryParameters() {
+ return getDecoder().parameters();
+ }
+
@Override
public String formParameter(String name) {
HttpPostRequestDecoder postDecoder = getPostDecoder();
@@ -475,7 +484,7 @@ public class DefaultHttpRequest implements HttpRequest {
values.add(HttpUtils.readPostValue(item));
}
}
- return values;
+ return values == null ? Collections.emptyList() : values;
}
@Override
@@ -498,7 +507,7 @@ public class DefaultHttpRequest implements HttpRequest {
names.add(item.getName());
}
}
- return names;
+ return names == null ? Collections.emptyList() : names;
}
@Override
@@ -544,7 +553,7 @@ public class DefaultHttpRequest implements HttpRequest {
allNames.add(item.getName());
}
}
- return allNames;
+ return allNames == null ? Collections.emptyList() : allNames;
}
@Override
diff --git a/dubbo-remoting/dubbo-remoting-http12/src/main/java/org/apache/dubbo/remoting/http12/message/codec/CodecUtils.java b/dubbo-remoting/dubbo-remoting-http12/src/main/java/org/apache/dubbo/remoting/http12/message/codec/CodecUtils.java
index 281ee15eb9..98428bb9a8 100644
--- a/dubbo-remoting/dubbo-remoting-http12/src/main/java/org/apache/dubbo/remoting/http12/message/codec/CodecUtils.java
+++ b/dubbo-remoting/dubbo-remoting-http12/src/main/java/org/apache/dubbo/remoting/http12/message/codec/CodecUtils.java
@@ -50,12 +50,20 @@ public final class CodecUtils {
.createCodec(url, frameworkModel, mediaType);
}
+ public HttpMessageDecoder determineHttpMessageDecoder(String mediaType) {
+ return determineHttpMessageDecoder(null, null, mediaType);
+ }
+
public HttpMessageEncoder determineHttpMessageEncoder(URL url, FrameworkModel frameworkModel, String mediaType) {
return determineHttpMessageEncoderFactory(mediaType)
.orElseThrow(() -> new UnsupportedMediaTypeException(mediaType))
.createCodec(url, frameworkModel, mediaType);
}
+ public HttpMessageEncoder determineHttpMessageEncoder(String mediaType) {
+ return determineHttpMessageEncoder(null, null, mediaType);
+ }
+
public Optional<HttpMessageDecoderFactory> determineHttpMessageDecoderFactory(String mediaType) {
Assert.notNull(mediaType, "mediaType must not be null");
return decoderCache.computeIfAbsent(mediaType, k -> {
diff --git a/dubbo-common/src/main/java/org/apache/dubbo/config/nested/PropagationConfig.java b/dubbo-remoting/dubbo-remoting-http12/src/main/java/org/apache/dubbo/remoting/http12/rest/Mapping.java
similarity index 54%
copy from dubbo-common/src/main/java/org/apache/dubbo/config/nested/PropagationConfig.java
copy to dubbo-remoting/dubbo-remoting-http12/src/main/java/org/apache/dubbo/remoting/http12/rest/Mapping.java
index c7ee0ba7dd..a05e479306 100644
--- a/dubbo-common/src/main/java/org/apache/dubbo/config/nested/PropagationConfig.java
+++ b/dubbo-remoting/dubbo-remoting-http12/src/main/java/org/apache/dubbo/remoting/http12/rest/Mapping.java
@@ -14,35 +14,32 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package org.apache.dubbo.config.nested;
+package org.apache.dubbo.remoting.http12.rest;
-import java.io.Serializable;
+import org.apache.dubbo.remoting.http12.HttpMethods;
-/**
- * Configuration for the propagation.
- */
-public class PropagationConfig implements Serializable {
+import java.lang.annotation.Documented;
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+@Target({ElementType.TYPE, ElementType.METHOD})
+@Retention(RetentionPolicy.RUNTIME)
+@Documented
+public @interface Mapping {
- public static final String B3 = "B3";
+ String[] value() default {};
- public static final String W3C = "W3C";
+ String[] path() default {};
- /**
- * Tracing context propagation type.
- */
- private String type = W3C;
+ HttpMethods[] method() default {};
- public PropagationConfig() {}
+ String[] params() default {};
- public PropagationConfig(String type) {
- this.type = type;
- }
+ String[] headers() default {};
- public String getType() {
- return type;
- }
+ String[] consumes() default {};
- public void setType(String type) {
- this.type = type;
- }
+ String[] produces() default {};
}
diff --git a/dubbo-common/src/main/java/org/apache/dubbo/config/nested/PropagationConfig.java b/dubbo-remoting/dubbo-remoting-http12/src/main/java/org/apache/dubbo/remoting/http12/rest/Param.java
similarity index 57%
copy from dubbo-common/src/main/java/org/apache/dubbo/config/nested/PropagationConfig.java
copy to dubbo-remoting/dubbo-remoting-http12/src/main/java/org/apache/dubbo/remoting/http12/rest/Param.java
index c7ee0ba7dd..2ff94d4d32 100644
--- a/dubbo-common/src/main/java/org/apache/dubbo/config/nested/PropagationConfig.java
+++ b/dubbo-remoting/dubbo-remoting-http12/src/main/java/org/apache/dubbo/remoting/http12/rest/Param.java
@@ -14,35 +14,26 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package org.apache.dubbo.config.nested;
+package org.apache.dubbo.remoting.http12.rest;
-import java.io.Serializable;
+import java.lang.annotation.Documented;
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
-/**
- * Configuration for the propagation.
- */
-public class PropagationConfig implements Serializable {
-
- public static final String B3 = "B3";
-
- public static final String W3C = "W3C";
+@Target({ElementType.PARAMETER, ElementType.FIELD})
+@Retention(RetentionPolicy.RUNTIME)
+@Documented
+public @interface Param {
- /**
- * Tracing context propagation type.
- */
- private String type = W3C;
+ String DEFAULT_NONE = "\n\t\t\n\t\t\n_DEFAULT_NONE_\n\t\t\t\t\n";
- public PropagationConfig() {}
+ ParamType type() default ParamType.Param;
- public PropagationConfig(String type) {
- this.type = type;
- }
+ String value() default "";
- public String getType() {
- return type;
- }
+ boolean required() default true;
- public void setType(String type) {
- this.type = type;
- }
+ String defaultValue() default DEFAULT_NONE;
}
diff --git a/dubbo-plugin/dubbo-rest-jaxrs/src/test/java/org/apache/dubbo/rpc/protocol/tri/rest/support/jaxrs/compatible/rest/ParamConverterServiceImpl.java b/dubbo-remoting/dubbo-remoting-http12/src/main/java/org/apache/dubbo/remoting/http12/rest/ParamType.java
similarity index 72%
rename from dubbo-plugin/dubbo-rest-jaxrs/src/test/java/org/apache/dubbo/rpc/protocol/tri/rest/support/jaxrs/compatible/rest/ParamConverterServiceImpl.java
rename to dubbo-remoting/dubbo-remoting-http12/src/main/java/org/apache/dubbo/remoting/http12/rest/ParamType.java
index 2b0bedfd21..fac5b864c4 100644
--- a/dubbo-plugin/dubbo-rest-jaxrs/src/test/java/org/apache/dubbo/rpc/protocol/tri/rest/support/jaxrs/compatible/rest/ParamConverterServiceImpl.java
+++ b/dubbo-remoting/dubbo-remoting-http12/src/main/java/org/apache/dubbo/remoting/http12/rest/ParamType.java
@@ -14,13 +14,15 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package org.apache.dubbo.rpc.protocol.tri.rest.support.jaxrs.compatible.rest;
+package org.apache.dubbo.remoting.http12.rest;
-import org.apache.dubbo.rpc.protocol.tri.rest.support.jaxrs.compatible.User;
-
-public class ParamConverterServiceImpl implements ParamConverterService {
- @Override
- public User convert(User user) {
- return user;
- }
+public enum ParamType {
+ PathVariable,
+ MatrixVariable,
+ Param,
+ Header,
+ Cookie,
+ Attribute,
+ Part,
+ Body
}
diff --git a/dubbo-remoting/dubbo-remoting-http3/src/main/java/org/apache/dubbo/remoting/transport/netty4/Helper.java b/dubbo-remoting/dubbo-remoting-http3/src/main/java/org/apache/dubbo/remoting/transport/netty4/Helper.java
index b1d88ff901..6df29c4f06 100644
--- a/dubbo-remoting/dubbo-remoting-http3/src/main/java/org/apache/dubbo/remoting/transport/netty4/Helper.java
+++ b/dubbo-remoting/dubbo-remoting-http3/src/main/java/org/apache/dubbo/remoting/transport/netty4/Helper.java
@@ -18,6 +18,7 @@ package org.apache.dubbo.remoting.transport.netty4;
import org.apache.dubbo.common.URL;
import org.apache.dubbo.config.context.ConfigManager;
+import org.apache.dubbo.config.nested.Http3Config;
import org.apache.dubbo.config.nested.TripleConfig;
import io.netty.incubator.codec.quic.QuicCodecBuilder;
@@ -30,43 +31,37 @@ final class Helper {
@SuppressWarnings("unchecked")
static <T extends QuicCodecBuilder<T>> T configCodec(QuicCodecBuilder<T> builder, URL url) {
TripleConfig tripleConfig = ConfigManager.getProtocol(url).getTriple();
- if (tripleConfig.getHttp3InitialMaxData() != null) {
- builder.initialMaxData(tripleConfig.getHttp3InitialMaxData());
+ Http3Config config = tripleConfig.getHttp3();
+ if (config == null) {
+ config = new Http3Config();
}
- if (tripleConfig.getHttp3RecvQueueLen() != null && tripleConfig.getHttp3SendQueueLen() != null) {
- builder.datagram(tripleConfig.getHttp3RecvQueueLen(), tripleConfig.getHttp3SendQueueLen());
- }
- if (tripleConfig.getHttp3InitialMaxStreamDataBidiLocal() != null) {
- builder.initialMaxStreamDataBidirectionalLocal(tripleConfig.getHttp3InitialMaxStreamDataBidiLocal());
- }
- if (tripleConfig.getHttp3InitialMaxStreamDataBidiRemote() != null) {
- builder.initialMaxStreamDataBidirectionalRemote(tripleConfig.getHttp3InitialMaxStreamDataBidiRemote());
- }
- if (tripleConfig.getHttp3InitialMaxStreamDataUni() != null) {
- builder.initialMaxStreamDataUnidirectional(tripleConfig.getHttp3InitialMaxStreamDataUni());
- }
- if (tripleConfig.getHttp3InitialMaxStreamsBidi() != null) {
- builder.initialMaxStreamsBidirectional(tripleConfig.getHttp3InitialMaxStreamsBidi());
- }
- if (tripleConfig.getHttp3InitialMaxStreamsUni() != null) {
- builder.initialMaxStreamsUnidirectional(tripleConfig.getHttp3InitialMaxStreamsUni());
+ config.checkDefault();
+ builder.initialMaxData(config.getInitialMaxData())
+ .initialMaxStreamDataBidirectionalLocal(config.getInitialMaxStreamDataBidiLocal())
+ .initialMaxStreamDataBidirectionalRemote(config.getInitialMaxStreamDataBidiRemote())
+ .initialMaxStreamDataUnidirectional(config.getInitialMaxStreamDataUni())
+ .initialMaxStreamsBidirectional(config.getInitialMaxStreamsBidi())
+ .initialMaxStreamsUnidirectional(config.getInitialMaxStreamsUni());
+
+ if (config.getRecvQueueLen() != null && config.getSendQueueLen() != null) {
+ builder.datagram(config.getRecvQueueLen(), config.getSendQueueLen());
}
- if (tripleConfig.getHttp3MaxAckDelayExponent() != null) {
- builder.ackDelayExponent(tripleConfig.getHttp3MaxAckDelayExponent());
+ if (config.getMaxAckDelayExponent() != null) {
+ builder.ackDelayExponent(config.getMaxAckDelayExponent());
}
- if (tripleConfig.getHttp3MaxAckDelay() != null) {
- builder.maxAckDelay(tripleConfig.getHttp3MaxAckDelay(), MILLISECONDS);
+ if (config.getMaxAckDelay() != null) {
+ builder.maxAckDelay(config.getMaxAckDelay(), MILLISECONDS);
}
- if (tripleConfig.getHttp3DisableActiveMigration() != null) {
- builder.activeMigration(tripleConfig.getHttp3DisableActiveMigration());
+ if (config.getDisableActiveMigration() != null) {
+ builder.activeMigration(config.getDisableActiveMigration());
}
- if (tripleConfig.getHttp3EnableHystart() != null) {
- builder.hystart(tripleConfig.getHttp3EnableHystart());
+ if (config.getEnableHystart() != null) {
+ builder.hystart(config.getEnableHystart());
}
- if (tripleConfig.getHttp3CcAlgorithm() != null) {
- if ("RENO".equalsIgnoreCase(tripleConfig.getHttp3CcAlgorithm())) {
+ if (config.getCcAlgorithm() != null) {
+ if ("RENO".equalsIgnoreCase(config.getCcAlgorithm())) {
builder.congestionControlAlgorithm(QuicCongestionControlAlgorithm.RENO);
- } else if ("BBR".equalsIgnoreCase(tripleConfig.getHttp3CcAlgorithm())) {
+ } else if ("BBR".equalsIgnoreCase(config.getCcAlgorithm())) {
builder.congestionControlAlgorithm(QuicCongestionControlAlgorithm.BBR);
}
}
diff --git a/dubbo-remoting/dubbo-remoting-zookeeper-curator5/pom.xml b/dubbo-remoting/dubbo-remoting-zookeeper-curator5/pom.xml
index 5f57672ced..5b8510d6f8 100644
--- a/dubbo-remoting/dubbo-remoting-zookeeper-curator5/pom.xml
+++ b/dubbo-remoting/dubbo-remoting-zookeeper-curator5/pom.xml
@@ -46,11 +46,6 @@
<scope>test</scope>
</dependency>
- <dependency>
- <groupId>org.apache.dubbo</groupId>
- <artifactId>dubbo-common</artifactId>
- <version>${project.parent.version}</version>
- </dependency>
<dependency>
<groupId>org.apache.curator</groupId>
<artifactId>curator-framework</artifactId>
diff --git a/dubbo-rpc/dubbo-rpc-api/src/main/java/org/apache/dubbo/rpc/Constants.java b/dubbo-rpc/dubbo-rpc-api/src/main/java/org/apache/dubbo/rpc/Constants.java
index 0e6100827d..9327ad6a7b 100644
--- a/dubbo-rpc/dubbo-rpc-api/src/main/java/org/apache/dubbo/rpc/Constants.java
+++ b/dubbo-rpc/dubbo-rpc-api/src/main/java/org/apache/dubbo/rpc/Constants.java
@@ -105,8 +105,8 @@ public interface Constants {
String H2_SETTINGS_BUILTIN_SERVICE_INIT = "dubbo.tri.builtin.service.init";
String H2_SETTINGS_PASS_THROUGH_STANDARD_HTTP_HEADERS = "dubbo.rpc.tri.pass-through-standard-http-headers";
- String H3_SETTINGS_HTTP3_ENABLE = "dubbo.protocol.triple.enable-http3";
- String H3_SETTINGS_SERVLET_ENABLE = "dubbo.protocol.triple.enable-servlet";
+ String H3_SETTINGS_HTTP3_ENABLE = "dubbo.protocol.triple.http3.enable";
+ String H3_SETTINGS_SERVLET_ENABLE = "dubbo.protocol.triple.servlet.enable";
String ADAPTIVE_LOADBALANCE_ATTACHMENT_KEY = "lb_adaptive";
String ADAPTIVE_LOADBALANCE_START_TIME = "adaptive_startTime";
diff --git a/dubbo-rpc/dubbo-rpc-triple/pom.xml b/dubbo-rpc/dubbo-rpc-triple/pom.xml
index aa29ea0bfe..cde7d597af 100644
--- a/dubbo-rpc/dubbo-rpc-triple/pom.xml
+++ b/dubbo-rpc/dubbo-rpc-triple/pom.xml
@@ -52,6 +52,12 @@
<version>${project.parent.version}</version>
<optional>true</optional>
</dependency>
+ <dependency>
+ <groupId>org.apache.dubbo</groupId>
+ <artifactId>dubbo-cluster</artifactId>
+ <version>${project.parent.version}</version>
+ <scope>test</scope>
+ </dependency>
<dependency>
<groupId>io.netty</groupId>
<artifactId>netty-codec-http2</artifactId>
@@ -100,6 +106,11 @@
<artifactId>reactor-core</artifactId>
<scope>test</scope>
</dependency>
+ <dependency>
+ <groupId>org.spockframework</groupId>
+ <artifactId>spock-core</artifactId>
+ <scope>test</scope>
+ </dependency>
<dependency>
<groupId>org.apache.dubbo</groupId>
<artifactId>dubbo-native</artifactId>
@@ -161,6 +172,40 @@
<skip>true</skip>
</configuration>
</plugin>
+
+ <plugin>
+ <groupId>org.apache.maven.plugins</groupId>
+ <artifactId>maven-jar-plugin</artifactId>
+ <executions>
+ <execution>
+ <goals>
+ <goal>test-jar</goal>
+ </goals>
+ <phase>package</phase>
+ <configuration>
+ <skip>false</skip>
+ <includes>
+ <include>org/apache/dubbo/rpc/protocol/tri/rest/test/*</include>
+ <include>org/apache/dubbo/rpc/protocol/tri/rest/service/*</include>
+ <include>org/apache/dubbo/rpc/protocol/tri/test/*</include>
+ <include>META-INF/dubbo/**</include>
+ </includes>
+ </configuration>
+ </execution>
+ </executions>
+ </plugin>
+
+ <plugin>
+ <groupId>org.codehaus.gmavenplus</groupId>
+ <artifactId>gmavenplus-plugin</artifactId>
+ <executions>
+ <execution>
+ <goals>
+ <goal>compileTests</goal>
+ </goals>
+ </execution>
+ </executions>
+ </plugin>
</plugins>
</build>
</project>
diff --git a/dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/TripleConstant.java b/dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/TripleConstant.java
index f36b78e6eb..0289e08f78 100644
--- a/dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/TripleConstant.java
+++ b/dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/TripleConstant.java
@@ -39,6 +39,5 @@ public class TripleConstant {
public static final String HTTP_RESPONSE_KEY = "tri.http.response";
public static final String TRIPLE_HANDLER_TYPE_REST = "rest";
- public static final String TRIPLE_HANDLER_TYPE_HTTP = "http";
public static final String TRIPLE_HANDLER_TYPE_GRPC = "grpc";
}
diff --git a/dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/h12/HttpRequestHandlerMapping.java b/dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/h12/HttpRequestHandlerMapping.java
deleted file mode 100644
index 2dbfea05fa..0000000000
--- a/dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/h12/HttpRequestHandlerMapping.java
+++ /dev/null
@@ -1,121 +0,0 @@
-/*
- * 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.dubbo.rpc.protocol.tri.h12;
-
-import org.apache.dubbo.common.URL;
-import org.apache.dubbo.common.extension.Activate;
-import org.apache.dubbo.remoting.http12.HttpRequest;
-import org.apache.dubbo.remoting.http12.HttpResponse;
-import org.apache.dubbo.remoting.http12.message.HttpMessageDecoder;
-import org.apache.dubbo.remoting.http12.message.HttpMessageEncoder;
-import org.apache.dubbo.remoting.http12.message.MediaType;
-import org.apache.dubbo.remoting.http12.message.codec.CodecUtils;
-import org.apache.dubbo.rpc.Invoker;
-import org.apache.dubbo.rpc.PathResolver;
-import org.apache.dubbo.rpc.model.FrameworkModel;
-import org.apache.dubbo.rpc.protocol.tri.DescriptorUtils;
-import org.apache.dubbo.rpc.protocol.tri.TripleConstant;
-import org.apache.dubbo.rpc.protocol.tri.TripleHeaderEnum;
-import org.apache.dubbo.rpc.protocol.tri.TripleProtocol;
-import org.apache.dubbo.rpc.protocol.tri.route.RequestHandler;
-import org.apache.dubbo.rpc.protocol.tri.route.RequestHandlerMapping;
-
-import java.nio.charset.Charset;
-import java.nio.charset.StandardCharsets;
-
-@Activate(order = -1000)
-public class HttpRequestHandlerMapping implements RequestHandlerMapping {
-
- private final FrameworkModel frameworkModel;
- private final PathResolver pathResolver;
- private final CodecUtils codecUtils;
-
- public HttpRequestHandlerMapping(FrameworkModel frameworkModel) {
- this.frameworkModel = frameworkModel;
- pathResolver = frameworkModel.getDefaultExtension(PathResolver.class);
- codecUtils = frameworkModel.getBeanFactory().getOrRegisterBean(CodecUtils.class);
- }
-
- @Override
- public RequestHandler getRequestHandler(URL url, HttpRequest request, HttpResponse response) {
- if (!supportContentType(request.contentType())) {
- return null;
- }
-
- String uri = request.uri();
- int index = uri.indexOf('/', 1);
- if (index == -1) {
- return null;
- }
- if (uri.indexOf('/', index + 1) != -1) {
- return null;
- }
-
- String serviceName = uri.substring(1, index);
- String version = request.header(TripleHeaderEnum.SERVICE_VERSION.getHeader());
- String group = request.header(TripleHeaderEnum.SERVICE_GROUP.getHeader());
- String key = URL.buildKey(serviceName, group, version);
- Invoker<?> invoker = pathResolver.resolve(key);
- if (invoker == null && TripleProtocol.RESOLVE_FALLBACK_TO_DEFAULT) {
- invoker = pathResolver.resolve(URL.buildKey(serviceName, group, TripleConstant.DEFAULT_VERSION));
- if (invoker == null) {
- invoker = pathResolver.resolve(serviceName);
- if (invoker == null) {
- return null;
- }
- }
- }
-
- RequestHandler handler = new RequestHandler(invoker);
- handler.setHasStub(pathResolver.hasNativeStub(uri));
- handler.setMethodName(uri.substring(index + 1));
- handler.setServiceDescriptor(DescriptorUtils.findServiceDescriptor(invoker, serviceName, handler.isHasStub()));
- determineHttpMessageCodec(handler, url, request);
- return handler;
- }
-
- protected boolean supportContentType(String contentType) {
- return true;
- }
-
- protected void determineHttpMessageCodec(RequestHandler handler, URL url, HttpRequest request) {
- String mediaType = request.mediaType();
- if (mediaType == null) {
- mediaType = MediaType.APPLICATION_JSON.getName();
- request.setContentType(mediaType);
- }
-
- Charset charset = request.charsetOrDefault();
- HttpMessageDecoder decoder = codecUtils.determineHttpMessageDecoder(url, frameworkModel, mediaType);
- HttpMessageEncoder encoder = codecUtils.determineHttpMessageEncoder(url, frameworkModel, mediaType);
- if (!StandardCharsets.UTF_8.equals(charset)) {
- decoder = new HttpMessageDecoderWrapper(charset, decoder);
- encoder = new HttpMessageEncoderWrapper(charset, encoder);
- }
- handler.setHttpMessageDecoder(decoder);
- handler.setHttpMessageEncoder(encoder);
- }
-
- protected final FrameworkModel getFrameworkModel() {
- return frameworkModel;
- }
-
- @Override
- public String getType() {
- return TripleConstant.TRIPLE_HANDLER_TYPE_HTTP;
- }
-}
diff --git a/dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/h12/UnaryServerCallListener.java b/dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/h12/UnaryServerCallListener.java
index 8c32c30031..7eb2704f6e 100644
--- a/dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/h12/UnaryServerCallListener.java
+++ b/dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/h12/UnaryServerCallListener.java
@@ -24,14 +24,14 @@ import org.apache.dubbo.rpc.TriRpcStatus;
public class UnaryServerCallListener extends AbstractServerCallListener {
- private boolean applyCustomizeException = false;
+ private final boolean applyCustomizeException;
public UnaryServerCallListener(
- RpcInvocation invocation, Invoker<?> invoker, StreamObserver<Object> responseObserver) {
+ RpcInvocation invocation,
+ Invoker<?> invoker,
+ StreamObserver<Object> responseObserver,
+ boolean applyCustomizeException) {
super(invocation, invoker, responseObserver);
- }
-
- public void setApplyCustomizeException(boolean applyCustomizeException) {
this.applyCustomizeException = applyCustomizeException;
}
diff --git a/dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/h12/grpc/GrpcHttp2ServerTransportListener.java b/dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/h12/grpc/GrpcHttp2ServerTransportListener.java
index 3aa1bc0ac7..cbc2501065 100644
--- a/dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/h12/grpc/GrpcHttp2ServerTransportListener.java
+++ b/dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/h12/grpc/GrpcHttp2ServerTransportListener.java
@@ -27,12 +27,14 @@ import org.apache.dubbo.remoting.http12.h2.H2StreamChannel;
import org.apache.dubbo.remoting.http12.h2.Http2Header;
import org.apache.dubbo.remoting.http12.h2.Http2ServerChannelObserver;
import org.apache.dubbo.remoting.http12.h2.Http2TransportListener;
+import org.apache.dubbo.remoting.http12.message.MethodMetadata;
import org.apache.dubbo.remoting.http12.message.StreamingDecoder;
import org.apache.dubbo.rpc.RpcInvocation;
import org.apache.dubbo.rpc.TriRpcStatus;
import org.apache.dubbo.rpc.model.FrameworkModel;
import org.apache.dubbo.rpc.model.MethodDescriptor;
import org.apache.dubbo.rpc.protocol.tri.DescriptorUtils;
+import org.apache.dubbo.rpc.protocol.tri.ReflectionPackableMethod;
import org.apache.dubbo.rpc.protocol.tri.RpcInvocationBuildContext;
import org.apache.dubbo.rpc.protocol.tri.compressor.DeCompressor;
import org.apache.dubbo.rpc.protocol.tri.compressor.Identity;
@@ -135,6 +137,19 @@ public class GrpcHttp2ServerTransportListener extends GenericHttp2ServerTranspor
return (GrpcStreamingDecoder) super.getStreamingDecoder();
}
+ @Override
+ protected boolean applyCustomizeException() {
+ RpcInvocationBuildContext context = getContext();
+ if (context.isHasStub()) {
+ return false;
+ }
+ MethodMetadata methodMetadata = context.getMethodMetadata();
+ return ReflectionPackableMethod.needWrap(
+ context.getMethodDescriptor(),
+ methodMetadata.getActualRequestTypes(),
+ methodMetadata.getActualResponseType());
+ }
+
@Override
protected void onSettingMethodDescriptor(MethodDescriptor methodDescriptor) {
GrpcCompositeCodec grpcCompositeCodec =
diff --git a/dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/h12/grpc/GrpcRequestHandlerMapping.java b/dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/h12/grpc/GrpcRequestHandlerMapping.java
index 52894e0df8..c2e8d9d3ae 100644
--- a/dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/h12/grpc/GrpcRequestHandlerMapping.java
+++ b/dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/h12/grpc/GrpcRequestHandlerMapping.java
@@ -19,32 +19,71 @@ package org.apache.dubbo.rpc.protocol.tri.h12.grpc;
import org.apache.dubbo.common.URL;
import org.apache.dubbo.common.extension.Activate;
import org.apache.dubbo.remoting.http12.HttpRequest;
+import org.apache.dubbo.remoting.http12.HttpResponse;
import org.apache.dubbo.remoting.http12.message.HttpMessageCodec;
import org.apache.dubbo.remoting.http12.message.MediaType;
+import org.apache.dubbo.rpc.Invoker;
+import org.apache.dubbo.rpc.PathResolver;
import org.apache.dubbo.rpc.model.FrameworkModel;
+import org.apache.dubbo.rpc.protocol.tri.DescriptorUtils;
import org.apache.dubbo.rpc.protocol.tri.TripleConstant;
-import org.apache.dubbo.rpc.protocol.tri.h12.HttpRequestHandlerMapping;
+import org.apache.dubbo.rpc.protocol.tri.TripleHeaderEnum;
+import org.apache.dubbo.rpc.protocol.tri.TripleProtocol;
import org.apache.dubbo.rpc.protocol.tri.route.RequestHandler;
+import org.apache.dubbo.rpc.protocol.tri.route.RequestHandlerMapping;
@Activate(order = -3000)
-public final class GrpcRequestHandlerMapping extends HttpRequestHandlerMapping {
+public final class GrpcRequestHandlerMapping implements RequestHandlerMapping {
public static final GrpcCompositeCodecFactory CODEC_FACTORY = new GrpcCompositeCodecFactory();
+ private final FrameworkModel frameworkModel;
+ private final PathResolver pathResolver;
+
public GrpcRequestHandlerMapping(FrameworkModel frameworkModel) {
- super(frameworkModel);
+ this.frameworkModel = frameworkModel;
+ pathResolver = frameworkModel.getDefaultExtension(PathResolver.class);
}
@Override
- protected boolean supportContentType(String contentType) {
- return contentType != null && contentType.startsWith(MediaType.APPLICATION_GRPC.getName());
- }
+ public RequestHandler getRequestHandler(URL url, HttpRequest request, HttpResponse response) {
+ String contentType = request.contentType();
+ if (contentType == null || !contentType.startsWith(MediaType.APPLICATION_GRPC.getName())) {
+ return null;
+ }
- @Override
- protected void determineHttpMessageCodec(RequestHandler handler, URL url, HttpRequest request) {
- HttpMessageCodec codec = CODEC_FACTORY.createCodec(url, getFrameworkModel(), request.contentType());
+ String uri = request.uri();
+ int index = uri.indexOf('/', 1);
+ if (index == -1) {
+ return null;
+ }
+ if (uri.indexOf('/', index + 1) != -1) {
+ return null;
+ }
+
+ String serviceName = uri.substring(1, index);
+ String version = request.header(TripleHeaderEnum.SERVICE_VERSION.getHeader());
+ String group = request.header(TripleHeaderEnum.SERVICE_GROUP.getHeader());
+ String key = URL.buildKey(serviceName, group, version);
+ Invoker<?> invoker = pathResolver.resolve(key);
+ if (invoker == null && TripleProtocol.RESOLVE_FALLBACK_TO_DEFAULT) {
+ invoker = pathResolver.resolve(URL.buildKey(serviceName, group, TripleConstant.DEFAULT_VERSION));
+ if (invoker == null) {
+ invoker = pathResolver.resolve(serviceName);
+ if (invoker == null) {
+ return null;
+ }
+ }
+ }
+
+ RequestHandler handler = new RequestHandler(invoker);
+ handler.setHasStub(pathResolver.hasNativeStub(uri));
+ handler.setMethodName(uri.substring(index + 1));
+ handler.setServiceDescriptor(DescriptorUtils.findServiceDescriptor(invoker, serviceName, handler.isHasStub()));
+ HttpMessageCodec codec = CODEC_FACTORY.createCodec(url, frameworkModel, request.contentType());
handler.setHttpMessageDecoder(codec);
handler.setHttpMessageEncoder(codec);
+ return handler;
}
@Override
diff --git a/dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/h12/http1/DefaultHttp11ServerTransportListener.java b/dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/h12/http1/DefaultHttp11ServerTransportListener.java
index 5d445dcb3c..7df724d47c 100644
--- a/dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/h12/http1/DefaultHttp11ServerTransportListener.java
+++ b/dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/h12/http1/DefaultHttp11ServerTransportListener.java
@@ -126,7 +126,7 @@ public class DefaultHttp11ServerTransportListener
public AutoCompleteUnaryServerCallListener(
RpcInvocation invocation, Invoker<?> invoker, StreamObserver<Object> responseObserver) {
- super(invocation, invoker, responseObserver);
+ super(invocation, invoker, responseObserver, false);
}
@Override
diff --git a/dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/h12/http2/GenericHttp2ServerTransportListener.java b/dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/h12/http2/GenericHttp2ServerTransportListener.java
index 16e44fd033..32b16bb238 100644
--- a/dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/h12/http2/GenericHttp2ServerTransportListener.java
+++ b/dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/h12/http2/GenericHttp2ServerTransportListener.java
@@ -29,7 +29,6 @@ import org.apache.dubbo.remoting.http12.h2.Http2TransportListener;
import org.apache.dubbo.remoting.http12.message.DefaultListeningDecoder;
import org.apache.dubbo.remoting.http12.message.DefaultStreamingDecoder;
import org.apache.dubbo.remoting.http12.message.ListeningDecoder;
-import org.apache.dubbo.remoting.http12.message.MethodMetadata;
import org.apache.dubbo.remoting.http12.message.StreamingDecoder;
import org.apache.dubbo.remoting.http12.message.codec.JsonCodec;
import org.apache.dubbo.rpc.CancellationContext;
@@ -39,7 +38,6 @@ import org.apache.dubbo.rpc.RpcInvocation;
import org.apache.dubbo.rpc.executor.ExecutorSupport;
import org.apache.dubbo.rpc.model.FrameworkModel;
import org.apache.dubbo.rpc.model.MethodDescriptor;
-import org.apache.dubbo.rpc.protocol.tri.ReflectionPackableMethod;
import org.apache.dubbo.rpc.protocol.tri.RpcInvocationBuildContext;
import org.apache.dubbo.rpc.protocol.tri.TripleProtocol;
import org.apache.dubbo.rpc.protocol.tri.h12.AbstractServerTransportListener;
@@ -109,19 +107,8 @@ public class GenericHttp2ServerTransportListener extends AbstractServerTransport
switch (methodDescriptor.getRpcType()) {
case UNARY:
onUnary();
- boolean applyCustomizeException = false;
- if (!getContext().isHasStub()) {
- MethodMetadata methodMetadata = getContext().getMethodMetadata();
- applyCustomizeException = ReflectionPackableMethod.needWrap(
- methodDescriptor,
- methodMetadata.getActualRequestTypes(),
- methodMetadata.getActualResponseType());
- }
onListenerStart();
- UnaryServerCallListener unaryServerCallListener =
- startUnary(invocation, invoker, getServerChannelObserver());
- unaryServerCallListener.setApplyCustomizeException(applyCustomizeException);
- return unaryServerCallListener;
+ return startUnary(invocation, invoker, getServerChannelObserver());
case SERVER_STREAM:
onListenerStart();
return startServerStreaming(invocation, invoker, getServerChannelObserver());
@@ -147,7 +134,7 @@ public class GenericHttp2ServerTransportListener extends AbstractServerTransport
private UnaryServerCallListener startUnary(
RpcInvocation invocation, Invoker<?> invoker, Http2ServerChannelObserver responseObserver) {
- return new UnaryServerCallListener(invocation, invoker, responseObserver);
+ return new UnaryServerCallListener(invocation, invoker, responseObserver, applyCustomizeException());
}
private ServerStreamServerCallListener startServerStreaming(
@@ -218,6 +205,10 @@ public class GenericHttp2ServerTransportListener extends AbstractServerTransport
return serverChannelObserver;
}
+ protected boolean applyCustomizeException() {
+ return false;
+ }
+
@Override
public void close() throws Exception {
getServerChannelObserver().close();
diff --git a/dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/rest/Messages.java b/dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/rest/Messages.java
index e1493f428d..2abcff1220 100644
--- a/dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/rest/Messages.java
+++ b/dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/rest/Messages.java
@@ -31,7 +31,7 @@ public enum Messages {
NO_MORE_DATA_ALLOWED("No more data allowed after '{*...}' or '**' pattern segment for path ''{0}'' at index {1}"),
CANNOT_COMBINE_PATHS("Cannot combine paths: ''{0}'' vs ''{1}''"),
DUPLICATE_MAPPING("Duplicate mapping for ''{0}'': current={1}, exists={2}"),
- AMBIGUOUS_MAPPING("Ambiguous mapping for ''{0}'': {{1}, {2}}"),
+ AMBIGUOUS_MAPPING("Ambiguous mapping for ''{0}'': [{1}, {2}]"),
EXTENSION_INIT_FAILED("Rest extension: ''{0}'' initialization failed for invoker: ''{1}''"),
ARGUMENT_NAME_MISSING("Name for argument of type [{0}] not specified, and parameter name information not "
+ "available via reflection. Ensure that the compiler uses the '-parameters' flag."),
diff --git a/dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/rest/RestConstants.java b/dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/rest/RestConstants.java
index 7c512278bb..d267d58144 100644
--- a/dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/rest/RestConstants.java
+++ b/dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/rest/RestConstants.java
@@ -29,6 +29,7 @@ public final class RestConstants {
public static final String EXTENSION_KEY = "extension";
public static final String EXTENSIONS_ATTRIBUTE_KEY = "restExtensionsAttributeKey";
+ public static final int DIALECT_BASIC = 0;
public static final int DIALECT_SPRING_MVC = 1;
public static final int DIALECT_JAXRS = 2;
@@ -40,6 +41,7 @@ public final class RestConstants {
/* Request Attribute */
public static final String BODY_ATTRIBUTE = HttpRequest.class.getName() + ".body";
public static final String BODY_DECODER_ATTRIBUTE = HttpMessageDecoder.class.getName() + ".body";
+ public static final String SIG_ATTRIBUTE = RequestMapping.class.getName() + ".sig";
public static final String MAPPING_ATTRIBUTE = RequestMapping.class.getName();
public static final String HANDLER_ATTRIBUTE = HandlerMeta.class.getName();
public static final String PATH_ATTRIBUTE = "org.springframework.web.util.UrlPathHelper.PATH";
@@ -49,9 +51,7 @@ public final class RestConstants {
"org.springframework.web.servlet.HandlerMapping.producibleMediaTypes";
/* Configuration Key */
- public static final String CONFIG_PREFIX = "dubbo.rpc.rest.";
- public static final String MAX_BODY_SIZE_KEY = CONFIG_PREFIX + "max-body-size";
- public static final String MAX_RESPONSE_BODY_SIZE_KEY = CONFIG_PREFIX + "max-response-body-size";
+ public static final String CONFIG_PREFIX = "dubbo.protocol.triple.rest.";
public static final String SUFFIX_PATTERN_MATCH_KEY = CONFIG_PREFIX + "suffix-pattern-match";
public static final String TRAILING_SLASH_MATCH_KEY = CONFIG_PREFIX + "trailing-slash-match";
public static final String CASE_SENSITIVE_MATCH_KEY = CONFIG_PREFIX + "case-sensitive-match";
diff --git a/dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/rest/RestHttpMessageCodec.java b/dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/rest/RestHttpMessageCodec.java
index 448a2b7db4..1451c8d2d1 100644
--- a/dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/rest/RestHttpMessageCodec.java
+++ b/dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/rest/RestHttpMessageCodec.java
@@ -62,6 +62,10 @@ public final class RestHttpMessageCodec implements HttpMessageDecoder, HttpMessa
charset = request.charsetOrDefault();
}
+ public HttpMessageEncoder getMessageEncoder() {
+ return messageEncoder;
+ }
+
@Override
public Object decode(InputStream inputStream, Class<?> targetType, Charset charset) throws DecodeException {
return decode(inputStream, new Class<?>[] {targetType}, charset);
diff --git a/dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/rest/argument/CompositeArgumentResolver.java b/dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/rest/argument/CompositeArgumentResolver.java
index 9a8690f611..69c50585bc 100644
--- a/dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/rest/argument/CompositeArgumentResolver.java
+++ b/dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/rest/argument/CompositeArgumentResolver.java
@@ -74,4 +74,8 @@ public final class CompositeArgumentResolver implements ArgumentResolver {
throw new IllegalStateException(Messages.ARGUMENT_COULD_NOT_RESOLVED.format(parameter.getDescription()));
}
+
+ public ArgumentConverter getArgumentConverter() {
+ return argumentConverter;
+ }
}
diff --git a/dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/rest/argument/GeneralTypeConverter.java b/dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/rest/argument/GeneralTypeConverter.java
index ee9fb15221..89dc6153f7 100644
--- a/dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/rest/argument/GeneralTypeConverter.java
+++ b/dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/rest/argument/GeneralTypeConverter.java
@@ -25,6 +25,9 @@ import org.apache.dubbo.common.utils.DateUtils;
import org.apache.dubbo.common.utils.JsonUtils;
import org.apache.dubbo.common.utils.StringUtils;
import org.apache.dubbo.remoting.http12.HttpCookie;
+import org.apache.dubbo.remoting.http12.HttpRequest.FileUpload;
+import org.apache.dubbo.remoting.http12.message.MediaType;
+import org.apache.dubbo.remoting.http12.message.codec.CodecUtils;
import org.apache.dubbo.rpc.model.FrameworkModel;
import org.apache.dubbo.rpc.protocol.tri.rest.RestException;
import org.apache.dubbo.rpc.protocol.tri.rest.RestParameterException;
@@ -93,6 +96,7 @@ import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.atomic.AtomicLong;
import java.util.regex.Pattern;
+import static java.nio.charset.StandardCharsets.UTF_8;
import static org.apache.dubbo.common.utils.StringUtils.tokenizeToList;
import static org.apache.dubbo.rpc.protocol.tri.rest.util.TypeUtils.getActualGenericType;
import static org.apache.dubbo.rpc.protocol.tri.rest.util.TypeUtils.getActualType;
@@ -104,13 +108,16 @@ public class GeneralTypeConverter implements TypeConverter {
private static final Logger LOGGER = LoggerFactory.getLogger(GeneralTypeConverter.class);
private final ConverterUtil converterUtil;
+ private final CodecUtils codecUtils;
public GeneralTypeConverter() {
converterUtil = null;
+ codecUtils = null;
}
public GeneralTypeConverter(FrameworkModel frameworkModel) {
converterUtil = frameworkModel.getBeanFactory().getOrRegisterBean(ConverterUtil.class);
+ codecUtils = frameworkModel.getBeanFactory().getOrRegisterBean(CodecUtils.class);
}
@Override
@@ -255,7 +262,7 @@ public class GeneralTypeConverter implements TypeConverter {
case "java.lang.Class":
return TypeUtils.loadClass(str);
case "[B":
- return str.getBytes(StandardCharsets.UTF_8);
+ return str.getBytes(UTF_8);
case "[C":
return str.toCharArray();
case "java.util.OptionalInt":
@@ -432,7 +439,7 @@ public class GeneralTypeConverter implements TypeConverter {
switch (targetClass.getName()) {
case "java.lang.String":
- return new String(bytes, StandardCharsets.UTF_8);
+ return new String(bytes, UTF_8);
case "java.lang.Double":
case "double":
return ByteBuffer.wrap(bytes).getDouble();
@@ -568,6 +575,11 @@ public class GeneralTypeConverter implements TypeConverter {
return StreamUtils.readBytes(is);
}
}
+ if (source instanceof FileUpload) {
+ try (InputStream is = ((FileUpload) source).inputStream()) {
+ return StreamUtils.readBytes(is);
+ }
+ }
if (source instanceof Character) {
char c = (Character) source;
return new byte[] {(byte) (c >> 8), (byte) c};
@@ -910,8 +922,7 @@ public class GeneralTypeConverter implements TypeConverter {
defCt = ct;
break;
case 1:
- Class paramType = ct.getParameterTypes()[0];
- if (paramType == int.class) {
+ if (ct.getParameterTypes()[0] == int.class) {
return (Map) ct.newInstance(CollectionUtils.capacity(size));
}
break;
@@ -1078,10 +1089,15 @@ public class GeneralTypeConverter implements TypeConverter {
return false;
}
- private static Object jsonToObject(String value, Type targetType) {
+ private Object jsonToObject(String value, Type targetType) {
if (isMaybeJSON(value)) {
try {
- return JsonUtils.toJavaObject(value, targetType);
+ if (codecUtils == null || !(targetType instanceof Class)) {
+ return JsonUtils.toJavaObject(value, targetType);
+ }
+ return codecUtils
+ .determineHttpMessageDecoder(MediaType.APPLICATION_JSON.getName())
+ .decode(new ByteArrayInputStream(value.getBytes(UTF_8)), (Class<?>) targetType);
} catch (Throwable t) {
LOGGER.debug("Failed to parse [{}] from json string [{}]", targetType, value, t);
}
diff --git a/dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/rest/argument/NamedValueArgumentResolverSupport.java b/dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/rest/argument/NamedValueArgumentResolverSupport.java
index 90b02b68a9..d7401c57cf 100644
--- a/dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/rest/argument/NamedValueArgumentResolverSupport.java
+++ b/dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/rest/argument/NamedValueArgumentResolverSupport.java
@@ -38,18 +38,17 @@ public abstract class NamedValueArgumentResolverSupport {
protected final Object resolve(NamedValueMeta meta, HttpRequest request, HttpResponse response) {
Class<?> type = meta.type();
- Object arg;
if (type.isArray() || Collection.class.isAssignableFrom(type)) {
- arg = resolveCollectionValue(meta, request, response);
- } else if (Map.class.isAssignableFrom(type)) {
- arg = resolveMapValue(meta, request, response);
- } else {
- arg = resolveValue(meta, request, response);
+ return resolveCollectionValue(meta, request, response);
}
-
+ if (Map.class.isAssignableFrom(type)) {
+ return resolveMapValue(meta, request, response);
+ }
+ Object arg = resolveValue(meta, request, response);
if (arg != null) {
- return StringUtils.EMPTY_STRING.equals(arg) ? meta.defaultValue() : arg;
+ return filterValue(arg, meta);
}
+
arg = meta.defaultValue();
if (arg != null) {
return arg;
@@ -68,7 +67,7 @@ public abstract class NamedValueArgumentResolverSupport {
Class<?> type = parameterMeta.getType();
Type genericType = parameterMeta.getGenericType();
if (type == Optional.class) {
- Class<?> actualType = TypeUtils.getNestedType(genericType, 0);
+ Class<?> actualType = TypeUtils.getNestedActualType(genericType, 0);
meta.setType(actualType == null ? Object.class : actualType);
} else {
meta.setType(type);
@@ -76,14 +75,22 @@ public abstract class NamedValueArgumentResolverSupport {
if (type.isArray()) {
meta.setNestedTypes(new Class<?>[] {type.getComponentType()});
} else {
- meta.setNestedTypes(TypeUtils.getNestedTypes(genericType));
+ meta.setNestedTypes(TypeUtils.getNestedActualTypes(genericType));
}
meta.setParameterMeta(parameterMeta);
return meta;
}
+ protected String emptyDefaultValue(NamedValueMeta meta) {
+ return meta.defaultValue();
+ }
+
protected abstract Object resolveValue(NamedValueMeta meta, HttpRequest request, HttpResponse response);
+ protected Object filterValue(Object value, NamedValueMeta meta) {
+ return value;
+ }
+
protected Object resolveCollectionValue(NamedValueMeta meta, HttpRequest request, HttpResponse response) {
return resolveValue(meta, request, response);
}
diff --git a/dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/rest/cors/CorsHeaderFilter.java b/dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/rest/cors/CorsHeaderFilter.java
index f3dbe3eb62..8b71b3eff9 100644
--- a/dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/rest/cors/CorsHeaderFilter.java
+++ b/dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/rest/cors/CorsHeaderFilter.java
@@ -52,16 +52,16 @@ import static org.apache.dubbo.common.constants.CommonConstants.ANY_VALUE;
@Activate(group = CommonConstants.PROVIDER, order = 1000)
public class CorsHeaderFilter extends RestHeaderFilterAdapter {
- public static final String VARY = "Vary";
- public static final String ORIGIN = "Origin";
- public static final String ACCESS_CONTROL_REQUEST_METHOD = "Access-Control-Request-Method";
- public static final String ACCESS_CONTROL_REQUEST_HEADERS = "Access-Control-Request-Headers";
- public static final String ACCESS_CONTROL_ALLOW_CREDENTIALS = "Access-Control-Allow-Credentials";
- public static final String ACCESS_CONTROL_EXPOSE_HEADERS = "Access-Control-Expose-Headers";
- public static final String ACCESS_CONTROL_MAX_AGE = "Access-Control-Max-Age";
- public static final String ACCESS_CONTROL_ALLOW_ORIGIN = "Access-Control-Allow-Origin";
- public static final String ACCESS_CONTROL_ALLOW_METHODS = "Access-Control-Allow-Methods";
- public static final String ACCESS_CONTROL_ALLOW_HEADERS = "Access-Control-Allow-Headers";
+ public static final String VARY = "vary";
+ public static final String ORIGIN = "origin";
+ public static final String ACCESS_CONTROL_REQUEST_METHOD = "access-control-request-method";
+ public static final String ACCESS_CONTROL_REQUEST_HEADERS = "access-control-request-headers";
+ public static final String ACCESS_CONTROL_ALLOW_CREDENTIALS = "access-control-allow-credentials";
+ public static final String ACCESS_CONTROL_EXPOSE_HEADERS = "access-control-expose-headers";
+ public static final String ACCESS_CONTROL_MAX_AGE = "access-control-max-age";
+ public static final String ACCESS_CONTROL_ALLOW_ORIGIN = "access-control-allow-origin";
+ public static final String ACCESS_CONTROL_ALLOW_METHODS = "access-control-allow-methods";
+ public static final String ACCESS_CONTROL_ALLOW_HEADERS = "access-control-allow-headers";
public static final String SEP = ", ";
@Override
diff --git a/dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/rest/mapping/ContentNegotiator.java b/dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/rest/mapping/ContentNegotiator.java
index d31858ce72..e92b0bd564 100644
--- a/dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/rest/mapping/ContentNegotiator.java
+++ b/dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/rest/mapping/ContentNegotiator.java
@@ -87,6 +87,10 @@ public class ContentNegotiator {
return null;
}
+ public boolean supportExtension(String extension) {
+ return getMediaTypeByExtension(extension) != null;
+ }
+
private String getSuitableMediaType(String name) {
int index = name.indexOf('/');
if (index == -1 || index == name.length() - 1) {
diff --git a/dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/rest/mapping/DefaultRequestMappingRegistry.java b/dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/rest/mapping/DefaultRequestMappingRegistry.java
index ffb3502778..17edec2101 100644
--- a/dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/rest/mapping/DefaultRequestMappingRegistry.java
+++ b/dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/rest/mapping/DefaultRequestMappingRegistry.java
@@ -16,12 +16,17 @@
*/
package org.apache.dubbo.rpc.protocol.tri.rest.mapping;
-import org.apache.dubbo.common.utils.Assert;
+import org.apache.dubbo.common.URL;
+import org.apache.dubbo.common.config.Configuration;
+import org.apache.dubbo.common.config.ConfigurationUtils;
+import org.apache.dubbo.config.nested.RestConfig;
import org.apache.dubbo.remoting.http12.HttpRequest;
import org.apache.dubbo.remoting.http12.message.MethodMetadata;
import org.apache.dubbo.rpc.Invoker;
import org.apache.dubbo.rpc.model.FrameworkModel;
import org.apache.dubbo.rpc.model.MethodDescriptor;
+import org.apache.dubbo.rpc.model.ReflectionMethodDescriptor;
+import org.apache.dubbo.rpc.model.ReflectionServiceDescriptor;
import org.apache.dubbo.rpc.model.ServiceDescriptor;
import org.apache.dubbo.rpc.protocol.tri.DescriptorUtils;
import org.apache.dubbo.rpc.protocol.tri.rest.RestConstants;
@@ -32,53 +37,99 @@ import org.apache.dubbo.rpc.protocol.tri.rest.mapping.condition.ProducesConditio
import org.apache.dubbo.rpc.protocol.tri.rest.mapping.meta.HandlerMeta;
import org.apache.dubbo.rpc.protocol.tri.rest.mapping.meta.MethodMeta;
import org.apache.dubbo.rpc.protocol.tri.rest.mapping.meta.ServiceMeta;
+import org.apache.dubbo.rpc.protocol.tri.rest.util.KeyString;
import org.apache.dubbo.rpc.protocol.tri.rest.util.MethodWalker;
import org.apache.dubbo.rpc.protocol.tri.rest.util.PathUtils;
-import org.apache.dubbo.rpc.protocol.tri.rest.util.RestToolKit;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
+import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.locks.ReadWriteLock;
import java.util.concurrent.locks.ReentrantReadWriteLock;
import static org.apache.dubbo.rpc.protocol.tri.rest.Messages.AMBIGUOUS_MAPPING;
-import static org.apache.dubbo.rpc.protocol.tri.rest.Messages.DUPLICATE_MAPPING;
public final class DefaultRequestMappingRegistry implements RequestMappingRegistry {
- private final List<RequestMappingResolver> resolvers;
-
- private final RadixTree<Registration> tree = new RadixTree<>();
+ private final FrameworkModel frameworkModel;
+ private final ContentNegotiator contentNegotiator;
private final ReadWriteLock lock = new ReentrantReadWriteLock();
+ private final AtomicBoolean initialized = new AtomicBoolean();
+
+ private RestConfig restConfig;
+ private List<RequestMappingResolver> resolvers;
+ private RadixTree<Registration> tree;
public DefaultRequestMappingRegistry(FrameworkModel frameworkModel) {
+ this.frameworkModel = frameworkModel;
+ contentNegotiator = frameworkModel.getBeanFactory().getOrRegisterBean(ContentNegotiator.class);
+ }
+
+ private void init() {
+ Configuration conf = ConfigurationUtils.getGlobalConfiguration(frameworkModel.defaultApplication());
+ restConfig = new RestConfig();
+ restConfig.setSuffixPatternMatch(conf.getBoolean(RestConstants.SUFFIX_PATTERN_MATCH_KEY, true));
+ restConfig.setTrailingSlashMatch(conf.getBoolean(RestConstants.TRAILING_SLASH_MATCH_KEY, true));
+ restConfig.setCaseSensitiveMatch(conf.getBoolean(RestConstants.CASE_SENSITIVE_MATCH_KEY, true));
+
resolvers = frameworkModel.getActivateExtensions(RequestMappingResolver.class);
+ tree = new RadixTree<>(restConfig.isCaseSensitiveMatch());
}
@Override
public void register(Invoker<?> invoker) {
- Object service = invoker.getUrl().getServiceModel().getProxyObject();
+ if (tree == null) {
+ lock.writeLock().lock();
+ try {
+ if (initialized.compareAndSet(false, true)) {
+ init();
+ }
+ } finally {
+ lock.writeLock().unlock();
+ }
+ }
+
+ URL url = invoker.getUrl();
+ Object service = url.getServiceModel().getProxyObject();
+ ServiceDescriptor sd = DescriptorUtils.getReflectionServiceDescriptor(url);
+ if (sd == null) {
+ return;
+ }
new MethodWalker().walk(service.getClass(), (classes, consumer) -> {
for (int i = 0, size = resolvers.size(); i < size; i++) {
RequestMappingResolver resolver = resolvers.get(i);
- RestToolKit toolKit = resolver.getRestToolKit();
- ServiceMeta serviceMeta = new ServiceMeta(classes, service, invoker.getUrl(), toolKit);
+ ServiceMeta serviceMeta = new ServiceMeta(classes, sd, service, url, resolver.getRestToolKit());
if (!resolver.accept(serviceMeta)) {
continue;
}
RequestMapping classMapping = resolver.resolve(serviceMeta);
consumer.accept((methods) -> {
- MethodMeta methodMeta = new MethodMeta(methods, serviceMeta);
+ Method method = methods.get(0);
+ MethodDescriptor md = sd.getMethod(method.getName(), method.getParameterTypes());
+ MethodMeta methodMeta = new MethodMeta(methods, md, serviceMeta);
+ if (!resolver.accept(methodMeta)) {
+ return;
+ }
RequestMapping methodMapping = resolver.resolve(methodMeta);
if (methodMapping == null) {
return;
}
+ if (md == null) {
+ if (!(sd instanceof ReflectionServiceDescriptor)) {
+ return;
+ }
+ md = new ReflectionMethodDescriptor(method);
+ ((ReflectionServiceDescriptor) sd).addMethod(md);
+ methodMeta.setMethodDescriptor(md);
+ }
if (classMapping != null) {
methodMapping = classMapping.combine(methodMapping);
}
- register0(methodMapping, buildHandlerMeta(invoker, methodMeta));
+ methodMeta.initParameters();
+ MethodMetadata methodMetadata = MethodMetadata.fromMethodDescriptor(md);
+ register0(methodMapping, new HandlerMeta(invoker, methodMeta, methodMetadata, md, sd));
});
}
});
@@ -91,31 +142,13 @@ public final class DefaultRequestMappingRegistry implements RequestMappingRegist
registration.mapping = mapping;
registration.meta = handler;
for (PathExpression path : mapping.getPathCondition().getExpressions()) {
- Registration exists = tree.addPath(path, registration);
- if (exists != null) {
- throw new RestInitializeException(DUPLICATE_MAPPING, path.getPath(), mapping, exists.mapping);
- }
+ tree.addPath(path, registration);
}
} finally {
lock.writeLock().unlock();
}
}
- private HandlerMeta buildHandlerMeta(Invoker<?> invoker, MethodMeta methodMeta) {
- ServiceDescriptor serviceDescriptor = DescriptorUtils.getReflectionServiceDescriptor(invoker.getUrl());
- String serviceInterface = invoker.getUrl().getServiceInterface();
- Assert.notNull(serviceDescriptor, "ServiceDescriptor for [%s] can't be null", serviceInterface);
- Method method = methodMeta.getMethod();
- MethodDescriptor methodDescriptor = serviceDescriptor.getMethod(method.getName(), method.getParameterTypes());
- Assert.notNull(methodDescriptor, "MethodDescriptor for [%s] can't be null", method);
- return new HandlerMeta(
- invoker,
- methodMeta,
- MethodMetadata.fromMethodDescriptor(methodDescriptor),
- methodDescriptor,
- serviceDescriptor);
- }
-
@Override
public void unregister(Invoker<?> invoker) {
lock.writeLock().lock();
@@ -128,6 +161,9 @@ public final class DefaultRequestMappingRegistry implements RequestMappingRegist
@Override
public void destroy() {
+ if (tree == null) {
+ return;
+ }
lock.writeLock().lock();
try {
tree.clear();
@@ -139,33 +175,48 @@ public final class DefaultRequestMappingRegistry implements RequestMappingRegist
public HandlerMeta lookup(HttpRequest request) {
String path = PathUtils.normalize(request.rawPath());
request.setAttribute(RestConstants.PATH_ATTRIBUTE, path);
- List<Match<Registration>> matches = new ArrayList<>();
- lock.readLock().lock();
- try {
- tree.match(path, matches);
- } finally {
- lock.readLock().unlock();
- }
- int size = matches.size();
- if (size == 0) {
- return null;
- }
- List<Candidate> candidates = new ArrayList<>(size);
- for (int i = 0; i < size; i++) {
- Match<Registration> match = matches.get(i);
- RequestMapping mapping = match.getValue().mapping.match(request, match.getExpression());
- if (mapping != null) {
- Candidate candidate = new Candidate();
- candidate.mapping = mapping;
- candidate.meta = match.getValue().meta;
- candidate.expression = match.getExpression();
- candidate.variableMap = match.getVariableMap();
- candidates.add(candidate);
+ List<Candidate> candidates = new ArrayList<>();
+ boolean cs = restConfig.isCaseSensitiveMatch();
+ tryMatch(request, new KeyString(path, cs), candidates);
+
+ if (candidates.isEmpty()) {
+ int end = path.length();
+
+ if (restConfig.isTrailingSlashMatch()) {
+ if (path.charAt(end - 1) == '/') {
+ end--;
+ tryMatch(request, new KeyString(path, end, cs), candidates);
+ }
+ }
+
+ if (candidates.isEmpty()) {
+ for (int i = end - 1; i >= 0; i--) {
+ char ch = path.charAt(i);
+ if (ch == '/') {
+ break;
+ }
+ if (ch == '.' && restConfig.isSuffixPatternMatch()) {
+ if (contentNegotiator.supportExtension(path.substring(i + 1, end))) {
+ tryMatch(request, new KeyString(path, i, cs), candidates);
+ if (!candidates.isEmpty()) {
+ break;
+ }
+ end = i;
+ }
+ }
+ if (ch == '~') {
+ request.setAttribute(RestConstants.SIG_ATTRIBUTE, path.substring(i + 1, end));
+ tryMatch(request, new KeyString(path, i, cs), candidates);
+ if (!candidates.isEmpty()) {
+ break;
+ }
+ }
+ }
}
}
- size = candidates.size();
+ int size = candidates.size();
if (size == 0) {
return null;
}
@@ -184,7 +235,8 @@ public final class DefaultRequestMappingRegistry implements RequestMappingRegist
Candidate first = candidates.get(0);
Candidate second = candidates.get(1);
if (first.mapping.compareTo(second.mapping, request) == 0) {
- throw new RestInitializeException(AMBIGUOUS_MAPPING, path, first.mapping, second.mapping);
+ throw new RestInitializeException(
+ AMBIGUOUS_MAPPING, path, first.mapping.getName(), second.mapping.getName());
}
}
@@ -206,8 +258,73 @@ public final class DefaultRequestMappingRegistry implements RequestMappingRegist
return handler;
}
+ private void tryMatch(HttpRequest request, KeyString path, List<Candidate> candidates) {
+ List<Match<Registration>> matches = new ArrayList<>();
+
+ lock.readLock().lock();
+ try {
+ tree.match(path, matches);
+ } finally {
+ lock.readLock().unlock();
+ }
+
+ int size = matches.size();
+ if (size == 0) {
+ return;
+ }
+ for (int i = 0; i < size; i++) {
+ Match<Registration> match = matches.get(i);
+ RequestMapping mapping = match.getValue().mapping.match(request, match.getExpression());
+ if (mapping != null) {
+ Candidate candidate = new Candidate();
+ candidate.mapping = mapping;
+ candidate.meta = match.getValue().meta;
+ candidate.expression = match.getExpression();
+ candidate.variableMap = match.getVariableMap();
+ candidates.add(candidate);
+ }
+ }
+ }
+
@Override
public boolean exists(String path, String method) {
+ boolean cs = restConfig.isCaseSensitiveMatch();
+ if (tryExists(new KeyString(path, cs), method)) {
+ return true;
+ }
+
+ int end = path.length();
+ if (restConfig.isTrailingSlashMatch()) {
+ if (path.charAt(end - 1) == '/') {
+ end--;
+ if (tryExists(new KeyString(path, end, cs), method)) {
+ return true;
+ }
+ }
+ }
+
+ for (int i = end - 1; i >= 0; i--) {
+ char ch = path.charAt(i);
+ if (ch == '/') {
+ break;
+ }
+ if (ch == '.' && restConfig.isSuffixPatternMatch()) {
+ if (contentNegotiator.supportExtension(path.substring(i + 1, end))) {
+ if (tryExists(new KeyString(path, i, cs), method)) {
+ return true;
+ }
+ end = i;
+ }
+ }
+ if (ch == '~') {
+ return tryExists(new KeyString(path, i, cs), method);
+ }
+ }
+
+ return false;
+ }
+
+ private boolean tryExists(KeyString path, String method) {
List<Match<Registration>> matches = new ArrayList<>();
lock.readLock().lock();
try {
@@ -224,6 +341,7 @@ public final class DefaultRequestMappingRegistry implements RequestMappingRegist
}
private static final class Registration {
+
RequestMapping mapping;
HandlerMeta meta;
@@ -245,6 +363,7 @@ public final class DefaultRequestMappingRegistry implements RequestMappingRegist
}
private static final class Candidate {
+
RequestMapping mapping;
HandlerMeta meta;
PathExpression expression;
diff --git a/dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/rest/mapping/RadixTree.java b/dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/rest/mapping/RadixTree.java
index cc13a38439..7e45bced4e 100644
--- a/dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/rest/mapping/RadixTree.java
+++ b/dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/rest/mapping/RadixTree.java
@@ -20,6 +20,7 @@ import org.apache.dubbo.common.utils.Pair;
import org.apache.dubbo.rpc.protocol.tri.rest.mapping.condition.PathExpression;
import org.apache.dubbo.rpc.protocol.tri.rest.mapping.condition.PathSegment;
import org.apache.dubbo.rpc.protocol.tri.rest.mapping.condition.PathSegment.Type;
+import org.apache.dubbo.rpc.protocol.tri.rest.util.KeyString;
import org.apache.dubbo.rpc.protocol.tri.rest.util.PathUtils;
import java.util.ArrayList;
@@ -39,12 +40,22 @@ import java.util.function.Predicate;
*/
public final class RadixTree<T> {
- private final Map<String, List<Match<T>>> directPathMap = new HashMap<>();
+ private final Map<KeyString, List<Match<T>>> directPathMap = new HashMap<>();
private final Node<T> root = new Node<>();
+ private final boolean caseSensitive;
+
+ public RadixTree(boolean caseSensitive) {
+ this.caseSensitive = caseSensitive;
+ }
+
+ public RadixTree() {
+ caseSensitive = true;
+ }
public T addPath(PathExpression path, T value) {
if (path.isDirect()) {
- List<Match<T>> matches = directPathMap.computeIfAbsent(path.getPath(), k -> new ArrayList<>());
+ KeyString key = new KeyString(path.getPath(), caseSensitive);
+ List<Match<T>> matches = directPathMap.computeIfAbsent(key, k -> new ArrayList<>());
for (int i = 0, len = matches.size(); i < len; i++) {
Match<T> match = matches.get(i);
if (match.getValue().equals(value)) {
@@ -73,11 +84,15 @@ public final class RadixTree<T> {
return null;
}
- private static <T> Node<T> getChild(Node<T> current, PathSegment segment) {
+ public T addPath(String path, T value) {
+ return addPath(PathExpression.parse(PathUtils.normalize(path)), value);
+ }
+
+ private Node<T> getChild(Node<T> current, PathSegment segment) {
Node<T> child;
if (segment.getType() == Type.LITERAL) {
- Map<Key, Node<T>> children = current.children;
- Key key = new Key(segment.getValue());
+ Map<KeyString, Node<T>> children = current.children;
+ KeyString key = new KeyString(segment.getValue(), caseSensitive);
child = children.get(key);
if (child == null) {
child = new Node<>();
@@ -124,7 +139,7 @@ public final class RadixTree<T> {
/**
* Ensure that the path is normalized using {@link PathUtils#normalize(String)} before matching.
*/
- public void match(String path, List<Match<T>> matches) {
+ public void match(KeyString path, List<Match<T>> matches) {
List<Match<T>> directMatches = directPathMap.get(path);
if (directMatches != null) {
for (int i = 0, size = directMatches.size(); i < size; i++) {
@@ -136,7 +151,11 @@ public final class RadixTree<T> {
matchRecursive(root, path, 1, new HashMap<>(), matches);
}
- public List<Match<T>> match(String path) {
+ public void match(String path, List<Match<T>> matches) {
+ match(new KeyString(path, caseSensitive), matches);
+ }
+
+ public List<Match<T>> match(KeyString path) {
List<Match<T>> matches = directPathMap.get(path);
if (matches != null) {
return new ArrayList<>(matches);
@@ -147,10 +166,14 @@ public final class RadixTree<T> {
return matches;
}
+ public List<Match<T>> match(String path) {
+ return match(new KeyString(path, caseSensitive));
+ }
+
private void matchRecursive(
- Node<T> current, String path, int start, Map<String, String> variableMap, List<Match<T>> matches) {
+ Node<T> current, KeyString path, int start, Map<String, String> variableMap, List<Match<T>> matches) {
int end = path.indexOf('/', start);
- Node<T> node = current.children.get(new Key(path, start, end));
+ Node<T> node = current.children.get(new KeyString(path, start, end));
if (node != null) {
if (node.isLeaf()) {
addMatch(node, variableMap, matches);
@@ -235,67 +258,9 @@ public final class RadixTree<T> {
}
}
- /**
- * Zero-copy string key.
- */
- private static final class Key implements CharSequence {
-
- private final String value;
- private final int offset;
- private final int length;
-
- private Key(String value, int start, int end) {
- this.value = value;
- offset = start;
- length = (end == -1 ? value.length() : end) - start;
- }
-
- public Key(String value) {
- this.value = value;
- offset = 0;
- length = value.length();
- }
-
- @Override
- public int length() {
- return length;
- }
-
- @Override
- public char charAt(int index) {
- return value.charAt(offset + index);
- }
-
- @Override
- public CharSequence subSequence(int start, int end) {
- return value.substring(offset + start, offset + end);
- }
-
- @Override
- public int hashCode() {
- int h = 0;
- for (int i = 0; i < length; i++) {
- h = 31 * h + value.charAt(offset + i);
- }
- return h;
- }
-
- @Override
- @SuppressWarnings("EqualsWhichDoesntCheckParameterClass")
- public boolean equals(Object obj) {
- Key other = (Key) obj;
- return value.regionMatches(offset, other.value, other.offset, length);
- }
-
- @Override
- public String toString() {
- return value.substring(offset, length - offset);
- }
- }
-
private static final class Node<T> {
- private final Map<Key, Node<T>> children = new HashMap<>();
+ private final Map<KeyString, Node<T>> children = new HashMap<>();
private final Map<PathSegment, Node<T>> fuzzyChildren = new HashMap<>();
private final List<Pair<PathExpression, T>> values = new ArrayList<>();
diff --git a/dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/rest/mapping/RequestMapping.java b/dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/rest/mapping/RequestMapping.java
index 3dd19f4f2b..bd84d96f3e 100644
--- a/dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/rest/mapping/RequestMapping.java
+++ b/dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/rest/mapping/RequestMapping.java
@@ -16,8 +16,10 @@
*/
package org.apache.dubbo.rpc.protocol.tri.rest.mapping;
+import org.apache.dubbo.common.utils.StringUtils;
import org.apache.dubbo.remoting.http12.HttpMethods;
import org.apache.dubbo.remoting.http12.HttpRequest;
+import org.apache.dubbo.rpc.protocol.tri.rest.RestConstants;
import org.apache.dubbo.rpc.protocol.tri.rest.mapping.condition.Condition;
import org.apache.dubbo.rpc.protocol.tri.rest.mapping.condition.ConditionWrapper;
import org.apache.dubbo.rpc.protocol.tri.rest.mapping.condition.ConsumesCondition;
@@ -37,6 +39,7 @@ import static org.apache.dubbo.common.utils.ArrayUtils.isEmpty;
public final class RequestMapping implements Condition<RequestMapping, HttpRequest> {
private final String name;
+ private final String sig;
private final PathCondition pathCondition;
private final MethodsCondition methodsCondition;
private final ParamsCondition paramsCondition;
@@ -51,6 +54,7 @@ public final class RequestMapping implements Condition<RequestMapping, HttpReque
private RequestMapping(
String name,
+ String sig,
PathCondition pathCondition,
MethodsCondition methodsCondition,
ParamsCondition paramsCondition,
@@ -61,6 +65,7 @@ public final class RequestMapping implements Condition<RequestMapping, HttpReque
CorsMeta cors,
ResponseMeta response) {
this.name = name;
+ this.sig = sig;
this.pathCondition = pathCondition;
this.methodsCondition = methodsCondition;
this.paramsCondition = paramsCondition;
@@ -78,28 +83,31 @@ public final class RequestMapping implements Condition<RequestMapping, HttpReque
@Override
public RequestMapping combine(RequestMapping other) {
- String name = this.name == null ? other.name : other.name == null ? this.name : this.name + "#" + other.name;
- PathCondition paths = combine(pathCondition, other.pathCondition);
- MethodsCondition methods = combine(methodsCondition, other.methodsCondition);
- ParamsCondition params = combine(paramsCondition, other.paramsCondition);
- HeadersCondition headers = combine(headersCondition, other.headersCondition);
- ConsumesCondition consumes = combine(consumesCondition, other.consumesCondition);
- ProducesCondition produces = combine(producesCondition, other.producesCondition);
- ConditionWrapper custom = combine(customCondition, other.customCondition);
- CorsMeta corsMeta = combine(this.cors, other.cors);
- ResponseMeta response = ResponseMeta.combine(this.response, other.response);
return new RequestMapping(
- name, paths, methods, params, headers, consumes, produces, custom, corsMeta, response);
+ combineName(name, other.name, other.sig),
+ other.sig,
+ combine(pathCondition, other.pathCondition),
+ combine(methodsCondition, other.methodsCondition),
+ combine(paramsCondition, other.paramsCondition),
+ combine(headersCondition, other.headersCondition),
+ combine(consumesCondition, other.consumesCondition),
+ combine(producesCondition, other.producesCondition),
+ combine(customCondition, other.customCondition),
+ CorsMeta.combine(cors, other.cors),
+ ResponseMeta.combine(response, other.response));
}
- private <T extends Condition<T, HttpRequest>> T combine(T source, T other) {
- return source == null ? other : other == null ? source : source.combine(other);
+ private String combineName(String name, String otherName, String otherSig) {
+ if (name == null) {
+ name = otherName;
+ } else if (otherName != null) {
+ name += '#' + otherName;
+ }
+ return otherSig == null ? name : name + '~' + otherSig;
}
- private CorsMeta combine(CorsMeta source, CorsMeta other) {
- return source == null || source.isEmpty()
- ? other == null || other.isEmpty() ? null : other.applyDefault()
- : source.combine(other).applyDefault();
+ private <T extends Condition<T, HttpRequest>> T combine(T source, T other) {
+ return source == null ? other : other == null ? source : source.combine(other);
}
public RequestMapping match(HttpRequest request, PathExpression path) {
@@ -172,13 +180,25 @@ public final class RequestMapping implements Condition<RequestMapping, HttpReque
}
}
- return new RequestMapping(name, paths, methods, params, headers, consumes, produces, custom, cors, response);
+ if (StringUtils.isNotEmpty(sig)) {
+ String rSig = request.attribute(RestConstants.SIG_ATTRIBUTE);
+ if (rSig != null && !rSig.equals(sig)) {
+ return null;
+ }
+ }
+
+ return new RequestMapping(
+ name, sig, paths, methods, params, headers, consumes, produces, custom, cors, response);
}
public String getName() {
return name;
}
+ public String getSig() {
+ return sig;
+ }
+
public PathCondition getPathCondition() {
return pathCondition;
}
@@ -242,7 +262,21 @@ public final class RequestMapping implements Condition<RequestMapping, HttpReque
}
if (customCondition != null) {
result = customCondition.compareTo(other.customCondition, request);
- return result;
+ if (result != 0) {
+ return result;
+ }
+ }
+ if (sig != null && other.sig != null) {
+ int size = request.queryParameters().size();
+ int size1 = sig.length();
+ int size2 = other.sig.length();
+ if (size1 == size) {
+ if (size2 != size) {
+ return -1;
+ }
+ } else if (size2 == size) {
+ return 1;
+ }
}
return 0;
}
@@ -258,7 +292,8 @@ public final class RequestMapping implements Condition<RequestMapping, HttpReque
headersCondition,
consumesCondition,
producesCondition,
- customCondition);
+ customCondition,
+ sig);
this.hashCode = hashCode;
}
return hashCode;
@@ -279,13 +314,15 @@ public final class RequestMapping implements Condition<RequestMapping, HttpReque
&& Objects.equals(headersCondition, other.headersCondition)
&& Objects.equals(consumesCondition, other.consumesCondition)
&& Objects.equals(producesCondition, other.producesCondition)
- && Objects.equals(customCondition, other.customCondition);
+ && Objects.equals(customCondition, other.customCondition)
+ && Objects.equals(sig, other.sig);
}
@Override
public String toString() {
StringBuilder sb = new StringBuilder("RequestMapping{name='");
sb.append(name).append('\'');
+ sb.append('\'');
if (pathCondition != null) {
sb.append(", pathCondition=").append(pathCondition);
}
@@ -318,7 +355,9 @@ public final class RequestMapping implements Condition<RequestMapping, HttpReque
}
public static final class Builder {
+
private String name;
+ private String sig;
private String contextPath;
private String[] paths;
private String[] methods;
@@ -327,7 +366,7 @@ public final class RequestMapping implements Condition<RequestMapping, HttpReque
private String[] consumes;
private String[] produces;
private Condition<?, HttpRequest> customCondition;
- private CorsMeta corsMeta;
+ private CorsMeta cors;
private Integer responseStatus;
private String responseReason;
@@ -336,6 +375,11 @@ public final class RequestMapping implements Condition<RequestMapping, HttpReque
return this;
}
+ public Builder sig(String sig) {
+ this.sig = sig;
+ return this;
+ }
+
public Builder contextPath(String contextPath) {
this.contextPath = contextPath;
return this;
@@ -376,8 +420,8 @@ public final class RequestMapping implements Condition<RequestMapping, HttpReque
return this;
}
- public Builder cors(CorsMeta corsMeta) {
- this.corsMeta = corsMeta;
+ public Builder cors(CorsMeta cors) {
+ this.cors = cors;
return this;
}
@@ -394,6 +438,7 @@ public final class RequestMapping implements Condition<RequestMapping, HttpReque
public RequestMapping build() {
return new RequestMapping(
name,
+ sig,
isEmpty(paths) ? null : new PathCondition(contextPath, paths),
isEmpty(methods) ? null : new MethodsCondition(methods),
isEmpty(params) ? null : new ParamsCondition(params),
@@ -401,7 +446,7 @@ public final class RequestMapping implements Condition<RequestMapping, HttpReque
isEmpty(consumes) ? null : new ConsumesCondition(consumes),
isEmpty(produces) ? null : new ProducesCondition(produces),
customCondition,
- corsMeta == null ? null : corsMeta,
+ cors == null ? null : cors,
responseStatus == null ? null : new ResponseMeta(responseStatus, responseReason));
}
}
diff --git a/dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/rest/mapping/RequestMappingResolver.java b/dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/rest/mapping/RequestMappingResolver.java
index 7c7b5f372e..803792f1a4 100644
--- a/dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/rest/mapping/RequestMappingResolver.java
+++ b/dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/rest/mapping/RequestMappingResolver.java
@@ -33,5 +33,9 @@ public interface RequestMappingResolver {
RequestMapping resolve(ServiceMeta serviceMeta);
+ default boolean accept(MethodMeta methodMeta) {
+ return true;
+ }
+
RequestMapping resolve(MethodMeta methodMeta);
}
diff --git a/dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/rest/mapping/condition/PathExpression.java b/dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/rest/mapping/condition/PathExpression.java
index 2859401816..ead98873a0 100644
--- a/dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/rest/mapping/condition/PathExpression.java
+++ b/dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/rest/mapping/condition/PathExpression.java
@@ -17,6 +17,7 @@
package org.apache.dubbo.rpc.protocol.tri.rest.mapping.condition;
import org.apache.dubbo.rpc.protocol.tri.rest.mapping.condition.PathSegment.Type;
+import org.apache.dubbo.rpc.protocol.tri.rest.util.KeyString;
import javax.annotation.Nonnull;
@@ -38,6 +39,10 @@ public final class PathExpression implements Comparable<PathExpression> {
return new PathExpression(path, PathParser.parse(path));
}
+ public static boolean match(@Nonnull String path, String value) {
+ return value != null && parse(path).match(value) != null;
+ }
+
public String getPath() {
return path;
}
@@ -60,7 +65,7 @@ public final class PathExpression implements Comparable<PathExpression> {
if (end != -1) {
start = end + 1;
end = path.indexOf('/', start);
- if (segment.match(path, start, end, variableMap)) {
+ if (segment.match(new KeyString(path), start, end, variableMap)) {
continue;
}
}
@@ -85,6 +90,9 @@ public final class PathExpression implements Comparable<PathExpression> {
public int compareTo(PathExpression other) {
int size = segments.length;
int otherSize = other.segments.length;
+ if (isDirect() && other.isDirect()) {
+ return other.path.length() - path.length();
+ }
for (int i = 0; i < size && i < otherSize; i++) {
int result = segments[i].compareTo(other.segments[i]);
if (result != 0) {
@@ -112,13 +120,21 @@ public final class PathExpression implements Comparable<PathExpression> {
@Override
public String toString() {
+ if (isDirect()) {
+ return path;
+ }
StringBuilder sb = new StringBuilder(32);
for (PathSegment segment : segments) {
sb.append('/');
+ String value = segment.getValue();
if (segment.getType() == Type.VARIABLE) {
- sb.append('{').append(segment.getValue()).append('}');
+ if (value.isEmpty()) {
+ sb.append('*');
+ } else {
+ sb.append('{').append(value).append('}');
+ }
} else {
- sb.append(segment.getValue());
+ sb.append(value);
}
}
return sb.toString();
diff --git a/dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/rest/mapping/condition/PathParser.java b/dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/rest/mapping/condition/PathParser.java
index 30e978c21e..8add3bd99e 100644
--- a/dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/rest/mapping/condition/PathParser.java
+++ b/dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/rest/mapping/condition/PathParser.java
@@ -23,7 +23,6 @@ import org.apache.dubbo.rpc.protocol.tri.rest.RestConstants;
import org.apache.dubbo.rpc.protocol.tri.rest.mapping.condition.PathSegment.Type;
import org.apache.dubbo.rpc.protocol.tri.rest.util.PathUtils;
-import java.util.Collections;
import java.util.LinkedList;
import java.util.List;
import java.util.ListIterator;
@@ -33,7 +32,7 @@ import java.util.ListIterator;
* <p>
* <a href="https://docs.spring.io/spring-framework/reference/web/webmvc/mvc-controller/ann-requestmapping.html#mvc-ann-requestmapping-uri-templates">Spring uri templates</a>
* <br/>
- * <a href="https://docs.jboss.org/resteasy/docs/6.2.6.Final/userguide/html/ch04.html">Path and regular expression mappings</a>
+ * <a href="https://docs.jboss.org/resteasy/docs/6.2.7.Final/userguide/html/ch04.html">Path and regular expression mappings</a>
* </p>
*/
final class PathParser {
@@ -59,9 +58,6 @@ final class PathParser {
private List<PathSegment> doParse(String path) {
parseSegments(path);
- if (segments.isEmpty()) {
- return Collections.emptyList();
- }
transformSegments(segments, path);
for (PathSegment segment : segments) {
try {
@@ -96,11 +92,11 @@ final class PathParser {
appendSegment(Type.WILDCARD);
break;
case State.REGEX_VARIABLE_START:
- if (path.charAt(i - 1) == '^' && path.charAt(i - 2) == '[') {
- break;
+ if (path.charAt(i - 1) != '^' || path.charAt(i - 2) != '[') {
+ regexMulti = true;
}
- regexMulti = true;
- break;
+ buf.append(c);
+ continue;
case State.VARIABLE_START:
case State.WILDCARD_VARIABLE_START:
throw new PathParserException(Messages.MISSING_CLOSE_CAPTURE, path, i);
@@ -235,8 +231,6 @@ final class PathParser {
case State.REGEX_VARIABLE_START:
case State.WILDCARD_VARIABLE_START:
throw new PathParserException(Messages.MISSING_CLOSE_CAPTURE, path, len - 1);
- case State.END:
- throw new PathParserException(Messages.NO_MORE_DATA_ALLOWED, path, len - 1);
default:
}
}
@@ -269,7 +263,9 @@ final class PathParser {
prev = curr;
break;
case PATTERN_MULTI:
- prev.setValue(prev.getValue() + '/');
+ if (!".*".equals(prev.getValue())) {
+ prev.setValue(prev.getValue() + '/');
+ }
break;
default:
}
@@ -338,9 +334,10 @@ final class PathParser {
throw new PathParserException(Messages.ILLEGAL_DOUBLE_CAPTURE, path);
case PATTERN:
case PATTERN_MULTI:
- prev.addVariable(curr.getVariable());
+ String var = curr.getVariable();
+ prev.addVariable(var);
prev.setType(type);
- prev.setValue("(?<" + prev.getVariable() + ">[^/]+)" + value);
+ prev.setValue("(?<" + prev.getVariable() + ">[^/]+)(?<" + var + '>' + value + ')');
iterator.remove();
continue;
default:
@@ -354,8 +351,13 @@ final class PathParser {
iterator.remove();
continue;
case WILDCARD_TAIL:
- prev.addVariable(curr.getVariable());
- prev.setValue(pValue + "(?<" + curr.getVariable() + ">.*)");
+ if (curr.getVariables() == null) {
+ prev.setValue(pValue + ".*");
+ } else {
+ prev.addVariable(curr.getVariable());
+ prev.setValue(pValue + "(?<" + curr.getVariable() + ">.*)");
+ }
+ prev.setType(Type.PATTERN_MULTI);
iterator.remove();
continue;
case VARIABLE:
@@ -368,10 +370,15 @@ final class PathParser {
prev.setValue(pValue + "(?<" + curr.getVariable() + ">[^/]+)");
iterator.remove();
continue;
- case PATTERN:
case PATTERN_MULTI:
- prev.addVariable(curr.getVariable());
- prev.setValue(pValue + "(?<" + curr.getVariable() + '>' + value + ')');
+ prev.setType(Type.PATTERN_MULTI);
+ case PATTERN:
+ if (curr.getVariables() == null) {
+ prev.setValue(pValue + value);
+ } else {
+ prev.addVariable(curr.getVariable());
+ prev.setValue(pValue + "(?<" + curr.getVariable() + '>' + value + ')');
+ }
iterator.remove();
continue;
default:
@@ -424,7 +431,7 @@ final class PathParser {
sb.append("[^/]*");
break;
case '?':
- if (i > 0 && sb.charAt(i - 1) == '*') {
+ if (i > 0 && wildcard.charAt(i - 1) == '*') {
continue;
}
sb.append("[^/]");
diff --git a/dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/rest/mapping/condition/PathSegment.java b/dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/rest/mapping/condition/PathSegment.java
index 4a05a20bf1..e31ccaebf1 100644
--- a/dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/rest/mapping/condition/PathSegment.java
+++ b/dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/rest/mapping/condition/PathSegment.java
@@ -16,9 +16,9 @@
*/
package org.apache.dubbo.rpc.protocol.tri.rest.mapping.condition;
-import org.apache.dubbo.common.utils.StringUtils;
import org.apache.dubbo.rpc.protocol.tri.rest.Messages;
import org.apache.dubbo.rpc.protocol.tri.rest.PathParserException;
+import org.apache.dubbo.rpc.protocol.tri.rest.util.KeyString;
import java.util.ArrayList;
import java.util.List;
@@ -106,7 +106,7 @@ public final class PathSegment implements Comparable<PathSegment> {
return type == Type.WILDCARD_TAIL || type == Type.PATTERN_MULTI;
}
- public boolean match(String path, int start, int end, Map<String, String> variableMap) {
+ public boolean match(KeyString path, int start, int end, Map<String, String> variableMap) {
switch (type) {
case SLASH:
case LITERAL:
@@ -118,11 +118,11 @@ public final class PathSegment implements Comparable<PathSegment> {
return true;
case VARIABLE:
if (variables != null) {
- variableMap.put(getVariable(), StringUtils.substring(path, start, end));
+ variableMap.put(getVariable(), path.substring(start, end));
}
return true;
case PATTERN:
- return matchPattern(StringUtils.substring(path, start, end), variableMap);
+ return matchPattern(path.substring(start, end), variableMap);
case PATTERN_MULTI:
return matchPattern(path.substring(start), variableMap);
default:
@@ -130,9 +130,16 @@ public final class PathSegment implements Comparable<PathSegment> {
}
}
+ public boolean match(String path, int start, int end, Map<String, String> variableMap) {
+ return match(new KeyString(path), start, end, variableMap);
+ }
+
private boolean matchPattern(String path, Map<String, String> variableMap) {
Matcher matcher = getPattern().matcher(path);
if (matcher.matches()) {
+ if (variables == null) {
+ return true;
+ }
for (int i = 0, size = variables.size(); i < size; i++) {
String variable = variables.get(i);
variableMap.put(variable, matcher.group(variable));
@@ -183,7 +190,7 @@ public final class PathSegment implements Comparable<PathSegment> {
}
int size = variables == null ? 0 : variables.size();
int otherSize = other.variables == null ? 0 : other.variables.size();
- return size - otherSize;
+ return otherSize - size;
}
public enum Type {
diff --git a/dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/rest/mapping/meta/AnnotationSupport.java b/dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/rest/mapping/meta/AnnotationSupport.java
index 393106e2d3..35592ce137 100644
--- a/dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/rest/mapping/meta/AnnotationSupport.java
+++ b/dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/rest/mapping/meta/AnnotationSupport.java
@@ -71,7 +71,7 @@ public abstract class AnnotationSupport {
return result;
}
- public final AnnotationMeta getAnnotation(Class<? extends Annotation> annotationType) {
+ public final <A extends Annotation> AnnotationMeta<A> getAnnotation(Class<A> annotationType) {
return cache.computeIfAbsent(Pair.of(annotationType, GET_KEY), k -> {
AnnotatedElement element = getAnnotatedElement();
Annotation annotation = element.getAnnotation(annotationType);
@@ -87,7 +87,7 @@ public abstract class AnnotationSupport {
return annotationEnum.isPresent() ? getAnnotation(annotationEnum.type()) : null;
}
- public final boolean isAnnotated(Class<Annotation> annotationType) {
+ public final boolean isAnnotated(Class<? extends Annotation> annotationType) {
return getAnnotation(annotationType) != null;
}
@@ -95,7 +95,7 @@ public abstract class AnnotationSupport {
return getAnnotation(annotationEnum) != null;
}
- public final AnnotationMeta getMergedAnnotation(Class<Annotation> annotationType) {
+ public final <A extends Annotation> AnnotationMeta<A> getMergedAnnotation(Class<A> annotationType) {
return cache.computeIfAbsent(Pair.of(annotationType, GET_MERGED_KEY), k -> {
AnnotatedElement element = getAnnotatedElement();
Annotation[] annotations = element.getAnnotations();
@@ -117,7 +117,7 @@ public abstract class AnnotationSupport {
return annotationEnum.isPresent() ? getMergedAnnotation(annotationEnum.type()) : null;
}
- public final boolean isMergedAnnotated(Class<Annotation> annotationType) {
+ public final boolean isMergedAnnotated(Class<? extends Annotation> annotationType) {
return getMergedAnnotation(annotationType) != null;
}
@@ -143,7 +143,7 @@ public abstract class AnnotationSupport {
});
}
- public final AnnotationMeta findAnnotation(Class<Annotation> annotationType) {
+ public final <A extends Annotation> AnnotationMeta<A> findAnnotation(Class<A> annotationType) {
return cache.computeIfAbsent(Pair.of(annotationType, FIND_KEY), k -> {
List<? extends AnnotatedElement> elements = getAnnotatedElements();
for (int i = 0, len = elements.size(); i < len; i++) {
@@ -162,15 +162,15 @@ public abstract class AnnotationSupport {
return annotationEnum.isPresent() ? findAnnotation(annotationEnum.type()) : null;
}
- public final boolean isHierarchyAnnotated(Class<Annotation> annotationType) {
- return getMergedAnnotation(annotationType) != null;
+ public final boolean isHierarchyAnnotated(Class<? extends Annotation> annotationType) {
+ return findAnnotation(annotationType) != null;
}
public final boolean isHierarchyAnnotated(AnnotationEnum annotationEnum) {
- return getMergedAnnotation(annotationEnum) != null;
+ return findAnnotation(annotationEnum) != null;
}
- public final AnnotationMeta findMergedAnnotation(Class<Annotation> annotationType) {
+ public final <A extends Annotation> AnnotationMeta<A> findMergedAnnotation(Class<A> annotationType) {
return cache.computeIfAbsent(Pair.of(annotationType, FIND_MERGED_KEY), k -> {
List<? extends AnnotatedElement> elements = getAnnotatedElements();
for (int i = 0, len = elements.size(); i < len; i++) {
@@ -196,7 +196,7 @@ public abstract class AnnotationSupport {
return annotationEnum.isPresent() ? findMergedAnnotation(annotationEnum.type()) : null;
}
- public final boolean isMergedHierarchyAnnotated(Class<Annotation> annotationType) {
+ public final boolean isMergedHierarchyAnnotated(Class<? extends Annotation> annotationType) {
return findMergedAnnotation(annotationType) != null;
}
diff --git a/dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/rest/mapping/meta/BeanMeta.java b/dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/rest/mapping/meta/BeanMeta.java
new file mode 100644
index 0000000000..ce8fd206e1
--- /dev/null
+++ b/dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/rest/mapping/meta/BeanMeta.java
@@ -0,0 +1,387 @@
+/*
+ * 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.dubbo.rpc.protocol.tri.rest.mapping.meta;
+
+import org.apache.dubbo.remoting.http12.rest.Param;
+import org.apache.dubbo.rpc.protocol.tri.rest.RestException;
+import org.apache.dubbo.rpc.protocol.tri.rest.util.RestToolKit;
+import org.apache.dubbo.rpc.protocol.tri.rest.util.TypeUtils;
+
+import javax.annotation.Nullable;
+
+import java.lang.reflect.AnnotatedElement;
+import java.lang.reflect.Constructor;
+import java.lang.reflect.Field;
+import java.lang.reflect.GenericArrayType;
+import java.lang.reflect.Method;
+import java.lang.reflect.Modifier;
+import java.lang.reflect.Parameter;
+import java.lang.reflect.Type;
+import java.util.Collection;
+import java.util.LinkedHashMap;
+import java.util.Map;
+
+public final class BeanMeta {
+
+ private final Map<String, FieldMeta> fields = new LinkedHashMap<>();
+ private final Map<String, SetMethodMeta> methods = new LinkedHashMap<>();
+ private final ConstructorMeta constructor;
+
+ public BeanMeta(RestToolKit toolKit, String prefix, Class<?> type) {
+ constructor = resolveConstructor(toolKit, prefix, type);
+ resolveFieldAndMethod(toolKit, prefix, type);
+ }
+
+ public BeanMeta(RestToolKit toolKit, Class<?> type) {
+ this(toolKit, null, type);
+ }
+
+ public Collection<FieldMeta> getFields() {
+ return fields.values();
+ }
+
+ public FieldMeta getField(String name) {
+ return fields.get(name);
+ }
+
+ public Collection<SetMethodMeta> getMethods() {
+ return methods.values();
+ }
+
+ public SetMethodMeta getMethod(String name) {
+ return methods.get(name);
+ }
+
+ public ConstructorMeta getConstructor() {
+ return constructor;
+ }
+
+ public Object newInstance() {
+ return constructor.newInstance();
+ }
+
+ public static ConstructorMeta resolveConstructor(RestToolKit toolKit, String prefix, Class<?> type) {
+ Constructor<?>[] constructors = type.getConstructors();
+ Constructor<?> ct = null;
+ if (constructors.length == 1) {
+ ct = constructors[0];
+ } else {
+ try {
+ ct = type.getDeclaredConstructor();
+ } catch (NoSuchMethodException ignored) {
+ }
+ }
+ if (ct == null) {
+ throw new IllegalArgumentException("No available default constructor found in " + type);
+ }
+ return new ConstructorMeta(toolKit, prefix, ct);
+ }
+
+ private void resolveFieldAndMethod(RestToolKit toolKit, String prefix, Class<?> type) {
+ if (type == Object.class) {
+ return;
+ }
+ for (Field field : type.getDeclaredFields()) {
+ int modifiers = field.getModifiers();
+ if (Modifier.isStatic(modifiers) || Modifier.isFinal(modifiers)) {
+ continue;
+ }
+ if (field.getAnnotations().length == 0) {
+ continue;
+ }
+ if (!field.isAccessible()) {
+ field.setAccessible(true);
+ }
+ FieldMeta fieldMeta = new FieldMeta(toolKit, prefix, field);
+ fields.put(fieldMeta.getName(), fieldMeta);
+ }
+ for (Method method : type.getDeclaredMethods()) {
+ if (method.getParameterCount() != 1) {
+ continue;
+ }
+ int modifiers = method.getModifiers();
+ if ((modifiers & (Modifier.PUBLIC | Modifier.ABSTRACT | Modifier.STATIC)) == Modifier.PUBLIC) {
+ Parameter parameter = method.getParameters()[0];
+ String name = method.getName();
+ if (name.length() > 3 && name.startsWith("set")) {
+ String getMethodName = "get" + name.substring(3);
+ Method getMethod = null;
+ try {
+ getMethod = type.getDeclaredMethod(getMethodName);
+ } catch (NoSuchMethodException ignored) {
+ }
+ name = Character.toLowerCase(name.charAt(3)) + name.substring(4);
+ SetMethodMeta methodMeta = new SetMethodMeta(toolKit, method, getMethod, parameter, prefix, name);
+ methods.put(methodMeta.getName(), methodMeta);
+ }
+ }
+ }
+ resolveFieldAndMethod(toolKit, prefix, type.getSuperclass());
+ }
+
+ public static final class ConstructorMeta {
+
+ private final Constructor<?> constructor;
+ private final ConstructorParameterMeta[] parameters;
+
+ ConstructorMeta(RestToolKit toolKit, String prefix, Constructor<?> constructor) {
+ this.constructor = constructor;
+ parameters = initParameters(toolKit, prefix, constructor);
+ }
+
+ public ConstructorParameterMeta[] getParameters() {
+ return parameters;
+ }
+
+ private ConstructorParameterMeta[] initParameters(RestToolKit toolKit, String prefix, Constructor<?> ct) {
+ Parameter[] cps = ct.getParameters();
+ int len = cps.length;
+ ConstructorParameterMeta[] parameters = new ConstructorParameterMeta[len];
+ for (int i = 0; i < len; i++) {
+ parameters[i] = new ConstructorParameterMeta(toolKit, cps[i], prefix);
+ }
+ return parameters;
+ }
+
+ public Object newInstance(Object... args) {
+ try {
+ return constructor.newInstance(args);
+ } catch (Throwable t) {
+ throw RestException.wrap(t);
+ }
+ }
+ }
+
+ public static final class ConstructorParameterMeta extends ParameterMeta {
+
+ private final Parameter parameter;
+
+ ConstructorParameterMeta(RestToolKit toolKit, Parameter parameter, String prefix) {
+ super(toolKit, prefix, parameter.isNamePresent() ? parameter.getName() : null);
+ this.parameter = parameter;
+ }
+
+ @Override
+ protected AnnotatedElement getAnnotatedElement() {
+ return parameter;
+ }
+
+ @Override
+ public Class<?> getType() {
+ return parameter.getType();
+ }
+
+ @Override
+ public Type getGenericType() {
+ return parameter.getParameterizedType();
+ }
+
+ @Override
+ public String getDescription() {
+ return "ConstructorParameter{" + parameter + '}';
+ }
+ }
+
+ public abstract static class NestableParameterMeta extends ParameterMeta {
+
+ private NestableParameterMeta nestedMeta;
+ private String finalName;
+
+ public NestableParameterMeta(RestToolKit toolKit, String prefix, String name) {
+ super(toolKit, prefix, name);
+ }
+
+ @Nullable
+ @Override
+ public final String getName() {
+ String name = finalName;
+ if (name == null) {
+ AnnotationMeta<Param> param = findAnnotation(Param.class);
+ if (param != null) {
+ name = param.getValue();
+ }
+ if (name == null || name.isEmpty()) {
+ name = super.getName();
+ }
+ finalName = name;
+ }
+ return name;
+ }
+
+ public void setValue(Object bean, Object value) {}
+
+ public Object getValue(Object bean) {
+ return null;
+ }
+
+ public final NestableParameterMeta getNestedMeta() {
+ return nestedMeta;
+ }
+
+ protected final void initNestedMeta() {
+ Type nestedType = null;
+ Class<?> type = getType();
+ if (Map.class.isAssignableFrom(type)) {
+ nestedType = TypeUtils.getNestedGenericType(getGenericType(), 1);
+ } else if (Collection.class.isAssignableFrom(type)) {
+ nestedType = TypeUtils.getNestedGenericType(getGenericType(), 0);
+ } else if (type.isArray()) {
+ Type genericType = getGenericType();
+ if (genericType instanceof GenericArrayType) {
+ nestedType = ((GenericArrayType) genericType).getGenericComponentType();
+ } else {
+ nestedType = type.getComponentType();
+ }
+ }
+ nestedMeta = nestedType == null ? null : new NestedMeta(getToolKit(), nestedType);
+ }
+ }
+
+ public static final class FieldMeta extends NestableParameterMeta {
+
+ private final Field field;
+
+ FieldMeta(RestToolKit toolKit, String prefix, Field field) {
+ super(toolKit, prefix, field.getName());
+ this.field = field;
+ initNestedMeta();
+ }
+
+ @Override
+ public Class<?> getType() {
+ return field.getType();
+ }
+
+ @Override
+ public Type getGenericType() {
+ return field.getGenericType();
+ }
+
+ @Override
+ protected AnnotatedElement getAnnotatedElement() {
+ return field;
+ }
+
+ public void setValue(Object bean, Object value) {
+ try {
+ field.set(bean, value);
+ } catch (Throwable t) {
+ throw RestException.wrap(t);
+ }
+ }
+
+ public Object getValue(Object bean) {
+ try {
+ return field.get(bean);
+ } catch (Throwable t) {
+ throw RestException.wrap(t);
+ }
+ }
+
+ @Override
+ public String getDescription() {
+ return "FieldParameter{" + field + '}';
+ }
+ }
+
+ public static final class SetMethodMeta extends NestableParameterMeta {
+
+ private final Method method;
+ private final Method getMethod;
+ private final Parameter parameter;
+
+ SetMethodMeta(RestToolKit toolKit, Method m, Method gm, Parameter p, String prefix, String name) {
+ super(toolKit, prefix, name);
+ method = m;
+ getMethod = gm;
+ parameter = p;
+ initNestedMeta();
+ }
+
+ @Override
+ public Class<?> getType() {
+ return parameter.getType();
+ }
+
+ @Override
+ public Type getGenericType() {
+ return parameter.getParameterizedType();
+ }
+
+ @Override
+ protected AnnotatedElement getAnnotatedElement() {
+ return parameter;
+ }
+
+ public void setValue(Object bean, Object value) {
+ try {
+ method.invoke(bean, value);
+ } catch (Throwable t) {
+ throw RestException.wrap(t);
+ }
+ }
+
+ public Object getValue(Object bean) {
+ if (getMethod == null) {
+ return null;
+ }
+ try {
+ return getMethod.invoke(bean);
+ } catch (Throwable t) {
+ throw RestException.wrap(t);
+ }
+ }
+
+ @Override
+ public String getDescription() {
+ return "SetMethodParameter{" + method + '}';
+ }
+ }
+
+ public static final class NestedMeta extends NestableParameterMeta {
+
+ private final Class<?> type;
+ private final Type genericType;
+
+ NestedMeta(RestToolKit toolKit, Type genericType) {
+ super(toolKit, null, null);
+ type = TypeUtils.getActualType(genericType);
+ this.genericType = genericType;
+ initNestedMeta();
+ }
+
+ @Override
+ public Class<?> getType() {
+ return type;
+ }
+
+ @Override
+ public Type getGenericType() {
+ return genericType;
+ }
+
+ @Override
+ protected AnnotatedElement getAnnotatedElement() {
+ return null;
+ }
+
+ @Override
+ public String getDescription() {
+ return "NestedParameter{" + (genericType == null ? type : genericType) + '}';
+ }
+ }
+}
diff --git a/dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/rest/mapping/meta/CorsMeta.java b/dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/rest/mapping/meta/CorsMeta.java
index d5545c5858..d16987ff85 100644
--- a/dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/rest/mapping/meta/CorsMeta.java
+++ b/dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/rest/mapping/meta/CorsMeta.java
@@ -65,6 +65,12 @@ public class CorsMeta {
return new Builder();
}
+ public static CorsMeta combine(CorsMeta source, CorsMeta other) {
+ return source == null || source.isEmpty()
+ ? other == null || other.isEmpty() ? null : other.applyDefault()
+ : source.combine(other).applyDefault();
+ }
+
public String[] getAllowedOrigins() {
return allowedOrigins;
}
@@ -105,24 +111,24 @@ public class CorsMeta {
public CorsMeta applyDefault() {
String[] allowedOriginArray = null;
Pattern[] allowedOriginPatternArray = null;
- if (this.allowedOrigins.length == 0) {
+ if (allowedOrigins.length == 0) {
allowedOriginArray = new String[] {ANY_VALUE};
allowedOriginPatternArray = new Pattern[] {null};
}
String[] allowedMethodArray = null;
- if (this.allowedMethods.length == 0) {
+ if (allowedMethods.length == 0) {
allowedMethodArray =
new String[] {HttpMethods.GET.name(), HttpMethods.HEAD.name(), HttpMethods.POST.name()};
}
String[] allowedHeaderArray = null;
- if (this.allowedHeaders.length == 0) {
+ if (allowedHeaders.length == 0) {
allowedHeaderArray = new String[] {ANY_VALUE};
}
Long maxAgeValue = null;
- if (this.maxAge == null) {
+ if (maxAge == null) {
maxAgeValue = 1800L;
}
@@ -134,10 +140,10 @@ public class CorsMeta {
}
return new CorsMeta(
- allowedOriginArray == null ? this.allowedOrigins : allowedOriginArray,
- allowedOriginPatternArray == null ? this.allowedOriginsPatterns : allowedOriginPatternArray,
- allowedMethodArray == null ? this.allowedMethods : allowedMethodArray,
- allowedHeaderArray == null ? this.allowedHeaders : allowedHeaderArray,
+ allowedOriginArray == null ? allowedOrigins : allowedOriginArray,
+ allowedOriginPatternArray == null ? allowedOriginsPatterns : allowedOriginPatternArray,
+ allowedMethodArray == null ? allowedMethods : allowedMethodArray,
+ allowedHeaderArray == null ? allowedHeaders : allowedHeaderArray,
exposedHeaders,
allowCredentials,
maxAgeValue);
@@ -158,7 +164,7 @@ public class CorsMeta {
}
/**
- * Merge two arrays of CORS config values, with the other array having higher priority.
+ * Merge two arrays of CORS config values, the other array having higher priority.
*/
private static String[] combine(String[] source, String[] other) {
if (other.length == 0) {
diff --git a/dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/rest/mapping/meta/MethodMeta.java b/dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/rest/mapping/meta/MethodMeta.java
index 93ea6b2faf..ce3e2350d6 100644
--- a/dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/rest/mapping/meta/MethodMeta.java
+++ b/dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/rest/mapping/meta/MethodMeta.java
@@ -16,6 +16,8 @@
*/
package org.apache.dubbo.rpc.protocol.tri.rest.mapping.meta;
+import org.apache.dubbo.rpc.model.MethodDescriptor;
+
import java.lang.reflect.AnnotatedElement;
import java.lang.reflect.Method;
import java.lang.reflect.Parameter;
@@ -27,18 +29,27 @@ public final class MethodMeta extends AnnotationSupport {
private final List<Method> hierarchy;
private final Method method;
- private final ParameterMeta[] parameters;
+ private MethodDescriptor methodDescriptor;
+ private ParameterMeta[] parameters;
private final ServiceMeta serviceMeta;
- public MethodMeta(List<Method> hierarchy, ServiceMeta serviceMeta) {
+ public MethodMeta(List<Method> hierarchy, MethodDescriptor methodDescriptor, ServiceMeta serviceMeta) {
super(serviceMeta.getToolKit());
this.hierarchy = hierarchy;
- method = hierarchy.get(0);
- parameters = initParameters(method, hierarchy);
+ method = initMethod(hierarchy, methodDescriptor);
+ this.methodDescriptor = methodDescriptor;
this.serviceMeta = serviceMeta;
}
- private ParameterMeta[] initParameters(Method method, List<Method> hierarchy) {
+ private Method initMethod(List<Method> hierarchy, MethodDescriptor methodDescriptor) {
+ Method method = null;
+ if (methodDescriptor != null) {
+ method = methodDescriptor.getMethod();
+ }
+ return method == null ? hierarchy.get(hierarchy.size() - 1) : method;
+ }
+
+ public void initParameters() {
int count = method.getParameterCount();
List<List<Parameter>> parameterHierarchies = new ArrayList<>(count);
for (int i = 0, len = hierarchy.size(); i < len; i++) {
@@ -62,7 +73,7 @@ public final class MethodMeta extends AnnotationSupport {
String parameterName = parameterNames == null ? null : parameterNames[i];
parameters[i] = new MethodParameterMeta(parameterHierarchies.get(i), parameterName, i, this);
}
- return parameters;
+ this.parameters = parameters;
}
public List<Method> getHierarchy() {
@@ -73,6 +84,14 @@ public final class MethodMeta extends AnnotationSupport {
return method;
}
+ public MethodDescriptor getMethodDescriptor() {
+ return methodDescriptor;
+ }
+
+ public void setMethodDescriptor(MethodDescriptor methodDescriptor) {
+ this.methodDescriptor = methodDescriptor;
+ }
+
public ParameterMeta[] getParameters() {
return parameters;
}
diff --git a/dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/rest/mapping/meta/MethodParameterMeta.java b/dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/rest/mapping/meta/MethodParameterMeta.java
index a6094410f8..845acf7d60 100644
--- a/dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/rest/mapping/meta/MethodParameterMeta.java
+++ b/dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/rest/mapping/meta/MethodParameterMeta.java
@@ -16,6 +16,9 @@
*/
package org.apache.dubbo.rpc.protocol.tri.rest.mapping.meta;
+import org.apache.dubbo.rpc.model.MethodDescriptor;
+import org.apache.dubbo.rpc.model.MethodDescriptor.RpcType;
+
import java.lang.reflect.AnnotatedElement;
import java.lang.reflect.Method;
import java.lang.reflect.Parameter;
@@ -45,6 +48,13 @@ public final class MethodParameterMeta extends ParameterMeta {
return parameter;
}
+ @Override
+ public boolean isSingle() {
+ MethodDescriptor methodDescriptor = methodMeta.getMethodDescriptor();
+ return methodDescriptor.getRpcType() != RpcType.UNARY || methodDescriptor.getParameterClasses().length == 1;
+ }
+
+ @Override
public int getIndex() {
return index;
}
diff --git a/dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/rest/mapping/meta/ParameterMeta.java b/dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/rest/mapping/meta/ParameterMeta.java
index 32779b34e0..2def707122 100644
--- a/dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/rest/mapping/meta/ParameterMeta.java
+++ b/dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/rest/mapping/meta/ParameterMeta.java
@@ -16,7 +16,6 @@
*/
package org.apache.dubbo.rpc.protocol.tri.rest.mapping.meta;
-import org.apache.dubbo.common.utils.StringUtils;
import org.apache.dubbo.remoting.http12.HttpRequest;
import org.apache.dubbo.remoting.http12.HttpResponse;
import org.apache.dubbo.rpc.protocol.tri.rest.Messages;
@@ -27,6 +26,7 @@ import org.apache.dubbo.rpc.protocol.tri.rest.util.TypeUtils;
import javax.annotation.Nullable;
import java.lang.reflect.Type;
+import java.util.Collection;
import java.util.Optional;
public abstract class ParameterMeta extends AnnotationSupport {
@@ -39,7 +39,7 @@ public abstract class ParameterMeta extends AnnotationSupport {
protected ParameterMeta(RestToolKit toolKit, String prefix, String name) {
super(toolKit);
- this.prefix = StringUtils.isEmpty(prefix) ? null : prefix;
+ this.prefix = prefix;
this.name = name;
}
@@ -54,11 +54,12 @@ public abstract class ParameterMeta extends AnnotationSupport {
}
@Nullable
- public final String getName() {
+ public String getName() {
return name;
}
- public final String getRequiredName() {
+ public String getRequiredName() {
+ String name = getName();
if (name == null) {
throw new RestException(Messages.ARGUMENT_NAME_MISSING, getType());
}
@@ -68,7 +69,10 @@ public abstract class ParameterMeta extends AnnotationSupport {
public final boolean isSimple() {
Boolean simple = this.simple;
if (simple == null) {
- simple = TypeUtils.isSimpleProperty(getActualType());
+ Class<?> type = Collection.class.isAssignableFrom(getType())
+ ? TypeUtils.getNestedActualType(getGenericType(), 0)
+ : getActualType();
+ simple = TypeUtils.isSimpleProperty(type);
this.simple = simple;
}
return simple;
@@ -79,7 +83,7 @@ public abstract class ParameterMeta extends AnnotationSupport {
if (type == null) {
type = getType();
if (type == Optional.class) {
- type = TypeUtils.getNestedType(getGenericType(), 0);
+ type = TypeUtils.getNestedActualType(getGenericType(), 0);
if (type == null) {
type = Object.class;
}
@@ -101,6 +105,14 @@ public abstract class ParameterMeta extends AnnotationSupport {
return getToolKit().bind(this, request, response);
}
+ public boolean isSingle() {
+ return false;
+ }
+
+ public int getIndex() {
+ return -1;
+ }
+
public String getDescription() {
return name;
}
diff --git a/dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/rest/mapping/meta/ServiceMeta.java b/dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/rest/mapping/meta/ServiceMeta.java
index f9f68ca6f5..28d940205e 100644
--- a/dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/rest/mapping/meta/ServiceMeta.java
+++ b/dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/rest/mapping/meta/ServiceMeta.java
@@ -17,6 +17,7 @@
package org.apache.dubbo.rpc.protocol.tri.rest.mapping.meta;
import org.apache.dubbo.common.URL;
+import org.apache.dubbo.rpc.model.ServiceDescriptor;
import org.apache.dubbo.rpc.protocol.tri.rest.util.PathUtils;
import org.apache.dubbo.rpc.protocol.tri.rest.util.RestToolKit;
@@ -30,14 +31,21 @@ public final class ServiceMeta extends AnnotationSupport {
private final List<Class<?>> hierarchy;
private final Class<?> type;
private final Object service;
+ private final ServiceDescriptor serviceDescriptor;
private final URL url;
private final String contextPath;
private List<MethodMeta> exceptionHandlers;
- public ServiceMeta(Collection<Class<?>> hierarchy, Object service, URL url, RestToolKit toolKit) {
+ public ServiceMeta(
+ Collection<Class<?>> hierarchy,
+ ServiceDescriptor serviceDescriptor,
+ Object service,
+ URL url,
+ RestToolKit toolKit) {
super(toolKit);
this.hierarchy = new ArrayList<>(hierarchy);
+ this.serviceDescriptor = serviceDescriptor;
type = this.hierarchy.get(0);
this.service = service;
this.url = url;
@@ -52,6 +60,10 @@ public final class ServiceMeta extends AnnotationSupport {
return type;
}
+ public ServiceDescriptor getServiceDescriptor() {
+ return serviceDescriptor;
+ }
+
public Object getService() {
return service;
}
diff --git a/dubbo-plugin/dubbo-rest-spring/src/main/java/org/apache/dubbo/rpc/protocol/tri/rest/support/spring/Annotations.java b/dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/rest/support/basic/Annotations.java
similarity index 78%
copy from dubbo-plugin/dubbo-rest-spring/src/main/java/org/apache/dubbo/rpc/protocol/tri/rest/support/spring/Annotations.java
copy to dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/rest/support/basic/Annotations.java
index be8b1c5865..5a1ef216aa 100644
--- a/dubbo-plugin/dubbo-rest-spring/src/main/java/org/apache/dubbo/rpc/protocol/tri/rest/support/spring/Annotations.java
+++ b/dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/rest/support/basic/Annotations.java
@@ -14,40 +14,28 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package org.apache.dubbo.rpc.protocol.tri.rest.support.spring;
+package org.apache.dubbo.rpc.protocol.tri.rest.support.basic;
import org.apache.dubbo.rpc.protocol.tri.rest.mapping.meta.AnnotationEnum;
import java.lang.annotation.Annotation;
public enum Annotations implements AnnotationEnum {
- RequestMapping,
- PathVariable,
- MatrixVariable,
- RequestParam,
- RequestHeader,
- CookieValue,
- RequestAttribute,
- RequestPart,
- RequestBody,
- ModelAttribute,
- ResponseStatus,
- CrossOrigin,
- ExceptionHandler,
+ Mapping,
+ Param,
Nonnull("javax.annotation.Nonnull");
private final String className;
private Class<Annotation> type;
- Annotations() {
- className = "org.springframework.web.bind.annotation." + name();
- }
-
Annotations(String className) {
this.className = className;
}
- @Override
+ Annotations() {
+ className = "org.apache.dubbo.remoting.http12.rest." + name();
+ }
+
public String className() {
return className;
}
diff --git a/dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/rest/support/basic/BasicRequestMappingResolver.java b/dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/rest/support/basic/BasicRequestMappingResolver.java
new file mode 100644
index 0000000000..db39ea43f1
--- /dev/null
+++ b/dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/rest/support/basic/BasicRequestMappingResolver.java
@@ -0,0 +1,121 @@
+/*
+ * 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.dubbo.rpc.protocol.tri.rest.support.basic;
+
+import org.apache.dubbo.common.extension.Activate;
+import org.apache.dubbo.common.utils.StringUtils;
+import org.apache.dubbo.rpc.model.FrameworkModel;
+import org.apache.dubbo.rpc.protocol.tri.rest.cors.CorsUtils;
+import org.apache.dubbo.rpc.protocol.tri.rest.mapping.RequestMapping;
+import org.apache.dubbo.rpc.protocol.tri.rest.mapping.RequestMapping.Builder;
+import org.apache.dubbo.rpc.protocol.tri.rest.mapping.RequestMappingResolver;
+import org.apache.dubbo.rpc.protocol.tri.rest.mapping.condition.ServiceVersionCondition;
+import org.apache.dubbo.rpc.protocol.tri.rest.mapping.meta.AnnotationMeta;
+import org.apache.dubbo.rpc.protocol.tri.rest.mapping.meta.CorsMeta;
+import org.apache.dubbo.rpc.protocol.tri.rest.mapping.meta.MethodMeta;
+import org.apache.dubbo.rpc.protocol.tri.rest.mapping.meta.ServiceMeta;
+import org.apache.dubbo.rpc.protocol.tri.rest.util.RestToolKit;
+import org.apache.dubbo.rpc.protocol.tri.rest.util.TypeUtils;
+
+import java.lang.reflect.Method;
+
+@Activate
+public class BasicRequestMappingResolver implements RequestMappingResolver {
+
+ private final FrameworkModel frameworkModel;
+ private final RestToolKit toolKit;
+ private CorsMeta globalCorsMeta;
+
+ public BasicRequestMappingResolver(FrameworkModel frameworkModel) {
+ this.frameworkModel = frameworkModel;
+ toolKit = new BasicRestToolKit(frameworkModel);
+ }
+
+ @Override
+ public RestToolKit getRestToolKit() {
+ return toolKit;
+ }
+
+ @Override
+ public boolean accept(MethodMeta methodMeta) {
+ return methodMeta.getMethodDescriptor() != null || methodMeta.findAnnotation(Annotations.Mapping) != null;
+ }
+
+ @Override
+ public RequestMapping resolve(ServiceMeta serviceMeta) {
+ AnnotationMeta<?> mapping = serviceMeta.findAnnotation(Annotations.Mapping);
+ Builder builder = builder(mapping);
+
+ String[] paths = getPaths(mapping);
+ if (paths.length == 0) {
+ builder.path(serviceMeta.getServiceInterface());
+ } else {
+ builder.path(paths);
+ }
+
+ return builder.name(serviceMeta.getType().getSimpleName())
+ .contextPath(serviceMeta.getContextPath())
+ .build();
+ }
+
+ @Override
+ public RequestMapping resolve(MethodMeta methodMeta) {
+ Method method = methodMeta.getMethod();
+ AnnotationMeta<?> mapping = methodMeta.findAnnotation(Annotations.Mapping);
+ Builder builder = builder(mapping);
+
+ String[] paths = getPaths(mapping);
+ if (paths.length == 0) {
+ builder.path(method.getName()).sig(TypeUtils.buildSig(method));
+ } else {
+ builder.path(paths);
+ }
+
+ ServiceMeta serviceMeta = methodMeta.getServiceMeta();
+ if (globalCorsMeta == null) {
+ globalCorsMeta = CorsUtils.getGlobalCorsMeta(frameworkModel);
+ }
+ return builder.name(method.getName())
+ .custom(new ServiceVersionCondition(serviceMeta.getServiceGroup(), serviceMeta.getServiceVersion()))
+ .cors(globalCorsMeta)
+ .build();
+ }
+
+ private Builder builder(AnnotationMeta<?> mapping) {
+ Builder builder = RequestMapping.builder();
+ if (mapping == null) {
+ return builder;
+ }
+ builder.method(mapping.getStringArray("method"))
+ .param(mapping.getStringArray("params"))
+ .header(mapping.getStringArray("headers"))
+ .consume(mapping.getStringArray("consumes"))
+ .produce(mapping.getStringArray("produces"));
+ return builder;
+ }
+
+ private static String[] getPaths(AnnotationMeta<?> mapping) {
+ if (mapping == null) {
+ return StringUtils.EMPTY_STRING_ARRAY;
+ }
+ String[] paths = mapping.getStringArray("path");
+ if (paths.length > 0) {
+ return paths;
+ }
+ return mapping.getValueArray();
+ }
+}
diff --git a/dubbo-plugin/dubbo-rest-spring/src/main/java/org/apache/dubbo/rpc/protocol/tri/rest/support/spring/AbstractSpringArgumentResolver.java b/dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/rest/support/basic/BasicRestToolKit.java
similarity index 51%
copy from dubbo-plugin/dubbo-rest-spring/src/main/java/org/apache/dubbo/rpc/protocol/tri/rest/support/spring/AbstractSpringArgumentResolver.java
copy to dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/rest/support/basic/BasicRestToolKit.java
index fef61723e7..8aac208d8b 100644
--- a/dubbo-plugin/dubbo-rest-spring/src/main/java/org/apache/dubbo/rpc/protocol/tri/rest/support/spring/AbstractSpringArgumentResolver.java
+++ b/dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/rest/support/basic/BasicRestToolKit.java
@@ -14,19 +14,31 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package org.apache.dubbo.rpc.protocol.tri.rest.support.spring;
+package org.apache.dubbo.rpc.protocol.tri.rest.support.basic;
-import org.apache.dubbo.rpc.protocol.tri.rest.argument.AbstractAnnotationBaseArgumentResolver;
-import org.apache.dubbo.rpc.protocol.tri.rest.mapping.meta.AnnotationMeta;
-import org.apache.dubbo.rpc.protocol.tri.rest.mapping.meta.NamedValueMeta;
+import org.apache.dubbo.remoting.http12.HttpRequest;
+import org.apache.dubbo.remoting.http12.HttpResponse;
+import org.apache.dubbo.rpc.model.FrameworkModel;
+import org.apache.dubbo.rpc.protocol.tri.rest.RestConstants;
import org.apache.dubbo.rpc.protocol.tri.rest.mapping.meta.ParameterMeta;
+import org.apache.dubbo.rpc.protocol.tri.rest.util.AbstractRestToolKit;
-import java.lang.annotation.Annotation;
+final class BasicRestToolKit extends AbstractRestToolKit {
-public abstract class AbstractSpringArgumentResolver extends AbstractAnnotationBaseArgumentResolver {
+ private final BeanArgumentBinder binder;
+
+ public BasicRestToolKit(FrameworkModel frameworkModel) {
+ super(frameworkModel);
+ binder = new BeanArgumentBinder(frameworkModel);
+ }
+
+ @Override
+ public int getDialect() {
+ return RestConstants.DIALECT_BASIC;
+ }
@Override
- protected NamedValueMeta createNamedValueMeta(ParameterMeta param, AnnotationMeta<Annotation> ann) {
- return new NamedValueMeta(ann.getValue(), Helper.isRequired(ann), Helper.defaultValue(ann));
+ public Object bind(ParameterMeta parameter, HttpRequest request, HttpResponse response) {
+ return binder.bind(parameter, request, response);
}
}
diff --git a/dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/rest/support/basic/BeanArgumentBinder.java b/dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/rest/support/basic/BeanArgumentBinder.java
new file mode 100644
index 0000000000..93214b2bff
--- /dev/null
+++ b/dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/rest/support/basic/BeanArgumentBinder.java
@@ -0,0 +1,375 @@
+/*
+ * 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.dubbo.rpc.protocol.tri.rest.support.basic;
+
+import org.apache.dubbo.common.utils.CollectionUtils;
+import org.apache.dubbo.common.utils.Pair;
+import org.apache.dubbo.common.utils.StringUtils;
+import org.apache.dubbo.remoting.http12.HttpRequest;
+import org.apache.dubbo.remoting.http12.HttpResponse;
+import org.apache.dubbo.remoting.http12.rest.Param;
+import org.apache.dubbo.remoting.http12.rest.ParamType;
+import org.apache.dubbo.rpc.model.FrameworkModel;
+import org.apache.dubbo.rpc.protocol.tri.rest.Messages;
+import org.apache.dubbo.rpc.protocol.tri.rest.RestException;
+import org.apache.dubbo.rpc.protocol.tri.rest.argument.CompositeArgumentResolver;
+import org.apache.dubbo.rpc.protocol.tri.rest.mapping.meta.AnnotationMeta;
+import org.apache.dubbo.rpc.protocol.tri.rest.mapping.meta.BeanMeta;
+import org.apache.dubbo.rpc.protocol.tri.rest.mapping.meta.BeanMeta.ConstructorMeta;
+import org.apache.dubbo.rpc.protocol.tri.rest.mapping.meta.BeanMeta.FieldMeta;
+import org.apache.dubbo.rpc.protocol.tri.rest.mapping.meta.BeanMeta.NestableParameterMeta;
+import org.apache.dubbo.rpc.protocol.tri.rest.mapping.meta.BeanMeta.SetMethodMeta;
+import org.apache.dubbo.rpc.protocol.tri.rest.mapping.meta.ParameterMeta;
+import org.apache.dubbo.rpc.protocol.tri.rest.util.TypeUtils;
+
+import java.lang.reflect.Array;
+import java.lang.reflect.Modifier;
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.function.Consumer;
+
+final class BeanArgumentBinder {
+
+ private static final Map<Class<?>, BeanMeta> CACHE = CollectionUtils.newConcurrentHashMap();
+
+ private final CompositeArgumentResolver argumentResolver;
+
+ public BeanArgumentBinder(FrameworkModel frameworkModel) {
+ argumentResolver = frameworkModel.getBeanFactory().getOrRegisterBean(CompositeArgumentResolver.class);
+ }
+
+ public Object bind(ParameterMeta paramMeta, HttpRequest request, HttpResponse response) {
+ try {
+ BeanMeta beanMeta = getBeanMeta(paramMeta);
+ if (beanMeta == null) {
+ return null;
+ }
+
+ ConstructorMeta constructor = beanMeta.getConstructor();
+ ParameterMeta[] parameters = constructor.getParameters();
+ Object bean;
+ int len = parameters.length;
+ if (len == 0) {
+ bean = constructor.newInstance();
+ } else {
+ Object[] args = new Object[len];
+ for (int i = 0; i < len; i++) {
+ ParameterMeta parameter = parameters[i];
+ args[i] = parameter.isSimple() ? argumentResolver.resolve(parameter, request, response) : null;
+ }
+ bean = constructor.newInstance(args);
+ }
+
+ Node root = new Node(paramMeta.getName(), bean, beanMeta);
+ for (String paramName : request.parameterNames()) {
+ Node current = root;
+ List<String> parts = StringUtils.tokenizeToList(paramName, '.');
+ for (int i = 0, size = parts.size(); i < size; i++) {
+ if (current == null) {
+ break;
+ }
+ String name = parts.get(i);
+ Pair<String, String> pair = parseKeyParam(name);
+ if (pair == null) {
+ current = current.getChild(name);
+ if (i == 0 && current == null && name.equals(root.name)) {
+ current = root;
+ }
+ } else {
+ name = pair.getLeft();
+ current = current.getChild(name);
+ if (current == null) {
+ break;
+ }
+ String key = pair.getValue();
+ if (!key.isEmpty()) {
+ if (Character.isDigit(key.charAt(0))) {
+ try {
+ current = current.getChild(Long.parseLong(key));
+ continue;
+ } catch (NumberFormatException ignored) {
+ }
+ }
+ current = current.getChild(key);
+ }
+ }
+ }
+ if (current == null) {
+ continue;
+ }
+ Class<?> type = current.paramMeta.getActualType();
+ Object value;
+ if (type.isArray() || Collection.class.isAssignableFrom(type)) {
+ value = request.parameterValues(paramName);
+ } else {
+ value = request.parameter(paramName);
+ }
+ //noinspection unchecked
+ current.setValue(argumentResolver.getArgumentConverter().convert(value, current.paramMeta));
+ }
+
+ for (FieldMeta fieldMeta : beanMeta.getFields()) {
+ resolveParam(fieldMeta, bean, request, response);
+ }
+
+ for (SetMethodMeta methodMeta : beanMeta.getMethods()) {
+ resolveParam(methodMeta, bean, request, response);
+ }
+
+ return bean;
+ } catch (Exception e) {
+ throw new RestException(e, Messages.ARGUMENT_BIND_ERROR, paramMeta.getName(), paramMeta.getType());
+ }
+ }
+
+ private void resolveParam(NestableParameterMeta meta, Object bean, HttpRequest request, HttpResponse response) {
+ AnnotationMeta<Param> param = meta.getAnnotation(Param.class);
+ if (param == null || param.getAnnotation().type() == ParamType.Param) {
+ return;
+ }
+ meta.setValue(bean, argumentResolver.resolve(meta, request, response));
+ }
+
+ private static BeanMeta getBeanMeta(ParameterMeta paramMeta) {
+ Class<?> type = paramMeta.getActualType();
+ if (paramMeta.isSimple() || Modifier.isAbstract(type.getModifiers())) {
+ return null;
+ }
+ return CACHE.computeIfAbsent(type, k -> new BeanMeta(paramMeta.getToolKit(), k));
+ }
+
+ /**
+ * See
+ * <p>
+ * <a href="https://docs.spring.io/spring-framework/reference/core/validation/beans-beans.html#beans-binding">Spring beans-binding</a>
+ */
+ private static Pair<String, String> parseKeyParam(String name) {
+ int len = name.length();
+ if (name.charAt(len - 1) == ']') {
+ int start = name.lastIndexOf('[');
+ if (start > -1) {
+ return Pair.of(name.substring(0, start), name.substring(start + 1, len - 1));
+ }
+ }
+ return null;
+ }
+
+ private static final class Node {
+
+ public final String name;
+ public NestableParameterMeta paramMeta;
+ public Consumer<Object> setter;
+ public Object value;
+ public Map<Object, Node> children;
+ public BeanMeta beanMeta;
+
+ public Node(String name, NestableParameterMeta paramMeta, Consumer<Object> setter) {
+ this.name = name;
+ this.paramMeta = paramMeta;
+ this.setter = setter;
+ }
+
+ public Node(String name, Object value, BeanMeta beanMeta) {
+ this.name = name;
+ this.value = value;
+ this.beanMeta = beanMeta;
+ }
+
+ @SuppressWarnings({"rawtypes", "unchecked"})
+ public Node getChild(String name) {
+ if (children != null) {
+ Node node = children.get(name);
+ if (node != null) {
+ return node;
+ }
+ }
+
+ if (beanMeta == null) {
+ Class<?> type = paramMeta.getType();
+ if (Map.class.isAssignableFrom(type)) {
+ return createChild(name, paramMeta.getNestedMeta(), v -> ((Map) value).put(name, v));
+ }
+ return null;
+ }
+
+ NestableParameterMeta methodMeta = beanMeta.getMethod(name);
+ if (methodMeta != null) {
+ return createChild(name, methodMeta, v -> methodMeta.setValue(value, v));
+ }
+
+ NestableParameterMeta fieldMeta = beanMeta.getField(name);
+ if (fieldMeta != null) {
+ return createChild(name, fieldMeta, v -> fieldMeta.setValue(value, v));
+ }
+
+ return null;
+ }
+
+ @SuppressWarnings({"rawtypes", "unchecked", "SuspiciousSystemArraycopy"})
+ public Node getChild(long num) {
+ if (children != null) {
+ Node node = children.get(num);
+ if (node != null) {
+ return node;
+ }
+ }
+
+ NestableParameterMeta nestedMeta = paramMeta.getNestedMeta();
+ Class<?> type = paramMeta.getType();
+ Object childValue = null;
+ Node child;
+ if (List.class.isAssignableFrom(type)) {
+ int index = (int) num;
+ List list = (List) value;
+ child = new Node(name, nestedMeta, v -> {
+ if (index < 0) {
+ return;
+ }
+ while (list.size() <= index) {
+ list.add(null);
+ }
+ list.set(index, v);
+ });
+ if (index < list.size()) {
+ childValue = list.get(index);
+ }
+ } else if (type.isArray()) {
+ int index = (int) num;
+ int len = Array.getLength(value);
+ child = new Node(name, nestedMeta, v -> {
+ if (num >= 0 && num < len) {
+ Array.set(value, index, v);
+ return;
+ }
+ int tail = index < 0 ? len : index;
+ Object newArr = Array.newInstance(value.getClass().getComponentType(), tail + 1);
+ System.arraycopy(value, 0, newArr, 0, len);
+ Array.set(newArr, tail, v);
+ setter.accept(newArr);
+ });
+ if (index < len) {
+ childValue = Array.get(value, index);
+ }
+ } else if (Map.class.isAssignableFrom(type)) {
+ Class<?> keyType = TypeUtils.getNestedActualType(paramMeta.getGenericType(), 0);
+ Object key = TypeUtils.longToObject(num, keyType);
+ child = new Node(name, nestedMeta, v -> {
+ ((Map) value).put(key, v);
+ });
+ childValue = ((Map) value).get(key);
+ } else {
+ return null;
+ }
+
+ if (childValue == null) {
+ childValue = createValue(nestedMeta.getType());
+ if (childValue == null) {
+ if (nestedMeta.isSimple()) {
+ return child;
+ }
+ BeanMeta beanMeta = getBeanMeta(nestedMeta);
+ if (beanMeta == null) {
+ return null;
+ }
+ child.beanMeta = beanMeta;
+ childValue = beanMeta.newInstance();
+ }
+ child.setter.accept(childValue);
+ } else {
+ child.beanMeta = getBeanMeta(nestedMeta);
+ }
+
+ putChild(child, num, childValue);
+ return child;
+ }
+
+ public void setValue(Object value) {
+ setter.accept(value);
+ }
+
+ private Node createChild(String name, NestableParameterMeta paramMeta, Consumer<Object> setter) {
+ Class<?> type = paramMeta.getType();
+ if (type.isArray()) {
+ Consumer<Object> arraySetter = setter;
+ setter = v -> {
+ arraySetter.accept(v);
+ if (children != null) {
+ children.get(name).value = v;
+ }
+ };
+ }
+ Node child = new Node(name, paramMeta, setter);
+ Object childValue = paramMeta.getValue(value);
+ boolean created = false;
+ if (childValue == null) {
+ if (value instanceof Map) {
+ childValue = ((Map<?, ?>) value).get(name);
+ if (childValue != null) {
+ child.beanMeta = getBeanMeta(paramMeta);
+ }
+ }
+ if (childValue == null) {
+ childValue = createValue(type);
+ if (childValue == null) {
+ if (paramMeta.isSimple()) {
+ return child;
+ }
+ } else {
+ created = true;
+ }
+ }
+ }
+ if (childValue == null) {
+ BeanMeta beanMeta = getBeanMeta(paramMeta);
+ if (beanMeta == null) {
+ return null;
+ }
+ child.beanMeta = beanMeta;
+ childValue = beanMeta.newInstance();
+ created = true;
+ }
+ if (created) {
+ setter.accept(childValue);
+ }
+ putChild(child, name, childValue);
+ return child;
+ }
+
+ private Object createValue(Class<?> type) {
+ if (Map.class.isAssignableFrom(type)) {
+ return TypeUtils.createMap(type);
+ }
+ if (Collection.class.isAssignableFrom(type)) {
+ return TypeUtils.createCollection(type);
+ }
+ if (type.isArray()) {
+ return Array.newInstance(type.getComponentType(), 1);
+ }
+ return null;
+ }
+
+ private void putChild(Node child, Object name, Object value) {
+ child.value = value;
+ if (children == null) {
+ children = new HashMap<>();
+ }
+ children.put(name, child);
+ }
+ }
+}
diff --git a/dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/rest/support/basic/FallbackArgumentResolver.java b/dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/rest/support/basic/FallbackArgumentResolver.java
new file mode 100644
index 0000000000..ab4cbd97a6
--- /dev/null
+++ b/dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/rest/support/basic/FallbackArgumentResolver.java
@@ -0,0 +1,91 @@
+/*
+ * 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.dubbo.rpc.protocol.tri.rest.support.basic;
+
+import org.apache.dubbo.common.extension.Activate;
+import org.apache.dubbo.remoting.http12.HttpMethods;
+import org.apache.dubbo.remoting.http12.HttpRequest;
+import org.apache.dubbo.remoting.http12.HttpResponse;
+import org.apache.dubbo.rpc.protocol.tri.rest.RestConstants;
+import org.apache.dubbo.rpc.protocol.tri.rest.argument.AbstractArgumentResolver;
+import org.apache.dubbo.rpc.protocol.tri.rest.mapping.meta.NamedValueMeta;
+import org.apache.dubbo.rpc.protocol.tri.rest.mapping.meta.ParameterMeta;
+import org.apache.dubbo.rpc.protocol.tri.rest.util.RequestUtils;
+import org.apache.dubbo.rpc.protocol.tri.rest.util.RestUtils;
+
+import java.util.List;
+import java.util.Map;
+
+@Activate(order = Integer.MAX_VALUE - 10000)
+public class FallbackArgumentResolver extends AbstractArgumentResolver {
+
+ @Override
+ public boolean accept(ParameterMeta param) {
+ return param.getToolKit().getDialect() == RestConstants.DIALECT_BASIC;
+ }
+
+ @Override
+ protected NamedValueMeta createNamedValueMeta(ParameterMeta param) {
+ return new NamedValueMeta(param.isAnnotated(Annotations.Nonnull), null);
+ }
+
+ @Override
+ protected Object resolveValue(NamedValueMeta meta, HttpRequest request, HttpResponse response) {
+ return doResolveValue(meta, true, request, response);
+ }
+
+ @Override
+ protected Object resolveCollectionValue(NamedValueMeta meta, HttpRequest request, HttpResponse response) {
+ return doResolveValue(meta, false, request, response);
+ }
+
+ protected Object doResolveValue(NamedValueMeta meta, boolean single, HttpRequest request, HttpResponse response) {
+ ParameterMeta parameter = meta.parameterMeta();
+ if (HttpMethods.supportBody(request.method())) {
+ Object body = RequestUtils.decodeBody(request, parameter.getType(), parameter.isSingle());
+ if (body != null) {
+ if (parameter.getType().isInstance(body)) {
+ return body;
+ } else if (body instanceof List) {
+ List<?> list = (List<?>) body;
+ int index = parameter.getIndex();
+ if (index >= 0 && list.size() > index) {
+ return list.get(index);
+ }
+ } else if (body instanceof Map) {
+ Object value = ((Map<?, ?>) body).get(meta.name());
+ if (value != null) {
+ return value;
+ }
+ }
+ }
+ }
+ if (single) {
+ String value = request.parameter(meta.name());
+ if (parameter.isSimple() || RestUtils.isMaybeJSONObject(value)) {
+ return value;
+ }
+ return parameter.bind(request, response);
+ }
+ return request.parameterValues(meta.name());
+ }
+
+ @Override
+ protected Object resolveMapValue(NamedValueMeta meta, HttpRequest request, HttpResponse response) {
+ return resolveValue(meta, request, response);
+ }
+}
diff --git a/dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/rest/support/basic/ParamArgumentResolver.java b/dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/rest/support/basic/ParamArgumentResolver.java
new file mode 100644
index 0000000000..52456c898c
--- /dev/null
+++ b/dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/rest/support/basic/ParamArgumentResolver.java
@@ -0,0 +1,217 @@
+/*
+ * 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.dubbo.rpc.protocol.tri.rest.support.basic;
+
+import org.apache.dubbo.common.extension.Activate;
+import org.apache.dubbo.common.io.StreamUtils;
+import org.apache.dubbo.common.utils.CollectionUtils;
+import org.apache.dubbo.common.utils.StringUtils;
+import org.apache.dubbo.remoting.http12.HttpCookie;
+import org.apache.dubbo.remoting.http12.HttpRequest;
+import org.apache.dubbo.remoting.http12.HttpRequest.FileUpload;
+import org.apache.dubbo.remoting.http12.HttpResponse;
+import org.apache.dubbo.remoting.http12.rest.Param;
+import org.apache.dubbo.remoting.http12.rest.ParamType;
+import org.apache.dubbo.rpc.protocol.tri.rest.Messages;
+import org.apache.dubbo.rpc.protocol.tri.rest.RestConstants;
+import org.apache.dubbo.rpc.protocol.tri.rest.RestException;
+import org.apache.dubbo.rpc.protocol.tri.rest.RestParameterException;
+import org.apache.dubbo.rpc.protocol.tri.rest.argument.AbstractAnnotationBaseArgumentResolver;
+import org.apache.dubbo.rpc.protocol.tri.rest.mapping.meta.AnnotationMeta;
+import org.apache.dubbo.rpc.protocol.tri.rest.mapping.meta.NamedValueMeta;
+import org.apache.dubbo.rpc.protocol.tri.rest.mapping.meta.ParameterMeta;
+import org.apache.dubbo.rpc.protocol.tri.rest.util.RequestUtils;
+
+import java.io.IOException;
+import java.lang.annotation.Annotation;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.LinkedHashMap;
+import java.util.List;
+import java.util.Map;
+
+@Activate
+public class ParamArgumentResolver extends AbstractAnnotationBaseArgumentResolver {
+
+ @Override
+ protected NamedValueMeta createNamedValueMeta(ParameterMeta param, AnnotationMeta<Annotation> ann) {
+ String defaultValue = ann.getString("defaultValue");
+ if (Param.DEFAULT_NONE.equals(defaultValue)) {
+ defaultValue = null;
+ }
+ ParamType paramType = ann.getEnum("type");
+ return new ParamNamedValueMeta(ann.getValue(), ann.getBoolean("required"), defaultValue, paramType);
+ }
+
+ @Override
+ public Class<Annotation> accept() {
+ return Annotations.Param.type();
+ }
+
+ @Override
+ protected Object resolveValue(NamedValueMeta meta, HttpRequest request, HttpResponse response) {
+ switch (((ParamNamedValueMeta) meta).paramType) {
+ case PathVariable:
+ return resolvePathVariable(meta, request);
+ case MatrixVariable:
+ return CollectionUtils.first(resolveMatrixVariable(meta, request));
+ case Param:
+ return request.parameter(meta.name());
+ case Header:
+ return request.header(meta.name());
+ case Cookie:
+ return request.cookie(meta.name());
+ case Attribute:
+ return request.attribute(meta.name());
+ case Part:
+ return request.part(meta.name());
+ case Body:
+ if (RequestUtils.isFormOrMultiPart(request)) {
+ return request.formParameter(meta.name());
+ }
+ return RequestUtils.decodeBody(request, meta.type());
+ }
+ return null;
+ }
+
+ @Override
+ protected Object filterValue(Object value, NamedValueMeta meta) {
+ return StringUtils.EMPTY_STRING.equals(value) ? meta.defaultValue() : value;
+ }
+
+ @Override
+ protected Object resolveCollectionValue(NamedValueMeta meta, HttpRequest request, HttpResponse response) {
+ switch (((ParamNamedValueMeta) meta).paramType) {
+ case PathVariable:
+ String value = resolvePathVariable(meta, request);
+ return value == null ? Collections.emptyList() : Collections.singletonList(value);
+ case MatrixVariable:
+ return resolveMatrixVariable(meta, request);
+ case Param:
+ return request.parameterValues(meta.name());
+ case Header:
+ return request.headerValues(meta.name());
+ case Cookie:
+ Collection<HttpCookie> cookies = request.cookies();
+ if (cookies.isEmpty()) {
+ return Collections.emptyList();
+ }
+ String name = meta.name();
+ List<HttpCookie> result = new ArrayList<>(cookies.size());
+ for (HttpCookie cookie : cookies) {
+ if (name.equals(cookie.name())) {
+ result.add(cookie);
+ }
+ }
+ return result;
+ case Attribute:
+ return request.attribute(meta.name());
+ case Part:
+ return request.parts();
+ case Body:
+ Class<?> type = meta.type();
+ if (type == byte[].class) {
+ try {
+ return StreamUtils.readBytes(request.inputStream());
+ } catch (IOException e) {
+ throw new RestException(e);
+ }
+ }
+ if (RequestUtils.isFormOrMultiPart(request)) {
+ return request.formParameterValues(meta.name());
+ }
+ return RequestUtils.decodeBody(request, meta.type());
+ }
+ return null;
+ }
+
+ @Override
+ protected Object resolveMapValue(NamedValueMeta meta, HttpRequest request, HttpResponse response) {
+ switch (((ParamNamedValueMeta) meta).paramType) {
+ case PathVariable:
+ String value = resolvePathVariable(meta, request);
+ return value == null ? Collections.emptyMap() : Collections.singletonMap(meta.name(), value);
+ case MatrixVariable:
+ return CollectionUtils.first(resolveMatrixVariable(meta, request));
+ case Param:
+ return RequestUtils.getParametersMap(request);
+ case Header:
+ return request.headers();
+ case Cookie:
+ Collection<HttpCookie> cookies = request.cookies();
+ if (cookies.isEmpty()) {
+ return Collections.emptyMap();
+ }
+ Map<String, List<HttpCookie>> mapValue = CollectionUtils.newLinkedHashMap(cookies.size());
+ for (HttpCookie cookie : cookies) {
+ mapValue.computeIfAbsent(cookie.name(), k -> new ArrayList<>())
+ .add(cookie);
+ }
+ return mapValue;
+ case Attribute:
+ return request.attributes();
+ case Part:
+ Collection<FileUpload> parts = request.parts();
+ if (parts.isEmpty()) {
+ return Collections.emptyMap();
+ }
+ Map<String, FileUpload> result = new LinkedHashMap<>(parts.size());
+ for (FileUpload part : parts) {
+ result.put(part.name(), part);
+ }
+ return result;
+ case Body:
+ if (RequestUtils.isFormOrMultiPart(request)) {
+ return RequestUtils.getFormParametersMap(request);
+ }
+ return RequestUtils.decodeBody(request, meta.type());
+ }
+ return null;
+ }
+
+ private static String resolvePathVariable(NamedValueMeta meta, HttpRequest request) {
+ Map<String, String> variableMap = request.attribute(RestConstants.URI_TEMPLATE_VARIABLES_ATTRIBUTE);
+ if (variableMap == null) {
+ if (meta.required()) {
+ throw new RestParameterException(Messages.ARGUMENT_VALUE_MISSING, meta.name(), meta.type());
+ }
+ return null;
+ }
+ String value = variableMap.get(meta.name());
+ if (value == null) {
+ return null;
+ }
+ int index = value.indexOf(';');
+ return RequestUtils.decodeURL(index == -1 ? value : value.substring(0, index));
+ }
+
+ private static List<String> resolveMatrixVariable(NamedValueMeta meta, HttpRequest request) {
+ Map<String, String> variableMap = request.attribute(RestConstants.URI_TEMPLATE_VARIABLES_ATTRIBUTE);
+ return RequestUtils.parseMatrixVariableValues(variableMap, meta.name());
+ }
+
+ private static class ParamNamedValueMeta extends NamedValueMeta {
+
+ private final ParamType paramType;
+
+ public ParamNamedValueMeta(String name, boolean required, String defaultValue, ParamType paramType) {
+ super(name, required, defaultValue);
+ this.paramType = paramType;
+ }
+ }
+}
diff --git a/dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/rest/util/DefaultRestToolKit.java b/dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/rest/util/AbstractRestToolKit.java
similarity index 86%
rename from dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/rest/util/DefaultRestToolKit.java
rename to dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/rest/util/AbstractRestToolKit.java
index 0d5a2a03f7..c35cd414d5 100644
--- a/dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/rest/util/DefaultRestToolKit.java
+++ b/dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/rest/util/AbstractRestToolKit.java
@@ -18,8 +18,6 @@ package org.apache.dubbo.rpc.protocol.tri.rest.util;
import org.apache.dubbo.common.config.Environment;
import org.apache.dubbo.common.utils.AnnotationUtils;
-import org.apache.dubbo.remoting.http12.HttpRequest;
-import org.apache.dubbo.remoting.http12.HttpResponse;
import org.apache.dubbo.rpc.model.FrameworkModel;
import org.apache.dubbo.rpc.protocol.tri.rest.argument.GeneralTypeConverter;
import org.apache.dubbo.rpc.protocol.tri.rest.argument.TypeConverter;
@@ -31,21 +29,16 @@ import java.lang.reflect.Method;
import java.lang.reflect.Parameter;
import java.util.Map;
-public class DefaultRestToolKit implements RestToolKit {
+public abstract class AbstractRestToolKit implements RestToolKit {
protected final FrameworkModel frameworkModel;
protected final TypeConverter typeConverter;
- public DefaultRestToolKit(FrameworkModel frameworkModel) {
+ public AbstractRestToolKit(FrameworkModel frameworkModel) {
this.frameworkModel = frameworkModel;
typeConverter = frameworkModel.getBeanFactory().getOrRegisterBean(GeneralTypeConverter.class);
}
- @Override
- public int getDialect() {
- return 0;
- }
-
@Override
public String resolvePlaceholders(String text) {
return RestUtils.hasPlaceholder(text) ? getEnvironment().resolvePlaceholders(text) : text;
@@ -60,11 +53,6 @@ public class DefaultRestToolKit implements RestToolKit {
return typeConverter.convert(value, parameter.getGenericType());
}
- @Override
- public Object bind(ParameterMeta parameter, HttpRequest request, HttpResponse response) {
- return null;
- }
-
@Override
public String[] getParameterNames(Method method) {
Parameter[] parameters = method.getParameters();
diff --git a/dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/rest/util/KeyString.java b/dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/rest/util/KeyString.java
new file mode 100644
index 0000000000..2ddac3c70c
--- /dev/null
+++ b/dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/rest/util/KeyString.java
@@ -0,0 +1,118 @@
+/*
+ * 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.dubbo.rpc.protocol.tri.rest.util;
+
+/**
+ * Zero-copy string as map key.
+ */
+public final class KeyString implements CharSequence {
+
+ private final String value;
+ private final int offset;
+ private final int length;
+ private final boolean caseSensitive;
+
+ public KeyString(String value, int start, int end, boolean caseSensitive) {
+ this.value = value;
+ offset = start;
+ length = (end == -1 ? value.length() : end) - start;
+ this.caseSensitive = caseSensitive;
+ }
+
+ public KeyString(String value, int start, int end) {
+ this(value, start, end, true);
+ }
+
+ public KeyString(String value, int end, boolean caseSensitive) {
+ this.value = value;
+ offset = 0;
+ length = end == -1 ? value.length() : end;
+ this.caseSensitive = caseSensitive;
+ }
+
+ public KeyString(String value, int end) {
+ this(value, end, true);
+ }
+
+ public KeyString(String value, boolean caseSensitive) {
+ this.value = value;
+ offset = 0;
+ length = value.length();
+ this.caseSensitive = caseSensitive;
+ }
+
+ public KeyString(String value) {
+ this(value, false);
+ }
+
+ public KeyString(KeyString path, int start, int end) {
+ this(path.value, path.offset + start, path.offset + end, path.caseSensitive);
+ }
+
+ @Override
+ public int length() {
+ return length;
+ }
+
+ @Override
+ public char charAt(int index) {
+ return value.charAt(offset + index);
+ }
+
+ @Override
+ public CharSequence subSequence(int start, int end) {
+ return value.substring(offset + start, offset + end);
+ }
+
+ @Override
+ public int hashCode() {
+ int h = 0;
+ for (int i = 0; i < length; i++) {
+ h = 31 * h + (caseSensitive ? value.charAt(offset + i) : Character.toLowerCase(value.charAt(offset + i)));
+ }
+ return h;
+ }
+
+ @Override
+ @SuppressWarnings("EqualsWhichDoesntCheckParameterClass")
+ public boolean equals(Object obj) {
+ KeyString other = (KeyString) obj;
+ return value.regionMatches(!caseSensitive, offset, other.value, other.offset, length);
+ }
+
+ @Override
+ public String toString() {
+ return value.substring(offset, length - offset);
+ }
+
+ public int indexOf(char ch, int start) {
+ int index = value.indexOf(ch, offset + start);
+ return index == -1 ? -1 : index - offset;
+ }
+
+ public boolean regionMatches(int start, String value, int i, int length) {
+ return this.value.regionMatches(!caseSensitive, offset + start, value, i, length);
+ }
+
+ public String substring(int start) {
+ return value.substring(offset + start, offset + length);
+ }
+
+ public String substring(int start, int end) {
+ return value.substring(offset + start, offset + (end == -1 ? length : end));
+ }
+}
diff --git a/dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/rest/util/PathUtils.java b/dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/rest/util/PathUtils.java
index e6a6bf51c0..609446f826 100644
--- a/dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/rest/util/PathUtils.java
+++ b/dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/rest/util/PathUtils.java
@@ -22,6 +22,7 @@ import org.apache.dubbo.common.utils.StringUtils;
import org.apache.dubbo.rpc.protocol.tri.rest.Messages;
import org.apache.dubbo.rpc.protocol.tri.rest.PathParserException;
import org.apache.dubbo.rpc.protocol.tri.rest.RestConstants;
+import org.apache.dubbo.rpc.protocol.tri.rest.mapping.condition.PathExpression;
import javax.annotation.Nonnull;
@@ -88,6 +89,12 @@ public final class PathUtils {
return path2;
}
} else if (path1.indexOf('{') == -1) {
+ if (path1.indexOf('*') != -1) {
+ if (PathExpression.parse(path1).match(path2) != null) {
+ return path2;
+ }
+ }
+
int starDotPos1 = path1.lastIndexOf("*.");
if (starDotPos1 > -1) {
String ext1 = path1.substring(starDotPos1 + 1);
@@ -100,7 +107,7 @@ public final class PathUtils {
file2 = path2.substring(0, dotPos2);
ext2 = path2.substring(dotPos2);
}
- boolean ext1All = ext1.equals(".*");
+ boolean ext1All = ext1.equals(".*") || ext1.isEmpty();
boolean ext2All = ext2.equals(".*") || ext2.isEmpty();
if (!ext1All && !ext2All) {
throw new PathParserException(Messages.CANNOT_COMBINE_PATHS, path1, path2);
@@ -240,8 +247,24 @@ public final class PathUtils {
}
end = i;
}
+ switch (state) {
+ case State.DOT:
+ end--;
+ break;
+ case State.DOT_DOT:
+ if (buf == null) {
+ buf = new StringBuilder(len);
+ }
+ if (end > 2) {
+ buf.append(path, start, end - 2);
+ buf.setLength(buf.lastIndexOf(RestConstants.SLASH) + 1);
+ start = -1;
+ }
+ break;
+ default:
+ }
if (buf == null) {
- return path;
+ return start == -1 ? path : path.substring(start, end + 1);
}
if (start != -1) {
buf.append(path, start, end + 1);
diff --git a/dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/rest/util/RequestUtils.java b/dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/rest/util/RequestUtils.java
index a91fbb3289..b75561a0ee 100644
--- a/dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/rest/util/RequestUtils.java
+++ b/dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/rest/util/RequestUtils.java
@@ -25,6 +25,9 @@ import org.apache.dubbo.remoting.http12.message.HttpMessageDecoder;
import org.apache.dubbo.remoting.http12.message.MediaType;
import org.apache.dubbo.rpc.protocol.tri.rest.RestConstants;
+import java.io.ByteArrayInputStream;
+import java.io.IOException;
+import java.io.InputStream;
import java.io.UnsupportedEncodingException;
import java.net.URLDecoder;
import java.net.URLEncoder;
@@ -41,6 +44,11 @@ public final class RequestUtils {
private RequestUtils() {}
+ public static boolean isMultiPart(HttpRequest request) {
+ String contentType = request.contentType();
+ return contentType != null && contentType.startsWith(MediaType.MULTIPART_FORM_DATA.getName());
+ }
+
public static boolean isFormOrMultiPart(HttpRequest request) {
String contentType = request.contentType();
if (contentType == null) {
@@ -168,12 +176,85 @@ public final class RequestUtils {
}
Object value = request.attribute(RestConstants.BODY_ATTRIBUTE);
if (value == null) {
- if (decoder.mediaType() == MediaType.TEXT_PLAIN) {
+ if (decoder.mediaType().isPureText()) {
type = String.class;
}
- value = decoder.decode(request.inputStream(), type, request.charsetOrDefault());
+ InputStream input = request.inputStream();
+ try {
+ int available = input.available();
+ if (available == 0) {
+ return emptyDefault(type);
+ }
+ } catch (IOException e) {
+ throw new DecodeException("Error reading input", e);
+ }
+
+ value = decoder.decode(input, type, request.charsetOrDefault());
request.setAttribute(RestConstants.BODY_ATTRIBUTE, value);
}
return value;
}
+
+ public static Object decodeBody(HttpRequest request, Class<?> type, boolean single) {
+ HttpMessageDecoder decoder = request.attribute(RestConstants.BODY_DECODER_ATTRIBUTE);
+ if (decoder == null) {
+ return null;
+ }
+ MediaType mediaType = decoder.mediaType();
+ if (mediaType == MediaType.APPLICATION_FROM_URLENCODED || mediaType == MediaType.MULTIPART_FORM_DATA) {
+ return null;
+ }
+
+ try {
+ InputStream input = request.inputStream();
+ if (single) {
+ if (mediaType.isPureText()) {
+ type = String.class;
+ }
+ int available = input.available();
+ if (available == 0) {
+ return emptyDefault(type);
+ }
+ if (!input.markSupported()) {
+ byte[] bytes = new byte[available];
+ input.read(bytes);
+ input = new ByteArrayInputStream(bytes);
+ }
+ try {
+ return decoder.decode(input, type, request.charsetOrDefault());
+ } catch (Throwable t) {
+ input.reset();
+ }
+ }
+
+ Object value = request.attribute(RestConstants.BODY_ATTRIBUTE);
+ if (value == null) {
+ if (mediaType == MediaType.APPLICATION_JSON || mediaType == MediaType.APPLICATION_YAML) {
+ type = Object.class;
+ } else if (mediaType.isPureText()) {
+ type = String.class;
+ } else {
+ return null;
+ }
+ int available = input.available();
+ if (available != 0) {
+ value = decoder.decode(input, type, request.charsetOrDefault());
+ }
+ request.setAttribute(RestConstants.BODY_ATTRIBUTE, value);
+ }
+ return value;
+ } catch (IOException e) {
+ throw new DecodeException("Error reading input", e);
+ }
+ }
+
+ private static Object emptyDefault(Class<?> type) {
+ if (type == String.class) {
+ return StringUtils.EMPTY_STRING;
+ }
+ if (type == byte[].class) {
+ return new byte[0];
+ }
+ return null;
+ }
}
diff --git a/dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/rest/util/RestUtils.java b/dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/rest/util/RestUtils.java
index a5e5331dc5..1155d3860a 100644
--- a/dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/rest/util/RestUtils.java
+++ b/dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/rest/util/RestUtils.java
@@ -21,6 +21,7 @@ import org.apache.dubbo.common.lang.Prioritized;
import org.apache.dubbo.rpc.protocol.tri.rest.filter.RestExtension;
public final class RestUtils {
+
private RestUtils() {}
public static boolean hasPlaceholder(String text) {
@@ -50,6 +51,36 @@ public final class RestUtils {
return false;
}
+ public static boolean isMaybeJSONObject(String str) {
+ if (str == null) {
+ return false;
+ }
+ int i = 0, n = str.length();
+ if (n < 3) {
+ return false;
+ }
+ char expected = 0;
+ for (; i < n; i++) {
+ char c = str.charAt(i);
+ if (Character.isWhitespace(c)) {
+ continue;
+ }
+ if (c == '{') {
+ expected = '}';
+ break;
+ }
+ return false;
+ }
+ for (int j = n - 1; j > i; j--) {
+ char c = str.charAt(j);
+ if (Character.isWhitespace(c)) {
+ continue;
+ }
+ return c == expected;
+ }
+ return false;
+ }
+
public static int getPriority(Object obj) {
if (obj instanceof Prioritized) {
int priority = ((Prioritized) obj).getPriority();
diff --git a/dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/rest/util/TypeUtils.java b/dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/rest/util/TypeUtils.java
index 1e6181795d..fc77b1f10b 100644
--- a/dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/rest/util/TypeUtils.java
+++ b/dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/rest/util/TypeUtils.java
@@ -22,7 +22,10 @@ import org.apache.dubbo.common.utils.ConcurrentHashSet;
import java.io.File;
import java.lang.reflect.Array;
+import java.lang.reflect.Constructor;
import java.lang.reflect.GenericArrayType;
+import java.lang.reflect.Method;
+import java.lang.reflect.Modifier;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.lang.reflect.TypeVariable;
@@ -34,18 +37,34 @@ import java.nio.charset.Charset;
import java.nio.file.Path;
import java.time.ZoneId;
import java.time.temporal.Temporal;
+import java.util.ArrayList;
+import java.util.Collection;
import java.util.Collections;
import java.util.Currency;
import java.util.Date;
+import java.util.Deque;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.LinkedHashMap;
+import java.util.LinkedHashSet;
+import java.util.LinkedList;
+import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.Optional;
+import java.util.Queue;
import java.util.Set;
+import java.util.SortedMap;
+import java.util.SortedSet;
import java.util.TimeZone;
+import java.util.TreeMap;
import java.util.UUID;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.ConcurrentMap;
import java.util.regex.Pattern;
public final class TypeUtils {
+
private static final Set<Class<?>> SIMPLE_TYPES = new ConcurrentHashSet<>();
static {
@@ -124,15 +143,15 @@ public final class TypeUtils {
}
public static Class<?> getSuperGenericType(Class<?> clazz, int index) {
- Class<?> result = getNestedType(clazz.getGenericSuperclass(), index);
- return result == null ? getNestedType(ArrayUtils.first(clazz.getGenericInterfaces()), index) : result;
+ Class<?> result = getNestedActualType(clazz.getGenericSuperclass(), index);
+ return result == null ? getNestedActualType(ArrayUtils.first(clazz.getGenericInterfaces()), index) : result;
}
public static Class<?> getSuperGenericType(Class<?> clazz) {
return getSuperGenericType(clazz, 0);
}
- public static Class<?>[] getNestedTypes(Type type) {
+ public static Class<?>[] getNestedActualTypes(Type type) {
if (type instanceof ParameterizedType) {
Type[] typeArgs = ((ParameterizedType) type).getActualTypeArguments();
int len = typeArgs.length;
@@ -145,7 +164,7 @@ public final class TypeUtils {
return null;
}
- public static Class<?> getNestedType(Type type, int index) {
+ public static Class<?> getNestedActualType(Type type, int index) {
if (type instanceof ParameterizedType) {
Type[] typeArgs = ((ParameterizedType) type).getActualTypeArguments();
if (index < typeArgs.length) {
@@ -175,6 +194,16 @@ public final class TypeUtils {
return null;
}
+ public static Type getNestedGenericType(Type type, int index) {
+ if (type instanceof ParameterizedType) {
+ Type[] typeArgs = ((ParameterizedType) type).getActualTypeArguments();
+ if (index < typeArgs.length) {
+ return getActualGenericType(typeArgs[index]);
+ }
+ }
+ return null;
+ }
+
public static Type getActualGenericType(Type type) {
if (type instanceof TypeVariable) {
return ((TypeVariable<?>) type).getBounds()[0];
@@ -215,4 +244,137 @@ public final class TypeUtils {
}
return null;
}
+
+ public static Object longToObject(long value, Class<?> targetClass) {
+ if (targetClass == Long.class) {
+ return value;
+ }
+ if (targetClass == Integer.class) {
+ return (int) value;
+ }
+ if (targetClass == Short.class) {
+ return (short) value;
+ }
+ if (targetClass == Character.class) {
+ return (char) value;
+ }
+ if (targetClass == Byte.class) {
+ return (byte) value;
+ }
+ return null;
+ }
+
+ @SuppressWarnings("rawtypes")
+ public static Collection createCollection(Class targetClass) {
+ if (targetClass.isInterface()) {
+ if (targetClass == List.class || targetClass == Collection.class) {
+ return new ArrayList<>();
+ }
+ if (targetClass == Set.class) {
+ return new HashSet<>();
+ }
+ if (targetClass == SortedSet.class) {
+ return new LinkedHashSet<>();
+ }
+ if (targetClass == Queue.class || targetClass == Deque.class) {
+ return new LinkedList<>();
+ }
+ } else if (Collection.class.isAssignableFrom(targetClass)) {
+ if (targetClass == ArrayList.class) {
+ return new ArrayList<>();
+ }
+ if (targetClass == LinkedList.class) {
+ return new LinkedList();
+ }
+ if (targetClass == HashSet.class) {
+ return new HashSet<>();
+ }
+ if (targetClass == LinkedHashSet.class) {
+ return new LinkedHashSet<>();
+ }
+ if (!Modifier.isAbstract(targetClass.getModifiers())) {
+ try {
+ Constructor sizeCt = null;
+ for (Constructor ct : targetClass.getConstructors()) {
+ switch (ct.getParameterCount()) {
+ case 0:
+ return (Collection) ct.newInstance();
+ case 1:
+ if (ct.getParameterTypes()[0] == int.class) {
+ sizeCt = ct;
+ }
+ break;
+ default:
+ }
+ }
+ if (sizeCt != null) {
+ return (Collection) sizeCt.newInstance(16);
+ }
+ } catch (Exception ignored) {
+ }
+ }
+ }
+ throw new IllegalArgumentException("Unsupported collection type: " + targetClass.getName());
+ }
+
+ @SuppressWarnings("rawtypes")
+ public static Map createMap(Class targetClass) {
+ if (targetClass.isInterface()) {
+ if (targetClass == Map.class) {
+ return new HashMap<>();
+ }
+ if (targetClass == ConcurrentMap.class) {
+ return new ConcurrentHashMap<>();
+ }
+ if (SortedMap.class.isAssignableFrom(targetClass)) {
+ return new TreeMap<>();
+ }
+ } else if (Map.class.isAssignableFrom(targetClass)) {
+ if (targetClass == HashMap.class) {
+ return new HashMap<>();
+ }
+ if (targetClass == LinkedHashMap.class) {
+ return new LinkedHashMap<>();
+ }
+ if (targetClass == TreeMap.class) {
+ return new TreeMap<>();
+ }
+ if (targetClass == ConcurrentHashMap.class) {
+ return new ConcurrentHashMap<>();
+ }
+ if (!Modifier.isAbstract(targetClass.getModifiers())) {
+ try {
+ Constructor sizeCt = null;
+ for (Constructor ct : targetClass.getConstructors()) {
+ if (Modifier.isPublic(ct.getModifiers())) {
+ switch (ct.getParameterCount()) {
+ case 0:
+ return (Map) ct.newInstance();
+ case 1:
+ if (ct.getParameterTypes()[0] == int.class) {
+ sizeCt = ct;
+ }
+ break;
+ default:
+ }
+ }
+ }
+ if (sizeCt != null) {
+ return (Map) sizeCt.newInstance(16);
+ }
+ } catch (Throwable ignored) {
+ }
+ }
+ }
+ throw new IllegalArgumentException("Unsupported map type: " + targetClass.getName());
+ }
+
+ public static String buildSig(Method method) {
+ StringBuilder sb = new StringBuilder(8);
+ for (Class<?> type : method.getParameterTypes()) {
+ String name = type.getName();
+ sb.append(name.charAt(name.lastIndexOf('.') + 1));
+ }
+ return sb.toString();
+ }
}
diff --git a/dubbo-rpc/dubbo-rpc-triple/src/main/resources/META-INF/dubbo/internal/org.apache.dubbo.rpc.protocol.tri.rest.argument.ArgumentResolver b/dubbo-rpc/dubbo-rpc-triple/src/main/resources/META-INF/dubbo/internal/org.apache.dubbo.rpc.protocol.tri.rest.argument.ArgumentResolver
index 3d2d4e5073..0486f00884 100644
--- a/dubbo-rpc/dubbo-rpc-triple/src/main/resources/META-INF/dubbo/internal/org.apache.dubbo.rpc.protocol.tri.rest.argument.ArgumentResolver
+++ b/dubbo-rpc/dubbo-rpc-triple/src/main/resources/META-INF/dubbo/internal/org.apache.dubbo.rpc.protocol.tri.rest.argument.ArgumentResolver
@@ -1 +1,3 @@
misc=org.apache.dubbo.rpc.protocol.tri.rest.argument.MiscArgumentResolver
+basic-param=org.apache.dubbo.rpc.protocol.tri.rest.support.basic.ParamArgumentResolver
+basic-fallback=org.apache.dubbo.rpc.protocol.tri.rest.support.basic.FallbackArgumentResolver
diff --git a/dubbo-rpc/dubbo-rpc-triple/src/main/resources/META-INF/dubbo/internal/org.apache.dubbo.rpc.protocol.tri.rest.mapping.RequestMappingResolver b/dubbo-rpc/dubbo-rpc-triple/src/main/resources/META-INF/dubbo/internal/org.apache.dubbo.rpc.protocol.tri.rest.mapping.RequestMappingResolver
new file mode 100644
index 0000000000..edb8a4f70f
--- /dev/null
+++ b/dubbo-rpc/dubbo-rpc-triple/src/main/resources/META-INF/dubbo/internal/org.apache.dubbo.rpc.protocol.tri.rest.mapping.RequestMappingResolver
@@ -0,0 +1 @@
+basic=org.apache.dubbo.rpc.protocol.tri.rest.support.basic.BasicRequestMappingResolver
diff --git a/dubbo-rpc/dubbo-rpc-triple/src/main/resources/META-INF/dubbo/internal/org.apache.dubbo.rpc.protocol.tri.route.RequestHandlerMapping b/dubbo-rpc/dubbo-rpc-triple/src/main/resources/META-INF/dubbo/internal/org.apache.dubbo.rpc.protocol.tri.route.RequestHandlerMapping
index 3f44b63ea2..af94fe1071 100644
--- a/dubbo-rpc/dubbo-rpc-triple/src/main/resources/META-INF/dubbo/internal/org.apache.dubbo.rpc.protocol.tri.route.RequestHandlerMapping
+++ b/dubbo-rpc/dubbo-rpc-triple/src/main/resources/META-INF/dubbo/internal/org.apache.dubbo.rpc.protocol.tri.route.RequestHandlerMapping
@@ -1,3 +1,2 @@
-http=org.apache.dubbo.rpc.protocol.tri.h12.HttpRequestHandlerMapping
grpc=org.apache.dubbo.rpc.protocol.tri.h12.grpc.GrpcRequestHandlerMapping
-rest=org.apache.dubbo.rpc.protocol.tri.rest.mapping.RestRequestHandlerMapping
\ No newline at end of file
+rest=org.apache.dubbo.rpc.protocol.tri.rest.mapping.RestRequestHandlerMapping
diff --git a/dubbo-rpc/dubbo-rpc-triple/src/test/groovy/org/apache/dubbo/rpc/protocol/tri/rest/mapping/RadixTreeTest.groovy b/dubbo-rpc/dubbo-rpc-triple/src/test/groovy/org/apache/dubbo/rpc/protocol/tri/rest/mapping/RadixTreeTest.groovy
new file mode 100644
index 0000000000..35bbe55031
--- /dev/null
+++ b/dubbo-rpc/dubbo-rpc-triple/src/test/groovy/org/apache/dubbo/rpc/protocol/tri/rest/mapping/RadixTreeTest.groovy
@@ -0,0 +1,47 @@
+/*
+ * 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.dubbo.rpc.protocol.tri.rest.mapping
+
+import spock.lang.Specification
+
+class RadixTreeTest extends Specification {
+
+ def "Match"() {
+ given:
+ def tree = new RadixTree<String>()
+ tree.addPath('/a/*', 'abc')
+ tree.addPath('/a/{x}/d/e', 'bcd')
+ tree.addPath('/a/{v:.*}/e', 'bcd')
+ when:
+ def match = tree.match('/a/b/d/e')
+ then:
+ !match.empty
+ }
+
+ def "Clear"() {
+ given:
+ def tree = new RadixTree<String>()
+ tree.addPath('/a/*', 'abc')
+ tree.addPath('/a/{x}/d/e', 'bcd')
+ tree.addPath('/a/{v:.*}/e', 'bcd')
+ when:
+ tree.remove(s -> s in ['abc', 'bcd'])
+ then:
+ tree.empty
+ }
+}
diff --git a/dubbo-rpc/dubbo-rpc-triple/src/test/groovy/org/apache/dubbo/rpc/protocol/tri/rest/mapping/condition/PathExpressionTest.groovy b/dubbo-rpc/dubbo-rpc-triple/src/test/groovy/org/apache/dubbo/rpc/protocol/tri/rest/mapping/condition/PathExpressionTest.groovy
new file mode 100644
index 0000000000..cbcec0922e
--- /dev/null
+++ b/dubbo-rpc/dubbo-rpc-triple/src/test/groovy/org/apache/dubbo/rpc/protocol/tri/rest/mapping/condition/PathExpressionTest.groovy
@@ -0,0 +1,261 @@
+/*
+ * 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.dubbo.rpc.protocol.tri.rest.mapping.condition
+
+import org.apache.dubbo.rpc.protocol.tri.rest.Messages
+import org.apache.dubbo.rpc.protocol.tri.rest.PathParserException
+
+import spock.lang.Specification
+
+import static org.apache.dubbo.rpc.protocol.tri.rest.mapping.condition.PathExpression.parse
+import static org.apache.dubbo.rpc.protocol.tri.rest.mapping.condition.PathSegment.Type.LITERAL
+import static org.apache.dubbo.rpc.protocol.tri.rest.mapping.condition.PathSegment.Type.PATTERN
+import static org.apache.dubbo.rpc.protocol.tri.rest.mapping.condition.PathSegment.Type.PATTERN_MULTI
+import static org.apache.dubbo.rpc.protocol.tri.rest.mapping.condition.PathSegment.Type.VARIABLE
+import static org.apache.dubbo.rpc.protocol.tri.rest.mapping.condition.PathSegment.Type.WILDCARD
+import static org.apache.dubbo.rpc.protocol.tri.rest.mapping.condition.PathSegment.Type.WILDCARD_TAIL
+
+class PathExpressionTest extends Specification {
+
+ def "Parse"() {
+ given:
+ def pss = PathParser.parse(path)
+ expect:
+ assertPath(pss, rs)
+ where:
+ path | rs
+ null | [[LITERAL, '/']]
+ '' | [[LITERAL, '/']]
+ '/' | [[LITERAL, '/']]
+ '/a?*b' | [[PATTERN, 'a[^/]*b']]
+ '/a*?b' | [[PATTERN, 'a[^/]*b']]
+ '/a**b' | [[PATTERN, 'a[^/]*b']]
+ '/{param:o?e}' | [[PATTERN, '(?<param>o?e)']]
+ '/{param: \\d+ }/name' | [[PATTERN, '(?<param>\\d+)', 'param'], [LITERAL, 'name']]
+ '/{param:\\S\\W}' | [[PATTERN_MULTI, '(?<param>\\S\\W)']]
+ '/{param:a{2}}' | [[PATTERN, '(?<param>a{2})']]
+ '/{param:[^/]+}' | [[PATTERN, '(?<param>[^/]+)', 'param']]
+ '/{param:[/]+}' | [[PATTERN_MULTI, '(?<param>[/]+)', 'param']]
+ '/{one}{two:\\d+}' | [[PATTERN, '(?<one>[^/]+)(?<two>\\d+)', 'one', 'two']]
+ '/{one:\\d+}{}' | [[PATTERN, '(?<one>\\d+)[^/]+', 'one']]
+ '/{one:\\d+}**' | [[PATTERN_MULTI, '(?<one>\\d+).*', 'one']]
+ '/{one:\\d+}{*two}' | [[PATTERN_MULTI, '(?<one>\\d+)(?<two>.*)', 'one', 'two']]
+ '/{first}-{last}/b/{vv}/v' | [[PATTERN, '(?<first>[^/]+)-(?<last>[^/]+)', 'first', 'last'], 'b', [VARIABLE, 'vv'], 'v']
+ }
+
+ def "Parse failed"() {
+ when:
+ PathParser.parse(path)
+ then:
+ def e = thrown(PathParserException)
+ e.errorCode == code.name()
+ where:
+ path | code
+ '/{param:[a}' | Messages.REGEX_PATTERN_INVALID
+ '/{param:}' | Messages.MISSING_REGEX_CONSTRAINT
+ '/{one}/{*/three' | Messages.MISSING_CLOSE_CAPTURE
+ '/{one}/{two/three' | Messages.MISSING_CLOSE_CAPTURE
+ '/{o{ne}' | Messages.ILLEGAL_NESTED_CAPTURE
+ '/{one}a}' | Messages.MISSING_OPEN_CAPTURE
+ '/one/{*postfix}t' | Messages.NO_MORE_DATA_ALLOWED
+ '/{one}{two}' | Messages.ILLEGAL_DOUBLE_CAPTURE
+ '/{one}-{one}' | Messages.DUPLICATE_CAPTURE_VARIABLE
+ '/{one}/{one' | Messages.MISSING_CLOSE_CAPTURE
+ }
+
+ /**
+ * <a href="https://docs.jboss.org/resteasy/docs/6.2.7.Final/userguide/html/ch04.html">resteasy @Path</a>
+ */
+ def "ParseJaxrs"() {
+ given:
+ def pss = PathParser.parse(path)
+ expect:
+ assertPath(pss, rs)
+ where:
+ path | rs
+ '/library/books' | ['/library/books']
+ '/library/{isbn}' | ['library', [VARIABLE, 'isbn']]
+ '/library/{isbn}/{type}' | ['library', [VARIABLE, 'isbn'], [VARIABLE, 'type']]
+ '{var:\\d+}/stuff' | [[PATTERN, '(?<var>\\d+)', 'var'], [LITERAL, 'stuff']]
+ '{var:.*}/stuff' | [[PATTERN_MULTI, '(?<var>.*)/stuff', 'var']]
+ '/aaa{param}bbb' | [[PATTERN, 'aaa(?<param>[^/]+)bbb', 'param']]
+ '/foo{name}-{zip}bar' | [[PATTERN, 'foo(?<name>[^/]+)-(?<zip>[^/]+)bar', 'name', 'zip']]
+ '/aaa{param:b+}/{many:.*}/stuff' | [[PATTERN, 'aaa(?<param>b+)', 'param'], [PATTERN_MULTI, '(?<many>.*)/stuff', 'many']]
+ }
+
+ /**
+ * <a href="https://docs.spring.io/spring-framework/reference/web/webmvc/mvc-controller/ann-requestmapping.html#mvc-ann-requestmapping-uri-templates">Spring uri templates</a>
+ */
+ def "ParseSpring"() {
+ given:
+ def pss = PathParser.parse(path)
+ expect:
+ assertPath(pss, rs)
+ where:
+ path | rs
+ '/resources/ima?e.png' | ['resources', [PATTERN, 'ima[^/]e\\.png']]
+ '/resources/*.png' | ['resources', [PATTERN, '[^/]*\\.png']]
+ '/resources/*/test.png' | ['resources', [VARIABLE, ''], 'test.png']
+ '/resources/**/test.png' | ['resources', [PATTERN_MULTI, '.*\\Qtest.png\\E']]
+ '/resources/**/*.png' | ['resources', [PATTERN_MULTI, '.*[^/]*\\.png']]
+ '/resources/**' | ['resources', [WILDCARD_TAIL, '']]
+ '/resources/{**}' | ['resources', [WILDCARD_TAIL, '*']]
+ '/resources/{*path}' | ['resources', [WILDCARD_TAIL, 'path', 'path']]
+ '/resources/*' | ['resources', [VARIABLE, '']]
+ '/projects/{project}/versions' | ['projects', [VARIABLE, 'project'], 'versions']
+ '/projects/{project:[a-z]+}/versions' | ['projects', [PATTERN, '(?<project>[a-z]+)', 'project'], 'versions']
+ /{name:[a-z-]+}-{version:\d\.\d\.\d}{ext:\.[a-z]+}/ | [[PATTERN, /(?<name>[a-z-]+)-(?<version>\d\.\d\.\d)(?<ext>\.[a-z]+)/]]
+ }
+
+ def assertPath(PathSegment[] pss, List rs) {
+ rs.eachWithIndex { s, i ->
+ assert pss.length > i
+ def ps = pss[i]
+ if (s instanceof String) {
+ assert ps.value == s
+ } else if (s instanceof List) {
+ assert ps.type == s[0]
+ assert ps.value == s[1]
+ for (j in 2..<s.size()) {
+ assert ps.variables[j - 2] == s[j]
+ }
+ }
+ }
+ }
+
+ def "IsDirect"() {
+ expect:
+ parse(path).direct == result
+ where:
+ path | result
+ '/library/books' | true
+ '/library/{isbn}' | false
+ '/library/**' | false
+ }
+
+ def "Match"() {
+ given:
+ def map = parse(path).match(value)
+ expect:
+ if (result instanceof Boolean) {
+ assert map != null == result
+ } else if (result instanceof Map) {
+ assert map == result
+ }
+ where:
+ path | value | result
+ '/library/books' | '/library/books' | true
+ '/library/{isbn}' | '/library/1-84356-028-3' | [isbn: '1-84356-028-3']
+ '/library/{isbn}/{type}' | '/library/1-84356-028-3/math' | [isbn: '1-84356-028-3', type: 'math']
+ '/{var:.*}/stuff' | '/123/stuff' | [var: '123']
+ '/{var:.*}/stu' | '/123/stuff' | false
+ '/{var:.*}/stuff' | '/123/abc/stuff' | [var: '123/abc']
+ '/aaa{param}bbb' | '/aaac1cbbb' | [param: 'c1c']
+ '/foo{name}-{zip}bar' | '/fooabc-123bar' | [name: 'abc', zip: '123']
+ '/aaa{param:b+}/{many:.*}/stuff' | '/aaabbb/ccc/stuff' | [param: 'bbb', many: 'ccc']
+ '/resources/*.png' | '/resources/cat.png' | true
+ '/resources/**/test.png' | '/resources/abc/def/test.png' | true
+ '/resources/**' | '/resources/a/b/c' | true
+ '/resources/{*path}' | '/resources/a/b/c' | [path: 'a/b/c']
+ '/resources/*' | '/resources/a' | true
+ '/resources/*' | '/resources/a/b/c' | true
+ '/{id:\\d+}' | '/123' | [id: '123']
+ '/{id:\\d+}' | '/one' | false
+ '/a?cd/ef' | '/abcd/ef' | true
+ '/a?cd/ef' | '/aaccd/ef' | false
+ '/a?cd/ef' | '/a/cd/ef' | false
+ '/a*cd/ef' | '/aaccd/ef' | true
+ '/a*cd/ef' | '/acd/ef' | true
+ '/a*cd/ef' | '/a/ccd/ef' | false
+ '/one/*/three' | '/one/two/three' | true
+ '/one/*/three' | '/one/three' | false
+ '/one/**/three' | '/one/three' | true
+ '/one/**/*/three' | '/one/three' | false
+ '/{one:\\d+}{*two}' | '/123abc/def' | [one: '123', two: 'abc/def']
+ '/{one:\\d+}{two:.*}' | '/123abc/def' | [one: '123', two: 'abc/def']
+ }
+
+ def "MatchTest"() {
+ expect:
+ parse(path).match(value) != null
+ where:
+ path | value
+ '/resources/*' | '/resources/a/b/c'
+ }
+
+ def "CompareTo"() {
+ expect:
+ parse(path) <=> parse(other) == result
+ where:
+ path | other | result
+ '/one' | '/one' | 0
+ '/one' | '/two' | 0
+ '/one/{two}/three' | '/one/{t}/three' | 0
+ '/one/{two}/three' | '/one/*/three' | -1
+ '/one/*/three' | '/one/**/three' | -1
+ '/one/two' | '/one' | -1
+ '/one/{two}' | '/{one}/two' | -1
+ '/one/{two}' | '/one/{two:\\d+}' | -1
+ '/one/*' | '/one/{two:\\d+}' | -1
+ '/one/{two:\\d+}/three' | '/one/**/three' | -1
+ }
+
+ def "CompareTo with lookup path"() {
+ expect:
+ parse(path).compareTo(parse(other), lookupPath) == result
+ where:
+ path | other | lookupPath | result
+ '/one' | '/two' | '/one' | -1
+ '/one' | '/two' | '/two' | 1
+ '/one' | '/two' | '/three' | 0
+ }
+
+ def "Equals"() {
+ expect:
+ Objects.equals(path, other) == result
+ if (result) {
+ path.hashCode() == other.hashCode()
+ }
+ where:
+ path | other | result
+ parse('/one') | parse('/one') | true
+ parse('/one') | parse('/two') | false
+ parse('/two') | null | false
+ parse('/two') | '/two' | false
+ }
+
+ @SuppressWarnings('ChangeToOperator')
+ def "Misc"() {
+ given:
+ def path = '/library/{isbn}/{type}'
+ def expr = parse(path)
+ def seg = expr.segments[1]
+ expect:
+ PathExpression.match(path, "/library/1-84356-028-3/math")
+ expr.path == path
+ expr.equals(expr)
+ expr.segments.length == 3
+ expr.toString() == path
+ seg.hashCode()
+ seg.equals(seg)
+ !seg.equals(null)
+ !seg.equals(expr.segments[2])
+ !new PathSegment(WILDCARD, '').match('', 0, 0, null)
+ seg.toString() == '{type=VARIABLE, value=isbn, variables=[isbn]}'
+ }
+}
diff --git a/dubbo-rpc/dubbo-rpc-triple/src/test/groovy/org/apache/dubbo/rpc/protocol/tri/rest/support/basic/RestProtocolTest.groovy b/dubbo-rpc/dubbo-rpc-triple/src/test/groovy/org/apache/dubbo/rpc/protocol/tri/rest/support/basic/RestProtocolTest.groovy
new file mode 100644
index 0000000000..23692bc020
--- /dev/null
+++ b/dubbo-rpc/dubbo-rpc-triple/src/test/groovy/org/apache/dubbo/rpc/protocol/tri/rest/support/basic/RestProtocolTest.groovy
@@ -0,0 +1,191 @@
+/*
+ * 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.dubbo.rpc.protocol.tri.rest.support.basic
+
+import org.apache.dubbo.remoting.http12.message.MediaType
+import org.apache.dubbo.rpc.protocol.tri.rest.service.Book
+import org.apache.dubbo.rpc.protocol.tri.rest.service.DemoServiceImpl
+import org.apache.dubbo.rpc.protocol.tri.rest.test.BaseServiceTest
+import org.apache.dubbo.rpc.protocol.tri.test.TestRequest
+import org.apache.dubbo.rpc.protocol.tri.test.TestRunnerBuilder
+
+class RestProtocolTest extends BaseServiceTest {
+
+ @Override
+ void setupService(TestRunnerBuilder builder) {
+ builder.provider(new DemoServiceImpl())
+ }
+
+ def "hello world"() {
+ expect:
+ runner.get(path) == output
+ where:
+ path | output
+ '/hello?name=world' | 'hello world'
+ '/hello/?name=world' | 'hello world'
+ '/hello.yml?name=world' | 'hello world'
+ }
+
+ def "argument test"() {
+ expect:
+ runner.post(path, body) == output
+ where:
+ path | body | output
+ '/argTest' | '["Sam","8"]' | 'Sam is 8 years old'
+ '/argTest' | '{"name": "Sam", "age": 8}' | 'Sam is 8 years old'
+ '/argTest?name=Sam' | '{"age": 8}' | 'Sam is 8 years old'
+ }
+
+ def "bean argument test"() {
+ expect:
+ runner.post(path, body, Book.class).name == output
+ where:
+ path | body | output
+ '/buy' | new Book(name: "Dubbo") | 'Dubbo'
+ '/buy' | [new Book(name: "Dubbo")] | 'Dubbo'
+ '/buy2' | [new Book(name: "Dubbo"), 2] | 'Dubbo'
+ '/buy2' | [book: new Book(name: "Dubbo"), count: 2] | 'Dubbo'
+ }
+
+ def "bean argument get test"() {
+ expect:
+ runner.get(path, Book.class).price == output
+ where:
+ path | output
+ '/beanArgTest' | 0
+ '/beanArgTest?quote=5' | 5
+ '/beanArgTest?book={"price": 6}' | 6
+ '/beanArgTest?book={"price": 6}"e=5' | 5
+ }
+
+ def "bean argument post test"() {
+ expect:
+ runner.post(path, body, Book.class).price == output
+ where:
+ path | body | output
+ '/beanArgTest' | [] | 0
+ '/beanArgTest' | [quote: 5] | 5
+ '/beanArgTest' | [book: new Book(price: 6)] | 6
+ '/beanArgTest' | [book: new Book(price: 6), quote: 5] | 5
+ '/beanArgTest' | [price: 6, quote: 5] | 5
+ }
+
+ def "advance bean argument get test"() {
+ expect:
+ runner.get(path) contains output
+ where:
+ path | output
+ '/bean?id=1&name=sam' | '"id":1,"name":"sam"'
+ '/bean?user.id=1&user.name=sam' | '"id":1,"name":"sam"'
+ '/bean?name=sam&p=123&email=a@b.com' | '"email":"a@b.com","name":"sam","phone":"123"'
+ '/bean?group.name=g1&group.owner.name=jack' | '"group":{"id":0,"name":"g1","owner":{"name":"jack"'
+ '/bean?group.parent.parent.children[0].name=xx' | '"group":{"id":0,"parent":{"id":0,"parent":{"children":[{"id":0,"name":"xx"}],"id":0}}}'
+ '/bean?group={"name":"g1","id":2}' | '"group":{"id":2,"name":"g1"}'
+ '/bean?ids=3&ids=4' | '"ids":[3,4]'
+ '/bean?ids[]=3&ids[]=4' | '"ids":[3,4]'
+ '/bean?ids[1]=3&ids[2]=4' | '"ids":[0,3,4]'
+ '/bean?scores=3&scores=4' | '"scores":[3,4]'
+ '/bean?scores[]=3&scores[]=4' | '"scores":[3,4]'
+ '/bean?scores[1]=3&scores[2]=4' | '"scores":[null,3,4]'
+ '/bean?tags[0].name=a&tags[0].value=b&tags[1].name=c&tags[1].value=d' | '"tags":[{"name":"a","value":"b"},{"name":"c","value":"d"}]'
+ '/bean?tagsA[0].name=a&tagsA[0].value=b&tagsA[1].name=c&tagsA[1].value=d' | '"tagsA":[{"name":"a","value":"b"},{"name":"c","value":"d"}]'
+ '/bean?tagsB[0].name=e&tagsB[1].name=c&tagsB[1].value=d' | '"tagsB":[{"name":"e","value":"b"},{"name":"c","value":"d"}]'
+ '/bean?tagsC[0].name=e&tagsC[1].name=c&tagsC[1].value=d' | '"tagsC":[{"name":"e","value":"b"},{"name":"c","value":"d"}]'
+ '/bean?groupMaps[0].one.name=a&groupMaps[1].two.name=b' | '"groupMaps":[{"one":{"id":0,"name":"a"}},{"two":{"id":0,"name":"b"}}]'
+ '/bean?id=1&features.a=xx&features.b=2' | '"features":{"a":"xx","b":"2"}'
+ '/bean?id=1&features[a]=xx&features[b]=2' | '"features":{"a":"xx","b":"2"}'
+ '/bean?group.id=2&group.features.a=1&group.features.b=xx' | '"group":{"features":{"a":"1","b":"xx"},"id":2}'
+ '/bean?tagMap.a.name=a&tagMap.a.value=b&tagMap.b.name=c&tagMap.b.value=d' | '"tagMap":{"a":{"name":"a","value":"b"},"b":{"name":"c","value":"d"}}'
+ '/bean?tagMapA.a.name=e&tagMapA.b.name=c&tagMapA.b.value=d' | '"tagMapA":{"a":{"name":"e","value":"b"},"b":{"name":"c","value":"d"}}'
+ '/bean?tagMapB[2].name=a&tagMapB[2].value=b&tagMapB[3].name=c' | '"tagMapB":{2:{"name":"a","value":"b"},3:{"name":"c"}}'
+ '/bean?groupsMap.one[0].name=a&groupsMap.one[1].name=b&groupsMap.two[1].name=c' | '"groupsMap":{"one":[{"id":0,"name":"a"},{"id":0,"name":"b"}],"two":[null,{"id":0,"name":"c"}]}'
+ }
+
+ def "urlEncodeForm test"() {
+ given:
+ def request = new TestRequest(
+ path: path,
+ contentType: MediaType.APPLICATION_FROM_URLENCODED,
+ params: [
+ 'name': 'Sam',
+ 'age' : 8
+ ]
+ )
+ expect:
+ runner.post(request) == output
+ where:
+ path | output
+ '/argTest' | 'Sam is 8 years old'
+ }
+
+ def "override mapping test"() {
+ expect:
+ runner.get(path) == output
+ where:
+ path | output
+ '/say?name=sam&count=2' | '2'
+ '/say?name=sam' | '1'
+ '/say~SL' | '2'
+ '/say~S' | '1'
+ '/say~S?name=sam&count=2' | '1'
+ '/say~S.yml?name=sam&count=2' | '1'
+ }
+
+ def "ambiguous mapping test"() {
+ expect:
+ runner.get(path) contains "Ambiguous mapping"
+ where:
+ path | _
+ '/say' | _
+ }
+
+ def "no interface method test"() {
+ expect:
+ runner.get(path) contains output
+ where:
+ path | output
+ '/noInterface' | 'ok'
+ '/noInterfaceAndMapping' | '404'
+ }
+
+ def "use interface argument name test"() {
+ expect:
+ runner.get(path) contains output
+ where:
+ path | output
+ '/argNameTest?name=Sam' | 'Sam'
+ }
+
+ def "pb server stream test"() {
+ expect:
+ runner.posts(path, body).size() == output
+ where:
+ path | body | output
+ '/pbServerStream' | '{"service": "3"}' | 3
+ '/pbServerStream' | '{}' | 0
+ }
+
+ def "pb server stream get test"() {
+ expect:
+ runner.gets(path).size() == output
+ where:
+ path | output
+ '/pbServerStream?request={"service": "3"}' | 3
+ }
+
+}
diff --git a/dubbo-common/src/main/java/org/apache/dubbo/config/nested/PropagationConfig.java b/dubbo-rpc/dubbo-rpc-triple/src/test/groovy/org/apache/dubbo/rpc/protocol/tri/rest/test/BaseServiceTest.groovy
similarity index 57%
copy from dubbo-common/src/main/java/org/apache/dubbo/config/nested/PropagationConfig.java
copy to dubbo-rpc/dubbo-rpc-triple/src/test/groovy/org/apache/dubbo/rpc/protocol/tri/rest/test/BaseServiceTest.groovy
index c7ee0ba7dd..cf2df41cd6 100644
--- a/dubbo-common/src/main/java/org/apache/dubbo/config/nested/PropagationConfig.java
+++ b/dubbo-rpc/dubbo-rpc-triple/src/test/groovy/org/apache/dubbo/rpc/protocol/tri/rest/test/BaseServiceTest.groovy
@@ -14,35 +14,36 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package org.apache.dubbo.config.nested;
-import java.io.Serializable;
+package org.apache.dubbo.rpc.protocol.tri.rest.test
-/**
- * Configuration for the propagation.
- */
-public class PropagationConfig implements Serializable {
+import org.apache.dubbo.rpc.protocol.tri.test.TestRunner
+import org.apache.dubbo.rpc.protocol.tri.test.TestRunnerBuilder
+
+import spock.lang.Shared
+import spock.lang.Specification
- public static final String B3 = "B3";
+abstract class BaseServiceTest extends Specification {
- public static final String W3C = "W3C";
+ @Shared
+ TestRunner runner
- /**
- * Tracing context propagation type.
- */
- private String type = W3C;
+ void setupSpec() {
+ TestRunnerBuilder build = TestRunnerBuilder.builder()
+ setupService(build)
+ runner = build.build()
+ }
- public PropagationConfig() {}
+ void cleanupSpec() {
+ runner.destroy()
+ }
- public PropagationConfig(String type) {
- this.type = type;
+ void setup() {
}
- public String getType() {
- return type;
+ void cleanup() {
}
- public void setType(String type) {
- this.type = type;
+ void setupService(TestRunnerBuilder builder) {
}
}
diff --git a/dubbo-rpc/dubbo-rpc-triple/src/test/groovy/org/apache/dubbo/rpc/protocol/tri/rest/util/PathUtilsTest.groovy b/dubbo-rpc/dubbo-rpc-triple/src/test/groovy/org/apache/dubbo/rpc/protocol/tri/rest/util/PathUtilsTest.groovy
new file mode 100644
index 0000000000..6e4047d0e9
--- /dev/null
+++ b/dubbo-rpc/dubbo-rpc-triple/src/test/groovy/org/apache/dubbo/rpc/protocol/tri/rest/util/PathUtilsTest.groovy
@@ -0,0 +1,147 @@
+/*
+ * 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.dubbo.rpc.protocol.tri.rest.util
+
+import org.apache.dubbo.common.URL
+import org.apache.dubbo.rpc.protocol.tri.rest.PathParserException
+
+import spock.lang.Specification
+
+class PathUtilsTest extends Specification {
+
+ def "GetContextPath"() {
+ expect:
+ PathUtils.getContextPath(URL.valueOf(url)) == result
+ where:
+ result | url
+ '' | 'tri://127.0.0.1'
+ '' | 'tri://127.0.0.1/'
+ '' | 'tri://127.0.0.1/one'
+ '' | 'tri://127.0.0.1/test.Demo?interface=test.DeMo'
+ 'one' | 'tri://127.0.0.1/one/test.Demo?interface=test.Demo'
+ 'one' | 'tri://127.0.0.1/one/?interface=test.Demo'
+ }
+
+ def "IsDirectPath"() {
+ expect:
+ PathUtils.isDirectPath(path) == result
+ where:
+ result | path
+ true | '/one'
+ true | '/one/{ab/c'
+ false | '/one/*'
+ false | '/one/**'
+ false | '/one/a?c'
+ false | '/one/{ab}/c'
+ }
+
+ def "Combine"() {
+ expect:
+ PathUtils.combine(path1, path2) == result
+ where:
+ path1 | path2 | result
+ '/hotels' | '' | '/hotels'
+ '' | '/hotels' | '/hotels'
+ '/hotels' | '/bookings' | '/hotels/bookings'
+ '/hotels' | 'bookings' | '/hotels/bookings'
+ '/hotels/' | '/bookings' | '/hotels/bookings'
+ '/hotels/' | '/bookings/' | '/hotels/bookings/'
+ '/hotels/*' | '/bookings' | '/hotels/bookings'
+ '/hotels/**' | '/bookings' | '/hotels/**/bookings'
+ '/hotels' | '{hotel}' | '/hotels/{hotel}'
+ '/{hotels}' | '/hotel' | '/{hotels}/hotel'
+ '/hotels/*' | '{hotel}' | '/hotels/{hotel}'
+ '/hotels/**' | '{hotel}' | '/hotels/**/{hotel}'
+ '/*.html' | '/hotels.html' | '/hotels.html'
+ '/hotels.*' | '/hotels.html' | '/hotels.html'
+ '/*.html' | '/hotels' | '/hotels.html'
+ '/' | '/bookings' | '/bookings'
+ '/hotels' | '/' | '/hotels/'
+ }
+
+ def "Combine failed"() {
+ when:
+ PathUtils.combine(path1, path2)
+ then:
+ thrown(PathParserException)
+ where:
+ path1 | path2
+ '/*.html' | '/*.txt'
+ '/*.html' | '/hotel.txt'
+ }
+
+ def "Normalize"() {
+ expect:
+ PathUtils.normalize(path) == result
+ where:
+ path | result
+ '' | '/'
+ '/' | '/'
+ '//' | '/'
+ '/.' | '/'
+ '/./' | '/'
+ '/./.' | '/'
+ '..' | '/..'
+ '/..' | '/..'
+ '../' | '/../'
+ '/../' | '/../'
+ 'one' | '/one'
+ '.one' | '/.one'
+ '/one.two' | '/one.two'
+ ' /one ' | '/one'
+ '\t /one \t\n \r' | '/one'
+ '/one#two' | '/one'
+ '/one?two' | '/one'
+ '/one' | '/one'
+ '/one/' | '/one/'
+ '/one//' | '/one/'
+ '/one/.' | '/one/'
+ 'a/..' | '/'
+ '/one/..' | '/'
+ '/one/.../' | '/one/.../'
+ '/one/../' | '/'
+ '/one/../../' | '/../'
+ '/one/../../../' | '/../../'
+ '/./one/../../../' | '/../../'
+ '/one/../two' | '/two'
+ '/one/./two' | '/one/two'
+ '/one/./two/..' | '/one/'
+ '/one/./two/../' | '/one/'
+ '/one/./two/../three' | '/one/three'
+ '/one/./two/../three/' | '/one/three/'
+ '/one/./two/../three/.' | '/one/three/'
+ '/one/./two/../three/..' | '/one/'
+ '/one/./two/../three/../' | '/one/'
+ }
+
+ def "Normalize with context path"() {
+ expect:
+ PathUtils.normalize(contextPath, path) == result
+ where:
+ contextPath | path | result
+ '' | '' | '/'
+ '' | '/' | '/'
+ '/' | '' | '/'
+ '' | '/one' | '/one'
+ '/one' | '' | '/one'
+ '/one' | 'two' | '/one/two'
+ 'one' | 'two' | '/one/two'
+ '/one' | '/two' | '/one/two'
+ '/one//' | '/two' | '/one/two'
+ }
+}
diff --git a/dubbo-rpc/dubbo-rpc-triple/src/test/java/org/apache/dubbo/rpc/protocol/tri/rest/mapping/RadixTreeTest.java b/dubbo-rpc/dubbo-rpc-triple/src/test/java/org/apache/dubbo/rpc/protocol/tri/rest/mapping/RadixTreeTest.java
deleted file mode 100644
index bd3142c0ef..0000000000
--- a/dubbo-rpc/dubbo-rpc-triple/src/test/java/org/apache/dubbo/rpc/protocol/tri/rest/mapping/RadixTreeTest.java
+++ /dev/null
@@ -1,56 +0,0 @@
-/*
- * 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.dubbo.rpc.protocol.tri.rest.mapping;
-
-import org.apache.dubbo.rpc.protocol.tri.rest.mapping.condition.PathExpression;
-import org.apache.dubbo.rpc.protocol.tri.rest.util.PathUtils;
-
-import java.util.List;
-
-import org.junit.jupiter.api.Assertions;
-import org.junit.jupiter.api.Test;
-
-class RadixTreeTest {
-
- @Test
- void match() {
- RadixTree<String> tree = new RadixTree<>();
- tree.addPath(PathExpression.parse("/a/*"), "abc");
- tree.addPath(PathExpression.parse("/a/{x}/d/e"), "acd");
- tree.addPath(PathExpression.parse("/a/{v:.*}/e"), "acd");
- List<RadixTree.Match<String>> match = tree.match("/a/b/d/e");
- Assertions.assertFalse(match.isEmpty());
- }
-
- @Test
- void match1() {
- RadixTree<String> tree = new RadixTree<>();
- tree.addPath(PathExpression.parse(PathUtils.normalize("")), "abc");
- List<RadixTree.Match<String>> match = tree.match(PathUtils.normalize(""));
- Assertions.assertFalse(match.isEmpty());
- }
-
- @Test
- void clear() {
- RadixTree<String> tree = new RadixTree<>();
- tree.addPath(PathExpression.parse("/a/*"), "abc");
- tree.addPath(PathExpression.parse("/a/{x}/d/e"), "acd");
- tree.addPath(PathExpression.parse("/a/{v:.*}/e"), "acd");
- tree.remove(s -> "abc".equals(s) || "acd".equals(s));
- Assertions.assertTrue(tree.isEmpty());
- }
-}
diff --git a/dubbo-rpc/dubbo-rpc-triple/src/test/java/org/apache/dubbo/rpc/protocol/tri/rest/mapping/condition/PathParserTest.java b/dubbo-rpc/dubbo-rpc-triple/src/test/java/org/apache/dubbo/rpc/protocol/tri/rest/mapping/condition/PathParserTest.java
deleted file mode 100644
index cb8616fc83..0000000000
--- a/dubbo-rpc/dubbo-rpc-triple/src/test/java/org/apache/dubbo/rpc/protocol/tri/rest/mapping/condition/PathParserTest.java
+++ /dev/null
@@ -1,49 +0,0 @@
-/*
- * 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.dubbo.rpc.protocol.tri.rest.mapping.condition;
-
-import org.apache.dubbo.rpc.protocol.tri.rest.util.PathUtils;
-
-import org.junit.jupiter.api.Assertions;
-import org.junit.jupiter.api.Test;
-
-class PathParserTest {
-
- @Test
- void parse1() {
- PathSegment[] parse = PathParser.parse("/{first}-{last}/b/{vv}/v");
- Assertions.assertEquals(parse.length, 4);
- }
-
- @Test
- void parse2() {
- System.out.println(PathUtils.normalize("/../a/b"));
- System.out.println(PathUtils.normalize("../a/b"));
- System.out.println(PathUtils.normalize("./a/b"));
- System.out.println(PathUtils.normalize("/a/b"));
- System.out.println(PathUtils.normalize("a/b"));
- System.out.println(PathUtils.normalize("a/../b"));
- System.out.println(PathUtils.normalize("a/../../b"));
- System.out.println(PathUtils.normalize("a/../../../b"));
- System.out.println(PathUtils.normalize(" a/../../../b \r"));
- }
-
- @Test
- void parse3() {
- System.out.println(PathUtils.normalize("/a/b/c"));
- }
-}
diff --git a/dubbo-rpc/dubbo-rpc-triple/src/test/java/org/apache/dubbo/rpc/protocol/tri/rest/service/Book.java b/dubbo-rpc/dubbo-rpc-triple/src/test/java/org/apache/dubbo/rpc/protocol/tri/rest/service/Book.java
new file mode 100644
index 0000000000..fcfcadc5bb
--- /dev/null
+++ b/dubbo-rpc/dubbo-rpc-triple/src/test/java/org/apache/dubbo/rpc/protocol/tri/rest/service/Book.java
@@ -0,0 +1,65 @@
+/*
+ * 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.dubbo.rpc.protocol.tri.rest.service;
+
+import java.util.Date;
+import java.util.Objects;
+
+public class Book {
+
+ private String name;
+ private int price;
+ private Date publishDate;
+
+ public String getName() {
+ return name;
+ }
+
+ public void setName(String name) {
+ this.name = name;
+ }
+
+ public int getPrice() {
+ return price;
+ }
+
+ public void setPrice(int price) {
+ this.price = price;
+ }
+
+ public Date getPublishDate() {
+ return publishDate;
+ }
+
+ public void setPublishDate(Date publishDate) {
+ this.publishDate = publishDate;
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) {
+ return true;
+ }
+ if (o instanceof Book) {
+ Book book = (Book) o;
+ return price == book.price
+ && Objects.equals(name, book.name)
+ && Objects.equals(publishDate, book.publishDate);
+ }
+ return false;
+ }
+}
diff --git a/dubbo-rpc/dubbo-rpc-triple/src/test/java/org/apache/dubbo/rpc/protocol/tri/rest/service/DemoService.java b/dubbo-rpc/dubbo-rpc-triple/src/test/java/org/apache/dubbo/rpc/protocol/tri/rest/service/DemoService.java
new file mode 100644
index 0000000000..cb3cc8f1da
--- /dev/null
+++ b/dubbo-rpc/dubbo-rpc-triple/src/test/java/org/apache/dubbo/rpc/protocol/tri/rest/service/DemoService.java
@@ -0,0 +1,50 @@
+/*
+ * 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.dubbo.rpc.protocol.tri.rest.service;
+
+import org.apache.dubbo.common.stream.StreamObserver;
+import org.apache.dubbo.remoting.http12.rest.Mapping;
+import org.apache.dubbo.rpc.protocol.tri.rest.service.User.UserEx;
+
+import io.grpc.health.v1.HealthCheckRequest;
+import io.grpc.health.v1.HealthCheckResponse;
+
+@Mapping("/")
+public interface DemoService {
+
+ String hello(String name);
+
+ String argTest(String name, int age);
+
+ Book beanArgTest(Book book, Integer quote);
+
+ @Mapping("/bean")
+ UserEx advanceBeanArgTest(UserEx user);
+
+ Book buy(Book book);
+
+ @Mapping("/buy2")
+ Book buy(Book book, int count);
+
+ String say(String name, Long count);
+
+ String say(String name);
+
+ String argNameTest(String name);
+
+ void pbServerStream(HealthCheckRequest request, StreamObserver<HealthCheckResponse> responseObserver);
+}
diff --git a/dubbo-rpc/dubbo-rpc-triple/src/test/java/org/apache/dubbo/rpc/protocol/tri/rest/service/DemoServiceImpl.java b/dubbo-rpc/dubbo-rpc-triple/src/test/java/org/apache/dubbo/rpc/protocol/tri/rest/service/DemoServiceImpl.java
new file mode 100644
index 0000000000..d17e71a00d
--- /dev/null
+++ b/dubbo-rpc/dubbo-rpc-triple/src/test/java/org/apache/dubbo/rpc/protocol/tri/rest/service/DemoServiceImpl.java
@@ -0,0 +1,103 @@
+/*
+ * 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.dubbo.rpc.protocol.tri.rest.service;
+
+import org.apache.dubbo.common.stream.StreamObserver;
+import org.apache.dubbo.common.utils.StringUtils;
+import org.apache.dubbo.remoting.http12.rest.Mapping;
+import org.apache.dubbo.rpc.protocol.tri.rest.service.User.UserEx;
+
+import io.grpc.health.v1.HealthCheckRequest;
+import io.grpc.health.v1.HealthCheckResponse;
+import io.grpc.health.v1.HealthCheckResponse.ServingStatus;
+
+public class DemoServiceImpl implements DemoService {
+
+ @Override
+ public String hello(String name) {
+ return "hello " + name;
+ }
+
+ @Override
+ public String argTest(String name, int age) {
+ return name + " is " + age + " years old";
+ }
+
+ @Override
+ public Book beanArgTest(Book book, Integer quote) {
+ if (book == null) {
+ book = new Book();
+ }
+ if (quote != null) {
+ book.setPrice(quote);
+ }
+ return book;
+ }
+
+ @Override
+ public UserEx advanceBeanArgTest(UserEx user) {
+ return user;
+ }
+
+ @Override
+ public Book buy(Book book) {
+ return book;
+ }
+
+ @Override
+ public Book buy(Book book, int count) {
+ return book;
+ }
+
+ @Override
+ public String say(String name, Long count) {
+ return "2";
+ }
+
+ @Override
+ public String say(String name) {
+ return "1";
+ }
+
+ @Mapping
+ public String noInterface() {
+ return "ok";
+ }
+
+ public String noInterfaceAndMapping() {
+ return "ok";
+ }
+
+ @Override
+ public String argNameTest(String name1) {
+ return name1;
+ }
+
+ @Override
+ public void pbServerStream(HealthCheckRequest request, StreamObserver<HealthCheckResponse> responseObserver) {
+ String service = request.getService();
+ if (StringUtils.isNotEmpty(service)) {
+ int count = Integer.parseInt(service);
+ for (int i = 0; i < count; i++) {
+ responseObserver.onNext(HealthCheckResponse.newBuilder()
+ .setStatus(ServingStatus.SERVING)
+ .build());
+ }
+ }
+ responseObserver.onCompleted();
+ }
+}
diff --git a/dubbo-rpc/dubbo-rpc-triple/src/test/java/org/apache/dubbo/rpc/protocol/tri/rest/service/User.java b/dubbo-rpc/dubbo-rpc-triple/src/test/java/org/apache/dubbo/rpc/protocol/tri/rest/service/User.java
new file mode 100644
index 0000000000..b74474220d
--- /dev/null
+++ b/dubbo-rpc/dubbo-rpc-triple/src/test/java/org/apache/dubbo/rpc/protocol/tri/rest/service/User.java
@@ -0,0 +1,283 @@
+/*
+ * 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.dubbo.rpc.protocol.tri.rest.service;
+
+import org.apache.dubbo.remoting.http12.rest.Param;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+public class User {
+
+ private Long id;
+ private String name;
+ private Group group;
+ private long[] ids;
+ private List<Integer> scores;
+ private List<Tag> tags;
+ private Tag[] tagsA;
+ private List<Tag> tagsB = new ArrayList<>();
+ private Tag[] tagsC = new Tag[] {new Tag("a", "b")};
+ private List<Map<String, Group>> groupMaps;
+ private Map<String, String> features;
+ private Map<String, Tag> tagMap;
+ private Map<String, Tag> tagMapA = new HashMap<>();
+ private Map<Integer, Tag> tagMapB;
+ private Map<String, List<Group>> groupsMap;
+
+ public User() {
+ tagsB.add(new Tag("a", "b"));
+ tagMapA.put("a", new Tag("a", "b"));
+ }
+
+ public Long getId() {
+ return id;
+ }
+
+ public void setId(Long id) {
+ this.id = id;
+ }
+
+ public String getName() {
+ return name;
+ }
+
+ public void setName(String name) {
+ this.name = name;
+ }
+
+ public Group getGroup() {
+ return group;
+ }
+
+ public void setGroup(Group group) {
+ this.group = group;
+ }
+
+ public long[] getIds() {
+ return ids;
+ }
+
+ public void setIds(long[] ids) {
+ this.ids = ids;
+ }
+
+ public List<Integer> getScores() {
+ return scores;
+ }
+
+ public void setScores(List<Integer> scores) {
+ this.scores = scores;
+ }
+
+ public List<Tag> getTags() {
+ return tags;
+ }
+
+ public void setTags(List<Tag> tags) {
+ this.tags = tags;
+ }
+
+ public Tag[] getTagsA() {
+ return tagsA;
+ }
+
+ public void setTagsA(Tag[] tagsA) {
+ this.tagsA = tagsA;
+ }
+
+ public List<Tag> getTagsB() {
+ return tagsB;
+ }
+
+ public void setTagsB(List<Tag> tagsB) {
+ this.tagsB = tagsB;
+ }
+
+ public Tag[] getTagsC() {
+ return tagsC;
+ }
+
+ public void setTagsC(Tag[] tagsC) {
+ this.tagsC = tagsC;
+ }
+
+ public List<Map<String, Group>> getGroupMaps() {
+ return groupMaps;
+ }
+
+ public void setGroupMaps(List<Map<String, Group>> groupMaps) {
+ this.groupMaps = groupMaps;
+ }
+
+ public Map<String, String> getFeatures() {
+ return features;
+ }
+
+ public void setFeatures(Map<String, String> features) {
+ this.features = features;
+ }
+
+ public Map<String, Tag> getTagMap() {
+ return tagMap;
+ }
+
+ public void setTagMap(Map<String, Tag> tagMap) {
+ this.tagMap = tagMap;
+ }
+
+ public Map<String, Tag> getTagMapA() {
+ return tagMapA;
+ }
+
+ public void setTagMapA(Map<String, Tag> tagMapA) {
+ this.tagMapA = tagMapA;
+ }
+
+ public Map<Integer, Tag> getTagMapB() {
+ return tagMapB;
+ }
+
+ public void setTagMapB(Map<Integer, Tag> tagMapB) {
+ this.tagMapB = tagMapB;
+ }
+
+ public Map<String, List<Group>> getGroupsMap() {
+ return groupsMap;
+ }
+
+ public void setGroupsMap(Map<String, List<Group>> groupsMap) {
+ this.groupsMap = groupsMap;
+ }
+
+ public static class UserEx extends User {
+
+ private String email;
+
+ @Param(value = "p")
+ private String phone;
+
+ public UserEx(String email) {
+ this.email = email;
+ }
+
+ public String getEmail() {
+ return email;
+ }
+
+ public void setEmail(String email) {
+ this.email = email;
+ }
+
+ public String getPhone() {
+ return phone;
+ }
+
+ public void setPhone(String phone) {
+ this.phone = phone;
+ }
+ }
+
+ public static class Group {
+
+ private int id;
+ private String name;
+ private User owner;
+ private Group parent;
+ private List<Group> children;
+ private Map<String, String> features;
+
+ public int getId() {
+ return id;
+ }
+
+ public void setId(int id) {
+ this.id = id;
+ }
+
+ public String getName() {
+ return name;
+ }
+
+ public void setName(String name) {
+ this.name = name;
+ }
+
+ public User getOwner() {
+ return owner;
+ }
+
+ public void setOwner(User owner) {
+ this.owner = owner;
+ }
+
+ public Group getParent() {
+ return parent;
+ }
+
+ public void setParent(Group parent) {
+ this.parent = parent;
+ }
+
+ public List<Group> getChildren() {
+ return children;
+ }
+
+ public void setChildren(List<Group> children) {
+ this.children = children;
+ }
+
+ public Map<String, String> getFeatures() {
+ return features;
+ }
+
+ public void setFeatures(Map<String, String> features) {
+ this.features = features;
+ }
+ }
+
+ public static class Tag {
+
+ private String name;
+ private String value;
+
+ public Tag() {}
+
+ public Tag(String name, String value) {
+ this.name = name;
+ this.value = value;
+ }
+
+ public String getName() {
+ return name;
+ }
+
+ public void setName(String name) {
+ this.name = name;
+ }
+
+ public String getValue() {
+ return value;
+ }
+
+ public void setValue(String value) {
+ this.value = value;
+ }
+ }
+}
diff --git a/dubbo-rpc/dubbo-rpc-triple/src/test/java/org/apache/dubbo/rpc/protocol/tri/test/MockH2StreamChannel.java b/dubbo-rpc/dubbo-rpc-triple/src/test/java/org/apache/dubbo/rpc/protocol/tri/test/MockH2StreamChannel.java
new file mode 100644
index 0000000000..d6a31e6e41
--- /dev/null
+++ b/dubbo-rpc/dubbo-rpc-triple/src/test/java/org/apache/dubbo/rpc/protocol/tri/test/MockH2StreamChannel.java
@@ -0,0 +1,78 @@
+/*
+ * 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.dubbo.rpc.protocol.tri.test;
+
+import org.apache.dubbo.remoting.http12.HttpMetadata;
+import org.apache.dubbo.remoting.http12.HttpOutputMessage;
+import org.apache.dubbo.remoting.http12.h2.H2StreamChannel;
+import org.apache.dubbo.remoting.http12.h2.Http2OutputMessage;
+
+import java.io.OutputStream;
+import java.net.InetSocketAddress;
+import java.net.SocketAddress;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.concurrent.CompletableFuture;
+
+public class MockH2StreamChannel implements H2StreamChannel {
+
+ private HttpMetadata httpMetadata;
+ private final List<OutputStream> bodies = new ArrayList<>();
+
+ @Override
+ public CompletableFuture<Void> writeHeader(HttpMetadata httpMetadata) {
+ this.httpMetadata = httpMetadata;
+ return CompletableFuture.completedFuture(null);
+ }
+
+ @Override
+ public CompletableFuture<Void> writeMessage(HttpOutputMessage httpOutputMessage) {
+ bodies.add(httpOutputMessage.getBody());
+ return CompletableFuture.completedFuture(null);
+ }
+
+ @Override
+ public SocketAddress remoteAddress() {
+ return InetSocketAddress.createUnresolved(TestProtocol.HOST, TestProtocol.PORT + 1);
+ }
+
+ @Override
+ public SocketAddress localAddress() {
+ return InetSocketAddress.createUnresolved(TestProtocol.HOST, TestProtocol.PORT);
+ }
+
+ @Override
+ public void flush() {}
+
+ @Override
+ public CompletableFuture<Void> writeResetFrame(long errorCode) {
+ return CompletableFuture.completedFuture(null);
+ }
+
+ @Override
+ public Http2OutputMessage newOutputMessage(boolean endStream) {
+ return new MockHttp2OutputMessage(endStream);
+ }
+
+ public HttpMetadata getHttpMetadata() {
+ return httpMetadata;
+ }
+
+ public List<OutputStream> getBodies() {
+ return bodies;
+ }
+}
diff --git a/dubbo-common/src/main/java/org/apache/dubbo/config/nested/PropagationConfig.java b/dubbo-rpc/dubbo-rpc-triple/src/test/java/org/apache/dubbo/rpc/protocol/tri/test/MockHttp2OutputMessage.java
similarity index 57%
copy from dubbo-common/src/main/java/org/apache/dubbo/config/nested/PropagationConfig.java
copy to dubbo-rpc/dubbo-rpc-triple/src/test/java/org/apache/dubbo/rpc/protocol/tri/test/MockHttp2OutputMessage.java
index c7ee0ba7dd..18fd78158a 100644
--- a/dubbo-common/src/main/java/org/apache/dubbo/config/nested/PropagationConfig.java
+++ b/dubbo-rpc/dubbo-rpc-triple/src/test/java/org/apache/dubbo/rpc/protocol/tri/test/MockHttp2OutputMessage.java
@@ -14,35 +14,30 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package org.apache.dubbo.config.nested;
+package org.apache.dubbo.rpc.protocol.tri.test;
-import java.io.Serializable;
+import org.apache.dubbo.remoting.http12.h2.Http2OutputMessage;
-/**
- * Configuration for the propagation.
- */
-public class PropagationConfig implements Serializable {
-
- public static final String B3 = "B3";
-
- public static final String W3C = "W3C";
+import java.io.ByteArrayOutputStream;
+import java.io.OutputStream;
- /**
- * Tracing context propagation type.
- */
- private String type = W3C;
+public class MockHttp2OutputMessage implements Http2OutputMessage {
- public PropagationConfig() {}
+ private final OutputStream outputStream;
+ private final boolean endStream;
- public PropagationConfig(String type) {
- this.type = type;
+ public MockHttp2OutputMessage(boolean endStream) {
+ outputStream = new ByteArrayOutputStream();
+ this.endStream = endStream;
}
- public String getType() {
- return type;
+ @Override
+ public OutputStream getBody() {
+ return outputStream;
}
- public void setType(String type) {
- this.type = type;
+ @Override
+ public boolean isEndStream() {
+ return endStream;
}
}
diff --git a/dubbo-rpc/dubbo-rpc-triple/src/test/java/org/apache/dubbo/rpc/protocol/tri/test/TestProtocol.java b/dubbo-rpc/dubbo-rpc-triple/src/test/java/org/apache/dubbo/rpc/protocol/tri/test/TestProtocol.java
new file mode 100644
index 0000000000..e7c0d07225
--- /dev/null
+++ b/dubbo-rpc/dubbo-rpc-triple/src/test/java/org/apache/dubbo/rpc/protocol/tri/test/TestProtocol.java
@@ -0,0 +1,65 @@
+/*
+ * 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.dubbo.rpc.protocol.tri.test;
+
+import org.apache.dubbo.common.URL;
+import org.apache.dubbo.rpc.Exporter;
+import org.apache.dubbo.rpc.Invoker;
+import org.apache.dubbo.rpc.RpcException;
+import org.apache.dubbo.rpc.model.FrameworkModel;
+import org.apache.dubbo.rpc.protocol.AbstractExporter;
+import org.apache.dubbo.rpc.protocol.AbstractProtocol;
+import org.apache.dubbo.rpc.protocol.tri.rest.mapping.DefaultRequestMappingRegistry;
+import org.apache.dubbo.rpc.protocol.tri.rest.mapping.RequestMappingRegistry;
+
+public class TestProtocol extends AbstractProtocol {
+
+ public static final String NAME = "test";
+ public static final String HOST = "127.0.0.1";
+ public static final int PORT = 8081;
+
+ private final RequestMappingRegistry mappingRegistry;
+
+ public TestProtocol(FrameworkModel frameworkModel) {
+ this.frameworkModel = frameworkModel;
+ mappingRegistry = frameworkModel.getBeanFactory().getOrRegisterBean(DefaultRequestMappingRegistry.class);
+ }
+
+ @Override
+ public int getDefaultPort() {
+ return PORT;
+ }
+
+ @Override
+ public <T> Exporter<T> export(Invoker<T> invoker) throws RpcException {
+ AbstractExporter<T> exporter = new AbstractExporter<T>(invoker) {
+ @Override
+ public void afterUnExport() {
+ mappingRegistry.unregister(invoker);
+ }
+ };
+
+ invokers.add(invoker);
+ mappingRegistry.register(invoker);
+ return exporter;
+ }
+
+ @Override
+ protected <T> Invoker<T> protocolBindingRefer(Class<T> type, URL url) throws RpcException {
+ throw new UnsupportedOperationException();
+ }
+}
diff --git a/dubbo-rpc/dubbo-rpc-triple/src/test/java/org/apache/dubbo/rpc/protocol/tri/test/TestRequest.java b/dubbo-rpc/dubbo-rpc-triple/src/test/java/org/apache/dubbo/rpc/protocol/tri/test/TestRequest.java
new file mode 100644
index 0000000000..7a14a0f82f
--- /dev/null
+++ b/dubbo-rpc/dubbo-rpc-triple/src/test/java/org/apache/dubbo/rpc/protocol/tri/test/TestRequest.java
@@ -0,0 +1,209 @@
+/*
+ * 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.dubbo.rpc.protocol.tri.test;
+
+import org.apache.dubbo.remoting.http12.HttpHeaderNames;
+import org.apache.dubbo.remoting.http12.HttpHeaders;
+import org.apache.dubbo.remoting.http12.HttpMethods;
+import org.apache.dubbo.remoting.http12.h2.Http2Header;
+import org.apache.dubbo.remoting.http12.h2.Http2Headers;
+import org.apache.dubbo.remoting.http12.h2.Http2MetadataFrame;
+import org.apache.dubbo.remoting.http12.message.MediaType;
+
+import java.nio.charset.Charset;
+import java.util.ArrayList;
+import java.util.LinkedHashMap;
+import java.util.List;
+import java.util.Map;
+
+@SuppressWarnings("UnusedReturnValue")
+public class TestRequest {
+
+ private final HttpHeaders headers = new HttpHeaders();
+ private final Map<String, String> cookies = new LinkedHashMap<>();
+ private final Map<String, Object> params = new LinkedHashMap<>();
+ private final Map<String, String> providerParams = new LinkedHashMap<>();
+ private List<Object> bodies;
+
+ public TestRequest(HttpMethods method, String path) {
+ setMethod(method);
+ setPath(path);
+ }
+
+ public TestRequest(String path) {
+ setPath(path);
+ }
+
+ public TestRequest() {}
+
+ public String getPath() {
+ return headers.getFirst(Http2Headers.PATH.getName());
+ }
+
+ public TestRequest setPath(String path) {
+ headers.set(Http2Headers.PATH.getName(), path);
... 1088 lines suppressed ...